mirror of
https://github.com/golang/go
synced 2024-11-26 03:07:57 -07:00
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I31c69e54c904c66c10920e4c4caacfef08bb834f
This commit is contained in:
commit
dea96ada17
@ -286,6 +286,14 @@ Do not send CLs removing the interior tags from such phrases.
|
||||
has no effect.
|
||||
</p>
|
||||
|
||||
<p><!-- CL 239748 -->
|
||||
Clients now ensure that the server selects
|
||||
<a href="/pkg/crypto/tls/#ConnectionState.NegotiatedProtocol">
|
||||
an ALPN protocol</a> from
|
||||
<a href="/pkg/crypto/tls/#Config.NextProtos">
|
||||
the list advertised by the client</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="crypto/x509"><a href="/pkg/crypto/x509">crypto/x509</a></h3>
|
||||
|
||||
<p><!-- CL 235078 -->
|
||||
|
1
src/cmd/dist/test.go
vendored
1
src/cmd/dist/test.go
vendored
@ -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",
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
83
src/cmd/go/testdata/script/build_overlay.txt
vendored
83
src/cmd/go/testdata/script/build_overlay.txt
vendored
@ -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 <stdio.h>
|
||||
|
||||
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 <cgo_header.h>
|
||||
import "C"
|
||||
|
||||
func main() {
|
||||
C.say_hello()
|
||||
}
|
||||
-- m/overlay/cgo_head.h --
|
||||
void say_hello();
|
||||
-- m/cgo_hello_quote/hello.c --
|
||||
#include <stdio.h>
|
||||
|
||||
void say_hello() { puts("hello cgo\n"); fflush(stdout); }
|
||||
-- m/cgo_hello_angle/hello.c --
|
||||
#include <stdio.h>
|
||||
|
||||
void say_hello() { puts("hello cgo\n"); fflush(stdout); }
|
||||
|
||||
|
@ -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 <stdio.h>
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 <errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
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
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user