github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/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=1060
    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  //
    77  // Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
    78  // we've consumed its content.
    79  int FetchPEMRoots(CFDataRef *pemRoots) {
    80  	if (useOldCode()) {
    81  		return FetchPEMRoots_MountainLion(pemRoots);
    82  	}
    83  
    84  	// Get certificates from all domains, not just System, this lets
    85  	// the user add CAs to their "login" keychain, and Admins to add
    86  	// to the "System" keychain
    87  	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
    88  					     kSecTrustSettingsDomainAdmin,
    89  					     kSecTrustSettingsDomainUser };
    90  
    91  	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
    92  	if (pemRoots == NULL) {
    93  		return -1;
    94  	}
    95  
    96  	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
    97  	for (int i = 0; i < numDomains; i++) {
    98  		CFArrayRef certs = NULL;
    99  		// Only get certificates from domain that are trusted
   100  		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
   101  		if (err != noErr) {
   102  			continue;
   103  		}
   104  
   105  		int numCerts = CFArrayGetCount(certs);
   106  		for (int j = 0; j < numCerts; j++) {
   107  			CFDataRef data = NULL;
   108  			CFErrorRef errRef = NULL;
   109  			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
   110  			if (cert == NULL) {
   111  				continue;
   112  			}
   113  			// We only want to add Root CAs, so make sure Subject and Issuer Name match
   114  			CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
   115  			if (errRef != NULL) {
   116  				CFRelease(errRef);
   117  				continue;
   118  			}
   119  			CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
   120  			if (errRef != NULL) {
   121  				CFRelease(subjectName);
   122  				CFRelease(errRef);
   123  				continue;
   124  			}
   125  			Boolean equal = CFEqual(subjectName, issuerName);
   126  			CFRelease(subjectName);
   127  			CFRelease(issuerName);
   128  			if (!equal) {
   129  				continue;
   130  			}
   131  
   132  			// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
   133  			// Once we support weak imports via cgo we should prefer that, and fall back to this
   134  			// for older systems.
   135  			err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
   136  			if (err != noErr) {
   137  				continue;
   138  			}
   139  
   140  			if (data != NULL) {
   141  				CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
   142  				CFRelease(data);
   143  			}
   144  		}
   145  		CFRelease(certs);
   146  	}
   147  	*pemRoots = combinedData;
   148  	return 0;
   149  }
   150  */
   151  import "C"
   152  import (
   153  	"errors"
   154  	"unsafe"
   155  )
   156  
   157  func loadSystemRoots() (*CertPool, error) {
   158  	roots := NewCertPool()
   159  
   160  	var data C.CFDataRef = nil
   161  	err := C.FetchPEMRoots(&data)
   162  	if err == -1 {
   163  		// TODO: better error message
   164  		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
   165  	}
   166  
   167  	defer C.CFRelease(C.CFTypeRef(data))
   168  	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
   169  	roots.AppendCertsFromPEM(buf)
   170  	return roots, nil
   171  }