]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
427c49bc | 2 | * Copyright (c) 2008-2012 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
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. | |
12 | * | |
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. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | /* | |
25 | * SecPolicyServer.c - Trust policies dealing with certificate revocation. | |
26 | */ | |
27 | ||
28 | #include <securityd/SecPolicyServer.h> | |
29 | #include <Security/SecPolicyInternal.h> | |
30 | #include <Security/SecPolicyPriv.h> | |
427c49bc | 31 | #include <utilities/SecIOFormat.h> |
b1ab9ed8 A |
32 | #include <securityd/asynchttp.h> |
33 | #include <securityd/policytree.h> | |
b1ab9ed8 A |
34 | #include <CoreFoundation/CFTimeZone.h> |
35 | #include <wctype.h> | |
36 | #include <libDER/oids.h> | |
37 | #include <CoreFoundation/CFNumber.h> | |
38 | #include <Security/SecCertificateInternal.h> | |
39 | #include <AssertMacros.h> | |
427c49bc | 40 | #include <utilities/debugging.h> |
b1ab9ed8 A |
41 | #include <security_asn1/SecAsn1Coder.h> |
42 | #include <security_asn1/ocspTemplates.h> | |
43 | #include <security_asn1/oidsalg.h> | |
44 | #include <security_asn1/oidsocsp.h> | |
45 | #include <CommonCrypto/CommonDigest.h> | |
46 | #include <Security/SecFramework.h> | |
47 | #include <Security/SecPolicyInternal.h> | |
48 | #include <Security/SecTrustPriv.h> | |
49 | #include <Security/SecInternal.h> | |
50 | #include <CFNetwork/CFHTTPMessage.h> | |
51 | #include <CFNetwork/CFHTTPStream.h> | |
52 | #include <SystemConfiguration/SCDynamicStoreCopySpecific.h> | |
53 | #include <asl.h> | |
54 | #include <securityd/SecOCSPRequest.h> | |
55 | #include <securityd/SecOCSPResponse.h> | |
56 | #include <securityd/asynchttp.h> | |
57 | #include <securityd/SecTrustServer.h> | |
58 | #include <securityd/SecOCSPCache.h> | |
427c49bc A |
59 | #include <utilities/array_size.h> |
60 | #include <utilities/SecCFWrappers.h> | |
61 | #include "OTATrustUtilities.h" | |
b1ab9ed8 A |
62 | |
63 | #define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args) | |
64 | ||
65 | /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */ | |
66 | #ifndef DUMP_OCSPRESPONSES | |
67 | #define DUMP_OCSPRESPONSES 0 | |
68 | #endif | |
69 | ||
70 | #if DUMP_OCSPRESPONSES | |
71 | ||
72 | #include <unistd.h> | |
73 | #include <fcntl.h> | |
74 | ||
75 | static void secdumpdata(CFDataRef data, const char *name) { | |
76 | int fd = open(name, O_CREAT | O_WRONLY | O_TRUNC, 0666); | |
77 | write(fd, CFDataGetBytePtr(data), CFDataGetLength(data)); | |
78 | close(fd); | |
79 | } | |
80 | ||
81 | #endif | |
82 | ||
427c49bc | 83 | |
b1ab9ed8 A |
84 | /******************************************************** |
85 | ****************** SecPolicy object ******************** | |
86 | ********************************************************/ | |
87 | ||
88 | static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL; | |
89 | static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL; | |
b1ab9ed8 | 90 | |
427c49bc A |
91 | static CFArrayRef SecPolicyAnchorDigestsForEVPolicy(const DERItem *policyOID) |
92 | { | |
93 | CFArrayRef result = NULL; | |
94 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
95 | if (NULL == otapkiRef) | |
96 | { | |
97 | return result; | |
98 | } | |
99 | ||
100 | CFDictionaryRef evToPolicyAnchorDigest = SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef); | |
101 | CFRelease(otapkiRef); | |
102 | ||
103 | if (NULL == evToPolicyAnchorDigest) | |
104 | { | |
105 | return result; | |
b1ab9ed8 | 106 | } |
b1ab9ed8 | 107 | |
b1ab9ed8 | 108 | CFArrayRef roots = NULL; |
427c49bc A |
109 | CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, policyOID); |
110 | if (oid && evToPolicyAnchorDigest) | |
111 | { | |
112 | result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid); | |
113 | if (roots && CFGetTypeID(result) != CFArrayGetTypeID()) | |
114 | { | |
b1ab9ed8 | 115 | ocspdErrorLog("EVRoot.plist has non array value"); |
427c49bc | 116 | result = NULL; |
b1ab9ed8 A |
117 | } |
118 | CFRelease(oid); | |
119 | } | |
427c49bc | 120 | return result; |
b1ab9ed8 A |
121 | } |
122 | ||
123 | ||
124 | static bool SecPolicyIsEVPolicy(const DERItem *policyOID) { | |
125 | return SecPolicyAnchorDigestsForEVPolicy(policyOID); | |
126 | } | |
127 | ||
128 | static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate, | |
129 | policy_set_t valid_policies) { | |
130 | /* Ensure that this certificate is a valid anchor for one of the | |
131 | certificate policy oids specified in the leaf. */ | |
132 | CFDataRef digest = SecCertificateGetSHA1Digest(certificate); | |
133 | policy_set_t ix; | |
134 | bool good_ev_anchor = false; | |
135 | for (ix = valid_policies; ix; ix = ix->oid_next) { | |
136 | CFArrayRef digests = SecPolicyAnchorDigestsForEVPolicy(&ix->oid); | |
137 | if (digests && CFArrayContainsValue(digests, | |
138 | CFRangeMake(0, CFArrayGetCount(digests)), digest)) { | |
139 | secdebug("ev", "found anchor for policy oid"); | |
140 | good_ev_anchor = true; | |
141 | break; | |
142 | } | |
143 | } | |
144 | require_quiet(good_ev_anchor, notEV); | |
145 | ||
146 | CFAbsoluteTime october2006 = 178761600; | |
147 | if (SecCertificateVersion(certificate) >= 3 | |
148 | && SecCertificateNotValidBefore(certificate) >= october2006) { | |
149 | const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate); | |
150 | require_quiet(bc && bc->isCA == true, notEV); | |
151 | SecKeyUsage ku = SecCertificateGetKeyUsage(certificate); | |
152 | require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) | |
153 | == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV); | |
154 | } | |
155 | ||
156 | CFAbsoluteTime jan2011 = 315532800; | |
157 | if (SecCertificateNotValidBefore(certificate) < jan2011) { | |
158 | /* At least MD5, SHA-1 with RSA 2048 or ECC NIST P-256. */ | |
159 | } else { | |
160 | /* At least SHA-1, SHA-256, SHA-384 or SHA-512 with RSA 2048 or | |
161 | ECC NIST P-256. */ | |
162 | } | |
163 | ||
164 | return true; | |
165 | notEV: | |
166 | return false; | |
167 | } | |
168 | ||
169 | static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate) { | |
170 | const SecCECertificatePolicies *cp; | |
171 | cp = SecCertificateGetCertificatePolicies(certificate); | |
172 | require_quiet(cp && cp->numPolicies > 0, notEV); | |
173 | /* SecCertificateGetCRLDistributionPoints() is a noop right now */ | |
174 | #if 0 | |
175 | CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate); | |
176 | require_quiet(cdp && CFArrayGetCount(cdp) > 0, notEV); | |
177 | #endif | |
178 | const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate); | |
179 | require_quiet(bc && bc->isCA == true, notEV); | |
180 | SecKeyUsage ku = SecCertificateGetKeyUsage(certificate); | |
181 | require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) | |
182 | == (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign), notEV); | |
183 | CFAbsoluteTime jan2011 = 315532800; | |
184 | if (SecCertificateNotValidBefore(certificate) < jan2011) { | |
185 | /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */ | |
186 | } else { | |
187 | /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or | |
188 | ECC NIST P-256. */ | |
189 | } | |
190 | ||
191 | return true; | |
192 | notEV: | |
193 | return false; | |
194 | } | |
195 | ||
196 | bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate) { | |
197 | /* 3. Subscriber Certificate. */ | |
198 | ||
199 | /* (a) certificate Policies */ | |
200 | const SecCECertificatePolicies *cp; | |
201 | cp = SecCertificateGetCertificatePolicies(certificate); | |
202 | require_quiet(cp && cp->numPolicies > 0, notEV); | |
203 | /* Now find at least one policy in here that has a qualifierID of id-qt 2 | |
204 | and a policyQualifier that is a URI to the CPS and an EV policy OID. */ | |
205 | uint32_t ix = 0; | |
206 | bool found_ev_anchor_for_leaf_policy = false; | |
207 | for (ix = 0; ix < cp->numPolicies; ++ix) { | |
208 | if (SecPolicyIsEVPolicy(&cp->policies[ix].policyIdentifier)) { | |
209 | found_ev_anchor_for_leaf_policy = true; | |
210 | } | |
211 | } | |
212 | require_quiet(found_ev_anchor_for_leaf_policy, notEV); | |
213 | ||
214 | /* SecCertificateGetCRLDistributionPoints() is a noop right now */ | |
215 | #if 0 | |
216 | /* (b) cRLDistributionPoint | |
217 | (c) authorityInformationAccess */ | |
218 | CFArrayRef cdp = SecCertificateGetCRLDistributionPoints(certificate); | |
219 | if (cdp) { | |
220 | require_quiet(CFArrayGetCount(cdp) > 0, notEV); | |
221 | } else { | |
222 | CFArrayRef or = SecCertificateGetOCSPResponders(certificate); | |
223 | require_quiet(or && CFArrayGetCount(or) > 0, notEV); | |
224 | //CFArrayRef ci = SecCertificateGetCAIssuers(certificate); | |
225 | } | |
226 | #endif | |
227 | ||
228 | /* (d) basicConstraints | |
229 | If present, the cA field MUST be set false. */ | |
230 | const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate); | |
231 | if (bc) { | |
232 | require_quiet(bc->isCA == false, notEV); | |
233 | } | |
234 | ||
235 | /* (e) keyUsage. */ | |
236 | SecKeyUsage ku = SecCertificateGetKeyUsage(certificate); | |
237 | if (ku) { | |
238 | require_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV); | |
239 | } | |
240 | ||
241 | #if 0 | |
242 | /* The EV Cert Spec errata specifies this, though this is a check for SSL | |
243 | not specifically EV. */ | |
244 | ||
245 | /* (e) extKeyUsage | |
246 | ||
247 | Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */ | |
248 | SecCertificateCopyExtendedKeyUsage(certificate); | |
249 | #endif | |
250 | ||
251 | CFAbsoluteTime jan2011 = 315532800; | |
252 | if (SecCertificateNotValidAfter(certificate) < jan2011) { | |
253 | /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */ | |
254 | } else { | |
255 | /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or | |
256 | ECC NIST P-256. */ | |
257 | } | |
258 | ||
259 | return true; | |
260 | notEV: | |
261 | return false; | |
262 | } | |
263 | ||
264 | /******************************************************** | |
265 | **************** SecPolicy Callbacks ******************* | |
266 | ********************************************************/ | |
267 | static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc, | |
268 | CFStringRef key) { | |
269 | } | |
270 | ||
271 | static void SecPolicyCheckIdLinkage(SecPVCRef pvc, | |
272 | CFStringRef key) { | |
273 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
274 | CFDataRef parentSubjectKeyID = NULL; | |
275 | for (ix = count - 1; ix >= 0; --ix) { | |
276 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
277 | /* If the previous certificate in the chain had a SubjectKeyID, | |
278 | make sure it matches the current certificates AuthorityKeyID. */ | |
279 | if (parentSubjectKeyID) { | |
280 | /* @@@ According to RFC 2459 neither AuthorityKeyID nor | |
281 | SubjectKeyID can be critical. Currenty we don't check | |
282 | for this. */ | |
283 | CFDataRef authorityKeyID = SecCertificateGetAuthorityKeyID(cert); | |
284 | if (authorityKeyID) { | |
285 | if (!CFEqual(parentSubjectKeyID, authorityKeyID)) { | |
286 | /* AuthorityKeyID doesn't match issuers SubjectKeyID. */ | |
287 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
288 | return; | |
289 | } | |
290 | } | |
291 | } | |
292 | ||
293 | parentSubjectKeyID = SecCertificateGetSubjectKeyID(cert); | |
294 | } | |
295 | } | |
296 | ||
297 | static bool keyusage_allows(SecKeyUsage keyUsage, CFTypeRef xku) { | |
298 | if (!xku || CFGetTypeID(xku) != CFNumberGetTypeID()) | |
299 | return false; | |
300 | ||
301 | SInt32 dku; | |
302 | CFNumberGetValue((CFNumberRef)xku, kCFNumberSInt32Type, &dku); | |
303 | SecKeyUsage ku = (SecKeyUsage)dku; | |
304 | return (keyUsage & ku) == ku; | |
305 | } | |
306 | ||
307 | static void SecPolicyCheckKeyUsage(SecPVCRef pvc, | |
308 | CFStringRef key) { | |
309 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
310 | SecKeyUsage keyUsage = SecCertificateGetKeyUsage(leaf); | |
311 | bool match = false; | |
312 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
313 | CFTypeRef xku = CFDictionaryGetValue(policy->_options, key); | |
314 | if (isArray(xku)) { | |
315 | CFIndex ix, count = CFArrayGetCount(xku); | |
316 | for (ix = 0; ix < count; ++ix) { | |
317 | CFTypeRef ku = CFArrayGetValueAtIndex(xku, ix); | |
318 | if (keyusage_allows(keyUsage, ku)) { | |
319 | match = true; | |
320 | break; | |
321 | } | |
322 | } | |
323 | } else { | |
324 | match = keyusage_allows(keyUsage, xku); | |
325 | } | |
326 | if (!match) { | |
327 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
328 | } | |
329 | } | |
330 | ||
331 | static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage, | |
332 | CFTypeRef xeku) { | |
333 | if (!xeku || CFGetTypeID(xeku) != CFDataGetTypeID()) | |
334 | return false; | |
335 | if (extendedKeyUsage) { | |
336 | CFRange all = { 0, CFArrayGetCount(extendedKeyUsage) }; | |
337 | return CFArrayContainsValue(extendedKeyUsage, all, xeku); | |
338 | } else { | |
339 | /* Certificate has no extended key usage, only a match if the policy | |
340 | contains a 0 length CFDataRef. */ | |
341 | return CFDataGetLength((CFDataRef)xeku) == 0; | |
342 | } | |
343 | } | |
344 | ||
345 | /* AUDIT[securityd](done): | |
346 | policy->_options is a caller provided dictionary, only its cf type has | |
347 | been checked. | |
348 | */ | |
349 | static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc, CFStringRef key) { | |
350 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
351 | CFArrayRef leafExtendedKeyUsage = SecCertificateCopyExtendedKeyUsage(leaf); | |
352 | bool match = false; | |
353 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
354 | CFTypeRef xeku = CFDictionaryGetValue(policy->_options, key); | |
355 | if (isArray(xeku)) { | |
356 | CFIndex ix, count = CFArrayGetCount(xeku); | |
357 | for (ix = 0; ix < count; ix++) { | |
358 | CFTypeRef eku = CFArrayGetValueAtIndex(xeku, ix); | |
359 | if (extendedkeyusage_allows(leafExtendedKeyUsage, eku)) { | |
360 | match = true; | |
361 | break; | |
362 | } | |
363 | } | |
364 | } else { | |
365 | match = extendedkeyusage_allows(leafExtendedKeyUsage, xeku); | |
366 | } | |
367 | CFReleaseSafe(leafExtendedKeyUsage); | |
368 | if (!match) { | |
369 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
370 | } | |
371 | } | |
372 | ||
373 | #if 0 | |
374 | static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc, | |
375 | CFStringRef key, bool strict) { | |
376 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
377 | for (ix = 0; ix < count; ++ix) { | |
378 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
379 | const SecCEBasicConstraints *bc = | |
380 | SecCertificateGetBasicConstraints(cert); | |
381 | if (bc) { | |
382 | if (strict) { | |
383 | if (ix == 0) { | |
384 | /* Leaf certificate has basic constraints extension. */ | |
385 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
386 | return; | |
387 | } else if (!bc->critical) { | |
388 | /* Basic constraints extension is not marked critical. */ | |
389 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
390 | return; | |
391 | } | |
392 | } | |
393 | ||
394 | if (ix > 0 || count == 1) { | |
395 | if (!bc->isCA) { | |
396 | /* Non leaf certificate marked as isCA false. */ | |
397 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
398 | return; | |
399 | } | |
400 | ||
401 | if (bc->pathLenConstraintPresent) { | |
402 | if (bc->pathLenConstraint < (uint32_t)(ix - 1)) { | |
403 | #if 0 | |
404 | /* @@@ If a self signed certificate is issued by | |
405 | another cert that is trusted, then we are supposed | |
406 | to treat the self signed cert itself as the anchor | |
407 | for path length purposes. */ | |
408 | CFIndex ssix = SecCertificatePathSelfSignedIndex(path); | |
409 | if (ssix >= 0 && ix >= ssix) { | |
410 | /* It's ok if the pathLenConstraint isn't met for | |
411 | certificates signing a self signed cert in the | |
412 | chain. */ | |
413 | } else | |
414 | #endif | |
415 | { | |
416 | /* Path Length Constraint Exceeded. */ | |
417 | if (!SecPVCSetResult(pvc, key, ix, | |
418 | kCFBooleanFalse)) | |
419 | return; | |
420 | } | |
421 | } | |
422 | } | |
423 | } | |
424 | } else if (strict && ix > 0) { | |
425 | /* In strict mode all CA certificates *MUST* have a critical | |
426 | basic constraints extension and the leaf certificate | |
427 | *MUST NOT* have a basic constraints extension. */ | |
428 | /* CA certificate is missing basicConstraints extension. */ | |
429 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
430 | return; | |
431 | } | |
432 | } | |
433 | } | |
434 | #endif | |
435 | ||
436 | static void SecPolicyCheckBasicContraints(SecPVCRef pvc, | |
437 | CFStringRef key) { | |
438 | //SecPolicyCheckBasicContraintsCommon(pvc, key, false); | |
439 | } | |
440 | ||
441 | static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc, | |
442 | CFStringRef key) { | |
443 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
444 | for (ix = 0; ix < count; ++ix) { | |
445 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
446 | /* If the certificate has a subject, or | |
447 | if it doesn't, and it's the leaf and not self signed, | |
448 | and also has a critical subjectAltName extension it's valid. */ | |
449 | if (!SecCertificateHasSubject(cert)) { | |
450 | if (ix == 0 && count > 1) { | |
451 | if (!SecCertificateHasCriticalSubjectAltName(cert)) { | |
452 | /* Leaf certificate with empty subject does not have | |
453 | a critical subject alt name extension. */ | |
454 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
455 | return; | |
456 | } | |
457 | } else { | |
458 | /* CA certificate has empty subject. */ | |
459 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
460 | return; | |
461 | } | |
462 | } | |
463 | } | |
464 | } | |
465 | ||
466 | static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc, | |
467 | CFStringRef key) { | |
468 | } | |
469 | ||
470 | /* Compare hostname, to a server name obtained from the server's cert | |
471 | Obtained from the SubjectAltName or the CommonName entry in the Subject. | |
472 | Limited wildcard checking is performed here as outlined in | |
473 | ||
474 | RFC 2818 Section 3.1. Server Identity | |
475 | ||
476 | [...] Names may contain the wildcard | |
477 | character * which is considered to match any single domain name | |
478 | component or component fragment. E.g., *.a.com matches foo.a.com but | |
479 | not bar.foo.a.com. f*.com matches foo.com but not bar.com. | |
480 | [...] | |
481 | ||
482 | Trailing '.' characters in the hostname will be ignored. | |
483 | ||
484 | Returns true on match, else false. | |
485 | */ | |
486 | static bool SecDNSMatch(CFStringRef hostname, CFStringRef servername) { | |
487 | CFStringInlineBuffer hbuf, sbuf; | |
488 | CFIndex hix, six, | |
489 | hlength = CFStringGetLength(hostname), | |
490 | slength = CFStringGetLength(servername); | |
491 | CFRange hrange = { 0, hlength }, srange = { 0, slength }; | |
492 | CFStringInitInlineBuffer(hostname, &hbuf, hrange); | |
493 | CFStringInitInlineBuffer(servername, &sbuf, srange); | |
494 | ||
495 | for (hix = six = 0; six < slength; ++six) { | |
496 | UniChar hch, sch = CFStringGetCharacterFromInlineBuffer(&sbuf, six); | |
497 | if (sch == '*') { | |
498 | if (six + 1 >= slength) { | |
499 | /* Trailing '*' in servername, match until end of hostname or | |
500 | trailing '.'. */ | |
501 | do { | |
502 | if (hix >= hlength) { | |
503 | /* If we reach the end of the hostname we have a | |
504 | match. */ | |
505 | return true; | |
506 | } | |
507 | hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++); | |
508 | } while (hch != '.'); | |
509 | /* We reached the end of servername and found a '.' in | |
510 | hostname. Return true if hostname has a single | |
511 | trailing '.' return false if there is anything after it. */ | |
512 | return hix == hlength; | |
513 | } | |
514 | ||
515 | /* Grab the character after the '*'. */ | |
516 | sch = CFStringGetCharacterFromInlineBuffer(&sbuf, ++six); | |
517 | if (sch != '.') { | |
518 | /* We have something of the form '*foo.com'. Or '**.com' | |
519 | We don't deal with that yet, since it might require | |
520 | backtracking. Also RFC 2818 doesn't seem to require it. */ | |
521 | return false; | |
522 | } | |
523 | ||
524 | /* We're looking at the '.' after the '*' in something of the | |
525 | form 'foo*.com' or '*.com'. Match until next '.' in hostname. */ | |
526 | do { | |
527 | /* Since we're not at the end of servername yet (that case | |
528 | was handeled above), running out of chars in hostname | |
529 | means we don't have a match. */ | |
530 | if (hix >= hlength) | |
531 | return false; | |
532 | hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++); | |
533 | } while (hch != '.'); | |
534 | } else { | |
535 | /* We're looking at a non wildcard character in the servername. | |
536 | If we reached the end of hostname it's not a match. */ | |
537 | if (hix >= hlength) | |
538 | return false; | |
539 | ||
540 | /* Otherwise make sure the hostname matches the character in the | |
541 | servername, case insensitively. */ | |
542 | hch = CFStringGetCharacterFromInlineBuffer(&hbuf, hix++); | |
543 | if (towlower(hch) != towlower(sch)) | |
544 | return false; | |
545 | } | |
546 | } | |
547 | ||
548 | if (hix < hlength) { | |
549 | /* We reached the end of servername but we have one or more characters | |
550 | left to compare against in the hostname. */ | |
551 | if (hix + 1 == hlength && | |
552 | CFStringGetCharacterFromInlineBuffer(&hbuf, hix) == '.') { | |
553 | /* Hostname has a single trailing '.', we're ok with that. */ | |
554 | return true; | |
555 | } | |
556 | /* Anything else is not a match. */ | |
557 | return false; | |
558 | } | |
559 | ||
560 | return true; | |
561 | } | |
562 | ||
563 | /* AUDIT[securityd](done): | |
564 | policy->_options is a caller provided dictionary, only its cf type has | |
565 | been checked. | |
566 | */ | |
567 | static void SecPolicyCheckSSLHostname(SecPVCRef pvc, | |
568 | CFStringRef key) { | |
569 | /* @@@ Consider what to do if the caller passes in no hostname. Should | |
570 | we then still fail if the leaf has no dnsNames or IPAddresses at all? */ | |
571 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
572 | CFStringRef hostName = (CFStringRef) | |
573 | CFDictionaryGetValue(policy->_options, key); | |
574 | if (!isString(hostName)) { | |
575 | /* @@@ We can't return an error here and making the evaluation fail | |
576 | won't help much either. */ | |
577 | return; | |
578 | } | |
579 | ||
580 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
581 | bool dnsMatch = false; | |
582 | CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf); | |
583 | if (dnsNames) { | |
584 | CFIndex ix, count = CFArrayGetCount(dnsNames); | |
585 | for (ix = 0; ix < count; ++ix) { | |
586 | CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix); | |
587 | if (SecDNSMatch(hostName, dns)) { | |
588 | dnsMatch = true; | |
589 | break; | |
590 | } | |
591 | } | |
592 | CFRelease(dnsNames); | |
593 | } | |
594 | ||
595 | if (!dnsMatch) { | |
596 | /* Maybe hostname is an IPv4 or IPv6 address, let's compare against | |
597 | the values returned by SecCertificateCopyIPAddresses() instead. */ | |
598 | CFArrayRef ipAddresses = SecCertificateCopyIPAddresses(leaf); | |
599 | if (ipAddresses) { | |
600 | CFIndex ix, count = CFArrayGetCount(ipAddresses); | |
601 | for (ix = 0; ix < count; ++ix) { | |
602 | CFStringRef ipAddress = (CFStringRef)CFArrayGetValueAtIndex(ipAddresses, ix); | |
603 | if (!CFStringCompare(hostName, ipAddress, kCFCompareCaseInsensitive)) { | |
604 | dnsMatch = true; | |
605 | break; | |
606 | } | |
607 | } | |
608 | CFRelease(ipAddresses); | |
609 | } | |
610 | } | |
611 | ||
612 | if (!dnsMatch) { | |
613 | /* Hostname mismatch or no hostnames found in certificate. */ | |
614 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
615 | } | |
616 | if ((dnsMatch || pvc->details) | |
617 | && SecPolicySubscriberCertificateCouldBeEV(leaf)) { | |
618 | secdebug("policy", "enabling optionally_ev"); | |
619 | pvc->optionally_ev = true; | |
620 | /* optionally_ev => check_revocation, so we don't enable revocation | |
621 | checking here, since we don't want it on for non EV ssl certs. */ | |
622 | #if 0 | |
623 | /* Check revocation status if the certificate asks for it (and we | |
624 | support it) currently we only support ocsp. */ | |
625 | CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(leaf); | |
626 | if (ocspResponders) { | |
627 | SecPVCSetCheckRevocation(pvc); | |
628 | } | |
629 | #endif | |
630 | } | |
631 | } | |
632 | ||
633 | /* AUDIT[securityd](done): | |
634 | policy->_options is a caller provided dictionary, only its cf type has | |
635 | been checked. | |
636 | */ | |
637 | static void SecPolicyCheckEmail(SecPVCRef pvc, CFStringRef key) { | |
638 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
639 | CFStringRef email = (CFStringRef)CFDictionaryGetValue(policy->_options, key); | |
640 | bool match = false; | |
641 | if (!isString(email)) { | |
642 | /* We can't return an error here and making the evaluation fail | |
643 | won't help much either. */ | |
644 | return; | |
645 | } | |
646 | ||
647 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
648 | CFArrayRef addrs = SecCertificateCopyRFC822Names(leaf); | |
649 | if (addrs) { | |
650 | CFIndex ix, count = CFArrayGetCount(addrs); | |
651 | for (ix = 0; ix < count; ++ix) { | |
652 | CFStringRef addr = (CFStringRef)CFArrayGetValueAtIndex(addrs, ix); | |
653 | if (!CFStringCompare(email, addr, kCFCompareCaseInsensitive)) { | |
654 | match = true; | |
655 | break; | |
656 | } | |
657 | } | |
658 | CFRelease(addrs); | |
659 | } | |
660 | ||
661 | if (!match) { | |
662 | /* Hostname mismatch or no hostnames found in certificate. */ | |
663 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
664 | } | |
665 | } | |
666 | ||
667 | static void SecPolicyCheckValidIntermediates(SecPVCRef pvc, | |
668 | CFStringRef key) { | |
669 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
670 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
671 | for (ix = 1; ix < count - 1; ++ix) { | |
672 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
673 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
674 | /* Intermediate certificate has expired. */ | |
675 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
676 | return; | |
677 | } | |
678 | } | |
679 | } | |
680 | ||
681 | static void SecPolicyCheckValidLeaf(SecPVCRef pvc, | |
682 | CFStringRef key) { | |
683 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
684 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
685 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
686 | /* Leaf certificate has expired. */ | |
687 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
688 | return; | |
689 | } | |
690 | } | |
691 | ||
692 | static void SecPolicyCheckValidRoot(SecPVCRef pvc, | |
693 | CFStringRef key) { | |
694 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
695 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
696 | ix = count - 1; | |
697 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
698 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
699 | /* Root certificate has expired. */ | |
700 | if (!SecPVCSetResult(pvc, key, ix, kCFBooleanFalse)) | |
701 | return; | |
702 | } | |
703 | } | |
704 | ||
705 | /* AUDIT[securityd](done): | |
706 | policy->_options is a caller provided dictionary, only its cf type has | |
707 | been checked. | |
708 | */ | |
709 | static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc, | |
710 | CFStringRef key) { | |
711 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
712 | if (count < 2) { | |
713 | /* Can't check intermediates common name if there is no intermediate. */ | |
714 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
715 | return; | |
716 | } | |
717 | ||
718 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 1); | |
719 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
720 | CFStringRef commonName = | |
721 | (CFStringRef)CFDictionaryGetValue(policy->_options, key); | |
722 | if (!isString(commonName)) { | |
723 | /* @@@ We can't return an error here and making the evaluation fail | |
724 | won't help much either. */ | |
725 | return; | |
726 | } | |
727 | CFArrayRef commonNames = SecCertificateCopyCommonNames(cert); | |
728 | if (!commonNames || CFArrayGetCount(commonNames) != 1 || | |
729 | !CFEqual(commonName, CFArrayGetValueAtIndex(commonNames, 0))) { | |
730 | /* Common Name mismatch. */ | |
731 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
732 | } | |
733 | CFReleaseSafe(commonNames); | |
734 | } | |
735 | ||
736 | /* AUDIT[securityd](done): | |
737 | policy->_options is a caller provided dictionary, only its cf type has | |
738 | been checked. | |
739 | */ | |
740 | static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc, | |
741 | CFStringRef key) { | |
742 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
743 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
744 | CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
745 | key); | |
746 | if (!isString(common_name)) { | |
747 | /* @@@ We can't return an error here and making the evaluation fail | |
748 | won't help much either. */ | |
749 | return; | |
750 | } | |
751 | CFArrayRef commonNames = SecCertificateCopyCommonNames(cert); | |
752 | if (!commonNames || CFArrayGetCount(commonNames) != 1 || | |
753 | !CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) { | |
754 | /* Common Name mismatch. */ | |
755 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
756 | } | |
757 | CFReleaseSafe(commonNames); | |
758 | } | |
759 | ||
760 | /* AUDIT[securityd](done): | |
761 | policy->_options is a caller provided dictionary, only its cf type has | |
762 | been checked. | |
763 | */ | |
764 | static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc, | |
765 | CFStringRef key) { | |
766 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
767 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
768 | CFStringRef prefix = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
769 | key); | |
770 | if (!isString(prefix)) { | |
771 | /* @@@ We can't return an error here and making the evaluation fail | |
772 | won't help much either. */ | |
773 | return; | |
774 | } | |
775 | CFArrayRef commonNames = SecCertificateCopyCommonNames(cert); | |
776 | if (!commonNames || CFArrayGetCount(commonNames) != 1 || | |
777 | !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames, 0), prefix)) { | |
778 | /* Common Name prefix mismatch. */ | |
779 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
780 | } | |
781 | CFReleaseSafe(commonNames); | |
782 | } | |
783 | ||
784 | /* AUDIT[securityd](done): | |
785 | policy->_options is a caller provided dictionary, only its cf type has | |
786 | been checked. | |
787 | */ | |
788 | static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc, | |
789 | CFStringRef key) { | |
790 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
791 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
792 | CFStringRef common_name = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
793 | key); | |
794 | if (!isString(common_name)) { | |
795 | /* @@@ We can't return an error here and making the evaluation fail | |
796 | won't help much either. */ | |
797 | return; | |
798 | } | |
799 | CFArrayRef commonNames = SecCertificateCopyCommonNames(cert); | |
800 | if (!commonNames || CFArrayGetCount(commonNames) != 1) { | |
801 | CFStringRef cert_common_name = CFArrayGetValueAtIndex(commonNames, 0); | |
802 | CFStringRef test_common_name = common_name ? | |
803 | CFStringCreateWithFormat(kCFAllocatorDefault, | |
804 | NULL, CFSTR("TEST %@ TEST"), common_name) : | |
805 | NULL; | |
806 | if (!CFEqual(common_name, cert_common_name) && | |
807 | (!test_common_name || !CFEqual(test_common_name, cert_common_name))) | |
808 | /* Common Name mismatch. */ | |
809 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
810 | CFReleaseSafe(test_common_name); | |
811 | } | |
812 | CFReleaseSafe(commonNames); | |
813 | } | |
814 | ||
815 | /* AUDIT[securityd](done): | |
816 | policy->_options is a caller provided dictionary, only its cf type has | |
817 | been checked. | |
818 | */ | |
819 | static void SecPolicyCheckNotValidBefore(SecPVCRef pvc, | |
820 | CFStringRef key) { | |
821 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
822 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
823 | CFDateRef date = (CFDateRef)CFDictionaryGetValue(policy->_options, key); | |
824 | if (!isDate(date)) { | |
825 | /* @@@ We can't return an error here and making the evaluation fail | |
826 | won't help much either. */ | |
827 | return; | |
828 | } | |
829 | CFAbsoluteTime at = CFDateGetAbsoluteTime(date); | |
830 | if (SecCertificateNotValidBefore(cert) <= at) { | |
831 | /* Leaf certificate has not valid before that is too old. */ | |
832 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
833 | return; | |
834 | } | |
835 | } | |
836 | ||
837 | /* AUDIT[securityd](done): | |
838 | policy->_options is a caller provided dictionary, only its cf type has | |
839 | been checked. | |
840 | */ | |
841 | static void SecPolicyCheckChainLength(SecPVCRef pvc, | |
842 | CFStringRef key) { | |
843 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
844 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
845 | CFNumberRef chainLength = | |
846 | (CFNumberRef)CFDictionaryGetValue(policy->_options, key); | |
847 | CFIndex value; | |
848 | if (!chainLength || CFGetTypeID(chainLength) != CFNumberGetTypeID() || | |
849 | !CFNumberGetValue(chainLength, kCFNumberCFIndexType, &value)) { | |
850 | /* @@@ We can't return an error here and making the evaluation fail | |
851 | won't help much either. */ | |
852 | return; | |
853 | } | |
854 | if (value != count) { | |
855 | /* Chain length doesn't match policy requirement. */ | |
856 | if (!SecPVCSetResult(pvc, key, 0, kCFBooleanFalse)) | |
857 | return; | |
858 | } | |
859 | } | |
860 | ||
861 | /* AUDIT[securityd](done): | |
862 | policy->_options is a caller provided dictionary, only its cf type has | |
863 | been checked. | |
864 | */ | |
865 | static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc, | |
866 | CFStringRef key) { | |
867 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
868 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, count - 1); | |
869 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
427c49bc | 870 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); |
b1ab9ed8 | 871 | CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert); |
427c49bc A |
872 | |
873 | bool foundMatch = false; | |
874 | ||
875 | if (isData(value)) | |
876 | foundMatch = CFEqual(anchorSHA1, value); | |
877 | else if (isArray(value)) | |
878 | foundMatch = CFArrayContainsValue((CFArrayRef) value, CFRangeMake(0, CFArrayGetCount((CFArrayRef) value)), anchorSHA1); | |
879 | else { | |
880 | /* @@@ We only support Data and Array but we can't return an error here so. | |
881 | we let the evaluation fail (not much help) and assert in debug. */ | |
882 | assert(false); | |
883 | } | |
884 | ||
885 | if (!foundMatch) | |
886 | if (!SecPVCSetResult(pvc, kSecPolicyCheckAnchorSHA1, 0, kCFBooleanFalse)) | |
887 | return; | |
888 | ||
889 | return; | |
b1ab9ed8 A |
890 | } |
891 | ||
892 | /* AUDIT[securityd](done): | |
893 | policy->_options is a caller provided dictionary, only its cf type has | |
894 | been checked. | |
895 | */ | |
896 | static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc, | |
897 | CFStringRef key) { | |
898 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
899 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
900 | CFStringRef org = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
901 | key); | |
902 | if (!isString(org)) { | |
903 | /* @@@ We can't return an error here and making the evaluation fail | |
904 | won't help much either. */ | |
905 | return; | |
906 | } | |
907 | CFArrayRef organization = SecCertificateCopyOrganization(cert); | |
908 | if (!organization || CFArrayGetCount(organization) != 1 || | |
909 | !CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) { | |
910 | /* Leaf Subject Organization mismatch. */ | |
911 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
912 | } | |
913 | CFReleaseSafe(organization); | |
914 | } | |
915 | ||
427c49bc A |
916 | static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc, |
917 | CFStringRef key) { | |
918 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
919 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
920 | CFStringRef orgUnit = (CFStringRef)CFDictionaryGetValue(policy->_options, | |
921 | key); | |
922 | if (!isString(orgUnit)) { | |
923 | /* @@@ We can't return an error here and making the evaluation fail | |
924 | won't help much either. */ | |
925 | return; | |
926 | } | |
927 | CFArrayRef organizationalUnit = SecCertificateCopyOrganizationalUnit(cert); | |
928 | if (!organizationalUnit || CFArrayGetCount(organizationalUnit) != 1 || | |
929 | !CFEqual(orgUnit, CFArrayGetValueAtIndex(organizationalUnit, 0))) { | |
930 | /* Leaf Subject Organizational Unit mismatch. */ | |
931 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
932 | } | |
933 | CFReleaseSafe(organizationalUnit); | |
934 | } | |
935 | ||
b1ab9ed8 A |
936 | /* AUDIT[securityd](done): |
937 | policy->_options is a caller provided dictionary, only its cf type has | |
938 | been checked. | |
939 | */ | |
940 | static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc, | |
941 | CFStringRef key) { | |
942 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
943 | CFArrayRef trustedServerNames = (CFArrayRef) | |
944 | CFDictionaryGetValue(policy->_options, key); | |
945 | /* No names specified means we accept any name. */ | |
946 | if (!trustedServerNames) | |
947 | return; | |
948 | if (!isArray(trustedServerNames)) { | |
949 | /* @@@ We can't return an error here and making the evaluation fail | |
950 | won't help much either. */ | |
951 | return; | |
952 | } | |
953 | ||
954 | CFIndex tsnCount = CFArrayGetCount(trustedServerNames); | |
955 | SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0); | |
956 | bool dnsMatch = false; | |
957 | CFArrayRef dnsNames = SecCertificateCopyDNSNames(leaf); | |
958 | if (dnsNames) { | |
959 | CFIndex ix, count = CFArrayGetCount(dnsNames); | |
960 | // @@@ This is O(N^2) unfortunately we can't do better easily unless | |
961 | // we don't do wildcard matching. */ | |
962 | for (ix = 0; !dnsMatch && ix < count; ++ix) { | |
963 | CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix); | |
964 | CFIndex tix; | |
965 | for (tix = 0; tix < tsnCount; ++tix) { | |
966 | CFStringRef serverName = | |
967 | (CFStringRef)CFArrayGetValueAtIndex(trustedServerNames, tix); | |
968 | if (!isString(serverName)) { | |
969 | /* @@@ We can't return an error here and making the | |
970 | evaluation fail won't help much either. */ | |
427c49bc | 971 | CFReleaseSafe(dnsNames); |
b1ab9ed8 A |
972 | return; |
973 | } | |
974 | /* we purposefully reverse the arguments here such that dns names | |
975 | from the cert are matched against a server name list, where | |
976 | the server names list can contain wildcards and the dns name | |
977 | cannot. References: http://support.microsoft.com/kb/941123 | |
978 | It's easy to find occurrences where people tried to use | |
979 | wildcard certificates and were told that those don't work | |
980 | in this context. */ | |
981 | if (SecDNSMatch(dns, serverName)) { | |
982 | dnsMatch = true; | |
983 | break; | |
984 | } | |
985 | } | |
986 | } | |
987 | CFRelease(dnsNames); | |
988 | } | |
989 | ||
990 | if (!dnsMatch) { | |
991 | /* Hostname mismatch or no hostnames found in certificate. */ | |
992 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
993 | } | |
994 | } | |
995 | ||
427c49bc | 996 | static const unsigned char UTN_USERFirst_Hardware_Serial[][16] = { |
b1ab9ed8 A |
997 | { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 }, |
998 | { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 }, | |
999 | { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 }, | |
1000 | { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 }, | |
1001 | { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 }, | |
1002 | { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 }, | |
1003 | { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 }, | |
1004 | { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e }, | |
1005 | { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } }; | |
1006 | ||
1007 | static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer[] = { | |
1008 | 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, | |
1009 | 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02, | |
1010 | 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, | |
1011 | 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43, | |
1012 | 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a, | |
1013 | 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52, | |
1014 | 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31, | |
1015 | 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54, | |
1016 | 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45, | |
1017 | 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f, | |
1018 | 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e, | |
1019 | 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48, | |
1020 | 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45 | |
1021 | }; | |
1022 | static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len = 151; | |
1023 | ||
1024 | ||
1025 | static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc, | |
1026 | CFStringRef key) { | |
1027 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1028 | CFDataRef issuer = cert ? SecCertificateGetNormalizedIssuerContent(cert) : NULL; | |
1029 | ||
1030 | if (issuer && (CFDataGetLength(issuer) == (CFIndex)UTN_USERFirst_Hardware_Normalized_Issuer_len) && | |
1031 | (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer, CFDataGetBytePtr(issuer), | |
1032 | UTN_USERFirst_Hardware_Normalized_Issuer_len))) | |
1033 | { | |
1034 | CFDataRef serial = SecCertificateCopySerialNumber(cert); | |
1035 | if (serial) { | |
1036 | CFIndex serial_length = CFDataGetLength(serial); | |
1037 | const uint8_t *serial_ptr = CFDataGetBytePtr(serial); | |
1038 | ||
1039 | while ((serial_length > 0) && (*serial_ptr == 0)) { | |
1040 | serial_ptr++; | |
1041 | serial_length--; | |
1042 | } | |
1043 | ||
1044 | if (serial_length == (CFIndex)sizeof(*UTN_USERFirst_Hardware_Serial)) { | |
1045 | unsigned int i; | |
427c49bc | 1046 | for (i = 0; i < array_size(UTN_USERFirst_Hardware_Serial); i++) |
b1ab9ed8 A |
1047 | { |
1048 | if (0 == memcmp(UTN_USERFirst_Hardware_Serial[i], | |
1049 | serial_ptr, sizeof(*UTN_USERFirst_Hardware_Serial))) | |
1050 | { | |
1051 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
427c49bc | 1052 | CFReleaseSafe(serial); |
b1ab9ed8 A |
1053 | return; |
1054 | } | |
1055 | } | |
1056 | } | |
1057 | CFRelease(serial); | |
1058 | } | |
1059 | } | |
427c49bc A |
1060 | |
1061 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
1062 | if (NULL != otapkiRef) | |
1063 | { | |
1064 | CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef); | |
1065 | CFRelease(otapkiRef); | |
1066 | if (NULL != blackListedKeys) | |
1067 | { | |
1068 | /* Check for blacklisted intermediates keys. */ | |
1069 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
1070 | if (dgst) | |
1071 | { | |
1072 | /* Check dgst against blacklist. */ | |
1073 | if (CFSetContainsValue(blackListedKeys, dgst)) | |
1074 | { | |
1075 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
1076 | } | |
1077 | CFRelease(dgst); | |
1078 | } | |
1079 | CFRelease(blackListedKeys); | |
1080 | } | |
1081 | } | |
b1ab9ed8 A |
1082 | } |
1083 | ||
427c49bc A |
1084 | static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc, CFStringRef key) |
1085 | { | |
1086 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
1087 | if (NULL != otapkiRef) | |
1088 | { | |
1089 | CFSetRef grayListedKeys = SecOTAPKICopyGrayList(otapkiRef); | |
1090 | CFRelease(otapkiRef); | |
1091 | if (NULL != grayListedKeys) | |
1092 | { | |
1093 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1094 | ||
1095 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
1096 | if (dgst) | |
1097 | { | |
1098 | /* Check dgst against gray. */ | |
1099 | if (CFSetContainsValue(grayListedKeys, dgst)) | |
1100 | { | |
1101 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
1102 | } | |
1103 | CFRelease(dgst); | |
1104 | } | |
1105 | CFRelease(grayListedKeys); | |
1106 | } | |
1107 | } | |
1108 | } | |
1109 | ||
b1ab9ed8 A |
1110 | static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc, CFStringRef key) |
1111 | { | |
1112 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1113 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1114 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
427c49bc | 1115 | |
b1ab9ed8 A |
1116 | if (value && SecCertificateHasMarkerExtension(cert, value)) |
1117 | return; | |
1118 | ||
1119 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
1120 | } | |
1121 | ||
1122 | static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc, CFStringRef key) | |
1123 | { | |
1124 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1125 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1126 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
1127 | ||
1128 | for (ix = 1; ix < count - 1; ix++) { | |
1129 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1130 | if (SecCertificateHasMarkerExtension(cert, value)) | |
1131 | return; | |
1132 | } | |
1133 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
1134 | } | |
1135 | ||
427c49bc A |
1136 | |
1137 | ||
b1ab9ed8 A |
1138 | /**************************************************************************** |
1139 | *********************** New rfc5280 Chain Validation *********************** | |
1140 | ****************************************************************************/ | |
1141 | ||
1142 | #if 0 | |
1143 | typedef struct cert_path *cert_path_t; | |
1144 | struct cert_path { | |
1145 | int length; | |
1146 | }; | |
1147 | ||
1148 | typedef struct x500_name *x500_name_t; | |
1149 | struct x500_name { | |
1150 | }; | |
1151 | ||
1152 | typedef struct algorithm_id *algorithm_id_t; | |
1153 | struct algorithm_id { | |
1154 | oid_t algorithm_oid; | |
1155 | der_t parameters; | |
1156 | }; | |
1157 | ||
1158 | typedef struct trust_anchor *trust_anchor_t; | |
1159 | struct trust_anchor { | |
1160 | x500_name_t issuer_name; | |
1161 | algorithm_id_t public_key_algorithm; /* includes optional params */ | |
1162 | SecKeyRef public_key; | |
1163 | }; | |
1164 | ||
1165 | typedef struct certificate_policy *certificate_policy_t; | |
1166 | struct certificate_policy { | |
1167 | policy_qualifier_t qualifiers; | |
1168 | oid_t oid; | |
1169 | SLIST_ENTRY(certificate_policy) policies; | |
1170 | }; | |
1171 | ||
1172 | typedef struct policy_mapping *policy_mapping_t; | |
1173 | struct policy_mapping { | |
1174 | SLIST_ENTRY(policy_mapping) mappings; | |
1175 | oid_t issuer_domain_policy; | |
1176 | oid_t subject_domain_policy; | |
1177 | }; | |
1178 | ||
1179 | typedef struct root_name *root_name_t; | |
1180 | struct root_name { | |
1181 | }; | |
1182 | #endif | |
1183 | ||
1184 | struct policy_tree_add_ctx { | |
1185 | oid_t p_oid; | |
1186 | policy_qualifier_t p_q; | |
1187 | }; | |
1188 | ||
1189 | /* For each node of depth i-1 in the valid_policy_tree where P-OID is in the expected_policy_set, create a child node as follows: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */ | |
1190 | static bool policy_tree_add_if_match(policy_tree_t node, void *ctx) { | |
1191 | struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx; | |
1192 | policy_set_t policy_set; | |
1193 | for (policy_set = node->expected_policy_set; | |
1194 | policy_set; | |
1195 | policy_set = policy_set->oid_next) { | |
1196 | if (oid_equal(policy_set->oid, info->p_oid)) { | |
1197 | policy_tree_add_child(node, &info->p_oid, info->p_q); | |
1198 | return true; | |
1199 | } | |
1200 | } | |
1201 | return false; | |
1202 | } | |
1203 | ||
1204 | /* If the valid_policy_tree includes a node of depth i-1 with the valid_policy anyPolicy, generate a child node with the following values: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */ | |
1205 | static bool policy_tree_add_if_any(policy_tree_t node, void *ctx) { | |
1206 | struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx; | |
1207 | if (oid_equal(node->valid_policy, oidAnyPolicy)) { | |
1208 | policy_tree_add_child(node, &info->p_oid, info->p_q); | |
1209 | return true; | |
1210 | } | |
1211 | return false; | |
1212 | } | |
1213 | ||
1214 | /* Return true iff node has a child with a valid_policy equal to oid. */ | |
1215 | static bool policy_tree_has_child_with_oid(policy_tree_t node, | |
1216 | const oid_t *oid) { | |
427c49bc | 1217 | policy_tree_t child; |
b1ab9ed8 A |
1218 | for (child = node->children; child; child = child->siblings) { |
1219 | if (oid_equal(child->valid_policy, (*oid))) { | |
1220 | return true; | |
1221 | } | |
1222 | } | |
1223 | return false; | |
1224 | } | |
1225 | ||
1226 | /* For each node in the valid_policy_tree of depth i-1, for each value in the expected_policy_set (including anyPolicy) that does not appear in a child node, create a child node with the following values: set the valid_policy to the value from the expected_policy_set in the parent node, set the qualifier_set to AP-Q, and set the expected_policy_set to the value in the valid_policy from this node. */ | |
1227 | static bool policy_tree_add_expected(policy_tree_t node, void *ctx) { | |
1228 | policy_qualifier_t p_q = (policy_qualifier_t)ctx; | |
1229 | policy_set_t policy_set; | |
1230 | bool added_node = false; | |
1231 | for (policy_set = node->expected_policy_set; | |
1232 | policy_set; | |
1233 | policy_set = policy_set->oid_next) { | |
1234 | if (!policy_tree_has_child_with_oid(node, &policy_set->oid)) { | |
1235 | policy_tree_add_child(node, &policy_set->oid, p_q); | |
1236 | added_node = true; | |
1237 | } | |
1238 | } | |
1239 | return added_node; | |
1240 | } | |
1241 | ||
1242 | #if 0 | |
1243 | /* For each node where ID-P is the valid_policy, set expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */ | |
1244 | static bool policy_tree_map(policy_tree_t node, void *ctx) { | |
1245 | /* Can't map oidAnyPolicy. */ | |
1246 | if (oid_equal(node->valid_policy, oidAnyPolicy)) | |
1247 | return false; | |
1248 | ||
1249 | const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx; | |
1250 | uint32_t mapping_ix, mapping_count = pm->numMappings; | |
1251 | policy_set_t policy_set = NULL; | |
1252 | /* First count how many mappings match this nodes valid_policy. */ | |
1253 | for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) { | |
1254 | const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix]; | |
1255 | if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) { | |
1256 | policy_set_t p_node = (policy_set_t)malloc(sizeof(*policy_set)); | |
1257 | p_node->oid = mapping->subjectDomainPolicy; | |
1258 | p_node->oid_next = policy_set ? policy_set : NULL; | |
1259 | policy_set = p_node; | |
1260 | } | |
1261 | } | |
1262 | if (policy_set) { | |
1263 | policy_tree_set_expected_policy(node, policy_set); | |
1264 | return true; | |
1265 | } | |
1266 | return false; | |
1267 | } | |
1268 | #endif | |
1269 | ||
1270 | #define POLICY_MAPPING 0 | |
1271 | #define POLICY_SUBTREES 0 | |
1272 | ||
1273 | /* rfc5280 basic cert processing. */ | |
1274 | static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc, | |
1275 | CFStringRef key) { | |
1276 | /* Inputs */ | |
1277 | //cert_path_t path; | |
1278 | CFIndex count = SecPVCGetCertificateCount(pvc); | |
1279 | /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */ | |
1280 | assert((unsigned long)count<=UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */ | |
1281 | uint32_t n = (uint32_t)count; | |
1282 | bool is_anchored = SecPVCIsAnchored(pvc); | |
1283 | if (is_anchored) { | |
1284 | /* If the anchor is trusted we don't procces the last cert in the | |
1285 | chain (root). */ | |
1286 | n--; | |
1287 | } else { | |
1288 | /* Add a detail for the root not being trusted. */ | |
1289 | if (SecPVCSetResultForced(pvc, kSecPolicyCheckAnchorTrusted, | |
1290 | n - 1, kCFBooleanFalse, true)) | |
1291 | return; | |
1292 | } | |
1293 | ||
1294 | CFAbsoluteTime verify_time = SecPVCGetVerifyTime(pvc); | |
1295 | //policy_set_t user_initial_policy_set = NULL; | |
1296 | //trust_anchor_t anchor; | |
1297 | bool initial_policy_mapping_inhibit = false; | |
1298 | bool initial_explicit_policy = false; | |
1299 | bool initial_any_policy_inhibit = false; | |
1300 | #if POLICY_SUBTREES | |
1301 | root_name_t initial_permitted_subtrees = NULL; | |
1302 | root_name_t initial_excluded_subtrees = NULL; | |
1303 | #endif | |
1304 | ||
1305 | /* Initialization */ | |
1306 | pvc->valid_policy_tree = policy_tree_create(&oidAnyPolicy, NULL); | |
1307 | #if POLICY_SUBTREES | |
1308 | root_name_t permitted_subtrees = initial_permitted_subtrees; | |
1309 | root_name_t excluded_subtrees = initial_excluded_subtrees; | |
1310 | #endif | |
1311 | uint32_t explicit_policy = initial_explicit_policy ? 0 : n + 1; | |
1312 | uint32_t inhibit_any_policy = initial_any_policy_inhibit ? 0 : n + 1; | |
1313 | uint32_t policy_mapping = initial_policy_mapping_inhibit ? 0 : n + 1; | |
1314 | ||
1315 | #if 0 | |
1316 | /* Path builder ensures we only get cert chains with proper issuer | |
1317 | chaining with valid signatures along the way. */ | |
1318 | algorithm_id_t working_public_key_algorithm = anchor->public_key_algorithm; | |
1319 | SecKeyRef working_public_key = anchor->public_key; | |
1320 | x500_name_t working_issuer_name = anchor->issuer_name; | |
1321 | #endif | |
1322 | uint32_t i, max_path_length = n; | |
1323 | SecCertificateRef cert = NULL; | |
1324 | for (i = 1; i <= n; ++i) { | |
1325 | /* Process Cert */ | |
1326 | cert = SecPVCGetCertificateAtIndex(pvc, n - i); | |
1327 | bool is_self_issued = SecPVCIsCertificateAtIndexSelfSigned(pvc, n - i); | |
1328 | ||
1329 | /* (a) Verify the basic certificate information. */ | |
1330 | /* @@@ Ensure that cert was signed with working_public_key_algorithm | |
1331 | using the working_public_key and the working_public_key_parameters. */ | |
1332 | #if 1 | |
1333 | /* Already done by chain builder. */ | |
1334 | if (!SecCertificateIsValid(cert, verify_time)) { | |
1335 | CFStringRef fail_key = i == n ? kSecPolicyCheckValidLeaf : kSecPolicyCheckValidIntermediates; | |
1336 | if (!SecPVCSetResult(pvc, fail_key, n - i, kCFBooleanFalse)) | |
1337 | return; | |
1338 | } | |
1339 | #endif | |
1340 | #if 0 | |
1341 | /* Check revocation status if the certificate asks for it. */ | |
1342 | CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert); | |
1343 | if (ocspResponders) { | |
1344 | SecPVCSetCheckRevocation(pvc); | |
1345 | } | |
1346 | #endif | |
1347 | /* @@@ cert.issuer == working_issuer_name. */ | |
1348 | ||
1349 | #if POLICY_SUBTREES | |
1350 | /* (b) (c) */ | |
1351 | if (!is_self_issued || i == n) { | |
1352 | /* Verify that the subject name is within one of the permitted_subtrees for X.500 distinguished names, and verify that each of the alternative names in the subjectAltName extension (critical or non-critical) is within one of the permitted_subtrees for that name type. */ | |
1353 | /* Verify that the subject name is not within any of the excluded_subtrees for X.500 distinguished names, and verify that each of the alternative names in the subjectAltName extension (critical or non-critical) is not within any of the excluded_subtrees for that name type. */ | |
1354 | } | |
1355 | #endif | |
1356 | /* (d) */ | |
1357 | if (pvc->valid_policy_tree) { | |
1358 | const SecCECertificatePolicies *cp = | |
1359 | SecCertificateGetCertificatePolicies(cert); | |
1360 | size_t policy_ix, policy_count = cp ? cp->numPolicies : 0; | |
1361 | for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) { | |
1362 | const SecCEPolicyInformation *policy = &cp->policies[policy_ix]; | |
1363 | oid_t p_oid = policy->policyIdentifier; | |
1364 | policy_qualifier_t p_q = &policy->policyQualifiers; | |
1365 | struct policy_tree_add_ctx ctx = { p_oid, p_q }; | |
1366 | if (!oid_equal(p_oid, oidAnyPolicy)) { | |
1367 | if (!policy_tree_walk_depth(pvc->valid_policy_tree, i - 1, | |
1368 | policy_tree_add_if_match, &ctx)) { | |
1369 | policy_tree_walk_depth(pvc->valid_policy_tree, i - 1, | |
1370 | policy_tree_add_if_any, &ctx); | |
1371 | } | |
1372 | } | |
1373 | } | |
1374 | /* The certificate policies extension includes the policy | |
1375 | anyPolicy with the qualifier set AP-Q and either | |
1376 | (a) inhibit_anyPolicy is greater than 0 or | |
1377 | (b) i < n and the certificate is self-issued. */ | |
1378 | if (inhibit_any_policy > 0 || (i < n && is_self_issued)) { | |
1379 | for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) { | |
1380 | const SecCEPolicyInformation *policy = &cp->policies[policy_ix]; | |
1381 | oid_t p_oid = policy->policyIdentifier; | |
1382 | policy_qualifier_t p_q = &policy->policyQualifiers; | |
1383 | if (oid_equal(p_oid, oidAnyPolicy)) { | |
1384 | policy_tree_walk_depth(pvc->valid_policy_tree, i - 1, | |
1385 | policy_tree_add_expected, (void *)p_q); | |
1386 | } | |
1387 | } | |
1388 | } | |
1389 | policy_tree_prune_childless(&pvc->valid_policy_tree, i - 1); | |
1390 | /* (e) */ | |
1391 | if (!cp) { | |
1392 | if (pvc->valid_policy_tree) | |
1393 | policy_tree_prune(&pvc->valid_policy_tree); | |
1394 | } | |
1395 | } | |
1396 | /* (f) Verify that either explicit_policy is greater than 0 or the | |
1397 | valid_policy_tree is not equal to NULL. */ | |
1398 | if (!pvc->valid_policy_tree && explicit_policy == 0) { | |
1399 | /* valid_policy_tree is empty and explicit policy is 0, illegal. */ | |
1400 | if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, n - i, kCFBooleanFalse, true)) | |
1401 | return; | |
1402 | } | |
1403 | /* If Last Cert in Path */ | |
1404 | if (i == n) | |
1405 | break; | |
1406 | ||
1407 | /* Prepare for Next Cert */ | |
1408 | #if POLICY_MAPPING | |
1409 | /* (a) verify that anyPolicy does not appear as an | |
1410 | issuerDomainPolicy or a subjectDomainPolicy */ | |
1411 | CFDictionaryRef pm = SecCertificateGetPolicyMappings(cert); | |
1412 | if (pm) { | |
1413 | uint32_t mapping_ix, mapping_count = pm->numMappings; | |
1414 | for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) { | |
1415 | const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix]; | |
1416 | if (oid_equal(mapping->issuerDomainPolicy, oidAnyPolicy) | |
1417 | || oid_equal(mapping->subjectDomainPolicy, oidAnyPolicy)) { | |
1418 | /* Policy mapping uses anyPolicy, illegal. */ | |
1419 | if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, n - i, kCFBooleanFalse)) | |
1420 | return; | |
1421 | } | |
1422 | } | |
1423 | /* (b) */ | |
1424 | /* (1) If the policy_mapping variable is greater than 0 */ | |
1425 | if (policy_mapping > 0) { | |
1426 | if (!policy_tree_walk_depth(pvc->valid_policy_tree, i, | |
1427 | policy_tree_map, (void *)pm)) { | |
1428 | /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1 that has a valid_policy of anyPolicy as follows: | |
1429 | ||
1430 | (i) set the valid_policy to ID-P; | |
1431 | ||
1432 | (ii) set the qualifier_set to the qualifier set of the | |
1433 | policy anyPolicy in the certificate policies | |
1434 | extension of certificate i; and | |
1435 | (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */ | |
1436 | } | |
1437 | } else { | |
1438 | #if 0 | |
1439 | /* (i) delete each node of depth i in the valid_policy_tree | |
1440 | where ID-P is the valid_policy. */ | |
1441 | struct policy_tree_map_ctx ctx = { idp_oid, sdp_oid }; | |
1442 | policy_tree_walk_depth(pvc->valid_policy_tree, i, | |
1443 | policy_tree_delete_if_match, &ctx); | |
1444 | #endif | |
1445 | /* (ii) If there is a node in the valid_policy_tree of depth | |
1446 | i-1 or less without any child nodes, delete that | |
1447 | node. Repeat this step until there are no nodes of | |
1448 | depth i-1 or less without children. */ | |
1449 | policy_tree_prune_childless(&pvc->valid_policy_tree, i - 1); | |
1450 | } | |
1451 | } | |
1452 | #endif /* POLICY_MAPPING */ | |
1453 | /* (c)(d)(e)(f) */ | |
1454 | //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert); | |
1455 | //working_public_key = SecCertificateCopyPublicKey(cert); | |
1456 | //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert); | |
1457 | //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert); | |
1458 | #if POLICY_SUBTREES | |
1459 | /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables as follows: | |
1460 | */ | |
1461 | /* @@@ handle name constraints. */ | |
1462 | #endif | |
1463 | /* (h) */ | |
1464 | if (!is_self_issued) { | |
1465 | if (explicit_policy) | |
1466 | explicit_policy--; | |
1467 | if (policy_mapping) | |
1468 | policy_mapping--; | |
1469 | if (inhibit_any_policy) | |
1470 | inhibit_any_policy--; | |
1471 | } | |
1472 | /* (i) */ | |
1473 | const SecCEPolicyConstraints *pc = | |
1474 | SecCertificateGetPolicyConstraints(cert); | |
1475 | if (pc) { | |
1476 | if (pc->requireExplicitPolicyPresent | |
1477 | && pc->requireExplicitPolicy < explicit_policy) { | |
1478 | explicit_policy = pc->requireExplicitPolicy; | |
1479 | } | |
1480 | if (pc->inhibitPolicyMappingPresent | |
1481 | && pc->inhibitPolicyMapping < policy_mapping) { | |
1482 | policy_mapping = pc->inhibitPolicyMapping; | |
1483 | } | |
1484 | } | |
1485 | /* (j) */ | |
1486 | uint32_t iap = SecCertificateGetInhibitAnyPolicySkipCerts(cert); | |
1487 | if (iap < inhibit_any_policy) { | |
1488 | inhibit_any_policy = iap; | |
1489 | } | |
1490 | /* (k) */ | |
1491 | const SecCEBasicConstraints *bc = | |
1492 | SecCertificateGetBasicConstraints(cert); | |
1493 | #if 0 /* Checked in chain builder pre signature verify already. */ | |
1494 | if (!bc || !bc->isCA) { | |
1495 | /* Basic constraints not present or not marked as isCA, illegal. */ | |
1496 | if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints, | |
1497 | n - i, kCFBooleanFalse)) | |
1498 | return; | |
1499 | } | |
1500 | #endif | |
1501 | /* (l) */ | |
1502 | if (!is_self_issued) { | |
1503 | if (max_path_length > 0) { | |
1504 | max_path_length--; | |
1505 | } else { | |
1506 | /* max_path_len exceeded, illegal. */ | |
1507 | if (!SecPVCSetResult(pvc, kSecPolicyCheckBasicContraints, | |
1508 | n - i, kCFBooleanFalse)) | |
1509 | return; | |
1510 | } | |
1511 | } | |
1512 | /* (m) */ | |
1513 | if (bc && bc->pathLenConstraintPresent | |
1514 | && bc->pathLenConstraint < max_path_length) { | |
1515 | max_path_length = bc->pathLenConstraint; | |
1516 | } | |
1517 | #if 0 /* Checked in chain builder pre signature verify already. */ | |
1518 | /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */ | |
1519 | SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert); | |
1520 | if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) { | |
1521 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage, | |
1522 | n - i, kCFBooleanFalse, true)) | |
1523 | return; | |
1524 | } | |
1525 | #endif | |
1526 | /* (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. */ | |
1527 | if (SecCertificateHasUnknownCriticalExtension(cert)) { | |
1528 | /* Certificate contains one or more unknown critical extensions. */ | |
1529 | if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions, | |
1530 | n - i, kCFBooleanFalse)) | |
1531 | return; | |
1532 | } | |
1533 | } | |
1534 | /* Wrap up */ | |
1535 | cert = SecPVCGetCertificateAtIndex(pvc, 0); | |
1536 | /* (a) */ | |
1537 | if (explicit_policy) | |
1538 | explicit_policy--; | |
1539 | /* (b) */ | |
1540 | const SecCEPolicyConstraints *pc = SecCertificateGetPolicyConstraints(cert); | |
1541 | if (pc) { | |
1542 | if (pc->requireExplicitPolicyPresent | |
1543 | && pc->requireExplicitPolicy == 0) { | |
1544 | explicit_policy = 0; | |
1545 | } | |
1546 | } | |
1547 | /* (c) */ | |
1548 | //working_public_key = SecCertificateCopyPublicKey(cert); | |
1549 | /* (d) */ | |
1550 | /* 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 | |
1551 | working_public_key_algorithm are different, set the working_public_key_parameters to null. */ | |
1552 | //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert); | |
1553 | /* (e) */ | |
1554 | //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert); | |
1555 | /* (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. */ | |
1556 | if (SecCertificateHasUnknownCriticalExtension(cert)) { | |
1557 | /* Certificate contains one or more unknown critical extensions. */ | |
1558 | if (!SecPVCSetResult(pvc, kSecPolicyCheckCriticalExtensions, | |
1559 | 0, kCFBooleanFalse)) | |
1560 | return; | |
1561 | } | |
1562 | /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */ | |
1563 | ||
1564 | if (pvc->valid_policy_tree) { | |
1565 | #if !defined(NDEBUG) | |
1566 | policy_tree_dump(pvc->valid_policy_tree); | |
1567 | #endif | |
1568 | /* (g3c4) */ | |
1569 | //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1); | |
1570 | } | |
1571 | ||
1572 | /* If either (1) the value of explicit_policy variable is greater than | |
1573 | zero or (2) the valid_policy_tree is not NULL, then path processing | |
1574 | has succeeded. */ | |
1575 | if (!pvc->valid_policy_tree && explicit_policy == 0) { | |
1576 | /* valid_policy_tree is empty and explicit policy is 0, illegal. */ | |
1577 | if (!SecPVCSetResultForced(pvc, key /* @@@ Need custom key */, 0, kCFBooleanFalse, true)) | |
1578 | return; | |
1579 | } | |
1580 | } | |
1581 | ||
1582 | static policy_set_t policies_for_cert(SecCertificateRef cert) { | |
1583 | policy_set_t policies = NULL; | |
1584 | const SecCECertificatePolicies *cp = | |
1585 | SecCertificateGetCertificatePolicies(cert); | |
1586 | size_t policy_ix, policy_count = cp ? cp->numPolicies : 0; | |
1587 | for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) { | |
1588 | policy_set_add(&policies, &cp->policies[policy_ix].policyIdentifier); | |
1589 | } | |
1590 | return policies; | |
1591 | } | |
1592 | ||
1593 | static void SecPolicyCheckEV(SecPVCRef pvc, | |
1594 | CFStringRef key) { | |
1595 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1596 | policy_set_t valid_policies = NULL; | |
1597 | ||
1598 | for (ix = 0; ix < count; ++ix) { | |
1599 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1600 | policy_set_t policies = policies_for_cert(cert); | |
1601 | if (ix == 0) { | |
1602 | /* Subscriber */ | |
1603 | /* anyPolicy in the leaf isn't allowed for EV, so only init | |
1604 | valid_policies if we have real policies. */ | |
1605 | if (!policy_set_contains(policies, &oidAnyPolicy)) { | |
1606 | valid_policies = policies; | |
1607 | policies = NULL; | |
1608 | } | |
1609 | } else if (ix < count - 1) { | |
1610 | /* Subordinate CA */ | |
1611 | if (!SecPolicySubordinateCACertificateCouldBeEV(cert)) { | |
1612 | secdebug("ev", "subordinate certificate is not ev"); | |
1613 | if (SecPVCSetResultForced(pvc, key, | |
1614 | ix, kCFBooleanFalse, true)) { | |
1615 | policy_set_free(valid_policies); | |
1616 | policy_set_free(policies); | |
1617 | return; | |
1618 | } | |
1619 | } | |
1620 | policy_set_intersect(&valid_policies, policies); | |
1621 | } else { | |
1622 | /* Root CA */ | |
1623 | if (!SecPolicyRootCACertificateIsEV(cert, valid_policies)) { | |
1624 | secdebug("ev", "anchor certificate is not ev"); | |
1625 | if (SecPVCSetResultForced(pvc, key, | |
1626 | ix, kCFBooleanFalse, true)) { | |
1627 | policy_set_free(valid_policies); | |
1628 | policy_set_free(policies); | |
1629 | return; | |
1630 | } | |
1631 | } | |
1632 | } | |
1633 | policy_set_free(policies); | |
1634 | if (!valid_policies) { | |
1635 | secdebug("ev", "valid_policies set is empty: chain not ev"); | |
1636 | /* If we ever get into a state where no policies are valid anymore | |
1637 | this can't be an ev chain. */ | |
1638 | if (SecPVCSetResultForced(pvc, key, | |
1639 | ix, kCFBooleanFalse, true)) { | |
1640 | return; | |
1641 | } | |
1642 | } | |
1643 | } | |
1644 | ||
1645 | policy_set_free(valid_policies); | |
1646 | ||
1647 | /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a | |
1648 | Subscriber MUST contain an OID defined by the CA in the certificate’s | |
1649 | certificatePolicies extension that: (i) indicates which CA policy statement relates | |
1650 | to that certificate, (ii) asserts the CA’s adherence to and compliance with these | |
1651 | Guidelines, and (iii), by pre-agreement with the Application Software Vendor, | |
1652 | marks the certificate as being an EV Certificate. | |
1653 | (b) EV Subordinate CA Certificates | |
1654 | (1) Certificates issued to Subordinate CAs that are not controlled by the issuing | |
1655 | CA MUST contain one or more OIDs defined by the issuing CA that | |
1656 | explicitly identify the EV Policies that are implemented by the Subordinate | |
1657 | CA; | |
1658 | (2) Certificates issued to Subordinate CAs that are controlled by the Root CA | |
1659 | MAY contain the special anyPolicy OID (2.5.29.32.0). | |
1660 | (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the | |
1661 | certificatePolicies or extendedKeyUsage extensions. | |
1662 | */ | |
1663 | } | |
1664 | ||
427c49bc A |
1665 | |
1666 | static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc, CFStringRef key) | |
1667 | { | |
1668 | CFIndex ix, count = SecPVCGetCertificateCount(pvc); | |
1669 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
1670 | CFTypeRef value = CFDictionaryGetValue(policy->_options, key); | |
1671 | DERItem key_value; | |
1672 | key_value.data = NULL; | |
1673 | key_value.length = 0; | |
1674 | ||
1675 | if (CFGetTypeID(value) == CFDataGetTypeID()) | |
1676 | { | |
1677 | CFDataRef key_data = (CFDataRef)value; | |
1678 | key_value.data = (DERByte *)CFDataGetBytePtr(key_data); | |
1679 | key_value.length = (DERSize)CFDataGetLength(key_data); | |
1680 | ||
1681 | for (ix = 0; ix < count; ix++) { | |
1682 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
1683 | policy_set_t policies = policies_for_cert(cert); | |
1684 | ||
1685 | if (policy_set_contains(policies, &key_value)) { | |
1686 | return; | |
1687 | } | |
1688 | } | |
1689 | SecPVCSetResult(pvc, key, 0, kCFBooleanFalse); | |
1690 | } | |
1691 | } | |
1692 | ||
1693 | ||
b1ab9ed8 A |
1694 | static void SecPolicyCheckRevocation(SecPVCRef pvc, |
1695 | CFStringRef key) { | |
1696 | SecPVCSetCheckRevocation(pvc); | |
1697 | } | |
1698 | ||
1699 | static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc, | |
1700 | CFStringRef key) { | |
1701 | SecPathBuilderSetCanAccessNetwork(pvc->builder, false); | |
1702 | } | |
1703 | ||
427c49bc A |
1704 | // MARK: - |
1705 | // MARK: SecRVCRef | |
b1ab9ed8 A |
1706 | /******************************************************** |
1707 | ****************** SecRVCRef Functions ***************** | |
1708 | ********************************************************/ | |
1709 | ||
1710 | /* Revocation verification context. */ | |
1711 | struct OpaqueSecRVC { | |
1712 | /* Will contain the response data. */ | |
1713 | asynchttp_t http; | |
1714 | ||
1715 | /* Pointer to the pvc for this revocation check. */ | |
1716 | SecPVCRef pvc; | |
1717 | ||
1718 | /* The ocsp request we send to each responder. */ | |
1719 | SecOCSPRequestRef ocspRequest; | |
1720 | ||
1721 | /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */ | |
1722 | CFIndex certIX; | |
1723 | ||
1724 | /* Index in array returned by SecCertificateGetOCSPResponders() for current | |
1725 | responder. */ | |
1726 | CFIndex responderIX; | |
1727 | ||
1728 | /* URL of current responder. */ | |
1729 | CFURLRef responder; | |
1730 | ||
1731 | /* Date until which this revocation status is valid. */ | |
1732 | CFAbsoluteTime nextUpdate; | |
1733 | ||
1734 | bool done; | |
1735 | }; | |
1736 | typedef struct OpaqueSecRVC *SecRVCRef; | |
1737 | ||
1738 | static void SecRVCDelete(SecRVCRef rvc) { | |
1739 | secdebug("alloc", "%p", rvc); | |
1740 | asynchttp_free(&rvc->http); | |
1741 | SecOCSPRequestFinalize(rvc->ocspRequest); | |
1742 | } | |
1743 | ||
1744 | /* Return the next responder we should contact for this rvc or NULL if we | |
1745 | exhausted them all. */ | |
1746 | static CFURLRef SecRVCGetNextResponder(SecRVCRef rvc) { | |
1747 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX); | |
1748 | CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert); | |
1749 | if (ocspResponders) { | |
1750 | CFIndex responderCount = CFArrayGetCount(ocspResponders); | |
1751 | while (rvc->responderIX < responderCount) { | |
1752 | CFURLRef responder = CFArrayGetValueAtIndex(ocspResponders, rvc->responderIX); | |
1753 | rvc->responderIX++; | |
1754 | CFStringRef scheme = CFURLCopyScheme(responder); | |
1755 | if (scheme) { | |
1756 | /* We only support http and https responders currently. */ | |
1757 | bool valid_responder = (CFEqual(CFSTR("http"), scheme) || | |
1758 | CFEqual(CFSTR("https"), scheme)); | |
1759 | CFRelease(scheme); | |
1760 | if (valid_responder) | |
1761 | return responder; | |
1762 | } | |
1763 | } | |
1764 | } | |
1765 | return NULL; | |
1766 | } | |
1767 | ||
1768 | /* Fire off an async http request for this certs revocation status, return | |
1769 | false if request was queued, true if we're done. */ | |
1770 | static bool SecRVCFetchNext(SecRVCRef rvc) { | |
1771 | while ((rvc->responder = SecRVCGetNextResponder(rvc))) { | |
1772 | CFDataRef request = SecOCSPRequestGetDER(rvc->ocspRequest); | |
1773 | if (!request) | |
1774 | goto errOut; | |
1775 | ||
1776 | if (!asyncHttpPost(rvc->responder, request, &rvc->http)) { | |
1777 | /* Async request was posted, wait for reply. */ | |
1778 | return false; | |
1779 | } | |
1780 | } | |
1781 | ||
1782 | errOut: | |
1783 | rvc->done = true; | |
1784 | return true; | |
1785 | } | |
1786 | ||
1787 | /* Proccess a verified ocsp response for a given cert. Return true if the | |
1788 | certificate status was obtained. */ | |
1789 | static bool SecOCSPSingleResponseProccess(SecOCSPSingleResponseRef this, | |
1790 | SecRVCRef rvc) { | |
1791 | bool proccessed; | |
1792 | switch (this->certStatus) { | |
1793 | case CS_Good: | |
427c49bc | 1794 | secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX); |
b1ab9ed8 A |
1795 | /* @@@ Mark cert as valid until a given date (nextUpdate if we have one) |
1796 | in the info dictionary. */ | |
1797 | //cert.revokeCheckGood(true); | |
1798 | rvc->nextUpdate = this->nextUpdate; | |
1799 | proccessed = true; | |
1800 | break; | |
1801 | case CS_Revoked: | |
427c49bc | 1802 | secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex, rvc->certIX); |
b1ab9ed8 A |
1803 | /* @@@ Mark cert as revoked (with reason) at revocation date in |
1804 | the info dictionary, or perhaps we should use a different key per | |
1805 | reason? That way a client using exceptions can ignore some but | |
1806 | not all reasons. */ | |
1807 | SInt32 reason = this->crlReason; | |
1808 | CFNumberRef cfreason = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &reason); | |
1809 | SecPVCSetResultForced(rvc->pvc, kSecPolicyCheckRevocation, rvc->certIX, | |
1810 | cfreason, true); | |
1811 | CFRelease(cfreason); | |
1812 | proccessed = true; | |
1813 | break; | |
1814 | case CS_Unknown: | |
1815 | /* not an error, no per-cert status, nothing here */ | |
427c49bc | 1816 | secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX); |
b1ab9ed8 A |
1817 | proccessed = false; |
1818 | break; | |
1819 | default: | |
427c49bc | 1820 | secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex, |
b1ab9ed8 A |
1821 | (int)this->certStatus, rvc->certIX); |
1822 | proccessed = false; | |
1823 | break; | |
1824 | } | |
1825 | ||
1826 | return proccessed; | |
1827 | } | |
1828 | ||
1829 | static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse, SecRVCRef rvc) { | |
1830 | bool trusted; | |
1831 | SecCertificatePathRef issuer = SecCertificatePathCopyFromParent(rvc->pvc->path, rvc->certIX + 1); | |
1832 | SecCertificatePathRef signer = SecOCSPResponseCopySigner(ocspResponse, issuer); | |
1833 | CFRelease(issuer); | |
1834 | ||
1835 | if (signer) { | |
1836 | if (signer == issuer) { | |
1837 | /* We already know we trust issuer since it's the path we are | |
1838 | trying to verify minus the leaf. */ | |
1839 | secdebug("ocsp", "ocsp responder: %@ response signed by issuer", | |
1840 | rvc->responder); | |
1841 | trusted = true; | |
1842 | } else { | |
1843 | secdebug("ocsp", | |
1844 | "ocsp responder: %@ response signed by cert issued by issuer", | |
1845 | rvc->responder); | |
1846 | /* @@@ Now check that we trust signer. */ | |
1847 | const void *ocspSigner = SecPolicyCreateOCSPSigner(); | |
1848 | CFArrayRef policies = CFArrayCreate(kCFAllocatorDefault, | |
1849 | &ocspSigner, 1, &kCFTypeArrayCallBacks); | |
1850 | CFRelease(ocspSigner); | |
1851 | CFAbsoluteTime verifyTime = SecOCSPResponseVerifyTime(ocspResponse); | |
1852 | struct OpaqueSecPVC ospvc; | |
1853 | SecPVCInit(&ospvc, rvc->pvc->builder, policies, verifyTime); | |
1854 | CFRelease(policies); | |
1855 | SecPVCSetPath(&ospvc, signer, NULL); | |
1856 | SecPVCLeafChecks(&ospvc); | |
1857 | if (ospvc.result) { | |
1858 | bool completed = SecPVCPathChecks(&ospvc); | |
1859 | /* If completed is false we are waiting for a callback, this | |
1860 | shouldn't happen since we aren't asking for details, no | |
1861 | revocation checking is done. */ | |
1862 | if (!completed) { | |
1863 | ocspdErrorLog("SecPVCPathChecks unexpectedly started " | |
1864 | "background job!"); | |
1865 | /* @@@ assert() or abort here perhaps? */ | |
1866 | } | |
1867 | } | |
1868 | if (ospvc.result) { | |
427c49bc | 1869 | secdebug("ocsp", "response satisfies ocspSigner policy (%@)", |
b1ab9ed8 A |
1870 | rvc->responder); |
1871 | trusted = true; | |
1872 | } else { | |
1873 | /* @@@ We don't trust the cert so don't use this response. */ | |
1874 | ocspdErrorLog("ocsp response signed by certificate which " | |
1875 | "does not satisfy ocspSigner policy"); | |
1876 | trusted = false; | |
1877 | } | |
1878 | SecPVCDelete(&ospvc); | |
1879 | } | |
1880 | ||
1881 | CFRelease(signer); | |
1882 | } else { | |
1883 | /* @@@ No signer found for this ocsp response, discard it. */ | |
1884 | secdebug("ocsp", "ocsp responder: %@ no signer found for response", | |
1885 | rvc->responder); | |
1886 | trusted = false; | |
1887 | } | |
1888 | ||
1889 | #if DUMP_OCSPRESPONSES | |
1890 | char buf[40]; | |
1891 | snprintf(buf, 40, "/tmp/ocspresponse%ld%s.der", | |
1892 | rvc->certIX, (trusted ? "t" : "u")); | |
1893 | secdumpdata(ocspResponse->data, buf); | |
1894 | #endif | |
1895 | ||
1896 | return trusted; | |
1897 | } | |
1898 | ||
1899 | /* Callback from async http code after an ocsp response has been received. */ | |
1900 | static void SecOCSPFetchCompleted(asynchttp_t *http, CFTimeInterval maxAge) { | |
1901 | SecRVCRef rvc = (SecRVCRef)http->info; | |
1902 | SecPVCRef pvc = rvc->pvc; | |
1903 | SecOCSPResponseRef ocspResponse = NULL; | |
1904 | if (http->response) { | |
1905 | CFDataRef data = CFHTTPMessageCopyBody(http->response); | |
1906 | if (data) { | |
1907 | /* Parse the returned data as if it's an ocspResponse. */ | |
1908 | ocspResponse = SecOCSPResponseCreate(data, maxAge); | |
1909 | CFRelease(data); | |
1910 | } | |
1911 | } | |
1912 | ||
1913 | if (ocspResponse) { | |
1914 | SecOCSPResponseStatus orStatus = SecOCSPGetResponseStatus(ocspResponse); | |
1915 | if (orStatus == kSecOCSPSuccess) { | |
1916 | SecOCSPSingleResponseRef sr = | |
1917 | SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest); | |
1918 | if (!sr) { | |
1919 | /* The ocsp response didn't have a singleResponse for the cert | |
1920 | we are looking for, let's try the next responder. */ | |
1921 | secdebug("ocsp", | |
1922 | "ocsp responder: %@ did not include status of requested cert", | |
1923 | rvc->responder); | |
1924 | } else { | |
1925 | /* We got a singleResponse for the cert we are interested in, | |
1926 | let's proccess it. */ | |
1927 | /* @@@ If the responder doesn't have the ocsp-nocheck extension | |
1928 | we should check whether the leaf was revoked (we are | |
1929 | already checking the rest of the chain). */ | |
1930 | /* Check the OCSP response signature and verify the | |
1931 | response. */ | |
1932 | if (SecOCSPResponseVerify(ocspResponse, rvc)) { | |
1933 | secdebug("ocsp","responder: %@ sent proper response", | |
1934 | rvc->responder); | |
1935 | ||
1936 | if (SecOCSPSingleResponseProccess(sr, rvc)) { | |
1937 | if (rvc->nextUpdate == 0) { | |
1938 | rvc->nextUpdate = | |
1939 | SecOCSPResponseGetExpirationTime(ocspResponse); | |
1940 | } | |
1941 | /* If the singleResponse had meaningful information, we | |
1942 | cache the response. */ | |
1943 | SecOCSPCacheAddResponse(ocspResponse, rvc->responder); | |
1944 | rvc->done = true; | |
1945 | } | |
1946 | } | |
1947 | SecOCSPSingleResponseDestroy(sr); | |
1948 | } | |
1949 | } else { | |
1950 | /* ocsp response not ok. Let's try next responder. */ | |
1951 | secdebug("ocsp", "responder: %@ returned status: %d", | |
1952 | rvc->responder, orStatus); | |
1953 | #if 0 | |
1954 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckRevocation, | |
1955 | rvc->certIX, kCFBooleanFalse, true)) | |
1956 | return; | |
1957 | #endif | |
1958 | } | |
1959 | SecOCSPResponseFinalize(ocspResponse); | |
1960 | } | |
1961 | ||
1962 | if (!rvc->done) { | |
1963 | /* Clear the data for the next response. */ | |
1964 | asynchttp_free(http); | |
1965 | SecRVCFetchNext(rvc); | |
1966 | } | |
1967 | ||
1968 | if (rvc->done) { | |
1969 | SecRVCDelete(rvc); | |
1970 | if (!--pvc->asyncJobCount) { | |
1971 | SecPathBuilderStep(pvc->builder); | |
1972 | } | |
1973 | } | |
1974 | } | |
1975 | ||
1976 | static void SecRVCInit(SecRVCRef rvc, SecPVCRef pvc, CFIndex certIX) { | |
1977 | secdebug("alloc", "%p", rvc); | |
1978 | rvc->pvc = pvc; | |
1979 | rvc->certIX = certIX; | |
427c49bc | 1980 | rvc->http.queue = SecPathBuilderGetQueue(pvc->builder); |
b1ab9ed8 A |
1981 | rvc->http.completed = SecOCSPFetchCompleted; |
1982 | rvc->http.info = rvc; | |
1983 | rvc->ocspRequest = NULL; | |
1984 | rvc->responderIX = 0; | |
1985 | rvc->responder = NULL; | |
1986 | rvc->nextUpdate = 0; | |
1987 | rvc->done = false; | |
1988 | } | |
1989 | ||
1990 | ||
1991 | static bool SecPVCCheckRevocation(SecPVCRef pvc) { | |
1992 | secdebug("ocsp", "checking revocation"); | |
1993 | CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc); | |
1994 | bool completed = true; | |
1995 | if (certCount <= 1) { | |
1996 | /* Can't verify without an issuer; we're done */ | |
1997 | return completed; | |
1998 | } | |
1999 | if (!SecPVCIsAnchored(pvc)) { | |
2000 | /* We can't check revocation for chains without a trusted anchor. */ | |
2001 | return completed; | |
2002 | } | |
2003 | certCount--; | |
2004 | ||
2005 | #if 0 | |
2006 | /* @@@ Implement getting this value from the client. | |
2007 | Optional responder passed in though policy. */ | |
2008 | CFURLRef localResponder = NULL; | |
2009 | /* Generate a nonce in outgoing request if true. */ | |
2010 | bool genNonce = false; | |
2011 | /* Require a nonce in response if true. */ | |
2012 | bool requireRespNonce = false; | |
2013 | bool cacheReadDisable = false; | |
2014 | bool cacheWriteDisable = false; | |
2015 | #endif | |
2016 | ||
2017 | if (pvc->rvcs) { | |
2018 | /* We have done revocation checking already, we're done. */ | |
2019 | secdebug("ocsp", "Not rechecking revocation"); | |
2020 | return completed; | |
2021 | } | |
2022 | ||
2023 | /* Setup things so we check revocation status of all certs except the | |
2024 | anchor. */ | |
2025 | pvc->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount); | |
2026 | ||
2027 | #if 0 | |
2028 | /* Lookup cached revocation data for each certificate. */ | |
2029 | for (certIX = 0; certIX < certCount; ++certIX) { | |
2030 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX); | |
2031 | CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert); | |
2032 | if (ocspResponders) { | |
2033 | /* First look though passed in ocsp responses. */ | |
2034 | //SecPVCGetOCSPResponseForCertificateAtIndex(pvc, ix, singleResponse); | |
2035 | ||
2036 | /* Then look though shared cache (we don't care which responder | |
2037 | something came from here). */ | |
2038 | CFDataRef ocspResponse = SecOCSPCacheCopyMatching(SecCertIDRef certID, NULL); | |
2039 | ||
2040 | /* Now let's parse the response. */ | |
2041 | if (decodeOCSPResponse(ocspResp)) { | |
2042 | secdebug("ocsp", "response ok: %@", ocspResp); | |
2043 | } else { | |
2044 | secdebug("ocsp", "response bad: %@", ocspResp); | |
2045 | /* ocsp response not ok. */ | |
2046 | if (!SecPVCSetResultForced(pvc, key, ix, kCFBooleanFalse, true)) | |
2047 | return completed; | |
2048 | } | |
2049 | CFReleaseSafe(ocspResp); | |
2050 | } else { | |
2051 | /* Check if certificate has any crl distributionPoints. */ | |
2052 | CFArrayRef distributionPoints = SecCertificateGetCRLDistributionPoints(cert); | |
2053 | if (distributionPoints) { | |
2054 | /* Look for a cached CRL and potentially delta CRL for this certificate. */ | |
2055 | } | |
2056 | } | |
2057 | } | |
2058 | #endif | |
2059 | ||
2060 | /* Note that if we are multi threaded and a job completes after it | |
2061 | is started but before we return from this function, we don't want | |
2062 | a callback to decrement asyncJobCount to zero before we finish issuing | |
2063 | all the jobs. To avoid this we pretend we issued certCount async jobs, | |
2064 | and decrement pvc->asyncJobCount for each cert that we don't start a | |
2065 | background fetch for. */ | |
427c49bc | 2066 | pvc->asyncJobCount = (unsigned int) certCount; |
b1ab9ed8 A |
2067 | |
2068 | /* Loop though certificates again and issue an ocsp fetch if the | |
2069 | revocation status checking isn't done yet. */ | |
2070 | for (certIX = 0; certIX < certCount; ++certIX) { | |
2071 | secdebug("ocsp", "checking revocation for cert: %ld", certIX); | |
2072 | SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX]; | |
2073 | SecRVCInit(rvc, pvc, certIX); | |
2074 | if (rvc->done) | |
2075 | continue; | |
2076 | ||
2077 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, | |
2078 | rvc->certIX); | |
2079 | /* The certIX + 1 is ok here since certCount is always at least 1 | |
2080 | less than the actual number of certs. */ | |
2081 | SecCertificateRef issuer = SecPVCGetCertificateAtIndex(rvc->pvc, | |
2082 | rvc->certIX + 1); | |
2083 | ||
2084 | rvc->ocspRequest = SecOCSPRequestCreate(cert, issuer); | |
2085 | SecOCSPResponseRef ocspResponse; | |
2086 | ocspResponse = SecOCSPCacheCopyMatching(rvc->ocspRequest, NULL); | |
2087 | if (ocspResponse) { | |
2088 | SecOCSPSingleResponseRef sr = | |
2089 | SecOCSPResponseCopySingleResponse(ocspResponse, rvc->ocspRequest); | |
2090 | if (!sr) { | |
2091 | /* The cached ocsp response didn't have a singleResponse for | |
2092 | the cert we are looking for, it's shouldn't be in the cache. */ | |
2093 | secdebug("ocsp", "cached ocsp response did not include status" | |
2094 | " of requested cert"); | |
2095 | } else { | |
2096 | /* We got a singleResponse for the cert we are interested in, | |
2097 | let's proccess it. */ | |
2098 | ||
2099 | /* @@@ If the responder doesn't have the ocsp-nocheck extension | |
2100 | we should check whether the leaf was revoked (we are | |
2101 | already checking the rest of the chain). */ | |
2102 | /* Recheck the OCSP response signature and verify the | |
2103 | response. */ | |
2104 | if (SecOCSPResponseVerify(ocspResponse, rvc)) { | |
2105 | secdebug("ocsp","cached response still has valid signature"); | |
2106 | ||
2107 | if (SecOCSPSingleResponseProccess(sr, rvc)) { | |
2108 | CFAbsoluteTime expTime = | |
2109 | SecOCSPResponseGetExpirationTime(ocspResponse); | |
2110 | if (rvc->nextUpdate == 0 || expTime < rvc->nextUpdate) | |
2111 | rvc->nextUpdate = expTime; | |
2112 | rvc->done = true; | |
2113 | } | |
2114 | } | |
2115 | SecOCSPSingleResponseDestroy(sr); | |
2116 | } | |
2117 | SecOCSPResponseFinalize(ocspResponse); | |
2118 | } | |
2119 | ||
2120 | /* Unless we succefully checked the revocation status of this cert | |
2121 | based on the cache, Attempt to fire off an async http request | |
2122 | for this certs revocation status. */ | |
2123 | bool fetch_done = true; | |
2124 | if (rvc->done || !SecPathBuilderCanAccessNetwork(pvc->builder) || | |
2125 | (fetch_done = SecRVCFetchNext(rvc))) { | |
427c49bc | 2126 | /* We got a cache hit or we aren't allowed to access the network, |
b1ab9ed8 A |
2127 | or the async http post failed. */ |
2128 | SecRVCDelete(rvc); | |
2129 | /* We didn't really start a background job for this cert. */ | |
2130 | pvc->asyncJobCount--; | |
2131 | } else if (!fetch_done) { | |
2132 | /* We started at least one background fetch. */ | |
2133 | completed = false; | |
2134 | } | |
2135 | } | |
2136 | ||
2137 | /* Return false if we started any background jobs. */ | |
2138 | /* We can't just return !pvc->asyncJobCount here, since if we started any | |
2139 | jobs the completion callback will be called eventually and it will call | |
2140 | SecPathBuilderStep(). If for some reason everything completed before we | |
2141 | get here we still want the outer SecPathBuilderStep() to terminate so we | |
2142 | keep track of whether we started any jobs and return false if so. */ | |
2143 | return completed; | |
2144 | } | |
2145 | ||
427c49bc | 2146 | |
b1ab9ed8 A |
2147 | void SecPolicyServerInitalize(void) { |
2148 | gSecPolicyLeafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
2149 | &kCFTypeDictionaryKeyCallBacks, NULL); | |
2150 | gSecPolicyPathCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, | |
2151 | &kCFTypeDictionaryKeyCallBacks, NULL); | |
2152 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2153 | kSecPolicyCheckBasicCertificateProcessing, | |
2154 | SecPolicyCheckBasicCertificateProcessing); | |
2155 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2156 | kSecPolicyCheckCriticalExtensions, SecPolicyCheckCriticalExtensions); | |
2157 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2158 | kSecPolicyCheckIdLinkage, SecPolicyCheckIdLinkage); | |
2159 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2160 | kSecPolicyCheckKeyUsage, SecPolicyCheckKeyUsage); | |
2161 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2162 | kSecPolicyCheckExtendedKeyUsage, SecPolicyCheckExtendedKeyUsage); | |
2163 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2164 | kSecPolicyCheckBasicContraints, SecPolicyCheckBasicContraints); | |
2165 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2166 | kSecPolicyCheckNonEmptySubject, SecPolicyCheckNonEmptySubject); | |
2167 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2168 | kSecPolicyCheckQualifiedCertStatements, | |
2169 | SecPolicyCheckQualifiedCertStatements); | |
2170 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2171 | kSecPolicyCheckSSLHostname, SecPolicyCheckSSLHostname); | |
2172 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2173 | kSecPolicyCheckEmail, SecPolicyCheckEmail); | |
2174 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2175 | kSecPolicyCheckValidIntermediates, SecPolicyCheckValidIntermediates); | |
2176 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2177 | kSecPolicyCheckValidLeaf, SecPolicyCheckValidLeaf); | |
2178 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2179 | kSecPolicyCheckValidRoot, SecPolicyCheckValidRoot); | |
2180 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2181 | kSecPolicyCheckIssuerCommonName, SecPolicyCheckIssuerCommonName); | |
2182 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2183 | kSecPolicyCheckSubjectCommonNamePrefix, | |
2184 | SecPolicyCheckSubjectCommonNamePrefix); | |
2185 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2186 | kSecPolicyCheckSubjectCommonName, | |
2187 | SecPolicyCheckSubjectCommonName); | |
2188 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2189 | kSecPolicyCheckNotValidBefore, | |
2190 | SecPolicyCheckNotValidBefore); | |
2191 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2192 | kSecPolicyCheckChainLength, SecPolicyCheckChainLength); | |
2193 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2194 | kSecPolicyCheckAnchorSHA1, SecPolicyCheckAnchorSHA1); | |
2195 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2196 | kSecPolicyCheckSubjectOrganization, | |
2197 | SecPolicyCheckSubjectOrganization); | |
427c49bc A |
2198 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2199 | kSecPolicyCheckSubjectOrganizationalUnit, | |
2200 | SecPolicyCheckSubjectOrganizationalUnit); | |
b1ab9ed8 A |
2201 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2202 | kSecPolicyCheckEAPTrustedServerNames, | |
2203 | SecPolicyCheckEAPTrustedServerNames); | |
2204 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2205 | kSecPolicyCheckSubjectCommonNameTEST, | |
2206 | SecPolicyCheckSubjectCommonNameTEST); | |
2207 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2208 | kSecPolicyCheckRevocation, | |
2209 | SecPolicyCheckRevocation); | |
2210 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2211 | kSecPolicyCheckNoNetworkAccess, | |
2212 | SecPolicyCheckNoNetworkAccess); | |
2213 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, | |
2214 | kSecPolicyCheckBlackListedLeaf, | |
2215 | SecPolicyCheckBlackListedLeaf); | |
427c49bc A |
2216 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2217 | kSecPolicyCheckGrayListedLeaf, | |
2218 | SecPolicyCheckGrayListedLeaf); | |
b1ab9ed8 A |
2219 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2220 | kSecPolicyCheckLeafMarkerOid, | |
2221 | SecPolicyCheckLeafMarkerOid); | |
2222 | CFDictionaryAddValue(gSecPolicyPathCallbacks, | |
2223 | kSecPolicyCheckIntermediateMarkerOid, | |
2224 | SecPolicyCheckIntermediateMarkerOid); | |
427c49bc A |
2225 | CFDictionaryAddValue(gSecPolicyLeafCallbacks, |
2226 | kSecPolicyCheckCertificatePolicy, | |
2227 | SecPolicyCheckCertificatePolicyOid); | |
b1ab9ed8 A |
2228 | } |
2229 | ||
2230 | /* AUDIT[securityd](done): | |
2231 | array (ok) is a caller provided array, only its cf type has | |
2232 | been checked. | |
2233 | The options (ok) field ends up in policy->_options unchecked, so every access | |
2234 | of policy->_options needs to be validated. | |
2235 | */ | |
2236 | static SecPolicyRef SecPolicyCreateWithArray(CFArrayRef array) { | |
2237 | SecPolicyRef policy = NULL; | |
2238 | require_quiet(array && CFArrayGetCount(array) == 2, errOut); | |
2239 | CFStringRef oid = (CFStringRef)CFArrayGetValueAtIndex(array, 0); | |
2240 | require_quiet(isString(oid), errOut); | |
2241 | CFDictionaryRef options = (CFDictionaryRef)CFArrayGetValueAtIndex(array, 1); | |
2242 | require_quiet(isDictionary(options), errOut); | |
2243 | policy = SecPolicyCreate(oid, options); | |
2244 | errOut: | |
2245 | return policy; | |
2246 | } | |
2247 | ||
2248 | /* AUDIT[securityd](done): | |
2249 | value (ok) is an element in a caller provided array. | |
2250 | */ | |
2251 | static void deserializePolicy(const void *value, void *context) { | |
2252 | CFArrayRef policyArray = (CFArrayRef)value; | |
2253 | if (isArray(policyArray)) { | |
2254 | CFTypeRef deserializedPolicy = SecPolicyCreateWithArray(policyArray); | |
2255 | if (deserializedPolicy) { | |
2256 | CFArrayAppendValue((CFMutableArrayRef)context, deserializedPolicy); | |
2257 | CFRelease(deserializedPolicy); | |
2258 | } | |
2259 | } | |
2260 | } | |
2261 | ||
2262 | /* AUDIT[securityd](done): | |
2263 | serializedPolicies (ok) is a caller provided array, only its cf type has | |
2264 | been checked. | |
2265 | */ | |
2266 | CFArrayRef SecPolicyArrayDeserialize(CFArrayRef serializedPolicies) { | |
2267 | CFMutableArrayRef result = NULL; | |
2268 | require_quiet(isArray(serializedPolicies), errOut); | |
2269 | CFIndex count = CFArrayGetCount(serializedPolicies); | |
2270 | result = CFArrayCreateMutable(kCFAllocatorDefault, count, &kCFTypeArrayCallBacks); | |
2271 | CFRange all_policies = { 0, count }; | |
2272 | CFArrayApplyFunction(serializedPolicies, all_policies, deserializePolicy, result); | |
2273 | errOut: | |
2274 | return result; | |
2275 | } | |
2276 | ||
427c49bc A |
2277 | // MARK: - |
2278 | // MARK: SecPVCRef | |
b1ab9ed8 A |
2279 | /******************************************************** |
2280 | ****************** SecPVCRef Functions ***************** | |
2281 | ********************************************************/ | |
2282 | ||
2283 | void SecPVCInit(SecPVCRef pvc, SecPathBuilderRef builder, CFArrayRef policies, | |
2284 | CFAbsoluteTime verifyTime) { | |
2285 | secdebug("alloc", "%p", pvc); | |
2286 | // Weird logging policies crashes. | |
2287 | //secdebug("policy", "%@", policies); | |
2288 | pvc->builder = builder; | |
2289 | pvc->policies = policies; | |
2290 | if (policies) | |
2291 | CFRetain(policies); | |
2292 | pvc->verifyTime = verifyTime; | |
2293 | pvc->path = NULL; | |
2294 | pvc->details = NULL; | |
2295 | pvc->info = NULL; | |
2296 | pvc->valid_policy_tree = NULL; | |
2297 | pvc->callbacks = NULL; | |
2298 | pvc->policyIX = 0; | |
2299 | pvc->rvcs = NULL; | |
2300 | pvc->asyncJobCount = 0; | |
2301 | pvc->check_revocation = false; | |
2302 | pvc->optionally_ev = false; | |
2303 | pvc->is_ev = false; | |
2304 | pvc->result = true; | |
2305 | } | |
2306 | ||
2307 | static void SecPVCDeleteRVCs(SecPVCRef pvc) { | |
2308 | secdebug("alloc", "%p", pvc); | |
2309 | if (pvc->rvcs) { | |
2310 | free(pvc->rvcs); | |
2311 | pvc->rvcs = NULL; | |
2312 | } | |
2313 | } | |
2314 | ||
2315 | void SecPVCDelete(SecPVCRef pvc) { | |
2316 | secdebug("alloc", "%p", pvc); | |
2317 | CFReleaseNull(pvc->policies); | |
2318 | CFReleaseNull(pvc->details); | |
2319 | CFReleaseNull(pvc->info); | |
2320 | if (pvc->valid_policy_tree) { | |
2321 | policy_tree_prune(&pvc->valid_policy_tree); | |
2322 | } | |
2323 | SecPVCDeleteRVCs(pvc); | |
2324 | } | |
2325 | ||
2326 | void SecPVCSetPath(SecPVCRef pvc, SecCertificatePathRef path, | |
2327 | CFArrayRef details) { | |
2328 | secdebug("policy", "%@", path); | |
2329 | if (pvc->path != path) { | |
2330 | /* Changing path makes us clear the Revocation Verification Contexts */ | |
2331 | SecPVCDeleteRVCs(pvc); | |
2332 | pvc->path = path; | |
2333 | } | |
2334 | pvc->details = details; | |
2335 | CFReleaseNull(pvc->info); | |
2336 | if (pvc->valid_policy_tree) { | |
2337 | policy_tree_prune(&pvc->valid_policy_tree); | |
2338 | } | |
2339 | pvc->policyIX = 0; | |
2340 | pvc->result = true; | |
2341 | } | |
2342 | ||
2343 | SecPolicyRef SecPVCGetPolicy(SecPVCRef pvc) { | |
2344 | return (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX); | |
2345 | } | |
2346 | ||
2347 | CFIndex SecPVCGetCertificateCount(SecPVCRef pvc) { | |
2348 | return SecCertificatePathGetCount(pvc->path); | |
2349 | } | |
2350 | ||
2351 | SecCertificateRef SecPVCGetCertificateAtIndex(SecPVCRef pvc, CFIndex ix) { | |
2352 | return SecCertificatePathGetCertificateAtIndex(pvc->path, ix); | |
2353 | } | |
2354 | ||
2355 | bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc, CFIndex ix) { | |
2356 | return SecCertificatePathSelfSignedIndex(pvc->path) == ix; | |
2357 | } | |
2358 | ||
2359 | void SecPVCSetCheckRevocation(SecPVCRef pvc) { | |
2360 | pvc->check_revocation = true; | |
2361 | secdebug("ocsp", "deferred revocation checking enabled"); | |
2362 | } | |
2363 | ||
2364 | bool SecPVCIsAnchored(SecPVCRef pvc) { | |
2365 | return SecCertificatePathIsAnchored(pvc->path); | |
2366 | } | |
2367 | ||
2368 | CFAbsoluteTime SecPVCGetVerifyTime(SecPVCRef pvc) { | |
2369 | return pvc->verifyTime; | |
2370 | } | |
2371 | ||
2372 | /* AUDIT[securityd](done): | |
2373 | policy->_options is a caller provided dictionary, only its cf type has | |
2374 | been checked. | |
2375 | */ | |
2376 | bool SecPVCSetResultForced(SecPVCRef pvc, | |
2377 | CFStringRef key, CFIndex ix, CFTypeRef result, bool force) { | |
2378 | ||
427c49bc | 2379 | secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, |
b1ab9ed8 A |
2380 | (pvc->callbacks == gSecPolicyLeafCallbacks ? "leaf" |
2381 | : (pvc->callbacks == gSecPolicyPathCallbacks ? "path" | |
2382 | : "custom")), | |
2383 | (force ? "force" : ""), result); | |
2384 | ||
2385 | /* If this is not something the current policy cares about ignore | |
2386 | this error and return true so our caller continues evaluation. */ | |
2387 | if (!force) { | |
2388 | /* @@@ The right long term fix might be to check if none of the passed | |
2389 | in policies contain this key, since not all checks are run for all | |
2390 | policies. */ | |
2391 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
2392 | if (policy && !CFDictionaryContainsKey(policy->_options, key)) | |
2393 | return true; | |
2394 | } | |
2395 | ||
2396 | /* @@@ Check to see if the SecTrustSettings for the certificate in question | |
2397 | tell us to ignore this error. */ | |
2398 | pvc->result = false; | |
2399 | if (!pvc->details) | |
2400 | return false; | |
2401 | ||
2402 | CFMutableDictionaryRef detail = | |
2403 | (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix); | |
2404 | ||
2405 | /* Perhaps detail should have an array of results per key? As it stands | |
2406 | in the case of multiple policy failures the last failure stands. */ | |
2407 | CFDictionarySetValue(detail, key, result); | |
2408 | ||
2409 | return true; | |
2410 | } | |
2411 | ||
2412 | bool SecPVCSetResult(SecPVCRef pvc, | |
2413 | CFStringRef key, CFIndex ix, CFTypeRef result) { | |
2414 | return SecPVCSetResultForced(pvc, key, ix, result, false); | |
2415 | } | |
2416 | ||
2417 | /* AUDIT[securityd](done): | |
2418 | key(ok) is a caller provided. | |
2419 | value(ok, unused) is a caller provided. | |
2420 | */ | |
2421 | static void SecPVCValidateKey(const void *key, const void *value, | |
2422 | void *context) { | |
2423 | SecPVCRef pvc = (SecPVCRef)context; | |
2424 | ||
2425 | /* If our caller doesn't want full details and we failed earlier there is | |
2426 | no point in doing additional checks. */ | |
2427 | if (!pvc->result && !pvc->details) | |
2428 | return; | |
2429 | ||
2430 | SecPolicyCheckFunction fcn = (SecPolicyCheckFunction) | |
2431 | CFDictionaryGetValue(pvc->callbacks, key); | |
2432 | ||
2433 | if (!fcn) { | |
2434 | #if 0 | |
2435 | /* Why not to have optional policy checks rant: | |
2436 | Not all keys are in all dictionaries anymore, so why not make checks | |
2437 | optional? This way a client can ask for something and the server will | |
2438 | do a best effort based on the supported flags. It works since they are | |
2439 | synchronized now, but we need some debug checking here for now. */ | |
2440 | pvc->result = false; | |
2441 | #endif | |
2442 | if (pvc->callbacks == gSecPolicyLeafCallbacks) { | |
2443 | if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks, key)) { | |
2444 | pvc->result = false; | |
2445 | } | |
2446 | } else if (pvc->callbacks == gSecPolicyPathCallbacks) { | |
2447 | if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks, key)) { | |
2448 | pvc->result = false; | |
2449 | } | |
2450 | } else { | |
2451 | /* Non standard valdation phase, nothing is optional. */ | |
2452 | pvc->result = false; | |
2453 | } | |
2454 | return; | |
2455 | } | |
2456 | ||
2457 | fcn(pvc, (CFStringRef)key); | |
2458 | } | |
2459 | ||
2460 | /* AUDIT[securityd](done): | |
2461 | policy->_options is a caller provided dictionary, only its cf type has | |
2462 | been checked. | |
2463 | */ | |
2464 | bool SecPVCLeafChecks(SecPVCRef pvc) { | |
2465 | pvc->result = true; | |
2466 | CFArrayRef policies = pvc->policies; | |
2467 | CFIndex ix, count = CFArrayGetCount(policies); | |
2468 | for (ix = 0; ix < count; ++ix) { | |
2469 | SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix); | |
2470 | pvc->policyIX = ix; | |
2471 | /* Validate all keys for all policies. */ | |
2472 | pvc->callbacks = gSecPolicyLeafCallbacks; | |
2473 | CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc); | |
2474 | if (!pvc->result && !pvc->details) | |
2475 | return pvc->result; | |
2476 | } | |
2477 | ||
2478 | return pvc->result; | |
2479 | } | |
2480 | ||
2481 | bool SecPVCParentCertificateChecks(SecPVCRef pvc, CFIndex ix) { | |
2482 | /* Check stuff common to intermediate and anchors. */ | |
2483 | CFAbsoluteTime verifyTime = SecPVCGetVerifyTime(pvc); | |
2484 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2485 | bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1 | |
2486 | && SecPVCIsAnchored(pvc)); | |
2487 | if (!SecCertificateIsValid(cert, verifyTime)) { | |
2488 | /* Certificate has expired. */ | |
2489 | if (!SecPVCSetResult(pvc, is_anchor ? kSecPolicyCheckValidRoot | |
2490 | : kSecPolicyCheckValidIntermediates, ix, kCFBooleanFalse)) | |
2491 | goto errOut; | |
2492 | } | |
2493 | ||
2494 | if (is_anchor) { | |
2495 | /* Perform anchor specific checks. */ | |
2496 | /* Don't think we have any of these. */ | |
2497 | } else { | |
2498 | /* Perform intermediate specific checks. */ | |
2499 | ||
2500 | /* (k) */ | |
2501 | const SecCEBasicConstraints *bc = | |
2502 | SecCertificateGetBasicConstraints(cert); | |
2503 | if (!bc || !bc->isCA) { | |
2504 | /* Basic constraints not present or not marked as isCA, illegal. */ | |
2505 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckBasicContraints, | |
2506 | ix, kCFBooleanFalse, true)) | |
2507 | goto errOut; | |
2508 | } | |
2509 | /* Consider adding (l) max_path_length checking here. */ | |
2510 | ||
2511 | /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */ | |
2512 | SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert); | |
2513 | if (keyUsage && !(keyUsage & kSecKeyUsageKeyCertSign)) { | |
2514 | if (!SecPVCSetResultForced(pvc, kSecPolicyCheckKeyUsage, | |
2515 | ix, kCFBooleanFalse, true)) | |
2516 | goto errOut; | |
2517 | } | |
2518 | } | |
2519 | ||
2520 | errOut: | |
2521 | return pvc->result; | |
2522 | } | |
2523 | ||
2524 | bool SecPVCBlackListedKeyChecks(SecPVCRef pvc, CFIndex ix) { | |
2525 | /* Check stuff common to intermediate and anchors. */ | |
b1ab9ed8 | 2526 | |
427c49bc A |
2527 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); |
2528 | if (NULL != otapkiRef) | |
2529 | { | |
2530 | CFSetRef blackListedKeys = SecOTAPKICopyBlackListSet(otapkiRef); | |
2531 | CFRelease(otapkiRef); | |
2532 | if (NULL != blackListedKeys) | |
2533 | { | |
2534 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2535 | bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1 | |
2536 | && SecPVCIsAnchored(pvc)); | |
2537 | if (!is_anchor) { | |
2538 | /* Check for blacklisted intermediates keys. */ | |
2539 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
2540 | if (dgst) { | |
2541 | /* Check dgst against blacklist. */ | |
2542 | if (CFSetContainsValue(blackListedKeys, dgst)) { | |
2543 | SecPVCSetResultForced(pvc, kSecPolicyCheckBlackListedKey, | |
2544 | ix, kCFBooleanFalse, true); | |
2545 | } | |
2546 | CFRelease(dgst); | |
2547 | } | |
2548 | } | |
2549 | CFRelease(blackListedKeys); | |
2550 | return pvc->result; | |
2551 | } | |
2552 | } | |
2553 | // Assume OK | |
2554 | return true; | |
2555 | } | |
2556 | ||
2557 | bool SecPVCGrayListedKeyChecks(SecPVCRef pvc, CFIndex ix) | |
2558 | { | |
2559 | /* Check stuff common to intermediate and anchors. */ | |
2560 | SecOTAPKIRef otapkiRef = SecOTAPKICopyCurrentOTAPKIRef(); | |
2561 | if (NULL != otapkiRef) | |
2562 | { | |
2563 | CFSetRef grayListKeys = SecOTAPKICopyGrayList(otapkiRef); | |
2564 | CFRelease(otapkiRef); | |
2565 | if (NULL != grayListKeys) | |
2566 | { | |
2567 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(pvc, ix); | |
2568 | bool is_anchor = (ix == SecPVCGetCertificateCount(pvc) - 1 | |
2569 | && SecPVCIsAnchored(pvc)); | |
2570 | if (!is_anchor) { | |
2571 | /* Check for gray listed intermediates keys. */ | |
2572 | CFDataRef dgst = SecCertificateCopyPublicKeySHA1Digest(cert); | |
2573 | if (dgst) { | |
2574 | /* Check dgst against gray list. */ | |
2575 | if (CFSetContainsValue(grayListKeys, dgst)) { | |
2576 | SecPVCSetResultForced(pvc, kSecPolicyCheckGrayListedKey, | |
2577 | ix, kCFBooleanFalse, true); | |
2578 | } | |
2579 | CFRelease(dgst); | |
2580 | } | |
2581 | } | |
2582 | CFRelease(grayListKeys); | |
2583 | return pvc->result; | |
2584 | } | |
2585 | } | |
2586 | // Assume ok | |
2587 | return true; | |
b1ab9ed8 A |
2588 | } |
2589 | ||
2590 | /* AUDIT[securityd](done): | |
2591 | policy->_options is a caller provided dictionary, only its cf type has | |
2592 | been checked. | |
2593 | */ | |
2594 | bool SecPVCPathChecks(SecPVCRef pvc) { | |
2595 | secdebug("policy", "begin path: %@", pvc->path); | |
2596 | bool completed = true; | |
2597 | /* This needs to be initialized before we call any function that might call | |
2598 | SecPVCSetResultForced(). */ | |
2599 | pvc->policyIX = 0; | |
2600 | SecPolicyCheckIdLinkage(pvc, kSecPolicyCheckIdLinkage); | |
2601 | if (pvc->result || pvc->details) { | |
2602 | SecPolicyCheckBasicCertificateProcessing(pvc, | |
2603 | kSecPolicyCheckBasicCertificateProcessing); | |
2604 | } | |
2605 | ||
2606 | CFArrayRef policies = pvc->policies; | |
2607 | CFIndex count = CFArrayGetCount(policies); | |
2608 | for (; pvc->policyIX < count; ++pvc->policyIX) { | |
2609 | /* Validate all keys for all policies. */ | |
2610 | pvc->callbacks = gSecPolicyPathCallbacks; | |
2611 | SecPolicyRef policy = SecPVCGetPolicy(pvc); | |
2612 | CFDictionaryApplyFunction(policy->_options, SecPVCValidateKey, pvc); | |
2613 | if (!pvc->result && !pvc->details) | |
2614 | return completed; | |
2615 | } | |
2616 | ||
2617 | /* Check the things we can't check statically for the certificate path. */ | |
2618 | /* Critical Extensions, chainLength. */ | |
2619 | ||
2620 | /* Policy tests. */ | |
2621 | pvc->is_ev = false; | |
2622 | if ((pvc->result || pvc->details) && pvc->optionally_ev) { | |
2623 | bool pre_ev_check_result = pvc->result; | |
2624 | SecPolicyCheckEV(pvc, kSecPolicyCheckExtendedValidation); | |
2625 | pvc->is_ev = pvc->result; | |
2626 | /* If ev checking failed, we still want to accept this chain | |
2627 | as a non EV one, if it was valid as such. */ | |
2628 | pvc->result = pre_ev_check_result; | |
2629 | } | |
2630 | /* Check revocation only if the chain is valid so far. Then only check | |
2631 | revocation if the client asked for it explicitly or is_ev is | |
2632 | true. */ | |
2633 | if (pvc->result && (pvc->is_ev || pvc->check_revocation)) { | |
2634 | completed = SecPVCCheckRevocation(pvc); | |
2635 | } | |
2636 | ||
2637 | //errOut: | |
2638 | secdebug("policy", "end %strusted completed: %d path: %@", | |
2639 | (pvc->result ? "" : "not "), completed, pvc->path); | |
2640 | return completed; | |
2641 | } | |
2642 | ||
2643 | /* This function returns 0 to indicate revocation checking was not completed | |
2644 | for this certificate chain, otherwise return to date at which the first | |
2645 | piece of revocation checking info we used expires. */ | |
2646 | CFAbsoluteTime SecPVCGetEarliestNextUpdate(SecPVCRef pvc) { | |
2647 | CFIndex certIX, certCount = SecPVCGetCertificateCount(pvc); | |
2648 | CFAbsoluteTime enu = 0; | |
2649 | if (certCount <= 1 || !pvc->rvcs) { | |
2650 | return enu; | |
2651 | } | |
2652 | certCount--; | |
2653 | ||
2654 | for (certIX = 0; certIX < certCount; ++certIX) { | |
2655 | SecRVCRef rvc = &((SecRVCRef)pvc->rvcs)[certIX]; | |
2656 | if (rvc->nextUpdate == 0) { | |
2657 | if (certIX > 0) { | |
2658 | /* We allow for CA certs to not be revocation checked if they | |
2659 | have no ocspResponders to check against, but the leaf | |
2660 | must be checked in order for us to claim we did revocation | |
2661 | checking. */ | |
2662 | SecCertificateRef cert = | |
2663 | SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX); | |
2664 | CFArrayRef ocspResponders = SecCertificateGetOCSPResponders(cert); | |
2665 | if (!ocspResponders || CFArrayGetCount(ocspResponders) == 0) { | |
2666 | /* We can't check this cert so we don't consider it a soft | |
2667 | failure that we didn't. Ideally we should support crl | |
2668 | checking and remove this workaround, since that more | |
2669 | strict. */ | |
2670 | continue; | |
2671 | } | |
2672 | } | |
2673 | secdebug("ocsp", "revocation checking soft failure for cert: %ld", | |
2674 | certIX); | |
2675 | enu = rvc->nextUpdate; | |
2676 | break; | |
2677 | } | |
2678 | if (enu == 0 || rvc->nextUpdate < enu) { | |
2679 | enu = rvc->nextUpdate; | |
2680 | } | |
2681 | #if 0 | |
2682 | /* Perhaps we don't want to do this since some policies might | |
2683 | ignore the certificate experation but still use revocation | |
2684 | checking. */ | |
2685 | ||
2686 | /* Earliest certificate expiration date. */ | |
2687 | SecCertificateRef cert = SecPVCGetCertificateAtIndex(rvc->pvc, rvc->certIX); | |
2688 | CFAbsoluteTime nva = SecCertificateNotValidAfter(cert); | |
2689 | if (nva && (enu == 0 || nva < enu) | |
2690 | enu = nva; | |
2691 | #endif | |
2692 | } | |
2693 | ||
2694 | secdebug("ocsp", "revocation valid until: %lg", enu); | |
2695 | return enu; | |
2696 | } |