123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- // 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,!arm,!arm64,!ios
- package x509
- /*
- #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
- #cgo LDFLAGS: -framework CoreFoundation -framework Security
- #include <errno.h>
- #include <sys/sysctl.h>
- #include <CoreFoundation/CoreFoundation.h>
- #include <Security/Security.h>
- // FetchPEMRootsCTX509_MountainLion is the version of FetchPEMRoots from Go 1.6
- // which still works on OS X 10.8 (Mountain Lion).
- // It lacks support for admin & user cert domains.
- // See golang.org/issue/16473
- int FetchPEMRootsCTX509_MountainLion(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;
- }
- // useOldCodeCTX509 reports whether the running machine is OS X 10.8 Mountain Lion
- // or older. We only support Mountain Lion and higher, but we'll at least try our
- // best on older machines and continue to use the old code path.
- //
- // See golang.org/issue/16473
- int useOldCodeCTX509() {
- char str[256];
- size_t size = sizeof(str);
- memset(str, 0, size);
- sysctlbyname("kern.osrelease", str, &size, NULL, 0);
- // OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*.
- // We never supported things before that.
- return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0;
- }
- // FetchPEMRootsCTX509 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.
- // 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.
- int FetchPEMRootsCTX509(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
- if (useOldCodeCTX509()) {
- return FetchPEMRootsCTX509_MountainLion(pemRoots);
- }
- // 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) {
- return -1;
- }
- // 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 policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
- CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
- CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
- for (int i = 0; i < numDomains; i++) {
- CFArrayRef certs = NULL;
- OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
- if (err != noErr) {
- continue;
- }
- CFIndex numCerts = CFArrayGetCount(certs);
- for (int j = 0; j < numCerts; j++) {
- CFDataRef data = NULL;
- CFErrorRef errRef = NULL;
- CFArrayRef trustSettings = NULL;
- SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
- if (cert == NULL) {
- continue;
- }
- // We only want trusted certs.
- int untrusted = 0;
- int trustAsRoot = 0;
- int trustRoot = 0;
- if (i == 0) {
- trustAsRoot = 1;
- } else {
- // 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.
- // Trust may be stored in any of the domains. According to Apple's
- // SecTrustServer.c, "user trust settings overrule admin trust settings",
- // so take the last trust settings array we find.
- // Skip the system domain since it is always trusted.
- for (int k = i; k < numDomains; k++) {
- CFArrayRef domainTrustSettings = NULL;
- err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
- if (err == errSecSuccess && domainTrustSettings != NULL) {
- if (trustSettings) {
- CFRelease(trustSettings);
- }
- trustSettings = domainTrustSettings;
- }
- }
- if (trustSettings == NULL) {
- // "this certificate must be verified to a known trusted certificate"; aka not a root.
- continue;
- }
- for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
- CFNumberRef cfNum;
- CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
- if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
- SInt32 result = 0;
- CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
- // TODO: The rest of the dictionary specifies conditions for evaluation.
- if (result == kSecTrustSettingsResultDeny) {
- untrusted = 1;
- } else if (result == kSecTrustSettingsResultTrustAsRoot) {
- trustAsRoot = 1;
- } else if (result == kSecTrustSettingsResultTrustRoot) {
- trustRoot = 1;
- }
- }
- }
- CFRelease(trustSettings);
- }
- if (trustRoot) {
- // We only want to add Root CAs, so make sure Subject and Issuer Name match
- CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
- if (errRef != NULL) {
- CFRelease(errRef);
- continue;
- }
- CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
- if (errRef != NULL) {
- CFRelease(subjectName);
- CFRelease(errRef);
- continue;
- }
- Boolean equal = CFEqual(subjectName, issuerName);
- CFRelease(subjectName);
- CFRelease(issuerName);
- if (!equal) {
- 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) {
- if (!trustRoot && !trustAsRoot) {
- untrusted = 1;
- }
- CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
- CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
- CFRelease(data);
- }
- }
- CFRelease(certs);
- }
- CFRelease(policy);
- *pemRoots = combinedData;
- *untrustedPemRoots = combinedUntrustedData;
- return 0;
- }
- */
- import "C"
- import (
- "errors"
- "unsafe"
- )
- func loadSystemRoots() (*CertPool, error) {
- roots := NewCertPool()
- var data C.CFDataRef
- setNilCFRef(&data)
- var untrustedData C.CFDataRef
- setNilCFRef(&untrustedData)
- err := C.FetchPEMRootsCTX509(&data, &untrustedData)
- if err == -1 {
- // TODO: better error message
- return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
- }
- defer C.CFRelease(C.CFTypeRef(data))
- buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
- roots.AppendCertsFromPEM(buf)
- if isNilCFRef(untrustedData) {
- return roots, nil
- }
- defer C.CFRelease(C.CFTypeRef(untrustedData))
- buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
- untrustedRoots := NewCertPool()
- untrustedRoots.AppendCertsFromPEM(buf)
- trustedRoots := NewCertPool()
- for _, c := range roots.certs {
- if !untrustedRoots.contains(c) {
- trustedRoots.AddCert(c)
- }
- }
- return trustedRoots, nil
- }
|