2 * Copyright (c) 2008-2020 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 - Engine for evaluating certificate paths against trust policies.
28 #include <AssertMacros.h>
32 #include <CommonCrypto/CommonDigest.h>
33 #include <CoreFoundation/CFTimeZone.h>
34 #include <CoreFoundation/CFNumber.h>
35 #include <sys/codesign.h>
36 #include <libDER/DER_CertCrl.h>
37 #include <libDER/DER_Encode.h>
38 #include <libDER/asn1Types.h>
39 #include <libDER/oids.h>
40 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
42 #include <utilities/array_size.h>
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/SecAppleAnchorPriv.h>
45 #include <utilities/debugging.h>
46 #include <utilities/SecInternalReleasePriv.h>
47 #include <security_asn1/SecAsn1Coder.h>
48 #include <security_asn1/ocspTemplates.h>
49 #include <security_asn1/oidsalg.h>
50 #include <security_asn1/oidsocsp.h>
51 #include <Security/SecPolicyInternal.h>
52 #include <Security/SecPolicyPriv.h>
53 #include <Security/SecTask.h>
54 #include <Security/SecCertificateInternal.h>
55 #include <Security/SecFramework.h>
56 #include <Security/SecPolicyInternal.h>
57 #include <Security/SecTrustPriv.h>
58 #include <Security/SecTrustInternal.h>
59 #include <Security/SecTrustSettingsPriv.h>
60 #include <Security/SecInternal.h>
61 #include <Security/SecKeyPriv.h>
63 #include "trust/trustd/SecPolicyServer.h"
64 #include "trust/trustd/policytree.h"
65 #include "trust/trustd/nameconstraints.h"
66 #include "trust/trustd/SecTrustServer.h"
67 #include "trust/trustd/SecTrustLoggingServer.h"
68 #include "trust/trustd/SecRevocationServer.h"
69 #include "trust/trustd/SecCertificateServer.h"
70 #include "trust/trustd/SecCertificateSource.h"
71 #include "trust/trustd/SecOCSPResponse.h"
72 #include "trust/trustd/SecTrustStoreServer.h"
73 #include "trust/trustd/OTATrustUtilities.h"
74 #include "trust/trustd/personalization.h"
75 #include "trust/trustd/CertificateTransparency.h"
78 #include <Security/SecTaskPriv.h>
81 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
82 #ifndef DUMP_OCSPRESPONSES
83 #define DUMP_OCSPRESPONSES 0
86 #if DUMP_OCSPRESPONSES
91 static void secdumpdata(CFDataRef data
, const char *name
) {
92 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
93 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
100 /********************************************************
101 ****************** SecPolicy object ********************
102 ********************************************************/
104 static SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
);
106 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
107 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
109 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
111 CFArrayRef result
= NULL
;
112 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
113 if (NULL
== otapkiRef
)
118 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
119 CFRelease(otapkiRef
);
121 if (NULL
== evToPolicyAnchorDigest
)
126 CFArrayRef roots
= NULL
;
127 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
128 if (oid
&& evToPolicyAnchorDigest
)
130 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
131 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
133 secerror("EVRoot.plist has non array value");
138 CFReleaseSafe(evToPolicyAnchorDigest
);
143 bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
144 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
147 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
148 policy_set_t valid_policies
) {
149 CFDictionaryRef keySizes
= NULL
;
150 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
152 /* Ensure that this certificate is a valid anchor for one of the
153 certificate policy oids specified in the leaf. */
154 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
156 bool good_ev_anchor
= false;
157 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
158 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
159 if (digests
&& CFArrayContainsValue(digests
,
160 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
161 secdebug("ev", "found anchor for policy oid");
162 good_ev_anchor
= true;
166 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
168 CFAbsoluteTime october2006
= 178761600;
169 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
170 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
171 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
173 if (SecCertificateVersion(certificate
) >= 3
174 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
175 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
176 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
177 secnotice("ev", "Anchor has invalid basic constraints"));
178 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
179 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
180 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
181 secnotice("ev", "Anchor has invalid key usage %u", ku
));
184 /* At least RSA 2048 or ECC NIST P-256. */
185 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
186 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
187 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
188 const void *values
[] = { rsaSize
, ecSize
};
189 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
190 &kCFTypeDictionaryKeyCallBacks
,
191 &kCFTypeDictionaryValueCallBacks
), notEV
);
192 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
193 secnotice("ev", "Anchor's public key is too weak for EV"));
198 CFReleaseNull(rsaSize
);
199 CFReleaseNull(ecSize
);
200 CFReleaseNull(keySizes
);
204 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
205 CFMutableDictionaryRef keySizes
= NULL
;
206 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
209 const SecCECertificatePolicies
*cp
;
210 cp
= SecCertificateGetCertificatePolicies(certificate
);
211 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
212 secnotice("ev", "SubCA missing certificate policies"));
213 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
214 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
215 secnotice("ev", "SubCA missing CRLDP"));
216 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
217 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
218 secnotice("ev", "SubCA has invalid basic constraints"));
219 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
220 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
221 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
222 secnotice("ev", "SubCA has invalid key usage %u", ku
));
224 /* 6.1.5 Key Sizes */
225 CFAbsoluteTime jan2011
= 315532800;
226 CFAbsoluteTime jan2014
= 410227200;
227 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
228 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
229 &kCFTypeDictionaryValueCallBacks
), notEV
);
230 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
231 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
232 SecCertificateNotValidAfter(certificate
) < jan2014
) {
233 /* At least RSA 1024 or ECC NIST P-256. */
234 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
235 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
236 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
237 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
239 /* At least RSA 2028 or ECC NIST P-256. */
240 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
241 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
242 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
243 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
246 /* 7.1.3 Algorithm Object Identifiers */
247 CFAbsoluteTime jan2016
= 473299200;
248 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
250 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
251 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
257 CFReleaseNull(rsaSize
);
258 CFReleaseNull(ecSize
);
259 CFReleaseNull(keySizes
);
263 /********************************************************
264 **************** SecPolicy Callbacks *******************
265 ********************************************************/
266 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
270 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
272 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
273 CFDataRef parentSubjectKeyID
= NULL
;
274 for (ix
= count
- 1; ix
>= 0; --ix
) {
275 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
276 /* If the previous certificate in the chain had a SubjectKeyID,
277 make sure it matches the current certificates AuthorityKeyID. */
278 if (parentSubjectKeyID
) {
279 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
280 SubjectKeyID can be critical. Currenty we don't check
282 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
283 if (authorityKeyID
) {
284 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
285 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
286 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
292 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
296 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
298 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
299 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
300 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
301 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
302 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
306 /* AUDIT[securityd](done):
307 policy->_options is a caller provided dictionary, only its cf type has
310 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
311 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
312 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
313 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
314 /* leaf check enforced */
315 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
316 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
319 /* subCA check produces metrics */
320 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
321 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
322 if (count
> 2 && analytics
) {
323 for (ix
= 1; ix
< count
- 1 ; ++ix
) {
324 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
325 CFArrayRef ekus
= SecCertificateCopyExtendedKeyUsage(cert
);
326 if (ekus
&& CFArrayGetCount(ekus
) && // subCA has ekus
327 !SecPolicyCheckCertExtendedKeyUsage(cert
, CFSTR("2.5.29.37.0")) && // but not the anyEKU
328 !SecPolicyCheckCertExtendedKeyUsage(cert
, xeku
)) { // and not the EKUs specified by the policy
329 analytics
->ca_fail_eku_check
= true;
336 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
338 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
341 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
343 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
344 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
345 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
346 for (ix
= 0; ix
< count
; ++ix
) {
347 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
348 if (!SecPolicyCheckCertNonEmptySubject(cert
, pvcValue
)) {
349 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
355 /* AUDIT[securityd](done):
356 policy->_options is a caller provided dictionary, only its cf type has
359 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
361 /* @@@ Consider what to do if the caller passes in no hostname. Should
362 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
363 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
364 CFStringRef hostName
= (CFStringRef
)
365 CFDictionaryGetValue(policy
->_options
, key
);
366 if (!isString(hostName
)) {
367 /* @@@ We can't return an error here and making the evaluation fail
368 won't help much either. */
372 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
373 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
376 /* Hostname mismatch or no hostnames found in certificate. */
377 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
382 /* AUDIT[securityd](done):
383 policy->_options is a caller provided dictionary, only its cf type has
386 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
387 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
388 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
389 if (!isString(email
)) {
390 /* We can't return an error here and making the evaluation fail
391 won't help much either. */
395 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
397 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
398 /* Hostname mismatch or no hostnames found in certificate. */
399 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
403 static void SecPolicyCheckTemporalValidity(SecPVCRef pvc
,
405 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
406 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
407 for (ix
= 0; ix
< count
; ++ix
) {
408 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
409 if (!SecCertificateIsValid(cert
, verifyTime
)) {
410 /* Intermediate certificate has expired. */
411 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
417 /* AUDIT[securityd](done):
418 policy->_options is a caller provided dictionary, only its cf type has
421 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
423 CFIndex count
= SecPVCGetCertificateCount(pvc
);
425 /* Can't check intermediates common name if there is no intermediate. */
426 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
430 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
431 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
432 CFStringRef commonName
=
433 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
434 if (!isString(commonName
)) {
435 /* @@@ We can't return an error here and making the evaluation fail
436 won't help much either. */
439 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
440 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
444 /* AUDIT[securityd](done):
445 policy->_options is a caller provided dictionary, only its cf type has
448 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
450 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
451 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
452 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
454 if (!isString(common_name
)) {
455 /* @@@ We can't return an error here and making the evaluation fail
456 won't help much either. */
459 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
460 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
464 /* AUDIT[securityd](done):
465 policy->_options is a caller provided dictionary, only its cf type has
468 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
470 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
471 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
472 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
474 if (!isString(prefix
)) {
475 /* @@@ We can't return an error here and making the evaluation fail
476 won't help much either. */
479 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
480 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
484 /* AUDIT[securityd](done):
485 policy->_options is a caller provided dictionary, only its cf type has
488 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
490 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
491 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
492 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
494 if (!isString(common_name
)) {
495 /* @@@ We can't return an error here and making the evaluation fail
496 won't help much either. */
499 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
500 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
504 /* AUDIT[securityd](done):
505 policy->_options is a caller provided dictionary, only its cf type has
508 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
510 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
511 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
512 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
514 /* @@@ We can't return an error here and making the evaluation fail
515 won't help much either. */
518 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
519 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
524 /* AUDIT[securityd](done):
525 policy->_options is a caller provided dictionary, only its cf type has
528 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
530 CFIndex count
= SecPVCGetCertificateCount(pvc
);
531 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
532 CFNumberRef chainLength
=
533 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
535 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
536 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
537 /* @@@ We can't return an error here and making the evaluation fail
538 won't help much either. */
541 if (value
!= count
) {
542 /* Chain length doesn't match policy requirement. */
543 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
548 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
549 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
550 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
552 bool foundMatch
= false;
554 foundMatch
= CFEqual(digest
, value
);
555 else if (isArray(value
))
556 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
558 /* @@@ We only support Data and Array but we can't return an error here so.
559 we let the evaluation fail (not much help) and assert in debug. */
566 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
567 CFIndex count
= SecPVCGetCertificateCount(pvc
);
568 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
569 CFDataRef anchorSHA256
= NULL
;
570 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
572 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
573 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
576 CFReleaseNull(anchorSHA256
);
581 Check the SHA256 of SPKI of the first intermediate CA certificate in the path.
582 policy->_options is a caller provided dictionary, only its cf type has
585 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
587 SecCertificateRef cert
= NULL
;
588 CFDataRef digest
= NULL
;
590 if (SecPVCGetCertificateCount(pvc
) < 2) {
591 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
595 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
596 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
598 if (!isDigestInPolicy(pvc
, key
, digest
)) {
599 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
601 CFReleaseNull(digest
);
605 Check the SPKI SHA256 of CA certificates in the path
606 policy->_options is a caller provided dictionary, only its cf type has
609 static void SecPolicyCheckCAspkiSHA256(SecPVCRef pvc
,
611 SecCertificateRef cert
= NULL
;
612 CFDataRef digest
= NULL
;
614 if (SecPVCGetCertificateCount(pvc
) < 2) {
615 SecPVCSetResult(pvc
, kSecPolicyCheckCAspkiSHA256
, 0, kCFBooleanFalse
);
619 bool spkiSHA256match
= false;
620 CFIndex count
= SecPVCGetCertificateCount(pvc
);
621 for (CFIndex i
= 1; i
< count
&& spkiSHA256match
== false; i
++) {
622 cert
= SecPVCGetCertificateAtIndex(pvc
, i
);
623 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
625 if (isDigestInPolicy(pvc
, key
, digest
)) {
626 spkiSHA256match
= true;
629 CFReleaseNull(digest
);
632 if (spkiSHA256match
== true) {
636 for (CFIndex i
= 1; i
< count
; i
++) {
637 SecPVCSetResult(pvc
, kSecPolicyCheckCAspkiSHA256
, i
, kCFBooleanFalse
);
642 Check the SPKI SHA256 of the leaf certificate.
643 policy->_options is a caller provided dictionary, only its cf type has
646 static void SecPolicyCheckLeafSPKISHA256(SecPVCRef pvc
,
648 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
650 CFArrayRef leafSPKISHA256
= CFDictionaryGetValue(policy
->_options
, key
);
651 if (isArray(leafSPKISHA256
) == false) {
652 /* @@@ We can't return an error here and making the evaluation fail
653 won't help much either. */
657 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
658 CFDataRef digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
660 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
664 if (!CFArrayContainsValue(leafSPKISHA256
, CFRangeMake(0, CFArrayGetCount(leafSPKISHA256
)), digest
)) {
665 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
668 CFReleaseNull(digest
);
672 policy->_options is a caller provided dictionary, only its cf type has
675 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
677 CFIndex count
= SecPVCGetCertificateCount(pvc
);
678 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
679 SecAppleTrustAnchorFlags flags
= 0;
682 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
685 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
692 /* AUDIT[securityd](done):
693 policy->_options is a caller provided dictionary, only its cf type has
696 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
698 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
699 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
700 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
702 if (!isString(org
)) {
703 /* @@@ We can't return an error here and making the evaluation fail
704 won't help much either. */
707 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
708 /* Leaf Subject Organization mismatch. */
709 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
713 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
715 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
716 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
717 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
719 if (!isString(orgUnit
)) {
720 /* @@@ We can't return an error here and making the evaluation fail
721 won't help much either. */
724 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
725 /* Leaf Subject Organization mismatch. */
726 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
730 /* AUDIT[securityd](done):
731 policy->_options is a caller provided dictionary, only its cf type has
734 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
736 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
737 CFArrayRef trustedServerNames
= (CFArrayRef
)
738 CFDictionaryGetValue(policy
->_options
, key
);
739 /* No names specified means we accept any name. */
740 if (!trustedServerNames
)
742 if (!isArray(trustedServerNames
)) {
743 /* @@@ We can't return an error here and making the evaluation fail
744 won't help much either. */
748 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
749 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
750 /* Hostname mismatch or no hostnames found in certificate. */
751 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
755 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
756 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
757 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
758 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
759 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
760 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
761 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
762 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
763 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
764 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
766 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
767 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
768 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
769 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
770 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
771 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
772 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
773 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
774 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
775 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
776 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
777 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
778 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
779 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
781 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
784 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
786 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
787 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
789 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
790 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
791 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
793 CFDataRef serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
795 CFIndex serial_length
= CFDataGetLength(serial
);
796 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
798 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
803 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
805 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
807 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
808 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
810 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
811 CFReleaseSafe(serial
);
820 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
821 if (NULL
!= otapkiRef
)
823 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
824 CFRelease(otapkiRef
);
825 if (NULL
!= blackListedKeys
)
827 /* Check for blacklisted intermediates keys. */
828 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
831 /* Check dgst against blacklist. */
832 if (CFSetContainsValue(blackListedKeys
, dgst
))
834 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
838 CFRelease(blackListedKeys
);
843 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
845 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
846 if (NULL
!= otapkiRef
)
848 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
849 CFRelease(otapkiRef
);
850 if (NULL
!= grayListedKeys
)
852 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
854 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
857 /* Check dgst against gray. */
858 if (CFSetContainsValue(grayListedKeys
, dgst
))
860 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
864 CFRelease(grayListedKeys
);
869 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
871 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
872 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
873 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
875 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
876 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
877 // Force because we may have gotten the legacy key instead of the new key in the policy
878 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkerOid
, 0, kCFBooleanFalse
, true);
882 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
884 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
885 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
886 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
888 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
889 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
895 * The value is a dictionary. The dictionary contains keys indicating
896 * whether the value is for Prod or QA. The values are the same as
897 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
899 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
901 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
902 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
903 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
904 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
906 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
909 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
910 // Force because we may have gotten the legacy key instead of the new key in the policy
911 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkersProdAndQA
, 0, kCFBooleanFalse
, true);
916 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
918 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
919 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
920 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
922 for (ix
= 1; ix
< count
- 1; ix
++) {
923 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
924 if (SecCertificateHasMarkerExtension(cert
, value
))
927 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
928 // Force because we may have gotten the legacy key instead of the new key in the policy
929 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateMarkerOid
, 0, kCFBooleanFalse
, true);
932 static void SecPolicyCheckIntermediateMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
934 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
935 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
936 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
938 for (ix
= 1; ix
< count
- 1; ix
++) {
939 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
940 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
941 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
946 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
948 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
949 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
950 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
952 for (ix
= 1; ix
< count
- 1; ix
++) {
953 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
954 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
955 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
960 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
962 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
963 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
964 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
966 for (ix
= 1; ix
< count
- 1; ix
++) {
967 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
968 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
969 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
970 // Force because we may have gotten the legacy key instead of the new key in the policy
971 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateOrganization
, ix
, kCFBooleanFalse
, true);
976 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
978 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
979 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
980 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
982 for (ix
= 1; ix
< count
- 1; ix
++) {
983 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
984 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
985 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
986 // Force because we may have gotten the legacy key instead of the new key in the policy
987 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateCountry
, ix
, kCFBooleanFalse
, true);
992 /****************************************************************************
993 *********************** New rfc5280 Chain Validation ***********************
994 ****************************************************************************/
996 #define POLICY_MAPPING 1
997 #define POLICY_SUBTREES 1
999 /* rfc5280 basic cert processing. */
1000 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1004 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1005 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1006 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1007 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1008 uint32_t n
= (uint32_t)count
;
1010 bool is_anchored
= SecPathBuilderIsAnchored(pvc
->builder
);
1011 bool is_anchor_trusted
= false;
1013 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, n
- 1);
1014 if (CFArrayGetCount(constraints
) == 0) {
1015 /* Given that the path builder has already indicated the last cert in this chain has
1016 * trust set on it, empty constraints means trusted. */
1017 is_anchor_trusted
= true;
1019 /* Determine whether constraints say to trust this cert for this PVC. */
1020 SecTrustSettingsResult tsResult
= SecPVCGetTrustSettingsResult(pvc
, SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1),
1022 if (tsResult
== kSecTrustSettingsResultTrustRoot
|| tsResult
== kSecTrustSettingsResultTrustAsRoot
) {
1023 is_anchor_trusted
= true;
1028 if (is_anchor_trusted
) {
1029 /* If the anchor is trusted we don't process the last cert in the
1033 Boolean isSelfSigned
= false;
1034 (void) SecCertificateIsSelfSigned(SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1), &isSelfSigned
);
1036 /* Add a detail for the root not being trusted. */
1037 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1038 n
- 1, kCFBooleanFalse
, true)) {
1042 /* Add a detail for the missing intermediate. */
1043 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckMissingIntermediate
,
1044 n
- 1, kCFBooleanFalse
, true)) {
1050 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1051 //policy_set_t user_initial_policy_set = NULL;
1052 //trust_anchor_t anchor;
1054 /* Initialization */
1056 CFMutableArrayRef permitted_subtrees
= NULL
;
1057 CFMutableArrayRef excluded_subtrees
= NULL
;
1058 /* set the initial subtrees to the trusted anchor's subtrees, if it has them */
1059 SecCertificateRef anchor
= SecCertificatePathVCGetRoot(path
);
1060 CFArrayRef anchor_permitted_subtrees
= SecCertificateGetPermittedSubtrees(anchor
);
1061 if (is_anchor_trusted
&& anchor_permitted_subtrees
) {
1062 permitted_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_permitted_subtrees
);
1064 permitted_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1067 CFArrayRef anchor_excluded_subtrees
= SecCertificateGetExcludedSubtrees(anchor
);
1068 if (is_anchor_trusted
&& anchor_excluded_subtrees
) {
1069 excluded_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_excluded_subtrees
);
1071 excluded_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1074 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1075 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1076 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1077 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1080 if (!SecCertificatePathVCVerifyPolicyTree(path
, is_anchor_trusted
)) {
1081 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckPolicyConstraints
, 0, kCFBooleanFalse
, true)) {
1087 /* Path builder ensures we only get cert chains with proper issuer
1088 chaining with valid signatures along the way. */
1089 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1090 SecKeyRef working_public_key
= anchor
->public_key
;
1091 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1093 uint32_t i
, max_path_length
= n
;
1094 SecCertificateRef cert
= NULL
;
1095 for (i
= 1; i
<= n
; ++i
) {
1097 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1098 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc
->builder
), n
- i
);
1100 /* (a) Verify the basic certificate information. */
1101 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1102 using the working_public_key and the working_public_key_parameters. */
1104 /* Already done by chain builder. */
1105 if (!SecCertificateIsValid(cert
, verify_time
)) {
1106 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, n
- i
, kCFBooleanFalse
)) {
1110 if (SecCertificateIsWeakKey(cert
)) {
1111 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, n
- i
, kCFBooleanFalse
)) {
1115 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
1116 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, n
- i
, kCFBooleanFalse
)) {
1121 /* @@@ cert.issuer == working_issuer_name. */
1125 if (!is_self_issued
|| i
== n
) {
1127 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1128 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1129 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1130 secnotice("policy", "name in excluded subtrees");
1131 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1134 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1135 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1136 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1137 secnotice("policy", "name not in permitted subtrees");
1138 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1143 /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
1145 /* If Last Cert in Path */
1149 /* Prepare for Next Cert */
1150 /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
1151 /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
1153 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1155 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1156 if (permitted_subtrees_in_cert
) {
1157 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1160 // could do something smart here to avoid inserting the exact same constraint
1161 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1162 if (excluded_subtrees_in_cert
) {
1163 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1164 CFRange range
= { 0, num_trees
};
1165 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1168 /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
1170 /* (k) Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1173 if (!is_self_issued
) {
1174 if (max_path_length
> 0) {
1177 /* max_path_len exceeded, illegal. */
1178 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsPathLen
,
1179 n
- i
, kCFBooleanFalse
, true)) {
1185 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(cert
);
1186 if (bc
&& bc
->pathLenConstraintPresent
1187 && bc
->pathLenConstraint
< max_path_length
) {
1188 max_path_length
= bc
->pathLenConstraint
;
1191 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1192 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1194 /* (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. */
1195 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1196 /* Certificate contains one or more unknown critical extensions. */
1197 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, n
- i
, kCFBooleanFalse
)) {
1202 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1203 /* Certificate contains one or more known exensions where parsing failed. */
1204 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, n
-i
, kCFBooleanFalse
, true)) {
1209 } /* end loop over certs in path */
1211 /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
1214 /* 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
1215 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1217 /* (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. */
1218 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1219 /* Certificate contains one or more unknown critical extensions. */
1220 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, 0, kCFBooleanFalse
)) {
1225 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1226 /* Certificate contains one or more known exensions where parsing failed. */
1227 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, 0, kCFBooleanFalse
, true)) {
1231 /* (g) done by SecCertificatePathVCVerifyPolicyTree */
1234 CFReleaseNull(permitted_subtrees
);
1235 CFReleaseNull(excluded_subtrees
);
1238 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1239 policy_set_t policies
= NULL
;
1240 const SecCECertificatePolicies
*cp
=
1241 SecCertificateGetCertificatePolicies(cert
);
1242 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1243 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1244 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1249 static void SecPolicyCheckEV(SecPVCRef pvc
,
1251 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1252 policy_set_t valid_policies
= NULL
;
1254 /* 6.1.7. Key Usage Purposes */
1256 CFAbsoluteTime jul2016
= 489024000;
1257 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1258 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1259 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1260 if (SecPVCSetResultForced(pvc
, key
,
1261 0, kCFBooleanFalse
, true)) {
1267 for (ix
= 0; ix
< count
; ++ix
) {
1268 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1269 policy_set_t policies
= policies_for_cert(cert
);
1272 /* anyPolicy in the leaf isn't allowed for EV, so only init
1273 valid_policies if we have real policies. */
1274 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1275 valid_policies
= policies
;
1278 } else if (ix
< count
- 1) {
1279 /* Subordinate CA */
1280 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1281 secnotice("ev", "subordinate certificate is not ev");
1282 if (SecPVCSetResultForced(pvc
, key
,
1283 ix
, kCFBooleanFalse
, true)) {
1284 policy_set_free(valid_policies
);
1285 policy_set_free(policies
);
1289 policy_set_intersect(&valid_policies
, policies
);
1292 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1293 secnotice("ev", "anchor certificate is not ev");
1294 if (SecPVCSetResultForced(pvc
, key
,
1295 ix
, kCFBooleanFalse
, true)) {
1296 policy_set_free(valid_policies
);
1297 policy_set_free(policies
);
1302 policy_set_free(policies
);
1303 if (!valid_policies
) {
1304 secnotice("ev", "valid_policies set is empty: chain not ev");
1305 /* If we ever get into a state where no policies are valid anymore
1306 this can't be an ev chain. */
1307 if (SecPVCSetResultForced(pvc
, key
,
1308 ix
, kCFBooleanFalse
, true)) {
1314 policy_set_free(valid_policies
);
1316 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1317 Subscriber MUST contain an OID defined by the CA in the certificate’s
1318 certificatePolicies extension that: (i) indicates which CA policy statement relates
1319 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1320 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1321 marks the certificate as being an EV Certificate.
1322 (b) EV Subordinate CA Certificates
1323 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1324 CA MUST contain one or more OIDs defined by the issuing CA that
1325 explicitly identify the EV Policies that are implemented by the Subordinate
1327 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1328 MAY contain the special anyPolicy OID (2.5.29.32.0).
1329 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1330 certificatePolicies or extendedKeyUsage extensions.
1334 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
1335 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1337 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
1338 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
1340 for (ix
= 0; ix
< count
; ix
++) {
1341 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1342 policy_set_t policies
= policies_for_cert(cert
);
1344 if (policy_set_contains(policies
, &key_value
)) {
1345 policy_set_free(policies
);
1348 policy_set_free(policies
);
1353 static void SecPolicyCheckCertificatePolicy(SecPVCRef pvc
, CFStringRef key
)
1355 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1356 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1357 bool result
= false;
1359 if (CFGetTypeID(value
) == CFDataGetTypeID())
1361 result
= checkPolicyOidData(pvc
, value
);
1362 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1363 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
1365 result
= checkPolicyOidData(pvc
, dataOid
);
1370 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1375 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
1377 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1378 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1379 if (isString(value
)) {
1380 SecPathBuilderSetRevocationMethod(pvc
->builder
, value
);
1384 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
1386 pvc
->require_revocation_response
= true;
1387 secdebug("policy", "revocation response required");
1390 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
1391 SecPathBuilderSetCheckRevocationOnline(pvc
->builder
);
1394 static void SecPolicyCheckRevocationIfTrusted(SecPVCRef pvc
, CFStringRef key
) {
1395 SecPathBuilderSetCheckRevocationIfTrusted(pvc
->builder
);
1398 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
1400 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1401 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1402 if (value
== kCFBooleanTrue
) {
1403 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
1405 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
1409 static void SecPolicyCheckWeakKeySize(SecPVCRef pvc
,
1411 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1412 for (ix
= 0; ix
< count
; ++ix
) {
1413 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1414 if (cert
&& SecCertificateIsWeakKey(cert
)) {
1415 /* Intermediate certificate has a weak key. */
1416 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1422 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
1423 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1424 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1425 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
1426 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1428 /* Don't check key size for user-anchored leafs */
1429 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, leaf
)) {
1433 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, leaf
)) {
1438 for (ix
= 0; ix
< count
; ++ix
) {
1439 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1440 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
1441 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1447 static void SecPolicyCheckWeakSignature(SecPVCRef pvc
, CFStringRef key
) {
1448 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1449 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1450 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
1451 for (ix
= 0; ix
< count
; ++ix
) {
1452 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1453 if (!SecPolicyCheckCertWeakSignature(cert
, pvcValue
)) {
1454 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1460 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
1462 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
1464 /* Ignore (a non-self-signed) anchor if it's trusted by the user */
1465 bool userAnchored
= SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, SecPVCGetCertificateAtIndex(pvc
, count
- 1));
1467 userAnchored
= userAnchored
|| SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
,
1468 SecPVCGetCertificateAtIndex(pvc
, count
- 1));
1470 if (SecPathBuilderIsAnchored(pvc
->builder
) && userAnchored
) {
1474 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1475 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
1476 while (ix
< count
) {
1477 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1478 /* note that these checks skip self-signed certs */
1479 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
1480 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1487 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
1488 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1489 require_quiet(leaf
, out
);
1491 /* And now a special snowflake from our tests */
1493 /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
1494 /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
1495 /* Not After : May 26 09:37:50 2017 GMT */
1496 static const uint8_t vodafone
[] = {
1497 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
1498 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
1501 CFDataRef leafFingerprint
= SecCertificateGetSHA1Digest(leaf
);
1502 require_quiet(leafFingerprint
, out
);
1503 const unsigned int len
= 20;
1504 const uint8_t *dp
= CFDataGetBytePtr(leafFingerprint
);
1505 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
1513 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
1515 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
1517 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1519 Boolean keyInPolicy
= false;
1520 CFArrayRef policies
= pvc
->policies
;
1521 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1522 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1523 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1524 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
1529 /* We only enforce this check when *both* of the following are true:
1530 * 1. One of the certs in the path has this usage constraint, and
1531 * 2. One of the policies in the PVC has this key
1532 * (As compared to normal policy options which require only one to be true..) */
1533 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
1536 /* Ignore the anchor if it's trusted */
1537 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
1540 for (ix
= 0; ix
< count
; ++ix
) {
1541 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1542 if (SecCertificateIsWeakHash(cert
)) {
1543 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
1544 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
1554 static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc
,
1556 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1558 Boolean keyInPolicy
= false;
1559 CFArrayRef policies
= pvc
->policies
;
1560 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1561 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1562 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1563 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
1568 /* We only enforce this check when *both* of the following are true:
1569 * 1. One of the certs in the path has this usage constraint, and
1570 * 2. One of the policies in the PVC has this key
1571 * (As compared to normal policy options which require only one to be true..) */
1572 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
1575 /* Ignore the anchor if it's trusted */
1576 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
1579 for (ix
= 0; ix
< count
; ++ix
) {
1580 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1581 if (!SecCertificateIsStrongKey(cert
)) {
1582 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
1592 static void SecPolicyCheckPinningRequired(SecPVCRef pvc
, CFStringRef key
) {
1593 /* Pinning is disabled on the system, skip. */
1594 if (SecIsInternalRelease()) {
1595 if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
1596 CFSTR("com.apple.security"), NULL
)) {
1601 CFArrayRef policies
= pvc
->policies
;
1602 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1603 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1604 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1605 CFStringRef policyName
= SecPolicyGetName(policy
);
1606 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
1607 /* policy required pinning, but we didn't use a pinning policy */
1608 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
)) {
1615 static bool is_configured_test_system_root(SecCertificateRef root
, CFStringRef preference
) {
1616 if (!SecIsInternalRelease()) {
1619 bool result
= false;
1620 CFDataRef rootHash
= SecCertificateCopySHA256Digest(root
);
1621 CFTypeRef value
= CFPreferencesCopyAppValue(preference
, CFSTR("com.apple.security"));
1622 require_quiet(isData(value
), out
);
1623 require_quiet(CFEqual(rootHash
, value
), out
);
1627 CFReleaseNull(value
);
1628 CFReleaseNull(rootHash
);
1632 static bool is_ct_excepted_domain(CFStringRef hostname
, CFStringRef exception
) {
1633 if (kCFCompareEqualTo
== CFStringCompare(exception
, hostname
, kCFCompareCaseInsensitive
)) {
1636 } else if (CFStringHasPrefix(exception
, CFSTR("."))) {
1638 CFIndex elength
= CFStringGetLength(exception
);
1639 CFIndex hlength
= CFStringGetLength(hostname
);
1640 if (hlength
> elength
) {
1641 CFRange compareRange
= { hlength
- elength
, elength
};
1642 if (kCFCompareEqualTo
== CFStringCompareWithOptions(hostname
, exception
, compareRange
, kCFCompareCaseInsensitive
)) {
1645 } else if (hlength
+ 1 == elength
) {
1646 CFRange compareRange
= { 1, hlength
};
1647 if (kCFCompareEqualTo
== CFStringCompareWithOptions(exception
, hostname
, compareRange
, kCFCompareCaseInsensitive
)) {
1655 static OSStatus
is_subtree_dn_with_org(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
1656 if (gnType
!= GNT_DirectoryName
) {
1657 return errSecInternal
;
1660 DERDecodedInfo subtreeName_content
;
1661 if (DR_Success
!= DERDecodeItem(generalName
, &subtreeName_content
) || subtreeName_content
.tag
!= ASN1_CONSTR_SEQUENCE
) {
1662 return errSecDecode
;
1665 CFArrayRef subtree_orgs
= SecCertificateCopyOrganizationFromX501NameContent(&subtreeName_content
.content
);
1667 CFReleaseNull(subtree_orgs
);
1668 return errSecSuccess
;
1670 return errSecInternal
;
1673 static bool has_ct_excepted_key(SecCertificatePathVCRef path
, CFDictionaryRef exception
) {
1674 __block
bool result
= false;
1675 CFDataRef exceptionHash
= CFDictionaryGetValue(exception
, kSecCTExceptionsSPKIHashKey
);
1677 /* exception for a leaf is always allowed */
1678 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
1679 CFDataRef spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
1680 if (CFEqualSafe(exceptionHash
, spkiHash
)) {
1683 CFReleaseNull(spkiHash
);
1685 if (result
) { return result
; }
1687 /* exceptions for CAs */
1688 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
1689 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
1691 spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
1692 if (!CFEqualSafe(exceptionHash
, spkiHash
)) {
1693 CFReleaseNull(spkiHash
);
1696 CFReleaseNull(spkiHash
);
1698 /* this CA matches but exceptions for CAs have constraints */
1699 if (SecCertificateGetPermittedSubtrees(ca
)) {
1700 /* Constrained CAs have to have a Distinguished Name permitted subtree with an Organization attribute */
1701 CFArrayForEach(SecCertificateGetPermittedSubtrees(ca
), ^(const void *value
) {
1702 CFDataRef subtree
= (CFDataRef
)value
;
1703 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(subtree
), CFDataGetLength(subtree
) };
1704 DERDecodedInfo general_name_content
;
1705 if (DR_Success
== DERDecodeItem(&general_name
, &general_name_content
)) {
1706 OSStatus status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
1707 &general_name_content
.content
,
1709 is_subtree_dn_with_org
);
1710 if (status
== errSecSuccess
) {
1718 /* The Organization attribute(s) in the CA subject have to exactly match the Organization attribute(s) in the leaf */
1719 CFArrayRef leafOrgs
= SecCertificateCopyOrganization(leaf
);
1720 CFArrayRef caOrgs
= SecCertificateCopyOrganization(ca
);
1721 if (caOrgs
&& leafOrgs
&& CFEqualSafe(leafOrgs
, caOrgs
)) {
1724 CFReleaseNull(leafOrgs
);
1725 CFReleaseNull(caOrgs
);
1736 static bool is_ct_excepted(SecPVCRef pvc
) {
1737 CFDictionaryRef ct_exceptions
= _SecTrustStoreCopyCTExceptions(NULL
, NULL
);
1738 if (!ct_exceptions
) {
1742 __block
bool result
= false;
1743 CFArrayRef domainExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsDomainsKey
);
1744 if (domainExceptions
) {
1745 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1746 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
1748 CFArrayForEach(domainExceptions
, ^(const void *value
) {
1749 result
= result
|| is_ct_excepted_domain(hostname
, value
);
1754 secinfo("policy", "domain-based CT exception applied");
1755 CFReleaseNull(ct_exceptions
);
1759 CFArrayRef keyExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsCAsKey
);
1760 if (keyExceptions
) {
1761 __block SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1762 CFArrayForEach(keyExceptions
, ^(const void *value
) {
1763 result
= result
|| has_ct_excepted_key(path
, value
);
1768 secinfo("policy" , "key-based CT exceptions applied");
1771 CFReleaseNull(ct_exceptions
);
1775 /* <rdar://45466778> some Apple servers not getting certs with embedded SCTs */
1776 static bool is_ct_allowlisted_cert(SecCertificateRef leaf
) {
1777 if (CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlist"), CFSTR("com.apple.security"), NULL
) ||
1778 CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlistApple"), CFSTR("com.apple.security"), NULL
)) {
1782 /* subject:/CN=basejumper.apple.com/OU=management:idms.group.110621/O=Apple Inc./ST=California/C=US */
1783 /* issuer :/CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US */
1784 static const uint8_t basejumper_hash
[] = {
1785 0x23, 0x32, 0x0b, 0x5a, 0x24, 0xd8, 0x4d, 0x27, 0x8c, 0x43, 0xc9, 0xed, 0x22, 0xed, 0x87, 0xb7,
1786 0xc5, 0x51, 0x43, 0x55, 0xa9, 0x84, 0x79, 0x5a, 0x77, 0xb9, 0xad, 0x0f, 0x88, 0x14, 0x61, 0xac,
1789 bool result
= false;
1790 CFDataRef leaf_fingerprint
= SecCertificateCopySHA256Digest(leaf
);
1791 const uint8_t *dp
= CFDataGetBytePtr(leaf_fingerprint
);
1792 if (dp
&& !memcmp(basejumper_hash
, dp
, CC_SHA256_DIGEST_LENGTH
)) {
1795 CFReleaseNull(leaf_fingerprint
);
1799 static void SecPolicyCheckSystemTrustedCTRequired(SecPVCRef pvc
) {
1800 SecCertificateSourceRef appleAnchorSource
= NULL
;
1801 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
1802 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1803 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
1805 /* Skip this check if we haven't done the CT checks yet */
1806 require_quiet(SecCertificatePathVCIsPathValidated(path
), out
);
1808 /* We only enforce this check when all of the following are true:
1809 * 0. Kill Switch not enabled */
1810 require_quiet(!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
), out
);
1812 /* 1. Not a pinning policy */
1813 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1814 require_quiet(CFEqualSafe(SecPolicyGetName(policy
),kSecPolicyNameSSLServer
), out
);
1816 /* 2. Device has checked in to MobileAsset for a current log list within the last 60 days.
1817 * Or the caller passed in the trusted log list. */
1818 require_quiet(SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
, out
);
1820 /* 3. Leaf issuance date is on or after 16 Oct 2018 at 00:00:00 AM UTC and not expired. */
1821 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1822 require_quiet(SecCertificateNotValidBefore(leaf
) >= 561340800.0 &&
1823 SecCertificateIsValid(leaf
, SecPVCGetVerifyTime(pvc
)), out
);
1825 /* 4. Chain is anchored with root in the system anchor source but not the Apple anchor source
1826 * with certain excepted CAs and configurable included CAs. */
1827 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1828 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1829 appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(false));
1830 require_quiet(SecPathBuilderIsAnchored(pvc
->builder
), out
);
1831 require_quiet((SecCertificateSourceContains(kSecSystemAnchorSource
, root
) &&
1832 appleAnchorSource
&& !SecCertificateSourceContains(appleAnchorSource
, root
) &&
1833 !is_ct_allowlisted_cert(leaf
)) ||
1834 is_configured_test_system_root(root
, CFSTR("TestCTRequiredSystemRoot")), out
);
1836 if (!SecCertificatePathVCIsCT(path
) && !is_ct_excepted(pvc
)) {
1837 /* Set failure. By not using the Forced variant, we implicitly check that this
1838 * policy had this options set. */
1839 SecPVCSetResult(pvc
, kSecPolicyCheckSystemTrustedCTRequired
, 0, kCFBooleanFalse
);
1843 CFReleaseNull(trustedLogs
);
1844 CFReleaseNull(otaref
);
1845 if (appleAnchorSource
) {
1846 SecMemoryCertificateSourceDestroy(appleAnchorSource
);
1850 static bool SecPolicyCheckSystemTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
1851 CFAbsoluteTime jul2016
= 489024000.0; // 1 July 2016 00:00:00 UTC
1852 CFAbsoluteTime mar2018
= 541555200.0; // 1 March 2018 00:00:00 UTC
1853 CFAbsoluteTime sep2020
= 620611200.0; // 1 September 2020 00:00:00 UTC
1854 if (notBefore
< jul2016
) {
1855 /* Validity Period no greater than 60 months.
1856 60 months is no more than 5 years and 2 leap days (and 1 hour slip). */
1857 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2) + 3600;
1858 if (notAfter
- notBefore
> maxPeriod
) {
1859 secnotice("policy", "System-trusted leaf validity period is more than 60 months");
1862 } else if (notBefore
< mar2018
) {
1863 /* Validity Period no greater than 39 months.
1864 39 months is no more than 3 years, 2 31-day months,
1865 1 30-day month, and 1 leap day (and 1 hour slip) */
1866 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1) + 3600;
1867 if (notAfter
- notBefore
> maxPeriod
) {
1868 secnotice("policy", "System-trusted leaf validity period longer than 39 months and issued after 30 June 2016");
1871 } else if (notBefore
< sep2020
) {
1872 /* Validity Period no greater than 825 days (and 1 hour slip). */
1873 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
1874 if (notAfter
- notBefore
> maxPeriod
) {
1875 secnotice("policy", "System-trusted leaf validity period longer than 825 days and issued on or after 1 March 2018");
1879 /* Validity Period no greater than 398 days (and no slip). HT211025 */
1880 CFAbsoluteTime maxPeriod
= 60*60*24*398;
1881 if (notAfter
- notBefore
> maxPeriod
) {
1882 secnotice("policy", "System-trusted leaf validity period longer than 398 days and issued on or after 1 September 2020");
1889 static bool SecPolicyCheckOtherTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
1890 /* Check whether we will enforce the validity period maximum for a non-system trusted leaf. */
1891 if (SecIsInternalRelease()) {
1892 if (CFPreferencesGetAppBooleanValue(CFSTR("IgnoreMaximumValidityPeriod"),
1893 CFSTR("com.apple.security"), NULL
)) {
1897 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
1898 if (notBefore
> jul2019
) {
1899 /* Validity Period no greater than 825 days (and 1 hour slip). */
1900 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
1901 if (notAfter
- notBefore
> maxPeriod
) {
1902 secnotice("policy", "Non-system-trusted leaf validity period longer than 825 days and issued on or after 1 July 2019");
1909 static void SecPolicyCheckValidityPeriodMaximums(SecPVCRef pvc
, CFStringRef key
) {
1910 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1911 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(leaf
);
1912 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
1914 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1915 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1916 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
1917 if (!SecPolicyCheckSystemTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
1918 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1923 /* Don't check validity periods against maximums for user-anchored leafs */
1924 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, leaf
)) {
1928 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, leaf
)) {
1933 /* all other trust */
1934 if (!SecPolicyCheckOtherTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
1935 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1939 static void SecPolicyCheckServerAuthEKU(SecPVCRef pvc
, CFStringRef key
) {
1940 /* The ExtendedKeyUsage check will verify a looser version of this check for all TLS server certs.
1941 * Here we want to be stricter (enforcing that there is an EKU extension and that it contains the
1942 * one true Server Auth EKU OID) for system and app anchors */
1943 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1944 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1945 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
1946 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
1947 /* all system-anchored chains must be compliant */
1948 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
1949 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1951 /* if any subCA cert has an EKU, it must have the server auth EKU */
1952 if (count
> 2) { // chain has subCAs
1953 for (int ix
= 1; ix
< count
- 1; ix
++) { // iterate through subCAs
1954 SecCertificateRef subCA
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1955 CFArrayRef eku
= NULL
;
1956 if ((eku
= SecCertificateCopyExtendedKeyUsage(subCA
)) && CFArrayGetCount(eku
)) { // subCA has EKU set
1957 if (!SecPolicyCheckCertExtendedKeyUsage(subCA
, CFSTR("1.3.6.1.5.5.7.3.1")) && // check server auth EKU
1958 !SecPolicyCheckCertExtendedKeyUsage(subCA
, CFSTR("2.5.29.37.0"))) { // check anyEKU
1959 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1968 /* skip user/admin-anchored chains */
1969 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, root
)) {
1973 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, root
)) {
1979 /* All other anchor types must be compliant if issued on or after 1 July 2019 */
1980 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
1981 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
1982 if (notBefore
> jul2019
) {
1983 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
1984 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1989 static void SecPolicyCheckCTRequired(SecPVCRef pvc
, CFStringRef key
) {
1990 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1991 SecCertificatePathVCSetRequiresCT(path
, kSecPathCTRequiredOverridable
);
1994 static void SecPolicyCheckNotCA(SecPVCRef pvc
, CFStringRef key
) {
1995 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1996 if (SecCertificateIsCA(leaf
)) {
1997 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2001 static void SecPolicyCheckNonTlsCTRequired(SecPVCRef pvc
, CFStringRef key
) {
2002 // Skip if kill switch enabled or log list not updated
2003 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
2004 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2005 if (!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchNonTLSCT
) &&
2006 (SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
)) {
2007 // Check CT against the non-TLS log list
2008 if (!SecPolicyCheckNonTlsCT(pvc
)) {
2009 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2012 CFReleaseNull(otaref
);
2013 CFReleaseNull(trustedLogs
);
2016 void SecPolicyServerInitialize(void) {
2017 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2018 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2019 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2020 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2022 #undef POLICYCHECKMACRO
2023 #define __PC_ADD_CHECK_(NAME)
2024 #define __PC_ADD_CHECK_L(NAME) CFDictionaryAddValue(gSecPolicyLeafCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2025 #define __PC_ADD_CHECK_A(NAME) CFDictionaryAddValue(gSecPolicyPathCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2027 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2028 __PC_ADD_CHECK_##LEAFCHECK(NAME) \
2029 __PC_ADD_CHECK_##PATHCHECK(NAME)
2030 #include "OSX/sec/Security/SecPolicyChecks.list"
2032 /* Some of these don't follow the naming conventions but are in the Pinning DB.
2033 * <rdar://34537018> fix policy check constant values */
2034 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkerOid"), SecPolicyCheckLeafMarkerOid
);
2035 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkersProdAndQA"), SecPolicyCheckLeafMarkersProdAndQA
);
2036 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateMarkerOid"), SecPolicyCheckIntermediateMarkerOid
);
2037 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateCountry"), SecPolicyCheckIntermediateCountry
);
2038 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateOrganization"), SecPolicyCheckIntermediateOrganization
);
2043 /********************************************************
2044 ****************** SecPVCRef Functions *****************
2045 ********************************************************/
2047 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
) {
2048 secdebug("alloc", "pvc %p", pvc
);
2049 // Weird logging policies crashes.
2050 //secdebug("policy", "%@", policies);
2052 // Zero the pvc struct so only non-zero fields need to be explicitly set
2053 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
2054 pvc
->builder
= builder
;
2055 pvc
->policies
= policies
;
2058 pvc
->result
= kSecTrustResultUnspecified
;
2060 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2061 &kCFTypeDictionaryKeyCallBacks
,
2062 &kCFTypeDictionaryValueCallBacks
);
2063 pvc
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
,
2064 1, &kCFTypeArrayCallBacks
);
2065 CFRelease(certDetail
);
2068 void SecPVCDelete(SecPVCRef pvc
) {
2069 secdebug("alloc", "delete pvc %p", pvc
);
2070 CFReleaseNull(pvc
->policies
);
2071 CFReleaseNull(pvc
->details
);
2072 CFReleaseNull(pvc
->leafDetails
);
2075 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2076 secdebug("policy", "%@", path
);
2078 pvc
->result
= kSecTrustResultUnspecified
;
2079 CFReleaseNull(pvc
->details
);
2082 void SecPVCComputeDetails(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2085 /* Since we don't run the LeafChecks again, we need to preserve the
2086 * result the leaf had. */
2087 CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(path
);
2088 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
2089 pathLength
, pvc
->leafDetails
);
2090 for (ix
= 1; ix
< pathLength
; ++ix
) {
2091 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2092 &kCFTypeDictionaryKeyCallBacks
,
2093 &kCFTypeDictionaryValueCallBacks
);
2094 CFArrayAppendValue(details
, certDetail
);
2095 CFRelease(certDetail
);
2097 CFRetainAssign(pvc
->details
, details
);
2098 pvc
->result
= pvc
->leafResult
;
2099 CFReleaseSafe(details
);
2102 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
2103 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
2106 CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
2107 return SecPathBuilderGetCertificateCount(pvc
->builder
);
2110 SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
2111 return SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2114 CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
2115 return SecPathBuilderGetVerifyTime(pvc
->builder
);
2118 static bool SecPVCIsExceptedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
, CFTypeRef value
) {
2119 CFArrayRef exceptions
= SecPathBuilderGetExceptions(pvc
->builder
);
2120 if (!exceptions
) { return false; }
2121 CFIndex exceptionsCount
= CFArrayGetCount(exceptions
);
2123 /* There are two types of exceptions:
2124 * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
2125 * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
2126 * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
2129 CFDictionaryRef options
= CFArrayGetValueAtIndex(exceptions
, 0);
2130 if (!isDictionary(options
)) {
2134 if (exceptionsCount
== 1 && (ix
> 0 || !CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
))) {
2135 /* SHA1Digest not allowed */
2136 if (CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
)) { return false; }
2138 if (CFDictionaryContainsKey(options
, key
)) {
2139 /* Special case -- AnchorTrusted only for self-signed certs */
2140 if (CFEqual(kSecPolicyCheckAnchorTrusted
, key
)) {
2141 Boolean isSelfSigned
= false;
2142 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2143 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2148 } else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
) && CFDictionaryContainsKey(options
, kSecPolicyCheckValidRoot
)) {
2149 /* Another special case - ValidRoot excepts Valid only for self-signed certs */
2150 Boolean isSelfSigned
= false;
2151 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2152 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2161 if (ix
>= exceptionsCount
) { return false; }
2162 CFDictionaryRef exception
= CFArrayGetValueAtIndex(exceptions
, ix
);
2163 if (!isDictionary(exception
)) {
2167 /* Compare the cert hash */
2168 if (!CFDictionaryContainsKey(exception
, kSecCertificateDetailSHA1Digest
)) { return false; }
2169 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2170 if (!CFEqual(SecCertificateGetSHA1Digest(cert
), CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
))) {
2175 CFTypeRef exceptionValue
= CFDictionaryGetValue(exception
, key
);
2176 if (exceptionValue
&& CFEqual(value
, exceptionValue
)) {
2177 /* Only change result if PVC is already ok */
2178 if (SecPVCIsOkResult(pvc
)) {
2179 // Chains that pass due to exceptions get Proceed result.
2180 pvc
->result
= kSecTrustResultProceed
;
2188 static int32_t detailKeyToCssmErr(CFStringRef key
) {
2191 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
2192 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
2194 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
2195 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
2197 else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
2198 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
2204 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
2206 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
2207 bool result
= false;
2208 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2209 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, ix
);
2210 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2211 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2213 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2214 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2215 CFNumberRef allowedErrorNumber
= NULL
;
2216 if (!isDictionary(constraint
)) {
2219 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
2220 int32_t allowedErrorValue
= 0;
2221 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
2225 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
2226 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
2235 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
2236 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2237 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2238 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2239 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2240 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2241 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2242 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2243 if (!isDictionary(constraint
)) {
2247 CFDictionaryRef policyOptions
= NULL
;
2248 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2249 if (policyOptions
&& isDictionary(policyOptions
) &&
2250 CFDictionaryContainsKey(policyOptions
, key
)) {
2258 static SecTrustResultType
trust_result_for_key(CFStringRef key
) {
2259 SecTrustResultType result
= kSecTrustResultRecoverableTrustFailure
;
2260 #undef POLICYCHECKMACRO
2261 #define __PC_TYPE_MEMBER_ false
2262 #define __PC_TYPE_MEMBER_R false
2263 #define __PC_TYPE_MEMBER_F true
2264 #define __PC_TYPE_MEMBER_D true
2266 #define __TRUSTRESULT_ kSecTrustResultRecoverableTrustFailure
2267 #define __TRUSTRESULT_F kSecTrustResultFatalTrustFailure
2268 #define __TRUSTRESULT_D kSecTrustResultDeny
2269 #define __TRUSTRESULT_R kSecTrustResultRecoverableTrustFailure
2271 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2272 if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
2273 result = __TRUSTRESULT_##TRUSTRESULT; \
2275 #include "OSX/sec/Security/SecPolicyChecks.list"
2280 /* AUDIT[securityd](done):
2281 policy->_options is a caller provided dictionary, only its cf type has
2284 bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc
, CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
,
2285 SecTrustResultType overrideDefaultTR
) {
2287 /* If this is not something the current policy cares about ignore
2288 this error and return true so our caller continues evaluation. */
2290 /* Either the policy or the usage constraints have to have this key */
2291 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2292 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
2293 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
2298 /* Get the default trust result for this key and override it if the caller needs to
2299 * set a different trust result than the default. */
2300 SecTrustResultType trustResult
= trust_result_for_key(key
);
2301 if (overrideDefaultTR
!= kSecTrustResultInvalid
) {
2302 trustResult
= overrideDefaultTR
;
2305 /* only recoverable errors can be allowed/excepted */
2306 if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
2307 /* Check to see if the SecTrustSettings for the certificate in question
2308 tell us to ignore this error. */
2309 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
2310 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
2314 /* Check to see if exceptions tells us to ignore this error. */
2315 if (SecPVCIsExceptedError(pvc
, ix
, key
, result
)) {
2316 secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix
, key
);
2321 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
2322 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
2323 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
2325 (force
? "force" : ""), result
);
2327 /* Avoid resetting deny or fatal to recoverable */
2328 if (SecPVCIsOkResult(pvc
) || trustResult
== kSecTrustResultFatalTrustFailure
) {
2329 pvc
->result
= trustResult
;
2330 } else if (trustResult
== kSecTrustResultDeny
&&
2331 pvc
->result
== kSecTrustResultRecoverableTrustFailure
) {
2332 pvc
->result
= trustResult
;
2338 CFMutableDictionaryRef detail
=
2339 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
2341 secerror("SecPVCSetResultForced: failed to get detail at index %ld (array length %ld)",
2342 ix
, CFArrayGetCount(pvc
->details
));
2346 /* Perhaps detail should have an array of results per key? As it stands
2347 in the case of multiple policy failures the last failure stands. */
2348 CFDictionarySetValue(detail
, key
, result
);
2353 bool SecPVCSetResultForced(SecPVCRef pvc
, CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
2354 return SecPVCSetResultForcedWithTrustResult(pvc
, key
, ix
, result
, force
, kSecTrustResultInvalid
);
2357 bool SecPVCSetResult(SecPVCRef pvc
,
2358 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
2359 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
2362 /* AUDIT[securityd](done):
2363 key(ok) is a caller provided.
2364 value(ok, unused) is a caller provided.
2366 static void SecPVCValidateKey(const void *key
, const void *value
,
2368 SecPVCRef pvc
= (SecPVCRef
)context
;
2370 /* If our caller doesn't want full details and we failed earlier there is
2371 no point in doing additional checks. */
2372 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
2375 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
2376 CFDictionaryGetValue(pvc
->callbacks
, key
);
2379 /* "Optional" policy checks. This may be a new key from the
2380 * pinning DB which is not implemented in this OS version. Log a
2381 * warning, and on debug builds fail evaluation, to encourage us
2382 * to ensure that checks are synchronized across the same build. */
2383 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
2384 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
2385 secwarning("policy: unknown policy key %@, skipping", key
);
2387 pvc
->result
= kSecTrustResultOtherError
;
2390 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
2391 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
2392 secwarning("policy: unknown policy key %@, skipping", key
);
2394 pvc
->result
= kSecTrustResultOtherError
;
2398 /* Non standard validation phase, nothing is optional. */
2399 pvc
->result
= kSecTrustResultOtherError
;
2404 fcn(pvc
, (CFStringRef
)key
);
2407 /* AUDIT[securityd](done):
2408 policy->_options is a caller provided dictionary, only its cf type has
2411 SecTrustResultType
SecPVCLeafChecks(SecPVCRef pvc
) {
2412 /* We need to compute details for the leaf. */
2413 CFRetainAssign(pvc
->details
, pvc
->leafDetails
);
2415 CFArrayRef policies
= pvc
->policies
;
2416 CFIndex ix
, count
= CFArrayGetCount(policies
);
2417 for (ix
= 0; ix
< count
; ++ix
) {
2418 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2420 /* Validate all keys for all policies. */
2421 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2422 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
2425 pvc
->leafResult
= pvc
->result
;
2426 CFRetainAssign(pvc
->leafDetails
, pvc
->details
);
2431 bool SecPVCIsOkResult(SecPVCRef pvc
) {
2432 if (pvc
->result
== kSecTrustResultRecoverableTrustFailure
||
2433 pvc
->result
== kSecTrustResultDeny
||
2434 pvc
->result
== kSecTrustResultFatalTrustFailure
||
2435 pvc
->result
== kSecTrustResultOtherError
) {
2441 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
2442 /* Check stuff common to intermediate and anchors. */
2443 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
2444 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2445 CFIndex anchor_ix
= SecPVCGetCertificateCount(pvc
) - 1;
2447 if (!SecCertificateIsValid(cert
, verifyTime
)) {
2448 /* Certificate has expired. */
2449 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, ix
, kCFBooleanFalse
)) {
2454 if (SecCertificateIsWeakKey(cert
)) {
2455 /* Certificate uses weak key. */
2456 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, ix
, kCFBooleanFalse
)) {
2461 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
2462 /* Certificate uses weak hash. */
2463 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, ix
, kCFBooleanFalse
)) {
2468 /* (k) Basic constraints only relevant for v3 and later. */
2469 if (SecCertificateVersion(cert
) >= 3) {
2470 const SecCEBasicConstraints
*bc
=
2471 SecCertificateGetBasicConstraints(cert
);
2473 /* Basic constraints not present, illegal. */
2474 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2475 ix
, kCFBooleanFalse
, true)) {
2478 } else if (!bc
->isCA
) {
2479 /* Basic constraints not marked as isCA, illegal. */
2480 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsCA
,
2481 ix
, kCFBooleanFalse
, true)) {
2486 /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
2487 not an anchor), we additionally require that the certificate chain
2488 does not end in a v3 or later anchor. [rdar://32204517] */
2489 else if (ix
> 0 && ix
< anchor_ix
) {
2490 SecCertificateRef anchor
= SecPVCGetCertificateAtIndex(pvc
, anchor_ix
);
2491 if (SecCertificateVersion(anchor
) >= 3) {
2492 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2493 ix
, kCFBooleanFalse
, true)) {
2498 /* (l) max_path_length is checked elsewhere. */
2500 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
2501 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
2502 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
2503 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
2504 ix
, kCFBooleanFalse
, true)) {
2510 return SecPVCIsOkResult(pvc
);
2513 static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
2514 /* Check stuff common to intermediate and anchors. */
2516 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2517 if (NULL
!= otapkiRef
)
2519 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
2520 CFRelease(otapkiRef
);
2521 if (NULL
!= blackListedKeys
)
2523 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2524 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2525 bool is_last
= (ix
== count
- 1);
2526 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2528 /* Check for blacklisted intermediate issuer keys. */
2529 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2531 /* Check dgst against blacklist. */
2532 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
2533 /* Check allow list for this blacklisted issuer key,
2534 which is the authority key of the issued cert at ix-1.
2536 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2537 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2539 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
2540 ix
, kCFBooleanFalse
, true);
2546 CFRelease(blackListedKeys
);
2547 return SecPVCIsOkResult(pvc
);
2554 static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
2556 /* Check stuff common to intermediate and anchors. */
2557 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2558 if (NULL
!= otapkiRef
)
2560 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
2561 CFRelease(otapkiRef
);
2562 if (NULL
!= grayListKeys
)
2564 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2565 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2566 bool is_last
= (ix
== count
- 1);
2567 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2569 /* Check for gray listed intermediate issuer keys. */
2570 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2572 /* Check dgst against gray list. */
2573 if (CFSetContainsValue(grayListKeys
, dgst
)) {
2574 /* Check allow list for this graylisted issuer key,
2575 which is the authority key of the issued cert at ix-1.
2577 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2578 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2580 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
2581 ix
, kCFBooleanFalse
, true);
2587 CFRelease(grayListKeys
);
2588 return SecPVCIsOkResult(pvc
);
2595 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
2596 if (!isString(searchName
) && !isString(searchOid
)) {
2599 CFArrayRef policies
= pvc
->policies
;
2600 CFIndex ix
, count
= CFArrayGetCount(policies
);
2601 for (ix
= 0; ix
< count
; ++ix
) {
2602 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2603 CFStringRef policyName
= SecPolicyGetName(policy
);
2604 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
2605 /* Prefer a match of both name and OID */
2606 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
2607 if (CFEqual(searchOid
, policyOid
) &&
2608 CFEqual(searchName
, policyName
)) {
2609 if (policyIX
) { *policyIX
= ix
; }
2612 /* <rdar://40617139> sslServer trust settings need to apply to evals using policyName pinning
2613 * but make sure we don't use this for SSL Client trust settings or policies. */
2614 if (CFEqual(searchOid
, policyOid
) &&
2615 CFEqual(searchName
, kSecPolicyNameSSLServer
) && !CFEqual(policyName
, kSecPolicyNameSSLClient
)) {
2616 if (policyIX
) { *policyIX
= ix
; }
2620 /* Next best is just OID. */
2621 if (!searchName
&& searchOid
&& policyOid
) {
2622 if (CFEqual(searchOid
, policyOid
)) {
2623 if (policyIX
) { *policyIX
= ix
; }
2627 if (!searchOid
&& searchName
&& policyName
) {
2628 if (CFEqual(searchName
, policyName
)) {
2629 if (policyIX
) { *policyIX
= ix
; }
2637 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
2638 if (!isString(stringValue
)) {
2641 bool result
= false;
2643 /* <rdar://27754596> Previous versions of macOS null-terminated the string, so we need to strip it. */
2644 CFStringRef tmpStringValue
= NULL
;
2645 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
2646 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
2648 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
2650 /* Some users have strings that only contain the null-termination, so we need to check that we
2651 * still have a string. */
2652 require(tmpStringValue
, out
);
2654 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
2655 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
2656 /* Have to look for all the possible locations of name string */
2657 CFStringRef policyString
= NULL
;
2658 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2659 if (!policyString
) {
2660 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
2662 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
2667 CFArrayRef policyStrings
= NULL
;
2668 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
2669 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
2670 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
2678 CFReleaseNull(tmpStringValue
);
2683 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
2684 uint32_t ourTSKeyUsage
= 0;
2685 uint32_t keyUsage
= 0;
2686 if (keyUsageNumber
&&
2687 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
2688 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
2689 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
2691 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
2692 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
2694 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
2695 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
2697 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
2698 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
2700 if (keyUsage
== kSecKeyUsageAll
) {
2701 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
2704 return ourTSKeyUsage
;
2707 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
2708 uint32_t ourTSKeyUsage
= 0;
2709 CFTypeRef policyKeyUsageType
= NULL
;
2711 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
2712 if (isArray(policyKeyUsageType
)) {
2713 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
2714 for (ix
= 0; ix
< count
; ix
++) {
2715 CFNumberRef policyKeyUsageNumber
= NULL
;
2716 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
2717 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
2719 } else if (isNumber(policyKeyUsageType
)) {
2720 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
2723 return ourTSKeyUsage
;
2726 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
2727 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
2728 int64_t keyUsageValue
= 0;
2729 uint32_t ourKeyUsage
= 0;
2731 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
2735 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
2739 /* We're using the key for revocation if we have the OCSPSigner policy.
2740 * @@@ If we support CRLs, we'd need to check for that policy here too.
2742 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
2743 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
2746 /* We're using the key for verifying a cert if it's a root/intermediate
2747 * in the chain. If the cert isn't in the path yet, we're about to add it,
2748 * so it's a root/intermediate. If there is no path, this is the leaf.
2750 CFIndex pathIndex
= -1;
2751 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2753 pathIndex
= SecCertificatePathVCGetIndexOfCertificate(path
, certificate
);
2757 if (pathIndex
!= 0) {
2758 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
2761 /* The rest of the key usages may be specified by the policy(ies). */
2762 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
2763 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
2764 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
2766 /* Get key usage from ALL policies */
2767 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
2768 for (ix
= 0; ix
< count
; ix
++) {
2769 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
2770 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
2774 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
2782 #include <Security/SecTrustedApplicationPriv.h>
2783 #include <Security/SecTask.h>
2784 #include <Security/SecTaskPriv.h>
2785 #include <bsm/libbsm.h>
2786 #include <libproc.h>
2788 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
2790 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
2791 bool result
= false;
2792 audit_token_t auditToken
= {};
2793 SecTaskRef task
= NULL
;
2794 SecRequirementRef requirement
= NULL
;
2795 CFStringRef stringRequirement
= NULL
;
2797 require_quiet(appRef
&& clientAuditToken
, out
);
2798 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
2799 require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef
)appRef
, &requirement
), out
);
2800 require(requirement
, out
);
2801 require_noerr(SecRequirementsCopyString(requirement
, kSecCSDefaultFlags
, &stringRequirement
), out
);
2802 require(stringRequirement
, out
);
2804 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
2805 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
2806 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
2808 if(errSecSuccess
== SecTaskValidateForRequirement(task
, stringRequirement
)) {
2813 CFReleaseNull(task
);
2814 CFReleaseNull(requirement
);
2815 CFReleaseNull(stringRequirement
);
2820 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
2821 if (!isDictionary(options
)) {
2826 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
2828 /* We need to run the leaf and path checks using these options. */
2829 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2830 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
2832 pvc
->callbacks
= gSecPolicyPathCallbacks
;
2833 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
2836 pvc
->callbacks
= currentCallbacks
;
2838 /* Our work here is done; no need to claim a match */
2842 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
2843 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
2844 CFNumberRef keyUsageNumber
= NULL
;
2845 CFTypeRef trustedApplicationData
= NULL
;
2846 CFDictionaryRef policyOptions
= NULL
;
2848 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
2849 keyUsageMatch
= false, policyOptionMatch
= false;
2850 bool result
= false;
2853 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
2854 SecPolicyRef policy
= NULL
;
2855 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
2856 policyOid
= (policy
) ? policy
->_oid
: NULL
;
2858 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
2860 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
2861 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
2862 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
2863 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2865 CFIndex policyIX
= -1;
2866 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
2867 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
2868 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
2869 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
2872 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
2873 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2874 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
2875 CFReleaseNull(clientAuditToken
);
2877 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
2878 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
2882 /* If we either didn't find the parameter in the dictionary or we got a match
2883 * against that parameter, for all possible parameters in the dictionary, then
2884 * this trust setting result applies to the output. */
2885 if (((!policyOid
&& !policyName
) || policyMatch
) &&
2886 (!policyString
|| policyStringMatch
) &&
2887 (!trustedApplicationData
|| applicationMatch
) &&
2888 (!keyUsageNumber
|| keyUsageMatch
) &&
2889 (!policyOptions
|| policyOptionMatch
)) {
2896 static SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
2897 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
2898 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2899 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2900 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2901 if (!isDictionary(constraint
)) {
2905 CFNumberRef resultNumber
= NULL
;
2906 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
2907 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
2908 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
2909 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
2910 resultValue
= kSecTrustSettingsResultTrustRoot
;
2913 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
2914 result
= resultValue
;
2921 /* This function assumes that the input source is an anchor source */
2922 bool SecPVCIsAnchorPerConstraints(SecPVCRef pvc
, SecCertificateSourceRef source
, SecCertificateRef certificate
) {
2923 __block
bool result
= false;
2924 CFArrayRef constraints
= NULL
;
2925 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
2927 /* Unrestricted certificates:
2928 * -those that come from anchor sources with no constraints
2929 * -self-signed certificates with empty contraints arrays
2931 Boolean selfSigned
= false;
2932 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
2933 if ((NULL
== source
->copyUsageConstraints
) ||
2934 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
2935 secinfo("trust", "unrestricted anchor%s",
2936 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
2941 /* Get the trust settings result for the PVC. Only one PVC need match to
2942 * trigger the anchor behavior -- policy validation will handle whether the
2943 * path is truly anchored for that PVC. */
2944 require_quiet(constraints
, out
);
2945 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
2946 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
2949 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
2950 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
2951 // For our purposes, this is an anchor.
2952 secinfo("trust", "complex trust settings anchor");
2956 if (settingsResult
== kSecTrustSettingsResultDeny
) {
2957 /* We consider denied certs "anchors" because the trust decision
2958 is set regardless of building the chain further. The policy
2959 validation will handle rejecting this chain. */
2960 secinfo("trust", "complex trust settings denied anchor");
2965 CFReleaseNull(constraints
);
2969 static void SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
2970 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2971 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2972 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2973 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2974 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2975 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
2977 /* Set the pvc trust result based on the usage constraints and anchor source. */
2978 if (result
== kSecTrustSettingsResultDeny
) {
2979 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
2980 } else if ((result
== kSecTrustSettingsResultTrustRoot
|| result
== kSecTrustSettingsResultTrustAsRoot
||
2981 result
== kSecTrustSettingsResultInvalid
) && SecPVCIsOkResult(pvc
)) {
2982 /* If we already think the PVC is ok and this cert is from one of the user/
2983 * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
2984 * all mean we should use the special "Proceed" trust result. */
2985 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecUserAnchorSource
) &&
2986 SecCertificateSourceContains(kSecUserAnchorSource
, cert
)) {
2987 pvc
->result
= kSecTrustResultProceed
;
2990 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecLegacyAnchorSource
) &&
2991 SecCertificateSourceContains(kSecLegacyAnchorSource
, cert
)) {
2992 pvc
->result
= kSecTrustResultProceed
;
2999 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
3000 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
3001 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
3003 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
3004 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
3005 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
3007 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
3008 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
3009 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
3011 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
3012 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
3013 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
3015 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
3016 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
3017 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
3019 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
3020 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
3021 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
3023 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
3024 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
3025 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
3027 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
3028 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
3029 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
3032 static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
3033 static CFSetRef sConstrainedRoots
= NULL
;
3034 static dispatch_once_t _t
;
3035 dispatch_once(&_t
, ^{
3036 const UInt8
*v_hashes
[] = {
3037 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
3038 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
3040 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3041 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
3042 for (ix
=0; ix
<count
; ix
++) {
3043 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
3044 kSecPolicySHA256Size
, kCFAllocatorNull
);
3046 CFSetAddValue(set
, hash
);
3050 sConstrainedRoots
= set
;
3053 bool shouldDeny
= false;
3054 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3055 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
3056 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3057 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
3058 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
3059 /* matched a constrained root; check notBefore dates on all its children. */
3060 CFIndex childIX
= certIX
;
3061 while (--childIX
>= 0) {
3062 SecCertificateRef child
= SecPVCGetCertificateAtIndex(pvc
, childIX
);
3063 /* 1 Dec 2016 00:00:00 GMT */
3064 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
3065 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
3071 CFReleaseNull(sha256
);
3075 static bool SecPVCPolicyPermitsCTRequired(SecPVCRef pvc
) {
3076 #if TARGET_OS_BRIDGE
3078 #else // !TARGET_OS_BRIDGE
3079 if (!pvc
|| !pvc
->policies
) {
3082 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
3087 CFStringRef policyName
= SecPolicyGetName(policy
);
3088 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
3091 // SSL policy by another name
3092 CFDictionaryRef options
= policy
->_options
;
3093 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckSSLHostname
)) {
3096 // Policy explicitly requires CT
3097 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckCTRequired
)) {
3101 #endif // !TARGET_OS_BRIDGE
3105 1. SecPVCCheckRequireCTConstraints must be called after SecPolicyCheckCT,
3106 so earliest issuance time has already been obtained from SCTs.
3107 2. If the issuance time value is 0 (i.e. 2001-01-01) or earlier, we
3108 assume it was not set, and thus we did not have CT info.
3110 static void SecPVCCheckRequireCTConstraints(SecPVCRef pvc
) {
3111 SecCertificatePathVCRef path
= (pvc
) ? SecPathBuilderGetPath(pvc
->builder
) : NULL
;
3115 /* If we are evaluating for a SSL server authentication policy, make sure
3116 SCT issuance time is prior to the earliest not-after date constraint.
3117 Note that CT will already be required if there is a not-after date
3118 constraint present (set in SecRVCProcessValidDateConstraints).
3120 if (SecPVCPolicyPermitsCTRequired(pvc
)) {
3121 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3122 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, 0);
3123 CFAbsoluteTime earliestNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
3124 CFAbsoluteTime issuanceTime
= SecCertificatePathVCIssuanceTime(path
);
3125 if (issuanceTime
<= 0) {
3126 /* if not set (or prior to 2001-01-01), use leaf's not-before time. */
3127 issuanceTime
= SecCertificateNotValidBefore(certificate
);
3129 for (ix
= 0; ix
< certCount
; ix
++) {
3130 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3131 if (!rvc
|| !rvc
->valid_info
|| !rvc
->valid_info
->hasDateConstraints
|| !rvc
->valid_info
->notAfterDate
) {
3134 /* Found CA certificate with a not-after date constraint. */
3135 CFAbsoluteTime caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
3136 if (caNotAfter
< earliestNotAfter
) {
3137 earliestNotAfter
= caNotAfter
;
3139 if (issuanceTime
> earliestNotAfter
) {
3140 /* Issuance time violates the not-after date constraint. */
3141 secnotice("rvc", "certificate issuance time (%f) is later than allowed value (%f)",
3142 issuanceTime
, earliestNotAfter
);
3143 SecRVCSetValidDeterminedErrorResult(rvc
);
3148 /* If path is CT validated, nothing further to do here. */
3149 if (SecCertificatePathVCIsCT(path
)) {
3153 /* Path is not CT validated, so check if CT was required. */
3154 SecPathCTPolicy ctp
= SecCertificatePathVCRequiresCT(path
);
3155 if (ctp
<= kSecPathCTNotRequired
|| !SecPVCPolicyPermitsCTRequired(pvc
)) {
3159 /* We need to have a recent log list or the CT check may have failed due to the list being out of date.
3160 * Also, honor the CT kill switch. */
3161 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3162 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
3163 if (!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) &&
3164 (SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
)) {
3165 /* CT was required. Error is always set on leaf certificate. */
3166 if (ctp
!= kSecPathCTRequiredOverridable
) {
3167 /* Normally kSecPolicyCheckCTRequired is recoverable */
3168 SecPVCSetResultForcedWithTrustResult(pvc
, kSecPolicyCheckCTRequired
, 0, kCFBooleanFalse
, true,
3169 kSecTrustResultFatalTrustFailure
);
3171 SecPVCSetResultForced(pvc
, kSecPolicyCheckCTRequired
, 0, kCFBooleanFalse
, true);
3174 CFReleaseNull(otaref
);
3175 CFReleaseNull(trustedLogs
);
3178 /* "Deep" copy the details array */
3179 static CFArrayRef CF_RETURNS_RETAINED
SecPVCCopyDetailsArray(SecPVCRef pvc
) {
3180 CFArrayRef details
= pvc
->details
;
3181 CFMutableArrayRef copiedDetails
= CFArrayCreateMutable(NULL
, CFArrayGetCount(details
), &kCFTypeArrayCallBacks
);
3182 CFArrayForEach(details
, ^(const void *value
) {
3183 CFMutableDictionaryRef copiedValue
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
3184 CFArrayAppendValue(copiedDetails
, copiedValue
);
3185 CFReleaseNull(copiedValue
);
3187 return copiedDetails
;
3190 /* AUDIT[securityd](done):
3191 policy->_options is a caller provided dictionary, only its cf type has
3194 void SecPVCPathChecks(SecPVCRef pvc
) {
3195 secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc
->builder
));
3196 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3197 /* This needs to be initialized before we call any function that might call
3198 SecPVCSetResultForced(). */
3200 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3201 if (SecPVCIsOkResult(pvc
) || pvc
->details
) {
3202 SecPolicyCheckBasicCertificateProcessing(pvc
,
3203 kSecPolicyCheckBasicCertificateProcessing
);
3206 CFArrayRef policies
= pvc
->policies
;
3207 CFIndex count
= CFArrayGetCount(policies
);
3208 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3209 /* Validate all keys for all policies. */
3210 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3211 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3212 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3213 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
3220 /* Check whether the TrustSettings say to deny a cert in the path. */
3221 SecPVCCheckUsageConstraints(pvc
);
3223 /* Check for Blocklisted certs */
3224 SecPVCCheckIssuerDateConstraints(pvc
);
3226 count
= SecCertificatePathVCGetCount(path
);
3227 for (ix
= 1; ix
< count
; ix
++) {
3228 SecPVCGrayListedKeyChecks(pvc
, ix
);
3229 SecPVCBlackListedKeyChecks(pvc
, ix
);
3232 /* Path-based check tests. */
3233 if (!SecCertificatePathVCIsPathValidated(path
)) {
3234 bool ev_check_ok
= false;
3235 if (SecCertificatePathVCIsOptionallyEV(path
)) {
3236 SecTrustResultType pre_ev_check_result
= pvc
->result
;
3237 CFArrayRef pre_ev_check_details
= pvc
->details
? SecPVCCopyDetailsArray(pvc
) : NULL
;
3238 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3239 ev_check_ok
= SecPVCIsOkResult(pvc
);
3240 /* If ev checking failed, we still want to accept this chain
3241 as a non EV one, if it was valid as such. */
3242 pvc
->result
= pre_ev_check_result
;
3243 CFAssignRetained(pvc
->details
, pre_ev_check_details
);
3247 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3248 SecPolicyCheckCT(pvc
);
3250 /* Certs are only EV if they are also CT verified (when the Kill Switch isn't enabled and against a recent log list) */
3251 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3252 if (ev_check_ok
&& (SecCertificatePathVCIsCT(path
) || SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) ||
3253 !SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
))) {
3254 SecCertificatePathVCSetIsEV(path
, true);
3256 CFReleaseNull(otaref
);
3259 /* Say that we did the expensive path checks (that we want to skip on the second call) */
3260 SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc
->builder
));
3262 /* Check that this path meets CT constraints. */
3263 SecPVCCheckRequireCTConstraints(pvc
);
3264 SecPolicyCheckSystemTrustedCTRequired(pvc
);
3266 /* Check that this path meets known-intermediate constraints. */
3267 SecPathBuilderCheckKnownIntermediateConstraints(pvc
->builder
);
3269 secdebug("policy", "end %strusted path: %@",
3270 (SecPVCIsOkResult(pvc
) ? "" : "not "), SecPathBuilderGetPath(pvc
->builder
));
3275 void SecPVCPathCheckRevocationResponsesReceived(SecPVCRef pvc
) {
3277 /* Since we don't currently allow networking on watchOS,
3278 * don't enforce the revocation-required check here. (32728029) */
3279 bool required
= false;
3281 bool required
= true;
3283 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3284 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3285 for (ix
= 0; ix
< certCount
; ix
++) {
3286 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3287 /* Do we have a valid revocation response? */
3288 if (SecRVCGetEarliestNextUpdate(rvc
) == NULL_TIME
) {
3289 /* No valid revocation response.
3290 * Do we require revocation (for that cert per the
3291 * SecCertificateVCRef, or per the pvc)? */
3292 if (required
&& (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path
, ix
) ||
3293 ((ix
== 0) && pvc
->require_revocation_response
))) {
3294 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
3295 ix
, kCFBooleanFalse
, true);
3297 /* Do we have a definitive Valid revocation result for this cert? */
3298 if (SecRVCHasDefinitiveValidInfo(rvc
) && SecRVCHasRevokedValidInfo(rvc
)) {
3299 SecRVCSetValidDeterminedErrorResult(rvc
);