Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mime/multipart: quoted printable decoding fails with invalid empty body #6507

Closed
eaigner opened this issue Sep 28, 2013 · 16 comments
Closed
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done. Suggested Issues that may be good for new contributors looking for work to do.
Milestone

Comments

@eaigner
Copy link
Contributor

eaigner commented Sep 28, 2013

I use the mime/multipart decoder on a production server and stumbled over a problem with
empty bodies.

Take for example this  multipart/alternative part:

    --===============9099738862860321038==
    MIME-Version: 1.0
    Content-Type: multipart/alternative; charset="utf-8";
     boundary="===============3362771883058111967=="
    Content-Transfer-Encoding: quoted-printable

   --===============3362771883058111967==

Obviously it is not correct because the alternative part is not quoted-printable encoded,
but it's child parts - however - the parser fails here with this error:

    "multipart: NextPart: multipart: invalid quoted-printable hex byte 0x3d"

It's probably because the parser tries to parse the next boundary line as
quoted-printable encoded text.
@eaigner
Copy link
Contributor Author

eaigner commented Sep 28, 2013

Comment 1:

Here is the complete example:
MIME-Version: 1.0
Content-Type: multipart/related; charset="utf-8";
 boundary="===============9099738862860321038=="
Content-Transfer-Encoding: quoted-printable
This is a multi-part message in MIME format.
--===============9099738862860321038==
MIME-Version: 1.0
Content-Type: multipart/alternative; charset="utf-8";
 boundary="===============3362771883058111967=="
Content-Transfer-Encoding: quoted-printable
--===============3362771883058111967==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
<p>Hello</p>
   =20
--===============3362771883058111967==--
--===============9099738862860321038==--

@eaigner
Copy link
Contributor Author

eaigner commented Sep 28, 2013

Comment 2:

I'm not quite sure what the appropriate "fix" for this is, since the input is obviously
invalid, but there certainly has to be a way to prevent/circumvent this, either by:
1) Disabling the automatic QP decoding with a flag on multipart.Reader (preferred)
2) Patching line 109 `if bp.Header.Get(cte) == "quoted-printable" {` in
mime/multipart/multipart.go to check for a multipart content type.

@eaigner
Copy link
Contributor Author

eaigner commented Sep 28, 2013

Comment 3:

I currently patched it like this
        const ct = "Content-Type"
    if !strings.HasPrefix(bp.Header.Get(ct), "multipart/") &&
        bp.Header.Get(cte) == "quoted-printable" {
        bp.Header.Del(cte)
        bp.r = newQuotedPrintableReader(bp.r)
    }

@eaigner
Copy link
Contributor Author

eaigner commented Sep 28, 2013

Comment 4:

Issue should probably be renamed to "mime/multipart: quoted-printable transfer encoding
on multipart child decodes boundary" or something like that.

@rsc
Copy link
Contributor

rsc commented Oct 18, 2013

Comment 5:

Since the input is invalid, it can wait until Go 1.3.

Labels changed: added priority-later, go1.3, removed priority-triage.

Status changed to Accepted.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 6:

Labels changed: added release-go1.3.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 7:

Labels changed: removed go1.3.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 8:

Labels changed: added repo-main.

@rsc
Copy link
Contributor

rsc commented Apr 3, 2014

Comment 9:

Labels changed: added suggested, removed release-go1.3.

@rsc
Copy link
Contributor

rsc commented Apr 3, 2014

Comment 10:

Labels changed: added release-none.

@eaigner eaigner added accepted Suggested Issues that may be good for new contributors looking for work to do. labels Apr 3, 2014
@rsc rsc added this to the Unplanned milestone Apr 10, 2015
@bradfitz bradfitz added help wanted NeedsFix The path to resolution is known, but the work has not been done. labels Jun 25, 2017
@bradfitz bradfitz modified the milestones: Go1.10, Unplanned Jun 25, 2017
@kennygrant
Copy link
Contributor

Github seems to have mangled whitespace in the original examples. I have I think reproduced this here:

https://play.golang.org/p/Kbw25pNXt_

but the code in mime/multipart has changed in the last few years, it seems the error message has changed to ErrUnexpectedEOF and is now on line 179 in Read.

The original suggestion was to add a flag to allow disabling transparent decoding of quoted printable encoding in parts which sounds reasonable. I'll put up a change for review adding a boolean flag to Reader.

@gopherbot
Copy link

Change https://golang.org/cl/74191 mentions this issue: mime: allow disabling decode of quoted printable encoding

@kennygrant
Copy link
Contributor

kennygrant commented Oct 31, 2017

@ianlancetaylor re CL 74191, I should probably have checked here first what needs fix and help wanted means - I took it to mean that mime/multipart should allow disabling the automatic decoding of parts.

The initial issue here is about an invalid message, but it also points out that there is no way to disable automatic decoding of quoted-printable within a mime message. This means if a service receives mime messages which may be malformed like the ones above (or in some other way within a part where the quoted printable fails), but which they'd like to be able to decode into parts, they'd have to munge them first to fix them up, or parse them without the standard library. Hence the suggestion above of adding a flag that lets the user set whether multipart decodes quoted-printable on parts which seems a more general fix than handling this specific malformed mime with a special case when parsing, but I see why you might not want to expand the API surface.

There are two possible solutions offered above and perhaps there are other fixes possible. Happy to look at a different fix if you have a preference, or leave this for now pending a decision.

@ianlancetaylor
Copy link
Contributor

There are lots of different ways for messages to be invalid. If you know ahead of time that the input is formatted incorrectly in a particular way, you can apply a correction to it as it arrives. If you don't know ahead of time, then a new API knob won't help, because you won't know that you need to set it. I don't know what the right answer is here, but I would need to be convinced that a new API knob is the right approach.

@rsc
Copy link
Contributor

rsc commented Nov 22, 2017

I spent a while digging into this issue and I don't understand why it is even an open issue.

As far as I can see, the multipart parser correctly rejects an invalid multipart message. What is the argument for not rejecting an invalid multipart message? The usual argument would be that some popular piece of software generates these inputs, so we must be able to deal with them. But I don't see that argument being made: I see nothing about how this might arise in practice. Short of a compelling reason to accept invalid input, we should probably continue to reject invalid input.

The full message in the example (with one annotation from me) is:

MIME-Version: 1.0
Content-Type: multipart/related; charset="utf-8";
 boundary="===============9099738862860321038=="
Content-Transfer-Encoding: quoted-printable                   <<<< THIS LINE IS WRONG!

This is a multi-part message in MIME format.
--===============9099738862860321038==
MIME-Version: 1.0
Content-Type: multipart/alternative; charset="utf-8";
 boundary="===============3362771883058111967=="
Content-Transfer-Encoding: quoted-printable                   <<<< THIS LINE IS WRONG!

--===============3362771883058111967==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable

<p>Hello</p>
   =20
--===============3362771883058111967==--
--===============9099738862860321038==--

The problem with this message is not an "invalid empty body" but the fact that both the lines line I marked are present. RFC 2046 says:

   As stated in the definition of the Content-Transfer-Encoding field
   [RFC 2045], no encoding other than "7bit", "8bit", or "binary" is
   permitted for entities of type "multipart".  The "multipart" boundary
   delimiters and header fields are always represented as 7bit US-ASCII
   in any case (though the header fields may encode non-US-ASCII header
   text as per RFC 2047) and data within the body parts can be encoded
   on a part-by-part basis, with Content-Transfer-Encoding fields for
   each appropriate body part.

Content-Transfer-Encoding: quoted-printable is not allowed in multipart/ headers. It's arguably a bug that the processing even reaches the second, when the first is wrong too.

Maybe we should put in an error check like this:

@@ -136,6 +138,16 @@ func newPart(mr *Reader) (*Part, error) {
 	bp.r = partReader{bp}
 	const cte = "Content-Transfer-Encoding"
 	if bp.Header.Get(cte) == "quoted-printable" {
+		if strings.HasPrefix(bp.Header.Get("Content-Type"), "multipart/") {
+			// RFC 2046 requires that a Content-Type: multipart/*
+			// must use Content-Transfer-Encoding: 7bit, 8bit, or binary,
+			// and that the farming is "always represented as 7bit US-ASCII
+			// in any case".
+			// Returning an error here avoids confusion trying to apply
+			// quoted-printable decoding to the body.
+			// See golang.org/issue/6507.
+			return nil, errors.New("mime: transfer encoding for multipart content type")
+		}
 		bp.Header.Del(cte)
 		bp.r = quotedprintable.NewReader(bp.r)
 	}

but I think that will catch the first line, which we're currently allowing. The right answer here depends on why anyone cares about this case, which we need more information about.

Moving to Go 1.11.

@rsc rsc modified the milestones: Go1.10, Go1.11 Nov 22, 2017
@ianlancetaylor
Copy link
Contributor

Closing per previous comment.

@golang golang locked and limited conversation to collaborators Jun 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge help wanted NeedsFix The path to resolution is known, but the work has not been done. Suggested Issues that may be good for new contributors looking for work to do.
Projects
None yet
Development

No branches or pull requests

6 participants