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 "trust/trustd/SecPolicyServer.h"
29 #include <Security/SecPolicyInternal.h>
30 #include <Security/SecPolicyPriv.h>
31 #include <Security/SecTask.h>
32 #include "trust/trustd/policytree.h"
33 #include "trust/trustd/nameconstraints.h"
34 #include <CoreFoundation/CFTimeZone.h>
36 #include <libDER/oids.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <Security/SecCertificateInternal.h>
39 #include <AssertMacros.h>
40 #include <utilities/debugging.h>
41 #include <utilities/SecInternalReleasePriv.h>
42 #include <security_asn1/SecAsn1Coder.h>
43 #include <security_asn1/ocspTemplates.h>
44 #include <security_asn1/oidsalg.h>
45 #include <security_asn1/oidsocsp.h>
46 #include <CommonCrypto/CommonDigest.h>
47 #include <Security/SecFramework.h>
48 #include <Security/SecPolicyInternal.h>
49 #include <Security/SecTrustPriv.h>
50 #include <Security/SecTrustInternal.h>
51 #include <Security/SecTrustSettingsPriv.h>
52 #include <Security/SecInternal.h>
53 #include <Security/SecKeyPriv.h>
54 #include <Security/SecTask.h>
55 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
57 #include "trust/trustd/SecTrustServer.h"
58 #include "trust/trustd/SecTrustLoggingServer.h"
59 #include "trust/trustd/SecRevocationServer.h"
60 #include "trust/trustd/SecCertificateServer.h"
61 #include "trust/trustd/SecCertificateSource.h"
62 #include "trust/trustd/SecOCSPResponse.h"
63 #include "trust/trustd/SecTrustStoreServer.h"
64 #include <utilities/array_size.h>
65 #include <utilities/SecCFWrappers.h>
66 #include <utilities/SecAppleAnchorPriv.h>
67 #include "OTATrustUtilities.h"
68 #include "personalization.h"
69 #include <sys/codesign.h>
72 #include <Security/SecTaskPriv.h>
75 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
76 #ifndef DUMP_OCSPRESPONSES
77 #define DUMP_OCSPRESPONSES 0
80 #if DUMP_OCSPRESPONSES
85 static void secdumpdata(CFDataRef data
, const char *name
) {
86 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
87 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
94 /********************************************************
95 ****************** SecPolicy object ********************
96 ********************************************************/
98 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
);
99 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
);
100 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
);
101 static SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
);
103 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
104 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
106 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
108 CFArrayRef result
= NULL
;
109 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
110 if (NULL
== otapkiRef
)
115 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
116 CFRelease(otapkiRef
);
118 if (NULL
== evToPolicyAnchorDigest
)
123 CFArrayRef roots
= NULL
;
124 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
125 if (oid
&& evToPolicyAnchorDigest
)
127 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
128 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
130 secerror("EVRoot.plist has non array value");
135 CFReleaseSafe(evToPolicyAnchorDigest
);
140 bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
141 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
144 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
145 policy_set_t valid_policies
) {
146 CFDictionaryRef keySizes
= NULL
;
147 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
149 /* Ensure that this certificate is a valid anchor for one of the
150 certificate policy oids specified in the leaf. */
151 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
153 bool good_ev_anchor
= false;
154 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
155 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
156 if (digests
&& CFArrayContainsValue(digests
,
157 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
158 secdebug("ev", "found anchor for policy oid");
159 good_ev_anchor
= true;
163 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
165 CFAbsoluteTime october2006
= 178761600;
166 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
167 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
168 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
170 if (SecCertificateVersion(certificate
) >= 3
171 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
172 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
173 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
174 secnotice("ev", "Anchor has invalid basic constraints"));
175 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
176 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
177 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
178 secnotice("ev", "Anchor has invalid key usage %u", ku
));
181 /* At least RSA 2048 or ECC NIST P-256. */
182 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
183 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
184 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
185 const void *values
[] = { rsaSize
, ecSize
};
186 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
187 &kCFTypeDictionaryKeyCallBacks
,
188 &kCFTypeDictionaryValueCallBacks
), notEV
);
189 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
190 secnotice("ev", "Anchor's public key is too weak for EV"));
195 CFReleaseNull(rsaSize
);
196 CFReleaseNull(ecSize
);
197 CFReleaseNull(keySizes
);
201 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
202 CFMutableDictionaryRef keySizes
= NULL
;
203 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
206 const SecCECertificatePolicies
*cp
;
207 cp
= SecCertificateGetCertificatePolicies(certificate
);
208 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
209 secnotice("ev", "SubCA missing certificate policies"));
210 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
211 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
212 secnotice("ev", "SubCA missing CRLDP"));
213 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
214 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
215 secnotice("ev", "SubCA has invalid basic constraints"));
216 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
217 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
218 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
219 secnotice("ev", "SubCA has invalid key usage %u", ku
));
221 /* 6.1.5 Key Sizes */
222 CFAbsoluteTime jan2011
= 315532800;
223 CFAbsoluteTime jan2014
= 410227200;
224 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
225 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
226 &kCFTypeDictionaryValueCallBacks
), notEV
);
227 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
228 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
229 SecCertificateNotValidAfter(certificate
) < jan2014
) {
230 /* At least RSA 1024 or ECC NIST P-256. */
231 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
232 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
233 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
234 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
236 /* At least RSA 2028 or ECC NIST P-256. */
237 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
238 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
239 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
240 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
243 /* 7.1.3 Algorithm Object Identifiers */
244 CFAbsoluteTime jan2016
= 473299200;
245 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
247 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
248 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
254 CFReleaseNull(rsaSize
);
255 CFReleaseNull(ecSize
);
256 CFReleaseNull(keySizes
);
260 /********************************************************
261 **************** SecPolicy Callbacks *******************
262 ********************************************************/
263 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
267 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
269 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
270 CFDataRef parentSubjectKeyID
= NULL
;
271 for (ix
= count
- 1; ix
>= 0; --ix
) {
272 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
273 /* If the previous certificate in the chain had a SubjectKeyID,
274 make sure it matches the current certificates AuthorityKeyID. */
275 if (parentSubjectKeyID
) {
276 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
277 SubjectKeyID can be critical. Currenty we don't check
279 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
280 if (authorityKeyID
) {
281 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
282 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
283 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
289 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
293 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
295 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
296 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
297 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
298 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
299 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
303 /* AUDIT[securityd](done):
304 policy->_options is a caller provided dictionary, only its cf type has
307 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
308 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
309 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
310 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
311 /* leaf check enforced */
312 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
313 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
316 /* subCA check produces metrics */
317 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
318 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
319 if (count
> 2 && analytics
) {
320 for (ix
= 1; ix
< count
- 1 ; ++ix
) {
321 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
322 CFArrayRef ekus
= SecCertificateCopyExtendedKeyUsage(cert
);
323 if (ekus
&& CFArrayGetCount(ekus
) && // subCA has ekus
324 !SecPolicyCheckCertExtendedKeyUsage(cert
, CFSTR("2.5.29.37.0")) && // but not the anyEKU
325 !SecPolicyCheckCertExtendedKeyUsage(cert
, xeku
)) { // and not the EKUs specified by the policy
326 analytics
->ca_fail_eku_check
= true;
333 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
335 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
338 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
340 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
341 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
342 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
343 for (ix
= 0; ix
< count
; ++ix
) {
344 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
345 if (!SecPolicyCheckCertNonEmptySubject(cert
, pvcValue
)) {
346 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
352 /* AUDIT[securityd](done):
353 policy->_options is a caller provided dictionary, only its cf type has
356 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
358 /* @@@ Consider what to do if the caller passes in no hostname. Should
359 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
360 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
361 CFStringRef hostName
= (CFStringRef
)
362 CFDictionaryGetValue(policy
->_options
, key
);
363 if (!isString(hostName
)) {
364 /* @@@ We can't return an error here and making the evaluation fail
365 won't help much either. */
369 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
370 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
373 /* Hostname mismatch or no hostnames found in certificate. */
374 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
379 /* AUDIT[securityd](done):
380 policy->_options is a caller provided dictionary, only its cf type has
383 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
384 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
385 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
386 if (!isString(email
)) {
387 /* We can't return an error here and making the evaluation fail
388 won't help much either. */
392 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
394 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
395 /* Hostname mismatch or no hostnames found in certificate. */
396 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
400 static void SecPolicyCheckTemporalValidity(SecPVCRef pvc
,
402 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
403 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
404 for (ix
= 0; ix
< count
; ++ix
) {
405 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
406 if (!SecCertificateIsValid(cert
, verifyTime
)) {
407 /* Intermediate certificate has expired. */
408 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
414 /* AUDIT[securityd](done):
415 policy->_options is a caller provided dictionary, only its cf type has
418 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
420 CFIndex count
= SecPVCGetCertificateCount(pvc
);
422 /* Can't check intermediates common name if there is no intermediate. */
423 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
427 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
428 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
429 CFStringRef commonName
=
430 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
431 if (!isString(commonName
)) {
432 /* @@@ We can't return an error here and making the evaluation fail
433 won't help much either. */
436 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
437 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
441 /* AUDIT[securityd](done):
442 policy->_options is a caller provided dictionary, only its cf type has
445 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
447 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
448 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
449 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
451 if (!isString(common_name
)) {
452 /* @@@ We can't return an error here and making the evaluation fail
453 won't help much either. */
456 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
457 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
461 /* AUDIT[securityd](done):
462 policy->_options is a caller provided dictionary, only its cf type has
465 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
467 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
468 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
469 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
471 if (!isString(prefix
)) {
472 /* @@@ We can't return an error here and making the evaluation fail
473 won't help much either. */
476 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
477 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
481 /* AUDIT[securityd](done):
482 policy->_options is a caller provided dictionary, only its cf type has
485 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
487 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
488 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
489 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
491 if (!isString(common_name
)) {
492 /* @@@ We can't return an error here and making the evaluation fail
493 won't help much either. */
496 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
497 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
501 /* AUDIT[securityd](done):
502 policy->_options is a caller provided dictionary, only its cf type has
505 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
507 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
508 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
509 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
511 /* @@@ We can't return an error here and making the evaluation fail
512 won't help much either. */
515 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
516 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
521 /* AUDIT[securityd](done):
522 policy->_options is a caller provided dictionary, only its cf type has
525 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
527 CFIndex count
= SecPVCGetCertificateCount(pvc
);
528 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
529 CFNumberRef chainLength
=
530 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
532 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
533 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
534 /* @@@ We can't return an error here and making the evaluation fail
535 won't help much either. */
538 if (value
!= count
) {
539 /* Chain length doesn't match policy requirement. */
540 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
545 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
546 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
547 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
549 bool foundMatch
= false;
551 foundMatch
= CFEqual(digest
, value
);
552 else if (isArray(value
))
553 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
555 /* @@@ We only support Data and Array but we can't return an error here so.
556 we let the evaluation fail (not much help) and assert in debug. */
563 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
564 CFIndex count
= SecPVCGetCertificateCount(pvc
);
565 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
566 CFDataRef anchorSHA256
= NULL
;
567 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
569 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
570 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
573 CFReleaseNull(anchorSHA256
);
578 Check the SHA256 of SPKI of the first intermediate CA certificate in the path.
579 policy->_options is a caller provided dictionary, only its cf type has
582 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
584 SecCertificateRef cert
= NULL
;
585 CFDataRef digest
= NULL
;
587 if (SecPVCGetCertificateCount(pvc
) < 2) {
588 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
592 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
593 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
595 if (!isDigestInPolicy(pvc
, key
, digest
)) {
596 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
598 CFReleaseNull(digest
);
602 Check the SPKI SHA256 of CA certificates in the path
603 policy->_options is a caller provided dictionary, only its cf type has
606 static void SecPolicyCheckCAspkiSHA256(SecPVCRef pvc
,
608 SecCertificateRef cert
= NULL
;
609 CFDataRef digest
= NULL
;
611 if (SecPVCGetCertificateCount(pvc
) < 2) {
612 SecPVCSetResult(pvc
, kSecPolicyCheckCAspkiSHA256
, 0, kCFBooleanFalse
);
616 bool spkiSHA256match
= false;
617 CFIndex count
= SecPVCGetCertificateCount(pvc
);
618 for (CFIndex i
= 1; i
< count
&& spkiSHA256match
== false; i
++) {
619 cert
= SecPVCGetCertificateAtIndex(pvc
, i
);
620 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
622 if (isDigestInPolicy(pvc
, key
, digest
)) {
623 spkiSHA256match
= true;
626 CFReleaseNull(digest
);
629 if (spkiSHA256match
== true) {
633 for (CFIndex i
= 1; i
< count
; i
++) {
634 SecPVCSetResult(pvc
, kSecPolicyCheckCAspkiSHA256
, i
, kCFBooleanFalse
);
639 Check the SPKI SHA256 of the leaf certificate.
640 policy->_options is a caller provided dictionary, only its cf type has
643 static void SecPolicyCheckLeafSPKISHA256(SecPVCRef pvc
,
645 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
647 CFArrayRef leafSPKISHA256
= CFDictionaryGetValue(policy
->_options
, key
);
648 if (isArray(leafSPKISHA256
) == false) {
649 /* @@@ We can't return an error here and making the evaluation fail
650 won't help much either. */
654 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
655 CFDataRef digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
657 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
661 if (!CFArrayContainsValue(leafSPKISHA256
, CFRangeMake(0, CFArrayGetCount(leafSPKISHA256
)), digest
)) {
662 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
665 CFReleaseNull(digest
);
669 policy->_options is a caller provided dictionary, only its cf type has
672 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
674 CFIndex count
= SecPVCGetCertificateCount(pvc
);
675 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
676 SecAppleTrustAnchorFlags flags
= 0;
679 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
682 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
689 /* AUDIT[securityd](done):
690 policy->_options is a caller provided dictionary, only its cf type has
693 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
695 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
696 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
697 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
699 if (!isString(org
)) {
700 /* @@@ We can't return an error here and making the evaluation fail
701 won't help much either. */
704 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
705 /* Leaf Subject Organization mismatch. */
706 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
710 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
712 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
713 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
714 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
716 if (!isString(orgUnit
)) {
717 /* @@@ We can't return an error here and making the evaluation fail
718 won't help much either. */
721 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
722 /* Leaf Subject Organization mismatch. */
723 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
727 /* AUDIT[securityd](done):
728 policy->_options is a caller provided dictionary, only its cf type has
731 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
733 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
734 CFArrayRef trustedServerNames
= (CFArrayRef
)
735 CFDictionaryGetValue(policy
->_options
, key
);
736 /* No names specified means we accept any name. */
737 if (!trustedServerNames
)
739 if (!isArray(trustedServerNames
)) {
740 /* @@@ We can't return an error here and making the evaluation fail
741 won't help much either. */
745 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
746 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
747 /* Hostname mismatch or no hostnames found in certificate. */
748 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
752 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
753 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
754 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
755 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
756 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
757 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
758 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
759 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
760 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
761 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
763 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
764 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
765 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
766 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
767 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
768 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
769 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
770 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
771 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
772 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
773 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
774 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
775 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
776 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
778 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
781 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
783 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
784 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
786 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
787 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
788 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
790 CFDataRef serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
792 CFIndex serial_length
= CFDataGetLength(serial
);
793 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
795 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
800 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
802 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
804 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
805 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
807 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
808 CFReleaseSafe(serial
);
817 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
818 if (NULL
!= otapkiRef
)
820 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
821 CFRelease(otapkiRef
);
822 if (NULL
!= blackListedKeys
)
824 /* Check for blacklisted intermediates keys. */
825 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
828 /* Check dgst against blacklist. */
829 if (CFSetContainsValue(blackListedKeys
, dgst
))
831 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
835 CFRelease(blackListedKeys
);
840 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
842 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
843 if (NULL
!= otapkiRef
)
845 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
846 CFRelease(otapkiRef
);
847 if (NULL
!= grayListedKeys
)
849 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
851 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
854 /* Check dgst against gray. */
855 if (CFSetContainsValue(grayListedKeys
, dgst
))
857 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
861 CFRelease(grayListedKeys
);
866 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
868 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
869 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
870 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
872 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
873 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
874 // Force because we may have gotten the legacy key instead of the new key in the policy
875 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkerOid
, 0, kCFBooleanFalse
, true);
879 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
881 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
882 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
883 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
885 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
886 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
892 * The value is a dictionary. The dictionary contains keys indicating
893 * whether the value is for Prod or QA. The values are the same as
894 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
896 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
898 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
899 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
900 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
901 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
903 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
906 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
907 // Force because we may have gotten the legacy key instead of the new key in the policy
908 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkersProdAndQA
, 0, kCFBooleanFalse
, true);
913 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
915 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
916 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
917 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
919 for (ix
= 1; ix
< count
- 1; ix
++) {
920 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
921 if (SecCertificateHasMarkerExtension(cert
, value
))
924 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
925 // Force because we may have gotten the legacy key instead of the new key in the policy
926 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateMarkerOid
, 0, kCFBooleanFalse
, true);
929 static void SecPolicyCheckIntermediateMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
931 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
932 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
933 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
935 for (ix
= 1; ix
< count
- 1; ix
++) {
936 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
937 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
938 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
943 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
945 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
946 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
947 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
949 for (ix
= 1; ix
< count
- 1; ix
++) {
950 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
951 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
952 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
957 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
959 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
960 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
961 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
963 for (ix
= 1; ix
< count
- 1; ix
++) {
964 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
965 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
966 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
967 // Force because we may have gotten the legacy key instead of the new key in the policy
968 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateOrganization
, ix
, kCFBooleanFalse
, true);
973 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
975 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
976 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
977 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
979 for (ix
= 1; ix
< count
- 1; ix
++) {
980 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
981 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
982 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
983 // Force because we may have gotten the legacy key instead of the new key in the policy
984 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateCountry
, ix
, kCFBooleanFalse
, true);
989 /****************************************************************************
990 *********************** New rfc5280 Chain Validation ***********************
991 ****************************************************************************/
993 #define POLICY_MAPPING 1
994 #define POLICY_SUBTREES 1
996 /* rfc5280 basic cert processing. */
997 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
1001 CFIndex count
= SecPVCGetCertificateCount(pvc
);
1002 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1003 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
1004 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1005 uint32_t n
= (uint32_t)count
;
1007 bool is_anchored
= SecPathBuilderIsAnchored(pvc
->builder
);
1008 bool is_anchor_trusted
= false;
1010 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, n
- 1);
1011 if (CFArrayGetCount(constraints
) == 0) {
1012 /* Given that the path builder has already indicated the last cert in this chain has
1013 * trust set on it, empty constraints means trusted. */
1014 is_anchor_trusted
= true;
1016 /* Determine whether constraints say to trust this cert for this PVC. */
1017 SecTrustSettingsResult tsResult
= SecPVCGetTrustSettingsResult(pvc
, SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1),
1019 if (tsResult
== kSecTrustSettingsResultTrustRoot
|| tsResult
== kSecTrustSettingsResultTrustAsRoot
) {
1020 is_anchor_trusted
= true;
1025 if (is_anchor_trusted
) {
1026 /* If the anchor is trusted we don't process the last cert in the
1030 Boolean isSelfSigned
= false;
1031 (void) SecCertificateIsSelfSigned(SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1), &isSelfSigned
);
1033 /* Add a detail for the root not being trusted. */
1034 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
1035 n
- 1, kCFBooleanFalse
, true)) {
1039 /* Add a detail for the missing intermediate. */
1040 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckMissingIntermediate
,
1041 n
- 1, kCFBooleanFalse
, true)) {
1047 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
1048 //policy_set_t user_initial_policy_set = NULL;
1049 //trust_anchor_t anchor;
1051 /* Initialization */
1053 CFMutableArrayRef permitted_subtrees
= NULL
;
1054 CFMutableArrayRef excluded_subtrees
= NULL
;
1055 /* set the initial subtrees to the trusted anchor's subtrees, if it has them */
1056 SecCertificateRef anchor
= SecCertificatePathVCGetRoot(path
);
1057 CFArrayRef anchor_permitted_subtrees
= SecCertificateGetPermittedSubtrees(anchor
);
1058 if (is_anchor_trusted
&& anchor_permitted_subtrees
) {
1059 permitted_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_permitted_subtrees
);
1061 permitted_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1064 CFArrayRef anchor_excluded_subtrees
= SecCertificateGetExcludedSubtrees(anchor
);
1065 if (is_anchor_trusted
&& anchor_excluded_subtrees
) {
1066 excluded_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_excluded_subtrees
);
1068 excluded_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1071 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1072 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1073 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1074 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1077 if (!SecCertificatePathVCVerifyPolicyTree(path
, is_anchor_trusted
)) {
1078 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckPolicyConstraints
, 0, kCFBooleanFalse
, true)) {
1084 /* Path builder ensures we only get cert chains with proper issuer
1085 chaining with valid signatures along the way. */
1086 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1087 SecKeyRef working_public_key
= anchor
->public_key
;
1088 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1090 uint32_t i
, max_path_length
= n
;
1091 SecCertificateRef cert
= NULL
;
1092 for (i
= 1; i
<= n
; ++i
) {
1094 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1095 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc
->builder
), n
- i
);
1097 /* (a) Verify the basic certificate information. */
1098 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1099 using the working_public_key and the working_public_key_parameters. */
1101 /* Already done by chain builder. */
1102 if (!SecCertificateIsValid(cert
, verify_time
)) {
1103 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, n
- i
, kCFBooleanFalse
)) {
1107 if (SecCertificateIsWeakKey(cert
)) {
1108 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, n
- i
, kCFBooleanFalse
)) {
1112 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
1113 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, n
- i
, kCFBooleanFalse
)) {
1118 /* @@@ cert.issuer == working_issuer_name. */
1122 if (!is_self_issued
|| i
== n
) {
1124 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1125 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1126 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1127 secnotice("policy", "name in excluded subtrees");
1128 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1131 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1132 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1133 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1134 secnotice("policy", "name not in permitted subtrees");
1135 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1140 /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
1142 /* If Last Cert in Path */
1146 /* Prepare for Next Cert */
1147 /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
1148 /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
1150 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1152 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1153 if (permitted_subtrees_in_cert
) {
1154 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1157 // could do something smart here to avoid inserting the exact same constraint
1158 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1159 if (excluded_subtrees_in_cert
) {
1160 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1161 CFRange range
= { 0, num_trees
};
1162 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1165 /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
1167 /* (k) Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1170 if (!is_self_issued
) {
1171 if (max_path_length
> 0) {
1174 /* max_path_len exceeded, illegal. */
1175 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsPathLen
,
1176 n
- i
, kCFBooleanFalse
, true)) {
1182 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(cert
);
1183 if (bc
&& bc
->pathLenConstraintPresent
1184 && bc
->pathLenConstraint
< max_path_length
) {
1185 max_path_length
= bc
->pathLenConstraint
;
1188 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1189 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1191 /* (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. */
1192 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1193 /* Certificate contains one or more unknown critical extensions. */
1194 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, n
- i
, kCFBooleanFalse
)) {
1199 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1200 /* Certificate contains one or more known exensions where parsing failed. */
1201 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, n
-i
, kCFBooleanFalse
, true)) {
1206 } /* end loop over certs in path */
1208 /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
1211 /* 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
1212 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1214 /* (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. */
1215 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1216 /* Certificate contains one or more unknown critical extensions. */
1217 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, 0, kCFBooleanFalse
)) {
1222 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1223 /* Certificate contains one or more known exensions where parsing failed. */
1224 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, 0, kCFBooleanFalse
, true)) {
1228 /* (g) done by SecCertificatePathVCVerifyPolicyTree */
1231 CFReleaseNull(permitted_subtrees
);
1232 CFReleaseNull(excluded_subtrees
);
1235 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1236 policy_set_t policies
= NULL
;
1237 const SecCECertificatePolicies
*cp
=
1238 SecCertificateGetCertificatePolicies(cert
);
1239 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1240 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1241 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1246 static void SecPolicyCheckEV(SecPVCRef pvc
,
1248 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1249 policy_set_t valid_policies
= NULL
;
1251 /* 6.1.7. Key Usage Purposes */
1253 CFAbsoluteTime jul2016
= 489024000;
1254 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1255 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1256 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1257 if (SecPVCSetResultForced(pvc
, key
,
1258 0, kCFBooleanFalse
, true)) {
1264 for (ix
= 0; ix
< count
; ++ix
) {
1265 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1266 policy_set_t policies
= policies_for_cert(cert
);
1269 /* anyPolicy in the leaf isn't allowed for EV, so only init
1270 valid_policies if we have real policies. */
1271 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1272 valid_policies
= policies
;
1275 } else if (ix
< count
- 1) {
1276 /* Subordinate CA */
1277 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1278 secnotice("ev", "subordinate certificate is not ev");
1279 if (SecPVCSetResultForced(pvc
, key
,
1280 ix
, kCFBooleanFalse
, true)) {
1281 policy_set_free(valid_policies
);
1282 policy_set_free(policies
);
1286 policy_set_intersect(&valid_policies
, policies
);
1289 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1290 secnotice("ev", "anchor certificate is not ev");
1291 if (SecPVCSetResultForced(pvc
, key
,
1292 ix
, kCFBooleanFalse
, true)) {
1293 policy_set_free(valid_policies
);
1294 policy_set_free(policies
);
1299 policy_set_free(policies
);
1300 if (!valid_policies
) {
1301 secnotice("ev", "valid_policies set is empty: chain not ev");
1302 /* If we ever get into a state where no policies are valid anymore
1303 this can't be an ev chain. */
1304 if (SecPVCSetResultForced(pvc
, key
,
1305 ix
, kCFBooleanFalse
, true)) {
1311 policy_set_free(valid_policies
);
1313 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1314 Subscriber MUST contain an OID defined by the CA in the certificate’s
1315 certificatePolicies extension that: (i) indicates which CA policy statement relates
1316 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1317 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1318 marks the certificate as being an EV Certificate.
1319 (b) EV Subordinate CA Certificates
1320 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1321 CA MUST contain one or more OIDs defined by the issuing CA that
1322 explicitly identify the EV Policies that are implemented by the Subordinate
1324 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1325 MAY contain the special anyPolicy OID (2.5.29.32.0).
1326 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1327 certificatePolicies or extendedKeyUsage extensions.
1333 * MARK: Certificate Transparency support
1335 const CFStringRef kSecCTRetirementDateKey
= CFSTR("expiry"); // For backwards compatibility, retirement date is represented with the "expiry" key
1336 const CFStringRef kSecCTReadOnlyDateKey
= CFSTR("frozen"); // For backwards compatibility, read-only date is represented with the "frozen" key
1337 const CFStringRef kSecCTShardStartDateKey
= CFSTR("start_inclusive");
1338 const CFStringRef kSecCTShardEndDateKey
= CFSTR("end_exclusive");
1339 const CFStringRef kSecCTPublicKeyKey
= CFSTR("key");
1342 kSecCTEntryTypeCert
= 0,
1343 kSecCTEntryTypePreCert
= 1,
1349 Version sct_version; // 1 byte
1350 LogID id; // 32 bytes
1351 uint64 timestamp; // 8 bytes
1352 CtExtensions extensions; // 2 bytes len field, + n bytes data
1353 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1354 Version sct_version;
1355 SignatureType signature_type = certificate_timestamp;
1357 LogEntryType entry_type;
1358 select(entry_type) {
1359 case x509_entry: ASN.1Cert;
1360 case precert_entry: PreCert;
1362 CtExtensions extensions;
1364 } SignedCertificateTimestamp;
1368 #include <Security/SecureTransportPriv.h>
1371 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1374 case SSL_SignatureAlgorithmRSA
:
1376 case SSL_HashAlgorithmSHA1
:
1377 return &CSSMOID_SHA1WithRSA
;
1378 case SSL_HashAlgorithmSHA256
:
1379 return &CSSMOID_SHA256WithRSA
;
1380 case SSL_HashAlgorithmSHA384
:
1381 return &CSSMOID_SHA384WithRSA
;
1385 case SSL_SignatureAlgorithmECDSA
:
1387 case SSL_HashAlgorithmSHA1
:
1388 return &CSSMOID_ECDSA_WithSHA1
;
1389 case SSL_HashAlgorithmSHA256
:
1390 return &CSSMOID_ECDSA_WithSHA256
;
1391 case SSL_HashAlgorithmSHA384
:
1392 return &CSSMOID_ECDSA_WithSHA384
;
1404 static size_t SSLDecodeUint16(const uint8_t *p
)
1406 return (p
[0]<<8 | p
[1]);
1409 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1411 p
[0] = (len
>> 8)&0xff;
1412 p
[1] = (len
& 0xff);
1416 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1418 p
[0] = (len
>> 16)&0xff;
1419 p
[1] = (len
>> 8)&0xff;
1420 p
[2] = (len
& 0xff);
1426 uint64_t SSLDecodeUint64(const uint8_t *p
)
1429 for(int i
=0; i
<8; i
++) {
1436 #include <libDER/DER_CertCrl.h>
1437 #include <libDER/DER_Encode.h>
1438 #include <libDER/asn1Types.h>
1441 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1443 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1445 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
1447 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
1449 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1450 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
1451 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
1457 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
1459 SecCertificateRef leafCert
= NULL
;
1460 SecCertificateRef issuer
= NULL
;
1461 CFDataRef issuerKeyHash
= NULL
;
1462 CFDataRef tbs_precert
= NULL
;
1463 CFMutableDataRef data
= NULL
;
1465 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1466 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1467 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1469 require(leafCert
, out
);
1470 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
1471 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
1472 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
1474 require(issuerKeyHash
, out
);
1475 require(tbs_precert
, out
);
1476 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1477 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1479 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1480 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
1481 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
1482 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
1485 CFReleaseSafe(issuerKeyHash
);
1486 CFReleaseSafe(tbs_precert
);
1491 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
1493 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
1497 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
1499 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
1502 static bool isSCTValidForLogData(CFDictionaryRef logData
, int entry_type
, CFAbsoluteTime sct_time
, CFAbsoluteTime cert_expiry_date
) {
1503 /* only embedded SCTs can be used from retired logs. */
1504 if(entry_type
==kSecCTEntryTypeCert
&& CFDictionaryContainsKey(logData
, kSecCTRetirementDateKey
)) {
1508 /* SCTs from after the transition to read-only are not valid (and indicate a operator failure) */
1509 CFDateRef frozen_date
= CFDictionaryGetValue(logData
, kSecCTReadOnlyDateKey
);
1510 if (frozen_date
&& (sct_time
> CFDateGetAbsoluteTime(frozen_date
))) {
1511 secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData
);
1515 /* If the log is temporally sharded, the certificate expiry date must be within the temporal shard window */
1516 CFDateRef start_inclusive
= CFDictionaryGetValue(logData
, kSecCTShardStartDateKey
);
1517 CFDateRef end_exclusive
= CFDictionaryGetValue(logData
, kSecCTShardEndDateKey
);
1518 if (start_inclusive
&& (cert_expiry_date
< CFDateGetAbsoluteTime(start_inclusive
))) {
1521 if (end_exclusive
&& (cert_expiry_date
>= CFDateGetAbsoluteTime(end_exclusive
))) {
1530 If the 'sct' is valid, add it to the validatingLogs dictionary.
1533 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
1535 - entry_type: 0 for x509 cert, 1 for precert.
1536 - entry: the cert or precert data.
1537 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
1538 - trustedLog: Dictionary contain the Trusted Logs.
1540 The SCT is valid if:
1541 - It decodes properly.
1542 - Its timestamp is less than 'verifyTime'.
1543 - It is signed by a log in 'trustedLogs'.
1544 - If entry_type = 0, the log must be currently qualified.
1545 - If entry_type = 1, the log may be expired.
1547 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
1548 If an entry for the same log already existing in the dictionary, the entry is replaced only if the timestamp of this SCT is earlier.
1551 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFAbsoluteTime cert_expiry_date
, CFDictionaryRef trustedLogs
, CFAbsoluteTime
*sct_at
)
1554 const uint8_t *logID
;
1555 const uint8_t *timestampData
;
1557 size_t extensionsLen
;
1558 const uint8_t *extensionsData
;
1561 size_t signatureLen
;
1562 const uint8_t *signatureData
;
1563 SecKeyRef pubKey
= NULL
;
1564 uint8_t *signed_data
= NULL
;
1565 const SecAsn1Oid
*oid
= NULL
;
1567 CFDataRef logIDData
= NULL
;
1568 CFDictionaryRef result
= 0;
1570 const uint8_t *p
= CFDataGetBytePtr(sct
);
1571 size_t len
= CFDataGetLength(sct
);
1573 require(len
>=43, out
);
1575 version
= p
[0]; p
++; len
--;
1576 logID
= p
; p
+=32; len
-=32;
1577 timestampData
= p
; p
+=8; len
-=8;
1578 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1580 require(len
>=extensionsLen
, out
);
1581 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
1583 require(len
>=4, out
);
1584 hashAlg
=p
[0]; p
++; len
--;
1585 sigAlg
=p
[0]; p
++; len
--;
1586 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1587 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
1590 /* verify version: only v1(0) is supported */
1592 secerror("SCT version unsupported: %d\n", version
);
1596 /* verify timestamp not in the future */
1597 timestamp
= SSLDecodeUint64(timestampData
);
1598 if(timestamp
> vt
) {
1599 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
1606 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
1607 signed_data
= malloc(signed_data_len
);
1608 require(signed_data
, out
);
1611 *q
++ = 0; // certificate_timestamp
1612 memcpy(q
, timestampData
, 8); q
+=8;
1613 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
1614 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
1615 q
= SSLEncodeUint16(q
, extensionsLen
);
1616 memcpy(q
, extensionsData
, extensionsLen
);
1618 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
1620 CFDictionaryRef logData
= CFDictionaryGetValue(trustedLogs
, logIDData
);
1621 CFAbsoluteTime sct_time
= TimestampToCFAbsoluteTime(timestamp
);
1622 require(logData
&& isSCTValidForLogData(logData
, entry_type
, sct_time
, cert_expiry_date
), out
);
1624 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, kSecCTPublicKeyKey
);
1625 require(logKeyData
, out
); // This failing would be an internal logic error
1626 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
1627 require(pubKey
, out
);
1629 oid
= oidForSigAlg(hashAlg
, sigAlg
);
1632 algId
.algorithm
= *oid
;
1633 algId
.parameters
.Data
= NULL
;
1634 algId
.parameters
.Length
= 0;
1636 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
1640 secerror("SCT signature failed (log=%@)\n", logData
);
1644 CFReleaseSafe(logIDData
);
1645 CFReleaseSafe(pubKey
);
1651 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
1653 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
1655 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
1656 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
1657 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
1658 CFReleaseSafe(sct_time
);
1662 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
1664 CFMutableArrayRef SCTs
= NULL
;
1665 SecCertificateRef leafCert
= NULL
;
1666 SecCertificateRef issuer
= NULL
;
1667 CFArrayRef ocspResponsesData
= NULL
;
1668 SecOCSPRequestRef ocspRequest
= NULL
;
1670 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
1671 require_quiet(ocspResponsesData
, out
);
1673 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1674 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1675 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1677 require(leafCert
, out
);
1678 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
1679 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
1681 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1684 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
1685 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
1686 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
1687 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
1688 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
1689 if(ocspSingleResponse
) {
1690 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
1691 if(singleResponseSCTs
) {
1692 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
1693 CFRelease(singleResponseSCTs
);
1695 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
1698 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
1701 if(CFArrayGetCount(SCTs
)==0) {
1702 CFReleaseNull(SCTs
);
1706 CFReleaseSafe(ocspResponsesData
);
1708 SecOCSPRequestFinalize(ocspRequest
);
1713 static void SecPolicyCheckCT(SecPVCRef pvc
)
1715 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1716 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
1717 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
1718 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
1719 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
1720 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
1721 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
1722 __block
uint32_t trustedSCTCount
= 0;
1723 __block CFAbsoluteTime issuanceTime
= SecPVCGetVerifyTime(pvc
);
1724 __block CFAbsoluteTime certExpiry
= SecCertificateNotValidAfter(leafCert
);
1725 TA_CTFailureReason failureReason
= TA_CTNoFailure
;
1728 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1729 trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
1730 CFReleaseSafe(otapkiref
);
1733 // This eventually contain list of logs who validated the SCT.
1734 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1735 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1737 uint64_t vt
= TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc
));
1739 __block
bool at_least_one_currently_valid_external
= 0;
1740 __block
bool at_least_one_currently_valid_embedded
= 0;
1741 __block
bool unknown_log
= 0;
1742 __block
bool disqualified_log
= 0;
1744 require(logsValidatingEmbeddedScts
, out
);
1745 require(currentLogsValidatingScts
, out
);
1747 /* Skip if there are no SCTs. */
1748 bool no_scts
= (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) ||
1749 (builderScts
&& CFArrayGetCount(builderScts
) > 0) ||
1750 (ocspScts
&& CFArrayGetCount(ocspScts
) > 0);
1751 require_action_quiet(no_scts
, out
,
1752 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1754 analytics
->ct_failure_reason
= TA_CTNoSCTs
;
1758 if(trustedLogs
&& CFDictionaryGetCount(trustedLogs
) > 0) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
1759 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
1760 CFArrayForEach(embeddedScts
, ^(const void *value
){
1761 CFAbsoluteTime sct_at
;
1762 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1764 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
1765 if(!CFDictionaryContainsKey(log
, kSecCTRetirementDateKey
)) {
1766 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1767 at_least_one_currently_valid_embedded
= true;
1770 disqualified_log
= true;
1778 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
1779 CFArrayForEach(builderScts
, ^(const void *value
){
1780 CFAbsoluteTime sct_at
;
1781 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1783 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1784 at_least_one_currently_valid_external
= true;
1792 if(ocspScts
&& x509Entry
) {
1793 CFArrayForEach(ocspScts
, ^(const void *value
){
1794 CFAbsoluteTime sct_at
;
1795 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1797 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1798 at_least_one_currently_valid_external
= true;
1806 failureReason
= TA_CTMissingLogs
;
1810 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
1813 is_ct = (A1 AND A2) OR (B1 AND B2).
1815 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
1816 A2: At least one embedded SCT from a currently valid log.
1818 B1: SCTs from 2 currently valid logs (from any source)
1819 B2: At least 1 external SCT from a currently valid log.
1823 bool hasValidExternalSCT
= (at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2);
1824 bool hasValidEmbeddedSCT
= (at_least_one_currently_valid_embedded
);
1825 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1826 SecCertificatePathVCSetIsCT(path
, false);
1828 if (hasValidEmbeddedSCT
) {
1829 /* Calculate issuance time based on timestamp of SCTs from current logs */
1830 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
1831 CFDictionaryRef log
= key
;
1832 if(!CFDictionaryContainsKey(log
, kSecCTRetirementDateKey
)) {
1833 // Log is still qualified
1834 CFDateRef ts
= (CFDateRef
) value
;
1835 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
1836 if(timestamp
< issuanceTime
) {
1837 issuanceTime
= timestamp
;
1841 SecCertificatePathVCSetIssuanceTime(path
, issuanceTime
);
1843 if (hasValidExternalSCT
) {
1844 /* Note: since external SCT validates this cert, we do not need to
1845 override issuance time here. If the cert also has a valid embedded
1846 SCT, issuanceTime will be calculated and set in the block above. */
1847 SecCertificatePathVCSetIsCT(path
, true);
1848 } else if (hasValidEmbeddedSCT
) {
1849 __block
int lifetime
; // in Months
1850 __block
unsigned once_or_current_qualified_embedded
= 0;
1853 __block
bool failed_once_check
= false;
1854 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
1855 CFDictionaryRef log
= key
;
1856 CFDateRef ts
= value
;
1857 CFDateRef expiry
= CFDictionaryGetValue(log
, kSecCTRetirementDateKey
);
1858 if (expiry
== NULL
) { // Currently qualified OR
1859 once_or_current_qualified_embedded
++;
1860 } else if (CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
&& // Once qualified. That is, qualified at the time of SCT AND
1861 issuanceTime
< CFDateGetAbsoluteTime(expiry
)) { // at the time of issuance.)
1862 once_or_current_qualified_embedded
++;
1865 failed_once_check
= true;
1869 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
1871 CFCalendarGetComponentDifference(zuluCalendar
,
1872 SecCertificateNotValidBefore(leafCert
),
1873 SecCertificateNotValidAfter(leafCert
),
1874 0, "M", &_lifetime
);
1875 lifetime
= _lifetime
;
1878 unsigned requiredEmbeddedSctsCount
;
1880 if (lifetime
< 15) {
1881 requiredEmbeddedSctsCount
= 2;
1882 } else if (lifetime
<= 27) {
1883 requiredEmbeddedSctsCount
= 3;
1884 } else if (lifetime
<= 39) {
1885 requiredEmbeddedSctsCount
= 4;
1887 requiredEmbeddedSctsCount
= 5;
1890 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
1891 SecCertificatePathVCSetIsCT(path
, true);
1893 /* Not enough "once or currently qualified" SCTs */
1894 if (failed_once_check
) {
1895 failureReason
= TA_CTEmbeddedNotEnoughDisqualified
;
1896 } else if (unknown_log
) {
1897 failureReason
= TA_CTEmbeddedNotEnoughUnknown
;
1899 failureReason
= TA_CTEmbeddedNotEnough
;
1902 } else if (!at_least_one_currently_valid_embedded
&& !at_least_one_currently_valid_external
) {
1903 /* No currently valid SCTs */
1904 if (disqualified_log
) {
1905 failureReason
= TA_CTNoCurrentSCTsDisqualifiedLog
;
1906 } else if (unknown_log
) {
1907 failureReason
= TA_CTNoCurrentSCTsUnknownLog
;
1909 } else if (at_least_one_currently_valid_external
) {
1910 /* One presented current SCT but failed total current check */
1911 if (disqualified_log
) {
1912 failureReason
= TA_CTPresentedNotEnoughDisqualified
;
1913 } else if (unknown_log
) {
1914 failureReason
= TA_CTPresentedNotEnoughUnknown
;
1916 failureReason
= TA_CTPresentedNotEnough
;
1920 /* Record analytics data for CT */
1921 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1922 require_quiet(analytics
, out
);
1923 uint32_t sctCount
= 0;
1924 /* Count the total number of SCTs we found and report where we got them */
1925 if (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) {
1926 analytics
->sct_sources
|= TA_SCTEmbedded
;
1927 sctCount
+= CFArrayGetCount(embeddedScts
);
1929 if (builderScts
&& CFArrayGetCount(builderScts
) > 0) {
1930 analytics
->sct_sources
|= TA_SCT_TLS
;
1931 sctCount
+= CFArrayGetCount(builderScts
);
1933 if (ocspScts
&& CFArrayGetCount(ocspScts
) > 0) {
1934 analytics
->sct_sources
|= TA_SCT_OCSP
;
1935 sctCount
+= CFArrayGetCount(ocspScts
);
1937 /* Report how many of those SCTs were once or currently qualified */
1938 analytics
->number_trusted_scts
= trustedSCTCount
;
1939 /* Report how many SCTs we got */
1940 analytics
->number_scts
= sctCount
;
1942 analytics
->ct_failure_reason
= failureReason
;
1943 /* Only one current SCT -- close to failure */
1944 if (CFDictionaryGetCount(currentLogsValidatingScts
) == 1) {
1945 analytics
->ct_one_current
= true;
1948 CFReleaseSafe(logsValidatingEmbeddedScts
);
1949 CFReleaseSafe(currentLogsValidatingScts
);
1950 CFReleaseSafe(builderScts
);
1951 CFReleaseSafe(embeddedScts
);
1952 CFReleaseSafe(ocspScts
);
1953 CFReleaseSafe(precertEntry
);
1954 CFReleaseSafe(trustedLogs
);
1955 CFReleaseSafe(x509Entry
);
1958 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
1959 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1961 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
1962 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
1964 for (ix
= 0; ix
< count
; ix
++) {
1965 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1966 policy_set_t policies
= policies_for_cert(cert
);
1968 if (policy_set_contains(policies
, &key_value
)) {
1969 policy_set_free(policies
);
1972 policy_set_free(policies
);
1977 static void SecPolicyCheckCertificatePolicy(SecPVCRef pvc
, CFStringRef key
)
1979 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1980 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1981 bool result
= false;
1983 if (CFGetTypeID(value
) == CFDataGetTypeID())
1985 result
= checkPolicyOidData(pvc
, value
);
1986 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1987 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
1989 result
= checkPolicyOidData(pvc
, dataOid
);
1994 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1999 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
2001 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2002 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2003 if (isString(value
)) {
2004 SecPathBuilderSetRevocationMethod(pvc
->builder
, value
);
2008 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
2010 pvc
->require_revocation_response
= true;
2011 secdebug("policy", "revocation response required");
2014 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
2015 SecPathBuilderSetCheckRevocationOnline(pvc
->builder
);
2018 static void SecPolicyCheckRevocationIfTrusted(SecPVCRef pvc
, CFStringRef key
) {
2019 SecPathBuilderSetCheckRevocationIfTrusted(pvc
->builder
);
2022 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
2024 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2025 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
2026 if (value
== kCFBooleanTrue
) {
2027 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
2029 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
2033 static void SecPolicyCheckWeakKeySize(SecPVCRef pvc
,
2035 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2036 for (ix
= 0; ix
< count
; ++ix
) {
2037 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2038 if (cert
&& SecCertificateIsWeakKey(cert
)) {
2039 /* Intermediate certificate has a weak key. */
2040 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2046 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
2047 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2048 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2049 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
2050 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2052 /* Don't check key size for user-anchored leafs */
2053 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, leaf
)) {
2057 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, leaf
)) {
2062 for (ix
= 0; ix
< count
; ++ix
) {
2063 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2064 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
2065 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2071 static void SecPolicyCheckWeakSignature(SecPVCRef pvc
, CFStringRef key
) {
2072 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2073 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2074 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
2075 for (ix
= 0; ix
< count
; ++ix
) {
2076 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2077 if (!SecPolicyCheckCertWeakSignature(cert
, pvcValue
)) {
2078 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2084 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
2086 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
2088 /* Ignore (a non-self-signed) anchor if it's trusted by the user */
2089 bool userAnchored
= SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, SecPVCGetCertificateAtIndex(pvc
, count
- 1));
2091 userAnchored
= userAnchored
|| SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
,
2092 SecPVCGetCertificateAtIndex(pvc
, count
- 1));
2094 if (SecPathBuilderIsAnchored(pvc
->builder
) && userAnchored
) {
2098 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2099 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
2100 while (ix
< count
) {
2101 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2102 /* note that these checks skip self-signed certs */
2103 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
2104 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2111 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
2112 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2113 require_quiet(leaf
, out
);
2115 /* And now a special snowflake from our tests */
2117 /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
2118 /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
2119 /* Not After : May 26 09:37:50 2017 GMT */
2120 static const uint8_t vodafone
[] = {
2121 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
2122 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
2125 CFDataRef leafFingerprint
= SecCertificateGetSHA1Digest(leaf
);
2126 require_quiet(leafFingerprint
, out
);
2127 const unsigned int len
= 20;
2128 const uint8_t *dp
= CFDataGetBytePtr(leafFingerprint
);
2129 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
2137 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
2139 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
2141 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2143 Boolean keyInPolicy
= false;
2144 CFArrayRef policies
= pvc
->policies
;
2145 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2146 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2147 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2148 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2153 /* We only enforce this check when *both* of the following are true:
2154 * 1. One of the certs in the path has this usage constraint, and
2155 * 2. One of the policies in the PVC has this key
2156 * (As compared to normal policy options which require only one to be true..) */
2157 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2160 /* Ignore the anchor if it's trusted */
2161 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2164 for (ix
= 0; ix
< count
; ++ix
) {
2165 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2166 if (SecCertificateIsWeakHash(cert
)) {
2167 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
2168 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2178 static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc
,
2180 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2182 Boolean keyInPolicy
= false;
2183 CFArrayRef policies
= pvc
->policies
;
2184 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2185 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2186 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2187 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2192 /* We only enforce this check when *both* of the following are true:
2193 * 1. One of the certs in the path has this usage constraint, and
2194 * 2. One of the policies in the PVC has this key
2195 * (As compared to normal policy options which require only one to be true..) */
2196 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2199 /* Ignore the anchor if it's trusted */
2200 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2203 for (ix
= 0; ix
< count
; ++ix
) {
2204 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2205 if (!SecCertificateIsStrongKey(cert
)) {
2206 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2216 static void SecPolicyCheckPinningRequired(SecPVCRef pvc
, CFStringRef key
) {
2217 /* Pinning is disabled on the system, skip. */
2218 if (SecIsInternalRelease()) {
2219 if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
2220 CFSTR("com.apple.security"), NULL
)) {
2225 CFArrayRef policies
= pvc
->policies
;
2226 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2227 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2228 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2229 CFStringRef policyName
= SecPolicyGetName(policy
);
2230 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
2231 /* policy required pinning, but we didn't use a pinning policy */
2232 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
)) {
2239 static bool is_configured_test_system_root(SecCertificateRef root
, CFStringRef preference
) {
2240 if (!SecIsInternalRelease()) {
2243 bool result
= false;
2244 CFDataRef rootHash
= SecCertificateCopySHA256Digest(root
);
2245 CFTypeRef value
= CFPreferencesCopyAppValue(preference
, CFSTR("com.apple.security"));
2246 require_quiet(isData(value
), out
);
2247 require_quiet(CFEqual(rootHash
, value
), out
);
2251 CFReleaseNull(value
);
2252 CFReleaseNull(rootHash
);
2256 static bool is_ct_excepted_domain(CFStringRef hostname
, CFStringRef exception
) {
2257 if (kCFCompareEqualTo
== CFStringCompare(exception
, hostname
, kCFCompareCaseInsensitive
)) {
2260 } else if (CFStringHasPrefix(exception
, CFSTR("."))) {
2262 CFIndex elength
= CFStringGetLength(exception
);
2263 CFIndex hlength
= CFStringGetLength(hostname
);
2264 if (hlength
> elength
) {
2265 CFRange compareRange
= { hlength
- elength
, elength
};
2266 if (kCFCompareEqualTo
== CFStringCompareWithOptions(hostname
, exception
, compareRange
, kCFCompareCaseInsensitive
)) {
2269 } else if (hlength
+ 1 == elength
) {
2270 CFRange compareRange
= { 1, hlength
};
2271 if (kCFCompareEqualTo
== CFStringCompareWithOptions(exception
, hostname
, compareRange
, kCFCompareCaseInsensitive
)) {
2279 static OSStatus
is_subtree_dn_with_org(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
2280 if (gnType
!= GNT_DirectoryName
) {
2281 return errSecInternal
;
2284 DERDecodedInfo subtreeName_content
;
2285 if (DR_Success
!= DERDecodeItem(generalName
, &subtreeName_content
) || subtreeName_content
.tag
!= ASN1_CONSTR_SEQUENCE
) {
2286 return errSecDecode
;
2289 CFArrayRef subtree_orgs
= SecCertificateCopyOrganizationFromX501NameContent(&subtreeName_content
.content
);
2291 CFReleaseNull(subtree_orgs
);
2292 return errSecSuccess
;
2294 return errSecInternal
;
2297 static bool has_ct_excepted_key(SecCertificatePathVCRef path
, CFDictionaryRef exception
) {
2298 __block
bool result
= false;
2299 CFDataRef exceptionHash
= CFDictionaryGetValue(exception
, kSecCTExceptionsSPKIHashKey
);
2301 /* exception for a leaf is always allowed */
2302 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
2303 CFDataRef spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
2304 if (CFEqualSafe(exceptionHash
, spkiHash
)) {
2307 CFReleaseNull(spkiHash
);
2309 if (result
) { return result
; }
2311 /* exceptions for CAs */
2312 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
2313 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
2315 spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
2316 if (!CFEqualSafe(exceptionHash
, spkiHash
)) {
2317 CFReleaseNull(spkiHash
);
2320 CFReleaseNull(spkiHash
);
2322 /* this CA matches but exceptions for CAs have constraints */
2323 if (SecCertificateGetPermittedSubtrees(ca
)) {
2324 /* Constrained CAs have to have a Distinguished Name permitted subtree with an Organization attribute */
2325 CFArrayForEach(SecCertificateGetPermittedSubtrees(ca
), ^(const void *value
) {
2326 CFDataRef subtree
= (CFDataRef
)value
;
2327 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(subtree
), CFDataGetLength(subtree
) };
2328 DERDecodedInfo general_name_content
;
2329 if (DR_Success
== DERDecodeItem(&general_name
, &general_name_content
)) {
2330 OSStatus status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
2331 &general_name_content
.content
,
2333 is_subtree_dn_with_org
);
2334 if (status
== errSecSuccess
) {
2342 /* The Organization attribute(s) in the CA subject have to exactly match the Organization attribute(s) in the leaf */
2343 CFArrayRef leafOrgs
= SecCertificateCopyOrganization(leaf
);
2344 CFArrayRef caOrgs
= SecCertificateCopyOrganization(ca
);
2345 if (caOrgs
&& leafOrgs
&& CFEqualSafe(leafOrgs
, caOrgs
)) {
2348 CFReleaseNull(leafOrgs
);
2349 CFReleaseNull(caOrgs
);
2360 static bool is_ct_excepted(SecPVCRef pvc
) {
2361 CFDictionaryRef ct_exceptions
= _SecTrustStoreCopyCTExceptions(NULL
, NULL
);
2362 if (!ct_exceptions
) {
2366 __block
bool result
= false;
2367 CFArrayRef domainExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsDomainsKey
);
2368 if (domainExceptions
) {
2369 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2370 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2372 CFArrayForEach(domainExceptions
, ^(const void *value
) {
2373 result
= result
|| is_ct_excepted_domain(hostname
, value
);
2378 secinfo("policy", "domain-based CT exception applied");
2379 CFReleaseNull(ct_exceptions
);
2383 CFArrayRef keyExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsCAsKey
);
2384 if (keyExceptions
) {
2385 __block SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2386 CFArrayForEach(keyExceptions
, ^(const void *value
) {
2387 result
= result
|| has_ct_excepted_key(path
, value
);
2392 secinfo("policy" , "key-based CT exceptions applied");
2395 CFReleaseNull(ct_exceptions
);
2399 /* <rdar://45466778> some Apple servers not getting certs with embedded SCTs */
2400 static bool is_ct_allowlisted_cert(SecCertificateRef leaf
) {
2401 if (CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlist"), CFSTR("com.apple.security"), NULL
) ||
2402 CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlistApple"), CFSTR("com.apple.security"), NULL
)) {
2406 /* subject:/CN=basejumper.apple.com/OU=management:idms.group.110621/O=Apple Inc./ST=California/C=US */
2407 /* issuer :/CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US */
2408 static const uint8_t basejumper_hash
[] = {
2409 0x23, 0x32, 0x0b, 0x5a, 0x24, 0xd8, 0x4d, 0x27, 0x8c, 0x43, 0xc9, 0xed, 0x22, 0xed, 0x87, 0xb7,
2410 0xc5, 0x51, 0x43, 0x55, 0xa9, 0x84, 0x79, 0x5a, 0x77, 0xb9, 0xad, 0x0f, 0x88, 0x14, 0x61, 0xac,
2413 bool result
= false;
2414 CFDataRef leaf_fingerprint
= SecCertificateCopySHA256Digest(leaf
);
2415 const uint8_t *dp
= CFDataGetBytePtr(leaf_fingerprint
);
2416 if (dp
&& !memcmp(basejumper_hash
, dp
, CC_SHA256_DIGEST_LENGTH
)) {
2419 CFReleaseNull(leaf_fingerprint
);
2423 static void SecPolicyCheckSystemTrustedCTRequired(SecPVCRef pvc
) {
2424 SecCertificateSourceRef appleAnchorSource
= NULL
;
2425 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
2426 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2427 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2429 /* Skip this check if we haven't done the CT checks yet */
2430 require_quiet(SecCertificatePathVCIsPathValidated(path
), out
);
2432 /* We only enforce this check when all of the following are true:
2433 * 0. Kill Switch not enabled */
2434 require_quiet(!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
), out
);
2436 /* 1. Not a pinning policy */
2437 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2438 require_quiet(CFEqualSafe(SecPolicyGetName(policy
),kSecPolicyNameSSLServer
), out
);
2440 /* 2. Device has checked in to MobileAsset for a current log list within the last 60 days.
2441 * Or the caller passed in the trusted log list. */
2442 require_quiet(SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
, out
);
2444 /* 3. Leaf issuance date is on or after 16 Oct 2018 at 00:00:00 AM UTC and not expired. */
2445 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2446 require_quiet(SecCertificateNotValidBefore(leaf
) >= 561340800.0 &&
2447 SecCertificateIsValid(leaf
, SecPVCGetVerifyTime(pvc
)), out
);
2449 /* 4. Chain is anchored with root in the system anchor source but not the Apple anchor source
2450 * with certain excepted CAs and configurable included CAs. */
2451 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2452 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2453 appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(false));
2454 require_quiet(SecPathBuilderIsAnchored(pvc
->builder
), out
);
2455 require_quiet((SecCertificateSourceContains(kSecSystemAnchorSource
, root
) &&
2456 appleAnchorSource
&& !SecCertificateSourceContains(appleAnchorSource
, root
) &&
2457 !is_ct_allowlisted_cert(leaf
)) ||
2458 is_configured_test_system_root(root
, CFSTR("TestCTRequiredSystemRoot")), out
);
2460 if (!SecCertificatePathVCIsCT(path
) && !is_ct_excepted(pvc
)) {
2461 /* Set failure. By not using the Forced variant, we implicitly check that this
2462 * policy had this options set. */
2463 SecPVCSetResult(pvc
, kSecPolicyCheckSystemTrustedCTRequired
, 0, kCFBooleanFalse
);
2467 CFReleaseNull(trustedLogs
);
2468 CFReleaseNull(otaref
);
2469 if (appleAnchorSource
) {
2470 SecMemoryCertificateSourceDestroy(appleAnchorSource
);
2474 static bool SecPolicyCheckSystemTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
2475 CFAbsoluteTime jul2016
= 489024000.0; // 1 July 2016 00:00:00 UTC
2476 CFAbsoluteTime mar2018
= 541555200.0; // 1 March 2018 00:00:00 UTC
2477 CFAbsoluteTime sep2020
= 620611200.0; // 1 September 2020 00:00:00 UTC
2478 if (notBefore
< jul2016
) {
2479 /* Validity Period no greater than 60 months.
2480 60 months is no more than 5 years and 2 leap days (and 1 hour slip). */
2481 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2) + 3600;
2482 if (notAfter
- notBefore
> maxPeriod
) {
2483 secnotice("policy", "System-trusted leaf validity period is more than 60 months");
2486 } else if (notBefore
< mar2018
) {
2487 /* Validity Period no greater than 39 months.
2488 39 months is no more than 3 years, 2 31-day months,
2489 1 30-day month, and 1 leap day (and 1 hour slip) */
2490 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1) + 3600;
2491 if (notAfter
- notBefore
> maxPeriod
) {
2492 secnotice("policy", "System-trusted leaf validity period longer than 39 months and issued after 30 June 2016");
2495 } else if (notBefore
< sep2020
) {
2496 /* Validity Period no greater than 825 days (and 1 hour slip). */
2497 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
2498 if (notAfter
- notBefore
> maxPeriod
) {
2499 secnotice("policy", "System-trusted leaf validity period longer than 825 days and issued on or after 1 March 2018");
2503 /* Validity Period no greater than 398 days (and no slip). HT211025 */
2504 CFAbsoluteTime maxPeriod
= 60*60*24*398;
2505 if (notAfter
- notBefore
> maxPeriod
) {
2506 secnotice("policy", "System-trusted leaf validity period longer than 398 days and issued on or after 1 September 2020");
2513 static bool SecPolicyCheckOtherTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
2514 /* Check whether we will enforce the validity period maximum for a non-system trusted leaf. */
2515 if (SecIsInternalRelease()) {
2516 if (CFPreferencesGetAppBooleanValue(CFSTR("IgnoreMaximumValidityPeriod"),
2517 CFSTR("com.apple.security"), NULL
)) {
2521 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
2522 if (notBefore
> jul2019
) {
2523 /* Validity Period no greater than 825 days (and 1 hour slip). */
2524 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
2525 if (notAfter
- notBefore
> maxPeriod
) {
2526 secnotice("policy", "Non-system-trusted leaf validity period longer than 825 days and issued on or after 1 July 2019");
2533 static void SecPolicyCheckValidityPeriodMaximums(SecPVCRef pvc
, CFStringRef key
) {
2534 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2535 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(leaf
);
2536 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
2538 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2539 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2540 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
2541 if (!SecPolicyCheckSystemTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
2542 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2547 /* Don't check validity periods against maximums for user-anchored leafs */
2548 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, leaf
)) {
2552 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, leaf
)) {
2557 /* all other trust */
2558 if (!SecPolicyCheckOtherTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
2559 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2563 static void SecPolicyCheckServerAuthEKU(SecPVCRef pvc
, CFStringRef key
) {
2564 /* The ExtendedKeyUsage check will verify a looser version of this check for all TLS server certs.
2565 * Here we want to be stricter (enforcing that there is an EKU extension and that it contains the
2566 * one true Server Auth EKU OID) for system and app anchors */
2567 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2568 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2569 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2570 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
2571 /* all system-anchored chains must be compliant */
2572 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
2573 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2575 /* if any subCA cert has an EKU, it must have the server auth EKU */
2576 if (count
> 2) { // chain has subCAs
2577 for (int ix
= 1; ix
< count
- 1; ix
++) { // iterate through subCAs
2578 SecCertificateRef subCA
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2579 CFArrayRef eku
= NULL
;
2580 if ((eku
= SecCertificateCopyExtendedKeyUsage(subCA
)) && CFArrayGetCount(eku
)) { // subCA has EKU set
2581 if (!SecPolicyCheckCertExtendedKeyUsage(subCA
, CFSTR("1.3.6.1.5.5.7.3.1")) && // check server auth EKU
2582 !SecPolicyCheckCertExtendedKeyUsage(subCA
, CFSTR("2.5.29.37.0"))) { // check anyEKU
2583 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2592 /* skip user/admin-anchored chains */
2593 if (SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, root
)) {
2597 if (SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, root
)) {
2603 /* All other anchor types must be compliant if issued on or after 1 July 2019 */
2604 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
2605 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
2606 if (notBefore
> jul2019
) {
2607 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
2608 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2613 static void SecPolicyCheckCTRequired(SecPVCRef pvc
, CFStringRef key
) {
2614 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2615 SecCertificatePathVCSetRequiresCT(path
, kSecPathCTRequiredOverridable
);
2618 static void SecPolicyCheckNotCA(SecPVCRef pvc
, CFStringRef key
) {
2619 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2620 if (SecCertificateIsCA(leaf
)) {
2621 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2625 void SecPolicyServerInitialize(void) {
2626 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2627 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2628 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2629 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2631 #undef POLICYCHECKMACRO
2632 #define __PC_ADD_CHECK_(NAME)
2633 #define __PC_ADD_CHECK_L(NAME) CFDictionaryAddValue(gSecPolicyLeafCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2634 #define __PC_ADD_CHECK_A(NAME) CFDictionaryAddValue(gSecPolicyPathCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2636 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2637 __PC_ADD_CHECK_##LEAFCHECK(NAME) \
2638 __PC_ADD_CHECK_##PATHCHECK(NAME)
2639 #include "OSX/sec/Security/SecPolicyChecks.list"
2641 /* Some of these don't follow the naming conventions but are in the Pinning DB.
2642 * <rdar://34537018> fix policy check constant values */
2643 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkerOid"), SecPolicyCheckLeafMarkerOid
);
2644 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkersProdAndQA"), SecPolicyCheckLeafMarkersProdAndQA
);
2645 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateMarkerOid"), SecPolicyCheckIntermediateMarkerOid
);
2646 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateCountry"), SecPolicyCheckIntermediateCountry
);
2647 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateOrganization"), SecPolicyCheckIntermediateOrganization
);
2652 /********************************************************
2653 ****************** SecPVCRef Functions *****************
2654 ********************************************************/
2656 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
) {
2657 secdebug("alloc", "pvc %p", pvc
);
2658 // Weird logging policies crashes.
2659 //secdebug("policy", "%@", policies);
2661 // Zero the pvc struct so only non-zero fields need to be explicitly set
2662 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
2663 pvc
->builder
= builder
;
2664 pvc
->policies
= policies
;
2667 pvc
->result
= kSecTrustResultUnspecified
;
2669 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2670 &kCFTypeDictionaryKeyCallBacks
,
2671 &kCFTypeDictionaryValueCallBacks
);
2672 pvc
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
,
2673 1, &kCFTypeArrayCallBacks
);
2674 CFRelease(certDetail
);
2677 void SecPVCDelete(SecPVCRef pvc
) {
2678 secdebug("alloc", "delete pvc %p", pvc
);
2679 CFReleaseNull(pvc
->policies
);
2680 CFReleaseNull(pvc
->details
);
2681 CFReleaseNull(pvc
->leafDetails
);
2684 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2685 secdebug("policy", "%@", path
);
2687 pvc
->result
= kSecTrustResultUnspecified
;
2688 CFReleaseNull(pvc
->details
);
2691 void SecPVCComputeDetails(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2694 /* Since we don't run the LeafChecks again, we need to preserve the
2695 * result the leaf had. */
2696 CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(path
);
2697 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
2698 pathLength
, pvc
->leafDetails
);
2699 for (ix
= 1; ix
< pathLength
; ++ix
) {
2700 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2701 &kCFTypeDictionaryKeyCallBacks
,
2702 &kCFTypeDictionaryValueCallBacks
);
2703 CFArrayAppendValue(details
, certDetail
);
2704 CFRelease(certDetail
);
2706 CFRetainAssign(pvc
->details
, details
);
2707 pvc
->result
= pvc
->leafResult
;
2708 CFReleaseSafe(details
);
2711 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
2712 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
2715 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
2716 return SecPathBuilderGetCertificateCount(pvc
->builder
);
2719 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
2720 return SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2723 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
2724 return SecPathBuilderGetVerifyTime(pvc
->builder
);
2727 static bool SecPVCIsExceptedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
, CFTypeRef value
) {
2728 CFArrayRef exceptions
= SecPathBuilderGetExceptions(pvc
->builder
);
2729 if (!exceptions
) { return false; }
2730 CFIndex exceptionsCount
= CFArrayGetCount(exceptions
);
2732 /* There are two types of exceptions:
2733 * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
2734 * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
2735 * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
2738 CFDictionaryRef options
= CFArrayGetValueAtIndex(exceptions
, 0);
2739 if (!isDictionary(options
)) {
2743 if (exceptionsCount
== 1 && (ix
> 0 || !CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
))) {
2744 /* SHA1Digest not allowed */
2745 if (CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
)) { return false; }
2747 if (CFDictionaryContainsKey(options
, key
)) {
2748 /* Special case -- AnchorTrusted only for self-signed certs */
2749 if (CFEqual(kSecPolicyCheckAnchorTrusted
, key
)) {
2750 Boolean isSelfSigned
= false;
2751 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2752 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2757 } else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
) && CFDictionaryContainsKey(options
, kSecPolicyCheckValidRoot
)) {
2758 /* Another special case - ValidRoot excepts Valid only for self-signed certs */
2759 Boolean isSelfSigned
= false;
2760 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2761 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2770 if (ix
>= exceptionsCount
) { return false; }
2771 CFDictionaryRef exception
= CFArrayGetValueAtIndex(exceptions
, ix
);
2772 if (!isDictionary(exception
)) {
2776 /* Compare the cert hash */
2777 if (!CFDictionaryContainsKey(exception
, kSecCertificateDetailSHA1Digest
)) { return false; }
2778 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2779 if (!CFEqual(SecCertificateGetSHA1Digest(cert
), CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
))) {
2784 CFTypeRef exceptionValue
= CFDictionaryGetValue(exception
, key
);
2785 if (exceptionValue
&& CFEqual(value
, exceptionValue
)) {
2786 /* Only change result if PVC is already ok */
2787 if (SecPVCIsOkResult(pvc
)) {
2788 // Chains that pass due to exceptions get Proceed result.
2789 pvc
->result
= kSecTrustResultProceed
;
2797 static int32_t detailKeyToCssmErr(CFStringRef key
) {
2800 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
2801 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
2803 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
2804 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
2806 else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
2807 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
2813 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
2815 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
2816 bool result
= false;
2817 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2818 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, ix
);
2819 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2820 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2822 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2823 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2824 CFNumberRef allowedErrorNumber
= NULL
;
2825 if (!isDictionary(constraint
)) {
2828 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
2829 int32_t allowedErrorValue
= 0;
2830 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
2834 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
2835 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
2844 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
2845 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2846 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2847 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2848 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2849 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2850 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2851 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2852 if (!isDictionary(constraint
)) {
2856 CFDictionaryRef policyOptions
= NULL
;
2857 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2858 if (policyOptions
&& isDictionary(policyOptions
) &&
2859 CFDictionaryContainsKey(policyOptions
, key
)) {
2867 static SecTrustResultType
trust_result_for_key(CFStringRef key
) {
2868 SecTrustResultType result
= kSecTrustResultRecoverableTrustFailure
;
2869 #undef POLICYCHECKMACRO
2870 #define __PC_TYPE_MEMBER_ false
2871 #define __PC_TYPE_MEMBER_R false
2872 #define __PC_TYPE_MEMBER_F true
2873 #define __PC_TYPE_MEMBER_D true
2875 #define __TRUSTRESULT_ kSecTrustResultRecoverableTrustFailure
2876 #define __TRUSTRESULT_F kSecTrustResultFatalTrustFailure
2877 #define __TRUSTRESULT_D kSecTrustResultDeny
2878 #define __TRUSTRESULT_R kSecTrustResultRecoverableTrustFailure
2880 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2881 if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
2882 result = __TRUSTRESULT_##TRUSTRESULT; \
2884 #include "OSX/sec/Security/SecPolicyChecks.list"
2889 /* AUDIT[securityd](done):
2890 policy->_options is a caller provided dictionary, only its cf type has
2893 bool SecPVCSetResultForcedWithTrustResult(SecPVCRef pvc
, CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
,
2894 SecTrustResultType overrideDefaultTR
) {
2896 /* If this is not something the current policy cares about ignore
2897 this error and return true so our caller continues evaluation. */
2899 /* Either the policy or the usage constraints have to have this key */
2900 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2901 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
2902 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
2907 /* Get the default trust result for this key and override it if the caller needs to
2908 * set a different trust result than the default. */
2909 SecTrustResultType trustResult
= trust_result_for_key(key
);
2910 if (overrideDefaultTR
!= kSecTrustResultInvalid
) {
2911 trustResult
= overrideDefaultTR
;
2914 /* only recoverable errors can be allowed/excepted */
2915 if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
2916 /* Check to see if the SecTrustSettings for the certificate in question
2917 tell us to ignore this error. */
2918 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
2919 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
2923 /* Check to see if exceptions tells us to ignore this error. */
2924 if (SecPVCIsExceptedError(pvc
, ix
, key
, result
)) {
2925 secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix
, key
);
2930 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
2931 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
2932 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
2934 (force
? "force" : ""), result
);
2936 /* Avoid resetting deny or fatal to recoverable */
2937 if (SecPVCIsOkResult(pvc
) || trustResult
== kSecTrustResultFatalTrustFailure
) {
2938 pvc
->result
= trustResult
;
2939 } else if (trustResult
== kSecTrustResultDeny
&&
2940 pvc
->result
== kSecTrustResultRecoverableTrustFailure
) {
2941 pvc
->result
= trustResult
;
2947 CFMutableDictionaryRef detail
=
2948 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
2950 secerror("SecPVCSetResultForced: failed to get detail at index %ld (array length %ld)",
2951 ix
, CFArrayGetCount(pvc
->details
));
2955 /* Perhaps detail should have an array of results per key? As it stands
2956 in the case of multiple policy failures the last failure stands. */
2957 CFDictionarySetValue(detail
, key
, result
);
2962 bool SecPVCSetResultForced(SecPVCRef pvc
, CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
2963 return SecPVCSetResultForcedWithTrustResult(pvc
, key
, ix
, result
, force
, kSecTrustResultInvalid
);
2966 bool SecPVCSetResult(SecPVCRef pvc
,
2967 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
2968 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
2971 /* AUDIT[securityd](done):
2972 key(ok) is a caller provided.
2973 value(ok, unused) is a caller provided.
2975 static void SecPVCValidateKey(const void *key
, const void *value
,
2977 SecPVCRef pvc
= (SecPVCRef
)context
;
2979 /* If our caller doesn't want full details and we failed earlier there is
2980 no point in doing additional checks. */
2981 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
2984 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
2985 CFDictionaryGetValue(pvc
->callbacks
, key
);
2988 /* "Optional" policy checks. This may be a new key from the
2989 * pinning DB which is not implemented in this OS version. Log a
2990 * warning, and on debug builds fail evaluation, to encourage us
2991 * to ensure that checks are synchronized across the same build. */
2992 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
2993 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
2994 secwarning("policy: unknown policy key %@, skipping", key
);
2996 pvc
->result
= kSecTrustResultOtherError
;
2999 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
3000 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
3001 secwarning("policy: unknown policy key %@, skipping", key
);
3003 pvc
->result
= kSecTrustResultOtherError
;
3007 /* Non standard validation phase, nothing is optional. */
3008 pvc
->result
= kSecTrustResultOtherError
;
3013 fcn(pvc
, (CFStringRef
)key
);
3016 /* AUDIT[securityd](done):
3017 policy->_options is a caller provided dictionary, only its cf type has
3020 SecTrustResultType
SecPVCLeafChecks(SecPVCRef pvc
) {
3021 /* We need to compute details for the leaf. */
3022 CFRetainAssign(pvc
->details
, pvc
->leafDetails
);
3024 CFArrayRef policies
= pvc
->policies
;
3025 CFIndex ix
, count
= CFArrayGetCount(policies
);
3026 for (ix
= 0; ix
< count
; ++ix
) {
3027 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3029 /* Validate all keys for all policies. */
3030 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3031 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3034 pvc
->leafResult
= pvc
->result
;
3035 CFRetainAssign(pvc
->leafDetails
, pvc
->details
);
3040 bool SecPVCIsOkResult(SecPVCRef pvc
) {
3041 if (pvc
->result
== kSecTrustResultRecoverableTrustFailure
||
3042 pvc
->result
== kSecTrustResultDeny
||
3043 pvc
->result
== kSecTrustResultFatalTrustFailure
||
3044 pvc
->result
== kSecTrustResultOtherError
) {
3050 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
3051 /* Check stuff common to intermediate and anchors. */
3052 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
3053 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3054 CFIndex anchor_ix
= SecPVCGetCertificateCount(pvc
) - 1;
3056 if (!SecCertificateIsValid(cert
, verifyTime
)) {
3057 /* Certificate has expired. */
3058 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, ix
, kCFBooleanFalse
)) {
3063 if (SecCertificateIsWeakKey(cert
)) {
3064 /* Certificate uses weak key. */
3065 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, ix
, kCFBooleanFalse
)) {
3070 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
3071 /* Certificate uses weak hash. */
3072 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, ix
, kCFBooleanFalse
)) {
3077 /* (k) Basic constraints only relevant for v3 and later. */
3078 if (SecCertificateVersion(cert
) >= 3) {
3079 const SecCEBasicConstraints
*bc
=
3080 SecCertificateGetBasicConstraints(cert
);
3082 /* Basic constraints not present, illegal. */
3083 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3084 ix
, kCFBooleanFalse
, true)) {
3087 } else if (!bc
->isCA
) {
3088 /* Basic constraints not marked as isCA, illegal. */
3089 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsCA
,
3090 ix
, kCFBooleanFalse
, true)) {
3095 /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
3096 not an anchor), we additionally require that the certificate chain
3097 does not end in a v3 or later anchor. [rdar://32204517] */
3098 else if (ix
> 0 && ix
< anchor_ix
) {
3099 SecCertificateRef anchor
= SecPVCGetCertificateAtIndex(pvc
, anchor_ix
);
3100 if (SecCertificateVersion(anchor
) >= 3) {
3101 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3102 ix
, kCFBooleanFalse
, true)) {
3107 /* (l) max_path_length is checked elsewhere. */
3109 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3110 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3111 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3112 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3113 ix
, kCFBooleanFalse
, true)) {
3119 return SecPVCIsOkResult(pvc
);
3122 static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3123 /* Check stuff common to intermediate and anchors. */
3125 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3126 if (NULL
!= otapkiRef
)
3128 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3129 CFRelease(otapkiRef
);
3130 if (NULL
!= blackListedKeys
)
3132 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3133 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3134 bool is_last
= (ix
== count
- 1);
3135 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
3137 /* Check for blacklisted intermediate issuer keys. */
3138 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3140 /* Check dgst against blacklist. */
3141 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3142 /* Check allow list for this blacklisted issuer key,
3143 which is the authority key of the issued cert at ix-1.
3145 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3146 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
3148 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3149 ix
, kCFBooleanFalse
, true);
3155 CFRelease(blackListedKeys
);
3156 return SecPVCIsOkResult(pvc
);
3163 static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3165 /* Check stuff common to intermediate and anchors. */
3166 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3167 if (NULL
!= otapkiRef
)
3169 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3170 CFRelease(otapkiRef
);
3171 if (NULL
!= grayListKeys
)
3173 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3174 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3175 bool is_last
= (ix
== count
- 1);
3176 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
3178 /* Check for gray listed intermediate issuer keys. */
3179 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3181 /* Check dgst against gray list. */
3182 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3183 /* Check allow list for this graylisted issuer key,
3184 which is the authority key of the issued cert at ix-1.
3186 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3187 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
3189 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3190 ix
, kCFBooleanFalse
, true);
3196 CFRelease(grayListKeys
);
3197 return SecPVCIsOkResult(pvc
);
3204 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
3205 if (!isString(searchName
) && !isString(searchOid
)) {
3208 CFArrayRef policies
= pvc
->policies
;
3209 CFIndex ix
, count
= CFArrayGetCount(policies
);
3210 for (ix
= 0; ix
< count
; ++ix
) {
3211 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3212 CFStringRef policyName
= SecPolicyGetName(policy
);
3213 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
3214 /* Prefer a match of both name and OID */
3215 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
3216 if (CFEqual(searchOid
, policyOid
) &&
3217 CFEqual(searchName
, policyName
)) {
3218 if (policyIX
) { *policyIX
= ix
; }
3221 /* <rdar://40617139> sslServer trust settings need to apply to evals using policyName pinning
3222 * but make sure we don't use this for SSL Client trust settings or policies. */
3223 if (CFEqual(searchOid
, policyOid
) &&
3224 CFEqual(searchName
, kSecPolicyNameSSLServer
) && !CFEqual(policyName
, kSecPolicyNameSSLClient
)) {
3225 if (policyIX
) { *policyIX
= ix
; }
3229 /* Next best is just OID. */
3230 if (!searchName
&& searchOid
&& policyOid
) {
3231 if (CFEqual(searchOid
, policyOid
)) {
3232 if (policyIX
) { *policyIX
= ix
; }
3236 if (!searchOid
&& searchName
&& policyName
) {
3237 if (CFEqual(searchName
, policyName
)) {
3238 if (policyIX
) { *policyIX
= ix
; }
3246 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
3247 if (!isString(stringValue
)) {
3250 bool result
= false;
3252 /* <rdar://27754596> Previous versions of macOS null-terminated the string, so we need to strip it. */
3253 CFStringRef tmpStringValue
= NULL
;
3254 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
3255 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
3257 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
3259 /* Some users have strings that only contain the null-termination, so we need to check that we
3260 * still have a string. */
3261 require(tmpStringValue
, out
);
3263 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3264 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3265 /* Have to look for all the possible locations of name string */
3266 CFStringRef policyString
= NULL
;
3267 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
3268 if (!policyString
) {
3269 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
3271 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
3276 CFArrayRef policyStrings
= NULL
;
3277 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
3278 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
3279 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
3287 CFReleaseNull(tmpStringValue
);
3292 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
3293 uint32_t ourTSKeyUsage
= 0;
3294 uint32_t keyUsage
= 0;
3295 if (keyUsageNumber
&&
3296 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
3297 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
3298 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
3300 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
3301 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
3303 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
3304 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
3306 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
3307 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
3309 if (keyUsage
== kSecKeyUsageAll
) {
3310 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
3313 return ourTSKeyUsage
;
3316 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
3317 uint32_t ourTSKeyUsage
= 0;
3318 CFTypeRef policyKeyUsageType
= NULL
;
3320 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
3321 if (isArray(policyKeyUsageType
)) {
3322 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
3323 for (ix
= 0; ix
< count
; ix
++) {
3324 CFNumberRef policyKeyUsageNumber
= NULL
;
3325 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
3326 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
3328 } else if (isNumber(policyKeyUsageType
)) {
3329 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
3332 return ourTSKeyUsage
;
3335 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
3336 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
3337 int64_t keyUsageValue
= 0;
3338 uint32_t ourKeyUsage
= 0;
3340 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
3344 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
3348 /* We're using the key for revocation if we have the OCSPSigner policy.
3349 * @@@ If we support CRLs, we'd need to check for that policy here too.
3351 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
3352 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
3355 /* We're using the key for verifying a cert if it's a root/intermediate
3356 * in the chain. If the cert isn't in the path yet, we're about to add it,
3357 * so it's a root/intermediate. If there is no path, this is the leaf.
3359 CFIndex pathIndex
= -1;
3360 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3362 pathIndex
= SecCertificatePathVCGetIndexOfCertificate(path
, certificate
);
3366 if (pathIndex
!= 0) {
3367 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
3370 /* The rest of the key usages may be specified by the policy(ies). */
3371 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3372 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3373 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3375 /* Get key usage from ALL policies */
3376 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
3377 for (ix
= 0; ix
< count
; ix
++) {
3378 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
3379 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3383 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
3391 #include <Security/SecTrustedApplicationPriv.h>
3392 #include <Security/SecTask.h>
3393 #include <Security/SecTaskPriv.h>
3394 #include <bsm/libbsm.h>
3395 #include <libproc.h>
3397 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
3399 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
3400 bool result
= false;
3401 audit_token_t auditToken
= {};
3402 SecTaskRef task
= NULL
;
3403 SecRequirementRef requirement
= NULL
;
3404 CFStringRef stringRequirement
= NULL
;
3406 require_quiet(appRef
&& clientAuditToken
, out
);
3407 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
3408 require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef
)appRef
, &requirement
), out
);
3409 require(requirement
, out
);
3410 require_noerr(SecRequirementsCopyString(requirement
, kSecCSDefaultFlags
, &stringRequirement
), out
);
3411 require(stringRequirement
, out
);
3413 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3414 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3415 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3417 if(errSecSuccess
== SecTaskValidateForRequirement(task
, stringRequirement
)) {
3422 CFReleaseNull(task
);
3423 CFReleaseNull(requirement
);
3424 CFReleaseNull(stringRequirement
);
3429 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
3430 if (!isDictionary(options
)) {
3435 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
3437 /* We need to run the leaf and path checks using these options. */
3438 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3439 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3441 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3442 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3445 pvc
->callbacks
= currentCallbacks
;
3447 /* Our work here is done; no need to claim a match */
3451 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
3452 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
3453 CFNumberRef keyUsageNumber
= NULL
;
3454 CFTypeRef trustedApplicationData
= NULL
;
3455 CFDictionaryRef policyOptions
= NULL
;
3457 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
3458 keyUsageMatch
= false, policyOptionMatch
= false;
3459 bool result
= false;
3462 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
3463 SecPolicyRef policy
= NULL
;
3464 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3465 policyOid
= (policy
) ? policy
->_oid
: NULL
;
3467 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3469 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
3470 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
3471 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
3472 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
3474 CFIndex policyIX
= -1;
3475 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
3476 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
3477 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
3478 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
3481 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
3482 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
3483 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
3484 CFReleaseNull(clientAuditToken
);
3486 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
3487 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
3491 /* If we either didn't find the parameter in the dictionary or we got a match
3492 * against that parameter, for all possible parameters in the dictionary, then
3493 * this trust setting result applies to the output. */
3494 if (((!policyOid
&& !policyName
) || policyMatch
) &&
3495 (!policyString
|| policyStringMatch
) &&
3496 (!trustedApplicationData
|| applicationMatch
) &&
3497 (!keyUsageNumber
|| keyUsageMatch
) &&
3498 (!policyOptions
|| policyOptionMatch
)) {
3505 static SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
3506 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
3507 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3508 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3509 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3510 if (!isDictionary(constraint
)) {
3514 CFNumberRef resultNumber
= NULL
;
3515 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
3516 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
3517 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
3518 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
3519 resultValue
= kSecTrustSettingsResultTrustRoot
;
3522 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
3523 result
= resultValue
;
3530 /* This function assumes that the input source is an anchor source */
3531 bool SecPVCIsAnchorPerConstraints(SecPVCRef pvc
, SecCertificateSourceRef source
, SecCertificateRef certificate
) {
3532 __block
bool result
= false;
3533 CFArrayRef constraints
= NULL
;
3534 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
3536 /* Unrestricted certificates:
3537 * -those that come from anchor sources with no constraints
3538 * -self-signed certificates with empty contraints arrays
3540 Boolean selfSigned
= false;
3541 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
3542 if ((NULL
== source
->copyUsageConstraints
) ||
3543 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
3544 secinfo("trust", "unrestricted anchor%s",
3545 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
3550 /* Get the trust settings result for the PVC. Only one PVC need match to
3551 * trigger the anchor behavior -- policy validation will handle whether the
3552 * path is truly anchored for that PVC. */
3553 require_quiet(constraints
, out
);
3554 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
3555 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
3558 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
3559 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
3560 // For our purposes, this is an anchor.
3561 secinfo("trust", "complex trust settings anchor");
3565 if (settingsResult
== kSecTrustSettingsResultDeny
) {
3566 /* We consider denied certs "anchors" because the trust decision
3567 is set regardless of building the chain further. The policy
3568 validation will handle rejecting this chain. */
3569 secinfo("trust", "complex trust settings denied anchor");
3574 CFReleaseNull(constraints
);
3578 static void SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
3579 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3580 for (certIX
= 0; certIX
< certCount
; certIX
++) {
3581 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3582 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
3583 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3584 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
3586 /* Set the pvc trust result based on the usage constraints and anchor source. */
3587 if (result
== kSecTrustSettingsResultDeny
) {
3588 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
3589 } else if ((result
== kSecTrustSettingsResultTrustRoot
|| result
== kSecTrustSettingsResultTrustAsRoot
||
3590 result
== kSecTrustSettingsResultInvalid
) && SecPVCIsOkResult(pvc
)) {
3591 /* If we already think the PVC is ok and this cert is from one of the user/
3592 * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
3593 * all mean we should use the special "Proceed" trust result. */
3594 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecUserAnchorSource
) &&
3595 SecCertificateSourceContains(kSecUserAnchorSource
, cert
)) {
3596 pvc
->result
= kSecTrustResultProceed
;
3599 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecLegacyAnchorSource
) &&
3600 SecCertificateSourceContains(kSecLegacyAnchorSource
, cert
)) {
3601 pvc
->result
= kSecTrustResultProceed
;
3608 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
3609 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
3610 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
3612 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
3613 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
3614 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
3616 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
3617 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
3618 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
3620 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
3621 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
3622 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
3624 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
3625 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
3626 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
3628 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
3629 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
3630 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
3632 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
3633 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
3634 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
3636 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
3637 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
3638 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
3641 static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
3642 static CFSetRef sConstrainedRoots
= NULL
;
3643 static dispatch_once_t _t
;
3644 dispatch_once(&_t
, ^{
3645 const UInt8
*v_hashes
[] = {
3646 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
3647 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
3649 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3650 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
3651 for (ix
=0; ix
<count
; ix
++) {
3652 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
3653 kSecPolicySHA256Size
, kCFAllocatorNull
);
3655 CFSetAddValue(set
, hash
);
3659 sConstrainedRoots
= set
;
3662 bool shouldDeny
= false;
3663 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3664 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
3665 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3666 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
3667 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
3668 /* matched a constrained root; check notBefore dates on all its children. */
3669 CFIndex childIX
= certIX
;
3670 while (--childIX
>= 0) {
3671 SecCertificateRef child
= SecPVCGetCertificateAtIndex(pvc
, childIX
);
3672 /* 1 Dec 2016 00:00:00 GMT */
3673 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
3674 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
3680 CFReleaseNull(sha256
);
3684 static bool SecPVCPolicyPermitsCTRequired(SecPVCRef pvc
) {
3685 #if TARGET_OS_BRIDGE
3687 #else // !TARGET_OS_BRIDGE
3688 if (!pvc
|| !pvc
->policies
) {
3691 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
3696 CFStringRef policyName
= SecPolicyGetName(policy
);
3697 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
3700 // SSL policy by another name
3701 CFDictionaryRef options
= policy
->_options
;
3702 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckSSLHostname
)) {
3705 // Policy explicitly requires CT
3706 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckCTRequired
)) {
3710 #endif // !TARGET_OS_BRIDGE
3714 1. SecPVCCheckRequireCTConstraints must be called after SecPolicyCheckCT,
3715 so earliest issuance time has already been obtained from SCTs.
3716 2. If the issuance time value is 0 (i.e. 2001-01-01) or earlier, we
3717 assume it was not set, and thus we did not have CT info.
3719 static void SecPVCCheckRequireCTConstraints(SecPVCRef pvc
) {
3720 SecCertificatePathVCRef path
= (pvc
) ? SecPathBuilderGetPath(pvc
->builder
) : NULL
;
3724 /* If we are evaluating for a SSL server authentication policy, make sure
3725 SCT issuance time is prior to the earliest not-after date constraint.
3726 Note that CT will already be required if there is a not-after date
3727 constraint present (set in SecRVCProcessValidDateConstraints).
3729 if (SecPVCPolicyPermitsCTRequired(pvc
)) {
3730 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3731 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, 0);
3732 CFAbsoluteTime earliestNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
3733 CFAbsoluteTime issuanceTime
= SecCertificatePathVCIssuanceTime(path
);
3734 if (issuanceTime
<= 0) {
3735 /* if not set (or prior to 2001-01-01), use leaf's not-before time. */
3736 issuanceTime
= SecCertificateNotValidBefore(certificate
);
3738 for (ix
= 0; ix
< certCount
; ix
++) {
3739 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3740 if (!rvc
|| !rvc
->valid_info
|| !rvc
->valid_info
->hasDateConstraints
|| !rvc
->valid_info
->notAfterDate
) {
3743 /* Found CA certificate with a not-after date constraint. */
3744 CFAbsoluteTime caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
3745 if (caNotAfter
< earliestNotAfter
) {
3746 earliestNotAfter
= caNotAfter
;
3748 if (issuanceTime
> earliestNotAfter
) {
3749 /* Issuance time violates the not-after date constraint. */
3750 secnotice("rvc", "certificate issuance time (%f) is later than allowed value (%f)",
3751 issuanceTime
, earliestNotAfter
);
3752 SecRVCSetValidDeterminedErrorResult(rvc
);
3757 /* If path is CT validated, nothing further to do here. */
3758 if (SecCertificatePathVCIsCT(path
)) {
3762 /* Path is not CT validated, so check if CT was required. */
3763 SecPathCTPolicy ctp
= SecCertificatePathVCRequiresCT(path
);
3764 if (ctp
<= kSecPathCTNotRequired
|| !SecPVCPolicyPermitsCTRequired(pvc
)) {
3768 /* We need to have a recent log list or the CT check may have failed due to the list being out of date.
3769 * Also, honor the CT kill switch. */
3770 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3771 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
3772 if (!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) &&
3773 (SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
)) {
3774 /* CT was required. Error is always set on leaf certificate. */
3775 if (ctp
!= kSecPathCTRequiredOverridable
) {
3776 /* Normally kSecPolicyCheckCTRequired is recoverable */
3777 SecPVCSetResultForcedWithTrustResult(pvc
, kSecPolicyCheckCTRequired
, 0, kCFBooleanFalse
, true,
3778 kSecTrustResultFatalTrustFailure
);
3780 SecPVCSetResultForced(pvc
, kSecPolicyCheckCTRequired
, 0, kCFBooleanFalse
, true);
3783 CFReleaseNull(otaref
);
3784 CFReleaseNull(trustedLogs
);
3787 /* "Deep" copy the details array */
3788 static CFArrayRef CF_RETURNS_RETAINED
SecPVCCopyDetailsArray(SecPVCRef pvc
) {
3789 CFArrayRef details
= pvc
->details
;
3790 CFMutableArrayRef copiedDetails
= CFArrayCreateMutable(NULL
, CFArrayGetCount(details
), &kCFTypeArrayCallBacks
);
3791 CFArrayForEach(details
, ^(const void *value
) {
3792 CFMutableDictionaryRef copiedValue
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
3793 CFArrayAppendValue(copiedDetails
, copiedValue
);
3794 CFReleaseNull(copiedValue
);
3796 return copiedDetails
;
3799 /* AUDIT[securityd](done):
3800 policy->_options is a caller provided dictionary, only its cf type has
3803 void SecPVCPathChecks(SecPVCRef pvc
) {
3804 secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc
->builder
));
3805 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3806 /* This needs to be initialized before we call any function that might call
3807 SecPVCSetResultForced(). */
3809 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3810 if (SecPVCIsOkResult(pvc
) || pvc
->details
) {
3811 SecPolicyCheckBasicCertificateProcessing(pvc
,
3812 kSecPolicyCheckBasicCertificateProcessing
);
3815 CFArrayRef policies
= pvc
->policies
;
3816 CFIndex count
= CFArrayGetCount(policies
);
3817 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3818 /* Validate all keys for all policies. */
3819 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3820 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3821 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3822 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
3829 /* Check whether the TrustSettings say to deny a cert in the path. */
3830 SecPVCCheckUsageConstraints(pvc
);
3832 /* Check for Blocklisted certs */
3833 SecPVCCheckIssuerDateConstraints(pvc
);
3835 count
= SecCertificatePathVCGetCount(path
);
3836 for (ix
= 1; ix
< count
; ix
++) {
3837 SecPVCGrayListedKeyChecks(pvc
, ix
);
3838 SecPVCBlackListedKeyChecks(pvc
, ix
);
3841 /* Path-based check tests. */
3842 if (!SecCertificatePathVCIsPathValidated(path
)) {
3843 bool ev_check_ok
= false;
3844 if (SecCertificatePathVCIsOptionallyEV(path
)) {
3845 SecTrustResultType pre_ev_check_result
= pvc
->result
;
3846 CFArrayRef pre_ev_check_details
= pvc
->details
? SecPVCCopyDetailsArray(pvc
) : NULL
;
3847 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3848 ev_check_ok
= SecPVCIsOkResult(pvc
);
3849 /* If ev checking failed, we still want to accept this chain
3850 as a non EV one, if it was valid as such. */
3851 pvc
->result
= pre_ev_check_result
;
3852 CFAssignRetained(pvc
->details
, pre_ev_check_details
);
3856 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3857 SecPolicyCheckCT(pvc
);
3859 /* Certs are only EV if they are also CT verified (when the Kill Switch isn't enabled and against a recent log list) */
3860 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3861 if (ev_check_ok
&& (SecCertificatePathVCIsCT(path
) || SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) ||
3862 !SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
))) {
3863 SecCertificatePathVCSetIsEV(path
, true);
3865 CFReleaseNull(otaref
);
3868 /* Say that we did the expensive path checks (that we want to skip on the second call) */
3869 SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc
->builder
));
3871 /* Check that this path meets CT constraints. */
3872 SecPVCCheckRequireCTConstraints(pvc
);
3873 SecPolicyCheckSystemTrustedCTRequired(pvc
);
3875 /* Check that this path meets known-intermediate constraints. */
3876 SecPathBuilderCheckKnownIntermediateConstraints(pvc
->builder
);
3878 secdebug("policy", "end %strusted path: %@",
3879 (SecPVCIsOkResult(pvc
) ? "" : "not "), SecPathBuilderGetPath(pvc
->builder
));
3884 void SecPVCPathCheckRevocationResponsesReceived(SecPVCRef pvc
) {
3886 /* Since we don't currently allow networking on watchOS,
3887 * don't enforce the revocation-required check here. (32728029) */
3888 bool required
= false;
3890 bool required
= true;
3892 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3893 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3894 for (ix
= 0; ix
< certCount
; ix
++) {
3895 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3896 /* Do we have a valid revocation response? */
3897 if (SecRVCGetEarliestNextUpdate(rvc
) == NULL_TIME
) {
3898 /* No valid revocation response.
3899 * Do we require revocation (for that cert per the
3900 * SecCertificateVCRef, or per the pvc)? */
3901 if (required
&& (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path
, ix
) ||
3902 ((ix
== 0) && pvc
->require_revocation_response
))) {
3903 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
3904 ix
, kCFBooleanFalse
, true);
3906 /* Do we have a definitive Valid revocation result for this cert? */
3907 if (SecRVCHasDefinitiveValidInfo(rvc
) && SecRVCHasRevokedValidInfo(rvc
)) {
3908 SecRVCSetValidDeterminedErrorResult(rvc
);