diff --git a/doc/go1.16.html b/doc/go1.16.html
index bb920a0cb8a..43ffb9dd7c0 100644
--- a/doc/go1.16.html
+++ b/doc/go1.16.html
@@ -286,6 +286,14 @@ Do not send CLs removing the interior tags from such phrases.
has no effect.
+
+ Clients now ensure that the server selects
+
+ an ALPN protocol from
+
+ the list advertised by the client.
+
+
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 5e3711b0c80..2a17ab9caeb 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -1444,7 +1444,6 @@ func (t *tester) testDirTest(dt *distTest, shard, shards int) error {
// cgoPackages is the standard packages that use cgo.
var cgoPackages = []string{
- "crypto/x509",
"net",
"os/user",
}
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 838b00a00d2..a1a357e2acc 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -8,6 +8,7 @@ package work
import (
"bytes"
+ "cmd/go/internal/fsys"
"context"
"encoding/json"
"errors"
@@ -2242,8 +2243,6 @@ func (b *Builder) ccompile(a *Action, p *load.Package, outfile string, flags []s
// when -trimpath is enabled.
if b.gccSupportsFlag(compiler, "-fdebug-prefix-map=a=b") {
if cfg.BuildTrimpath {
- // TODO(#39958): handle overlays
-
// Keep in sync with Action.trimpath.
// The trimmed paths are a little different, but we need to trim in the
// same situations.
@@ -2313,7 +2312,8 @@ func (b *Builder) gccld(a *Action, p *load.Package, objdir, outfile string, flag
cmdargs := []interface{}{cmd, "-o", outfile, objs, flags}
dir := p.Dir
- out, err := b.runOut(a, dir, b.cCompilerEnv(), cmdargs...)
+ out, err := b.runOut(a, base.Cwd, b.cCompilerEnv(), cmdargs...)
+
if len(out) > 0 {
// Filter out useless linker warnings caused by bugs outside Go.
// See also cmd/link/internal/ld's hostlink method.
@@ -2641,7 +2641,8 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cgoLDFLAGS = append([]string{"-fsanitize=memory"}, cgoLDFLAGS...)
}
- // Allows including _cgo_export.h from .[ch] files in the package.
+ // Allows including _cgo_export.h, as well as the user's .h files,
+ // from .[ch] files in the package.
cgoCPPFLAGS = append(cgoCPPFLAGS, "-I", objdir)
// cgo
@@ -2654,6 +2655,8 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cfiles = append(cfiles, f+".cgo2.c")
}
+ hfiles := append([]string{}, p.HFiles...)
+
// TODO: make cgo not depend on $GOARCH?
cgoflags := []string{}
@@ -2698,7 +2701,38 @@ func (b *Builder) cgo(a *Action, cgoExe, objdir string, pcCFLAGS, pcLDFLAGS, cgo
cgoflags = append(cgoflags, "-exportheader="+objdir+"_cgo_install.h")
}
- if err := b.run(a, p.Dir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
+ execdir := p.Dir
+
+ // If any of the Cgo, C, or H files are overlaid, copy them all to
+ // objdir to ensure that they refer to the right header files.
+ // TODO(#39958): Ideally, we'd always do this, but this could
+ // subtly break some cgo files that include .h files across directory
+ // boundaries, even though they shouldn't.
+ hasOverlay := false
+ cgoFileLists := [][]string{cgofiles, gccfiles, gxxfiles, mfiles, ffiles, hfiles}
+OverlayLoop:
+ for _, fs := range cgoFileLists {
+ for _, f := range fs {
+ if _, ok := fsys.OverlayPath(mkAbs(p.Dir, f)); ok {
+ hasOverlay = true
+ break OverlayLoop
+ }
+ }
+ }
+ if hasOverlay {
+ execdir = objdir
+ for _, fs := range cgoFileLists {
+ for i := range fs {
+ opath, _ := fsys.OverlayPath(mkAbs(p.Dir, fs[i]))
+ fs[i] = objdir + filepath.Base(fs[i])
+ if err := b.copyFile(fs[i], opath, 0666, false); err != nil {
+ return nil, nil, err
+ }
+ }
+ }
+ }
+
+ if err := b.run(a, execdir, p.ImportPath, cgoenv, cfg.BuildToolexec, cgoExe, "-objdir", objdir, "-importpath", p.ImportPath, cgoflags, "--", cgoCPPFLAGS, cgoCFLAGS, cgofiles); err != nil {
return nil, nil, err
}
outGo = append(outGo, gofiles...)
@@ -2792,7 +2826,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
return err
}
- linkobj := str.StringList(ofile, outObj, p.SysoFiles)
+ linkobj := str.StringList(ofile, outObj, mkAbsFiles(p.Dir, p.SysoFiles))
dynobj := objdir + "_cgo_.o"
// we need to use -pie for Linux/ARM to get accurate imported sym
@@ -2817,7 +2851,7 @@ func (b *Builder) dynimport(a *Action, p *load.Package, objdir, importGo, cgoExe
if p.Standard && p.ImportPath == "runtime/cgo" {
cgoflags = []string{"-dynlinker"} // record path to dynamic linker
}
- return b.run(a, p.Dir, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
+ return b.run(a, base.Cwd, p.ImportPath, b.cCompilerEnv(), cfg.BuildToolexec, cgoExe, "-dynpackage", p.Name, "-dynimport", dynobj, "-dynout", importGo, cgoflags)
}
// Run SWIG on all SWIG input files.
diff --git a/src/cmd/go/internal/work/gc.go b/src/cmd/go/internal/work/gc.go
index e79173485db..56711b52d87 100644
--- a/src/cmd/go/internal/work/gc.go
+++ b/src/cmd/go/internal/work/gc.go
@@ -262,7 +262,7 @@ func (a *Action) trimpath() string {
if len(objdir) > 1 && objdir[len(objdir)-1] == filepath.Separator {
objdir = objdir[:len(objdir)-1]
}
- rewrite := objdir + "=>"
+ rewrite := ""
rewriteDir := a.Package.Dir
if cfg.BuildTrimpath {
@@ -271,21 +271,54 @@ func (a *Action) trimpath() string {
} else {
rewriteDir = a.Package.ImportPath
}
- rewrite += ";" + a.Package.Dir + "=>" + rewriteDir
+ rewrite += a.Package.Dir + "=>" + rewriteDir + ";"
}
// Add rewrites for overlays. The 'from' and 'to' paths in overlays don't need to have
// same basename, so go from the overlay contents file path (passed to the compiler)
// to the path the disk path would be rewritten to.
+
+ cgoFiles := make(map[string]bool)
+ for _, f := range a.Package.CgoFiles {
+ cgoFiles[f] = true
+ }
+
+ // TODO(matloob): Higher up in the stack, when the logic for deciding when to make copies
+ // of c/c++/m/f/hfiles is consolidated, use the same logic that Build uses to determine
+ // whether to create the copies in objdir to decide whether to rewrite objdir to the
+ // package directory here.
+ var overlayNonGoRewrites string // rewrites for non-go files
+ hasCgoOverlay := false
if fsys.OverlayFile != "" {
for _, filename := range a.Package.AllFiles() {
- overlayPath, ok := fsys.OverlayPath(filepath.Join(a.Package.Dir, filename))
- if !ok {
- continue
+ path := filename
+ if !filepath.IsAbs(path) {
+ path = filepath.Join(a.Package.Dir, path)
+ }
+ base := filepath.Base(path)
+ isGo := strings.HasSuffix(filename, ".go") || strings.HasSuffix(filename, ".s")
+ isCgo := cgoFiles[filename] || !isGo
+ overlayPath, isOverlay := fsys.OverlayPath(path)
+ if isCgo && isOverlay {
+ hasCgoOverlay = true
+ }
+ if !isCgo && isOverlay {
+ rewrite += overlayPath + "=>" + filepath.Join(rewriteDir, base) + ";"
+ } else if isCgo {
+ // Generate rewrites for non-Go files copied to files in objdir.
+ if filepath.Dir(path) == a.Package.Dir {
+ // This is a file copied to objdir.
+ overlayNonGoRewrites += filepath.Join(objdir, base) + "=>" + filepath.Join(rewriteDir, base) + ";"
+ }
+ } else {
+ // Non-overlay Go files are covered by the a.Package.Dir rewrite rule above.
}
- rewrite += ";" + overlayPath + "=>" + filepath.Join(rewriteDir, filename)
}
}
+ if hasCgoOverlay {
+ rewrite += overlayNonGoRewrites
+ }
+ rewrite += objdir + "=>"
return rewrite
}
diff --git a/src/cmd/go/testdata/script/build_overlay.txt b/src/cmd/go/testdata/script/build_overlay.txt
index 0602e706e9e..e18a8f5b288 100644
--- a/src/cmd/go/testdata/script/build_overlay.txt
+++ b/src/cmd/go/testdata/script/build_overlay.txt
@@ -1,9 +1,11 @@
[short] skip
# Test building in overlays.
-# TODO(matloob): add a test case where the destination file in the replace map
+# TODO(#39958): add a test case where the destination file in the replace map
# isn't a go file. Either completely exclude that case in fs.IsDirWithGoFiles
# if the compiler doesn't allow it, or test that it works all the way.
+# TODO(#39958): add a test that both gc and gccgo assembly files can include .h
+# files.
# The main package (m) is contained in an overlay. It imports m/dir2 which has one
# file in an overlay and one file outside the overlay, which in turn imports m/dir,
@@ -29,6 +31,18 @@ exec ./print_trimpath_two_files$GOEXE
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]main.go
stdout $WORK[/\\]gopath[/\\]src[/\\]m[/\\]printpath[/\\]other.go
+go build -overlay overlay.json -o main_cgo_replace$GOEXE ./cgo_hello_replace
+exec ./main_cgo_replace$GOEXE
+stdout '^hello cgo\r?\n'
+
+go build -overlay overlay.json -o main_cgo_quote$GOEXE ./cgo_hello_quote
+exec ./main_cgo_quote$GOEXE
+stdout '^hello cgo\r?\n'
+
+go build -overlay overlay.json -o main_cgo_angle$GOEXE ./cgo_hello_angle
+exec ./main_cgo_angle$GOEXE
+stdout '^hello cgo\r?\n'
+
# Run same tests but with gccgo.
env GO111MODULE=off
[!exec:gccgo] stop
@@ -46,6 +60,19 @@ go build -compiler=gccgo -overlay overlay.json -o print_trimpath_gccgo$GOEXE -tr
exec ./print_trimpath_gccgo$GOEXE
stdout ^\.[/\\]printpath[/\\]main.go
+
+go build -compiler=gccgo -overlay overlay.json -o main_cgo_replace_gccgo$GOEXE ./cgo_hello_replace
+exec ./main_cgo_replace_gccgo$GOEXE
+stdout '^hello cgo\r?\n'
+
+go build -compiler=gccgo -overlay overlay.json -o main_cgo_quote_gccgo$GOEXE ./cgo_hello_quote
+exec ./main_cgo_quote_gccgo$GOEXE
+stdout '^hello cgo\r?\n'
+
+go build -compiler=gccgo -overlay overlay.json -o main_cgo_angle_gccgo$GOEXE ./cgo_hello_angle
+exec ./main_cgo_angle_gccgo$GOEXE
+stdout '^hello cgo\r?\n'
+
-- m/go.mod --
// TODO(matloob): how do overlays work with go.mod (especially if mod=readonly)
module m
@@ -71,9 +98,32 @@ the actual code is in the overlay
"dir/g.go": "overlay/dir_g.go",
"dir2/i.go": "overlay/dir2_i.go",
"printpath/main.go": "overlay/printpath.go",
- "printpath/other.go": "overlay2/printpath2.go"
+ "printpath/other.go": "overlay2/printpath2.go",
+ "cgo_hello_replace/cgo_header.h": "overlay/cgo_head.h",
+ "cgo_hello_quote/cgo_hello.go": "overlay/cgo_hello_quote.go",
+ "cgo_hello_quote/cgo_header.h": "overlay/cgo_head.h",
+ "cgo_hello_angle/cgo_hello.go": "overlay/cgo_hello_angle.go",
+ "cgo_hello_angle/cgo_header.h": "overlay/cgo_head.h"
}
}
+-- m/cgo_hello_replace/cgo_hello_replace.go --
+package main
+
+// #include "cgo_header.h"
+import "C"
+
+func main() {
+ C.say_hello()
+}
+-- m/cgo_hello_replace/cgo_header.h --
+ // Test that this header is replaced with one that has the proper declaration.
+void say_goodbye();
+
+-- m/cgo_hello_replace/goodbye.c --
+#include
+
+void say_hello() { puts("hello cgo\n"); fflush(stdout); }
+
-- m/overlay/f.go --
package main
@@ -128,3 +178,32 @@ import "m/dir"
func printMessage() {
dir.PrintMessage()
}
+-- m/overlay/cgo_hello_quote.go --
+package main
+
+// #include "cgo_header.h"
+import "C"
+
+func main() {
+ C.say_hello()
+}
+-- m/overlay/cgo_hello_angle.go --
+package main
+
+// #include
+import "C"
+
+func main() {
+ C.say_hello()
+}
+-- m/overlay/cgo_head.h --
+void say_hello();
+-- m/cgo_hello_quote/hello.c --
+#include
+
+void say_hello() { puts("hello cgo\n"); fflush(stdout); }
+-- m/cgo_hello_angle/hello.c --
+#include
+
+void say_hello() { puts("hello cgo\n"); fflush(stdout); }
+
diff --git a/src/cmd/go/testdata/script/build_trimpath_cgo.txt b/src/cmd/go/testdata/script/build_trimpath_cgo.txt
index 4608d9ac6bf..3187b4d6439 100644
--- a/src/cmd/go/testdata/script/build_trimpath_cgo.txt
+++ b/src/cmd/go/testdata/script/build_trimpath_cgo.txt
@@ -20,10 +20,38 @@ go build -trimpath -o hello.exe .
go run ./list-dwarf hello.exe
! stdout gopath/src
+
+# Do the above, with the cgo (but not .c) sources in an overlay
+# Check that the source path appears when -trimpath is not used.
+mkdir $WORK/overlay
+cp hello.go $WORK/overlay/hello.go
+mkdir hello_overlay
+cp hello.c hello_overlay/hello.c
+go build -overlay overlay.json -o hello_overlay.exe ./hello_overlay
+grep -q gopath[/\\]src hello_overlay.exe
+! grep -q $WORK[/\\]overlay hello_overlay.exe
+go run ./list-dwarf hello_overlay.exe
+stdout gopath[/\\]src
+! stdout $WORK[/\\]overlay
+
+# Check that the source path does not appear when -trimpath is used.
+go build -overlay overlay.json -trimpath -o hello_overlay.exe ./hello_overlay
+! grep -q gopath[/\\]src hello_overlay.exe
+! grep -q $WORK[/\\]overlay hello_overlay.exe
+go run ./list-dwarf hello_overlay.exe
+! stdout gopath/src
+! stdout $WORK[/\\]overlay
+
-- go.mod --
module m
go 1.14
+-- overlay.json --
+{
+ "Replace": {
+ "hello_overlay/hello.go": "../../overlay/hello.go"
+ }
+}
-- hello.c --
#include
diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go
index 7a009949c88..f8bbc3004d0 100644
--- a/src/crypto/tls/common.go
+++ b/src/crypto/tls/common.go
@@ -229,9 +229,6 @@ type ConnectionState struct {
CipherSuite uint16
// NegotiatedProtocol is the application protocol negotiated with ALPN.
- //
- // Note that on the client side, this is currently not guaranteed to be from
- // Config.NextProtos.
NegotiatedProtocol string
// NegotiatedProtocolIsMutual used to indicate a mutual NPN negotiation.
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index 2788c3c3934..969f357834c 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -88,8 +88,8 @@ type Conn struct {
clientFinished [12]byte
serverFinished [12]byte
- clientProtocol string
- clientProtocolFallback bool
+ // clientProtocol is the negotiated ALPN protocol.
+ clientProtocol string
// input/output
in, out halfConn
@@ -1471,7 +1471,7 @@ func (c *Conn) connectionStateLocked() ConnectionState {
state.Version = c.vers
state.NegotiatedProtocol = c.clientProtocol
state.DidResume = c.didResume
- state.NegotiatedProtocolIsMutual = !c.clientProtocolFallback
+ state.NegotiatedProtocolIsMutual = true
state.ServerName = c.serverName
state.CipherSuite = c.cipherSuite
state.PeerCertificates = c.peerCertificates
diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go
index 6d32a72fd33..ac19961d3c1 100644
--- a/src/crypto/tls/handshake_client.go
+++ b/src/crypto/tls/handshake_client.go
@@ -708,18 +708,18 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) {
}
}
- clientDidALPN := len(hs.hello.alpnProtocols) > 0
- serverHasALPN := len(hs.serverHello.alpnProtocol) > 0
-
- if !clientDidALPN && serverHasALPN {
- c.sendAlert(alertHandshakeFailure)
- return false, errors.New("tls: server advertised unrequested ALPN extension")
- }
-
- if serverHasALPN {
+ if hs.serverHello.alpnProtocol != "" {
+ if len(hs.hello.alpnProtocols) == 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return false, errors.New("tls: server advertised unrequested ALPN extension")
+ }
+ if mutualProtocol([]string{hs.serverHello.alpnProtocol}, hs.hello.alpnProtocols) == "" {
+ c.sendAlert(alertUnsupportedExtension)
+ return false, errors.New("tls: server selected unadvertised ALPN protocol")
+ }
c.clientProtocol = hs.serverHello.alpnProtocol
- c.clientProtocolFallback = false
}
+
c.scts = hs.serverHello.scts
if !hs.serverResumedSession() {
@@ -978,20 +978,17 @@ func clientSessionCacheKey(serverAddr net.Addr, config *Config) string {
return serverAddr.String()
}
-// mutualProtocol finds the mutual Next Protocol Negotiation or ALPN protocol
-// given list of possible protocols and a list of the preference order. The
-// first list must not be empty. It returns the resulting protocol and flag
-// indicating if the fallback case was reached.
-func mutualProtocol(protos, preferenceProtos []string) (string, bool) {
+// mutualProtocol finds the mutual ALPN protocol given list of possible
+// protocols and a list of the preference order.
+func mutualProtocol(protos, preferenceProtos []string) string {
for _, s := range preferenceProtos {
for _, c := range protos {
if s == c {
- return s, false
+ return s
}
}
}
-
- return protos[0], true
+ return ""
}
// hostnameInSNI converts name into an appropriate hostname for SNI.
diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go
index 7a6ef04e789..1405d6811f3 100644
--- a/src/crypto/tls/handshake_client_tls13.go
+++ b/src/crypto/tls/handshake_client_tls13.go
@@ -400,11 +400,17 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error {
}
hs.transcript.Write(encryptedExtensions.marshal())
- if len(encryptedExtensions.alpnProtocol) != 0 && len(hs.hello.alpnProtocols) == 0 {
- c.sendAlert(alertUnsupportedExtension)
- return errors.New("tls: server advertised unrequested ALPN extension")
+ if encryptedExtensions.alpnProtocol != "" {
+ if len(hs.hello.alpnProtocols) == 0 {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server advertised unrequested ALPN extension")
+ }
+ if mutualProtocol([]string{encryptedExtensions.alpnProtocol}, hs.hello.alpnProtocols) == "" {
+ c.sendAlert(alertUnsupportedExtension)
+ return errors.New("tls: server selected unadvertised ALPN protocol")
+ }
+ c.clientProtocol = encryptedExtensions.alpnProtocol
}
- c.clientProtocol = encryptedExtensions.alpnProtocol
return nil
}
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
index 17abe0f4d5c..8095ed17086 100644
--- a/src/crypto/tls/handshake_server.go
+++ b/src/crypto/tls/handshake_server.go
@@ -218,7 +218,7 @@ func (hs *serverHandshakeState) processClientHello() error {
}
if len(hs.clientHello.alpnProtocols) > 0 {
- if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
+ if selectedProto := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); selectedProto != "" {
hs.hello.alpnProtocol = selectedProto
c.clientProtocol = selectedProto
}
diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go
index 222d63418c0..b4ad5e18979 100644
--- a/src/crypto/tls/handshake_server_tls13.go
+++ b/src/crypto/tls/handshake_server_tls13.go
@@ -559,7 +559,7 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
encryptedExtensions := new(encryptedExtensionsMsg)
if len(hs.clientHello.alpnProtocols) > 0 {
- if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
+ if selectedProto := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); selectedProto != "" {
encryptedExtensions.alpnProtocol = selectedProto
c.clientProtocol = selectedProto
}
diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go
index a248ee3292d..9b776d4b857 100644
--- a/src/crypto/x509/internal/macos/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
@@ -16,6 +16,10 @@ import (
"unsafe"
)
+// Core Foundation linker flags for the external linker. See Issue 42459.
+//go:cgo_ldflag "-framework"
+//go:cgo_ldflag "CoreFoundation"
+
// CFRef is an opaque reference to a Core Foundation object. It is a pointer,
// but to memory not owned by Go, so not an unsafe.Pointer.
type CFRef uintptr
diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go
index 59cc19c5878..5e39e936666 100644
--- a/src/crypto/x509/internal/macos/security.go
+++ b/src/crypto/x509/internal/macos/security.go
@@ -12,6 +12,10 @@ import (
"unsafe"
)
+// Security.framework linker flags for the external linker. See Issue 42459.
+//go:cgo_ldflag "-framework"
+//go:cgo_ldflag "Security"
+
// Based on https://opensource.apple.com/source/Security/Security-59306.41.2/base/Security.h
type SecTrustSettingsResult int32
diff --git a/src/crypto/x509/root_cgo_darwin.go b/src/crypto/x509/root_cgo_darwin.go
deleted file mode 100644
index 825e8d4812b..00000000000
--- a/src/crypto/x509/root_cgo_darwin.go
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build !ios
-
-package x509
-
-// This cgo implementation exists only to support side-by-side testing by
-// TestSystemRoots. It can be removed once we are confident in the no-cgo
-// implementation.
-
-/*
-#cgo CFLAGS: -mmacosx-version-min=10.11
-#cgo LDFLAGS: -framework CoreFoundation -framework Security
-
-#include
-#include
-
-#include
-#include
-
-static Boolean isSSLPolicy(SecPolicyRef policyRef) {
- if (!policyRef) {
- return false;
- }
- CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
- if (properties == NULL) {
- return false;
- }
- Boolean isSSL = false;
- CFTypeRef value = NULL;
- if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
- isSSL = CFEqual(value, kSecPolicyAppleSSL);
- }
- CFRelease(properties);
- return isSSL;
-}
-
-// sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
-// for a certificate in the user or admin domain, combining usage constraints
-// for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
-// kSecTrustSettingsAllowedError.
-// https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
-static SInt32 sslTrustSettingsResult(SecCertificateRef cert) {
- CFArrayRef trustSettings = NULL;
- OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);
-
- // According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings",
- // but the rules of the override are unclear. Let's assume admin trust settings are applicable
- // if and only if user trust settings fail to load or are NULL.
- if (err != errSecSuccess || trustSettings == NULL) {
- if (trustSettings != NULL) CFRelease(trustSettings);
- err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings);
- }
-
- // > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
- // (Should this cause a fallback from user to admin domain? It's unclear.)
- if (err != errSecSuccess || trustSettings == NULL) {
- if (trustSettings != NULL) CFRelease(trustSettings);
- return kSecTrustSettingsResultUnspecified;
- }
-
- // > An empty trust settings array means "always trust this certificate” with an
- // > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
- if (CFArrayGetCount(trustSettings) == 0) {
- CFRelease(trustSettings);
- return kSecTrustSettingsResultTrustRoot;
- }
-
- // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
- // but the Go linker's internal linking mode can't handle CFSTR relocations.
- // Create our own dynamic string instead and release it below.
- CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
- NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
- CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
- NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
- CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
- NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);
-
- CFIndex m; SInt32 result = 0;
- for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
- CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);
-
- // First, check if this trust setting is constrained to a non-SSL policy.
- SecPolicyRef policyRef;
- if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
- if (!isSSLPolicy(policyRef)) {
- continue;
- }
- }
-
- if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
- // Restricted to a hostname, not a root.
- continue;
- }
-
- CFNumberRef cfNum;
- if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
- CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
- } else {
- // > If this key is not present, a default value of
- // > kSecTrustSettingsResultTrustRoot is assumed.
- result = kSecTrustSettingsResultTrustRoot;
- }
-
- // If multiple dictionaries match, we are supposed to "OR" them,
- // the semantics of which are not clear. Since TrustRoot and TrustAsRoot
- // are mutually exclusive, Deny should probably override, and Invalid and
- // Unspecified be overridden, approximate this by stopping at the first
- // TrustRoot, TrustAsRoot or Deny.
- if (result == kSecTrustSettingsResultTrustRoot) {
- break;
- } else if (result == kSecTrustSettingsResultTrustAsRoot) {
- break;
- } else if (result == kSecTrustSettingsResultDeny) {
- break;
- }
- }
-
- // If trust settings are present, but none of them match the policy...
- // the docs don't tell us what to do.
- //
- // "Trust settings for a given use apply if any of the dictionaries in the
- // certificate’s trust settings array satisfies the specified use." suggests
- // that it's as if there were no trust settings at all, so we should probably
- // fallback to the admin trust settings. TODO.
- if (result == 0) {
- result = kSecTrustSettingsResultUnspecified;
- }
-
- CFRelease(_kSecTrustSettingsPolicy);
- CFRelease(_kSecTrustSettingsPolicyString);
- CFRelease(_kSecTrustSettingsResult);
- CFRelease(trustSettings);
-
- return result;
-}
-
-// isRootCertificate reports whether Subject and Issuer match.
-static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
- CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
- if (*errRef != NULL) {
- return false;
- }
- CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
- if (*errRef != NULL) {
- CFRelease(subjectName);
- return false;
- }
- Boolean equal = CFEqual(subjectName, issuerName);
- CFRelease(subjectName);
- CFRelease(issuerName);
- return equal;
-}
-
-// CopyPEMRoots fetches the system's list of trusted X.509 root certificates
-// for the kSecTrustSettingsPolicy SSL.
-//
-// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
-// certificates of the system. On failure, the function returns -1.
-// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
-//
-// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
-// be released (using CFRelease) after we've consumed its content.
-static int CopyPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
- int i;
-
- if (debugDarwinRoots) {
- fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
- fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
- fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
- fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
- fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
- }
-
- // Get certificates from all domains, not just System, this lets
- // the user add CAs to their "login" keychain, and Admins to add
- // to the "System" keychain
- SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
- kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };
-
- int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
- if (pemRoots == NULL || untrustedPemRoots == NULL) {
- return -1;
- }
-
- CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
- CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
- for (i = 0; i < numDomains; i++) {
- int j;
- CFArrayRef certs = NULL;
- OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
- if (err != noErr) {
- continue;
- }
-
- CFIndex numCerts = CFArrayGetCount(certs);
- for (j = 0; j < numCerts; j++) {
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
- if (cert == NULL) {
- continue;
- }
-
- SInt32 result;
- if (domains[i] == kSecTrustSettingsDomainSystem) {
- // Certs found in the system domain are always trusted. If the user
- // configures "Never Trust" on such a cert, it will also be found in the
- // admin or user domain, causing it to be added to untrustedPemRoots. The
- // Go code will then clean this up.
- result = kSecTrustSettingsResultTrustRoot;
- } else {
- result = sslTrustSettingsResult(cert);
- if (debugDarwinRoots) {
- CFErrorRef errRef = NULL;
- CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
- if (errRef != NULL) {
- fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n");
- CFRelease(errRef);
- continue;
- }
-
- CFIndex length = CFStringGetLength(summary);
- CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
- char *buffer = malloc(maxSize);
- if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
- fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result);
- }
- free(buffer);
- CFRelease(summary);
- }
- }
-
- CFMutableDataRef appendTo;
- // > Note the distinction between the results kSecTrustSettingsResultTrustRoot
- // > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
- // > root (self-signed) certificates; the latter can only be applied to
- // > non-root certificates.
- if (result == kSecTrustSettingsResultTrustRoot) {
- CFErrorRef errRef = NULL;
- if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
- if (errRef != NULL) CFRelease(errRef);
- continue;
- }
-
- appendTo = combinedData;
- } else if (result == kSecTrustSettingsResultTrustAsRoot) {
- CFErrorRef errRef = NULL;
- if (isRootCertificate(cert, &errRef) || errRef != NULL) {
- if (errRef != NULL) CFRelease(errRef);
- continue;
- }
-
- appendTo = combinedData;
- } else if (result == kSecTrustSettingsResultDeny) {
- appendTo = combinedUntrustedData;
- } else if (result == kSecTrustSettingsResultUnspecified) {
- // Certificates with unspecified trust should probably be added to a pool of
- // intermediates for chain building, or checked for transitive trust and
- // added to the root pool (which is an imprecise approximation because it
- // cuts chains short) but we don't support either at the moment. TODO.
- continue;
- } else {
- continue;
- }
-
- CFDataRef data = NULL;
- err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
- if (err != noErr) {
- continue;
- }
- if (data != NULL) {
- CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
- CFRelease(data);
- }
- }
- CFRelease(certs);
- }
- *pemRoots = combinedData;
- *untrustedPemRoots = combinedUntrustedData;
- return 0;
-}
-*/
-import "C"
-import (
- "errors"
- "unsafe"
-)
-
-func init() {
- loadSystemRootsWithCgo = _loadSystemRootsWithCgo
-}
-
-func _loadSystemRootsWithCgo() (*CertPool, error) {
- var data, untrustedData C.CFDataRef
- err := C.CopyPEMRoots(&data, &untrustedData, C.bool(debugDarwinRoots))
- if err == -1 {
- return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
- }
- defer C.CFRelease(C.CFTypeRef(data))
- defer C.CFRelease(C.CFTypeRef(untrustedData))
-
- buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
- roots := NewCertPool()
- roots.AppendCertsFromPEM(buf)
-
- if C.CFDataGetLength(untrustedData) == 0 {
- return roots, nil
- }
-
- buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
- untrustedRoots := NewCertPool()
- untrustedRoots.AppendCertsFromPEM(buf)
-
- trustedRoots := NewCertPool()
- for _, lc := range roots.lazyCerts {
- c, err := lc.getCert()
- if err != nil {
- return nil, err
- }
- if !untrustedRoots.contains(c) {
- trustedRoots.AddCert(c)
- }
- }
- return trustedRoots, nil
-}
diff --git a/src/crypto/x509/root_darwin.go b/src/crypto/x509/root_darwin.go
index ce88de025ec..c9ea7e80f37 100644
--- a/src/crypto/x509/root_darwin.go
+++ b/src/crypto/x509/root_darwin.go
@@ -20,10 +20,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return nil, nil
}
-// loadSystemRootsWithCgo is set in root_cgo_darwin_amd64.go when cgo is
-// available, and is only used for testing.
-var loadSystemRootsWithCgo func() (*CertPool, error)
-
func loadSystemRoots() (*CertPool, error) {
var trustedRoots []*Certificate
untrustedRoots := make(map[string]bool)
diff --git a/src/crypto/x509/root_darwin_test.go b/src/crypto/x509/root_darwin_test.go
index 69f181c2d48..ae2bd02bf85 100644
--- a/src/crypto/x509/root_darwin_test.go
+++ b/src/crypto/x509/root_darwin_test.go
@@ -28,39 +28,6 @@ func TestSystemRoots(t *testing.T) {
t.Errorf("want at least %d system roots, have %d", want, have)
}
- if loadSystemRootsWithCgo == nil {
- t.Skip("cgo not available, can't compare pool")
- }
-
- t1 := time.Now()
- cgoRoots, err := loadSystemRootsWithCgo() // cgo roots
- cgoSysRootsDuration := time.Since(t1)
-
- if err != nil {
- t.Fatalf("failed to read cgo roots: %v", err)
- }
-
- t.Logf("loadSystemRootsWithCgo: %v", cgoSysRootsDuration)
-
- // Check that the two cert pools are the same.
- sysPool := make(map[string]*Certificate, sysRoots.len())
- for i := 0; i < sysRoots.len(); i++ {
- c := sysRoots.mustCert(t, i)
- sysPool[string(c.Raw)] = c
- }
- for i := 0; i < cgoRoots.len(); i++ {
- c := cgoRoots.mustCert(t, i)
-
- if _, ok := sysPool[string(c.Raw)]; ok {
- delete(sysPool, string(c.Raw))
- } else {
- t.Errorf("certificate only present in cgo pool: %v", c.Subject)
- }
- }
- for _, c := range sysPool {
- t.Errorf("certificate only present in real pool: %v", c.Subject)
- }
-
if t.Failed() {
cmd := exec.Command("security", "dump-trust-settings")
cmd.Stdout, cmd.Stderr = os.Stderr, os.Stderr
diff --git a/src/crypto/x509/root_ios.go b/src/crypto/x509/root_ios.go
index bb4a5f75ba8..cb3529d6d53 100644
--- a/src/crypto/x509/root_ios.go
+++ b/src/crypto/x509/root_ios.go
@@ -10,9 +10,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return nil, nil
}
-// loadSystemRootsWithCgo is not available on iOS.
-var loadSystemRootsWithCgo func() (*CertPool, error)
-
func loadSystemRoots() (*CertPool, error) {
p := NewCertPool()
p.AppendCertsFromPEM([]byte(systemRootsPEM))
diff --git a/src/crypto/x509/root_ios_gen.go b/src/crypto/x509/root_ios_gen.go
index f7eecb576d6..2bcdab1a779 100644
--- a/src/crypto/x509/root_ios_gen.go
+++ b/src/crypto/x509/root_ios_gen.go
@@ -172,9 +172,6 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return nil, nil
}
-// loadSystemRootsWithCgo is not available on iOS.
-var loadSystemRootsWithCgo func() (*CertPool, error)
-
func loadSystemRoots() (*CertPool, error) {
p := NewCertPool()
p.AppendCertsFromPEM([]byte(systemRootsPEM))
diff --git a/src/crypto/x509/root_omit.go b/src/crypto/x509/root_omit.go
index 175d71643b2..0055b3b8627 100644
--- a/src/crypto/x509/root_omit.go
+++ b/src/crypto/x509/root_omit.go
@@ -24,6 +24,3 @@ func loadSystemRoots() (*CertPool, error) {
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
-
-// loadSystemRootsWithCgo is not available on iOS.
-var loadSystemRootsWithCgo func() (*CertPool, error)
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index 3a0726a293e..89bae9fd511 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -393,7 +393,7 @@ var depsRules = `
net !< CRYPTO-BORING;
# TLS, Prince of Dependencies.
- CGO, CRYPTO-BORING, NET, container/list, encoding/hex, encoding/pem
+ CRYPTO-BORING, NET, container/list, encoding/hex, encoding/pem
< golang.org/x/crypto/internal/subtle
< golang.org/x/crypto/chacha20
< golang.org/x/crypto/poly1305