Skip to content

Commit

Permalink
crypto/x509: use platform verifier on darwin
Browse files Browse the repository at this point in the history
When VerifyOptions.Roots is nil, default to using the platform X.509
certificate verification APIs on darwin, rather than using the Go
verifier. Since our oldest supported version of macOS is 10.12, we are
able to use the modern verification APIs, and don't need to resort to
the complex chain building trickery employed by chromium et al.

Unfortunately there is not a clean way to programmatically add test
roots to the system trust store that the builders would tolerate. The
most obvious solution, using 'security add-trusted-cert' requires human
interaction for authorization. We could also manually add anchors to
the constructed SecTrustRef, but that would require adding a whole
bunch of plumbing for test functionality, and would mean we weren't
really testing the actual non-test path. The path I've chosen here is
to just utilize existing valid, and purposefully invalid, trusted
chains, from google.com and the badssl.com test suite. This requires
external network access, but most accurately reflects real world
contexts.

This change removes the x509.SystemCertPool() functionality, which will
be ammended in a follow-up change which supports the suggested hybrid
pool approach described in #46287.

Updates #46287
Fixes #42414
Fixes #38888
Fixes #35631
Fixes #19561

Change-Id: I17f0d6c5cb3ef8a1f2731ce3296478b28d30df46
Reviewed-on: https://go-review.googlesource.com/c/go/+/353132
Trust: Roland Shoemaker <roland@golang.org>
Run-TryBot: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
  • Loading branch information
rolandshoemaker committed Nov 5, 2021
1 parent 8f923a4 commit feb024f
Show file tree
Hide file tree
Showing 10 changed files with 409 additions and 225 deletions.
2 changes: 2 additions & 0 deletions src/crypto/x509/cert_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ func SystemCertPool() (*CertPool, error) {
if runtime.GOOS == "windows" {
// Issue 16736, 18609:
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
} else if runtime.GOOS == "darwin" {
return nil, errors.New("crypto/x509: system root pool is not available on macOS")
}

if sysRoots := systemRootsPool(); sysRoots != nil {
Expand Down
77 changes: 77 additions & 0 deletions src/crypto/x509/internal/macos/corefoundation.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"internal/abi"
"reflect"
"runtime"
"time"
"unsafe"
)

Expand All @@ -35,11 +36,37 @@ func CFDataToSlice(data CFRef) []byte {
return out
}

// CFStringToString returns a Go string representation of the passed
// in CFString.
func CFStringToString(ref CFRef) string {
data := CFStringCreateExternalRepresentation(ref)
b := CFDataToSlice(data)
CFRelease(data)
return string(b)
}

// TimeToCFDateRef converts a time.Time into an apple CFDateRef
func TimeToCFDateRef(t time.Time) CFRef {
secs := t.Sub(time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)).Seconds()
ref := CFDateCreate(int(secs))
return ref
}

type CFString CFRef

const kCFAllocatorDefault = 0
const kCFStringEncodingUTF8 = 0x08000100

//go:cgo_import_dynamic x509_CFDataCreate CFDataCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func BytesToCFData(b []byte) CFRef {
p := unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&b)).Data)
ret := syscall(abi.FuncPCABI0(x509_CFDataCreate_trampoline), kCFAllocatorDefault, uintptr(p), uintptr(len(b)), 0, 0, 0)
runtime.KeepAlive(p)
return CFRef(ret)
}
func x509_CFDataCreate_trampoline()

//go:cgo_import_dynamic x509_CFStringCreateWithBytes CFStringCreateWithBytes "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

// StringToCFString returns a copy of the UTF-8 contents of s as a new CFString.
Expand Down Expand Up @@ -126,5 +153,55 @@ func CFRelease(ref CFRef) {
}
func x509_CFRelease_trampoline()

//go:cgo_import_dynamic x509_CFArrayCreateMutable CFArrayCreateMutable "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func CFArrayCreateMutable() CFRef {
ret := syscall(abi.FuncPCABI0(x509_CFArrayCreateMutable_trampoline), kCFAllocatorDefault, 0, 0 /* kCFTypeArrayCallBacks */, 0, 0, 0)
return CFRef(ret)
}
func x509_CFArrayCreateMutable_trampoline()

//go:cgo_import_dynamic x509_CFArrayAppendValue CFArrayAppendValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func CFArrayAppendValue(array CFRef, val CFRef) {
syscall(abi.FuncPCABI0(x509_CFArrayAppendValue_trampoline), uintptr(array), uintptr(val), 0, 0, 0, 0)
}
func x509_CFArrayAppendValue_trampoline()

//go:cgo_import_dynamic x509_CFDateCreate CFDateCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func CFDateCreate(seconds int) CFRef {
ret := syscall(abi.FuncPCABI0(x509_CFDateCreate_trampoline), kCFAllocatorDefault, uintptr(seconds), 0, 0, 0, 0)
return CFRef(ret)
}
func x509_CFDateCreate_trampoline()

//go:cgo_import_dynamic x509_CFErrorCopyDescription CFErrorCopyDescription "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func CFErrorCopyDescription(errRef CFRef) CFRef {
ret := syscall(abi.FuncPCABI0(x509_CFErrorCopyDescription_trampoline), uintptr(errRef), 0, 0, 0, 0, 0)
return CFRef(ret)
}
func x509_CFErrorCopyDescription_trampoline()

//go:cgo_import_dynamic x509_CFStringCreateExternalRepresentation CFStringCreateExternalRepresentation "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"

func CFStringCreateExternalRepresentation(strRef CFRef) CFRef {
ret := syscall(abi.FuncPCABI0(x509_CFStringCreateExternalRepresentation_trampoline), kCFAllocatorDefault, uintptr(strRef), kCFStringEncodingUTF8, 0, 0, 0)
return CFRef(ret)
}
func x509_CFStringCreateExternalRepresentation_trampoline()

// syscall is implemented in the runtime package (runtime/sys_darwin.go)
func syscall(fn, a1, a2, a3, a4, a5, a6 uintptr) uintptr

// ReleaseCFArray iterates through an array, releasing its contents, and then
// releases the array itself. This is necessary because we cannot, easily, set the
// CFArrayCallBacks argument when creating CFArrays.
func ReleaseCFArray(array CFRef) {
for i := 0; i < CFArrayGetCount(array); i++ {
ref := CFArrayGetValueAtIndex(array, i)
CFRelease(ref)
}
CFRelease(array)
}
12 changes: 12 additions & 0 deletions src/crypto/x509/internal/macos/corefoundation.s
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,15 @@ TEXT ·x509_CFNumberGetValue_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFNumberGetValue(SB)
TEXT ·x509_CFEqual_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFEqual(SB)
TEXT ·x509_CFArrayCreateMutable_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFArrayCreateMutable(SB)
TEXT ·x509_CFArrayAppendValue_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFArrayAppendValue(SB)
TEXT ·x509_CFDateCreate_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDateCreate(SB)
TEXT ·x509_CFDataCreate_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFDataCreate(SB)
TEXT ·x509_CFErrorCopyDescription_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFErrorCopyDescription(SB)
TEXT ·x509_CFStringCreateExternalRepresentation_trampoline(SB),NOSPLIT,$0-0
JMP x509_CFStringCreateExternalRepresentation(SB)
118 changes: 118 additions & 0 deletions src/crypto/x509/internal/macos/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package macOS

import (
"errors"
"fmt"
"internal/abi"
"strconv"
"unsafe"
Expand All @@ -29,6 +30,19 @@ const (
SecTrustSettingsResultUnspecified
)

type SecTrustResultType int32

const (
SecTrustResultInvalid SecTrustResultType = iota
SecTrustResultProceed
SecTrustResultConfirm // deprecated
SecTrustResultDeny
SecTrustResultUnspecified
SecTrustResultRecoverableTrustFailure
SecTrustResultFatalTrustFailure
SecTrustResultOtherError
)

type SecTrustSettingsDomain int32

const (
Expand Down Expand Up @@ -115,3 +129,107 @@ func SecPolicyCopyProperties(policy CFRef) CFRef {
return CFRef(ret)
}
func x509_SecPolicyCopyProperties_trampoline()

//go:cgo_import_dynamic x509_SecTrustCreateWithCertificates SecTrustCreateWithCertificates "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustCreateWithCertificates(certs CFRef, policies CFRef) (CFRef, error) {
var trustObj CFRef
ret := syscall(abi.FuncPCABI0(x509_SecTrustCreateWithCertificates_trampoline), uintptr(certs), uintptr(policies),
uintptr(unsafe.Pointer(&trustObj)), 0, 0, 0)
if int32(ret) != 0 {
return 0, OSStatus{"SecTrustCreateWithCertificates", int32(ret)}
}
return trustObj, nil
}
func x509_SecTrustCreateWithCertificates_trampoline()

//go:cgo_import_dynamic x509_SecCertificateCreateWithData SecCertificateCreateWithData "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecCertificateCreateWithData(b []byte) CFRef {
data := BytesToCFData(b)
ret := syscall(abi.FuncPCABI0(x509_SecCertificateCreateWithData_trampoline), kCFAllocatorDefault, uintptr(data), 0, 0, 0, 0)
CFRelease(data)
return CFRef(ret)
}
func x509_SecCertificateCreateWithData_trampoline()

//go:cgo_import_dynamic x509_SecPolicyCreateSSL SecPolicyCreateSSL "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecPolicyCreateSSL(name string) CFRef {
var hostname CFString
if name != "" {
hostname = StringToCFString(name)
defer CFRelease(CFRef(hostname))
}
ret := syscall(abi.FuncPCABI0(x509_SecPolicyCreateSSL_trampoline), 1 /* true */, uintptr(hostname), 0, 0, 0, 0)
return CFRef(ret)
}
func x509_SecPolicyCreateSSL_trampoline()

//go:cgo_import_dynamic x509_SecTrustSetVerifyDate SecTrustSetVerifyDate "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustSetVerifyDate(trustObj CFRef, dateRef CFRef) error {
ret := syscall(abi.FuncPCABI0(x509_SecTrustSetVerifyDate_trampoline), uintptr(trustObj), uintptr(dateRef), 0, 0, 0, 0)
if int32(ret) != 0 {
return OSStatus{"SecTrustSetVerifyDate", int32(ret)}
}
return nil
}
func x509_SecTrustSetVerifyDate_trampoline()

//go:cgo_import_dynamic x509_SecTrustEvaluate SecTrustEvaluate "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustEvaluate(trustObj CFRef) (CFRef, error) {
var result CFRef
ret := syscall(abi.FuncPCABI0(x509_SecTrustEvaluate_trampoline), uintptr(trustObj), uintptr(unsafe.Pointer(&result)), 0, 0, 0, 0)
if int32(ret) != 0 {
return 0, OSStatus{"SecTrustEvaluate", int32(ret)}
}
return CFRef(result), nil
}
func x509_SecTrustEvaluate_trampoline()

//go:cgo_import_dynamic x509_SecTrustGetResult SecTrustGetResult "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustGetResult(trustObj CFRef, result CFRef) (CFRef, CFRef, error) {
var chain, info CFRef
ret := syscall(abi.FuncPCABI0(x509_SecTrustGetResult_trampoline), uintptr(trustObj), uintptr(unsafe.Pointer(&result)),
uintptr(unsafe.Pointer(&chain)), uintptr(unsafe.Pointer(&info)), 0, 0)
if int32(ret) != 0 {
return 0, 0, OSStatus{"SecTrustGetResult", int32(ret)}
}
return chain, info, nil
}
func x509_SecTrustGetResult_trampoline()

//go:cgo_import_dynamic x509_SecTrustEvaluateWithError SecTrustEvaluateWithError "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustEvaluateWithError(trustObj CFRef) error {
var errRef CFRef
ret := syscall(abi.FuncPCABI0(x509_SecTrustEvaluateWithError_trampoline), uintptr(trustObj), uintptr(unsafe.Pointer(&errRef)), 0, 0, 0, 0)
if int32(ret) != 1 {
errStr := CFErrorCopyDescription(errRef)
err := fmt.Errorf("x509: %s", CFStringToString(errStr))
CFRelease(errRef)
CFRelease(errStr)
return err
}
return nil
}
func x509_SecTrustEvaluateWithError_trampoline()

//go:cgo_import_dynamic x509_SecTrustGetCertificateCount SecTrustGetCertificateCount "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustGetCertificateCount(trustObj CFRef) int {
ret := syscall(abi.FuncPCABI0(x509_SecTrustGetCertificateCount_trampoline), uintptr(trustObj), 0, 0, 0, 0, 0)
return int(ret)
}
func x509_SecTrustGetCertificateCount_trampoline()

//go:cgo_import_dynamic x509_SecTrustGetCertificateAtIndex SecTrustGetCertificateAtIndex "/System/Library/Frameworks/Security.framework/Versions/A/Security"

func SecTrustGetCertificateAtIndex(trustObj CFRef, i int) CFRef {
ret := syscall(abi.FuncPCABI0(x509_SecTrustGetCertificateAtIndex_trampoline), uintptr(trustObj), uintptr(i), 0, 0, 0, 0)
return CFRef(ret)
}
func x509_SecTrustGetCertificateAtIndex_trampoline()
18 changes: 18 additions & 0 deletions src/crypto/x509/internal/macos/security.s
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,21 @@ TEXT ·x509_SecTrustSettingsCopyTrustSettings_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustSettingsCopyTrustSettings(SB)
TEXT ·x509_SecPolicyCopyProperties_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecPolicyCopyProperties(SB)
TEXT ·x509_SecTrustCreateWithCertificates_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustCreateWithCertificates(SB)
TEXT ·x509_SecCertificateCreateWithData_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecCertificateCreateWithData(SB)
TEXT ·x509_SecPolicyCreateSSL_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecPolicyCreateSSL(SB)
TEXT ·x509_SecTrustSetVerifyDate_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustSetVerifyDate(SB)
TEXT ·x509_SecTrustEvaluate_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustEvaluate(SB)
TEXT ·x509_SecTrustGetResult_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustGetResult(SB)
TEXT ·x509_SecTrustEvaluateWithError_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustEvaluateWithError(SB)
TEXT ·x509_SecTrustGetCertificateCount_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustGetCertificateCount(SB)
TEXT ·x509_SecTrustGetCertificateAtIndex_trampoline(SB),NOSPLIT,$0-0
JMP x509_SecTrustGetCertificateAtIndex(SB)
Loading

0 comments on commit feb024f

Please sign in to comment.