github.com/fisco-bcos/crypto@v0.0.0-20200202032121-bd8ab0b5d4f1/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.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 static 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 }