mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/go: restore netrc preferences for GOAUTH and fix domain lookup
Store netrc lines into the credential map backward so that earlier lines take priority over later lines. This matches Go 1.23 netrc lookup which stopped at the first match it found. Additionally, this fixes a security issue related to domain parsing which could have allowed servers to read credentials belonging to other servers. The fix was to switch from using path.Dir(currentPrefix) to strings.Cut(currentPrefix, "/") Thanks to Juho Forsén of Mattermost for reporting this issue. Fixes #71249 Fixes CVE-2024-45340 Change-Id: I175a00d6d7f4d31c9e4d79b7cf1c2a0ad35b2781 Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/1781 Reviewed-by: Tatiana Bradley <tatianabradley@google.com> Commit-Queue: Roland Shoemaker <bracewell@google.com> Reviewed-by: Roland Shoemaker <bracewell@google.com> Reviewed-by: Damien Neil <dneil@google.com> Reviewed-on: https://go-review.googlesource.com/c/go/+/643097 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Michael Knyszek <mknyszek@google.com>
This commit is contained in:
parent
2b2314e9f6
commit
139d6eedae
3 changed files with 38 additions and 10 deletions
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -73,7 +72,12 @@ func runGoAuth(client *http.Client, res *http.Response, url string) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
base.Fatalf("go: could not parse netrc (GOAUTH=%s): %v", cfg.GOAUTH, err)
|
base.Fatalf("go: could not parse netrc (GOAUTH=%s): %v", cfg.GOAUTH, err)
|
||||||
}
|
}
|
||||||
for _, l := range lines {
|
// Process lines in reverse so that if the same machine is listed
|
||||||
|
// multiple times, we end up saving the earlier one
|
||||||
|
// (overwriting later ones). This matches the way the go command
|
||||||
|
// worked before GOAUTH.
|
||||||
|
for i := len(lines) - 1; i >= 0; i-- {
|
||||||
|
l := lines[i]
|
||||||
r := http.Request{Header: make(http.Header)}
|
r := http.Request{Header: make(http.Header)}
|
||||||
r.SetBasicAuth(l.login, l.password)
|
r.SetBasicAuth(l.login, l.password)
|
||||||
storeCredential(l.machine, r.Header)
|
storeCredential(l.machine, r.Header)
|
||||||
|
|
@ -137,11 +141,13 @@ func runGoAuth(client *http.Client, res *http.Response, url string) {
|
||||||
func loadCredential(req *http.Request, url string) bool {
|
func loadCredential(req *http.Request, url string) bool {
|
||||||
currentPrefix := strings.TrimPrefix(url, "https://")
|
currentPrefix := strings.TrimPrefix(url, "https://")
|
||||||
// Iteratively try prefixes, moving up the path hierarchy.
|
// Iteratively try prefixes, moving up the path hierarchy.
|
||||||
for currentPrefix != "/" && currentPrefix != "." && currentPrefix != "" {
|
for {
|
||||||
headers, ok := credentialCache.Load(currentPrefix)
|
headers, ok := credentialCache.Load(currentPrefix)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Move to the parent directory.
|
currentPrefix, _, ok = strings.Cut(currentPrefix, "/")
|
||||||
currentPrefix = path.Dir(currentPrefix)
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for key, values := range headers.(http.Header) {
|
for key, values := range headers.(http.Header) {
|
||||||
|
|
@ -151,7 +157,6 @@ func loadCredential(req *http.Request, url string) bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// storeCredential caches or removes credentials (represented by HTTP headers)
|
// storeCredential caches or removes credentials (represented by HTTP headers)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,29 @@ func TestCredentialCache(t *testing.T) {
|
||||||
got := &http.Request{Header: make(http.Header)}
|
got := &http.Request{Header: make(http.Header)}
|
||||||
ok := loadCredential(got, tc.machine)
|
ok := loadCredential(got, tc.machine)
|
||||||
if !ok || !reflect.DeepEqual(got.Header, want.Header) {
|
if !ok || !reflect.DeepEqual(got.Header, want.Header) {
|
||||||
t.Errorf("loadCredential:\nhave %q\nwant %q", got.Header, want.Header)
|
t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Having stored those credentials, we should be able to look up longer URLs too.
|
||||||
|
extraCases := []netrcLine{
|
||||||
|
{"https://api.github.com/foo", "user", "pwd"},
|
||||||
|
{"https://api.github.com/foo/bar/baz", "user", "pwd"},
|
||||||
|
{"https://example.com/abc", "", ""},
|
||||||
|
{"https://example.com/?/../api.github.com/", "", ""},
|
||||||
|
{"https://example.com/?/../api.github.com", "", ""},
|
||||||
|
{"https://example.com/../api.github.com/", "", ""},
|
||||||
|
{"https://example.com/../api.github.com", "", ""},
|
||||||
|
}
|
||||||
|
for _, tc := range extraCases {
|
||||||
|
want := http.Request{Header: make(http.Header)}
|
||||||
|
if tc.login != "" {
|
||||||
|
want.SetBasicAuth(tc.login, tc.password)
|
||||||
|
}
|
||||||
|
got := &http.Request{Header: make(http.Header)}
|
||||||
|
loadCredential(got, tc.machine)
|
||||||
|
if !reflect.DeepEqual(got.Header, want.Header) {
|
||||||
|
t.Errorf("loadCredential(%q):\nhave %q\nwant %q", tc.machine, got.Header, want.Header)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
src/cmd/go/testdata/script/goauth_netrc.txt
vendored
7
src/cmd/go/testdata/script/goauth_netrc.txt
vendored
|
|
@ -2,8 +2,6 @@
|
||||||
# credentials passed in HTTPS requests to VCS servers.
|
# credentials passed in HTTPS requests to VCS servers.
|
||||||
# See golang.org/issue/26232
|
# See golang.org/issue/26232
|
||||||
|
|
||||||
[short] skip
|
|
||||||
|
|
||||||
env GOPROXY=direct
|
env GOPROXY=direct
|
||||||
env GOSUMDB=off
|
env GOSUMDB=off
|
||||||
|
|
||||||
|
|
@ -55,7 +53,6 @@ go get vcs-test.golang.org/auth/or401
|
||||||
env NETRC=$WORK/missing
|
env NETRC=$WORK/missing
|
||||||
! go get vcs-test.golang.org/auth/or401
|
! go get vcs-test.golang.org/auth/or401
|
||||||
stderr '^\tserver response: ACCESS DENIED, buddy$'
|
stderr '^\tserver response: ACCESS DENIED, buddy$'
|
||||||
|
|
||||||
-- go.mod --
|
-- go.mod --
|
||||||
module private.example.com
|
module private.example.com
|
||||||
-- $WORK/empty --
|
-- $WORK/empty --
|
||||||
|
|
@ -63,3 +60,7 @@ module private.example.com
|
||||||
machine vcs-test.golang.org
|
machine vcs-test.golang.org
|
||||||
login aladdin
|
login aladdin
|
||||||
password opensesame
|
password opensesame
|
||||||
|
# first one should override this one
|
||||||
|
machine vcs-test.golang.org
|
||||||
|
login aladdin
|
||||||
|
password ignored
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue