...

Source file src/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go

Documentation: github.com/google/certificate-transparency-go/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  //go:build cgo && !arm && !arm64 && !ios
     6  // +build cgo,!arm,!arm64,!ios
     7  
     8  package x509
     9  
    10  /*
    11  #cgo CFLAGS: -mmacosx-version-min=10.10 -D__MAC_OS_X_VERSION_MAX_ALLOWED=101300
    12  #cgo LDFLAGS: -framework CoreFoundation -framework Security
    13  
    14  #include <errno.h>
    15  #include <sys/sysctl.h>
    16  
    17  #include <CoreFoundation/CoreFoundation.h>
    18  #include <Security/Security.h>
    19  
    20  static Boolean isSSLPolicy(SecPolicyRef policyRef) {
    21  	if (!policyRef) {
    22  		return false;
    23  	}
    24  	CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
    25  	if (properties == NULL) {
    26  		return false;
    27  	}
    28  	Boolean isSSL = false;
    29  	CFTypeRef value = NULL;
    30  	if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
    31  		isSSL = CFEqual(value, kSecPolicyAppleSSL);
    32  	}
    33  	CFRelease(properties);
    34  	return isSSL;
    35  }
    36  
    37  // sslTrustSettingsResult obtains the final kSecTrustSettingsResult value
    38  // for a certificate in the user or admin domain, combining usage constraints
    39  // for the SSL SecTrustSettingsPolicy, ignoring SecTrustSettingsKeyUsage and
    40  // kSecTrustSettingsAllowedError.
    41  // https://developer.apple.com/documentation/security/1400261-sectrustsettingscopytrustsetting
    42  static SInt32 sslTrustSettingsResult(SecCertificateRef cert) {
    43  	CFArrayRef trustSettings = NULL;
    44  	OSStatus err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &trustSettings);
    45  
    46  	// According to Apple's SecTrustServer.c, "user trust settings overrule admin trust settings",
    47  	// but the rules of the override are unclear. Let's assume admin trust settings are applicable
    48  	// if and only if user trust settings fail to load or are NULL.
    49  	if (err != errSecSuccess || trustSettings == NULL) {
    50  		if (trustSettings != NULL) CFRelease(trustSettings);
    51  		err = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &trustSettings);
    52  	}
    53  
    54  	// > no trust settings [...] means "this certificate must be verified to a known trusted certificate”
    55  	// (Should this cause a fallback from user to admin domain? It's unclear.)
    56  	if (err != errSecSuccess || trustSettings == NULL) {
    57  		if (trustSettings != NULL) CFRelease(trustSettings);
    58  		return kSecTrustSettingsResultUnspecified;
    59  	}
    60  
    61  	// > An empty trust settings array means "always trust this certificate” with an
    62  	// > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
    63  	if (CFArrayGetCount(trustSettings) == 0) {
    64  		CFRelease(trustSettings);
    65  		return kSecTrustSettingsResultTrustRoot;
    66  	}
    67  
    68  	// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
    69  	// but the Go linker's internal linking mode can't handle CFSTR relocations.
    70  	// Create our own dynamic string instead and release it below.
    71  	CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
    72  		NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
    73  	CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
    74  		NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
    75  	CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
    76  		NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);
    77  
    78  	CFIndex m; SInt32 result = 0;
    79  	for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
    80  		CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);
    81  
    82  		// First, check if this trust setting is constrained to a non-SSL policy.
    83  		SecPolicyRef policyRef;
    84  		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
    85  			if (!isSSLPolicy(policyRef)) {
    86  				continue;
    87  			}
    88  		}
    89  
    90  		if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
    91  			// Restricted to a hostname, not a root.
    92  			continue;
    93  		}
    94  
    95  		CFNumberRef cfNum;
    96  		if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
    97  			CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
    98  		} else {
    99  			// > If this key is not present, a default value of
   100  			// > kSecTrustSettingsResultTrustRoot is assumed.
   101  			result = kSecTrustSettingsResultTrustRoot;
   102  		}
   103  
   104  		// If multiple dictionaries match, we are supposed to "OR" them,
   105  		// the semantics of which are not clear. Since TrustRoot and TrustAsRoot
   106  		// are mutually exclusive, Deny should probably override, and Invalid and
   107  		// Unspecified be overridden, approximate this by stopping at the first
   108  		// TrustRoot, TrustAsRoot or Deny.
   109  		if (result == kSecTrustSettingsResultTrustRoot) {
   110  			break;
   111  		} else if (result == kSecTrustSettingsResultTrustAsRoot) {
   112  			break;
   113  		} else if (result == kSecTrustSettingsResultDeny) {
   114  			break;
   115  		}
   116  	}
   117  
   118  	// If trust settings are present, but none of them match the policy...
   119  	// the docs don't tell us what to do.
   120  	//
   121  	// "Trust settings for a given use apply if any of the dictionaries in the
   122  	// certificate’s trust settings array satisfies the specified use." suggests
   123  	// that it's as if there were no trust settings at all, so we should probably
   124  	// fallback to the admin trust settings. TODO.
   125  	if (result == 0) {
   126  		result = kSecTrustSettingsResultUnspecified;
   127  	}
   128  
   129  	CFRelease(_kSecTrustSettingsPolicy);
   130  	CFRelease(_kSecTrustSettingsPolicyString);
   131  	CFRelease(_kSecTrustSettingsResult);
   132  	CFRelease(trustSettings);
   133  
   134  	return result;
   135  }
   136  
   137  // isRootCertificate reports whether Subject and Issuer match.
   138  static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
   139  	CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
   140  	if (*errRef != NULL) {
   141  		return false;
   142  	}
   143  	CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
   144  	if (*errRef != NULL) {
   145  		CFRelease(subjectName);
   146  		return false;
   147  	}
   148  	Boolean equal = CFEqual(subjectName, issuerName);
   149  	CFRelease(subjectName);
   150  	CFRelease(issuerName);
   151  	return equal;
   152  }
   153  
   154  // CopyPEMRootsCTX509 fetches the system's list of trusted X.509 root certificates
   155  // for the kSecTrustSettingsPolicy SSL.
   156  //
   157  // On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
   158  // certificates of the system. On failure, the function returns -1.
   159  // Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
   160  //
   161  // Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
   162  // be released (using CFRelease) after we've consumed its content.
   163  static int CopyPEMRootsCTX509(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots) {
   164  	int i;
   165  
   166  	if (debugDarwinRoots) {
   167  		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
   168  		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
   169  		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
   170  		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
   171  		fprintf(stderr, "crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
   172  	}
   173  
   174  	// Get certificates from all domains, not just System, this lets
   175  	// the user add CAs to their "login" keychain, and Admins to add
   176  	// to the "System" keychain
   177  	SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
   178  		kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };
   179  
   180  	int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
   181  	if (pemRoots == NULL || untrustedPemRoots == NULL) {
   182  		return -1;
   183  	}
   184  
   185  	CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
   186  	CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
   187  	for (i = 0; i < numDomains; i++) {
   188  		int j;
   189  		CFArrayRef certs = NULL;
   190  		OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
   191  		if (err != noErr) {
   192  			continue;
   193  		}
   194  
   195  		CFIndex numCerts = CFArrayGetCount(certs);
   196  		for (j = 0; j < numCerts; j++) {
   197  			SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
   198  			if (cert == NULL) {
   199  				continue;
   200  			}
   201  
   202  			SInt32 result;
   203  			if (domains[i] == kSecTrustSettingsDomainSystem) {
   204  				// Certs found in the system domain are always trusted. If the user
   205  				// configures "Never Trust" on such a cert, it will also be found in the
   206  				// admin or user domain, causing it to be added to untrustedPemRoots. The
   207  				// Go code will then clean this up.
   208  				result = kSecTrustSettingsResultTrustRoot;
   209  			} else {
   210  				result = sslTrustSettingsResult(cert);
   211  				if (debugDarwinRoots) {
   212  					CFErrorRef errRef = NULL;
   213  					CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
   214  					if (errRef != NULL) {
   215  						fprintf(stderr, "crypto/x509: SecCertificateCopyShortDescription failed\n");
   216  						CFRelease(errRef);
   217  						continue;
   218  					}
   219  
   220  					CFIndex length = CFStringGetLength(summary);
   221  					CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
   222  					char *buffer = malloc(maxSize);
   223  					if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
   224  						fprintf(stderr, "crypto/x509: %s returned %d\n", buffer, (int)result);
   225  					}
   226  					free(buffer);
   227  					CFRelease(summary);
   228  				}
   229  			}
   230  
   231  			CFMutableDataRef appendTo;
   232  			// > Note the distinction between the results kSecTrustSettingsResultTrustRoot
   233  			// > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
   234  			// > root (self-signed) certificates; the latter can only be applied to
   235  			// > non-root certificates.
   236  			if (result == kSecTrustSettingsResultTrustRoot) {
   237  				CFErrorRef errRef = NULL;
   238  				if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
   239  					if (errRef != NULL) CFRelease(errRef);
   240  					continue;
   241  				}
   242  
   243  				appendTo = combinedData;
   244  			} else if (result == kSecTrustSettingsResultTrustAsRoot) {
   245  				CFErrorRef errRef = NULL;
   246  				if (isRootCertificate(cert, &errRef) || errRef != NULL) {
   247  					if (errRef != NULL) CFRelease(errRef);
   248  					continue;
   249  				}
   250  
   251  				appendTo = combinedData;
   252  			} else if (result == kSecTrustSettingsResultDeny) {
   253  				appendTo = combinedUntrustedData;
   254  			} else if (result == kSecTrustSettingsResultUnspecified) {
   255  				// Certificates with unspecified trust should probably be added to a pool of
   256  				// intermediates for chain building, or checked for transitive trust and
   257  				// added to the root pool (which is an imprecise approximation because it
   258  				// cuts chains short) but we don't support either at the moment. TODO.
   259  				continue;
   260  			} else {
   261  				continue;
   262  			}
   263  
   264  			CFDataRef data = NULL;
   265  			err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
   266  			if (err != noErr) {
   267  				continue;
   268  			}
   269  			if (data != NULL) {
   270  				CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
   271  				CFRelease(data);
   272  			}
   273  		}
   274  		CFRelease(certs);
   275  	}
   276  	*pemRoots = combinedData;
   277  	*untrustedPemRoots = combinedUntrustedData;
   278  	return 0;
   279  }
   280  */
   281  import "C"
   282  import (
   283  	"errors"
   284  	"unsafe"
   285  )
   286  
   287  func loadSystemRoots() (*CertPool, error) {
   288  	var data, untrustedData C.CFDataRef
   289  	err := C.CopyPEMRootsCTX509(&data, &untrustedData, C.bool(debugDarwinRoots))
   290  	if err == -1 {
   291  		return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
   292  	}
   293  	defer C.CFRelease(C.CFTypeRef(data))
   294  	defer C.CFRelease(C.CFTypeRef(untrustedData))
   295  
   296  	buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
   297  	roots := NewCertPool()
   298  	roots.AppendCertsFromPEM(buf)
   299  
   300  	if C.CFDataGetLength(untrustedData) == 0 {
   301  		return roots, nil
   302  	}
   303  
   304  	buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
   305  	untrustedRoots := NewCertPool()
   306  	untrustedRoots.AppendCertsFromPEM(buf)
   307  
   308  	trustedRoots := NewCertPool()
   309  	for _, c := range roots.certs {
   310  		if !untrustedRoots.contains(c) {
   311  			trustedRoots.AddCert(c)
   312  		}
   313  	}
   314  	return trustedRoots, nil
   315  }
   316  

View as plain text