...
Run Format

Source file src/crypto/x509/root_cgo_darwin.go

Documentation: crypto/x509

     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.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300
    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 fetches the system's list of trusted X.509 root certificates.
    20  //
    21  // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
    22  // certificates of the system. On failure, the function returns -1.
    23  // Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
    24  //
    25  // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
    26  // be released (using CFRelease) after we've consumed its content.
    27  int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
    28  	int i;
    29  
    30  	// Get certificates from all domains, not just System, this lets
    31  	// the user add CAs to their "login" keychain, and Admins to add
    32  	// to the "System" keychain
    33  	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
    34  					     kSecTrustSettingsDomainAdmin,
    35  					     kSecTrustSettingsDomainUser };
    36  
    37  	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
    38  	if (pemRoots == NULL) {
    39  		return -1;
    40  	}
    41  
    42  	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
    43  	// but the Go linker's internal linking mode can't handle CFSTR relocations.
    44  	// Create our own dynamic string instead and release it below.
    45  	CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
    46  
    47  	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
    48  	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
    49  	for (i = 0; i < numDomains; i++) {
    50  		int j;
    51  		CFArrayRef certs = NULL;
    52  		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
    53  		if (err != noErr) {
    54  			continue;
    55  		}
    56  
    57  		CFIndex numCerts = CFArrayGetCount(certs);
    58  		for (j = 0; j < numCerts; j++) {
    59  			CFDataRef data = NULL;
    60  			CFErrorRef errRef = NULL;
    61  			CFArrayRef trustSettings = NULL;
    62  			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
    63  			if (cert == NULL) {
    64  				continue;
    65  			}
    66  			// We only want trusted certs.
    67  			int untrusted = 0;
    68  			int trustAsRoot = 0;
    69  			int trustRoot = 0;
    70  			if (i == 0) {
    71  				trustAsRoot = 1;
    72  			} else {
    73  				int k;
    74  				CFIndex m;
    75  
    76  				// Certs found in the system domain are always trusted. If the user
    77  				// configures "Never Trust" on such a cert, it will also be found in the
    78  				// admin or user domain, causing it to be added to untrustedPemRoots. The
    79  				// Go code will then clean this up.
    80  
    81  				// Trust may be stored in any of the domains. According to Apple's
    82  				// SecTrustServer.c, "user trust settings overrule admin trust settings",
    83  				// so take the last trust settings array we find.
    84  				// Skip the system domain since it is always trusted.
    85  				for (k = i; k < numDomains; k++) {
    86  					CFArrayRef domainTrustSettings = NULL;
    87  					err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
    88  					if (err == errSecSuccess && domainTrustSettings != NULL) {
    89  						if (trustSettings) {
    90  							CFRelease(trustSettings);
    91  						}
    92  						trustSettings = domainTrustSettings;
    93  					}
    94  				}
    95  				if (trustSettings == NULL) {
    96  					// "this certificate must be verified to a known trusted certificate"; aka not a root.
    97  					continue;
    98  				}
    99  				for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
   100  					CFNumberRef cfNum;
   101  					CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);
   102  					if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
   103  						SInt32 result = 0;
   104  						CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
   105  						// TODO: The rest of the dictionary specifies conditions for evaluation.
   106  						if (result == kSecTrustSettingsResultDeny) {
   107  							untrusted = 1;
   108  						} else if (result == kSecTrustSettingsResultTrustAsRoot) {
   109  							trustAsRoot = 1;
   110  						} else if (result == kSecTrustSettingsResultTrustRoot) {
   111  							trustRoot = 1;
   112  						}
   113  					}
   114  				}
   115  				CFRelease(trustSettings);
   116  			}
   117  
   118  			if (trustRoot) {
   119  				// We only want to add Root CAs, so make sure Subject and Issuer Name match
   120  				CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
   121  				if (errRef != NULL) {
   122  					CFRelease(errRef);
   123  					continue;
   124  				}
   125  				CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
   126  				if (errRef != NULL) {
   127  					CFRelease(subjectName);
   128  					CFRelease(errRef);
   129  					continue;
   130  				}
   131  				Boolean equal = CFEqual(subjectName, issuerName);
   132  				CFRelease(subjectName);
   133  				CFRelease(issuerName);
   134  				if (!equal) {
   135  					continue;
   136  				}
   137  			}
   138  
   139  			err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
   140  			if (err != noErr) {
   141  				continue;
   142  			}
   143  
   144  			if (data != NULL) {
   145  				if (!trustRoot && !trustAsRoot) {
   146  					untrusted = 1;
   147  				}
   148  				CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
   149  				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
   150  				CFRelease(data);
   151  			}
   152  		}
   153  		CFRelease(certs);
   154  	}
   155  	CFRelease(policy);
   156  	*pemRoots = combinedData;
   157  	*untrustedPemRoots = combinedUntrustedData;
   158  	return 0;
   159  }
   160  */
   161  import "C"
   162  import (
   163  	"errors"
   164  	"unsafe"
   165  )
   166  
   167  func loadSystemRoots() (*CertPool, error) {
   168  	roots := NewCertPool()
   169  
   170  	var data C.CFDataRef = 0
   171  	var untrustedData C.CFDataRef = 0
   172  	err := C.FetchPEMRoots(&data, &untrustedData)
   173  	if err == -1 {
   174  		// TODO: better error message
   175  		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
   176  	}
   177  
   178  	defer C.CFRelease(C.CFTypeRef(data))
   179  	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
   180  	roots.AppendCertsFromPEM(buf)
   181  	if untrustedData == 0 {
   182  		return roots, nil
   183  	}
   184  	defer C.CFRelease(C.CFTypeRef(untrustedData))
   185  	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
   186  	untrustedRoots := NewCertPool()
   187  	untrustedRoots.AppendCertsFromPEM(buf)
   188  
   189  	trustedRoots := NewCertPool()
   190  	for _, c := range roots.certs {
   191  		if !untrustedRoots.contains(c) {
   192  			trustedRoots.AddCert(c)
   193  		}
   194  	}
   195  	return trustedRoots, nil
   196  }
   197  

View as plain text