// 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. //go:build cgo && !arm && !arm64 && !ios // +build cgo,!arm,!arm64,!ios package x509 // Use non-cgo on Darwin to prevent duplicate symbols on cgo // ///* //#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080 //#cgo LDFLAGS: -framework CoreFoundation -framework Security // //#include //#include // //#include //#include // //// FetchPEMRoots_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 FetchPEMRoots_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; //} // //// useOldCode 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 useOldCode() { // 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; //} // //// 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. //// 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 FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) { // if (useOldCode()) { // return FetchPEMRoots_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; // if (i != 0) { // // 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 = 1; 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; // } // } // } // CFRelease(trustSettings); // } // // 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) { // 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 = nil // var untrustedData C.CFDataRef = nil // err := C.FetchPEMRoots(&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 untrustedData == nil { // 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 //}