2 * Copyright (c) 2008-2016 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 <Security/SecTask.h>
32 #include <utilities/SecIOFormat.h>
33 #include <securityd/asynchttp.h>
34 #include <securityd/policytree.h>
35 #include <securityd/nameconstraints.h>
36 #include <CoreFoundation/CFTimeZone.h>
38 #include <libDER/oidsPriv.h>
39 #include <CoreFoundation/CFNumber.h>
40 #include <Security/SecCertificateInternal.h>
41 #include <AssertMacros.h>
42 #include <utilities/debugging.h>
43 #include <utilities/SecInternalReleasePriv.h>
44 #include <security_asn1/SecAsn1Coder.h>
45 #include <security_asn1/ocspTemplates.h>
46 #include <security_asn1/oidsalg.h>
47 #include <security_asn1/oidsocsp.h>
48 #include <CommonCrypto/CommonDigest.h>
49 #include <Security/SecFramework.h>
50 #include <Security/SecPolicyInternal.h>
51 #include <Security/SecTrustPriv.h>
52 #include <Security/SecTrustSettings.h>
53 #include <Security/SecInternal.h>
54 #include <Security/SecKeyPriv.h>
55 #include <Security/SecTask.h>
56 #include <CFNetwork/CFHTTPMessage.h>
57 #include <CFNetwork/CFHTTPStream.h>
58 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
60 #include <securityd/SecOCSPRequest.h>
61 #include <securityd/SecOCSPResponse.h>
62 #include <securityd/asynchttp.h>
63 #include <securityd/SecTrustServer.h>
64 #include <securityd/SecOCSPCache.h>
65 #include <utilities/array_size.h>
66 #include <utilities/SecCFWrappers.h>
67 #include <utilities/SecAppleAnchorPriv.h>
68 #include "OTATrustUtilities.h"
69 #include "personalization.h"
70 #include <sys/codesign.h>
73 #include <Security/SecTaskPriv.h>
76 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
77 #ifndef DUMP_OCSPRESPONSES
78 #define DUMP_OCSPRESPONSES 0
81 #if DUMP_OCSPRESPONSES
86 static void secdumpdata(CFDataRef data
, const char *name
) {
87 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
88 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
95 /********************************************************
96 ****************** SecPolicy object ********************
97 ********************************************************/
99 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
100 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
102 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
104 CFArrayRef result
= NULL
;
105 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
106 if (NULL
== otapkiRef
)
111 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
112 CFRelease(otapkiRef
);
114 if (NULL
== evToPolicyAnchorDigest
)
119 CFArrayRef roots
= NULL
;
120 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
121 if (oid
&& evToPolicyAnchorDigest
)
123 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
124 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
126 secerror("EVRoot.plist has non array value");
131 CFReleaseSafe(evToPolicyAnchorDigest
);
136 static bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
137 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
140 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
141 policy_set_t valid_policies
) {
142 CFDictionaryRef keySizes
= NULL
;
143 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
145 /* Ensure that this certificate is a valid anchor for one of the
146 certificate policy oids specified in the leaf. */
147 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
149 bool good_ev_anchor
= false;
150 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
151 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
152 if (digests
&& CFArrayContainsValue(digests
,
153 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
154 secdebug("ev", "found anchor for policy oid");
155 good_ev_anchor
= true;
159 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
161 CFAbsoluteTime october2006
= 178761600;
162 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
163 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
164 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
166 if (SecCertificateVersion(certificate
) >= 3
167 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
168 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
169 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
170 secnotice("ev", "Anchor has invalid basic constraints"));
171 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
172 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
173 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
174 secnotice("ev", "Anchor has invalid key usage %u", ku
));
177 /* At least RSA 2048 or ECC NIST P-256. */
178 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
179 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
180 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
181 const void *values
[] = { rsaSize
, ecSize
};
182 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
183 &kCFTypeDictionaryKeyCallBacks
,
184 &kCFTypeDictionaryValueCallBacks
), notEV
);
185 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
186 secnotice("ev", "Anchor's public key is too weak for EV"));
191 CFReleaseNull(rsaSize
);
192 CFReleaseNull(ecSize
);
193 CFReleaseNull(keySizes
);
197 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
198 CFMutableDictionaryRef keySizes
= NULL
;
199 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
202 const SecCECertificatePolicies
*cp
;
203 cp
= SecCertificateGetCertificatePolicies(certificate
);
204 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
205 secnotice("ev", "SubCA missing certificate policies"));
206 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
207 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
208 secnotice("ev", "SubCA missing CRLDP"));
209 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
210 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
211 secnotice("ev", "SubCA has invalid basic constraints"));
212 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
213 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
214 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
215 secnotice("ev", "SubCA has invalid key usage %u", ku
));
217 /* 6.1.5 Key Sizes */
218 CFAbsoluteTime jan2011
= 315532800;
219 CFAbsoluteTime jan2014
= 410227200;
220 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
221 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
222 &kCFTypeDictionaryValueCallBacks
), notEV
);
223 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
224 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
225 SecCertificateNotValidAfter(certificate
) < jan2014
) {
226 /* At least RSA 1024 or ECC NIST P-256. */
227 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
228 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
229 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
230 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
232 /* At least RSA 2028 or ECC NIST P-256. */
233 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
234 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
235 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
236 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
239 /* 7.1.3 Algorithm Object Identifiers */
240 CFAbsoluteTime jan2016
= 473299200;
241 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
243 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
244 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
250 CFReleaseNull(rsaSize
);
251 CFReleaseNull(ecSize
);
252 CFReleaseNull(keySizes
);
256 bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate
) {
257 CFMutableDictionaryRef keySizes
= NULL
;
258 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
261 /* 3. Subscriber Certificate. */
263 /* (a) certificate Policies */
264 const SecCECertificatePolicies
*cp
;
265 cp
= SecCertificateGetCertificatePolicies(certificate
);
266 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
267 /* Now find at least one policy in here that has a qualifierID of id-qt 2
268 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
270 bool found_ev_anchor_for_leaf_policy
= false;
271 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
272 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
273 found_ev_anchor_for_leaf_policy
= true;
276 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
278 /* (b) cRLDistributionPoint
279 (c) authorityInformationAccess
280 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
283 /* (d) basicConstraints
284 If present, the cA field MUST be set false. */
285 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
287 require_action_quiet(bc
->isCA
== false, notEV
,
288 secnotice("ev", "Leaf has invalid basic constraints"));
292 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
294 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
,
295 secnotice("ev", "Leaf has invalid key usage %u", ku
));
299 /* The EV Cert Spec errata specifies this, though this is a check for SSL
300 not specifically EV. */
304 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
305 SecCertificateCopyExtendedKeyUsage(certificate
);
308 /* 6.1.5 Key Sizes */
309 CFAbsoluteTime jan2014
= 410227200;
310 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
311 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
312 &kCFTypeDictionaryValueCallBacks
), notEV
);
313 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
314 if (SecCertificateNotValidBefore(certificate
) < jan2014
) {
315 /* At least RSA 1024 or ECC NIST P-256. */
316 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
317 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
318 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
319 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
321 /* At least RSA 2028 or ECC NIST P-256. */
322 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
323 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
324 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
325 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
328 /* 6.3.2 Validity Periods */
329 CFAbsoluteTime jul2016
= 489024000;
330 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(certificate
);
331 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(certificate
);
332 if (SecCertificateNotValidBefore(certificate
) < jul2016
) {
333 /* Validity Period no greater than 60 months.
334 60 months is no more than 5 years and 2 leap days. */
335 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2);
336 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
337 secnotice("ev", "Leaf's validity period is more than 60 months"));
339 /* Validity Period no greater than 39 months.
340 39 months is no more than 3 years, 2 31-day months,
341 1 30-day month, and 1 leap day */
342 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1);
343 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
344 secnotice("ev", "Leaf has validity period longer than 39 months and issued after 30 June 2016"));
347 /* 7.1.3 Algorithm Object Identifiers */
348 CFAbsoluteTime jan2016
= 473299200;
349 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
351 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
352 notEV
, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
358 CFReleaseNull(rsaSize
);
359 CFReleaseNull(ecSize
);
360 CFReleaseNull(keySizes
);
364 /********************************************************
365 **************** SecPolicy Callbacks *******************
366 ********************************************************/
367 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
371 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
373 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
374 CFDataRef parentSubjectKeyID
= NULL
;
375 for (ix
= count
- 1; ix
>= 0; --ix
) {
376 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
377 /* If the previous certificate in the chain had a SubjectKeyID,
378 make sure it matches the current certificates AuthorityKeyID. */
379 if (parentSubjectKeyID
) {
380 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
381 SubjectKeyID can be critical. Currenty we don't check
383 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
384 if (authorityKeyID
) {
385 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
386 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
387 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
393 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
397 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
399 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
400 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
401 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
402 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
403 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
407 /* AUDIT[securityd](done):
408 policy->_options is a caller provided dictionary, only its cf type has
411 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
412 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
413 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
414 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
415 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
416 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
421 static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc
,
422 CFStringRef key
, bool strict
) {
423 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
424 for (ix
= 0; ix
< count
; ++ix
) {
425 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
426 const SecCEBasicConstraints
*bc
=
427 SecCertificateGetBasicConstraints(cert
);
431 /* Leaf certificate has basic constraints extension. */
432 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
434 } else if (!bc
->critical
) {
435 /* Basic constraints extension is not marked critical. */
436 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
441 if (ix
> 0 || count
== 1) {
443 /* Non leaf certificate marked as isCA false. */
444 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
448 if (bc
->pathLenConstraintPresent
) {
449 if (bc
->pathLenConstraint
< (uint32_t)(ix
- 1)) {
451 /* @@@ If a self signed certificate is issued by
452 another cert that is trusted, then we are supposed
453 to treat the self signed cert itself as the anchor
454 for path length purposes. */
455 CFIndex ssix
= SecCertificatePathSelfSignedIndex(path
);
456 if (ssix
>= 0 && ix
>= ssix
) {
457 /* It's ok if the pathLenConstraint isn't met for
458 certificates signing a self signed cert in the
463 /* Path Length Constraint Exceeded. */
464 if (!SecPVCSetResult(pvc
, key
, ix
,
471 } else if (strict
&& ix
> 0) {
472 /* In strict mode all CA certificates *MUST* have a critical
473 basic constraints extension and the leaf certificate
474 *MUST NOT* have a basic constraints extension. */
475 /* CA certificate is missing basicConstraints extension. */
476 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
483 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
485 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
488 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
490 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
491 for (ix
= 0; ix
< count
; ++ix
) {
492 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
493 /* If the certificate has a subject, or
494 if it doesn't, and it's the leaf and not self signed,
495 and also has a critical subjectAltName extension it's valid. */
496 if (!SecCertificateHasSubject(cert
)) {
497 if (ix
== 0 && count
> 1) {
498 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
499 /* Leaf certificate with empty subject does not have
500 a critical subject alt name extension. */
501 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
505 /* CA certificate has empty subject. */
506 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
513 static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc
,
517 /* Compare hostname suffix to domain name.
518 This function does not process wildcards, and allows hostname to match
519 any subdomain level of the provided domain.
521 To match, the last domain length chars of hostname must equal domain,
522 and the character immediately preceding domain in hostname (if any)
523 must be a dot. This means that domain 'bar.com' will match hostname
524 values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
526 Characters in each string are converted to lowercase for the comparison.
527 Trailing '.' characters in both names will be ignored.
529 Returns true on match, else false.
531 static bool SecDomainSuffixMatch(CFStringRef hostname
, CFStringRef domain
) {
532 CFStringInlineBuffer hbuf
= {}, dbuf
= {};
535 hlength
= CFStringGetLength(hostname
),
536 dlength
= CFStringGetLength(domain
);
537 CFRange hrange
= { 0, hlength
}, drange
= { 0, dlength
};
538 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
539 CFStringInitInlineBuffer(domain
, &dbuf
, drange
);
541 if((hlength
== 0) || (dlength
== 0)) {
542 /* trivial case with at least one empty name */
543 return (hlength
== dlength
) ? true : false;
546 /* trim off trailing dots */
547 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hlength
-1);
548 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dlength
-1);
550 hrange
.length
= --hlength
;
553 drange
.length
= --dlength
;
556 /* trim off leading dot in suffix, if present */
557 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, 0);
558 if((dlength
> 0) && (dch
== '.')) {
560 drange
.length
= --dlength
;
563 if(hlength
< dlength
) {
567 /* perform case-insensitive comparison of domain suffix */
568 for (hix
= (hlength
-dlength
),
569 dix
= drange
.location
; dix
< drange
.length
; dix
++) {
570 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
);
571 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dix
);
572 if (towlower(hch
) != towlower(dch
)) {
577 /* require a dot prior to domain suffix, unless hostname == domain */
578 if(hlength
> dlength
) {
579 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, (hlength
-(dlength
+1)));
588 #define kSecPolicySHA1Size 20
589 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
590 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
591 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
594 /* Check whether hostname is in a particular set of allowed domains.
595 Returns true if OK, false if not allowed.
597 static bool SecPolicyCheckDomain(SecPVCRef pvc
, CFStringRef hostname
)
599 CFIndex count
= SecPVCGetCertificateCount(pvc
);
600 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
601 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
603 /* is this chain anchored by kAppleCorpCASHA1? */
604 CFDataRef corpSHA1
= CFDataCreateWithBytesNoCopy(NULL
,
605 kAppleCorpCASHA1
, kSecPolicySHA1Size
, kCFAllocatorNull
);
606 bool isCorpSHA1
= (corpSHA1
&& CFEqual(anchorSHA1
, corpSHA1
));
607 CFReleaseSafe(corpSHA1
);
609 /* limit hostname to specified domains */
610 const CFStringRef dnlist
[] = {
614 unsigned int idx
, dncount
=2;
615 for (idx
= 0; idx
< dncount
; idx
++) {
616 if (SecDomainSuffixMatch(hostname
, dnlist
[idx
])) {
622 /* %%% other CA pinning checks TBA */
628 /* AUDIT[securityd](done):
629 policy->_options is a caller provided dictionary, only its cf type has
632 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
634 /* @@@ Consider what to do if the caller passes in no hostname. Should
635 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
636 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
637 CFStringRef hostName
= (CFStringRef
)
638 CFDictionaryGetValue(policy
->_options
, key
);
639 if (!isString(hostName
)) {
640 /* @@@ We can't return an error here and making the evaluation fail
641 won't help much either. */
645 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
646 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
649 /* Hostname mismatch or no hostnames found in certificate. */
650 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
652 else if (!SecPolicyCheckDomain(pvc
, hostName
)) {
653 /* Hostname match, but domain not allowed for this CA */
654 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
657 if ((dnsMatch
|| pvc
->details
)
658 && SecPolicySubscriberCertificateCouldBeEV(leaf
)) {
659 secdebug("policy", "enabling optionally_ev");
660 pvc
->optionally_ev
= true;
665 /* AUDIT[securityd](done):
666 policy->_options is a caller provided dictionary, only its cf type has
669 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
670 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
671 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
672 if (!isString(email
)) {
673 /* We can't return an error here and making the evaluation fail
674 won't help much either. */
678 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
680 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
681 /* Hostname mismatch or no hostnames found in certificate. */
682 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
686 static void SecPolicyCheckValidIntermediates(SecPVCRef pvc
,
688 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
689 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
690 for (ix
= 1; ix
< count
- 1; ++ix
) {
691 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
692 if (!SecCertificateIsValid(cert
, verifyTime
)) {
693 /* Intermediate certificate has expired. */
694 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
700 static void SecPolicyCheckValidLeaf(SecPVCRef pvc
,
702 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
703 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
704 if (!SecCertificateIsValid(cert
, verifyTime
)) {
705 /* Leaf certificate has expired. */
706 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
711 static void SecPolicyCheckValidRoot(SecPVCRef pvc
,
713 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
714 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
716 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
717 if (!SecCertificateIsValid(cert
, verifyTime
)) {
718 /* Root certificate has expired. */
719 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
724 /* AUDIT[securityd](done):
725 policy->_options is a caller provided dictionary, only its cf type has
728 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
730 CFIndex count
= SecPVCGetCertificateCount(pvc
);
732 /* Can't check intermediates common name if there is no intermediate. */
733 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
737 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
738 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
739 CFStringRef commonName
=
740 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
741 if (!isString(commonName
)) {
742 /* @@@ We can't return an error here and making the evaluation fail
743 won't help much either. */
746 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
747 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
751 /* AUDIT[securityd](done):
752 policy->_options is a caller provided dictionary, only its cf type has
755 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
757 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
758 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
759 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
761 if (!isString(common_name
)) {
762 /* @@@ We can't return an error here and making the evaluation fail
763 won't help much either. */
766 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
767 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
771 /* AUDIT[securityd](done):
772 policy->_options is a caller provided dictionary, only its cf type has
775 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
777 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
778 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
779 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
781 if (!isString(prefix
)) {
782 /* @@@ We can't return an error here and making the evaluation fail
783 won't help much either. */
786 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
787 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
791 /* AUDIT[securityd](done):
792 policy->_options is a caller provided dictionary, only its cf type has
795 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
797 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
798 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
799 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
801 if (!isString(common_name
)) {
802 /* @@@ We can't return an error here and making the evaluation fail
803 won't help much either. */
806 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
807 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
811 /* AUDIT[securityd](done):
812 policy->_options is a caller provided dictionary, only its cf type has
815 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
817 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
818 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
819 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
821 /* @@@ We can't return an error here and making the evaluation fail
822 won't help much either. */
825 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
826 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
831 /* AUDIT[securityd](done):
832 policy->_options is a caller provided dictionary, only its cf type has
835 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
837 CFIndex count
= SecPVCGetCertificateCount(pvc
);
838 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
839 CFNumberRef chainLength
=
840 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
842 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
843 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
844 /* @@@ We can't return an error here and making the evaluation fail
845 won't help much either. */
848 if (value
!= count
) {
849 /* Chain length doesn't match policy requirement. */
850 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
855 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
856 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
857 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
859 bool foundMatch
= false;
861 foundMatch
= CFEqual(digest
, value
);
862 else if (isArray(value
))
863 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
865 /* @@@ We only support Data and Array but we can't return an error here so.
866 we let the evaluation fail (not much help) and assert in debug. */
873 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
874 CFIndex count
= SecPVCGetCertificateCount(pvc
);
875 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
876 CFDataRef anchorSHA256
= NULL
;
877 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
879 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
880 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
883 CFReleaseNull(anchorSHA256
);
888 /* AUDIT[securityd](done):
889 policy->_options is a caller provided dictionary, only its cf type has
892 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
894 CFIndex count
= SecPVCGetCertificateCount(pvc
);
895 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
896 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
898 if (!isDigestInPolicy(pvc
, key
, anchorSHA1
))
899 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, count
-1, kCFBooleanFalse
))
906 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
907 policy->_options is a caller provided dictionary, only its cf type has
910 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
912 SecCertificateRef cert
= NULL
;
913 CFDataRef digest
= NULL
;
915 if (SecPVCGetCertificateCount(pvc
) < 2) {
916 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
920 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
921 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
923 if (!isDigestInPolicy(pvc
, key
, digest
)) {
924 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
926 CFReleaseNull(digest
);
930 policy->_options is a caller provided dictionary, only its cf type has
933 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
935 CFIndex count
= SecPVCGetCertificateCount(pvc
);
936 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
937 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
938 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
939 SecAppleTrustAnchorFlags flags
= 0;
941 if (isDictionary(value
)) {
942 if (CFDictionaryGetValue(value
, kSecPolicyAppleAnchorIncludeTestRoots
)) {
943 flags
|= kSecAppleTrustAnchorFlagsIncludeTestAnchors
;
947 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
950 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
957 /* AUDIT[securityd](done):
958 policy->_options is a caller provided dictionary, only its cf type has
961 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
963 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
964 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
965 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
967 if (!isString(org
)) {
968 /* @@@ We can't return an error here and making the evaluation fail
969 won't help much either. */
972 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
973 /* Leaf Subject Organization mismatch. */
974 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
978 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
980 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
981 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
982 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
984 if (!isString(orgUnit
)) {
985 /* @@@ We can't return an error here and making the evaluation fail
986 won't help much either. */
989 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
990 /* Leaf Subject Organization mismatch. */
991 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
995 /* AUDIT[securityd](done):
996 policy->_options is a caller provided dictionary, only its cf type has
999 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
1001 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1002 CFArrayRef trustedServerNames
= (CFArrayRef
)
1003 CFDictionaryGetValue(policy
->_options
, key
);
1004 /* No names specified means we accept any name. */
1005 if (!trustedServerNames
)
1007 if (!isArray(trustedServerNames
)) {
1008 /* @@@ We can't return an error here and making the evaluation fail
1009 won't help much either. */
1013 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1014 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
1015 /* Hostname mismatch or no hostnames found in certificate. */
1016 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1020 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
1021 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
1022 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
1023 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
1024 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
1025 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
1026 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
1027 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
1028 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
1029 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
1031 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
1032 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
1033 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
1034 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
1035 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
1036 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
1037 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
1038 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
1039 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
1040 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
1041 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
1042 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
1043 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
1044 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
1046 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
1049 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
1051 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1052 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
1054 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
1055 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
1056 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
1058 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1059 CFDataRef serial
= SecCertificateCopySerialNumber(cert
, NULL
);
1061 CFDataRef serial
= SecCertificateCopySerialNumber(cert
);
1065 CFIndex serial_length
= CFDataGetLength(serial
);
1066 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
1068 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
1073 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
1075 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
1077 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
1078 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
1080 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1081 CFReleaseSafe(serial
);
1090 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1091 if (NULL
!= otapkiRef
)
1093 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
1094 CFRelease(otapkiRef
);
1095 if (NULL
!= blackListedKeys
)
1097 /* Check for blacklisted intermediates keys. */
1098 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1101 /* Check dgst against blacklist. */
1102 if (CFSetContainsValue(blackListedKeys
, dgst
))
1104 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1108 CFRelease(blackListedKeys
);
1113 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
1115 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1116 if (NULL
!= otapkiRef
)
1118 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
1119 CFRelease(otapkiRef
);
1120 if (NULL
!= grayListedKeys
)
1122 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1124 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1127 /* Check dgst against gray. */
1128 if (CFSetContainsValue(grayListedKeys
, dgst
))
1130 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1134 CFRelease(grayListedKeys
);
1139 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1141 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1142 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1143 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1145 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
1146 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1150 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
1152 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1153 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1154 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1156 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
1157 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1161 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1163 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1164 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1165 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1167 for (ix
= 1; ix
< count
- 1; ix
++) {
1168 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1169 if (SecCertificateHasMarkerExtension(cert
, value
))
1172 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1175 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
1177 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1178 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1179 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
1181 for (ix
= 1; ix
< count
- 1; ix
++) {
1182 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1183 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
1184 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
1189 /* Returns true if path is on the allow list for the authority key of the
1190 certificate at certix, false otherwise.
1192 static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc
, CFIndex certix
)
1194 bool result
= false;
1195 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1196 CFStringRef authKey
= NULL
;
1197 CFArrayRef allowedCerts
= NULL
;
1198 SecOTAPKIRef otapkiRef
= NULL
;
1200 if (certix
< 0 || certix
>= count
) {
1204 //get authKeyID from the specified cert in the chain
1205 SecCertificateRef issuedCert
= SecPVCGetCertificateAtIndex(pvc
, certix
);
1206 CFDataRef authKeyID
= SecCertificateGetAuthorityKeyID(issuedCert
);
1207 if (NULL
== authKeyID
) {
1210 authKey
= CFDataCopyHexString(authKeyID
);
1211 if (NULL
== authKey
) {
1215 otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1216 if (NULL
== otapkiRef
) {
1220 allowedCerts
= SecOTAPKICopyAllowListForAuthKeyID(otapkiRef
, authKey
);
1221 if (NULL
== allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
1225 //search sorted array for the SHA256 hash of a cert in the chain
1226 CFRange range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
1227 for (ix
= 0; ix
<= certix
; ix
++) {
1228 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1233 CFDataRef certHash
= SecCertificateCopySHA256Digest(cert
);
1238 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, certHash
,
1239 (CFComparatorFunction
)CFDataCompare
, NULL
);
1240 if (position
< CFArrayGetCount(allowedCerts
)) {
1241 CFDataRef possibleMatch
= CFArrayGetValueAtIndex(allowedCerts
, position
);
1242 if (!CFDataCompare(certHash
, possibleMatch
)) {
1243 //this cert is in the allowlist
1248 CFRelease(certHash
);
1252 CFReleaseNull(authKey
);
1253 CFReleaseNull(otapkiRef
);
1254 CFReleaseNull(allowedCerts
);
1258 #define DCMP(_idx_) memcmp(data+(8*_idx_), digest, 8)
1260 /* Returns true if leaf is on the CT whitelist */
1261 static bool SecPVCCheckCTWhiteListedLeaf(SecPVCRef pvc
)
1263 SecOTAPKIRef otapkiRef
= NULL
;
1264 CFDataRef whiteList
= NULL
;
1265 SecCertificateRef cert
= NULL
;
1266 CFDataRef dgst
= NULL
;
1267 bool result
= false;
1268 const uint8_t *digest
= NULL
;
1269 const uint8_t *data
= NULL
;
1270 require(otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef(), out
);
1271 require(whiteList
= SecOTAPKICopyCTWhiteList(otapkiRef
), out
);
1272 require(cert
= SecPVCGetCertificateAtIndex(pvc
, 0), out
);
1273 require(dgst
= SecCertificateCopySHA256Digest(cert
), out
);
1275 digest
= CFDataGetBytePtr(dgst
);
1276 data
= CFDataGetBytePtr(whiteList
);
1278 CFIndex h
= CFDataGetLength(whiteList
)/8-1;
1280 if(DCMP(l
)==0 || DCMP(h
)==0) {
1285 if(DCMP(l
)>0 || DCMP(h
)<0) {
1304 CFReleaseSafe(dgst
);
1305 CFReleaseSafe(whiteList
);
1306 CFReleaseSafe(otapkiRef
);
1310 /****************************************************************************
1311 *********************** New rfc5280 Chain Validation ***********************
1312 ****************************************************************************/
1315 typedef struct cert_path
*cert_path_t
;
1320 typedef struct x500_name
*x500_name_t
;
1324 typedef struct algorithm_id
*algorithm_id_t
;
1325 struct algorithm_id
{
1326 oid_t algorithm_oid
;
1330 typedef struct trust_anchor
*trust_anchor_t
;
1331 struct trust_anchor
{
1332 x500_name_t issuer_name
;
1333 algorithm_id_t public_key_algorithm
; /* includes optional params */
1334 SecKeyRef public_key
;
1337 typedef struct certificate_policy
*certificate_policy_t
;
1338 struct certificate_policy
{
1339 policy_qualifier_t qualifiers
;
1341 SLIST_ENTRY(certificate_policy
) policies
;
1344 typedef struct policy_mapping
*policy_mapping_t
;
1345 struct policy_mapping
{
1346 SLIST_ENTRY(policy_mapping
) mappings
;
1347 oid_t issuer_domain_policy
;
1348 oid_t subject_domain_policy
;
1351 typedef struct root_name
*root_name_t
;
1356 struct policy_tree_add_ctx
{
1358 policy_qualifier_t p_q
;
1361 /* 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}. */
1362 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1363 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1364 policy_set_t policy_set
;
1365 for (policy_set
= node
->expected_policy_set
;
1367 policy_set
= policy_set
->oid_next
) {
1368 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1369 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1376 /* 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}. */
1377 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1378 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1379 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1380 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1386 /* Return true iff node has a child with a valid_policy equal to oid. */
1387 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1389 policy_tree_t child
;
1390 for (child
= node
->children
; child
; child
= child
->siblings
) {
1391 if (oid_equal(child
->valid_policy
, (*oid
))) {
1398 /* 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. */
1399 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1400 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1401 policy_set_t policy_set
;
1402 bool added_node
= false;
1403 for (policy_set
= node
->expected_policy_set
;
1405 policy_set
= policy_set
->oid_next
) {
1406 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1407 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1415 /* 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. */
1416 static bool policy_tree_map(policy_tree_t node
, void *ctx
) {
1417 /* Can't map oidAnyPolicy. */
1418 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1421 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1422 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1423 policy_set_t policy_set
= NULL
;
1424 /* First count how many mappings match this nodes valid_policy. */
1425 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1426 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1427 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1428 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1429 p_node
->oid
= mapping
->subjectDomainPolicy
;
1430 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1431 policy_set
= p_node
;
1435 policy_tree_set_expected_policy(node
, policy_set
);
1442 #define POLICY_MAPPING 0
1443 #define POLICY_SUBTREES 1
1445 /* rfc5280 basic cert processing. */
1446 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1450 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1451 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1452 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1453 uint32_t n
= (uint32_t)count
;
1454 bool is_anchored
= SecPVCIsAnchored(pvc
);
1456 /* If the anchor is trusted we don't process the last cert in the
1460 /* trust may be restored for a path with an untrusted root that matches the allow list */
1461 pvc
->is_allowlisted
= SecPVCCheckCertificateAllowList(pvc
, n
- 1);
1462 if (!pvc
->is_allowlisted
) {
1463 /* Add a detail for the root not being trusted. */
1464 if (SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1465 n
- 1, kCFBooleanFalse
, true))
1470 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1471 //policy_set_t user_initial_policy_set = NULL;
1472 //trust_anchor_t anchor;
1473 bool initial_policy_mapping_inhibit
= false;
1474 bool initial_explicit_policy
= false;
1475 bool initial_any_policy_inhibit
= false;
1477 /* Initialization */
1478 pvc
->valid_policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1480 CFMutableArrayRef permitted_subtrees
= NULL
;
1481 CFMutableArrayRef excluded_subtrees
= NULL
;
1482 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1483 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1484 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1485 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1486 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1487 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1489 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1490 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1491 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1494 /* Path builder ensures we only get cert chains with proper issuer
1495 chaining with valid signatures along the way. */
1496 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1497 SecKeyRef working_public_key
= anchor
->public_key
;
1498 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1500 uint32_t i
, max_path_length
= n
;
1501 SecCertificateRef cert
= NULL
;
1502 for (i
= 1; i
<= n
; ++i
) {
1504 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1505 bool is_self_issued
= SecPVCIsCertificateAtIndexSelfSigned(pvc
, n
- i
);
1507 /* (a) Verify the basic certificate information. */
1508 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1509 using the working_public_key and the working_public_key_parameters. */
1511 /* Already done by chain builder. */
1512 if (!SecCertificateIsValid(cert
, verify_time
)) {
1513 CFStringRef fail_key
= i
== n
? kSecPolicyCheckValidLeaf
: kSecPolicyCheckValidIntermediates
;
1514 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1518 if (SecCertificateIsWeakKey(cert
)) {
1519 CFStringRef fail_key
= i
== n
? kSecPolicyCheckWeakLeaf
: kSecPolicyCheckWeakIntermediates
;
1520 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1525 /* @@@ cert.issuer == working_issuer_name. */
1529 if (!is_self_issued
|| i
== n
) {
1531 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1532 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1533 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1534 secnotice("policy", "name in excluded subtrees");
1535 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1538 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1539 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1540 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1541 secnotice("policy", "name not in permitted subtrees");
1542 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1548 if (pvc
->valid_policy_tree
) {
1549 const SecCECertificatePolicies
*cp
=
1550 SecCertificateGetCertificatePolicies(cert
);
1551 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1552 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1553 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1554 oid_t p_oid
= policy
->policyIdentifier
;
1555 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1556 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1557 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1558 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1559 policy_tree_add_if_match
, &ctx
)) {
1560 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1561 policy_tree_add_if_any
, &ctx
);
1565 /* The certificate policies extension includes the policy
1566 anyPolicy with the qualifier set AP-Q and either
1567 (a) inhibit_anyPolicy is greater than 0 or
1568 (b) i < n and the certificate is self-issued. */
1569 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1570 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1571 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1572 oid_t p_oid
= policy
->policyIdentifier
;
1573 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1574 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1575 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1576 policy_tree_add_expected
, (void *)p_q
);
1580 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1583 if (pvc
->valid_policy_tree
)
1584 policy_tree_prune(&pvc
->valid_policy_tree
);
1587 /* (f) Verify that either explicit_policy is greater than 0 or the
1588 valid_policy_tree is not equal to NULL. */
1589 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1590 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1591 secnotice("policy", "policy tree failure");
1592 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true)) {
1596 /* If Last Cert in Path */
1600 /* Prepare for Next Cert */
1602 /* (a) verify that anyPolicy does not appear as an
1603 issuerDomainPolicy or a subjectDomainPolicy */
1604 CFDictionaryRef pm
= SecCertificateGetPolicyMappings(cert
);
1606 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1607 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1608 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1609 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1610 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1611 /* Policy mapping uses anyPolicy, illegal. */
1612 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
)) {
1618 /* (1) If the policy_mapping variable is greater than 0 */
1619 if (policy_mapping
> 0) {
1620 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1621 policy_tree_map
, (void *)pm
)) {
1622 /* 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:
1624 (i) set the valid_policy to ID-P;
1626 (ii) set the qualifier_set to the qualifier set of the
1627 policy anyPolicy in the certificate policies
1628 extension of certificate i; and
1629 (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. */
1633 /* (i) delete each node of depth i in the valid_policy_tree
1634 where ID-P is the valid_policy. */
1635 struct policy_tree_map_ctx ctx
= { idp_oid
, sdp_oid
};
1636 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1637 policy_tree_delete_if_match
, &ctx
);
1639 /* (ii) If there is a node in the valid_policy_tree of depth
1640 i-1 or less without any child nodes, delete that
1641 node. Repeat this step until there are no nodes of
1642 depth i-1 or less without children. */
1643 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1646 #endif /* POLICY_MAPPING */
1648 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1649 //working_public_key = SecCertificateCopyPublicKey(cert);
1650 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1651 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1653 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1655 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1656 if (permitted_subtrees_in_cert
) {
1657 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1660 // could do something smart here to avoid inserting the exact same constraint
1661 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1662 if (excluded_subtrees_in_cert
) {
1663 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1664 CFRange range
= { 0, num_trees
};
1665 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1669 if (!is_self_issued
) {
1670 if (explicit_policy
)
1674 if (inhibit_any_policy
)
1675 inhibit_any_policy
--;
1678 const SecCEPolicyConstraints
*pc
=
1679 SecCertificateGetPolicyConstraints(cert
);
1681 if (pc
->requireExplicitPolicyPresent
1682 && pc
->requireExplicitPolicy
< explicit_policy
) {
1683 explicit_policy
= pc
->requireExplicitPolicy
;
1685 if (pc
->inhibitPolicyMappingPresent
1686 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1687 policy_mapping
= pc
->inhibitPolicyMapping
;
1691 uint32_t iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1692 if (iap
< inhibit_any_policy
) {
1693 inhibit_any_policy
= iap
;
1696 const SecCEBasicConstraints
*bc
=
1697 SecCertificateGetBasicConstraints(cert
);
1698 #if 0 /* Checked in chain builder pre signature verify already. */
1699 if (!bc
|| !bc
->isCA
) {
1700 /* Basic constraints not present or not marked as isCA, illegal. */
1701 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1702 n
- i
, kCFBooleanFalse
)) {
1708 if (!is_self_issued
) {
1709 if (max_path_length
> 0) {
1712 /* max_path_len exceeded, illegal. */
1713 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1714 n
- i
, kCFBooleanFalse
)) {
1720 if (bc
&& bc
->pathLenConstraintPresent
1721 && bc
->pathLenConstraint
< max_path_length
) {
1722 max_path_length
= bc
->pathLenConstraint
;
1724 #if 0 /* Checked in chain builder pre signature verify already. */
1725 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1726 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1727 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1728 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1729 n
- i
, kCFBooleanFalse
, true)) {
1734 /* (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. */
1735 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1736 /* Certificate contains one or more unknown critical extensions. */
1737 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1738 n
- i
, kCFBooleanFalse
)) {
1742 } /* end loop over certs in path */
1744 cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1746 if (explicit_policy
)
1749 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1751 if (pc
->requireExplicitPolicyPresent
1752 && pc
->requireExplicitPolicy
== 0) {
1753 explicit_policy
= 0;
1757 //working_public_key = SecCertificateCopyPublicKey(cert);
1759 /* 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
1760 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1761 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1763 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1764 /* (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. */
1765 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1766 /* Certificate contains one or more unknown critical extensions. */
1767 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1768 0, kCFBooleanFalse
)) {
1772 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1774 if (pvc
->valid_policy_tree
) {
1775 #if !defined(NDEBUG)
1776 policy_tree_dump(pvc
->valid_policy_tree
);
1779 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1782 /* If either (1) the value of explicit_policy variable is greater than
1783 zero or (2) the valid_policy_tree is not NULL, then path processing
1785 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1786 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1787 secnotice("policy", "policy tree failure");
1788 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, 0, kCFBooleanFalse
, true)) {
1794 CFReleaseNull(permitted_subtrees
);
1795 CFReleaseNull(excluded_subtrees
);
1798 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1799 policy_set_t policies
= NULL
;
1800 const SecCECertificatePolicies
*cp
=
1801 SecCertificateGetCertificatePolicies(cert
);
1802 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1803 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1804 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1809 static void SecPolicyCheckEV(SecPVCRef pvc
,
1811 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1812 policy_set_t valid_policies
= NULL
;
1814 /* 6.1.7. Key Usage Purposes */
1816 CFAbsoluteTime jul2016
= 489024000;
1817 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1818 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1819 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1820 if (SecPVCSetResultForced(pvc
, key
,
1821 0, kCFBooleanFalse
, true)) {
1827 for (ix
= 0; ix
< count
; ++ix
) {
1828 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1829 policy_set_t policies
= policies_for_cert(cert
);
1832 /* anyPolicy in the leaf isn't allowed for EV, so only init
1833 valid_policies if we have real policies. */
1834 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1835 valid_policies
= policies
;
1838 } else if (ix
< count
- 1) {
1839 /* Subordinate CA */
1840 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1841 secnotice("ev", "subordinate certificate is not ev");
1842 if (SecPVCSetResultForced(pvc
, key
,
1843 ix
, kCFBooleanFalse
, true)) {
1844 policy_set_free(valid_policies
);
1845 policy_set_free(policies
);
1849 policy_set_intersect(&valid_policies
, policies
);
1852 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1853 secnotice("ev", "anchor certificate is not ev");
1854 if (SecPVCSetResultForced(pvc
, key
,
1855 ix
, kCFBooleanFalse
, true)) {
1856 policy_set_free(valid_policies
);
1857 policy_set_free(policies
);
1862 policy_set_free(policies
);
1863 if (!valid_policies
) {
1864 secnotice("ev", "valid_policies set is empty: chain not ev");
1865 /* If we ever get into a state where no policies are valid anymore
1866 this can't be an ev chain. */
1867 if (SecPVCSetResultForced(pvc
, key
,
1868 ix
, kCFBooleanFalse
, true)) {
1874 policy_set_free(valid_policies
);
1876 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1877 Subscriber MUST contain an OID defined by the CA in the certificate’s
1878 certificatePolicies extension that: (i) indicates which CA policy statement relates
1879 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1880 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1881 marks the certificate as being an EV Certificate.
1882 (b) EV Subordinate CA Certificates
1883 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1884 CA MUST contain one or more OIDs defined by the issuing CA that
1885 explicitly identify the EV Policies that are implemented by the Subordinate
1887 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1888 MAY contain the special anyPolicy OID (2.5.29.32.0).
1889 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1890 certificatePolicies or extendedKeyUsage extensions.
1896 * MARK: Certificate Transparency support
1902 Version sct_version; // 1 byte
1903 LogID id; // 32 bytes
1904 uint64 timestamp; // 8 bytes
1905 CtExtensions extensions; // 2 bytes len field, + n bytes data
1906 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1907 Version sct_version;
1908 SignatureType signature_type = certificate_timestamp;
1910 LogEntryType entry_type;
1911 select(entry_type) {
1912 case x509_entry: ASN.1Cert;
1913 case precert_entry: PreCert;
1915 CtExtensions extensions;
1917 } SignedCertificateTimestamp;
1921 #include <Security/SecureTransportPriv.h>
1924 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1927 case SSL_SignatureAlgorithmRSA
:
1929 case SSL_HashAlgorithmSHA1
:
1930 return &CSSMOID_SHA1WithRSA
;
1931 case SSL_HashAlgorithmSHA256
:
1932 return &CSSMOID_SHA256WithRSA
;
1933 case SSL_HashAlgorithmSHA384
:
1934 return &CSSMOID_SHA384WithRSA
;
1938 case SSL_SignatureAlgorithmECDSA
:
1940 case SSL_HashAlgorithmSHA1
:
1941 return &CSSMOID_ECDSA_WithSHA1
;
1942 case SSL_HashAlgorithmSHA256
:
1943 return &CSSMOID_ECDSA_WithSHA256
;
1944 case SSL_HashAlgorithmSHA384
:
1945 return &CSSMOID_ECDSA_WithSHA384
;
1957 static size_t SSLDecodeUint16(const uint8_t *p
)
1959 return (p
[0]<<8 | p
[1]);
1962 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1964 p
[0] = (len
>> 8)&0xff;
1965 p
[1] = (len
& 0xff);
1969 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1971 p
[0] = (len
>> 16)&0xff;
1972 p
[1] = (len
>> 8)&0xff;
1973 p
[2] = (len
& 0xff);
1979 uint64_t SSLDecodeUint64(const uint8_t *p
)
1982 for(int i
=0; i
<8; i
++) {
1989 #include <libDER/DER_CertCrl.h>
1990 #include <libDER/DER_Encode.h>
1991 #include <libDER/asn1Types.h>
1994 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1996 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1998 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
2000 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
2002 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2003 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
2004 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
2010 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
2012 SecCertificateRef leafCert
= NULL
;
2013 SecCertificateRef issuer
= NULL
;
2014 CFDataRef issuerKeyHash
= NULL
;
2015 CFDataRef tbs_precert
= NULL
;
2016 CFMutableDataRef data
= NULL
;
2018 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2019 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2020 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2022 require(leafCert
, out
);
2023 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
2024 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
2025 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
2027 require(issuerKeyHash
, out
);
2028 require(tbs_precert
, out
);
2029 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2030 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2032 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2033 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
2034 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
2035 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
2038 CFReleaseSafe(issuerKeyHash
);
2039 CFReleaseSafe(tbs_precert
);
2044 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
2046 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
2050 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
2052 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
2059 If the 'sct' is valid, add it to the validatingLogs dictionary.
2062 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
2064 - entry_type: 0 for x509 cert, 1 for precert.
2065 - entry: the cert or precert data.
2066 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
2067 - trustedLog: Dictionary contain the Trusted Logs.
2069 The SCT is valid if:
2070 - It decodes properly.
2071 - Its timestamp is less than 'verifyTime'.
2072 - It is signed by a log in 'trustedLogs'.
2073 - If entry_type = 0, the log must be currently qualified.
2074 - If entry_type = 1, the log may be expired.
2076 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
2077 If an entry for the same log already existing in the dictionary, the entry is replaced only if the timestamp of this SCT is earlier.
2082 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFArrayRef trustedLogs
, CFAbsoluteTime
*sct_at
)
2085 const uint8_t *logID
;
2086 const uint8_t *timestampData
;
2088 size_t extensionsLen
;
2089 const uint8_t *extensionsData
;
2092 size_t signatureLen
;
2093 const uint8_t *signatureData
;
2094 SecKeyRef pubKey
= NULL
;
2095 uint8_t *signed_data
= NULL
;
2096 const SecAsn1Oid
*oid
= NULL
;
2098 CFDataRef logIDData
= NULL
;
2099 CFDictionaryRef result
= 0;
2101 const uint8_t *p
= CFDataGetBytePtr(sct
);
2102 size_t len
= CFDataGetLength(sct
);
2104 require(len
>=43, out
);
2106 version
= p
[0]; p
++; len
--;
2107 logID
= p
; p
+=32; len
-=32;
2108 timestampData
= p
; p
+=8; len
-=8;
2109 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2111 require(len
>=extensionsLen
, out
);
2112 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
2114 require(len
>=4, out
);
2115 hashAlg
=p
[0]; p
++; len
--;
2116 sigAlg
=p
[0]; p
++; len
--;
2117 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2118 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
2121 /* verify version: only v1(0) is supported */
2123 secerror("SCT version unsupported: %d\n", version
);
2127 /* verify timestamp not in the future */
2128 timestamp
= SSLDecodeUint64(timestampData
);
2129 if(timestamp
> vt
) {
2130 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
2137 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
2138 signed_data
= malloc(signed_data_len
);
2139 require(signed_data
, out
);
2142 *q
++ = 0; // certificate_timestamp
2143 memcpy(q
, timestampData
, 8); q
+=8;
2144 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
2145 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
2146 q
= SSLEncodeUint16(q
, extensionsLen
);
2147 memcpy(q
, extensionsData
, extensionsLen
);
2149 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
2151 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
2152 const void *key_data
;
2153 if(!isDictionary(dict
)) return false;
2154 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
2155 if(!isData(key_data
)) return false;
2156 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
2157 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
2158 CFReleaseSafe(valueID
);
2161 require(logData
, out
);
2164 // For external SCTs, only keep SCTs from currently valid logs.
2165 require(!CFDictionaryContainsKey(logData
, CFSTR("expiry")), out
);
2168 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
2169 require(logKeyData
, out
); // This failing would be an internal logic error
2170 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
2171 require(pubKey
, out
);
2173 oid
= oidForSigAlg(hashAlg
, sigAlg
);
2176 algId
.algorithm
= *oid
;
2177 algId
.parameters
.Data
= NULL
;
2178 algId
.parameters
.Length
= 0;
2180 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
2181 *sct_at
= TimestampToCFAbsoluteTime(timestamp
);
2184 secerror("SCT signature failed (log=%@)\n", logData
);
2188 CFReleaseSafe(logIDData
);
2189 CFReleaseSafe(pubKey
);
2195 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
2197 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
2199 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
2200 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
2201 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
2202 CFReleaseSafe(sct_time
);
2206 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
2208 CFMutableArrayRef SCTs
= NULL
;
2209 SecCertificateRef leafCert
= NULL
;
2210 SecCertificateRef issuer
= NULL
;
2211 CFArrayRef ocspResponsesData
= NULL
;
2212 SecOCSPRequestRef ocspRequest
= NULL
;
2214 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2215 require_quiet(ocspResponsesData
, out
);
2217 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2218 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2219 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2221 require(leafCert
, out
);
2222 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
2223 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
2225 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2228 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2229 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2230 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2231 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
2232 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
2233 if(ocspSingleResponse
) {
2234 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
2235 if(singleResponseSCTs
) {
2236 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
2237 CFRelease(singleResponseSCTs
);
2239 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
2242 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2245 if(CFArrayGetCount(SCTs
)==0) {
2246 CFReleaseNull(SCTs
);
2250 CFReleaseSafe(ocspResponsesData
);
2252 SecOCSPRequestFinalize(ocspRequest
);
2257 static void SecPolicyCheckCT(SecPVCRef pvc
, CFStringRef key
)
2259 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2260 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
2261 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
2262 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2263 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
2264 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
2265 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
2267 // This eventually contain list of logs who validated the SCT.
2268 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2269 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2271 uint64_t vt
= TimestampFromCFAbsoluteTime(pvc
->verifyTime
);
2273 __block
bool at_least_one_currently_valid_external
= 0;
2274 __block
bool at_least_one_currently_valid_embedded
= 0;
2276 require(logsValidatingEmbeddedScts
, out
);
2277 require(currentLogsValidatingScts
, out
);
2279 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
2280 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
2281 CFArrayForEach(embeddedScts
, ^(const void *value
){
2282 CFAbsoluteTime sct_at
;
2283 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, trustedLogs
, &sct_at
);
2285 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
2286 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2287 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2288 at_least_one_currently_valid_embedded
= true;
2294 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
2295 CFArrayForEach(builderScts
, ^(const void *value
){
2296 CFAbsoluteTime sct_at
;
2297 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2299 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2300 at_least_one_currently_valid_external
= true;
2305 if(ocspScts
&& x509Entry
) {
2306 CFArrayForEach(ocspScts
, ^(const void *value
){
2307 CFAbsoluteTime sct_at
;
2308 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2310 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2311 at_least_one_currently_valid_external
= true;
2318 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
2321 is_ct = (A1 AND A2) OR (B1 AND B2).
2323 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
2324 A2: At least one embedded SCT from a currently valid log.
2326 B1: SCTs from 2 currently valid logs (from any source)
2327 B2: At least 1 external SCT from a currently valid log.
2333 if(at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2) {
2335 } else if(at_least_one_currently_valid_embedded
) {
2336 __block CFAbsoluteTime issuanceTime
= pvc
->verifyTime
;
2337 __block
int lifetime
; // in Months
2338 __block
unsigned once_or_current_qualified_embedded
= 0;
2340 /* Calculate issuance time base on timestamp of SCTs from current logs */
2341 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
2342 CFDictionaryRef log
= key
;
2343 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2344 // Log is still qualified
2345 CFDateRef ts
= (CFDateRef
) value
;
2346 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
2347 if(timestamp
< issuanceTime
) {
2348 issuanceTime
= timestamp
;
2354 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
2355 CFDictionaryRef log
= key
;
2356 CFDateRef ts
= value
;
2357 CFDateRef expiry
= CFDictionaryGetValue(log
, CFSTR("expiry"));
2358 if(expiry
== NULL
|| CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
) {
2359 once_or_current_qualified_embedded
++;
2363 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
2365 CFCalendarGetComponentDifference(zuluCalendar
,
2366 SecCertificateNotValidBefore(leafCert
),
2367 SecCertificateNotValidAfter(leafCert
),
2368 0, "M", &_lifetime
);
2369 lifetime
= _lifetime
;
2372 unsigned requiredEmbeddedSctsCount
;
2374 if (lifetime
< 15) {
2375 requiredEmbeddedSctsCount
= 2;
2376 } else if (lifetime
<= 27) {
2377 requiredEmbeddedSctsCount
= 3;
2378 } else if (lifetime
<= 39) {
2379 requiredEmbeddedSctsCount
= 4;
2381 requiredEmbeddedSctsCount
= 5;
2384 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
2390 CFReleaseSafe(logsValidatingEmbeddedScts
);
2391 CFReleaseSafe(currentLogsValidatingScts
);
2392 CFReleaseSafe(builderScts
);
2393 CFReleaseSafe(embeddedScts
);
2394 CFReleaseSafe(ocspScts
);
2395 CFReleaseSafe(precertEntry
);
2396 CFReleaseSafe(trustedLogs
);
2397 CFReleaseSafe(x509Entry
);
2400 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
2401 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2403 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
2404 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
2406 for (ix
= 0; ix
< count
; ix
++) {
2407 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2408 policy_set_t policies
= policies_for_cert(cert
);
2410 if (policy_set_contains(policies
, &key_value
)) {
2417 static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc
, CFStringRef key
)
2419 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2420 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2421 bool result
= false;
2423 if (CFGetTypeID(value
) == CFDataGetTypeID())
2425 result
= checkPolicyOidData(pvc
, value
);
2426 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
2427 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
2429 result
= checkPolicyOidData(pvc
, dataOid
);
2434 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2439 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2441 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2442 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2443 if (isString(value
)) {
2444 SecPVCSetCheckRevocation(pvc
, value
);
2448 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2450 SecPVCSetCheckRevocationResponseRequired(pvc
);
2453 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2455 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2456 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2457 if (value
== kCFBooleanTrue
) {
2458 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2460 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
2464 static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc
,
2466 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2467 for (ix
= 1; ix
< count
- 1; ++ix
) {
2468 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2469 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2470 /* Intermediate certificate has a weak key. */
2471 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2477 static void SecPolicyCheckWeakLeaf(SecPVCRef pvc
,
2479 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2480 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2481 /* Leaf certificate has a weak key. */
2482 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
2487 static void SecPolicyCheckWeakRoot(SecPVCRef pvc
,
2489 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2491 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2492 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2493 /* Root certificate has a weak key. */
2494 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2499 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
2500 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2501 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2502 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
2503 for (ix
= 0; ix
< count
; ++ix
) {
2504 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2505 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
2506 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2512 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
2514 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2515 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2516 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
2517 for (ix
= 0; ix
< count
; ++ix
) {
2518 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2519 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
2520 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2526 #define ENABLE_CRLS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
2530 /********************************************************
2531 ****************** SecRVCRef Functions *****************
2532 ********************************************************/
2533 typedef struct OpaqueSecORVC
*SecORVCRef
;
2535 typedef struct OpaqueSecCRVC
*SecCRVCRef
;
2538 /* Revocation verification context. */
2539 struct OpaqueSecRVC
{
2540 /* Pointer to the pvc for this revocation check */
2543 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2546 /* The OCSP Revocation verification context */
2555 typedef struct OpaqueSecRVC
*SecRVCRef
;
2558 /********************************************************
2559 ****************** OCSP RVC Functions ******************
2560 ********************************************************/
2561 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
2562 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
2564 /* OCSP Revocation verification context. */
2565 struct OpaqueSecORVC
{
2566 /* Will contain the response data. */
2569 /* Pointer to the pvc for this revocation check. */
2572 /* Pointer to the generic rvc for this revocation check */
2575 /* The ocsp request we send to each responder. */
2576 SecOCSPRequestRef ocspRequest
;
2578 /* The freshest response we received so far, from stapling or cache or responder. */
2579 SecOCSPResponseRef ocspResponse
;
2581 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
2582 SecOCSPSingleResponseRef ocspSingleResponse
;
2584 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2587 /* Index in array returned by SecCertificateGetOCSPResponders() for current
2589 CFIndex responderIX
;
2591 /* URL of current responder. */
2594 /* Date until which this revocation status is valid. */
2595 CFAbsoluteTime nextUpdate
;
2600 static void SecORVCFinish(SecORVCRef orvc
) {
2601 secdebug("alloc", "%p", orvc
);
2602 asynchttp_free(&orvc
->http
);
2603 if (orvc
->ocspRequest
) {
2604 SecOCSPRequestFinalize(orvc
->ocspRequest
);
2605 orvc
->ocspRequest
= NULL
;
2607 if (orvc
->ocspResponse
) {
2608 SecOCSPResponseFinalize(orvc
->ocspResponse
);
2609 orvc
->ocspResponse
= NULL
;
2610 if (orvc
->ocspSingleResponse
) {
2611 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
2612 orvc
->ocspSingleResponse
= NULL
;
2617 #define MAX_OCSP_RESPONDERS 3
2618 #define OCSP_REQUEST_THRESHOLD 10
2620 /* Return the next responder we should contact for this rvc or NULL if we
2621 exhausted them all. */
2622 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
2623 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2624 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2625 if (ocspResponders
) {
2626 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2627 if (responderCount
>= OCSP_REQUEST_THRESHOLD
) {
2628 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount
);
2631 while (rvc
->responderIX
< responderCount
&& rvc
->responderIX
< MAX_OCSP_RESPONDERS
) {
2632 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2634 CFStringRef scheme
= CFURLCopyScheme(responder
);
2636 /* We only support http and https responders currently. */
2637 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2638 CFEqual(CFSTR("https"), scheme
));
2640 if (valid_responder
)
2648 /* Fire off an async http request for this certs revocation status, return
2649 false if request was queued, true if we're done. */
2650 static bool SecORVCFetchNext(SecORVCRef rvc
) {
2651 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
2652 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2656 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2657 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
2658 /* Async request was posted, wait for reply. */
2668 /* Process a verified ocsp response for a given cert. Return true if the
2669 certificate status was obtained. */
2670 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2673 switch (this->certStatus
) {
2675 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2676 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2677 in the info dictionary. */
2678 //cert.revokeCheckGood(true);
2679 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2683 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2684 /* @@@ Mark cert as revoked (with reason) at revocation date in
2685 the info dictionary, or perhaps we should use a different key per
2686 reason? That way a client using exceptions can ignore some but
2688 SInt32 reason
= this->crlReason
;
2689 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2690 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2692 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2693 /* make the revocation reason available in the trust result */
2694 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2696 CFRelease(cfreason
);
2700 /* not an error, no per-cert status, nothing here */
2701 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2705 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2706 (int)this->certStatus
, rvc
->certIX
);
2714 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
2715 if (rvc
->ocspSingleResponse
) {
2716 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2718 if (rvc
->ocspResponse
) {
2719 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2723 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
2726 SecOCSPEvaluateCompleted(const void *userData
,
2727 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
2728 SecTrustResultType result
) {
2729 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
2731 Block_release(evaluated
);
2735 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
2736 __block
bool evaluated
= false;
2737 bool trusted
= false;
2738 if (!signers
|| !issuers
) {
2742 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
2743 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2744 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2745 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2746 CFRelease(ocspSigner
);
2748 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
2749 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
2754 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
2755 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
2756 signers
, issuers
, true, false,
2757 policies
, NULL
, NULL
, NULL
,
2759 SecOCSPEvaluateCompleted
, completed
);
2760 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
2761 SecPathBuilderStep(oBuilder
);
2762 CFReleaseNull(clientAuditToken
);
2763 CFReleaseNull(policies
);
2765 /* verify the public key of the issuer signed the OCSP signer */
2767 SecCertificateRef issuer
= NULL
, signer
= NULL
;
2768 SecKeyRef issuerPubKey
= NULL
;
2770 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
2771 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
2774 #if TARGET_OS_IPHONE
2775 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
2777 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
2780 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
2783 secnotice("ocsp", "ocsp signer cert not signed by issuer");
2785 CFReleaseNull(issuerPubKey
);
2791 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
2793 SecCertificatePathRef issuers
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2794 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathGetCertificateAtIndex(issuers
, 0)) : NULL
;
2795 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
2796 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2798 if (signer
&& signers
) {
2799 if (issuer
&& CFEqual(signer
, issuer
)) {
2800 /* We already know we trust issuer since it's the issuer of the
2801 * cert we are verifying. */
2802 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
2806 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
2808 CFMutableArrayRef signerCerts
= NULL
;
2809 CFArrayRef issuerCerts
= NULL
;
2811 /* Ensure the signer cert is the 0th cert for trust evaluation */
2812 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2813 CFArrayAppendValue(signerCerts
, signer
);
2814 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
2817 issuerCerts
= SecCertificatePathCopyCertificates(issuers
, NULL
);
2820 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
2821 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
2825 /* @@@ We don't trust the cert so don't use this response. */
2826 secnotice("ocsp", "ocsp response signed by certificate which "
2827 "does not satisfy ocspSigner policy");
2830 CFReleaseNull(signerCerts
);
2831 CFReleaseNull(issuerCerts
);
2834 /* @@@ No signer found for this ocsp response, discard it. */
2835 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
2840 #if DUMP_OCSPRESPONSES
2842 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
2843 rvc
->certIX
, (trusted
? "t" : "u"));
2844 secdumpdata(ocspResponse
->data
, buf
);
2846 CFReleaseNull(issuers
);
2847 CFReleaseNull(issuer
);
2848 CFReleaseNull(signers
);
2849 CFReleaseNull(signer
);
2853 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
2854 SecOCSPSingleResponseRef sr
= NULL
;
2855 require_quiet(ocspResponse
, errOut
);
2856 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
2857 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
2858 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
2859 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
2860 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
2861 // Check if this response is fresher than any (cached) response we might still have in the rvc.
2862 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
2864 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
2865 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
2866 check whether the leaf was revoked (we are already checking the rest of
2868 /* Check the OCSP response signature and verify the response. */
2869 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
2870 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
2872 // If we get here, we have a properly signed ocsp response
2873 // but we haven't checked dates yet.
2875 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
2876 if (sr
->certStatus
== CS_Good
) {
2877 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
2878 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
2879 } else if (sr
->certStatus
== CS_Revoked
) {
2880 // Expire revoked responses when the subject certificate itself expires.
2881 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
2884 // Ok we like the new response, let's toss the old one.
2886 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
2888 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
2889 rvc
->ocspResponse
= ocspResponse
;
2890 ocspResponse
= NULL
;
2892 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2893 rvc
->ocspSingleResponse
= sr
;
2896 rvc
->done
= sr_valid
;
2899 if (sr
) SecOCSPSingleResponseDestroy(sr
);
2900 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2903 /* Callback from async http code after an ocsp response has been received. */
2904 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
2905 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
2906 SecPVCRef pvc
= rvc
->pvc
;
2907 SecOCSPResponseRef ocspResponse
= NULL
;
2908 if (http
->response
) {
2909 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
2911 /* Parse the returned data as if it's an ocspResponse. */
2912 ocspResponse
= SecOCSPResponseCreate(data
);
2917 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
2918 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
2921 /* Clear the data for the next response. */
2922 asynchttp_free(http
);
2923 SecORVCFetchNext(rvc
);
2927 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
2928 SecORVCUpdatePVC(rvc
);
2930 if (!--pvc
->asyncJobCount
) {
2931 secdebug("rvc", "done with all async jobs");
2932 SecPathBuilderStep(pvc
->builder
);
2937 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
2938 SecORVCRef orvc
= NULL
;
2939 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
2941 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
2944 orvc
->certIX
= certIX
;
2945 orvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
2946 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2947 orvc
->http
.completed
= SecOCSPFetchCompleted
;
2948 orvc
->http
.info
= orvc
;
2949 orvc
->ocspRequest
= NULL
;
2950 orvc
->responderIX
= 0;
2951 orvc
->responder
= NULL
;
2952 orvc
->nextUpdate
= NULL_TIME
;
2953 orvc
->ocspResponse
= NULL
;
2954 orvc
->ocspSingleResponse
= NULL
;
2957 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2958 /* The certIX + 1 is ok here since certCount is always at least 1
2959 less than the actual number of certs in SecPVCCheckRevocation. */
2960 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(pvc
, certIX
+ 1);
2961 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
2966 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
2967 /* Get stapled OCSP responses */
2968 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->pvc
->builder
);
2970 if(ocspResponsesData
) {
2971 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
2972 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2973 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2974 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
2976 CFRelease(ocspResponsesData
);
2981 /********************************************************
2982 ******************* CRL RVC Functions ******************
2983 ********************************************************/
2985 #include <../trustd/SecTrustOSXEntryPoints.h>
2986 OSStatus errSecCertificateRevoked
= -67820;
2987 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
2989 /* CRL Revocation verification context. */
2990 struct OpaqueSecCRVC
{
2991 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
2992 async_ocspd_t async_ocspd
;
2994 /* Pointer to the pvc for this revocation check. */
2997 /* Pointer to the generic rvc for this revocation check */
3000 /* The current CRL status from ocspd. */
3003 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
3006 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
3007 current distribution point. */
3008 CFIndex distributionPointIX
;
3010 /* URL of current distribution point. */
3011 CFURLRef distributionPoint
;
3013 /* Date until which this revocation status is valid. */
3014 CFAbsoluteTime nextUpdate
;
3019 static void SecCRVCFinish(SecCRVCRef crvc
) {
3023 #define MAX_CRL_DPS 3
3024 #define CRL_REQUEST_THRESHOLD 10
3026 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
3027 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3028 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
3030 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
3031 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
3032 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
3035 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
3036 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
3037 rvc
->distributionPointIX
++;
3038 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
3040 /* We only support http and https responders currently. */
3041 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
3042 CFEqual(CFSTR("https"), scheme
) ||
3043 CFEqual(CFSTR("ldap"), scheme
));
3046 return distributionPoint
;
3053 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
3054 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3055 SecCertificatePathRef path
= rvc
->pvc
->path
;
3056 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3057 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
3058 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
3059 CFReleaseNull(serializedCertPath
);
3060 /* we got a response indicating that the CRL was checked */
3061 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
3063 /* ocspd doesn't give us the nextUpdate time, so set to default */
3064 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3068 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
3069 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3070 SecCRVCGetCRLStatus(rvc
);
3071 if (rvc
->status
== errSecCertificateRevoked
) {
3077 /* Fire off an async http request for this certs revocation status, return
3078 false if request was queued, true if we're done. */
3079 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
3080 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3081 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3082 SecCertificatePathRef path
= rvc
->pvc
->path
;
3083 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3084 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
3085 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
3086 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
3087 CFDataRef clientAuditToken
= NULL
;
3088 SecTaskRef task
= NULL
;
3089 audit_token_t auditToken
= {};
3090 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
3091 require(clientAuditToken
, out
);
3092 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3093 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3094 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3095 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
3096 rvc
->distributionPoint
, task
);
3099 CFReleaseNull(clientAuditToken
);
3100 CFReleaseNull(task
);
3101 /* Async request was posted, wait for reply. */
3109 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
3110 if (rvc
->status
== errSecCertificateRevoked
) {
3111 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
3112 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
3113 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
3114 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
3116 if (rvc
->pvc
&& rvc
->pvc
->info
) {
3117 /* make the revocation reason available in the trust result */
3118 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
3120 CFReleaseNull(cfreason
);
3124 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
3125 SecCRVCRef rvc
= ocspd
->info
;
3126 SecPVCRef pvc
= rvc
->pvc
;
3127 /* we got a response indicating that the CRL was checked */
3128 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
3129 rvc
->status
= ocspd
->response
;
3131 /* ocspd doesn't give us the nextUpdate time, so set to default */
3132 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3133 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
3134 SecCRVCUpdatePVC(rvc
);
3136 if (!--pvc
->asyncJobCount
) {
3137 secdebug("rvc", "done with all async jobs");
3138 SecPathBuilderStep(pvc
->builder
);
3141 if(SecCRVCFetchNext(rvc
)) {
3142 if (!--pvc
->asyncJobCount
) {
3143 secdebug("rvc", "done with all async jobs");
3144 SecPathBuilderStep(pvc
->builder
);
3150 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3151 SecCRVCRef crvc
= NULL
;
3152 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
3154 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
3157 crvc
->certIX
= certIX
;
3158 crvc
->status
= errSecInternal
;
3159 crvc
->distributionPointIX
= 0;
3160 crvc
->distributionPoint
= NULL
;
3161 crvc
->nextUpdate
= NULL_TIME
;
3162 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
3163 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
3164 crvc
->async_ocspd
.response
= errSecInternal
;
3165 crvc
->async_ocspd
.info
= crvc
;
3171 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
3172 if (rvc
->pvc
->check_revocation
&&
3173 CFEqual(kSecPolicyCheckRevocationCRL
, rvc
->pvc
->check_revocation
)) {
3174 /* Our client insists on CRLs */
3175 secinfo("rvc", "client told us to check CRL");
3178 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3179 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3180 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
3181 (rvc
->pvc
->check_revocation
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, rvc
->pvc
->check_revocation
))) {
3182 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
3183 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
3184 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
3189 #endif /* ENABLE_CRLS */
3191 static void SecRVCFinish(SecRVCRef rvc
) {
3193 SecORVCFinish(rvc
->orvc
);
3197 SecCRVCFinish(rvc
->crvc
);
3202 static void SecRVCDelete(SecRVCRef rvc
) {
3204 SecORVCFinish(rvc
->orvc
);
3209 SecCRVCFinish(rvc
->crvc
);
3215 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3216 secdebug("alloc", "%p", rvc
);
3218 rvc
->certIX
= certIX
;
3219 rvc
->orvc
= SecORVCCreate(rvc
, pvc
, certIX
);
3221 rvc
->crvc
= SecCRVCCreate(rvc
, pvc
, certIX
);
3226 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
3227 SecORVCUpdatePVC(rvc
->orvc
);
3229 SecCRVCUpdatePVC(rvc
->crvc
);
3234 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3235 if (!rvc
->pvc
->check_revocation
3236 || !CFEqual(rvc
->pvc
->check_revocation
, kSecPolicyCheckRevocationCRL
)) {
3242 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3247 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
3248 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
3249 if (SecRVCShouldCheckOCSP(rvc
)) {
3250 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
3251 SecORVCConsumeOCSPResponse(rvc
->orvc
,
3252 SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
),
3256 /* Don't check CRL cache if policy requested OCSP only */
3257 if (SecRVCShouldCheckCRL(rvc
)) {
3258 SecCRVCCheckRevocationCache(rvc
->crvc
);
3263 static bool SecRVCFetchNext(SecRVCRef rvc
) {
3264 bool OCSP_fetch_finished
= true;
3265 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
3266 if (SecRVCShouldCheckOCSP(rvc
)) {
3267 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
3269 if (OCSP_fetch_finished
) {
3270 /* we didn't start an OCSP background job for this cert */
3271 rvc
->pvc
->asyncJobCount
--;
3275 bool CRL_fetch_finished
= true;
3276 /* Don't check CRL cache if policy requested OCSP only */
3277 if (SecRVCShouldCheckCRL(rvc
)) {
3278 /* reset the distributionPointIX because we already iterated through the CRLDPs
3279 * in SecCRVCCheckRevocationCache */
3280 rvc
->crvc
->distributionPointIX
= 0;
3281 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
3283 if (CRL_fetch_finished
) {
3284 /* we didn't start a CRL background job for this cert */
3285 rvc
->pvc
->asyncJobCount
--;
3287 OCSP_fetch_finished
&= CRL_fetch_finished
;
3290 return OCSP_fetch_finished
;
3293 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
3294 secdebug("rvc", "checking revocation");
3295 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3296 bool completed
= true;
3297 if (certCount
<= 1) {
3298 /* Can't verify without an issuer; we're done */
3303 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
3304 * We can't check revocation for the final cert in the chain.
3309 /* We have done revocation checking already, we're done. */
3310 secdebug("rvc", "Not rechecking revocation");
3314 /* Setup things so we check revocation status of all certs except the
3316 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
3318 /* Note that if we are multi threaded and a job completes after it
3319 is started but before we return from this function, we don't want
3320 a callback to decrement asyncJobCount to zero before we finish issuing
3321 all the jobs. To avoid this we pretend we issued certCount async jobs,
3322 and decrement pvc->asyncJobCount for each cert that we don't start a
3323 background fetch for. */
3325 pvc
->asyncJobCount
= (unsigned int) certCount
;
3327 /* If we enable CRLS, we may end up with two async jobs per cert: one
3328 * for OCSP and one for fetching the CRL */
3329 pvc
->asyncJobCount
= 2 * (unsigned int)certCount
;
3331 secdebug("rvc", "set asyncJobCount to %d", pvc
->asyncJobCount
);
3333 /* Loop though certificates again and issue an ocsp fetch if the
3334 revocation status checking isn't done yet. */
3335 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3336 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
3337 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3338 SecRVCInit(rvc
, pvc
, certIX
);
3342 /* Ignore stapled OCSP responses only if CRLs are enabled and the
3343 * policy specifically requested CRLs only. */
3344 if (SecRVCShouldCheckOCSP(rvc
)) {
3345 /* If we have any OCSP stapled responses, check those first */
3346 SecORVCProcessStapledResponses(rvc
->orvc
);
3349 #if TARGET_OS_BRIDGE
3350 /* The bridge has no writeable storage and no network. Nothing else we can
3356 /* Then check the caches for revocation results. */
3357 SecRVCCheckRevocationCaches(rvc
);
3359 /* The check is done if we found cached responses from either method. */
3365 secdebug("rvc", "found cached response for cert: %ld", certIX
);
3369 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
3370 async http request for this cert's revocation status, unless we already successfully checked
3371 the revocation status of this cert based on the cache or stapled responses, */
3372 bool allow_fetch
= SecPathBuilderCanAccessNetwork(pvc
->builder
) && (pvc
->is_ev
|| pvc
->check_revocation
);
3373 bool fetch_done
= true;
3374 if (rvc
->done
|| !allow_fetch
) {
3375 /* We got a cache hit or we aren't allowed to access the network */
3376 SecRVCUpdatePVC(rvc
);
3378 /* We didn't really start any background jobs for this cert. */
3379 pvc
->asyncJobCount
--;
3381 pvc
->asyncJobCount
--;
3383 secdebug("rvc", "not fetching and job count is %d for cert %ld", pvc
->asyncJobCount
, certIX
);
3385 fetch_done
= SecRVCFetchNext(rvc
);
3388 /* We started at least one background fetch. */
3389 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
3394 /* Return false if we started any background jobs. */
3395 /* We can't just return !pvc->asyncJobCount here, since if we started any
3396 jobs the completion callback will be called eventually and it will call
3397 SecPathBuilderStep(). If for some reason everything completed before we
3398 get here we still want the outer SecPathBuilderStep() to terminate so we
3399 keep track of whether we started any jobs and return false if so. */
3403 static CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
3404 CFAbsoluteTime enu
= NULL_TIME
;
3405 enu
= rvc
->orvc
->nextUpdate
;
3407 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
3408 if (enu
== NULL_TIME
||
3409 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
3410 /* We didn't check OCSP or CRL next update time was sooner */
3411 enu
= crlNextUpdate
;
3418 void SecPolicyServerInitalize(void) {
3419 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3420 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3421 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3422 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3424 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3425 kSecPolicyCheckBasicCertificateProcessing
,
3426 SecPolicyCheckBasicCertificateProcessing
);
3427 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3428 kSecPolicyCheckCriticalExtensions
,
3429 SecPolicyCheckCriticalExtensions
);
3430 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3431 kSecPolicyCheckIdLinkage
,
3432 SecPolicyCheckIdLinkage
);
3433 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3434 kSecPolicyCheckKeyUsage
,
3435 SecPolicyCheckKeyUsage
);
3436 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3437 kSecPolicyCheckExtendedKeyUsage
,
3438 SecPolicyCheckExtendedKeyUsage
);
3439 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3440 kSecPolicyCheckBasicConstraints
,
3441 SecPolicyCheckBasicConstraints
);
3442 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3443 kSecPolicyCheckNonEmptySubject
,
3444 SecPolicyCheckNonEmptySubject
);
3445 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3446 kSecPolicyCheckQualifiedCertStatements
,
3447 SecPolicyCheckQualifiedCertStatements
);
3448 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3449 kSecPolicyCheckSSLHostname
,
3450 SecPolicyCheckSSLHostname
);
3451 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3452 kSecPolicyCheckEmail
,
3453 SecPolicyCheckEmail
);
3454 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3455 kSecPolicyCheckValidIntermediates
,
3456 SecPolicyCheckValidIntermediates
);
3457 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3458 kSecPolicyCheckValidLeaf
,
3459 SecPolicyCheckValidLeaf
);
3460 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3461 kSecPolicyCheckValidRoot
,
3462 SecPolicyCheckValidRoot
);
3463 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3464 kSecPolicyCheckIssuerCommonName
,
3465 SecPolicyCheckIssuerCommonName
);
3466 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3467 kSecPolicyCheckSubjectCommonNamePrefix
,
3468 SecPolicyCheckSubjectCommonNamePrefix
);
3469 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3470 kSecPolicyCheckSubjectCommonName
,
3471 SecPolicyCheckSubjectCommonName
);
3472 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3473 kSecPolicyCheckNotValidBefore
,
3474 SecPolicyCheckNotValidBefore
);
3475 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3476 kSecPolicyCheckChainLength
,
3477 SecPolicyCheckChainLength
);
3478 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3479 kSecPolicyCheckAnchorSHA1
,
3480 SecPolicyCheckAnchorSHA1
);
3481 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3482 kSecPolicyCheckAnchorSHA256
,
3483 SecPolicyCheckAnchorSHA256
);
3484 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3485 kSecPolicyCheckAnchorApple
,
3486 SecPolicyCheckAnchorApple
);
3487 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3488 kSecPolicyCheckSubjectOrganization
,
3489 SecPolicyCheckSubjectOrganization
);
3490 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3491 kSecPolicyCheckSubjectOrganizationalUnit
,
3492 SecPolicyCheckSubjectOrganizationalUnit
);
3493 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3494 kSecPolicyCheckEAPTrustedServerNames
,
3495 SecPolicyCheckEAPTrustedServerNames
);
3496 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3497 kSecPolicyCheckSubjectCommonNameTEST
,
3498 SecPolicyCheckSubjectCommonNameTEST
);
3499 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3500 kSecPolicyCheckRevocation
,
3501 SecPolicyCheckRevocation
);
3502 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3503 kSecPolicyCheckRevocationResponseRequired
,
3504 SecPolicyCheckRevocationResponseRequired
);
3505 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3506 kSecPolicyCheckNoNetworkAccess
,
3507 SecPolicyCheckNoNetworkAccess
);
3508 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3509 kSecPolicyCheckBlackListedLeaf
,
3510 SecPolicyCheckBlackListedLeaf
);
3511 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3512 kSecPolicyCheckGrayListedLeaf
,
3513 SecPolicyCheckGrayListedLeaf
);
3514 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3515 kSecPolicyCheckLeafMarkerOid
,
3516 SecPolicyCheckLeafMarkerOid
);
3517 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3518 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
3519 SecPolicyCheckLeafMarkerOidWithoutValueCheck
);
3520 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3521 kSecPolicyCheckIntermediateSPKISHA256
,
3522 SecPolicyCheckIntermediateSPKISHA256
);
3523 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3524 kSecPolicyCheckIntermediateEKU
,
3525 SecPolicyCheckIntermediateEKU
);
3526 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3527 kSecPolicyCheckIntermediateMarkerOid
,
3528 SecPolicyCheckIntermediateMarkerOid
);
3529 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3530 kSecPolicyCheckCertificatePolicy
,
3531 SecPolicyCheckCertificatePolicyOid
);
3532 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3533 kSecPolicyCheckWeakIntermediates
,
3534 SecPolicyCheckWeakIntermediates
);
3535 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3536 kSecPolicyCheckWeakLeaf
,
3537 SecPolicyCheckWeakLeaf
);
3538 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3539 kSecPolicyCheckWeakRoot
,
3540 SecPolicyCheckWeakRoot
);
3541 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3542 kSecPolicyCheckKeySize
,
3543 SecPolicyCheckKeySize
);
3544 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3545 kSecPolicyCheckSignatureHashAlgorithms
,
3546 SecPolicyCheckSignatureHashAlgorithms
);
3551 /********************************************************
3552 ****************** SecPVCRef Functions *****************
3553 ********************************************************/
3555 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3556 CFAbsoluteTime verifyTime
) {
3557 secdebug("alloc", "%p", pvc
);
3558 // Weird logging policies crashes.
3559 //secdebug("policy", "%@", policies);
3561 // Zero the pvc struct so only non-zero fields need to be explicitly set
3562 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
3563 pvc
->builder
= builder
;
3564 pvc
->policies
= policies
;
3567 pvc
->verifyTime
= verifyTime
;
3571 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3572 secdebug("alloc", "%p", pvc
);
3574 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
) - 1;
3575 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3576 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3584 void SecPVCDelete(SecPVCRef pvc
) {
3585 secdebug("alloc", "%p", pvc
);
3586 CFReleaseNull(pvc
->policies
);
3587 CFReleaseNull(pvc
->details
);
3588 CFReleaseNull(pvc
->info
);
3589 if (pvc
->valid_policy_tree
) {
3590 policy_tree_prune(&pvc
->valid_policy_tree
);
3592 SecPVCDeleteRVCs(pvc
);
3593 CFReleaseNull(pvc
->path
);
3596 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3597 CF_CONSUMED CFArrayRef details
) {
3598 secdebug("policy", "%@", path
);
3599 bool samePath
= ((!path
&& !pvc
->path
) || (path
&& pvc
->path
&& CFEqual(path
, pvc
->path
)));
3601 /* Changing path makes us clear the Revocation Verification Contexts */
3602 SecPVCDeleteRVCs(pvc
);
3603 CFReleaseSafe(pvc
->path
);
3604 pvc
->path
= CFRetainSafe(path
);
3606 pvc
->details
= details
;
3607 CFReleaseNull(pvc
->info
);
3608 if (pvc
->valid_policy_tree
) {
3609 policy_tree_prune(&pvc
->valid_policy_tree
);
3613 /* Since we don't run the LeafChecks again, we need to preserve the
3614 * result the leaf had. */
3615 pvc
->result
= (details
) ? (CFDictionaryGetCount(CFArrayGetValueAtIndex(details
, 0)) == 0)
3619 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3620 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3623 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3624 return SecCertificatePathGetCount(pvc
->path
);
3627 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3628 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3631 bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc
, CFIndex ix
) {
3632 return SecCertificatePathSelfSignedIndex(pvc
->path
) == ix
;
3635 void SecPVCSetCheckRevocation(SecPVCRef pvc
, CFStringRef method
) {
3636 pvc
->check_revocation
= method
;
3637 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
3640 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3641 pvc
->response_required
= true;
3642 secdebug("rvc", "revocation response required");
3645 bool SecPVCIsAnchored(SecPVCRef pvc
) {
3646 return SecCertificatePathIsAnchored(pvc
->path
);
3649 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
3650 return pvc
->verifyTime
;
3653 static int32_t detailKeyToCssmErr(CFStringRef key
) {
3656 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
3657 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
3659 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
3660 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
3662 else if (CFEqual(key
, kSecPolicyCheckValidLeaf
) ||
3663 CFEqual(key
, kSecPolicyCheckValidIntermediates
) ||
3664 CFEqual(key
, kSecPolicyCheckValidRoot
)) {
3665 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
3671 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
3673 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
3674 bool result
= false;
3675 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, ix
);
3676 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3677 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3679 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3680 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3681 CFNumberRef allowedErrorNumber
= NULL
;
3682 if (!isDictionary(constraint
)) {
3685 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
3686 int32_t allowedErrorValue
= 0;
3687 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
3691 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
3692 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
3701 /* AUDIT[securityd](done):
3702 policy->_options is a caller provided dictionary, only its cf type has
3705 bool SecPVCSetResultForced(SecPVCRef pvc
,
3706 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
3708 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
3709 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
3710 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
3712 (force
? "force" : ""), result
);
3714 /* If this is not something the current policy cares about ignore
3715 this error and return true so our caller continues evaluation. */
3717 /* @@@ The right long term fix might be to check if none of the passed
3718 in policies contain this key, since not all checks are run for all
3720 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3721 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
3725 /* Check to see if the SecTrustSettings for the certificate in question
3726 tell us to ignore this error. */
3727 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
3728 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
3732 pvc
->result
= false;
3736 CFMutableDictionaryRef detail
=
3737 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
3739 /* Perhaps detail should have an array of results per key? As it stands
3740 in the case of multiple policy failures the last failure stands. */
3741 CFDictionarySetValue(detail
, key
, result
);
3746 bool SecPVCSetResult(SecPVCRef pvc
,
3747 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
3748 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
3751 /* AUDIT[securityd](done):
3752 key(ok) is a caller provided.
3753 value(ok, unused) is a caller provided.
3755 static void SecPVCValidateKey(const void *key
, const void *value
,
3757 SecPVCRef pvc
= (SecPVCRef
)context
;
3759 /* If our caller doesn't want full details and we failed earlier there is
3760 no point in doing additional checks. */
3761 if (!pvc
->result
&& !pvc
->details
)
3764 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
3765 CFDictionaryGetValue(pvc
->callbacks
, key
);
3769 /* Why not to have optional policy checks rant:
3770 Not all keys are in all dictionaries anymore, so why not make checks
3771 optional? This way a client can ask for something and the server will
3772 do a best effort based on the supported flags. It works since they are
3773 synchronized now, but we need some debug checking here for now. */
3774 pvc
->result
= false;
3776 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
3777 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
3778 pvc
->result
= false;
3780 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3781 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3782 pvc
->result
= false;
3785 /* Non standard validation phase, nothing is optional. */
3786 pvc
->result
= false;
3791 fcn(pvc
, (CFStringRef
)key
);
3794 /* AUDIT[securityd](done):
3795 policy->_options is a caller provided dictionary, only its cf type has
3798 bool SecPVCLeafChecks(SecPVCRef pvc
) {
3800 CFArrayRef policies
= pvc
->policies
;
3801 CFIndex ix
, count
= CFArrayGetCount(policies
);
3802 for (ix
= 0; ix
< count
; ++ix
) {
3803 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3805 /* Validate all keys for all policies. */
3806 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3807 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3808 if (!pvc
->result
&& !pvc
->details
)
3815 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3816 /* Check stuff common to intermediate and anchors. */
3817 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3818 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3819 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3820 && SecPVCIsAnchored(pvc
));
3821 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3822 /* Certificate has expired. */
3823 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
3824 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
3828 if (SecCertificateIsWeakKey(cert
)) {
3829 /* Certificate uses weak key. */
3830 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckWeakRoot
3831 : kSecPolicyCheckWeakIntermediates
, ix
, kCFBooleanFalse
))
3836 /* Perform anchor specific checks. */
3837 /* Don't think we have any of these. */
3839 /* Perform intermediate specific checks. */
3841 /* (k) Basic constraints only relevant for v3 and later. */
3842 if (SecCertificateVersion(cert
) >= 3) {
3843 const SecCEBasicConstraints
*bc
=
3844 SecCertificateGetBasicConstraints(cert
);
3845 if (!bc
|| !bc
->isCA
) {
3846 /* Basic constraints not present or not marked as isCA, illegal. */
3847 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3848 ix
, kCFBooleanFalse
, true))
3852 /* (l) max_path_length is checked elsewhere. */
3854 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3855 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3856 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3857 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3858 ix
, kCFBooleanFalse
, true))
3867 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3868 /* Check stuff common to intermediate and anchors. */
3870 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3871 if (NULL
!= otapkiRef
)
3873 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3874 CFRelease(otapkiRef
);
3875 if (NULL
!= blackListedKeys
)
3877 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3878 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3879 bool is_last
= (ix
== count
- 1);
3880 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
3882 /* Check for blacklisted intermediate issuer keys. */
3883 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3885 /* Check dgst against blacklist. */
3886 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3887 /* Check allow list for this blacklisted issuer key,
3888 which is the authority key of the issued cert at ix-1.
3889 If ix is the last cert, the root is missing, so we
3890 also check our own authority key in that case.
3892 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
3893 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
3895 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3896 ix
, kCFBooleanFalse
, true);
3898 pvc
->is_allowlisted
= allowed
;
3903 CFRelease(blackListedKeys
);
3911 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3913 /* Check stuff common to intermediate and anchors. */
3914 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3915 if (NULL
!= otapkiRef
)
3917 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3918 CFRelease(otapkiRef
);
3919 if (NULL
!= grayListKeys
)
3921 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3922 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3923 bool is_last
= (ix
== count
- 1);
3924 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
3926 /* Check for gray listed intermediate issuer keys. */
3927 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3929 /* Check dgst against gray list. */
3930 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3931 /* Check allow list for this graylisted issuer key,
3932 which is the authority key of the issued cert at ix-1.
3933 If ix is the last cert, the root is missing, so we
3934 also check our own authority key in that case.
3936 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
3937 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
3939 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3940 ix
, kCFBooleanFalse
, true);
3942 pvc
->is_allowlisted
= allowed
;
3947 CFRelease(grayListKeys
);
3955 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
3956 if (!isString(searchName
) && !isString(searchOid
)) {
3959 CFArrayRef policies
= pvc
->policies
;
3960 CFIndex ix
, count
= CFArrayGetCount(policies
);
3961 for (ix
= 0; ix
< count
; ++ix
) {
3962 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3963 CFStringRef policyName
= SecPolicyGetName(policy
);
3964 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
3965 /* Prefer a match of both name and OID */
3966 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
3967 if (CFEqual(searchOid
, policyOid
) &&
3968 CFEqual(searchName
, policyName
)) {
3969 if (policyIX
) { *policyIX
= ix
; }
3973 /* Next best is just OID. */
3974 if (!searchName
&& searchOid
&& policyOid
) {
3975 if (CFEqual(searchOid
, policyOid
)) {
3976 if (policyIX
) { *policyIX
= ix
; }
3980 if (!searchOid
&& searchName
&& policyName
) {
3981 if (CFEqual(searchName
, policyName
)) {
3982 if (policyIX
) { *policyIX
= ix
; }
3990 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
3991 if (!isString(stringValue
)) {
3994 bool result
= false;
3996 CFStringRef tmpStringValue
= NULL
;
3997 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
3998 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
4000 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
4002 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4003 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4004 /* Have to look for all the possible locations of name string */
4005 CFStringRef policyString
= NULL
;
4006 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
4007 if (!policyString
) {
4008 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
4010 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
4015 CFArrayRef policyStrings
= NULL
;
4016 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
4017 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
4018 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
4026 CFReleaseNull(tmpStringValue
);
4031 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
4032 uint32_t ourTSKeyUsage
= 0;
4033 uint32_t keyUsage
= 0;
4034 if (keyUsageNumber
&&
4035 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
4036 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
4037 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
4039 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
4040 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
4042 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
4043 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
4045 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
4046 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
4048 if (keyUsage
== kSecKeyUsageAll
) {
4049 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
4052 return ourTSKeyUsage
;
4055 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
4056 uint32_t ourTSKeyUsage
= 0;
4057 CFTypeRef policyKeyUsageType
= NULL
;
4059 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
4060 if (isArray(policyKeyUsageType
)) {
4061 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
4062 for (ix
= 0; ix
< count
; ix
++) {
4063 CFNumberRef policyKeyUsageNumber
= NULL
;
4064 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
4065 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
4067 } else if (isNumber(policyKeyUsageType
)) {
4068 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
4071 return ourTSKeyUsage
;
4074 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
4075 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
4076 int64_t keyUsageValue
= 0;
4077 uint32_t ourKeyUsage
= 0;
4079 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
4083 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
4087 /* We're using the key for revocation if we have the OCSPSigner policy.
4088 * @@@ If we support CRLs, we'd need to check for that policy here too.
4090 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
4091 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
4094 /* We're using the key for verifying a cert if it's a root/intermediate
4095 * in the chain. If the cert isn't in the path yet, we're about to add it,
4096 * so it's a root/intermediate. If there is no path, this is the leaf.
4098 CFIndex pathIndex
= -1;
4100 pathIndex
= SecCertificatePathGetIndexOfCertificate(pvc
->path
, certificate
);
4104 if (pathIndex
!= 0) {
4105 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
4108 /* The rest of the key usages may be specified by the policy(ies). */
4109 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4110 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4111 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4113 /* Get key usage from ALL policies */
4114 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
4115 for (ix
= 0; ix
< count
; ix
++) {
4116 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
4117 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4121 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
4128 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4129 /* We need to declare the SecTrustedApplicationRef type for those binaries
4130 * that don't include the OS X Security Framework headers. */
4131 typedef struct CF_BRIDGED_TYPE(id
) OpaqueSecTrustedApplicationRef
*SecTrustedApplicationRef
;
4133 #include <Security/SecTrustedApplicationPriv.h>
4134 #include <bsm/libbsm.h>
4135 #include <libproc.h>
4137 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
4138 bool result
= false;
4139 audit_token_t auditToken
= {};
4140 char path
[MAXPATHLEN
];
4142 require(appRef
&& clientAuditToken
, out
);
4143 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
4145 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
4146 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
4147 require(proc_pidpath(audit_token_to_pid(auditToken
), path
, sizeof(path
)) > 0, out
);
4149 if(errSecSuccess
== SecTrustedApplicationValidateWithPath((SecTrustedApplicationRef
)appRef
, path
)) {
4158 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
4159 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
4160 CFNumberRef keyUsageNumber
= NULL
;
4161 CFTypeRef trustedApplicationData
= NULL
;
4163 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false , keyUsageMatch
= false;
4164 bool result
= false;
4166 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4167 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
4168 SecPolicyRef policy
= NULL
;
4169 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4170 policyOid
= (policy
) ? policy
->_oid
: NULL
;
4172 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4174 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
4175 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
4176 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
4178 CFIndex policyIX
= -1;
4179 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
4180 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
4181 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
4183 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4184 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
4185 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
4186 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
4187 CFReleaseNull(clientAuditToken
);
4189 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
4190 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
4194 /* If we either didn't find the parameter in the dictionary or we got a match
4195 * against that parameter, for all possible parameters in the dictionary, then
4196 * this trust setting result applies to the output. */
4197 if (((!policyOid
&& !policyName
) || policyMatch
) &&
4198 (!policyString
|| policyStringMatch
) &&
4199 (!trustedApplicationData
|| applicationMatch
) &&
4200 (!keyUsageNumber
|| keyUsageMatch
)) {
4207 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
4208 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
4209 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4210 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4211 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4212 if (!isDictionary(constraint
)) {
4216 CFNumberRef resultNumber
= NULL
;
4217 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
4218 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
4219 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
4220 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
4221 resultValue
= kSecTrustSettingsResultTrustRoot
;
4224 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
4225 result
= resultValue
;
4232 bool SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
4233 bool shouldDeny
= false;
4234 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4235 for (certIX
= 0; certIX
< certCount
; certIX
++) {
4236 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, certIX
);
4237 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4238 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
4240 if (result
== kSecTrustSettingsResultDeny
) {
4241 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
4248 #define kSecPolicySHA256Size 32
4249 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
4250 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
4251 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
4253 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
4254 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
4255 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
4257 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
4258 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
4259 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
4261 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
4262 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
4263 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
4265 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
4266 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
4267 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
4269 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
4270 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
4271 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
4273 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
4274 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
4275 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
4277 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
4278 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
4279 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
4282 bool SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
4283 static CFSetRef sConstrainedRoots
= NULL
;
4284 static dispatch_once_t _t
;
4285 dispatch_once(&_t
, ^{
4286 const UInt8
*v_hashes
[] = {
4287 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
4288 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
4290 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4291 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
4292 for (ix
=0; ix
<count
; ix
++) {
4293 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
4294 kSecPolicySHA256Size
, kCFAllocatorNull
);
4296 CFSetAddValue(set
, hash
);
4300 sConstrainedRoots
= set
;
4303 bool shouldDeny
= false;
4304 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4305 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
4306 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4307 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
4308 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
4309 /* matched a constrained root; check notBefore dates on all its children. */
4310 CFIndex childIX
= certIX
;
4311 while (--childIX
>= 0) {
4312 SecCertificateRef child
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, childIX
);
4313 /* 1 Dec 2016 00:00:00 GMT */
4314 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
4315 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
4321 CFReleaseNull(sha256
);
4326 /* AUDIT[securityd](done):
4327 policy->_options is a caller provided dictionary, only its cf type has
4330 bool SecPVCPathChecks(SecPVCRef pvc
) {
4331 secdebug("policy", "begin path: %@", pvc
->path
);
4332 bool completed
= true;
4333 /* This needs to be initialized before we call any function that might call
4334 SecPVCSetResultForced(). */
4336 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
4337 if (pvc
->result
|| pvc
->details
) {
4338 SecPolicyCheckBasicCertificateProcessing(pvc
,
4339 kSecPolicyCheckBasicCertificateProcessing
);
4342 CFArrayRef policies
= pvc
->policies
;
4343 CFIndex count
= CFArrayGetCount(policies
);
4344 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
4345 /* Validate all keys for all policies. */
4346 pvc
->callbacks
= gSecPolicyPathCallbacks
;
4347 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
4348 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
4349 if (!pvc
->result
&& !pvc
->details
)
4353 /* Check whether the TrustSettings say to deny a cert in the path. */
4354 (void)SecPVCCheckUsageConstraints(pvc
);
4356 /* Check for issuer date constraints. */
4357 (void)SecPVCCheckIssuerDateConstraints(pvc
);
4359 /* Check the things we can't check statically for the certificate path. */
4360 /* Critical Extensions, chainLength. */
4364 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
4365 bool pre_ev_check_result
= pvc
->result
;
4366 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
4367 pvc
->is_ev
= pvc
->result
;
4368 /* If ev checking failed, we still want to accept this chain
4369 as a non EV one, if it was valid as such. */
4370 pvc
->result
= pre_ev_check_result
;
4373 /* Check revocation always, since we don't want a lesser recoverable result
4374 * to prevent the check from occurring. */
4375 completed
= SecPVCCheckRevocation(pvc
);
4378 if (pvc
->result
|| pvc
->details
) {
4379 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
4380 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
4383 if (pvc
->is_ev
&& !pvc
->is_ct
) {
4384 pvc
->is_ct_whitelisted
= SecPVCCheckCTWhiteListedLeaf(pvc
);
4386 pvc
->is_ct_whitelisted
= false;
4390 secdebug("policy", "end %strusted completed: %d path: %@",
4391 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
4395 /* This function returns 0 to indicate revocation checking was not completed
4396 for this certificate chain, otherwise return to date at which the first
4397 piece of revocation checking info we used expires. */
4398 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
4399 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
4400 CFAbsoluteTime enu
= NULL_TIME
;
4401 if (certCount
<= 1 || !pvc
->rvcs
) {
4406 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
4407 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
4408 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
4409 if (thisCertNextUpdate
== 0) {
4411 /* We allow for CA certs to not be revocation checked if they
4412 have no ocspResponders nor CRLDPs to check against, but the leaf
4413 must be checked in order for us to claim we did revocation
4415 SecCertificateRef cert
=
4416 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
4417 CFArrayRef ocspResponders
= NULL
;
4418 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
4420 CFArrayRef crlDPs
= NULL
;
4421 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
4423 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
4425 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
4428 /* We can't check this cert so we don't consider it a soft
4429 failure that we didn't. Ideally we should support crl
4430 checking and remove this workaround, since that more
4435 secdebug("rvc", "revocation checking soft failure for cert: %ld",
4437 enu
= thisCertNextUpdate
;
4440 if (enu
== 0 || thisCertNextUpdate
< enu
) {
4441 enu
= thisCertNextUpdate
;
4445 secdebug("rvc", "revocation valid until: %lg", enu
);