2 * Copyright (c) 2008-2018 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 <securityd/SecPolicyServer.h>
29 #include <Security/SecPolicyInternal.h>
30 #include <Security/SecPolicyPriv.h>
31 #include <Security/SecTask.h>
32 #include <securityd/policytree.h>
33 #include <securityd/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 <securityd/SecTrustServer.h>
58 #include <securityd/SecTrustLoggingServer.h>
59 #include <securityd/SecRevocationServer.h>
60 #include <securityd/SecCertificateServer.h>
61 #include <securityd/SecCertificateSource.h>
62 #include <securityd/SecOCSPResponse.h>
63 #include <securityd/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 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
312 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
316 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
318 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
321 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
323 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
324 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
325 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
326 for (ix
= 0; ix
< count
; ++ix
) {
327 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
328 if (!SecPolicyCheckCertNonEmptySubject(cert
, pvcValue
)) {
329 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
335 /* AUDIT[securityd](done):
336 policy->_options is a caller provided dictionary, only its cf type has
339 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
341 /* @@@ Consider what to do if the caller passes in no hostname. Should
342 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
343 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
344 CFStringRef hostName
= (CFStringRef
)
345 CFDictionaryGetValue(policy
->_options
, key
);
346 if (!isString(hostName
)) {
347 /* @@@ We can't return an error here and making the evaluation fail
348 won't help much either. */
352 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
353 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
356 /* Hostname mismatch or no hostnames found in certificate. */
357 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
362 /* AUDIT[securityd](done):
363 policy->_options is a caller provided dictionary, only its cf type has
366 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
367 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
368 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
369 if (!isString(email
)) {
370 /* We can't return an error here and making the evaluation fail
371 won't help much either. */
375 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
377 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
378 /* Hostname mismatch or no hostnames found in certificate. */
379 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
383 static void SecPolicyCheckTemporalValidity(SecPVCRef pvc
,
385 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
386 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
387 for (ix
= 0; ix
< count
; ++ix
) {
388 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
389 if (!SecCertificateIsValid(cert
, verifyTime
)) {
390 /* Intermediate certificate has expired. */
391 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
397 /* AUDIT[securityd](done):
398 policy->_options is a caller provided dictionary, only its cf type has
401 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
403 CFIndex count
= SecPVCGetCertificateCount(pvc
);
405 /* Can't check intermediates common name if there is no intermediate. */
406 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
410 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
411 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
412 CFStringRef commonName
=
413 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
414 if (!isString(commonName
)) {
415 /* @@@ We can't return an error here and making the evaluation fail
416 won't help much either. */
419 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
420 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
424 /* AUDIT[securityd](done):
425 policy->_options is a caller provided dictionary, only its cf type has
428 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
430 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
431 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
432 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
434 if (!isString(common_name
)) {
435 /* @@@ We can't return an error here and making the evaluation fail
436 won't help much either. */
439 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
440 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
444 /* AUDIT[securityd](done):
445 policy->_options is a caller provided dictionary, only its cf type has
448 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
450 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
451 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
452 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
454 if (!isString(prefix
)) {
455 /* @@@ We can't return an error here and making the evaluation fail
456 won't help much either. */
459 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
460 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
464 /* AUDIT[securityd](done):
465 policy->_options is a caller provided dictionary, only its cf type has
468 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
470 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
471 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
472 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
474 if (!isString(common_name
)) {
475 /* @@@ We can't return an error here and making the evaluation fail
476 won't help much either. */
479 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
480 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
484 /* AUDIT[securityd](done):
485 policy->_options is a caller provided dictionary, only its cf type has
488 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
490 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
491 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
492 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
494 /* @@@ We can't return an error here and making the evaluation fail
495 won't help much either. */
498 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
499 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
504 /* AUDIT[securityd](done):
505 policy->_options is a caller provided dictionary, only its cf type has
508 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
510 CFIndex count
= SecPVCGetCertificateCount(pvc
);
511 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
512 CFNumberRef chainLength
=
513 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
515 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
516 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
517 /* @@@ We can't return an error here and making the evaluation fail
518 won't help much either. */
521 if (value
!= count
) {
522 /* Chain length doesn't match policy requirement. */
523 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
528 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
529 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
530 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
532 bool foundMatch
= false;
534 foundMatch
= CFEqual(digest
, value
);
535 else if (isArray(value
))
536 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
538 /* @@@ We only support Data and Array but we can't return an error here so.
539 we let the evaluation fail (not much help) and assert in debug. */
546 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
547 CFIndex count
= SecPVCGetCertificateCount(pvc
);
548 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
549 CFDataRef anchorSHA256
= NULL
;
550 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
552 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
553 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
556 CFReleaseNull(anchorSHA256
);
561 /* AUDIT[securityd](done):
562 policy->_options is a caller provided dictionary, only its cf type has
565 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
567 CFIndex count
= SecPVCGetCertificateCount(pvc
);
568 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
569 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
571 if (!isDigestInPolicy(pvc
, key
, anchorSHA1
))
572 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, count
-1, kCFBooleanFalse
))
579 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
580 policy->_options is a caller provided dictionary, only its cf type has
583 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
585 SecCertificateRef cert
= NULL
;
586 CFDataRef digest
= NULL
;
588 if (SecPVCGetCertificateCount(pvc
) < 2) {
589 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
593 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
594 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
596 if (!isDigestInPolicy(pvc
, key
, digest
)) {
597 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
599 CFReleaseNull(digest
);
603 policy->_options is a caller provided dictionary, only its cf type has
606 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
608 CFIndex count
= SecPVCGetCertificateCount(pvc
);
609 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
610 SecAppleTrustAnchorFlags flags
= 0;
613 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
616 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
623 /* AUDIT[securityd](done):
624 policy->_options is a caller provided dictionary, only its cf type has
627 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
629 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
630 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
631 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
633 if (!isString(org
)) {
634 /* @@@ We can't return an error here and making the evaluation fail
635 won't help much either. */
638 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
639 /* Leaf Subject Organization mismatch. */
640 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
644 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
646 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
647 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
648 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
650 if (!isString(orgUnit
)) {
651 /* @@@ We can't return an error here and making the evaluation fail
652 won't help much either. */
655 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
656 /* Leaf Subject Organization mismatch. */
657 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
661 /* AUDIT[securityd](done):
662 policy->_options is a caller provided dictionary, only its cf type has
665 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
667 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
668 CFArrayRef trustedServerNames
= (CFArrayRef
)
669 CFDictionaryGetValue(policy
->_options
, key
);
670 /* No names specified means we accept any name. */
671 if (!trustedServerNames
)
673 if (!isArray(trustedServerNames
)) {
674 /* @@@ We can't return an error here and making the evaluation fail
675 won't help much either. */
679 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
680 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
681 /* Hostname mismatch or no hostnames found in certificate. */
682 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
686 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
687 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
688 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
689 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
690 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
691 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
692 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
693 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
694 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
695 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
697 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
698 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
699 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
700 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
701 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
702 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
703 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
704 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
705 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
706 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
707 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
708 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
709 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
710 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
712 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
715 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
717 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
718 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
720 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
721 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
722 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
724 CFDataRef serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
726 CFIndex serial_length
= CFDataGetLength(serial
);
727 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
729 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
734 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
736 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
738 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
739 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
741 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
742 CFReleaseSafe(serial
);
751 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
752 if (NULL
!= otapkiRef
)
754 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
755 CFRelease(otapkiRef
);
756 if (NULL
!= blackListedKeys
)
758 /* Check for blacklisted intermediates keys. */
759 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
762 /* Check dgst against blacklist. */
763 if (CFSetContainsValue(blackListedKeys
, dgst
))
765 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
769 CFRelease(blackListedKeys
);
774 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
776 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
777 if (NULL
!= otapkiRef
)
779 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
780 CFRelease(otapkiRef
);
781 if (NULL
!= grayListedKeys
)
783 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
785 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
788 /* Check dgst against gray. */
789 if (CFSetContainsValue(grayListedKeys
, dgst
))
791 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
795 CFRelease(grayListedKeys
);
800 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
802 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
803 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
804 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
806 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
807 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
808 // Force because we may have gotten the legacy key instead of the new key in the policy
809 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkerOid
, 0, kCFBooleanFalse
, true);
813 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
815 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
816 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
817 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
819 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
820 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
826 * The value is a dictionary. The dictionary contains keys indicating
827 * whether the value is for Prod or QA. The values are the same as
828 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
830 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
832 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
833 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
834 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
835 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
837 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
840 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
841 // Force because we may have gotten the legacy key instead of the new key in the policy
842 SecPVCSetResultForced(pvc
, kSecPolicyCheckLeafMarkersProdAndQA
, 0, kCFBooleanFalse
, true);
847 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
849 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
850 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
851 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
853 for (ix
= 1; ix
< count
- 1; ix
++) {
854 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
855 if (SecCertificateHasMarkerExtension(cert
, value
))
858 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
859 // Force because we may have gotten the legacy key instead of the new key in the policy
860 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateMarkerOid
, 0, kCFBooleanFalse
, true);
863 static void SecPolicyCheckIntermediateMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
865 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
866 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
867 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
869 for (ix
= 1; ix
< count
- 1; ix
++) {
870 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
871 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
872 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
877 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
879 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
880 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
881 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
883 for (ix
= 1; ix
< count
- 1; ix
++) {
884 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
885 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
886 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
891 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
893 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
894 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
895 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
897 for (ix
= 1; ix
< count
- 1; ix
++) {
898 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
899 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
900 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
901 // Force because we may have gotten the legacy key instead of the new key in the policy
902 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateOrganization
, ix
, kCFBooleanFalse
, true);
907 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
909 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
910 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
911 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
913 for (ix
= 1; ix
< count
- 1; ix
++) {
914 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
915 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
916 // Don't use 'key' because that may be the legacy key (see <rdar://34537018>)
917 // Force because we may have gotten the legacy key instead of the new key in the policy
918 SecPVCSetResultForced(pvc
, kSecPolicyCheckIntermediateCountry
, ix
, kCFBooleanFalse
, true);
923 /****************************************************************************
924 *********************** New rfc5280 Chain Validation ***********************
925 ****************************************************************************/
927 #define POLICY_MAPPING 1
928 #define POLICY_SUBTREES 1
930 /* rfc5280 basic cert processing. */
931 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
935 CFIndex count
= SecPVCGetCertificateCount(pvc
);
936 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
937 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
938 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
939 uint32_t n
= (uint32_t)count
;
941 bool is_anchored
= SecPathBuilderIsAnchored(pvc
->builder
);
942 bool is_anchor_trusted
= false;
944 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, n
- 1);
945 if (CFArrayGetCount(constraints
) == 0) {
946 /* Given that the path builder has already indicated the last cert in this chain has
947 * trust set on it, empty constraints means trusted. */
948 is_anchor_trusted
= true;
950 /* Determine whether constraints say to trust this cert for this PVC. */
951 SecTrustSettingsResult tsResult
= SecPVCGetTrustSettingsResult(pvc
, SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1),
953 if (tsResult
== kSecTrustSettingsResultTrustRoot
|| tsResult
== kSecTrustSettingsResultTrustAsRoot
) {
954 is_anchor_trusted
= true;
959 if (is_anchor_trusted
) {
960 /* If the anchor is trusted we don't process the last cert in the
964 Boolean isSelfSigned
= false;
965 (void) SecCertificateIsSelfSigned(SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1), &isSelfSigned
);
967 /* Add a detail for the root not being trusted. */
968 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
969 n
- 1, kCFBooleanFalse
, true)) {
973 /* Add a detail for the missing intermediate. */
974 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckMissingIntermediate
,
975 n
- 1, kCFBooleanFalse
, true)) {
981 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
982 //policy_set_t user_initial_policy_set = NULL;
983 //trust_anchor_t anchor;
987 CFMutableArrayRef permitted_subtrees
= NULL
;
988 CFMutableArrayRef excluded_subtrees
= NULL
;
989 /* set the initial subtrees to the trusted anchor's subtrees, if it has them */
990 SecCertificateRef anchor
= SecCertificatePathVCGetRoot(path
);
991 CFArrayRef anchor_permitted_subtrees
= SecCertificateGetPermittedSubtrees(anchor
);
992 if (is_anchor_trusted
&& anchor_permitted_subtrees
) {
993 permitted_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_permitted_subtrees
);
995 permitted_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
998 CFArrayRef anchor_excluded_subtrees
= SecCertificateGetExcludedSubtrees(anchor
);
999 if (is_anchor_trusted
&& anchor_excluded_subtrees
) {
1000 excluded_subtrees
= CFArrayCreateMutableCopy(NULL
, 0, anchor_excluded_subtrees
);
1002 excluded_subtrees
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
1005 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
1006 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1007 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
1008 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
1011 if (!SecCertificatePathVCVerifyPolicyTree(path
, is_anchor_trusted
)) {
1012 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckPolicyConstraints
, 0, kCFBooleanFalse
, true)) {
1018 /* Path builder ensures we only get cert chains with proper issuer
1019 chaining with valid signatures along the way. */
1020 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
1021 SecKeyRef working_public_key
= anchor
->public_key
;
1022 x500_name_t working_issuer_name
= anchor
->issuer_name
;
1024 uint32_t i
, max_path_length
= n
;
1025 SecCertificateRef cert
= NULL
;
1026 for (i
= 1; i
<= n
; ++i
) {
1028 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1029 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc
->builder
), n
- i
);
1031 /* (a) Verify the basic certificate information. */
1032 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1033 using the working_public_key and the working_public_key_parameters. */
1035 /* Already done by chain builder. */
1036 if (!SecCertificateIsValid(cert
, verify_time
)) {
1037 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, n
- i
, kCFBooleanFalse
)) {
1041 if (SecCertificateIsWeakKey(cert
)) {
1042 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, n
- i
, kCFBooleanFalse
)) {
1046 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
1047 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, n
- i
, kCFBooleanFalse
)) {
1052 /* @@@ cert.issuer == working_issuer_name. */
1056 if (!is_self_issued
|| i
== n
) {
1058 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1059 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1060 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1061 secnotice("policy", "name in excluded subtrees");
1062 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1065 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1066 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1067 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1068 secnotice("policy", "name not in permitted subtrees");
1069 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1074 /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
1076 /* If Last Cert in Path */
1080 /* Prepare for Next Cert */
1081 /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
1082 /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
1084 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1086 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1087 if (permitted_subtrees_in_cert
) {
1088 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1091 // could do something smart here to avoid inserting the exact same constraint
1092 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1093 if (excluded_subtrees_in_cert
) {
1094 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1095 CFRange range
= { 0, num_trees
};
1096 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1099 /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
1101 /* (k) Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1104 if (!is_self_issued
) {
1105 if (max_path_length
> 0) {
1108 /* max_path_len exceeded, illegal. */
1109 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsPathLen
,
1110 n
- i
, kCFBooleanFalse
, true)) {
1116 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(cert
);
1117 if (bc
&& bc
->pathLenConstraintPresent
1118 && bc
->pathLenConstraint
< max_path_length
) {
1119 max_path_length
= bc
->pathLenConstraint
;
1122 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1123 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1125 /* (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. */
1126 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1127 /* Certificate contains one or more unknown critical extensions. */
1128 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, n
- i
, kCFBooleanFalse
)) {
1133 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1134 /* Certificate contains one or more known exensions where parsing failed. */
1135 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, n
-i
, kCFBooleanFalse
, true)) {
1140 } /* end loop over certs in path */
1142 /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
1145 /* 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
1146 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1148 /* (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. */
1149 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1150 /* Certificate contains one or more unknown critical extensions. */
1151 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
, 0, kCFBooleanFalse
)) {
1156 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
1157 /* Certificate contains one or more known exensions where parsing failed. */
1158 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckUnparseableExtension
, 0, kCFBooleanFalse
, true)) {
1162 /* (g) done by SecCertificatePathVCVerifyPolicyTree */
1165 CFReleaseNull(permitted_subtrees
);
1166 CFReleaseNull(excluded_subtrees
);
1169 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1170 policy_set_t policies
= NULL
;
1171 const SecCECertificatePolicies
*cp
=
1172 SecCertificateGetCertificatePolicies(cert
);
1173 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1174 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1175 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1180 static void SecPolicyCheckEV(SecPVCRef pvc
,
1182 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1183 policy_set_t valid_policies
= NULL
;
1185 /* 6.1.7. Key Usage Purposes */
1187 CFAbsoluteTime jul2016
= 489024000;
1188 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1189 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1190 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1191 if (SecPVCSetResultForced(pvc
, key
,
1192 0, kCFBooleanFalse
, true)) {
1198 for (ix
= 0; ix
< count
; ++ix
) {
1199 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1200 policy_set_t policies
= policies_for_cert(cert
);
1203 /* anyPolicy in the leaf isn't allowed for EV, so only init
1204 valid_policies if we have real policies. */
1205 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1206 valid_policies
= policies
;
1209 } else if (ix
< count
- 1) {
1210 /* Subordinate CA */
1211 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1212 secnotice("ev", "subordinate certificate is not ev");
1213 if (SecPVCSetResultForced(pvc
, key
,
1214 ix
, kCFBooleanFalse
, true)) {
1215 policy_set_free(valid_policies
);
1216 policy_set_free(policies
);
1220 policy_set_intersect(&valid_policies
, policies
);
1223 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1224 secnotice("ev", "anchor certificate is not ev");
1225 if (SecPVCSetResultForced(pvc
, key
,
1226 ix
, kCFBooleanFalse
, true)) {
1227 policy_set_free(valid_policies
);
1228 policy_set_free(policies
);
1233 policy_set_free(policies
);
1234 if (!valid_policies
) {
1235 secnotice("ev", "valid_policies set is empty: chain not ev");
1236 /* If we ever get into a state where no policies are valid anymore
1237 this can't be an ev chain. */
1238 if (SecPVCSetResultForced(pvc
, key
,
1239 ix
, kCFBooleanFalse
, true)) {
1245 policy_set_free(valid_policies
);
1247 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1248 Subscriber MUST contain an OID defined by the CA in the certificate’s
1249 certificatePolicies extension that: (i) indicates which CA policy statement relates
1250 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1251 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1252 marks the certificate as being an EV Certificate.
1253 (b) EV Subordinate CA Certificates
1254 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1255 CA MUST contain one or more OIDs defined by the issuing CA that
1256 explicitly identify the EV Policies that are implemented by the Subordinate
1258 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1259 MAY contain the special anyPolicy OID (2.5.29.32.0).
1260 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1261 certificatePolicies or extendedKeyUsage extensions.
1267 * MARK: Certificate Transparency support
1269 const CFStringRef kSecCTRetirementDateKey
= CFSTR("expiry"); // For backwards compatibility, retirement date is represented with the "expiry" key
1270 const CFStringRef kSecCTReadOnlyDateKey
= CFSTR("frozen"); // For backwards compatibility, read-only date is represented with the "frozen" key
1271 const CFStringRef kSecCTShardStartDateKey
= CFSTR("start_inclusive");
1272 const CFStringRef kSecCTShardEndDateKey
= CFSTR("end_exclusive");
1273 const CFStringRef kSecCTPublicKeyKey
= CFSTR("key");
1276 kSecCTEntryTypeCert
= 0,
1277 kSecCTEntryTypePreCert
= 1,
1283 Version sct_version; // 1 byte
1284 LogID id; // 32 bytes
1285 uint64 timestamp; // 8 bytes
1286 CtExtensions extensions; // 2 bytes len field, + n bytes data
1287 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1288 Version sct_version;
1289 SignatureType signature_type = certificate_timestamp;
1291 LogEntryType entry_type;
1292 select(entry_type) {
1293 case x509_entry: ASN.1Cert;
1294 case precert_entry: PreCert;
1296 CtExtensions extensions;
1298 } SignedCertificateTimestamp;
1302 #include <Security/SecureTransportPriv.h>
1305 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1308 case SSL_SignatureAlgorithmRSA
:
1310 case SSL_HashAlgorithmSHA1
:
1311 return &CSSMOID_SHA1WithRSA
;
1312 case SSL_HashAlgorithmSHA256
:
1313 return &CSSMOID_SHA256WithRSA
;
1314 case SSL_HashAlgorithmSHA384
:
1315 return &CSSMOID_SHA384WithRSA
;
1319 case SSL_SignatureAlgorithmECDSA
:
1321 case SSL_HashAlgorithmSHA1
:
1322 return &CSSMOID_ECDSA_WithSHA1
;
1323 case SSL_HashAlgorithmSHA256
:
1324 return &CSSMOID_ECDSA_WithSHA256
;
1325 case SSL_HashAlgorithmSHA384
:
1326 return &CSSMOID_ECDSA_WithSHA384
;
1338 static size_t SSLDecodeUint16(const uint8_t *p
)
1340 return (p
[0]<<8 | p
[1]);
1343 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1345 p
[0] = (len
>> 8)&0xff;
1346 p
[1] = (len
& 0xff);
1350 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1352 p
[0] = (len
>> 16)&0xff;
1353 p
[1] = (len
>> 8)&0xff;
1354 p
[2] = (len
& 0xff);
1360 uint64_t SSLDecodeUint64(const uint8_t *p
)
1363 for(int i
=0; i
<8; i
++) {
1370 #include <libDER/DER_CertCrl.h>
1371 #include <libDER/DER_Encode.h>
1372 #include <libDER/asn1Types.h>
1375 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1377 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1379 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
1381 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
1383 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1384 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
1385 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
1391 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
1393 SecCertificateRef leafCert
= NULL
;
1394 SecCertificateRef issuer
= NULL
;
1395 CFDataRef issuerKeyHash
= NULL
;
1396 CFDataRef tbs_precert
= NULL
;
1397 CFMutableDataRef data
= NULL
;
1399 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1400 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1401 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1403 require(leafCert
, out
);
1404 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
1405 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
1406 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
1408 require(issuerKeyHash
, out
);
1409 require(tbs_precert
, out
);
1410 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1411 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1413 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1414 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
1415 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
1416 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
1419 CFReleaseSafe(issuerKeyHash
);
1420 CFReleaseSafe(tbs_precert
);
1425 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
1427 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
1431 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
1433 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
1436 static bool isSCTValidForLogData(CFDictionaryRef logData
, int entry_type
, CFAbsoluteTime sct_time
, CFAbsoluteTime cert_expiry_date
) {
1437 /* only embedded SCTs can be used from retired logs. */
1438 if(entry_type
==kSecCTEntryTypeCert
&& CFDictionaryContainsKey(logData
, kSecCTRetirementDateKey
)) {
1442 /* SCTs from after the transition to read-only are not valid (and indicate a operator failure) */
1443 CFDateRef frozen_date
= CFDictionaryGetValue(logData
, kSecCTReadOnlyDateKey
);
1444 if (frozen_date
&& (sct_time
> CFDateGetAbsoluteTime(frozen_date
))) {
1445 secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData
);
1449 /* If the log is temporally sharded, the certificate expiry date must be within the temporal shard window */
1450 CFDateRef start_inclusive
= CFDictionaryGetValue(logData
, kSecCTShardStartDateKey
);
1451 CFDateRef end_exclusive
= CFDictionaryGetValue(logData
, kSecCTShardEndDateKey
);
1452 if (start_inclusive
&& (cert_expiry_date
< CFDateGetAbsoluteTime(start_inclusive
))) {
1455 if (end_exclusive
&& (cert_expiry_date
>= CFDateGetAbsoluteTime(end_exclusive
))) {
1464 If the 'sct' is valid, add it to the validatingLogs dictionary.
1467 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
1469 - entry_type: 0 for x509 cert, 1 for precert.
1470 - entry: the cert or precert data.
1471 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
1472 - trustedLog: Dictionary contain the Trusted Logs.
1474 The SCT is valid if:
1475 - It decodes properly.
1476 - Its timestamp is less than 'verifyTime'.
1477 - It is signed by a log in 'trustedLogs'.
1478 - If entry_type = 0, the log must be currently qualified.
1479 - If entry_type = 1, the log may be expired.
1481 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
1482 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.
1485 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFAbsoluteTime cert_expiry_date
, CFDictionaryRef trustedLogs
, CFAbsoluteTime
*sct_at
)
1488 const uint8_t *logID
;
1489 const uint8_t *timestampData
;
1491 size_t extensionsLen
;
1492 const uint8_t *extensionsData
;
1495 size_t signatureLen
;
1496 const uint8_t *signatureData
;
1497 SecKeyRef pubKey
= NULL
;
1498 uint8_t *signed_data
= NULL
;
1499 const SecAsn1Oid
*oid
= NULL
;
1501 CFDataRef logIDData
= NULL
;
1502 CFDictionaryRef result
= 0;
1504 const uint8_t *p
= CFDataGetBytePtr(sct
);
1505 size_t len
= CFDataGetLength(sct
);
1507 require(len
>=43, out
);
1509 version
= p
[0]; p
++; len
--;
1510 logID
= p
; p
+=32; len
-=32;
1511 timestampData
= p
; p
+=8; len
-=8;
1512 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1514 require(len
>=extensionsLen
, out
);
1515 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
1517 require(len
>=4, out
);
1518 hashAlg
=p
[0]; p
++; len
--;
1519 sigAlg
=p
[0]; p
++; len
--;
1520 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1521 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
1524 /* verify version: only v1(0) is supported */
1526 secerror("SCT version unsupported: %d\n", version
);
1530 /* verify timestamp not in the future */
1531 timestamp
= SSLDecodeUint64(timestampData
);
1532 if(timestamp
> vt
) {
1533 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
1540 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
1541 signed_data
= malloc(signed_data_len
);
1542 require(signed_data
, out
);
1545 *q
++ = 0; // certificate_timestamp
1546 memcpy(q
, timestampData
, 8); q
+=8;
1547 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
1548 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
1549 q
= SSLEncodeUint16(q
, extensionsLen
);
1550 memcpy(q
, extensionsData
, extensionsLen
);
1552 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
1554 CFDictionaryRef logData
= CFDictionaryGetValue(trustedLogs
, logIDData
);
1555 CFAbsoluteTime sct_time
= TimestampToCFAbsoluteTime(timestamp
);
1556 require(logData
&& isSCTValidForLogData(logData
, entry_type
, sct_time
, cert_expiry_date
), out
);
1558 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, kSecCTPublicKeyKey
);
1559 require(logKeyData
, out
); // This failing would be an internal logic error
1560 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
1561 require(pubKey
, out
);
1563 oid
= oidForSigAlg(hashAlg
, sigAlg
);
1566 algId
.algorithm
= *oid
;
1567 algId
.parameters
.Data
= NULL
;
1568 algId
.parameters
.Length
= 0;
1570 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
1574 secerror("SCT signature failed (log=%@)\n", logData
);
1578 CFReleaseSafe(logIDData
);
1579 CFReleaseSafe(pubKey
);
1585 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
1587 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
1589 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
1590 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
1591 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
1592 CFReleaseSafe(sct_time
);
1596 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
1598 CFMutableArrayRef SCTs
= NULL
;
1599 SecCertificateRef leafCert
= NULL
;
1600 SecCertificateRef issuer
= NULL
;
1601 CFArrayRef ocspResponsesData
= NULL
;
1602 SecOCSPRequestRef ocspRequest
= NULL
;
1604 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
1605 require_quiet(ocspResponsesData
, out
);
1607 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1608 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1609 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1611 require(leafCert
, out
);
1612 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
1613 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
1615 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1618 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
1619 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
1620 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
1621 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
1622 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
1623 if(ocspSingleResponse
) {
1624 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
1625 if(singleResponseSCTs
) {
1626 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
1627 CFRelease(singleResponseSCTs
);
1629 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
1632 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
1635 if(CFArrayGetCount(SCTs
)==0) {
1636 CFReleaseNull(SCTs
);
1640 CFReleaseSafe(ocspResponsesData
);
1642 SecOCSPRequestFinalize(ocspRequest
);
1647 static void SecPolicyCheckCT(SecPVCRef pvc
)
1649 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1650 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
1651 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
1652 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
1653 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
1654 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
1655 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
1656 __block
uint32_t trustedSCTCount
= 0;
1657 __block CFAbsoluteTime issuanceTime
= SecPVCGetVerifyTime(pvc
);
1658 __block CFAbsoluteTime certExpiry
= SecCertificateNotValidAfter(leafCert
);
1659 TA_CTFailureReason failureReason
= TA_CTNoFailure
;
1662 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1663 trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
1664 CFReleaseSafe(otapkiref
);
1667 // This eventually contain list of logs who validated the SCT.
1668 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1669 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1671 uint64_t vt
= TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc
));
1673 __block
bool at_least_one_currently_valid_external
= 0;
1674 __block
bool at_least_one_currently_valid_embedded
= 0;
1675 __block
bool unknown_log
= 0;
1676 __block
bool disqualified_log
= 0;
1678 require(logsValidatingEmbeddedScts
, out
);
1679 require(currentLogsValidatingScts
, out
);
1681 /* Skip if there are no SCTs. */
1682 bool no_scts
= (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) ||
1683 (builderScts
&& CFArrayGetCount(builderScts
) > 0) ||
1684 (ocspScts
&& CFArrayGetCount(ocspScts
) > 0);
1685 require_action_quiet(no_scts
, out
,
1686 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1688 analytics
->ct_failure_reason
= TA_CTNoSCTs
;
1692 if(trustedLogs
&& CFDictionaryGetCount(trustedLogs
) > 0) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
1693 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
1694 CFArrayForEach(embeddedScts
, ^(const void *value
){
1695 CFAbsoluteTime sct_at
;
1696 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1698 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
1699 if(!CFDictionaryContainsKey(log
, kSecCTRetirementDateKey
)) {
1700 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1701 at_least_one_currently_valid_embedded
= true;
1704 disqualified_log
= true;
1712 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
1713 CFArrayForEach(builderScts
, ^(const void *value
){
1714 CFAbsoluteTime sct_at
;
1715 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1717 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1718 at_least_one_currently_valid_external
= true;
1726 if(ocspScts
&& x509Entry
) {
1727 CFArrayForEach(ocspScts
, ^(const void *value
){
1728 CFAbsoluteTime sct_at
;
1729 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, certExpiry
, trustedLogs
, &sct_at
);
1731 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1732 at_least_one_currently_valid_external
= true;
1740 failureReason
= TA_CTMissingLogs
;
1744 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
1747 is_ct = (A1 AND A2) OR (B1 AND B2).
1749 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
1750 A2: At least one embedded SCT from a currently valid log.
1752 B1: SCTs from 2 currently valid logs (from any source)
1753 B2: At least 1 external SCT from a currently valid log.
1757 bool hasValidExternalSCT
= (at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2);
1758 bool hasValidEmbeddedSCT
= (at_least_one_currently_valid_embedded
);
1759 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1760 SecCertificatePathVCSetIsCT(path
, false);
1762 if (hasValidEmbeddedSCT
) {
1763 /* Calculate issuance time based on timestamp of SCTs from current logs */
1764 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
1765 CFDictionaryRef log
= key
;
1766 if(!CFDictionaryContainsKey(log
, kSecCTRetirementDateKey
)) {
1767 // Log is still qualified
1768 CFDateRef ts
= (CFDateRef
) value
;
1769 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
1770 if(timestamp
< issuanceTime
) {
1771 issuanceTime
= timestamp
;
1775 SecCertificatePathVCSetIssuanceTime(path
, issuanceTime
);
1777 if (hasValidExternalSCT
) {
1778 /* Note: since external SCT validates this cert, we do not need to
1779 override issuance time here. If the cert also has a valid embedded
1780 SCT, issuanceTime will be calculated and set in the block above. */
1781 SecCertificatePathVCSetIsCT(path
, true);
1782 } else if (hasValidEmbeddedSCT
) {
1783 __block
int lifetime
; // in Months
1784 __block
unsigned once_or_current_qualified_embedded
= 0;
1787 __block
bool failed_once_check
= false;
1788 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
1789 CFDictionaryRef log
= key
;
1790 CFDateRef ts
= value
;
1791 CFDateRef expiry
= CFDictionaryGetValue(log
, kSecCTRetirementDateKey
);
1792 if (expiry
== NULL
) { // Currently qualified OR
1793 once_or_current_qualified_embedded
++;
1794 } else if (CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
&& // Once qualified. That is, qualified at the time of SCT AND
1795 issuanceTime
< CFDateGetAbsoluteTime(expiry
)) { // at the time of issuance.)
1796 once_or_current_qualified_embedded
++;
1799 failed_once_check
= true;
1803 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
1805 CFCalendarGetComponentDifference(zuluCalendar
,
1806 SecCertificateNotValidBefore(leafCert
),
1807 SecCertificateNotValidAfter(leafCert
),
1808 0, "M", &_lifetime
);
1809 lifetime
= _lifetime
;
1812 unsigned requiredEmbeddedSctsCount
;
1814 if (lifetime
< 15) {
1815 requiredEmbeddedSctsCount
= 2;
1816 } else if (lifetime
<= 27) {
1817 requiredEmbeddedSctsCount
= 3;
1818 } else if (lifetime
<= 39) {
1819 requiredEmbeddedSctsCount
= 4;
1821 requiredEmbeddedSctsCount
= 5;
1824 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
1825 SecCertificatePathVCSetIsCT(path
, true);
1827 /* Not enough "once or currently qualified" SCTs */
1828 if (failed_once_check
) {
1829 failureReason
= TA_CTEmbeddedNotEnoughDisqualified
;
1830 } else if (unknown_log
) {
1831 failureReason
= TA_CTEmbeddedNotEnoughUnknown
;
1833 failureReason
= TA_CTEmbeddedNotEnough
;
1836 } else if (!at_least_one_currently_valid_embedded
&& !at_least_one_currently_valid_external
) {
1837 /* No currently valid SCTs */
1838 if (disqualified_log
) {
1839 failureReason
= TA_CTNoCurrentSCTsDisqualifiedLog
;
1840 } else if (unknown_log
) {
1841 failureReason
= TA_CTNoCurrentSCTsUnknownLog
;
1843 } else if (at_least_one_currently_valid_external
) {
1844 /* One presented current SCT but failed total current check */
1845 if (disqualified_log
) {
1846 failureReason
= TA_CTPresentedNotEnoughDisqualified
;
1847 } else if (unknown_log
) {
1848 failureReason
= TA_CTPresentedNotEnoughUnknown
;
1850 failureReason
= TA_CTPresentedNotEnough
;
1854 /* Record analytics data for CT */
1855 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1856 require_quiet(analytics
, out
);
1857 uint32_t sctCount
= 0;
1858 /* Count the total number of SCTs we found and report where we got them */
1859 if (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) {
1860 analytics
->sct_sources
|= TA_SCTEmbedded
;
1861 sctCount
+= CFArrayGetCount(embeddedScts
);
1863 if (builderScts
&& CFArrayGetCount(builderScts
) > 0) {
1864 analytics
->sct_sources
|= TA_SCT_TLS
;
1865 sctCount
+= CFArrayGetCount(builderScts
);
1867 if (ocspScts
&& CFArrayGetCount(ocspScts
) > 0) {
1868 analytics
->sct_sources
|= TA_SCT_OCSP
;
1869 sctCount
+= CFArrayGetCount(ocspScts
);
1871 /* Report how many of those SCTs were once or currently qualified */
1872 analytics
->number_trusted_scts
= trustedSCTCount
;
1873 /* Report how many SCTs we got */
1874 analytics
->number_scts
= sctCount
;
1876 analytics
->ct_failure_reason
= failureReason
;
1877 /* Only one current SCT -- close to failure */
1878 if (CFDictionaryGetCount(currentLogsValidatingScts
) == 1) {
1879 analytics
->ct_one_current
= true;
1882 CFReleaseSafe(logsValidatingEmbeddedScts
);
1883 CFReleaseSafe(currentLogsValidatingScts
);
1884 CFReleaseSafe(builderScts
);
1885 CFReleaseSafe(embeddedScts
);
1886 CFReleaseSafe(ocspScts
);
1887 CFReleaseSafe(precertEntry
);
1888 CFReleaseSafe(trustedLogs
);
1889 CFReleaseSafe(x509Entry
);
1892 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
1893 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1895 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
1896 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
1898 for (ix
= 0; ix
< count
; ix
++) {
1899 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1900 policy_set_t policies
= policies_for_cert(cert
);
1902 if (policy_set_contains(policies
, &key_value
)) {
1903 policy_set_free(policies
);
1906 policy_set_free(policies
);
1911 static void SecPolicyCheckCertificatePolicy(SecPVCRef pvc
, CFStringRef key
)
1913 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1914 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1915 bool result
= false;
1917 if (CFGetTypeID(value
) == CFDataGetTypeID())
1919 result
= checkPolicyOidData(pvc
, value
);
1920 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1921 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
1923 result
= checkPolicyOidData(pvc
, dataOid
);
1928 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1933 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
1935 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1936 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1937 if (isString(value
)) {
1938 SecPathBuilderSetRevocationMethod(pvc
->builder
, value
);
1942 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
1944 pvc
->require_revocation_response
= true;
1945 secdebug("policy", "revocation response required");
1948 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
1949 SecPathBuilderSetCheckRevocationOnline(pvc
->builder
);
1952 static void SecPolicyCheckRevocationIfTrusted(SecPVCRef pvc
, CFStringRef key
) {
1953 SecPathBuilderSetCheckRevocationIfTrusted(pvc
->builder
);
1956 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
1958 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1959 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1960 if (value
== kCFBooleanTrue
) {
1961 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
1963 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
1967 static void SecPolicyCheckWeakKeySize(SecPVCRef pvc
,
1969 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1970 for (ix
= 0; ix
< count
; ++ix
) {
1971 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1972 if (cert
&& SecCertificateIsWeakKey(cert
)) {
1973 /* Intermediate certificate has a weak key. */
1974 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1980 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
1981 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1982 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1983 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
1984 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1986 /* Don't check key size for user-anchored leafs */
1987 #if TARGET_OS_IPHONE
1988 SecCertificateSourceRef userSource
= kSecUserAnchorSource
;
1990 SecCertificateSourceRef userSource
= kSecLegacyAnchorSource
;
1992 if (SecPVCIsAnchorPerConstraints(pvc
, userSource
, leaf
)) {
1996 for (ix
= 0; ix
< count
; ++ix
) {
1997 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1998 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
1999 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2005 static void SecPolicyCheckWeakSignature(SecPVCRef pvc
, CFStringRef key
) {
2006 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2007 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2008 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
2009 for (ix
= 0; ix
< count
; ++ix
) {
2010 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2011 if (!SecPolicyCheckCertWeakSignature(cert
, pvcValue
)) {
2012 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2018 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
2020 CFIndex ix
= 0, count
= SecPVCGetCertificateCount(pvc
);
2022 /* Ignore (a non-self-signed) anchor if it's trusted by the user */
2023 #if TARGET_OS_IPHONE
2024 bool userAnchored
= SecPVCIsAnchorPerConstraints(pvc
, kSecUserAnchorSource
, SecPVCGetCertificateAtIndex(pvc
, count
- 1));
2026 bool userAnchored
= SecPVCIsAnchorPerConstraints(pvc
, kSecLegacyAnchorSource
, SecPVCGetCertificateAtIndex(pvc
, count
- 1));
2028 if (SecPathBuilderIsAnchored(pvc
->builder
) && userAnchored
) {
2032 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2033 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
2034 while (ix
< count
) {
2035 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2036 /* note that these checks skip self-signed certs */
2037 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
2038 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
2045 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
2046 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2047 require_quiet(leaf
, out
);
2049 /* And now a special snowflake from our tests */
2051 /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
2052 /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
2053 /* Not After : May 26 09:37:50 2017 GMT */
2054 static const uint8_t vodafone
[] = {
2055 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
2056 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
2059 CFDataRef leafFingerprint
= SecCertificateGetSHA1Digest(leaf
);
2060 require_quiet(leafFingerprint
, out
);
2061 const unsigned int len
= 20;
2062 const uint8_t *dp
= CFDataGetBytePtr(leafFingerprint
);
2063 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
2071 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
2073 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
2075 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2077 Boolean keyInPolicy
= false;
2078 CFArrayRef policies
= pvc
->policies
;
2079 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2080 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2081 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2082 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2087 /* We only enforce this check when *both* of the following are true:
2088 * 1. One of the certs in the path has this usage constraint, and
2089 * 2. One of the policies in the PVC has this key
2090 * (As compared to normal policy options which require only one to be true..) */
2091 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2094 /* Ignore the anchor if it's trusted */
2095 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2098 for (ix
= 0; ix
< count
; ++ix
) {
2099 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2100 if (SecCertificateIsWeakHash(cert
)) {
2101 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
2102 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2112 static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc
,
2114 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2116 Boolean keyInPolicy
= false;
2117 CFArrayRef policies
= pvc
->policies
;
2118 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2119 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2120 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2121 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2126 /* We only enforce this check when *both* of the following are true:
2127 * 1. One of the certs in the path has this usage constraint, and
2128 * 2. One of the policies in the PVC has this key
2129 * (As compared to normal policy options which require only one to be true..) */
2130 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2133 /* Ignore the anchor if it's trusted */
2134 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2137 for (ix
= 0; ix
< count
; ++ix
) {
2138 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2139 if (!SecCertificateIsStrongKey(cert
)) {
2140 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2150 static void SecPolicyCheckPinningRequired(SecPVCRef pvc
, CFStringRef key
) {
2151 /* Pinning is disabled on the system, skip. */
2152 if (SecIsInternalRelease()) {
2153 if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
2154 CFSTR("com.apple.security"), NULL
)) {
2159 CFArrayRef policies
= pvc
->policies
;
2160 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2161 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2162 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2163 CFStringRef policyName
= SecPolicyGetName(policy
);
2164 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
2165 /* policy required pinning, but we didn't use a pinning policy */
2166 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
)) {
2173 static bool is_configured_test_system_root(SecCertificateRef root
, CFStringRef preference
) {
2174 if (!SecIsInternalRelease()) {
2177 bool result
= false;
2178 CFDataRef rootHash
= SecCertificateCopySHA256Digest(root
);
2179 CFTypeRef value
= CFPreferencesCopyAppValue(preference
, CFSTR("com.apple.security"));
2180 require_quiet(isData(value
), out
);
2181 require_quiet(kCFCompareEqualTo
== CFDataCompare(rootHash
, value
), out
);
2185 CFReleaseNull(value
);
2186 CFReleaseNull(rootHash
);
2190 static bool is_ct_excepted_domain(CFStringRef hostname
, CFStringRef exception
) {
2191 if (kCFCompareEqualTo
== CFStringCompare(exception
, hostname
, kCFCompareCaseInsensitive
)) {
2194 } else if (CFStringHasPrefix(exception
, CFSTR("."))) {
2196 CFIndex elength
= CFStringGetLength(exception
);
2197 CFIndex hlength
= CFStringGetLength(hostname
);
2198 if (hlength
> elength
) {
2199 CFRange compareRange
= { hlength
- elength
, elength
};
2200 if (kCFCompareEqualTo
== CFStringCompareWithOptions(hostname
, exception
, compareRange
, kCFCompareCaseInsensitive
)) {
2203 } else if (hlength
+ 1 == elength
) {
2204 CFRange compareRange
= { 1, hlength
};
2205 if (kCFCompareEqualTo
== CFStringCompareWithOptions(exception
, hostname
, compareRange
, kCFCompareCaseInsensitive
)) {
2213 static OSStatus
is_subtree_dn_with_org(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
2214 if (gnType
!= GNT_DirectoryName
) {
2215 return errSecInternal
;
2218 DERDecodedInfo subtreeName_content
;
2219 if (DR_Success
!= DERDecodeItem(generalName
, &subtreeName_content
) || subtreeName_content
.tag
!= ASN1_CONSTR_SEQUENCE
) {
2220 return errSecDecode
;
2223 CFArrayRef subtree_orgs
= SecCertificateCopyOrganizationFromX501NameContent(&subtreeName_content
.content
);
2225 CFReleaseNull(subtree_orgs
);
2226 return errSecSuccess
;
2228 return errSecInternal
;
2231 static bool has_ct_excepted_key(SecCertificatePathVCRef path
, CFDictionaryRef exception
) {
2232 __block
bool result
= false;
2233 CFDataRef exceptionHash
= CFDictionaryGetValue(exception
, kSecCTExceptionsSPKIHashKey
);
2235 /* exception for a leaf is always allowed */
2236 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
2237 CFDataRef spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
2238 if (CFEqualSafe(exceptionHash
, spkiHash
)) {
2241 CFReleaseNull(spkiHash
);
2243 if (result
) { return result
; }
2245 /* exceptions for CAs */
2246 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
2247 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
2249 spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
2250 if (!CFEqualSafe(exceptionHash
, spkiHash
)) {
2251 CFReleaseNull(spkiHash
);
2254 CFReleaseNull(spkiHash
);
2256 /* this CA matches but exceptions for CAs have constraints */
2257 if (SecCertificateGetPermittedSubtrees(ca
)) {
2258 /* Constrained CAs have to have a Distinguished Name permitted subtree with an Organization attribute */
2259 CFArrayForEach(SecCertificateGetPermittedSubtrees(ca
), ^(const void *value
) {
2260 CFDataRef subtree
= (CFDataRef
)value
;
2261 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(subtree
), CFDataGetLength(subtree
) };
2262 DERDecodedInfo general_name_content
;
2263 if (DR_Success
== DERDecodeItem(&general_name
, &general_name_content
)) {
2264 OSStatus status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
2265 &general_name_content
.content
,
2267 is_subtree_dn_with_org
);
2268 if (status
== errSecSuccess
) {
2276 /* The Organization attribute(s) in the CA subject have to exactly match the Organization attribute(s) in the leaf */
2277 CFArrayRef leafOrgs
= SecCertificateCopyOrganization(leaf
);
2278 CFArrayRef caOrgs
= SecCertificateCopyOrganization(ca
);
2279 if (caOrgs
&& leafOrgs
&& CFEqualSafe(leafOrgs
, caOrgs
)) {
2282 CFReleaseNull(leafOrgs
);
2283 CFReleaseNull(caOrgs
);
2294 static bool is_ct_excepted(SecPVCRef pvc
) {
2295 CFDictionaryRef ct_exceptions
= _SecTrustStoreCopyCTExceptions(NULL
, NULL
);
2296 if (!ct_exceptions
) {
2300 __block
bool result
= false;
2301 CFArrayRef domainExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsDomainsKey
);
2302 if (domainExceptions
) {
2303 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2304 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2306 CFArrayForEach(domainExceptions
, ^(const void *value
) {
2307 result
= result
|| is_ct_excepted_domain(hostname
, value
);
2312 secinfo("policy", "domain-based CT exception applied");
2313 CFReleaseNull(ct_exceptions
);
2317 CFArrayRef keyExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsCAsKey
);
2318 if (keyExceptions
) {
2319 __block SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2320 CFArrayForEach(keyExceptions
, ^(const void *value
) {
2321 result
= result
|| has_ct_excepted_key(path
, value
);
2326 secinfo("policy" , "key-based CT exceptions applied");
2329 CFReleaseNull(ct_exceptions
);
2333 /* <rdar://45466778> some Apple servers not getting certs with embedded SCTs
2334 * <rdar://45545270> some Google apps have their own TLS stacks and aren't passing us TLS SCTs */
2335 static bool is_ct_allowlisted_ca(SecCertificatePathVCRef path
) {
2336 if (CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlist"), CFSTR("com.apple.security"), NULL
)) {
2340 /* subject:/CN=Apple IST CA 8 - G1/OU=Certification Authority/O=Apple Inc./C=US */
2341 /* issuer :/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2 */
2342 static const uint8_t appleISTCA8G1_spkiSHA256
[] = {
2343 0xe2, 0x4f, 0x8e, 0x8c, 0x21, 0x85, 0xda, 0x2f, 0x5e, 0x88, 0xd4, 0x57, 0x9e, 0x81, 0x7c, 0x47,
2344 0xbf, 0x6e, 0xaf, 0xbc, 0x85, 0x05, 0xf0, 0xf9, 0x60, 0xfd, 0x5a, 0x0d, 0xf4, 0x47, 0x3a, 0xd3
2347 /* subject:/CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US */
2348 /* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA */
2349 static const uint8_t appleISTCA2G1_spkiSHA256
[] = {
2350 0xb5, 0xcf, 0x82, 0xd4, 0x7e, 0xf9, 0x82, 0x3f, 0x9a, 0xa7, 0x8f, 0x12, 0x31, 0x86, 0xc5, 0x2e,
2351 0x88, 0x79, 0xea, 0x84, 0xb0, 0xf8, 0x22, 0xc9, 0x1d, 0x83, 0xe0, 0x42, 0x79, 0xb7, 0x8f, 0xd5
2354 /* subject:/C=US/O=Google Trust Services/CN=Google Internet Authority G3 */
2355 /* issuer :/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign */
2356 static const uint8_t googleIAG3_spkiSHA256
[] = {
2357 0x7f, 0xc3, 0x67, 0x10, 0x56, 0x71, 0x43, 0x81, 0x31, 0x14, 0xe8, 0x52, 0x37, 0xb1, 0x22, 0x15,
2358 0x6b, 0x62, 0xb9, 0xd6, 0x50, 0x54, 0x3d, 0xa8, 0x63, 0xad, 0x2e, 0x6a, 0xe5, 0x7f, 0x9f, 0xbf
2361 bool result
= false;
2362 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
2363 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
2365 bool allowlistAppleCAs
= true, allowlistGoogleCA
= true;
2366 if (CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlistApple"), CFSTR("com.apple.security"), NULL
)) {
2367 allowlistAppleCAs
= false;
2369 if (CFPreferencesGetAppBooleanValue(CFSTR("DisableCTAllowlistGoogle"), CFSTR("com.apple.security"), NULL
)) {
2370 allowlistGoogleCA
= false;
2373 CFDataRef caSPKIHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
2374 const uint8_t *dp
= CFDataGetBytePtr(caSPKIHash
);
2375 if (dp
&& allowlistAppleCAs
&& (!memcmp(appleISTCA8G1_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
) ||
2376 !memcmp(appleISTCA2G1_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
))) {
2379 if (dp
&& allowlistGoogleCA
&& !memcmp(googleIAG3_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
)) {
2382 CFReleaseNull(caSPKIHash
);
2390 static void SecPolicyCheckSystemTrustedCTRequired(SecPVCRef pvc
) {
2391 SecCertificateSourceRef appleAnchorSource
= NULL
;
2392 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
2393 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2394 CFDictionaryRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2396 /* Skip this check if we haven't done the CT checks yet */
2397 require_quiet(SecCertificatePathVCIsPathValidated(path
), out
);
2399 /* We only enforce this check when all of the following are true:
2400 * 0. Kill Switch not enabled */
2401 require_quiet(!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
), out
);
2403 /* 1. Not a pinning policy */
2404 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2405 require_quiet(CFEqualSafe(SecPolicyGetName(policy
),kSecPolicyNameSSLServer
), out
);
2407 /* 2. Device has checked in to MobileAsset for a current log list within the last 60 days.
2408 * Or the caller passed in the trusted log list. */
2409 require_quiet(SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
, out
);
2411 /* 3. Leaf issuance date is on or after 16 Oct 2018 at 00:00:00 AM UTC and not expired. */
2412 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2413 require_quiet(SecCertificateNotValidBefore(leaf
) >= 561340800.0 &&
2414 SecCertificateIsValid(leaf
, SecPVCGetVerifyTime(pvc
)), out
);
2416 /* 4. Chain is anchored with root in the system anchor source but not the Apple anchor source
2417 * with certain excepted CAs and configurable included CAs. */
2418 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2419 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2420 appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(false));
2421 require_quiet(SecPathBuilderIsAnchored(pvc
->builder
), out
);
2422 require_quiet((SecCertificateSourceContains(kSecSystemAnchorSource
, root
) &&
2423 appleAnchorSource
&& !SecCertificateSourceContains(appleAnchorSource
, root
) &&
2424 !is_ct_allowlisted_ca(path
)) ||
2425 is_configured_test_system_root(root
, CFSTR("TestCTRequiredSystemRoot")), out
);
2427 if (!SecCertificatePathVCIsCT(path
) && !is_ct_excepted(pvc
)) {
2428 /* Set failure. By not using the Forced variant, we implicitly check that this
2429 * policy had this options set. */
2430 SecPVCSetResult(pvc
, kSecPolicyCheckSystemTrustedCTRequired
, 0, kCFBooleanFalse
);
2434 CFReleaseNull(trustedLogs
);
2435 CFReleaseNull(otaref
);
2436 if (appleAnchorSource
) {
2437 SecMemoryCertificateSourceDestroy(appleAnchorSource
);
2441 static bool SecPolicyCheckSystemTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
2442 CFAbsoluteTime jul2016
= 489024000.0; // 1 July 2016 00:00:00 UTC
2443 CFAbsoluteTime mar2018
= 541555200.0; // 1 March 2018 00:00:00 UTC
2444 if (notBefore
< jul2016
) {
2445 /* Validity Period no greater than 60 months.
2446 60 months is no more than 5 years and 2 leap days (and 1 hour slip). */
2447 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2) + 3600;
2448 if (notAfter
- notBefore
> maxPeriod
) {
2449 secnotice("policy", "System-trusted leaf validity period is more than 60 months");
2452 } else if (notBefore
< mar2018
) {
2453 /* Validity Period no greater than 39 months.
2454 39 months is no more than 3 years, 2 31-day months,
2455 1 30-day month, and 1 leap day (and 1 hour slip) */
2456 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1) + 3600;
2457 if (notAfter
- notBefore
> maxPeriod
) {
2458 secnotice("policy", "System-trusted leaf validity period longer than 39 months and issued after 30 June 2016");
2462 /* Validity Period no greater than 825 days (and 1 hour slip). */
2463 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
2464 if (notAfter
- notBefore
> maxPeriod
) {
2465 secnotice("policy", "System-trusted leaf validity period longer than 825 days and issued on or after 1 March 2018");
2472 static bool SecPolicyCheckOtherTrustValidityPeriodMaximums(CFAbsoluteTime notBefore
, CFAbsoluteTime notAfter
) {
2473 /* Check whether we will enforce the validity period maximum for a non-system trusted leaf. */
2474 if (SecIsInternalRelease()) {
2475 if (CFPreferencesGetAppBooleanValue(CFSTR("IgnoreMaximumValidityPeriod"),
2476 CFSTR("com.apple.security"), NULL
)) {
2480 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
2481 if (notBefore
> jul2019
) {
2482 /* Validity Period no greater than 825 days (and 1 hour slip). */
2483 CFAbsoluteTime maxPeriod
= 60*60*24*825 + 3600;
2484 if (notAfter
- notBefore
> maxPeriod
) {
2485 secnotice("policy", "Non-system-trusted leaf validity period longer than 825 days and issued on or after 1 July 2019");
2492 static void SecPolicyCheckValidityPeriodMaximums(SecPVCRef pvc
, CFStringRef key
) {
2493 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2494 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(leaf
);
2495 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
2497 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2498 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2499 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
2500 if (!SecPolicyCheckSystemTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
2501 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2506 /* Don't check validity periods against maximums for user-anchored leafs */
2507 #if TARGET_OS_IPHONE
2508 SecCertificateSourceRef userSource
= kSecUserAnchorSource
;
2510 SecCertificateSourceRef userSource
= kSecLegacyAnchorSource
;
2512 if (SecPVCIsAnchorPerConstraints(pvc
, userSource
, leaf
)) {
2516 /* all other trust */
2517 if (!SecPolicyCheckOtherTrustValidityPeriodMaximums(notBefore
, notAfter
)) {
2518 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2522 static void SecPolicyCheckServerAuthEKU(SecPVCRef pvc
, CFStringRef key
) {
2523 /* The ExtendedKeyUsage check will verify a looser version of this check for all TLS server certs.
2524 * Here we want to be stricter (enforcing that there is an EKU extension and that it contains the
2525 * one true Server Auth EKU OID) for system and app anchors */
2526 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2527 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2528 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2529 if (SecCertificateSourceContains(kSecSystemAnchorSource
, root
) || is_configured_test_system_root(root
, CFSTR("TestSystemRoot"))) {
2530 /* all system-anchored chains must be compliant */
2531 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
2532 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2537 /* skip user/admin-anchored chains */
2538 #if TARGET_OS_IPHONE
2539 SecCertificateSourceRef userSource
= kSecUserAnchorSource
;
2541 SecCertificateSourceRef userSource
= kSecLegacyAnchorSource
;
2543 if (SecPVCIsAnchorPerConstraints(pvc
, userSource
, root
)) {
2547 /* All other anchor types must be compliant if issued on or after 1 July 2019 */
2548 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(leaf
);
2549 CFAbsoluteTime jul2019
= 583628400.0; // 1 July 2019 00:00:00 UTC
2550 if (notBefore
> jul2019
) {
2551 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, CFSTR("1.3.6.1.5.5.7.3.1"))) { // server auth EKU
2552 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
2557 void SecPolicyServerInitialize(void) {
2558 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2559 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2560 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2561 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2563 #undef POLICYCHECKMACRO
2564 #define __PC_ADD_CHECK_(NAME)
2565 #define __PC_ADD_CHECK_L(NAME) CFDictionaryAddValue(gSecPolicyLeafCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2566 #define __PC_ADD_CHECK_A(NAME) CFDictionaryAddValue(gSecPolicyPathCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2568 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2569 __PC_ADD_CHECK_##LEAFCHECK(NAME) \
2570 __PC_ADD_CHECK_##PATHCHECK(NAME)
2571 #include "../Security/SecPolicyChecks.list"
2573 /* Some of these don't follow the naming conventions but are in the Pinning DB.
2574 * <rdar://34537018> fix policy check constant values */
2575 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkerOid"), SecPolicyCheckLeafMarkerOid
);
2576 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkersProdAndQA"), SecPolicyCheckLeafMarkersProdAndQA
);
2577 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateMarkerOid"), SecPolicyCheckIntermediateMarkerOid
);
2578 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateCountry"), SecPolicyCheckIntermediateCountry
);
2579 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateOrganization"), SecPolicyCheckIntermediateOrganization
);
2584 /********************************************************
2585 ****************** SecPVCRef Functions *****************
2586 ********************************************************/
2588 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
) {
2589 secdebug("alloc", "pvc %p", pvc
);
2590 // Weird logging policies crashes.
2591 //secdebug("policy", "%@", policies);
2593 // Zero the pvc struct so only non-zero fields need to be explicitly set
2594 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
2595 pvc
->builder
= builder
;
2596 pvc
->policies
= policies
;
2599 pvc
->result
= kSecTrustResultUnspecified
;
2601 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2602 &kCFTypeDictionaryKeyCallBacks
,
2603 &kCFTypeDictionaryValueCallBacks
);
2604 pvc
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
,
2605 1, &kCFTypeArrayCallBacks
);
2606 CFRelease(certDetail
);
2609 void SecPVCDelete(SecPVCRef pvc
) {
2610 secdebug("alloc", "delete pvc %p", pvc
);
2611 CFReleaseNull(pvc
->policies
);
2612 CFReleaseNull(pvc
->details
);
2613 CFReleaseNull(pvc
->leafDetails
);
2616 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2617 secdebug("policy", "%@", path
);
2619 pvc
->result
= kSecTrustResultUnspecified
;
2620 CFReleaseNull(pvc
->details
);
2623 void SecPVCComputeDetails(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2626 /* Since we don't run the LeafChecks again, we need to preserve the
2627 * result the leaf had. */
2628 CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(path
);
2629 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
2630 pathLength
, pvc
->leafDetails
);
2631 for (ix
= 1; ix
< pathLength
; ++ix
) {
2632 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2633 &kCFTypeDictionaryKeyCallBacks
,
2634 &kCFTypeDictionaryValueCallBacks
);
2635 CFArrayAppendValue(details
, certDetail
);
2636 CFRelease(certDetail
);
2638 CFRetainAssign(pvc
->details
, details
);
2639 pvc
->result
= pvc
->leafResult
;
2640 CFReleaseSafe(details
);
2643 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
2644 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
2647 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
2648 return SecPathBuilderGetCertificateCount(pvc
->builder
);
2651 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
2652 return SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2655 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
2656 return SecPathBuilderGetVerifyTime(pvc
->builder
);
2659 static bool SecPVCIsExceptedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
, CFTypeRef value
) {
2660 CFArrayRef exceptions
= SecPathBuilderGetExceptions(pvc
->builder
);
2661 if (!exceptions
) { return false; }
2662 CFIndex exceptionsCount
= CFArrayGetCount(exceptions
);
2664 /* There are two types of exceptions:
2665 * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
2666 * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
2667 * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
2670 CFDictionaryRef options
= CFArrayGetValueAtIndex(exceptions
, 0);
2672 if (exceptionsCount
== 1 && (ix
> 0 || !CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
))) {
2673 /* SHA1Digest not allowed */
2674 if (CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
)) { return false; }
2676 if (CFDictionaryContainsKey(options
, key
)) {
2677 /* Special case -- AnchorTrusted only for self-signed certs */
2678 if (CFEqual(kSecPolicyCheckAnchorTrusted
, key
)) {
2679 Boolean isSelfSigned
= false;
2680 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2681 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2686 } else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
) && CFDictionaryContainsKey(options
, kSecPolicyCheckValidRoot
)) {
2687 /* Another special case - ValidRoot excepts Valid only for self-signed certs */
2688 Boolean isSelfSigned
= false;
2689 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2690 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2699 if (ix
>= exceptionsCount
) { return false; }
2700 CFDictionaryRef exception
= CFArrayGetValueAtIndex(exceptions
, ix
);
2702 /* Compare the cert hash */
2703 if (!CFDictionaryContainsKey(exception
, kSecCertificateDetailSHA1Digest
)) { return false; }
2704 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2705 if (!CFEqual(SecCertificateGetSHA1Digest(cert
), CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
))) {
2710 CFTypeRef exceptionValue
= CFDictionaryGetValue(exception
, key
);
2711 if (exceptionValue
&& CFEqual(value
, exceptionValue
)) {
2712 /* Only change result if PVC is already ok */
2713 if (SecPVCIsOkResult(pvc
)) {
2714 // Chains that pass due to exceptions get Proceed result.
2715 pvc
->result
= kSecTrustResultProceed
;
2723 static int32_t detailKeyToCssmErr(CFStringRef key
) {
2726 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
2727 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
2729 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
2730 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
2732 else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
2733 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
2739 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
2741 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
2742 bool result
= false;
2743 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2744 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, ix
);
2745 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2746 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2748 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2749 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2750 CFNumberRef allowedErrorNumber
= NULL
;
2751 if (!isDictionary(constraint
)) {
2754 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
2755 int32_t allowedErrorValue
= 0;
2756 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
2760 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
2761 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
2770 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
2771 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2772 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2773 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2774 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2775 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2776 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2777 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2778 if (!isDictionary(constraint
)) {
2782 CFDictionaryRef policyOptions
= NULL
;
2783 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2784 if (policyOptions
&& isDictionary(policyOptions
) &&
2785 CFDictionaryContainsKey(policyOptions
, key
)) {
2793 static SecTrustResultType
trust_result_for_key(CFStringRef key
) {
2794 SecTrustResultType result
= kSecTrustResultRecoverableTrustFailure
;
2795 #undef POLICYCHECKMACRO
2796 #define __PC_TYPE_MEMBER_ false
2797 #define __PC_TYPE_MEMBER_R false
2798 #define __PC_TYPE_MEMBER_F true
2799 #define __PC_TYPE_MEMBER_D true
2801 #define __TRUSTRESULT_ kSecTrustResultRecoverableTrustFailure
2802 #define __TRUSTRESULT_F kSecTrustResultFatalTrustFailure
2803 #define __TRUSTRESULT_D kSecTrustResultDeny
2804 #define __TRUSTRESULT_R kSecTrustResultRecoverableTrustFailure
2806 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2807 if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
2808 result = __TRUSTRESULT_##TRUSTRESULT; \
2810 #include "../Security/SecPolicyChecks.list"
2815 /* AUDIT[securityd](done):
2816 policy->_options is a caller provided dictionary, only its cf type has
2819 bool SecPVCSetResultForced(SecPVCRef pvc
,
2820 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
2822 /* If this is not something the current policy cares about ignore
2823 this error and return true so our caller continues evaluation. */
2825 /* Either the policy or the usage constraints have to have this key */
2826 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2827 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
2828 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
2833 /* Check to see if the SecTrustSettings for the certificate in question
2834 tell us to ignore this error. */
2835 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
2836 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
2840 /* Check to see if exceptions tells us to ignore this error. */
2841 if (SecPVCIsExceptedError(pvc
, ix
, key
, result
)) {
2842 secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix
, key
);
2846 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
2847 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
2848 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
2850 (force
? "force" : ""), result
);
2852 /* Avoid resetting deny or fatal to recoverable */
2853 SecTrustResultType trustResult
= trust_result_for_key(key
);
2854 if (SecPVCIsOkResult(pvc
) || trustResult
== kSecTrustResultFatalTrustFailure
) {
2855 pvc
->result
= trustResult
;
2856 } else if (trustResult
== kSecTrustResultDeny
&&
2857 pvc
->result
== kSecTrustResultRecoverableTrustFailure
) {
2858 pvc
->result
= trustResult
;
2864 CFMutableDictionaryRef detail
=
2865 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
2867 secerror("SecPVCSetResultForced: failed to get detail at index %ld (array length %ld)",
2868 ix
, CFArrayGetCount(pvc
->details
));
2872 /* Perhaps detail should have an array of results per key? As it stands
2873 in the case of multiple policy failures the last failure stands. */
2874 CFDictionarySetValue(detail
, key
, result
);
2879 bool SecPVCSetResult(SecPVCRef pvc
,
2880 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
2881 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
2884 /* AUDIT[securityd](done):
2885 key(ok) is a caller provided.
2886 value(ok, unused) is a caller provided.
2888 static void SecPVCValidateKey(const void *key
, const void *value
,
2890 SecPVCRef pvc
= (SecPVCRef
)context
;
2892 /* If our caller doesn't want full details and we failed earlier there is
2893 no point in doing additional checks. */
2894 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
2897 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
2898 CFDictionaryGetValue(pvc
->callbacks
, key
);
2901 /* "Optional" policy checks. This may be a new key from the
2902 * pinning DB which is not implemented in this OS version. Log a
2903 * warning, and on debug builds fail evaluation, to encourage us
2904 * to ensure that checks are synchronized across the same build. */
2905 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
2906 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
2907 secwarning("policy: unknown policy key %@, skipping", key
);
2909 pvc
->result
= kSecTrustResultOtherError
;
2912 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
2913 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
2914 secwarning("policy: unknown policy key %@, skipping", key
);
2916 pvc
->result
= kSecTrustResultOtherError
;
2920 /* Non standard validation phase, nothing is optional. */
2921 pvc
->result
= kSecTrustResultOtherError
;
2926 fcn(pvc
, (CFStringRef
)key
);
2929 /* AUDIT[securityd](done):
2930 policy->_options is a caller provided dictionary, only its cf type has
2933 SecTrustResultType
SecPVCLeafChecks(SecPVCRef pvc
) {
2934 /* We need to compute details for the leaf. */
2935 CFRetainAssign(pvc
->details
, pvc
->leafDetails
);
2937 CFArrayRef policies
= pvc
->policies
;
2938 CFIndex ix
, count
= CFArrayGetCount(policies
);
2939 for (ix
= 0; ix
< count
; ++ix
) {
2940 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2942 /* Validate all keys for all policies. */
2943 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2944 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
2947 pvc
->leafResult
= pvc
->result
;
2948 CFRetainAssign(pvc
->leafDetails
, pvc
->details
);
2953 bool SecPVCIsOkResult(SecPVCRef pvc
) {
2954 if (pvc
->result
== kSecTrustResultRecoverableTrustFailure
||
2955 pvc
->result
== kSecTrustResultDeny
||
2956 pvc
->result
== kSecTrustResultFatalTrustFailure
||
2957 pvc
->result
== kSecTrustResultOtherError
) {
2963 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
2964 /* Check stuff common to intermediate and anchors. */
2965 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
2966 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2967 CFIndex anchor_ix
= SecPVCGetCertificateCount(pvc
) - 1;
2968 bool is_anchor
= (ix
== anchor_ix
&& SecPathBuilderIsAnchored(pvc
->builder
));
2970 if (!SecCertificateIsValid(cert
, verifyTime
)) {
2971 /* Certificate has expired. */
2972 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, ix
, kCFBooleanFalse
)) {
2977 if (SecCertificateIsWeakKey(cert
)) {
2978 /* Certificate uses weak key. */
2979 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, ix
, kCFBooleanFalse
)) {
2984 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
2985 /* Certificate uses weak hash. */
2986 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, ix
, kCFBooleanFalse
)) {
2992 /* Perform anchor specific checks. */
2993 /* Don't think we have any of these. */
2995 /* Perform intermediate specific checks. */
2997 /* (k) Basic constraints only relevant for v3 and later. */
2998 if (SecCertificateVersion(cert
) >= 3) {
2999 const SecCEBasicConstraints
*bc
=
3000 SecCertificateGetBasicConstraints(cert
);
3002 /* Basic constraints not present, illegal. */
3003 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3004 ix
, kCFBooleanFalse
, true)) {
3007 } else if (!bc
->isCA
) {
3008 /* Basic constraints not marked as isCA, illegal. */
3009 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsCA
,
3010 ix
, kCFBooleanFalse
, true)) {
3015 /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
3016 not an anchor), we additionally require that the certificate chain
3017 does not end in a v3 or later anchor. [rdar://32204517] */
3018 else if (ix
> 0 && ix
< anchor_ix
) {
3019 SecCertificateRef anchor
= SecPVCGetCertificateAtIndex(pvc
, anchor_ix
);
3020 if (SecCertificateVersion(anchor
) >= 3) {
3021 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
3022 ix
, kCFBooleanFalse
, true)) {
3027 /* (l) max_path_length is checked elsewhere. */
3029 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
3030 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
3031 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
3032 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
3033 ix
, kCFBooleanFalse
, true)) {
3040 return SecPVCIsOkResult(pvc
);
3043 static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
3044 /* Check stuff common to intermediate and anchors. */
3046 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3047 if (NULL
!= otapkiRef
)
3049 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
3050 CFRelease(otapkiRef
);
3051 if (NULL
!= blackListedKeys
)
3053 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3054 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3055 bool is_last
= (ix
== count
- 1);
3056 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
3058 /* Check for blacklisted intermediate issuer keys. */
3059 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3061 /* Check dgst against blacklist. */
3062 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
3063 /* Check allow list for this blacklisted issuer key,
3064 which is the authority key of the issued cert at ix-1.
3066 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3067 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
3069 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
3070 ix
, kCFBooleanFalse
, true);
3076 CFRelease(blackListedKeys
);
3077 return SecPVCIsOkResult(pvc
);
3084 static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
3086 /* Check stuff common to intermediate and anchors. */
3087 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
3088 if (NULL
!= otapkiRef
)
3090 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
3091 CFRelease(otapkiRef
);
3092 if (NULL
!= grayListKeys
)
3094 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
3095 CFIndex count
= SecPVCGetCertificateCount(pvc
);
3096 bool is_last
= (ix
== count
- 1);
3097 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
3099 /* Check for gray listed intermediate issuer keys. */
3100 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
3102 /* Check dgst against gray list. */
3103 if (CFSetContainsValue(grayListKeys
, dgst
)) {
3104 /* Check allow list for this graylisted issuer key,
3105 which is the authority key of the issued cert at ix-1.
3107 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3108 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
3110 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
3111 ix
, kCFBooleanFalse
, true);
3117 CFRelease(grayListKeys
);
3118 return SecPVCIsOkResult(pvc
);
3125 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
3126 if (!isString(searchName
) && !isString(searchOid
)) {
3129 CFArrayRef policies
= pvc
->policies
;
3130 CFIndex ix
, count
= CFArrayGetCount(policies
);
3131 for (ix
= 0; ix
< count
; ++ix
) {
3132 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
3133 CFStringRef policyName
= SecPolicyGetName(policy
);
3134 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
3135 /* Prefer a match of both name and OID */
3136 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
3137 if (CFEqual(searchOid
, policyOid
) &&
3138 CFEqual(searchName
, policyName
)) {
3139 if (policyIX
) { *policyIX
= ix
; }
3142 /* <rdar://40617139> sslServer trust settings need to apply to evals using policyName pinning
3143 * but make sure we don't use this for SSL Client trust settings or policies. */
3144 if (CFEqual(searchOid
, policyOid
) &&
3145 CFEqual(searchName
, kSecPolicyNameSSLServer
) && !CFEqual(policyName
, kSecPolicyNameSSLClient
)) {
3146 if (policyIX
) { *policyIX
= ix
; }
3150 /* Next best is just OID. */
3151 if (!searchName
&& searchOid
&& policyOid
) {
3152 if (CFEqual(searchOid
, policyOid
)) {
3153 if (policyIX
) { *policyIX
= ix
; }
3157 if (!searchOid
&& searchName
&& policyName
) {
3158 if (CFEqual(searchName
, policyName
)) {
3159 if (policyIX
) { *policyIX
= ix
; }
3167 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
3168 if (!isString(stringValue
)) {
3171 bool result
= false;
3173 /* <rdar://27754596> Previous versions of macOS null-terminated the string, so we need to strip it. */
3174 CFStringRef tmpStringValue
= NULL
;
3175 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
3176 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
3178 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
3180 /* Some users have strings that only contain the null-termination, so we need to check that we
3181 * still have a string. */
3182 require(tmpStringValue
, out
);
3184 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3185 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3186 /* Have to look for all the possible locations of name string */
3187 CFStringRef policyString
= NULL
;
3188 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
3189 if (!policyString
) {
3190 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
3192 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
3197 CFArrayRef policyStrings
= NULL
;
3198 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
3199 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
3200 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
3208 CFReleaseNull(tmpStringValue
);
3213 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
3214 uint32_t ourTSKeyUsage
= 0;
3215 uint32_t keyUsage
= 0;
3216 if (keyUsageNumber
&&
3217 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
3218 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
3219 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
3221 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
3222 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
3224 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
3225 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
3227 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
3228 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
3230 if (keyUsage
== kSecKeyUsageAll
) {
3231 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
3234 return ourTSKeyUsage
;
3237 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
3238 uint32_t ourTSKeyUsage
= 0;
3239 CFTypeRef policyKeyUsageType
= NULL
;
3241 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
3242 if (isArray(policyKeyUsageType
)) {
3243 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
3244 for (ix
= 0; ix
< count
; ix
++) {
3245 CFNumberRef policyKeyUsageNumber
= NULL
;
3246 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
3247 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
3249 } else if (isNumber(policyKeyUsageType
)) {
3250 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
3253 return ourTSKeyUsage
;
3256 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
3257 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
3258 int64_t keyUsageValue
= 0;
3259 uint32_t ourKeyUsage
= 0;
3261 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
3265 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
3269 /* We're using the key for revocation if we have the OCSPSigner policy.
3270 * @@@ If we support CRLs, we'd need to check for that policy here too.
3272 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
3273 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
3276 /* We're using the key for verifying a cert if it's a root/intermediate
3277 * in the chain. If the cert isn't in the path yet, we're about to add it,
3278 * so it's a root/intermediate. If there is no path, this is the leaf.
3280 CFIndex pathIndex
= -1;
3281 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3283 pathIndex
= SecCertificatePathVCGetIndexOfCertificate(path
, certificate
);
3287 if (pathIndex
!= 0) {
3288 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
3291 /* The rest of the key usages may be specified by the policy(ies). */
3292 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3293 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3294 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3296 /* Get key usage from ALL policies */
3297 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
3298 for (ix
= 0; ix
< count
; ix
++) {
3299 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
3300 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3304 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
3312 #include <Security/SecTrustedApplicationPriv.h>
3313 #include <Security/SecTask.h>
3314 #include <Security/SecTaskPriv.h>
3315 #include <bsm/libbsm.h>
3316 #include <libproc.h>
3318 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
3320 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
3321 bool result
= false;
3322 audit_token_t auditToken
= {};
3323 SecTaskRef task
= NULL
;
3324 SecRequirementRef requirement
= NULL
;
3325 CFStringRef stringRequirement
= NULL
;
3327 require(appRef
&& clientAuditToken
, out
);
3328 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
3329 require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef
)appRef
, &requirement
), out
);
3330 require(requirement
, out
);
3331 require_noerr(SecRequirementsCopyString(requirement
, kSecCSDefaultFlags
, &stringRequirement
), out
);
3332 require(stringRequirement
, out
);
3334 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3335 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3336 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3338 if(errSecSuccess
== SecTaskValidateForRequirement(task
, stringRequirement
)) {
3343 CFReleaseNull(task
);
3344 CFReleaseNull(requirement
);
3345 CFReleaseNull(stringRequirement
);
3350 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
3351 if (!isDictionary(options
)) {
3356 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
3358 /* We need to run the leaf and path checks using these options. */
3359 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3360 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3362 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3363 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3366 pvc
->callbacks
= currentCallbacks
;
3368 /* Our work here is done; no need to claim a match */
3372 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
3373 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
3374 CFNumberRef keyUsageNumber
= NULL
;
3375 CFTypeRef trustedApplicationData
= NULL
;
3376 CFDictionaryRef policyOptions
= NULL
;
3378 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
3379 keyUsageMatch
= false, policyOptionMatch
= false;
3380 bool result
= false;
3383 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
3384 SecPolicyRef policy
= NULL
;
3385 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3386 policyOid
= (policy
) ? policy
->_oid
: NULL
;
3388 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3390 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
3391 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
3392 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
3393 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
3395 CFIndex policyIX
= -1;
3396 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
3397 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
3398 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
3399 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
3402 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
3403 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
3404 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
3405 CFReleaseNull(clientAuditToken
);
3407 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
3408 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
3412 /* If we either didn't find the parameter in the dictionary or we got a match
3413 * against that parameter, for all possible parameters in the dictionary, then
3414 * this trust setting result applies to the output. */
3415 if (((!policyOid
&& !policyName
) || policyMatch
) &&
3416 (!policyString
|| policyStringMatch
) &&
3417 (!trustedApplicationData
|| applicationMatch
) &&
3418 (!keyUsageNumber
|| keyUsageMatch
) &&
3419 (!policyOptions
|| policyOptionMatch
)) {
3426 static SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
3427 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
3428 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3429 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3430 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3431 if (!isDictionary(constraint
)) {
3435 CFNumberRef resultNumber
= NULL
;
3436 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
3437 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
3438 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
3439 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
3440 resultValue
= kSecTrustSettingsResultTrustRoot
;
3443 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
3444 result
= resultValue
;
3451 /* This function assumes that the input source is an anchor source */
3452 bool SecPVCIsAnchorPerConstraints(SecPVCRef pvc
, SecCertificateSourceRef source
, SecCertificateRef certificate
) {
3453 __block
bool result
= false;
3454 CFArrayRef constraints
= NULL
;
3455 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
3457 /* Unrestricted certificates:
3458 * -those that come from anchor sources with no constraints
3459 * -self-signed certificates with empty contraints arrays
3461 Boolean selfSigned
= false;
3462 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
3463 if ((NULL
== source
->copyUsageConstraints
) ||
3464 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
3465 secinfo("trust", "unrestricted anchor%s",
3466 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
3471 /* Get the trust settings result for the PVC. Only one PVC need match to
3472 * trigger the anchor behavior -- policy validation will handle whether the
3473 * path is truly anchored for that PVC. */
3474 require_quiet(constraints
, out
);
3475 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
3476 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
3479 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
3480 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
3481 // For our purposes, this is an anchor.
3482 secinfo("trust", "complex trust settings anchor");
3486 if (settingsResult
== kSecTrustSettingsResultDeny
) {
3487 /* We consider denied certs "anchors" because the trust decision
3488 is set regardless of building the chain further. The policy
3489 validation will handle rejecting this chain. */
3490 secinfo("trust", "complex trust settings denied anchor");
3495 CFReleaseNull(constraints
);
3499 static void SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
3500 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3501 for (certIX
= 0; certIX
< certCount
; certIX
++) {
3502 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3503 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
3504 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3505 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
3507 /* Set the pvc trust result based on the usage constraints and anchor source. */
3508 if (result
== kSecTrustSettingsResultDeny
) {
3509 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
3510 } else if ((result
== kSecTrustSettingsResultTrustRoot
|| result
== kSecTrustSettingsResultTrustAsRoot
||
3511 result
== kSecTrustSettingsResultInvalid
) && SecPVCIsOkResult(pvc
)) {
3512 /* If we already think the PVC is ok and this cert is from one of the user/
3513 * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
3514 * all mean we should use the special "Proceed" trust result. */
3515 #if TARGET_OS_IPHONE
3516 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecUserAnchorSource
) &&
3517 SecCertificateSourceContains(kSecUserAnchorSource
, cert
))
3519 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecLegacyAnchorSource
) &&
3520 SecCertificateSourceContains(kSecLegacyAnchorSource
, cert
))
3523 pvc
->result
= kSecTrustResultProceed
;
3529 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
3530 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
3531 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
3533 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
3534 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
3535 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
3537 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
3538 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
3539 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
3541 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
3542 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
3543 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
3545 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
3546 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
3547 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
3549 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
3550 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
3551 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
3553 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
3554 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
3555 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
3557 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
3558 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
3559 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
3562 static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
3563 static CFSetRef sConstrainedRoots
= NULL
;
3564 static dispatch_once_t _t
;
3565 dispatch_once(&_t
, ^{
3566 const UInt8
*v_hashes
[] = {
3567 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
3568 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
3570 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3571 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
3572 for (ix
=0; ix
<count
; ix
++) {
3573 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
3574 kSecPolicySHA256Size
, kCFAllocatorNull
);
3576 CFSetAddValue(set
, hash
);
3580 sConstrainedRoots
= set
;
3583 bool shouldDeny
= false;
3584 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3585 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
3586 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3587 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
3588 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
3589 /* matched a constrained root; check notBefore dates on all its children. */
3590 CFIndex childIX
= certIX
;
3591 while (--childIX
>= 0) {
3592 SecCertificateRef child
= SecPVCGetCertificateAtIndex(pvc
, childIX
);
3593 /* 1 Dec 2016 00:00:00 GMT */
3594 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
3595 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
3601 CFReleaseNull(sha256
);
3605 static bool SecPVCIsSSLServerAuthenticationPolicy(SecPVCRef pvc
) {
3606 if (!pvc
|| !pvc
->policies
) {
3609 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
3613 CFStringRef policyName
= SecPolicyGetName(policy
);
3614 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
3617 CFDictionaryRef options
= policy
->_options
;
3618 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckSSLHostname
)) {
3625 1. SecPVCCheckRequireCTConstraints must be called after SecPolicyCheckCT,
3626 so earliest issuance time has already been obtained from SCTs.
3627 2. If the issuance time value is 0 (i.e. 2001-01-01) or earlier, we
3628 assume it was not set, and thus we did not have CT info.
3630 static void SecPVCCheckRequireCTConstraints(SecPVCRef pvc
) {
3631 SecCertificatePathVCRef path
= (pvc
) ? SecPathBuilderGetPath(pvc
->builder
) : NULL
;
3635 /* If we are evaluating for a SSL server authentication policy, make sure
3636 SCT issuance time is prior to the earliest not-after date constraint.
3637 Note that CT will already be required if there is a not-after date
3638 constraint present (set in SecRVCProcessValidDateConstraints).
3640 if (SecPVCIsSSLServerAuthenticationPolicy(pvc
)) {
3641 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3642 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, 0);
3643 CFAbsoluteTime earliestNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
3644 CFAbsoluteTime issuanceTime
= SecCertificatePathVCIssuanceTime(path
);
3645 if (issuanceTime
<= 0) {
3646 /* if not set (or prior to 2001-01-01), use leaf's not-before time. */
3647 issuanceTime
= SecCertificateNotValidBefore(certificate
);
3649 for (ix
= 0; ix
< certCount
; ix
++) {
3650 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3651 if (!rvc
|| !rvc
->valid_info
|| !rvc
->valid_info
->hasDateConstraints
|| !rvc
->valid_info
->notAfterDate
) {
3654 /* Found CA certificate with a not-after date constraint. */
3655 CFAbsoluteTime caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
3656 if (caNotAfter
< earliestNotAfter
) {
3657 earliestNotAfter
= caNotAfter
;
3659 if (issuanceTime
> earliestNotAfter
) {
3660 /* Issuance time violates the not-after date constraint. */
3661 secnotice("rvc", "certificate issuance time (%f) is later than allowed value (%f)",
3662 issuanceTime
, earliestNotAfter
);
3663 SecRVCSetValidDeterminedErrorResult(rvc
);
3668 /* If path is CT validated, nothing further to do here. */
3669 if (SecCertificatePathVCIsCT(path
)) {
3673 /* Path is not CT validated, so check if CT was required. */
3674 SecPathCTPolicy ctp
= SecCertificatePathVCRequiresCT(path
);
3675 if (ctp
<= kSecPathCTNotRequired
|| !SecPVCIsSSLServerAuthenticationPolicy(pvc
)) {
3679 /* We need to have a recent log list or the CT check may have failed due to the list being out of date.
3680 * Also, honor the CT kill switch. */
3681 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3682 if (!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) &&
3683 SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
)) {
3684 /* CT was required. Error is always set on leaf certificate. */
3685 SecPVCSetResultForced(pvc
, kSecPolicyCheckCTRequired
,
3686 0, kCFBooleanFalse
, true);
3687 if (ctp
!= kSecPathCTRequiredOverridable
) {
3688 /* Normally kSecPolicyCheckCTRequired is recoverable,
3689 so need to manually change trust result here. */
3690 pvc
->result
= kSecTrustResultFatalTrustFailure
;
3693 CFReleaseNull(otaref
);
3696 /* "Deep" copy the details array */
3697 static CFArrayRef CF_RETURNS_RETAINED
SecPVCCopyDetailsArray(SecPVCRef pvc
) {
3698 CFArrayRef details
= pvc
->details
;
3699 CFMutableArrayRef copiedDetails
= CFArrayCreateMutable(NULL
, CFArrayGetCount(details
), &kCFTypeArrayCallBacks
);
3700 CFArrayForEach(details
, ^(const void *value
) {
3701 CFMutableDictionaryRef copiedValue
= CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
)value
);
3702 CFArrayAppendValue(copiedDetails
, copiedValue
);
3703 CFReleaseNull(copiedValue
);
3705 return copiedDetails
;
3708 /* AUDIT[securityd](done):
3709 policy->_options is a caller provided dictionary, only its cf type has
3712 void SecPVCPathChecks(SecPVCRef pvc
) {
3713 secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc
->builder
));
3714 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3715 /* This needs to be initialized before we call any function that might call
3716 SecPVCSetResultForced(). */
3718 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3719 if (SecPVCIsOkResult(pvc
) || pvc
->details
) {
3720 SecPolicyCheckBasicCertificateProcessing(pvc
,
3721 kSecPolicyCheckBasicCertificateProcessing
);
3724 CFArrayRef policies
= pvc
->policies
;
3725 CFIndex count
= CFArrayGetCount(policies
);
3726 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3727 /* Validate all keys for all policies. */
3728 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3729 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3730 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3731 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
3738 /* Check whether the TrustSettings say to deny a cert in the path. */
3739 SecPVCCheckUsageConstraints(pvc
);
3741 /* Check for Blocklisted certs */
3742 SecPVCCheckIssuerDateConstraints(pvc
);
3744 count
= SecCertificatePathVCGetCount(path
);
3745 for (ix
= 1; ix
< count
; ix
++) {
3746 SecPVCGrayListedKeyChecks(pvc
, ix
);
3747 SecPVCBlackListedKeyChecks(pvc
, ix
);
3750 /* Path-based check tests. */
3751 if (!SecCertificatePathVCIsPathValidated(path
)) {
3752 bool ev_check_ok
= false;
3753 if (SecCertificatePathVCIsOptionallyEV(path
)) {
3754 SecTrustResultType pre_ev_check_result
= pvc
->result
;
3755 CFArrayRef pre_ev_check_details
= pvc
->details
? SecPVCCopyDetailsArray(pvc
) : NULL
;
3756 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3757 ev_check_ok
= SecPVCIsOkResult(pvc
);
3758 /* If ev checking failed, we still want to accept this chain
3759 as a non EV one, if it was valid as such. */
3760 pvc
->result
= pre_ev_check_result
;
3761 CFAssignRetained(pvc
->details
, pre_ev_check_details
);
3765 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3766 SecPolicyCheckCT(pvc
);
3768 /* Certs are only EV if they are also CT verified (when the Kill Switch isn't enabled and against a recent log list) */
3769 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3770 if (ev_check_ok
&& (SecCertificatePathVCIsCT(path
) || SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) ||
3771 !SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
))) {
3772 SecCertificatePathVCSetIsEV(path
, true);
3774 CFReleaseNull(otaref
);
3777 /* Say that we did the expensive path checks (that we want to skip on the second call) */
3778 SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc
->builder
));
3780 /* Check that this path meets CT constraints. */
3781 SecPVCCheckRequireCTConstraints(pvc
);
3782 SecPolicyCheckSystemTrustedCTRequired(pvc
);
3784 /* Check that this path meets known-intermediate constraints. */
3785 SecPathBuilderCheckKnownIntermediateConstraints(pvc
->builder
);
3787 secdebug("policy", "end %strusted path: %@",
3788 (SecPVCIsOkResult(pvc
) ? "" : "not "), SecPathBuilderGetPath(pvc
->builder
));
3793 void SecPVCPathCheckRevocationResponsesReceived(SecPVCRef pvc
) {
3795 /* Since we don't currently allow networking on watchOS,
3796 * don't enforce the revocation-required check here. (32728029) */
3797 bool required
= false;
3799 bool required
= true;
3801 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3802 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3803 for (ix
= 0; ix
< certCount
; ix
++) {
3804 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3805 /* Do we have a valid revocation response? */
3806 if (SecRVCGetEarliestNextUpdate(rvc
) == NULL_TIME
) {
3807 /* No valid revocation response.
3808 * Do we require revocation (for that cert per the
3809 * SecCertificateVCRef, or per the pvc)? */
3810 if (required
&& (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path
, ix
) ||
3811 ((ix
== 0) && pvc
->require_revocation_response
))) {
3812 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
3813 ix
, kCFBooleanFalse
, true);
3815 /* Do we have a definitive Valid revocation result for this cert? */
3816 if (SecRVCHasDefinitiveValidInfo(rvc
) && SecRVCHasRevokedValidInfo(rvc
)) {
3817 SecRVCSetValidDeterminedErrorResult(rvc
);