summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/recipes-devtools/go/go-1.12.inc2
-rw-r--r--meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch28
-rw-r--r--meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch429
3 files changed, 459 insertions, 0 deletions
diff --git a/meta/recipes-devtools/go/go-1.12.inc b/meta/recipes-devtools/go/go-1.12.inc
index fd2d641554..2a0680aeaa 100644
--- a/meta/recipes-devtools/go/go-1.12.inc
+++ b/meta/recipes-devtools/go/go-1.12.inc
@@ -20,6 +20,8 @@ SRC_URI += "\
20 file://0010-fix-CVE-2019-17596.patch \ 20 file://0010-fix-CVE-2019-17596.patch \
21 file://CVE-2020-15586.patch \ 21 file://CVE-2020-15586.patch \
22 file://CVE-2020-16845.patch \ 22 file://CVE-2020-16845.patch \
23 file://0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch \
24 file://CVE-2020-24553.patch \
23" 25"
24SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch" 26SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch"
25 27
diff --git a/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch b/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch
new file mode 100644
index 0000000000..7c07961c03
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.12/0001-net-http-cgi-rename-a-test-file-to-be-less-cute.patch
@@ -0,0 +1,28 @@
1From 8390c478600b852392cb116741b3cb239c94d123 Mon Sep 17 00:00:00 2001
2From: Brad Fitzpatrick <bradfitz@golang.org>
3Date: Wed, 15 Jan 2020 18:08:10 +0000
4Subject: [PATCH] net/http/cgi: rename a test file to be less cute
5
6My fault (from CL 4245070), sorry.
7
8Change-Id: Ib95d3170dc326e74aa74c22421c4e44a8b00f577
9Reviewed-on: https://go-review.googlesource.com/c/go/+/214920
10Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
11TryBot-Result: Gobot Gobot <gobot@golang.org>
12Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
13
14Upstream-Status: Backport
15[lz: Add this patch for merging the patch for CVE-2020-24553]
16Signed-off-by: Li Zhou <li.zhou@windriver.com>
17---
18 src/net/http/cgi/{matryoshka_test.go => integration_test.go} | 0
19 1 file changed, 0 insertions(+), 0 deletions(-)
20 rename src/net/http/cgi/{matryoshka_test.go => integration_test.go} (100%)
21
22diff --git a/src/net/http/cgi/matryoshka_test.go b/src/net/http/cgi/integration_test.go
23similarity index 100%
24rename from src/net/http/cgi/matryoshka_test.go
25rename to src/net/http/cgi/integration_test.go
26--
272.17.1
28
diff --git a/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch b/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch
new file mode 100644
index 0000000000..18a218bc9a
--- /dev/null
+++ b/meta/recipes-devtools/go/go-1.12/CVE-2020-24553.patch
@@ -0,0 +1,429 @@
1From eb07103a083237414145a45f029c873d57037e06 Mon Sep 17 00:00:00 2001
2From: Roberto Clapis <roberto@golang.org>
3Date: Wed, 26 Aug 2020 08:53:03 +0200
4Subject: [PATCH] [release-branch.go1.15-security] net/http/cgi,net/http/fcgi:
5 add Content-Type detection
6
7This CL ensures that responses served via CGI and FastCGI
8have a Content-Type header based on the content of the
9response if not explicitly set by handlers.
10
11If the implementers of the handler did not explicitly
12specify a Content-Type both CGI implementations would default
13to "text/html", potentially causing cross-site scripting.
14
15Thanks to RedTeam Pentesting GmbH for reporting this.
16
17Fixes CVE-2020-24553
18
19Change-Id: I82cfc396309b5ab2e8d6e9a87eda8ea7e3799473
20Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/823217
21Reviewed-by: Russ Cox <rsc@google.com>
22(cherry picked from commit 23d675d07fdc56aafd67c0a0b63d5b7e14708ff0)
23Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/835311
24Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
25
26Upstream-Status: Backport
27CVE: CVE-2020-24553
28Signed-off-by: Li Zhou <li.zhou@windriver.com>
29---
30 src/net/http/cgi/child.go | 36 ++++++++++-----
31 src/net/http/cgi/child_test.go | 69 ++++++++++++++++++++++++++++
32 src/net/http/cgi/integration_test.go | 53 ++++++++++++++++++++-
33 src/net/http/fcgi/child.go | 39 ++++++++++++----
34 src/net/http/fcgi/fcgi_test.go | 52 +++++++++++++++++++++
35 5 files changed, 227 insertions(+), 22 deletions(-)
36
37diff --git a/src/net/http/cgi/child.go b/src/net/http/cgi/child.go
38index 9474175f17..61de6165f6 100644
39--- a/src/net/http/cgi/child.go
40+++ b/src/net/http/cgi/child.go
41@@ -163,10 +163,12 @@ func Serve(handler http.Handler) error {
42 }
43
44 type response struct {
45- req *http.Request
46- header http.Header
47- bufw *bufio.Writer
48- headerSent bool
49+ req *http.Request
50+ header http.Header
51+ code int
52+ wroteHeader bool
53+ wroteCGIHeader bool
54+ bufw *bufio.Writer
55 }
56
57 func (r *response) Flush() {
58@@ -178,26 +180,38 @@ func (r *response) Header() http.Header {
59 }
60
61 func (r *response) Write(p []byte) (n int, err error) {
62- if !r.headerSent {
63+ if !r.wroteHeader {
64 r.WriteHeader(http.StatusOK)
65 }
66+ if !r.wroteCGIHeader {
67+ r.writeCGIHeader(p)
68+ }
69 return r.bufw.Write(p)
70 }
71
72 func (r *response) WriteHeader(code int) {
73- if r.headerSent {
74+ if r.wroteHeader {
75 // Note: explicitly using Stderr, as Stdout is our HTTP output.
76 fmt.Fprintf(os.Stderr, "CGI attempted to write header twice on request for %s", r.req.URL)
77 return
78 }
79- r.headerSent = true
80- fmt.Fprintf(r.bufw, "Status: %d %s\r\n", code, http.StatusText(code))
81+ r.wroteHeader = true
82+ r.code = code
83+}
84
85- // Set a default Content-Type
86+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
87+// p is not written by writeHeader, but is the first chunk of the body
88+// that will be written. It is sniffed for a Content-Type if none is
89+// set explicitly.
90+func (r *response) writeCGIHeader(p []byte) {
91+ if r.wroteCGIHeader {
92+ return
93+ }
94+ r.wroteCGIHeader = true
95+ fmt.Fprintf(r.bufw, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
96 if _, hasType := r.header["Content-Type"]; !hasType {
97- r.header.Add("Content-Type", "text/html; charset=utf-8")
98+ r.header.Set("Content-Type", http.DetectContentType(p))
99 }
100-
101 r.header.Write(r.bufw)
102 r.bufw.WriteString("\r\n")
103 r.bufw.Flush()
104diff --git a/src/net/http/cgi/child_test.go b/src/net/http/cgi/child_test.go
105index 14e0af475f..f6ecb6eb80 100644
106--- a/src/net/http/cgi/child_test.go
107+++ b/src/net/http/cgi/child_test.go
108@@ -7,6 +7,11 @@
109 package cgi
110
111 import (
112+ "bufio"
113+ "bytes"
114+ "net/http"
115+ "net/http/httptest"
116+ "strings"
117 "testing"
118 )
119
120@@ -148,3 +153,67 @@ func TestRequestWithoutRemotePort(t *testing.T) {
121 t.Errorf("RemoteAddr: got %q; want %q", g, e)
122 }
123 }
124+
125+type countingWriter int
126+
127+func (c *countingWriter) Write(p []byte) (int, error) {
128+ *c += countingWriter(len(p))
129+ return len(p), nil
130+}
131+func (c *countingWriter) WriteString(p string) (int, error) {
132+ *c += countingWriter(len(p))
133+ return len(p), nil
134+}
135+
136+func TestResponse(t *testing.T) {
137+ var tests = []struct {
138+ name string
139+ body string
140+ wantCT string
141+ }{
142+ {
143+ name: "no body",
144+ wantCT: "text/plain; charset=utf-8",
145+ },
146+ {
147+ name: "html",
148+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
149+ wantCT: "text/html; charset=utf-8",
150+ },
151+ {
152+ name: "text",
153+ body: strings.Repeat("gopher", 86),
154+ wantCT: "text/plain; charset=utf-8",
155+ },
156+ {
157+ name: "jpg",
158+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
159+ wantCT: "image/jpeg",
160+ },
161+ }
162+ for _, tt := range tests {
163+ t.Run(tt.name, func(t *testing.T) {
164+ var buf bytes.Buffer
165+ resp := response{
166+ req: httptest.NewRequest("GET", "/", nil),
167+ header: http.Header{},
168+ bufw: bufio.NewWriter(&buf),
169+ }
170+ n, err := resp.Write([]byte(tt.body))
171+ if err != nil {
172+ t.Errorf("Write: unexpected %v", err)
173+ }
174+ if want := len(tt.body); n != want {
175+ t.Errorf("reported short Write: got %v want %v", n, want)
176+ }
177+ resp.writeCGIHeader(nil)
178+ resp.Flush()
179+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
180+ t.Errorf("wrong content-type: got %q, want %q", got, tt.wantCT)
181+ }
182+ if !bytes.HasSuffix(buf.Bytes(), []byte(tt.body)) {
183+ t.Errorf("body was not correctly written")
184+ }
185+ })
186+ }
187+}
188diff --git a/src/net/http/cgi/integration_test.go b/src/net/http/cgi/integration_test.go
189index 32d59c09a3..295c3b82d4 100644
190--- a/src/net/http/cgi/integration_test.go
191+++ b/src/net/http/cgi/integration_test.go
192@@ -16,7 +16,9 @@ import (
193 "io"
194 "net/http"
195 "net/http/httptest"
196+ "net/url"
197 "os"
198+ "strings"
199 "testing"
200 "time"
201 )
202@@ -52,7 +54,7 @@ func TestHostingOurselves(t *testing.T) {
203 }
204 replay := runCgiTest(t, h, "GET /test.go?foo=bar&a=b HTTP/1.0\nHost: example.com\n\n", expectedMap)
205
206- if expected, got := "text/html; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
207+ if expected, got := "text/plain; charset=utf-8", replay.Header().Get("Content-Type"); got != expected {
208 t.Errorf("got a Content-Type of %q; expected %q", got, expected)
209 }
210 if expected, got := "X-Test-Value", replay.Header().Get("X-Test-Header"); got != expected {
211@@ -152,6 +154,51 @@ func TestChildOnlyHeaders(t *testing.T) {
212 }
213 }
214
215+func TestChildContentType(t *testing.T) {
216+ testenv.MustHaveExec(t)
217+
218+ h := &Handler{
219+ Path: os.Args[0],
220+ Root: "/test.go",
221+ Args: []string{"-test.run=TestBeChildCGIProcess"},
222+ }
223+ var tests = []struct {
224+ name string
225+ body string
226+ wantCT string
227+ }{
228+ {
229+ name: "no body",
230+ wantCT: "text/plain; charset=utf-8",
231+ },
232+ {
233+ name: "html",
234+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
235+ wantCT: "text/html; charset=utf-8",
236+ },
237+ {
238+ name: "text",
239+ body: strings.Repeat("gopher", 86),
240+ wantCT: "text/plain; charset=utf-8",
241+ },
242+ {
243+ name: "jpg",
244+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
245+ wantCT: "image/jpeg",
246+ },
247+ }
248+ for _, tt := range tests {
249+ t.Run(tt.name, func(t *testing.T) {
250+ expectedMap := map[string]string{"_body": tt.body}
251+ req := fmt.Sprintf("GET /test.go?exact-body=%s HTTP/1.0\nHost: example.com\n\n", url.QueryEscape(tt.body))
252+ replay := runCgiTest(t, h, req, expectedMap)
253+ if got := replay.Header().Get("Content-Type"); got != tt.wantCT {
254+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
255+ }
256+ })
257+ }
258+}
259+
260 // golang.org/issue/7198
261 func Test500WithNoHeaders(t *testing.T) { want500Test(t, "/immediate-disconnect") }
262 func Test500WithNoContentType(t *testing.T) { want500Test(t, "/no-content-type") }
263@@ -203,6 +250,10 @@ func TestBeChildCGIProcess(t *testing.T) {
264 if req.FormValue("no-body") == "1" {
265 return
266 }
267+ if eb, ok := req.Form["exact-body"]; ok {
268+ io.WriteString(rw, eb[0])
269+ return
270+ }
271 if req.FormValue("write-forever") == "1" {
272 io.Copy(rw, neverEnding('a'))
273 for {
274diff --git a/src/net/http/fcgi/child.go b/src/net/http/fcgi/child.go
275index 30a6b2ce2d..a31273b3ec 100644
276--- a/src/net/http/fcgi/child.go
277+++ b/src/net/http/fcgi/child.go
278@@ -74,10 +74,12 @@ func (r *request) parseParams() {
279
280 // response implements http.ResponseWriter.
281 type response struct {
282- req *request
283- header http.Header
284- w *bufWriter
285- wroteHeader bool
286+ req *request
287+ header http.Header
288+ code int
289+ wroteHeader bool
290+ wroteCGIHeader bool
291+ w *bufWriter
292 }
293
294 func newResponse(c *child, req *request) *response {
295@@ -92,11 +94,14 @@ func (r *response) Header() http.Header {
296 return r.header
297 }
298
299-func (r *response) Write(data []byte) (int, error) {
300+func (r *response) Write(p []byte) (n int, err error) {
301 if !r.wroteHeader {
302 r.WriteHeader(http.StatusOK)
303 }
304- return r.w.Write(data)
305+ if !r.wroteCGIHeader {
306+ r.writeCGIHeader(p)
307+ }
308+ return r.w.Write(p)
309 }
310
311 func (r *response) WriteHeader(code int) {
312@@ -104,22 +109,34 @@ func (r *response) WriteHeader(code int) {
313 return
314 }
315 r.wroteHeader = true
316+ r.code = code
317 if code == http.StatusNotModified {
318 // Must not have body.
319 r.header.Del("Content-Type")
320 r.header.Del("Content-Length")
321 r.header.Del("Transfer-Encoding")
322- } else if r.header.Get("Content-Type") == "" {
323- r.header.Set("Content-Type", "text/html; charset=utf-8")
324 }
325-
326 if r.header.Get("Date") == "" {
327 r.header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
328 }
329+}
330
331- fmt.Fprintf(r.w, "Status: %d %s\r\n", code, http.StatusText(code))
332+// writeCGIHeader finalizes the header sent to the client and writes it to the output.
333+// p is not written by writeHeader, but is the first chunk of the body
334+// that will be written. It is sniffed for a Content-Type if none is
335+// set explicitly.
336+func (r *response) writeCGIHeader(p []byte) {
337+ if r.wroteCGIHeader {
338+ return
339+ }
340+ r.wroteCGIHeader = true
341+ fmt.Fprintf(r.w, "Status: %d %s\r\n", r.code, http.StatusText(r.code))
342+ if _, hasType := r.header["Content-Type"]; r.code != http.StatusNotModified && !hasType {
343+ r.header.Set("Content-Type", http.DetectContentType(p))
344+ }
345 r.header.Write(r.w)
346 r.w.WriteString("\r\n")
347+ r.w.Flush()
348 }
349
350 func (r *response) Flush() {
351@@ -290,6 +307,8 @@ func (c *child) serveRequest(req *request, body io.ReadCloser) {
352 httpReq = httpReq.WithContext(envVarCtx)
353 c.handler.ServeHTTP(r, httpReq)
354 }
355+ // Make sure we serve something even if nothing was written to r
356+ r.Write(nil)
357 r.Close()
358 c.mu.Lock()
359 delete(c.requests, req.reqId)
360diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go
361index e9d2b34023..4a27a12c35 100644
362--- a/src/net/http/fcgi/fcgi_test.go
363+++ b/src/net/http/fcgi/fcgi_test.go
364@@ -10,6 +10,7 @@ import (
365 "io"
366 "io/ioutil"
367 "net/http"
368+ "strings"
369 "testing"
370 )
371
372@@ -344,3 +345,54 @@ func TestChildServeReadsEnvVars(t *testing.T) {
373 <-done
374 }
375 }
376+
377+func TestResponseWriterSniffsContentType(t *testing.T) {
378+ var tests = []struct {
379+ name string
380+ body string
381+ wantCT string
382+ }{
383+ {
384+ name: "no body",
385+ wantCT: "text/plain; charset=utf-8",
386+ },
387+ {
388+ name: "html",
389+ body: "<html><head><title>test page</title></head><body>This is a body</body></html>",
390+ wantCT: "text/html; charset=utf-8",
391+ },
392+ {
393+ name: "text",
394+ body: strings.Repeat("gopher", 86),
395+ wantCT: "text/plain; charset=utf-8",
396+ },
397+ {
398+ name: "jpg",
399+ body: "\xFF\xD8\xFF" + strings.Repeat("B", 1024),
400+ wantCT: "image/jpeg",
401+ },
402+ }
403+ for _, tt := range tests {
404+ t.Run(tt.name, func(t *testing.T) {
405+ input := make([]byte, len(streamFullRequestStdin))
406+ copy(input, streamFullRequestStdin)
407+ rc := nopWriteCloser{bytes.NewBuffer(input)}
408+ done := make(chan bool)
409+ var resp *response
410+ c := newChild(rc, http.HandlerFunc(func(
411+ w http.ResponseWriter,
412+ r *http.Request,
413+ ) {
414+ io.WriteString(w, tt.body)
415+ resp = w.(*response)
416+ done <- true
417+ }))
418+ defer c.cleanUp()
419+ go c.serve()
420+ <-done
421+ if got := resp.Header().Get("Content-Type"); got != tt.wantCT {
422+ t.Errorf("got a Content-Type of %q; expected it to start with %q", got, tt.wantCT)
423+ }
424+ })
425+ }
426+}
427--
4282.17.1
429