@@ -16,34 +16,164 @@ package x509
16
16
#include <CoreFoundation/CoreFoundation.h>
17
17
#include <Security/Security.h>
18
18
19
- // FetchPEMRoots fetches the system's list of trusted X.509 root certificates.
19
+ static bool isSSLPolicy(SecPolicyRef policyRef) {
20
+ if (!policyRef) {
21
+ return false;
22
+ }
23
+ CFDictionaryRef properties = SecPolicyCopyProperties(policyRef);
24
+ if (properties == NULL) {
25
+ return false;
26
+ }
27
+ CFTypeRef value = NULL;
28
+ if (CFDictionaryGetValueIfPresent(properties, kSecPolicyOid, (const void **)&value)) {
29
+ CFRelease(properties);
30
+ return CFEqual(value, kSecPolicyAppleSSL);
31
+ }
32
+ CFRelease(properties);
33
+ return false;
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
+ if (err != errSecSuccess || trustSettings == NULL) {
55
+ if (trustSettings != NULL) CFRelease(trustSettings);
56
+ return kSecTrustSettingsResultUnspecified;
57
+ }
58
+
59
+ // > An empty trust settings array means "always trust this certificate” with an
60
+ // > overall trust setting for the certificate of kSecTrustSettingsResultTrustRoot.
61
+ if (CFArrayGetCount(trustSettings) == 0) {
62
+ CFRelease(trustSettings);
63
+ return kSecTrustSettingsResultTrustRoot;
64
+ }
65
+
66
+ // kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
67
+ // but the Go linker's internal linking mode can't handle CFSTR relocations.
68
+ // Create our own dynamic string instead and release it below.
69
+ CFStringRef _kSecTrustSettingsResult = CFStringCreateWithCString(
70
+ NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
71
+ CFStringRef _kSecTrustSettingsPolicy = CFStringCreateWithCString(
72
+ NULL, "kSecTrustSettingsPolicy", kCFStringEncodingUTF8);
73
+ CFStringRef _kSecTrustSettingsPolicyString = CFStringCreateWithCString(
74
+ NULL, "kSecTrustSettingsPolicyString", kCFStringEncodingUTF8);
75
+
76
+ CFIndex m; SInt32 result = 0;
77
+ for (m = 0; m < CFArrayGetCount(trustSettings); m++) {
78
+ CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, m);
79
+
80
+ // First, check if this trust setting applies to our policy. We assume
81
+ // only one will. The docs suggest that there might be multiple applying
82
+ // but don't explain how to combine them.
83
+ SecPolicyRef policyRef;
84
+ if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsPolicy, (const void**)&policyRef)) {
85
+ if (!isSSLPolicy(policyRef)) {
86
+ continue;
87
+ }
88
+ } else {
89
+ continue;
90
+ }
91
+
92
+ if (CFDictionaryContainsKey(tSetting, _kSecTrustSettingsPolicyString)) {
93
+ // Restricted to a hostname, not a root.
94
+ continue;
95
+ }
96
+
97
+ CFNumberRef cfNum;
98
+ if (CFDictionaryGetValueIfPresent(tSetting, _kSecTrustSettingsResult, (const void**)&cfNum)) {
99
+ CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
100
+ } else {
101
+ // > If the value of the kSecTrustSettingsResult component is not
102
+ // > kSecTrustSettingsResultUnspecified for a usage constraints dictionary that has
103
+ // > no constraints, the default value kSecTrustSettingsResultTrustRoot is assumed.
104
+ result = kSecTrustSettingsResultTrustRoot;
105
+ }
106
+
107
+ break;
108
+ }
109
+
110
+ // If trust settings are present, but none of them match the policy...
111
+ // the docs don't tell us what to do.
112
+ //
113
+ // "Trust settings for a given use apply if any of the dictionaries in the
114
+ // certificate’s trust settings array satisfies the specified use." suggests
115
+ // that it's as if there were no trust settings at all, so we should probably
116
+ // fallback to the admin trust settings. TODO.
117
+ if (result == 0) {
118
+ result = kSecTrustSettingsResultUnspecified;
119
+ }
120
+
121
+ CFRelease(_kSecTrustSettingsPolicy);
122
+ CFRelease(_kSecTrustSettingsPolicyString);
123
+ CFRelease(_kSecTrustSettingsResult);
124
+ CFRelease(trustSettings);
125
+
126
+ return result;
127
+ }
128
+
129
+ // isRootCertificate reports whether Subject and Issuer match.
130
+ static Boolean isRootCertificate(SecCertificateRef cert, CFErrorRef *errRef) {
131
+ CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, errRef);
132
+ if (*errRef != NULL) {
133
+ return false;
134
+ }
135
+ CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, errRef);
136
+ if (*errRef != NULL) {
137
+ CFRelease(subjectName);
138
+ return false;
139
+ }
140
+ Boolean equal = CFEqual(subjectName, issuerName);
141
+ CFRelease(subjectName);
142
+ CFRelease(issuerName);
143
+ return equal;
144
+ }
145
+
146
+ // FetchPEMRoots fetches the system's list of trusted X.509 root certificates
147
+ // for the kSecTrustSettingsPolicy SSL.
20
148
//
21
149
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
22
150
// certificates of the system. On failure, the function returns -1.
23
151
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
24
152
//
25
153
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
26
154
// be released (using CFRelease) after we've consumed its content.
27
- int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
155
+ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots, bool debugDarwinRoots ) {
28
156
int i;
29
157
158
+ if (debugDarwinRoots) {
159
+ printf("crypto/x509: kSecTrustSettingsResultInvalid = %d\n", kSecTrustSettingsResultInvalid);
160
+ printf("crypto/x509: kSecTrustSettingsResultTrustRoot = %d\n", kSecTrustSettingsResultTrustRoot);
161
+ printf("crypto/x509: kSecTrustSettingsResultTrustAsRoot = %d\n", kSecTrustSettingsResultTrustAsRoot);
162
+ printf("crypto/x509: kSecTrustSettingsResultDeny = %d\n", kSecTrustSettingsResultDeny);
163
+ printf("crypto/x509: kSecTrustSettingsResultUnspecified = %d\n", kSecTrustSettingsResultUnspecified);
164
+ }
165
+
30
166
// Get certificates from all domains, not just System, this lets
31
167
// the user add CAs to their "login" keychain, and Admins to add
32
168
// to the "System" keychain
33
169
SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
34
- kSecTrustSettingsDomainAdmin,
35
- kSecTrustSettingsDomainUser };
170
+ kSecTrustSettingsDomainAdmin, kSecTrustSettingsDomainUser };
36
171
37
172
int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
38
173
if (pemRoots == NULL) {
39
174
return -1;
40
175
}
41
176
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
177
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
48
178
CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
49
179
for (i = 0; i < numDomains; i++) {
@@ -57,102 +187,81 @@ int FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
57
187
CFIndex numCerts = CFArrayGetCount(certs);
58
188
for (j = 0; j < numCerts; j++) {
59
189
CFDataRef data = NULL;
60
- CFErrorRef errRef = NULL;
61
190
CFArrayRef trustSettings = NULL;
62
191
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
63
192
if (cert == NULL) {
64
193
continue;
65
194
}
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
195
196
+ SInt32 result;
197
+ if (domains[i] == kSecTrustSettingsDomainSystem) {
76
198
// Certs found in the system domain are always trusted. If the user
77
199
// configures "Never Trust" on such a cert, it will also be found in the
78
200
// admin or user domain, causing it to be added to untrustedPemRoots. The
79
201
// 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;
202
+ result = kSecTrustSettingsResultTrustRoot;
203
+ } else {
204
+ result = sslTrustSettingsResult(cert);
205
+ if (debugDarwinRoots) {
206
+ CFErrorRef errRef = NULL;
207
+ CFStringRef summary = SecCertificateCopyShortDescription(NULL, cert, &errRef);
208
+ if (errRef != NULL) {
209
+ printf("crypto/x509: SecCertificateCopyShortDescription failed\n");
210
+ CFRelease(errRef);
211
+ continue;
93
212
}
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
- }
213
+
214
+ CFIndex length = CFStringGetLength(summary);
215
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
216
+ char *buffer = malloc(maxSize);
217
+ if (CFStringGetCString(summary, buffer, maxSize, kCFStringEncodingUTF8)) {
218
+ printf("crypto/x509: %s returned %d\n", buffer, result);
113
219
}
220
+ free(buffer);
221
+ CFRelease(summary);
114
222
}
115
- CFRelease(trustSettings);
116
223
}
117
224
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);
225
+ CFMutableDataRef appendTo;
226
+ // > Note the distinction between the results kSecTrustSettingsResultTrustRoot
227
+ // > and kSecTrustSettingsResultTrustAsRoot: The former can only be applied to
228
+ // > root (self-signed) certificates; the latter can only be applied to
229
+ // > non-root certificates.
230
+ if (result == kSecTrustSettingsResultTrustRoot) {
231
+ CFErrorRef errRef = NULL;
232
+ if (!isRootCertificate(cert, &errRef) || errRef != NULL) {
233
+ if (errRef != NULL) CFRelease(errRef);
129
234
continue;
130
235
}
131
- Boolean equal = CFEqual(subjectName, issuerName);
132
- CFRelease(subjectName);
133
- CFRelease(issuerName);
134
- if (!equal) {
236
+
237
+ appendTo = combinedData;
238
+ } else if (result == kSecTrustSettingsResultTrustAsRoot) {
239
+ CFErrorRef errRef = NULL;
240
+ if (isRootCertificate(cert, &errRef) || errRef != NULL) {
241
+ if (errRef != NULL) CFRelease(errRef);
135
242
continue;
136
243
}
244
+
245
+ appendTo = combinedData;
246
+ } else if (result == kSecTrustSettingsResultDeny) {
247
+ appendTo = combinedUntrustedData;
248
+ } else if (result == kSecTrustSettingsResultUnspecified) {
249
+ continue;
250
+ } else {
251
+ continue;
137
252
}
138
253
139
254
err = SecItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
140
255
if (err != noErr) {
141
256
continue;
142
257
}
143
-
144
258
if (data != NULL) {
145
- if (!trustRoot && !trustAsRoot) {
146
- untrusted = 1;
147
- }
148
- CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
149
259
CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
150
260
CFRelease(data);
151
261
}
152
262
}
153
263
CFRelease(certs);
154
264
}
155
- CFRelease(policy);
156
265
*pemRoots = combinedData;
157
266
*untrustedPemRoots = combinedUntrustedData;
158
267
return 0;
@@ -169,9 +278,8 @@ func loadSystemRoots() (*CertPool, error) {
169
278
170
279
var data C.CFDataRef = 0
171
280
var untrustedData C.CFDataRef = 0
172
- err := C .FetchPEMRoots (& data , & untrustedData )
281
+ err := C .FetchPEMRoots (& data , & untrustedData , C . bool ( debugDarwinRoots ) )
173
282
if err == - 1 {
174
- // TODO: better error message
175
283
return nil , errors .New ("crypto/x509: failed to load darwin system roots with cgo" )
176
284
}
177
285
0 commit comments