]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
866f8763 | 2 | * Copyright (c) 2008-2017 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5c19dc3a | 5 | * |
b1ab9ed8 A |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
5c19dc3a | 12 | * |
b1ab9ed8 A |
13 | * The Original Code and all software distributed under the License are |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
5c19dc3a | 20 | * |
b1ab9ed8 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
24 | /* | |
866f8763 | 25 | * SecPolicyServer.c - Engine for evaluating certificate paths against trust policies. |
b1ab9ed8 A |
26 | */ |
27 | ||
28 | #include <securityd/SecPolicyServer.h> | |
29 | #include <Security/SecPolicyInternal.h> | |
30 | #include <Security/SecPolicyPriv.h> | |
fa7225c8 | 31 | #include <Security/SecTask.h> |
b1ab9ed8 A |
32 | #include <securityd/asynchttp.h> |
33 | #include <securityd/policytree.h> | |
5c19dc3a | 34 | #include <securityd/nameconstraints.h> |
b1ab9ed8 A |
35 | #include <CoreFoundation/CFTimeZone.h> |
36 | #include <wctype.h> | |
5c19dc3a | 37 | #include <libDER/oidsPriv.h> |
b1ab9ed8 A |
38 | #include <CoreFoundation/CFNumber.h> |
39 | #include <Security/SecCertificateInternal.h> | |
40 | #include <AssertMacros.h> | |
427c49bc | 41 | #include <utilities/debugging.h> |
fa7225c8 | 42 | #include <utilities/SecInternalReleasePriv.h> |
b1ab9ed8 A |
43 | #include <security_asn1/SecAsn1Coder.h> |
44 | #include <security_asn1/ocspTemplates.h> | |
45 | #include <security_asn1/oidsalg.h> | |
46 | #include <security_asn1/oidsocsp.h> | |
47 | #include <CommonCrypto/CommonDigest.h> | |
48 | #include <Security/SecFramework.h> | |
49 | #include <Security/SecPolicyInternal.h> | |
50 | #include <Security/SecTrustPriv.h> | |
866f8763 | 51 | #include <Security/SecTrustInternal.h> |
6b200bc3 | 52 | #include <Security/SecTrustSettingsPriv.h> |
b1ab9ed8 | 53 | #include <Security/SecInternal.h> |
5c19dc3a | 54 | #include <Security/SecKeyPriv.h> |
fa7225c8 | 55 | #include <Security/SecTask.h> |
b1ab9ed8 A |
56 | #include <CFNetwork/CFHTTPMessage.h> |
57 | #include <CFNetwork/CFHTTPStream.h> | |
58 | #include <SystemConfiguration/SCDynamicStoreCopySpecific.h> | |
59 | #include <asl.h> | |
b1ab9ed8 | 60 | #include <securityd/SecTrustServer.h> |
6b200bc3 | 61 | #include <securityd/SecTrustLoggingServer.h> |
866f8763 A |
62 | #include <securityd/SecRevocationServer.h> |
63 | #include <securityd/SecCertificateServer.h> | |
64 | #include <securityd/SecCertificateSource.h> | |
65 | #include <securityd/SecOCSPResponse.h> | |
427c49bc A |
66 | #include <utilities/array_size.h> |
67 | #include <utilities/SecCFWrappers.h> | |
5c19dc3a | 68 | #include <utilities/SecAppleAnchorPriv.h> |
427c49bc | 69 | #include "OTATrustUtilities.h" |
fa7225c8 A |
70 | #include "personalization.h" |
71 | #include <sys/codesign.h> | |
b1ab9ed8 | 72 | |
fa7225c8 A |
73 | #if !TARGET_OS_IPHONE |
74 | #include <Security/SecTaskPriv.h> | |
75 | #endif | |
b1ab9ed8 A |
76 | |
77 | /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */ | |
78 | #ifndef DUMP_OCSPRESPONSES | |
79 | #define DUMP_OCSPRESPONSES 0 | |
80 | #endif | |
81 | ||
82 | #if DUMP_OCSPRESPONSES | |
83 | ||
84 | #include <unistd.h> | |
85 | #include <fcntl.h> | |
86 | ||
87 | static void secdumpdata(CFDataRef data, const char *name) { | |
88 | int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0666); | |
89 | write(fd, CFDataGetBytePtr(data), CFDataGetLength(data)); | |
90 | close(fd); | |
91 | } | |
92 | ||
93 | #endif | |
94 | ||
427c49bc | 95 | |
b1ab9ed8 A |
96 | /******************************************************** |
97 | ****************** SecPolicy object ******************** | |
98 | ********************************************************/ | |
99 | ||
866f8763 A |
100 | static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix); |
101 | static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc); | |
102 | static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc); | |
103 | ||
b1ab9ed8 A |
104 | static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL; |
105 | static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL; | |
b1ab9ed8 | 106 | |
427c49bc A |
107 | static CFArrayRef SecPolicyAnchorDigestsForEVPolicy(const DERItem *policyOID) |
108 | { | |
109 | CFArrayRef result = NULL; | |
110 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
111 | if (NULL == otapkiRef) | |
112 | { | |
113 | return result; | |
114 | } | |
d8f41ccd | 115 | |
427c49bc A |
116 | CFDictionaryRef evToPolicyAnchorDigest = SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef); |
117 | CFRelease(otapkiRef); | |
d8f41ccd | 118 | |
427c49bc A |
119 | if (NULL == evToPolicyAnchorDigest) |
120 | { | |
121 | return result; | |
b1ab9ed8 | 122 | } |
b1ab9ed8 | 123 | |
b1ab9ed8 | 124 | CFArrayRef roots = NULL; |
427c49bc | 125 | CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, policyOID); |
d8f41ccd | 126 | if (oid && evToPolicyAnchorDigest) |
427c49bc A |
127 | { |
128 | result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid); | |
d8f41ccd | 129 | if (roots && CFGetTypeID(result) != CFArrayGetTypeID()) |
427c49bc | 130 | { |
fa7225c8 | 131 | secerror("EVRoot.plist has non array value"); |
427c49bc | 132 | result = NULL; |
b1ab9ed8 A |
133 | } |
134 | CFRelease(oid); | |
135 | } | |
d8f41ccd | 136 | CFReleaseSafe(evToPolicyAnchorDigest); |
427c49bc | 137 | return result; |
b1ab9ed8 A |
138 | } |
139 | ||
140 | ||
866f8763 | 141 | bool SecPolicyIsEVPolicy(const DERItem *policyOID) { |
b1ab9ed8 A |
142 | return SecPolicyAnchorDigestsForEVPolicy(policyOID); |
143 | } | |
144 | ||
145 | static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate, | |
146 | policy_set_t valid_policies) { | |
fa7225c8 A |
147 | CFDictionaryRef keySizes = NULL; |
148 | CFNumberRef rsaSize = NULL, ecSize = NULL; | |
149 | bool isEV = false; | |
b1ab9ed8 A |
150 | /* Ensure that this certificate is a valid anchor for one of the |
151 | certificate policy oids specified in the leaf. */ | |
152 | CFDataRef digest = SecCertificateGetSHA1Digest(certificate); | |
153 | policy_set_t ix; | |
154 | bool good_ev_anchor = false; | |
155 | for (ix = valid_policies; ix; ix = ix->oid_next) { | |
156 | CFArrayRef digests = SecPolicyAnchorDigestsForEVPolicy(&ix->oid); | |
157 | if (digests && CFArrayContainsValue(digests, | |
158 | CFRangeMake(0, CFArrayGetCount(digests)), digest)) { | |
159 | secdebug("ev", "found anchor for policy oid"); | |
160 | good_ev_anchor = true; | |
161 | break; | |
162 | } | |
163 | } | |
fa7225c8 | 164 | require_action_quiet(good_ev_anchor, notEV, secnotice("ev", "anchor not in plist")); |
b1ab9ed8 A |
165 | |
166 | CFAbsoluteTime october2006 = 178761600; | |
fa7225c8 A |
167 | if (SecCertificateNotValidBefore(certificate) >= october2006) { |
168 | require_action_quiet(SecCertificateVersion(certificate) >= 3, notEV, | |
169 | secnotice("ev", "Anchor issued after October 2006 and is not v3")); | |
170 | } | |
b1ab9ed8 A |
171 | if (SecCertificateVersion(certificate) >= 3 |
172 | && SecCertificateNotValidBefore(certificate) >= october2006) { | |
173 | const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate); | |
fa7225c8 A |
174 | require_action_quiet(bc && bc->isCA == true, notEV, |
175 | secnotice("ev", "Anchor has invalid basic constraints")); | |
b1ab9ed8 | 176 | SecKeyUsage ku = SecCertificateGetKeyUsage(certificate); |
fa7225c8 A |
177 | require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) |
178 | == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV, | |
179 | secnotice("ev", "Anchor has invalid key usage %u", ku)); | |
b1ab9ed8 A |
180 | } |
181 | ||
fa7225c8 A |
182 | /* At least RSA 2048 or ECC NIST P-256. */ |
183 | require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV); | |
184 | require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV); | |
185 | const void *keys[] = { kSecAttrKeyTypeRSA, kSecAttrKeyTypeEC }; | |
186 | const void *values[] = { rsaSize, ecSize }; | |
187 | require_quiet(keySizes = CFDictionaryCreate(NULL, keys, values, 2, | |
188 | &kCFTypeDictionaryKeyCallBacks, | |
189 | &kCFTypeDictionaryValueCallBacks), notEV); | |
190 | require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV, | |
191 | secnotice("ev", "Anchor's public key is too weak for EV")); | |
192 | ||
193 | isEV = true; | |
b1ab9ed8 | 194 | |
b1ab9ed8 | 195 | notEV: |
fa7225c8 A |
196 | CFReleaseNull(rsaSize); |
197 | CFReleaseNull(ecSize); | |
198 | CFReleaseNull(keySizes); | |
199 | return isEV; | |
b1ab9ed8 A |
200 | } |
201 | ||
202 | static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate) { | |
fa7225c8 A |
203 | CFMutableDictionaryRef keySizes = NULL; |
204 | CFNumberRef rsaSize = NULL, ecSize = NULL; | |
205 | bool isEV = false; | |
206 | ||
b1ab9ed8 A |
207 | const SecCECertificatePolicies *cp; |
208 | cp = SecCertificateGetCertificatePolicies(certificate); | |
fa7225c8 A |
209 | require_action_quiet(cp && cp->numPolicies > 0, notEV, |
210 | secnotice("ev", "SubCA missing certificate policies")); | |
b1ab9ed8 | 211 | CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate); |
fa7225c8 A |
212 | require_action_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV, |
213 | secnotice("ev", "SubCA missing CRLDP")); | |
b1ab9ed8 | 214 | const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate); |
fa7225c8 A |
215 | require_action_quiet(bc && bc->isCA == true, notEV, |
216 | secnotice("ev", "SubCA has invalid basic constraints")); | |
b1ab9ed8 | 217 | SecKeyUsage ku = SecCertificateGetKeyUsage(certificate); |
fa7225c8 A |
218 | require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) |
219 | == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV, | |
220 | secnotice("ev", "SubCA has invalid key usage %u", ku)); | |
221 | ||
222 | /* 6.1.5 Key Sizes */ | |
b1ab9ed8 | 223 | CFAbsoluteTime jan2011 = 315532800; |
fa7225c8 A |
224 | CFAbsoluteTime jan2014 = 410227200; |
225 | require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV); | |
226 | require_quiet(keySizes = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, | |
227 | &kCFTypeDictionaryValueCallBacks), notEV); | |
228 | CFDictionaryAddValue(keySizes, kSecAttrKeyTypeEC, ecSize); | |
229 | if (SecCertificateNotValidBefore(certificate) < jan2011 || | |
230 | SecCertificateNotValidAfter(certificate) < jan2014) { | |
231 | /* At least RSA 1024 or ECC NIST P-256. */ | |
232 | require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 1024), notEV); | |
233 | CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize); | |
234 | require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV, | |
235 | secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014")); | |
b1ab9ed8 | 236 | } else { |
fa7225c8 A |
237 | /* At least RSA 2028 or ECC NIST P-256. */ |
238 | require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV); | |
239 | CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize); | |
240 | require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV, | |
241 | secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013")); | |
b1ab9ed8 A |
242 | } |
243 | ||
fa7225c8 A |
244 | /* 7.1.3 Algorithm Object Identifiers */ |
245 | CFAbsoluteTime jan2016 = 473299200; | |
246 | if (SecCertificateNotValidBefore(certificate) > jan2016) { | |
247 | /* SHA-2 only */ | |
248 | require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1, | |
249 | notEV, secnotice("ev", "SubCA was issued with SHA-1 after 2015")); | |
250 | } | |
251 | ||
252 | isEV = true; | |
253 | ||
b1ab9ed8 | 254 | notEV: |
fa7225c8 A |
255 | CFReleaseNull(rsaSize); |
256 | CFReleaseNull(ecSize); | |
257 | CFReleaseNull(keySizes); | |
258 | return isEV; | |
b1ab9ed8 A |
259 | } |
260 | ||
b1ab9ed8 A |
261 | /******************************************************** |
262 | **************** SecPolicy Callbacks ******************* | |
263 | ********************************************************/ | |
264 | static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc, | |
265 | CFStringRef key) { | |
266 | } | |
267 | ||
268 | static void SecPolicyCheckIdLinkage(SecPVCRef pvc, | |
269 | CFStringRef key) { | |
270 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
271 | CFDataRef parentSubjectKeyID = NULL; | |
272 | for (ix = count - 1; ix >= 0; --ix) { | |
273 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
274 | /* If the previous certificate in the chain had a SubjectKeyID, | |
275 | make sure it matches the current certificates AuthorityKeyID. */ | |
276 | if (parentSubjectKeyID) { | |
277 | /* @@@ According to RFC 2459 neither AuthorityKeyID nor | |
278 | SubjectKeyID can be critical. Currenty we don't check | |
279 | for this. */ | |
280 | CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyID(cert); | |
281 | if (authorityKeyID) { | |
282 | if (!CFEqual(parentSubjectKeyID, authorityKeyID)) { | |
283 | /* AuthorityKeyID doesn't match issuers SubjectKeyID. */ | |
284 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
285 | return; | |
286 | } | |
287 | } | |
288 | } | |
289 | ||
290 | parentSubjectKeyID = SecCertificateGetSubjectKeyID(cert); | |
291 | } | |
292 | } | |
293 | ||
b1ab9ed8 A |
294 | static void SecPolicyCheckKeyUsage(SecPVCRef pvc, |
295 | CFStringRef key) { | |
296 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
b1ab9ed8 A |
297 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
298 | CFTypeRef xku = CFDictionaryGetValue(policy->_options, key); | |
fa7225c8 | 299 | if (!SecPolicyCheckCertKeyUsage(leaf, xku)) { |
b1ab9ed8 A |
300 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); |
301 | } | |
302 | } | |
303 | ||
b1ab9ed8 A |
304 | /* AUDIT[securityd](done): |
305 | policy->_options is a caller provided dictionary, only its cf type has | |
306 | been checked. | |
307 | */ | |
308 | static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc, CFStringRef key) { | |
309 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
b1ab9ed8 A |
310 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
311 | CFTypeRef xeku = CFDictionaryGetValue(policy->_options, key); | |
fa7225c8 | 312 | if (!SecPolicyCheckCertExtendedKeyUsage(leaf, xeku)){ |
b1ab9ed8 A |
313 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); |
314 | } | |
315 | } | |
316 | ||
317 | #if 0 | |
318 | static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc, | |
319 | CFStringRef key, bool strict) { | |
320 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
321 | for (ix = 0; ix < count; ++ix) { | |
322 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
323 | const SecCEBasicConstraints *bc = | |
324 | SecCertificateGetBasicConstraints(cert); | |
325 | if (bc) { | |
326 | if (strict) { | |
327 | if (ix == 0) { | |
328 | /* Leaf certificate has basic constraints extension. */ | |
329 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
330 | return; | |
331 | } else if (!bc->critical) { | |
332 | /* Basic constraints extension is not marked critical. */ | |
333 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
334 | return; | |
335 | } | |
336 | } | |
337 | ||
338 | if (ix > 0 || count == 1) { | |
339 | if (!bc->isCA) { | |
340 | /* Non leaf certificate marked as isCA false. */ | |
341 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
342 | return; | |
343 | } | |
344 | ||
345 | if (bc->pathLenConstraintPresent) { | |
346 | if (bc->pathLenConstraint < (uint32_t)(ix - 1)) { | |
347 | #if 0 | |
348 | /* @@@ If a self signed certificate is issued by | |
349 | another cert that is trusted, then we are supposed | |
350 | to treat the self signed cert itself as the anchor | |
351 | for path length purposes. */ | |
352 | CFIndex ssix = SecCertificatePathSelfSignedIndex(path); | |
353 | if (ssix >= 0 && ix >= ssix) { | |
354 | /* It's ok if the pathLenConstraint isn't met for | |
355 | certificates signing a self signed cert in the | |
356 | chain. */ | |
357 | } else | |
358 | #endif | |
359 | { | |
360 | /* Path Length Constraint Exceeded. */ | |
361 | if (!SecPVCSetResult(pvc, key, ix, | |
362 | kCFBooleanFalse)) | |
363 | return; | |
364 | } | |
365 | } | |
366 | } | |
367 | } | |
368 | } else if (strict && ix > 0) { | |
369 | /* In strict mode all CA certificates *MUST* have a critical | |
370 | basic constraints extension and the leaf certificate | |
371 | *MUST NOT* have a basic constraints extension. */ | |
372 | /* CA certificate is missing basicConstraints extension. */ | |
373 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
374 | return; | |
375 | } | |
376 | } | |
377 | } | |
378 | #endif | |
379 | ||
fa7225c8 | 380 | static void SecPolicyCheckBasicConstraints(SecPVCRef pvc, |
b1ab9ed8 A |
381 | CFStringRef key) { |
382 | //SecPolicyCheckBasicContraintsCommon(pvc, key, false); | |
383 | } | |
384 | ||
385 | static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc, | |
386 | CFStringRef key) { | |
387 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
866f8763 A |
388 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
389 | CFTypeRef pvcValue = CFDictionaryGetValue(policy->_options, key); | |
b1ab9ed8 A |
390 | for (ix = 0; ix < count; ++ix) { |
391 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
866f8763 A |
392 | if (!SecPolicyCheckCertNonEmptySubject(cert, pvcValue)) { |
393 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
394 | return; | |
395 | } | |
b1ab9ed8 A |
396 | } |
397 | } | |
398 | ||
399 | static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc, | |
400 | CFStringRef key) { | |
401 | } | |
402 | ||
b1ab9ed8 A |
403 | /* AUDIT[securityd](done): |
404 | policy->_options is a caller provided dictionary, only its cf type has | |
405 | been checked. | |
406 | */ | |
407 | static void SecPolicyCheckSSLHostname(SecPVCRef pvc, | |
408 | CFStringRef key) { | |
409 | /* @@@ Consider what to do if the caller passes in no hostname. Should | |
410 | we then still fail if the leaf has no dnsNames or IPAddresses at all? */ | |
411 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
412 | CFStringRef hostName = (CFStringRef) | |
413 | CFDictionaryGetValue(policy->_options, key); | |
414 | if (!isString(hostName)) { | |
415 | /* @@@ We can't return an error here and making the evaluation fail | |
416 | won't help much either. */ | |
417 | return; | |
418 | } | |
419 | ||
420 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
fa7225c8 | 421 | bool dnsMatch = SecPolicyCheckCertSSLHostname(leaf, hostName); |
b1ab9ed8 A |
422 | |
423 | if (!dnsMatch) { | |
424 | /* Hostname mismatch or no hostnames found in certificate. */ | |
425 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
d8f41ccd | 426 | } |
fa7225c8 | 427 | |
b1ab9ed8 A |
428 | } |
429 | ||
430 | /* AUDIT[securityd](done): | |
431 | policy->_options is a caller provided dictionary, only its cf type has | |
432 | been checked. | |
433 | */ | |
434 | static void SecPolicyCheckEmail(SecPVCRef pvc, CFStringRef key) { | |
435 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
436 | CFStringRef email = (CFStringRef)CFDictionaryGetValue(policy->_options, key); | |
b1ab9ed8 A |
437 | if (!isString(email)) { |
438 | /* We can't return an error here and making the evaluation fail | |
439 | won't help much either. */ | |
440 | return; | |
441 | } | |
442 | ||
443 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
b1ab9ed8 | 444 | |
fa7225c8 | 445 | if (!SecPolicyCheckCertEmail(leaf, email)) { |
b1ab9ed8 A |
446 | /* Hostname mismatch or no hostnames found in certificate. */ |
447 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
448 | } | |
449 | } | |
450 | ||
451 | static void SecPolicyCheckValidIntermediates(SecPVCRef pvc, | |
452 | CFStringRef key) { | |
453 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
454 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
455 | for (ix = 1; ix < count - 1; ++ix) { | |
456 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
457 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
458 | /* Intermediate certificate has expired. */ | |
459 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
460 | return; | |
461 | } | |
462 | } | |
463 | } | |
464 | ||
465 | static void SecPolicyCheckValidLeaf(SecPVCRef pvc, | |
466 | CFStringRef key) { | |
467 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
468 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
469 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
470 | /* Leaf certificate has expired. */ | |
471 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
472 | return; | |
473 | } | |
474 | } | |
475 | ||
476 | static void SecPolicyCheckValidRoot(SecPVCRef pvc, | |
477 | CFStringRef key) { | |
478 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
479 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
480 | ix = count - 1; | |
481 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
482 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
483 | /* Root certificate has expired. */ | |
484 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
485 | return; | |
486 | } | |
487 | } | |
488 | ||
489 | /* AUDIT[securityd](done): | |
490 | policy->_options is a caller provided dictionary, only its cf type has | |
491 | been checked. | |
492 | */ | |
493 | static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc, | |
494 | CFStringRef key) { | |
495 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
496 | if (count < 2) { | |
497 | /* Can't check intermediates common name if there is no intermediate. */ | |
498 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
499 | return; | |
500 | } | |
501 | ||
502 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 1); | |
503 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
504 | CFStringRef commonName = | |
505 | (CFStringRef)CFDictionaryGetValue(policy->_options, key); | |
506 | if (!isString(commonName)) { | |
507 | /* @@@ We can't return an error here and making the evaluation fail | |
508 | won't help much either. */ | |
509 | return; | |
510 | } | |
fa7225c8 A |
511 | if (!SecPolicyCheckCertSubjectCommonName(cert, commonName)) { |
512 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
513 | } | |
b1ab9ed8 A |
514 | } |
515 | ||
516 | /* AUDIT[securityd](done): | |
517 | policy->_options is a caller provided dictionary, only its cf type has | |
518 | been checked. | |
519 | */ | |
520 | static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc, | |
521 | CFStringRef key) { | |
522 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
523 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
524 | CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
525 | key); | |
526 | if (!isString(common_name)) { | |
527 | /* @@@ We can't return an error here and making the evaluation fail | |
528 | won't help much either. */ | |
529 | return; | |
530 | } | |
fa7225c8 | 531 | if (!SecPolicyCheckCertSubjectCommonName(cert, common_name)) { |
b1ab9ed8 | 532 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); |
fa7225c8 | 533 | } |
b1ab9ed8 A |
534 | } |
535 | ||
536 | /* AUDIT[securityd](done): | |
537 | policy->_options is a caller provided dictionary, only its cf type has | |
538 | been checked. | |
539 | */ | |
540 | static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc, | |
541 | CFStringRef key) { | |
542 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
543 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
544 | CFStringRef prefix = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
545 | key); | |
546 | if (!isString(prefix)) { | |
547 | /* @@@ We can't return an error here and making the evaluation fail | |
548 | won't help much either. */ | |
549 | return; | |
550 | } | |
fa7225c8 A |
551 | if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert, prefix)) { |
552 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
553 | } | |
b1ab9ed8 A |
554 | } |
555 | ||
556 | /* AUDIT[securityd](done): | |
557 | policy->_options is a caller provided dictionary, only its cf type has | |
558 | been checked. | |
559 | */ | |
560 | static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc, | |
561 | CFStringRef key) { | |
562 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
563 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
564 | CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
565 | key); | |
566 | if (!isString(common_name)) { | |
567 | /* @@@ We can't return an error here and making the evaluation fail | |
568 | won't help much either. */ | |
569 | return; | |
570 | } | |
fa7225c8 A |
571 | if (!SecPolicyCheckCertSubjectCommonNameTEST(cert, common_name)) { |
572 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
573 | } | |
b1ab9ed8 A |
574 | } |
575 | ||
576 | /* AUDIT[securityd](done): | |
577 | policy->_options is a caller provided dictionary, only its cf type has | |
578 | been checked. | |
579 | */ | |
580 | static void SecPolicyCheckNotValidBefore(SecPVCRef pvc, | |
581 | CFStringRef key) { | |
582 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
583 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
584 | CFDateRef date = (CFDateRef)CFDictionaryGetValue(policy->_options, key); | |
585 | if (!isDate(date)) { | |
586 | /* @@@ We can't return an error here and making the evaluation fail | |
587 | won't help much either. */ | |
588 | return; | |
589 | } | |
fa7225c8 | 590 | if (!SecPolicyCheckCertNotValidBefore(cert, date)) { |
b1ab9ed8 A |
591 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) |
592 | return; | |
593 | } | |
594 | } | |
595 | ||
596 | /* AUDIT[securityd](done): | |
597 | policy->_options is a caller provided dictionary, only its cf type has | |
598 | been checked. | |
599 | */ | |
600 | static void SecPolicyCheckChainLength(SecPVCRef pvc, | |
601 | CFStringRef key) { | |
602 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
603 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
604 | CFNumberRef chainLength = | |
605 | (CFNumberRef)CFDictionaryGetValue(policy->_options, key); | |
606 | CFIndex value; | |
607 | if (!chainLength || CFGetTypeID(chainLength) != CFNumberGetTypeID() || | |
608 | !CFNumberGetValue(chainLength, kCFNumberCFIndexType, &value)) { | |
609 | /* @@@ We can't return an error here and making the evaluation fail | |
610 | won't help much either. */ | |
611 | return; | |
612 | } | |
613 | if (value != count) { | |
614 | /* Chain length doesn't match policy requirement. */ | |
615 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
616 | return; | |
617 | } | |
618 | } | |
619 | ||
fa7225c8 A |
620 | static bool isDigestInPolicy(SecPVCRef pvc, CFStringRef key, CFDataRef digest) { |
621 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
427c49bc | 622 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); |
427c49bc A |
623 | |
624 | bool foundMatch = false; | |
427c49bc | 625 | if (isData(value)) |
fa7225c8 | 626 | foundMatch = CFEqual(digest, value); |
427c49bc | 627 | else if (isArray(value)) |
fa7225c8 | 628 | foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), digest); |
427c49bc A |
629 | else { |
630 | /* @@@ We only support Data and Array but we can't return an error here so. | |
fa7225c8 | 631 | we let the evaluation fail (not much help) and assert in debug. */ |
427c49bc A |
632 | assert(false); |
633 | } | |
634 | ||
fa7225c8 A |
635 | return foundMatch; |
636 | } | |
637 | ||
638 | static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc, CFStringRef key) { | |
639 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
640 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1); | |
641 | CFDataRef anchorSHA256 = NULL; | |
642 | anchorSHA256 = SecCertificateCopySHA256Digest(cert); | |
643 | ||
644 | if (!isDigestInPolicy(pvc, key, anchorSHA256)) { | |
645 | SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA256, count-1, kCFBooleanFalse); | |
646 | } | |
647 | ||
648 | CFReleaseNull(anchorSHA256); | |
649 | return; | |
650 | } | |
651 | ||
652 | ||
653 | /* AUDIT[securityd](done): | |
654 | policy->_options is a caller provided dictionary, only its cf type has | |
655 | been checked. | |
656 | */ | |
657 | static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc, | |
658 | CFStringRef key) { | |
659 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
660 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1); | |
661 | CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert); | |
662 | ||
663 | if (!isDigestInPolicy(pvc, key, anchorSHA1)) | |
664 | if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, count-1, kCFBooleanFalse)) | |
427c49bc A |
665 | return; |
666 | ||
667 | return; | |
b1ab9ed8 A |
668 | } |
669 | ||
5c19dc3a A |
670 | /* |
671 | Check the SHA256 of SPKI of the first intermediate CA certificate in the path | |
672 | policy->_options is a caller provided dictionary, only its cf type has | |
673 | been checked. | |
674 | */ | |
675 | static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc, | |
676 | CFStringRef key) { | |
5c19dc3a A |
677 | SecCertificateRef cert = NULL; |
678 | CFDataRef digest = NULL; | |
5c19dc3a A |
679 | |
680 | if (SecPVCGetCertificateCount(pvc) < 2) { | |
681 | SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 0, kCFBooleanFalse); | |
682 | return; | |
683 | } | |
684 | ||
685 | cert = SecPVCGetCertificateAtIndex(pvc, 1); | |
686 | digest = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert); | |
687 | ||
fa7225c8 A |
688 | if (!isDigestInPolicy(pvc, key, digest)) { |
689 | SecPVCSetResult(pvc, kSecPolicyCheckIntermediateSPKISHA256, 1, kCFBooleanFalse); | |
5c19dc3a | 690 | } |
5c19dc3a | 691 | CFReleaseNull(digest); |
5c19dc3a A |
692 | } |
693 | ||
694 | /* | |
695 | policy->_options is a caller provided dictionary, only its cf type has | |
696 | been checked. | |
697 | */ | |
698 | static void SecPolicyCheckAnchorApple(SecPVCRef pvc, | |
699 | CFStringRef key) { | |
700 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
701 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1); | |
5c19dc3a A |
702 | SecAppleTrustAnchorFlags flags = 0; |
703 | ||
5c19dc3a A |
704 | |
705 | bool foundMatch = SecIsAppleTrustAnchor(cert, flags); | |
706 | ||
707 | if (!foundMatch) | |
708 | if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorApple, 0, kCFBooleanFalse)) | |
709 | return; | |
e0e0d90e | 710 | |
5c19dc3a A |
711 | return; |
712 | } | |
713 | ||
714 | ||
b1ab9ed8 A |
715 | /* AUDIT[securityd](done): |
716 | policy->_options is a caller provided dictionary, only its cf type has | |
717 | been checked. | |
718 | */ | |
719 | static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc, | |
720 | CFStringRef key) { | |
721 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
722 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
723 | CFStringRef org = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
724 | key); | |
725 | if (!isString(org)) { | |
726 | /* @@@ We can't return an error here and making the evaluation fail | |
727 | won't help much either. */ | |
728 | return; | |
729 | } | |
fa7225c8 | 730 | if (!SecPolicyCheckCertSubjectOrganization(cert, org)) { |
b1ab9ed8 A |
731 | /* Leaf Subject Organization mismatch. */ |
732 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
733 | } | |
b1ab9ed8 A |
734 | } |
735 | ||
427c49bc A |
736 | static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc, |
737 | CFStringRef key) { | |
738 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
739 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
740 | CFStringRef orgUnit = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
741 | key); | |
742 | if (!isString(orgUnit)) { | |
743 | /* @@@ We can't return an error here and making the evaluation fail | |
744 | won't help much either. */ | |
745 | return; | |
746 | } | |
fa7225c8 A |
747 | if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert, orgUnit)) { |
748 | /* Leaf Subject Organization mismatch. */ | |
749 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
750 | } | |
427c49bc A |
751 | } |
752 | ||
b1ab9ed8 A |
753 | /* AUDIT[securityd](done): |
754 | policy->_options is a caller provided dictionary, only its cf type has | |
755 | been checked. | |
756 | */ | |
757 | static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc, | |
758 | CFStringRef key) { | |
759 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
760 | CFArrayRef trustedServerNames = (CFArrayRef) | |
761 | CFDictionaryGetValue(policy->_options, key); | |
762 | /* No names specified means we accept any name. */ | |
763 | if (!trustedServerNames) | |
764 | return; | |
765 | if (!isArray(trustedServerNames)) { | |
766 | /* @@@ We can't return an error here and making the evaluation fail | |
767 | won't help much either. */ | |
768 | return; | |
769 | } | |
770 | ||
b1ab9ed8 | 771 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); |
fa7225c8 | 772 | if (!SecPolicyCheckCertEAPTrustedServerNames(leaf, trustedServerNames)) { |
b1ab9ed8 A |
773 | /* Hostname mismatch or no hostnames found in certificate. */ |
774 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
775 | } | |
776 | } | |
777 | ||
427c49bc | 778 | static const unsigned char UTN_USERFirst_Hardware_Serial[][16] = { |
b1ab9ed8 A |
779 | { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 }, |
780 | { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 }, | |
781 | { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 }, | |
782 | { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 }, | |
783 | { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 }, | |
784 | { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 }, | |
785 | { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 }, | |
786 | { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e }, | |
787 | { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } }; | |
788 | ||
789 | static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer[] = { | |
790 | 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, | |
791 | 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, | |
792 | 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, | |
793 | 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43, | |
794 | 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, | |
795 | 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, | |
796 | 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31, | |
797 | 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54, | |
798 | 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45, | |
799 | 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f, | |
800 | 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e, | |
801 | 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48, | |
802 | 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45 | |
803 | }; | |
804 | static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len = 151; | |
805 | ||
806 | ||
807 | static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc, | |
808 | CFStringRef key) { | |
809 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
810 | CFDataRef issuer = cert ? SecCertificateGetNormalizedIssuerContent(cert) : NULL; | |
811 | ||
812 | if (issuer && (CFDataGetLength(issuer) == (CFIndex)UTN_USERFirst_Hardware_Normalized_Issuer_len) && | |
813 | (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer, CFDataGetBytePtr(issuer), | |
814 | UTN_USERFirst_Hardware_Normalized_Issuer_len))) | |
815 | { | |
5c19dc3a A |
816 | #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) |
817 | CFDataRef serial = SecCertificateCopySerialNumber(cert, NULL); | |
818 | #else | |
b1ab9ed8 | 819 | CFDataRef serial = SecCertificateCopySerialNumber(cert); |
5c19dc3a A |
820 | #endif |
821 | ||
b1ab9ed8 A |
822 | if (serial) { |
823 | CFIndex serial_length = CFDataGetLength(serial); | |
824 | const uint8_t *serial_ptr = CFDataGetBytePtr(serial); | |
825 | ||
826 | while ((serial_length > 0) && (*serial_ptr == 0)) { | |
827 | serial_ptr++; | |
828 | serial_length--; | |
829 | } | |
830 | ||
831 | if (serial_length == (CFIndex)sizeof(*UTN_USERFirst_Hardware_Serial)) { | |
832 | unsigned int i; | |
427c49bc | 833 | for (i = 0; i < array_size(UTN_USERFirst_Hardware_Serial); i++) |
b1ab9ed8 A |
834 | { |
835 | if (0 == memcmp(UTN_USERFirst_Hardware_Serial[i], | |
836 | serial_ptr, sizeof(*UTN_USERFirst_Hardware_Serial))) | |
837 | { | |
838 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
866f8763 | 839 | pvc->result = kSecTrustResultFatalTrustFailure; |
427c49bc | 840 | CFReleaseSafe(serial); |
b1ab9ed8 A |
841 | return; |
842 | } | |
843 | } | |
844 | } | |
845 | CFRelease(serial); | |
846 | } | |
847 | } | |
427c49bc A |
848 | |
849 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
850 | if (NULL != otapkiRef) | |
851 | { | |
852 | CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef); | |
853 | CFRelease(otapkiRef); | |
854 | if (NULL != blackListedKeys) | |
855 | { | |
856 | /* Check for blacklisted intermediates keys. */ | |
857 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
d8f41ccd | 858 | if (dgst) |
427c49bc A |
859 | { |
860 | /* Check dgst against blacklist. */ | |
d8f41ccd | 861 | if (CFSetContainsValue(blackListedKeys, dgst)) |
427c49bc A |
862 | { |
863 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
866f8763 | 864 | pvc->result = kSecTrustResultFatalTrustFailure; |
427c49bc A |
865 | } |
866 | CFRelease(dgst); | |
867 | } | |
868 | CFRelease(blackListedKeys); | |
869 | } | |
870 | } | |
b1ab9ed8 A |
871 | } |
872 | ||
427c49bc A |
873 | static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc, CFStringRef key) |
874 | { | |
875 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
876 | if (NULL != otapkiRef) | |
877 | { | |
878 | CFSetRef grayListedKeys = SecOTAPKICopyGrayList(otapkiRef); | |
879 | CFRelease(otapkiRef); | |
880 | if (NULL != grayListedKeys) | |
881 | { | |
882 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
883 | ||
884 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
d8f41ccd | 885 | if (dgst) |
427c49bc A |
886 | { |
887 | /* Check dgst against gray. */ | |
d8f41ccd | 888 | if (CFSetContainsValue(grayListedKeys, dgst)) |
427c49bc A |
889 | { |
890 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
891 | } | |
892 | CFRelease(dgst); | |
893 | } | |
894 | CFRelease(grayListedKeys); | |
895 | } | |
896 | } | |
fa7225c8 | 897 | } |
427c49bc | 898 | |
b1ab9ed8 A |
899 | static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc, CFStringRef key) |
900 | { | |
901 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
902 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
903 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
427c49bc | 904 | |
fa7225c8 A |
905 | if (!SecPolicyCheckCertLeafMarkerOid(cert, value)) { |
906 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
907 | } | |
908 | } | |
b1ab9ed8 | 909 | |
fa7225c8 A |
910 | static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc, CFStringRef key) |
911 | { | |
912 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
913 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
914 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
915 | ||
916 | if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert, value)) { | |
917 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
918 | } | |
b1ab9ed8 A |
919 | } |
920 | ||
6b200bc3 A |
921 | /* |
922 | * The value is a dictionary. The dictionary contains keys indicating | |
923 | * whether the value is for Prod or QA. The values are the same as | |
924 | * in the options dictionary for SecPolicyCheckLeafMarkerOid. | |
925 | */ | |
926 | static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc, CFStringRef key) | |
927 | { | |
928 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
929 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
930 | CFDictionaryRef value = CFDictionaryGetValue(policy->_options, key); | |
931 | CFTypeRef prodValue = CFDictionaryGetValue(value, kSecPolicyLeafMarkerProd); | |
932 | ||
933 | if (!SecPolicyCheckCertLeafMarkerOid(cert, prodValue)) { | |
934 | bool result = false; | |
935 | if (!result) { | |
936 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
937 | } | |
938 | } | |
939 | } | |
940 | ||
b1ab9ed8 A |
941 | static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key) |
942 | { | |
943 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
944 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
945 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
946 | ||
947 | for (ix = 1; ix < count - 1; ix++) { | |
948 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
949 | if (SecCertificateHasMarkerExtension(cert, value)) | |
950 | return; | |
951 | } | |
952 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
953 | } | |
954 | ||
fa7225c8 A |
955 | static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc, CFStringRef key) |
956 | { | |
957 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
958 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
959 | CFTypeRef peku = CFDictionaryGetValue(policy->_options, key); | |
960 | ||
961 | for (ix = 1; ix < count - 1; ix++) { | |
962 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
963 | if (!SecPolicyCheckCertExtendedKeyUsage(cert, peku)) { | |
964 | SecPVCSetResult(pvc, key, ix, kCFBooleanFalse); | |
965 | } | |
966 | } | |
967 | } | |
968 | ||
6b200bc3 A |
969 | static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc, CFStringRef key) |
970 | { | |
971 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
972 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
973 | CFTypeRef organization = CFDictionaryGetValue(policy->_options, key); | |
974 | ||
975 | for (ix = 1; ix < count - 1; ix++) { | |
976 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
977 | if (!SecPolicyCheckCertSubjectOrganization(cert, organization)) { | |
978 | SecPVCSetResult(pvc, key, ix, kCFBooleanFalse); | |
979 | } | |
980 | } | |
981 | } | |
982 | ||
983 | static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc, CFStringRef key) | |
984 | { | |
985 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
986 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
987 | CFTypeRef country = CFDictionaryGetValue(policy->_options, key); | |
988 | ||
989 | for (ix = 1; ix < count - 1; ix++) { | |
990 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
991 | if (!SecPolicyCheckCertSubjectCountry(cert, country)) { | |
992 | SecPVCSetResult(pvc, key, ix, kCFBooleanFalse); | |
993 | } | |
994 | } | |
995 | } | |
996 | ||
b1ab9ed8 A |
997 | /**************************************************************************** |
998 | *********************** New rfc5280 Chain Validation *********************** | |
999 | ****************************************************************************/ | |
1000 | ||
6b200bc3 A |
1001 | #define POLICY_MAPPING 1 |
1002 | #define POLICY_SUBTREES 1 | |
b1ab9ed8 | 1003 | |
b1ab9ed8 A |
1004 | /* rfc5280 basic cert processing. */ |
1005 | static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc, | |
1006 | CFStringRef key) { | |
1007 | /* Inputs */ | |
1008 | //cert_path_t path; | |
1009 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
866f8763 | 1010 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
b1ab9ed8 A |
1011 | /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */ |
1012 | assert((unsigned long)count<=UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */ | |
1013 | uint32_t n = (uint32_t)count; | |
866f8763 A |
1014 | |
1015 | bool is_anchored = SecPathBuilderIsAnchored(pvc->builder); | |
1016 | bool is_anchor_trusted = false; | |
b1ab9ed8 | 1017 | if (is_anchored) { |
866f8763 A |
1018 | CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, n - 1); |
1019 | if (CFArrayGetCount(constraints) == 0) { | |
1020 | /* Given that the path builder has already indicated the last cert in this chain has | |
1021 | * trust set on it, empty constraints means trusted. */ | |
1022 | is_anchor_trusted = true; | |
1023 | } else { | |
1024 | /* Determine whether constraints say to trust this cert for this PVC. */ | |
1025 | SecTrustSettingsResult tsResult = SecPVCGetTrustSettingsResult(pvc, SecCertificatePathVCGetCertificateAtIndex(path, n - 1), | |
1026 | constraints); | |
1027 | if (tsResult == kSecTrustSettingsResultTrustRoot || tsResult == kSecTrustSettingsResultTrustAsRoot) { | |
1028 | is_anchor_trusted = true; | |
1029 | } | |
1030 | } | |
1031 | } | |
1032 | ||
1033 | if (is_anchor_trusted) { | |
fa7225c8 | 1034 | /* If the anchor is trusted we don't process the last cert in the |
b1ab9ed8 A |
1035 | chain (root). */ |
1036 | n--; | |
1037 | } else { | |
866f8763 A |
1038 | /* trust may be restored for a path with an untrusted root that matches the allow list. |
1039 | (isAllowlisted is set by revocation check, which is performed prior to path checks) */ | |
1040 | if (!SecCertificatePathVCIsAllowlisted(path)) { | |
5c19dc3a | 1041 | /* Add a detail for the root not being trusted. */ |
6b200bc3 | 1042 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckAnchorTrusted, |
866f8763 | 1043 | n - 1, kCFBooleanFalse, true)) { |
5c19dc3a | 1044 | return; |
866f8763 | 1045 | } |
5c19dc3a | 1046 | } |
b1ab9ed8 A |
1047 | } |
1048 | ||
1049 | CFAbsoluteTime verify_time = SecPVCGetVerifyTime(pvc); | |
1050 | //policy_set_t user_initial_policy_set = NULL; | |
1051 | //trust_anchor_t anchor; | |
b1ab9ed8 A |
1052 | |
1053 | /* Initialization */ | |
b1ab9ed8 | 1054 | #if POLICY_SUBTREES |
5c19dc3a A |
1055 | CFMutableArrayRef permitted_subtrees = NULL; |
1056 | CFMutableArrayRef excluded_subtrees = NULL; | |
1057 | permitted_subtrees = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
1058 | excluded_subtrees = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
fa7225c8 A |
1059 | require_action_quiet(permitted_subtrees != NULL, errOut, |
1060 | SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true)); | |
1061 | require_action_quiet(excluded_subtrees != NULL, errOut, | |
1062 | SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true)); | |
b1ab9ed8 | 1063 | #endif |
866f8763 A |
1064 | |
1065 | if (!SecCertificatePathVCVerifyPolicyTree(path, is_anchor_trusted)) { | |
1066 | if (!SecPVCSetResultForced(pvc, key, 0, kCFBooleanFalse, true)) { | |
1067 | goto errOut; | |
1068 | } | |
1069 | } | |
b1ab9ed8 A |
1070 | |
1071 | #if 0 | |
1072 | /* Path builder ensures we only get cert chains with proper issuer | |
1073 | chaining with valid signatures along the way. */ | |
1074 | algorithm_id_t working_public_key_algorithm = anchor->public_key_algorithm; | |
1075 | SecKeyRef working_public_key = anchor->public_key; | |
1076 | x500_name_t working_issuer_name = anchor->issuer_name; | |
1077 | #endif | |
1078 | uint32_t i, max_path_length = n; | |
1079 | SecCertificateRef cert = NULL; | |
1080 | for (i = 1; i <= n; ++i) { | |
1081 | /* Process Cert */ | |
1082 | cert = SecPVCGetCertificateAtIndex(pvc, n - i); | |
866f8763 | 1083 | bool is_self_issued = SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc->builder), n - i); |
b1ab9ed8 A |
1084 | |
1085 | /* (a) Verify the basic certificate information. */ | |
1086 | /* @@@ Ensure that cert was signed with working_public_key_algorithm | |
1087 | using the working_public_key and the working_public_key_parameters. */ | |
1088 | #if 1 | |
1089 | /* Already done by chain builder. */ | |
1090 | if (!SecCertificateIsValid(cert, verify_time)) { | |
1091 | CFStringRef fail_key = i == n ? kSecPolicyCheckValidLeaf : kSecPolicyCheckValidIntermediates; | |
fa7225c8 A |
1092 | if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) { |
1093 | goto errOut; | |
1094 | } | |
b1ab9ed8 | 1095 | } |
3a7be6fd | 1096 | if (SecCertificateIsWeakKey(cert)) { |
e3d460c9 | 1097 | CFStringRef fail_key = i == n ? kSecPolicyCheckWeakLeaf : kSecPolicyCheckWeakIntermediates; |
fa7225c8 A |
1098 | if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) { |
1099 | goto errOut; | |
1100 | } | |
866f8763 | 1101 | pvc->result = kSecTrustResultFatalTrustFailure; |
b1ab9ed8 A |
1102 | } |
1103 | #endif | |
1104 | /* @@@ cert.issuer == working_issuer_name. */ | |
1105 | ||
1106 | #if POLICY_SUBTREES | |
1107 | /* (b) (c) */ | |
1108 | if (!is_self_issued || i == n) { | |
5c19dc3a A |
1109 | bool found = false; |
1110 | /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */ | |
1111 | if(excluded_subtrees && CFArrayGetCount(excluded_subtrees)) { | |
e0e0d90e | 1112 | if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, excluded_subtrees, &found, false)) || found) { |
fa7225c8 A |
1113 | secnotice("policy", "name in excluded subtrees"); |
1114 | if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; } | |
5c19dc3a A |
1115 | } |
1116 | } | |
1117 | /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */ | |
1118 | if(permitted_subtrees && CFArrayGetCount(permitted_subtrees)) { | |
e0e0d90e | 1119 | if ((errSecSuccess != SecNameContraintsMatchSubtrees(cert, permitted_subtrees, &found, true)) || !found) { |
fa7225c8 A |
1120 | secnotice("policy", "name not in permitted subtrees"); |
1121 | if(!SecPVCSetResultForced(pvc, key, n - i, kCFBooleanFalse, true)) { goto errOut; } | |
5c19dc3a A |
1122 | } |
1123 | } | |
b1ab9ed8 A |
1124 | } |
1125 | #endif | |
866f8763 A |
1126 | /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */ |
1127 | ||
b1ab9ed8 A |
1128 | /* If Last Cert in Path */ |
1129 | if (i == n) | |
1130 | break; | |
1131 | ||
1132 | /* Prepare for Next Cert */ | |
866f8763 A |
1133 | /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */ |
1134 | /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */ | |
b1ab9ed8 A |
1135 | //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert); |
1136 | //working_public_key = SecCertificateCopyPublicKey(cert); | |
1137 | //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert); | |
1138 | //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert); | |
1139 | #if POLICY_SUBTREES | |
5c19dc3a A |
1140 | /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables. |
1141 | */ | |
1142 | CFArrayRef permitted_subtrees_in_cert = SecCertificateGetPermittedSubtrees(cert); | |
1143 | if (permitted_subtrees_in_cert) { | |
1144 | SecNameConstraintsIntersectSubtrees(permitted_subtrees, permitted_subtrees_in_cert); | |
1145 | } | |
1146 | ||
1147 | // could do something smart here to avoid inserting the exact same constraint | |
1148 | CFArrayRef excluded_subtrees_in_cert = SecCertificateGetExcludedSubtrees(cert); | |
1149 | if (excluded_subtrees_in_cert) { | |
1150 | CFIndex num_trees = CFArrayGetCount(excluded_subtrees_in_cert); | |
1151 | CFRange range = { 0, num_trees }; | |
1152 | CFArrayAppendArray(excluded_subtrees, excluded_subtrees_in_cert, range); | |
1153 | } | |
b1ab9ed8 | 1154 | #endif |
866f8763 A |
1155 | /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */ |
1156 | ||
b1ab9ed8 A |
1157 | /* (k) */ |
1158 | const SecCEBasicConstraints *bc = | |
1159 | SecCertificateGetBasicConstraints(cert); | |
866f8763 | 1160 | #if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */ |
b1ab9ed8 A |
1161 | if (!bc || !bc->isCA) { |
1162 | /* Basic constraints not present or not marked as isCA, illegal. */ | |
fa7225c8 A |
1163 | if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints, |
1164 | n - i, kCFBooleanFalse)) { | |
1165 | goto errOut; | |
1166 | } | |
b1ab9ed8 A |
1167 | } |
1168 | #endif | |
1169 | /* (l) */ | |
1170 | if (!is_self_issued) { | |
1171 | if (max_path_length > 0) { | |
1172 | max_path_length--; | |
1173 | } else { | |
1174 | /* max_path_len exceeded, illegal. */ | |
fa7225c8 A |
1175 | if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicConstraints, |
1176 | n - i, kCFBooleanFalse)) { | |
1177 | goto errOut; | |
1178 | } | |
b1ab9ed8 A |
1179 | } |
1180 | } | |
1181 | /* (m) */ | |
1182 | if (bc && bc->pathLenConstraintPresent | |
1183 | && bc->pathLenConstraint < max_path_length) { | |
1184 | max_path_length = bc->pathLenConstraint; | |
1185 | } | |
866f8763 | 1186 | #if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */ |
b1ab9ed8 A |
1187 | /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */ |
1188 | SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert); | |
1189 | if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) { | |
1190 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage, | |
fa7225c8 A |
1191 | n - i, kCFBooleanFalse, true)) { |
1192 | goto errOut; | |
1193 | } | |
b1ab9ed8 A |
1194 | } |
1195 | #endif | |
1196 | /* (o) Recognize and process any other critical extension present in the certificate. Process any other recognized non-critical extension present in the certificate that is relevant to path processing. */ | |
1197 | if (SecCertificateHasUnknownCriticalExtension(cert)) { | |
1198 | /* Certificate contains one or more unknown critical extensions. */ | |
1199 | if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions, | |
fa7225c8 A |
1200 | n - i, kCFBooleanFalse)) { |
1201 | goto errOut; | |
1202 | } | |
b1ab9ed8 | 1203 | } |
5c19dc3a | 1204 | } /* end loop over certs in path */ |
b1ab9ed8 | 1205 | /* Wrap up */ |
866f8763 | 1206 | /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */ |
b1ab9ed8 A |
1207 | /* (c) */ |
1208 | //working_public_key = SecCertificateCopyPublicKey(cert); | |
1209 | /* (d) */ | |
1210 | /* If the subjectPublicKeyInfo field of the certificate contains an algorithm field with null parameters or parameters are omitted, compare the certificate subjectPublicKey algorithm to the working_public_key_algorithm. If the certificate subjectPublicKey algorithm and the | |
1211 | working_public_key_algorithm are different, set the working_public_key_parameters to null. */ | |
1212 | //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert); | |
1213 | /* (e) */ | |
1214 | //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert); | |
1215 | /* (f) Recognize and process any other critical extension present in the certificate n. Process any other recognized non-critical extension present in certificate n that is relevant to path processing. */ | |
1216 | if (SecCertificateHasUnknownCriticalExtension(cert)) { | |
1217 | /* Certificate contains one or more unknown critical extensions. */ | |
1218 | if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions, | |
fa7225c8 A |
1219 | 0, kCFBooleanFalse)) { |
1220 | goto errOut; | |
1221 | } | |
b1ab9ed8 | 1222 | } |
866f8763 | 1223 | /* (g) done by SecCertificatePathVCVerifyPolicyTree */ |
5c19dc3a | 1224 | |
fa7225c8 | 1225 | errOut: |
5c19dc3a A |
1226 | CFReleaseNull(permitted_subtrees); |
1227 | CFReleaseNull(excluded_subtrees); | |
b1ab9ed8 A |
1228 | } |
1229 | ||
1230 | static policy_set_t policies_for_cert(SecCertificateRef cert) { | |
1231 | policy_set_t policies = NULL; | |
1232 | const SecCECertificatePolicies *cp = | |
1233 | SecCertificateGetCertificatePolicies(cert); | |
1234 | size_t policy_ix, policy_count = cp ? cp->numPolicies : 0; | |
1235 | for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) { | |
1236 | policy_set_add(&policies, &cp->policies[policy_ix].policyIdentifier); | |
1237 | } | |
1238 | return policies; | |
1239 | } | |
1240 | ||
1241 | static void SecPolicyCheckEV(SecPVCRef pvc, | |
1242 | CFStringRef key) { | |
1243 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1244 | policy_set_t valid_policies = NULL; | |
1245 | ||
fa7225c8 A |
1246 | /* 6.1.7. Key Usage Purposes */ |
1247 | if (count) { | |
1248 | CFAbsoluteTime jul2016 = 489024000; | |
1249 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
1250 | if (SecCertificateNotValidBefore(leaf) > jul2016 && count < 3) { | |
1251 | /* Root CAs may not sign subscriber certificates after 30 June 2016. */ | |
1252 | if (SecPVCSetResultForced(pvc, key, | |
1253 | 0, kCFBooleanFalse, true)) { | |
1254 | return; | |
1255 | } | |
1256 | } | |
1257 | } | |
1258 | ||
1259 | for (ix = 0; ix < count; ++ix) { | |
1260 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1261 | policy_set_t policies = policies_for_cert(cert); | |
1262 | if (ix == 0) { | |
1263 | /* Subscriber */ | |
b1ab9ed8 A |
1264 | /* anyPolicy in the leaf isn't allowed for EV, so only init |
1265 | valid_policies if we have real policies. */ | |
1266 | if (!policy_set_contains(policies, &oidAnyPolicy)) { | |
1267 | valid_policies = policies; | |
1268 | policies = NULL; | |
1269 | } | |
1270 | } else if (ix < count - 1) { | |
1271 | /* Subordinate CA */ | |
1272 | if (!SecPolicySubordinateCACertificateCouldBeEV(cert)) { | |
fa7225c8 | 1273 | secnotice("ev", "subordinate certificate is not ev"); |
b1ab9ed8 A |
1274 | if (SecPVCSetResultForced(pvc, key, |
1275 | ix, kCFBooleanFalse, true)) { | |
1276 | policy_set_free(valid_policies); | |
1277 | policy_set_free(policies); | |
1278 | return; | |
1279 | } | |
1280 | } | |
1281 | policy_set_intersect(&valid_policies, policies); | |
1282 | } else { | |
1283 | /* Root CA */ | |
1284 | if (!SecPolicyRootCACertificateIsEV(cert, valid_policies)) { | |
fa7225c8 | 1285 | secnotice("ev", "anchor certificate is not ev"); |
b1ab9ed8 A |
1286 | if (SecPVCSetResultForced(pvc, key, |
1287 | ix, kCFBooleanFalse, true)) { | |
1288 | policy_set_free(valid_policies); | |
1289 | policy_set_free(policies); | |
1290 | return; | |
1291 | } | |
1292 | } | |
1293 | } | |
1294 | policy_set_free(policies); | |
1295 | if (!valid_policies) { | |
fa7225c8 | 1296 | secnotice("ev", "valid_policies set is empty: chain not ev"); |
b1ab9ed8 A |
1297 | /* If we ever get into a state where no policies are valid anymore |
1298 | this can't be an ev chain. */ | |
1299 | if (SecPVCSetResultForced(pvc, key, | |
1300 | ix, kCFBooleanFalse, true)) { | |
1301 | return; | |
1302 | } | |
1303 | } | |
1304 | } | |
1305 | ||
1306 | policy_set_free(valid_policies); | |
1307 | ||
1308 | /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a | |
1309 | Subscriber MUST contain an OID defined by the CA in the certificate’s | |
1310 | certificatePolicies extension that: (i) indicates which CA policy statement relates | |
1311 | to that certificate, (ii) asserts the CA’s adherence to and compliance with these | |
1312 | Guidelines, and (iii), by pre-agreement with the Application Software Vendor, | |
1313 | marks the certificate as being an EV Certificate. | |
1314 | (b) EV Subordinate CA Certificates | |
1315 | (1) Certificates issued to Subordinate CAs that are not controlled by the issuing | |
1316 | CA MUST contain one or more OIDs defined by the issuing CA that | |
1317 | explicitly identify the EV Policies that are implemented by the Subordinate | |
1318 | CA; | |
1319 | (2) Certificates issued to Subordinate CAs that are controlled by the Root CA | |
1320 | MAY contain the special anyPolicy OID (2.5.29.32.0). | |
1321 | (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the | |
1322 | certificatePolicies or extendedKeyUsage extensions. | |
1323 | */ | |
1324 | } | |
1325 | ||
427c49bc | 1326 | |
5c19dc3a A |
1327 | /* |
1328 | * MARK: Certificate Transparency support | |
1329 | */ | |
1330 | ||
1331 | /*** | |
1332 | ||
1333 | struct { | |
1334 | Version sct_version; // 1 byte | |
1335 | LogID id; // 32 bytes | |
1336 | uint64 timestamp; // 8 bytes | |
1337 | CtExtensions extensions; // 2 bytes len field, + n bytes data | |
1338 | digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature | |
1339 | Version sct_version; | |
1340 | SignatureType signature_type = certificate_timestamp; | |
1341 | uint64 timestamp; | |
1342 | LogEntryType entry_type; | |
1343 | select(entry_type) { | |
1344 | case x509_entry: ASN.1Cert; | |
1345 | case precert_entry: PreCert; | |
1346 | } signed_entry; | |
1347 | CtExtensions extensions; | |
1348 | }; | |
1349 | } SignedCertificateTimestamp; | |
1350 | ||
1351 | ***/ | |
1352 | ||
1353 | #include <Security/SecureTransportPriv.h> | |
1354 | ||
1355 | static const | |
1356 | SecAsn1Oid *oidForSigAlg(SSL_HashAlgorithm hash, SSL_SignatureAlgorithm alg) | |
1357 | { | |
1358 | switch(alg) { | |
1359 | case SSL_SignatureAlgorithmRSA: | |
1360 | switch (hash) { | |
1361 | case SSL_HashAlgorithmSHA1: | |
1362 | return &CSSMOID_SHA1WithRSA; | |
1363 | case SSL_HashAlgorithmSHA256: | |
1364 | return &CSSMOID_SHA256WithRSA; | |
1365 | case SSL_HashAlgorithmSHA384: | |
1366 | return &CSSMOID_SHA384WithRSA; | |
1367 | default: | |
1368 | break; | |
1369 | } | |
1370 | case SSL_SignatureAlgorithmECDSA: | |
1371 | switch (hash) { | |
1372 | case SSL_HashAlgorithmSHA1: | |
1373 | return &CSSMOID_ECDSA_WithSHA1; | |
1374 | case SSL_HashAlgorithmSHA256: | |
1375 | return &CSSMOID_ECDSA_WithSHA256; | |
1376 | case SSL_HashAlgorithmSHA384: | |
1377 | return &CSSMOID_ECDSA_WithSHA384; | |
1378 | default: | |
1379 | break; | |
1380 | } | |
1381 | default: | |
1382 | break; | |
1383 | } | |
1384 | ||
1385 | return NULL; | |
1386 | } | |
1387 | ||
1388 | ||
1389 | static size_t SSLDecodeUint16(const uint8_t *p) | |
1390 | { | |
1391 | return (p[0]<<8 | p[1]); | |
1392 | } | |
1393 | ||
1394 | static uint8_t *SSLEncodeUint16(uint8_t *p, size_t len) | |
1395 | { | |
1396 | p[0] = (len >> 8)&0xff; | |
1397 | p[1] = (len & 0xff); | |
1398 | return p+2; | |
1399 | } | |
1400 | ||
1401 | static uint8_t *SSLEncodeUint24(uint8_t *p, size_t len) | |
1402 | { | |
1403 | p[0] = (len >> 16)&0xff; | |
1404 | p[1] = (len >> 8)&0xff; | |
1405 | p[2] = (len & 0xff); | |
1406 | return p+3; | |
1407 | } | |
1408 | ||
1409 | ||
1410 | static | |
1411 | uint64_t SSLDecodeUint64(const uint8_t *p) | |
1412 | { | |
1413 | uint64_t u = 0; | |
1414 | for(int i=0; i<8; i++) { | |
1415 | u=(u<<8)|p[0]; | |
1416 | p++; | |
1417 | } | |
1418 | return u; | |
1419 | } | |
1420 | ||
1421 | #include <libDER/DER_CertCrl.h> | |
1422 | #include <libDER/DER_Encode.h> | |
1423 | #include <libDER/asn1Types.h> | |
1424 | ||
1425 | ||
1426 | static CFDataRef copy_x509_entry_from_chain(SecPVCRef pvc) | |
1427 | { | |
1428 | SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1429 | ||
1430 | CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 3+SecCertificateGetLength(leafCert)); | |
1431 | ||
1432 | CFDataSetLength(data, 3+SecCertificateGetLength(leafCert)); | |
1433 | ||
1434 | uint8_t *q = CFDataGetMutableBytePtr(data); | |
1435 | q = SSLEncodeUint24(q, SecCertificateGetLength(leafCert)); | |
1436 | memcpy(q, SecCertificateGetBytePtr(leafCert), SecCertificateGetLength(leafCert)); | |
1437 | ||
1438 | return data; | |
1439 | } | |
1440 | ||
1441 | ||
1442 | static CFDataRef copy_precert_entry_from_chain(SecPVCRef pvc) | |
1443 | { | |
1444 | SecCertificateRef leafCert = NULL; | |
1445 | SecCertificateRef issuer = NULL; | |
1446 | CFDataRef issuerKeyHash = NULL; | |
1447 | CFDataRef tbs_precert = NULL; | |
1448 | CFMutableDataRef data= NULL; | |
1449 | ||
1450 | require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts. | |
1451 | leafCert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1452 | issuer = SecPVCGetCertificateAtIndex(pvc, 1); | |
1453 | ||
1454 | require(leafCert, out); | |
1455 | require(issuer, out); // Those two would likely indicate an internal error, since we already checked the chain length above. | |
1456 | issuerKeyHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer); | |
1457 | tbs_precert = SecCertificateCopyPrecertTBS(leafCert); | |
1458 | ||
1459 | require(issuerKeyHash, out); | |
1460 | require(tbs_precert, out); | |
1461 | data = CFDataCreateMutable(kCFAllocatorDefault, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert)); | |
1462 | CFDataSetLength(data, CFDataGetLength(issuerKeyHash) + 3 + CFDataGetLength(tbs_precert)); | |
1463 | ||
1464 | uint8_t *q = CFDataGetMutableBytePtr(data); | |
1465 | memcpy(q, CFDataGetBytePtr(issuerKeyHash), CFDataGetLength(issuerKeyHash)); q += CFDataGetLength(issuerKeyHash); // issuer key hash | |
1466 | q = SSLEncodeUint24(q, CFDataGetLength(tbs_precert)); | |
1467 | memcpy(q, CFDataGetBytePtr(tbs_precert), CFDataGetLength(tbs_precert)); | |
1468 | ||
1469 | out: | |
1470 | CFReleaseSafe(issuerKeyHash); | |
1471 | CFReleaseSafe(tbs_precert); | |
1472 | return data; | |
1473 | } | |
1474 | ||
fa7225c8 A |
1475 | static |
1476 | CFAbsoluteTime TimestampToCFAbsoluteTime(uint64_t ts) | |
1477 | { | |
1478 | return (ts / 1000) - kCFAbsoluteTimeIntervalSince1970; | |
1479 | } | |
1480 | ||
1481 | static | |
1482 | uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at) | |
1483 | { | |
1484 | return (uint64_t)(at + kCFAbsoluteTimeIntervalSince1970) * 1000; | |
1485 | } | |
1486 | ||
1487 | ||
1488 | ||
1489 | ||
1490 | /* | |
1491 | If the 'sct' is valid, add it to the validatingLogs dictionary. | |
1492 | ||
1493 | Inputs: | |
1494 | - validatingLogs: mutable dictionary to which to add the log that validate this SCT. | |
1495 | - sct: the SCT date | |
1496 | - entry_type: 0 for x509 cert, 1 for precert. | |
1497 | - entry: the cert or precert data. | |
1498 | - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch) | |
1499 | - trustedLog: Dictionary contain the Trusted Logs. | |
5c19dc3a A |
1500 | |
1501 | The SCT is valid if: | |
1502 | - It decodes properly. | |
1503 | - Its timestamp is less than 'verifyTime'. | |
1504 | - It is signed by a log in 'trustedLogs'. | |
fa7225c8 A |
1505 | - If entry_type = 0, the log must be currently qualified. |
1506 | - If entry_type = 1, the log may be expired. | |
5c19dc3a | 1507 | |
fa7225c8 A |
1508 | If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value. |
1509 | If an entry for the same log already existing in the dictionary, the entry is replaced only if the timestamp of this SCT is earlier. | |
5c19dc3a | 1510 | |
5c19dc3a | 1511 | */ |
fa7225c8 A |
1512 | |
1513 | ||
1514 | static CFDictionaryRef getSCTValidatingLog(CFDataRef sct, int entry_type, CFDataRef entry, uint64_t vt, CFArrayRef trustedLogs, CFAbsoluteTime *sct_at) | |
5c19dc3a A |
1515 | { |
1516 | uint8_t version; | |
1517 | const uint8_t *logID; | |
1518 | const uint8_t *timestampData; | |
1519 | uint64_t timestamp; | |
1520 | size_t extensionsLen; | |
1521 | const uint8_t *extensionsData; | |
1522 | uint8_t hashAlg; | |
1523 | uint8_t sigAlg; | |
1524 | size_t signatureLen; | |
1525 | const uint8_t *signatureData; | |
5c19dc3a A |
1526 | SecKeyRef pubKey = NULL; |
1527 | uint8_t *signed_data = NULL; | |
1528 | const SecAsn1Oid *oid = NULL; | |
1529 | SecAsn1AlgId algId; | |
fa7225c8 A |
1530 | CFDataRef logIDData = NULL; |
1531 | CFDictionaryRef result = 0; | |
5c19dc3a A |
1532 | |
1533 | const uint8_t *p = CFDataGetBytePtr(sct); | |
1534 | size_t len = CFDataGetLength(sct); | |
5c19dc3a A |
1535 | |
1536 | require(len>=43, out); | |
1537 | ||
1538 | version = p[0]; p++; len--; | |
1539 | logID = p; p+=32; len-=32; | |
1540 | timestampData = p; p+=8; len-=8; | |
1541 | extensionsLen = SSLDecodeUint16(p); p+=2; len-=2; | |
1542 | ||
1543 | require(len>=extensionsLen, out); | |
1544 | extensionsData = p; p+=extensionsLen; len-=extensionsLen; | |
1545 | ||
1546 | require(len>=4, out); | |
1547 | hashAlg=p[0]; p++; len--; | |
1548 | sigAlg=p[0]; p++; len--; | |
1549 | signatureLen = SSLDecodeUint16(p); p+=2; len-=2; | |
1550 | require(len==signatureLen, out); /* We do not tolerate any extra data after the signature */ | |
1551 | signatureData = p; | |
1552 | ||
1553 | /* verify version: only v1(0) is supported */ | |
1554 | if(version!=0) { | |
1555 | secerror("SCT version unsupported: %d\n", version); | |
1556 | goto out; | |
1557 | } | |
1558 | ||
1559 | /* verify timestamp not in the future */ | |
1560 | timestamp = SSLDecodeUint64(timestampData); | |
1561 | if(timestamp > vt) { | |
1562 | secerror("SCT is in the future: %llu > %llu\n", timestamp, vt); | |
1563 | goto out; | |
1564 | } | |
1565 | ||
1566 | uint8_t *q; | |
1567 | ||
1568 | /* signed entry */ | |
1569 | size_t signed_data_len = 12 + CFDataGetLength(entry) + 2 + extensionsLen ; | |
1570 | signed_data = malloc(signed_data_len); | |
1571 | require(signed_data, out); | |
1572 | q = signed_data; | |
1573 | *q++ = version; | |
1574 | *q++ = 0; // certificate_timestamp | |
1575 | memcpy(q, timestampData, 8); q+=8; | |
1576 | q = SSLEncodeUint16(q, entry_type); // logentry type: 0=cert 1=precert | |
1577 | memcpy(q, CFDataGetBytePtr(entry), CFDataGetLength(entry)); q += CFDataGetLength(entry); | |
1578 | q = SSLEncodeUint16(q, extensionsLen); | |
1579 | memcpy(q, extensionsData, extensionsLen); | |
1580 | ||
fa7225c8 | 1581 | logIDData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, logID, 32, kCFAllocatorNull); |
5c19dc3a A |
1582 | |
1583 | CFDictionaryRef logData = CFArrayGetValueMatching(trustedLogs, ^bool(const void *dict) { | |
1584 | const void *key_data; | |
1585 | if(!isDictionary(dict)) return false; | |
1586 | if(!CFDictionaryGetValueIfPresent(dict, CFSTR("key"), &key_data)) return false; | |
1587 | if(!isData(key_data)) return false; | |
1588 | CFDataRef valueID = SecSHA256DigestCreateFromData(kCFAllocatorDefault, (CFDataRef)key_data); | |
1589 | bool result = (bool)(CFDataCompare(logIDData, valueID)==kCFCompareEqualTo); | |
1590 | CFReleaseSafe(valueID); | |
1591 | return result; | |
1592 | }); | |
1593 | require(logData, out); | |
1594 | ||
fa7225c8 A |
1595 | if(entry_type==0) { |
1596 | // For external SCTs, only keep SCTs from currently valid logs. | |
1597 | require(!CFDictionaryContainsKey(logData, CFSTR("expiry")), out); | |
5c19dc3a A |
1598 | } |
1599 | ||
1600 | CFDataRef logKeyData = CFDictionaryGetValue(logData, CFSTR("key")); | |
1601 | require(logKeyData, out); // This failing would be an internal logic error | |
1602 | pubKey = SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault, logKeyData); | |
1603 | require(pubKey, out); | |
1604 | ||
1605 | oid = oidForSigAlg(hashAlg, sigAlg); | |
1606 | require(oid, out); | |
1607 | ||
1608 | algId.algorithm = *oid; | |
1609 | algId.parameters.Data = NULL; | |
1610 | algId.parameters.Length = 0; | |
1611 | ||
1612 | if(SecKeyDigestAndVerify(pubKey, &algId, signed_data, signed_data_len, signatureData, signatureLen)==0) { | |
fa7225c8 A |
1613 | *sct_at = TimestampToCFAbsoluteTime(timestamp); |
1614 | result = logData; | |
5c19dc3a A |
1615 | } else { |
1616 | secerror("SCT signature failed (log=%@)\n", logData); | |
1617 | } | |
1618 | ||
1619 | out: | |
fa7225c8 | 1620 | CFReleaseSafe(logIDData); |
5c19dc3a A |
1621 | CFReleaseSafe(pubKey); |
1622 | free(signed_data); | |
1623 | return result; | |
1624 | } | |
1625 | ||
fa7225c8 A |
1626 | |
1627 | static void addValidatingLog(CFMutableDictionaryRef validatingLogs, CFDictionaryRef log, CFAbsoluteTime sct_at) | |
1628 | { | |
1629 | CFDateRef validated_time = CFDictionaryGetValue(validatingLogs, log); | |
1630 | ||
1631 | if(validated_time==NULL || (sct_at < CFDateGetAbsoluteTime(validated_time))) { | |
1632 | CFDateRef sct_time = CFDateCreate(kCFAllocatorDefault, sct_at); | |
1633 | CFDictionarySetValue(validatingLogs, log, sct_time); | |
1634 | CFReleaseSafe(sct_time); | |
1635 | } | |
1636 | } | |
1637 | ||
5c19dc3a A |
1638 | static CFArrayRef copy_ocsp_scts(SecPVCRef pvc) |
1639 | { | |
1640 | CFMutableArrayRef SCTs = NULL; | |
1641 | SecCertificateRef leafCert = NULL; | |
1642 | SecCertificateRef issuer = NULL; | |
1643 | CFArrayRef ocspResponsesData = NULL; | |
1644 | SecOCSPRequestRef ocspRequest = NULL; | |
1645 | ||
1646 | ocspResponsesData = SecPathBuilderCopyOCSPResponses(pvc->builder); | |
1647 | require_quiet(ocspResponsesData, out); | |
1648 | ||
1649 | require_quiet(SecPVCGetCertificateCount(pvc)>=2, out); //we need the issuer key for precerts. | |
1650 | leafCert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1651 | issuer = SecPVCGetCertificateAtIndex(pvc, 1); | |
1652 | ||
1653 | require(leafCert, out); | |
1654 | require(issuer, out); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above. | |
1655 | ocspRequest = SecOCSPRequestCreate(leafCert, issuer); | |
1656 | ||
1657 | SCTs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); | |
1658 | require(SCTs, out); | |
1659 | ||
1660 | CFArrayForEach(ocspResponsesData, ^(const void *value) { | |
1661 | /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */ | |
1662 | SecOCSPResponseRef ocspResponse = SecOCSPResponseCreate(value); | |
1663 | if(ocspResponse && SecOCSPGetResponseStatus(ocspResponse)==kSecOCSPSuccess) { | |
1664 | SecOCSPSingleResponseRef ocspSingleResponse = SecOCSPResponseCopySingleResponse(ocspResponse, ocspRequest); | |
1665 | if(ocspSingleResponse) { | |
1666 | CFArrayRef singleResponseSCTs = SecOCSPSingleResponseCopySCTs(ocspSingleResponse); | |
1667 | if(singleResponseSCTs) { | |
1668 | CFArrayAppendArray(SCTs, singleResponseSCTs, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs))); | |
1669 | CFRelease(singleResponseSCTs); | |
1670 | } | |
1671 | SecOCSPSingleResponseDestroy(ocspSingleResponse); | |
1672 | } | |
5c19dc3a | 1673 | } |
641423b6 | 1674 | if(ocspResponse) SecOCSPResponseFinalize(ocspResponse); |
5c19dc3a A |
1675 | }); |
1676 | ||
1677 | if(CFArrayGetCount(SCTs)==0) { | |
1678 | CFReleaseNull(SCTs); | |
1679 | } | |
1680 | ||
1681 | out: | |
1682 | CFReleaseSafe(ocspResponsesData); | |
1683 | if(ocspRequest) | |
1684 | SecOCSPRequestFinalize(ocspRequest); | |
1685 | ||
1686 | return SCTs; | |
1687 | } | |
1688 | ||
1689 | static void SecPolicyCheckCT(SecPVCRef pvc, CFStringRef key) | |
1690 | { | |
1691 | SecCertificateRef leafCert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1692 | CFArrayRef embeddedScts = SecCertificateCopySignedCertificateTimestamps(leafCert); | |
1693 | CFArrayRef builderScts = SecPathBuilderCopySignedCertificateTimestamps(pvc->builder); | |
1694 | CFArrayRef trustedLogs = SecPathBuilderCopyTrustedLogs(pvc->builder); | |
1695 | CFArrayRef ocspScts = copy_ocsp_scts(pvc); | |
1696 | CFDataRef precertEntry = copy_precert_entry_from_chain(pvc); | |
1697 | CFDataRef x509Entry = copy_x509_entry_from_chain(pvc); | |
1698 | ||
fa7225c8 A |
1699 | // This eventually contain list of logs who validated the SCT. |
1700 | CFMutableDictionaryRef currentLogsValidatingScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1701 | CFMutableDictionaryRef logsValidatingEmbeddedScts = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
1702 | ||
866f8763 | 1703 | uint64_t vt = TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc)); |
5c19dc3a | 1704 | |
fa7225c8 A |
1705 | __block bool at_least_one_currently_valid_external = 0; |
1706 | __block bool at_least_one_currently_valid_embedded = 0; | |
5c19dc3a | 1707 | |
fa7225c8 A |
1708 | require(logsValidatingEmbeddedScts, out); |
1709 | require(currentLogsValidatingScts, out); | |
5c19dc3a A |
1710 | |
1711 | if(trustedLogs) { // Don't bother trying to validate SCTs if we don't have any trusted logs. | |
e3d460c9 | 1712 | if(embeddedScts && precertEntry) { // Don't bother if we could not get the precert. |
5c19dc3a | 1713 | CFArrayForEach(embeddedScts, ^(const void *value){ |
fa7225c8 A |
1714 | CFAbsoluteTime sct_at; |
1715 | CFDictionaryRef log = getSCTValidatingLog(value, 1, precertEntry, vt, trustedLogs, &sct_at); | |
1716 | if(log) { | |
1717 | addValidatingLog(logsValidatingEmbeddedScts, log, sct_at); | |
1718 | if(!CFDictionaryContainsKey(log, CFSTR("expiry"))) { | |
1719 | addValidatingLog(currentLogsValidatingScts, log, sct_at); | |
1720 | at_least_one_currently_valid_embedded = true; | |
1721 | } | |
1722 | } | |
5c19dc3a A |
1723 | }); |
1724 | } | |
1725 | ||
e3d460c9 | 1726 | if(builderScts && x509Entry) { // Don't bother if we could not get the cert. |
5c19dc3a | 1727 | CFArrayForEach(builderScts, ^(const void *value){ |
fa7225c8 A |
1728 | CFAbsoluteTime sct_at; |
1729 | CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, trustedLogs, &sct_at); | |
1730 | if(log) { | |
1731 | addValidatingLog(currentLogsValidatingScts, log, sct_at); | |
1732 | at_least_one_currently_valid_external = true; | |
1733 | } | |
5c19dc3a A |
1734 | }); |
1735 | } | |
1736 | ||
e3d460c9 | 1737 | if(ocspScts && x509Entry) { |
5c19dc3a | 1738 | CFArrayForEach(ocspScts, ^(const void *value){ |
fa7225c8 A |
1739 | CFAbsoluteTime sct_at; |
1740 | CFDictionaryRef log = getSCTValidatingLog(value, 0, x509Entry, vt, trustedLogs, &sct_at); | |
1741 | if(log) { | |
1742 | addValidatingLog(currentLogsValidatingScts, log, sct_at); | |
1743 | at_least_one_currently_valid_external = true; | |
1744 | } | |
5c19dc3a A |
1745 | }); |
1746 | } | |
1747 | } | |
1748 | ||
5c19dc3a | 1749 | |
fa7225c8 | 1750 | /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision. |
e0e0d90e | 1751 | |
fa7225c8 A |
1752 | Current Policy: |
1753 | is_ct = (A1 AND A2) OR (B1 AND B2). | |
5c19dc3a | 1754 | |
fa7225c8 A |
1755 | A1: embedded SCTs from 2+ to 5+ logs valid at issuance time |
1756 | A2: At least one embedded SCT from a currently valid log. | |
5c19dc3a | 1757 | |
fa7225c8 A |
1758 | B1: SCTs from 2 currently valid logs (from any source) |
1759 | B2: At least 1 external SCT from a currently valid log. | |
5c19dc3a | 1760 | |
fa7225c8 | 1761 | */ |
5c19dc3a | 1762 | |
866f8763 | 1763 | SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), false); |
fa7225c8 A |
1764 | |
1765 | if(at_least_one_currently_valid_external && CFDictionaryGetCount(currentLogsValidatingScts)>=2) { | |
866f8763 | 1766 | SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), true); |
fa7225c8 | 1767 | } else if(at_least_one_currently_valid_embedded) { |
866f8763 | 1768 | __block CFAbsoluteTime issuanceTime = SecPVCGetVerifyTime(pvc); |
fa7225c8 A |
1769 | __block int lifetime; // in Months |
1770 | __block unsigned once_or_current_qualified_embedded = 0; | |
1771 | ||
1772 | /* Calculate issuance time base on timestamp of SCTs from current logs */ | |
1773 | CFDictionaryForEach(currentLogsValidatingScts, ^(const void *key, const void *value) { | |
1774 | CFDictionaryRef log = key; | |
1775 | if(!CFDictionaryContainsKey(log, CFSTR("expiry"))) { | |
1776 | // Log is still qualified | |
1777 | CFDateRef ts = (CFDateRef) value; | |
1778 | CFAbsoluteTime timestamp = CFDateGetAbsoluteTime(ts); | |
1779 | if(timestamp < issuanceTime) { | |
1780 | issuanceTime = timestamp; | |
1781 | } | |
1782 | } | |
1783 | }); | |
1784 | ||
1785 | /* Count Logs */ | |
1786 | CFDictionaryForEach(logsValidatingEmbeddedScts, ^(const void *key, const void *value) { | |
1787 | CFDictionaryRef log = key; | |
1788 | CFDateRef ts = value; | |
1789 | CFDateRef expiry = CFDictionaryGetValue(log, CFSTR("expiry")); | |
1790 | if(expiry == NULL || CFDateCompare(ts, expiry, NULL) == kCFCompareLessThan) { | |
1791 | once_or_current_qualified_embedded++; | |
1792 | } | |
1793 | }); | |
1794 | ||
1795 | SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar) { | |
1796 | int _lifetime; | |
1797 | CFCalendarGetComponentDifference(zuluCalendar, | |
1798 | SecCertificateNotValidBefore(leafCert), | |
1799 | SecCertificateNotValidAfter(leafCert), | |
1800 | 0, "M", &_lifetime); | |
1801 | lifetime = _lifetime; | |
1802 | }); | |
1803 | ||
1804 | unsigned requiredEmbeddedSctsCount; | |
1805 | ||
1806 | if (lifetime < 15) { | |
1807 | requiredEmbeddedSctsCount = 2; | |
1808 | } else if (lifetime <= 27) { | |
1809 | requiredEmbeddedSctsCount = 3; | |
1810 | } else if (lifetime <= 39) { | |
1811 | requiredEmbeddedSctsCount = 4; | |
1812 | } else { | |
1813 | requiredEmbeddedSctsCount = 5; | |
1814 | } | |
5c19dc3a | 1815 | |
fa7225c8 | 1816 | if(once_or_current_qualified_embedded >= requiredEmbeddedSctsCount){ |
866f8763 | 1817 | SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc->builder), true); |
fa7225c8 A |
1818 | } |
1819 | } | |
5c19dc3a A |
1820 | |
1821 | out: | |
fa7225c8 A |
1822 | CFReleaseSafe(logsValidatingEmbeddedScts); |
1823 | CFReleaseSafe(currentLogsValidatingScts); | |
5c19dc3a A |
1824 | CFReleaseSafe(builderScts); |
1825 | CFReleaseSafe(embeddedScts); | |
1826 | CFReleaseSafe(ocspScts); | |
1827 | CFReleaseSafe(precertEntry); | |
1828 | CFReleaseSafe(trustedLogs); | |
1829 | CFReleaseSafe(x509Entry); | |
1830 | } | |
1831 | ||
fa7225c8 A |
1832 | static bool checkPolicyOidData(SecPVCRef pvc, CFDataRef oid) { |
1833 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1834 | DERItem key_value; | |
1835 | key_value.data = (DERByte *)CFDataGetBytePtr(oid); | |
1836 | key_value.length = (DERSize)CFDataGetLength(oid); | |
1837 | ||
1838 | for (ix = 0; ix < count; ix++) { | |
1839 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1840 | policy_set_t policies = policies_for_cert(cert); | |
1841 | ||
1842 | if (policy_set_contains(policies, &key_value)) { | |
866f8763 | 1843 | policy_set_free(policies); |
fa7225c8 A |
1844 | return true; |
1845 | } | |
866f8763 | 1846 | policy_set_free(policies); |
fa7225c8 A |
1847 | } |
1848 | return false; | |
1849 | } | |
1850 | ||
427c49bc A |
1851 | static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc, CFStringRef key) |
1852 | { | |
427c49bc A |
1853 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
1854 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
fa7225c8 | 1855 | bool result = false; |
d8f41ccd | 1856 | |
427c49bc A |
1857 | if (CFGetTypeID(value) == CFDataGetTypeID()) |
1858 | { | |
fa7225c8 A |
1859 | result = checkPolicyOidData(pvc, value); |
1860 | } else if (CFGetTypeID(value) == CFStringGetTypeID()) { | |
1861 | CFDataRef dataOid = SecCertificateCreateOidDataFromString(NULL, value); | |
1862 | if (dataOid) { | |
1863 | result = checkPolicyOidData(pvc, dataOid); | |
1864 | CFRelease(dataOid); | |
1865 | } | |
1866 | } | |
1867 | if(!result) { | |
427c49bc | 1868 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); |
fa7225c8 | 1869 | } |
427c49bc A |
1870 | } |
1871 | ||
1872 | ||
b1ab9ed8 A |
1873 | static void SecPolicyCheckRevocation(SecPVCRef pvc, |
1874 | CFStringRef key) { | |
fa7225c8 A |
1875 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
1876 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
1877 | if (isString(value)) { | |
866f8763 | 1878 | SecPathBuilderSetRevocationMethod(pvc->builder, value); |
fa7225c8 | 1879 | } |
b1ab9ed8 A |
1880 | } |
1881 | ||
5c19dc3a A |
1882 | static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc, |
1883 | CFStringRef key) { | |
866f8763 A |
1884 | pvc->require_revocation_response = true; |
1885 | secdebug("policy", "revocation response required"); | |
5c19dc3a A |
1886 | } |
1887 | ||
6b200bc3 | 1888 | static void SecPolicyCheckRevocationOnline(SecPVCRef pvc, CFStringRef key) { |
866f8763 | 1889 | SecPathBuilderSetCheckRevocationOnline(pvc->builder); |
6b200bc3 A |
1890 | } |
1891 | ||
b1ab9ed8 | 1892 | static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc, |
fa7225c8 A |
1893 | CFStringRef key) { |
1894 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1895 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
1896 | if (value == kCFBooleanTrue) { | |
1897 | SecPathBuilderSetCanAccessNetwork(pvc->builder, false); | |
1898 | } else { | |
1899 | SecPathBuilderSetCanAccessNetwork(pvc->builder, true); | |
1900 | } | |
b1ab9ed8 A |
1901 | } |
1902 | ||
e3d460c9 A |
1903 | static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc, |
1904 | CFStringRef key) { | |
1905 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1906 | for (ix = 1; ix < count - 1; ++ix) { | |
1907 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
3a7be6fd | 1908 | if (cert && SecCertificateIsWeakKey(cert)) { |
e3d460c9 A |
1909 | /* Intermediate certificate has a weak key. */ |
1910 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
1911 | return; | |
866f8763 | 1912 | pvc->result = kSecTrustResultFatalTrustFailure; |
e3d460c9 A |
1913 | } |
1914 | } | |
1915 | } | |
1916 | ||
1917 | static void SecPolicyCheckWeakLeaf(SecPVCRef pvc, | |
1918 | CFStringRef key) { | |
1919 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
3a7be6fd | 1920 | if (cert && SecCertificateIsWeakKey(cert)) { |
e3d460c9 A |
1921 | /* Leaf certificate has a weak key. */ |
1922 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
1923 | return; | |
866f8763 | 1924 | pvc->result = kSecTrustResultFatalTrustFailure; |
e3d460c9 A |
1925 | } |
1926 | } | |
1927 | ||
1928 | static void SecPolicyCheckWeakRoot(SecPVCRef pvc, | |
1929 | CFStringRef key) { | |
1930 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1931 | ix = count - 1; | |
1932 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
3a7be6fd | 1933 | if (cert && SecCertificateIsWeakKey(cert)) { |
e3d460c9 A |
1934 | /* Root certificate has a weak key. */ |
1935 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
1936 | return; | |
866f8763 | 1937 | pvc->result = kSecTrustResultFatalTrustFailure; |
e3d460c9 A |
1938 | } |
1939 | } | |
1940 | ||
fa7225c8 A |
1941 | static void SecPolicyCheckKeySize(SecPVCRef pvc, CFStringRef key) { |
1942 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1943 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1944 | CFDictionaryRef keySizes = CFDictionaryGetValue(policy->_options, key); | |
1945 | for (ix = 0; ix < count; ++ix) { | |
1946 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1947 | if (!SecCertificateIsAtLeastMinKeySize(cert, keySizes)) { | |
1948 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
1949 | return; | |
1950 | } | |
1951 | } | |
1952 | } | |
1953 | ||
1954 | static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc, | |
1955 | CFStringRef key) { | |
1956 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1957 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1958 | CFSetRef disallowedHashAlgorithms = CFDictionaryGetValue(policy->_options, key); | |
1959 | for (ix = 0; ix < count; ++ix) { | |
1960 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1961 | if (!SecPolicyCheckCertSignatureHashAlgorithms(cert, disallowedHashAlgorithms)) { | |
1962 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
1963 | return; | |
1964 | } | |
1965 | } | |
1966 | } | |
1967 | ||
6b200bc3 A |
1968 | static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc) { |
1969 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
1970 | require_quiet(leaf, out); | |
1971 | ||
866f8763 | 1972 | /* And now a special snowflake from our tests */ |
6b200bc3 | 1973 | |
866f8763 A |
1974 | /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */ |
1975 | /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */ | |
1976 | /* Not After : May 26 09:37:50 2017 GMT */ | |
6b200bc3 | 1977 | static const uint8_t vodafone[] = { |
866f8763 A |
1978 | 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a, |
1979 | 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18 | |
6b200bc3 A |
1980 | }; |
1981 | ||
866f8763 A |
1982 | /* subject:/C=US/ST=Kansas/L=Overland Park/O=Sprint/CN=oma.ssprov.sprint.com */ |
1983 | /* issuer :/C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C */ | |
1984 | /* Not After : Aug 16 05:04:29 2017 GMT */ | |
1985 | static const uint8_t sprint[] = { | |
1986 | 0xa3, 0x18, 0x70, 0x4f, 0xf7, 0xbf, 0xfb, 0x2b, 0xe2, 0x64, | |
1987 | 0x3a, 0x2d, 0x2b, 0xb8, 0x10, 0x5f, 0x77, 0xd5, 0x01, 0xab | |
1988 | }; | |
1989 | ||
1990 | CFDataRef leafFingerprint = SecCertificateGetSHA1Digest(leaf); | |
1991 | require_quiet(leafFingerprint, out); | |
6b200bc3 | 1992 | const unsigned int len = 20; |
866f8763 A |
1993 | const uint8_t *dp = CFDataGetBytePtr(leafFingerprint); |
1994 | if (dp && (!memcmp(vodafone, dp, len) || !memcmp(sprint,dp,len))) { | |
6b200bc3 A |
1995 | return true; |
1996 | } | |
1997 | ||
1998 | out: | |
1999 | return false; | |
2000 | } | |
2001 | ||
2002 | static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc, CFStringRef key); | |
2003 | ||
2004 | static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc, | |
2005 | CFStringRef key) { | |
2006 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
6b200bc3 A |
2007 | |
2008 | Boolean keyInPolicy = false; | |
2009 | CFArrayRef policies = pvc->policies; | |
2010 | CFIndex policyIX, policyCount = CFArrayGetCount(policies); | |
2011 | for (policyIX = 0; policyIX < policyCount; ++policyIX) { | |
2012 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX); | |
2013 | if (policy && CFDictionaryContainsKey(policy->_options, key)) { | |
2014 | keyInPolicy = true; | |
2015 | } | |
2016 | } | |
2017 | ||
2018 | /* We only enforce this check when *both* of the following are true: | |
2019 | * 1. One of the certs in the path has this usage constraint, and | |
2020 | * 2. One of the policies in the PVC has this key | |
2021 | * (As compared to normal policy options which require only one to be true..) */ | |
2022 | require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc, key) && | |
2023 | keyInPolicy, out); | |
2024 | ||
2025 | /* Ignore the anchor if it's trusted */ | |
866f8763 | 2026 | if (SecPathBuilderIsAnchored(pvc->builder)) { |
6b200bc3 A |
2027 | count--; |
2028 | } | |
2029 | for (ix = 0; ix < count; ++ix) { | |
2030 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2031 | if (SecCertificateIsWeakHash(cert)) { | |
2032 | if (!leaf_is_on_weak_hash_whitelist(pvc)) { | |
2033 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) { | |
866f8763 | 2034 | return; |
6b200bc3 A |
2035 | } |
2036 | } | |
2037 | } | |
2038 | } | |
2039 | out: | |
6b200bc3 A |
2040 | return; |
2041 | } | |
2042 | ||
866f8763 A |
2043 | static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc, |
2044 | CFStringRef key) { | |
2045 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
427c49bc | 2046 | |
866f8763 A |
2047 | Boolean keyInPolicy = false; |
2048 | CFArrayRef policies = pvc->policies; | |
2049 | CFIndex policyIX, policyCount = CFArrayGetCount(policies); | |
2050 | for (policyIX = 0; policyIX < policyCount; ++policyIX) { | |
2051 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX); | |
2052 | if (policy && CFDictionaryContainsKey(policy->_options, key)) { | |
2053 | keyInPolicy = true; | |
fa7225c8 A |
2054 | } |
2055 | } | |
fa7225c8 | 2056 | |
866f8763 A |
2057 | /* We only enforce this check when *both* of the following are true: |
2058 | * 1. One of the certs in the path has this usage constraint, and | |
2059 | * 2. One of the policies in the PVC has this key | |
2060 | * (As compared to normal policy options which require only one to be true..) */ | |
2061 | require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc, key) && | |
2062 | keyInPolicy, out); | |
fa7225c8 | 2063 | |
866f8763 A |
2064 | /* Ignore the anchor if it's trusted */ |
2065 | if (SecPathBuilderIsAnchored(pvc->builder)) { | |
2066 | count--; | |
fa7225c8 | 2067 | } |
866f8763 A |
2068 | for (ix = 0; ix < count; ++ix) { |
2069 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2070 | if (!SecCertificateIsStrongKey(cert)) { | |
2071 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) { | |
2072 | return; | |
fa7225c8 A |
2073 | } |
2074 | } | |
fa7225c8 | 2075 | |
866f8763 A |
2076 | } /* Cert loop */ |
2077 | out: | |
2078 | return; | |
fa7225c8 | 2079 | } |
6b200bc3 | 2080 | |
866f8763 A |
2081 | static void SecPolicyCheckPinningRequired(SecPVCRef pvc, CFStringRef key) { |
2082 | /* Pinning is disabled on the system, skip. */ | |
2083 | if (SecIsInternalRelease()) { | |
2084 | if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"), | |
2085 | CFSTR("com.apple.security"), NULL)) { | |
6b200bc3 A |
2086 | return; |
2087 | } | |
6b200bc3 A |
2088 | } |
2089 | ||
866f8763 A |
2090 | CFArrayRef policies = pvc->policies; |
2091 | CFIndex policyIX, policyCount = CFArrayGetCount(policies); | |
2092 | for (policyIX = 0; policyIX < policyCount; ++policyIX) { | |
2093 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX); | |
2094 | CFStringRef policyName = SecPolicyGetName(policy); | |
2095 | if (CFEqualSafe(policyName, CFSTR("sslServer"))) { | |
2096 | /* policy required pinning, but we didn't use a pinning policy */ | |
2097 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) { | |
2098 | return; | |
6b200bc3 A |
2099 | } |
2100 | } | |
fa7225c8 | 2101 | } |
fa7225c8 A |
2102 | } |
2103 | ||
866f8763 | 2104 | void SecPolicyServerInitialize(void) { |
fa7225c8 A |
2105 | gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
2106 | &kCFTypeDictionaryKeyCallBacks, NULL); | |
2107 | gSecPolicyPathCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
2108 | &kCFTypeDictionaryKeyCallBacks, NULL); | |
5c19dc3a | 2109 | |
b1ab9ed8 A |
2110 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2111 | kSecPolicyCheckBasicCertificateProcessing, | |
5c19dc3a | 2112 | SecPolicyCheckBasicCertificateProcessing); |
b1ab9ed8 | 2113 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2114 | kSecPolicyCheckCriticalExtensions, |
2115 | SecPolicyCheckCriticalExtensions); | |
b1ab9ed8 | 2116 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2117 | kSecPolicyCheckIdLinkage, |
2118 | SecPolicyCheckIdLinkage); | |
b1ab9ed8 | 2119 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
5c19dc3a A |
2120 | kSecPolicyCheckKeyUsage, |
2121 | SecPolicyCheckKeyUsage); | |
b1ab9ed8 | 2122 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
5c19dc3a A |
2123 | kSecPolicyCheckExtendedKeyUsage, |
2124 | SecPolicyCheckExtendedKeyUsage); | |
b1ab9ed8 | 2125 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
fa7225c8 A |
2126 | kSecPolicyCheckBasicConstraints, |
2127 | SecPolicyCheckBasicConstraints); | |
866f8763 | 2128 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2129 | kSecPolicyCheckNonEmptySubject, |
2130 | SecPolicyCheckNonEmptySubject); | |
b1ab9ed8 A |
2131 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2132 | kSecPolicyCheckQualifiedCertStatements, | |
2133 | SecPolicyCheckQualifiedCertStatements); | |
2134 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
5c19dc3a A |
2135 | kSecPolicyCheckSSLHostname, |
2136 | SecPolicyCheckSSLHostname); | |
b1ab9ed8 | 2137 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
5c19dc3a A |
2138 | kSecPolicyCheckEmail, |
2139 | SecPolicyCheckEmail); | |
b1ab9ed8 | 2140 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2141 | kSecPolicyCheckValidIntermediates, |
2142 | SecPolicyCheckValidIntermediates); | |
b1ab9ed8 | 2143 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
5c19dc3a A |
2144 | kSecPolicyCheckValidLeaf, |
2145 | SecPolicyCheckValidLeaf); | |
b1ab9ed8 | 2146 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2147 | kSecPolicyCheckValidRoot, |
2148 | SecPolicyCheckValidRoot); | |
b1ab9ed8 | 2149 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2150 | kSecPolicyCheckIssuerCommonName, |
2151 | SecPolicyCheckIssuerCommonName); | |
b1ab9ed8 A |
2152 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2153 | kSecPolicyCheckSubjectCommonNamePrefix, | |
2154 | SecPolicyCheckSubjectCommonNamePrefix); | |
2155 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2156 | kSecPolicyCheckSubjectCommonName, | |
2157 | SecPolicyCheckSubjectCommonName); | |
2158 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2159 | kSecPolicyCheckNotValidBefore, | |
2160 | SecPolicyCheckNotValidBefore); | |
2161 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
5c19dc3a A |
2162 | kSecPolicyCheckChainLength, |
2163 | SecPolicyCheckChainLength); | |
b1ab9ed8 | 2164 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
5c19dc3a A |
2165 | kSecPolicyCheckAnchorSHA1, |
2166 | SecPolicyCheckAnchorSHA1); | |
fa7225c8 A |
2167 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2168 | kSecPolicyCheckAnchorSHA256, | |
2169 | SecPolicyCheckAnchorSHA256); | |
5c19dc3a A |
2170 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2171 | kSecPolicyCheckAnchorApple, | |
2172 | SecPolicyCheckAnchorApple); | |
b1ab9ed8 A |
2173 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2174 | kSecPolicyCheckSubjectOrganization, | |
2175 | SecPolicyCheckSubjectOrganization); | |
427c49bc A |
2176 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2177 | kSecPolicyCheckSubjectOrganizationalUnit, | |
2178 | SecPolicyCheckSubjectOrganizationalUnit); | |
b1ab9ed8 A |
2179 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2180 | kSecPolicyCheckEAPTrustedServerNames, | |
5c19dc3a | 2181 | SecPolicyCheckEAPTrustedServerNames); |
b1ab9ed8 A |
2182 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2183 | kSecPolicyCheckSubjectCommonNameTEST, | |
2184 | SecPolicyCheckSubjectCommonNameTEST); | |
2185 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2186 | kSecPolicyCheckRevocation, | |
2187 | SecPolicyCheckRevocation); | |
5c19dc3a A |
2188 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2189 | kSecPolicyCheckRevocationResponseRequired, | |
2190 | SecPolicyCheckRevocationResponseRequired); | |
6b200bc3 A |
2191 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2192 | kSecPolicyCheckRevocationOnline, | |
2193 | SecPolicyCheckRevocationOnline); | |
b1ab9ed8 A |
2194 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2195 | kSecPolicyCheckNoNetworkAccess, | |
2196 | SecPolicyCheckNoNetworkAccess); | |
427c49bc | 2197 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
5c19dc3a A |
2198 | kSecPolicyCheckBlackListedLeaf, |
2199 | SecPolicyCheckBlackListedLeaf); | |
2200 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2201 | kSecPolicyCheckGrayListedLeaf, | |
2202 | SecPolicyCheckGrayListedLeaf); | |
2203 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2204 | kSecPolicyCheckLeafMarkerOid, | |
2205 | SecPolicyCheckLeafMarkerOid); | |
fa7225c8 A |
2206 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2207 | kSecPolicyCheckLeafMarkerOidWithoutValueCheck, | |
2208 | SecPolicyCheckLeafMarkerOidWithoutValueCheck); | |
6b200bc3 A |
2209 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2210 | kSecPolicyCheckLeafMarkersProdAndQA, | |
2211 | SecPolicyCheckLeafMarkersProdAndQA); | |
fa7225c8 A |
2212 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2213 | kSecPolicyCheckIntermediateSPKISHA256, | |
2214 | SecPolicyCheckIntermediateSPKISHA256); | |
2215 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2216 | kSecPolicyCheckIntermediateEKU, | |
2217 | SecPolicyCheckIntermediateEKU); | |
5c19dc3a A |
2218 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2219 | kSecPolicyCheckIntermediateMarkerOid, | |
2220 | SecPolicyCheckIntermediateMarkerOid); | |
866f8763 | 2221 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
427c49bc A |
2222 | kSecPolicyCheckCertificatePolicy, |
2223 | SecPolicyCheckCertificatePolicyOid); | |
e3d460c9 A |
2224 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2225 | kSecPolicyCheckWeakIntermediates, | |
2226 | SecPolicyCheckWeakIntermediates); | |
2227 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2228 | kSecPolicyCheckWeakLeaf, | |
2229 | SecPolicyCheckWeakLeaf); | |
2230 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2231 | kSecPolicyCheckWeakRoot, | |
2232 | SecPolicyCheckWeakRoot); | |
fa7225c8 A |
2233 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2234 | kSecPolicyCheckKeySize, | |
2235 | SecPolicyCheckKeySize); | |
2236 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2237 | kSecPolicyCheckSignatureHashAlgorithms, | |
2238 | SecPolicyCheckSignatureHashAlgorithms); | |
6b200bc3 A |
2239 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2240 | kSecPolicyCheckSystemTrustedWeakHash, | |
2241 | SecPolicyCheckSystemTrustedWeakHash); | |
866f8763 A |
2242 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2243 | kSecPolicyCheckSystemTrustedWeakKey, | |
2244 | SecPolicyCheckSystemTrustedWeakKey); | |
6b200bc3 A |
2245 | CFDictionaryAddValue(gSecPolicyPathCallbacks, |
2246 | kSecPolicyCheckIntermediateOrganization, | |
2247 | SecPolicyCheckIntermediateOrganization); | |
2248 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2249 | kSecPolicyCheckIntermediateCountry, | |
2250 | SecPolicyCheckIntermediateCountry); | |
866f8763 A |
2251 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2252 | kSecPolicyCheckPinningRequired, | |
2253 | SecPolicyCheckPinningRequired); | |
b1ab9ed8 A |
2254 | } |
2255 | ||
427c49bc A |
2256 | // MARK: - |
2257 | // MARK: SecPVCRef | |
b1ab9ed8 A |
2258 | /******************************************************** |
2259 | ****************** SecPVCRef Functions ***************** | |
2260 | ********************************************************/ | |
2261 | ||
866f8763 | 2262 | void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies) { |
b1ab9ed8 A |
2263 | secdebug("alloc", "%p", pvc); |
2264 | // Weird logging policies crashes. | |
2265 | //secdebug("policy", "%@", policies); | |
3a7be6fd A |
2266 | |
2267 | // Zero the pvc struct so only non-zero fields need to be explicitly set | |
2268 | memset(pvc, 0, sizeof(struct OpaqueSecPVC)); | |
b1ab9ed8 A |
2269 | pvc->builder = builder; |
2270 | pvc->policies = policies; | |
2271 | if (policies) | |
2272 | CFRetain(policies); | |
866f8763 | 2273 | pvc->result = kSecTrustResultUnspecified; |
b1ab9ed8 | 2274 | |
866f8763 A |
2275 | CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, |
2276 | &kCFTypeDictionaryKeyCallBacks, | |
2277 | &kCFTypeDictionaryValueCallBacks); | |
2278 | pvc->leafDetails = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail, | |
2279 | 1, &kCFTypeArrayCallBacks); | |
2280 | CFRelease(certDetail); | |
b1ab9ed8 A |
2281 | } |
2282 | ||
2283 | void SecPVCDelete(SecPVCRef pvc) { | |
2284 | secdebug("alloc", "%p", pvc); | |
2285 | CFReleaseNull(pvc->policies); | |
2286 | CFReleaseNull(pvc->details); | |
866f8763 | 2287 | CFReleaseNull(pvc->leafDetails); |
b1ab9ed8 A |
2288 | } |
2289 | ||
866f8763 | 2290 | void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathVCRef path) { |
b1ab9ed8 | 2291 | secdebug("policy", "%@", path); |
866f8763 A |
2292 | pvc->policyIX = 0; |
2293 | pvc->result = kSecTrustResultUnspecified; | |
2294 | CFReleaseNull(pvc->details); | |
2295 | } | |
2296 | ||
2297 | void SecPVCComputeDetails(SecPVCRef pvc, SecCertificatePathVCRef path) { | |
b1ab9ed8 | 2298 | pvc->policyIX = 0; |
b04fe171 A |
2299 | |
2300 | /* Since we don't run the LeafChecks again, we need to preserve the | |
2301 | * result the leaf had. */ | |
866f8763 A |
2302 | CFIndex ix, pathLength = SecCertificatePathVCGetCount(path); |
2303 | CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault, | |
2304 | pathLength, pvc->leafDetails); | |
2305 | for (ix = 1; ix < pathLength; ++ix) { | |
2306 | CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
2307 | &kCFTypeDictionaryKeyCallBacks, | |
2308 | &kCFTypeDictionaryValueCallBacks); | |
2309 | CFArrayAppendValue(details, certDetail); | |
2310 | CFRelease(certDetail); | |
2311 | } | |
2312 | CFRetainAssign(pvc->details, details); | |
2313 | pvc->result = pvc->leafResult; | |
2314 | CFReleaseSafe(details); | |
b1ab9ed8 A |
2315 | } |
2316 | ||
2317 | SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) { | |
2318 | return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX); | |
2319 | } | |
2320 | ||
866f8763 A |
2321 | static CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) { |
2322 | return SecPathBuilderGetCertificateCount(pvc->builder); | |
b1ab9ed8 A |
2323 | } |
2324 | ||
866f8763 A |
2325 | static SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) { |
2326 | return SecPathBuilderGetCertificateAtIndex(pvc->builder, ix); | |
b1ab9ed8 A |
2327 | } |
2328 | ||
866f8763 A |
2329 | static CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) { |
2330 | return SecPathBuilderGetVerifyTime(pvc->builder); | |
b1ab9ed8 A |
2331 | } |
2332 | ||
866f8763 A |
2333 | static bool SecPVCIsExceptedError(SecPVCRef pvc, CFIndex ix, CFStringRef key, CFTypeRef value) { |
2334 | CFArrayRef exceptions = SecPathBuilderGetExceptions(pvc->builder); | |
2335 | if (!exceptions) { return false; } | |
2336 | CFIndex exceptionsCount = CFArrayGetCount(exceptions); | |
b1ab9ed8 | 2337 | |
866f8763 A |
2338 | /* There are two types of exceptions: |
2339 | * 1. Those that are built from SecTrustCopyExceptions, which are particular to the | |
2340 | * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary. | |
2341 | * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors. | |
2342 | */ | |
2343 | #if TARGET_OS_OSX | |
2344 | CFDictionaryRef options = CFArrayGetValueAtIndex(exceptions, 0); | |
2345 | /* Type 2 */ | |
2346 | if (exceptionsCount == 1 && (ix > 0 || !CFDictionaryContainsKey(options, kSecCertificateDetailSHA1Digest))) { | |
2347 | /* SHA1Digest not allowed */ | |
2348 | if (CFDictionaryContainsKey(options, kSecCertificateDetailSHA1Digest)) { return false; } | |
2349 | /* Key excepted */ | |
2350 | if (CFDictionaryContainsKey(options, key)) { | |
2351 | /* Special case -- AnchorTrusted only for self-signed certs */ | |
2352 | if (CFEqual(kSecPolicyCheckAnchorTrusted, key)) { | |
2353 | Boolean isSelfSigned = false; | |
2354 | SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(pvc->builder, ix); | |
2355 | if (!cert || (errSecSuccess != SecCertificateIsSelfSigned(cert, &isSelfSigned)) || !isSelfSigned) { | |
2356 | return false; | |
2357 | } | |
2358 | } | |
2359 | return true; | |
2360 | } | |
2361 | } | |
2362 | #endif | |
5c19dc3a | 2363 | |
866f8763 A |
2364 | /* Type 1 */ |
2365 | if (ix >= exceptionsCount) { return false; } | |
2366 | CFDictionaryRef exception = CFArrayGetValueAtIndex(exceptions, ix); | |
6b200bc3 | 2367 | |
866f8763 A |
2368 | /* Compare the cert hash */ |
2369 | if (!CFDictionaryContainsKey(exception, kSecCertificateDetailSHA1Digest)) { return false; } | |
2370 | SecCertificateRef cert = SecPathBuilderGetCertificateAtIndex(pvc->builder, ix); | |
2371 | if (!CFEqual(SecCertificateGetSHA1Digest(cert), CFDictionaryGetValue(exception, kSecCertificateDetailSHA1Digest))) { | |
2372 | return false; | |
2373 | } | |
b1ab9ed8 | 2374 | |
866f8763 A |
2375 | /* Key Excepted */ |
2376 | CFTypeRef exceptionValue = CFDictionaryGetValue(exception, key); | |
2377 | if (exceptionValue && CFEqual(value, exceptionValue)) { | |
2378 | /* Only change result if PVC is already ok */ | |
2379 | if (SecPVCIsOkResult(pvc)) { | |
2380 | // Chains that pass due to exceptions get Proceed result. | |
2381 | pvc->result = kSecTrustResultProceed; | |
2382 | } | |
2383 | return true; | |
2384 | } | |
2385 | ||
2386 | return false; | |
b1ab9ed8 A |
2387 | } |
2388 | ||
fa7225c8 A |
2389 | static int32_t detailKeyToCssmErr(CFStringRef key) { |
2390 | int32_t result = 0; | |
2391 | ||
2392 | if (CFEqual(key, kSecPolicyCheckSSLHostname)) { | |
2393 | result = -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH | |
2394 | } | |
2395 | else if (CFEqual(key, kSecPolicyCheckEmail)) { | |
2396 | result = -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND | |
2397 | } | |
2398 | else if (CFEqual(key, kSecPolicyCheckValidLeaf) || | |
2399 | CFEqual(key, kSecPolicyCheckValidIntermediates) || | |
2400 | CFEqual(key, kSecPolicyCheckValidRoot)) { | |
2401 | result = -2147409654; // CSSMERR_TP_CERT_EXPIRED | |
2402 | } | |
2403 | ||
2404 | return result; | |
2405 | } | |
2406 | ||
2407 | static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint); | |
2408 | ||
2409 | static bool SecPVCIsAllowedError(SecPVCRef pvc, CFIndex ix, CFStringRef key) { | |
2410 | bool result = false; | |
866f8763 A |
2411 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
2412 | CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, ix); | |
2413 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
fa7225c8 A |
2414 | CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints); |
2415 | ||
2416 | for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) { | |
2417 | CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX); | |
2418 | CFNumberRef allowedErrorNumber = NULL; | |
2419 | if (!isDictionary(constraint)) { | |
2420 | continue; | |
2421 | } | |
2422 | allowedErrorNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsAllowedError); | |
2423 | int32_t allowedErrorValue = 0; | |
2424 | if (!isNumber(allowedErrorNumber) || !CFNumberGetValue(allowedErrorNumber, kCFNumberSInt32Type, &allowedErrorValue)) { | |
2425 | continue; | |
2426 | } | |
2427 | ||
2428 | if (SecPVCMeetsConstraint(pvc, cert, constraint)) { | |
2429 | if (allowedErrorValue == detailKeyToCssmErr(key)) { | |
2430 | result = true; | |
2431 | break; | |
2432 | } | |
2433 | } | |
2434 | } | |
2435 | return result; | |
2436 | } | |
2437 | ||
6b200bc3 | 2438 | static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc, CFStringRef key) { |
866f8763 | 2439 | CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc); |
6b200bc3 | 2440 | for (certIX = 0; certIX < certCount; certIX++) { |
866f8763 A |
2441 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
2442 | CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, certIX); | |
6b200bc3 A |
2443 | CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints); |
2444 | for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) { | |
2445 | CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX); | |
2446 | if (!isDictionary(constraint)) { | |
2447 | continue; | |
2448 | } | |
2449 | ||
2450 | CFDictionaryRef policyOptions = NULL; | |
2451 | policyOptions = (CFDictionaryRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyOptions); | |
2452 | if (policyOptions && isDictionary(policyOptions) && | |
2453 | CFDictionaryContainsKey(policyOptions, key)) { | |
2454 | return true; | |
2455 | } | |
2456 | } | |
2457 | } | |
2458 | return false; | |
2459 | } | |
2460 | ||
b1ab9ed8 A |
2461 | /* AUDIT[securityd](done): |
2462 | policy->_options is a caller provided dictionary, only its cf type has | |
2463 | been checked. | |
2464 | */ | |
2465 | bool SecPVCSetResultForced(SecPVCRef pvc, | |
2466 | CFStringRef key, CFIndex ix, CFTypeRef result, bool force) { | |
2467 | ||
fa7225c8 | 2468 | secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, |
b1ab9ed8 A |
2469 | (pvc->callbacks == gSecPolicyLeafCallbacks ? "leaf" |
2470 | : (pvc->callbacks == gSecPolicyPathCallbacks ? "path" | |
2471 | : "custom")), | |
2472 | (force ? "force" : ""), result); | |
2473 | ||
2474 | /* If this is not something the current policy cares about ignore | |
2475 | this error and return true so our caller continues evaluation. */ | |
2476 | if (!force) { | |
6b200bc3 | 2477 | /* Either the policy or the usage constraints have to have this key */ |
b1ab9ed8 | 2478 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
6b200bc3 A |
2479 | if (!(SecPVCKeyIsConstraintPolicyOption(pvc, key) || |
2480 | (policy && CFDictionaryContainsKey(policy->_options, key)))) { | |
b1ab9ed8 | 2481 | return true; |
6b200bc3 | 2482 | } |
b1ab9ed8 A |
2483 | } |
2484 | ||
fa7225c8 | 2485 | /* Check to see if the SecTrustSettings for the certificate in question |
b1ab9ed8 | 2486 | tell us to ignore this error. */ |
fa7225c8 A |
2487 | if (SecPVCIsAllowedError(pvc, ix, key)) { |
2488 | secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix, key); | |
2489 | return true; | |
2490 | } | |
2491 | ||
866f8763 A |
2492 | /* Check to see if exceptions tells us to ignore this error. */ |
2493 | if (SecPVCIsExceptedError(pvc, ix, key, result)) { | |
2494 | secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix, key); | |
2495 | return true; | |
2496 | } | |
2497 | ||
2498 | /* Check SecPVCIsOkResult to avoid resetting deny or fatal to recoverable */ | |
2499 | if (SecPVCIsOkResult(pvc)) { | |
2500 | pvc->result = kSecTrustResultRecoverableTrustFailure; | |
2501 | } | |
b1ab9ed8 A |
2502 | if (!pvc->details) |
2503 | return false; | |
2504 | ||
2505 | CFMutableDictionaryRef detail = | |
2506 | (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix); | |
2507 | ||
2508 | /* Perhaps detail should have an array of results per key? As it stands | |
2509 | in the case of multiple policy failures the last failure stands. */ | |
2510 | CFDictionarySetValue(detail, key, result); | |
2511 | ||
2512 | return true; | |
2513 | } | |
2514 | ||
2515 | bool SecPVCSetResult(SecPVCRef pvc, | |
2516 | CFStringRef key, CFIndex ix, CFTypeRef result) { | |
2517 | return SecPVCSetResultForced(pvc, key, ix, result, false); | |
2518 | } | |
2519 | ||
2520 | /* AUDIT[securityd](done): | |
2521 | key(ok) is a caller provided. | |
2522 | value(ok, unused) is a caller provided. | |
2523 | */ | |
2524 | static void SecPVCValidateKey(const void *key, const void *value, | |
2525 | void *context) { | |
2526 | SecPVCRef pvc = (SecPVCRef)context; | |
2527 | ||
2528 | /* If our caller doesn't want full details and we failed earlier there is | |
2529 | no point in doing additional checks. */ | |
866f8763 | 2530 | if (!SecPVCIsOkResult(pvc) && !pvc->details) |
b1ab9ed8 A |
2531 | return; |
2532 | ||
2533 | SecPolicyCheckFunction fcn = (SecPolicyCheckFunction) | |
2534 | CFDictionaryGetValue(pvc->callbacks, key); | |
2535 | ||
2536 | if (!fcn) { | |
b1ab9ed8 A |
2537 | if (pvc->callbacks == gSecPolicyLeafCallbacks) { |
2538 | if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks, key)) { | |
866f8763 | 2539 | pvc->result = kSecTrustResultOtherError; |
b1ab9ed8 A |
2540 | } |
2541 | } else if (pvc->callbacks == gSecPolicyPathCallbacks) { | |
2542 | if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks, key)) { | |
866f8763 | 2543 | pvc->result = kSecTrustResultOtherError; |
b1ab9ed8 A |
2544 | } |
2545 | } else { | |
866f8763 A |
2546 | /* Non standard validation phase. This may be a new key from the |
2547 | * pinning DB which is not implemented in this OS version. Log | |
2548 | * a warning. */ | |
2549 | secwarning("policy: unknown policy key %@, skipping", key); | |
b1ab9ed8 A |
2550 | } |
2551 | return; | |
2552 | } | |
2553 | ||
2554 | fcn(pvc, (CFStringRef)key); | |
2555 | } | |
2556 | ||
2557 | /* AUDIT[securityd](done): | |
2558 | policy->_options is a caller provided dictionary, only its cf type has | |
2559 | been checked. | |
2560 | */ | |
866f8763 A |
2561 | SecTrustResultType SecPVCLeafChecks(SecPVCRef pvc) { |
2562 | /* We need to compute details for the leaf. */ | |
2563 | CFRetainAssign(pvc->details, pvc->leafDetails); | |
2564 | ||
b1ab9ed8 A |
2565 | CFArrayRef policies = pvc->policies; |
2566 | CFIndex ix, count = CFArrayGetCount(policies); | |
2567 | for (ix = 0; ix < count; ++ix) { | |
2568 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix); | |
2569 | pvc->policyIX = ix; | |
2570 | /* Validate all keys for all policies. */ | |
2571 | pvc->callbacks = gSecPolicyLeafCallbacks; | |
2572 | CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc); | |
b1ab9ed8 A |
2573 | } |
2574 | ||
866f8763 A |
2575 | pvc->leafResult = pvc->result; |
2576 | CFRetainAssign(pvc->leafDetails, pvc->details); | |
2577 | ||
b1ab9ed8 A |
2578 | return pvc->result; |
2579 | } | |
2580 | ||
866f8763 A |
2581 | bool SecPVCIsOkResult(SecPVCRef pvc) { |
2582 | if (pvc->result == kSecTrustResultRecoverableTrustFailure || | |
2583 | pvc->result == kSecTrustResultDeny || | |
2584 | pvc->result == kSecTrustResultFatalTrustFailure || | |
2585 | pvc->result == kSecTrustResultOtherError) { | |
2586 | return false; | |
2587 | } | |
2588 | return true; | |
2589 | } | |
2590 | ||
b1ab9ed8 A |
2591 | bool SecPVCParentCertificateChecks(SecPVCRef pvc, CFIndex ix) { |
2592 | /* Check stuff common to intermediate and anchors. */ | |
866f8763 A |
2593 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); |
2594 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2595 | CFIndex anchor_ix = SecPVCGetCertificateCount(pvc) - 1; | |
2596 | bool is_anchor = (ix == anchor_ix && SecPathBuilderIsAnchored(pvc->builder)); | |
2597 | ||
2598 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
2599 | /* Certificate has expired. */ | |
2600 | if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckValidRoot | |
2601 | : kSecPolicyCheckValidIntermediates, ix, kCFBooleanFalse)) { | |
b1ab9ed8 | 2602 | goto errOut; |
866f8763 A |
2603 | } |
2604 | } | |
b1ab9ed8 | 2605 | |
3a7be6fd | 2606 | if (SecCertificateIsWeakKey(cert)) { |
e3d460c9 A |
2607 | /* Certificate uses weak key. */ |
2608 | if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckWeakRoot | |
866f8763 | 2609 | : kSecPolicyCheckWeakIntermediates, ix, kCFBooleanFalse)) { |
e3d460c9 | 2610 | goto errOut; |
866f8763 | 2611 | } |
e3d460c9 A |
2612 | } |
2613 | ||
b1ab9ed8 A |
2614 | if (is_anchor) { |
2615 | /* Perform anchor specific checks. */ | |
2616 | /* Don't think we have any of these. */ | |
2617 | } else { | |
2618 | /* Perform intermediate specific checks. */ | |
2619 | ||
866f8763 A |
2620 | /* (k) Basic constraints only relevant for v3 and later. */ |
2621 | if (SecCertificateVersion(cert) >= 3) { | |
2622 | const SecCEBasicConstraints *bc = | |
2623 | SecCertificateGetBasicConstraints(cert); | |
2624 | if (!bc || !bc->isCA) { | |
2625 | /* Basic constraints not present or not marked as isCA, illegal. */ | |
2626 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicConstraints, | |
2627 | ix, kCFBooleanFalse, true)) { | |
2628 | goto errOut; | |
2629 | } | |
2630 | } | |
2631 | } | |
2632 | /* For a v1 or v2 certificate in an intermediate slot (not a leaf and | |
2633 | not an anchor), we additionally require that the certificate chain | |
2634 | does not end in a v3 or later anchor. [rdar://32204517] */ | |
2635 | else if (ix > 0 && ix < anchor_ix) { | |
2636 | SecCertificateRef anchor = SecPVCGetCertificateAtIndex(pvc, anchor_ix); | |
2637 | if (SecCertificateVersion(anchor) >= 3) { | |
2638 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicConstraints, | |
2639 | ix, kCFBooleanFalse, true)) { | |
2640 | goto errOut; | |
2641 | } | |
2642 | } | |
2643 | } | |
2644 | /* (l) max_path_length is checked elsewhere. */ | |
b1ab9ed8 A |
2645 | |
2646 | /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */ | |
2647 | SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert); | |
2648 | if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) { | |
2649 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage, | |
866f8763 | 2650 | ix, kCFBooleanFalse, true)) { |
b1ab9ed8 | 2651 | goto errOut; |
866f8763 | 2652 | } |
b1ab9ed8 A |
2653 | } |
2654 | } | |
2655 | ||
2656 | errOut: | |
866f8763 | 2657 | return SecPVCIsOkResult(pvc); |
b1ab9ed8 A |
2658 | } |
2659 | ||
866f8763 | 2660 | static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) { |
b1ab9ed8 | 2661 | /* Check stuff common to intermediate and anchors. */ |
b1ab9ed8 | 2662 | |
427c49bc A |
2663 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); |
2664 | if (NULL != otapkiRef) | |
2665 | { | |
2666 | CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef); | |
2667 | CFRelease(otapkiRef); | |
2668 | if (NULL != blackListedKeys) | |
2669 | { | |
2670 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
3a7be6fd A |
2671 | CFIndex count = SecPVCGetCertificateCount(pvc); |
2672 | bool is_last = (ix == count - 1); | |
866f8763 | 2673 | bool is_anchor = (is_last && SecPathBuilderIsAnchored(pvc->builder)); |
3a7be6fd A |
2674 | if (!is_anchor) { |
2675 | /* Check for blacklisted intermediate issuer keys. */ | |
2676 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
2677 | if (dgst) { | |
2678 | /* Check dgst against blacklist. */ | |
2679 | if (CFSetContainsValue(blackListedKeys, dgst)) { | |
2680 | /* Check allow list for this blacklisted issuer key, | |
2681 | which is the authority key of the issued cert at ix-1. | |
3a7be6fd | 2682 | */ |
866f8763 A |
2683 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
2684 | bool allowed = path && SecCertificatePathVCIsAllowlisted(path); | |
3a7be6fd A |
2685 | if (!allowed) { |
2686 | SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey, | |
2687 | ix, kCFBooleanFalse, true); | |
866f8763 | 2688 | pvc->result = kSecTrustResultFatalTrustFailure; |
3a7be6fd | 2689 | } |
3a7be6fd A |
2690 | } |
2691 | CFRelease(dgst); | |
2692 | } | |
2693 | } | |
427c49bc | 2694 | CFRelease(blackListedKeys); |
866f8763 | 2695 | return SecPVCIsOkResult(pvc); |
427c49bc A |
2696 | } |
2697 | } | |
2698 | // Assume OK | |
2699 | return true; | |
2700 | } | |
2701 | ||
866f8763 | 2702 | static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix) |
427c49bc | 2703 | { |
3a7be6fd | 2704 | /* Check stuff common to intermediate and anchors. */ |
427c49bc A |
2705 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); |
2706 | if (NULL != otapkiRef) | |
2707 | { | |
2708 | CFSetRef grayListKeys = SecOTAPKICopyGrayList(otapkiRef); | |
2709 | CFRelease(otapkiRef); | |
2710 | if (NULL != grayListKeys) | |
2711 | { | |
2712 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
3a7be6fd A |
2713 | CFIndex count = SecPVCGetCertificateCount(pvc); |
2714 | bool is_last = (ix == count - 1); | |
866f8763 | 2715 | bool is_anchor = (is_last && SecPathBuilderIsAnchored(pvc->builder)); |
3a7be6fd A |
2716 | if (!is_anchor) { |
2717 | /* Check for gray listed intermediate issuer keys. */ | |
2718 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
2719 | if (dgst) { | |
2720 | /* Check dgst against gray list. */ | |
2721 | if (CFSetContainsValue(grayListKeys, dgst)) { | |
2722 | /* Check allow list for this graylisted issuer key, | |
2723 | which is the authority key of the issued cert at ix-1. | |
3a7be6fd | 2724 | */ |
866f8763 A |
2725 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
2726 | bool allowed = path && SecCertificatePathVCIsAllowlisted(path); | |
3a7be6fd A |
2727 | if (!allowed) { |
2728 | SecPVCSetResultForced(pvc, kSecPolicyCheckGrayListedKey, | |
2729 | ix, kCFBooleanFalse, true); | |
2730 | } | |
3a7be6fd A |
2731 | } |
2732 | CFRelease(dgst); | |
2733 | } | |
2734 | } | |
427c49bc | 2735 | CFRelease(grayListKeys); |
866f8763 | 2736 | return SecPVCIsOkResult(pvc); |
427c49bc A |
2737 | } |
2738 | } | |
2739 | // Assume ok | |
2740 | return true; | |
b1ab9ed8 A |
2741 | } |
2742 | ||
fa7225c8 A |
2743 | static bool SecPVCContainsPolicy(SecPVCRef pvc, CFStringRef searchOid, CFStringRef searchName, CFIndex *policyIX) { |
2744 | if (!isString(searchName) && !isString(searchOid)) { | |
2745 | return false; | |
2746 | } | |
2747 | CFArrayRef policies = pvc->policies; | |
2748 | CFIndex ix, count = CFArrayGetCount(policies); | |
2749 | for (ix = 0; ix < count; ++ix) { | |
2750 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix); | |
2751 | CFStringRef policyName = SecPolicyGetName(policy); | |
2752 | CFStringRef policyOid = SecPolicyGetOidString(policy); | |
2753 | /* Prefer a match of both name and OID */ | |
2754 | if (searchOid && searchName && policyOid && policyName) { | |
2755 | if (CFEqual(searchOid, policyOid) && | |
2756 | CFEqual(searchName, policyName)) { | |
2757 | if (policyIX) { *policyIX = ix; } | |
2758 | return true; | |
2759 | } | |
2760 | } | |
2761 | /* Next best is just OID. */ | |
2762 | if (!searchName && searchOid && policyOid) { | |
2763 | if (CFEqual(searchOid, policyOid)) { | |
2764 | if (policyIX) { *policyIX = ix; } | |
2765 | return true; | |
2766 | } | |
2767 | } | |
2768 | if (!searchOid && searchName && policyName) { | |
2769 | if (CFEqual(searchName, policyName)) { | |
2770 | if (policyIX) { *policyIX = ix; } | |
2771 | return true; | |
2772 | } | |
2773 | } | |
2774 | } | |
2775 | return false; | |
2776 | } | |
2777 | ||
2778 | static bool SecPVCContainsString(SecPVCRef pvc, CFIndex policyIX, CFStringRef stringValue) { | |
2779 | if (!isString(stringValue)) { | |
2780 | return false; | |
2781 | } | |
2782 | bool result = false; | |
2783 | ||
2784 | CFStringRef tmpStringValue = NULL; | |
2785 | if (CFStringGetCharacterAtIndex(stringValue, CFStringGetLength(stringValue) -1) == (UniChar)0x0000) { | |
2786 | tmpStringValue = CFStringCreateTruncatedCopy(stringValue, CFStringGetLength(stringValue) - 1); | |
2787 | } else { | |
2788 | tmpStringValue = CFStringCreateCopy(NULL, stringValue); | |
2789 | } | |
2790 | if (policyIX >= 0 && policyIX < CFArrayGetCount(pvc->policies)) { | |
b04fe171 | 2791 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, policyIX); |
fa7225c8 A |
2792 | /* Have to look for all the possible locations of name string */ |
2793 | CFStringRef policyString = NULL; | |
2794 | policyString = CFDictionaryGetValue(policy->_options, kSecPolicyCheckSSLHostname); | |
2795 | if (!policyString) { | |
2796 | policyString = CFDictionaryGetValue(policy->_options, kSecPolicyCheckEmail); | |
2797 | } | |
2798 | if (policyString && (CFStringCompare(tmpStringValue, policyString, kCFCompareCaseInsensitive) == kCFCompareEqualTo)) { | |
2799 | result = true; | |
2800 | goto out; | |
2801 | } | |
2802 | ||
2803 | CFArrayRef policyStrings = NULL; | |
2804 | policyStrings = CFDictionaryGetValue(policy->_options, kSecPolicyCheckEAPTrustedServerNames); | |
2805 | if (policyStrings && CFArrayContainsValue(policyStrings, | |
2806 | CFRangeMake(0, CFArrayGetCount(policyStrings)), | |
2807 | tmpStringValue)) { | |
2808 | result = true; | |
2809 | goto out; | |
2810 | } | |
2811 | } | |
2812 | ||
2813 | out: | |
2814 | CFReleaseNull(tmpStringValue); | |
2815 | return result; | |
2816 | } | |
2817 | ||
2818 | ||
2819 | static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber) { | |
2820 | uint32_t ourTSKeyUsage = 0; | |
2821 | uint32_t keyUsage = 0; | |
2822 | if (keyUsageNumber && | |
2823 | CFNumberGetValue(keyUsageNumber, kCFNumberSInt32Type, &keyUsage)) { | |
2824 | if (keyUsage & kSecKeyUsageDigitalSignature) { | |
2825 | ourTSKeyUsage |= kSecTrustSettingsKeyUseSignature; | |
2826 | } | |
2827 | if (keyUsage & kSecKeyUsageDataEncipherment) { | |
2828 | ourTSKeyUsage |= kSecTrustSettingsKeyUseEnDecryptData; | |
2829 | } | |
2830 | if (keyUsage & kSecKeyUsageKeyEncipherment) { | |
2831 | ourTSKeyUsage |= kSecTrustSettingsKeyUseEnDecryptKey; | |
2832 | } | |
2833 | if (keyUsage & kSecKeyUsageKeyAgreement) { | |
2834 | ourTSKeyUsage |= kSecTrustSettingsKeyUseKeyExchange; | |
2835 | } | |
2836 | if (keyUsage == kSecKeyUsageAll) { | |
2837 | ourTSKeyUsage = kSecTrustSettingsKeyUseAny; | |
2838 | } | |
2839 | } | |
2840 | return ourTSKeyUsage; | |
2841 | } | |
2842 | ||
2843 | static uint32_t ts_key_usage_for_policy(SecPolicyRef policy) { | |
2844 | uint32_t ourTSKeyUsage = 0; | |
2845 | CFTypeRef policyKeyUsageType = NULL; | |
2846 | ||
2847 | policyKeyUsageType = (CFTypeRef)CFDictionaryGetValue(policy->_options, kSecPolicyCheckKeyUsage); | |
2848 | if (isArray(policyKeyUsageType)) { | |
2849 | CFIndex ix, count = CFArrayGetCount(policyKeyUsageType); | |
2850 | for (ix = 0; ix < count; ix++) { | |
2851 | CFNumberRef policyKeyUsageNumber = NULL; | |
2852 | policyKeyUsageNumber = (CFNumberRef)CFArrayGetValueAtIndex(policyKeyUsageType, ix); | |
2853 | ourTSKeyUsage |= ts_key_usage_for_kuNumber(policyKeyUsageNumber); | |
2854 | } | |
2855 | } else if (isNumber(policyKeyUsageType)) { | |
2856 | ourTSKeyUsage |= ts_key_usage_for_kuNumber(policyKeyUsageType); | |
2857 | } | |
2858 | ||
2859 | return ourTSKeyUsage; | |
2860 | } | |
2861 | ||
2862 | static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc, | |
2863 | SecCertificateRef certificate, CFIndex policyIX, CFNumberRef keyUsageNumber) { | |
2864 | int64_t keyUsageValue = 0; | |
2865 | uint32_t ourKeyUsage = 0; | |
2866 | ||
2867 | if (!isNumber(keyUsageNumber) || !CFNumberGetValue(keyUsageNumber, kCFNumberSInt64Type, &keyUsageValue)) { | |
2868 | return false; | |
2869 | } | |
2870 | ||
2871 | if (keyUsageValue == kSecTrustSettingsKeyUseAny) { | |
2872 | return true; | |
2873 | } | |
2874 | ||
2875 | /* We're using the key for revocation if we have the OCSPSigner policy. | |
2876 | * @@@ If we support CRLs, we'd need to check for that policy here too. | |
2877 | */ | |
2878 | if (SecPVCContainsPolicy(pvc, kSecPolicyAppleOCSPSigner, NULL, NULL)) { | |
2879 | ourKeyUsage |= kSecTrustSettingsKeyUseSignRevocation; | |
2880 | } | |
2881 | ||
2882 | /* We're using the key for verifying a cert if it's a root/intermediate | |
2883 | * in the chain. If the cert isn't in the path yet, we're about to add it, | |
2884 | * so it's a root/intermediate. If there is no path, this is the leaf. | |
2885 | */ | |
2886 | CFIndex pathIndex = -1; | |
866f8763 A |
2887 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
2888 | if (path) { | |
2889 | pathIndex = SecCertificatePathVCGetIndexOfCertificate(path, certificate); | |
fa7225c8 A |
2890 | } else { |
2891 | pathIndex = 0; | |
2892 | } | |
2893 | if (pathIndex != 0) { | |
2894 | ourKeyUsage |= kSecTrustSettingsKeyUseSignCert; | |
2895 | } | |
2896 | ||
2897 | /* The rest of the key usages may be specified by the policy(ies). */ | |
2898 | if (policyIX >= 0 && policyIX < CFArrayGetCount(pvc->policies)) { | |
2899 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, policyIX); | |
2900 | ourKeyUsage |= ts_key_usage_for_policy(policy); | |
2901 | } else { | |
2902 | /* Get key usage from ALL policies */ | |
2903 | CFIndex ix, count = CFArrayGetCount(pvc->policies); | |
2904 | for (ix = 0; ix < count; ix++) { | |
2905 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, ix); | |
2906 | ourKeyUsage |= ts_key_usage_for_policy(policy); | |
2907 | } | |
2908 | } | |
2909 | ||
2910 | if (ourKeyUsage == (uint32_t)(keyUsageValue & 0x00ffffffff)) { | |
2911 | return true; | |
2912 | } | |
2913 | ||
2914 | return false; | |
2915 | } | |
2916 | ||
866f8763 | 2917 | #if TARGET_OS_OSX |
fa7225c8 | 2918 | #include <Security/SecTrustedApplicationPriv.h> |
866f8763 A |
2919 | #include <Security/SecTask.h> |
2920 | #include <Security/SecTaskPriv.h> | |
fa7225c8 A |
2921 | #include <bsm/libbsm.h> |
2922 | #include <libproc.h> | |
2923 | ||
866f8763 A |
2924 | extern OSStatus SecTaskValidateForRequirement(SecTaskRef task, CFStringRef requirement); |
2925 | ||
fa7225c8 A |
2926 | static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken, CFTypeRef appRef) { |
2927 | bool result = false; | |
2928 | audit_token_t auditToken = {}; | |
866f8763 A |
2929 | SecTaskRef task = NULL; |
2930 | SecRequirementRef requirement = NULL; | |
2931 | CFStringRef stringRequirement = NULL; | |
fa7225c8 A |
2932 | |
2933 | require(appRef && clientAuditToken, out); | |
2934 | require(CFGetTypeID(appRef) == SecTrustedApplicationGetTypeID(), out); | |
866f8763 A |
2935 | require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef)appRef, &requirement), out); |
2936 | require(requirement, out); | |
2937 | require_noerr(SecRequirementsCopyString(requirement, kSecCSDefaultFlags, &stringRequirement), out); | |
2938 | require(stringRequirement, out); | |
fa7225c8 A |
2939 | |
2940 | require(sizeof(auditToken) == CFDataGetLength(clientAuditToken), out); | |
2941 | CFDataGetBytes(clientAuditToken, CFRangeMake(0, sizeof(auditToken)), (uint8_t *)&auditToken); | |
866f8763 | 2942 | require(task = SecTaskCreateWithAuditToken(NULL, auditToken), out); |
fa7225c8 | 2943 | |
866f8763 | 2944 | if(errSecSuccess == SecTaskValidateForRequirement(task, stringRequirement)) { |
fa7225c8 A |
2945 | result = true; |
2946 | } | |
2947 | ||
2948 | out: | |
866f8763 A |
2949 | CFReleaseNull(task); |
2950 | CFReleaseNull(requirement); | |
2951 | CFReleaseNull(stringRequirement); | |
fa7225c8 A |
2952 | return result; |
2953 | } | |
2954 | #endif | |
2955 | ||
6b200bc3 A |
2956 | static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc, CFDictionaryRef options) { |
2957 | if (!isDictionary(options)) { | |
2958 | return false; | |
2959 | } | |
2960 | ||
2961 | /* Push */ | |
2962 | CFDictionaryRef currentCallbacks = pvc->callbacks; | |
2963 | ||
2964 | /* We need to run the leaf and path checks using these options. */ | |
2965 | pvc->callbacks = gSecPolicyLeafCallbacks; | |
2966 | CFDictionaryApplyFunction(options, SecPVCValidateKey, pvc); | |
2967 | ||
2968 | pvc->callbacks = gSecPolicyPathCallbacks; | |
2969 | CFDictionaryApplyFunction(options, SecPVCValidateKey, pvc); | |
2970 | ||
2971 | /* Pop */ | |
2972 | pvc->callbacks = currentCallbacks; | |
2973 | ||
2974 | /* Our work here is done; no need to claim a match */ | |
2975 | return false; | |
2976 | } | |
2977 | ||
fa7225c8 A |
2978 | static bool SecPVCMeetsConstraint(SecPVCRef pvc, SecCertificateRef certificate, CFDictionaryRef constraint) { |
2979 | CFStringRef policyOid = NULL, policyString = NULL, policyName = NULL; | |
2980 | CFNumberRef keyUsageNumber = NULL; | |
2981 | CFTypeRef trustedApplicationData = NULL; | |
6b200bc3 | 2982 | CFDictionaryRef policyOptions = NULL; |
fa7225c8 | 2983 | |
6b200bc3 A |
2984 | bool policyMatch = false, policyStringMatch = false, applicationMatch = false , |
2985 | keyUsageMatch = false, policyOptionMatch = false; | |
fa7225c8 A |
2986 | bool result = false; |
2987 | ||
2988 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE | |
2989 | /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */ | |
2990 | SecPolicyRef policy = NULL; | |
2991 | policy = (SecPolicyRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicy); | |
2992 | policyOid = (policy) ? policy->_oid : NULL; | |
2993 | #else | |
2994 | policyOid = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicy); | |
2995 | #endif | |
2996 | policyName = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyName); | |
2997 | policyString = (CFStringRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyString); | |
2998 | keyUsageNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsKeyUsage); | |
6b200bc3 | 2999 | policyOptions = (CFDictionaryRef)CFDictionaryGetValue(constraint, kSecTrustSettingsPolicyOptions); |
fa7225c8 A |
3000 | |
3001 | CFIndex policyIX = -1; | |
3002 | policyMatch = SecPVCContainsPolicy(pvc, policyOid, policyName, &policyIX); | |
3003 | policyStringMatch = SecPVCContainsString(pvc, policyIX, policyString); | |
3004 | keyUsageMatch = SecPVCContainsTrustSettingsKeyUsage(pvc, certificate, policyIX, keyUsageNumber); | |
6b200bc3 | 3005 | policyOptionMatch = SecPVCContainsTrustSettingsPolicyOption(pvc, policyOptions); |
fa7225c8 A |
3006 | |
3007 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE | |
3008 | trustedApplicationData = CFDictionaryGetValue(constraint, kSecTrustSettingsApplication); | |
3009 | CFDataRef clientAuditToken = SecPathBuilderCopyClientAuditToken(pvc->builder); | |
3010 | applicationMatch = SecPVCCallerIsApplication(clientAuditToken, trustedApplicationData); | |
3011 | CFReleaseNull(clientAuditToken); | |
3012 | #else | |
3013 | if(CFDictionaryContainsKey(constraint, kSecTrustSettingsApplication)) { | |
3014 | secerror("kSecTrustSettingsApplication is not yet supported on this platform"); | |
3015 | } | |
3016 | #endif | |
3017 | ||
3018 | /* If we either didn't find the parameter in the dictionary or we got a match | |
3019 | * against that parameter, for all possible parameters in the dictionary, then | |
3020 | * this trust setting result applies to the output. */ | |
3021 | if (((!policyOid && !policyName) || policyMatch) && | |
3022 | (!policyString || policyStringMatch) && | |
3023 | (!trustedApplicationData || applicationMatch) && | |
6b200bc3 A |
3024 | (!keyUsageNumber || keyUsageMatch) && |
3025 | (!policyOptions || policyOptionMatch)) { | |
fa7225c8 A |
3026 | result = true; |
3027 | } | |
3028 | ||
3029 | return result; | |
3030 | } | |
3031 | ||
3032 | SecTrustSettingsResult SecPVCGetTrustSettingsResult(SecPVCRef pvc, SecCertificateRef certificate, CFArrayRef constraints) { | |
3033 | SecTrustSettingsResult result = kSecTrustSettingsResultInvalid; | |
3034 | CFIndex constraintIX, constraintCount = CFArrayGetCount(constraints); | |
3035 | for (constraintIX = 0; constraintIX < constraintCount; constraintIX++) { | |
3036 | CFDictionaryRef constraint = (CFDictionaryRef)CFArrayGetValueAtIndex(constraints, constraintIX); | |
3037 | if (!isDictionary(constraint)) { | |
3038 | continue; | |
3039 | } | |
3040 | ||
3041 | CFNumberRef resultNumber = NULL; | |
3042 | resultNumber = (CFNumberRef)CFDictionaryGetValue(constraint, kSecTrustSettingsResult); | |
3043 | uint32_t resultValue = kSecTrustSettingsResultInvalid; | |
3044 | if (!isNumber(resultNumber) || !CFNumberGetValue(resultNumber, kCFNumberSInt32Type, &resultValue)) { | |
3045 | /* no SecTrustSettingsResult entry defaults to TrustRoot*/ | |
3046 | resultValue = kSecTrustSettingsResultTrustRoot; | |
3047 | } | |
3048 | ||
3049 | if (SecPVCMeetsConstraint(pvc, certificate, constraint)) { | |
3050 | result = resultValue; | |
3051 | break; | |
3052 | } | |
3053 | } | |
3054 | return result; | |
3055 | } | |
3056 | ||
866f8763 A |
3057 | static void SecPVCCheckUsageConstraints(SecPVCRef pvc) { |
3058 | CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc); | |
fa7225c8 | 3059 | for (certIX = 0; certIX < certCount; certIX++) { |
866f8763 A |
3060 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); |
3061 | CFArrayRef constraints = SecCertificatePathVCGetUsageConstraintsAtIndex(path, certIX); | |
3062 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, certIX); | |
fa7225c8 A |
3063 | SecTrustSettingsResult result = SecPVCGetTrustSettingsResult(pvc, cert, constraints); |
3064 | ||
866f8763 | 3065 | /* Set the pvc trust result based on the usage constraints and anchor source. */ |
fa7225c8 A |
3066 | if (result == kSecTrustSettingsResultDeny) { |
3067 | SecPVCSetResultForced(pvc, kSecPolicyCheckUsageConstraints, certIX, kCFBooleanFalse, true); | |
866f8763 A |
3068 | pvc->result = kSecTrustResultDeny; |
3069 | } else if ((result == kSecTrustSettingsResultTrustRoot || result == kSecTrustSettingsResultTrustAsRoot || | |
3070 | result == kSecTrustSettingsResultInvalid) && SecPVCIsOkResult(pvc)) { | |
3071 | /* If we already think the PVC is ok and this cert is from one of the user/ | |
3072 | * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints), | |
3073 | * all mean we should use the special "Proceed" trust result. */ | |
3074 | #if TARGET_OS_IPHONE | |
3075 | if (SecPathBuilderIsAnchorSource(pvc->builder, kSecUserAnchorSource) && | |
3076 | SecCertificateSourceContains(kSecUserAnchorSource, cert)) { | |
3077 | #else | |
3078 | if (SecPathBuilderIsAnchorSource(pvc->builder, kSecLegacyAnchorSource) && | |
3079 | SecCertificateSourceContains(kSecLegacyAnchorSource, cert)) { | |
3080 | #endif | |
3081 | pvc->result = kSecTrustResultProceed; | |
3082 | } | |
fa7225c8 A |
3083 | } |
3084 | } | |
fa7225c8 A |
3085 | } |
3086 | ||
b04fe171 A |
3087 | #define kSecPolicySHA256Size 32 |
3088 | static const UInt8 kTestDateConstraintsRoot[kSecPolicySHA256Size] = { | |
3089 | 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C, | |
3090 | 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D | |
3091 | }; | |
3092 | static const UInt8 kWS_CA1_G2[kSecPolicySHA256Size] = { | |
3093 | 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2, | |
3094 | 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16 | |
3095 | }; | |
3096 | static const UInt8 kWS_CA1_NEW[kSecPolicySHA256Size] = { | |
3097 | 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47, | |
3098 | 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08 | |
3099 | }; | |
3100 | static const UInt8 kWS_CA2_NEW[kSecPolicySHA256Size] = { | |
3101 | 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39, | |
3102 | 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54 | |
3103 | }; | |
3104 | static const UInt8 kWS_ECC[kSecPolicySHA256Size] = { | |
3105 | 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB, | |
3106 | 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02 | |
3107 | }; | |
3108 | static const UInt8 kSC_SFSCA[kSecPolicySHA256Size] = { | |
3109 | 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13, | |
3110 | 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA | |
3111 | }; | |
3112 | static const UInt8 kSC_SHA2[kSecPolicySHA256Size] = { | |
3113 | 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37, | |
3114 | 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11 | |
3115 | }; | |
3116 | static const UInt8 kSC_G2[kSecPolicySHA256Size] = { | |
3117 | 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37, | |
3118 | 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95 | |
3119 | }; | |
3120 | ||
866f8763 | 3121 | static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc) { |
b04fe171 A |
3122 | static CFSetRef sConstrainedRoots = NULL; |
3123 | static dispatch_once_t _t; | |
3124 | dispatch_once(&_t, ^{ | |
3125 | const UInt8 *v_hashes[] = { | |
3126 | kWS_CA1_G2, kWS_CA1_NEW, kWS_CA2_NEW, kWS_ECC, | |
3127 | kSC_SFSCA, kSC_SHA2, kSC_G2, kTestDateConstraintsRoot | |
3128 | }; | |
3129 | CFMutableSetRef set = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks); | |
3130 | CFIndex ix, count = sizeof(v_hashes)/sizeof(*v_hashes); | |
3131 | for (ix=0; ix<count; ix++) { | |
3132 | CFDataRef hash = CFDataCreateWithBytesNoCopy(NULL, v_hashes[ix], | |
3133 | kSecPolicySHA256Size, kCFAllocatorNull); | |
3134 | if (hash) { | |
3135 | CFSetAddValue(set, hash); | |
3136 | CFRelease(hash); | |
3137 | } | |
3138 | } | |
3139 | sConstrainedRoots = set; | |
3140 | }); | |
3141 | ||
3142 | bool shouldDeny = false; | |
866f8763 | 3143 | CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc); |
b04fe171 | 3144 | for (certIX = certCount - 1; certIX >= 0 && !shouldDeny; certIX--) { |
866f8763 | 3145 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, certIX); |
b04fe171 A |
3146 | CFDataRef sha256 = SecCertificateCopySHA256Digest(cert); |
3147 | if (sha256 && CFSetContainsValue(sConstrainedRoots, sha256)) { | |
3148 | /* matched a constrained root; check notBefore dates on all its children. */ | |
3149 | CFIndex childIX = certIX; | |
3150 | while (--childIX >= 0) { | |
866f8763 | 3151 | SecCertificateRef child = SecPVCGetCertificateAtIndex(pvc, childIX); |
b04fe171 A |
3152 | /* 1 Dec 2016 00:00:00 GMT */ |
3153 | if (child && (CFAbsoluteTime)502243200.0 <= SecCertificateNotValidBefore(child)) { | |
3154 | SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey, certIX, kCFBooleanFalse, true); | |
866f8763 | 3155 | pvc->result = kSecTrustResultFatalTrustFailure; |
b04fe171 A |
3156 | shouldDeny = true; |
3157 | break; | |
3158 | } | |
3159 | } | |
3160 | } | |
3161 | CFReleaseNull(sha256); | |
3162 | } | |
b04fe171 A |
3163 | } |
3164 | ||
b1ab9ed8 A |
3165 | /* AUDIT[securityd](done): |
3166 | policy->_options is a caller provided dictionary, only its cf type has | |
3167 | been checked. | |
3168 | */ | |
866f8763 A |
3169 | void SecPVCPathChecks(SecPVCRef pvc) { |
3170 | secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc->builder)); | |
3171 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); | |
b1ab9ed8 A |
3172 | /* This needs to be initialized before we call any function that might call |
3173 | SecPVCSetResultForced(). */ | |
3174 | pvc->policyIX = 0; | |
3175 | SecPolicyCheckIdLinkage(pvc, kSecPolicyCheckIdLinkage); | |
866f8763 A |
3176 | if (SecPVCIsOkResult(pvc) || pvc->details) { |
3177 | /* @@@ This theoretically only needs to be done once per path, but since | |
3178 | this function affects the pvc result, we'll run it every time. */ | |
b1ab9ed8 A |
3179 | SecPolicyCheckBasicCertificateProcessing(pvc, |
3180 | kSecPolicyCheckBasicCertificateProcessing); | |
3181 | } | |
3182 | ||
3183 | CFArrayRef policies = pvc->policies; | |
b04fe171 A |
3184 | CFIndex count = CFArrayGetCount(policies); |
3185 | for (; pvc->policyIX < count; ++pvc->policyIX) { | |
b1ab9ed8 A |
3186 | /* Validate all keys for all policies. */ |
3187 | pvc->callbacks = gSecPolicyPathCallbacks; | |
b04fe171 | 3188 | SecPolicyRef policy = SecPVCGetPolicy(pvc); |
b1ab9ed8 | 3189 | CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc); |
866f8763 A |
3190 | if (!SecPVCIsOkResult(pvc) && !pvc->details) |
3191 | return; | |
b04fe171 | 3192 | } |
b1ab9ed8 | 3193 | |
6b200bc3 A |
3194 | // Reset |
3195 | pvc->policyIX = 0; | |
3196 | ||
fa7225c8 | 3197 | /* Check whether the TrustSettings say to deny a cert in the path. */ |
866f8763 A |
3198 | SecPVCCheckUsageConstraints(pvc); |
3199 | ||
3200 | /* Check for Blocklisted certs */ | |
3201 | SecPVCCheckIssuerDateConstraints(pvc); | |
3202 | CFIndex ix; | |
3203 | count = SecCertificatePathVCGetCount(path); | |
3204 | for (ix = 1; ix < count; ix++) { | |
3205 | SecPVCGrayListedKeyChecks(pvc, ix); | |
3206 | SecPVCBlackListedKeyChecks(pvc, ix); | |
3207 | } | |
3208 | ||
3209 | /* Path-based check tests. */ | |
3210 | if (!SecCertificatePathVCIsPathValidated(path)) { | |
3211 | bool ev_check_ok = false; | |
3212 | if (SecCertificatePathVCIsOptionallyEV(path)) { | |
3213 | SecTrustResultType pre_ev_check_result = pvc->result; | |
3214 | SecPolicyCheckEV(pvc, kSecPolicyCheckExtendedValidation); | |
3215 | ev_check_ok = SecPVCIsOkResult(pvc); | |
3216 | /* If ev checking failed, we still want to accept this chain | |
3217 | as a non EV one, if it was valid as such. */ | |
3218 | pvc->result = pre_ev_check_result; | |
3219 | } | |
3220 | ||
3221 | /* Check for CT */ | |
5c19dc3a A |
3222 | /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */ |
3223 | SecPolicyCheckCT(pvc, kSecPolicyCheckCertificateTransparency); | |
5c19dc3a | 3224 | |
866f8763 A |
3225 | /* Certs are only EV if they are also CT verified */ |
3226 | if (ev_check_ok && SecCertificatePathVCIsCT(path)) { | |
3227 | SecCertificatePathVCSetIsEV(path, true); | |
3228 | } | |
fa7225c8 | 3229 | } |
5c19dc3a | 3230 | |
b1ab9ed8 | 3231 | //errOut: |
866f8763 A |
3232 | secdebug("policy", "end %strusted path: %@", |
3233 | (SecPVCIsOkResult(pvc) ? "" : "not "), SecPathBuilderGetPath(pvc->builder)); | |
3234 | ||
3235 | SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc->builder)); | |
3236 | return; | |
b1ab9ed8 A |
3237 | } |
3238 | ||
866f8763 A |
3239 | void SecPVCPathCheckRevocationRequired(SecPVCRef pvc) { |
3240 | SecCertificatePathVCRef path = SecPathBuilderGetPath(pvc->builder); | |
3241 | CFIndex ix, certCount = SecCertificatePathVCGetCount(path); | |
3242 | for (ix = 0; ix < certCount; ix++) { | |
3243 | /* If we require revocation (for that cert per the SecCertificateVCRef or | |
3244 | * per the pvc) */ | |
3245 | if (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path, ix) || | |
3246 | ((ix == 0) && pvc->require_revocation_response)) { | |
3247 | /* Do we have a valid revocation response? */ | |
3248 | SecRVCRef rvc = SecCertificatePathVCGetRVCAtIndex(path, ix); | |
3249 | if (SecRVCGetEarliestNextUpdate(rvc) == NULL_TIME) { | |
3250 | SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired, | |
3251 | ix, kCFBooleanFalse, true); | |
b1ab9ed8 | 3252 | } |
b1ab9ed8 | 3253 | } |
b1ab9ed8 | 3254 | } |
b1ab9ed8 | 3255 | } |