root_cgo_darwin.go 12 KB

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