backport: fix CVE-2024-24783,CVE-2024-24785,CVE-2023-45290,CVE-2023-45289
This commit is contained in:
parent
a0067aaa00
commit
5f0e9e311b
@ -0,0 +1,78 @@
|
||||
From 5dfc2e6c42724349a9e9ecbcc69be920c18d90e9 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Thu, 18 Jan 2024 12:51:13 -0800
|
||||
Subject: [PATCH 1/4] [release-branch.go1.21] crypto/x509: make sure pub key is
|
||||
non-nil before interface conversion
|
||||
|
||||
alreadyInChain assumes all keys fit a interface which contains the
|
||||
Equal method (which they do), but this ignores that certificates may
|
||||
have a nil key when PublicKeyAlgorithm is UnknownPublicKeyAlgorithm. In
|
||||
this case alreadyInChain panics.
|
||||
|
||||
Check that the key is non-nil as part of considerCandidate (we are never
|
||||
going to build a chain containing UnknownPublicKeyAlgorithm anyway).
|
||||
|
||||
For #65390
|
||||
Fixes #65392
|
||||
Fixes CVE-2024-24783
|
||||
|
||||
Change-Id: Ibdccc0a487e3368b6812be35daad2512220243f3
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2137282
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173774
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Carlos Amedee <amedee@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/569238
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
---
|
||||
src/crypto/x509/verify.go | 2 +-
|
||||
src/crypto/x509/verify_test.go | 19 +++++++++++++++++++
|
||||
2 files changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
|
||||
index 345d434453c..56a1a1725cc 100644
|
||||
--- a/src/crypto/x509/verify.go
|
||||
+++ b/src/crypto/x509/verify.go
|
||||
@@ -899,7 +899,7 @@ func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, o
|
||||
)
|
||||
|
||||
considerCandidate := func(certType int, candidate *Certificate) {
|
||||
- if alreadyInChain(candidate, currentChain) {
|
||||
+ if candidate.PublicKey == nil || alreadyInChain(candidate, currentChain) {
|
||||
return
|
||||
}
|
||||
|
||||
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
|
||||
index 3551b470ced..d8678d03f93 100644
|
||||
--- a/src/crypto/x509/verify_test.go
|
||||
+++ b/src/crypto/x509/verify_test.go
|
||||
@@ -2693,3 +2693,22 @@ func TestVerifyEKURootAsLeaf(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
+
|
||||
+func TestVerifyNilPubKey(t *testing.T) {
|
||||
+ c := &Certificate{
|
||||
+ RawIssuer: []byte{1, 2, 3},
|
||||
+ AuthorityKeyId: []byte{1, 2, 3},
|
||||
+ }
|
||||
+ opts := &VerifyOptions{}
|
||||
+ opts.Roots = NewCertPool()
|
||||
+ r := &Certificate{
|
||||
+ RawSubject: []byte{1, 2, 3},
|
||||
+ SubjectKeyId: []byte{1, 2, 3},
|
||||
+ }
|
||||
+ opts.Roots.AddCert(r)
|
||||
+
|
||||
+ _, err := c.buildChains([]*Certificate{r}, nil, opts)
|
||||
+ if _, ok := err.(UnknownAuthorityError); !ok {
|
||||
+ t.Fatalf("buildChains returned unexpected error, got: %v, want %v", err, UnknownAuthorityError{})
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -0,0 +1,193 @@
|
||||
From 0787ee55dd0d42dd8bef97d4e7ed7584f44c16e9 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <roland@golang.org>
|
||||
Date: Wed, 14 Feb 2024 17:18:36 -0800
|
||||
Subject: [PATCH 2/4] [release-branch.go1.21] html/template: escape additional
|
||||
tokens in MarshalJSON errors
|
||||
|
||||
Escape "</script" and "<!--" in errors returned from MarshalJSON errors
|
||||
when attempting to marshal types in script blocks. This prevents any
|
||||
user controlled content from prematurely terminating the script block.
|
||||
|
||||
Updates #65697
|
||||
Fixes #65968
|
||||
|
||||
Change-Id: Icf0e26c54ea7d9c1deed0bff11b6506c99ddef1b
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/564196
|
||||
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
(cherry picked from commit ccbc725f2d678255df1bd326fa511a492aa3a0aa)
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/567515
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
---
|
||||
src/html/template/js.go | 22 ++++++++-
|
||||
src/html/template/js_test.go | 96 ++++++++++++++++++++----------------
|
||||
2 files changed, 74 insertions(+), 44 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/js.go b/src/html/template/js.go
|
||||
index 4e05c145572..f4d1303bebe 100644
|
||||
--- a/src/html/template/js.go
|
||||
+++ b/src/html/template/js.go
|
||||
@@ -171,13 +171,31 @@ func jsValEscaper(args ...any) string {
|
||||
// cyclic data. This may be an unacceptable DoS risk.
|
||||
b, err := json.Marshal(a)
|
||||
if err != nil {
|
||||
- // Put a space before comment so that if it is flush against
|
||||
+ // While the standard JSON marshaller does not include user controlled
|
||||
+ // information in the error message, if a type has a MarshalJSON method,
|
||||
+ // the content of the error message is not guaranteed. Since we insert
|
||||
+ // the error into the template, as part of a comment, we attempt to
|
||||
+ // prevent the error from either terminating the comment, or the script
|
||||
+ // block itself.
|
||||
+ //
|
||||
+ // In particular we:
|
||||
+ // * replace "*/" comment end tokens with "* /", which does not
|
||||
+ // terminate the comment
|
||||
+ // * replace "</script" with "\x3C/script", and "<!--" with
|
||||
+ // "\x3C!--", which prevents confusing script block termination
|
||||
+ // semantics
|
||||
+ //
|
||||
+ // We also put a space before the comment so that if it is flush against
|
||||
// a division operator it is not turned into a line comment:
|
||||
// x/{{y}}
|
||||
// turning into
|
||||
// x//* error marshaling y:
|
||||
// second line of error message */null
|
||||
- return fmt.Sprintf(" /* %s */null ", strings.ReplaceAll(err.Error(), "*/", "* /"))
|
||||
+ errStr := err.Error()
|
||||
+ errStr = strings.ReplaceAll(errStr, "*/", "* /")
|
||||
+ errStr = strings.ReplaceAll(errStr, "</script", `\x3C/script`)
|
||||
+ errStr = strings.ReplaceAll(errStr, "<!--", `\x3C!--`)
|
||||
+ return fmt.Sprintf(" /* %s */null ", errStr)
|
||||
}
|
||||
|
||||
// TODO: maybe post-process output to prevent it from containing
|
||||
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
|
||||
index 259dcfbdc5b..17cedcec05f 100644
|
||||
--- a/src/html/template/js_test.go
|
||||
+++ b/src/html/template/js_test.go
|
||||
@@ -5,6 +5,7 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
+ "errors"
|
||||
"math"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -103,61 +104,72 @@ func TestNextJsCtx(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+type jsonErrType struct{}
|
||||
+
|
||||
+func (e *jsonErrType) MarshalJSON() ([]byte, error) {
|
||||
+ return nil, errors.New("beep */ boop </script blip <!--")
|
||||
+}
|
||||
+
|
||||
func TestJSValEscaper(t *testing.T) {
|
||||
tests := []struct {
|
||||
- x any
|
||||
- js string
|
||||
+ x any
|
||||
+ js string
|
||||
+ skipNest bool
|
||||
}{
|
||||
- {int(42), " 42 "},
|
||||
- {uint(42), " 42 "},
|
||||
- {int16(42), " 42 "},
|
||||
- {uint16(42), " 42 "},
|
||||
- {int32(-42), " -42 "},
|
||||
- {uint32(42), " 42 "},
|
||||
- {int16(-42), " -42 "},
|
||||
- {uint16(42), " 42 "},
|
||||
- {int64(-42), " -42 "},
|
||||
- {uint64(42), " 42 "},
|
||||
- {uint64(1) << 53, " 9007199254740992 "},
|
||||
+ {int(42), " 42 ", false},
|
||||
+ {uint(42), " 42 ", false},
|
||||
+ {int16(42), " 42 ", false},
|
||||
+ {uint16(42), " 42 ", false},
|
||||
+ {int32(-42), " -42 ", false},
|
||||
+ {uint32(42), " 42 ", false},
|
||||
+ {int16(-42), " -42 ", false},
|
||||
+ {uint16(42), " 42 ", false},
|
||||
+ {int64(-42), " -42 ", false},
|
||||
+ {uint64(42), " 42 ", false},
|
||||
+ {uint64(1) << 53, " 9007199254740992 ", false},
|
||||
// ulp(1 << 53) > 1 so this loses precision in JS
|
||||
// but it is still a representable integer literal.
|
||||
- {uint64(1)<<53 + 1, " 9007199254740993 "},
|
||||
- {float32(1.0), " 1 "},
|
||||
- {float32(-1.0), " -1 "},
|
||||
- {float32(0.5), " 0.5 "},
|
||||
- {float32(-0.5), " -0.5 "},
|
||||
- {float32(1.0) / float32(256), " 0.00390625 "},
|
||||
- {float32(0), " 0 "},
|
||||
- {math.Copysign(0, -1), " -0 "},
|
||||
- {float64(1.0), " 1 "},
|
||||
- {float64(-1.0), " -1 "},
|
||||
- {float64(0.5), " 0.5 "},
|
||||
- {float64(-0.5), " -0.5 "},
|
||||
- {float64(0), " 0 "},
|
||||
- {math.Copysign(0, -1), " -0 "},
|
||||
- {"", `""`},
|
||||
- {"foo", `"foo"`},
|
||||
+ {uint64(1)<<53 + 1, " 9007199254740993 ", false},
|
||||
+ {float32(1.0), " 1 ", false},
|
||||
+ {float32(-1.0), " -1 ", false},
|
||||
+ {float32(0.5), " 0.5 ", false},
|
||||
+ {float32(-0.5), " -0.5 ", false},
|
||||
+ {float32(1.0) / float32(256), " 0.00390625 ", false},
|
||||
+ {float32(0), " 0 ", false},
|
||||
+ {math.Copysign(0, -1), " -0 ", false},
|
||||
+ {float64(1.0), " 1 ", false},
|
||||
+ {float64(-1.0), " -1 ", false},
|
||||
+ {float64(0.5), " 0.5 ", false},
|
||||
+ {float64(-0.5), " -0.5 ", false},
|
||||
+ {float64(0), " 0 ", false},
|
||||
+ {math.Copysign(0, -1), " -0 ", false},
|
||||
+ {"", `""`, false},
|
||||
+ {"foo", `"foo"`, false},
|
||||
// Newlines.
|
||||
- {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
|
||||
+ {"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`, false},
|
||||
// "\v" == "v" on IE 6 so use "\u000b" instead.
|
||||
- {"\t\x0b", `"\t\u000b"`},
|
||||
- {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
|
||||
- {[]any{}, "[]"},
|
||||
- {[]any{42, "foo", nil}, `[42,"foo",null]`},
|
||||
- {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`},
|
||||
- {"<!--", `"\u003c!--"`},
|
||||
- {"-->", `"--\u003e"`},
|
||||
- {"<![CDATA[", `"\u003c![CDATA["`},
|
||||
- {"]]>", `"]]\u003e"`},
|
||||
- {"</script", `"\u003c/script"`},
|
||||
- {"\U0001D11E", "\"\U0001D11E\""}, // or "\uD834\uDD1E"
|
||||
- {nil, " null "},
|
||||
+ {"\t\x0b", `"\t\u000b"`, false},
|
||||
+ {struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`, false},
|
||||
+ {[]any{}, "[]", false},
|
||||
+ {[]any{42, "foo", nil}, `[42,"foo",null]`, false},
|
||||
+ {[]string{"<!--", "</script>", "-->"}, `["\u003c!--","\u003c/script\u003e","--\u003e"]`, false},
|
||||
+ {"<!--", `"\u003c!--"`, false},
|
||||
+ {"-->", `"--\u003e"`, false},
|
||||
+ {"<![CDATA[", `"\u003c![CDATA["`, false},
|
||||
+ {"]]>", `"]]\u003e"`, false},
|
||||
+ {"</script", `"\u003c/script"`, false},
|
||||
+ {"\U0001D11E", "\"\U0001D11E\"", false}, // or "\uD834\uDD1E"
|
||||
+ {nil, " null ", false},
|
||||
+ {&jsonErrType{}, " /* json: error calling MarshalJSON for type *template.jsonErrType: beep * / boop \\x3C/script blip \\x3C!-- */null ", true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if js := jsValEscaper(test.x); js != test.js {
|
||||
t.Errorf("%+v: want\n\t%q\ngot\n\t%q", test.x, test.js, js)
|
||||
}
|
||||
+ if test.skipNest {
|
||||
+ continue
|
||||
+ }
|
||||
// Make sure that escaping corner cases are not broken
|
||||
// by nesting.
|
||||
a := []any{test.x}
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -0,0 +1,266 @@
|
||||
From 1e8ba3a45a208d2d9838904e4ea8730d31f24d5b Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Tue, 16 Jan 2024 15:37:52 -0800
|
||||
Subject: [PATCH 3/4] [release-branch.go1.21] net/textproto, mime/multipart:
|
||||
avoid unbounded read in MIME header
|
||||
|
||||
mime/multipart.Reader.ReadForm allows specifying the maximum amount
|
||||
of memory that will be consumed by the form. While this limit is
|
||||
correctly applied to the parsed form data structure, it was not
|
||||
being applied to individual header lines in a form.
|
||||
|
||||
For example, when presented with a form containing a header line
|
||||
that never ends, ReadForm will continue to read the line until it
|
||||
runs out of memory.
|
||||
|
||||
Limit the amount of data consumed when reading a header.
|
||||
|
||||
Fixes CVE-2023-45290
|
||||
Fixes #65389
|
||||
For #65383
|
||||
|
||||
Change-Id: I7f9264d25752009e95f6b2c80e3d76aaf321d658
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2134435
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173776
|
||||
Reviewed-by: Carlos Amedee <amedee@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/569240
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
---
|
||||
src/mime/multipart/formdata_test.go | 42 +++++++++++++++++++++++++
|
||||
src/net/textproto/reader.go | 48 ++++++++++++++++++++---------
|
||||
src/net/textproto/reader_test.go | 12 ++++++++
|
||||
3 files changed, 87 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index d422729c96a..bfa9f683825 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -452,6 +452,48 @@ func TestReadFormLimits(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+func TestReadFormEndlessHeaderLine(t *testing.T) {
|
||||
+ for _, test := range []struct {
|
||||
+ name string
|
||||
+ prefix string
|
||||
+ }{{
|
||||
+ name: "name",
|
||||
+ prefix: "X-",
|
||||
+ }, {
|
||||
+ name: "value",
|
||||
+ prefix: "X-Header: ",
|
||||
+ }, {
|
||||
+ name: "continuation",
|
||||
+ prefix: "X-Header: foo\r\n ",
|
||||
+ }} {
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ const eol = "\r\n"
|
||||
+ s := `--boundary` + eol
|
||||
+ s += `Content-Disposition: form-data; name="a"` + eol
|
||||
+ s += `Content-Type: text/plain` + eol
|
||||
+ s += test.prefix
|
||||
+ fr := io.MultiReader(
|
||||
+ strings.NewReader(s),
|
||||
+ neverendingReader('X'),
|
||||
+ )
|
||||
+ r := NewReader(fr, "boundary")
|
||||
+ _, err := r.ReadForm(1 << 20)
|
||||
+ if err != ErrMessageTooLarge {
|
||||
+ t.Fatalf("ReadForm(1 << 20): %v, want ErrMessageTooLarge", err)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+type neverendingReader byte
|
||||
+
|
||||
+func (r neverendingReader) Read(p []byte) (n int, err error) {
|
||||
+ for i := range p {
|
||||
+ p[i] = byte(r)
|
||||
+ }
|
||||
+ return len(p), nil
|
||||
+}
|
||||
+
|
||||
func BenchmarkReadForm(b *testing.B) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
|
||||
index fc2590b1cdc..fcd1a011ac7 100644
|
||||
--- a/src/net/textproto/reader.go
|
||||
+++ b/src/net/textproto/reader.go
|
||||
@@ -16,6 +16,10 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
+// TODO: This should be a distinguishable error (ErrMessageTooLarge)
|
||||
+// to allow mime/multipart to detect it.
|
||||
+var errMessageTooLarge = errors.New("message too large")
|
||||
+
|
||||
// A Reader implements convenience methods for reading requests
|
||||
// or responses from a text protocol network connection.
|
||||
type Reader struct {
|
||||
@@ -36,20 +40,23 @@ func NewReader(r *bufio.Reader) *Reader {
|
||||
// ReadLine reads a single line from r,
|
||||
// eliding the final \n or \r\n from the returned string.
|
||||
func (r *Reader) ReadLine() (string, error) {
|
||||
- line, err := r.readLineSlice()
|
||||
+ line, err := r.readLineSlice(-1)
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
// ReadLineBytes is like ReadLine but returns a []byte instead of a string.
|
||||
func (r *Reader) ReadLineBytes() ([]byte, error) {
|
||||
- line, err := r.readLineSlice()
|
||||
+ line, err := r.readLineSlice(-1)
|
||||
if line != nil {
|
||||
line = bytes.Clone(line)
|
||||
}
|
||||
return line, err
|
||||
}
|
||||
|
||||
-func (r *Reader) readLineSlice() ([]byte, error) {
|
||||
+// readLineSlice reads a single line from r,
|
||||
+// up to lim bytes long (or unlimited if lim is less than 0),
|
||||
+// eliding the final \r or \r\n from the returned string.
|
||||
+func (r *Reader) readLineSlice(lim int64) ([]byte, error) {
|
||||
r.closeDot()
|
||||
var line []byte
|
||||
for {
|
||||
@@ -57,6 +64,9 @@ func (r *Reader) readLineSlice() ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
+ if lim >= 0 && int64(len(line))+int64(len(l)) > lim {
|
||||
+ return nil, errMessageTooLarge
|
||||
+ }
|
||||
// Avoid the copy if the first call produced a full line.
|
||||
if line == nil && !more {
|
||||
return l, nil
|
||||
@@ -88,7 +98,7 @@ func (r *Reader) readLineSlice() ([]byte, error) {
|
||||
//
|
||||
// Empty lines are never continued.
|
||||
func (r *Reader) ReadContinuedLine() (string, error) {
|
||||
- line, err := r.readContinuedLineSlice(noValidation)
|
||||
+ line, err := r.readContinuedLineSlice(-1, noValidation)
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
@@ -109,7 +119,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(noValidation)
|
||||
+ line, err := r.readContinuedLineSlice(-1, noValidation)
|
||||
if line != nil {
|
||||
line = bytes.Clone(line)
|
||||
}
|
||||
@@ -120,13 +130,14 @@ func (r *Reader) ReadContinuedLineBytes() ([]byte, error) {
|
||||
// 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) {
|
||||
+// It reads up to lim bytes of data (or unlimited if lim is less than 0).
|
||||
+func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) {
|
||||
if validateFirstLine == nil {
|
||||
return nil, fmt.Errorf("missing validateFirstLine func")
|
||||
}
|
||||
|
||||
// Read the first line.
|
||||
- line, err := r.readLineSlice()
|
||||
+ line, err := r.readLineSlice(lim)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -154,13 +165,21 @@ func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([
|
||||
// copy the slice into buf.
|
||||
r.buf = append(r.buf[:0], trim(line)...)
|
||||
|
||||
+ if lim < 0 {
|
||||
+ lim = math.MaxInt64
|
||||
+ }
|
||||
+ lim -= int64(len(r.buf))
|
||||
+
|
||||
// Read continuation lines.
|
||||
for r.skipSpace() > 0 {
|
||||
- line, err := r.readLineSlice()
|
||||
+ r.buf = append(r.buf, ' ')
|
||||
+ if int64(len(r.buf)) >= lim {
|
||||
+ return nil, errMessageTooLarge
|
||||
+ }
|
||||
+ line, err := r.readLineSlice(lim - int64(len(r.buf)))
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
- r.buf = append(r.buf, ' ')
|
||||
r.buf = append(r.buf, trim(line)...)
|
||||
}
|
||||
return r.buf, nil
|
||||
@@ -507,7 +526,8 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error)
|
||||
|
||||
// The first line cannot start with a leading space.
|
||||
if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
|
||||
- line, err := r.readLineSlice()
|
||||
+ const errorLimit = 80 // arbitrary limit on how much of the line we'll quote
|
||||
+ line, err := r.readLineSlice(errorLimit)
|
||||
if err != nil {
|
||||
return m, err
|
||||
}
|
||||
@@ -515,7 +535,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error)
|
||||
}
|
||||
|
||||
for {
|
||||
- kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon)
|
||||
+ kv, err := r.readContinuedLineSlice(maxMemory, mustHaveFieldNameColon)
|
||||
if len(kv) == 0 {
|
||||
return m, err
|
||||
}
|
||||
@@ -544,7 +564,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error)
|
||||
|
||||
maxHeaders--
|
||||
if maxHeaders < 0 {
|
||||
- return nil, errors.New("message too large")
|
||||
+ return nil, errMessageTooLarge
|
||||
}
|
||||
|
||||
// Skip initial spaces in value.
|
||||
@@ -557,9 +577,7 @@ func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error)
|
||||
}
|
||||
maxMemory -= int64(len(value))
|
||||
if maxMemory < 0 {
|
||||
- // TODO: This should be a distinguishable error (ErrMessageTooLarge)
|
||||
- // to allow mime/multipart to detect it.
|
||||
- return m, errors.New("message too large")
|
||||
+ return m, errMessageTooLarge
|
||||
}
|
||||
if vv == nil && len(strs) > 0 {
|
||||
// More than likely this will be a single-element key.
|
||||
diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go
|
||||
index 696ae406f38..26ff617470b 100644
|
||||
--- a/src/net/textproto/reader_test.go
|
||||
+++ b/src/net/textproto/reader_test.go
|
||||
@@ -36,6 +36,18 @@ func TestReadLine(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+func TestReadLineLongLine(t *testing.T) {
|
||||
+ line := strings.Repeat("12345", 10000)
|
||||
+ r := reader(line + "\r\n")
|
||||
+ s, err := r.ReadLine()
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("Line 1: %v", err)
|
||||
+ }
|
||||
+ if s != line {
|
||||
+ t.Fatalf("%v-byte line does not match expected %v-byte line", len(s), len(line))
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func TestReadContinuedLine(t *testing.T) {
|
||||
r := reader("line1\nline\n 2\nline3\n")
|
||||
s, err := r.ReadContinuedLine()
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -0,0 +1,117 @@
|
||||
From 6021dca711926c32959c2dc4c2a68c9494e9a4fc Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Thu, 11 Jan 2024 11:31:57 -0800
|
||||
Subject: [PATCH 4/4] [release-branch.go1.21] net/http, net/http/cookiejar:
|
||||
avoid subdomain matches on IPv6 zones
|
||||
|
||||
When deciding whether to forward cookies or sensitive headers
|
||||
across a redirect, do not attempt to interpret an IPv6 address
|
||||
as a domain name.
|
||||
|
||||
Avoids a case where a maliciously-crafted redirect to an
|
||||
IPv6 address with a scoped addressing zone could be
|
||||
misinterpreted as a within-domain redirect. For example,
|
||||
we could interpret "::1%.www.example.com" as a subdomain
|
||||
of "www.example.com".
|
||||
|
||||
Thanks to Juho Nurminen of Mattermost for reporting this issue.
|
||||
|
||||
Fixes CVE-2023-45289
|
||||
Fixes #65385
|
||||
For #65065
|
||||
|
||||
Change-Id: I8f463f59f0e700c8a18733d2b264a8bcb3a19599
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2131938
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2173775
|
||||
Reviewed-by: Carlos Amedee <amedee@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/569239
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
TryBot-Bypass: Michael Knyszek <mknyszek@google.com>
|
||||
---
|
||||
src/net/http/client.go | 6 ++++++
|
||||
src/net/http/client_test.go | 1 +
|
||||
src/net/http/cookiejar/jar.go | 7 +++++++
|
||||
src/net/http/cookiejar/jar_test.go | 10 ++++++++++
|
||||
4 files changed, 24 insertions(+)
|
||||
|
||||
diff --git a/src/net/http/client.go b/src/net/http/client.go
|
||||
index 2cab53a585a..77a701b8061 100644
|
||||
--- a/src/net/http/client.go
|
||||
+++ b/src/net/http/client.go
|
||||
@@ -1014,6 +1014,12 @@ func isDomainOrSubdomain(sub, parent string) bool {
|
||||
if sub == parent {
|
||||
return true
|
||||
}
|
||||
+ // If sub contains a :, it's probably an IPv6 address (and is definitely not a hostname).
|
||||
+ // Don't check the suffix in this case, to avoid matching the contents of a IPv6 zone.
|
||||
+ // For example, "::1%.www.example.com" is not a subdomain of "www.example.com".
|
||||
+ if strings.ContainsAny(sub, ":%") {
|
||||
+ return false
|
||||
+ }
|
||||
// If sub is "foo.example.com" and parent is "example.com",
|
||||
// that means sub must end in "."+parent.
|
||||
// Do it without allocating.
|
||||
diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go
|
||||
index 0fe555af38f..fc1d7916124 100644
|
||||
--- a/src/net/http/client_test.go
|
||||
+++ b/src/net/http/client_test.go
|
||||
@@ -1725,6 +1725,7 @@ func TestShouldCopyHeaderOnRedirect(t *testing.T) {
|
||||
{"authorization", "http://foo.com/", "https://foo.com/", true},
|
||||
{"authorization", "http://foo.com:1234/", "http://foo.com:4321/", true},
|
||||
{"www-authenticate", "http://foo.com/", "http://bar.com/", false},
|
||||
+ {"authorization", "http://foo.com/", "http://[::1%25.foo.com]/", false},
|
||||
|
||||
// But subdomains should work:
|
||||
{"www-authenticate", "http://foo.com/", "http://foo.com/", true},
|
||||
diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go
|
||||
index 273b54c84c7..4b16266057b 100644
|
||||
--- a/src/net/http/cookiejar/jar.go
|
||||
+++ b/src/net/http/cookiejar/jar.go
|
||||
@@ -362,6 +362,13 @@ func jarKey(host string, psl PublicSuffixList) string {
|
||||
|
||||
// isIP reports whether host is an IP address.
|
||||
func isIP(host string) bool {
|
||||
+ if strings.ContainsAny(host, ":%") {
|
||||
+ // Probable IPv6 address.
|
||||
+ // Hostnames can't contain : or %, so this is definitely not a valid host.
|
||||
+ // Treating it as an IP is the more conservative option, and avoids the risk
|
||||
+ // of interpeting ::1%.www.example.com as a subtomain of www.example.com.
|
||||
+ return true
|
||||
+ }
|
||||
return net.ParseIP(host) != nil
|
||||
}
|
||||
|
||||
diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go
|
||||
index 56d0695a660..251f7c16171 100644
|
||||
--- a/src/net/http/cookiejar/jar_test.go
|
||||
+++ b/src/net/http/cookiejar/jar_test.go
|
||||
@@ -252,6 +252,7 @@ var isIPTests = map[string]bool{
|
||||
"127.0.0.1": true,
|
||||
"1.2.3.4": true,
|
||||
"2001:4860:0:2001::68": true,
|
||||
+ "::1%zone": true,
|
||||
"example.com": false,
|
||||
"1.1.1.300": false,
|
||||
"www.foo.bar.net": false,
|
||||
@@ -629,6 +630,15 @@ var basicsTests = [...]jarTest{
|
||||
{"http://www.host.test:1234/", "a=1"},
|
||||
},
|
||||
},
|
||||
+ {
|
||||
+ "IPv6 zone is not treated as a host.",
|
||||
+ "https://example.com/",
|
||||
+ []string{"a=1"},
|
||||
+ "a=1",
|
||||
+ []query{
|
||||
+ {"https://[::1%25.example.com]:80/", ""},
|
||||
+ },
|
||||
+ },
|
||||
}
|
||||
|
||||
func TestBasics(t *testing.T) {
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
|
||||
Name: golang
|
||||
Version: 1.21.4
|
||||
Release: 2
|
||||
Release: 3
|
||||
Summary: The Go Programming Language
|
||||
License: BSD and Public Domain
|
||||
URL: https://golang.org/
|
||||
@ -354,6 +354,9 @@ fi
|
||||
%files devel -f go-tests.list -f go-misc.list -f go-src.list
|
||||
|
||||
%changelog
|
||||
* Fri Mar 15 2024 hanchao <hanchao63@huawei.com> - 1.21.4-3
|
||||
- fix CVE-2024-24783,CVE-2024-24785,CVE-2023-45290,CVE-2023-45289
|
||||
|
||||
* Wed Dec 13 2023 jiahua.yu <jiahua.yu@shingroup.cn> - 1.21.4-2
|
||||
- init support for arch ppc64le
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user