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
);
102 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
103 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
105 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
107 CFArrayRef result
= NULL
;
108 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
109 if (NULL
== otapkiRef
)
114 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
115 CFRelease(otapkiRef
);
117 if (NULL
== evToPolicyAnchorDigest
)
122 CFArrayRef roots
= NULL
;
123 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
124 if (oid
&& evToPolicyAnchorDigest
)
126 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
127 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
129 secerror("EVRoot.plist has non array value");
134 CFReleaseSafe(evToPolicyAnchorDigest
);
139 bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
140 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
143 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
144 policy_set_t valid_policies
) {
145 CFDictionaryRef keySizes
= NULL
;
146 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
148 /* Ensure that this certificate is a valid anchor for one of the
149 certificate policy oids specified in the leaf. */
150 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
152 bool good_ev_anchor
= false;
153 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
154 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
155 if (digests
&& CFArrayContainsValue(digests
,
156 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
157 secdebug("ev", "found anchor for policy oid");
158 good_ev_anchor
= true;
162 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
164 CFAbsoluteTime october2006
= 178761600;
165 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
166 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
167 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
169 if (SecCertificateVersion(certificate
) >= 3
170 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
171 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
172 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
173 secnotice("ev", "Anchor has invalid basic constraints"));
174 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
175 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
176 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
177 secnotice("ev", "Anchor has invalid key usage %u", ku
));
180 /* At least RSA 2048 or ECC NIST P-256. */
181 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
182 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
183 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
184 const void *values
[] = { rsaSize
, ecSize
};
185 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
186 &kCFTypeDictionaryKeyCallBacks
,
187 &kCFTypeDictionaryValueCallBacks
), notEV
);
188 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
189 secnotice("ev", "Anchor's public key is too weak for EV"));
194 CFReleaseNull(rsaSize
);
195 CFReleaseNull(ecSize
);
196 CFReleaseNull(keySizes
);
200 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
201 CFMutableDictionaryRef keySizes
= NULL
;
202 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
205 const SecCECertificatePolicies
*cp
;
206 cp
= SecCertificateGetCertificatePolicies(certificate
);
207 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
208 secnotice("ev", "SubCA missing certificate policies"));
209 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
210 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
211 secnotice("ev", "SubCA missing CRLDP"));
212 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
213 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
214 secnotice("ev", "SubCA has invalid basic constraints"));
215 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
216 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
217 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
218 secnotice("ev", "SubCA has invalid key usage %u", ku
));
220 /* 6.1.5 Key Sizes */
221 CFAbsoluteTime jan2011
= 315532800;
222 CFAbsoluteTime jan2014
= 410227200;
223 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
224 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
225 &kCFTypeDictionaryValueCallBacks
), notEV
);
226 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
227 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
228 SecCertificateNotValidAfter(certificate
) < jan2014
) {
229 /* At least RSA 1024 or ECC NIST P-256. */
230 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
231 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
232 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
233 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
235 /* At least RSA 2028 or ECC NIST P-256. */
236 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
237 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
238 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
239 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
242 /* 7.1.3 Algorithm Object Identifiers */
243 CFAbsoluteTime jan2016
= 473299200;
244 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
246 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
247 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
253 CFReleaseNull(rsaSize
);
254 CFReleaseNull(ecSize
);
255 CFReleaseNull(keySizes
);
259 /********************************************************
260 **************** SecPolicy Callbacks *******************
261 ********************************************************/
262 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
266 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
268 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
269 CFDataRef parentSubjectKeyID
= NULL
;
270 for (ix
= count
- 1; ix
>= 0; --ix
) {
271 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
272 /* If the previous certificate in the chain had a SubjectKeyID,
273 make sure it matches the current certificates AuthorityKeyID. */
274 if (parentSubjectKeyID
) {
275 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
276 SubjectKeyID can be critical. Currenty we don't check
278 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
279 if (authorityKeyID
) {
280 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
281 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
282 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
288 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
292 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
294 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
295 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
296 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
297 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
298 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
302 /* AUDIT[securityd](done):
303 policy->_options is a caller provided dictionary, only its cf type has
306 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
307 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
308 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
309 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
310 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
311 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
315 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
317 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
320 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
322 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
323 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
324 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
325 for (ix
= 0; ix
< count
; ++ix
) {
326 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
327 if (!SecPolicyCheckCertNonEmptySubject(cert
, pvcValue
)) {
328 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
334 /* AUDIT[securityd](done):
335 policy->_options is a caller provided dictionary, only its cf type has
338 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
340 /* @@@ Consider what to do if the caller passes in no hostname. Should
341 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
342 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
343 CFStringRef hostName
= (CFStringRef
)
344 CFDictionaryGetValue(policy
->_options
, key
);
345 if (!isString(hostName
)) {
346 /* @@@ We can't return an error here and making the evaluation fail
347 won't help much either. */
351 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
352 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
355 /* Hostname mismatch or no hostnames found in certificate. */
356 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
361 /* AUDIT[securityd](done):
362 policy->_options is a caller provided dictionary, only its cf type has
365 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
366 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
367 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
368 if (!isString(email
)) {
369 /* We can't return an error here and making the evaluation fail
370 won't help much either. */
374 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
376 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
377 /* Hostname mismatch or no hostnames found in certificate. */
378 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
382 static void SecPolicyCheckTemporalValidity(SecPVCRef pvc
,
384 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
385 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
386 for (ix
= 0; ix
< count
; ++ix
) {
387 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
388 if (!SecCertificateIsValid(cert
, verifyTime
)) {
389 /* Intermediate certificate has expired. */
390 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
396 /* AUDIT[securityd](done):
397 policy->_options is a caller provided dictionary, only its cf type has
400 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
402 CFIndex count
= SecPVCGetCertificateCount(pvc
);
404 /* Can't check intermediates common name if there is no intermediate. */
405 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
409 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
410 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
411 CFStringRef commonName
=
412 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
413 if (!isString(commonName
)) {
414 /* @@@ We can't return an error here and making the evaluation fail
415 won't help much either. */
418 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
419 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
423 /* AUDIT[securityd](done):
424 policy->_options is a caller provided dictionary, only its cf type has
427 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
429 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
430 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
431 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
433 if (!isString(common_name
)) {
434 /* @@@ We can't return an error here and making the evaluation fail
435 won't help much either. */
438 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
439 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
443 /* AUDIT[securityd](done):
444 policy->_options is a caller provided dictionary, only its cf type has
447 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
449 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
450 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
451 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
453 if (!isString(prefix
)) {
454 /* @@@ We can't return an error here and making the evaluation fail
455 won't help much either. */
458 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
459 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
463 /* AUDIT[securityd](done):
464 policy->_options is a caller provided dictionary, only its cf type has
467 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
469 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
470 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
471 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
473 if (!isString(common_name
)) {
474 /* @@@ We can't return an error here and making the evaluation fail
475 won't help much either. */
478 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
479 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
483 /* AUDIT[securityd](done):
484 policy->_options is a caller provided dictionary, only its cf type has
487 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
489 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
490 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
491 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
493 /* @@@ We can't return an error here and making the evaluation fail
494 won't help much either. */
497 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
498 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
503 /* AUDIT[securityd](done):
504 policy->_options is a caller provided dictionary, only its cf type has
507 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
509 CFIndex count
= SecPVCGetCertificateCount(pvc
);
510 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
511 CFNumberRef chainLength
=
512 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
514 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
515 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
516 /* @@@ We can't return an error here and making the evaluation fail
517 won't help much either. */
520 if (value
!= count
) {
521 /* Chain length doesn't match policy requirement. */
522 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
527 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
528 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
529 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
531 bool foundMatch
= false;
533 foundMatch
= CFEqual(digest
, value
);
534 else if (isArray(value
))
535 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
537 /* @@@ We only support Data and Array but we can't return an error here so.
538 we let the evaluation fail (not much help) and assert in debug. */
545 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
546 CFIndex count
= SecPVCGetCertificateCount(pvc
);
547 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
548 CFDataRef anchorSHA256
= NULL
;
549 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
551 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
552 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
555 CFReleaseNull(anchorSHA256
);
560 /* AUDIT[securityd](done):
561 policy->_options is a caller provided dictionary, only its cf type has
564 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
566 CFIndex count
= SecPVCGetCertificateCount(pvc
);
567 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
568 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
570 if (!isDigestInPolicy(pvc
, key
, anchorSHA1
))
571 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, count
-1, kCFBooleanFalse
))
578 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
579 policy->_options is a caller provided dictionary, only its cf type has
582 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
584 SecCertificateRef cert
= NULL
;
585 CFDataRef digest
= NULL
;
587 if (SecPVCGetCertificateCount(pvc
) < 2) {
588 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
592 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
593 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
595 if (!isDigestInPolicy(pvc
, key
, digest
)) {
596 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
598 CFReleaseNull(digest
);
602 policy->_options is a caller provided dictionary, only its cf type has
605 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
607 CFIndex count
= SecPVCGetCertificateCount(pvc
);
608 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
609 SecAppleTrustAnchorFlags flags
= 0;
612 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
615 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
622 /* AUDIT[securityd](done):
623 policy->_options is a caller provided dictionary, only its cf type has
626 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
628 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
629 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
630 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
632 if (!isString(org
)) {
633 /* @@@ We can't return an error here and making the evaluation fail
634 won't help much either. */
637 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
638 /* Leaf Subject Organization mismatch. */
639 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
643 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
645 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
646 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
647 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
649 if (!isString(orgUnit
)) {
650 /* @@@ We can't return an error here and making the evaluation fail
651 won't help much either. */
654 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
655 /* Leaf Subject Organization mismatch. */
656 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
660 /* AUDIT[securityd](done):
661 policy->_options is a caller provided dictionary, only its cf type has
664 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
666 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
667 CFArrayRef trustedServerNames
= (CFArrayRef
)
668 CFDictionaryGetValue(policy
->_options
, key
);
669 /* No names specified means we accept any name. */
670 if (!trustedServerNames
)
672 if (!isArray(trustedServerNames
)) {
673 /* @@@ We can't return an error here and making the evaluation fail
674 won't help much either. */
678 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
679 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
680 /* Hostname mismatch or no hostnames found in certificate. */
681 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
685 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
686 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
687 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
688 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
689 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
690 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
691 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
692 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
693 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
694 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
696 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
697 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
698 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
699 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
700 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
701 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
702 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
703 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
704 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
705 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
706 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
707 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
708 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
709 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
711 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
714 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
716 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
717 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
719 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
720 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
721 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
723 CFDataRef serial
= SecCertificateCopySerialNumberData(cert
, NULL
);
725 CFIndex serial_length
= CFDataGetLength(serial
);
726 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
728 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
733 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
735 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
737 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
738 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
740 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
741 CFReleaseSafe(serial
);
750 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
751 if (NULL
!= otapkiRef
)
753 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
754 CFRelease(otapkiRef
);
755 if (NULL
!= blackListedKeys
)
757 /* Check for blacklisted intermediates keys. */
758 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
761 /* Check dgst against blacklist. */
762 if (CFSetContainsValue(blackListedKeys
, dgst
))
764 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
768 CFRelease(blackListedKeys
);
773 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
775 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
776 if (NULL
!= otapkiRef
)
778 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
779 CFRelease(otapkiRef
);
780 if (NULL
!= grayListedKeys
)
782 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
784 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
787 /* Check dgst against gray. */
788 if (CFSetContainsValue(grayListedKeys
, dgst
))
790 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
794 CFRelease(grayListedKeys
);
799 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
801 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
802 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
803 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
805 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
806 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
810 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
812 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
813 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
814 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
816 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
817 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
823 * The value is a dictionary. The dictionary contains keys indicating
824 * whether the value is for Prod or QA. The values are the same as
825 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
827 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
829 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
830 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
831 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
832 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
834 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
837 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
842 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
844 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
845 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
846 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
848 for (ix
= 1; ix
< count
- 1; ix
++) {
849 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
850 if (SecCertificateHasMarkerExtension(cert
, value
))
853 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
856 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
858 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
859 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
860 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
862 for (ix
= 1; ix
< count
- 1; ix
++) {
863 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
864 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
865 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
870 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
872 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
873 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
874 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
876 for (ix
= 1; ix
< count
- 1; ix
++) {
877 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
878 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
879 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
884 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
886 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
887 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
888 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
890 for (ix
= 1; ix
< count
- 1; ix
++) {
891 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
892 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
893 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
898 /****************************************************************************
899 *********************** New rfc5280 Chain Validation ***********************
900 ****************************************************************************/
902 #define POLICY_MAPPING 1
903 #define POLICY_SUBTREES 1
905 /* rfc5280 basic cert processing. */
906 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
910 CFIndex count
= SecPVCGetCertificateCount(pvc
);
911 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
912 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
913 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
914 uint32_t n
= (uint32_t)count
;
916 bool is_anchored
= SecPathBuilderIsAnchored(pvc
->builder
);
917 bool is_anchor_trusted
= false;
919 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, n
- 1);
920 if (CFArrayGetCount(constraints
) == 0) {
921 /* Given that the path builder has already indicated the last cert in this chain has
922 * trust set on it, empty constraints means trusted. */
923 is_anchor_trusted
= true;
925 /* Determine whether constraints say to trust this cert for this PVC. */
926 SecTrustSettingsResult tsResult
= SecPVCGetTrustSettingsResult(pvc
, SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1),
928 if (tsResult
== kSecTrustSettingsResultTrustRoot
|| tsResult
== kSecTrustSettingsResultTrustAsRoot
) {
929 is_anchor_trusted
= true;
934 if (is_anchor_trusted
) {
935 /* If the anchor is trusted we don't process the last cert in the
939 Boolean isSelfSigned
= false;
940 (void) SecCertificateIsSelfSigned(SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1), &isSelfSigned
);
942 /* Add a detail for the root not being trusted. */
943 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
944 n
- 1, kCFBooleanFalse
, true)) {
948 /* Add a detail for the missing intermediate. */
949 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckMissingIntermediate
,
950 n
- 1, kCFBooleanFalse
, true)) {
956 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
957 //policy_set_t user_initial_policy_set = NULL;
958 //trust_anchor_t anchor;
962 CFMutableArrayRef permitted_subtrees
= NULL
;
963 CFMutableArrayRef excluded_subtrees
= NULL
;
964 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
965 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
966 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
967 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
968 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
969 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
972 if (!SecCertificatePathVCVerifyPolicyTree(path
, is_anchor_trusted
)) {
973 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckPolicyConstraints
, 0, kCFBooleanFalse
, true)) {
979 /* Path builder ensures we only get cert chains with proper issuer
980 chaining with valid signatures along the way. */
981 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
982 SecKeyRef working_public_key
= anchor
->public_key
;
983 x500_name_t working_issuer_name
= anchor
->issuer_name
;
985 uint32_t i
, max_path_length
= n
;
986 SecCertificateRef cert
= NULL
;
987 for (i
= 1; i
<= n
; ++i
) {
989 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
990 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc
->builder
), n
- i
);
992 /* (a) Verify the basic certificate information. */
993 /* @@@ Ensure that cert was signed with working_public_key_algorithm
994 using the working_public_key and the working_public_key_parameters. */
996 /* Already done by chain builder. */
997 if (!SecCertificateIsValid(cert
, verify_time
)) {
998 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, n
- i
, kCFBooleanFalse
)) {
1002 if (SecCertificateIsWeakKey(cert
)) {
1003 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, n
- i
, kCFBooleanFalse
)) {
1007 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
1008 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, n
- i
, kCFBooleanFalse
)) {
1013 /* @@@ cert.issuer == working_issuer_name. */
1017 if (!is_self_issued
|| i
== n
) {
1019 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1020 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1021 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1022 secnotice("policy", "name in excluded subtrees");
1023 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1026 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1027 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1028 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1029 secnotice("policy", "name not in permitted subtrees");
1030 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1035 /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
1037 /* If Last Cert in Path */
1041 /* Prepare for Next Cert */
1042 /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
1043 /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
1045 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1047 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1048 if (permitted_subtrees_in_cert
) {
1049 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1052 // could do something smart here to avoid inserting the exact same constraint
1053 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1054 if (excluded_subtrees_in_cert
) {
1055 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1056 CFRange range
= { 0, num_trees
};
1057 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1060 /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
1062 /* (k) Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1065 if (!is_self_issued
) {
1066 if (max_path_length
> 0) {
1069 /* max_path_len exceeded, illegal. */
1070 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsPathLen
,
1071 n
- i
, kCFBooleanFalse
, true)) {
1077 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(cert
);
1078 if (bc
&& bc
->pathLenConstraintPresent
1079 && bc
->pathLenConstraint
< max_path_length
) {
1080 max_path_length
= bc
->pathLenConstraint
;
1082 #if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1083 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1084 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1085 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1086 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1087 n
- i
, kCFBooleanFalse
, true)) {
1092 /* (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. */
1093 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1094 /* Certificate contains one or more unknown critical extensions. */
1095 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1096 n
- i
, kCFBooleanFalse
)) {
1100 } /* end loop over certs in path */
1102 /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
1105 /* 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
1106 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1108 /* (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. */
1109 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1110 /* Certificate contains one or more unknown critical extensions. */
1111 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1112 0, kCFBooleanFalse
)) {
1116 /* (g) done by SecCertificatePathVCVerifyPolicyTree */
1119 CFReleaseNull(permitted_subtrees
);
1120 CFReleaseNull(excluded_subtrees
);
1123 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1124 policy_set_t policies
= NULL
;
1125 const SecCECertificatePolicies
*cp
=
1126 SecCertificateGetCertificatePolicies(cert
);
1127 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1128 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1129 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1134 static void SecPolicyCheckEV(SecPVCRef pvc
,
1136 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1137 policy_set_t valid_policies
= NULL
;
1139 /* 6.1.7. Key Usage Purposes */
1141 CFAbsoluteTime jul2016
= 489024000;
1142 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1143 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1144 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1145 if (SecPVCSetResultForced(pvc
, key
,
1146 0, kCFBooleanFalse
, true)) {
1152 for (ix
= 0; ix
< count
; ++ix
) {
1153 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1154 policy_set_t policies
= policies_for_cert(cert
);
1157 /* anyPolicy in the leaf isn't allowed for EV, so only init
1158 valid_policies if we have real policies. */
1159 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1160 valid_policies
= policies
;
1163 } else if (ix
< count
- 1) {
1164 /* Subordinate CA */
1165 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1166 secnotice("ev", "subordinate certificate is not ev");
1167 if (SecPVCSetResultForced(pvc
, key
,
1168 ix
, kCFBooleanFalse
, true)) {
1169 policy_set_free(valid_policies
);
1170 policy_set_free(policies
);
1174 policy_set_intersect(&valid_policies
, policies
);
1177 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1178 secnotice("ev", "anchor certificate is not ev");
1179 if (SecPVCSetResultForced(pvc
, key
,
1180 ix
, kCFBooleanFalse
, true)) {
1181 policy_set_free(valid_policies
);
1182 policy_set_free(policies
);
1187 policy_set_free(policies
);
1188 if (!valid_policies
) {
1189 secnotice("ev", "valid_policies set is empty: chain not ev");
1190 /* If we ever get into a state where no policies are valid anymore
1191 this can't be an ev chain. */
1192 if (SecPVCSetResultForced(pvc
, key
,
1193 ix
, kCFBooleanFalse
, true)) {
1199 policy_set_free(valid_policies
);
1201 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1202 Subscriber MUST contain an OID defined by the CA in the certificate’s
1203 certificatePolicies extension that: (i) indicates which CA policy statement relates
1204 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1205 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1206 marks the certificate as being an EV Certificate.
1207 (b) EV Subordinate CA Certificates
1208 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1209 CA MUST contain one or more OIDs defined by the issuing CA that
1210 explicitly identify the EV Policies that are implemented by the Subordinate
1212 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1213 MAY contain the special anyPolicy OID (2.5.29.32.0).
1214 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1215 certificatePolicies or extendedKeyUsage extensions.
1221 * MARK: Certificate Transparency support
1227 Version sct_version; // 1 byte
1228 LogID id; // 32 bytes
1229 uint64 timestamp; // 8 bytes
1230 CtExtensions extensions; // 2 bytes len field, + n bytes data
1231 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1232 Version sct_version;
1233 SignatureType signature_type = certificate_timestamp;
1235 LogEntryType entry_type;
1236 select(entry_type) {
1237 case x509_entry: ASN.1Cert;
1238 case precert_entry: PreCert;
1240 CtExtensions extensions;
1242 } SignedCertificateTimestamp;
1246 #include <Security/SecureTransportPriv.h>
1249 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1252 case SSL_SignatureAlgorithmRSA
:
1254 case SSL_HashAlgorithmSHA1
:
1255 return &CSSMOID_SHA1WithRSA
;
1256 case SSL_HashAlgorithmSHA256
:
1257 return &CSSMOID_SHA256WithRSA
;
1258 case SSL_HashAlgorithmSHA384
:
1259 return &CSSMOID_SHA384WithRSA
;
1263 case SSL_SignatureAlgorithmECDSA
:
1265 case SSL_HashAlgorithmSHA1
:
1266 return &CSSMOID_ECDSA_WithSHA1
;
1267 case SSL_HashAlgorithmSHA256
:
1268 return &CSSMOID_ECDSA_WithSHA256
;
1269 case SSL_HashAlgorithmSHA384
:
1270 return &CSSMOID_ECDSA_WithSHA384
;
1282 static size_t SSLDecodeUint16(const uint8_t *p
)
1284 return (p
[0]<<8 | p
[1]);
1287 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1289 p
[0] = (len
>> 8)&0xff;
1290 p
[1] = (len
& 0xff);
1294 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1296 p
[0] = (len
>> 16)&0xff;
1297 p
[1] = (len
>> 8)&0xff;
1298 p
[2] = (len
& 0xff);
1304 uint64_t SSLDecodeUint64(const uint8_t *p
)
1307 for(int i
=0; i
<8; i
++) {
1314 #include <libDER/DER_CertCrl.h>
1315 #include <libDER/DER_Encode.h>
1316 #include <libDER/asn1Types.h>
1319 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1321 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1323 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
1325 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
1327 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1328 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
1329 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
1335 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
1337 SecCertificateRef leafCert
= NULL
;
1338 SecCertificateRef issuer
= NULL
;
1339 CFDataRef issuerKeyHash
= NULL
;
1340 CFDataRef tbs_precert
= NULL
;
1341 CFMutableDataRef data
= NULL
;
1343 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1344 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1345 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1347 require(leafCert
, out
);
1348 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
1349 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
1350 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
1352 require(issuerKeyHash
, out
);
1353 require(tbs_precert
, out
);
1354 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1355 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1357 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1358 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
1359 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
1360 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
1363 CFReleaseSafe(issuerKeyHash
);
1364 CFReleaseSafe(tbs_precert
);
1369 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
1371 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
1375 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
1377 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
1384 If the 'sct' is valid, add it to the validatingLogs dictionary.
1387 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
1389 - entry_type: 0 for x509 cert, 1 for precert.
1390 - entry: the cert or precert data.
1391 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
1392 - trustedLog: Dictionary contain the Trusted Logs.
1394 The SCT is valid if:
1395 - It decodes properly.
1396 - Its timestamp is less than 'verifyTime'.
1397 - It is signed by a log in 'trustedLogs'.
1398 - If entry_type = 0, the log must be currently qualified.
1399 - If entry_type = 1, the log may be expired.
1401 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
1402 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.
1407 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFArrayRef trustedLogs
, CFAbsoluteTime
*sct_at
)
1410 const uint8_t *logID
;
1411 const uint8_t *timestampData
;
1413 size_t extensionsLen
;
1414 const uint8_t *extensionsData
;
1417 size_t signatureLen
;
1418 const uint8_t *signatureData
;
1419 SecKeyRef pubKey
= NULL
;
1420 uint8_t *signed_data
= NULL
;
1421 const SecAsn1Oid
*oid
= NULL
;
1423 CFDataRef logIDData
= NULL
;
1424 CFDictionaryRef result
= 0;
1426 const uint8_t *p
= CFDataGetBytePtr(sct
);
1427 size_t len
= CFDataGetLength(sct
);
1429 require(len
>=43, out
);
1431 version
= p
[0]; p
++; len
--;
1432 logID
= p
; p
+=32; len
-=32;
1433 timestampData
= p
; p
+=8; len
-=8;
1434 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1436 require(len
>=extensionsLen
, out
);
1437 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
1439 require(len
>=4, out
);
1440 hashAlg
=p
[0]; p
++; len
--;
1441 sigAlg
=p
[0]; p
++; len
--;
1442 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1443 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
1446 /* verify version: only v1(0) is supported */
1448 secerror("SCT version unsupported: %d\n", version
);
1452 /* verify timestamp not in the future */
1453 timestamp
= SSLDecodeUint64(timestampData
);
1454 if(timestamp
> vt
) {
1455 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
1462 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
1463 signed_data
= malloc(signed_data_len
);
1464 require(signed_data
, out
);
1467 *q
++ = 0; // certificate_timestamp
1468 memcpy(q
, timestampData
, 8); q
+=8;
1469 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
1470 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
1471 q
= SSLEncodeUint16(q
, extensionsLen
);
1472 memcpy(q
, extensionsData
, extensionsLen
);
1474 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
1476 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
1477 const void *key_data
;
1478 if(!isDictionary(dict
)) return false;
1479 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
1480 if(!isData(key_data
)) return false;
1481 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
1482 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
1483 CFReleaseSafe(valueID
);
1486 require(logData
, out
);
1489 // For external SCTs, only keep SCTs from currently valid logs.
1490 require(!CFDictionaryContainsKey(logData
, CFSTR("expiry")), out
);
1493 CFAbsoluteTime sct_time
= TimestampToCFAbsoluteTime(timestamp
);
1494 CFDateRef frozen_date
= CFDictionaryGetValue(logData
, CFSTR("frozen"));
1495 if (frozen_date
&& (sct_time
> CFDateGetAbsoluteTime(frozen_date
))) {
1496 secerror("Frozen CT log issued SCT after freezing (log=%@)\n", logData
);
1500 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
1501 require(logKeyData
, out
); // This failing would be an internal logic error
1502 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
1503 require(pubKey
, out
);
1505 oid
= oidForSigAlg(hashAlg
, sigAlg
);
1508 algId
.algorithm
= *oid
;
1509 algId
.parameters
.Data
= NULL
;
1510 algId
.parameters
.Length
= 0;
1512 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
1516 secerror("SCT signature failed (log=%@)\n", logData
);
1520 CFReleaseSafe(logIDData
);
1521 CFReleaseSafe(pubKey
);
1527 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
1529 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
1531 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
1532 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
1533 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
1534 CFReleaseSafe(sct_time
);
1538 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
1540 CFMutableArrayRef SCTs
= NULL
;
1541 SecCertificateRef leafCert
= NULL
;
1542 SecCertificateRef issuer
= NULL
;
1543 CFArrayRef ocspResponsesData
= NULL
;
1544 SecOCSPRequestRef ocspRequest
= NULL
;
1546 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
1547 require_quiet(ocspResponsesData
, out
);
1549 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1550 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1551 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1553 require(leafCert
, out
);
1554 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
1555 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
1557 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1560 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
1561 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
1562 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
1563 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
1564 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
1565 if(ocspSingleResponse
) {
1566 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
1567 if(singleResponseSCTs
) {
1568 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
1569 CFRelease(singleResponseSCTs
);
1571 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
1574 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
1577 if(CFArrayGetCount(SCTs
)==0) {
1578 CFReleaseNull(SCTs
);
1582 CFReleaseSafe(ocspResponsesData
);
1584 SecOCSPRequestFinalize(ocspRequest
);
1589 static void SecPolicyCheckCT(SecPVCRef pvc
)
1591 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1592 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
1593 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
1594 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
1595 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
1596 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
1597 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
1598 __block
uint32_t trustedSCTCount
= 0;
1599 __block CFAbsoluteTime issuanceTime
= SecPVCGetVerifyTime(pvc
);
1600 TA_CTFailureReason failureReason
= TA_CTNoFailure
;
1603 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1604 trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
1605 CFReleaseSafe(otapkiref
);
1608 // This eventually contain list of logs who validated the SCT.
1609 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1610 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1612 uint64_t vt
= TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc
));
1614 __block
bool at_least_one_currently_valid_external
= 0;
1615 __block
bool at_least_one_currently_valid_embedded
= 0;
1616 __block
bool unknown_log
= 0;
1617 __block
bool disqualified_log
= 0;
1619 require(logsValidatingEmbeddedScts
, out
);
1620 require(currentLogsValidatingScts
, out
);
1622 /* Skip if there are no SCTs. */
1623 require_action((embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) ||
1624 (builderScts
&& CFArrayGetCount(builderScts
) > 0) ||
1625 (ocspScts
&& CFArrayGetCount(ocspScts
) > 0),
1627 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1629 analytics
->ct_failure_reason
= TA_CTNoSCTs
;
1633 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
1634 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
1635 CFArrayForEach(embeddedScts
, ^(const void *value
){
1636 CFAbsoluteTime sct_at
;
1637 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, trustedLogs
, &sct_at
);
1639 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
1640 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
1641 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1642 at_least_one_currently_valid_embedded
= true;
1645 disqualified_log
= true;
1653 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
1654 CFArrayForEach(builderScts
, ^(const void *value
){
1655 CFAbsoluteTime sct_at
;
1656 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
1658 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1659 at_least_one_currently_valid_external
= true;
1667 if(ocspScts
&& x509Entry
) {
1668 CFArrayForEach(ocspScts
, ^(const void *value
){
1669 CFAbsoluteTime sct_at
;
1670 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
1672 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1673 at_least_one_currently_valid_external
= true;
1681 failureReason
= TA_CTMissingLogs
;
1685 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
1688 is_ct = (A1 AND A2) OR (B1 AND B2).
1690 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
1691 A2: At least one embedded SCT from a currently valid log.
1693 B1: SCTs from 2 currently valid logs (from any source)
1694 B2: At least 1 external SCT from a currently valid log.
1698 bool hasValidExternalSCT
= (at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2);
1699 bool hasValidEmbeddedSCT
= (at_least_one_currently_valid_embedded
);
1700 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
1701 SecCertificatePathVCSetIsCT(path
, false);
1703 if (hasValidEmbeddedSCT
) {
1704 /* Calculate issuance time based on timestamp of SCTs from current logs */
1705 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
1706 CFDictionaryRef log
= key
;
1707 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
1708 // Log is still qualified
1709 CFDateRef ts
= (CFDateRef
) value
;
1710 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
1711 if(timestamp
< issuanceTime
) {
1712 issuanceTime
= timestamp
;
1716 SecCertificatePathVCSetIssuanceTime(path
, issuanceTime
);
1718 if (hasValidExternalSCT
) {
1719 /* Note: since external SCT validates this cert, we do not need to
1720 override issuance time here. If the cert also has a valid embedded
1721 SCT, issuanceTime will be calculated and set in the block above. */
1722 SecCertificatePathVCSetIsCT(path
, true);
1723 } else if (hasValidEmbeddedSCT
) {
1724 __block
int lifetime
; // in Months
1725 __block
unsigned once_or_current_qualified_embedded
= 0;
1728 __block
bool failed_once_check
= false;
1729 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
1730 CFDictionaryRef log
= key
;
1731 CFDateRef ts
= value
;
1732 CFDateRef expiry
= CFDictionaryGetValue(log
, CFSTR("expiry"));
1733 if (expiry
== NULL
) { // Currently qualified OR
1734 once_or_current_qualified_embedded
++;
1735 } else if (CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
&& // Once qualified. That is, qualified at the time of SCT AND
1736 issuanceTime
< CFDateGetAbsoluteTime(expiry
)) { // at the time of issuance.)
1737 once_or_current_qualified_embedded
++;
1740 failed_once_check
= true;
1744 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
1746 CFCalendarGetComponentDifference(zuluCalendar
,
1747 SecCertificateNotValidBefore(leafCert
),
1748 SecCertificateNotValidAfter(leafCert
),
1749 0, "M", &_lifetime
);
1750 lifetime
= _lifetime
;
1753 unsigned requiredEmbeddedSctsCount
;
1755 if (lifetime
< 15) {
1756 requiredEmbeddedSctsCount
= 2;
1757 } else if (lifetime
<= 27) {
1758 requiredEmbeddedSctsCount
= 3;
1759 } else if (lifetime
<= 39) {
1760 requiredEmbeddedSctsCount
= 4;
1762 requiredEmbeddedSctsCount
= 5;
1765 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
1766 SecCertificatePathVCSetIsCT(path
, true);
1768 /* Not enough "once or currently qualified" SCTs */
1769 if (failed_once_check
) {
1770 failureReason
= TA_CTEmbeddedNotEnoughDisqualified
;
1771 } else if (unknown_log
) {
1772 failureReason
= TA_CTEmbeddedNotEnoughUnknown
;
1774 failureReason
= TA_CTEmbeddedNotEnough
;
1777 } else if (!at_least_one_currently_valid_embedded
&& !at_least_one_currently_valid_external
) {
1778 /* No currently valid SCTs */
1779 if (disqualified_log
) {
1780 failureReason
= TA_CTNoCurrentSCTsDisqualifiedLog
;
1781 } else if (unknown_log
) {
1782 failureReason
= TA_CTNoCurrentSCTsUnknownLog
;
1784 } else if (at_least_one_currently_valid_external
) {
1785 /* One presented current SCT but failed total current check */
1786 if (disqualified_log
) {
1787 failureReason
= TA_CTPresentedNotEnoughDisqualified
;
1788 } else if (unknown_log
) {
1789 failureReason
= TA_CTPresentedNotEnoughUnknown
;
1791 failureReason
= TA_CTPresentedNotEnough
;
1795 /* Record analytics data for CT */
1796 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1797 require_quiet(analytics
, out
);
1798 uint32_t sctCount
= 0;
1799 /* Count the total number of SCTs we found and report where we got them */
1800 if (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) {
1801 analytics
->sct_sources
|= TA_SCTEmbedded
;
1802 sctCount
+= CFArrayGetCount(embeddedScts
);
1804 if (builderScts
&& CFArrayGetCount(builderScts
) > 0) {
1805 analytics
->sct_sources
|= TA_SCT_TLS
;
1806 sctCount
+= CFArrayGetCount(builderScts
);
1808 if (ocspScts
&& CFArrayGetCount(ocspScts
) > 0) {
1809 analytics
->sct_sources
|= TA_SCT_OCSP
;
1810 sctCount
+= CFArrayGetCount(ocspScts
);
1812 /* Report how many of those SCTs were once or currently qualified */
1813 analytics
->number_trusted_scts
= trustedSCTCount
;
1814 /* Report how many SCTs we got */
1815 analytics
->number_scts
= sctCount
;
1817 analytics
->ct_failure_reason
= failureReason
;
1818 /* Only one current SCT -- close to failure */
1819 if (CFDictionaryGetCount(currentLogsValidatingScts
) == 1) {
1820 analytics
->ct_one_current
= true;
1823 CFReleaseSafe(logsValidatingEmbeddedScts
);
1824 CFReleaseSafe(currentLogsValidatingScts
);
1825 CFReleaseSafe(builderScts
);
1826 CFReleaseSafe(embeddedScts
);
1827 CFReleaseSafe(ocspScts
);
1828 CFReleaseSafe(precertEntry
);
1829 CFReleaseSafe(trustedLogs
);
1830 CFReleaseSafe(x509Entry
);
1833 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
1834 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1836 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
1837 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
1839 for (ix
= 0; ix
< count
; ix
++) {
1840 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1841 policy_set_t policies
= policies_for_cert(cert
);
1843 if (policy_set_contains(policies
, &key_value
)) {
1844 policy_set_free(policies
);
1847 policy_set_free(policies
);
1852 static void SecPolicyCheckCertificatePolicy(SecPVCRef pvc
, CFStringRef key
)
1854 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1855 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1856 bool result
= false;
1858 if (CFGetTypeID(value
) == CFDataGetTypeID())
1860 result
= checkPolicyOidData(pvc
, value
);
1861 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1862 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
1864 result
= checkPolicyOidData(pvc
, dataOid
);
1869 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1874 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
1876 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1877 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1878 if (isString(value
)) {
1879 SecPathBuilderSetRevocationMethod(pvc
->builder
, value
);
1883 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
1885 pvc
->require_revocation_response
= true;
1886 secdebug("policy", "revocation response required");
1889 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
1890 SecPathBuilderSetCheckRevocationOnline(pvc
->builder
);
1893 static void SecPolicyCheckRevocationIfTrusted(SecPVCRef pvc
, CFStringRef key
) {
1894 SecPathBuilderSetCheckRevocationIfTrusted(pvc
->builder
);
1897 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
1899 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1900 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1901 if (value
== kCFBooleanTrue
) {
1902 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
1904 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
1908 static void SecPolicyCheckWeakKeySize(SecPVCRef pvc
,
1910 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1911 for (ix
= 0; ix
< count
; ++ix
) {
1912 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1913 if (cert
&& SecCertificateIsWeakKey(cert
)) {
1914 /* Intermediate certificate has a weak key. */
1915 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1921 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
1922 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1923 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1924 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
1925 for (ix
= 0; ix
< count
; ++ix
) {
1926 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1927 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
1928 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1934 static void SecPolicyCheckWeakSignature(SecPVCRef pvc
, CFStringRef key
) {
1935 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1936 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1937 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
1938 for (ix
= 0; ix
< count
; ++ix
) {
1939 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1940 if (!SecPolicyCheckCertWeakSignature(cert
, pvcValue
)) {
1941 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1947 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
1949 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1950 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1951 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
1952 for (ix
= 0; ix
< count
; ++ix
) {
1953 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1954 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
1955 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1961 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
1962 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1963 require_quiet(leaf
, out
);
1965 /* And now a special snowflake from our tests */
1967 /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
1968 /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
1969 /* Not After : May 26 09:37:50 2017 GMT */
1970 static const uint8_t vodafone
[] = {
1971 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
1972 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
1975 CFDataRef leafFingerprint
= SecCertificateGetSHA1Digest(leaf
);
1976 require_quiet(leafFingerprint
, out
);
1977 const unsigned int len
= 20;
1978 const uint8_t *dp
= CFDataGetBytePtr(leafFingerprint
);
1979 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
1987 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
1989 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
1991 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1993 Boolean keyInPolicy
= false;
1994 CFArrayRef policies
= pvc
->policies
;
1995 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1996 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1997 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1998 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2003 /* We only enforce this check when *both* of the following are true:
2004 * 1. One of the certs in the path has this usage constraint, and
2005 * 2. One of the policies in the PVC has this key
2006 * (As compared to normal policy options which require only one to be true..) */
2007 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2010 /* Ignore the anchor if it's trusted */
2011 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2014 for (ix
= 0; ix
< count
; ++ix
) {
2015 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2016 if (SecCertificateIsWeakHash(cert
)) {
2017 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
2018 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2028 static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc
,
2030 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
2032 Boolean keyInPolicy
= false;
2033 CFArrayRef policies
= pvc
->policies
;
2034 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2035 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2036 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2037 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
2042 /* We only enforce this check when *both* of the following are true:
2043 * 1. One of the certs in the path has this usage constraint, and
2044 * 2. One of the policies in the PVC has this key
2045 * (As compared to normal policy options which require only one to be true..) */
2046 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
2049 /* Ignore the anchor if it's trusted */
2050 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
2053 for (ix
= 0; ix
< count
; ++ix
) {
2054 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2055 if (!SecCertificateIsStrongKey(cert
)) {
2056 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2066 static void SecPolicyCheckPinningRequired(SecPVCRef pvc
, CFStringRef key
) {
2067 /* Pinning is disabled on the system, skip. */
2068 if (SecIsInternalRelease()) {
2069 if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
2070 CFSTR("com.apple.security"), NULL
)) {
2075 CFArrayRef policies
= pvc
->policies
;
2076 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2077 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2078 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2079 CFStringRef policyName
= SecPolicyGetName(policy
);
2080 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
2081 /* policy required pinning, but we didn't use a pinning policy */
2082 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
)) {
2089 static bool is_configured_test_system_root(SecCertificateRef root
) {
2090 if (!SecIsInternalRelease()) {
2093 bool result
= false;
2094 CFDataRef rootHash
= SecCertificateCopySHA256Digest(root
);
2095 CFTypeRef value
= CFPreferencesCopyValue(CFSTR("TestCTRequiredSystemRoot"), CFSTR("com.apple.security"),
2096 kCFPreferencesAnyUser
, kCFPreferencesCurrentHost
);
2097 require_quiet(isData(value
), out
);
2098 require_quiet(kCFCompareEqualTo
== CFDataCompare(rootHash
, value
), out
);
2102 CFReleaseNull(value
);
2103 CFReleaseNull(rootHash
);
2107 static bool is_ct_excepted_domain(CFStringRef hostname
, CFStringRef exception
) {
2108 if (kCFCompareEqualTo
== CFStringCompare(exception
, hostname
, kCFCompareCaseInsensitive
)) {
2111 } else if (CFStringHasPrefix(exception
, CFSTR("."))) {
2113 CFIndex elength
= CFStringGetLength(exception
);
2114 CFIndex hlength
= CFStringGetLength(hostname
);
2115 if (hlength
> elength
) {
2116 CFRange compareRange
= { hlength
- elength
, elength
};
2117 if (kCFCompareEqualTo
== CFStringCompareWithOptions(hostname
, exception
, compareRange
, kCFCompareCaseInsensitive
)) {
2120 } else if (hlength
+ 1 == elength
) {
2121 CFRange compareRange
= { 1, hlength
};
2122 if (kCFCompareEqualTo
== CFStringCompareWithOptions(exception
, hostname
, compareRange
, kCFCompareCaseInsensitive
)) {
2130 static OSStatus
is_subtree_dn_with_org(void *context
, SecCEGeneralNameType gnType
, const DERItem
*generalName
) {
2131 if (gnType
!= GNT_DirectoryName
) {
2132 return errSecInternal
;
2135 DERDecodedInfo subtreeName_content
;
2136 if (DR_Success
!= DERDecodeItem(generalName
, &subtreeName_content
) || subtreeName_content
.tag
!= ASN1_CONSTR_SEQUENCE
) {
2137 return errSecDecode
;
2140 CFArrayRef subtree_orgs
= SecCertificateCopyOrganizationFromX501NameContent(&subtreeName_content
.content
);
2142 CFReleaseNull(subtree_orgs
);
2143 return errSecSuccess
;
2145 return errSecInternal
;
2148 static bool has_ct_excepted_key(SecCertificatePathVCRef path
, CFDictionaryRef exception
) {
2149 __block
bool result
= false;
2150 CFDataRef exceptionHash
= CFDictionaryGetValue(exception
, kSecCTExceptionsSPKIHashKey
);
2152 /* exception for a leaf is always allowed */
2153 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
2154 CFDataRef spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(leaf
);
2155 if (CFEqualSafe(exceptionHash
, spkiHash
)) {
2158 CFReleaseNull(spkiHash
);
2160 if (result
) { return result
; }
2162 /* exceptions for CAs */
2163 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
2164 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
2166 spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
2167 if (!CFEqualSafe(exceptionHash
, spkiHash
)) {
2168 CFReleaseNull(spkiHash
);
2171 CFReleaseNull(spkiHash
);
2173 /* this CA matches but exceptions for CAs have constraints */
2174 if (SecCertificateGetPermittedSubtrees(ca
)) {
2175 /* Constrained CAs have to have a Distinguished Name permitted subtree with an Organization attribute */
2176 CFArrayForEach(SecCertificateGetPermittedSubtrees(ca
), ^(const void *value
) {
2177 CFDataRef subtree
= (CFDataRef
)value
;
2178 const DERItem general_name
= { (unsigned char *)CFDataGetBytePtr(subtree
), CFDataGetLength(subtree
) };
2179 DERDecodedInfo general_name_content
;
2180 if (DR_Success
== DERDecodeItem(&general_name
, &general_name_content
)) {
2181 OSStatus status
= SecCertificateParseGeneralNameContentProperty(general_name_content
.tag
,
2182 &general_name_content
.content
,
2184 is_subtree_dn_with_org
);
2185 if (status
== errSecSuccess
) {
2193 /* The Organization attribute(s) in the CA subject have to exactly match the Organization attribute(s) in the leaf */
2194 CFArrayRef leafOrgs
= SecCertificateCopyOrganization(leaf
);
2195 CFArrayRef caOrgs
= SecCertificateCopyOrganization(ca
);
2196 if (caOrgs
&& leafOrgs
&& CFEqualSafe(leafOrgs
, caOrgs
)) {
2199 CFReleaseNull(leafOrgs
);
2200 CFReleaseNull(caOrgs
);
2211 static bool is_ct_excepted(SecPVCRef pvc
) {
2212 CFDictionaryRef ct_exceptions
= _SecTrustStoreCopyCTExceptions(NULL
, NULL
);
2213 if (!ct_exceptions
) {
2217 __block
bool result
= false;
2218 CFArrayRef domainExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsDomainsKey
);
2219 if (domainExceptions
) {
2220 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2221 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2223 CFArrayForEach(domainExceptions
, ^(const void *value
) {
2224 result
= result
|| is_ct_excepted_domain(hostname
, value
);
2229 secinfo("policy", "domain-based CT exception applied");
2230 CFReleaseNull(ct_exceptions
);
2234 CFArrayRef keyExceptions
= CFDictionaryGetValue(ct_exceptions
, kSecCTExceptionsCAsKey
);
2235 if (keyExceptions
) {
2236 __block SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2237 CFArrayForEach(keyExceptions
, ^(const void *value
) {
2238 result
= result
|| has_ct_excepted_key(path
, value
);
2243 secinfo("policy" , "key-based CT exceptions applied");
2246 CFReleaseNull(ct_exceptions
);
2250 /* <rdar://45466778> some Apple servers not getting certs with embedded SCTs
2251 * <rdar://45545270> some Google apps have their own TLS stacks and aren't passing us TLS SCTs */
2252 static bool is_apple_ca(SecCertificatePathVCRef path
) {
2253 /* subject:/CN=Apple IST CA 8 - G1/OU=Certification Authority/O=Apple Inc./C=US */
2254 /* issuer :/C=US/O=GeoTrust Inc./OU=(c) 2007 GeoTrust Inc. - For authorized use only/CN=GeoTrust Primary Certification Authority - G2 */
2255 static const uint8_t appleISTCA8G1_spkiSHA256
[] = {
2256 0xe2, 0x4f, 0x8e, 0x8c, 0x21, 0x85, 0xda, 0x2f, 0x5e, 0x88, 0xd4, 0x57, 0x9e, 0x81, 0x7c, 0x47,
2257 0xbf, 0x6e, 0xaf, 0xbc, 0x85, 0x05, 0xf0, 0xf9, 0x60, 0xfd, 0x5a, 0x0d, 0xf4, 0x47, 0x3a, 0xd3
2260 /* subject:/CN=Apple IST CA 2 - G1/OU=Certification Authority/O=Apple Inc./C=US */
2261 /* issuer :/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA */
2262 static const uint8_t appleISTCA2G1_spkiSHA256
[] = {
2263 0xb5, 0xcf, 0x82, 0xd4, 0x7e, 0xf9, 0x82, 0x3f, 0x9a, 0xa7, 0x8f, 0x12, 0x31, 0x86, 0xc5, 0x2e,
2264 0x88, 0x79, 0xea, 0x84, 0xb0, 0xf8, 0x22, 0xc9, 0x1d, 0x83, 0xe0, 0x42, 0x79, 0xb7, 0x8f, 0xd5
2267 /* subject:/C=US/O=Google Trust Services/CN=Google Internet Authority G3 */
2268 /* issuer :/OU=GlobalSign Root CA - R2/O=GlobalSign/CN=GlobalSign */
2269 static const uint8_t googleIAG3_spkiSHA256
[] = {
2270 0x7f, 0xc3, 0x67, 0x10, 0x56, 0x71, 0x43, 0x81, 0x31, 0x14, 0xe8, 0x52, 0x37, 0xb1, 0x22, 0x15,
2271 0x6b, 0x62, 0xb9, 0xd6, 0x50, 0x54, 0x3d, 0xa8, 0x63, 0xad, 0x2e, 0x6a, 0xe5, 0x7f, 0x9f, 0xbf
2274 bool result
= false;
2275 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
2276 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
2278 CFDataRef caSPKIHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
2279 const uint8_t *dp
= CFDataGetBytePtr(caSPKIHash
);
2280 if (dp
&& (!memcmp(appleISTCA8G1_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
) ||
2281 !memcmp(appleISTCA2G1_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
) ||
2282 !memcmp(googleIAG3_spkiSHA256
, dp
, CC_SHA256_DIGEST_LENGTH
))) {
2285 CFReleaseNull(caSPKIHash
);
2293 static void SecPolicyCheckSystemTrustedCTRequired(SecPVCRef pvc
) {
2294 SecCertificateSourceRef appleAnchorSource
= NULL
;
2295 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
2296 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2297 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
2299 /* Skip this check if we haven't done the CT checks yet */
2300 require_quiet(SecCertificatePathVCIsPathValidated(path
), out
);
2302 /* We only enforce this check when all of the following are true:
2303 * 0. Kill Switch not enabled */
2304 require_quiet(!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
), out
);
2306 /* 1. Not a pinning policy */
2307 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2308 require_quiet(CFEqualSafe(SecPolicyGetName(policy
),kSecPolicyNameSSLServer
), out
);
2310 /* 2. Device has checked in to MobileAsset for a current log list within the last 60 days.
2311 * Or the caller passed in the trusted log list. */
2312 require_quiet(SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
) || trustedLogs
, out
);
2314 /* 3. Leaf issuance date is on or after 16 Oct 2018 at 00:00:00 AM UTC and not expired. */
2315 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
2316 require_quiet(SecCertificateNotValidBefore(leaf
) >= 561340800.0 &&
2317 SecCertificateIsValid(leaf
, SecPVCGetVerifyTime(pvc
)), out
);
2319 /* 4. Chain is anchored with root in the system anchor source but not the Apple anchor source
2320 * with certain excepted CAs and configurable included CAs. */
2321 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2322 SecCertificateRef root
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
2323 appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(false));
2324 require_quiet(SecPathBuilderIsAnchored(pvc
->builder
), out
);
2325 require_quiet((SecCertificateSourceContains(kSecSystemAnchorSource
, root
) &&
2326 appleAnchorSource
&& !SecCertificateSourceContains(appleAnchorSource
, root
) &&
2327 !is_apple_ca(path
)) ||
2328 is_configured_test_system_root(root
), out
);
2330 if (!SecCertificatePathVCIsCT(path
) && !is_ct_excepted(pvc
)) {
2331 /* Set failure. By not using the Forced variant, we implicitly check that this
2332 * policy had this options set. */
2333 SecPVCSetResult(pvc
, kSecPolicyCheckSystemTrustedCTRequired
, 0, kCFBooleanFalse
);
2337 CFReleaseNull(trustedLogs
);
2338 CFReleaseNull(otaref
);
2339 if (appleAnchorSource
) {
2340 SecMemoryCertificateSourceDestroy(appleAnchorSource
);
2344 void SecPolicyServerInitialize(void) {
2345 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2346 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2347 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2348 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2350 #undef POLICYCHECKMACRO
2351 #define __PC_ADD_CHECK_(NAME)
2352 #define __PC_ADD_CHECK_L(NAME) CFDictionaryAddValue(gSecPolicyLeafCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2353 #define __PC_ADD_CHECK_A(NAME) CFDictionaryAddValue(gSecPolicyPathCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2355 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2356 __PC_ADD_CHECK_##LEAFCHECK(NAME) \
2357 __PC_ADD_CHECK_##PATHCHECK(NAME)
2358 #include "../Security/SecPolicyChecks.list"
2360 /* Some of these don't follow the naming conventions but are in the Pinning DB.
2361 * <rdar://34537018> fix policy check constant values */
2362 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkerOid"), SecPolicyCheckLeafMarkerOid
);
2363 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkersProdAndQA"), SecPolicyCheckLeafMarkersProdAndQA
);
2364 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateMarkerOid"), SecPolicyCheckIntermediateMarkerOid
);
2365 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateCountry"), SecPolicyCheckIntermediateCountry
);
2366 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateOrganization"), SecPolicyCheckIntermediateOrganization
);
2371 /********************************************************
2372 ****************** SecPVCRef Functions *****************
2373 ********************************************************/
2375 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
) {
2376 secdebug("alloc", "pvc %p", pvc
);
2377 // Weird logging policies crashes.
2378 //secdebug("policy", "%@", policies);
2380 // Zero the pvc struct so only non-zero fields need to be explicitly set
2381 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
2382 pvc
->builder
= builder
;
2383 pvc
->policies
= policies
;
2386 pvc
->result
= kSecTrustResultUnspecified
;
2388 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2389 &kCFTypeDictionaryKeyCallBacks
,
2390 &kCFTypeDictionaryValueCallBacks
);
2391 pvc
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
,
2392 1, &kCFTypeArrayCallBacks
);
2393 CFRelease(certDetail
);
2396 void SecPVCDelete(SecPVCRef pvc
) {
2397 secdebug("alloc", "delete pvc %p", pvc
);
2398 CFReleaseNull(pvc
->policies
);
2399 CFReleaseNull(pvc
->details
);
2400 CFReleaseNull(pvc
->leafDetails
);
2403 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2404 secdebug("policy", "%@", path
);
2406 pvc
->result
= kSecTrustResultUnspecified
;
2407 CFReleaseNull(pvc
->details
);
2410 void SecPVCComputeDetails(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2413 /* Since we don't run the LeafChecks again, we need to preserve the
2414 * result the leaf had. */
2415 CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(path
);
2416 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
2417 pathLength
, pvc
->leafDetails
);
2418 for (ix
= 1; ix
< pathLength
; ++ix
) {
2419 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2420 &kCFTypeDictionaryKeyCallBacks
,
2421 &kCFTypeDictionaryValueCallBacks
);
2422 CFArrayAppendValue(details
, certDetail
);
2423 CFRelease(certDetail
);
2425 CFRetainAssign(pvc
->details
, details
);
2426 pvc
->result
= pvc
->leafResult
;
2427 CFReleaseSafe(details
);
2430 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
2431 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
2434 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
2435 return SecPathBuilderGetCertificateCount(pvc
->builder
);
2438 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
2439 return SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2442 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
2443 return SecPathBuilderGetVerifyTime(pvc
->builder
);
2446 static bool SecPVCIsExceptedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
, CFTypeRef value
) {
2447 CFArrayRef exceptions
= SecPathBuilderGetExceptions(pvc
->builder
);
2448 if (!exceptions
) { return false; }
2449 CFIndex exceptionsCount
= CFArrayGetCount(exceptions
);
2451 /* There are two types of exceptions:
2452 * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
2453 * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
2454 * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
2457 CFDictionaryRef options
= CFArrayGetValueAtIndex(exceptions
, 0);
2459 if (exceptionsCount
== 1 && (ix
> 0 || !CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
))) {
2460 /* SHA1Digest not allowed */
2461 if (CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
)) { return false; }
2463 if (CFDictionaryContainsKey(options
, key
)) {
2464 /* Special case -- AnchorTrusted only for self-signed certs */
2465 if (CFEqual(kSecPolicyCheckAnchorTrusted
, key
)) {
2466 Boolean isSelfSigned
= false;
2467 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2468 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2473 } else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
) && CFDictionaryContainsKey(options
, kSecPolicyCheckValidRoot
)) {
2474 /* Another special case - ValidRoot excepts Valid only for self-signed certs */
2475 Boolean isSelfSigned
= false;
2476 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2477 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2486 if (ix
>= exceptionsCount
) { return false; }
2487 CFDictionaryRef exception
= CFArrayGetValueAtIndex(exceptions
, ix
);
2489 /* Compare the cert hash */
2490 if (!CFDictionaryContainsKey(exception
, kSecCertificateDetailSHA1Digest
)) { return false; }
2491 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2492 if (!CFEqual(SecCertificateGetSHA1Digest(cert
), CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
))) {
2497 CFTypeRef exceptionValue
= CFDictionaryGetValue(exception
, key
);
2498 if (exceptionValue
&& CFEqual(value
, exceptionValue
)) {
2499 /* Only change result if PVC is already ok */
2500 if (SecPVCIsOkResult(pvc
)) {
2501 // Chains that pass due to exceptions get Proceed result.
2502 pvc
->result
= kSecTrustResultProceed
;
2510 static int32_t detailKeyToCssmErr(CFStringRef key
) {
2513 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
2514 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
2516 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
2517 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
2519 else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
2520 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
2526 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
2528 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
2529 bool result
= false;
2530 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2531 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, ix
);
2532 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2533 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2535 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2536 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2537 CFNumberRef allowedErrorNumber
= NULL
;
2538 if (!isDictionary(constraint
)) {
2541 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
2542 int32_t allowedErrorValue
= 0;
2543 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
2547 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
2548 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
2557 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
2558 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2559 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2560 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2561 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2562 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2563 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2564 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2565 if (!isDictionary(constraint
)) {
2569 CFDictionaryRef policyOptions
= NULL
;
2570 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2571 if (policyOptions
&& isDictionary(policyOptions
) &&
2572 CFDictionaryContainsKey(policyOptions
, key
)) {
2580 static SecTrustResultType
trust_result_for_key(CFStringRef key
) {
2581 SecTrustResultType result
= kSecTrustResultRecoverableTrustFailure
;
2582 #undef POLICYCHECKMACRO
2583 #define __PC_TYPE_MEMBER_ false
2584 #define __PC_TYPE_MEMBER_R false
2585 #define __PC_TYPE_MEMBER_F true
2586 #define __PC_TYPE_MEMBER_D true
2588 #define __TRUSTRESULT_ kSecTrustResultRecoverableTrustFailure
2589 #define __TRUSTRESULT_F kSecTrustResultFatalTrustFailure
2590 #define __TRUSTRESULT_D kSecTrustResultDeny
2591 #define __TRUSTRESULT_R kSecTrustResultRecoverableTrustFailure
2593 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2594 if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
2595 result = __TRUSTRESULT_##TRUSTRESULT; \
2597 #include "../Security/SecPolicyChecks.list"
2602 /* AUDIT[securityd](done):
2603 policy->_options is a caller provided dictionary, only its cf type has
2606 bool SecPVCSetResultForced(SecPVCRef pvc
,
2607 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
2609 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
2610 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
2611 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
2613 (force
? "force" : ""), result
);
2615 /* If this is not something the current policy cares about ignore
2616 this error and return true so our caller continues evaluation. */
2618 /* Either the policy or the usage constraints have to have this key */
2619 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2620 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
2621 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
2626 /* Check to see if the SecTrustSettings for the certificate in question
2627 tell us to ignore this error. */
2628 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
2629 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
2633 /* Check to see if exceptions tells us to ignore this error. */
2634 if (SecPVCIsExceptedError(pvc
, ix
, key
, result
)) {
2635 secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix
, key
);
2639 /* Avoid resetting deny or fatal to recoverable */
2640 SecTrustResultType trustResult
= trust_result_for_key(key
);
2641 if (SecPVCIsOkResult(pvc
) || trustResult
== kSecTrustResultFatalTrustFailure
) {
2642 pvc
->result
= trustResult
;
2643 } else if (trustResult
== kSecTrustResultDeny
&&
2644 pvc
->result
== kSecTrustResultRecoverableTrustFailure
) {
2645 pvc
->result
= trustResult
;
2651 CFMutableDictionaryRef detail
=
2652 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
2654 /* Perhaps detail should have an array of results per key? As it stands
2655 in the case of multiple policy failures the last failure stands. */
2656 CFDictionarySetValue(detail
, key
, result
);
2661 bool SecPVCSetResult(SecPVCRef pvc
,
2662 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
2663 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
2666 /* AUDIT[securityd](done):
2667 key(ok) is a caller provided.
2668 value(ok, unused) is a caller provided.
2670 static void SecPVCValidateKey(const void *key
, const void *value
,
2672 SecPVCRef pvc
= (SecPVCRef
)context
;
2674 /* If our caller doesn't want full details and we failed earlier there is
2675 no point in doing additional checks. */
2676 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
2679 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
2680 CFDictionaryGetValue(pvc
->callbacks
, key
);
2683 /* "Optional" policy checks. This may be a new key from the
2684 * pinning DB which is not implemented in this OS version. Log a
2685 * warning, and on debug builds fail evaluation, to encourage us
2686 * to ensure that checks are synchronized across the same build. */
2687 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
2688 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
2689 secwarning("policy: unknown policy key %@, skipping", key
);
2691 pvc
->result
= kSecTrustResultOtherError
;
2694 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
2695 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
2696 secwarning("policy: unknown policy key %@, skipping", key
);
2698 pvc
->result
= kSecTrustResultOtherError
;
2702 /* Non standard validation phase, nothing is optional. */
2703 pvc
->result
= kSecTrustResultOtherError
;
2708 fcn(pvc
, (CFStringRef
)key
);
2711 /* AUDIT[securityd](done):
2712 policy->_options is a caller provided dictionary, only its cf type has
2715 SecTrustResultType
SecPVCLeafChecks(SecPVCRef pvc
) {
2716 /* We need to compute details for the leaf. */
2717 CFRetainAssign(pvc
->details
, pvc
->leafDetails
);
2719 CFArrayRef policies
= pvc
->policies
;
2720 CFIndex ix
, count
= CFArrayGetCount(policies
);
2721 for (ix
= 0; ix
< count
; ++ix
) {
2722 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2724 /* Validate all keys for all policies. */
2725 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2726 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
2729 pvc
->leafResult
= pvc
->result
;
2730 CFRetainAssign(pvc
->leafDetails
, pvc
->details
);
2735 bool SecPVCIsOkResult(SecPVCRef pvc
) {
2736 if (pvc
->result
== kSecTrustResultRecoverableTrustFailure
||
2737 pvc
->result
== kSecTrustResultDeny
||
2738 pvc
->result
== kSecTrustResultFatalTrustFailure
||
2739 pvc
->result
== kSecTrustResultOtherError
) {
2745 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
2746 /* Check stuff common to intermediate and anchors. */
2747 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
2748 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2749 CFIndex anchor_ix
= SecPVCGetCertificateCount(pvc
) - 1;
2750 bool is_anchor
= (ix
== anchor_ix
&& SecPathBuilderIsAnchored(pvc
->builder
));
2752 if (!SecCertificateIsValid(cert
, verifyTime
)) {
2753 /* Certificate has expired. */
2754 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, ix
, kCFBooleanFalse
)) {
2759 if (SecCertificateIsWeakKey(cert
)) {
2760 /* Certificate uses weak key. */
2761 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, ix
, kCFBooleanFalse
)) {
2766 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
2767 /* Certificate uses weak hash. */
2768 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, ix
, kCFBooleanFalse
)) {
2774 /* Perform anchor specific checks. */
2775 /* Don't think we have any of these. */
2777 /* Perform intermediate specific checks. */
2779 /* (k) Basic constraints only relevant for v3 and later. */
2780 if (SecCertificateVersion(cert
) >= 3) {
2781 const SecCEBasicConstraints
*bc
=
2782 SecCertificateGetBasicConstraints(cert
);
2784 /* Basic constraints not present, illegal. */
2785 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2786 ix
, kCFBooleanFalse
, true)) {
2789 } else if (!bc
->isCA
) {
2790 /* Basic constraints not marked as isCA, illegal. */
2791 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsCA
,
2792 ix
, kCFBooleanFalse
, true)) {
2797 /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
2798 not an anchor), we additionally require that the certificate chain
2799 does not end in a v3 or later anchor. [rdar://32204517] */
2800 else if (ix
> 0 && ix
< anchor_ix
) {
2801 SecCertificateRef anchor
= SecPVCGetCertificateAtIndex(pvc
, anchor_ix
);
2802 if (SecCertificateVersion(anchor
) >= 3) {
2803 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2804 ix
, kCFBooleanFalse
, true)) {
2809 /* (l) max_path_length is checked elsewhere. */
2811 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
2812 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
2813 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
2814 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
2815 ix
, kCFBooleanFalse
, true)) {
2822 return SecPVCIsOkResult(pvc
);
2825 static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
2826 /* Check stuff common to intermediate and anchors. */
2828 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2829 if (NULL
!= otapkiRef
)
2831 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
2832 CFRelease(otapkiRef
);
2833 if (NULL
!= blackListedKeys
)
2835 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2836 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2837 bool is_last
= (ix
== count
- 1);
2838 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2840 /* Check for blacklisted intermediate issuer keys. */
2841 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2843 /* Check dgst against blacklist. */
2844 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
2845 /* Check allow list for this blacklisted issuer key,
2846 which is the authority key of the issued cert at ix-1.
2848 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2849 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2851 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
2852 ix
, kCFBooleanFalse
, true);
2858 CFRelease(blackListedKeys
);
2859 return SecPVCIsOkResult(pvc
);
2866 static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
2868 /* Check stuff common to intermediate and anchors. */
2869 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2870 if (NULL
!= otapkiRef
)
2872 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
2873 CFRelease(otapkiRef
);
2874 if (NULL
!= grayListKeys
)
2876 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2877 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2878 bool is_last
= (ix
== count
- 1);
2879 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2881 /* Check for gray listed intermediate issuer keys. */
2882 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2884 /* Check dgst against gray list. */
2885 if (CFSetContainsValue(grayListKeys
, dgst
)) {
2886 /* Check allow list for this graylisted issuer key,
2887 which is the authority key of the issued cert at ix-1.
2889 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2890 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2892 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
2893 ix
, kCFBooleanFalse
, true);
2899 CFRelease(grayListKeys
);
2900 return SecPVCIsOkResult(pvc
);
2907 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
2908 if (!isString(searchName
) && !isString(searchOid
)) {
2911 CFArrayRef policies
= pvc
->policies
;
2912 CFIndex ix
, count
= CFArrayGetCount(policies
);
2913 for (ix
= 0; ix
< count
; ++ix
) {
2914 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2915 CFStringRef policyName
= SecPolicyGetName(policy
);
2916 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
2917 /* Prefer a match of both name and OID */
2918 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
2919 if (CFEqual(searchOid
, policyOid
) &&
2920 CFEqual(searchName
, policyName
)) {
2921 if (policyIX
) { *policyIX
= ix
; }
2924 /* <rdar://40617139> sslServer trust settings need to apply to evals using policyName pinning
2925 * but make sure we don't use this for SSL Client trust settings or policies. */
2926 if (CFEqual(searchOid
, policyOid
) &&
2927 CFEqual(searchName
, kSecPolicyNameSSLServer
) && !CFEqual(policyName
, kSecPolicyNameSSLClient
)) {
2928 if (policyIX
) { *policyIX
= ix
; }
2932 /* Next best is just OID. */
2933 if (!searchName
&& searchOid
&& policyOid
) {
2934 if (CFEqual(searchOid
, policyOid
)) {
2935 if (policyIX
) { *policyIX
= ix
; }
2939 if (!searchOid
&& searchName
&& policyName
) {
2940 if (CFEqual(searchName
, policyName
)) {
2941 if (policyIX
) { *policyIX
= ix
; }
2949 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
2950 if (!isString(stringValue
)) {
2953 bool result
= false;
2955 CFStringRef tmpStringValue
= NULL
;
2956 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
2957 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
2959 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
2961 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
2962 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
2963 /* Have to look for all the possible locations of name string */
2964 CFStringRef policyString
= NULL
;
2965 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2966 if (!policyString
) {
2967 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
2969 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
2974 CFArrayRef policyStrings
= NULL
;
2975 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
2976 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
2977 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
2985 CFReleaseNull(tmpStringValue
);
2990 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
2991 uint32_t ourTSKeyUsage
= 0;
2992 uint32_t keyUsage
= 0;
2993 if (keyUsageNumber
&&
2994 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
2995 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
2996 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
2998 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
2999 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
3001 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
3002 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
3004 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
3005 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
3007 if (keyUsage
== kSecKeyUsageAll
) {
3008 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
3011 return ourTSKeyUsage
;
3014 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
3015 uint32_t ourTSKeyUsage
= 0;
3016 CFTypeRef policyKeyUsageType
= NULL
;
3018 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
3019 if (isArray(policyKeyUsageType
)) {
3020 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
3021 for (ix
= 0; ix
< count
; ix
++) {
3022 CFNumberRef policyKeyUsageNumber
= NULL
;
3023 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
3024 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
3026 } else if (isNumber(policyKeyUsageType
)) {
3027 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
3030 return ourTSKeyUsage
;
3033 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
3034 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
3035 int64_t keyUsageValue
= 0;
3036 uint32_t ourKeyUsage
= 0;
3038 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
3042 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
3046 /* We're using the key for revocation if we have the OCSPSigner policy.
3047 * @@@ If we support CRLs, we'd need to check for that policy here too.
3049 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
3050 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
3053 /* We're using the key for verifying a cert if it's a root/intermediate
3054 * in the chain. If the cert isn't in the path yet, we're about to add it,
3055 * so it's a root/intermediate. If there is no path, this is the leaf.
3057 CFIndex pathIndex
= -1;
3058 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3060 pathIndex
= SecCertificatePathVCGetIndexOfCertificate(path
, certificate
);
3064 if (pathIndex
!= 0) {
3065 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
3068 /* The rest of the key usages may be specified by the policy(ies). */
3069 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
3070 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
3071 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3073 /* Get key usage from ALL policies */
3074 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
3075 for (ix
= 0; ix
< count
; ix
++) {
3076 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
3077 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
3081 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
3089 #include <Security/SecTrustedApplicationPriv.h>
3090 #include <Security/SecTask.h>
3091 #include <Security/SecTaskPriv.h>
3092 #include <bsm/libbsm.h>
3093 #include <libproc.h>
3095 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
3097 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
3098 bool result
= false;
3099 audit_token_t auditToken
= {};
3100 SecTaskRef task
= NULL
;
3101 SecRequirementRef requirement
= NULL
;
3102 CFStringRef stringRequirement
= NULL
;
3104 require(appRef
&& clientAuditToken
, out
);
3105 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
3106 require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef
)appRef
, &requirement
), out
);
3107 require(requirement
, out
);
3108 require_noerr(SecRequirementsCopyString(requirement
, kSecCSDefaultFlags
, &stringRequirement
), out
);
3109 require(stringRequirement
, out
);
3111 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
3112 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
3113 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
3115 if(errSecSuccess
== SecTaskValidateForRequirement(task
, stringRequirement
)) {
3120 CFReleaseNull(task
);
3121 CFReleaseNull(requirement
);
3122 CFReleaseNull(stringRequirement
);
3127 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
3128 if (!isDictionary(options
)) {
3133 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
3135 /* We need to run the leaf and path checks using these options. */
3136 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
3137 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3139 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3140 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
3143 pvc
->callbacks
= currentCallbacks
;
3145 /* Our work here is done; no need to claim a match */
3149 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
3150 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
3151 CFNumberRef keyUsageNumber
= NULL
;
3152 CFTypeRef trustedApplicationData
= NULL
;
3153 CFDictionaryRef policyOptions
= NULL
;
3155 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
3156 keyUsageMatch
= false, policyOptionMatch
= false;
3157 bool result
= false;
3159 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
3160 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
3161 SecPolicyRef policy
= NULL
;
3162 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3163 policyOid
= (policy
) ? policy
->_oid
: NULL
;
3165 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
3167 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
3168 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
3169 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
3170 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
3172 CFIndex policyIX
= -1;
3173 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
3174 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
3175 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
3176 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
3178 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
3179 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
3180 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
3181 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
3182 CFReleaseNull(clientAuditToken
);
3184 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
3185 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
3189 /* If we either didn't find the parameter in the dictionary or we got a match
3190 * against that parameter, for all possible parameters in the dictionary, then
3191 * this trust setting result applies to the output. */
3192 if (((!policyOid
&& !policyName
) || policyMatch
) &&
3193 (!policyString
|| policyStringMatch
) &&
3194 (!trustedApplicationData
|| applicationMatch
) &&
3195 (!keyUsageNumber
|| keyUsageMatch
) &&
3196 (!policyOptions
|| policyOptionMatch
)) {
3203 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
3204 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
3205 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
3206 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
3207 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
3208 if (!isDictionary(constraint
)) {
3212 CFNumberRef resultNumber
= NULL
;
3213 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
3214 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
3215 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
3216 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
3217 resultValue
= kSecTrustSettingsResultTrustRoot
;
3220 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
3221 result
= resultValue
;
3228 static void SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
3229 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3230 for (certIX
= 0; certIX
< certCount
; certIX
++) {
3231 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3232 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
3233 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3234 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
3236 /* Set the pvc trust result based on the usage constraints and anchor source. */
3237 if (result
== kSecTrustSettingsResultDeny
) {
3238 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
3239 } else if ((result
== kSecTrustSettingsResultTrustRoot
|| result
== kSecTrustSettingsResultTrustAsRoot
||
3240 result
== kSecTrustSettingsResultInvalid
) && SecPVCIsOkResult(pvc
)) {
3241 /* If we already think the PVC is ok and this cert is from one of the user/
3242 * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
3243 * all mean we should use the special "Proceed" trust result. */
3244 #if TARGET_OS_IPHONE
3245 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecUserAnchorSource
) &&
3246 SecCertificateSourceContains(kSecUserAnchorSource
, cert
)) {
3248 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecLegacyAnchorSource
) &&
3249 SecCertificateSourceContains(kSecLegacyAnchorSource
, cert
)) {
3251 pvc
->result
= kSecTrustResultProceed
;
3257 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
3258 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
3259 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
3261 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
3262 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
3263 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
3265 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
3266 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
3267 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
3269 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
3270 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
3271 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
3273 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
3274 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
3275 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
3277 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
3278 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
3279 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
3281 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
3282 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
3283 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
3285 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
3286 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
3287 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
3290 static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
3291 static CFSetRef sConstrainedRoots
= NULL
;
3292 static dispatch_once_t _t
;
3293 dispatch_once(&_t
, ^{
3294 const UInt8
*v_hashes
[] = {
3295 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
3296 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
3298 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
3299 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
3300 for (ix
=0; ix
<count
; ix
++) {
3301 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
3302 kSecPolicySHA256Size
, kCFAllocatorNull
);
3304 CFSetAddValue(set
, hash
);
3308 sConstrainedRoots
= set
;
3311 bool shouldDeny
= false;
3312 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
3313 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
3314 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
3315 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
3316 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
3317 /* matched a constrained root; check notBefore dates on all its children. */
3318 CFIndex childIX
= certIX
;
3319 while (--childIX
>= 0) {
3320 SecCertificateRef child
= SecPVCGetCertificateAtIndex(pvc
, childIX
);
3321 /* 1 Dec 2016 00:00:00 GMT */
3322 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
3323 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
3329 CFReleaseNull(sha256
);
3333 static bool SecPVCIsSSLServerAuthenticationPolicy(SecPVCRef pvc
) {
3334 if (!pvc
|| !pvc
->policies
) {
3337 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, 0);
3341 CFStringRef policyName
= SecPolicyGetName(policy
);
3342 if (CFEqualSafe(policyName
, kSecPolicyNameSSLServer
)) {
3345 CFDictionaryRef options
= policy
->_options
;
3346 if (options
&& CFDictionaryGetValue(options
, kSecPolicyCheckSSLHostname
)) {
3353 1. SecPVCCheckRequireCTConstraints must be called after SecPolicyCheckCT,
3354 so earliest issuance time has already been obtained from SCTs.
3355 2. If the issuance time value is 0 (i.e. 2001-01-01) or earlier, we
3356 assume it was not set, and thus we did not have CT info.
3358 static void SecPVCCheckRequireCTConstraints(SecPVCRef pvc
) {
3359 SecCertificatePathVCRef path
= (pvc
) ? SecPathBuilderGetPath(pvc
->builder
) : NULL
;
3363 /* If we are evaluating for a SSL server authentication policy, make sure
3364 SCT issuance time is prior to the earliest not-after date constraint.
3365 Note that CT will already be required if there is a not-after date
3366 constraint present (set in SecRVCProcessValidDateConstraints).
3368 if (SecPVCIsSSLServerAuthenticationPolicy(pvc
)) {
3369 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3370 SecCertificateRef certificate
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, 0);
3371 CFAbsoluteTime earliestNotAfter
= 31556908800.0; /* default: 3001-01-01 00:00:00-0000 */
3372 CFAbsoluteTime issuanceTime
= SecCertificatePathVCIssuanceTime(path
);
3373 if (issuanceTime
<= 0) {
3374 /* if not set (or prior to 2001-01-01), use leaf's not-before time. */
3375 issuanceTime
= SecCertificateNotValidBefore(certificate
);
3377 for (ix
= 0; ix
< certCount
; ix
++) {
3378 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3379 if (!rvc
|| !rvc
->valid_info
|| !rvc
->valid_info
->hasDateConstraints
|| !rvc
->valid_info
->notAfterDate
) {
3382 /* Found CA certificate with a not-after date constraint. */
3383 CFAbsoluteTime caNotAfter
= CFDateGetAbsoluteTime(rvc
->valid_info
->notAfterDate
);
3384 if (caNotAfter
< earliestNotAfter
) {
3385 earliestNotAfter
= caNotAfter
;
3387 if (issuanceTime
> earliestNotAfter
) {
3388 /* Issuance time violates the not-after date constraint. */
3389 secnotice("rvc", "certificate issuance time (%f) is later than allowed value (%f)",
3390 issuanceTime
, earliestNotAfter
);
3391 SecRVCSetValidDeterminedErrorResult(rvc
);
3396 /* If path is CT validated, nothing further to do here. */
3397 if (SecCertificatePathVCIsCT(path
)) {
3401 /* Path is not CT validated, so check if CT was required. */
3402 SecPathCTPolicy ctp
= SecCertificatePathVCRequiresCT(path
);
3403 if (ctp
<= kSecPathCTNotRequired
|| !SecPVCIsSSLServerAuthenticationPolicy(pvc
)) {
3407 /* We need to have a recent log list or the CT check may have failed due to the list being out of date.
3408 * Also, honor the CT kill switch. */
3409 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3410 if (!SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) &&
3411 SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
)) {
3412 /* CT was required. Error is always set on leaf certificate. */
3413 SecPVCSetResultForced(pvc
, kSecPolicyCheckCTRequired
,
3414 0, kCFBooleanFalse
, true);
3415 if (ctp
!= kSecPathCTRequiredOverridable
) {
3416 /* Normally kSecPolicyCheckCTRequired is recoverable,
3417 so need to manually change trust result here. */
3418 pvc
->result
= kSecTrustResultFatalTrustFailure
;
3421 CFReleaseNull(otaref
);
3424 /* AUDIT[securityd](done):
3425 policy->_options is a caller provided dictionary, only its cf type has
3428 void SecPVCPathChecks(SecPVCRef pvc
) {
3429 secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc
->builder
));
3430 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3431 /* This needs to be initialized before we call any function that might call
3432 SecPVCSetResultForced(). */
3434 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3435 if (SecPVCIsOkResult(pvc
) || pvc
->details
) {
3436 SecPolicyCheckBasicCertificateProcessing(pvc
,
3437 kSecPolicyCheckBasicCertificateProcessing
);
3440 CFArrayRef policies
= pvc
->policies
;
3441 CFIndex count
= CFArrayGetCount(policies
);
3442 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3443 /* Validate all keys for all policies. */
3444 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3445 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3446 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3447 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
3454 /* Check whether the TrustSettings say to deny a cert in the path. */
3455 SecPVCCheckUsageConstraints(pvc
);
3457 /* Check for Blocklisted certs */
3458 SecPVCCheckIssuerDateConstraints(pvc
);
3460 count
= SecCertificatePathVCGetCount(path
);
3461 for (ix
= 1; ix
< count
; ix
++) {
3462 SecPVCGrayListedKeyChecks(pvc
, ix
);
3463 SecPVCBlackListedKeyChecks(pvc
, ix
);
3466 /* Path-based check tests. */
3467 if (!SecCertificatePathVCIsPathValidated(path
)) {
3468 bool ev_check_ok
= false;
3469 if (SecCertificatePathVCIsOptionallyEV(path
)) {
3470 SecTrustResultType pre_ev_check_result
= pvc
->result
;
3471 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3472 ev_check_ok
= SecPVCIsOkResult(pvc
);
3473 /* If ev checking failed, we still want to accept this chain
3474 as a non EV one, if it was valid as such. */
3475 pvc
->result
= pre_ev_check_result
;
3479 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3480 SecPolicyCheckCT(pvc
);
3482 /* Certs are only EV if they are also CT verified (when the Kill Switch isn't enabled and against a recent log list) */
3483 SecOTAPKIRef otaref
= SecOTAPKICopyCurrentOTAPKIRef();
3484 if (ev_check_ok
&& (SecCertificatePathVCIsCT(path
) || SecOTAPKIKillSwitchEnabled(otaref
, kOTAPKIKillSwitchCT
) ||
3485 !SecOTAPKIAssetStalenessLessThanSeconds(otaref
, kSecOTAPKIAssetStalenessDisable
))) {
3486 SecCertificatePathVCSetIsEV(path
, true);
3488 CFReleaseNull(otaref
);
3491 /* Say that we did the expensive path checks (that we want to skip on the second call) */
3492 SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc
->builder
));
3494 /* Check that this path meets CT constraints. */
3495 SecPVCCheckRequireCTConstraints(pvc
);
3496 SecPolicyCheckSystemTrustedCTRequired(pvc
);
3498 /* Check that this path meets known-intermediate constraints. */
3499 SecPathBuilderCheckKnownIntermediateConstraints(pvc
->builder
);
3501 secdebug("policy", "end %strusted path: %@",
3502 (SecPVCIsOkResult(pvc
) ? "" : "not "), SecPathBuilderGetPath(pvc
->builder
));
3507 void SecPVCPathCheckRevocationResponsesReceived(SecPVCRef pvc
) {
3509 /* Since we don't currently allow networking on watchOS,
3510 * don't enforce the revocation-required check here. (32728029) */
3511 bool required
= false;
3513 bool required
= true;
3515 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3516 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3517 for (ix
= 0; ix
< certCount
; ix
++) {
3518 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3519 /* Do we have a valid revocation response? */
3520 if (SecRVCGetEarliestNextUpdate(rvc
) == NULL_TIME
) {
3521 /* No valid revocation response.
3522 * Do we require revocation (for that cert per the
3523 * SecCertificateVCRef, or per the pvc)? */
3524 if (required
&& (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path
, ix
) ||
3525 ((ix
== 0) && pvc
->require_revocation_response
))) {
3526 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
3527 ix
, kCFBooleanFalse
, true);
3529 /* Do we have a definitive Valid revocation result for this cert? */
3530 if (SecRVCHasDefinitiveValidInfo(rvc
) && SecRVCHasRevokedValidInfo(rvc
)) {
3531 SecRVCSetValidDeterminedErrorResult(rvc
);