...

Source file src/pkg/crypto/x509/root_cgo_darwin.go

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

View as plain text