github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/crypto/x509/root_cgo_darwin.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build cgo,!arm,!arm64,!ios
     6  
     7  package x509
     8  
     9  /*
    10  #cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
    11  #cgo LDFLAGS: -framework CoreFoundation -framework Security
    12  
    13  #include <errno.h>
    14  #include <sys/sysctl.h>
    15  
    16  #include <CoreFoundation/CoreFoundation.h>
    17  #include <Security/Security.h>
    18  
    19  // FetchPEMRoots_MountainLion is the version of FetchPEMRoots from Go 1.6
    20  // which still works on OS X 10.8 (Mountain Lion).
    21  // It lacks support for admin & user cert domains.
    22  // See golang.org/issue/16473
    23  int FetchPEMRoots_MountainLion(CFDataRef *pemRoots) {
    24  	if (pemRoots == NULL) {
    25  		return -1;
    26  	}
    27  	CFArrayRef certs = NULL;
    28  	OSStatus err = SecTrustCopyAnchorCertificates(&certs);
    29  	if (err != noErr) {
    30  		return -1;
    31  	}
    32  	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
    33  	int i, ncerts = CFArrayGetCount(certs);
    34  	for (i = 0; i < ncerts; i++) {
    35  		CFDataRef data = NULL;
    36  		SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
    37  		if (cert == NULL) {
    38  			continue;
    39  		}
    40  		// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
    41  		// Once we support weak imports via cgo we should prefer that, and fall back to this
    42  		// for older systems.
    43  		err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
    44  		if (err != noErr) {
    45  			continue;
    46  		}
    47  		if (data != NULL) {
    48  			CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
    49  			CFRelease(data);
    50  		}
    51  	}
    52  	CFRelease(certs);
    53  	*pemRoots = combinedData;
    54  	return 0;
    55  }
    56  
    57  // useOldCode reports whether the running machine is OS X 10.8 Mountain Lion
    58  // or older. We only support Mountain Lion and higher, but we'll at least try our
    59  // best on older machines and continue to use the old code path.
    60  //
    61  // See golang.org/issue/16473
    62  int useOldCode() {
    63  	char str[256];
    64  	size_t size = sizeof(str);
    65  	memset(str, 0, size);
    66  	sysctlbyname("kern.osrelease", str, &size, NULL, 0);
    67  	// OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*.
    68  	// We never supported things before that.
    69  	return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0;
    70  }
    71  
    72  // FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
    73  //
    74  // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
    75  // certificates of the system. On failure, the function returns -1.
    76  // Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
    77  //
    78  // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
    79  // be released (using CFRelease) after we've consumed its content.
    80  int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
    81  	if (useOldCode()) {
    82  		return FetchPEMRoots_MountainLion(pemRoots);
    83  	}
    84  
    85  	// Get certificates from all domains, not just System, this lets
    86  	// the user add CAs to their "login" keychain, and Admins to add
    87  	// to the "System" keychain
    88  	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
    89  					     kSecTrustSettingsDomainAdmin,
    90  					     kSecTrustSettingsDomainUser };
    91  
    92  	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
    93  	if (pemRoots == NULL) {
    94  		return -1;
    95  	}
    96  
    97  	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
    98  	// but the Go linker's internal linking mode can't handle CFSTR relocations.
    99  	// Create our own dynamic string instead and release it below.
   100  	CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
   101  
   102  	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
   103  	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
   104  	for (int i = 0; i < numDomains; i++) {
   105  		CFArrayRef certs = NULL;
   106  		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
   107  		if (err != noErr) {
   108  			continue;
   109  		}
   110  
   111  		CFIndex numCerts = CFArrayGetCount(certs);
   112  		for (int j = 0; j < numCerts; j++) {
   113  			CFDataRef data = NULL;
   114  			CFErrorRef errRef = NULL;
   115  			CFArrayRef trustSettings = NULL;
   116  			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
   117  			if (cert == NULL) {
   118  				continue;
   119  			}
   120  			// We only want trusted certs.
   121  			int untrusted = 0;
   122  			if (i != 0) {
   123  				// Certs found in the system domain are always trusted. If the user
   124  				// configures "Never Trust" on such a cert, it will also be found in the
   125  				// admin or user domain, causing it to be added to untrustedPemRoots. The
   126  				// Go code will then clean this up.
   127  
   128  				// Trust may be stored in any of the domains. According to Apple's
   129  				// SecTrustServer.c, "user trust settings overrule admin trust settings",
   130  				// so take the last trust settings array we find.
   131  				// Skip the system domain since it is always trusted.
   132  				for (int k = 1; k < numDomains; k++) {
   133  					CFArrayRef domainTrustSettings = NULL;
   134  					err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
   135  					if (err == errSecSuccess && domainTrustSettings != NULL) {
   136  						if (trustSettings) {
   137  							CFRelease(trustSettings);
   138  						}
   139  						trustSettings = domainTrustSettings;
   140  					}
   141  				}
   142  				if (trustSettings == NULL) {
   143  					// "this certificate must be verified to a known trusted certificate"; aka not a root.
   144  					continue;
   145  				}
   146  				for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
   147  					CFNumberRef cfNum;
   148  					CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
   149  					if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
   150  						SInt32 result = 0;
   151  						CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
   152  						// TODO: The rest of the dictionary specifies conditions for evaluation.
   153  						if (result == kSecTrustSettingsResultDeny) {
   154  							untrusted = 1;
   155  						}
   156  					}
   157  				}
   158  				CFRelease(trustSettings);
   159  			}
   160  			// We only want to add Root CAs, so make sure Subject and Issuer Name match
   161  			CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
   162  			if (errRef != NULL) {
   163  				CFRelease(errRef);
   164  				continue;
   165  			}
   166  			CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
   167  			if (errRef != NULL) {
   168  				CFRelease(subjectName);
   169  				CFRelease(errRef);
   170  				continue;
   171  			}
   172  			Boolean equal = CFEqual(subjectName, issuerName);
   173  			CFRelease(subjectName);
   174  			CFRelease(issuerName);
   175  			if (!equal) {
   176  				continue;
   177  			}
   178  
   179  			// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
   180  			// Once we support weak imports via cgo we should prefer that, and fall back to this
   181  			// for older systems.
   182  			err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
   183  			if (err != noErr) {
   184  				continue;
   185  			}
   186  
   187  			if (data != NULL) {
   188  				CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
   189  				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
   190  				CFRelease(data);
   191  			}
   192  		}
   193  		CFRelease(certs);
   194  	}
   195  	CFRelease(policy);
   196  	*pemRoots = combinedData;
   197  	*untrustedPemRoots = combinedUntrustedData;
   198  	return 0;
   199  }
   200  */
   201  import "C"
   202  import (
   203  	"errors"
   204  	"unsafe"
   205  )
   206  
   207  func loadSystemRoots() (*CertPool, error) {
   208  	roots := NewCertPool()
   209  
   210  	var data C.CFDataRef = nil
   211  	var untrustedData C.CFDataRef = nil
   212  	err := C.FetchPEMRoots(&data, &untrustedData)
   213  	if err == -1 {
   214  		// TODO: better error message
   215  		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
   216  	}
   217  
   218  	defer C.CFRelease(C.CFTypeRef(data))
   219  	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
   220  	roots.AppendCertsFromPEM(buf)
   221  	if untrustedData == nil {
   222  		return roots, nil
   223  	}
   224  	defer C.CFRelease(C.CFTypeRef(untrustedData))
   225  	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
   226  	untrustedRoots := NewCertPool()
   227  	untrustedRoots.AppendCertsFromPEM(buf)
   228  
   229  	trustedRoots := NewCertPool()
   230  	for _, c := range roots.certs {
   231  		if !untrustedRoots.contains(c) {
   232  			trustedRoots.AddCert(c)
   233  		}
   234  	}
   235  	return trustedRoots, nil
   236  }