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 /* Return the next responder we should contact for this rvc or NULL if we
2618 exhausted them all. */
2619 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
2620 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2621 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2622 if (ocspResponders
) {
2623 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2624 while (rvc
->responderIX
< responderCount
) {
2625 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2627 CFStringRef scheme
= CFURLCopyScheme(responder
);
2629 /* We only support http and https responders currently. */
2630 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2631 CFEqual(CFSTR("https"), scheme
));
2633 if (valid_responder
)
2641 /* Fire off an async http request for this certs revocation status, return
2642 false if request was queued, true if we're done. */
2643 static bool SecORVCFetchNext(SecORVCRef rvc
) {
2644 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
2645 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2649 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2650 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
2651 /* Async request was posted, wait for reply. */
2661 /* Process a verified ocsp response for a given cert. Return true if the
2662 certificate status was obtained. */
2663 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2666 switch (this->certStatus
) {
2668 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2669 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2670 in the info dictionary. */
2671 //cert.revokeCheckGood(true);
2672 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2676 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2677 /* @@@ Mark cert as revoked (with reason) at revocation date in
2678 the info dictionary, or perhaps we should use a different key per
2679 reason? That way a client using exceptions can ignore some but
2681 SInt32 reason
= this->crlReason
;
2682 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2683 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2685 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2686 /* make the revocation reason available in the trust result */
2687 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2689 CFRelease(cfreason
);
2693 /* not an error, no per-cert status, nothing here */
2694 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2698 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2699 (int)this->certStatus
, rvc
->certIX
);
2707 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
2708 if (rvc
->ocspSingleResponse
) {
2709 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2711 if (rvc
->ocspResponse
) {
2712 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2716 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
2719 SecOCSPEvaluateCompleted(const void *userData
,
2720 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
2721 SecTrustResultType result
) {
2722 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
2724 Block_release(evaluated
);
2728 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
2729 __block
bool evaluated
= false;
2730 bool trusted
= false;
2731 if (!signers
|| !issuers
) {
2735 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
2736 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2737 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2738 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2739 CFRelease(ocspSigner
);
2741 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
2742 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
2747 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
2748 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
2749 signers
, issuers
, true, false,
2750 policies
, NULL
, NULL
, NULL
,
2752 SecOCSPEvaluateCompleted
, completed
);
2753 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
2754 SecPathBuilderStep(oBuilder
);
2755 CFReleaseNull(clientAuditToken
);
2756 CFReleaseNull(policies
);
2758 /* verify the public key of the issuer signed the OCSP signer */
2760 SecCertificateRef issuer
= NULL
, signer
= NULL
;
2761 SecKeyRef issuerPubKey
= NULL
;
2763 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
2764 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
2767 #if TARGET_OS_IPHONE
2768 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
2770 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
2773 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
2776 secnotice("ocsp", "ocsp signer cert not signed by issuer");
2778 CFReleaseNull(issuerPubKey
);
2784 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
2786 SecCertificatePathRef issuers
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2787 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathGetCertificateAtIndex(issuers
, 0)) : NULL
;
2788 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
2789 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2791 if (signer
&& signers
) {
2792 if (issuer
&& CFEqual(signer
, issuer
)) {
2793 /* We already know we trust issuer since it's the issuer of the
2794 * cert we are verifying. */
2795 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
2799 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
2801 CFMutableArrayRef signerCerts
= NULL
;
2802 CFArrayRef issuerCerts
= NULL
;
2804 /* Ensure the signer cert is the 0th cert for trust evaluation */
2805 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2806 CFArrayAppendValue(signerCerts
, signer
);
2807 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
2810 issuerCerts
= SecCertificatePathCopyCertificates(issuers
, NULL
);
2813 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
2814 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
2818 /* @@@ We don't trust the cert so don't use this response. */
2819 secnotice("ocsp", "ocsp response signed by certificate which "
2820 "does not satisfy ocspSigner policy");
2823 CFReleaseNull(signerCerts
);
2824 CFReleaseNull(issuerCerts
);
2827 /* @@@ No signer found for this ocsp response, discard it. */
2828 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
2833 #if DUMP_OCSPRESPONSES
2835 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
2836 rvc
->certIX
, (trusted
? "t" : "u"));
2837 secdumpdata(ocspResponse
->data
, buf
);
2839 CFReleaseNull(issuers
);
2840 CFReleaseNull(issuer
);
2841 CFReleaseNull(signers
);
2842 CFReleaseNull(signer
);
2846 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
2847 SecOCSPSingleResponseRef sr
= NULL
;
2848 require_quiet(ocspResponse
, errOut
);
2849 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
2850 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
2851 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
2852 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
2853 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
2854 // Check if this response is fresher than any (cached) response we might still have in the rvc.
2855 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
2857 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
2858 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
2859 check whether the leaf was revoked (we are already checking the rest of
2861 /* Check the OCSP response signature and verify the response. */
2862 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
2863 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
2865 // If we get here, we have a properly signed ocsp response
2866 // but we haven't checked dates yet.
2868 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
2869 if (sr
->certStatus
== CS_Good
) {
2870 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
2871 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
2872 } else if (sr
->certStatus
== CS_Revoked
) {
2873 // Expire revoked responses when the subject certificate itself expires.
2874 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
2877 // Ok we like the new response, let's toss the old one.
2879 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
2881 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
2882 rvc
->ocspResponse
= ocspResponse
;
2883 ocspResponse
= NULL
;
2885 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2886 rvc
->ocspSingleResponse
= sr
;
2889 rvc
->done
= sr_valid
;
2892 if (sr
) SecOCSPSingleResponseDestroy(sr
);
2893 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2896 /* Callback from async http code after an ocsp response has been received. */
2897 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
2898 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
2899 SecPVCRef pvc
= rvc
->pvc
;
2900 SecOCSPResponseRef ocspResponse
= NULL
;
2901 if (http
->response
) {
2902 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
2904 /* Parse the returned data as if it's an ocspResponse. */
2905 ocspResponse
= SecOCSPResponseCreate(data
);
2910 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
2911 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
2914 /* Clear the data for the next response. */
2915 asynchttp_free(http
);
2916 SecORVCFetchNext(rvc
);
2920 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
2921 SecORVCUpdatePVC(rvc
);
2923 if (!--pvc
->asyncJobCount
) {
2924 secdebug("rvc", "done with all async jobs");
2925 SecPathBuilderStep(pvc
->builder
);
2930 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
2931 SecORVCRef orvc
= NULL
;
2932 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
2934 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
2937 orvc
->certIX
= certIX
;
2938 orvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
2939 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2940 orvc
->http
.completed
= SecOCSPFetchCompleted
;
2941 orvc
->http
.info
= orvc
;
2942 orvc
->ocspRequest
= NULL
;
2943 orvc
->responderIX
= 0;
2944 orvc
->responder
= NULL
;
2945 orvc
->nextUpdate
= NULL_TIME
;
2946 orvc
->ocspResponse
= NULL
;
2947 orvc
->ocspSingleResponse
= NULL
;
2950 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2951 /* The certIX + 1 is ok here since certCount is always at least 1
2952 less than the actual number of certs in SecPVCCheckRevocation. */
2953 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(pvc
, certIX
+ 1);
2954 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
2959 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
2960 /* Get stapled OCSP responses */
2961 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->pvc
->builder
);
2963 if(ocspResponsesData
) {
2964 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
2965 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2966 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2967 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
2969 CFRelease(ocspResponsesData
);
2974 /********************************************************
2975 ******************* CRL RVC Functions ******************
2976 ********************************************************/
2978 #include <../trustd/SecTrustOSXEntryPoints.h>
2979 OSStatus errSecCertificateRevoked
= -67820;
2980 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
2982 /* CRL Revocation verification context. */
2983 struct OpaqueSecCRVC
{
2984 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
2985 async_ocspd_t async_ocspd
;
2987 /* Pointer to the pvc for this revocation check. */
2990 /* Pointer to the generic rvc for this revocation check */
2993 /* The current CRL status from ocspd. */
2996 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2999 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
3000 current distribution point. */
3001 CFIndex distributionPointIX
;
3003 /* URL of current distribution point. */
3004 CFURLRef distributionPoint
;
3006 /* Date until which this revocation status is valid. */
3007 CFAbsoluteTime nextUpdate
;
3012 static void SecCRVCFinish(SecCRVCRef crvc
) {
3016 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
3017 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3018 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
3020 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
3021 while (rvc
->distributionPointIX
< crlDPCount
) {
3022 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
3023 rvc
->distributionPointIX
++;
3024 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
3026 /* We only support http and https responders currently. */
3027 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
3028 CFEqual(CFSTR("https"), scheme
) ||
3029 CFEqual(CFSTR("ldap"), scheme
));
3032 return distributionPoint
;
3039 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
3040 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3041 SecCertificatePathRef path
= rvc
->pvc
->path
;
3042 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3043 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
3044 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
3045 CFReleaseNull(serializedCertPath
);
3046 /* we got a response indicating that the CRL was checked */
3047 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
3049 /* ocspd doesn't give us the nextUpdate time, so set to default */
3050 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3054 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
3055 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3056 SecCRVCGetCRLStatus(rvc
);
3057 if (rvc
->status
== errSecCertificateRevoked
) {
3063 /* Fire off an async http request for this certs revocation status, return
3064 false if request was queued, true if we're done. */
3065 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
3066 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3067 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3068 SecCertificatePathRef path
= rvc
->pvc
->path
;
3069 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3070 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
3071 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
3072 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
3073 CFDataRef clientAuditToken
= NULL
;
3074 SecTaskRef task
= NULL
;
3075 audit_token_t auditToken
= {};
3076 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
3077 require(clientAuditToken
, out
);
3078 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3079 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3080 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3081 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
3082 rvc
->distributionPoint
, task
);
3085 CFReleaseNull(clientAuditToken
);
3086 CFReleaseNull(task
);
3087 /* Async request was posted, wait for reply. */
3095 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
3096 if (rvc
->status
== errSecCertificateRevoked
) {
3097 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
3098 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
3099 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
3100 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
3102 if (rvc
->pvc
&& rvc
->pvc
->info
) {
3103 /* make the revocation reason available in the trust result */
3104 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
3106 CFReleaseNull(cfreason
);
3110 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
3111 SecCRVCRef rvc
= ocspd
->info
;
3112 SecPVCRef pvc
= rvc
->pvc
;
3113 /* we got a response indicating that the CRL was checked */
3114 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
3115 rvc
->status
= ocspd
->response
;
3117 /* ocspd doesn't give us the nextUpdate time, so set to default */
3118 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3119 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
3120 SecCRVCUpdatePVC(rvc
);
3122 if (!--pvc
->asyncJobCount
) {
3123 secdebug("rvc", "done with all async jobs");
3124 SecPathBuilderStep(pvc
->builder
);
3127 if(SecCRVCFetchNext(rvc
)) {
3128 if (!--pvc
->asyncJobCount
) {
3129 secdebug("rvc", "done with all async jobs");
3130 SecPathBuilderStep(pvc
->builder
);
3136 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3137 SecCRVCRef crvc
= NULL
;
3138 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
3140 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
3143 crvc
->certIX
= certIX
;
3144 crvc
->status
= errSecInternal
;
3145 crvc
->distributionPointIX
= 0;
3146 crvc
->distributionPoint
= NULL
;
3147 crvc
->nextUpdate
= NULL_TIME
;
3148 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
3149 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
3150 crvc
->async_ocspd
.response
= errSecInternal
;
3151 crvc
->async_ocspd
.info
= crvc
;
3157 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
3158 if (rvc
->pvc
->check_revocation
&&
3159 CFEqual(kSecPolicyCheckRevocationCRL
, rvc
->pvc
->check_revocation
)) {
3160 /* Our client insists on CRLs */
3161 secinfo("rvc", "client told us to check CRL");
3164 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3165 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3166 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
3167 (rvc
->pvc
->check_revocation
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, rvc
->pvc
->check_revocation
))) {
3168 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
3169 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
3170 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
3175 #endif /* ENABLE_CRLS */
3177 static void SecRVCFinish(SecRVCRef rvc
) {
3179 SecORVCFinish(rvc
->orvc
);
3183 SecCRVCFinish(rvc
->crvc
);
3188 static void SecRVCDelete(SecRVCRef rvc
) {
3190 SecORVCFinish(rvc
->orvc
);
3195 SecCRVCFinish(rvc
->crvc
);
3201 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3202 secdebug("alloc", "%p", rvc
);
3204 rvc
->certIX
= certIX
;
3205 rvc
->orvc
= SecORVCCreate(rvc
, pvc
, certIX
);
3207 rvc
->crvc
= SecCRVCCreate(rvc
, pvc
, certIX
);
3212 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
3213 SecORVCUpdatePVC(rvc
->orvc
);
3215 SecCRVCUpdatePVC(rvc
->crvc
);
3220 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3221 if (!rvc
->pvc
->check_revocation
3222 || !CFEqual(rvc
->pvc
->check_revocation
, kSecPolicyCheckRevocationCRL
)) {
3228 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3233 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
3234 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
3235 if (SecRVCShouldCheckOCSP(rvc
)) {
3236 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
3237 SecORVCConsumeOCSPResponse(rvc
->orvc
,
3238 SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
),
3242 /* Don't check CRL cache if policy requested OCSP only */
3243 if (SecRVCShouldCheckCRL(rvc
)) {
3244 SecCRVCCheckRevocationCache(rvc
->crvc
);
3249 static bool SecRVCFetchNext(SecRVCRef rvc
) {
3250 bool OCSP_fetch_finished
= true;
3251 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
3252 if (SecRVCShouldCheckOCSP(rvc
)) {
3253 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
3255 if (OCSP_fetch_finished
) {
3256 /* we didn't start an OCSP background job for this cert */
3257 rvc
->pvc
->asyncJobCount
--;
3261 bool CRL_fetch_finished
= true;
3262 /* Don't check CRL cache if policy requested OCSP only */
3263 if (SecRVCShouldCheckCRL(rvc
)) {
3264 /* reset the distributionPointIX because we already iterated through the CRLDPs
3265 * in SecCRVCCheckRevocationCache */
3266 rvc
->crvc
->distributionPointIX
= 0;
3267 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
3269 if (CRL_fetch_finished
) {
3270 /* we didn't start a CRL background job for this cert */
3271 rvc
->pvc
->asyncJobCount
--;
3273 OCSP_fetch_finished
&= CRL_fetch_finished
;
3276 return OCSP_fetch_finished
;
3279 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
3280 secdebug("rvc", "checking revocation");
3281 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3282 bool completed
= true;
3283 if (certCount
<= 1) {
3284 /* Can't verify without an issuer; we're done */
3289 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
3290 * We can't check revocation for the final cert in the chain.
3295 /* We have done revocation checking already, we're done. */
3296 secdebug("rvc", "Not rechecking revocation");
3300 /* Setup things so we check revocation status of all certs except the
3302 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
3304 /* Note that if we are multi threaded and a job completes after it
3305 is started but before we return from this function, we don't want
3306 a callback to decrement asyncJobCount to zero before we finish issuing
3307 all the jobs. To avoid this we pretend we issued certCount async jobs,
3308 and decrement pvc->asyncJobCount for each cert that we don't start a
3309 background fetch for. */
3311 pvc
->asyncJobCount
= (unsigned int) certCount
;
3313 /* If we enable CRLS, we may end up with two async jobs per cert: one
3314 * for OCSP and one for fetching the CRL */
3315 pvc
->asyncJobCount
= 2 * (unsigned int)certCount
;
3317 secdebug("rvc", "set asyncJobCount to %d", pvc
->asyncJobCount
);
3319 /* Loop though certificates again and issue an ocsp fetch if the
3320 revocation status checking isn't done yet. */
3321 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3322 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
3323 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3324 SecRVCInit(rvc
, pvc
, certIX
);
3328 /* Ignore stapled OCSP responses only if CRLs are enabled and the
3329 * policy specifically requested CRLs only. */
3330 if (SecRVCShouldCheckOCSP(rvc
)) {
3331 /* If we have any OCSP stapled responses, check those first */
3332 SecORVCProcessStapledResponses(rvc
->orvc
);
3335 #if TARGET_OS_BRIDGE
3336 /* The bridge has no writeable storage and no network. Nothing else we can
3342 /* Then check the caches for revocation results. */
3343 SecRVCCheckRevocationCaches(rvc
);
3345 /* The check is done if we found cached responses from either method. */
3351 secdebug("rvc", "found cached response for cert: %ld", certIX
);
3355 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
3356 async http request for this cert's revocation status, unless we already successfully checked
3357 the revocation status of this cert based on the cache or stapled responses, */
3358 bool allow_fetch
= SecPathBuilderCanAccessNetwork(pvc
->builder
) && (pvc
->is_ev
|| pvc
->check_revocation
);
3359 bool fetch_done
= true;
3360 if (rvc
->done
|| !allow_fetch
) {
3361 /* We got a cache hit or we aren't allowed to access the network */
3362 SecRVCUpdatePVC(rvc
);
3364 /* We didn't really start any background jobs for this cert. */
3365 pvc
->asyncJobCount
--;
3367 pvc
->asyncJobCount
--;
3369 secdebug("rvc", "not fetching and job count is %d for cert %ld", pvc
->asyncJobCount
, certIX
);
3371 fetch_done
= SecRVCFetchNext(rvc
);
3374 /* We started at least one background fetch. */
3375 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
3380 /* Return false if we started any background jobs. */
3381 /* We can't just return !pvc->asyncJobCount here, since if we started any
3382 jobs the completion callback will be called eventually and it will call
3383 SecPathBuilderStep(). If for some reason everything completed before we
3384 get here we still want the outer SecPathBuilderStep() to terminate so we
3385 keep track of whether we started any jobs and return false if so. */
3389 static CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
3390 CFAbsoluteTime enu
= NULL_TIME
;
3391 enu
= rvc
->orvc
->nextUpdate
;
3393 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
3394 if (enu
== NULL_TIME
||
3395 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
3396 /* We didn't check OCSP or CRL next update time was sooner */
3397 enu
= crlNextUpdate
;
3404 void SecPolicyServerInitalize(void) {
3405 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3406 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3407 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3408 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3410 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3411 kSecPolicyCheckBasicCertificateProcessing
,
3412 SecPolicyCheckBasicCertificateProcessing
);
3413 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3414 kSecPolicyCheckCriticalExtensions
,
3415 SecPolicyCheckCriticalExtensions
);
3416 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3417 kSecPolicyCheckIdLinkage
,
3418 SecPolicyCheckIdLinkage
);
3419 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3420 kSecPolicyCheckKeyUsage
,
3421 SecPolicyCheckKeyUsage
);
3422 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3423 kSecPolicyCheckExtendedKeyUsage
,
3424 SecPolicyCheckExtendedKeyUsage
);
3425 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3426 kSecPolicyCheckBasicConstraints
,
3427 SecPolicyCheckBasicConstraints
);
3428 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3429 kSecPolicyCheckNonEmptySubject
,
3430 SecPolicyCheckNonEmptySubject
);
3431 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3432 kSecPolicyCheckQualifiedCertStatements
,
3433 SecPolicyCheckQualifiedCertStatements
);
3434 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3435 kSecPolicyCheckSSLHostname
,
3436 SecPolicyCheckSSLHostname
);
3437 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3438 kSecPolicyCheckEmail
,
3439 SecPolicyCheckEmail
);
3440 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3441 kSecPolicyCheckValidIntermediates
,
3442 SecPolicyCheckValidIntermediates
);
3443 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3444 kSecPolicyCheckValidLeaf
,
3445 SecPolicyCheckValidLeaf
);
3446 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3447 kSecPolicyCheckValidRoot
,
3448 SecPolicyCheckValidRoot
);
3449 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3450 kSecPolicyCheckIssuerCommonName
,
3451 SecPolicyCheckIssuerCommonName
);
3452 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3453 kSecPolicyCheckSubjectCommonNamePrefix
,
3454 SecPolicyCheckSubjectCommonNamePrefix
);
3455 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3456 kSecPolicyCheckSubjectCommonName
,
3457 SecPolicyCheckSubjectCommonName
);
3458 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3459 kSecPolicyCheckNotValidBefore
,
3460 SecPolicyCheckNotValidBefore
);
3461 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3462 kSecPolicyCheckChainLength
,
3463 SecPolicyCheckChainLength
);
3464 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3465 kSecPolicyCheckAnchorSHA1
,
3466 SecPolicyCheckAnchorSHA1
);
3467 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3468 kSecPolicyCheckAnchorSHA256
,
3469 SecPolicyCheckAnchorSHA256
);
3470 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3471 kSecPolicyCheckAnchorApple
,
3472 SecPolicyCheckAnchorApple
);
3473 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3474 kSecPolicyCheckSubjectOrganization
,
3475 SecPolicyCheckSubjectOrganization
);
3476 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3477 kSecPolicyCheckSubjectOrganizationalUnit
,
3478 SecPolicyCheckSubjectOrganizationalUnit
);
3479 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3480 kSecPolicyCheckEAPTrustedServerNames
,
3481 SecPolicyCheckEAPTrustedServerNames
);
3482 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3483 kSecPolicyCheckSubjectCommonNameTEST
,
3484 SecPolicyCheckSubjectCommonNameTEST
);
3485 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3486 kSecPolicyCheckRevocation
,
3487 SecPolicyCheckRevocation
);
3488 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3489 kSecPolicyCheckRevocationResponseRequired
,
3490 SecPolicyCheckRevocationResponseRequired
);
3491 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3492 kSecPolicyCheckNoNetworkAccess
,
3493 SecPolicyCheckNoNetworkAccess
);
3494 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3495 kSecPolicyCheckBlackListedLeaf
,
3496 SecPolicyCheckBlackListedLeaf
);
3497 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3498 kSecPolicyCheckGrayListedLeaf
,
3499 SecPolicyCheckGrayListedLeaf
);
3500 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3501 kSecPolicyCheckLeafMarkerOid
,
3502 SecPolicyCheckLeafMarkerOid
);
3503 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3504 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
3505 SecPolicyCheckLeafMarkerOidWithoutValueCheck
);
3506 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3507 kSecPolicyCheckIntermediateSPKISHA256
,
3508 SecPolicyCheckIntermediateSPKISHA256
);
3509 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3510 kSecPolicyCheckIntermediateEKU
,
3511 SecPolicyCheckIntermediateEKU
);
3512 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3513 kSecPolicyCheckIntermediateMarkerOid
,
3514 SecPolicyCheckIntermediateMarkerOid
);
3515 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3516 kSecPolicyCheckCertificatePolicy
,
3517 SecPolicyCheckCertificatePolicyOid
);
3518 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3519 kSecPolicyCheckWeakIntermediates
,
3520 SecPolicyCheckWeakIntermediates
);
3521 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3522 kSecPolicyCheckWeakLeaf
,
3523 SecPolicyCheckWeakLeaf
);
3524 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3525 kSecPolicyCheckWeakRoot
,
3526 SecPolicyCheckWeakRoot
);
3527 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3528 kSecPolicyCheckKeySize
,
3529 SecPolicyCheckKeySize
);
3530 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3531 kSecPolicyCheckSignatureHashAlgorithms
,
3532 SecPolicyCheckSignatureHashAlgorithms
);
3537 /********************************************************
3538 ****************** SecPVCRef Functions *****************
3539 ********************************************************/
3541 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3542 CFAbsoluteTime verifyTime
) {
3543 secdebug("alloc", "%p", pvc
);
3544 // Weird logging policies crashes.
3545 //secdebug("policy", "%@", policies);
3547 // Zero the pvc struct so only non-zero fields need to be explicitly set
3548 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
3549 pvc
->builder
= builder
;
3550 pvc
->policies
= policies
;
3553 pvc
->verifyTime
= verifyTime
;
3557 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3558 secdebug("alloc", "%p", pvc
);
3560 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
) - 1;
3561 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3562 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3570 void SecPVCDelete(SecPVCRef pvc
) {
3571 secdebug("alloc", "%p", pvc
);
3572 CFReleaseNull(pvc
->policies
);
3573 CFReleaseNull(pvc
->details
);
3574 CFReleaseNull(pvc
->info
);
3575 if (pvc
->valid_policy_tree
) {
3576 policy_tree_prune(&pvc
->valid_policy_tree
);
3578 SecPVCDeleteRVCs(pvc
);
3579 CFReleaseNull(pvc
->path
);
3582 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3583 CF_CONSUMED CFArrayRef details
) {
3584 secdebug("policy", "%@", path
);
3585 bool samePath
= ((!path
&& !pvc
->path
) || (path
&& pvc
->path
&& CFEqual(path
, pvc
->path
)));
3587 /* Changing path makes us clear the Revocation Verification Contexts */
3588 SecPVCDeleteRVCs(pvc
);
3589 CFReleaseSafe(pvc
->path
);
3590 pvc
->path
= CFRetainSafe(path
);
3592 pvc
->details
= details
;
3593 CFReleaseNull(pvc
->info
);
3594 if (pvc
->valid_policy_tree
) {
3595 policy_tree_prune(&pvc
->valid_policy_tree
);
3601 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3602 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3605 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3606 return SecCertificatePathGetCount(pvc
->path
);
3609 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3610 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3613 bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc
, CFIndex ix
) {
3614 return SecCertificatePathSelfSignedIndex(pvc
->path
) == ix
;
3617 void SecPVCSetCheckRevocation(SecPVCRef pvc
, CFStringRef method
) {
3618 pvc
->check_revocation
= method
;
3619 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
3622 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3623 pvc
->response_required
= true;
3624 secdebug("rvc", "revocation response required");
3627 bool SecPVCIsAnchored(SecPVCRef pvc
) {
3628 return SecCertificatePathIsAnchored(pvc
->path
);
3631 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
3632 return pvc
->verifyTime
;
3635 static int32_t detailKeyToCssmErr(CFStringRef key
) {
3638 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
3639 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
3641 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
3642 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
3644 else if (CFEqual(key
, kSecPolicyCheckValidLeaf
) ||
3645 CFEqual(key
, kSecPolicyCheckValidIntermediates
) ||
3646 CFEqual(key
, kSecPolicyCheckValidRoot
)) {
3647 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
3653 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
3655 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
3656 bool result
= false;
3657 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, ix
);
3658 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3659 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3661 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3662 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3663 CFNumberRef allowedErrorNumber
= NULL
;
3664 if (!isDictionary(constraint
)) {
3667 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
3668 int32_t allowedErrorValue
= 0;
3669 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
3673 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
3674 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
3683 /* AUDIT[securityd](done):
3684 policy->_options is a caller provided dictionary, only its cf type has
3687 bool SecPVCSetResultForced(SecPVCRef pvc
,
3688 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
3690 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
3691 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
3692 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
3694 (force
? "force" : ""), result
);
3696 /* If this is not something the current policy cares about ignore
3697 this error and return true so our caller continues evaluation. */
3699 /* @@@ The right long term fix might be to check if none of the passed
3700 in policies contain this key, since not all checks are run for all
3702 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3703 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
3707 /* Check to see if the SecTrustSettings for the certificate in question
3708 tell us to ignore this error. */
3709 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
3710 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
3714 pvc
->result
= false;
3718 CFMutableDictionaryRef detail
=
3719 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
3721 /* Perhaps detail should have an array of results per key? As it stands
3722 in the case of multiple policy failures the last failure stands. */
3723 CFDictionarySetValue(detail
, key
, result
);
3728 bool SecPVCSetResult(SecPVCRef pvc
,
3729 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
3730 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
3733 /* AUDIT[securityd](done):
3734 key(ok) is a caller provided.
3735 value(ok, unused) is a caller provided.
3737 static void SecPVCValidateKey(const void *key
, const void *value
,
3739 SecPVCRef pvc
= (SecPVCRef
)context
;
3741 /* If our caller doesn't want full details and we failed earlier there is
3742 no point in doing additional checks. */
3743 if (!pvc
->result
&& !pvc
->details
)
3746 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
3747 CFDictionaryGetValue(pvc
->callbacks
, key
);
3751 /* Why not to have optional policy checks rant:
3752 Not all keys are in all dictionaries anymore, so why not make checks
3753 optional? This way a client can ask for something and the server will
3754 do a best effort based on the supported flags. It works since they are
3755 synchronized now, but we need some debug checking here for now. */
3756 pvc
->result
= false;
3758 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
3759 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
3760 pvc
->result
= false;
3762 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3763 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3764 pvc
->result
= false;
3767 /* Non standard validation phase, nothing is optional. */
3768 pvc
->result
= false;
3773 fcn(pvc
, (CFStringRef
)key
);
3776 /* AUDIT[securityd](done):
3777 policy->_options is a caller provided dictionary, only its cf type has
3780 bool SecPVCLeafChecks(SecPVCRef pvc
) {
3782 CFArrayRef policies
= pvc
->policies
;
3783 CFIndex ix
, count
= CFArrayGetCount(policies
);
3784 for (ix
= 0; ix
< count
; ++ix
) {
3785 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3787 /* Validate all keys for all policies. */
3788 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3789 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3790 if (!pvc
->result
&& !pvc
->details
)
3797 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3798 /* Check stuff common to intermediate and anchors. */
3799 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3800 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3801 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3802 && SecPVCIsAnchored(pvc
));
3803 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3804 /* Certificate has expired. */
3805 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
3806 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
3810 if (SecCertificateIsWeakKey(cert
)) {
3811 /* Certificate uses weak key. */
3812 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckWeakRoot
3813 : kSecPolicyCheckWeakIntermediates
, ix
, kCFBooleanFalse
))
3818 /* Perform anchor specific checks. */
3819 /* Don't think we have any of these. */
3821 /* Perform intermediate specific checks. */
3823 /* (k) Basic constraints only relevant for v3 and later. */
3824 if (SecCertificateVersion(cert
) >= 3) {
3825 const SecCEBasicConstraints
*bc
=
3826 SecCertificateGetBasicConstraints(cert
);
3827 if (!bc
|| !bc
->isCA
) {
3828 /* Basic constraints not present or not marked as isCA, illegal. */
3829 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3830 ix
, kCFBooleanFalse
, true))
3834 /* (l) max_path_length is checked elsewhere. */
3836 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3837 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3838 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3839 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3840 ix
, kCFBooleanFalse
, true))
3849 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3850 /* Check stuff common to intermediate and anchors. */
3852 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3853 if (NULL
!= otapkiRef
)
3855 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3856 CFRelease(otapkiRef
);
3857 if (NULL
!= blackListedKeys
)
3859 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3860 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3861 bool is_last
= (ix
== count
- 1);
3862 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
3864 /* Check for blacklisted intermediate issuer keys. */
3865 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3867 /* Check dgst against blacklist. */
3868 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3869 /* Check allow list for this blacklisted issuer key,
3870 which is the authority key of the issued cert at ix-1.
3871 If ix is the last cert, the root is missing, so we
3872 also check our own authority key in that case.
3874 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
3875 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
3877 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3878 ix
, kCFBooleanFalse
, true);
3880 pvc
->is_allowlisted
= allowed
;
3885 CFRelease(blackListedKeys
);
3893 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3895 /* Check stuff common to intermediate and anchors. */
3896 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3897 if (NULL
!= otapkiRef
)
3899 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3900 CFRelease(otapkiRef
);
3901 if (NULL
!= grayListKeys
)
3903 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3904 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3905 bool is_last
= (ix
== count
- 1);
3906 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
3908 /* Check for gray listed intermediate issuer keys. */
3909 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3911 /* Check dgst against gray list. */
3912 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3913 /* Check allow list for this graylisted issuer key,
3914 which is the authority key of the issued cert at ix-1.
3915 If ix is the last cert, the root is missing, so we
3916 also check our own authority key in that case.
3918 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
3919 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
3921 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3922 ix
, kCFBooleanFalse
, true);
3924 pvc
->is_allowlisted
= allowed
;
3929 CFRelease(grayListKeys
);
3937 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
3938 if (!isString(searchName
) && !isString(searchOid
)) {
3941 CFArrayRef policies
= pvc
->policies
;
3942 CFIndex ix
, count
= CFArrayGetCount(policies
);
3943 for (ix
= 0; ix
< count
; ++ix
) {
3944 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3945 CFStringRef policyName
= SecPolicyGetName(policy
);
3946 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
3947 /* Prefer a match of both name and OID */
3948 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
3949 if (CFEqual(searchOid
, policyOid
) &&
3950 CFEqual(searchName
, policyName
)) {
3951 if (policyIX
) { *policyIX
= ix
; }
3955 /* Next best is just OID. */
3956 if (!searchName
&& searchOid
&& policyOid
) {
3957 if (CFEqual(searchOid
, policyOid
)) {
3958 if (policyIX
) { *policyIX
= ix
; }
3962 if (!searchOid
&& searchName
&& policyName
) {
3963 if (CFEqual(searchName
, policyName
)) {
3964 if (policyIX
) { *policyIX
= ix
; }
3972 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
3973 if (!isString(stringValue
)) {
3976 bool result
= false;
3978 CFStringRef tmpStringValue
= NULL
;
3979 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
3980 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
3982 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
3984 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3985 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3986 /* Have to look for all the possible locations of name string */
3987 CFStringRef policyString
= NULL
;
3988 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
3989 if (!policyString
) {
3990 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
3992 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
3997 CFArrayRef policyStrings
= NULL
;
3998 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
3999 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
4000 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
4008 CFReleaseNull(tmpStringValue
);
4013 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
4014 uint32_t ourTSKeyUsage
= 0;
4015 uint32_t keyUsage
= 0;
4016 if (keyUsageNumber
&&
4017 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
4018 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
4019 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
4021 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
4022 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
4024 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
4025 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
4027 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
4028 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
4030 if (keyUsage
== kSecKeyUsageAll
) {
4031 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
4034 return ourTSKeyUsage
;
4037 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
4038 uint32_t ourTSKeyUsage
= 0;
4039 CFTypeRef policyKeyUsageType
= NULL
;
4041 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
4042 if (isArray(policyKeyUsageType
)) {
4043 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
4044 for (ix
= 0; ix
< count
; ix
++) {
4045 CFNumberRef policyKeyUsageNumber
= NULL
;
4046 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
4047 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
4049 } else if (isNumber(policyKeyUsageType
)) {
4050 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
4053 return ourTSKeyUsage
;
4056 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
4057 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
4058 int64_t keyUsageValue
= 0;
4059 uint32_t ourKeyUsage
= 0;
4061 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
4065 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
4069 /* We're using the key for revocation if we have the OCSPSigner policy.
4070 * @@@ If we support CRLs, we'd need to check for that policy here too.
4072 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
4073 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
4076 /* We're using the key for verifying a cert if it's a root/intermediate
4077 * in the chain. If the cert isn't in the path yet, we're about to add it,
4078 * so it's a root/intermediate. If there is no path, this is the leaf.
4080 CFIndex pathIndex
= -1;
4082 pathIndex
= SecCertificatePathGetIndexOfCertificate(pvc
->path
, certificate
);
4086 if (pathIndex
!= 0) {
4087 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
4090 /* The rest of the key usages may be specified by the policy(ies). */
4091 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4092 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4093 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4095 /* Get key usage from ALL policies */
4096 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
4097 for (ix
= 0; ix
< count
; ix
++) {
4098 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
4099 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4103 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
4110 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4111 /* We need to declare the SecTrustedApplicationRef type for those binaries
4112 * that don't include the OS X Security Framework headers. */
4113 typedef struct CF_BRIDGED_TYPE(id
) OpaqueSecTrustedApplicationRef
*SecTrustedApplicationRef
;
4115 #include <Security/SecTrustedApplicationPriv.h>
4116 #include <bsm/libbsm.h>
4117 #include <libproc.h>
4119 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
4120 bool result
= false;
4121 audit_token_t auditToken
= {};
4122 char path
[MAXPATHLEN
];
4124 require(appRef
&& clientAuditToken
, out
);
4125 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
4127 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
4128 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
4129 require(proc_pidpath(audit_token_to_pid(auditToken
), path
, sizeof(path
)) > 0, out
);
4131 if(errSecSuccess
== SecTrustedApplicationValidateWithPath((SecTrustedApplicationRef
)appRef
, path
)) {
4140 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
4141 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
4142 CFNumberRef keyUsageNumber
= NULL
;
4143 CFTypeRef trustedApplicationData
= NULL
;
4145 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false , keyUsageMatch
= false;
4146 bool result
= false;
4148 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4149 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
4150 SecPolicyRef policy
= NULL
;
4151 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4152 policyOid
= (policy
) ? policy
->_oid
: NULL
;
4154 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4156 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
4157 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
4158 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
4160 CFIndex policyIX
= -1;
4161 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
4162 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
4163 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
4165 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4166 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
4167 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
4168 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
4169 CFReleaseNull(clientAuditToken
);
4171 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
4172 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
4176 /* If we either didn't find the parameter in the dictionary or we got a match
4177 * against that parameter, for all possible parameters in the dictionary, then
4178 * this trust setting result applies to the output. */
4179 if (((!policyOid
&& !policyName
) || policyMatch
) &&
4180 (!policyString
|| policyStringMatch
) &&
4181 (!trustedApplicationData
|| applicationMatch
) &&
4182 (!keyUsageNumber
|| keyUsageMatch
)) {
4189 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
4190 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
4191 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4192 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4193 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4194 if (!isDictionary(constraint
)) {
4198 CFNumberRef resultNumber
= NULL
;
4199 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
4200 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
4201 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
4202 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
4203 resultValue
= kSecTrustSettingsResultTrustRoot
;
4206 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
4207 result
= resultValue
;
4214 bool SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
4215 bool shouldDeny
= false;
4216 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4217 for (certIX
= 0; certIX
< certCount
; certIX
++) {
4218 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, certIX
);
4219 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4220 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
4222 if (result
== kSecTrustSettingsResultDeny
) {
4223 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
4230 /* AUDIT[securityd](done):
4231 policy->_options is a caller provided dictionary, only its cf type has
4234 bool SecPVCPathChecks(SecPVCRef pvc
) {
4235 secdebug("policy", "begin path: %@", pvc
->path
);
4236 bool completed
= true;
4237 /* This needs to be initialized before we call any function that might call
4238 SecPVCSetResultForced(). */
4240 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
4241 if (pvc
->result
|| pvc
->details
) {
4242 SecPolicyCheckBasicCertificateProcessing(pvc
,
4243 kSecPolicyCheckBasicCertificateProcessing
);
4246 CFArrayRef policies
= pvc
->policies
;
4247 CFIndex count
= CFArrayGetCount(policies
);
4248 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
4249 /* Validate all keys for all policies. */
4250 pvc
->callbacks
= gSecPolicyPathCallbacks
;
4251 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
4252 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
4253 if (!pvc
->result
&& !pvc
->details
)
4257 /* Check whether the TrustSettings say to deny a cert in the path. */
4258 (void)SecPVCCheckUsageConstraints(pvc
);
4260 /* Check the things we can't check statically for the certificate path. */
4261 /* Critical Extensions, chainLength. */
4265 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
4266 bool pre_ev_check_result
= pvc
->result
;
4267 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
4268 pvc
->is_ev
= pvc
->result
;
4269 /* If ev checking failed, we still want to accept this chain
4270 as a non EV one, if it was valid as such. */
4271 pvc
->result
= pre_ev_check_result
;
4274 /* Check revocation always, since we don't want a lesser recoverable result
4275 * to prevent the check from occurring. */
4276 completed
= SecPVCCheckRevocation(pvc
);
4279 if (pvc
->result
|| pvc
->details
) {
4280 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
4281 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
4284 if (pvc
->is_ev
&& !pvc
->is_ct
) {
4285 pvc
->is_ct_whitelisted
= SecPVCCheckCTWhiteListedLeaf(pvc
);
4287 pvc
->is_ct_whitelisted
= false;
4291 secdebug("policy", "end %strusted completed: %d path: %@",
4292 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
4296 /* This function returns 0 to indicate revocation checking was not completed
4297 for this certificate chain, otherwise return to date at which the first
4298 piece of revocation checking info we used expires. */
4299 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
4300 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
4301 CFAbsoluteTime enu
= NULL_TIME
;
4302 if (certCount
<= 1 || !pvc
->rvcs
) {
4307 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
4308 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
4309 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
4310 if (thisCertNextUpdate
== 0) {
4312 /* We allow for CA certs to not be revocation checked if they
4313 have no ocspResponders nor CRLDPs to check against, but the leaf
4314 must be checked in order for us to claim we did revocation
4316 SecCertificateRef cert
=
4317 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
4318 CFArrayRef ocspResponders
= NULL
;
4319 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
4321 CFArrayRef crlDPs
= NULL
;
4322 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
4324 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
4326 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
4329 /* We can't check this cert so we don't consider it a soft
4330 failure that we didn't. Ideally we should support crl
4331 checking and remove this workaround, since that more
4336 secdebug("rvc", "revocation checking soft failure for cert: %ld",
4338 enu
= thisCertNextUpdate
;
4341 if (enu
== 0 || thisCertNextUpdate
< enu
) {
4342 enu
= thisCertNextUpdate
;
4346 secdebug("rvc", "revocation valid until: %lg", enu
);