github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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  			int trustAsRoot = 0;
   123  			int trustRoot = 0;
   124  			if (i == 0) {
   125  				trustAsRoot = 1;
   126  			} else {
   127  				// Certs found in the system domain are always trusted. If the user
   128  				// configures "Never Trust" on such a cert, it will also be found in the
   129  				// admin or user domain, causing it to be added to untrustedPemRoots. The
   130  				// Go code will then clean this up.
   131  
   132  				// Trust may be stored in any of the domains. According to Apple's
   133  				// SecTrustServer.c, "user trust settings overrule admin trust settings",
   134  				// so take the last trust settings array we find.
   135  				// Skip the system domain since it is always trusted.
   136  				for (int k = i; k < numDomains; k++) {
   137  					CFArrayRef domainTrustSettings = NULL;
   138  					err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
   139  					if (err == errSecSuccess && domainTrustSettings != NULL) {
   140  						if (trustSettings) {
   141  							CFRelease(trustSettings);
   142  						}
   143  						trustSettings = domainTrustSettings;
   144  					}
   145  				}
   146  				if (trustSettings == NULL) {
   147  					// "this certificate must be verified to a known trusted certificate"; aka not a root.
   148  					continue;
   149  				}
   150  				for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
   151  					CFNumberRef cfNum;
   152  					CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
   153  					if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
   154  						SInt32 result = 0;
   155  						CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
   156  						// TODO: The rest of the dictionary specifies conditions for evaluation.
   157  						if (result == kSecTrustSettingsResultDeny) {
   158  							untrusted = 1;
   159  						} else if (result == kSecTrustSettingsResultTrustAsRoot) {
   160  							trustAsRoot = 1;
   161  						} else if (result == kSecTrustSettingsResultTrustRoot) {
   162  							trustRoot = 1;
   163  						}
   164  					}
   165  				}
   166  				CFRelease(trustSettings);
   167  			}
   168  
   169  			if (trustRoot) {
   170  				// We only want to add Root CAs, so make sure Subject and Issuer Name match
   171  				CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
   172  				if (errRef != NULL) {
   173  					CFRelease(errRef);
   174  					continue;
   175  				}
   176  				CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
   177  				if (errRef != NULL) {
   178  					CFRelease(subjectName);
   179  					CFRelease(errRef);
   180  					continue;
   181  				}
   182  				Boolean equal = CFEqual(subjectName, issuerName);
   183  				CFRelease(subjectName);
   184  				CFRelease(issuerName);
   185  				if (!equal) {
   186  					continue;
   187  				}
   188  			}
   189  
   190  			// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
   191  			// Once we support weak imports via cgo we should prefer that, and fall back to this
   192  			// for older systems.
   193  			err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
   194  			if (err != noErr) {
   195  				continue;
   196  			}
   197  
   198  			if (data != NULL) {
   199  				if (!trustRoot && !trustAsRoot) {
   200  					untrusted = 1;
   201  				}
   202  				CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
   203  				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
   204  				CFRelease(data);
   205  			}
   206  		}
   207  		CFRelease(certs);
   208  	}
   209  	CFRelease(policy);
   210  	*pemRoots = combinedData;
   211  	*untrustedPemRoots = combinedUntrustedData;
   212  	return 0;
   213  }
   214  */
   215  import "C"
   216  import (
   217  	"errors"
   218  	"unsafe"
   219  )
   220  
   221  func loadSystemRoots() (*CertPool, error) {
   222  	roots := NewCertPool()
   223  
   224  	var data C.CFDataRef = 0
   225  	var untrustedData C.CFDataRef = 0
   226  	err := C.FetchPEMRoots(&data, &untrustedData)
   227  	if err == -1 {
   228  		// TODO: better error message
   229  		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
   230  	}
   231  
   232  	defer C.CFRelease(C.CFTypeRef(data))
   233  	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
   234  	roots.AppendCertsFromPEM(buf)
   235  	if untrustedData == 0 {
   236  		return roots, nil
   237  	}
   238  	defer C.CFRelease(C.CFTypeRef(untrustedData))
   239  	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
   240  	untrustedRoots := NewCertPool()
   241  	untrustedRoots.AppendCertsFromPEM(buf)
   242  
   243  	trustedRoots := NewCertPool()
   244  	for _, c := range roots.certs {
   245  		if !untrustedRoots.contains(c) {
   246  			trustedRoots.AddCert(c)
   247  		}
   248  	}
   249  	return trustedRoots, nil
   250  }