1
0
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:
Dmitri Shuralyov 2020-12-01 17:16:25 -05:00
commit dea96ada17
21 changed files with 236 additions and 419 deletions

View File

@ -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 -->

View File

@ -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",
}

View File

@ -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.

View File

@ -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
}

View File

@ -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); }

View File

@ -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>

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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
// certificates 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
}

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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))

View File

@ -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)

View File

@ -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