Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crypto/x509: add non-cgo darwin system anchor certs
The set of certs fetched via exec'ing `security` is not quite identical to the certs fetched via the cgo call. The cgo fetch includes any trusted root certs that the user may have added; exec does not. The exec fetch includes an Apple-specific root cert; the cgo fetch does not. Other than that, they appear to be the same. Unfortunately, os/exec depends on crypto/x509, via net/http. Break the circular dependency by moving the exec tests to their own package. This will not work in iOS; we'll cross that bridge when we get to it. R=golang-dev, minux.ma, agl CC=golang-dev https://golang.org/cl/22020045
- Loading branch information
Showing
6 changed files
with
166 additions
and
94 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// 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 cgo | ||
|
||
package x509 | ||
|
||
/* | ||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 | ||
#cgo LDFLAGS: -framework CoreFoundation -framework Security | ||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <Security/Security.h> | ||
// FetchPEMRoots fetches the system's list of trusted X.509 root certificates. | ||
// | ||
// 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. | ||
// | ||
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after | ||
// we've consumed its content. | ||
int FetchPEMRoots(CFDataRef *pemRoots) { | ||
if (pemRoots == NULL) { | ||
return -1; | ||
} | ||
CFArrayRef certs = NULL; | ||
OSStatus err = SecTrustCopyAnchorCertificates(&certs); | ||
if (err != noErr) { | ||
return -1; | ||
} | ||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); | ||
int i, ncerts = CFArrayGetCount(certs); | ||
for (i = 0; i < ncerts; i++) { | ||
CFDataRef data = NULL; | ||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); | ||
if (cert == NULL) { | ||
continue; | ||
} | ||
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. | ||
// Once we support weak imports via cgo we should prefer that, and fall back to this | ||
// for older systems. | ||
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); | ||
if (err != noErr) { | ||
continue; | ||
} | ||
if (data != NULL) { | ||
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); | ||
CFRelease(data); | ||
} | ||
} | ||
CFRelease(certs); | ||
*pemRoots = combinedData; | ||
return 0; | ||
} | ||
*/ | ||
import "C" | ||
import "unsafe" | ||
|
||
func initSystemRoots() { | ||
roots := NewCertPool() | ||
|
||
var data C.CFDataRef = nil | ||
err := C.FetchPEMRoots(&data) | ||
if err == -1 { | ||
return | ||
} | ||
|
||
defer C.CFRelease(C.CFTypeRef(data)) | ||
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) | ||
roots.AppendCertsFromPEM(buf) | ||
systemRoots = roots | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,81 +1,23 @@ | ||
// Copyright 2011 The Go Authors. All rights reserved. | ||
// Copyright 2013 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. | ||
|
||
package x509 | ||
|
||
/* | ||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060 | ||
#cgo LDFLAGS: -framework CoreFoundation -framework Security | ||
#include <CoreFoundation/CoreFoundation.h> | ||
#include <Security/Security.h> | ||
// FetchPEMRoots fetches the system's list of trusted X.509 root certificates. | ||
// | ||
// 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. | ||
// | ||
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after | ||
// we've consumed its content. | ||
int FetchPEMRoots(CFDataRef *pemRoots) { | ||
if (pemRoots == NULL) { | ||
return -1; | ||
} | ||
CFArrayRef certs = NULL; | ||
OSStatus err = SecTrustCopyAnchorCertificates(&certs); | ||
if (err != noErr) { | ||
return -1; | ||
} | ||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0); | ||
int i, ncerts = CFArrayGetCount(certs); | ||
for (i = 0; i < ncerts; i++) { | ||
CFDataRef data = NULL; | ||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i); | ||
if (cert == NULL) { | ||
continue; | ||
} | ||
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport. | ||
// Once we support weak imports via cgo we should prefer that, and fall back to this | ||
// for older systems. | ||
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data); | ||
if (err != noErr) { | ||
continue; | ||
} | ||
if (data != NULL) { | ||
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data)); | ||
CFRelease(data); | ||
} | ||
} | ||
CFRelease(certs); | ||
*pemRoots = combinedData; | ||
return 0; | ||
} | ||
*/ | ||
import "C" | ||
import "unsafe" | ||
import "os/exec" | ||
|
||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) { | ||
return nil, nil | ||
} | ||
|
||
func initSystemRoots() { | ||
roots := NewCertPool() | ||
|
||
var data C.CFDataRef = nil | ||
err := C.FetchPEMRoots(&data) | ||
if err == -1 { | ||
return | ||
func execSecurityRoots() (*CertPool, error) { | ||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain") | ||
data, err := cmd.Output() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
defer C.CFRelease(C.CFTypeRef(data)) | ||
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data))) | ||
roots.AppendCertsFromPEM(buf) | ||
systemRoots = roots | ||
roots := NewCertPool() | ||
roots.AppendCertsFromPEM(data) | ||
return roots, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package x509 | ||
|
||
import "testing" | ||
|
||
func TestSystemRoots(t *testing.T) { | ||
sysRoots := systemRootsPool() // actual system roots | ||
execRoots, err := execSecurityRoots() // non-cgo roots | ||
|
||
if err != nil { | ||
t.Fatalf("failed to read system roots: %v", err) | ||
} | ||
|
||
for _, tt := range []*CertPool{sysRoots, execRoots} { | ||
if tt == nil { | ||
t.Fatal("no system roots") | ||
} | ||
// On Mavericks, there are 212 bundled certs; require only | ||
// 150 here, since this is just a sanity check, and the | ||
// exact number will vary over time. | ||
if want, have := 150, len(tt.certs); have < want { | ||
t.Fatalf("want at least %d system roots, have %d", want, have) | ||
} | ||
} | ||
|
||
// Check that the two cert pools are roughly the same; | ||
// |A∩B| > max(|A|, |B|) / 2 should be a reasonably robust check. | ||
|
||
isect := make(map[string]bool, len(sysRoots.certs)) | ||
for _, c := range sysRoots.certs { | ||
isect[string(c.Raw)] = true | ||
} | ||
|
||
have := 0 | ||
for _, c := range execRoots.certs { | ||
if isect[string(c.Raw)] { | ||
have++ | ||
} | ||
} | ||
|
||
var want int | ||
if nsys, nexec := len(sysRoots.certs), len(execRoots.certs); nsys > nexec { | ||
want = nsys / 2 | ||
} else { | ||
want = nexec / 2 | ||
} | ||
|
||
if have < want { | ||
t.Errorf("insufficent overlap between cgo and non-cgo roots; want at least %d, have %d", want, have) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2013 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 !cgo | ||
|
||
package x509 | ||
|
||
func initSystemRoots() { | ||
systemRoots, _ = execSecurityRoots() | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters