2 * Copyright (c) 2008-2015 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecPolicyServer.c - Trust policies dealing with certificate revocation.
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 <securityd/nameconstraints.h>
35 #include <CoreFoundation/CFTimeZone.h>
37 #include <libDER/oidsPriv.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <AssertMacros.h>
41 #include <utilities/debugging.h>
42 #include <security_asn1/SecAsn1Coder.h>
43 #include <security_asn1/ocspTemplates.h>
44 #include <security_asn1/oidsalg.h>
45 #include <security_asn1/oidsocsp.h>
46 #include <CommonCrypto/CommonDigest.h>
47 #include <Security/SecFramework.h>
48 #include <Security/SecPolicyInternal.h>
49 #include <Security/SecTrustPriv.h>
50 #include <Security/SecInternal.h>
51 #include <Security/SecKeyPriv.h>
52 #include <CFNetwork/CFHTTPMessage.h>
53 #include <CFNetwork/CFHTTPStream.h>
54 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
56 #include <securityd/SecOCSPRequest.h>
57 #include <securityd/SecOCSPResponse.h>
58 #include <securityd/asynchttp.h>
59 #include <securityd/SecTrustServer.h>
60 #include <securityd/SecOCSPCache.h>
61 #include <utilities/array_size.h>
62 #include <utilities/SecCFWrappers.h>
63 #include <utilities/SecAppleAnchorPriv.h>
64 #include "OTATrustUtilities.h"
66 #define ocspdErrorLog(args...) asl_log(NULL, NULL, ASL_LEVEL_ERR, ## args)
68 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
69 #ifndef DUMP_OCSPRESPONSES
70 #define DUMP_OCSPRESPONSES 0
73 #if DUMP_OCSPRESPONSES
78 static void secdumpdata(CFDataRef data
, const char *name
) {
79 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
80 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
87 /********************************************************
88 ****************** SecPolicy object ********************
89 ********************************************************/
91 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
92 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
94 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
96 CFArrayRef result
= NULL
;
97 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
98 if (NULL
== otapkiRef
)
103 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
104 CFRelease(otapkiRef
);
106 if (NULL
== evToPolicyAnchorDigest
)
111 CFArrayRef roots
= NULL
;
112 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
113 if (oid
&& evToPolicyAnchorDigest
)
115 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
116 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
118 ocspdErrorLog("EVRoot.plist has non array value");
123 CFReleaseSafe(evToPolicyAnchorDigest
);
128 static bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
129 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
132 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
133 policy_set_t valid_policies
) {
134 /* Ensure that this certificate is a valid anchor for one of the
135 certificate policy oids specified in the leaf. */
136 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
138 bool good_ev_anchor
= false;
139 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
140 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
141 if (digests
&& CFArrayContainsValue(digests
,
142 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
143 secdebug("ev", "found anchor for policy oid");
144 good_ev_anchor
= true;
148 require_quiet(good_ev_anchor
, notEV
);
150 CFAbsoluteTime october2006
= 178761600;
151 if (SecCertificateVersion(certificate
) >= 3
152 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
153 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
154 require_quiet(bc
&& bc
->isCA
== true, notEV
);
155 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
156 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
157 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
);
160 CFAbsoluteTime jan2011
= 315532800;
161 if (SecCertificateNotValidBefore(certificate
) < jan2011
) {
162 /* At least MD5, SHA-1 with RSA 2048 or ECC NIST P-256. */
164 /* At least SHA-1, SHA-256, SHA-384 or SHA-512 with RSA 2048 or
173 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
174 const SecCECertificatePolicies
*cp
;
175 cp
= SecCertificateGetCertificatePolicies(certificate
);
176 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
177 /* SecCertificateGetCRLDistributionPoints() is a noop right now */
179 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
180 require_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
);
182 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
183 require_quiet(bc
&& bc
->isCA
== true, notEV
);
184 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
185 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
186 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
);
187 CFAbsoluteTime jan2011
= 315532800;
188 if (SecCertificateNotValidBefore(certificate
) < jan2011
) {
189 /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
191 /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
200 bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate
) {
201 /* 3. Subscriber Certificate. */
203 /* (a) certificate Policies */
204 const SecCECertificatePolicies
*cp
;
205 cp
= SecCertificateGetCertificatePolicies(certificate
);
206 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
207 /* Now find at least one policy in here that has a qualifierID of id-qt 2
208 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
210 bool found_ev_anchor_for_leaf_policy
= false;
211 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
212 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
213 found_ev_anchor_for_leaf_policy
= true;
216 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
218 /* SecCertificateGetCRLDistributionPoints() is a noop right now */
220 /* (b) cRLDistributionPoint
221 (c) authorityInformationAccess */
222 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
224 require_quiet(CFArrayGetCount(cdp
) > 0, notEV
);
226 CFArrayRef
or = SecCertificateGetOCSPResponders(certificate
);
227 require_quiet(or && CFArrayGetCount(or) > 0, notEV
);
228 //CFArrayRef ci = SecCertificateGetCAIssuers(certificate);
232 /* (d) basicConstraints
233 If present, the cA field MUST be set false. */
234 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
236 require_quiet(bc
->isCA
== false, notEV
);
240 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
242 require_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
);
246 /* The EV Cert Spec errata specifies this, though this is a check for SSL
247 not specifically EV. */
251 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
252 SecCertificateCopyExtendedKeyUsage(certificate
);
255 CFAbsoluteTime jan2011
= 315532800;
256 if (SecCertificateNotValidAfter(certificate
) < jan2011
) {
257 /* At least SHA-1 with RSA 1024 or ECC NIST P-256. */
259 /* At least SHA-1, SHA-256, SHA-284 or SHA-512 with RSA 2028 or
268 /********************************************************
269 **************** SecPolicy Callbacks *******************
270 ********************************************************/
271 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
275 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
277 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
278 CFDataRef parentSubjectKeyID
= NULL
;
279 for (ix
= count
- 1; ix
>= 0; --ix
) {
280 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
281 /* If the previous certificate in the chain had a SubjectKeyID,
282 make sure it matches the current certificates AuthorityKeyID. */
283 if (parentSubjectKeyID
) {
284 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
285 SubjectKeyID can be critical. Currenty we don't check
287 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
288 if (authorityKeyID
) {
289 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
290 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
291 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
297 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
301 static bool keyusage_allows(SecKeyUsage keyUsage
, CFTypeRef xku
) {
302 if (!xku
|| CFGetTypeID(xku
) != CFNumberGetTypeID())
306 CFNumberGetValue((CFNumberRef
)xku
, kCFNumberSInt32Type
, &dku
);
307 SecKeyUsage ku
= (SecKeyUsage
)dku
;
308 return (keyUsage
& ku
) == ku
;
311 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
313 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
314 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(leaf
);
316 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
317 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
319 CFIndex ix
, count
= CFArrayGetCount(xku
);
320 for (ix
= 0; ix
< count
; ++ix
) {
321 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
322 if (keyusage_allows(keyUsage
, ku
)) {
328 match
= keyusage_allows(keyUsage
, xku
);
331 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
335 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
337 if (!xeku
|| CFGetTypeID(xeku
) != CFDataGetTypeID())
339 if (extendedKeyUsage
) {
340 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
341 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
343 /* Certificate has no extended key usage, only a match if the policy
344 contains a 0 length CFDataRef. */
345 return CFDataGetLength((CFDataRef
)xeku
) == 0;
349 /* AUDIT[securityd](done):
350 policy->_options is a caller provided dictionary, only its cf type has
353 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
354 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
355 CFArrayRef leafExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(leaf
);
357 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
358 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
360 CFIndex ix
, count
= CFArrayGetCount(xeku
);
361 for (ix
= 0; ix
< count
; ix
++) {
362 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
363 if (extendedkeyusage_allows(leafExtendedKeyUsage
, eku
)) {
369 match
= extendedkeyusage_allows(leafExtendedKeyUsage
, xeku
);
371 CFReleaseSafe(leafExtendedKeyUsage
);
373 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
378 static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc
,
379 CFStringRef key
, bool strict
) {
380 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
381 for (ix
= 0; ix
< count
; ++ix
) {
382 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
383 const SecCEBasicConstraints
*bc
=
384 SecCertificateGetBasicConstraints(cert
);
388 /* Leaf certificate has basic constraints extension. */
389 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
391 } else if (!bc
->critical
) {
392 /* Basic constraints extension is not marked critical. */
393 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
398 if (ix
> 0 || count
== 1) {
400 /* Non leaf certificate marked as isCA false. */
401 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
405 if (bc
->pathLenConstraintPresent
) {
406 if (bc
->pathLenConstraint
< (uint32_t)(ix
- 1)) {
408 /* @@@ If a self signed certificate is issued by
409 another cert that is trusted, then we are supposed
410 to treat the self signed cert itself as the anchor
411 for path length purposes. */
412 CFIndex ssix
= SecCertificatePathSelfSignedIndex(path
);
413 if (ssix
>= 0 && ix
>= ssix
) {
414 /* It's ok if the pathLenConstraint isn't met for
415 certificates signing a self signed cert in the
420 /* Path Length Constraint Exceeded. */
421 if (!SecPVCSetResult(pvc
, key
, ix
,
428 } else if (strict
&& ix
> 0) {
429 /* In strict mode all CA certificates *MUST* have a critical
430 basic constraints extension and the leaf certificate
431 *MUST NOT* have a basic constraints extension. */
432 /* CA certificate is missing basicConstraints extension. */
433 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
440 static void SecPolicyCheckBasicContraints(SecPVCRef pvc
,
442 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
445 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
447 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
448 for (ix
= 0; ix
< count
; ++ix
) {
449 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
450 /* If the certificate has a subject, or
451 if it doesn't, and it's the leaf and not self signed,
452 and also has a critical subjectAltName extension it's valid. */
453 if (!SecCertificateHasSubject(cert
)) {
454 if (ix
== 0 && count
> 1) {
455 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
456 /* Leaf certificate with empty subject does not have
457 a critical subject alt name extension. */
458 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
462 /* CA certificate has empty subject. */
463 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
470 static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc
,
474 /* Compare hostname suffix to domain name.
475 This function does not process wildcards, and allows hostname to match
476 any subdomain level of the provided domain.
478 To match, the last domain length chars of hostname must equal domain,
479 and the character immediately preceding domain in hostname (if any)
480 must be a dot. This means that domain 'bar.com' will match hostname
481 values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
483 Characters in each string are converted to lowercase for the comparison.
484 Trailing '.' characters in both names will be ignored.
486 Returns true on match, else false.
488 static bool SecDomainSuffixMatch(CFStringRef hostname
, CFStringRef domain
) {
489 CFStringInlineBuffer hbuf
, dbuf
;
492 hlength
= CFStringGetLength(hostname
),
493 dlength
= CFStringGetLength(domain
);
494 CFRange hrange
= { 0, hlength
}, drange
= { 0, dlength
};
495 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
496 CFStringInitInlineBuffer(domain
, &dbuf
, drange
);
498 if((hlength
== 0) || (dlength
== 0)) {
499 /* trivial case with at least one empty name */
500 return (hlength
== dlength
) ? true : false;
503 /* trim off trailing dots */
504 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hlength
-1);
505 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dlength
-1);
507 hrange
.length
= --hlength
;
510 drange
.length
= --dlength
;
513 /* trim off leading dot in suffix, if present */
514 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, 0);
515 if((dlength
> 0) && (dch
== '.')) {
517 drange
.length
= --dlength
;
520 if(hlength
< dlength
) {
524 /* perform case-insensitive comparison of domain suffix */
525 for (hix
= (hlength
-dlength
),
526 dix
= drange
.location
; dix
< drange
.length
; dix
++) {
527 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
);
528 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dix
);
529 if (towlower(hch
) != towlower(dch
)) {
534 /* require a dot prior to domain suffix, unless hostname == domain */
535 if(hlength
> dlength
) {
536 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, (hlength
-(dlength
+1)));
545 /* Compare hostname, to a server name obtained from the server's cert
546 Obtained from the SubjectAltName or the CommonName entry in the Subject.
547 Limited wildcard checking is performed here as outlined in
549 RFC 2818 Section 3.1. Server Identity
551 [...] Names may contain the wildcard
552 character * which is considered to match any single domain name
553 component or component fragment. E.g., *.a.com matches foo.a.com but
554 not bar.foo.a.com. f*.com matches foo.com but not bar.com.
557 Trailing '.' characters in the hostname will be ignored.
559 Returns true on match, else false.
563 bool SecDNSMatch(CFStringRef hostname
, CFStringRef servername
) {
564 CFStringInlineBuffer hbuf
, sbuf
;
565 CFIndex hix
, six
, tix
,
566 hlength
= CFStringGetLength(hostname
),
567 slength
= CFStringGetLength(servername
);
568 CFRange hrange
= { 0, hlength
}, srange
= { 0, slength
};
569 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
570 CFStringInitInlineBuffer(servername
, &sbuf
, srange
);
571 bool prevLabel
=false;
573 for (hix
= six
= 0; six
< slength
; ++six
) {
574 UniChar tch
, hch
, sch
= CFStringGetCharacterFromInlineBuffer(&sbuf
, six
);
577 /* RFC6125: No wildcard after a Previous Label */
578 /* INVALID: Means we have something like foo.*.<public_suffix> */
582 if (six
+ 1 >= slength
) {
583 /* Trailing '*' in servername, match until end of hostname or
586 if (hix
>= hlength
) {
587 /* If we reach the end of the hostname we have a
591 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
592 } while (hch
!= '.');
593 /* We reached the end of servername and found a '.' in
594 hostname. Return true if hostname has a single
595 trailing '.' return false if there is anything after it. */
596 return hix
== hlength
;
599 /* Grab the character after the '*'. */
600 sch
= CFStringGetCharacterFromInlineBuffer(&sbuf
, ++six
);
602 /* We have something of the form '*foo.com'. Or '**.com'
603 We don't deal with that yet, since it might require
604 backtracking. Also RFC 2818 doesn't seem to require it. */
608 /* We're looking at the '.' after the '*' in something of the
609 form 'foo*.com' or '*.com'. Match until next '.' in hostname. */
610 if (prevLabel
==false) { /* RFC6125: Check if *.<tld> */
612 do { /* Loop to end of servername */
614 return false; /* Means we have something like *.com */
615 tch
= CFStringGetCharacterFromInlineBuffer(&sbuf
, tix
++);
616 } while (tch
!= '.');
618 return false; /* In case we have *.com. */
622 /* Since we're not at the end of servername yet (that case
623 was handled above), running out of chars in hostname
624 means we don't have a match. */
627 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
628 } while (hch
!= '.');
630 /* We're looking at a non wildcard character in the servername.
631 If we reached the end of hostname, it's not a match. */
635 /* Otherwise make sure the hostname matches the character in the
636 servername, case insensitively. */
637 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
++);
638 if (towlower(hch
) != towlower(sch
))
641 prevLabel
=true; /* Set if a confirmed previous component */
646 /* We reached the end of servername but we have one or more characters
647 left to compare against in the hostname. */
648 if (hix
+ 1 == hlength
&&
649 CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
) == '.') {
650 /* Hostname has a single trailing '.', we're ok with that. */
653 /* Anything else is not a match. */
660 #define kSecPolicySHA1Size 20
661 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
662 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
663 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
666 /* Check whether hostname is in a particular set of allowed domains.
667 Returns true if OK, false if not allowed.
669 static bool SecPolicyCheckDomain(SecPVCRef pvc
, CFStringRef hostname
)
671 CFIndex count
= SecPVCGetCertificateCount(pvc
);
672 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
673 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
675 /* is this chain anchored by kAppleCorpCASHA1? */
676 CFDataRef corpSHA1
= CFDataCreateWithBytesNoCopy(NULL
,
677 kAppleCorpCASHA1
, kSecPolicySHA1Size
, kCFAllocatorNull
);
678 bool isCorpSHA1
= (corpSHA1
&& CFEqual(anchorSHA1
, corpSHA1
));
679 CFReleaseSafe(corpSHA1
);
681 /* limit hostname to specified domains */
682 const CFStringRef dnlist
[] = {
686 unsigned int idx
, dncount
=2;
687 for (idx
= 0; idx
< dncount
; idx
++) {
688 if (SecDomainSuffixMatch(hostname
, dnlist
[idx
])) {
694 /* %%% other CA pinning checks TBA */
699 /* AUDIT[securityd](done):
700 policy->_options is a caller provided dictionary, only its cf type has
703 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
705 /* @@@ Consider what to do if the caller passes in no hostname. Should
706 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
707 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
708 CFStringRef hostName
= (CFStringRef
)
709 CFDictionaryGetValue(policy
->_options
, key
);
710 if (!isString(hostName
)) {
711 /* @@@ We can't return an error here and making the evaluation fail
712 won't help much either. */
716 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
717 bool dnsMatch
= false;
718 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(leaf
);
720 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
721 for (ix
= 0; ix
< count
; ++ix
) {
722 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
723 if (SecDNSMatch(hostName
, dns
)) {
732 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
733 the values returned by SecCertificateCopyIPAddresses() instead. */
734 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(leaf
);
736 CFIndex ix
, count
= CFArrayGetCount(ipAddresses
);
737 for (ix
= 0; ix
< count
; ++ix
) {
738 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
739 if (!CFStringCompare(hostName
, ipAddress
, kCFCompareCaseInsensitive
)) {
744 CFRelease(ipAddresses
);
749 /* Hostname mismatch or no hostnames found in certificate. */
750 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
752 else if (!SecPolicyCheckDomain(pvc
, hostName
)) {
753 /* Hostname match, but domain not allowed for this CA */
754 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
757 if ((dnsMatch
|| pvc
->details
)
758 && SecPolicySubscriberCertificateCouldBeEV(leaf
)) {
759 secdebug("policy", "enabling optionally_ev");
760 pvc
->optionally_ev
= true;
761 /* optionally_ev => check_revocation, so we don't enable revocation
762 checking here, since we don't want it on for non EV ssl certs. */
764 /* Check revocation status if the certificate asks for it (and we
765 support it) currently we only support ocsp. */
766 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(leaf
);
767 if (ocspResponders
) {
768 SecPVCSetCheckRevocation(pvc
);
774 /* AUDIT[securityd](done):
775 policy->_options is a caller provided dictionary, only its cf type has
778 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
779 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
780 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
782 if (!isString(email
)) {
783 /* We can't return an error here and making the evaluation fail
784 won't help much either. */
788 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
789 CFArrayRef addrs
= SecCertificateCopyRFC822Names(leaf
);
791 CFIndex ix
, count
= CFArrayGetCount(addrs
);
792 for (ix
= 0; ix
< count
; ++ix
) {
793 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
794 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
803 /* Hostname mismatch or no hostnames found in certificate. */
804 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
808 static void SecPolicyCheckValidIntermediates(SecPVCRef pvc
,
810 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
811 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
812 for (ix
= 1; ix
< count
- 1; ++ix
) {
813 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
814 if (!SecCertificateIsValid(cert
, verifyTime
)) {
815 /* Intermediate certificate has expired. */
816 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
822 static void SecPolicyCheckValidLeaf(SecPVCRef pvc
,
824 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
825 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
826 if (!SecCertificateIsValid(cert
, verifyTime
)) {
827 /* Leaf certificate has expired. */
828 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
833 static void SecPolicyCheckValidRoot(SecPVCRef pvc
,
835 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
836 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
838 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
839 if (!SecCertificateIsValid(cert
, verifyTime
)) {
840 /* Root certificate has expired. */
841 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
846 /* AUDIT[securityd](done):
847 policy->_options is a caller provided dictionary, only its cf type has
850 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
852 CFIndex count
= SecPVCGetCertificateCount(pvc
);
854 /* Can't check intermediates common name if there is no intermediate. */
855 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
859 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
860 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
861 CFStringRef commonName
=
862 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
863 if (!isString(commonName
)) {
864 /* @@@ We can't return an error here and making the evaluation fail
865 won't help much either. */
868 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
869 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
870 !CFEqual(commonName
, CFArrayGetValueAtIndex(commonNames
, 0))) {
871 /* Common Name mismatch. */
872 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
874 CFReleaseSafe(commonNames
);
877 /* AUDIT[securityd](done):
878 policy->_options is a caller provided dictionary, only its cf type has
881 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
883 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
884 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
885 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
887 if (!isString(common_name
)) {
888 /* @@@ We can't return an error here and making the evaluation fail
889 won't help much either. */
892 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
893 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
894 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
895 /* Common Name mismatch. */
896 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
898 CFReleaseSafe(commonNames
);
901 /* AUDIT[securityd](done):
902 policy->_options is a caller provided dictionary, only its cf type has
905 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
907 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
908 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
909 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
911 if (!isString(prefix
)) {
912 /* @@@ We can't return an error here and making the evaluation fail
913 won't help much either. */
916 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
917 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
918 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
919 /* Common Name prefix mismatch. */
920 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
922 CFReleaseSafe(commonNames
);
925 /* AUDIT[securityd](done):
926 policy->_options is a caller provided dictionary, only its cf type has
929 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
931 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
932 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
933 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
935 if (!isString(common_name
)) {
936 /* @@@ We can't return an error here and making the evaluation fail
937 won't help much either. */
940 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
941 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
942 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
943 CFStringRef test_common_name
= common_name
?
944 CFStringCreateWithFormat(kCFAllocatorDefault
,
945 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
947 if (!CFEqual(common_name
, cert_common_name
) &&
948 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
949 /* Common Name mismatch. */
950 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
951 CFReleaseSafe(test_common_name
);
953 CFReleaseSafe(commonNames
);
956 /* AUDIT[securityd](done):
957 policy->_options is a caller provided dictionary, only its cf type has
960 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
962 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
963 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
964 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
966 /* @@@ We can't return an error here and making the evaluation fail
967 won't help much either. */
970 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
971 if (SecCertificateNotValidBefore(cert
) <= at
) {
972 /* Leaf certificate has not valid before that is too old. */
973 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
978 /* AUDIT[securityd](done):
979 policy->_options is a caller provided dictionary, only its cf type has
982 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
984 CFIndex count
= SecPVCGetCertificateCount(pvc
);
985 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
986 CFNumberRef chainLength
=
987 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
989 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
990 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
991 /* @@@ We can't return an error here and making the evaluation fail
992 won't help much either. */
995 if (value
!= count
) {
996 /* Chain length doesn't match policy requirement. */
997 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
1002 /* AUDIT[securityd](done):
1003 policy->_options is a caller provided dictionary, only its cf type has
1006 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
1008 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1009 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1010 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1011 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1012 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
1014 bool foundMatch
= false;
1017 foundMatch
= CFEqual(anchorSHA1
, value
);
1018 else if (isArray(value
))
1019 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), anchorSHA1
);
1021 /* @@@ We only support Data and Array but we can't return an error here so.
1022 we let the evaluation fail (not much help) and assert in debug. */
1027 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, 0, kCFBooleanFalse
))
1034 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
1035 policy->_options is a caller provided dictionary, only its cf type has
1038 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
1040 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1041 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1042 SecCertificateRef cert
= NULL
;
1043 CFDataRef digest
= NULL
;
1044 bool foundMatch
= false;
1046 if (SecPVCGetCertificateCount(pvc
) < 2) {
1047 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
1051 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
1052 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
1055 foundMatch
= CFEqual(digest
, value
);
1056 else if (isArray(value
))
1057 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
1059 /* @@@ We only support Data and Array but we can't return an error here so.
1060 we let the evaluation fail (not much help) and assert in debug. */
1064 CFReleaseNull(digest
);
1067 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
1072 policy->_options is a caller provided dictionary, only its cf type has
1075 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
1077 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1078 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1079 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1080 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1081 SecAppleTrustAnchorFlags flags
= 0;
1083 if (isDictionary(value
)) {
1084 if (CFDictionaryGetValue(value
, kSecPolicyAppleAnchorIncludeTestRoots
))
1085 flags
|= kSecAppleTrustAnchorFlagsIncludeTestAnchors
;
1086 if (CFDictionaryGetValue(value
, kSecPolicyAppleAnchorAllowTestRootsOnProduction
))
1087 flags
|= kSecAppleTrustAnchorFlagsAllowNonProduction
;
1090 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
1093 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
1100 /* AUDIT[securityd](done):
1101 policy->_options is a caller provided dictionary, only its cf type has
1104 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
1106 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1107 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1108 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
1110 if (!isString(org
)) {
1111 /* @@@ We can't return an error here and making the evaluation fail
1112 won't help much either. */
1115 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
1116 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
1117 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
1118 /* Leaf Subject Organization mismatch. */
1119 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1121 CFReleaseSafe(organization
);
1124 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
1126 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1127 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1128 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
1130 if (!isString(orgUnit
)) {
1131 /* @@@ We can't return an error here and making the evaluation fail
1132 won't help much either. */
1135 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
1136 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
1137 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
1138 /* Leaf Subject Organizational Unit mismatch. */
1139 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1141 CFReleaseSafe(organizationalUnit
);
1144 /* AUDIT[securityd](done):
1145 policy->_options is a caller provided dictionary, only its cf type has
1148 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
1150 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1151 CFArrayRef trustedServerNames
= (CFArrayRef
)
1152 CFDictionaryGetValue(policy
->_options
, key
);
1153 /* No names specified means we accept any name. */
1154 if (!trustedServerNames
)
1156 if (!isArray(trustedServerNames
)) {
1157 /* @@@ We can't return an error here and making the evaluation fail
1158 won't help much either. */
1162 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
1163 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1164 bool dnsMatch
= false;
1165 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(leaf
);
1167 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
1168 // @@@ This is O(N^2) unfortunately we can't do better easily unless
1169 // we don't do wildcard matching. */
1170 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
1171 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
1173 for (tix
= 0; tix
< tsnCount
; ++tix
) {
1174 CFStringRef serverName
=
1175 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
1176 if (!isString(serverName
)) {
1177 /* @@@ We can't return an error here and making the
1178 evaluation fail won't help much either. */
1179 CFReleaseSafe(dnsNames
);
1182 /* we purposefully reverse the arguments here such that dns names
1183 from the cert are matched against a server name list, where
1184 the server names list can contain wildcards and the dns name
1185 cannot. References: http://support.microsoft.com/kb/941123
1186 It's easy to find occurrences where people tried to use
1187 wildcard certificates and were told that those don't work
1189 if (SecDNSMatch(dns
, serverName
)) {
1195 CFRelease(dnsNames
);
1199 /* Hostname mismatch or no hostnames found in certificate. */
1200 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1204 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
1205 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
1206 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
1207 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
1208 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
1209 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
1210 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
1211 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
1212 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
1213 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
1215 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
1216 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
1217 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
1218 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
1219 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
1220 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
1221 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
1222 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
1223 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
1224 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
1225 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
1226 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
1227 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
1228 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
1230 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
1233 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
1235 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1236 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
1238 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
1239 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
1240 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
1242 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1243 CFDataRef serial
= SecCertificateCopySerialNumber(cert
, NULL
);
1245 CFDataRef serial
= SecCertificateCopySerialNumber(cert
);
1249 CFIndex serial_length
= CFDataGetLength(serial
);
1250 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
1252 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
1257 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
1259 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
1261 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
1262 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
1264 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1265 CFReleaseSafe(serial
);
1274 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1275 if (NULL
!= otapkiRef
)
1277 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
1278 CFRelease(otapkiRef
);
1279 if (NULL
!= blackListedKeys
)
1281 /* Check for blacklisted intermediates keys. */
1282 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1285 /* Check dgst against blacklist. */
1286 if (CFSetContainsValue(blackListedKeys
, dgst
))
1288 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1292 CFRelease(blackListedKeys
);
1297 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
1299 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1300 if (NULL
!= otapkiRef
)
1302 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
1303 CFRelease(otapkiRef
);
1304 if (NULL
!= grayListedKeys
)
1306 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1308 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1311 /* Check dgst against gray. */
1312 if (CFSetContainsValue(grayListedKeys
, dgst
))
1314 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1318 CFRelease(grayListedKeys
);
1323 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1325 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1326 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1327 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1329 if (value
&& SecCertificateHasMarkerExtension(cert
, value
))
1332 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1335 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1337 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1338 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1339 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1341 for (ix
= 1; ix
< count
- 1; ix
++) {
1342 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1343 if (SecCertificateHasMarkerExtension(cert
, value
))
1346 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1349 /* Returns true if path is on the allow list, false otherwise */
1350 static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc
)
1352 bool result
= false;
1353 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1354 CFStringRef authKey
= NULL
;
1355 SecOTAPKIRef otapkiRef
= NULL
;
1357 //get authKeyID from the last chain in the cert
1361 SecCertificateRef lastCert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1362 CFDataRef authKeyID
= SecCertificateGetAuthorityKeyID(lastCert
);
1363 if (NULL
== authKeyID
) {
1366 authKey
= CFDataCopyHexString(authKeyID
);
1368 //if allowList && key is in allowList, this would have chained up to a now-removed anchor
1369 otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1370 if (NULL
== otapkiRef
) {
1373 CFDictionaryRef allowList
= SecOTAPKICopyAllowList(otapkiRef
);
1374 if (NULL
== allowList
) {
1378 CFArrayRef allowedCerts
= CFDictionaryGetValue(allowList
, authKey
);
1379 if (!allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
1383 //search sorted array for the SHA256 hash of a cert in the chain
1384 CFRange range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
1385 for (ix
= 0; ix
< count
; ix
++) {
1386 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1391 CFDataRef certHash
= SecCertificateCopySHA256Digest(cert
);
1396 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, certHash
,
1397 (CFComparatorFunction
)CFDataCompare
, NULL
);
1398 if (position
< CFArrayGetCount(allowedCerts
)) {
1399 CFDataRef possibleMatch
= CFArrayGetValueAtIndex(allowedCerts
, position
);
1400 if (!CFDataCompare(certHash
, possibleMatch
)) {
1401 //this cert is in the allowlist
1406 CFRelease(certHash
);
1411 CFReleaseNull(otapkiRef
);
1412 CFReleaseNull(allowList
);
1417 /****************************************************************************
1418 *********************** New rfc5280 Chain Validation ***********************
1419 ****************************************************************************/
1422 typedef struct cert_path
*cert_path_t
;
1427 typedef struct x500_name
*x500_name_t
;
1431 typedef struct algorithm_id
*algorithm_id_t
;
1432 struct algorithm_id
{
1433 oid_t algorithm_oid
;
1437 typedef struct trust_anchor
*trust_anchor_t
;
1438 struct trust_anchor
{
1439 x500_name_t issuer_name
;
1440 algorithm_id_t public_key_algorithm
; /* includes optional params */
1441 SecKeyRef public_key
;
1444 typedef struct certificate_policy
*certificate_policy_t
;
1445 struct certificate_policy
{
1446 policy_qualifier_t qualifiers
;
1448 SLIST_ENTRY(certificate_policy
) policies
;
1451 typedef struct policy_mapping
*policy_mapping_t
;
1452 struct policy_mapping
{
1453 SLIST_ENTRY(policy_mapping
) mappings
;
1454 oid_t issuer_domain_policy
;
1455 oid_t subject_domain_policy
;
1458 typedef struct root_name
*root_name_t
;
1463 struct policy_tree_add_ctx
{
1465 policy_qualifier_t p_q
;
1468 /* 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}. */
1469 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1470 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1471 policy_set_t policy_set
;
1472 for (policy_set
= node
->expected_policy_set
;
1474 policy_set
= policy_set
->oid_next
) {
1475 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1476 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1483 /* 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}. */
1484 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1485 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1486 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1487 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1493 /* Return true iff node has a child with a valid_policy equal to oid. */
1494 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1496 policy_tree_t child
;
1497 for (child
= node
->children
; child
; child
= child
->siblings
) {
1498 if (oid_equal(child
->valid_policy
, (*oid
))) {
1505 /* 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. */
1506 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1507 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1508 policy_set_t policy_set
;
1509 bool added_node
= false;
1510 for (policy_set
= node
->expected_policy_set
;
1512 policy_set
= policy_set
->oid_next
) {
1513 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1514 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1522 /* 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. */
1523 static bool policy_tree_map(policy_tree_t node
, void *ctx
) {
1524 /* Can't map oidAnyPolicy. */
1525 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1528 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1529 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1530 policy_set_t policy_set
= NULL
;
1531 /* First count how many mappings match this nodes valid_policy. */
1532 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1533 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1534 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1535 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1536 p_node
->oid
= mapping
->subjectDomainPolicy
;
1537 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1538 policy_set
= p_node
;
1542 policy_tree_set_expected_policy(node
, policy_set
);
1549 #define POLICY_MAPPING 0
1550 #define POLICY_SUBTREES 1
1552 /* rfc5280 basic cert processing. */
1553 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1557 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1558 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1559 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1560 uint32_t n
= (uint32_t)count
;
1561 bool is_anchored
= SecPVCIsAnchored(pvc
);
1563 /* If the anchor is trusted we don't procces the last cert in the
1567 /* trust may be restored for a path with an untrusted root that matches the allow list */
1568 if (!SecPVCCheckCertificateAllowList(pvc
)) {
1569 /* Add a detail for the root not being trusted. */
1570 if (SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1571 n
- 1, kCFBooleanFalse
, true))
1576 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1577 //policy_set_t user_initial_policy_set = NULL;
1578 //trust_anchor_t anchor;
1579 bool initial_policy_mapping_inhibit
= false;
1580 bool initial_explicit_policy
= false;
1581 bool initial_any_policy_inhibit
= false;
1583 /* Initialization */
1584 pvc
->valid_policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1586 CFMutableArrayRef permitted_subtrees
= NULL
;
1587 CFMutableArrayRef excluded_subtrees
= NULL
;
1588 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1589 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1590 assert(permitted_subtrees
!= NULL
);
1591 assert(excluded_subtrees
!= NULL
);
1593 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1594 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1595 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1598 /* Path builder ensures we only get cert chains with proper issuer
1599 chaining with valid signatures along the way. */
1600 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1601 SecKeyRef working_public_key
= anchor
->public_key
;
1602 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1604 uint32_t i
, max_path_length
= n
;
1605 SecCertificateRef cert
= NULL
;
1606 for (i
= 1; i
<= n
; ++i
) {
1608 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1609 bool is_self_issued
= SecPVCIsCertificateAtIndexSelfSigned(pvc
, n
- i
);
1611 /* (a) Verify the basic certificate information. */
1612 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1613 using the working_public_key and the working_public_key_parameters. */
1615 /* Already done by chain builder. */
1616 if (!SecCertificateIsValid(cert
, verify_time
)) {
1617 CFStringRef fail_key
= i
== n
? kSecPolicyCheckValidLeaf
: kSecPolicyCheckValidIntermediates
;
1618 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
))
1621 if (SecCertificateIsWeak(cert
)) {
1622 CFStringRef fail_key
= i
== n
? kSecPolicyCheckWeakLeaf
: kSecPolicyCheckWeakIntermediates
;
1623 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
))
1628 /* Check revocation status if the certificate asks for it. */
1629 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
1630 if (ocspResponders
) {
1631 SecPVCSetCheckRevocation(pvc
);
1634 /* @@@ cert.issuer == working_issuer_name. */
1638 if (!is_self_issued
|| i
== n
) {
1640 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1641 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1642 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1643 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) return;
1646 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1647 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1648 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1649 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) return;
1655 if (pvc
->valid_policy_tree
) {
1656 const SecCECertificatePolicies
*cp
=
1657 SecCertificateGetCertificatePolicies(cert
);
1658 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1659 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1660 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1661 oid_t p_oid
= policy
->policyIdentifier
;
1662 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1663 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1664 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1665 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1666 policy_tree_add_if_match
, &ctx
)) {
1667 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1668 policy_tree_add_if_any
, &ctx
);
1672 /* The certificate policies extension includes the policy
1673 anyPolicy with the qualifier set AP-Q and either
1674 (a) inhibit_anyPolicy is greater than 0 or
1675 (b) i < n and the certificate is self-issued. */
1676 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1677 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1678 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1679 oid_t p_oid
= policy
->policyIdentifier
;
1680 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1681 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1682 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1683 policy_tree_add_expected
, (void *)p_q
);
1687 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1690 if (pvc
->valid_policy_tree
)
1691 policy_tree_prune(&pvc
->valid_policy_tree
);
1694 /* (f) Verify that either explicit_policy is greater than 0 or the
1695 valid_policy_tree is not equal to NULL. */
1696 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1697 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1698 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true))
1701 /* If Last Cert in Path */
1705 /* Prepare for Next Cert */
1707 /* (a) verify that anyPolicy does not appear as an
1708 issuerDomainPolicy or a subjectDomainPolicy */
1709 CFDictionaryRef pm
= SecCertificateGetPolicyMappings(cert
);
1711 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1712 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1713 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1714 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1715 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1716 /* Policy mapping uses anyPolicy, illegal. */
1717 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
))
1722 /* (1) If the policy_mapping variable is greater than 0 */
1723 if (policy_mapping
> 0) {
1724 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1725 policy_tree_map
, (void *)pm
)) {
1726 /* 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:
1728 (i) set the valid_policy to ID-P;
1730 (ii) set the qualifier_set to the qualifier set of the
1731 policy anyPolicy in the certificate policies
1732 extension of certificate i; and
1733 (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. */
1737 /* (i) delete each node of depth i in the valid_policy_tree
1738 where ID-P is the valid_policy. */
1739 struct policy_tree_map_ctx ctx
= { idp_oid
, sdp_oid
};
1740 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1741 policy_tree_delete_if_match
, &ctx
);
1743 /* (ii) If there is a node in the valid_policy_tree of depth
1744 i-1 or less without any child nodes, delete that
1745 node. Repeat this step until there are no nodes of
1746 depth i-1 or less without children. */
1747 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1750 #endif /* POLICY_MAPPING */
1752 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1753 //working_public_key = SecCertificateCopyPublicKey(cert);
1754 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1755 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1757 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1759 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1760 if (permitted_subtrees_in_cert
) {
1761 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1764 // could do something smart here to avoid inserting the exact same constraint
1765 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1766 if (excluded_subtrees_in_cert
) {
1767 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1768 CFRange range
= { 0, num_trees
};
1769 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1773 if (!is_self_issued
) {
1774 if (explicit_policy
)
1778 if (inhibit_any_policy
)
1779 inhibit_any_policy
--;
1782 const SecCEPolicyConstraints
*pc
=
1783 SecCertificateGetPolicyConstraints(cert
);
1785 if (pc
->requireExplicitPolicyPresent
1786 && pc
->requireExplicitPolicy
< explicit_policy
) {
1787 explicit_policy
= pc
->requireExplicitPolicy
;
1789 if (pc
->inhibitPolicyMappingPresent
1790 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1791 policy_mapping
= pc
->inhibitPolicyMapping
;
1795 uint32_t iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1796 if (iap
< inhibit_any_policy
) {
1797 inhibit_any_policy
= iap
;
1800 const SecCEBasicConstraints
*bc
=
1801 SecCertificateGetBasicConstraints(cert
);
1802 #if 0 /* Checked in chain builder pre signature verify already. */
1803 if (!bc
|| !bc
->isCA
) {
1804 /* Basic constraints not present or not marked as isCA, illegal. */
1805 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicContraints
,
1806 n
- i
, kCFBooleanFalse
))
1811 if (!is_self_issued
) {
1812 if (max_path_length
> 0) {
1815 /* max_path_len exceeded, illegal. */
1816 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicContraints
,
1817 n
- i
, kCFBooleanFalse
))
1822 if (bc
&& bc
->pathLenConstraintPresent
1823 && bc
->pathLenConstraint
< max_path_length
) {
1824 max_path_length
= bc
->pathLenConstraint
;
1826 #if 0 /* Checked in chain builder pre signature verify already. */
1827 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1828 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1829 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1830 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1831 n
- i
, kCFBooleanFalse
, true))
1835 /* (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. */
1836 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1837 /* Certificate contains one or more unknown critical extensions. */
1838 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1839 n
- i
, kCFBooleanFalse
))
1842 } /* end loop over certs in path */
1844 cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1846 if (explicit_policy
)
1849 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1851 if (pc
->requireExplicitPolicyPresent
1852 && pc
->requireExplicitPolicy
== 0) {
1853 explicit_policy
= 0;
1857 //working_public_key = SecCertificateCopyPublicKey(cert);
1859 /* 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
1860 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1861 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1863 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1864 /* (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. */
1865 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1866 /* Certificate contains one or more unknown critical extensions. */
1867 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1868 0, kCFBooleanFalse
))
1871 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1873 if (pvc
->valid_policy_tree
) {
1874 #if !defined(NDEBUG)
1875 policy_tree_dump(pvc
->valid_policy_tree
);
1878 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1881 /* If either (1) the value of explicit_policy variable is greater than
1882 zero or (2) the valid_policy_tree is not NULL, then path processing
1884 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1885 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1886 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, 0, kCFBooleanFalse
, true))
1890 CFReleaseNull(permitted_subtrees
);
1891 CFReleaseNull(excluded_subtrees
);
1894 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1895 policy_set_t policies
= NULL
;
1896 const SecCECertificatePolicies
*cp
=
1897 SecCertificateGetCertificatePolicies(cert
);
1898 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1899 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1900 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1905 static void SecPolicyCheckEV(SecPVCRef pvc
,
1907 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1908 policy_set_t valid_policies
= NULL
;
1910 for (ix
= 0; ix
< count
; ++ix
) {
1911 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1912 policy_set_t policies
= policies_for_cert(cert
);
1915 /* anyPolicy in the leaf isn't allowed for EV, so only init
1916 valid_policies if we have real policies. */
1917 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1918 valid_policies
= policies
;
1921 } else if (ix
< count
- 1) {
1922 /* Subordinate CA */
1923 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1924 secdebug("ev", "subordinate certificate is not ev");
1925 if (SecPVCSetResultForced(pvc
, key
,
1926 ix
, kCFBooleanFalse
, true)) {
1927 policy_set_free(valid_policies
);
1928 policy_set_free(policies
);
1932 policy_set_intersect(&valid_policies
, policies
);
1935 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1936 secdebug("ev", "anchor certificate is not ev");
1937 if (SecPVCSetResultForced(pvc
, key
,
1938 ix
, kCFBooleanFalse
, true)) {
1939 policy_set_free(valid_policies
);
1940 policy_set_free(policies
);
1945 policy_set_free(policies
);
1946 if (!valid_policies
) {
1947 secdebug("ev", "valid_policies set is empty: chain not ev");
1948 /* If we ever get into a state where no policies are valid anymore
1949 this can't be an ev chain. */
1950 if (SecPVCSetResultForced(pvc
, key
,
1951 ix
, kCFBooleanFalse
, true)) {
1957 policy_set_free(valid_policies
);
1959 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1960 Subscriber MUST contain an OID defined by the CA in the certificate’s
1961 certificatePolicies extension that: (i) indicates which CA policy statement relates
1962 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1963 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1964 marks the certificate as being an EV Certificate.
1965 (b) EV Subordinate CA Certificates
1966 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1967 CA MUST contain one or more OIDs defined by the issuing CA that
1968 explicitly identify the EV Policies that are implemented by the Subordinate
1970 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1971 MAY contain the special anyPolicy OID (2.5.29.32.0).
1972 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1973 certificatePolicies or extendedKeyUsage extensions.
1979 * MARK: Certificate Transparency support
1985 Version sct_version; // 1 byte
1986 LogID id; // 32 bytes
1987 uint64 timestamp; // 8 bytes
1988 CtExtensions extensions; // 2 bytes len field, + n bytes data
1989 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1990 Version sct_version;
1991 SignatureType signature_type = certificate_timestamp;
1993 LogEntryType entry_type;
1994 select(entry_type) {
1995 case x509_entry: ASN.1Cert;
1996 case precert_entry: PreCert;
1998 CtExtensions extensions;
2000 } SignedCertificateTimestamp;
2004 #include <Security/SecureTransportPriv.h>
2007 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
2010 case SSL_SignatureAlgorithmRSA
:
2012 case SSL_HashAlgorithmSHA1
:
2013 return &CSSMOID_SHA1WithRSA
;
2014 case SSL_HashAlgorithmSHA256
:
2015 return &CSSMOID_SHA256WithRSA
;
2016 case SSL_HashAlgorithmSHA384
:
2017 return &CSSMOID_SHA384WithRSA
;
2021 case SSL_SignatureAlgorithmECDSA
:
2023 case SSL_HashAlgorithmSHA1
:
2024 return &CSSMOID_ECDSA_WithSHA1
;
2025 case SSL_HashAlgorithmSHA256
:
2026 return &CSSMOID_ECDSA_WithSHA256
;
2027 case SSL_HashAlgorithmSHA384
:
2028 return &CSSMOID_ECDSA_WithSHA384
;
2040 static size_t SSLDecodeUint16(const uint8_t *p
)
2042 return (p
[0]<<8 | p
[1]);
2045 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
2047 p
[0] = (len
>> 8)&0xff;
2048 p
[1] = (len
& 0xff);
2052 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
2054 p
[0] = (len
>> 16)&0xff;
2055 p
[1] = (len
>> 8)&0xff;
2056 p
[2] = (len
& 0xff);
2062 uint64_t SSLDecodeUint64(const uint8_t *p
)
2065 for(int i
=0; i
<8; i
++) {
2072 #include <libDER/DER_CertCrl.h>
2073 #include <libDER/DER_Encode.h>
2074 #include <libDER/asn1Types.h>
2077 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
2079 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2081 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
2083 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
2085 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2086 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
2087 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
2093 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
2095 SecCertificateRef leafCert
= NULL
;
2096 SecCertificateRef issuer
= NULL
;
2097 CFDataRef issuerKeyHash
= NULL
;
2098 CFDataRef tbs_precert
= NULL
;
2099 CFMutableDataRef data
= NULL
;
2101 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2102 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2103 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2105 require(leafCert
, out
);
2106 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
2107 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
2108 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
2110 require(issuerKeyHash
, out
);
2111 require(tbs_precert
, out
);
2112 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2113 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2115 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2116 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
2117 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
2118 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
2121 CFReleaseSafe(issuerKeyHash
);
2122 CFReleaseSafe(tbs_precert
);
2126 /* If the 'sct' is valid, return the operator ID of the log that signed this sct.
2128 The SCT is valid if:
2129 - It decodes properly.
2130 - Its timestamp is less than 'verifyTime'.
2131 - It is signed by a log in 'trustedLogs'.
2132 - The signing log expiryTime (if any) is less than 'verifyTime' (entry_type==0) or 'issuanceTime' (entry_type==1).
2134 If the SCT is valid, the returned CFStringRef is the identifier for the log operator. That value is not retained.
2135 If the SCT is valid, '*validLogAtVerifyTime' is set to true if the log is not expired at 'verifyTime'
2137 If the SCT is not valid this function return NULL.
2139 static CFStringRef
get_valid_sct_operator(CFDataRef sct
, int entry_type
, CFDataRef entry
, CFAbsoluteTime verifyTime
, CFAbsoluteTime issuanceTime
, CFArrayRef trustedLogs
, bool *validLogAtVerifyTime
)
2142 const uint8_t *logID
;
2143 const uint8_t *timestampData
;
2145 size_t extensionsLen
;
2146 const uint8_t *extensionsData
;
2149 size_t signatureLen
;
2150 const uint8_t *signatureData
;
2151 CFStringRef result
= NULL
;
2152 SecKeyRef pubKey
= NULL
;
2153 uint8_t *signed_data
= NULL
;
2154 const SecAsn1Oid
*oid
= NULL
;
2157 const uint8_t *p
= CFDataGetBytePtr(sct
);
2158 size_t len
= CFDataGetLength(sct
);
2159 uint64_t vt
=(uint64_t)( verifyTime
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
2161 require(len
>=43, out
);
2163 version
= p
[0]; p
++; len
--;
2164 logID
= p
; p
+=32; len
-=32;
2165 timestampData
= p
; p
+=8; len
-=8;
2166 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2168 require(len
>=extensionsLen
, out
);
2169 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
2171 require(len
>=4, out
);
2172 hashAlg
=p
[0]; p
++; len
--;
2173 sigAlg
=p
[0]; p
++; len
--;
2174 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2175 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
2178 /* verify version: only v1(0) is supported */
2180 secerror("SCT version unsupported: %d\n", version
);
2184 /* verify timestamp not in the future */
2185 timestamp
= SSLDecodeUint64(timestampData
);
2186 if(timestamp
> vt
) {
2187 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
2194 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
2195 signed_data
= malloc(signed_data_len
);
2196 require(signed_data
, out
);
2199 *q
++ = 0; // certificate_timestamp
2200 memcpy(q
, timestampData
, 8); q
+=8;
2201 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
2202 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
2203 q
= SSLEncodeUint16(q
, extensionsLen
);
2204 memcpy(q
, extensionsData
, extensionsLen
);
2206 CFDataRef logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, NULL
);
2208 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
2209 const void *key_data
;
2210 if(!isDictionary(dict
)) return false;
2211 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
2212 if(!isData(key_data
)) return false;
2213 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
2214 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
2215 CFReleaseSafe(valueID
);
2218 require(logData
, out
);
2220 /* If an expiry date is specified, and is a valid CFDate, then we check it against issuanceTime or verifyTime */
2221 const void *expiry_date
;
2222 if(CFDictionaryGetValueIfPresent(logData
, CFSTR("expiry"), &expiry_date
) && isDate(expiry_date
)) {
2223 CFAbsoluteTime expiryTime
= CFDateGetAbsoluteTime(expiry_date
);
2224 if(entry_type
== 1) {/* pre-cert: check the validity of the log at issuanceTime */
2225 require(issuanceTime
<=expiryTime
, out
);
2227 require(verifyTime
<=expiryTime
, out
);
2229 *validLogAtVerifyTime
= (verifyTime
<=expiryTime
);
2231 *validLogAtVerifyTime
= true;
2234 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
2235 require(logKeyData
, out
); // This failing would be an internal logic error
2236 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
2237 require(pubKey
, out
);
2239 oid
= oidForSigAlg(hashAlg
, sigAlg
);
2242 algId
.algorithm
= *oid
;
2243 algId
.parameters
.Data
= NULL
;
2244 algId
.parameters
.Length
= 0;
2246 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
2247 result
= CFDictionaryGetValue(logData
, CFSTR("operator"));
2249 secerror("SCT signature failed (log=%@)\n", logData
);
2253 CFReleaseSafe(pubKey
);
2258 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
2260 CFMutableArrayRef SCTs
= NULL
;
2261 SecCertificateRef leafCert
= NULL
;
2262 SecCertificateRef issuer
= NULL
;
2263 CFArrayRef ocspResponsesData
= NULL
;
2264 SecOCSPRequestRef ocspRequest
= NULL
;
2266 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2267 require_quiet(ocspResponsesData
, out
);
2269 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2270 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2271 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2273 require(leafCert
, out
);
2274 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
2275 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
2277 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2280 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2281 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2282 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2283 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
2284 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
2285 if(ocspSingleResponse
) {
2286 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
2287 if(singleResponseSCTs
) {
2288 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
2289 CFRelease(singleResponseSCTs
);
2291 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
2293 SecOCSPResponseFinalize(ocspResponse
);
2297 if(CFArrayGetCount(SCTs
)==0) {
2298 CFReleaseNull(SCTs
);
2302 CFReleaseSafe(ocspResponsesData
);
2304 SecOCSPRequestFinalize(ocspRequest
);
2309 static void SecPolicyCheckCT(SecPVCRef pvc
, CFStringRef key
)
2311 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2312 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
2313 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
2314 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2315 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
2316 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
2317 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
2319 // This eventually contain the list of operators who validated the SCT.
2320 CFMutableSetRef operatorsValidatingEmbeddedScts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeSetCallBacks
);
2321 CFMutableSetRef operatorsValidatingExternalScts
= CFSetCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeSetCallBacks
);
2323 __block
bool atLeastOneValidAtVerifyTime
= false;
2324 __block
int lifetime
; // in Months
2326 require(operatorsValidatingEmbeddedScts
, out
);
2327 require(operatorsValidatingExternalScts
, out
);
2329 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
2330 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
2331 CFArrayForEach(embeddedScts
, ^(const void *value
){
2332 bool validLogAtVerifyTime
= false;
2333 CFStringRef
operator = get_valid_sct_operator(value
, 1, precertEntry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2334 if(operator) CFSetAddValue(operatorsValidatingEmbeddedScts
, operator);
2335 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2339 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
2340 CFArrayForEach(builderScts
, ^(const void *value
){
2341 bool validLogAtVerifyTime
= false;
2342 CFStringRef
operator = get_valid_sct_operator(value
, 0, x509Entry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2343 if(operator) CFSetAddValue(operatorsValidatingExternalScts
, operator);
2344 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2348 if(ocspScts
&& x509Entry
) {
2349 CFArrayForEach(ocspScts
, ^(const void *value
){
2350 bool validLogAtVerifyTime
= false;
2351 CFStringRef
operator = get_valid_sct_operator(value
, 0, x509Entry
, pvc
->verifyTime
, SecCertificateNotValidBefore(leafCert
), trustedLogs
, &validLogAtVerifyTime
);
2352 if(operator) CFSetAddValue(operatorsValidatingExternalScts
, operator);
2353 if(validLogAtVerifyTime
) atLeastOneValidAtVerifyTime
= true;
2358 /* We now have 2 sets of operators that validated those SCTS, count them and make a final decision.
2360 is_ct = (A1 OR A2) AND B.
2362 A1: 2+ to 5+ SCTs from the cert from independent logs valid at issuance time
2363 (operatorsValidatingEmbeddedScts)
2364 A2: 2+ SCTs from external sources (OCSP stapled response and TLS extension)
2365 from independent logs valid at verify time. (operatorsValidatingExternalScts)
2366 B: All least one SCTs from a log valid at verify time.
2368 Policy is based on: https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxjZXJ0aWZpY2F0ZXRyYW5zcGFyZW5jeXxneDo0ODhjNGRlOTIyMzYwNTcz
2369 with one difference: we consider SCTs from OCSP and TLS extensions as a whole.
2370 It sounds like this is what Google will eventually do, per:
2371 https://groups.google.com/forum/?fromgroups#!topic/certificate-transparency/VdXuzA3TLWY
2375 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
2377 CFCalendarGetComponentDifference(zuluCalendar
,
2378 SecCertificateNotValidBefore(leafCert
),
2379 SecCertificateNotValidAfter(leafCert
),
2380 0, "M", &_lifetime
);
2381 lifetime
= _lifetime
;
2384 CFIndex requiredEmbeddedSctsCount
;
2386 if (lifetime
< 15) {
2387 requiredEmbeddedSctsCount
= 2;
2388 } else if (lifetime
<= 27) {
2389 requiredEmbeddedSctsCount
= 3;
2390 } else if (lifetime
<= 39) {
2391 requiredEmbeddedSctsCount
= 4;
2393 requiredEmbeddedSctsCount
= 5;
2396 pvc
->is_ct
= ((CFSetGetCount(operatorsValidatingEmbeddedScts
) >= requiredEmbeddedSctsCount
) ||
2397 (CFSetGetCount(operatorsValidatingExternalScts
) >= 2)
2398 ) && atLeastOneValidAtVerifyTime
;
2402 CFReleaseSafe(operatorsValidatingEmbeddedScts
);
2403 CFReleaseSafe(operatorsValidatingExternalScts
);
2404 CFReleaseSafe(builderScts
);
2405 CFReleaseSafe(embeddedScts
);
2406 CFReleaseSafe(ocspScts
);
2407 CFReleaseSafe(precertEntry
);
2408 CFReleaseSafe(trustedLogs
);
2409 CFReleaseSafe(x509Entry
);
2412 static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc
, CFStringRef key
)
2414 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2415 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2416 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2418 key_value
.data
= NULL
;
2419 key_value
.length
= 0;
2421 if (CFGetTypeID(value
) == CFDataGetTypeID())
2423 CFDataRef key_data
= (CFDataRef
)value
;
2424 key_value
.data
= (DERByte
*)CFDataGetBytePtr(key_data
);
2425 key_value
.length
= (DERSize
)CFDataGetLength(key_data
);
2427 for (ix
= 0; ix
< count
; ix
++) {
2428 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2429 policy_set_t policies
= policies_for_cert(cert
);
2431 if (policy_set_contains(policies
, &key_value
)) {
2435 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2440 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2442 SecPVCSetCheckRevocation(pvc
);
2445 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2447 SecPVCSetCheckRevocationResponseRequired(pvc
);
2450 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2452 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2455 static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc
,
2457 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2458 for (ix
= 1; ix
< count
- 1; ++ix
) {
2459 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2460 if (cert
&& SecCertificateIsWeak(cert
)) {
2461 /* Intermediate certificate has a weak key. */
2462 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2468 static void SecPolicyCheckWeakLeaf(SecPVCRef pvc
,
2470 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2471 if (cert
&& SecCertificateIsWeak(cert
)) {
2472 /* Leaf certificate has a weak key. */
2473 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
2478 static void SecPolicyCheckWeakRoot(SecPVCRef pvc
,
2480 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2482 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2483 if (cert
&& SecCertificateIsWeak(cert
)) {
2484 /* Root certificate has a weak key. */
2485 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2492 /********************************************************
2493 ****************** SecRVCRef Functions *****************
2494 ********************************************************/
2496 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
2498 /* Revocation verification context. */
2499 struct OpaqueSecRVC
{
2500 /* Will contain the response data. */
2503 /* Pointer to the pvc for this revocation check. */
2506 /* The ocsp request we send to each responder. */
2507 SecOCSPRequestRef ocspRequest
;
2509 /* The freshest response we received so far, from stapling or cache or responder. */
2510 SecOCSPResponseRef ocspResponse
;
2512 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
2513 SecOCSPSingleResponseRef ocspSingleResponse
;
2515 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2518 /* Index in array returned by SecCertificateGetOCSPResponders() for current
2520 CFIndex responderIX
;
2522 /* URL of current responder. */
2525 /* Date until which this revocation status is valid. */
2526 CFAbsoluteTime nextUpdate
;
2530 typedef struct OpaqueSecRVC
*SecRVCRef
;
2532 static void SecRVCDelete(SecRVCRef rvc
) {
2533 secdebug("alloc", "%p", rvc
);
2534 asynchttp_free(&rvc
->http
);
2535 SecOCSPRequestFinalize(rvc
->ocspRequest
);
2536 if (rvc
->ocspResponse
) {
2537 SecOCSPResponseFinalize(rvc
->ocspResponse
);
2538 rvc
->ocspResponse
= NULL
;
2539 if (rvc
->ocspSingleResponse
) {
2540 SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2541 rvc
->ocspSingleResponse
= NULL
;
2546 /* Return the next responder we should contact for this rvc or NULL if we
2547 exhausted them all. */
2548 static CFURLRef
SecRVCGetNextResponder(SecRVCRef rvc
) {
2549 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2550 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2551 if (ocspResponders
) {
2552 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2553 while (rvc
->responderIX
< responderCount
) {
2554 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2556 CFStringRef scheme
= CFURLCopyScheme(responder
);
2558 /* We only support http and https responders currently. */
2559 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2560 CFEqual(CFSTR("https"), scheme
));
2562 if (valid_responder
)
2570 /* Fire off an async http request for this certs revocation status, return
2571 false if request was queued, true if we're done. */
2572 static bool SecRVCFetchNext(SecRVCRef rvc
) {
2573 while ((rvc
->responder
= SecRVCGetNextResponder(rvc
))) {
2574 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2578 secdebug("ocsp", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2579 if (!asyncHttpPost(rvc
->responder
, request
, &rvc
->http
)) {
2580 /* Async request was posted, wait for reply. */
2590 /* Process a verified ocsp response for a given cert. Return true if the
2591 certificate status was obtained. */
2592 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2595 switch (this->certStatus
) {
2597 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2598 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2599 in the info dictionary. */
2600 //cert.revokeCheckGood(true);
2601 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2605 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2606 /* @@@ Mark cert as revoked (with reason) at revocation date in
2607 the info dictionary, or perhaps we should use a different key per
2608 reason? That way a client using exceptions can ignore some but
2610 SInt32 reason
= this->crlReason
;
2611 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2612 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2614 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2615 /* make the revocation reason available in the trust result */
2616 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2618 CFRelease(cfreason
);
2622 /* not an error, no per-cert status, nothing here */
2623 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2627 secdebug("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2628 (int)this->certStatus
, rvc
->certIX
);
2636 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
2637 if (rvc
->ocspSingleResponse
) {
2638 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2640 if (rvc
->ocspResponse
) {
2641 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2645 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecRVCRef rvc
, CFAbsoluteTime verifyTime
) {
2647 SecCertificatePathRef issuer
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2648 SecCertificatePathRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2652 if (signer
== issuer
) {
2653 /* We already know we trust issuer since it's the path we are
2654 trying to verify minus the leaf. */
2655 secdebug("ocsp", "ocsp responder: %@ response signed by issuer",
2660 "ocsp responder: %@ response signed by cert issued by issuer",
2662 /* @@@ Now check that we trust signer. */
2663 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2664 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2665 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2666 CFRelease(ocspSigner
);
2667 struct OpaqueSecPVC ospvc
;
2668 SecPVCInit(&ospvc
, rvc
->pvc
->builder
, policies
, verifyTime
);
2669 CFRelease(policies
);
2670 SecPVCSetPath(&ospvc
, signer
, NULL
);
2671 SecPVCLeafChecks(&ospvc
);
2673 bool completed
= SecPVCPathChecks(&ospvc
);
2674 /* If completed is false we are waiting for a callback, this
2675 shouldn't happen since we aren't asking for details, no
2676 revocation checking is done. */
2678 ocspdErrorLog("SecPVCPathChecks unexpectedly started "
2680 /* @@@ assert() or abort here perhaps? */
2684 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
2688 /* @@@ We don't trust the cert so don't use this response. */
2689 ocspdErrorLog("ocsp response signed by certificate which "
2690 "does not satisfy ocspSigner policy");
2693 SecPVCDelete(&ospvc
);
2698 /* @@@ No signer found for this ocsp response, discard it. */
2699 secdebug("ocsp", "ocsp responder: %@ no signer found for response",
2704 #if DUMP_OCSPRESPONSES
2706 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
2707 rvc
->certIX
, (trusted
? "t" : "u"));
2708 secdumpdata(ocspResponse
->data
, buf
);
2714 static void SecRVCConsumeOCSPResponse(SecRVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
2715 SecOCSPSingleResponseRef sr
= NULL
;
2716 require_quiet(ocspResponse
, errOut
);
2717 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
2718 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
2719 secdebug("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
2720 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
2721 secdebug("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
2722 // Check if this response is fresher than any (cached) response we might still have in the rvc.
2723 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
2725 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
2726 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
2727 check whether the leaf was revoked (we are already checking the rest of
2729 /* Check the OCSP response signature and verify the response. */
2730 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
2731 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
2733 // If we get here, we have a properly signed ocsp response
2734 // but we haven't checked dates yet.
2736 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
2737 if (sr
->certStatus
== CS_Good
) {
2738 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
2739 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
2740 } else if (sr
->certStatus
== CS_Revoked
) {
2741 // Expire revoked responses when the subject certificate itself expires.
2742 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
2745 // Ok we like the new response, let's toss the old one.
2747 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
2749 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
2750 rvc
->ocspResponse
= ocspResponse
;
2751 ocspResponse
= NULL
;
2753 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2754 rvc
->ocspSingleResponse
= sr
;
2757 rvc
->done
= sr_valid
;
2760 if (sr
) SecOCSPSingleResponseDestroy(sr
);
2761 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2764 /* Callback from async http code after an ocsp response has been received. */
2765 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
2766 SecRVCRef rvc
= (SecRVCRef
)http
->info
;
2767 SecPVCRef pvc
= rvc
->pvc
;
2768 SecOCSPResponseRef ocspResponse
= NULL
;
2769 if (http
->response
) {
2770 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
2772 /* Parse the returned data as if it's an ocspResponse. */
2773 ocspResponse
= SecOCSPResponseCreate(data
);
2778 SecRVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
2779 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
2782 /* Clear the data for the next response. */
2783 asynchttp_free(http
);
2784 SecRVCFetchNext(rvc
);
2788 SecRVCUpdatePVC(rvc
);
2790 if (!--pvc
->asyncJobCount
) {
2791 SecPathBuilderStep(pvc
->builder
);
2796 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
2797 secdebug("alloc", "%p", rvc
);
2799 rvc
->certIX
= certIX
;
2800 rvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
2801 rvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2802 rvc
->http
.completed
= SecOCSPFetchCompleted
;
2803 rvc
->http
.info
= rvc
;
2804 rvc
->ocspRequest
= NULL
;
2805 rvc
->responderIX
= 0;
2806 rvc
->responder
= NULL
;
2807 rvc
->nextUpdate
= NULL_TIME
;
2808 rvc
->ocspResponse
= NULL
;
2809 rvc
->ocspSingleResponse
= NULL
;
2814 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
2815 secdebug("ocsp", "checking revocation");
2816 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2817 bool completed
= true;
2818 if (certCount
<= 1) {
2819 /* Can't verify without an issuer; we're done */
2822 if (!SecPVCIsAnchored(pvc
)) {
2823 /* We can't check revocation for chains without a trusted anchor. */
2829 /* TODO: Implement getting this value from the client.
2830 Optional responder passed in though policy. */
2831 CFURLRef localResponder
= NULL
;
2832 /* Generate a nonce in outgoing request if true. */
2833 bool genNonce
= false;
2834 /* Require a nonce in response if true. */
2835 bool requireRespNonce
= false;
2836 bool cacheReadDisable
= false;
2837 bool cacheWriteDisable
= false;
2841 /* We have done revocation checking already, we're done. */
2842 secdebug("ocsp", "Not rechecking revocation");
2846 /* Setup things so we check revocation status of all certs except the
2848 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
2851 /* Lookup cached revocation data for each certificate. */
2852 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
2853 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2854 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2855 if (ocspResponders
) {
2856 /* First look though passed in ocsp responses. */
2857 //SecPVCGetOCSPResponseForCertificateAtIndex(pvc, ix, singleResponse);
2859 /* Then look though shared cache (we don't care which responder
2860 something came from here). */
2861 CFDataRef ocspResponse
= SecOCSPCacheCopyMatching(SecCertIDRef certID
, NULL
);
2863 /* Now let's parse the response. */
2864 if (decodeOCSPResponse(ocspResp
)) {
2865 secdebug("ocsp", "response ok: %@", ocspResp
);
2867 secdebug("ocsp", "response bad: %@", ocspResp
);
2868 /* ocsp response not ok. */
2869 if (!SecPVCSetResultForced(pvc
, key
, ix
, kCFBooleanFalse
, true))
2872 CFReleaseSafe(ocspResp
);
2874 /* Check if certificate has any crl distributionPoints. */
2875 CFArrayRef distributionPoints
= SecCertificateGetCRLDistributionPoints(cert
);
2876 if (distributionPoints
) {
2877 /* Look for a cached CRL and potentially delta CRL for this certificate. */
2883 /* Note that if we are multi threaded and a job completes after it
2884 is started but before we return from this function, we don't want
2885 a callback to decrement asyncJobCount to zero before we finish issuing
2886 all the jobs. To avoid this we pretend we issued certCount async jobs,
2887 and decrement pvc->asyncJobCount for each cert that we don't start a
2888 background fetch for. */
2889 pvc
->asyncJobCount
= (unsigned int) certCount
;
2891 /* Loop though certificates again and issue an ocsp fetch if the
2892 revocation status checking isn't done yet. */
2893 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
2894 secdebug("ocsp", "checking revocation for cert: %ld", certIX
);
2895 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
2896 SecRVCInit(rvc
, pvc
, certIX
);
2900 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
,
2902 /* The certIX + 1 is ok here since certCount is always at least 1
2903 less than the actual number of certs. */
2904 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(rvc
->pvc
,
2907 rvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
2909 /* Get stapled OCSP responses */
2910 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2912 /* If we have any OCSP stapled responses, check those first */
2913 if(ocspResponsesData
) {
2914 secdebug("ocsp", "Checking stapled responses for cert %ld", certIX
);
2915 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2916 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2917 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2918 SecRVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
2920 CFRelease(ocspResponsesData
);
2923 /* Then check the cached response */
2924 secdebug("ocsp", "Checking cached responses for cert %ld", certIX
);
2925 SecRVCConsumeOCSPResponse(rvc
, SecOCSPCacheCopyMatching(rvc
->ocspRequest
, NULL
), NULL_TIME
, false);
2927 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
2928 async http request for this cert's revocation status, unless we already successfully checked
2929 the revocation status of this cert based on the cache or stapled responses, */
2930 bool allow_fetch
= SecPathBuilderCanAccessNetwork(pvc
->builder
) && (pvc
->is_ev
|| pvc
->check_revocation
);
2931 bool fetch_done
= true;
2932 if (rvc
->done
|| !allow_fetch
||
2933 (fetch_done
= SecRVCFetchNext(rvc
))) {
2934 /* We got a cache hit or we aren't allowed to access the network,
2935 or the async http post failed. */
2936 SecRVCUpdatePVC(rvc
);
2938 /* We didn't really start a background job for this cert. */
2939 pvc
->asyncJobCount
--;
2940 } else if (!fetch_done
) {
2941 /* We started at least one background fetch. */
2946 /* Return false if we started any background jobs. */
2947 /* We can't just return !pvc->asyncJobCount here, since if we started any
2948 jobs the completion callback will be called eventually and it will call
2949 SecPathBuilderStep(). If for some reason everything completed before we
2950 get here we still want the outer SecPathBuilderStep() to terminate so we
2951 keep track of whether we started any jobs and return false if so. */
2956 void SecPolicyServerInitalize(void) {
2957 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2958 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2959 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2960 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2962 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2963 kSecPolicyCheckBasicCertificateProcessing
,
2964 SecPolicyCheckBasicCertificateProcessing
);
2965 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2966 kSecPolicyCheckCriticalExtensions
,
2967 SecPolicyCheckCriticalExtensions
);
2968 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2969 kSecPolicyCheckIdLinkage
,
2970 SecPolicyCheckIdLinkage
);
2971 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2972 kSecPolicyCheckKeyUsage
,
2973 SecPolicyCheckKeyUsage
);
2974 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2975 kSecPolicyCheckExtendedKeyUsage
,
2976 SecPolicyCheckExtendedKeyUsage
);
2977 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2978 kSecPolicyCheckBasicContraints
,
2979 SecPolicyCheckBasicContraints
);
2980 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2981 kSecPolicyCheckNonEmptySubject
,
2982 SecPolicyCheckNonEmptySubject
);
2983 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2984 kSecPolicyCheckQualifiedCertStatements
,
2985 SecPolicyCheckQualifiedCertStatements
);
2986 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2987 kSecPolicyCheckSSLHostname
,
2988 SecPolicyCheckSSLHostname
);
2989 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2990 kSecPolicyCheckEmail
,
2991 SecPolicyCheckEmail
);
2992 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2993 kSecPolicyCheckValidIntermediates
,
2994 SecPolicyCheckValidIntermediates
);
2995 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
2996 kSecPolicyCheckValidLeaf
,
2997 SecPolicyCheckValidLeaf
);
2998 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
2999 kSecPolicyCheckValidRoot
,
3000 SecPolicyCheckValidRoot
);
3001 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3002 kSecPolicyCheckIssuerCommonName
,
3003 SecPolicyCheckIssuerCommonName
);
3004 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3005 kSecPolicyCheckSubjectCommonNamePrefix
,
3006 SecPolicyCheckSubjectCommonNamePrefix
);
3007 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3008 kSecPolicyCheckSubjectCommonName
,
3009 SecPolicyCheckSubjectCommonName
);
3010 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3011 kSecPolicyCheckNotValidBefore
,
3012 SecPolicyCheckNotValidBefore
);
3013 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3014 kSecPolicyCheckChainLength
,
3015 SecPolicyCheckChainLength
);
3016 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3017 kSecPolicyCheckAnchorSHA1
,
3018 SecPolicyCheckAnchorSHA1
);
3019 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3020 kSecPolicyCheckIntermediateSPKISHA256
,
3021 SecPolicyCheckIntermediateSPKISHA256
);
3022 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3023 kSecPolicyCheckAnchorApple
,
3024 SecPolicyCheckAnchorApple
);
3025 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3026 kSecPolicyCheckSubjectOrganization
,
3027 SecPolicyCheckSubjectOrganization
);
3028 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3029 kSecPolicyCheckSubjectOrganizationalUnit
,
3030 SecPolicyCheckSubjectOrganizationalUnit
);
3031 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3032 kSecPolicyCheckEAPTrustedServerNames
,
3033 SecPolicyCheckEAPTrustedServerNames
);
3034 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3035 kSecPolicyCheckSubjectCommonNameTEST
,
3036 SecPolicyCheckSubjectCommonNameTEST
);
3037 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3038 kSecPolicyCheckRevocation
,
3039 SecPolicyCheckRevocation
);
3040 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3041 kSecPolicyCheckRevocationResponseRequired
,
3042 SecPolicyCheckRevocationResponseRequired
);
3043 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3044 kSecPolicyCheckNoNetworkAccess
,
3045 SecPolicyCheckNoNetworkAccess
);
3046 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3047 kSecPolicyCheckBlackListedLeaf
,
3048 SecPolicyCheckBlackListedLeaf
);
3049 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3050 kSecPolicyCheckGrayListedLeaf
,
3051 SecPolicyCheckGrayListedLeaf
);
3052 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3053 kSecPolicyCheckLeafMarkerOid
,
3054 SecPolicyCheckLeafMarkerOid
);
3055 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3056 kSecPolicyCheckIntermediateMarkerOid
,
3057 SecPolicyCheckIntermediateMarkerOid
);
3058 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3059 kSecPolicyCheckCertificatePolicy
,
3060 SecPolicyCheckCertificatePolicyOid
);
3061 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3062 kSecPolicyCheckWeakIntermediates
,
3063 SecPolicyCheckWeakIntermediates
);
3064 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3065 kSecPolicyCheckWeakLeaf
,
3066 SecPolicyCheckWeakLeaf
);
3067 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3068 kSecPolicyCheckWeakRoot
,
3069 SecPolicyCheckWeakRoot
);
3072 /* AUDIT[securityd](done):
3073 array (ok) is a caller provided array, only its cf type has
3075 The options (ok) field ends up in policy->_options unchecked, so every access
3076 of policy->_options needs to be validated.
3078 static SecPolicyRef
SecPolicyCreateWithArray(CFArrayRef array
) {
3079 SecPolicyRef policy
= NULL
;
3080 require_quiet(array
&& CFArrayGetCount(array
) == 2, errOut
);
3081 CFStringRef oid
= (CFStringRef
)CFArrayGetValueAtIndex(array
, 0);
3082 require_quiet(isString(oid
), errOut
);
3083 CFDictionaryRef options
= (CFDictionaryRef
)CFArrayGetValueAtIndex(array
, 1);
3084 require_quiet(isDictionary(options
), errOut
);
3085 policy
= SecPolicyCreate(oid
, options
);
3090 /* AUDIT[securityd](done):
3091 value (ok) is an element in a caller provided array.
3093 static void deserializePolicy(const void *value
, void *context
) {
3094 CFArrayRef policyArray
= (CFArrayRef
)value
;
3095 if (isArray(policyArray
)) {
3096 CFTypeRef deserializedPolicy
= SecPolicyCreateWithArray(policyArray
);
3097 if (deserializedPolicy
) {
3098 CFArrayAppendValue((CFMutableArrayRef
)context
, deserializedPolicy
);
3099 CFRelease(deserializedPolicy
);
3104 /* AUDIT[securityd](done):
3105 serializedPolicies (ok) is a caller provided array, only its cf type has
3108 CFArrayRef
SecPolicyArrayDeserialize(CFArrayRef serializedPolicies
) {
3109 CFMutableArrayRef result
= NULL
;
3110 require_quiet(isArray(serializedPolicies
), errOut
);
3111 CFIndex count
= CFArrayGetCount(serializedPolicies
);
3112 result
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
3113 CFRange all_policies
= { 0, count
};
3114 CFArrayApplyFunction(serializedPolicies
, all_policies
, deserializePolicy
, result
);
3121 /********************************************************
3122 ****************** SecPVCRef Functions *****************
3123 ********************************************************/
3125 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3126 CFAbsoluteTime verifyTime
) {
3127 secdebug("alloc", "%p", pvc
);
3128 // Weird logging policies crashes.
3129 //secdebug("policy", "%@", policies);
3130 pvc
->builder
= builder
;
3131 pvc
->policies
= policies
;
3134 pvc
->verifyTime
= verifyTime
;
3136 pvc
->details
= NULL
;
3138 pvc
->valid_policy_tree
= NULL
;
3139 pvc
->callbacks
= NULL
;
3142 pvc
->asyncJobCount
= 0;
3143 pvc
->check_revocation
= false;
3144 pvc
->response_required
= false;
3145 pvc
->optionally_ev
= false;
3150 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3151 secdebug("alloc", "%p", pvc
);
3158 void SecPVCDelete(SecPVCRef pvc
) {
3159 secdebug("alloc", "%p", pvc
);
3160 CFReleaseNull(pvc
->policies
);
3161 CFReleaseNull(pvc
->details
);
3162 CFReleaseNull(pvc
->info
);
3163 if (pvc
->valid_policy_tree
) {
3164 policy_tree_prune(&pvc
->valid_policy_tree
);
3166 SecPVCDeleteRVCs(pvc
);
3169 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3170 CF_CONSUMED CFArrayRef details
) {
3171 secdebug("policy", "%@", path
);
3172 if (pvc
->path
!= path
) {
3173 /* Changing path makes us clear the Revocation Verification Contexts */
3174 SecPVCDeleteRVCs(pvc
);
3177 pvc
->details
= details
;
3178 CFReleaseNull(pvc
->info
);
3179 if (pvc
->valid_policy_tree
) {
3180 policy_tree_prune(&pvc
->valid_policy_tree
);
3186 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3187 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3190 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3191 return SecCertificatePathGetCount(pvc
->path
);
3194 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3195 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3198 bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc
, CFIndex ix
) {
3199 return SecCertificatePathSelfSignedIndex(pvc
->path
) == ix
;
3202 void SecPVCSetCheckRevocation(SecPVCRef pvc
) {
3203 pvc
->check_revocation
= true;
3204 secdebug("ocsp", "deferred revocation checking enabled");
3207 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3208 pvc
->response_required
= true;
3209 secdebug("ocsp", "revocation response required");
3212 bool SecPVCIsAnchored(SecPVCRef pvc
) {
3213 return SecCertificatePathIsAnchored(pvc
->path
);
3216 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
3217 return pvc
->verifyTime
;
3220 /* AUDIT[securityd](done):
3221 policy->_options is a caller provided dictionary, only its cf type has
3224 bool SecPVCSetResultForced(SecPVCRef pvc
,
3225 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
3227 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
3228 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
3229 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
3231 (force
? "force" : ""), result
);
3233 /* If this is not something the current policy cares about ignore
3234 this error and return true so our caller continues evaluation. */
3236 /* @@@ The right long term fix might be to check if none of the passed
3237 in policies contain this key, since not all checks are run for all
3239 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3240 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
3244 /* @@@ Check to see if the SecTrustSettings for the certificate in question
3245 tell us to ignore this error. */
3246 pvc
->result
= false;
3250 CFMutableDictionaryRef detail
=
3251 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
3253 /* Perhaps detail should have an array of results per key? As it stands
3254 in the case of multiple policy failures the last failure stands. */
3255 CFDictionarySetValue(detail
, key
, result
);
3260 bool SecPVCSetResult(SecPVCRef pvc
,
3261 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
3262 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
3265 /* AUDIT[securityd](done):
3266 key(ok) is a caller provided.
3267 value(ok, unused) is a caller provided.
3269 static void SecPVCValidateKey(const void *key
, const void *value
,
3271 SecPVCRef pvc
= (SecPVCRef
)context
;
3273 /* If our caller doesn't want full details and we failed earlier there is
3274 no point in doing additional checks. */
3275 if (!pvc
->result
&& !pvc
->details
)
3278 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
3279 CFDictionaryGetValue(pvc
->callbacks
, key
);
3283 /* Why not to have optional policy checks rant:
3284 Not all keys are in all dictionaries anymore, so why not make checks
3285 optional? This way a client can ask for something and the server will
3286 do a best effort based on the supported flags. It works since they are
3287 synchronized now, but we need some debug checking here for now. */
3288 pvc
->result
= false;
3290 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
3291 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
3292 pvc
->result
= false;
3294 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3295 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3296 pvc
->result
= false;
3299 /* Non standard validation phase, nothing is optional. */
3300 pvc
->result
= false;
3305 fcn(pvc
, (CFStringRef
)key
);
3308 /* AUDIT[securityd](done):
3309 policy->_options is a caller provided dictionary, only its cf type has
3312 bool SecPVCLeafChecks(SecPVCRef pvc
) {
3314 CFArrayRef policies
= pvc
->policies
;
3315 CFIndex ix
, count
= CFArrayGetCount(policies
);
3316 for (ix
= 0; ix
< count
; ++ix
) {
3317 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3319 /* Validate all keys for all policies. */
3320 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3321 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3322 if (!pvc
->result
&& !pvc
->details
)
3329 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3330 /* Check stuff common to intermediate and anchors. */
3331 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3332 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3333 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3334 && SecPVCIsAnchored(pvc
));
3335 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3336 /* Certificate has expired. */
3337 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
3338 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
3342 if (SecCertificateIsWeak(cert
)) {
3343 /* Certificate uses weak key. */
3344 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckWeakRoot
3345 : kSecPolicyCheckWeakIntermediates
, ix
, kCFBooleanFalse
))
3350 /* Perform anchor specific checks. */
3351 /* Don't think we have any of these. */
3353 /* Perform intermediate specific checks. */
3356 const SecCEBasicConstraints
*bc
=
3357 SecCertificateGetBasicConstraints(cert
);
3358 if (!bc
|| !bc
->isCA
) {
3359 /* Basic constraints not present or not marked as isCA, illegal. */
3360 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicContraints
,
3361 ix
, kCFBooleanFalse
, true))
3364 /* Consider adding (l) max_path_length checking here. */
3366 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3367 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3368 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3369 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3370 ix
, kCFBooleanFalse
, true))
3379 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3380 /* Check stuff common to intermediate and anchors. */
3382 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3383 if (NULL
!= otapkiRef
)
3385 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3386 CFRelease(otapkiRef
);
3387 if (NULL
!= blackListedKeys
)
3389 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3390 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3391 && SecPVCIsAnchored(pvc
));
3393 /* Check for blacklisted intermediates keys. */
3394 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3396 /* Check dgst against blacklist. */
3397 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3398 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3399 ix
, kCFBooleanFalse
, true);
3404 CFRelease(blackListedKeys
);
3412 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3414 /* Check stuff common to intermediate and anchors. */
3415 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3416 if (NULL
!= otapkiRef
)
3418 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3419 CFRelease(otapkiRef
);
3420 if (NULL
!= grayListKeys
)
3422 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3423 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3424 && SecPVCIsAnchored(pvc
));
3426 /* Check for gray listed intermediates keys. */
3427 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3429 /* Check dgst against gray list. */
3430 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3431 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3432 ix
, kCFBooleanFalse
, true);
3437 CFRelease(grayListKeys
);
3445 /* AUDIT[securityd](done):
3446 policy->_options is a caller provided dictionary, only its cf type has
3449 bool SecPVCPathChecks(SecPVCRef pvc
) {
3450 secdebug("policy", "begin path: %@", pvc
->path
);
3451 bool completed
= true;
3452 /* This needs to be initialized before we call any function that might call
3453 SecPVCSetResultForced(). */
3455 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3456 if (pvc
->result
|| pvc
->details
) {
3457 SecPolicyCheckBasicCertificateProcessing(pvc
,
3458 kSecPolicyCheckBasicCertificateProcessing
);
3461 CFArrayRef policies
= pvc
->policies
;
3462 CFIndex count
= CFArrayGetCount(policies
);
3463 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3464 /* Validate all keys for all policies. */
3465 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3466 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3467 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3468 if (!pvc
->result
&& !pvc
->details
)
3472 /* Check the things we can't check statically for the certificate path. */
3473 /* Critical Extensions, chainLength. */
3477 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
3478 bool pre_ev_check_result
= pvc
->result
;
3479 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3480 pvc
->is_ev
= pvc
->result
;
3481 /* If ev checking failed, we still want to accept this chain
3482 as a non EV one, if it was valid as such. */
3483 pvc
->result
= pre_ev_check_result
;
3485 /* Check revocation only if the chain is valid so far. The revocation will
3486 only fetch OCSP response over the network if the client asked for revocation
3487 check explicitly or is_ev is true. */
3489 completed
= SecPVCCheckRevocation(pvc
);
3493 if (pvc
->result
|| pvc
->details
) {
3494 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3495 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
3500 secdebug("policy", "end %strusted completed: %d path: %@",
3501 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
3505 /* This function returns 0 to indicate revocation checking was not completed
3506 for this certificate chain, otherwise return to date at which the first
3507 piece of revocation checking info we used expires. */
3508 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
3509 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3510 CFAbsoluteTime enu
= 0;
3511 if (certCount
<= 1 || !pvc
->rvcs
) {
3516 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3517 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3518 if (rvc
->nextUpdate
== 0) {
3520 /* We allow for CA certs to not be revocation checked if they
3521 have no ocspResponders to check against, but the leaf
3522 must be checked in order for us to claim we did revocation
3524 SecCertificateRef cert
=
3525 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3526 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3527 if (!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) {
3528 /* We can't check this cert so we don't consider it a soft
3529 failure that we didn't. Ideally we should support crl
3530 checking and remove this workaround, since that more
3535 secdebug("ocsp", "revocation checking soft failure for cert: %ld",
3537 enu
= rvc
->nextUpdate
;
3540 if (enu
== 0 || rvc
->nextUpdate
< enu
) {
3541 enu
= rvc
->nextUpdate
;
3544 /* Perhaps we don't want to do this since some policies might
3545 ignore the certificate expiration but still use revocation
3548 /* Earliest certificate expiration date. */
3549 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3550 CFAbsoluteTime nva
= SecCertificateNotValidAfter(cert
);
3551 if (nva
&& (enu
== 0 || nva
< enu
)
3556 secdebug("ocsp", "revocation valid until: %lg", enu
);