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, false otherwise */
1190 static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc
)
1192 bool result
= false;
1193 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1194 CFStringRef authKey
= NULL
;
1195 SecOTAPKIRef otapkiRef
= NULL
;
1196 CFDictionaryRef allowList
= NULL
;
1198 //get authKeyID from the last chain in the cert
1202 SecCertificateRef lastCert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1203 CFDataRef authKeyID
= SecCertificateGetAuthorityKeyID(lastCert
);
1204 if (NULL
== authKeyID
) {
1207 authKey
= CFDataCopyHexString(authKeyID
);
1208 if (NULL
== authKey
) {
1212 //if allowList && key is in allowList, this would have chained up to a now-removed anchor
1213 otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1214 if (NULL
== otapkiRef
) {
1217 allowList
= SecOTAPKICopyAllowList(otapkiRef
);
1218 if (NULL
== allowList
) {
1222 CFArrayRef allowedCerts
= CFDictionaryGetValue(allowList
, authKey
);
1223 if (!allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
1227 //search sorted array for the SHA256 hash of a cert in the chain
1228 CFRange range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
1229 for (ix
= 0; ix
< count
; ix
++) {
1230 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1235 CFDataRef certHash
= SecCertificateCopySHA256Digest(cert
);
1240 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, certHash
,
1241 (CFComparatorFunction
)CFDataCompare
, NULL
);
1242 if (position
< CFArrayGetCount(allowedCerts
)) {
1243 CFDataRef possibleMatch
= CFArrayGetValueAtIndex(allowedCerts
, position
);
1244 if (!CFDataCompare(certHash
, possibleMatch
)) {
1245 //this cert is in the allowlist
1250 CFRelease(certHash
);
1254 CFReleaseNull(authKey
);
1255 CFReleaseNull(otapkiRef
);
1256 CFReleaseNull(allowList
);
1260 #define DCMP(_idx_) memcmp(data+(8*_idx_), digest, 8)
1262 /* Returns true if leaf is on the CT whitelist */
1263 static bool SecPVCCheckCTWhiteListedLeaf(SecPVCRef pvc
)
1265 SecOTAPKIRef otapkiRef
= NULL
;
1266 CFDataRef whiteList
= NULL
;
1267 SecCertificateRef cert
= NULL
;
1268 CFDataRef dgst
= NULL
;
1269 bool result
= false;
1270 const uint8_t *digest
= NULL
;
1271 const uint8_t *data
= NULL
;
1272 require(otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef(), out
);
1273 require(whiteList
= SecOTAPKICopyCTWhiteList(otapkiRef
), out
);
1274 require(cert
= SecPVCGetCertificateAtIndex(pvc
, 0), out
);
1275 require(dgst
= SecCertificateCopySHA256Digest(cert
), out
);
1277 digest
= CFDataGetBytePtr(dgst
);
1278 data
= CFDataGetBytePtr(whiteList
);
1280 CFIndex h
= CFDataGetLength(whiteList
)/8-1;
1282 if(DCMP(l
)==0 || DCMP(h
)==0) {
1287 if(DCMP(l
)>0 || DCMP(h
)<0) {
1306 CFReleaseSafe(dgst
);
1307 CFReleaseSafe(whiteList
);
1308 CFReleaseSafe(otapkiRef
);
1312 /****************************************************************************
1313 *********************** New rfc5280 Chain Validation ***********************
1314 ****************************************************************************/
1317 typedef struct cert_path
*cert_path_t
;
1322 typedef struct x500_name
*x500_name_t
;
1326 typedef struct algorithm_id
*algorithm_id_t
;
1327 struct algorithm_id
{
1328 oid_t algorithm_oid
;
1332 typedef struct trust_anchor
*trust_anchor_t
;
1333 struct trust_anchor
{
1334 x500_name_t issuer_name
;
1335 algorithm_id_t public_key_algorithm
; /* includes optional params */
1336 SecKeyRef public_key
;
1339 typedef struct certificate_policy
*certificate_policy_t
;
1340 struct certificate_policy
{
1341 policy_qualifier_t qualifiers
;
1343 SLIST_ENTRY(certificate_policy
) policies
;
1346 typedef struct policy_mapping
*policy_mapping_t
;
1347 struct policy_mapping
{
1348 SLIST_ENTRY(policy_mapping
) mappings
;
1349 oid_t issuer_domain_policy
;
1350 oid_t subject_domain_policy
;
1353 typedef struct root_name
*root_name_t
;
1358 struct policy_tree_add_ctx
{
1360 policy_qualifier_t p_q
;
1363 /* 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}. */
1364 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1365 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1366 policy_set_t policy_set
;
1367 for (policy_set
= node
->expected_policy_set
;
1369 policy_set
= policy_set
->oid_next
) {
1370 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1371 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1378 /* 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}. */
1379 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1380 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1381 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1382 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1388 /* Return true iff node has a child with a valid_policy equal to oid. */
1389 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1391 policy_tree_t child
;
1392 for (child
= node
->children
; child
; child
= child
->siblings
) {
1393 if (oid_equal(child
->valid_policy
, (*oid
))) {
1400 /* 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. */
1401 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1402 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1403 policy_set_t policy_set
;
1404 bool added_node
= false;
1405 for (policy_set
= node
->expected_policy_set
;
1407 policy_set
= policy_set
->oid_next
) {
1408 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1409 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1417 /* 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. */
1418 static bool policy_tree_map(policy_tree_t node
, void *ctx
) {
1419 /* Can't map oidAnyPolicy. */
1420 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1423 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1424 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1425 policy_set_t policy_set
= NULL
;
1426 /* First count how many mappings match this nodes valid_policy. */
1427 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1428 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1429 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1430 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1431 p_node
->oid
= mapping
->subjectDomainPolicy
;
1432 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1433 policy_set
= p_node
;
1437 policy_tree_set_expected_policy(node
, policy_set
);
1444 #define POLICY_MAPPING 0
1445 #define POLICY_SUBTREES 1
1447 /* rfc5280 basic cert processing. */
1448 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1452 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1453 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1454 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1455 uint32_t n
= (uint32_t)count
;
1456 bool is_anchored
= SecPVCIsAnchored(pvc
);
1458 /* If the anchor is trusted we don't process the last cert in the
1462 /* trust may be restored for a path with an untrusted root that matches the allow list */
1463 if (!SecPVCCheckCertificateAllowList(pvc
)) {
1464 /* Add a detail for the root not being trusted. */
1465 if (SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1466 n
- 1, kCFBooleanFalse
, true))
1471 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1472 //policy_set_t user_initial_policy_set = NULL;
1473 //trust_anchor_t anchor;
1474 bool initial_policy_mapping_inhibit
= false;
1475 bool initial_explicit_policy
= false;
1476 bool initial_any_policy_inhibit
= false;
1478 /* Initialization */
1479 pvc
->valid_policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1481 CFMutableArrayRef permitted_subtrees
= NULL
;
1482 CFMutableArrayRef excluded_subtrees
= NULL
;
1483 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1484 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1485 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1486 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1487 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1488 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1490 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1491 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1492 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1495 /* Path builder ensures we only get cert chains with proper issuer
1496 chaining with valid signatures along the way. */
1497 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1498 SecKeyRef working_public_key
= anchor
->public_key
;
1499 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1501 uint32_t i
, max_path_length
= n
;
1502 SecCertificateRef cert
= NULL
;
1503 for (i
= 1; i
<= n
; ++i
) {
1505 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1506 bool is_self_issued
= SecPVCIsCertificateAtIndexSelfSigned(pvc
, n
- i
);
1508 /* (a) Verify the basic certificate information. */
1509 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1510 using the working_public_key and the working_public_key_parameters. */
1512 /* Already done by chain builder. */
1513 if (!SecCertificateIsValid(cert
, verify_time
)) {
1514 CFStringRef fail_key
= i
== n
? kSecPolicyCheckValidLeaf
: kSecPolicyCheckValidIntermediates
;
1515 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1519 if (SecCertificateIsWeak(cert
)) {
1520 CFStringRef fail_key
= i
== n
? kSecPolicyCheckWeakLeaf
: kSecPolicyCheckWeakIntermediates
;
1521 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1526 /* @@@ cert.issuer == working_issuer_name. */
1530 if (!is_self_issued
|| i
== n
) {
1532 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1533 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1534 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1535 secnotice("policy", "name in excluded subtrees");
1536 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1539 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1540 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1541 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1542 secnotice("policy", "name not in permitted subtrees");
1543 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1549 if (pvc
->valid_policy_tree
) {
1550 const SecCECertificatePolicies
*cp
=
1551 SecCertificateGetCertificatePolicies(cert
);
1552 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1553 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1554 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1555 oid_t p_oid
= policy
->policyIdentifier
;
1556 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1557 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1558 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1559 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1560 policy_tree_add_if_match
, &ctx
)) {
1561 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1562 policy_tree_add_if_any
, &ctx
);
1566 /* The certificate policies extension includes the policy
1567 anyPolicy with the qualifier set AP-Q and either
1568 (a) inhibit_anyPolicy is greater than 0 or
1569 (b) i < n and the certificate is self-issued. */
1570 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1571 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1572 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1573 oid_t p_oid
= policy
->policyIdentifier
;
1574 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1575 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1576 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1577 policy_tree_add_expected
, (void *)p_q
);
1581 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1584 if (pvc
->valid_policy_tree
)
1585 policy_tree_prune(&pvc
->valid_policy_tree
);
1588 /* (f) Verify that either explicit_policy is greater than 0 or the
1589 valid_policy_tree is not equal to NULL. */
1590 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1591 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1592 secnotice("policy", "policy tree failure");
1593 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true)) {
1597 /* If Last Cert in Path */
1601 /* Prepare for Next Cert */
1603 /* (a) verify that anyPolicy does not appear as an
1604 issuerDomainPolicy or a subjectDomainPolicy */
1605 CFDictionaryRef pm
= SecCertificateGetPolicyMappings(cert
);
1607 uint32_t mapping_ix
, mapping_count
= pm
->numMappings
;
1608 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1609 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1610 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1611 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1612 /* Policy mapping uses anyPolicy, illegal. */
1613 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
)) {
1619 /* (1) If the policy_mapping variable is greater than 0 */
1620 if (policy_mapping
> 0) {
1621 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1622 policy_tree_map
, (void *)pm
)) {
1623 /* 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:
1625 (i) set the valid_policy to ID-P;
1627 (ii) set the qualifier_set to the qualifier set of the
1628 policy anyPolicy in the certificate policies
1629 extension of certificate i; and
1630 (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. */
1634 /* (i) delete each node of depth i in the valid_policy_tree
1635 where ID-P is the valid_policy. */
1636 struct policy_tree_map_ctx ctx
= { idp_oid
, sdp_oid
};
1637 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1638 policy_tree_delete_if_match
, &ctx
);
1640 /* (ii) If there is a node in the valid_policy_tree of depth
1641 i-1 or less without any child nodes, delete that
1642 node. Repeat this step until there are no nodes of
1643 depth i-1 or less without children. */
1644 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1647 #endif /* POLICY_MAPPING */
1649 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1650 //working_public_key = SecCertificateCopyPublicKey(cert);
1651 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1652 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1654 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1656 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1657 if (permitted_subtrees_in_cert
) {
1658 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1661 // could do something smart here to avoid inserting the exact same constraint
1662 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1663 if (excluded_subtrees_in_cert
) {
1664 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1665 CFRange range
= { 0, num_trees
};
1666 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1670 if (!is_self_issued
) {
1671 if (explicit_policy
)
1675 if (inhibit_any_policy
)
1676 inhibit_any_policy
--;
1679 const SecCEPolicyConstraints
*pc
=
1680 SecCertificateGetPolicyConstraints(cert
);
1682 if (pc
->requireExplicitPolicyPresent
1683 && pc
->requireExplicitPolicy
< explicit_policy
) {
1684 explicit_policy
= pc
->requireExplicitPolicy
;
1686 if (pc
->inhibitPolicyMappingPresent
1687 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1688 policy_mapping
= pc
->inhibitPolicyMapping
;
1692 uint32_t iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1693 if (iap
< inhibit_any_policy
) {
1694 inhibit_any_policy
= iap
;
1697 const SecCEBasicConstraints
*bc
=
1698 SecCertificateGetBasicConstraints(cert
);
1699 #if 0 /* Checked in chain builder pre signature verify already. */
1700 if (!bc
|| !bc
->isCA
) {
1701 /* Basic constraints not present or not marked as isCA, illegal. */
1702 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1703 n
- i
, kCFBooleanFalse
)) {
1709 if (!is_self_issued
) {
1710 if (max_path_length
> 0) {
1713 /* max_path_len exceeded, illegal. */
1714 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1715 n
- i
, kCFBooleanFalse
)) {
1721 if (bc
&& bc
->pathLenConstraintPresent
1722 && bc
->pathLenConstraint
< max_path_length
) {
1723 max_path_length
= bc
->pathLenConstraint
;
1725 #if 0 /* Checked in chain builder pre signature verify already. */
1726 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1727 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1728 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1729 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1730 n
- i
, kCFBooleanFalse
, true)) {
1735 /* (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. */
1736 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1737 /* Certificate contains one or more unknown critical extensions. */
1738 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1739 n
- i
, kCFBooleanFalse
)) {
1743 } /* end loop over certs in path */
1745 cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1747 if (explicit_policy
)
1750 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1752 if (pc
->requireExplicitPolicyPresent
1753 && pc
->requireExplicitPolicy
== 0) {
1754 explicit_policy
= 0;
1758 //working_public_key = SecCertificateCopyPublicKey(cert);
1760 /* 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
1761 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1762 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1764 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1765 /* (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. */
1766 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1767 /* Certificate contains one or more unknown critical extensions. */
1768 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1769 0, kCFBooleanFalse
)) {
1773 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1775 if (pvc
->valid_policy_tree
) {
1776 #if !defined(NDEBUG)
1777 policy_tree_dump(pvc
->valid_policy_tree
);
1780 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1783 /* If either (1) the value of explicit_policy variable is greater than
1784 zero or (2) the valid_policy_tree is not NULL, then path processing
1786 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1787 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1788 secnotice("policy", "policy tree failure");
1789 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, 0, kCFBooleanFalse
, true)) {
1795 CFReleaseNull(permitted_subtrees
);
1796 CFReleaseNull(excluded_subtrees
);
1799 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1800 policy_set_t policies
= NULL
;
1801 const SecCECertificatePolicies
*cp
=
1802 SecCertificateGetCertificatePolicies(cert
);
1803 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1804 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1805 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1810 static void SecPolicyCheckEV(SecPVCRef pvc
,
1812 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1813 policy_set_t valid_policies
= NULL
;
1815 /* 6.1.7. Key Usage Purposes */
1817 CFAbsoluteTime jul2016
= 489024000;
1818 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1819 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1820 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1821 if (SecPVCSetResultForced(pvc
, key
,
1822 0, kCFBooleanFalse
, true)) {
1828 for (ix
= 0; ix
< count
; ++ix
) {
1829 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1830 policy_set_t policies
= policies_for_cert(cert
);
1833 /* anyPolicy in the leaf isn't allowed for EV, so only init
1834 valid_policies if we have real policies. */
1835 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1836 valid_policies
= policies
;
1839 } else if (ix
< count
- 1) {
1840 /* Subordinate CA */
1841 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1842 secnotice("ev", "subordinate certificate is not ev");
1843 if (SecPVCSetResultForced(pvc
, key
,
1844 ix
, kCFBooleanFalse
, true)) {
1845 policy_set_free(valid_policies
);
1846 policy_set_free(policies
);
1850 policy_set_intersect(&valid_policies
, policies
);
1853 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1854 secnotice("ev", "anchor certificate is not ev");
1855 if (SecPVCSetResultForced(pvc
, key
,
1856 ix
, kCFBooleanFalse
, true)) {
1857 policy_set_free(valid_policies
);
1858 policy_set_free(policies
);
1863 policy_set_free(policies
);
1864 if (!valid_policies
) {
1865 secnotice("ev", "valid_policies set is empty: chain not ev");
1866 /* If we ever get into a state where no policies are valid anymore
1867 this can't be an ev chain. */
1868 if (SecPVCSetResultForced(pvc
, key
,
1869 ix
, kCFBooleanFalse
, true)) {
1875 policy_set_free(valid_policies
);
1877 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1878 Subscriber MUST contain an OID defined by the CA in the certificate’s
1879 certificatePolicies extension that: (i) indicates which CA policy statement relates
1880 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1881 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1882 marks the certificate as being an EV Certificate.
1883 (b) EV Subordinate CA Certificates
1884 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1885 CA MUST contain one or more OIDs defined by the issuing CA that
1886 explicitly identify the EV Policies that are implemented by the Subordinate
1888 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1889 MAY contain the special anyPolicy OID (2.5.29.32.0).
1890 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1891 certificatePolicies or extendedKeyUsage extensions.
1897 * MARK: Certificate Transparency support
1903 Version sct_version; // 1 byte
1904 LogID id; // 32 bytes
1905 uint64 timestamp; // 8 bytes
1906 CtExtensions extensions; // 2 bytes len field, + n bytes data
1907 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1908 Version sct_version;
1909 SignatureType signature_type = certificate_timestamp;
1911 LogEntryType entry_type;
1912 select(entry_type) {
1913 case x509_entry: ASN.1Cert;
1914 case precert_entry: PreCert;
1916 CtExtensions extensions;
1918 } SignedCertificateTimestamp;
1922 #include <Security/SecureTransportPriv.h>
1925 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1928 case SSL_SignatureAlgorithmRSA
:
1930 case SSL_HashAlgorithmSHA1
:
1931 return &CSSMOID_SHA1WithRSA
;
1932 case SSL_HashAlgorithmSHA256
:
1933 return &CSSMOID_SHA256WithRSA
;
1934 case SSL_HashAlgorithmSHA384
:
1935 return &CSSMOID_SHA384WithRSA
;
1939 case SSL_SignatureAlgorithmECDSA
:
1941 case SSL_HashAlgorithmSHA1
:
1942 return &CSSMOID_ECDSA_WithSHA1
;
1943 case SSL_HashAlgorithmSHA256
:
1944 return &CSSMOID_ECDSA_WithSHA256
;
1945 case SSL_HashAlgorithmSHA384
:
1946 return &CSSMOID_ECDSA_WithSHA384
;
1958 static size_t SSLDecodeUint16(const uint8_t *p
)
1960 return (p
[0]<<8 | p
[1]);
1963 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1965 p
[0] = (len
>> 8)&0xff;
1966 p
[1] = (len
& 0xff);
1970 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1972 p
[0] = (len
>> 16)&0xff;
1973 p
[1] = (len
>> 8)&0xff;
1974 p
[2] = (len
& 0xff);
1980 uint64_t SSLDecodeUint64(const uint8_t *p
)
1983 for(int i
=0; i
<8; i
++) {
1990 #include <libDER/DER_CertCrl.h>
1991 #include <libDER/DER_Encode.h>
1992 #include <libDER/asn1Types.h>
1995 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1997 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1999 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
2001 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
2003 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2004 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
2005 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
2011 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
2013 SecCertificateRef leafCert
= NULL
;
2014 SecCertificateRef issuer
= NULL
;
2015 CFDataRef issuerKeyHash
= NULL
;
2016 CFDataRef tbs_precert
= NULL
;
2017 CFMutableDataRef data
= NULL
;
2019 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2020 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2021 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2023 require(leafCert
, out
);
2024 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
2025 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
2026 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
2028 require(issuerKeyHash
, out
);
2029 require(tbs_precert
, out
);
2030 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2031 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2033 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2034 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
2035 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
2036 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
2039 CFReleaseSafe(issuerKeyHash
);
2040 CFReleaseSafe(tbs_precert
);
2045 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
2047 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
2051 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
2053 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
2060 If the 'sct' is valid, add it to the validatingLogs dictionary.
2063 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
2065 - entry_type: 0 for x509 cert, 1 for precert.
2066 - entry: the cert or precert data.
2067 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
2068 - trustedLog: Dictionary contain the Trusted Logs.
2070 The SCT is valid if:
2071 - It decodes properly.
2072 - Its timestamp is less than 'verifyTime'.
2073 - It is signed by a log in 'trustedLogs'.
2074 - If entry_type = 0, the log must be currently qualified.
2075 - If entry_type = 1, the log may be expired.
2077 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
2078 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.
2083 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFArrayRef trustedLogs
, CFAbsoluteTime
*sct_at
)
2086 const uint8_t *logID
;
2087 const uint8_t *timestampData
;
2089 size_t extensionsLen
;
2090 const uint8_t *extensionsData
;
2093 size_t signatureLen
;
2094 const uint8_t *signatureData
;
2095 SecKeyRef pubKey
= NULL
;
2096 uint8_t *signed_data
= NULL
;
2097 const SecAsn1Oid
*oid
= NULL
;
2099 CFDataRef logIDData
= NULL
;
2100 CFDictionaryRef result
= 0;
2102 const uint8_t *p
= CFDataGetBytePtr(sct
);
2103 size_t len
= CFDataGetLength(sct
);
2105 require(len
>=43, out
);
2107 version
= p
[0]; p
++; len
--;
2108 logID
= p
; p
+=32; len
-=32;
2109 timestampData
= p
; p
+=8; len
-=8;
2110 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2112 require(len
>=extensionsLen
, out
);
2113 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
2115 require(len
>=4, out
);
2116 hashAlg
=p
[0]; p
++; len
--;
2117 sigAlg
=p
[0]; p
++; len
--;
2118 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2119 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
2122 /* verify version: only v1(0) is supported */
2124 secerror("SCT version unsupported: %d\n", version
);
2128 /* verify timestamp not in the future */
2129 timestamp
= SSLDecodeUint64(timestampData
);
2130 if(timestamp
> vt
) {
2131 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
2138 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
2139 signed_data
= malloc(signed_data_len
);
2140 require(signed_data
, out
);
2143 *q
++ = 0; // certificate_timestamp
2144 memcpy(q
, timestampData
, 8); q
+=8;
2145 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
2146 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
2147 q
= SSLEncodeUint16(q
, extensionsLen
);
2148 memcpy(q
, extensionsData
, extensionsLen
);
2150 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
2152 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
2153 const void *key_data
;
2154 if(!isDictionary(dict
)) return false;
2155 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
2156 if(!isData(key_data
)) return false;
2157 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
2158 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
2159 CFReleaseSafe(valueID
);
2162 require(logData
, out
);
2165 // For external SCTs, only keep SCTs from currently valid logs.
2166 require(!CFDictionaryContainsKey(logData
, CFSTR("expiry")), out
);
2169 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
2170 require(logKeyData
, out
); // This failing would be an internal logic error
2171 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
2172 require(pubKey
, out
);
2174 oid
= oidForSigAlg(hashAlg
, sigAlg
);
2177 algId
.algorithm
= *oid
;
2178 algId
.parameters
.Data
= NULL
;
2179 algId
.parameters
.Length
= 0;
2181 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
2182 *sct_at
= TimestampToCFAbsoluteTime(timestamp
);
2185 secerror("SCT signature failed (log=%@)\n", logData
);
2189 CFReleaseSafe(logIDData
);
2190 CFReleaseSafe(pubKey
);
2196 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
2198 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
2200 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
2201 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
2202 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
2203 CFReleaseSafe(sct_time
);
2207 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
2209 CFMutableArrayRef SCTs
= NULL
;
2210 SecCertificateRef leafCert
= NULL
;
2211 SecCertificateRef issuer
= NULL
;
2212 CFArrayRef ocspResponsesData
= NULL
;
2213 SecOCSPRequestRef ocspRequest
= NULL
;
2215 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2216 require_quiet(ocspResponsesData
, out
);
2218 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2219 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2220 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2222 require(leafCert
, out
);
2223 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
2224 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
2226 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2229 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2230 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2231 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2232 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
2233 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
2234 if(ocspSingleResponse
) {
2235 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
2236 if(singleResponseSCTs
) {
2237 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
2238 CFRelease(singleResponseSCTs
);
2240 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
2243 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2246 if(CFArrayGetCount(SCTs
)==0) {
2247 CFReleaseNull(SCTs
);
2251 CFReleaseSafe(ocspResponsesData
);
2253 SecOCSPRequestFinalize(ocspRequest
);
2258 static void SecPolicyCheckCT(SecPVCRef pvc
, CFStringRef key
)
2260 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2261 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
2262 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
2263 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2264 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
2265 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
2266 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
2268 // This eventually contain list of logs who validated the SCT.
2269 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2270 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2272 uint64_t vt
= TimestampFromCFAbsoluteTime(pvc
->verifyTime
);
2274 __block
bool at_least_one_currently_valid_external
= 0;
2275 __block
bool at_least_one_currently_valid_embedded
= 0;
2277 require(logsValidatingEmbeddedScts
, out
);
2278 require(currentLogsValidatingScts
, out
);
2280 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
2281 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
2282 CFArrayForEach(embeddedScts
, ^(const void *value
){
2283 CFAbsoluteTime sct_at
;
2284 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, trustedLogs
, &sct_at
);
2286 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
2287 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2288 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2289 at_least_one_currently_valid_embedded
= true;
2295 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
2296 CFArrayForEach(builderScts
, ^(const void *value
){
2297 CFAbsoluteTime sct_at
;
2298 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2300 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2301 at_least_one_currently_valid_external
= true;
2306 if(ocspScts
&& x509Entry
) {
2307 CFArrayForEach(ocspScts
, ^(const void *value
){
2308 CFAbsoluteTime sct_at
;
2309 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2311 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2312 at_least_one_currently_valid_external
= true;
2319 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
2322 is_ct = (A1 AND A2) OR (B1 AND B2).
2324 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
2325 A2: At least one embedded SCT from a currently valid log.
2327 B1: SCTs from 2 currently valid logs (from any source)
2328 B2: At least 1 external SCT from a currently valid log.
2334 if(at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2) {
2336 } else if(at_least_one_currently_valid_embedded
) {
2337 __block CFAbsoluteTime issuanceTime
= pvc
->verifyTime
;
2338 __block
int lifetime
; // in Months
2339 __block
unsigned once_or_current_qualified_embedded
= 0;
2341 /* Calculate issuance time base on timestamp of SCTs from current logs */
2342 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
2343 CFDictionaryRef log
= key
;
2344 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2345 // Log is still qualified
2346 CFDateRef ts
= (CFDateRef
) value
;
2347 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
2348 if(timestamp
< issuanceTime
) {
2349 issuanceTime
= timestamp
;
2355 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
2356 CFDictionaryRef log
= key
;
2357 CFDateRef ts
= value
;
2358 CFDateRef expiry
= CFDictionaryGetValue(log
, CFSTR("expiry"));
2359 if(expiry
== NULL
|| CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
) {
2360 once_or_current_qualified_embedded
++;
2364 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
2366 CFCalendarGetComponentDifference(zuluCalendar
,
2367 SecCertificateNotValidBefore(leafCert
),
2368 SecCertificateNotValidAfter(leafCert
),
2369 0, "M", &_lifetime
);
2370 lifetime
= _lifetime
;
2373 unsigned requiredEmbeddedSctsCount
;
2375 if (lifetime
< 15) {
2376 requiredEmbeddedSctsCount
= 2;
2377 } else if (lifetime
<= 27) {
2378 requiredEmbeddedSctsCount
= 3;
2379 } else if (lifetime
<= 39) {
2380 requiredEmbeddedSctsCount
= 4;
2382 requiredEmbeddedSctsCount
= 5;
2385 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
2391 CFReleaseSafe(logsValidatingEmbeddedScts
);
2392 CFReleaseSafe(currentLogsValidatingScts
);
2393 CFReleaseSafe(builderScts
);
2394 CFReleaseSafe(embeddedScts
);
2395 CFReleaseSafe(ocspScts
);
2396 CFReleaseSafe(precertEntry
);
2397 CFReleaseSafe(trustedLogs
);
2398 CFReleaseSafe(x509Entry
);
2401 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
2402 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2404 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
2405 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
2407 for (ix
= 0; ix
< count
; ix
++) {
2408 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2409 policy_set_t policies
= policies_for_cert(cert
);
2411 if (policy_set_contains(policies
, &key_value
)) {
2418 static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc
, CFStringRef key
)
2420 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2421 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2422 bool result
= false;
2424 if (CFGetTypeID(value
) == CFDataGetTypeID())
2426 result
= checkPolicyOidData(pvc
, value
);
2427 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
2428 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
2430 result
= checkPolicyOidData(pvc
, dataOid
);
2435 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2440 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2442 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2443 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2444 if (isString(value
)) {
2445 SecPVCSetCheckRevocation(pvc
, value
);
2449 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2451 SecPVCSetCheckRevocationResponseRequired(pvc
);
2454 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2456 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2457 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2458 if (value
== kCFBooleanTrue
) {
2459 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2461 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
2465 static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc
,
2467 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2468 for (ix
= 1; ix
< count
- 1; ++ix
) {
2469 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2470 if (cert
&& SecCertificateIsWeak(cert
)) {
2471 /* Intermediate certificate has a weak key. */
2472 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2478 static void SecPolicyCheckWeakLeaf(SecPVCRef pvc
,
2480 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2481 if (cert
&& SecCertificateIsWeak(cert
)) {
2482 /* Leaf certificate has a weak key. */
2483 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
2488 static void SecPolicyCheckWeakRoot(SecPVCRef pvc
,
2490 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2492 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2493 if (cert
&& SecCertificateIsWeak(cert
)) {
2494 /* Root certificate has a weak key. */
2495 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2500 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
2501 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2502 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2503 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
2504 for (ix
= 0; ix
< count
; ++ix
) {
2505 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2506 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
2507 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2513 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
2515 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2516 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2517 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
2518 for (ix
= 0; ix
< count
; ++ix
) {
2519 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2520 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
2521 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2527 #define ENABLE_CRLS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
2531 /********************************************************
2532 ****************** SecRVCRef Functions *****************
2533 ********************************************************/
2534 typedef struct OpaqueSecORVC
*SecORVCRef
;
2536 typedef struct OpaqueSecCRVC
*SecCRVCRef
;
2539 /* Revocation verification context. */
2540 struct OpaqueSecRVC
{
2541 /* Pointer to the pvc for this revocation check */
2544 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2547 /* The OCSP Revocation verification context */
2556 typedef struct OpaqueSecRVC
*SecRVCRef
;
2559 /********************************************************
2560 ****************** OCSP RVC Functions ******************
2561 ********************************************************/
2562 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
2563 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
2565 /* OCSP Revocation verification context. */
2566 struct OpaqueSecORVC
{
2567 /* Will contain the response data. */
2570 /* Pointer to the pvc for this revocation check. */
2573 /* Pointer to the generic rvc for this revocation check */
2576 /* The ocsp request we send to each responder. */
2577 SecOCSPRequestRef ocspRequest
;
2579 /* The freshest response we received so far, from stapling or cache or responder. */
2580 SecOCSPResponseRef ocspResponse
;
2582 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
2583 SecOCSPSingleResponseRef ocspSingleResponse
;
2585 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2588 /* Index in array returned by SecCertificateGetOCSPResponders() for current
2590 CFIndex responderIX
;
2592 /* URL of current responder. */
2595 /* Date until which this revocation status is valid. */
2596 CFAbsoluteTime nextUpdate
;
2601 static void SecORVCFinish(SecORVCRef orvc
) {
2602 secdebug("alloc", "%p", orvc
);
2603 asynchttp_free(&orvc
->http
);
2604 if (orvc
->ocspRequest
) {
2605 SecOCSPRequestFinalize(orvc
->ocspRequest
);
2606 orvc
->ocspRequest
= NULL
;
2608 if (orvc
->ocspResponse
) {
2609 SecOCSPResponseFinalize(orvc
->ocspResponse
);
2610 orvc
->ocspResponse
= NULL
;
2611 if (orvc
->ocspSingleResponse
) {
2612 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
2613 orvc
->ocspSingleResponse
= NULL
;
2618 /* Return the next responder we should contact for this rvc or NULL if we
2619 exhausted them all. */
2620 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
2621 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2622 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2623 if (ocspResponders
) {
2624 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2625 while (rvc
->responderIX
< responderCount
) {
2626 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2628 CFStringRef scheme
= CFURLCopyScheme(responder
);
2630 /* We only support http and https responders currently. */
2631 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2632 CFEqual(CFSTR("https"), scheme
));
2634 if (valid_responder
)
2642 /* Fire off an async http request for this certs revocation status, return
2643 false if request was queued, true if we're done. */
2644 static bool SecORVCFetchNext(SecORVCRef rvc
) {
2645 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
2646 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2650 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2651 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
2652 /* Async request was posted, wait for reply. */
2662 /* Process a verified ocsp response for a given cert. Return true if the
2663 certificate status was obtained. */
2664 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2667 switch (this->certStatus
) {
2669 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2670 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2671 in the info dictionary. */
2672 //cert.revokeCheckGood(true);
2673 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2677 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2678 /* @@@ Mark cert as revoked (with reason) at revocation date in
2679 the info dictionary, or perhaps we should use a different key per
2680 reason? That way a client using exceptions can ignore some but
2682 SInt32 reason
= this->crlReason
;
2683 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2684 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2686 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2687 /* make the revocation reason available in the trust result */
2688 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2690 CFRelease(cfreason
);
2694 /* not an error, no per-cert status, nothing here */
2695 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2699 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2700 (int)this->certStatus
, rvc
->certIX
);
2708 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
2709 if (rvc
->ocspSingleResponse
) {
2710 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2712 if (rvc
->ocspResponse
) {
2713 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2717 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
2720 SecOCSPEvaluateCompleted(const void *userData
,
2721 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
2722 SecTrustResultType result
) {
2723 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
2725 Block_release(evaluated
);
2729 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
2730 __block
bool evaluated
= false;
2731 bool trusted
= false;
2732 if (!signers
|| !issuers
) {
2736 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
2737 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2738 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2739 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2740 CFRelease(ocspSigner
);
2742 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
2743 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
2748 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
2749 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
2750 signers
, issuers
, true, false,
2751 policies
, NULL
, NULL
, NULL
,
2753 SecOCSPEvaluateCompleted
, completed
);
2754 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
2755 SecPathBuilderStep(oBuilder
);
2756 CFReleaseNull(clientAuditToken
);
2757 CFReleaseNull(policies
);
2759 /* verify the public key of the issuer signed the OCSP signer */
2761 SecCertificateRef issuer
= NULL
, signer
= NULL
;
2762 SecKeyRef issuerPubKey
= NULL
;
2764 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
2765 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
2768 #if TARGET_OS_IPHONE
2769 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
2771 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
2774 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
2777 secnotice("ocsp", "ocsp signer cert not signed by issuer");
2779 CFReleaseNull(issuerPubKey
);
2785 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
2787 SecCertificatePathRef issuers
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2788 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathGetCertificateAtIndex(issuers
, 0)) : NULL
;
2789 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
2790 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2792 if (signer
&& signers
) {
2793 if (issuer
&& CFEqual(signer
, issuer
)) {
2794 /* We already know we trust issuer since it's the issuer of the
2795 * cert we are verifying. */
2796 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
2800 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
2802 CFMutableArrayRef signerCerts
= NULL
;
2803 CFArrayRef issuerCerts
= NULL
;
2805 /* Ensure the signer cert is the 0th cert for trust evaluation */
2806 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
2807 CFArrayAppendValue(signerCerts
, signer
);
2808 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
2811 issuerCerts
= SecCertificatePathCopyCertificates(issuers
, NULL
);
2814 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
2815 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
2819 /* @@@ We don't trust the cert so don't use this response. */
2820 secnotice("ocsp", "ocsp response signed by certificate which "
2821 "does not satisfy ocspSigner policy");
2824 CFReleaseNull(signerCerts
);
2825 CFReleaseNull(issuerCerts
);
2828 /* @@@ No signer found for this ocsp response, discard it. */
2829 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
2834 #if DUMP_OCSPRESPONSES
2836 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
2837 rvc
->certIX
, (trusted
? "t" : "u"));
2838 secdumpdata(ocspResponse
->data
, buf
);
2840 CFReleaseNull(issuers
);
2841 CFReleaseNull(issuer
);
2842 CFReleaseNull(signers
);
2843 CFReleaseNull(signer
);
2847 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
2848 SecOCSPSingleResponseRef sr
= NULL
;
2849 require_quiet(ocspResponse
, errOut
);
2850 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
2851 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
2852 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
2853 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
2854 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
2855 // Check if this response is fresher than any (cached) response we might still have in the rvc.
2856 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
2858 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
2859 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
2860 check whether the leaf was revoked (we are already checking the rest of
2862 /* Check the OCSP response signature and verify the response. */
2863 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
2864 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
2866 // If we get here, we have a properly signed ocsp response
2867 // but we haven't checked dates yet.
2869 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
2870 if (sr
->certStatus
== CS_Good
) {
2871 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
2872 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
2873 } else if (sr
->certStatus
== CS_Revoked
) {
2874 // Expire revoked responses when the subject certificate itself expires.
2875 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
2878 // Ok we like the new response, let's toss the old one.
2880 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
2882 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
2883 rvc
->ocspResponse
= ocspResponse
;
2884 ocspResponse
= NULL
;
2886 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
2887 rvc
->ocspSingleResponse
= sr
;
2890 rvc
->done
= sr_valid
;
2893 if (sr
) SecOCSPSingleResponseDestroy(sr
);
2894 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2897 /* Callback from async http code after an ocsp response has been received. */
2898 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
2899 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
2900 SecPVCRef pvc
= rvc
->pvc
;
2901 SecOCSPResponseRef ocspResponse
= NULL
;
2902 if (http
->response
) {
2903 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
2905 /* Parse the returned data as if it's an ocspResponse. */
2906 ocspResponse
= SecOCSPResponseCreate(data
);
2911 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
2912 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
2915 /* Clear the data for the next response. */
2916 asynchttp_free(http
);
2917 SecORVCFetchNext(rvc
);
2921 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
2922 SecORVCUpdatePVC(rvc
);
2924 if (!--pvc
->asyncJobCount
) {
2925 secdebug("rvc", "done with all async jobs");
2926 SecPathBuilderStep(pvc
->builder
);
2931 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
2932 SecORVCRef orvc
= NULL
;
2933 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
2935 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
2938 orvc
->certIX
= certIX
;
2939 orvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
2940 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2941 orvc
->http
.completed
= SecOCSPFetchCompleted
;
2942 orvc
->http
.info
= orvc
;
2943 orvc
->ocspRequest
= NULL
;
2944 orvc
->responderIX
= 0;
2945 orvc
->responder
= NULL
;
2946 orvc
->nextUpdate
= NULL_TIME
;
2947 orvc
->ocspResponse
= NULL
;
2948 orvc
->ocspSingleResponse
= NULL
;
2951 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2952 /* The certIX + 1 is ok here since certCount is always at least 1
2953 less than the actual number of certs in SecPVCCheckRevocation. */
2954 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(pvc
, certIX
+ 1);
2955 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
2960 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
2961 /* Get stapled OCSP responses */
2962 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->pvc
->builder
);
2964 if(ocspResponsesData
) {
2965 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
2966 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2967 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2968 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
2970 CFRelease(ocspResponsesData
);
2975 /********************************************************
2976 ******************* CRL RVC Functions ******************
2977 ********************************************************/
2979 #include <../trustd/SecTrustOSXEntryPoints.h>
2980 OSStatus errSecCertificateRevoked
= -67820;
2981 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
2983 /* CRL Revocation verification context. */
2984 struct OpaqueSecCRVC
{
2985 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
2986 async_ocspd_t async_ocspd
;
2988 /* Pointer to the pvc for this revocation check. */
2991 /* Pointer to the generic rvc for this revocation check */
2994 /* The current CRL status from ocspd. */
2997 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
3000 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
3001 current distribution point. */
3002 CFIndex distributionPointIX
;
3004 /* URL of current distribution point. */
3005 CFURLRef distributionPoint
;
3007 /* Date until which this revocation status is valid. */
3008 CFAbsoluteTime nextUpdate
;
3013 static void SecCRVCFinish(SecCRVCRef crvc
) {
3017 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
3018 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3019 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
3021 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
3022 while (rvc
->distributionPointIX
< crlDPCount
) {
3023 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
3024 rvc
->distributionPointIX
++;
3025 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
3027 /* We only support http and https responders currently. */
3028 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
3029 CFEqual(CFSTR("https"), scheme
) ||
3030 CFEqual(CFSTR("ldap"), scheme
));
3033 return distributionPoint
;
3040 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
3041 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3042 SecCertificatePathRef path
= rvc
->pvc
->path
;
3043 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3044 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
3045 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
3046 CFReleaseNull(serializedCertPath
);
3047 /* we got a response indicating that the CRL was checked */
3048 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
3050 /* ocspd doesn't give us the nextUpdate time, so set to default */
3051 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3055 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
3056 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3057 SecCRVCGetCRLStatus(rvc
);
3058 if (rvc
->status
== errSecCertificateRevoked
) {
3064 /* Fire off an async http request for this certs revocation status, return
3065 false if request was queued, true if we're done. */
3066 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
3067 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3068 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3069 SecCertificatePathRef path
= rvc
->pvc
->path
;
3070 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3071 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
3072 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
3073 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
3074 CFDataRef clientAuditToken
= NULL
;
3075 SecTaskRef task
= NULL
;
3076 audit_token_t auditToken
= {};
3077 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
3078 require(clientAuditToken
, out
);
3079 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3080 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3081 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3082 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
3083 rvc
->distributionPoint
, task
);
3086 CFReleaseNull(clientAuditToken
);
3087 CFReleaseNull(task
);
3088 /* Async request was posted, wait for reply. */
3096 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
3097 if (rvc
->status
== errSecCertificateRevoked
) {
3098 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
3099 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
3100 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
3101 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
3103 if (rvc
->pvc
&& rvc
->pvc
->info
) {
3104 /* make the revocation reason available in the trust result */
3105 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
3107 CFReleaseNull(cfreason
);
3111 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
3112 SecCRVCRef rvc
= ocspd
->info
;
3113 SecPVCRef pvc
= rvc
->pvc
;
3114 /* we got a response indicating that the CRL was checked */
3115 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
3116 rvc
->status
= ocspd
->response
;
3118 /* ocspd doesn't give us the nextUpdate time, so set to default */
3119 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3120 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
3121 SecCRVCUpdatePVC(rvc
);
3123 if (!--pvc
->asyncJobCount
) {
3124 secdebug("rvc", "done with all async jobs");
3125 SecPathBuilderStep(pvc
->builder
);
3128 if(SecCRVCFetchNext(rvc
)) {
3129 if (!--pvc
->asyncJobCount
) {
3130 secdebug("rvc", "done with all async jobs");
3131 SecPathBuilderStep(pvc
->builder
);
3137 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3138 SecCRVCRef crvc
= NULL
;
3139 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
3141 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
3144 crvc
->certIX
= certIX
;
3145 crvc
->status
= errSecInternal
;
3146 crvc
->distributionPointIX
= 0;
3147 crvc
->distributionPoint
= NULL
;
3148 crvc
->nextUpdate
= NULL_TIME
;
3149 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
3150 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
3151 crvc
->async_ocspd
.response
= errSecInternal
;
3152 crvc
->async_ocspd
.info
= crvc
;
3158 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
3159 if (rvc
->pvc
->check_revocation
&&
3160 CFEqual(kSecPolicyCheckRevocationCRL
, rvc
->pvc
->check_revocation
)) {
3161 /* Our client insists on CRLs */
3162 secinfo("rvc", "client told us to check CRL");
3165 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3166 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3167 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
3168 (rvc
->pvc
->check_revocation
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, rvc
->pvc
->check_revocation
))) {
3169 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
3170 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
3171 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
3176 #endif /* ENABLE_CRLS */
3178 static void SecRVCFinish(SecRVCRef rvc
) {
3180 SecORVCFinish(rvc
->orvc
);
3184 SecCRVCFinish(rvc
->crvc
);
3189 static void SecRVCDelete(SecRVCRef rvc
) {
3191 SecORVCFinish(rvc
->orvc
);
3196 SecCRVCFinish(rvc
->crvc
);
3202 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3203 secdebug("alloc", "%p", rvc
);
3205 rvc
->certIX
= certIX
;
3206 rvc
->orvc
= SecORVCCreate(rvc
, pvc
, certIX
);
3208 rvc
->crvc
= SecCRVCCreate(rvc
, pvc
, certIX
);
3213 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
3214 SecORVCUpdatePVC(rvc
->orvc
);
3216 SecCRVCUpdatePVC(rvc
->crvc
);
3221 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3222 if (!rvc
->pvc
->check_revocation
3223 || !CFEqual(rvc
->pvc
->check_revocation
, kSecPolicyCheckRevocationCRL
)) {
3229 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3234 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
3235 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
3236 if (SecRVCShouldCheckOCSP(rvc
)) {
3237 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
3238 SecORVCConsumeOCSPResponse(rvc
->orvc
,
3239 SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
),
3243 /* Don't check CRL cache if policy requested OCSP only */
3244 if (SecRVCShouldCheckCRL(rvc
)) {
3245 SecCRVCCheckRevocationCache(rvc
->crvc
);
3250 static bool SecRVCFetchNext(SecRVCRef rvc
) {
3251 bool OCSP_fetch_finished
= true;
3252 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
3253 if (SecRVCShouldCheckOCSP(rvc
)) {
3254 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
3256 if (OCSP_fetch_finished
) {
3257 /* we didn't start an OCSP background job for this cert */
3258 rvc
->pvc
->asyncJobCount
--;
3262 bool CRL_fetch_finished
= true;
3263 /* Don't check CRL cache if policy requested OCSP only */
3264 if (SecRVCShouldCheckCRL(rvc
)) {
3265 /* reset the distributionPointIX because we already iterated through the CRLDPs
3266 * in SecCRVCCheckRevocationCache */
3267 rvc
->crvc
->distributionPointIX
= 0;
3268 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
3270 if (CRL_fetch_finished
) {
3271 /* we didn't start a CRL background job for this cert */
3272 rvc
->pvc
->asyncJobCount
--;
3274 OCSP_fetch_finished
&= CRL_fetch_finished
;
3277 return OCSP_fetch_finished
;
3280 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
3281 secdebug("rvc", "checking revocation");
3282 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3283 bool completed
= true;
3284 if (certCount
<= 1) {
3285 /* Can't verify without an issuer; we're done */
3290 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
3291 * We can't check revocation for the final cert in the chain.
3296 /* We have done revocation checking already, we're done. */
3297 secdebug("rvc", "Not rechecking revocation");
3301 /* Setup things so we check revocation status of all certs except the
3303 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
3305 /* Note that if we are multi threaded and a job completes after it
3306 is started but before we return from this function, we don't want
3307 a callback to decrement asyncJobCount to zero before we finish issuing
3308 all the jobs. To avoid this we pretend we issued certCount async jobs,
3309 and decrement pvc->asyncJobCount for each cert that we don't start a
3310 background fetch for. */
3312 pvc
->asyncJobCount
= (unsigned int) certCount
;
3314 /* If we enable CRLS, we may end up with two async jobs per cert: one
3315 * for OCSP and one for fetching the CRL */
3316 pvc
->asyncJobCount
= 2 * (unsigned int)certCount
;
3318 secdebug("rvc", "set asyncJobCount to %d", pvc
->asyncJobCount
);
3320 /* Loop though certificates again and issue an ocsp fetch if the
3321 revocation status checking isn't done yet. */
3322 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3323 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
3324 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3325 SecRVCInit(rvc
, pvc
, certIX
);
3329 /* Ignore stapled OCSP responses only if CRLs are enabled and the
3330 * policy specifically requested CRLs only. */
3331 if (SecRVCShouldCheckOCSP(rvc
)) {
3332 /* If we have any OCSP stapled responses, check those first */
3333 SecORVCProcessStapledResponses(rvc
->orvc
);
3336 /* Then check the caches for revocation results. */
3337 SecRVCCheckRevocationCaches(rvc
);
3339 /* The check is done if we found cached responses from either method. */
3345 secdebug("rvc", "found cached response for cert: %ld", certIX
);
3349 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
3350 async http request for this cert's revocation status, unless we already successfully checked
3351 the revocation status of this cert based on the cache or stapled responses, */
3352 bool allow_fetch
= SecPathBuilderCanAccessNetwork(pvc
->builder
) && (pvc
->is_ev
|| pvc
->check_revocation
);
3353 bool fetch_done
= true;
3354 if (rvc
->done
|| !allow_fetch
) {
3355 /* We got a cache hit or we aren't allowed to access the network */
3356 SecRVCUpdatePVC(rvc
);
3358 /* We didn't really start any background jobs for this cert. */
3359 pvc
->asyncJobCount
--;
3361 pvc
->asyncJobCount
--;
3363 secdebug("rvc", "not fetching and job count is %d for cert %ld", pvc
->asyncJobCount
, certIX
);
3365 fetch_done
= SecRVCFetchNext(rvc
);
3368 /* We started at least one background fetch. */
3369 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
3374 /* Return false if we started any background jobs. */
3375 /* We can't just return !pvc->asyncJobCount here, since if we started any
3376 jobs the completion callback will be called eventually and it will call
3377 SecPathBuilderStep(). If for some reason everything completed before we
3378 get here we still want the outer SecPathBuilderStep() to terminate so we
3379 keep track of whether we started any jobs and return false if so. */
3383 static CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
3384 CFAbsoluteTime enu
= NULL_TIME
;
3385 enu
= rvc
->orvc
->nextUpdate
;
3387 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
3388 if (enu
== NULL_TIME
||
3389 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
3390 /* We didn't check OCSP or CRL next update time was sooner */
3391 enu
= crlNextUpdate
;
3398 void SecPolicyServerInitalize(void) {
3399 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3400 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3401 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3402 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3404 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3405 kSecPolicyCheckBasicCertificateProcessing
,
3406 SecPolicyCheckBasicCertificateProcessing
);
3407 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3408 kSecPolicyCheckCriticalExtensions
,
3409 SecPolicyCheckCriticalExtensions
);
3410 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3411 kSecPolicyCheckIdLinkage
,
3412 SecPolicyCheckIdLinkage
);
3413 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3414 kSecPolicyCheckKeyUsage
,
3415 SecPolicyCheckKeyUsage
);
3416 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3417 kSecPolicyCheckExtendedKeyUsage
,
3418 SecPolicyCheckExtendedKeyUsage
);
3419 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3420 kSecPolicyCheckBasicConstraints
,
3421 SecPolicyCheckBasicConstraints
);
3422 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3423 kSecPolicyCheckNonEmptySubject
,
3424 SecPolicyCheckNonEmptySubject
);
3425 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3426 kSecPolicyCheckQualifiedCertStatements
,
3427 SecPolicyCheckQualifiedCertStatements
);
3428 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3429 kSecPolicyCheckSSLHostname
,
3430 SecPolicyCheckSSLHostname
);
3431 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3432 kSecPolicyCheckEmail
,
3433 SecPolicyCheckEmail
);
3434 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3435 kSecPolicyCheckValidIntermediates
,
3436 SecPolicyCheckValidIntermediates
);
3437 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3438 kSecPolicyCheckValidLeaf
,
3439 SecPolicyCheckValidLeaf
);
3440 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3441 kSecPolicyCheckValidRoot
,
3442 SecPolicyCheckValidRoot
);
3443 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3444 kSecPolicyCheckIssuerCommonName
,
3445 SecPolicyCheckIssuerCommonName
);
3446 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3447 kSecPolicyCheckSubjectCommonNamePrefix
,
3448 SecPolicyCheckSubjectCommonNamePrefix
);
3449 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3450 kSecPolicyCheckSubjectCommonName
,
3451 SecPolicyCheckSubjectCommonName
);
3452 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3453 kSecPolicyCheckNotValidBefore
,
3454 SecPolicyCheckNotValidBefore
);
3455 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3456 kSecPolicyCheckChainLength
,
3457 SecPolicyCheckChainLength
);
3458 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3459 kSecPolicyCheckAnchorSHA1
,
3460 SecPolicyCheckAnchorSHA1
);
3461 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3462 kSecPolicyCheckAnchorSHA256
,
3463 SecPolicyCheckAnchorSHA256
);
3464 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3465 kSecPolicyCheckAnchorApple
,
3466 SecPolicyCheckAnchorApple
);
3467 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3468 kSecPolicyCheckSubjectOrganization
,
3469 SecPolicyCheckSubjectOrganization
);
3470 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3471 kSecPolicyCheckSubjectOrganizationalUnit
,
3472 SecPolicyCheckSubjectOrganizationalUnit
);
3473 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3474 kSecPolicyCheckEAPTrustedServerNames
,
3475 SecPolicyCheckEAPTrustedServerNames
);
3476 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3477 kSecPolicyCheckSubjectCommonNameTEST
,
3478 SecPolicyCheckSubjectCommonNameTEST
);
3479 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3480 kSecPolicyCheckRevocation
,
3481 SecPolicyCheckRevocation
);
3482 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3483 kSecPolicyCheckRevocationResponseRequired
,
3484 SecPolicyCheckRevocationResponseRequired
);
3485 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3486 kSecPolicyCheckNoNetworkAccess
,
3487 SecPolicyCheckNoNetworkAccess
);
3488 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3489 kSecPolicyCheckBlackListedLeaf
,
3490 SecPolicyCheckBlackListedLeaf
);
3491 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3492 kSecPolicyCheckGrayListedLeaf
,
3493 SecPolicyCheckGrayListedLeaf
);
3494 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3495 kSecPolicyCheckLeafMarkerOid
,
3496 SecPolicyCheckLeafMarkerOid
);
3497 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3498 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
3499 SecPolicyCheckLeafMarkerOidWithoutValueCheck
);
3500 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3501 kSecPolicyCheckIntermediateSPKISHA256
,
3502 SecPolicyCheckIntermediateSPKISHA256
);
3503 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3504 kSecPolicyCheckIntermediateEKU
,
3505 SecPolicyCheckIntermediateEKU
);
3506 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3507 kSecPolicyCheckIntermediateMarkerOid
,
3508 SecPolicyCheckIntermediateMarkerOid
);
3509 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3510 kSecPolicyCheckCertificatePolicy
,
3511 SecPolicyCheckCertificatePolicyOid
);
3512 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3513 kSecPolicyCheckWeakIntermediates
,
3514 SecPolicyCheckWeakIntermediates
);
3515 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3516 kSecPolicyCheckWeakLeaf
,
3517 SecPolicyCheckWeakLeaf
);
3518 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3519 kSecPolicyCheckWeakRoot
,
3520 SecPolicyCheckWeakRoot
);
3521 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3522 kSecPolicyCheckKeySize
,
3523 SecPolicyCheckKeySize
);
3524 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3525 kSecPolicyCheckSignatureHashAlgorithms
,
3526 SecPolicyCheckSignatureHashAlgorithms
);
3531 /********************************************************
3532 ****************** SecPVCRef Functions *****************
3533 ********************************************************/
3535 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3536 CFAbsoluteTime verifyTime
) {
3537 secdebug("alloc", "%p", pvc
);
3538 // Weird logging policies crashes.
3539 //secdebug("policy", "%@", policies);
3540 pvc
->builder
= builder
;
3541 pvc
->policies
= policies
;
3544 pvc
->verifyTime
= verifyTime
;
3546 pvc
->details
= NULL
;
3548 pvc
->valid_policy_tree
= NULL
;
3549 pvc
->callbacks
= NULL
;
3552 pvc
->asyncJobCount
= 0;
3553 pvc
->check_revocation
= NULL
;
3554 pvc
->response_required
= false;
3555 pvc
->optionally_ev
= false;
3560 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3561 secdebug("alloc", "%p", pvc
);
3563 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
) - 1;
3564 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3565 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3573 void SecPVCDelete(SecPVCRef pvc
) {
3574 secdebug("alloc", "%p", pvc
);
3575 CFReleaseNull(pvc
->policies
);
3576 CFReleaseNull(pvc
->details
);
3577 CFReleaseNull(pvc
->info
);
3578 if (pvc
->valid_policy_tree
) {
3579 policy_tree_prune(&pvc
->valid_policy_tree
);
3581 SecPVCDeleteRVCs(pvc
);
3582 CFReleaseNull(pvc
->path
);
3585 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3586 CF_CONSUMED CFArrayRef details
) {
3587 secdebug("policy", "%@", path
);
3588 bool samePath
= ((!path
&& !pvc
->path
) || (path
&& pvc
->path
&& CFEqual(path
, pvc
->path
)));
3590 /* Changing path makes us clear the Revocation Verification Contexts */
3591 SecPVCDeleteRVCs(pvc
);
3592 CFReleaseSafe(pvc
->path
);
3593 pvc
->path
= CFRetainSafe(path
);
3595 pvc
->details
= details
;
3596 CFReleaseNull(pvc
->info
);
3597 if (pvc
->valid_policy_tree
) {
3598 policy_tree_prune(&pvc
->valid_policy_tree
);
3604 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3605 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3608 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3609 return SecCertificatePathGetCount(pvc
->path
);
3612 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3613 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3616 bool SecPVCIsCertificateAtIndexSelfSigned(SecPVCRef pvc
, CFIndex ix
) {
3617 return SecCertificatePathSelfSignedIndex(pvc
->path
) == ix
;
3620 void SecPVCSetCheckRevocation(SecPVCRef pvc
, CFStringRef method
) {
3621 pvc
->check_revocation
= method
;
3622 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
3625 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3626 pvc
->response_required
= true;
3627 secdebug("rvc", "revocation response required");
3630 bool SecPVCIsAnchored(SecPVCRef pvc
) {
3631 return SecCertificatePathIsAnchored(pvc
->path
);
3634 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
3635 return pvc
->verifyTime
;
3638 static int32_t detailKeyToCssmErr(CFStringRef key
) {
3641 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
3642 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
3644 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
3645 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
3647 else if (CFEqual(key
, kSecPolicyCheckValidLeaf
) ||
3648 CFEqual(key
, kSecPolicyCheckValidIntermediates
) ||
3649 CFEqual(key
, kSecPolicyCheckValidRoot
)) {
3650 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
3656 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
3658 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
3659 bool result
= false;
3660 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, ix
);
3661 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3662 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3664 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3665 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3666 CFNumberRef allowedErrorNumber
= NULL
;
3667 if (!isDictionary(constraint
)) {
3670 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
3671 int32_t allowedErrorValue
= 0;
3672 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
3676 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
3677 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
3686 /* AUDIT[securityd](done):
3687 policy->_options is a caller provided dictionary, only its cf type has
3690 bool SecPVCSetResultForced(SecPVCRef pvc
,
3691 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
3693 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
3694 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
3695 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
3697 (force
? "force" : ""), result
);
3699 /* If this is not something the current policy cares about ignore
3700 this error and return true so our caller continues evaluation. */
3702 /* @@@ The right long term fix might be to check if none of the passed
3703 in policies contain this key, since not all checks are run for all
3705 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3706 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
3710 /* Check to see if the SecTrustSettings for the certificate in question
3711 tell us to ignore this error. */
3712 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
3713 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
3717 pvc
->result
= false;
3721 CFMutableDictionaryRef detail
=
3722 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
3724 /* Perhaps detail should have an array of results per key? As it stands
3725 in the case of multiple policy failures the last failure stands. */
3726 CFDictionarySetValue(detail
, key
, result
);
3731 bool SecPVCSetResult(SecPVCRef pvc
,
3732 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
3733 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
3736 /* AUDIT[securityd](done):
3737 key(ok) is a caller provided.
3738 value(ok, unused) is a caller provided.
3740 static void SecPVCValidateKey(const void *key
, const void *value
,
3742 SecPVCRef pvc
= (SecPVCRef
)context
;
3744 /* If our caller doesn't want full details and we failed earlier there is
3745 no point in doing additional checks. */
3746 if (!pvc
->result
&& !pvc
->details
)
3749 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
3750 CFDictionaryGetValue(pvc
->callbacks
, key
);
3754 /* Why not to have optional policy checks rant:
3755 Not all keys are in all dictionaries anymore, so why not make checks
3756 optional? This way a client can ask for something and the server will
3757 do a best effort based on the supported flags. It works since they are
3758 synchronized now, but we need some debug checking here for now. */
3759 pvc
->result
= false;
3761 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
3762 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
3763 pvc
->result
= false;
3765 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3766 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3767 pvc
->result
= false;
3770 /* Non standard validation phase, nothing is optional. */
3771 pvc
->result
= false;
3776 fcn(pvc
, (CFStringRef
)key
);
3779 /* AUDIT[securityd](done):
3780 policy->_options is a caller provided dictionary, only its cf type has
3783 bool SecPVCLeafChecks(SecPVCRef pvc
) {
3785 CFArrayRef policies
= pvc
->policies
;
3786 CFIndex ix
, count
= CFArrayGetCount(policies
);
3787 for (ix
= 0; ix
< count
; ++ix
) {
3788 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3790 /* Validate all keys for all policies. */
3791 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3792 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3793 if (!pvc
->result
&& !pvc
->details
)
3800 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3801 /* Check stuff common to intermediate and anchors. */
3802 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3803 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3804 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3805 && SecPVCIsAnchored(pvc
));
3806 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3807 /* Certificate has expired. */
3808 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
3809 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
3813 if (SecCertificateIsWeak(cert
)) {
3814 /* Certificate uses weak key. */
3815 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckWeakRoot
3816 : kSecPolicyCheckWeakIntermediates
, ix
, kCFBooleanFalse
))
3821 /* Perform anchor specific checks. */
3822 /* Don't think we have any of these. */
3824 /* Perform intermediate specific checks. */
3826 /* (k) Basic constraints only relevant for v3 and later. */
3827 if (SecCertificateVersion(cert
) >= 3) {
3828 const SecCEBasicConstraints
*bc
=
3829 SecCertificateGetBasicConstraints(cert
);
3830 if (!bc
|| !bc
->isCA
) {
3831 /* Basic constraints not present or not marked as isCA, illegal. */
3832 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3833 ix
, kCFBooleanFalse
, true))
3837 /* (l) max_path_length is checked elsewhere. */
3839 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3840 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3841 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3842 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3843 ix
, kCFBooleanFalse
, true))
3852 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3853 /* Check stuff common to intermediate and anchors. */
3855 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3856 if (NULL
!= otapkiRef
)
3858 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3859 CFRelease(otapkiRef
);
3860 if (NULL
!= blackListedKeys
)
3862 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3863 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3864 && SecPVCIsAnchored(pvc
));
3866 /* Check for blacklisted intermediates keys. */
3867 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3869 /* Check dgst against blacklist. */
3870 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3871 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3872 ix
, kCFBooleanFalse
, true);
3877 CFRelease(blackListedKeys
);
3885 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3887 /* Check stuff common to intermediate and anchors. */
3888 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3889 if (NULL
!= otapkiRef
)
3891 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3892 CFRelease(otapkiRef
);
3893 if (NULL
!= grayListKeys
)
3895 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3896 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
3897 && SecPVCIsAnchored(pvc
));
3899 /* Check for gray listed intermediates keys. */
3900 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3902 /* Check dgst against gray list. */
3903 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3904 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3905 ix
, kCFBooleanFalse
, true);
3910 CFRelease(grayListKeys
);
3918 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
3919 if (!isString(searchName
) && !isString(searchOid
)) {
3922 CFArrayRef policies
= pvc
->policies
;
3923 CFIndex ix
, count
= CFArrayGetCount(policies
);
3924 for (ix
= 0; ix
< count
; ++ix
) {
3925 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3926 CFStringRef policyName
= SecPolicyGetName(policy
);
3927 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
3928 /* Prefer a match of both name and OID */
3929 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
3930 if (CFEqual(searchOid
, policyOid
) &&
3931 CFEqual(searchName
, policyName
)) {
3932 if (policyIX
) { *policyIX
= ix
; }
3936 /* Next best is just OID. */
3937 if (!searchName
&& searchOid
&& policyOid
) {
3938 if (CFEqual(searchOid
, policyOid
)) {
3939 if (policyIX
) { *policyIX
= ix
; }
3943 if (!searchOid
&& searchName
&& policyName
) {
3944 if (CFEqual(searchName
, policyName
)) {
3945 if (policyIX
) { *policyIX
= ix
; }
3953 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
3954 if (!isString(stringValue
)) {
3957 bool result
= false;
3959 CFStringRef tmpStringValue
= NULL
;
3960 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
3961 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
3963 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
3965 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3966 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3967 /* Have to look for all the possible locations of name string */
3968 CFStringRef policyString
= NULL
;
3969 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
3970 if (!policyString
) {
3971 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
3973 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
3978 CFArrayRef policyStrings
= NULL
;
3979 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
3980 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
3981 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
3989 CFReleaseNull(tmpStringValue
);
3994 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
3995 uint32_t ourTSKeyUsage
= 0;
3996 uint32_t keyUsage
= 0;
3997 if (keyUsageNumber
&&
3998 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
3999 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
4000 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
4002 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
4003 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
4005 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
4006 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
4008 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
4009 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
4011 if (keyUsage
== kSecKeyUsageAll
) {
4012 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
4015 return ourTSKeyUsage
;
4018 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
4019 uint32_t ourTSKeyUsage
= 0;
4020 CFTypeRef policyKeyUsageType
= NULL
;
4022 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
4023 if (isArray(policyKeyUsageType
)) {
4024 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
4025 for (ix
= 0; ix
< count
; ix
++) {
4026 CFNumberRef policyKeyUsageNumber
= NULL
;
4027 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
4028 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
4030 } else if (isNumber(policyKeyUsageType
)) {
4031 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
4034 return ourTSKeyUsage
;
4037 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
4038 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
4039 int64_t keyUsageValue
= 0;
4040 uint32_t ourKeyUsage
= 0;
4042 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
4046 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
4050 /* We're using the key for revocation if we have the OCSPSigner policy.
4051 * @@@ If we support CRLs, we'd need to check for that policy here too.
4053 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
4054 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
4057 /* We're using the key for verifying a cert if it's a root/intermediate
4058 * in the chain. If the cert isn't in the path yet, we're about to add it,
4059 * so it's a root/intermediate. If there is no path, this is the leaf.
4061 CFIndex pathIndex
= -1;
4063 pathIndex
= SecCertificatePathGetIndexOfCertificate(pvc
->path
, certificate
);
4067 if (pathIndex
!= 0) {
4068 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
4071 /* The rest of the key usages may be specified by the policy(ies). */
4072 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4073 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4074 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4076 /* Get key usage from ALL policies */
4077 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
4078 for (ix
= 0; ix
< count
; ix
++) {
4079 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
4080 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4084 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
4091 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4092 /* We need to declare the SecTrustedApplicationRef type for those binaries
4093 * that don't include the OS X Security Framework headers. */
4094 typedef struct CF_BRIDGED_TYPE(id
) OpaqueSecTrustedApplicationRef
*SecTrustedApplicationRef
;
4096 #include <Security/SecTrustedApplicationPriv.h>
4097 #include <bsm/libbsm.h>
4098 #include <libproc.h>
4100 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
4101 bool result
= false;
4102 audit_token_t auditToken
= {};
4103 char path
[MAXPATHLEN
];
4105 require(appRef
&& clientAuditToken
, out
);
4106 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
4108 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
4109 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
4110 require(proc_pidpath(audit_token_to_pid(auditToken
), path
, sizeof(path
)) > 0, out
);
4112 if(errSecSuccess
== SecTrustedApplicationValidateWithPath((SecTrustedApplicationRef
)appRef
, path
)) {
4121 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
4122 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
4123 CFNumberRef keyUsageNumber
= NULL
;
4124 CFTypeRef trustedApplicationData
= NULL
;
4126 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false , keyUsageMatch
= false;
4127 bool result
= false;
4129 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4130 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
4131 SecPolicyRef policy
= NULL
;
4132 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4133 policyOid
= (policy
) ? policy
->_oid
: NULL
;
4135 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4137 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
4138 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
4139 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
4141 CFIndex policyIX
= -1;
4142 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
4143 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
4144 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
4146 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4147 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
4148 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
4149 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
4150 CFReleaseNull(clientAuditToken
);
4152 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
4153 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
4157 /* If we either didn't find the parameter in the dictionary or we got a match
4158 * against that parameter, for all possible parameters in the dictionary, then
4159 * this trust setting result applies to the output. */
4160 if (((!policyOid
&& !policyName
) || policyMatch
) &&
4161 (!policyString
|| policyStringMatch
) &&
4162 (!trustedApplicationData
|| applicationMatch
) &&
4163 (!keyUsageNumber
|| keyUsageMatch
)) {
4170 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
4171 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
4172 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4173 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4174 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4175 if (!isDictionary(constraint
)) {
4179 CFNumberRef resultNumber
= NULL
;
4180 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
4181 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
4182 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
4183 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
4184 resultValue
= kSecTrustSettingsResultTrustRoot
;
4187 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
4188 result
= resultValue
;
4195 bool SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
4196 bool shouldDeny
= false;
4197 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4198 for (certIX
= 0; certIX
< certCount
; certIX
++) {
4199 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, certIX
);
4200 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4201 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
4203 if (result
== kSecTrustSettingsResultDeny
) {
4204 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
4211 /* AUDIT[securityd](done):
4212 policy->_options is a caller provided dictionary, only its cf type has
4215 bool SecPVCPathChecks(SecPVCRef pvc
) {
4216 secdebug("policy", "begin path: %@", pvc
->path
);
4217 bool completed
= true;
4218 /* This needs to be initialized before we call any function that might call
4219 SecPVCSetResultForced(). */
4221 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
4222 if (pvc
->result
|| pvc
->details
) {
4223 SecPolicyCheckBasicCertificateProcessing(pvc
,
4224 kSecPolicyCheckBasicCertificateProcessing
);
4227 CFArrayRef policies
= pvc
->policies
;
4228 CFIndex count
= CFArrayGetCount(policies
);
4229 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
4230 /* Validate all keys for all policies. */
4231 pvc
->callbacks
= gSecPolicyPathCallbacks
;
4232 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
4233 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
4234 if (!pvc
->result
&& !pvc
->details
)
4238 /* Check whether the TrustSettings say to deny a cert in the path. */
4239 (void)SecPVCCheckUsageConstraints(pvc
);
4241 /* Check the things we can't check statically for the certificate path. */
4242 /* Critical Extensions, chainLength. */
4246 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
4247 bool pre_ev_check_result
= pvc
->result
;
4248 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
4249 pvc
->is_ev
= pvc
->result
;
4250 /* If ev checking failed, we still want to accept this chain
4251 as a non EV one, if it was valid as such. */
4252 pvc
->result
= pre_ev_check_result
;
4255 /* Check revocation always, since we don't want a lesser recoverable result
4256 * to prevent the check from occurring. */
4257 completed
= SecPVCCheckRevocation(pvc
);
4260 if (pvc
->result
|| pvc
->details
) {
4261 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
4262 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
4265 if (pvc
->is_ev
&& !pvc
->is_ct
) {
4266 pvc
->is_ct_whitelisted
= SecPVCCheckCTWhiteListedLeaf(pvc
);
4268 pvc
->is_ct_whitelisted
= false;
4272 secdebug("policy", "end %strusted completed: %d path: %@",
4273 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
4277 /* This function returns 0 to indicate revocation checking was not completed
4278 for this certificate chain, otherwise return to date at which the first
4279 piece of revocation checking info we used expires. */
4280 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
4281 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
4282 CFAbsoluteTime enu
= NULL_TIME
;
4283 if (certCount
<= 1 || !pvc
->rvcs
) {
4288 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
4289 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
4290 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
4291 if (thisCertNextUpdate
== 0) {
4293 /* We allow for CA certs to not be revocation checked if they
4294 have no ocspResponders nor CRLDPs to check against, but the leaf
4295 must be checked in order for us to claim we did revocation
4297 SecCertificateRef cert
=
4298 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
4299 CFArrayRef ocspResponders
= NULL
;
4300 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
4302 CFArrayRef crlDPs
= NULL
;
4303 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
4305 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
4307 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
4310 /* We can't check this cert so we don't consider it a soft
4311 failure that we didn't. Ideally we should support crl
4312 checking and remove this workaround, since that more
4317 secdebug("rvc", "revocation checking soft failure for cert: %ld",
4319 enu
= thisCertNextUpdate
;
4322 if (enu
== 0 || thisCertNextUpdate
< enu
) {
4323 enu
= thisCertNextUpdate
;
4327 secdebug("rvc", "revocation valid until: %lg", enu
);