]> git.saurik.com Git - apple/security.git/blob - sec/securityd/SecPolicyServer.c
Security-55471.tar.gz
[apple/security.git] / sec / securityd / SecPolicyServer.c
1 /*
2 * Copyright (c) 2008-2012 Apple Inc. All Rights Reserved.
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>
31 #include <utilities/SecIOFormat.h>
32 #include <securityd/asynchttp.h>
33 #include <securityd/policytree.h>
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>
40 #include <utilities/debugging.h>
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>
59 #include <utilities/array_size.h>
60 #include <utilities/SecCFWrappers.h>
61 #include "OTATrustUtilities.h"
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
83
84 /********************************************************
85 ****************** SecPolicy object ********************
86 ********************************************************/
87
88 static CFMutableDictionaryRef gSecPolicyLeafCallbacks = NULL;
89 static CFMutableDictionaryRef gSecPolicyPathCallbacks = NULL;
90
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;
106 }
107
108 CFArrayRef roots = NULL;
109 CFStringRef oid = SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault, policyOID);
110 if (oid && evToPolicyAnchorDigest)
111 {
112 result = (CFArrayRef)CFDictionaryGetValue(evToPolicyAnchorDigest, oid);
113 if (roots && CFGetTypeID(result) != CFArrayGetTypeID())
114 {
115 ocspdErrorLog("EVRoot.plist has non array value");
116 result = NULL;
117 }
118 CFRelease(oid);
119 }
120 return result;
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);
870 CFTypeRef value = CFDictionaryGetValue(policy->_options, key);
871 CFDataRef anchorSHA1 = SecCertificateGetSHA1Digest(cert);
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;
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
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
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. */
971 CFReleaseSafe(dnsNames);
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
996 static const unsigned char UTN_USERFirst_Hardware_Serial[][16] = {
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;
1046 for (i = 0; i < array_size(UTN_USERFirst_Hardware_Serial); i++)
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);
1052 CFReleaseSafe(serial);
1053 return;
1054 }
1055 }
1056 }
1057 CFRelease(serial);
1058 }
1059 }
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 }
1082 }
1083
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
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);
1115
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
1136
1137
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) {
1217 policy_tree_t child;
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
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
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
1704 // MARK: -
1705 // MARK: SecRVCRef
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:
1794 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex, rvc->certIX);
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:
1802 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex, rvc->certIX);
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 */
1816 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex, rvc->certIX);
1817 proccessed = false;
1818 break;
1819 default:
1820 secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex,
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) {
1869 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
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;
1980 rvc->http.queue = SecPathBuilderGetQueue(pvc->builder);
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. */
2066 pvc->asyncJobCount = (unsigned int) certCount;
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))) {
2126 /* We got a cache hit or we aren't allowed to access the network,
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
2146
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);
2198 CFDictionaryAddValue(gSecPolicyLeafCallbacks,
2199 kSecPolicyCheckSubjectOrganizationalUnit,
2200 SecPolicyCheckSubjectOrganizationalUnit);
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);
2216 CFDictionaryAddValue(gSecPolicyLeafCallbacks,
2217 kSecPolicyCheckGrayListedLeaf,
2218 SecPolicyCheckGrayListedLeaf);
2219 CFDictionaryAddValue(gSecPolicyLeafCallbacks,
2220 kSecPolicyCheckLeafMarkerOid,
2221 SecPolicyCheckLeafMarkerOid);
2222 CFDictionaryAddValue(gSecPolicyPathCallbacks,
2223 kSecPolicyCheckIntermediateMarkerOid,
2224 SecPolicyCheckIntermediateMarkerOid);
2225 CFDictionaryAddValue(gSecPolicyLeafCallbacks,
2226 kSecPolicyCheckCertificatePolicy,
2227 SecPolicyCheckCertificatePolicyOid);
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
2277 // MARK: -
2278 // MARK: SecPVCRef
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
2379 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key,
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. */
2526
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;
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 }