]> Cypherpunks repositories - gostls13.git/commitdiff
net/textproto: do not allow multi-line header field names
authorKatie Hockman <katie@golang.org>
Tue, 8 Oct 2019 18:19:34 +0000 (14:19 -0400)
committerKatie Hockman <katie@golang.org>
Tue, 8 Oct 2019 21:04:21 +0000 (21:04 +0000)
Fixes #34702

Change-Id: I98320d54726e646a310e583283ddab676c3503e7
Reviewed-on: https://go-review.googlesource.com/c/go/+/199838
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
src/net/textproto/reader.go
src/net/textproto/reader_test.go

index 87f901b4fce17ee3ee1b03faee0494cc69a9fdf5..a505da985c281a038726899dbf55a1662b8e8d1a 100644 (file)
@@ -7,6 +7,7 @@ package textproto
 import (
        "bufio"
        "bytes"
+       "fmt"
        "io"
        "io/ioutil"
        "strconv"
@@ -90,7 +91,7 @@ func (r *Reader) readLineSlice() ([]byte, error) {
 // A line consisting of only white space is never continued.
 //
 func (r *Reader) ReadContinuedLine() (string, error) {
-       line, err := r.readContinuedLineSlice()
+       line, err := r.readContinuedLineSlice(noValidation)
        return string(line), err
 }
 
@@ -111,7 +112,7 @@ func trim(s []byte) []byte {
 // ReadContinuedLineBytes is like ReadContinuedLine but
 // returns a []byte instead of a string.
 func (r *Reader) ReadContinuedLineBytes() ([]byte, error) {
-       line, err := r.readContinuedLineSlice()
+       line, err := r.readContinuedLineSlice(noValidation)
        if line != nil {
                buf := make([]byte, len(line))
                copy(buf, line)
@@ -120,7 +121,15 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, error) {
        return line, err
 }
 
-func (r *Reader) readContinuedLineSlice() ([]byte, error) {
+// readContinuedLineSlice reads continued lines from the reader buffer,
+// returning a byte slice with all lines. The validateFirstLine function
+// is run on the first read line, and if it returns an error then this
+// error is returned from readContinuedLineSlice.
+func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) {
+       if validateFirstLine == nil {
+               return nil, fmt.Errorf("missing validateFirstLine func")
+       }
+
        // Read the first line.
        line, err := r.readLineSlice()
        if err != nil {
@@ -130,6 +139,10 @@ func (r *Reader) readContinuedLineSlice() ([]byte, error) {
                return line, nil
        }
 
+       if err := validateFirstLine(line); err != nil {
+               return nil, err
+       }
+
        // Optimistically assume that we have started to buffer the next line
        // and it starts with an ASCII letter (the next header key), or a blank
        // line, so we can avoid copying that buffered data around in memory
@@ -490,7 +503,7 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
        }
 
        for {
-               kv, err := r.readContinuedLineSlice()
+               kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon)
                if len(kv) == 0 {
                        return m, err
                }
@@ -535,6 +548,20 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
        }
 }
 
+// noValidation is a no-op validation func for readContinuedLineSlice
+// that permits any lines.
+func noValidation(_ []byte) error { return nil }
+
+// mustHaveFieldNameColon ensures that, per RFC 7230, the
+// field-name is on a single line, so the first line must
+// contain a colon.
+func mustHaveFieldNameColon(line []byte) error {
+       if bytes.IndexByte(line, ':') < 0 {
+               return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q" + string(line)))
+       }
+       return nil
+}
+
 // upcomingHeaderNewlines returns an approximation of the number of newlines
 // that will be in this header. If it gets confused, it returns 0.
 func (r *Reader) upcomingHeaderNewlines() (n int) {
index 97fb1ab028107fb73bb52d1e32158c4ba21bbb46..595d94f938af4c2b891f47c87e993e4a1af6c800 100644 (file)
@@ -218,6 +218,10 @@ func TestReadMIMEHeaderMalformed(t *testing.T) {
                " First: line with leading space\r\nFoo: foo\r\n\r\n",
                "\tFirst: line with leading tab\r\nFoo: foo\r\n\r\n",
                "Foo: foo\r\nNo colon second line\r\n\r\n",
+               "Foo-\n\tBar: foo\r\n\r\n",
+               "Foo-\r\n\tBar: foo\r\n\r\n",
+               "Foo\r\n\t: foo\r\n\r\n",
+               "Foo-\n\tBar",
        }
 
        for _, input := range inputs {