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/SecTrustSettingsPriv.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 <securityd/SecRevocationDb.h>
66 #include <securityd/SecTrustLoggingServer.h>
67 #include <utilities/array_size.h>
68 #include <utilities/SecCFWrappers.h>
69 #include <utilities/SecAppleAnchorPriv.h>
70 #include "OTATrustUtilities.h"
71 #include "personalization.h"
72 #include <sys/codesign.h>
75 #include <Security/SecTaskPriv.h>
78 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
79 #ifndef DUMP_OCSPRESPONSES
80 #define DUMP_OCSPRESPONSES 0
83 #if DUMP_OCSPRESPONSES
88 static void secdumpdata(CFDataRef data
, const char *name
) {
89 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
90 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
97 /********************************************************
98 ****************** SecPolicy object ********************
99 ********************************************************/
101 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
102 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
104 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
106 CFArrayRef result
= NULL
;
107 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
108 if (NULL
== otapkiRef
)
113 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
114 CFRelease(otapkiRef
);
116 if (NULL
== evToPolicyAnchorDigest
)
121 CFArrayRef roots
= NULL
;
122 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
123 if (oid
&& evToPolicyAnchorDigest
)
125 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
126 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
128 secerror("EVRoot.plist has non array value");
133 CFReleaseSafe(evToPolicyAnchorDigest
);
138 static bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
139 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
142 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
143 policy_set_t valid_policies
) {
144 CFDictionaryRef keySizes
= NULL
;
145 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
147 /* Ensure that this certificate is a valid anchor for one of the
148 certificate policy oids specified in the leaf. */
149 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
151 bool good_ev_anchor
= false;
152 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
153 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
154 if (digests
&& CFArrayContainsValue(digests
,
155 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
156 secdebug("ev", "found anchor for policy oid");
157 good_ev_anchor
= true;
161 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
163 CFAbsoluteTime october2006
= 178761600;
164 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
165 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
166 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
168 if (SecCertificateVersion(certificate
) >= 3
169 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
170 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
171 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
172 secnotice("ev", "Anchor has invalid basic constraints"));
173 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
174 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
175 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
176 secnotice("ev", "Anchor has invalid key usage %u", ku
));
179 /* At least RSA 2048 or ECC NIST P-256. */
180 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
181 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
182 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
183 const void *values
[] = { rsaSize
, ecSize
};
184 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
185 &kCFTypeDictionaryKeyCallBacks
,
186 &kCFTypeDictionaryValueCallBacks
), notEV
);
187 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
188 secnotice("ev", "Anchor's public key is too weak for EV"));
193 CFReleaseNull(rsaSize
);
194 CFReleaseNull(ecSize
);
195 CFReleaseNull(keySizes
);
199 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
200 CFMutableDictionaryRef keySizes
= NULL
;
201 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
204 const SecCECertificatePolicies
*cp
;
205 cp
= SecCertificateGetCertificatePolicies(certificate
);
206 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
207 secnotice("ev", "SubCA missing certificate policies"));
208 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
209 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
210 secnotice("ev", "SubCA missing CRLDP"));
211 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
212 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
213 secnotice("ev", "SubCA has invalid basic constraints"));
214 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
215 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
216 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
217 secnotice("ev", "SubCA has invalid key usage %u", ku
));
219 /* 6.1.5 Key Sizes */
220 CFAbsoluteTime jan2011
= 315532800;
221 CFAbsoluteTime jan2014
= 410227200;
222 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
223 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
224 &kCFTypeDictionaryValueCallBacks
), notEV
);
225 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
226 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
227 SecCertificateNotValidAfter(certificate
) < jan2014
) {
228 /* At least RSA 1024 or ECC NIST P-256. */
229 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
230 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
231 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
232 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
234 /* At least RSA 2028 or ECC NIST P-256. */
235 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
236 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
237 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
238 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
241 /* 7.1.3 Algorithm Object Identifiers */
242 CFAbsoluteTime jan2016
= 473299200;
243 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
245 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
246 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
252 CFReleaseNull(rsaSize
);
253 CFReleaseNull(ecSize
);
254 CFReleaseNull(keySizes
);
258 bool SecPolicySubscriberCertificateCouldBeEV(SecCertificateRef certificate
) {
259 CFMutableDictionaryRef keySizes
= NULL
;
260 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
263 /* 3. Subscriber Certificate. */
265 /* (a) certificate Policies */
266 const SecCECertificatePolicies
*cp
;
267 cp
= SecCertificateGetCertificatePolicies(certificate
);
268 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
269 /* Now find at least one policy in here that has a qualifierID of id-qt 2
270 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
272 bool found_ev_anchor_for_leaf_policy
= false;
273 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
274 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
275 found_ev_anchor_for_leaf_policy
= true;
278 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
280 /* (b) cRLDistributionPoint
281 (c) authorityInformationAccess
282 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
285 /* (d) basicConstraints
286 If present, the cA field MUST be set false. */
287 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
289 require_action_quiet(bc
->isCA
== false, notEV
,
290 secnotice("ev", "Leaf has invalid basic constraints"));
294 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
296 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
,
297 secnotice("ev", "Leaf has invalid key usage %u", ku
));
301 /* The EV Cert Spec errata specifies this, though this is a check for SSL
302 not specifically EV. */
306 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
307 SecCertificateCopyExtendedKeyUsage(certificate
);
310 /* 6.1.5 Key Sizes */
311 CFAbsoluteTime jan2014
= 410227200;
312 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
313 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
314 &kCFTypeDictionaryValueCallBacks
), notEV
);
315 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
316 if (SecCertificateNotValidBefore(certificate
) < jan2014
) {
317 /* At least RSA 1024 or ECC NIST P-256. */
318 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
319 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
320 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
321 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
323 /* At least RSA 2028 or ECC NIST P-256. */
324 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
325 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
326 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
327 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
330 /* 6.3.2 Validity Periods */
331 CFAbsoluteTime jul2016
= 489024000;
332 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(certificate
);
333 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(certificate
);
334 if (SecCertificateNotValidBefore(certificate
) < jul2016
) {
335 /* Validity Period no greater than 60 months.
336 60 months is no more than 5 years and 2 leap days. */
337 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2);
338 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
339 secnotice("ev", "Leaf's validity period is more than 60 months"));
341 /* Validity Period no greater than 39 months.
342 39 months is no more than 3 years, 2 31-day months,
343 1 30-day month, and 1 leap day */
344 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1);
345 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
346 secnotice("ev", "Leaf has validity period longer than 39 months and issued after 30 June 2016"));
349 /* 7.1.3 Algorithm Object Identifiers */
350 CFAbsoluteTime jan2016
= 473299200;
351 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
353 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
354 notEV
, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
360 CFReleaseNull(rsaSize
);
361 CFReleaseNull(ecSize
);
362 CFReleaseNull(keySizes
);
366 /********************************************************
367 **************** SecPolicy Callbacks *******************
368 ********************************************************/
369 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
373 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
375 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
376 CFDataRef parentSubjectKeyID
= NULL
;
377 for (ix
= count
- 1; ix
>= 0; --ix
) {
378 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
379 /* If the previous certificate in the chain had a SubjectKeyID,
380 make sure it matches the current certificates AuthorityKeyID. */
381 if (parentSubjectKeyID
) {
382 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
383 SubjectKeyID can be critical. Currenty we don't check
385 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
386 if (authorityKeyID
) {
387 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
388 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
389 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
395 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
399 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
401 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
402 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
403 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
404 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
405 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
409 /* AUDIT[securityd](done):
410 policy->_options is a caller provided dictionary, only its cf type has
413 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
414 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
415 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
416 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
417 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
418 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
423 static void SecPolicyCheckBasicContraintsCommon(SecPVCRef pvc
,
424 CFStringRef key
, bool strict
) {
425 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
426 for (ix
= 0; ix
< count
; ++ix
) {
427 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
428 const SecCEBasicConstraints
*bc
=
429 SecCertificateGetBasicConstraints(cert
);
433 /* Leaf certificate has basic constraints extension. */
434 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
436 } else if (!bc
->critical
) {
437 /* Basic constraints extension is not marked critical. */
438 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
443 if (ix
> 0 || count
== 1) {
445 /* Non leaf certificate marked as isCA false. */
446 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
450 if (bc
->pathLenConstraintPresent
) {
451 if (bc
->pathLenConstraint
< (uint32_t)(ix
- 1)) {
453 /* @@@ If a self signed certificate is issued by
454 another cert that is trusted, then we are supposed
455 to treat the self signed cert itself as the anchor
456 for path length purposes. */
457 CFIndex ssix
= SecCertificatePathSelfSignedIndex(path
);
458 if (ssix
>= 0 && ix
>= ssix
) {
459 /* It's ok if the pathLenConstraint isn't met for
460 certificates signing a self signed cert in the
465 /* Path Length Constraint Exceeded. */
466 if (!SecPVCSetResult(pvc
, key
, ix
,
473 } else if (strict
&& ix
> 0) {
474 /* In strict mode all CA certificates *MUST* have a critical
475 basic constraints extension and the leaf certificate
476 *MUST NOT* have a basic constraints extension. */
477 /* CA certificate is missing basicConstraints extension. */
478 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
485 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
487 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
490 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
492 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
493 for (ix
= 0; ix
< count
; ++ix
) {
494 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
495 /* If the certificate has a subject, or
496 if it doesn't, and it's the leaf and not self signed,
497 and also has a critical subjectAltName extension it's valid. */
498 if (!SecCertificateHasSubject(cert
)) {
499 if (ix
== 0 && count
> 1) {
500 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
501 /* Leaf certificate with empty subject does not have
502 a critical subject alt name extension. */
503 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
507 /* CA certificate has empty subject. */
508 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
515 static void SecPolicyCheckQualifiedCertStatements(SecPVCRef pvc
,
519 /* Compare hostname suffix to domain name.
520 This function does not process wildcards, and allows hostname to match
521 any subdomain level of the provided domain.
523 To match, the last domain length chars of hostname must equal domain,
524 and the character immediately preceding domain in hostname (if any)
525 must be a dot. This means that domain 'bar.com' will match hostname
526 values 'host.bar.com' or 'host.sub.bar.com', but not 'host.foobar.com'.
528 Characters in each string are converted to lowercase for the comparison.
529 Trailing '.' characters in both names will be ignored.
531 Returns true on match, else false.
533 static bool SecDomainSuffixMatch(CFStringRef hostname
, CFStringRef domain
) {
534 CFStringInlineBuffer hbuf
= {}, dbuf
= {};
537 hlength
= CFStringGetLength(hostname
),
538 dlength
= CFStringGetLength(domain
);
539 CFRange hrange
= { 0, hlength
}, drange
= { 0, dlength
};
540 CFStringInitInlineBuffer(hostname
, &hbuf
, hrange
);
541 CFStringInitInlineBuffer(domain
, &dbuf
, drange
);
543 if((hlength
== 0) || (dlength
== 0)) {
544 /* trivial case with at least one empty name */
545 return (hlength
== dlength
) ? true : false;
548 /* trim off trailing dots */
549 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hlength
-1);
550 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dlength
-1);
552 hrange
.length
= --hlength
;
555 drange
.length
= --dlength
;
558 /* trim off leading dot in suffix, if present */
559 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, 0);
560 if((dlength
> 0) && (dch
== '.')) {
562 drange
.length
= --dlength
;
565 if(hlength
< dlength
) {
569 /* perform case-insensitive comparison of domain suffix */
570 for (hix
= (hlength
-dlength
),
571 dix
= drange
.location
; dix
< drange
.length
; dix
++) {
572 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, hix
);
573 dch
= CFStringGetCharacterFromInlineBuffer(&dbuf
, dix
);
574 if (towlower(hch
) != towlower(dch
)) {
579 /* require a dot prior to domain suffix, unless hostname == domain */
580 if(hlength
> dlength
) {
581 hch
= CFStringGetCharacterFromInlineBuffer(&hbuf
, (hlength
-(dlength
+1)));
590 #define kSecPolicySHA1Size 20
591 static const UInt8 kAppleCorpCASHA1
[kSecPolicySHA1Size
] = {
592 0xA1, 0x71, 0xDC, 0xDE, 0xE0, 0x8B, 0x1B, 0xAE, 0x30, 0xA1,
593 0xAE, 0x6C, 0xC6, 0xD4, 0x03, 0x3B, 0xFD, 0xEF, 0x91, 0xCE
596 /* Check whether hostname is in a particular set of allowed domains.
597 Returns true if OK, false if not allowed.
599 static bool SecPolicyCheckDomain(SecPVCRef pvc
, CFStringRef hostname
)
601 CFIndex count
= SecPVCGetCertificateCount(pvc
);
602 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
603 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
605 /* is this chain anchored by kAppleCorpCASHA1? */
606 CFDataRef corpSHA1
= CFDataCreateWithBytesNoCopy(NULL
,
607 kAppleCorpCASHA1
, kSecPolicySHA1Size
, kCFAllocatorNull
);
608 bool isCorpSHA1
= (corpSHA1
&& CFEqual(anchorSHA1
, corpSHA1
));
609 CFReleaseSafe(corpSHA1
);
611 /* limit hostname to specified domains */
612 const CFStringRef dnlist
[] = {
616 unsigned int idx
, dncount
=2;
617 for (idx
= 0; idx
< dncount
; idx
++) {
618 if (SecDomainSuffixMatch(hostname
, dnlist
[idx
])) {
624 /* %%% other CA pinning checks TBA */
629 /* AUDIT[securityd](done):
630 policy->_options is a caller provided dictionary, only its cf type has
633 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
635 /* @@@ Consider what to do if the caller passes in no hostname. Should
636 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
637 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
638 CFStringRef hostName
= (CFStringRef
)
639 CFDictionaryGetValue(policy
->_options
, key
);
640 if (!isString(hostName
)) {
641 /* @@@ We can't return an error here and making the evaluation fail
642 won't help much either. */
646 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
647 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
650 /* Hostname mismatch or no hostnames found in certificate. */
651 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
653 else if (!SecPolicyCheckDomain(pvc
, hostName
)) {
654 /* Hostname match, but domain not allowed for this CA */
655 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
658 if ((dnsMatch
|| pvc
->details
)
659 && SecPolicySubscriberCertificateCouldBeEV(leaf
)) {
660 secdebug("policy", "enabling optionally_ev");
661 pvc
->optionally_ev
= true;
666 /* AUDIT[securityd](done):
667 policy->_options is a caller provided dictionary, only its cf type has
670 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
671 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
672 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
673 if (!isString(email
)) {
674 /* We can't return an error here and making the evaluation fail
675 won't help much either. */
679 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
681 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
682 /* Hostname mismatch or no hostnames found in certificate. */
683 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
687 static void SecPolicyCheckValidIntermediates(SecPVCRef pvc
,
689 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
690 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
691 for (ix
= 1; ix
< count
- 1; ++ix
) {
692 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
693 if (!SecCertificateIsValid(cert
, verifyTime
)) {
694 /* Intermediate certificate has expired. */
695 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
701 static void SecPolicyCheckValidLeaf(SecPVCRef pvc
,
703 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
704 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
705 if (!SecCertificateIsValid(cert
, verifyTime
)) {
706 /* Leaf certificate has expired. */
707 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
712 static void SecPolicyCheckValidRoot(SecPVCRef pvc
,
714 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
715 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
717 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
718 if (!SecCertificateIsValid(cert
, verifyTime
)) {
719 /* Root certificate has expired. */
720 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
725 /* AUDIT[securityd](done):
726 policy->_options is a caller provided dictionary, only its cf type has
729 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
731 CFIndex count
= SecPVCGetCertificateCount(pvc
);
733 /* Can't check intermediates common name if there is no intermediate. */
734 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
738 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
739 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
740 CFStringRef commonName
=
741 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
742 if (!isString(commonName
)) {
743 /* @@@ We can't return an error here and making the evaluation fail
744 won't help much either. */
747 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
748 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
752 /* AUDIT[securityd](done):
753 policy->_options is a caller provided dictionary, only its cf type has
756 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
758 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
759 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
760 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
762 if (!isString(common_name
)) {
763 /* @@@ We can't return an error here and making the evaluation fail
764 won't help much either. */
767 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
768 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
772 /* AUDIT[securityd](done):
773 policy->_options is a caller provided dictionary, only its cf type has
776 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
778 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
779 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
780 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
782 if (!isString(prefix
)) {
783 /* @@@ We can't return an error here and making the evaluation fail
784 won't help much either. */
787 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
788 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
792 /* AUDIT[securityd](done):
793 policy->_options is a caller provided dictionary, only its cf type has
796 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
798 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
799 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
800 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
802 if (!isString(common_name
)) {
803 /* @@@ We can't return an error here and making the evaluation fail
804 won't help much either. */
807 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
808 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
812 /* AUDIT[securityd](done):
813 policy->_options is a caller provided dictionary, only its cf type has
816 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
818 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
819 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
820 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
822 /* @@@ We can't return an error here and making the evaluation fail
823 won't help much either. */
826 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
827 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
832 /* AUDIT[securityd](done):
833 policy->_options is a caller provided dictionary, only its cf type has
836 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
838 CFIndex count
= SecPVCGetCertificateCount(pvc
);
839 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
840 CFNumberRef chainLength
=
841 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
843 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
844 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
845 /* @@@ We can't return an error here and making the evaluation fail
846 won't help much either. */
849 if (value
!= count
) {
850 /* Chain length doesn't match policy requirement. */
851 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
856 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
857 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
858 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
860 bool foundMatch
= false;
862 foundMatch
= CFEqual(digest
, value
);
863 else if (isArray(value
))
864 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
866 /* @@@ We only support Data and Array but we can't return an error here so.
867 we let the evaluation fail (not much help) and assert in debug. */
874 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
875 CFIndex count
= SecPVCGetCertificateCount(pvc
);
876 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
877 CFDataRef anchorSHA256
= NULL
;
878 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
880 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
881 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
884 CFReleaseNull(anchorSHA256
);
889 /* AUDIT[securityd](done):
890 policy->_options is a caller provided dictionary, only its cf type has
893 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
895 CFIndex count
= SecPVCGetCertificateCount(pvc
);
896 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
897 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
899 if (!isDigestInPolicy(pvc
, key
, anchorSHA1
))
900 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, count
-1, kCFBooleanFalse
))
907 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
908 policy->_options is a caller provided dictionary, only its cf type has
911 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
913 SecCertificateRef cert
= NULL
;
914 CFDataRef digest
= NULL
;
916 if (SecPVCGetCertificateCount(pvc
) < 2) {
917 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
921 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
922 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
924 if (!isDigestInPolicy(pvc
, key
, digest
)) {
925 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
927 CFReleaseNull(digest
);
931 policy->_options is a caller provided dictionary, only its cf type has
934 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
936 CFIndex count
= SecPVCGetCertificateCount(pvc
);
937 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
938 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
939 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
940 SecAppleTrustAnchorFlags flags
= 0;
942 if (isDictionary(value
)) {
943 if (CFDictionaryGetValue(value
, kSecPolicyAppleAnchorIncludeTestRoots
)) {
944 flags
|= kSecAppleTrustAnchorFlagsIncludeTestAnchors
;
948 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
951 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
958 /* AUDIT[securityd](done):
959 policy->_options is a caller provided dictionary, only its cf type has
962 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
964 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
965 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
966 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
968 if (!isString(org
)) {
969 /* @@@ We can't return an error here and making the evaluation fail
970 won't help much either. */
973 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
974 /* Leaf Subject Organization mismatch. */
975 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
979 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
981 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
982 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
983 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
985 if (!isString(orgUnit
)) {
986 /* @@@ We can't return an error here and making the evaluation fail
987 won't help much either. */
990 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
991 /* Leaf Subject Organization mismatch. */
992 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
996 /* AUDIT[securityd](done):
997 policy->_options is a caller provided dictionary, only its cf type has
1000 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
1002 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1003 CFArrayRef trustedServerNames
= (CFArrayRef
)
1004 CFDictionaryGetValue(policy
->_options
, key
);
1005 /* No names specified means we accept any name. */
1006 if (!trustedServerNames
)
1008 if (!isArray(trustedServerNames
)) {
1009 /* @@@ We can't return an error here and making the evaluation fail
1010 won't help much either. */
1014 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1015 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
1016 /* Hostname mismatch or no hostnames found in certificate. */
1017 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1021 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
1022 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
1023 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
1024 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
1025 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
1026 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
1027 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
1028 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
1029 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
1030 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
1032 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
1033 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
1034 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
1035 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
1036 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
1037 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
1038 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
1039 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
1040 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
1041 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
1042 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
1043 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
1044 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
1045 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
1047 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
1050 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
1052 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1053 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
1055 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
1056 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
1057 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
1059 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1060 CFDataRef serial
= SecCertificateCopySerialNumber(cert
, NULL
);
1062 CFDataRef serial
= SecCertificateCopySerialNumber(cert
);
1066 CFIndex serial_length
= CFDataGetLength(serial
);
1067 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
1069 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
1074 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
1076 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
1078 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
1079 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
1081 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1082 CFReleaseSafe(serial
);
1091 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1092 if (NULL
!= otapkiRef
)
1094 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
1095 CFRelease(otapkiRef
);
1096 if (NULL
!= blackListedKeys
)
1098 /* Check for blacklisted intermediates keys. */
1099 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1102 /* Check dgst against blacklist. */
1103 if (CFSetContainsValue(blackListedKeys
, dgst
))
1105 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1109 CFRelease(blackListedKeys
);
1114 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
1116 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1117 if (NULL
!= otapkiRef
)
1119 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
1120 CFRelease(otapkiRef
);
1121 if (NULL
!= grayListedKeys
)
1123 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1125 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
1128 /* Check dgst against gray. */
1129 if (CFSetContainsValue(grayListedKeys
, dgst
))
1131 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1135 CFRelease(grayListedKeys
);
1140 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1142 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1143 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1144 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1146 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
1147 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1151 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
1153 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1154 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1155 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1157 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
1158 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1163 * The value is a dictionary. The dictionary contains keys indicating
1164 * whether the value is for Prod or QA. The values are the same as
1165 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
1167 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
1169 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1170 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1171 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1172 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
1174 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
1175 bool result
= false;
1177 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1182 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
1184 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1185 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1186 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1188 for (ix
= 1; ix
< count
- 1; ix
++) {
1189 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1190 if (SecCertificateHasMarkerExtension(cert
, value
))
1193 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1196 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
1198 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1199 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1200 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
1202 for (ix
= 1; ix
< count
- 1; ix
++) {
1203 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1204 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
1205 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
1210 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
1212 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1213 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1214 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
1216 for (ix
= 1; ix
< count
- 1; ix
++) {
1217 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1218 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
1219 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
1224 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
1226 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1227 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1228 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
1230 for (ix
= 1; ix
< count
- 1; ix
++) {
1231 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1232 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
1233 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
1238 /* Returns true if path is on the allow list for the authority key of the
1239 certificate at certix, false otherwise.
1241 static bool SecPVCCheckCertificateAllowList(SecPVCRef pvc
, CFIndex certix
)
1243 bool result
= false;
1244 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1245 CFStringRef authKey
= NULL
;
1246 CFArrayRef allowedCerts
= NULL
;
1247 SecOTAPKIRef otapkiRef
= NULL
;
1249 if (certix
< 0 || certix
>= count
) {
1253 //get authKeyID from the specified cert in the chain
1254 SecCertificateRef issuedCert
= SecPVCGetCertificateAtIndex(pvc
, certix
);
1255 CFDataRef authKeyID
= SecCertificateGetAuthorityKeyID(issuedCert
);
1256 if (NULL
== authKeyID
) {
1259 authKey
= CFDataCopyHexString(authKeyID
);
1260 if (NULL
== authKey
) {
1264 otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
1265 if (NULL
== otapkiRef
) {
1269 allowedCerts
= SecOTAPKICopyAllowListForAuthKeyID(otapkiRef
, authKey
);
1270 if (NULL
== allowedCerts
|| !CFArrayGetCount(allowedCerts
)) {
1274 //search sorted array for the SHA256 hash of a cert in the chain
1275 CFRange range
= CFRangeMake(0, CFArrayGetCount(allowedCerts
));
1276 for (ix
= 0; ix
<= certix
; ix
++) {
1277 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1282 CFDataRef certHash
= SecCertificateCopySHA256Digest(cert
);
1287 CFIndex position
= CFArrayBSearchValues(allowedCerts
, range
, certHash
,
1288 (CFComparatorFunction
)CFDataCompare
, NULL
);
1289 if (position
< CFArrayGetCount(allowedCerts
)) {
1290 CFDataRef possibleMatch
= CFArrayGetValueAtIndex(allowedCerts
, position
);
1291 if (!CFDataCompare(certHash
, possibleMatch
)) {
1292 //this cert is in the allowlist
1297 CFRelease(certHash
);
1301 CFReleaseNull(authKey
);
1302 CFReleaseNull(otapkiRef
);
1303 CFReleaseNull(allowedCerts
);
1307 #define DCMP(_idx_) memcmp(data+(8*_idx_), digest, 8)
1309 /* Returns true if leaf is on the CT whitelist */
1310 static bool SecPVCCheckCTWhiteListedLeaf(SecPVCRef pvc
)
1312 SecOTAPKIRef otapkiRef
= NULL
;
1313 CFDataRef whiteList
= NULL
;
1314 SecCertificateRef cert
= NULL
;
1315 CFDataRef dgst
= NULL
;
1316 bool result
= false;
1317 const uint8_t *digest
= NULL
;
1318 const uint8_t *data
= NULL
;
1319 require(otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef(), out
);
1320 require(whiteList
= SecOTAPKICopyCTWhiteList(otapkiRef
), out
);
1321 require(cert
= SecPVCGetCertificateAtIndex(pvc
, 0), out
);
1322 require(dgst
= SecCertificateCopySHA256Digest(cert
), out
);
1324 digest
= CFDataGetBytePtr(dgst
);
1325 data
= CFDataGetBytePtr(whiteList
);
1327 CFIndex h
= CFDataGetLength(whiteList
)/8-1;
1329 if(DCMP(l
)==0 || DCMP(h
)==0) {
1334 if(DCMP(l
)>0 || DCMP(h
)<0) {
1353 CFReleaseSafe(dgst
);
1354 CFReleaseSafe(whiteList
);
1355 CFReleaseSafe(otapkiRef
);
1359 /****************************************************************************
1360 *********************** New rfc5280 Chain Validation ***********************
1361 ****************************************************************************/
1363 #define POLICY_MAPPING 1
1364 #define POLICY_SUBTREES 1
1366 struct policy_tree_add_ctx
{
1368 policy_qualifier_t p_q
;
1371 /* 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}. */
1372 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1373 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1374 policy_set_t policy_set
;
1375 for (policy_set
= node
->expected_policy_set
;
1377 policy_set
= policy_set
->oid_next
) {
1378 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1379 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1386 /* 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}. */
1387 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1388 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1389 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1390 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1396 /* Return true iff node has a child with a valid_policy equal to oid. */
1397 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1399 policy_tree_t child
;
1400 for (child
= node
->children
; child
; child
= child
->siblings
) {
1401 if (oid_equal(child
->valid_policy
, (*oid
))) {
1408 /* 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. */
1409 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1410 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1411 policy_set_t policy_set
;
1412 bool added_node
= false;
1413 for (policy_set
= node
->expected_policy_set
;
1415 policy_set
= policy_set
->oid_next
) {
1416 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1417 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1425 /* 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. */
1426 static bool policy_tree_map_if_match(policy_tree_t node
, void *ctx
) {
1427 /* Can't map oidAnyPolicy. */
1428 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1431 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1432 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1433 policy_set_t policy_set
= NULL
;
1434 /* Generate the policy_set of sdps for matching idp */
1435 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1436 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1437 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1438 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1439 p_node
->oid
= mapping
->subjectDomainPolicy
;
1440 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1441 policy_set
= p_node
;
1445 policy_tree_set_expected_policy(node
, policy_set
);
1451 /* 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:
1452 (i) set the valid_policy to ID-P;
1453 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1454 (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. */
1455 static bool policy_tree_map_if_any(policy_tree_t node
, void *ctx
) {
1456 if (!oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1460 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1461 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1462 CFMutableDictionaryRef mappings
= NULL
;
1463 CFDataRef idp
= NULL
;
1464 CFDataRef sdp
= NULL
;
1465 require_quiet(mappings
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1466 &kCFTypeDictionaryValueCallBacks
),
1468 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1469 for (mapping_ix
= 0; mapping_ix
< mapping_count
; mapping_ix
++) {
1470 oid_t issuerDomainPolicy
= pm
->mappings
[mapping_ix
].issuerDomainPolicy
;
1471 oid_t subjectDomainPolicy
= pm
->mappings
[mapping_ix
].subjectDomainPolicy
;
1472 idp
= CFDataCreateWithBytesNoCopy(NULL
, issuerDomainPolicy
.data
, issuerDomainPolicy
.length
, kCFAllocatorNull
);
1473 sdp
= CFDataCreateWithBytesNoCopy(NULL
, subjectDomainPolicy
.data
, subjectDomainPolicy
.length
, kCFAllocatorNull
);
1474 CFMutableArrayRef sdps
= (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
1476 CFArrayAppendValue(sdps
, sdp
);
1478 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1479 &kCFTypeArrayCallBacks
), errOut
);
1480 CFArrayAppendValue(sdps
, sdp
);
1481 CFDictionarySetValue(mappings
, idp
, sdps
);
1488 /* Now we use the dictionary to generate the new nodes */
1489 CFDictionaryForEach(mappings
, ^(const void *key
, const void *value
) {
1490 CFDataRef idp
= key
;
1491 CFArrayRef sdps
= value
;
1493 /* (i) set the valid_policy to ID-P; */
1495 p_oid
.data
= (uint8_t *)CFDataGetBytePtr(idp
);
1496 p_oid
.length
= CFDataGetLength(idp
);
1498 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1499 policy_qualifier_t p_q
= node
->qualifier_set
;
1501 /* (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. */
1502 __block policy_set_t p_expected
= NULL
;
1503 CFArrayForEach(sdps
, ^(const void *value
) {
1504 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*p_expected
));
1505 p_node
->oid
.data
= (void *)CFDataGetBytePtr(value
);
1506 p_node
->oid
.length
= CFDataGetLength(value
);
1507 p_node
->oid_next
= p_expected
? p_expected
: NULL
;
1508 p_expected
= p_node
;
1511 policy_tree_add_sibling(node
, &p_oid
, p_q
, p_expected
);
1513 CFReleaseNull(mappings
);
1517 CFReleaseNull(mappings
);
1523 static bool policy_tree_map_delete_if_match(policy_tree_t node
, void *ctx
) {
1524 /* Can't map oidAnyPolicy. */
1525 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1528 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1529 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1530 /* If this node matches any of the idps, delete it. */
1531 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1532 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1533 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1534 policy_tree_remove_node(&node
);
1540 #endif /* POLICY_MAPPINGS */
1542 /* rfc5280 basic cert processing. */
1543 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1547 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1548 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1549 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1550 uint32_t n
= (uint32_t)count
;
1551 bool is_anchored
= SecPVCIsAnchored(pvc
);
1553 /* If the anchor is trusted we don't process the last cert in the
1557 /* trust may be restored for a path with an untrusted root that matches the allow list */
1558 pvc
->is_allowlisted
= SecPVCCheckCertificateAllowList(pvc
, n
- 1);
1559 if (!pvc
->is_allowlisted
) {
1560 /* Add a detail for the root not being trusted. */
1561 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1562 n
- 1, kCFBooleanFalse
, true))
1567 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1568 //policy_set_t user_initial_policy_set = NULL;
1569 //trust_anchor_t anchor;
1570 bool initial_policy_mapping_inhibit
= false;
1571 bool initial_explicit_policy
= false;
1572 bool initial_any_policy_inhibit
= false;
1574 /* Initialization */
1575 pvc
->valid_policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1577 CFMutableArrayRef permitted_subtrees
= NULL
;
1578 CFMutableArrayRef excluded_subtrees
= NULL
;
1579 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1580 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1581 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1582 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1583 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1584 SecPVCSetResultForced(pvc
, key
, 0, kCFBooleanFalse
, true));
1586 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1587 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1588 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1591 /* Path builder ensures we only get cert chains with proper issuer
1592 chaining with valid signatures along the way. */
1593 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1594 SecKeyRef working_public_key
= anchor
->public_key
;
1595 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1597 uint32_t i
, max_path_length
= n
;
1598 SecCertificateRef cert
= NULL
;
1599 for (i
= 1; i
<= n
; ++i
) {
1601 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1602 bool is_self_issued
= SecPVCIsCertificateAtIndexSelfIssued(pvc
, n
- i
);
1604 /* (a) Verify the basic certificate information. */
1605 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1606 using the working_public_key and the working_public_key_parameters. */
1608 /* Already done by chain builder. */
1609 if (!SecCertificateIsValid(cert
, verify_time
)) {
1610 CFStringRef fail_key
= i
== n
? kSecPolicyCheckValidLeaf
: kSecPolicyCheckValidIntermediates
;
1611 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1615 if (SecCertificateIsWeakKey(cert
)) {
1616 CFStringRef fail_key
= i
== n
? kSecPolicyCheckWeakLeaf
: kSecPolicyCheckWeakIntermediates
;
1617 if (!SecPVCSetResult(pvc
, fail_key
, n
- i
, kCFBooleanFalse
)) {
1622 /* @@@ cert.issuer == working_issuer_name. */
1626 if (!is_self_issued
|| i
== n
) {
1628 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1629 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1630 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1631 secnotice("policy", "name in excluded subtrees");
1632 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1635 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1636 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1637 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1638 secnotice("policy", "name not in permitted subtrees");
1639 if(!SecPVCSetResultForced(pvc
, key
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1645 if (pvc
->valid_policy_tree
) {
1646 const SecCECertificatePolicies
*cp
=
1647 SecCertificateGetCertificatePolicies(cert
);
1648 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1649 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1650 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1651 oid_t p_oid
= policy
->policyIdentifier
;
1652 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1653 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1654 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1655 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1656 policy_tree_add_if_match
, &ctx
)) {
1657 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1658 policy_tree_add_if_any
, &ctx
);
1662 /* The certificate policies extension includes the policy
1663 anyPolicy with the qualifier set AP-Q and either
1664 (a) inhibit_anyPolicy is greater than 0 or
1665 (b) i < n and the certificate is self-issued. */
1666 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1667 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1668 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1669 oid_t p_oid
= policy
->policyIdentifier
;
1670 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1671 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1672 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
- 1,
1673 policy_tree_add_expected
, (void *)p_q
);
1677 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1680 if (pvc
->valid_policy_tree
)
1681 policy_tree_prune(&pvc
->valid_policy_tree
);
1684 /* (f) Verify that either explicit_policy is greater than 0 or the
1685 valid_policy_tree is not equal to NULL. */
1686 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1687 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1688 secnotice("policy", "policy tree failure");
1689 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true)) {
1693 /* If Last Cert in Path */
1697 /* Prepare for Next Cert */
1699 /* (a) verify that anyPolicy does not appear as an
1700 issuerDomainPolicy or a subjectDomainPolicy */
1701 const SecCEPolicyMappings
*pm
= SecCertificateGetPolicyMappings(cert
);
1702 if (pm
&& pm
->present
) {
1703 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1704 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1705 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1706 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1707 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1708 /* Policy mapping uses anyPolicy, illegal. */
1709 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, n
- i
, kCFBooleanFalse
, true)) {
1716 /* (1) If the policy_mapping variable is greater than 0 */
1717 if (policy_mapping
> 0 && pvc
->valid_policy_tree
) {
1718 if (!policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1719 policy_tree_map_if_match
, (void *)pm
)) {
1720 /* 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. */
1721 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
, policy_tree_map_if_any
, (void *)pm
);
1723 } else if (pvc
->valid_policy_tree
) {
1724 /* (i) delete each node of depth i in the valid_policy_tree
1725 where ID-P is the valid_policy. */
1726 policy_tree_walk_depth(pvc
->valid_policy_tree
, i
,
1727 policy_tree_map_delete_if_match
, (void *)pm
);
1728 /* (ii) If there is a node in the valid_policy_tree of depth
1729 i-1 or less without any child nodes, delete that
1730 node. Repeat this step until there are no nodes of
1731 depth i-1 or less without children. */
1732 policy_tree_prune_childless(&pvc
->valid_policy_tree
, i
- 1);
1735 #endif /* POLICY_MAPPING */
1737 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1738 //working_public_key = SecCertificateCopyPublicKey(cert);
1739 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1740 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1742 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1744 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1745 if (permitted_subtrees_in_cert
) {
1746 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1749 // could do something smart here to avoid inserting the exact same constraint
1750 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1751 if (excluded_subtrees_in_cert
) {
1752 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1753 CFRange range
= { 0, num_trees
};
1754 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1758 if (!is_self_issued
) {
1759 if (explicit_policy
)
1763 if (inhibit_any_policy
)
1764 inhibit_any_policy
--;
1767 const SecCEPolicyConstraints
*pc
=
1768 SecCertificateGetPolicyConstraints(cert
);
1770 if (pc
->requireExplicitPolicyPresent
1771 && pc
->requireExplicitPolicy
< explicit_policy
) {
1772 explicit_policy
= pc
->requireExplicitPolicy
;
1774 if (pc
->inhibitPolicyMappingPresent
1775 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1776 policy_mapping
= pc
->inhibitPolicyMapping
;
1780 const SecCEInhibitAnyPolicy
*iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1781 if (iap
&& iap
->skipCerts
< inhibit_any_policy
) {
1782 inhibit_any_policy
= iap
->skipCerts
;
1785 const SecCEBasicConstraints
*bc
=
1786 SecCertificateGetBasicConstraints(cert
);
1787 #if 0 /* Checked in chain builder pre signature verify already. */
1788 if (!bc
|| !bc
->isCA
) {
1789 /* Basic constraints not present or not marked as isCA, illegal. */
1790 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1791 n
- i
, kCFBooleanFalse
)) {
1797 if (!is_self_issued
) {
1798 if (max_path_length
> 0) {
1801 /* max_path_len exceeded, illegal. */
1802 if (!SecPVCSetResult(pvc
, kSecPolicyCheckBasicConstraints
,
1803 n
- i
, kCFBooleanFalse
)) {
1809 if (bc
&& bc
->pathLenConstraintPresent
1810 && bc
->pathLenConstraint
< max_path_length
) {
1811 max_path_length
= bc
->pathLenConstraint
;
1813 #if 0 /* Checked in chain builder pre signature verify already. */
1814 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1815 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1816 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1817 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1818 n
- i
, kCFBooleanFalse
, true)) {
1823 /* (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. */
1824 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1825 /* Certificate contains one or more unknown critical extensions. */
1826 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1827 n
- i
, kCFBooleanFalse
)) {
1831 } /* end loop over certs in path */
1833 cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1835 if (explicit_policy
)
1838 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1840 if (pc
->requireExplicitPolicyPresent
1841 && pc
->requireExplicitPolicy
== 0) {
1842 explicit_policy
= 0;
1846 //working_public_key = SecCertificateCopyPublicKey(cert);
1848 /* 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
1849 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1850 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1852 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1853 /* (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. */
1854 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1855 /* Certificate contains one or more unknown critical extensions. */
1856 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1857 0, kCFBooleanFalse
)) {
1861 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1863 if (pvc
->valid_policy_tree
) {
1864 #if !defined(NDEBUG)
1865 policy_tree_dump(pvc
->valid_policy_tree
);
1868 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1871 /* If either (1) the value of explicit_policy variable is greater than
1872 zero or (2) the valid_policy_tree is not NULL, then path processing
1874 if (!pvc
->valid_policy_tree
&& explicit_policy
== 0) {
1875 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1876 secnotice("policy", "policy tree failure");
1877 if (!SecPVCSetResultForced(pvc
, key
/* @@@ Need custom key */, 0, kCFBooleanFalse
, true)) {
1883 CFReleaseNull(permitted_subtrees
);
1884 CFReleaseNull(excluded_subtrees
);
1887 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1888 policy_set_t policies
= NULL
;
1889 const SecCECertificatePolicies
*cp
=
1890 SecCertificateGetCertificatePolicies(cert
);
1891 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1892 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1893 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1898 static void SecPolicyCheckEV(SecPVCRef pvc
,
1900 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1901 policy_set_t valid_policies
= NULL
;
1903 /* 6.1.7. Key Usage Purposes */
1905 CFAbsoluteTime jul2016
= 489024000;
1906 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1907 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1908 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1909 if (SecPVCSetResultForced(pvc
, key
,
1910 0, kCFBooleanFalse
, true)) {
1916 for (ix
= 0; ix
< count
; ++ix
) {
1917 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1918 policy_set_t policies
= policies_for_cert(cert
);
1921 /* anyPolicy in the leaf isn't allowed for EV, so only init
1922 valid_policies if we have real policies. */
1923 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1924 valid_policies
= policies
;
1927 } else if (ix
< count
- 1) {
1928 /* Subordinate CA */
1929 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1930 secnotice("ev", "subordinate certificate is not ev");
1931 if (SecPVCSetResultForced(pvc
, key
,
1932 ix
, kCFBooleanFalse
, true)) {
1933 policy_set_free(valid_policies
);
1934 policy_set_free(policies
);
1938 policy_set_intersect(&valid_policies
, policies
);
1941 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1942 secnotice("ev", "anchor certificate is not ev");
1943 if (SecPVCSetResultForced(pvc
, key
,
1944 ix
, kCFBooleanFalse
, true)) {
1945 policy_set_free(valid_policies
);
1946 policy_set_free(policies
);
1951 policy_set_free(policies
);
1952 if (!valid_policies
) {
1953 secnotice("ev", "valid_policies set is empty: chain not ev");
1954 /* If we ever get into a state where no policies are valid anymore
1955 this can't be an ev chain. */
1956 if (SecPVCSetResultForced(pvc
, key
,
1957 ix
, kCFBooleanFalse
, true)) {
1963 policy_set_free(valid_policies
);
1965 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1966 Subscriber MUST contain an OID defined by the CA in the certificate’s
1967 certificatePolicies extension that: (i) indicates which CA policy statement relates
1968 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1969 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1970 marks the certificate as being an EV Certificate.
1971 (b) EV Subordinate CA Certificates
1972 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1973 CA MUST contain one or more OIDs defined by the issuing CA that
1974 explicitly identify the EV Policies that are implemented by the Subordinate
1976 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1977 MAY contain the special anyPolicy OID (2.5.29.32.0).
1978 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1979 certificatePolicies or extendedKeyUsage extensions.
1985 * MARK: Certificate Transparency support
1991 Version sct_version; // 1 byte
1992 LogID id; // 32 bytes
1993 uint64 timestamp; // 8 bytes
1994 CtExtensions extensions; // 2 bytes len field, + n bytes data
1995 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1996 Version sct_version;
1997 SignatureType signature_type = certificate_timestamp;
1999 LogEntryType entry_type;
2000 select(entry_type) {
2001 case x509_entry: ASN.1Cert;
2002 case precert_entry: PreCert;
2004 CtExtensions extensions;
2006 } SignedCertificateTimestamp;
2010 #include <Security/SecureTransportPriv.h>
2013 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
2016 case SSL_SignatureAlgorithmRSA
:
2018 case SSL_HashAlgorithmSHA1
:
2019 return &CSSMOID_SHA1WithRSA
;
2020 case SSL_HashAlgorithmSHA256
:
2021 return &CSSMOID_SHA256WithRSA
;
2022 case SSL_HashAlgorithmSHA384
:
2023 return &CSSMOID_SHA384WithRSA
;
2027 case SSL_SignatureAlgorithmECDSA
:
2029 case SSL_HashAlgorithmSHA1
:
2030 return &CSSMOID_ECDSA_WithSHA1
;
2031 case SSL_HashAlgorithmSHA256
:
2032 return &CSSMOID_ECDSA_WithSHA256
;
2033 case SSL_HashAlgorithmSHA384
:
2034 return &CSSMOID_ECDSA_WithSHA384
;
2046 static size_t SSLDecodeUint16(const uint8_t *p
)
2048 return (p
[0]<<8 | p
[1]);
2051 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
2053 p
[0] = (len
>> 8)&0xff;
2054 p
[1] = (len
& 0xff);
2058 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
2060 p
[0] = (len
>> 16)&0xff;
2061 p
[1] = (len
>> 8)&0xff;
2062 p
[2] = (len
& 0xff);
2068 uint64_t SSLDecodeUint64(const uint8_t *p
)
2071 for(int i
=0; i
<8; i
++) {
2078 #include <libDER/DER_CertCrl.h>
2079 #include <libDER/DER_Encode.h>
2080 #include <libDER/asn1Types.h>
2083 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
2085 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2087 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
2089 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
2091 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2092 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
2093 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
2099 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
2101 SecCertificateRef leafCert
= NULL
;
2102 SecCertificateRef issuer
= NULL
;
2103 CFDataRef issuerKeyHash
= NULL
;
2104 CFDataRef tbs_precert
= NULL
;
2105 CFMutableDataRef data
= NULL
;
2107 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2108 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2109 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2111 require(leafCert
, out
);
2112 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
2113 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
2114 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
2116 require(issuerKeyHash
, out
);
2117 require(tbs_precert
, out
);
2118 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2119 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
2121 uint8_t *q
= CFDataGetMutableBytePtr(data
);
2122 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
2123 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
2124 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
2127 CFReleaseSafe(issuerKeyHash
);
2128 CFReleaseSafe(tbs_precert
);
2133 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
2135 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
2139 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
2141 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
2148 If the 'sct' is valid, add it to the validatingLogs dictionary.
2151 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
2153 - entry_type: 0 for x509 cert, 1 for precert.
2154 - entry: the cert or precert data.
2155 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
2156 - trustedLog: Dictionary contain the Trusted Logs.
2158 The SCT is valid if:
2159 - It decodes properly.
2160 - Its timestamp is less than 'verifyTime'.
2161 - It is signed by a log in 'trustedLogs'.
2162 - If entry_type = 0, the log must be currently qualified.
2163 - If entry_type = 1, the log may be expired.
2165 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
2166 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.
2171 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFArrayRef trustedLogs
, CFAbsoluteTime
*sct_at
)
2174 const uint8_t *logID
;
2175 const uint8_t *timestampData
;
2177 size_t extensionsLen
;
2178 const uint8_t *extensionsData
;
2181 size_t signatureLen
;
2182 const uint8_t *signatureData
;
2183 SecKeyRef pubKey
= NULL
;
2184 uint8_t *signed_data
= NULL
;
2185 const SecAsn1Oid
*oid
= NULL
;
2187 CFDataRef logIDData
= NULL
;
2188 CFDictionaryRef result
= 0;
2190 const uint8_t *p
= CFDataGetBytePtr(sct
);
2191 size_t len
= CFDataGetLength(sct
);
2193 require(len
>=43, out
);
2195 version
= p
[0]; p
++; len
--;
2196 logID
= p
; p
+=32; len
-=32;
2197 timestampData
= p
; p
+=8; len
-=8;
2198 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2200 require(len
>=extensionsLen
, out
);
2201 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
2203 require(len
>=4, out
);
2204 hashAlg
=p
[0]; p
++; len
--;
2205 sigAlg
=p
[0]; p
++; len
--;
2206 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
2207 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
2210 /* verify version: only v1(0) is supported */
2212 secerror("SCT version unsupported: %d\n", version
);
2216 /* verify timestamp not in the future */
2217 timestamp
= SSLDecodeUint64(timestampData
);
2218 if(timestamp
> vt
) {
2219 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
2226 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
2227 signed_data
= malloc(signed_data_len
);
2228 require(signed_data
, out
);
2231 *q
++ = 0; // certificate_timestamp
2232 memcpy(q
, timestampData
, 8); q
+=8;
2233 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
2234 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
2235 q
= SSLEncodeUint16(q
, extensionsLen
);
2236 memcpy(q
, extensionsData
, extensionsLen
);
2238 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
2240 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
2241 const void *key_data
;
2242 if(!isDictionary(dict
)) return false;
2243 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
2244 if(!isData(key_data
)) return false;
2245 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
2246 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
2247 CFReleaseSafe(valueID
);
2250 require(logData
, out
);
2253 // For external SCTs, only keep SCTs from currently valid logs.
2254 require(!CFDictionaryContainsKey(logData
, CFSTR("expiry")), out
);
2257 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
2258 require(logKeyData
, out
); // This failing would be an internal logic error
2259 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
2260 require(pubKey
, out
);
2262 oid
= oidForSigAlg(hashAlg
, sigAlg
);
2265 algId
.algorithm
= *oid
;
2266 algId
.parameters
.Data
= NULL
;
2267 algId
.parameters
.Length
= 0;
2269 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
2270 *sct_at
= TimestampToCFAbsoluteTime(timestamp
);
2273 secerror("SCT signature failed (log=%@)\n", logData
);
2277 CFReleaseSafe(logIDData
);
2278 CFReleaseSafe(pubKey
);
2284 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
2286 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
2288 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
2289 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
2290 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
2291 CFReleaseSafe(sct_time
);
2295 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
2297 CFMutableArrayRef SCTs
= NULL
;
2298 SecCertificateRef leafCert
= NULL
;
2299 SecCertificateRef issuer
= NULL
;
2300 CFArrayRef ocspResponsesData
= NULL
;
2301 SecOCSPRequestRef ocspRequest
= NULL
;
2303 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
2304 require_quiet(ocspResponsesData
, out
);
2306 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
2307 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2308 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
2310 require(leafCert
, out
);
2311 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
2312 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
2314 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
2317 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
2318 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
2319 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
2320 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
2321 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
2322 if(ocspSingleResponse
) {
2323 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
2324 if(singleResponseSCTs
) {
2325 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
2326 CFRelease(singleResponseSCTs
);
2328 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
2331 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
2334 if(CFArrayGetCount(SCTs
)==0) {
2335 CFReleaseNull(SCTs
);
2339 CFReleaseSafe(ocspResponsesData
);
2341 SecOCSPRequestFinalize(ocspRequest
);
2346 static void SecPolicyCheckCT(SecPVCRef pvc
, CFStringRef key
)
2348 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2349 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
2350 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
2351 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2352 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
2353 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
2354 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
2356 // This eventually contain list of logs who validated the SCT.
2357 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2358 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
2360 uint64_t vt
= TimestampFromCFAbsoluteTime(pvc
->verifyTime
);
2362 __block
bool at_least_one_currently_valid_external
= 0;
2363 __block
bool at_least_one_currently_valid_embedded
= 0;
2365 require(logsValidatingEmbeddedScts
, out
);
2366 require(currentLogsValidatingScts
, out
);
2368 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
2369 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
2370 CFArrayForEach(embeddedScts
, ^(const void *value
){
2371 CFAbsoluteTime sct_at
;
2372 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, trustedLogs
, &sct_at
);
2374 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
2375 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2376 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2377 at_least_one_currently_valid_embedded
= true;
2383 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
2384 CFArrayForEach(builderScts
, ^(const void *value
){
2385 CFAbsoluteTime sct_at
;
2386 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2388 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2389 at_least_one_currently_valid_external
= true;
2394 if(ocspScts
&& x509Entry
) {
2395 CFArrayForEach(ocspScts
, ^(const void *value
){
2396 CFAbsoluteTime sct_at
;
2397 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
2399 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
2400 at_least_one_currently_valid_external
= true;
2407 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
2410 is_ct = (A1 AND A2) OR (B1 AND B2).
2412 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
2413 A2: At least one embedded SCT from a currently valid log.
2415 B1: SCTs from 2 currently valid logs (from any source)
2416 B2: At least 1 external SCT from a currently valid log.
2422 if(at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2) {
2424 } else if(at_least_one_currently_valid_embedded
) {
2425 __block CFAbsoluteTime issuanceTime
= pvc
->verifyTime
;
2426 __block
int lifetime
; // in Months
2427 __block
unsigned once_or_current_qualified_embedded
= 0;
2429 /* Calculate issuance time base on timestamp of SCTs from current logs */
2430 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
2431 CFDictionaryRef log
= key
;
2432 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
2433 // Log is still qualified
2434 CFDateRef ts
= (CFDateRef
) value
;
2435 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
2436 if(timestamp
< issuanceTime
) {
2437 issuanceTime
= timestamp
;
2443 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
2444 CFDictionaryRef log
= key
;
2445 CFDateRef ts
= value
;
2446 CFDateRef expiry
= CFDictionaryGetValue(log
, CFSTR("expiry"));
2447 if(expiry
== NULL
|| CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
) {
2448 once_or_current_qualified_embedded
++;
2452 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
2454 CFCalendarGetComponentDifference(zuluCalendar
,
2455 SecCertificateNotValidBefore(leafCert
),
2456 SecCertificateNotValidAfter(leafCert
),
2457 0, "M", &_lifetime
);
2458 lifetime
= _lifetime
;
2461 unsigned requiredEmbeddedSctsCount
;
2463 if (lifetime
< 15) {
2464 requiredEmbeddedSctsCount
= 2;
2465 } else if (lifetime
<= 27) {
2466 requiredEmbeddedSctsCount
= 3;
2467 } else if (lifetime
<= 39) {
2468 requiredEmbeddedSctsCount
= 4;
2470 requiredEmbeddedSctsCount
= 5;
2473 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
2479 CFReleaseSafe(logsValidatingEmbeddedScts
);
2480 CFReleaseSafe(currentLogsValidatingScts
);
2481 CFReleaseSafe(builderScts
);
2482 CFReleaseSafe(embeddedScts
);
2483 CFReleaseSafe(ocspScts
);
2484 CFReleaseSafe(precertEntry
);
2485 CFReleaseSafe(trustedLogs
);
2486 CFReleaseSafe(x509Entry
);
2489 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
2490 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2492 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
2493 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
2495 for (ix
= 0; ix
< count
; ix
++) {
2496 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2497 policy_set_t policies
= policies_for_cert(cert
);
2499 if (policy_set_contains(policies
, &key_value
)) {
2506 static void SecPolicyCheckCertificatePolicyOid(SecPVCRef pvc
, CFStringRef key
)
2508 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2509 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2510 bool result
= false;
2512 if (CFGetTypeID(value
) == CFDataGetTypeID())
2514 result
= checkPolicyOidData(pvc
, value
);
2515 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
2516 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
2518 result
= checkPolicyOidData(pvc
, dataOid
);
2523 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2528 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2530 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2531 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2532 if (isString(value
)) {
2533 SecPVCSetCheckRevocation(pvc
, value
);
2537 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2539 SecPVCSetCheckRevocationResponseRequired(pvc
);
2542 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
2543 SecPVCSetCheckRevocationOnline(pvc
);
2546 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2548 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2549 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2550 if (value
== kCFBooleanTrue
) {
2551 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2553 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
2557 static void SecPolicyCheckWeakIntermediates(SecPVCRef pvc
,
2559 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2560 for (ix
= 1; ix
< count
- 1; ++ix
) {
2561 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2562 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2563 /* Intermediate certificate has a weak key. */
2564 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2570 static void SecPolicyCheckWeakLeaf(SecPVCRef pvc
,
2572 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
2573 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2574 /* Leaf certificate has a weak key. */
2575 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
2580 static void SecPolicyCheckWeakRoot(SecPVCRef pvc
,
2582 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2584 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2585 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2586 /* Root certificate has a weak key. */
2587 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2592 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
2593 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2594 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2595 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
2596 for (ix
= 0; ix
< count
; ++ix
) {
2597 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2598 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
2599 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2605 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
2607 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2608 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2609 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
2610 for (ix
= 0; ix
< count
; ++ix
) {
2611 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2612 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
2613 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2619 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
2620 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2621 require_quiet(leaf
, out
);
2623 /* Leaf certificates that expire before Jan 3 2017 can get a pass.
2624 * They must be updated before this goes live. */
2625 if (SecCertificateNotValidAfter(leaf
) < 505200000.0) {
2629 /* And now a few special snowflakes */
2631 /* subject:/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Domain 2009) */
2632 /* issuer :/C=IE/O=Baltimore/OU=CyberTrust/CN=Baltimore CyberTrust Root */
2633 /* Not After : Dec 19 17:25:36 2019 GMT */
2634 static const uint8_t vodafone
[] = {
2635 0xC5, 0x0E, 0x88, 0xE5, 0x20, 0xA8, 0x10, 0x41, 0x1D, 0x63,
2636 0x4C, 0xB8, 0xF9, 0xCC, 0x93, 0x9B, 0xFD, 0x76, 0x93, 0x99
2639 CFIndex intermediate_ix
= SecPVCGetCertificateCount(pvc
) - 2;
2640 require_quiet(intermediate_ix
> 0, out
);
2641 SecCertificateRef intermediate
= SecPVCGetCertificateAtIndex(pvc
, intermediate_ix
);
2642 CFDataRef fingerprint
= SecCertificateGetSHA1Digest(intermediate
);
2643 require_quiet(fingerprint
, out
);
2644 const unsigned int len
= 20;
2645 const uint8_t *dp
= CFDataGetBytePtr(fingerprint
);
2646 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
2654 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
2656 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
2658 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2660 CFDataRef clientAuditToken
= NULL
;
2661 SecTaskRef task
= NULL
;
2663 CFStringRef signingIdentifier
= NULL
;
2665 /* Only for Safari and WebKit. */
2667 require_quiet(signingIdentifier
= CFRetainSafe(CFBundleGetIdentifier(CFBundleGetMainBundle())), out
);
2669 require_quiet(clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
), out
);
2670 audit_token_t auditToken
= {};
2671 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
2672 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
2673 require_quiet(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
2674 require_quiet(signingIdentifier
= SecTaskCopySigningIdentifier(task
, NULL
), out
);
2676 require_quiet(CFStringHasPrefix(signingIdentifier
, CFSTR("com.apple.Safari")) ||
2677 CFStringHasPrefix(signingIdentifier
, CFSTR("com.apple.mobilesafari")) ||
2678 CFStringHasPrefix(signingIdentifier
, CFSTR("com.apple.WebKit.Networking")) ||
2679 /* Or one of our test apps */
2680 CFStringHasPrefix(signingIdentifier
, CFSTR("com.apple.security.SecurityTests")) ||
2681 CFStringHasPrefix(signingIdentifier
, CFSTR("com.apple.security.SecurityDevTests")), out
);
2683 Boolean keyInPolicy
= false;
2684 CFArrayRef policies
= pvc
->policies
;
2685 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2686 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2687 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2688 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2693 /* We only enforce this check when *both* of the following are true:
2694 * 1. One of the certs in the path has this usage constraint, and
2695 * 2. One of the policies in the PVC has this key
2696 * (As compared to normal policy options which require only one to be true..) */
2697 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2700 /* Ignore the anchor if it's trusted */
2701 if (SecCertificatePathIsAnchored(pvc
->path
)) {
2704 for (ix
= 0; ix
< count
; ++ix
) {
2705 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2706 if (SecCertificateIsWeakHash(cert
)) {
2707 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
2708 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2716 CFReleaseNull(clientAuditToken
);
2717 CFReleaseNull(task
);
2719 CFReleaseNull(signingIdentifier
);
2723 #define ENABLE_CRLS (TARGET_OS_MAC && !TARGET_OS_IPHONE)
2727 /********************************************************
2728 ****************** SecRVCRef Functions *****************
2729 ********************************************************/
2730 typedef struct OpaqueSecORVC
*SecORVCRef
;
2732 typedef struct OpaqueSecCRVC
*SecCRVCRef
;
2735 /* Revocation verification context. */
2736 struct OpaqueSecRVC
{
2737 /* Pointer to the pvc for this revocation check */
2740 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2743 /* The OCSP Revocation verification context */
2750 /* Valid database info for this revocation check */
2751 SecValidInfoRef valid_info
;
2755 typedef struct OpaqueSecRVC
*SecRVCRef
;
2758 /********************************************************
2759 ****************** OCSP RVC Functions ******************
2760 ********************************************************/
2761 const CFAbsoluteTime kSecDefaultOCSPResponseTTL
= 24.0 * 60.0 * 60.0;
2762 const CFAbsoluteTime kSecOCSPResponseOnlineTTL
= 5.0 * 60.0;
2763 #define OCSP_RESPONSE_TIMEOUT (3 * NSEC_PER_SEC)
2765 /* OCSP Revocation verification context. */
2766 struct OpaqueSecORVC
{
2767 /* Will contain the response data. */
2770 /* Pointer to the pvc for this revocation check. */
2773 /* Pointer to the generic rvc for this revocation check */
2776 /* The ocsp request we send to each responder. */
2777 SecOCSPRequestRef ocspRequest
;
2779 /* The freshest response we received so far, from stapling or cache or responder. */
2780 SecOCSPResponseRef ocspResponse
;
2782 /* The best validated candidate single response we received so far, from stapling or cache or responder. */
2783 SecOCSPSingleResponseRef ocspSingleResponse
;
2785 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
2788 /* Index in array returned by SecCertificateGetOCSPResponders() for current
2790 CFIndex responderIX
;
2792 /* URL of current responder. */
2795 /* Date until which this revocation status is valid. */
2796 CFAbsoluteTime nextUpdate
;
2801 static void SecORVCFinish(SecORVCRef orvc
) {
2802 secdebug("alloc", "%p", orvc
);
2803 asynchttp_free(&orvc
->http
);
2804 if (orvc
->ocspRequest
) {
2805 SecOCSPRequestFinalize(orvc
->ocspRequest
);
2806 orvc
->ocspRequest
= NULL
;
2808 if (orvc
->ocspResponse
) {
2809 SecOCSPResponseFinalize(orvc
->ocspResponse
);
2810 orvc
->ocspResponse
= NULL
;
2811 if (orvc
->ocspSingleResponse
) {
2812 SecOCSPSingleResponseDestroy(orvc
->ocspSingleResponse
);
2813 orvc
->ocspSingleResponse
= NULL
;
2818 #define MAX_OCSP_RESPONDERS 3
2819 #define OCSP_REQUEST_THRESHOLD 10
2821 /* Return the next responder we should contact for this rvc or NULL if we
2822 exhausted them all. */
2823 static CFURLRef
SecORVCGetNextResponder(SecORVCRef rvc
) {
2824 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
2825 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
2826 if (ocspResponders
) {
2827 CFIndex responderCount
= CFArrayGetCount(ocspResponders
);
2828 if (responderCount
>= OCSP_REQUEST_THRESHOLD
) {
2829 secnotice("rvc", "too many ocsp responders (%ld)", (long)responderCount
);
2832 while (rvc
->responderIX
< responderCount
&& rvc
->responderIX
< MAX_OCSP_RESPONDERS
) {
2833 CFURLRef responder
= CFArrayGetValueAtIndex(ocspResponders
, rvc
->responderIX
);
2835 CFStringRef scheme
= CFURLCopyScheme(responder
);
2837 /* We only support http and https responders currently. */
2838 bool valid_responder
= (CFEqual(CFSTR("http"), scheme
) ||
2839 CFEqual(CFSTR("https"), scheme
));
2841 if (valid_responder
)
2849 /* Fire off an async http request for this certs revocation status, return
2850 false if request was queued, true if we're done. */
2851 static bool SecORVCFetchNext(SecORVCRef rvc
) {
2852 while ((rvc
->responder
= SecORVCGetNextResponder(rvc
))) {
2853 CFDataRef request
= SecOCSPRequestGetDER(rvc
->ocspRequest
);
2857 secinfo("rvc", "Sending http ocsp request for cert %ld", rvc
->certIX
);
2858 if (!asyncHttpPost(rvc
->responder
, request
, OCSP_RESPONSE_TIMEOUT
, &rvc
->http
)) {
2859 /* Async request was posted, wait for reply. */
2869 /* Process a verified ocsp response for a given cert. Return true if the
2870 certificate status was obtained. */
2871 static bool SecOCSPSingleResponseProcess(SecOCSPSingleResponseRef
this,
2874 switch (this->certStatus
) {
2876 secdebug("ocsp", "CS_Good for cert %" PRIdCFIndex
, rvc
->certIX
);
2877 /* @@@ Mark cert as valid until a given date (nextUpdate if we have one)
2878 in the info dictionary. */
2879 //cert.revokeCheckGood(true);
2880 rvc
->nextUpdate
= this->nextUpdate
== NULL_TIME
? this->thisUpdate
+ kSecDefaultOCSPResponseTTL
: this->nextUpdate
;
2884 secdebug("ocsp", "CS_Revoked for cert %" PRIdCFIndex
, rvc
->certIX
);
2885 /* @@@ Mark cert as revoked (with reason) at revocation date in
2886 the info dictionary, or perhaps we should use a different key per
2887 reason? That way a client using exceptions can ignore some but
2889 SInt32 reason
= this->crlReason
;
2890 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
2891 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
2893 if (rvc
->pvc
&& rvc
->pvc
->info
) {
2894 /* make the revocation reason available in the trust result */
2895 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
2897 CFRelease(cfreason
);
2901 /* not an error, no per-cert status, nothing here */
2902 secdebug("ocsp", "CS_Unknown for cert %" PRIdCFIndex
, rvc
->certIX
);
2906 secnotice("ocsp", "BAD certStatus (%d) for cert %" PRIdCFIndex
,
2907 (int)this->certStatus
, rvc
->certIX
);
2915 static void SecORVCUpdatePVC(SecORVCRef rvc
) {
2916 if (rvc
->ocspSingleResponse
) {
2917 SecOCSPSingleResponseProcess(rvc
->ocspSingleResponse
, rvc
);
2919 if (rvc
->ocspResponse
) {
2920 rvc
->nextUpdate
= SecOCSPResponseGetExpirationTime(rvc
->ocspResponse
);
2924 typedef void (^SecOCSPEvaluationCompleted
)(SecTrustResultType tr
);
2927 SecOCSPEvaluateCompleted(const void *userData
,
2928 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
2929 SecTrustResultType result
) {
2930 SecOCSPEvaluationCompleted evaluated
= (SecOCSPEvaluationCompleted
)userData
;
2932 Block_release(evaluated
);
2936 static bool SecOCSPResponseEvaluateSigner(SecORVCRef rvc
, CFArrayRef signers
, CFArrayRef issuers
, CFAbsoluteTime verifyTime
) {
2937 __block
bool evaluated
= false;
2938 bool trusted
= false;
2939 if (!signers
|| !issuers
) {
2943 /* Verify the signer chain against the OCSPSigner policy, using the issuer chain as anchors. */
2944 const void *ocspSigner
= SecPolicyCreateOCSPSigner();
2945 CFArrayRef policies
= CFArrayCreate(kCFAllocatorDefault
,
2946 &ocspSigner
, 1, &kCFTypeArrayCallBacks
);
2947 CFRelease(ocspSigner
);
2949 SecOCSPEvaluationCompleted completed
= Block_copy(^(SecTrustResultType result
) {
2950 if (result
== kSecTrustResultProceed
|| result
== kSecTrustResultUnspecified
) {
2955 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
2956 SecPathBuilderRef oBuilder
= SecPathBuilderCreate(clientAuditToken
,
2957 signers
, issuers
, true, false,
2958 policies
, NULL
, NULL
, NULL
,
2960 SecOCSPEvaluateCompleted
, completed
);
2961 /* Build the chain(s), evaluate them, call the completed block, free the block and builder */
2962 SecPathBuilderStep(oBuilder
);
2963 CFReleaseNull(clientAuditToken
);
2964 CFReleaseNull(policies
);
2966 /* verify the public key of the issuer signed the OCSP signer */
2968 SecCertificateRef issuer
= NULL
, signer
= NULL
;
2969 SecKeyRef issuerPubKey
= NULL
;
2971 issuer
= (SecCertificateRef
)CFArrayGetValueAtIndex(issuers
, 0);
2972 signer
= (SecCertificateRef
)CFArrayGetValueAtIndex(signers
, 0);
2975 #if TARGET_OS_IPHONE
2976 issuerPubKey
= SecCertificateCopyPublicKey(issuer
);
2978 issuerPubKey
= SecCertificateCopyPublicKey_ios(issuer
);
2981 if (signer
&& issuerPubKey
&& (errSecSuccess
== SecCertificateIsSignedBy(signer
, issuerPubKey
))) {
2984 secnotice("ocsp", "ocsp signer cert not signed by issuer");
2986 CFReleaseNull(issuerPubKey
);
2992 static bool SecOCSPResponseVerify(SecOCSPResponseRef ocspResponse
, SecORVCRef rvc
, CFAbsoluteTime verifyTime
) {
2994 SecCertificatePathRef issuers
= SecCertificatePathCopyFromParent(rvc
->pvc
->path
, rvc
->certIX
+ 1);
2995 SecCertificateRef issuer
= issuers
? CFRetainSafe(SecCertificatePathGetCertificateAtIndex(issuers
, 0)) : NULL
;
2996 CFArrayRef signers
= SecOCSPResponseCopySigners(ocspResponse
);
2997 SecCertificateRef signer
= SecOCSPResponseCopySigner(ocspResponse
, issuer
);
2999 if (signer
&& signers
) {
3000 if (issuer
&& CFEqual(signer
, issuer
)) {
3001 /* We already know we trust issuer since it's the issuer of the
3002 * cert we are verifying. */
3003 secinfo("ocsp", "ocsp responder: %@ response signed by issuer",
3007 secinfo("ocsp", "ocsp responder: %@ response signed by cert issued by issuer",
3009 CFMutableArrayRef signerCerts
= NULL
;
3010 CFArrayRef issuerCerts
= NULL
;
3012 /* Ensure the signer cert is the 0th cert for trust evaluation */
3013 signerCerts
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
3014 CFArrayAppendValue(signerCerts
, signer
);
3015 CFArrayAppendArray(signerCerts
, signers
, CFRangeMake(0, CFArrayGetCount(signers
)));
3018 issuerCerts
= SecCertificatePathCopyCertificates(issuers
, NULL
);
3021 if (SecOCSPResponseEvaluateSigner(rvc
, signerCerts
, issuerCerts
, verifyTime
)) {
3022 secdebug("ocsp", "response satisfies ocspSigner policy (%@)",
3026 /* @@@ We don't trust the cert so don't use this response. */
3027 secnotice("ocsp", "ocsp response signed by certificate which "
3028 "does not satisfy ocspSigner policy");
3031 CFReleaseNull(signerCerts
);
3032 CFReleaseNull(issuerCerts
);
3035 /* @@@ No signer found for this ocsp response, discard it. */
3036 secnotice("ocsp", "ocsp responder: %@ no signer found for response",
3041 #if DUMP_OCSPRESPONSES
3043 snprintf(buf
, 40, "/tmp/ocspresponse%ld%s.der",
3044 rvc
->certIX
, (trusted
? "t" : "u"));
3045 secdumpdata(ocspResponse
->data
, buf
);
3047 CFReleaseNull(issuers
);
3048 CFReleaseNull(issuer
);
3049 CFReleaseNull(signers
);
3050 CFReleaseNull(signer
);
3054 static void SecORVCConsumeOCSPResponse(SecORVCRef rvc
, SecOCSPResponseRef ocspResponse
/*CF_CONSUMED*/, CFTimeInterval maxAge
, bool updateCache
) {
3055 SecOCSPSingleResponseRef sr
= NULL
;
3056 require_quiet(ocspResponse
, errOut
);
3057 SecOCSPResponseStatus orStatus
= SecOCSPGetResponseStatus(ocspResponse
);
3058 require_action_quiet(orStatus
== kSecOCSPSuccess
, errOut
,
3059 secnotice("ocsp", "responder: %@ returned status: %d", rvc
->responder
, orStatus
));
3060 require_action_quiet(sr
= SecOCSPResponseCopySingleResponse(ocspResponse
, rvc
->ocspRequest
), errOut
,
3061 secnotice("ocsp", "ocsp responder: %@ did not include status of requested cert", rvc
->responder
));
3062 // Check if this response is fresher than any (cached) response we might still have in the rvc.
3063 require_quiet(!rvc
->ocspSingleResponse
|| rvc
->ocspSingleResponse
->thisUpdate
< sr
->thisUpdate
, errOut
);
3065 CFAbsoluteTime verifyTime
= CFAbsoluteTimeGetCurrent();
3066 /* TODO: If the responder doesn't have the ocsp-nocheck extension we should
3067 check whether the leaf was revoked (we are already checking the rest of
3069 /* Check the OCSP response signature and verify the response. */
3070 require_quiet(SecOCSPResponseVerify(ocspResponse
, rvc
,
3071 sr
->certStatus
== CS_Revoked
? SecOCSPResponseProducedAt(ocspResponse
) : verifyTime
), errOut
);
3073 // If we get here, we have a properly signed ocsp response
3074 // but we haven't checked dates yet.
3076 bool sr_valid
= SecOCSPSingleResponseCalculateValidity(sr
, kSecDefaultOCSPResponseTTL
, verifyTime
);
3077 if (sr
->certStatus
== CS_Good
) {
3078 // Side effect of SecOCSPResponseCalculateValidity sets ocspResponse->expireTime
3079 require_quiet(sr_valid
&& SecOCSPResponseCalculateValidity(ocspResponse
, maxAge
, kSecDefaultOCSPResponseTTL
, verifyTime
), errOut
);
3080 } else if (sr
->certStatus
== CS_Revoked
) {
3081 // Expire revoked responses when the subject certificate itself expires.
3082 ocspResponse
->expireTime
= SecCertificateNotValidAfter(SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
));
3085 // Ok we like the new response, let's toss the old one.
3087 SecOCSPCacheReplaceResponse(rvc
->ocspResponse
, ocspResponse
, rvc
->responder
, verifyTime
);
3089 if (rvc
->ocspResponse
) SecOCSPResponseFinalize(rvc
->ocspResponse
);
3090 rvc
->ocspResponse
= ocspResponse
;
3091 ocspResponse
= NULL
;
3093 if (rvc
->ocspSingleResponse
) SecOCSPSingleResponseDestroy(rvc
->ocspSingleResponse
);
3094 rvc
->ocspSingleResponse
= sr
;
3097 rvc
->done
= sr_valid
;
3100 if (sr
) SecOCSPSingleResponseDestroy(sr
);
3101 if (ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
3104 /* Callback from async http code after an ocsp response has been received. */
3105 static void SecOCSPFetchCompleted(asynchttp_t
*http
, CFTimeInterval maxAge
) {
3106 SecORVCRef rvc
= (SecORVCRef
)http
->info
;
3107 SecPVCRef pvc
= rvc
->pvc
;
3108 SecOCSPResponseRef ocspResponse
= NULL
;
3109 if (http
->response
) {
3110 CFDataRef data
= CFHTTPMessageCopyBody(http
->response
);
3112 /* Parse the returned data as if it's an ocspResponse. */
3113 ocspResponse
= SecOCSPResponseCreate(data
);
3118 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, maxAge
, true);
3119 // TODO: maybe we should set the cache-control: false in the http header and try again if the response is stale
3122 /* Clear the data for the next response. */
3123 asynchttp_free(http
);
3124 SecORVCFetchNext(rvc
);
3128 secdebug("rvc", "got OCSP response for cert: %ld", rvc
->certIX
);
3129 SecORVCUpdatePVC(rvc
);
3131 if (!--pvc
->asyncJobCount
) {
3132 secdebug("rvc", "done with all async jobs");
3133 SecPathBuilderStep(pvc
->builder
);
3138 static SecORVCRef
SecORVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3139 SecORVCRef orvc
= NULL
;
3140 orvc
= malloc(sizeof(struct OpaqueSecORVC
));
3142 memset(orvc
, 0, sizeof(struct OpaqueSecORVC
));
3145 orvc
->certIX
= certIX
;
3146 orvc
->http
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
3147 orvc
->http
.token
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
3148 orvc
->http
.completed
= SecOCSPFetchCompleted
;
3149 orvc
->http
.info
= orvc
;
3150 orvc
->ocspRequest
= NULL
;
3151 orvc
->responderIX
= 0;
3152 orvc
->responder
= NULL
;
3153 orvc
->nextUpdate
= NULL_TIME
;
3154 orvc
->ocspResponse
= NULL
;
3155 orvc
->ocspSingleResponse
= NULL
;
3158 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3159 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3160 if (certIX
+ 1 < count
) {
3161 SecCertificateRef issuer
= SecPVCGetCertificateAtIndex(pvc
, certIX
+ 1);
3162 orvc
->ocspRequest
= SecOCSPRequestCreate(cert
, issuer
);
3168 static void SecORVCProcessStapledResponses(SecORVCRef rvc
) {
3169 /* Get stapled OCSP responses */
3170 CFArrayRef ocspResponsesData
= SecPathBuilderCopyOCSPResponses(rvc
->pvc
->builder
);
3172 if(ocspResponsesData
) {
3173 secdebug("rvc", "Checking stapled responses for cert %ld", rvc
->certIX
);
3174 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
3175 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
3176 SecORVCConsumeOCSPResponse(rvc
, ocspResponse
, NULL_TIME
, false);
3178 CFRelease(ocspResponsesData
);
3183 /********************************************************
3184 ******************* CRL RVC Functions ******************
3185 ********************************************************/
3187 #include <../trustd/SecTrustOSXEntryPoints.h>
3188 #define kSecDefaultCRLTTL kSecDefaultOCSPResponseTTL
3190 /* CRL Revocation verification context. */
3191 struct OpaqueSecCRVC
{
3192 /* Response data from ocspd. Yes, ocspd does CRLs, but not OCSP... */
3193 async_ocspd_t async_ocspd
;
3195 /* Pointer to the pvc for this revocation check. */
3198 /* Pointer to the generic rvc for this revocation check */
3201 /* The current CRL status from ocspd. */
3204 /* Index of cert in pvc that this RVC is for 0 = leaf, etc. */
3207 /* Index in array returned by SecCertificateGetCRLDistributionPoints() for
3208 current distribution point. */
3209 CFIndex distributionPointIX
;
3211 /* URL of current distribution point. */
3212 CFURLRef distributionPoint
;
3214 /* Date until which this revocation status is valid. */
3215 CFAbsoluteTime nextUpdate
;
3220 static void SecCRVCFinish(SecCRVCRef crvc
) {
3224 #define MAX_CRL_DPS 3
3225 #define CRL_REQUEST_THRESHOLD 10
3227 static CFURLRef
SecCRVCGetNextDistributionPoint(SecCRVCRef rvc
) {
3228 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3229 CFArrayRef crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
3231 CFIndex crlDPCount
= CFArrayGetCount(crlDPs
);
3232 if (crlDPCount
>= CRL_REQUEST_THRESHOLD
) {
3233 secnotice("rvc", "too many CRL DP entries (%ld)", (long)crlDPCount
);
3236 while (rvc
->distributionPointIX
< crlDPCount
&& rvc
->distributionPointIX
< MAX_CRL_DPS
) {
3237 CFURLRef distributionPoint
= CFArrayGetValueAtIndex(crlDPs
, rvc
->distributionPointIX
);
3238 rvc
->distributionPointIX
++;
3239 CFStringRef scheme
= CFURLCopyScheme(distributionPoint
);
3241 /* We only support http and https responders currently. */
3242 bool valid_DP
= (CFEqual(CFSTR("http"), scheme
) ||
3243 CFEqual(CFSTR("https"), scheme
) ||
3244 CFEqual(CFSTR("ldap"), scheme
));
3247 return distributionPoint
;
3254 static void SecCRVCGetCRLStatus(SecCRVCRef rvc
) {
3255 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3256 SecCertificatePathRef path
= rvc
->pvc
->path
;
3257 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3258 secdebug("rvc", "searching CRL cache for cert: %ld", rvc
->certIX
);
3259 rvc
->status
= SecTrustLegacyCRLStatus(cert
, serializedCertPath
, rvc
->distributionPoint
);
3260 CFReleaseNull(serializedCertPath
);
3261 /* we got a response indicating that the CRL was checked */
3262 if (rvc
->status
== errSecSuccess
|| rvc
->status
== errSecCertificateRevoked
) {
3264 /* ocspd doesn't give us the nextUpdate time, so set to default */
3265 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3269 static void SecCRVCCheckRevocationCache(SecCRVCRef rvc
) {
3270 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3271 SecCRVCGetCRLStatus(rvc
);
3272 if (rvc
->status
== errSecCertificateRevoked
) {
3278 /* Fire off an async http request for this certs revocation status, return
3279 false if request was queued, true if we're done. */
3280 static bool SecCRVCFetchNext(SecCRVCRef rvc
) {
3281 while ((rvc
->distributionPoint
= SecCRVCGetNextDistributionPoint(rvc
))) {
3282 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3283 SecCertificatePathRef path
= rvc
->pvc
->path
;
3284 CFArrayRef serializedCertPath
= SecCertificatePathCreateSerialized(path
, NULL
);
3285 secinfo("rvc", "fetching CRL for cert: %ld", rvc
->certIX
);
3286 if (!SecTrustLegacyCRLFetch(&rvc
->async_ocspd
, rvc
->distributionPoint
,
3287 CFAbsoluteTimeGetCurrent(), cert
, serializedCertPath
)) {
3288 CFDataRef clientAuditToken
= NULL
;
3289 SecTaskRef task
= NULL
;
3290 audit_token_t auditToken
= {};
3291 clientAuditToken
= SecPathBuilderCopyClientAuditToken(rvc
->pvc
->builder
);
3292 require(clientAuditToken
, out
);
3293 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3294 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3295 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3296 secnotice("rvc", "asynchronously fetching CRL (%@) for client (%@)",
3297 rvc
->distributionPoint
, task
);
3300 CFReleaseNull(clientAuditToken
);
3301 CFReleaseNull(task
);
3302 /* Async request was posted, wait for reply. */
3310 static void SecCRVCUpdatePVC(SecCRVCRef rvc
) {
3311 if (rvc
->status
== errSecCertificateRevoked
) {
3312 secdebug("rvc", "CRL revoked cert %" PRIdCFIndex
, rvc
->certIX
);
3313 SInt32 reason
= 0; // unspecified, since ocspd didn't tell us
3314 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
3315 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
3317 if (rvc
->pvc
&& rvc
->pvc
->info
) {
3318 /* make the revocation reason available in the trust result */
3319 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
3321 CFReleaseNull(cfreason
);
3325 static void SecCRVCFetchCompleted(async_ocspd_t
*ocspd
) {
3326 SecCRVCRef rvc
= ocspd
->info
;
3327 SecPVCRef pvc
= rvc
->pvc
;
3328 /* we got a response indicating that the CRL was checked */
3329 if (ocspd
->response
== errSecSuccess
|| ocspd
->response
== errSecCertificateRevoked
) {
3330 rvc
->status
= ocspd
->response
;
3332 /* ocspd doesn't give us the nextUpdate time, so set to default */
3333 rvc
->nextUpdate
= SecPVCGetVerifyTime(rvc
->pvc
) + kSecDefaultCRLTTL
;
3334 secdebug("rvc", "got CRL response for cert: %ld", rvc
->certIX
);
3335 SecCRVCUpdatePVC(rvc
);
3337 if (!--pvc
->asyncJobCount
) {
3338 secdebug("rvc", "done with all async jobs");
3339 SecPathBuilderStep(pvc
->builder
);
3342 if(SecCRVCFetchNext(rvc
)) {
3343 if (!--pvc
->asyncJobCount
) {
3344 secdebug("rvc", "done with all async jobs");
3345 SecPathBuilderStep(pvc
->builder
);
3351 static SecCRVCRef
SecCRVCCreate(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3352 SecCRVCRef crvc
= NULL
;
3353 crvc
= malloc(sizeof(struct OpaqueSecCRVC
));
3355 memset(crvc
, 0, sizeof(struct OpaqueSecCRVC
));
3358 crvc
->certIX
= certIX
;
3359 crvc
->status
= errSecInternal
;
3360 crvc
->distributionPointIX
= 0;
3361 crvc
->distributionPoint
= NULL
;
3362 crvc
->nextUpdate
= NULL_TIME
;
3363 crvc
->async_ocspd
.queue
= SecPathBuilderGetQueue(pvc
->builder
);
3364 crvc
->async_ocspd
.completed
= SecCRVCFetchCompleted
;
3365 crvc
->async_ocspd
.response
= errSecInternal
;
3366 crvc
->async_ocspd
.info
= crvc
;
3372 static bool SecRVCShouldCheckCRL(SecRVCRef rvc
) {
3373 if (rvc
->pvc
->check_revocation
&&
3374 CFEqual(kSecPolicyCheckRevocationCRL
, rvc
->pvc
->check_revocation
)) {
3375 /* Our client insists on CRLs */
3376 secinfo("rvc", "client told us to check CRL");
3379 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3380 CFArrayRef ocspResponders
= SecCertificateGetOCSPResponders(cert
);
3381 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) &&
3382 (rvc
->pvc
->check_revocation
&& !CFEqual(kSecPolicyCheckRevocationOCSP
, rvc
->pvc
->check_revocation
))) {
3383 /* The cert doesn't have OCSP responders and the client didn't specifically ask for OCSP.
3384 * This logic will skip the CRL cache check if the client didn't ask for revocation checking */
3385 secinfo("rvc", "client told us to check revocation and CRL is only option for cert: %ld", rvc
->certIX
);
3390 #endif /* ENABLE_CRLS */
3392 static void SecRVCFinish(SecRVCRef rvc
) {
3394 SecORVCFinish(rvc
->orvc
);
3398 SecCRVCFinish(rvc
->crvc
);
3403 static void SecRVCDelete(SecRVCRef rvc
) {
3405 SecORVCFinish(rvc
->orvc
);
3410 SecCRVCFinish(rvc
->crvc
);
3414 if (rvc
->valid_info
) {
3415 SecValidInfoRelease(rvc
->valid_info
);
3419 static void SecRVCInit(SecRVCRef rvc
, SecPVCRef pvc
, CFIndex certIX
) {
3420 secdebug("alloc", "%p", rvc
);
3422 rvc
->certIX
= certIX
;
3423 rvc
->orvc
= SecORVCCreate(rvc
, pvc
, certIX
);
3425 rvc
->crvc
= SecCRVCCreate(rvc
, pvc
, certIX
);
3430 static void SecRVCUpdatePVC(SecRVCRef rvc
) {
3431 SecORVCUpdatePVC(rvc
->orvc
);
3433 SecCRVCUpdatePVC(rvc
->crvc
);
3438 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3439 if (!rvc
->pvc
->check_revocation
3440 || !CFEqual(rvc
->pvc
->check_revocation
, kSecPolicyCheckRevocationCRL
)) {
3446 static bool SecRVCShouldCheckOCSP(SecRVCRef rvc
) {
3451 static void SecRVCProcessValidInfoResults(SecRVCRef rvc
) {
3452 if (!rvc
|| !rvc
->valid_info
|| !rvc
->pvc
) {
3455 /* Handle definitive revocations.
3457 bool valid
= rvc
->valid_info
->valid
;
3458 SecValidInfoFormat format
= rvc
->valid_info
->format
;
3459 if (!valid
&& (format
== kSecValidInfoFormatSerial
|| format
== kSecValidInfoFormatSHA256
)) {
3460 secdebug("validupdate", "rvc: revoked cert %" PRIdCFIndex
, rvc
->certIX
);
3461 SInt32 reason
= 0; // unspecified, since the Valid db doesn't tell us
3462 CFNumberRef cfreason
= CFNumberCreate(kCFAllocatorDefault
, kCFNumberSInt32Type
, &reason
);
3463 SecPVCSetResultForced(rvc
->pvc
, kSecPolicyCheckRevocation
, rvc
->certIX
,
3465 if (rvc
->pvc
->info
) {
3466 /* make the revocation reason available in the trust result */
3467 CFDictionarySetValue(rvc
->pvc
->info
, kSecTrustRevocationReason
, cfreason
);
3469 CFReleaseNull(cfreason
);
3475 /* Handle non-definitive information.
3476 We set rvc->done = true above ONLY if the result was definitive;
3477 otherwise we require a revocation check for SSL usage.
3479 if (format
== kSecValidInfoFormatNto1
) {
3480 /* matched the filter */
3481 CFIndex count
= SecPVCGetCertificateCount(rvc
->pvc
);
3482 CFIndex issuerIX
= rvc
->certIX
+ 1;
3483 if (issuerIX
>= count
) {
3484 /* cannot perform a revocation check on the last cert in the
3485 chain, since we don't have its issuer. */
3488 SecPolicyRef policy
= SecPVCGetPolicy(rvc
->pvc
);
3489 CFStringRef policyName
= (policy
) ? SecPolicyGetName(policy
) : NULL
;
3490 if (policyName
&& CFEqual(CFSTR("sslServer"), policyName
)) {
3491 /* perform revocation check for SSL policy;
3492 require for leaf if an OCSP responder is present. */
3493 if (0 == rvc
->certIX
) {
3494 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3495 CFArrayRef resps
= (cert
) ? SecCertificateGetOCSPResponders(cert
) : NULL
;
3496 CFIndex rcount
= (resps
) ? CFArrayGetCount(resps
) : 0;
3498 rvc
->pvc
->response_required
= true;
3501 rvc
->pvc
->check_revocation
= kSecPolicyCheckRevocationAny
;
3507 static bool SecRVCCheckValidInfoDatabase(SecRVCRef rvc
) {
3508 /* If the valid database is enabled... */
3509 #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 101300 || __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000)
3510 /* Make sure revocation db info is up-to-date,
3511 if we are allowed to access the network */
3512 #if !TARGET_OS_BRIDGE
3513 SecPathBuilderRef builder
= rvc
->pvc
->builder
;
3514 if (SecPathBuilderCanAccessNetwork(builder
)) {
3515 SecRevocationDbCheckNextUpdate();
3518 /* Check whether we have valid db info for this cert,
3519 given the cert and its issuer */
3520 SecValidInfoRef info
= NULL
;
3521 CFIndex count
= SecPVCGetCertificateCount(rvc
->pvc
);
3523 SecCertificateRef cert
= NULL
;
3524 SecCertificateRef issuer
= NULL
;
3525 CFIndex issuerIX
= rvc
->certIX
+ 1;
3526 if (count
> issuerIX
) {
3527 issuer
= SecPVCGetCertificateAtIndex(rvc
->pvc
, issuerIX
);
3528 } else if (count
== issuerIX
) {
3529 CFIndex rootIX
= SecCertificatePathSelfSignedIndex(rvc
->pvc
->path
);
3530 if (rootIX
== rvc
->certIX
) {
3531 issuer
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rootIX
);
3534 cert
= SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
3535 info
= SecRevocationDbCopyMatching(cert
, issuer
);
3538 SecValidInfoRef old_info
= rvc
->valid_info
;
3539 rvc
->valid_info
= info
;
3541 SecValidInfoRelease(old_info
);
3549 static void SecRVCCheckRevocationCaches(SecRVCRef rvc
) {
3550 /* Don't check OCSP cache if CRLs enabled and policy requested CRL only */
3551 if (SecRVCShouldCheckOCSP(rvc
)) {
3552 secdebug("ocsp", "Checking cached responses for cert %ld", rvc
->certIX
);
3553 SecOCSPResponseRef response
= NULL
;
3554 if (rvc
->pvc
->online_revocation
) {
3555 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
3556 response
= SecOCSPCacheCopyMatchingWithMinInsertTime(rvc
->orvc
->ocspRequest
, NULL
, now
- kSecOCSPResponseOnlineTTL
);
3558 response
= SecOCSPCacheCopyMatching(rvc
->orvc
->ocspRequest
, NULL
);
3560 SecORVCConsumeOCSPResponse(rvc
->orvc
,
3565 /* Don't check CRL cache if policy requested OCSP only */
3566 if (SecRVCShouldCheckCRL(rvc
)) {
3567 SecCRVCCheckRevocationCache(rvc
->crvc
);
3572 static bool SecRVCFetchNext(SecRVCRef rvc
) {
3573 bool OCSP_fetch_finished
= true;
3574 /* Don't send OCSP request only if CRLs enabled and policy requested CRL only */
3575 if (SecRVCShouldCheckOCSP(rvc
)) {
3576 OCSP_fetch_finished
&= SecORVCFetchNext(rvc
->orvc
);
3578 if (OCSP_fetch_finished
) {
3579 /* we didn't start an OCSP background job for this cert */
3580 rvc
->pvc
->asyncJobCount
--;
3584 bool CRL_fetch_finished
= true;
3585 /* Don't check CRL cache if policy requested OCSP only */
3586 if (SecRVCShouldCheckCRL(rvc
)) {
3587 /* reset the distributionPointIX because we already iterated through the CRLDPs
3588 * in SecCRVCCheckRevocationCache */
3589 rvc
->crvc
->distributionPointIX
= 0;
3590 CRL_fetch_finished
&= SecCRVCFetchNext(rvc
->crvc
);
3592 if (CRL_fetch_finished
) {
3593 /* we didn't start a CRL background job for this cert */
3594 rvc
->pvc
->asyncJobCount
--;
3596 OCSP_fetch_finished
&= CRL_fetch_finished
;
3599 return OCSP_fetch_finished
;
3602 static bool SecPVCCheckRevocation(SecPVCRef pvc
) {
3603 secdebug("rvc", "checking revocation");
3604 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3605 bool completed
= true;
3606 if (certCount
<= 1) {
3607 /* Can't verify without an issuer; we're done */
3612 * Don't need to call SecPVCIsAnchored; having an issuer is sufficient here.
3614 * Note: we can't check revocation for the last certificate in the chain
3615 * via OCSP or CRL methods, since there isn't a separate issuer cert to
3616 * sign those responses. However, since a self-signed root has an implied
3617 * issuer of itself, we can check for it in the valid database.
3621 /* We have done revocation checking already, we're done. */
3622 secdebug("rvc", "Not rechecking revocation");
3626 /* Setup things so we check revocation status of all certs. */
3627 pvc
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
3629 /* Note that if we are multi threaded and a job completes after it
3630 is started but before we return from this function, we don't want
3631 a callback to decrement asyncJobCount to zero before we finish issuing
3632 all the jobs. To avoid this we pretend we issued certCount-1 async jobs,
3633 and decrement pvc->asyncJobCount for each cert that we don't start a
3634 background fetch for. (We will never start an async job for the final
3635 cert in the chain.) */
3637 pvc
->asyncJobCount
= (unsigned int)(certCount
-1);
3639 /* If we enable CRLS, we may end up with two async jobs per cert: one
3640 * for OCSP and one for fetching the CRL */
3641 pvc
->asyncJobCount
= 2 * (unsigned int)(certCount
-1);
3643 secdebug("rvc", "set asyncJobCount to %d", pvc
->asyncJobCount
);
3645 /* Loop though certificates again and issue an ocsp fetch if the
3646 * revocation status checking isn't done yet (and we have an issuer!) */
3647 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3648 secdebug("rvc", "checking revocation for cert: %ld", certIX
);
3649 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3650 SecRVCInit(rvc
, pvc
, certIX
);
3655 #if !TARGET_OS_BRIDGE
3656 /* Check valid database first (separate from OCSP response cache) */
3657 if (SecRVCCheckValidInfoDatabase(rvc
)) {
3658 SecRVCProcessValidInfoResults(rvc
);
3661 /* Any other revocation method requires an issuer certificate;
3662 * skip the last cert in the chain since it doesn't have one. */
3663 if (certIX
+1 >= certCount
) {
3667 /* Ignore stapled OCSP responses only if CRLs are enabled and the
3668 * policy specifically requested CRLs only. */
3669 if (SecRVCShouldCheckOCSP(rvc
)) {
3670 /* If we have any OCSP stapled responses, check those first */
3671 SecORVCProcessStapledResponses(rvc
->orvc
);
3674 #if TARGET_OS_BRIDGE
3675 /* The bridge has no writeable storage and no network. Nothing else we can
3681 /* Then check the caches for revocation results. */
3682 SecRVCCheckRevocationCaches(rvc
);
3684 /* The check is done if we found cached responses from either method. */
3690 secdebug("rvc", "found cached response for cert: %ld", certIX
);
3694 /* If we got a cached response that is no longer valid (which can only be true for
3695 * revoked responses), let's try to get a fresher response even if no one asked.
3696 * This check resolves unrevocation events after the nextUpdate time. */
3697 bool old_cached_response
= (!rvc
->done
&& rvc
->orvc
->ocspResponse
);
3699 /* If the cert is EV or if revocation checking was explicitly enabled, attempt to fire off an
3700 async http request for this cert's revocation status, unless we already successfully checked
3701 the revocation status of this cert based on the cache or stapled responses. */
3702 bool allow_fetch
= SecPathBuilderCanAccessNetwork(pvc
->builder
) &&
3703 (pvc
->is_ev
|| pvc
->check_revocation
|| old_cached_response
);
3704 bool fetch_done
= true;
3705 if (rvc
->done
|| !allow_fetch
) {
3706 /* We got a cache hit or we aren't allowed to access the network */
3707 SecRVCUpdatePVC(rvc
);
3709 /* We didn't really start any background jobs for this cert. */
3710 pvc
->asyncJobCount
--;
3712 pvc
->asyncJobCount
--;
3714 secdebug("rvc", "not fetching and job count is %d for cert %ld", pvc
->asyncJobCount
, certIX
);
3716 fetch_done
= SecRVCFetchNext(rvc
);
3719 /* We started at least one background fetch. */
3720 secdebug("rvc", "waiting on background fetch for cert %ld", certIX
);
3725 /* Return false if we started any background jobs. */
3726 /* We can't just return !pvc->asyncJobCount here, since if we started any
3727 jobs the completion callback will be called eventually and it will call
3728 SecPathBuilderStep(). If for some reason everything completed before we
3729 get here we still want the outer SecPathBuilderStep() to terminate so we
3730 keep track of whether we started any jobs and return false if so. */
3734 static CFAbsoluteTime
SecRVCGetEarliestNextUpdate(SecRVCRef rvc
) {
3735 CFAbsoluteTime enu
= NULL_TIME
;
3736 enu
= rvc
->orvc
->nextUpdate
;
3738 CFAbsoluteTime crlNextUpdate
= rvc
->crvc
->nextUpdate
;
3739 if (enu
== NULL_TIME
||
3740 ((crlNextUpdate
> NULL_TIME
) && (enu
> crlNextUpdate
))) {
3741 /* We didn't check OCSP or CRL next update time was sooner */
3742 enu
= crlNextUpdate
;
3749 void SecPolicyServerInitalize(void) {
3750 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3751 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3752 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
3753 &kCFTypeDictionaryKeyCallBacks
, NULL
);
3755 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3756 kSecPolicyCheckBasicCertificateProcessing
,
3757 SecPolicyCheckBasicCertificateProcessing
);
3758 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3759 kSecPolicyCheckCriticalExtensions
,
3760 SecPolicyCheckCriticalExtensions
);
3761 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3762 kSecPolicyCheckIdLinkage
,
3763 SecPolicyCheckIdLinkage
);
3764 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3765 kSecPolicyCheckKeyUsage
,
3766 SecPolicyCheckKeyUsage
);
3767 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3768 kSecPolicyCheckExtendedKeyUsage
,
3769 SecPolicyCheckExtendedKeyUsage
);
3770 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3771 kSecPolicyCheckBasicConstraints
,
3772 SecPolicyCheckBasicConstraints
);
3773 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3774 kSecPolicyCheckNonEmptySubject
,
3775 SecPolicyCheckNonEmptySubject
);
3776 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3777 kSecPolicyCheckQualifiedCertStatements
,
3778 SecPolicyCheckQualifiedCertStatements
);
3779 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3780 kSecPolicyCheckSSLHostname
,
3781 SecPolicyCheckSSLHostname
);
3782 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3783 kSecPolicyCheckEmail
,
3784 SecPolicyCheckEmail
);
3785 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3786 kSecPolicyCheckValidIntermediates
,
3787 SecPolicyCheckValidIntermediates
);
3788 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3789 kSecPolicyCheckValidLeaf
,
3790 SecPolicyCheckValidLeaf
);
3791 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3792 kSecPolicyCheckValidRoot
,
3793 SecPolicyCheckValidRoot
);
3794 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3795 kSecPolicyCheckIssuerCommonName
,
3796 SecPolicyCheckIssuerCommonName
);
3797 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3798 kSecPolicyCheckSubjectCommonNamePrefix
,
3799 SecPolicyCheckSubjectCommonNamePrefix
);
3800 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3801 kSecPolicyCheckSubjectCommonName
,
3802 SecPolicyCheckSubjectCommonName
);
3803 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3804 kSecPolicyCheckNotValidBefore
,
3805 SecPolicyCheckNotValidBefore
);
3806 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3807 kSecPolicyCheckChainLength
,
3808 SecPolicyCheckChainLength
);
3809 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3810 kSecPolicyCheckAnchorSHA1
,
3811 SecPolicyCheckAnchorSHA1
);
3812 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3813 kSecPolicyCheckAnchorSHA256
,
3814 SecPolicyCheckAnchorSHA256
);
3815 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3816 kSecPolicyCheckAnchorApple
,
3817 SecPolicyCheckAnchorApple
);
3818 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3819 kSecPolicyCheckSubjectOrganization
,
3820 SecPolicyCheckSubjectOrganization
);
3821 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3822 kSecPolicyCheckSubjectOrganizationalUnit
,
3823 SecPolicyCheckSubjectOrganizationalUnit
);
3824 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3825 kSecPolicyCheckEAPTrustedServerNames
,
3826 SecPolicyCheckEAPTrustedServerNames
);
3827 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3828 kSecPolicyCheckSubjectCommonNameTEST
,
3829 SecPolicyCheckSubjectCommonNameTEST
);
3830 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3831 kSecPolicyCheckRevocation
,
3832 SecPolicyCheckRevocation
);
3833 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3834 kSecPolicyCheckRevocationResponseRequired
,
3835 SecPolicyCheckRevocationResponseRequired
);
3836 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3837 kSecPolicyCheckRevocationOnline
,
3838 SecPolicyCheckRevocationOnline
);
3839 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3840 kSecPolicyCheckNoNetworkAccess
,
3841 SecPolicyCheckNoNetworkAccess
);
3842 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3843 kSecPolicyCheckBlackListedLeaf
,
3844 SecPolicyCheckBlackListedLeaf
);
3845 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3846 kSecPolicyCheckGrayListedLeaf
,
3847 SecPolicyCheckGrayListedLeaf
);
3848 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3849 kSecPolicyCheckLeafMarkerOid
,
3850 SecPolicyCheckLeafMarkerOid
);
3851 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3852 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
3853 SecPolicyCheckLeafMarkerOidWithoutValueCheck
);
3854 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3855 kSecPolicyCheckLeafMarkersProdAndQA
,
3856 SecPolicyCheckLeafMarkersProdAndQA
);
3857 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3858 kSecPolicyCheckIntermediateSPKISHA256
,
3859 SecPolicyCheckIntermediateSPKISHA256
);
3860 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3861 kSecPolicyCheckIntermediateEKU
,
3862 SecPolicyCheckIntermediateEKU
);
3863 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3864 kSecPolicyCheckIntermediateMarkerOid
,
3865 SecPolicyCheckIntermediateMarkerOid
);
3866 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3867 kSecPolicyCheckCertificatePolicy
,
3868 SecPolicyCheckCertificatePolicyOid
);
3869 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3870 kSecPolicyCheckWeakIntermediates
,
3871 SecPolicyCheckWeakIntermediates
);
3872 CFDictionaryAddValue(gSecPolicyLeafCallbacks
,
3873 kSecPolicyCheckWeakLeaf
,
3874 SecPolicyCheckWeakLeaf
);
3875 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3876 kSecPolicyCheckWeakRoot
,
3877 SecPolicyCheckWeakRoot
);
3878 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3879 kSecPolicyCheckKeySize
,
3880 SecPolicyCheckKeySize
);
3881 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3882 kSecPolicyCheckSignatureHashAlgorithms
,
3883 SecPolicyCheckSignatureHashAlgorithms
);
3884 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3885 kSecPolicyCheckSystemTrustedWeakHash
,
3886 SecPolicyCheckSystemTrustedWeakHash
);
3887 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3888 kSecPolicyCheckIntermediateOrganization
,
3889 SecPolicyCheckIntermediateOrganization
);
3890 CFDictionaryAddValue(gSecPolicyPathCallbacks
,
3891 kSecPolicyCheckIntermediateCountry
,
3892 SecPolicyCheckIntermediateCountry
);
3897 /********************************************************
3898 ****************** SecPVCRef Functions *****************
3899 ********************************************************/
3901 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
,
3902 CFAbsoluteTime verifyTime
) {
3903 secdebug("alloc", "%p", pvc
);
3904 // Weird logging policies crashes.
3905 //secdebug("policy", "%@", policies);
3907 // Zero the pvc struct so only non-zero fields need to be explicitly set
3908 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
3909 pvc
->builder
= builder
;
3910 pvc
->policies
= policies
;
3913 pvc
->verifyTime
= verifyTime
;
3917 static void SecPVCDeleteRVCs(SecPVCRef pvc
) {
3918 secdebug("alloc", "%p", pvc
);
3920 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3921 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
3922 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
3930 void SecPVCDelete(SecPVCRef pvc
) {
3931 secdebug("alloc", "%p", pvc
);
3932 CFReleaseNull(pvc
->policies
);
3933 CFReleaseNull(pvc
->details
);
3934 CFReleaseNull(pvc
->info
);
3935 if (pvc
->valid_policy_tree
) {
3936 policy_tree_prune(&pvc
->valid_policy_tree
);
3938 SecPVCDeleteRVCs(pvc
);
3939 CFReleaseNull(pvc
->path
);
3942 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathRef path
,
3943 CF_CONSUMED CFArrayRef details
) {
3944 secdebug("policy", "%@", path
);
3945 bool samePath
= ((!path
&& !pvc
->path
) || (path
&& pvc
->path
&& CFEqual(path
, pvc
->path
)));
3947 /* Changing path makes us clear the Revocation Verification Contexts */
3948 SecPVCDeleteRVCs(pvc
);
3949 CFReleaseSafe(pvc
->path
);
3950 pvc
->path
= CFRetainSafe(path
);
3952 pvc
->details
= details
;
3953 CFReleaseNull(pvc
->info
);
3954 if (pvc
->valid_policy_tree
) {
3955 policy_tree_prune(&pvc
->valid_policy_tree
);
3959 /* Since we don't run the LeafChecks again, we need to preserve the
3960 * result the leaf had. */
3961 pvc
->result
= (details
) ? (CFDictionaryGetCount(CFArrayGetValueAtIndex(details
, 0)) == 0)
3965 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
3966 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
3969 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
3970 return SecCertificatePathGetCount(pvc
->path
);
3973 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
3974 return SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
3977 bool SecPVCIsCertificateAtIndexSelfIssued(SecPVCRef pvc
, CFIndex ix
) {
3978 /* The SecCertificatePath only tells us the last self-issued cert.
3979 * The chain may have more than one self-issued cert, so we need to
3980 * do the comparison. */
3981 bool result
= false;
3982 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3983 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
3984 CFDataRef subject
= SecCertificateCopyNormalizedSubjectSequence(cert
);
3985 if (issuer
&& subject
&& CFEqual(issuer
, subject
)) {
3988 CFReleaseNull(issuer
);
3989 CFReleaseNull(subject
);
3993 void SecPVCSetCheckRevocation(SecPVCRef pvc
, CFStringRef method
) {
3994 pvc
->check_revocation
= method
;
3995 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
3998 void SecPVCSetCheckRevocationResponseRequired(SecPVCRef pvc
) {
3999 pvc
->response_required
= true;
4000 secdebug("rvc", "revocation response required");
4003 void SecPVCSetCheckRevocationOnline(SecPVCRef pvc
) {
4004 pvc
->online_revocation
= true;
4005 secdebug("rvc", "revocation force online check");
4008 bool SecPVCIsAnchored(SecPVCRef pvc
) {
4009 return SecCertificatePathIsAnchored(pvc
->path
);
4012 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
4013 return pvc
->verifyTime
;
4016 static int32_t detailKeyToCssmErr(CFStringRef key
) {
4019 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
4020 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
4022 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
4023 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
4025 else if (CFEqual(key
, kSecPolicyCheckValidLeaf
) ||
4026 CFEqual(key
, kSecPolicyCheckValidIntermediates
) ||
4027 CFEqual(key
, kSecPolicyCheckValidRoot
)) {
4028 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
4034 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
4036 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
4037 bool result
= false;
4038 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, ix
);
4039 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, ix
);
4040 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4042 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4043 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4044 CFNumberRef allowedErrorNumber
= NULL
;
4045 if (!isDictionary(constraint
)) {
4048 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
4049 int32_t allowedErrorValue
= 0;
4050 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
4054 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
4055 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
4064 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
4065 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4066 for (certIX
= 0; certIX
< certCount
; certIX
++) {
4067 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, certIX
);
4068 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4069 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4070 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4071 if (!isDictionary(constraint
)) {
4075 CFDictionaryRef policyOptions
= NULL
;
4076 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
4077 if (policyOptions
&& isDictionary(policyOptions
) &&
4078 CFDictionaryContainsKey(policyOptions
, key
)) {
4086 /* AUDIT[securityd](done):
4087 policy->_options is a caller provided dictionary, only its cf type has
4090 bool SecPVCSetResultForced(SecPVCRef pvc
,
4091 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
4093 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
4094 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
4095 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
4097 (force
? "force" : ""), result
);
4099 /* If this is not something the current policy cares about ignore
4100 this error and return true so our caller continues evaluation. */
4102 /* Either the policy or the usage constraints have to have this key */
4103 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
4104 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
4105 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
4110 /* Check to see if the SecTrustSettings for the certificate in question
4111 tell us to ignore this error. */
4112 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
4113 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
4117 pvc
->result
= false;
4121 CFMutableDictionaryRef detail
=
4122 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
4124 /* Perhaps detail should have an array of results per key? As it stands
4125 in the case of multiple policy failures the last failure stands. */
4126 CFDictionarySetValue(detail
, key
, result
);
4131 bool SecPVCSetResult(SecPVCRef pvc
,
4132 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
4133 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
4136 /* AUDIT[securityd](done):
4137 key(ok) is a caller provided.
4138 value(ok, unused) is a caller provided.
4140 static void SecPVCValidateKey(const void *key
, const void *value
,
4142 SecPVCRef pvc
= (SecPVCRef
)context
;
4144 /* If our caller doesn't want full details and we failed earlier there is
4145 no point in doing additional checks. */
4146 if (!pvc
->result
&& !pvc
->details
)
4149 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
4150 CFDictionaryGetValue(pvc
->callbacks
, key
);
4154 /* Why not to have optional policy checks rant:
4155 Not all keys are in all dictionaries anymore, so why not make checks
4156 optional? This way a client can ask for something and the server will
4157 do a best effort based on the supported flags. It works since they are
4158 synchronized now, but we need some debug checking here for now. */
4159 pvc
->result
= false;
4161 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
4162 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
4163 pvc
->result
= false;
4165 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
4166 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
4167 pvc
->result
= false;
4170 /* Non standard validation phase, nothing is optional. */
4171 pvc
->result
= false;
4176 fcn(pvc
, (CFStringRef
)key
);
4179 /* AUDIT[securityd](done):
4180 policy->_options is a caller provided dictionary, only its cf type has
4183 bool SecPVCLeafChecks(SecPVCRef pvc
) {
4185 CFArrayRef policies
= pvc
->policies
;
4186 CFIndex ix
, count
= CFArrayGetCount(policies
);
4187 for (ix
= 0; ix
< count
; ++ix
) {
4188 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
4190 /* Validate all keys for all policies. */
4191 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
4192 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
4193 if (!pvc
->result
&& !pvc
->details
)
4200 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
4201 /* Check stuff common to intermediate and anchors. */
4202 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
4203 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
4204 bool is_anchor
= (ix
== SecPVCGetCertificateCount(pvc
) - 1
4205 && SecPVCIsAnchored(pvc
));
4206 if (!SecCertificateIsValid(cert
, verifyTime
)) {
4207 /* Certificate has expired. */
4208 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckValidRoot
4209 : kSecPolicyCheckValidIntermediates
, ix
, kCFBooleanFalse
))
4213 if (SecCertificateIsWeakKey(cert
)) {
4214 /* Certificate uses weak key. */
4215 if (!SecPVCSetResult(pvc
, is_anchor
? kSecPolicyCheckWeakRoot
4216 : kSecPolicyCheckWeakIntermediates
, ix
, kCFBooleanFalse
))
4221 /* Perform anchor specific checks. */
4222 /* Don't think we have any of these. */
4224 /* Perform intermediate specific checks. */
4226 /* (k) Basic constraints only relevant for v3 and later. */
4227 if (SecCertificateVersion(cert
) >= 3) {
4228 const SecCEBasicConstraints
*bc
=
4229 SecCertificateGetBasicConstraints(cert
);
4230 if (!bc
|| !bc
->isCA
) {
4231 /* Basic constraints not present or not marked as isCA, illegal. */
4232 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
4233 ix
, kCFBooleanFalse
, true))
4237 /* (l) max_path_length is checked elsewhere. */
4239 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
4240 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
4241 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
4242 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
4243 ix
, kCFBooleanFalse
, true))
4252 bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
4253 /* Check stuff common to intermediate and anchors. */
4255 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
4256 if (NULL
!= otapkiRef
)
4258 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
4259 CFRelease(otapkiRef
);
4260 if (NULL
!= blackListedKeys
)
4262 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
4263 CFIndex count
= SecPVCGetCertificateCount(pvc
);
4264 bool is_last
= (ix
== count
- 1);
4265 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
4267 /* Check for blacklisted intermediate issuer keys. */
4268 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
4270 /* Check dgst against blacklist. */
4271 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
4272 /* Check allow list for this blacklisted issuer key,
4273 which is the authority key of the issued cert at ix-1.
4274 If ix is the last cert, the root is missing, so we
4275 also check our own authority key in that case.
4277 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
4278 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
4280 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
4281 ix
, kCFBooleanFalse
, true);
4283 pvc
->is_allowlisted
= allowed
;
4288 CFRelease(blackListedKeys
);
4296 bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
4298 /* Check stuff common to intermediate and anchors. */
4299 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
4300 if (NULL
!= otapkiRef
)
4302 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
4303 CFRelease(otapkiRef
);
4304 if (NULL
!= grayListKeys
)
4306 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
4307 CFIndex count
= SecPVCGetCertificateCount(pvc
);
4308 bool is_last
= (ix
== count
- 1);
4309 bool is_anchor
= (is_last
&& SecPVCIsAnchored(pvc
));
4311 /* Check for gray listed intermediate issuer keys. */
4312 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
4314 /* Check dgst against gray list. */
4315 if (CFSetContainsValue(grayListKeys
, dgst
)) {
4316 /* Check allow list for this graylisted issuer key,
4317 which is the authority key of the issued cert at ix-1.
4318 If ix is the last cert, the root is missing, so we
4319 also check our own authority key in that case.
4321 bool allowed
= ((ix
&& SecPVCCheckCertificateAllowList(pvc
, ix
- 1)) ||
4322 (is_last
&& SecPVCCheckCertificateAllowList(pvc
, ix
)));
4324 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
4325 ix
, kCFBooleanFalse
, true);
4327 pvc
->is_allowlisted
= allowed
;
4332 CFRelease(grayListKeys
);
4340 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
4341 if (!isString(searchName
) && !isString(searchOid
)) {
4344 CFArrayRef policies
= pvc
->policies
;
4345 CFIndex ix
, count
= CFArrayGetCount(policies
);
4346 for (ix
= 0; ix
< count
; ++ix
) {
4347 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
4348 CFStringRef policyName
= SecPolicyGetName(policy
);
4349 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
4350 /* Prefer a match of both name and OID */
4351 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
4352 if (CFEqual(searchOid
, policyOid
) &&
4353 CFEqual(searchName
, policyName
)) {
4354 if (policyIX
) { *policyIX
= ix
; }
4358 /* Next best is just OID. */
4359 if (!searchName
&& searchOid
&& policyOid
) {
4360 if (CFEqual(searchOid
, policyOid
)) {
4361 if (policyIX
) { *policyIX
= ix
; }
4365 if (!searchOid
&& searchName
&& policyName
) {
4366 if (CFEqual(searchName
, policyName
)) {
4367 if (policyIX
) { *policyIX
= ix
; }
4375 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
4376 if (!isString(stringValue
)) {
4379 bool result
= false;
4381 CFStringRef tmpStringValue
= NULL
;
4382 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
4383 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
4385 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
4387 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4388 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4389 /* Have to look for all the possible locations of name string */
4390 CFStringRef policyString
= NULL
;
4391 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
4392 if (!policyString
) {
4393 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
4395 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
4400 CFArrayRef policyStrings
= NULL
;
4401 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
4402 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
4403 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
4411 CFReleaseNull(tmpStringValue
);
4416 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
4417 uint32_t ourTSKeyUsage
= 0;
4418 uint32_t keyUsage
= 0;
4419 if (keyUsageNumber
&&
4420 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
4421 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
4422 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
4424 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
4425 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
4427 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
4428 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
4430 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
4431 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
4433 if (keyUsage
== kSecKeyUsageAll
) {
4434 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
4437 return ourTSKeyUsage
;
4440 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
4441 uint32_t ourTSKeyUsage
= 0;
4442 CFTypeRef policyKeyUsageType
= NULL
;
4444 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
4445 if (isArray(policyKeyUsageType
)) {
4446 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
4447 for (ix
= 0; ix
< count
; ix
++) {
4448 CFNumberRef policyKeyUsageNumber
= NULL
;
4449 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
4450 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
4452 } else if (isNumber(policyKeyUsageType
)) {
4453 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
4456 return ourTSKeyUsage
;
4459 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
4460 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
4461 int64_t keyUsageValue
= 0;
4462 uint32_t ourKeyUsage
= 0;
4464 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
4468 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
4472 /* We're using the key for revocation if we have the OCSPSigner policy.
4473 * @@@ If we support CRLs, we'd need to check for that policy here too.
4475 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
4476 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
4479 /* We're using the key for verifying a cert if it's a root/intermediate
4480 * in the chain. If the cert isn't in the path yet, we're about to add it,
4481 * so it's a root/intermediate. If there is no path, this is the leaf.
4483 CFIndex pathIndex
= -1;
4485 pathIndex
= SecCertificatePathGetIndexOfCertificate(pvc
->path
, certificate
);
4489 if (pathIndex
!= 0) {
4490 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
4493 /* The rest of the key usages may be specified by the policy(ies). */
4494 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
4495 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
4496 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4498 /* Get key usage from ALL policies */
4499 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
4500 for (ix
= 0; ix
< count
; ix
++) {
4501 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
4502 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
4506 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
4513 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4515 #include <Security/SecTrustedApplicationPriv.h>
4516 #include <bsm/libbsm.h>
4517 #include <libproc.h>
4519 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
4520 bool result
= false;
4521 audit_token_t auditToken
= {};
4522 char path
[MAXPATHLEN
];
4524 require(appRef
&& clientAuditToken
, out
);
4525 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
4527 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
4528 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
4529 require(proc_pidpath(audit_token_to_pid(auditToken
), path
, sizeof(path
)) > 0, out
);
4531 if(errSecSuccess
== SecTrustedApplicationValidateWithPath((SecTrustedApplicationRef
)appRef
, path
)) {
4540 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
4541 if (!isDictionary(options
)) {
4546 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
4548 /* We need to run the leaf and path checks using these options. */
4549 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
4550 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
4552 pvc
->callbacks
= gSecPolicyPathCallbacks
;
4553 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
4556 pvc
->callbacks
= currentCallbacks
;
4558 /* Our work here is done; no need to claim a match */
4562 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
4563 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
4564 CFNumberRef keyUsageNumber
= NULL
;
4565 CFTypeRef trustedApplicationData
= NULL
;
4566 CFDictionaryRef policyOptions
= NULL
;
4568 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
4569 keyUsageMatch
= false, policyOptionMatch
= false;
4570 bool result
= false;
4572 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4573 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
4574 SecPolicyRef policy
= NULL
;
4575 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4576 policyOid
= (policy
) ? policy
->_oid
: NULL
;
4578 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
4580 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
4581 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
4582 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
4583 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
4585 CFIndex policyIX
= -1;
4586 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
4587 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
4588 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
4589 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
4591 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
4592 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
4593 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
4594 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
4595 CFReleaseNull(clientAuditToken
);
4597 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
4598 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
4602 /* If we either didn't find the parameter in the dictionary or we got a match
4603 * against that parameter, for all possible parameters in the dictionary, then
4604 * this trust setting result applies to the output. */
4605 if (((!policyOid
&& !policyName
) || policyMatch
) &&
4606 (!policyString
|| policyStringMatch
) &&
4607 (!trustedApplicationData
|| applicationMatch
) &&
4608 (!keyUsageNumber
|| keyUsageMatch
) &&
4609 (!policyOptions
|| policyOptionMatch
)) {
4616 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
4617 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
4618 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
4619 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
4620 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
4621 if (!isDictionary(constraint
)) {
4625 CFNumberRef resultNumber
= NULL
;
4626 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
4627 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
4628 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
4629 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
4630 resultValue
= kSecTrustSettingsResultTrustRoot
;
4633 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
4634 result
= resultValue
;
4641 bool SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
4642 bool shouldDeny
= false;
4643 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4644 for (certIX
= 0; certIX
< certCount
; certIX
++) {
4645 CFArrayRef constraints
= SecCertificatePathGetUsageConstraintsAtIndex(pvc
->path
, certIX
);
4646 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4647 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
4649 if (result
== kSecTrustSettingsResultDeny
) {
4650 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
4657 #define kSecPolicySHA256Size 32
4658 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
4659 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
4660 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
4662 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
4663 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
4664 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
4666 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
4667 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
4668 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
4670 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
4671 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
4672 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
4674 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
4675 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
4676 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
4678 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
4679 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
4680 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
4682 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
4683 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
4684 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
4686 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
4687 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
4688 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
4691 bool SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
4692 static CFSetRef sConstrainedRoots
= NULL
;
4693 static dispatch_once_t _t
;
4694 dispatch_once(&_t
, ^{
4695 const UInt8
*v_hashes
[] = {
4696 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
4697 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
4699 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
4700 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
4701 for (ix
=0; ix
<count
; ix
++) {
4702 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
4703 kSecPolicySHA256Size
, kCFAllocatorNull
);
4705 CFSetAddValue(set
, hash
);
4709 sConstrainedRoots
= set
;
4712 bool shouldDeny
= false;
4713 CFIndex certIX
, certCount
= SecCertificatePathGetCount(pvc
->path
);
4714 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
4715 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, certIX
);
4716 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
4717 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
4718 /* matched a constrained root; check notBefore dates on all its children. */
4719 CFIndex childIX
= certIX
;
4720 while (--childIX
>= 0) {
4721 SecCertificateRef child
= SecCertificatePathGetCertificateAtIndex(pvc
->path
, childIX
);
4722 /* 1 Dec 2016 00:00:00 GMT */
4723 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
4724 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
4730 CFReleaseNull(sha256
);
4735 /* AUDIT[securityd](done):
4736 policy->_options is a caller provided dictionary, only its cf type has
4739 bool SecPVCPathChecks(SecPVCRef pvc
) {
4740 secdebug("policy", "begin path: %@", pvc
->path
);
4741 bool completed
= true;
4742 /* This needs to be initialized before we call any function that might call
4743 SecPVCSetResultForced(). */
4745 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
4746 if (pvc
->result
|| pvc
->details
) {
4747 SecPolicyCheckBasicCertificateProcessing(pvc
,
4748 kSecPolicyCheckBasicCertificateProcessing
);
4751 CFArrayRef policies
= pvc
->policies
;
4752 CFIndex count
= CFArrayGetCount(policies
);
4753 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
4754 /* Validate all keys for all policies. */
4755 pvc
->callbacks
= gSecPolicyPathCallbacks
;
4756 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
4757 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
4758 if (!pvc
->result
&& !pvc
->details
)
4765 /* Check whether the TrustSettings say to deny a cert in the path. */
4766 (void)SecPVCCheckUsageConstraints(pvc
);
4768 /* Check for issuer date constraints. */
4769 (void)SecPVCCheckIssuerDateConstraints(pvc
);
4771 /* Check the things we can't check statically for the certificate path. */
4772 /* Critical Extensions, chainLength. */
4776 if ((pvc
->result
|| pvc
->details
) && pvc
->optionally_ev
) {
4777 bool pre_ev_check_result
= pvc
->result
;
4778 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
4779 pvc
->is_ev
= pvc
->result
;
4780 /* If ev checking failed, we still want to accept this chain
4781 as a non EV one, if it was valid as such. */
4782 pvc
->result
= pre_ev_check_result
;
4785 /* Check revocation always, since we don't want a lesser recoverable result
4786 * to prevent the check from occurring. */
4787 completed
= SecPVCCheckRevocation(pvc
);
4790 if (pvc
->result
|| pvc
->details
) {
4791 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
4792 SecPolicyCheckCT(pvc
, kSecPolicyCheckCertificateTransparency
);
4795 if (pvc
->is_ev
&& !pvc
->is_ct
) {
4796 pvc
->is_ct_whitelisted
= SecPVCCheckCTWhiteListedLeaf(pvc
);
4798 pvc
->is_ct_whitelisted
= false;
4802 secdebug("policy", "end %strusted completed: %d path: %@",
4803 (pvc
->result
? "" : "not "), completed
, pvc
->path
);
4807 /* This function returns 0 to indicate revocation checking was not completed
4808 for this certificate chain, otherwise return to date at which the first
4809 piece of revocation checking info we used expires. */
4810 CFAbsoluteTime
SecPVCGetEarliestNextUpdate(SecPVCRef pvc
) {
4811 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
4812 CFAbsoluteTime enu
= NULL_TIME
;
4813 if (certCount
<= 1 || !pvc
->rvcs
) {
4818 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
4819 SecRVCRef rvc
= &((SecRVCRef
)pvc
->rvcs
)[certIX
];
4820 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
4821 if (thisCertNextUpdate
== 0) {
4823 /* We allow for CA certs to not be revocation checked if they
4824 have no ocspResponders nor CRLDPs to check against, but the leaf
4825 must be checked in order for us to claim we did revocation
4827 SecCertificateRef cert
=
4828 SecPVCGetCertificateAtIndex(rvc
->pvc
, rvc
->certIX
);
4829 CFArrayRef ocspResponders
= NULL
;
4830 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
4832 CFArrayRef crlDPs
= NULL
;
4833 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
4835 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
4837 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
4840 /* We can't check this cert so we don't consider it a soft
4841 failure that we didn't. Ideally we should support crl
4842 checking and remove this workaround, since that more
4847 secdebug("rvc", "revocation checking soft failure for cert: %ld",
4849 enu
= thisCertNextUpdate
;
4852 if (enu
== 0 || thisCertNextUpdate
< enu
) {
4853 enu
= thisCertNextUpdate
;
4857 secdebug("rvc", "revocation valid until: %lg", enu
);