2 * Copyright (c) 2008-2017 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/asynchttp.h>
33 #include <securityd/policytree.h>
34 #include <securityd/nameconstraints.h>
35 #include <CoreFoundation/CFTimeZone.h>
37 #include <libDER/oids.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <AssertMacros.h>
41 #include <utilities/debugging.h>
42 #include <utilities/SecInternalReleasePriv.h>
43 #include <security_asn1/SecAsn1Coder.h>
44 #include <security_asn1/ocspTemplates.h>
45 #include <security_asn1/oidsalg.h>
46 #include <security_asn1/oidsocsp.h>
47 #include <CommonCrypto/CommonDigest.h>
48 #include <Security/SecFramework.h>
49 #include <Security/SecPolicyInternal.h>
50 #include <Security/SecTrustPriv.h>
51 #include <Security/SecTrustInternal.h>
52 #include <Security/SecTrustSettingsPriv.h>
53 #include <Security/SecInternal.h>
54 #include <Security/SecKeyPriv.h>
55 #include <Security/SecTask.h>
56 #include <CFNetwork/CFHTTPMessage.h>
57 #include <CFNetwork/CFHTTPStream.h>
58 #include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
60 #include <securityd/SecTrustServer.h>
61 #include <securityd/SecTrustLoggingServer.h>
62 #include <securityd/SecRevocationServer.h>
63 #include <securityd/SecCertificateServer.h>
64 #include <securityd/SecCertificateSource.h>
65 #include <securityd/SecOCSPResponse.h>
66 #include <utilities/array_size.h>
67 #include <utilities/SecCFWrappers.h>
68 #include <utilities/SecAppleAnchorPriv.h>
69 #include "OTATrustUtilities.h"
70 #include "personalization.h"
71 #include <sys/codesign.h>
74 #include <Security/SecTaskPriv.h>
77 /* Set this to 1 to dump the ocsp responses received in DER form in /tmp. */
78 #ifndef DUMP_OCSPRESPONSES
79 #define DUMP_OCSPRESPONSES 0
82 #if DUMP_OCSPRESPONSES
87 static void secdumpdata(CFDataRef data
, const char *name
) {
88 int fd
= open(name
, O_CREAT
| O_WRONLY
| O_TRUNC
, 0666);
89 write(fd
, CFDataGetBytePtr(data
), CFDataGetLength(data
));
96 /********************************************************
97 ****************** SecPolicy object ********************
98 ********************************************************/
100 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
);
101 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
);
102 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
);
104 static CFMutableDictionaryRef gSecPolicyLeafCallbacks
= NULL
;
105 static CFMutableDictionaryRef gSecPolicyPathCallbacks
= NULL
;
107 static CFArrayRef
SecPolicyAnchorDigestsForEVPolicy(const DERItem
*policyOID
)
109 CFArrayRef result
= NULL
;
110 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
111 if (NULL
== otapkiRef
)
116 CFDictionaryRef evToPolicyAnchorDigest
= SecOTAPKICopyEVPolicyToAnchorMapping(otapkiRef
);
117 CFRelease(otapkiRef
);
119 if (NULL
== evToPolicyAnchorDigest
)
124 CFArrayRef roots
= NULL
;
125 CFStringRef oid
= SecDERItemCopyOIDDecimalRepresentation(kCFAllocatorDefault
, policyOID
);
126 if (oid
&& evToPolicyAnchorDigest
)
128 result
= (CFArrayRef
)CFDictionaryGetValue(evToPolicyAnchorDigest
, oid
);
129 if (roots
&& CFGetTypeID(result
) != CFArrayGetTypeID())
131 secerror("EVRoot.plist has non array value");
136 CFReleaseSafe(evToPolicyAnchorDigest
);
141 bool SecPolicyIsEVPolicy(const DERItem
*policyOID
) {
142 return SecPolicyAnchorDigestsForEVPolicy(policyOID
);
145 static bool SecPolicyRootCACertificateIsEV(SecCertificateRef certificate
,
146 policy_set_t valid_policies
) {
147 CFDictionaryRef keySizes
= NULL
;
148 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
150 /* Ensure that this certificate is a valid anchor for one of the
151 certificate policy oids specified in the leaf. */
152 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
154 bool good_ev_anchor
= false;
155 for (ix
= valid_policies
; ix
; ix
= ix
->oid_next
) {
156 CFArrayRef digests
= SecPolicyAnchorDigestsForEVPolicy(&ix
->oid
);
157 if (digests
&& CFArrayContainsValue(digests
,
158 CFRangeMake(0, CFArrayGetCount(digests
)), digest
)) {
159 secdebug("ev", "found anchor for policy oid");
160 good_ev_anchor
= true;
164 require_action_quiet(good_ev_anchor
, notEV
, secnotice("ev", "anchor not in plist"));
166 CFAbsoluteTime october2006
= 178761600;
167 if (SecCertificateNotValidBefore(certificate
) >= october2006
) {
168 require_action_quiet(SecCertificateVersion(certificate
) >= 3, notEV
,
169 secnotice("ev", "Anchor issued after October 2006 and is not v3"));
171 if (SecCertificateVersion(certificate
) >= 3
172 && SecCertificateNotValidBefore(certificate
) >= october2006
) {
173 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
174 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
175 secnotice("ev", "Anchor has invalid basic constraints"));
176 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
177 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
178 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
179 secnotice("ev", "Anchor has invalid key usage %u", ku
));
182 /* At least RSA 2048 or ECC NIST P-256. */
183 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
184 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
185 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
186 const void *values
[] = { rsaSize
, ecSize
};
187 require_quiet(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
188 &kCFTypeDictionaryKeyCallBacks
,
189 &kCFTypeDictionaryValueCallBacks
), notEV
);
190 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
191 secnotice("ev", "Anchor's public key is too weak for EV"));
196 CFReleaseNull(rsaSize
);
197 CFReleaseNull(ecSize
);
198 CFReleaseNull(keySizes
);
202 static bool SecPolicySubordinateCACertificateCouldBeEV(SecCertificateRef certificate
) {
203 CFMutableDictionaryRef keySizes
= NULL
;
204 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
207 const SecCECertificatePolicies
*cp
;
208 cp
= SecCertificateGetCertificatePolicies(certificate
);
209 require_action_quiet(cp
&& cp
->numPolicies
> 0, notEV
,
210 secnotice("ev", "SubCA missing certificate policies"));
211 CFArrayRef cdp
= SecCertificateGetCRLDistributionPoints(certificate
);
212 require_action_quiet(cdp
&& CFArrayGetCount(cdp
) > 0, notEV
,
213 secnotice("ev", "SubCA missing CRLDP"));
214 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
215 require_action_quiet(bc
&& bc
->isCA
== true, notEV
,
216 secnotice("ev", "SubCA has invalid basic constraints"));
217 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
218 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
))
219 == (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
), notEV
,
220 secnotice("ev", "SubCA has invalid key usage %u", ku
));
222 /* 6.1.5 Key Sizes */
223 CFAbsoluteTime jan2011
= 315532800;
224 CFAbsoluteTime jan2014
= 410227200;
225 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
226 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
227 &kCFTypeDictionaryValueCallBacks
), notEV
);
228 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
229 if (SecCertificateNotValidBefore(certificate
) < jan2011
||
230 SecCertificateNotValidAfter(certificate
) < jan2014
) {
231 /* At least RSA 1024 or ECC NIST P-256. */
232 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
233 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
234 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
235 secnotice("ev", "SubCA's public key is too small for issuance before 2011 or expiration before 2014"));
237 /* At least RSA 2028 or ECC NIST P-256. */
238 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
239 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
240 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
241 secnotice("ev", "SubCA's public key is too small for issuance after 2010 or expiration after 2013"));
244 /* 7.1.3 Algorithm Object Identifiers */
245 CFAbsoluteTime jan2016
= 473299200;
246 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
248 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
249 notEV
, secnotice("ev", "SubCA was issued with SHA-1 after 2015"));
255 CFReleaseNull(rsaSize
);
256 CFReleaseNull(ecSize
);
257 CFReleaseNull(keySizes
);
261 /********************************************************
262 **************** SecPolicy Callbacks *******************
263 ********************************************************/
264 static void SecPolicyCheckCriticalExtensions(SecPVCRef pvc
,
268 static void SecPolicyCheckIdLinkage(SecPVCRef pvc
,
270 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
271 CFDataRef parentSubjectKeyID
= NULL
;
272 for (ix
= count
- 1; ix
>= 0; --ix
) {
273 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
274 /* If the previous certificate in the chain had a SubjectKeyID,
275 make sure it matches the current certificates AuthorityKeyID. */
276 if (parentSubjectKeyID
) {
277 /* @@@ According to RFC 2459 neither AuthorityKeyID nor
278 SubjectKeyID can be critical. Currenty we don't check
280 CFDataRef authorityKeyID
= SecCertificateGetAuthorityKeyID(cert
);
281 if (authorityKeyID
) {
282 if (!CFEqual(parentSubjectKeyID
, authorityKeyID
)) {
283 /* AuthorityKeyID doesn't match issuers SubjectKeyID. */
284 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
290 parentSubjectKeyID
= SecCertificateGetSubjectKeyID(cert
);
294 static void SecPolicyCheckKeyUsage(SecPVCRef pvc
,
296 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
297 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
298 CFTypeRef xku
= CFDictionaryGetValue(policy
->_options
, key
);
299 if (!SecPolicyCheckCertKeyUsage(leaf
, xku
)) {
300 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
304 /* AUDIT[securityd](done):
305 policy->_options is a caller provided dictionary, only its cf type has
308 static void SecPolicyCheckExtendedKeyUsage(SecPVCRef pvc
, CFStringRef key
) {
309 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
310 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
311 CFTypeRef xeku
= CFDictionaryGetValue(policy
->_options
, key
);
312 if (!SecPolicyCheckCertExtendedKeyUsage(leaf
, xeku
)){
313 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
317 static void SecPolicyCheckBasicConstraints(SecPVCRef pvc
,
319 //SecPolicyCheckBasicContraintsCommon(pvc, key, false);
322 static void SecPolicyCheckNonEmptySubject(SecPVCRef pvc
,
324 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
325 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
326 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
327 for (ix
= 0; ix
< count
; ++ix
) {
328 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
329 if (!SecPolicyCheckCertNonEmptySubject(cert
, pvcValue
)) {
330 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
336 /* AUDIT[securityd](done):
337 policy->_options is a caller provided dictionary, only its cf type has
340 static void SecPolicyCheckSSLHostname(SecPVCRef pvc
,
342 /* @@@ Consider what to do if the caller passes in no hostname. Should
343 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
344 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
345 CFStringRef hostName
= (CFStringRef
)
346 CFDictionaryGetValue(policy
->_options
, key
);
347 if (!isString(hostName
)) {
348 /* @@@ We can't return an error here and making the evaluation fail
349 won't help much either. */
353 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
354 bool dnsMatch
= SecPolicyCheckCertSSLHostname(leaf
, hostName
);
357 /* Hostname mismatch or no hostnames found in certificate. */
358 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
363 /* AUDIT[securityd](done):
364 policy->_options is a caller provided dictionary, only its cf type has
367 static void SecPolicyCheckEmail(SecPVCRef pvc
, CFStringRef key
) {
368 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
369 CFStringRef email
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
370 if (!isString(email
)) {
371 /* We can't return an error here and making the evaluation fail
372 won't help much either. */
376 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
378 if (!SecPolicyCheckCertEmail(leaf
, email
)) {
379 /* Hostname mismatch or no hostnames found in certificate. */
380 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
384 static void SecPolicyCheckTemporalValidity(SecPVCRef pvc
,
386 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
387 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
388 for (ix
= 0; ix
< count
; ++ix
) {
389 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
390 if (!SecCertificateIsValid(cert
, verifyTime
)) {
391 /* Intermediate certificate has expired. */
392 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
398 /* AUDIT[securityd](done):
399 policy->_options is a caller provided dictionary, only its cf type has
402 static void SecPolicyCheckIssuerCommonName(SecPVCRef pvc
,
404 CFIndex count
= SecPVCGetCertificateCount(pvc
);
406 /* Can't check intermediates common name if there is no intermediate. */
407 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
411 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
412 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
413 CFStringRef commonName
=
414 (CFStringRef
)CFDictionaryGetValue(policy
->_options
, key
);
415 if (!isString(commonName
)) {
416 /* @@@ We can't return an error here and making the evaluation fail
417 won't help much either. */
420 if (!SecPolicyCheckCertSubjectCommonName(cert
, commonName
)) {
421 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
425 /* AUDIT[securityd](done):
426 policy->_options is a caller provided dictionary, only its cf type has
429 static void SecPolicyCheckSubjectCommonName(SecPVCRef pvc
,
431 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
432 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
433 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
435 if (!isString(common_name
)) {
436 /* @@@ We can't return an error here and making the evaluation fail
437 won't help much either. */
440 if (!SecPolicyCheckCertSubjectCommonName(cert
, common_name
)) {
441 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
445 /* AUDIT[securityd](done):
446 policy->_options is a caller provided dictionary, only its cf type has
449 static void SecPolicyCheckSubjectCommonNamePrefix(SecPVCRef pvc
,
451 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
452 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
453 CFStringRef prefix
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
455 if (!isString(prefix
)) {
456 /* @@@ We can't return an error here and making the evaluation fail
457 won't help much either. */
460 if (!SecPolicyCheckCertSubjectCommonNamePrefix(cert
, prefix
)) {
461 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
465 /* AUDIT[securityd](done):
466 policy->_options is a caller provided dictionary, only its cf type has
469 static void SecPolicyCheckSubjectCommonNameTEST(SecPVCRef pvc
,
471 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
472 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
473 CFStringRef common_name
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
475 if (!isString(common_name
)) {
476 /* @@@ We can't return an error here and making the evaluation fail
477 won't help much either. */
480 if (!SecPolicyCheckCertSubjectCommonNameTEST(cert
, common_name
)) {
481 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
485 /* AUDIT[securityd](done):
486 policy->_options is a caller provided dictionary, only its cf type has
489 static void SecPolicyCheckNotValidBefore(SecPVCRef pvc
,
491 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
492 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
493 CFDateRef date
= (CFDateRef
)CFDictionaryGetValue(policy
->_options
, key
);
495 /* @@@ We can't return an error here and making the evaluation fail
496 won't help much either. */
499 if (!SecPolicyCheckCertNotValidBefore(cert
, date
)) {
500 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
505 /* AUDIT[securityd](done):
506 policy->_options is a caller provided dictionary, only its cf type has
509 static void SecPolicyCheckChainLength(SecPVCRef pvc
,
511 CFIndex count
= SecPVCGetCertificateCount(pvc
);
512 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
513 CFNumberRef chainLength
=
514 (CFNumberRef
)CFDictionaryGetValue(policy
->_options
, key
);
516 if (!chainLength
|| CFGetTypeID(chainLength
) != CFNumberGetTypeID() ||
517 !CFNumberGetValue(chainLength
, kCFNumberCFIndexType
, &value
)) {
518 /* @@@ We can't return an error here and making the evaluation fail
519 won't help much either. */
522 if (value
!= count
) {
523 /* Chain length doesn't match policy requirement. */
524 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
))
529 static bool isDigestInPolicy(SecPVCRef pvc
, CFStringRef key
, CFDataRef digest
) {
530 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
531 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
533 bool foundMatch
= false;
535 foundMatch
= CFEqual(digest
, value
);
536 else if (isArray(value
))
537 foundMatch
= CFArrayContainsValue((CFArrayRef
) value
, CFRangeMake(0, CFArrayGetCount((CFArrayRef
) value
)), digest
);
539 /* @@@ We only support Data and Array but we can't return an error here so.
540 we let the evaluation fail (not much help) and assert in debug. */
547 static void SecPolicyCheckAnchorSHA256(SecPVCRef pvc
, CFStringRef key
) {
548 CFIndex count
= SecPVCGetCertificateCount(pvc
);
549 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
550 CFDataRef anchorSHA256
= NULL
;
551 anchorSHA256
= SecCertificateCopySHA256Digest(cert
);
553 if (!isDigestInPolicy(pvc
, key
, anchorSHA256
)) {
554 SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA256
, count
-1, kCFBooleanFalse
);
557 CFReleaseNull(anchorSHA256
);
562 /* AUDIT[securityd](done):
563 policy->_options is a caller provided dictionary, only its cf type has
566 static void SecPolicyCheckAnchorSHA1(SecPVCRef pvc
,
568 CFIndex count
= SecPVCGetCertificateCount(pvc
);
569 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
570 CFDataRef anchorSHA1
= SecCertificateGetSHA1Digest(cert
);
572 if (!isDigestInPolicy(pvc
, key
, anchorSHA1
))
573 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorSHA1
, count
-1, kCFBooleanFalse
))
580 Check the SHA256 of SPKI of the first intermediate CA certificate in the path
581 policy->_options is a caller provided dictionary, only its cf type has
584 static void SecPolicyCheckIntermediateSPKISHA256(SecPVCRef pvc
,
586 SecCertificateRef cert
= NULL
;
587 CFDataRef digest
= NULL
;
589 if (SecPVCGetCertificateCount(pvc
) < 2) {
590 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 0, kCFBooleanFalse
);
594 cert
= SecPVCGetCertificateAtIndex(pvc
, 1);
595 digest
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert
);
597 if (!isDigestInPolicy(pvc
, key
, digest
)) {
598 SecPVCSetResult(pvc
, kSecPolicyCheckIntermediateSPKISHA256
, 1, kCFBooleanFalse
);
600 CFReleaseNull(digest
);
604 policy->_options is a caller provided dictionary, only its cf type has
607 static void SecPolicyCheckAnchorApple(SecPVCRef pvc
,
609 CFIndex count
= SecPVCGetCertificateCount(pvc
);
610 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, count
- 1);
611 SecAppleTrustAnchorFlags flags
= 0;
614 bool foundMatch
= SecIsAppleTrustAnchor(cert
, flags
);
617 if (!SecPVCSetResult(pvc
, kSecPolicyCheckAnchorApple
, 0, kCFBooleanFalse
))
624 /* AUDIT[securityd](done):
625 policy->_options is a caller provided dictionary, only its cf type has
628 static void SecPolicyCheckSubjectOrganization(SecPVCRef pvc
,
630 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
631 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
632 CFStringRef org
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
634 if (!isString(org
)) {
635 /* @@@ We can't return an error here and making the evaluation fail
636 won't help much either. */
639 if (!SecPolicyCheckCertSubjectOrganization(cert
, org
)) {
640 /* Leaf Subject Organization mismatch. */
641 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
645 static void SecPolicyCheckSubjectOrganizationalUnit(SecPVCRef pvc
,
647 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
648 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
649 CFStringRef orgUnit
= (CFStringRef
)CFDictionaryGetValue(policy
->_options
,
651 if (!isString(orgUnit
)) {
652 /* @@@ We can't return an error here and making the evaluation fail
653 won't help much either. */
656 if (!SecPolicyCheckCertSubjectOrganizationalUnit(cert
, orgUnit
)) {
657 /* Leaf Subject Organization mismatch. */
658 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
662 /* AUDIT[securityd](done):
663 policy->_options is a caller provided dictionary, only its cf type has
666 static void SecPolicyCheckEAPTrustedServerNames(SecPVCRef pvc
,
668 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
669 CFArrayRef trustedServerNames
= (CFArrayRef
)
670 CFDictionaryGetValue(policy
->_options
, key
);
671 /* No names specified means we accept any name. */
672 if (!trustedServerNames
)
674 if (!isArray(trustedServerNames
)) {
675 /* @@@ We can't return an error here and making the evaluation fail
676 won't help much either. */
680 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
681 if (!SecPolicyCheckCertEAPTrustedServerNames(leaf
, trustedServerNames
)) {
682 /* Hostname mismatch or no hostnames found in certificate. */
683 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
687 static const unsigned char UTN_USERFirst_Hardware_Serial
[][16] = {
688 { 0xd8, 0xf3, 0x5f, 0x4e, 0xb7, 0x87, 0x2b, 0x2d, 0xab, 0x06, 0x92, 0xe3, 0x15, 0x38, 0x2f, 0xb0 },
689 { 0x92, 0x39, 0xd5, 0x34, 0x8f, 0x40, 0xd1, 0x69, 0x5a, 0x74, 0x54, 0x70, 0xe1, 0xf2, 0x3f, 0x43 },
690 { 0xb0, 0xb7, 0x13, 0x3e, 0xd0, 0x96, 0xf9, 0xb5, 0x6f, 0xae, 0x91, 0xc8, 0x74, 0xbd, 0x3a, 0xc0 },
691 { 0xe9, 0x02, 0x8b, 0x95, 0x78, 0xe4, 0x15, 0xdc, 0x1a, 0x71, 0x0a, 0x2b, 0x88, 0x15, 0x44, 0x47 },
692 { 0x39, 0x2a, 0x43, 0x4f, 0x0e, 0x07, 0xdf, 0x1f, 0x8a, 0xa3, 0x05, 0xde, 0x34, 0xe0, 0xc2, 0x29 },
693 { 0x3e, 0x75, 0xce, 0xd4, 0x6b, 0x69, 0x30, 0x21, 0x21, 0x88, 0x30, 0xae, 0x86, 0xa8, 0x2a, 0x71 },
694 { 0xd7, 0x55, 0x8f, 0xda, 0xf5, 0xf1, 0x10, 0x5b, 0xb2, 0x13, 0x28, 0x2b, 0x70, 0x77, 0x29, 0xa3 },
695 { 0x04, 0x7e, 0xcb, 0xe9, 0xfc, 0xa5, 0x5f, 0x7b, 0xd0, 0x9e, 0xae, 0x36, 0xe1, 0x0c, 0xae, 0x1e },
696 { 0xf5, 0xc8, 0x6a, 0xf3, 0x61, 0x62, 0xf1, 0x3a, 0x64, 0xf5, 0x4f, 0x6d, 0xc9, 0x58, 0x7c, 0x06 } };
698 static const unsigned char UTN_USERFirst_Hardware_Normalized_Issuer
[] = {
699 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55,
700 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x02,
701 0x55, 0x54, 0x31, 0x17, 0x30, 0x15, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
702 0x0e, 0x53, 0x41, 0x4c, 0x54, 0x20, 0x4c, 0x41, 0x4b, 0x45, 0x20, 0x43,
703 0x49, 0x54, 0x59, 0x31, 0x1e, 0x30, 0x1c, 0x06, 0x03, 0x55, 0x04, 0x0a,
704 0x13, 0x15, 0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x52, 0x54, 0x52,
705 0x55, 0x53, 0x54, 0x20, 0x4e, 0x45, 0x54, 0x57, 0x4f, 0x52, 0x4b, 0x31,
706 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x18, 0x48, 0x54,
707 0x54, 0x50, 0x3a, 0x2f, 0x2f, 0x57, 0x57, 0x57, 0x2e, 0x55, 0x53, 0x45,
708 0x52, 0x54, 0x52, 0x55, 0x53, 0x54, 0x2e, 0x43, 0x4f, 0x4d, 0x31, 0x1f,
709 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x16, 0x55, 0x54, 0x4e,
710 0x2d, 0x55, 0x53, 0x45, 0x52, 0x46, 0x49, 0x52, 0x53, 0x54, 0x2d, 0x48,
711 0x41, 0x52, 0x44, 0x57, 0x41, 0x52, 0x45
713 static const unsigned int UTN_USERFirst_Hardware_Normalized_Issuer_len
= 151;
716 static void SecPolicyCheckBlackListedLeaf(SecPVCRef pvc
,
718 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
719 CFDataRef issuer
= cert
? SecCertificateGetNormalizedIssuerContent(cert
) : NULL
;
721 if (issuer
&& (CFDataGetLength(issuer
) == (CFIndex
)UTN_USERFirst_Hardware_Normalized_Issuer_len
) &&
722 (0 == memcmp(UTN_USERFirst_Hardware_Normalized_Issuer
, CFDataGetBytePtr(issuer
),
723 UTN_USERFirst_Hardware_Normalized_Issuer_len
)))
725 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
726 CFDataRef serial
= SecCertificateCopySerialNumber(cert
, NULL
);
728 CFDataRef serial
= SecCertificateCopySerialNumber(cert
);
732 CFIndex serial_length
= CFDataGetLength(serial
);
733 const uint8_t *serial_ptr
= CFDataGetBytePtr(serial
);
735 while ((serial_length
> 0) && (*serial_ptr
== 0)) {
740 if (serial_length
== (CFIndex
)sizeof(*UTN_USERFirst_Hardware_Serial
)) {
742 for (i
= 0; i
< array_size(UTN_USERFirst_Hardware_Serial
); i
++)
744 if (0 == memcmp(UTN_USERFirst_Hardware_Serial
[i
],
745 serial_ptr
, sizeof(*UTN_USERFirst_Hardware_Serial
)))
747 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
748 CFReleaseSafe(serial
);
757 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
758 if (NULL
!= otapkiRef
)
760 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
761 CFRelease(otapkiRef
);
762 if (NULL
!= blackListedKeys
)
764 /* Check for blacklisted intermediates keys. */
765 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
768 /* Check dgst against blacklist. */
769 if (CFSetContainsValue(blackListedKeys
, dgst
))
771 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
775 CFRelease(blackListedKeys
);
780 static void SecPolicyCheckGrayListedLeaf(SecPVCRef pvc
, CFStringRef key
)
782 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
783 if (NULL
!= otapkiRef
)
785 CFSetRef grayListedKeys
= SecOTAPKICopyGrayList(otapkiRef
);
786 CFRelease(otapkiRef
);
787 if (NULL
!= grayListedKeys
)
789 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
791 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
794 /* Check dgst against gray. */
795 if (CFSetContainsValue(grayListedKeys
, dgst
))
797 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
801 CFRelease(grayListedKeys
);
806 static void SecPolicyCheckLeafMarkerOid(SecPVCRef pvc
, CFStringRef key
)
808 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
809 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
810 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
812 if (!SecPolicyCheckCertLeafMarkerOid(cert
, value
)) {
813 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
817 static void SecPolicyCheckLeafMarkerOidWithoutValueCheck(SecPVCRef pvc
, CFStringRef key
)
819 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
820 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
821 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
823 if (!SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
, value
)) {
824 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
830 * The value is a dictionary. The dictionary contains keys indicating
831 * whether the value is for Prod or QA. The values are the same as
832 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
834 static void SecPolicyCheckLeafMarkersProdAndQA(SecPVCRef pvc
, CFStringRef key
)
836 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, 0);
837 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
838 CFDictionaryRef value
= CFDictionaryGetValue(policy
->_options
, key
);
839 CFTypeRef prodValue
= CFDictionaryGetValue(value
, kSecPolicyLeafMarkerProd
);
841 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
844 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
849 static void SecPolicyCheckIntermediateMarkerOid(SecPVCRef pvc
, CFStringRef key
)
851 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
852 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
853 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
855 for (ix
= 1; ix
< count
- 1; ix
++) {
856 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
857 if (SecCertificateHasMarkerExtension(cert
, value
))
860 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
863 static void SecPolicyCheckIntermediateEKU(SecPVCRef pvc
, CFStringRef key
)
865 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
866 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
867 CFTypeRef peku
= CFDictionaryGetValue(policy
->_options
, key
);
869 for (ix
= 1; ix
< count
- 1; ix
++) {
870 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
871 if (!SecPolicyCheckCertExtendedKeyUsage(cert
, peku
)) {
872 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
877 static void SecPolicyCheckIntermediateOrganization(SecPVCRef pvc
, CFStringRef key
)
879 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
880 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
881 CFTypeRef organization
= CFDictionaryGetValue(policy
->_options
, key
);
883 for (ix
= 1; ix
< count
- 1; ix
++) {
884 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
885 if (!SecPolicyCheckCertSubjectOrganization(cert
, organization
)) {
886 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
891 static void SecPolicyCheckIntermediateCountry(SecPVCRef pvc
, CFStringRef key
)
893 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
894 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
895 CFTypeRef country
= CFDictionaryGetValue(policy
->_options
, key
);
897 for (ix
= 1; ix
< count
- 1; ix
++) {
898 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
899 if (!SecPolicyCheckCertSubjectCountry(cert
, country
)) {
900 SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
);
905 /****************************************************************************
906 *********************** New rfc5280 Chain Validation ***********************
907 ****************************************************************************/
909 #define POLICY_MAPPING 1
910 #define POLICY_SUBTREES 1
912 /* rfc5280 basic cert processing. */
913 static void SecPolicyCheckBasicCertificateProcessing(SecPVCRef pvc
,
917 CFIndex count
= SecPVCGetCertificateCount(pvc
);
918 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
919 /* 64 bits cast: worst case here is we truncate the number of cert, and the validation may fail */
920 assert((unsigned long)count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
921 uint32_t n
= (uint32_t)count
;
923 bool is_anchored
= SecPathBuilderIsAnchored(pvc
->builder
);
924 bool is_anchor_trusted
= false;
926 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, n
- 1);
927 if (CFArrayGetCount(constraints
) == 0) {
928 /* Given that the path builder has already indicated the last cert in this chain has
929 * trust set on it, empty constraints means trusted. */
930 is_anchor_trusted
= true;
932 /* Determine whether constraints say to trust this cert for this PVC. */
933 SecTrustSettingsResult tsResult
= SecPVCGetTrustSettingsResult(pvc
, SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1),
935 if (tsResult
== kSecTrustSettingsResultTrustRoot
|| tsResult
== kSecTrustSettingsResultTrustAsRoot
) {
936 is_anchor_trusted
= true;
941 if (is_anchor_trusted
) {
942 /* If the anchor is trusted we don't process the last cert in the
946 /* trust may be restored for a path with an untrusted root that matches the allow list.
947 (isAllowlisted is set by revocation check, which is performed prior to path checks) */
948 if (!SecCertificatePathVCIsAllowlisted(path
)) {
949 Boolean isSelfSigned
= false;
950 (void) SecCertificateIsSelfSigned(SecCertificatePathVCGetCertificateAtIndex(path
, n
- 1), &isSelfSigned
);
952 /* Add a detail for the root not being trusted. */
953 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckAnchorTrusted
,
954 n
- 1, kCFBooleanFalse
, true)) {
958 /* Add a detail for the missing intermediate. */
959 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckMissingIntermediate
,
960 n
- 1, kCFBooleanFalse
, true)) {
967 CFAbsoluteTime verify_time
= SecPVCGetVerifyTime(pvc
);
968 //policy_set_t user_initial_policy_set = NULL;
969 //trust_anchor_t anchor;
973 CFMutableArrayRef permitted_subtrees
= NULL
;
974 CFMutableArrayRef excluded_subtrees
= NULL
;
975 permitted_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
976 excluded_subtrees
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
977 require_action_quiet(permitted_subtrees
!= NULL
, errOut
,
978 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
979 require_action_quiet(excluded_subtrees
!= NULL
, errOut
,
980 SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, 0, kCFBooleanFalse
, true));
983 if (!SecCertificatePathVCVerifyPolicyTree(path
, is_anchor_trusted
)) {
984 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckPolicyConstraints
, 0, kCFBooleanFalse
, true)) {
990 /* Path builder ensures we only get cert chains with proper issuer
991 chaining with valid signatures along the way. */
992 algorithm_id_t working_public_key_algorithm
= anchor
->public_key_algorithm
;
993 SecKeyRef working_public_key
= anchor
->public_key
;
994 x500_name_t working_issuer_name
= anchor
->issuer_name
;
996 uint32_t i
, max_path_length
= n
;
997 SecCertificateRef cert
= NULL
;
998 for (i
= 1; i
<= n
; ++i
) {
1000 cert
= SecPVCGetCertificateAtIndex(pvc
, n
- i
);
1001 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecPathBuilderGetPath(pvc
->builder
), n
- i
);
1003 /* (a) Verify the basic certificate information. */
1004 /* @@@ Ensure that cert was signed with working_public_key_algorithm
1005 using the working_public_key and the working_public_key_parameters. */
1007 /* Already done by chain builder. */
1008 if (!SecCertificateIsValid(cert
, verify_time
)) {
1009 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, n
- i
, kCFBooleanFalse
)) {
1013 if (SecCertificateIsWeakKey(cert
)) {
1014 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, n
- i
, kCFBooleanFalse
)) {
1018 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
1019 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, n
- i
, kCFBooleanFalse
)) {
1024 /* @@@ cert.issuer == working_issuer_name. */
1028 if (!is_self_issued
|| i
== n
) {
1030 /* Verify certificate Subject Name and SubjectAltNames are not within any of the excluded_subtrees */
1031 if(excluded_subtrees
&& CFArrayGetCount(excluded_subtrees
)) {
1032 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, excluded_subtrees
, &found
, false)) || found
) {
1033 secnotice("policy", "name in excluded subtrees");
1034 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1037 /* Verify certificate Subject Name and SubjectAltNames are within the permitted_subtrees */
1038 if(permitted_subtrees
&& CFArrayGetCount(permitted_subtrees
)) {
1039 if ((errSecSuccess
!= SecNameContraintsMatchSubtrees(cert
, permitted_subtrees
, &found
, true)) || !found
) {
1040 secnotice("policy", "name not in permitted subtrees");
1041 if(!SecPVCSetResultForced(pvc
, kSecPolicyCheckNameConstraints
, n
- i
, kCFBooleanFalse
, true)) { goto errOut
; }
1046 /* (d) (e) (f) handled by SecCertificatePathVCVerifyPolicyTree */
1048 /* If Last Cert in Path */
1052 /* Prepare for Next Cert */
1053 /* (a) (b) Done by SecCertificatePathVCVerifyPolicyTree */
1054 /* (c)(d)(e)(f) Done by SecPathBuilderGetNext and SecCertificatePathVCVerify */
1055 //working_issuer_name = SecCertificateGetNormalizedSubjectContent(cert);
1056 //working_public_key = SecCertificateCopyPublicKey(cert);
1057 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1058 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1060 /* (g) If a name constraints extension is included in the certificate, modify the permitted_subtrees and excluded_subtrees state variables.
1062 CFArrayRef permitted_subtrees_in_cert
= SecCertificateGetPermittedSubtrees(cert
);
1063 if (permitted_subtrees_in_cert
) {
1064 SecNameConstraintsIntersectSubtrees(permitted_subtrees
, permitted_subtrees_in_cert
);
1067 // could do something smart here to avoid inserting the exact same constraint
1068 CFArrayRef excluded_subtrees_in_cert
= SecCertificateGetExcludedSubtrees(cert
);
1069 if (excluded_subtrees_in_cert
) {
1070 CFIndex num_trees
= CFArrayGetCount(excluded_subtrees_in_cert
);
1071 CFRange range
= { 0, num_trees
};
1072 CFArrayAppendArray(excluded_subtrees
, excluded_subtrees_in_cert
, range
);
1075 /* (h), (i), (j) done by SecCertificatePathVCVerifyPolicyTree */
1077 /* (k) Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1080 if (!is_self_issued
) {
1081 if (max_path_length
> 0) {
1084 /* max_path_len exceeded, illegal. */
1085 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsPathLen
,
1086 n
- i
, kCFBooleanFalse
, true)) {
1092 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(cert
);
1093 if (bc
&& bc
->pathLenConstraintPresent
1094 && bc
->pathLenConstraint
< max_path_length
) {
1095 max_path_length
= bc
->pathLenConstraint
;
1097 #if 0 /* Checked in chain builder pre signature verify already. SecPVCParentCertificateChecks */
1098 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
1099 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
1100 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
1101 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
1102 n
- i
, kCFBooleanFalse
, true)) {
1107 /* (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. */
1108 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1109 /* Certificate contains one or more unknown critical extensions. */
1110 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1111 n
- i
, kCFBooleanFalse
)) {
1115 } /* end loop over certs in path */
1117 /* (a) (b) done by SecCertificatePathVCVerifyPolicyTree */
1119 //working_public_key = SecCertificateCopyPublicKey(cert);
1121 /* 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
1122 working_public_key_algorithm are different, set the working_public_key_parameters to null. */
1123 //working_public_key_parameters = SecCertificateCopyPublicKeyParameters(cert);
1125 //working_public_key_algorithm = SecCertificateCopyPublicKeyAlgorithm(cert);
1126 /* (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. */
1127 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
1128 /* Certificate contains one or more unknown critical extensions. */
1129 if (!SecPVCSetResult(pvc
, kSecPolicyCheckCriticalExtensions
,
1130 0, kCFBooleanFalse
)) {
1134 /* (g) done by SecCertificatePathVCVerifyPolicyTree */
1137 CFReleaseNull(permitted_subtrees
);
1138 CFReleaseNull(excluded_subtrees
);
1141 static policy_set_t
policies_for_cert(SecCertificateRef cert
) {
1142 policy_set_t policies
= NULL
;
1143 const SecCECertificatePolicies
*cp
=
1144 SecCertificateGetCertificatePolicies(cert
);
1145 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1146 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1147 policy_set_add(&policies
, &cp
->policies
[policy_ix
].policyIdentifier
);
1152 static void SecPolicyCheckEV(SecPVCRef pvc
,
1154 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1155 policy_set_t valid_policies
= NULL
;
1157 /* 6.1.7. Key Usage Purposes */
1159 CFAbsoluteTime jul2016
= 489024000;
1160 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1161 if (SecCertificateNotValidBefore(leaf
) > jul2016
&& count
< 3) {
1162 /* Root CAs may not sign subscriber certificates after 30 June 2016. */
1163 if (SecPVCSetResultForced(pvc
, key
,
1164 0, kCFBooleanFalse
, true)) {
1170 for (ix
= 0; ix
< count
; ++ix
) {
1171 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1172 policy_set_t policies
= policies_for_cert(cert
);
1175 /* anyPolicy in the leaf isn't allowed for EV, so only init
1176 valid_policies if we have real policies. */
1177 if (!policy_set_contains(policies
, &oidAnyPolicy
)) {
1178 valid_policies
= policies
;
1181 } else if (ix
< count
- 1) {
1182 /* Subordinate CA */
1183 if (!SecPolicySubordinateCACertificateCouldBeEV(cert
)) {
1184 secnotice("ev", "subordinate certificate is not ev");
1185 if (SecPVCSetResultForced(pvc
, key
,
1186 ix
, kCFBooleanFalse
, true)) {
1187 policy_set_free(valid_policies
);
1188 policy_set_free(policies
);
1192 policy_set_intersect(&valid_policies
, policies
);
1195 if (!SecPolicyRootCACertificateIsEV(cert
, valid_policies
)) {
1196 secnotice("ev", "anchor certificate is not ev");
1197 if (SecPVCSetResultForced(pvc
, key
,
1198 ix
, kCFBooleanFalse
, true)) {
1199 policy_set_free(valid_policies
);
1200 policy_set_free(policies
);
1205 policy_set_free(policies
);
1206 if (!valid_policies
) {
1207 secnotice("ev", "valid_policies set is empty: chain not ev");
1208 /* If we ever get into a state where no policies are valid anymore
1209 this can't be an ev chain. */
1210 if (SecPVCSetResultForced(pvc
, key
,
1211 ix
, kCFBooleanFalse
, true)) {
1217 policy_set_free(valid_policies
);
1219 /* (a) EV Subscriber Certificates Each EV Certificate issued by the CA to a
1220 Subscriber MUST contain an OID defined by the CA in the certificate’s
1221 certificatePolicies extension that: (i) indicates which CA policy statement relates
1222 to that certificate, (ii) asserts the CA’s adherence to and compliance with these
1223 Guidelines, and (iii), by pre-agreement with the Application Software Vendor,
1224 marks the certificate as being an EV Certificate.
1225 (b) EV Subordinate CA Certificates
1226 (1) Certificates issued to Subordinate CAs that are not controlled by the issuing
1227 CA MUST contain one or more OIDs defined by the issuing CA that
1228 explicitly identify the EV Policies that are implemented by the Subordinate
1230 (2) Certificates issued to Subordinate CAs that are controlled by the Root CA
1231 MAY contain the special anyPolicy OID (2.5.29.32.0).
1232 (c) Root CA Certificates Root CA Certificates SHOULD NOT contain the
1233 certificatePolicies or extendedKeyUsage extensions.
1239 * MARK: Certificate Transparency support
1245 Version sct_version; // 1 byte
1246 LogID id; // 32 bytes
1247 uint64 timestamp; // 8 bytes
1248 CtExtensions extensions; // 2 bytes len field, + n bytes data
1249 digitally-signed struct { // 1 byte hash alg, 1 byte sig alg, n bytes signature
1250 Version sct_version;
1251 SignatureType signature_type = certificate_timestamp;
1253 LogEntryType entry_type;
1254 select(entry_type) {
1255 case x509_entry: ASN.1Cert;
1256 case precert_entry: PreCert;
1258 CtExtensions extensions;
1260 } SignedCertificateTimestamp;
1264 #include <Security/SecureTransportPriv.h>
1267 SecAsn1Oid
*oidForSigAlg(SSL_HashAlgorithm hash
, SSL_SignatureAlgorithm alg
)
1270 case SSL_SignatureAlgorithmRSA
:
1272 case SSL_HashAlgorithmSHA1
:
1273 return &CSSMOID_SHA1WithRSA
;
1274 case SSL_HashAlgorithmSHA256
:
1275 return &CSSMOID_SHA256WithRSA
;
1276 case SSL_HashAlgorithmSHA384
:
1277 return &CSSMOID_SHA384WithRSA
;
1281 case SSL_SignatureAlgorithmECDSA
:
1283 case SSL_HashAlgorithmSHA1
:
1284 return &CSSMOID_ECDSA_WithSHA1
;
1285 case SSL_HashAlgorithmSHA256
:
1286 return &CSSMOID_ECDSA_WithSHA256
;
1287 case SSL_HashAlgorithmSHA384
:
1288 return &CSSMOID_ECDSA_WithSHA384
;
1300 static size_t SSLDecodeUint16(const uint8_t *p
)
1302 return (p
[0]<<8 | p
[1]);
1305 static uint8_t *SSLEncodeUint16(uint8_t *p
, size_t len
)
1307 p
[0] = (len
>> 8)&0xff;
1308 p
[1] = (len
& 0xff);
1312 static uint8_t *SSLEncodeUint24(uint8_t *p
, size_t len
)
1314 p
[0] = (len
>> 16)&0xff;
1315 p
[1] = (len
>> 8)&0xff;
1316 p
[2] = (len
& 0xff);
1322 uint64_t SSLDecodeUint64(const uint8_t *p
)
1325 for(int i
=0; i
<8; i
++) {
1332 #include <libDER/DER_CertCrl.h>
1333 #include <libDER/DER_Encode.h>
1334 #include <libDER/asn1Types.h>
1337 static CFDataRef
copy_x509_entry_from_chain(SecPVCRef pvc
)
1339 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1341 CFMutableDataRef data
= CFDataCreateMutable(kCFAllocatorDefault
, 3+SecCertificateGetLength(leafCert
));
1343 CFDataSetLength(data
, 3+SecCertificateGetLength(leafCert
));
1345 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1346 q
= SSLEncodeUint24(q
, SecCertificateGetLength(leafCert
));
1347 memcpy(q
, SecCertificateGetBytePtr(leafCert
), SecCertificateGetLength(leafCert
));
1353 static CFDataRef
copy_precert_entry_from_chain(SecPVCRef pvc
)
1355 SecCertificateRef leafCert
= NULL
;
1356 SecCertificateRef issuer
= NULL
;
1357 CFDataRef issuerKeyHash
= NULL
;
1358 CFDataRef tbs_precert
= NULL
;
1359 CFMutableDataRef data
= NULL
;
1361 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1362 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1363 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1365 require(leafCert
, out
);
1366 require(issuer
, out
); // Those two would likely indicate an internal error, since we already checked the chain length above.
1367 issuerKeyHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(issuer
);
1368 tbs_precert
= SecCertificateCopyPrecertTBS(leafCert
);
1370 require(issuerKeyHash
, out
);
1371 require(tbs_precert
, out
);
1372 data
= CFDataCreateMutable(kCFAllocatorDefault
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1373 CFDataSetLength(data
, CFDataGetLength(issuerKeyHash
) + 3 + CFDataGetLength(tbs_precert
));
1375 uint8_t *q
= CFDataGetMutableBytePtr(data
);
1376 memcpy(q
, CFDataGetBytePtr(issuerKeyHash
), CFDataGetLength(issuerKeyHash
)); q
+= CFDataGetLength(issuerKeyHash
); // issuer key hash
1377 q
= SSLEncodeUint24(q
, CFDataGetLength(tbs_precert
));
1378 memcpy(q
, CFDataGetBytePtr(tbs_precert
), CFDataGetLength(tbs_precert
));
1381 CFReleaseSafe(issuerKeyHash
);
1382 CFReleaseSafe(tbs_precert
);
1387 CFAbsoluteTime
TimestampToCFAbsoluteTime(uint64_t ts
)
1389 return (ts
/ 1000) - kCFAbsoluteTimeIntervalSince1970
;
1393 uint64_t TimestampFromCFAbsoluteTime(CFAbsoluteTime at
)
1395 return (uint64_t)(at
+ kCFAbsoluteTimeIntervalSince1970
) * 1000;
1402 If the 'sct' is valid, add it to the validatingLogs dictionary.
1405 - validatingLogs: mutable dictionary to which to add the log that validate this SCT.
1407 - entry_type: 0 for x509 cert, 1 for precert.
1408 - entry: the cert or precert data.
1409 - vt: verification time timestamp (as used in SCTs: ms since 1970 Epoch)
1410 - trustedLog: Dictionary contain the Trusted Logs.
1412 The SCT is valid if:
1413 - It decodes properly.
1414 - Its timestamp is less than 'verifyTime'.
1415 - It is signed by a log in 'trustedLogs'.
1416 - If entry_type = 0, the log must be currently qualified.
1417 - If entry_type = 1, the log may be expired.
1419 If the SCT is valid, it's added to the validatinLogs dictionary using the log dictionary as the key, and the timestamp as value.
1420 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.
1425 static CFDictionaryRef
getSCTValidatingLog(CFDataRef sct
, int entry_type
, CFDataRef entry
, uint64_t vt
, CFArrayRef trustedLogs
, CFAbsoluteTime
*sct_at
)
1428 const uint8_t *logID
;
1429 const uint8_t *timestampData
;
1431 size_t extensionsLen
;
1432 const uint8_t *extensionsData
;
1435 size_t signatureLen
;
1436 const uint8_t *signatureData
;
1437 SecKeyRef pubKey
= NULL
;
1438 uint8_t *signed_data
= NULL
;
1439 const SecAsn1Oid
*oid
= NULL
;
1441 CFDataRef logIDData
= NULL
;
1442 CFDictionaryRef result
= 0;
1444 const uint8_t *p
= CFDataGetBytePtr(sct
);
1445 size_t len
= CFDataGetLength(sct
);
1447 require(len
>=43, out
);
1449 version
= p
[0]; p
++; len
--;
1450 logID
= p
; p
+=32; len
-=32;
1451 timestampData
= p
; p
+=8; len
-=8;
1452 extensionsLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1454 require(len
>=extensionsLen
, out
);
1455 extensionsData
= p
; p
+=extensionsLen
; len
-=extensionsLen
;
1457 require(len
>=4, out
);
1458 hashAlg
=p
[0]; p
++; len
--;
1459 sigAlg
=p
[0]; p
++; len
--;
1460 signatureLen
= SSLDecodeUint16(p
); p
+=2; len
-=2;
1461 require(len
==signatureLen
, out
); /* We do not tolerate any extra data after the signature */
1464 /* verify version: only v1(0) is supported */
1466 secerror("SCT version unsupported: %d\n", version
);
1470 /* verify timestamp not in the future */
1471 timestamp
= SSLDecodeUint64(timestampData
);
1472 if(timestamp
> vt
) {
1473 secerror("SCT is in the future: %llu > %llu\n", timestamp
, vt
);
1480 size_t signed_data_len
= 12 + CFDataGetLength(entry
) + 2 + extensionsLen
;
1481 signed_data
= malloc(signed_data_len
);
1482 require(signed_data
, out
);
1485 *q
++ = 0; // certificate_timestamp
1486 memcpy(q
, timestampData
, 8); q
+=8;
1487 q
= SSLEncodeUint16(q
, entry_type
); // logentry type: 0=cert 1=precert
1488 memcpy(q
, CFDataGetBytePtr(entry
), CFDataGetLength(entry
)); q
+= CFDataGetLength(entry
);
1489 q
= SSLEncodeUint16(q
, extensionsLen
);
1490 memcpy(q
, extensionsData
, extensionsLen
);
1492 logIDData
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, logID
, 32, kCFAllocatorNull
);
1494 CFDictionaryRef logData
= CFArrayGetValueMatching(trustedLogs
, ^bool(const void *dict
) {
1495 const void *key_data
;
1496 if(!isDictionary(dict
)) return false;
1497 if(!CFDictionaryGetValueIfPresent(dict
, CFSTR("key"), &key_data
)) return false;
1498 if(!isData(key_data
)) return false;
1499 CFDataRef valueID
= SecSHA256DigestCreateFromData(kCFAllocatorDefault
, (CFDataRef
)key_data
);
1500 bool result
= (bool)(CFDataCompare(logIDData
, valueID
)==kCFCompareEqualTo
);
1501 CFReleaseSafe(valueID
);
1504 require(logData
, out
);
1507 // For external SCTs, only keep SCTs from currently valid logs.
1508 require(!CFDictionaryContainsKey(logData
, CFSTR("expiry")), out
);
1511 CFDataRef logKeyData
= CFDictionaryGetValue(logData
, CFSTR("key"));
1512 require(logKeyData
, out
); // This failing would be an internal logic error
1513 pubKey
= SecKeyCreateFromSubjectPublicKeyInfoData(kCFAllocatorDefault
, logKeyData
);
1514 require(pubKey
, out
);
1516 oid
= oidForSigAlg(hashAlg
, sigAlg
);
1519 algId
.algorithm
= *oid
;
1520 algId
.parameters
.Data
= NULL
;
1521 algId
.parameters
.Length
= 0;
1523 if(SecKeyDigestAndVerify(pubKey
, &algId
, signed_data
, signed_data_len
, signatureData
, signatureLen
)==0) {
1524 *sct_at
= TimestampToCFAbsoluteTime(timestamp
);
1527 secerror("SCT signature failed (log=%@)\n", logData
);
1531 CFReleaseSafe(logIDData
);
1532 CFReleaseSafe(pubKey
);
1538 static void addValidatingLog(CFMutableDictionaryRef validatingLogs
, CFDictionaryRef log
, CFAbsoluteTime sct_at
)
1540 CFDateRef validated_time
= CFDictionaryGetValue(validatingLogs
, log
);
1542 if(validated_time
==NULL
|| (sct_at
< CFDateGetAbsoluteTime(validated_time
))) {
1543 CFDateRef sct_time
= CFDateCreate(kCFAllocatorDefault
, sct_at
);
1544 CFDictionarySetValue(validatingLogs
, log
, sct_time
);
1545 CFReleaseSafe(sct_time
);
1549 static CFArrayRef
copy_ocsp_scts(SecPVCRef pvc
)
1551 CFMutableArrayRef SCTs
= NULL
;
1552 SecCertificateRef leafCert
= NULL
;
1553 SecCertificateRef issuer
= NULL
;
1554 CFArrayRef ocspResponsesData
= NULL
;
1555 SecOCSPRequestRef ocspRequest
= NULL
;
1557 ocspResponsesData
= SecPathBuilderCopyOCSPResponses(pvc
->builder
);
1558 require_quiet(ocspResponsesData
, out
);
1560 require_quiet(SecPVCGetCertificateCount(pvc
)>=2, out
); //we need the issuer key for precerts.
1561 leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1562 issuer
= SecPVCGetCertificateAtIndex(pvc
, 1);
1564 require(leafCert
, out
);
1565 require(issuer
, out
); // not quiet: Those two would likely indicate an internal error, since we already checked the chain length above.
1566 ocspRequest
= SecOCSPRequestCreate(leafCert
, issuer
);
1568 SCTs
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
1571 CFArrayForEach(ocspResponsesData
, ^(const void *value
) {
1572 /* TODO: Should the builder already have the appropriate SecOCSPResponseRef ? */
1573 SecOCSPResponseRef ocspResponse
= SecOCSPResponseCreate(value
);
1574 if(ocspResponse
&& SecOCSPGetResponseStatus(ocspResponse
)==kSecOCSPSuccess
) {
1575 SecOCSPSingleResponseRef ocspSingleResponse
= SecOCSPResponseCopySingleResponse(ocspResponse
, ocspRequest
);
1576 if(ocspSingleResponse
) {
1577 CFArrayRef singleResponseSCTs
= SecOCSPSingleResponseCopySCTs(ocspSingleResponse
);
1578 if(singleResponseSCTs
) {
1579 CFArrayAppendArray(SCTs
, singleResponseSCTs
, CFRangeMake(0, CFArrayGetCount(singleResponseSCTs
)));
1580 CFRelease(singleResponseSCTs
);
1582 SecOCSPSingleResponseDestroy(ocspSingleResponse
);
1585 if(ocspResponse
) SecOCSPResponseFinalize(ocspResponse
);
1588 if(CFArrayGetCount(SCTs
)==0) {
1589 CFReleaseNull(SCTs
);
1593 CFReleaseSafe(ocspResponsesData
);
1595 SecOCSPRequestFinalize(ocspRequest
);
1600 static void SecPolicyCheckCT(SecPVCRef pvc
)
1602 SecCertificateRef leafCert
= SecPVCGetCertificateAtIndex(pvc
, 0);
1603 CFArrayRef embeddedScts
= SecCertificateCopySignedCertificateTimestamps(leafCert
);
1604 CFArrayRef builderScts
= SecPathBuilderCopySignedCertificateTimestamps(pvc
->builder
);
1605 CFArrayRef trustedLogs
= SecPathBuilderCopyTrustedLogs(pvc
->builder
);
1606 CFArrayRef ocspScts
= copy_ocsp_scts(pvc
);
1607 CFDataRef precertEntry
= copy_precert_entry_from_chain(pvc
);
1608 CFDataRef x509Entry
= copy_x509_entry_from_chain(pvc
);
1609 __block
uint32_t trustedSCTCount
= 0;
1610 __block CFIndex totalSCTSize
= 0;
1612 // This eventually contain list of logs who validated the SCT.
1613 CFMutableDictionaryRef currentLogsValidatingScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1614 CFMutableDictionaryRef logsValidatingEmbeddedScts
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1616 uint64_t vt
= TimestampFromCFAbsoluteTime(SecPVCGetVerifyTime(pvc
));
1618 __block
bool at_least_one_currently_valid_external
= 0;
1619 __block
bool at_least_one_currently_valid_embedded
= 0;
1621 require(logsValidatingEmbeddedScts
, out
);
1622 require(currentLogsValidatingScts
, out
);
1624 if(trustedLogs
) { // Don't bother trying to validate SCTs if we don't have any trusted logs.
1625 if(embeddedScts
&& precertEntry
) { // Don't bother if we could not get the precert.
1626 CFArrayForEach(embeddedScts
, ^(const void *value
){
1627 CFAbsoluteTime sct_at
;
1628 CFDictionaryRef log
= getSCTValidatingLog(value
, 1, precertEntry
, vt
, trustedLogs
, &sct_at
);
1630 addValidatingLog(logsValidatingEmbeddedScts
, log
, sct_at
);
1631 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
1632 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1633 at_least_one_currently_valid_embedded
= true;
1637 totalSCTSize
+= CFDataGetLength(value
);
1641 if(builderScts
&& x509Entry
) { // Don't bother if we could not get the cert.
1642 CFArrayForEach(builderScts
, ^(const void *value
){
1643 CFAbsoluteTime sct_at
;
1644 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
1646 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1647 at_least_one_currently_valid_external
= true;
1650 totalSCTSize
+= CFDataGetLength(value
);
1654 if(ocspScts
&& x509Entry
) {
1655 CFArrayForEach(ocspScts
, ^(const void *value
){
1656 CFAbsoluteTime sct_at
;
1657 CFDictionaryRef log
= getSCTValidatingLog(value
, 0, x509Entry
, vt
, trustedLogs
, &sct_at
);
1659 addValidatingLog(currentLogsValidatingScts
, log
, sct_at
);
1660 at_least_one_currently_valid_external
= true;
1663 totalSCTSize
+= CFDataGetLength(value
);
1669 /* We now have 2 sets of logs that validated those SCTS, count them and make a final decision.
1672 is_ct = (A1 AND A2) OR (B1 AND B2).
1674 A1: embedded SCTs from 2+ to 5+ logs valid at issuance time
1675 A2: At least one embedded SCT from a currently valid log.
1677 B1: SCTs from 2 currently valid logs (from any source)
1678 B2: At least 1 external SCT from a currently valid log.
1682 SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc
->builder
), false);
1684 if(at_least_one_currently_valid_external
&& CFDictionaryGetCount(currentLogsValidatingScts
)>=2) {
1685 SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc
->builder
), true);
1686 } else if(at_least_one_currently_valid_embedded
) {
1687 __block CFAbsoluteTime issuanceTime
= SecPVCGetVerifyTime(pvc
);
1688 __block
int lifetime
; // in Months
1689 __block
unsigned once_or_current_qualified_embedded
= 0;
1691 /* Calculate issuance time based on timestamp of SCTs from current logs */
1692 CFDictionaryForEach(currentLogsValidatingScts
, ^(const void *key
, const void *value
) {
1693 CFDictionaryRef log
= key
;
1694 if(!CFDictionaryContainsKey(log
, CFSTR("expiry"))) {
1695 // Log is still qualified
1696 CFDateRef ts
= (CFDateRef
) value
;
1697 CFAbsoluteTime timestamp
= CFDateGetAbsoluteTime(ts
);
1698 if(timestamp
< issuanceTime
) {
1699 issuanceTime
= timestamp
;
1705 CFDictionaryForEach(logsValidatingEmbeddedScts
, ^(const void *key
, const void *value
) {
1706 CFDictionaryRef log
= key
;
1707 CFDateRef ts
= value
;
1708 CFDateRef expiry
= CFDictionaryGetValue(log
, CFSTR("expiry"));
1709 if (expiry
== NULL
) { // Currently qualified OR
1710 once_or_current_qualified_embedded
++;
1711 } else if (CFDateCompare(ts
, expiry
, NULL
) == kCFCompareLessThan
&& // Once qualified. That is, qualified at the time of SCT AND
1712 issuanceTime
< CFDateGetAbsoluteTime(expiry
)) { // at the time of issuance.)
1713 once_or_current_qualified_embedded
++;
1718 SecCFCalendarDoWithZuluCalendar(^(CFCalendarRef zuluCalendar
) {
1720 CFCalendarGetComponentDifference(zuluCalendar
,
1721 SecCertificateNotValidBefore(leafCert
),
1722 SecCertificateNotValidAfter(leafCert
),
1723 0, "M", &_lifetime
);
1724 lifetime
= _lifetime
;
1727 unsigned requiredEmbeddedSctsCount
;
1729 if (lifetime
< 15) {
1730 requiredEmbeddedSctsCount
= 2;
1731 } else if (lifetime
<= 27) {
1732 requiredEmbeddedSctsCount
= 3;
1733 } else if (lifetime
<= 39) {
1734 requiredEmbeddedSctsCount
= 4;
1736 requiredEmbeddedSctsCount
= 5;
1739 if(once_or_current_qualified_embedded
>= requiredEmbeddedSctsCount
){
1740 SecCertificatePathVCSetIsCT(SecPathBuilderGetPath(pvc
->builder
), true);
1744 /* Record analytics data for CT */
1745 TrustAnalyticsBuilder
*analytics
= SecPathBuilderGetAnalyticsData(pvc
->builder
);
1746 require_quiet(analytics
, out
);
1747 uint32_t sctCount
= 0;
1748 /* Count the total number of SCTs we found and report where we got them */
1749 if (embeddedScts
&& CFArrayGetCount(embeddedScts
) > 0) {
1750 analytics
->sct_sources
|= TA_SCTEmbedded
;
1751 sctCount
+= CFArrayGetCount(embeddedScts
);
1753 if (builderScts
&& CFArrayGetCount(builderScts
) > 0) {
1754 analytics
->sct_sources
|= TA_SCT_TLS
;
1755 sctCount
+= CFArrayGetCount(builderScts
);
1757 if (ocspScts
&& CFArrayGetCount(ocspScts
) > 0) {
1758 analytics
->sct_sources
|= TA_SCT_OCSP
;
1759 sctCount
+= CFArrayGetCount(ocspScts
);
1761 /* Report how many of those SCTs were once or currently qualified */
1762 analytics
->number_trusted_scts
= trustedSCTCount
;
1763 /* Report the total number of bytes in the SCTs */
1764 analytics
->total_sct_size
= totalSCTSize
;
1765 /* Report how many SCTs we got */
1766 analytics
->number_scts
= sctCount
;
1769 CFReleaseSafe(logsValidatingEmbeddedScts
);
1770 CFReleaseSafe(currentLogsValidatingScts
);
1771 CFReleaseSafe(builderScts
);
1772 CFReleaseSafe(embeddedScts
);
1773 CFReleaseSafe(ocspScts
);
1774 CFReleaseSafe(precertEntry
);
1775 CFReleaseSafe(trustedLogs
);
1776 CFReleaseSafe(x509Entry
);
1779 static bool checkPolicyOidData(SecPVCRef pvc
, CFDataRef oid
) {
1780 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1782 key_value
.data
= (DERByte
*)CFDataGetBytePtr(oid
);
1783 key_value
.length
= (DERSize
)CFDataGetLength(oid
);
1785 for (ix
= 0; ix
< count
; ix
++) {
1786 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1787 policy_set_t policies
= policies_for_cert(cert
);
1789 if (policy_set_contains(policies
, &key_value
)) {
1790 policy_set_free(policies
);
1793 policy_set_free(policies
);
1798 static void SecPolicyCheckCertificatePolicy(SecPVCRef pvc
, CFStringRef key
)
1800 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1801 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1802 bool result
= false;
1804 if (CFGetTypeID(value
) == CFDataGetTypeID())
1806 result
= checkPolicyOidData(pvc
, value
);
1807 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
1808 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
1810 result
= checkPolicyOidData(pvc
, dataOid
);
1815 SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
1820 static void SecPolicyCheckRevocation(SecPVCRef pvc
,
1822 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1823 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1824 if (isString(value
)) {
1825 SecPathBuilderSetRevocationMethod(pvc
->builder
, value
);
1829 static void SecPolicyCheckRevocationResponseRequired(SecPVCRef pvc
,
1831 pvc
->require_revocation_response
= true;
1832 secdebug("policy", "revocation response required");
1835 static void SecPolicyCheckRevocationOnline(SecPVCRef pvc
, CFStringRef key
) {
1836 SecPathBuilderSetCheckRevocationOnline(pvc
->builder
);
1839 static void SecPolicyCheckNoNetworkAccess(SecPVCRef pvc
,
1841 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1842 CFTypeRef value
= CFDictionaryGetValue(policy
->_options
, key
);
1843 if (value
== kCFBooleanTrue
) {
1844 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, false);
1846 SecPathBuilderSetCanAccessNetwork(pvc
->builder
, true);
1850 static void SecPolicyCheckWeakKeySize(SecPVCRef pvc
,
1852 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1853 for (ix
= 0; ix
< count
; ++ix
) {
1854 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1855 if (cert
&& SecCertificateIsWeakKey(cert
)) {
1856 /* Intermediate certificate has a weak key. */
1857 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1863 static void SecPolicyCheckKeySize(SecPVCRef pvc
, CFStringRef key
) {
1864 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1865 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1866 CFDictionaryRef keySizes
= CFDictionaryGetValue(policy
->_options
, key
);
1867 for (ix
= 0; ix
< count
; ++ix
) {
1868 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1869 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
1870 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1876 static void SecPolicyCheckWeakSignature(SecPVCRef pvc
, CFStringRef key
) {
1877 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1878 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1879 CFTypeRef pvcValue
= CFDictionaryGetValue(policy
->_options
, key
);
1880 for (ix
= 0; ix
< count
; ++ix
) {
1881 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1882 if (!SecPolicyCheckCertWeakSignature(cert
, pvcValue
)) {
1883 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1889 static void SecPolicyCheckSignatureHashAlgorithms(SecPVCRef pvc
,
1891 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1892 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
1893 CFSetRef disallowedHashAlgorithms
= CFDictionaryGetValue(policy
->_options
, key
);
1894 for (ix
= 0; ix
< count
; ++ix
) {
1895 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1896 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashAlgorithms
)) {
1897 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
))
1903 static bool leaf_is_on_weak_hash_whitelist(SecPVCRef pvc
) {
1904 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1905 require_quiet(leaf
, out
);
1907 /* And now a special snowflake from our tests */
1909 /* subject:/C=AU/ST=NSW/L=St Leonards/O=VODAFONE HUTCHISON AUSTRALIA PTY LIMITED/OU=Technology Shared Services/CN=mybill.vodafone.com.au */
1910 /* issuer :/C=UK/O=Vodafone Group/CN=Vodafone (Corporate Services 2009) */
1911 /* Not After : May 26 09:37:50 2017 GMT */
1912 static const uint8_t vodafone
[] = {
1913 0xde, 0x77, 0x63, 0x97, 0x79, 0x47, 0xee, 0x6e, 0xc1, 0x3a,
1914 0x7b, 0x3b, 0xad, 0x43, 0x88, 0xa9, 0x66, 0x59, 0xa8, 0x18
1917 CFDataRef leafFingerprint
= SecCertificateGetSHA1Digest(leaf
);
1918 require_quiet(leafFingerprint
, out
);
1919 const unsigned int len
= 20;
1920 const uint8_t *dp
= CFDataGetBytePtr(leafFingerprint
);
1921 if (dp
&& (!memcmp(vodafone
, dp
, len
))) {
1929 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
);
1931 static void SecPolicyCheckSystemTrustedWeakHash(SecPVCRef pvc
,
1933 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1935 Boolean keyInPolicy
= false;
1936 CFArrayRef policies
= pvc
->policies
;
1937 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1938 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1939 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1940 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
1945 /* We only enforce this check when *both* of the following are true:
1946 * 1. One of the certs in the path has this usage constraint, and
1947 * 2. One of the policies in the PVC has this key
1948 * (As compared to normal policy options which require only one to be true..) */
1949 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
1952 /* Ignore the anchor if it's trusted */
1953 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
1956 for (ix
= 0; ix
< count
; ++ix
) {
1957 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1958 if (SecCertificateIsWeakHash(cert
)) {
1959 if (!leaf_is_on_weak_hash_whitelist(pvc
)) {
1960 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
1970 static void SecPolicyCheckSystemTrustedWeakKey(SecPVCRef pvc
,
1972 CFIndex ix
, count
= SecPVCGetCertificateCount(pvc
);
1974 Boolean keyInPolicy
= false;
1975 CFArrayRef policies
= pvc
->policies
;
1976 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
1977 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
1978 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
1979 if (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)) {
1984 /* We only enforce this check when *both* of the following are true:
1985 * 1. One of the certs in the path has this usage constraint, and
1986 * 2. One of the policies in the PVC has this key
1987 * (As compared to normal policy options which require only one to be true..) */
1988 require_quiet(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) &&
1991 /* Ignore the anchor if it's trusted */
1992 if (SecPathBuilderIsAnchored(pvc
->builder
)) {
1995 for (ix
= 0; ix
< count
; ++ix
) {
1996 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
1997 if (!SecCertificateIsStrongKey(cert
)) {
1998 if (!SecPVCSetResult(pvc
, key
, ix
, kCFBooleanFalse
)) {
2008 static void SecPolicyCheckPinningRequired(SecPVCRef pvc
, CFStringRef key
) {
2009 /* Pinning is disabled on the system, skip. */
2010 if (SecIsInternalRelease()) {
2011 if (CFPreferencesGetAppBooleanValue(CFSTR("AppleServerAuthenticationNoPinning"),
2012 CFSTR("com.apple.security"), NULL
)) {
2017 CFArrayRef policies
= pvc
->policies
;
2018 CFIndex policyIX
, policyCount
= CFArrayGetCount(policies
);
2019 for (policyIX
= 0; policyIX
< policyCount
; ++policyIX
) {
2020 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
2021 CFStringRef policyName
= SecPolicyGetName(policy
);
2022 if (CFEqualSafe(policyName
, CFSTR("sslServer"))) {
2023 /* policy required pinning, but we didn't use a pinning policy */
2024 if (!SecPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
)) {
2031 void SecPolicyServerInitialize(void) {
2032 gSecPolicyLeafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2033 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2034 gSecPolicyPathCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2035 &kCFTypeDictionaryKeyCallBacks
, NULL
);
2037 #undef POLICYCHECKMACRO
2038 #define __PC_ADD_CHECK_(NAME)
2039 #define __PC_ADD_CHECK_L(NAME) CFDictionaryAddValue(gSecPolicyLeafCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2040 #define __PC_ADD_CHECK_A(NAME) CFDictionaryAddValue(gSecPolicyPathCallbacks, kSecPolicyCheck##NAME, SecPolicyCheck##NAME);
2042 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2043 __PC_ADD_CHECK_##LEAFCHECK(NAME) \
2044 __PC_ADD_CHECK_##PATHCHECK(NAME)
2045 #include "../Security/SecPolicyChecks.list"
2047 /* Some of these don't follow the naming conventions but are in the Pinning DB.
2048 * <rdar://34537018> fix policy check constant values */
2049 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkerOid"), SecPolicyCheckLeafMarkerOid
);
2050 CFDictionaryAddValue(gSecPolicyLeafCallbacks
, CFSTR("CheckLeafMarkersProdAndQA"), SecPolicyCheckLeafMarkersProdAndQA
);
2051 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateMarkerOid"), SecPolicyCheckIntermediateMarkerOid
);
2052 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateCountry"), SecPolicyCheckIntermediateCountry
);
2053 CFDictionaryAddValue(gSecPolicyPathCallbacks
, CFSTR("CheckIntermediateOrganization"), SecPolicyCheckIntermediateOrganization
);
2058 /********************************************************
2059 ****************** SecPVCRef Functions *****************
2060 ********************************************************/
2062 void SecPVCInit(SecPVCRef pvc
, SecPathBuilderRef builder
, CFArrayRef policies
) {
2063 secdebug("alloc", "%p", pvc
);
2064 // Weird logging policies crashes.
2065 //secdebug("policy", "%@", policies);
2067 // Zero the pvc struct so only non-zero fields need to be explicitly set
2068 memset(pvc
, 0, sizeof(struct OpaqueSecPVC
));
2069 pvc
->builder
= builder
;
2070 pvc
->policies
= policies
;
2073 pvc
->result
= kSecTrustResultUnspecified
;
2075 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2076 &kCFTypeDictionaryKeyCallBacks
,
2077 &kCFTypeDictionaryValueCallBacks
);
2078 pvc
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
,
2079 1, &kCFTypeArrayCallBacks
);
2080 CFRelease(certDetail
);
2083 void SecPVCDelete(SecPVCRef pvc
) {
2084 secdebug("alloc", "%p", pvc
);
2085 CFReleaseNull(pvc
->policies
);
2086 CFReleaseNull(pvc
->details
);
2087 CFReleaseNull(pvc
->leafDetails
);
2090 void SecPVCSetPath(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2091 secdebug("policy", "%@", path
);
2093 pvc
->result
= kSecTrustResultUnspecified
;
2094 CFReleaseNull(pvc
->details
);
2097 void SecPVCComputeDetails(SecPVCRef pvc
, SecCertificatePathVCRef path
) {
2100 /* Since we don't run the LeafChecks again, we need to preserve the
2101 * result the leaf had. */
2102 CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(path
);
2103 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
2104 pathLength
, pvc
->leafDetails
);
2105 for (ix
= 1; ix
< pathLength
; ++ix
) {
2106 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
2107 &kCFTypeDictionaryKeyCallBacks
,
2108 &kCFTypeDictionaryValueCallBacks
);
2109 CFArrayAppendValue(details
, certDetail
);
2110 CFRelease(certDetail
);
2112 CFRetainAssign(pvc
->details
, details
);
2113 pvc
->result
= pvc
->leafResult
;
2114 CFReleaseSafe(details
);
2117 SecPolicyRef
SecPVCGetPolicy(SecPVCRef pvc
) {
2118 return (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
2121 static CFIndex
SecPVCGetCertificateCount(SecPVCRef pvc
) {
2122 return SecPathBuilderGetCertificateCount(pvc
->builder
);
2125 static SecCertificateRef
SecPVCGetCertificateAtIndex(SecPVCRef pvc
, CFIndex ix
) {
2126 return SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2129 static CFAbsoluteTime
SecPVCGetVerifyTime(SecPVCRef pvc
) {
2130 return SecPathBuilderGetVerifyTime(pvc
->builder
);
2133 static bool SecPVCIsExceptedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
, CFTypeRef value
) {
2134 CFArrayRef exceptions
= SecPathBuilderGetExceptions(pvc
->builder
);
2135 if (!exceptions
) { return false; }
2136 CFIndex exceptionsCount
= CFArrayGetCount(exceptions
);
2138 /* There are two types of exceptions:
2139 * 1. Those that are built from SecTrustCopyExceptions, which are particular to the
2140 * certs in the chain -- as indicated by the SHA1 digest in the exception dictionary.
2141 * 2. On macOS, those built from SecTrustSetOptions, which are generic excepted errors.
2144 CFDictionaryRef options
= CFArrayGetValueAtIndex(exceptions
, 0);
2146 if (exceptionsCount
== 1 && (ix
> 0 || !CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
))) {
2147 /* SHA1Digest not allowed */
2148 if (CFDictionaryContainsKey(options
, kSecCertificateDetailSHA1Digest
)) { return false; }
2150 if (CFDictionaryContainsKey(options
, key
)) {
2151 /* Special case -- AnchorTrusted only for self-signed certs */
2152 if (CFEqual(kSecPolicyCheckAnchorTrusted
, key
)) {
2153 Boolean isSelfSigned
= false;
2154 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2155 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2160 } else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
) && CFDictionaryContainsKey(options
, kSecPolicyCheckValidRoot
)) {
2161 /* Another special case - ValidRoot excepts Valid only for self-signed certs */
2162 Boolean isSelfSigned
= false;
2163 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2164 if (!cert
|| (errSecSuccess
!= SecCertificateIsSelfSigned(cert
, &isSelfSigned
)) || !isSelfSigned
) {
2173 if (ix
>= exceptionsCount
) { return false; }
2174 CFDictionaryRef exception
= CFArrayGetValueAtIndex(exceptions
, ix
);
2176 /* Compare the cert hash */
2177 if (!CFDictionaryContainsKey(exception
, kSecCertificateDetailSHA1Digest
)) { return false; }
2178 SecCertificateRef cert
= SecPathBuilderGetCertificateAtIndex(pvc
->builder
, ix
);
2179 if (!CFEqual(SecCertificateGetSHA1Digest(cert
), CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
))) {
2184 CFTypeRef exceptionValue
= CFDictionaryGetValue(exception
, key
);
2185 if (exceptionValue
&& CFEqual(value
, exceptionValue
)) {
2186 /* Only change result if PVC is already ok */
2187 if (SecPVCIsOkResult(pvc
)) {
2188 // Chains that pass due to exceptions get Proceed result.
2189 pvc
->result
= kSecTrustResultProceed
;
2197 static int32_t detailKeyToCssmErr(CFStringRef key
) {
2200 if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
2201 result
= -2147408896; // CSSMERR_APPLETP_HOSTNAME_MISMATCH
2203 else if (CFEqual(key
, kSecPolicyCheckEmail
)) {
2204 result
= -2147408872; // CSSMERR_APPLETP_SMIME_EMAIL_ADDRS_NOT_FOUND
2206 else if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
2207 result
= -2147409654; // CSSMERR_TP_CERT_EXPIRED
2213 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
);
2215 static bool SecPVCIsAllowedError(SecPVCRef pvc
, CFIndex ix
, CFStringRef key
) {
2216 bool result
= false;
2217 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2218 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, ix
);
2219 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2220 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2222 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2223 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2224 CFNumberRef allowedErrorNumber
= NULL
;
2225 if (!isDictionary(constraint
)) {
2228 allowedErrorNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsAllowedError
);
2229 int32_t allowedErrorValue
= 0;
2230 if (!isNumber(allowedErrorNumber
) || !CFNumberGetValue(allowedErrorNumber
, kCFNumberSInt32Type
, &allowedErrorValue
)) {
2234 if (SecPVCMeetsConstraint(pvc
, cert
, constraint
)) {
2235 if (allowedErrorValue
== detailKeyToCssmErr(key
)) {
2244 static bool SecPVCKeyIsConstraintPolicyOption(SecPVCRef pvc
, CFStringRef key
) {
2245 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2246 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2247 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2248 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2249 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2250 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2251 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2252 if (!isDictionary(constraint
)) {
2256 CFDictionaryRef policyOptions
= NULL
;
2257 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2258 if (policyOptions
&& isDictionary(policyOptions
) &&
2259 CFDictionaryContainsKey(policyOptions
, key
)) {
2267 static SecTrustResultType
trust_result_for_key(CFStringRef key
) {
2268 SecTrustResultType result
= kSecTrustResultRecoverableTrustFailure
;
2269 #undef POLICYCHECKMACRO
2270 #define __PC_TYPE_MEMBER_ false
2271 #define __PC_TYPE_MEMBER_R false
2272 #define __PC_TYPE_MEMBER_F true
2273 #define __PC_TYPE_MEMBER_D true
2275 #define __TRUSTRESULT_ kSecTrustResultRecoverableTrustFailure
2276 #define __TRUSTRESULT_F kSecTrustResultFatalTrustFailure
2277 #define __TRUSTRESULT_D kSecTrustResultDeny
2278 #define __TRUSTRESULT_R kSecTrustResultRecoverableTrustFailure
2280 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
2281 if (__PC_TYPE_MEMBER_##TRUSTRESULT && CFEqual(key,CFSTR(#NAME))) { \
2282 result = __TRUSTRESULT_##TRUSTRESULT; \
2284 #include "../Security/SecPolicyChecks.list"
2289 /* AUDIT[securityd](done):
2290 policy->_options is a caller provided dictionary, only its cf type has
2293 bool SecPVCSetResultForced(SecPVCRef pvc
,
2294 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
2296 secnotice("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
,
2297 (pvc
->callbacks
== gSecPolicyLeafCallbacks
? "leaf"
2298 : (pvc
->callbacks
== gSecPolicyPathCallbacks
? "path"
2300 (force
? "force" : ""), result
);
2302 /* If this is not something the current policy cares about ignore
2303 this error and return true so our caller continues evaluation. */
2305 /* Either the policy or the usage constraints have to have this key */
2306 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
2307 if (!(SecPVCKeyIsConstraintPolicyOption(pvc
, key
) ||
2308 (policy
&& CFDictionaryContainsKey(policy
->_options
, key
)))) {
2313 /* Check to see if the SecTrustSettings for the certificate in question
2314 tell us to ignore this error. */
2315 if (SecPVCIsAllowedError(pvc
, ix
, key
)) {
2316 secinfo("policy", "cert[%d]: skipped allowed error %@", (int) ix
, key
);
2320 /* Check to see if exceptions tells us to ignore this error. */
2321 if (SecPVCIsExceptedError(pvc
, ix
, key
, result
)) {
2322 secinfo("policy", "cert[%d]: skipped exception error %@", (int) ix
, key
);
2326 /* Avoid resetting deny or fatal to recoverable */
2327 SecTrustResultType trustResult
= trust_result_for_key(key
);
2328 if (SecPVCIsOkResult(pvc
) || trustResult
== kSecTrustResultFatalTrustFailure
) {
2329 pvc
->result
= trustResult
;
2330 } else if (trustResult
== kSecTrustResultDeny
&&
2331 pvc
->result
== kSecTrustResultRecoverableTrustFailure
) {
2332 pvc
->result
= trustResult
;
2338 CFMutableDictionaryRef detail
=
2339 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
2341 /* Perhaps detail should have an array of results per key? As it stands
2342 in the case of multiple policy failures the last failure stands. */
2343 CFDictionarySetValue(detail
, key
, result
);
2348 bool SecPVCSetResult(SecPVCRef pvc
,
2349 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
2350 return SecPVCSetResultForced(pvc
, key
, ix
, result
, false);
2353 /* AUDIT[securityd](done):
2354 key(ok) is a caller provided.
2355 value(ok, unused) is a caller provided.
2357 static void SecPVCValidateKey(const void *key
, const void *value
,
2359 SecPVCRef pvc
= (SecPVCRef
)context
;
2361 /* If our caller doesn't want full details and we failed earlier there is
2362 no point in doing additional checks. */
2363 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
2366 SecPolicyCheckFunction fcn
= (SecPolicyCheckFunction
)
2367 CFDictionaryGetValue(pvc
->callbacks
, key
);
2370 /* "Optional" policy checks. This may be a new key from the
2371 * pinning DB which is not implemented in this OS version. Log a
2372 * warning, and on debug builds fail evaluation, to encourage us
2373 * to ensure that checks are synchronized across the same build. */
2374 if (pvc
->callbacks
== gSecPolicyLeafCallbacks
) {
2375 if (!CFDictionaryContainsKey(gSecPolicyPathCallbacks
, key
)) {
2376 secwarning("policy: unknown policy key %@, skipping", key
);
2378 pvc
->result
= kSecTrustResultOtherError
;
2381 } else if (pvc
->callbacks
== gSecPolicyPathCallbacks
) {
2382 if (!CFDictionaryContainsKey(gSecPolicyLeafCallbacks
, key
)) {
2383 secwarning("policy: unknown policy key %@, skipping", key
);
2385 pvc
->result
= kSecTrustResultOtherError
;
2389 /* Non standard validation phase, nothing is optional. */
2390 pvc
->result
= kSecTrustResultOtherError
;
2395 fcn(pvc
, (CFStringRef
)key
);
2398 /* AUDIT[securityd](done):
2399 policy->_options is a caller provided dictionary, only its cf type has
2402 SecTrustResultType
SecPVCLeafChecks(SecPVCRef pvc
) {
2403 /* We need to compute details for the leaf. */
2404 CFRetainAssign(pvc
->details
, pvc
->leafDetails
);
2406 CFArrayRef policies
= pvc
->policies
;
2407 CFIndex ix
, count
= CFArrayGetCount(policies
);
2408 for (ix
= 0; ix
< count
; ++ix
) {
2409 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2411 /* Validate all keys for all policies. */
2412 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2413 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
2416 pvc
->leafResult
= pvc
->result
;
2417 CFRetainAssign(pvc
->leafDetails
, pvc
->details
);
2422 bool SecPVCIsOkResult(SecPVCRef pvc
) {
2423 if (pvc
->result
== kSecTrustResultRecoverableTrustFailure
||
2424 pvc
->result
== kSecTrustResultDeny
||
2425 pvc
->result
== kSecTrustResultFatalTrustFailure
||
2426 pvc
->result
== kSecTrustResultOtherError
) {
2432 bool SecPVCParentCertificateChecks(SecPVCRef pvc
, CFIndex ix
) {
2433 /* Check stuff common to intermediate and anchors. */
2434 CFAbsoluteTime verifyTime
= SecPVCGetVerifyTime(pvc
);
2435 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2436 CFIndex anchor_ix
= SecPVCGetCertificateCount(pvc
) - 1;
2437 bool is_anchor
= (ix
== anchor_ix
&& SecPathBuilderIsAnchored(pvc
->builder
));
2439 if (!SecCertificateIsValid(cert
, verifyTime
)) {
2440 /* Certificate has expired. */
2441 if (!SecPVCSetResult(pvc
, kSecPolicyCheckTemporalValidity
, ix
, kCFBooleanFalse
)) {
2446 if (SecCertificateIsWeakKey(cert
)) {
2447 /* Certificate uses weak key. */
2448 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakKeySize
, ix
, kCFBooleanFalse
)) {
2453 if (!SecPolicyCheckCertWeakSignature(cert
, NULL
)) {
2454 /* Certificate uses weak hash. */
2455 if (!SecPVCSetResult(pvc
, kSecPolicyCheckWeakSignature
, ix
, kCFBooleanFalse
)) {
2461 /* Perform anchor specific checks. */
2462 /* Don't think we have any of these. */
2464 /* Perform intermediate specific checks. */
2466 /* (k) Basic constraints only relevant for v3 and later. */
2467 if (SecCertificateVersion(cert
) >= 3) {
2468 const SecCEBasicConstraints
*bc
=
2469 SecCertificateGetBasicConstraints(cert
);
2471 /* Basic constraints not present, illegal. */
2472 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2473 ix
, kCFBooleanFalse
, true)) {
2476 } else if (!bc
->isCA
) {
2477 /* Basic constraints not marked as isCA, illegal. */
2478 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraintsCA
,
2479 ix
, kCFBooleanFalse
, true)) {
2484 /* For a v1 or v2 certificate in an intermediate slot (not a leaf and
2485 not an anchor), we additionally require that the certificate chain
2486 does not end in a v3 or later anchor. [rdar://32204517] */
2487 else if (ix
> 0 && ix
< anchor_ix
) {
2488 SecCertificateRef anchor
= SecPVCGetCertificateAtIndex(pvc
, anchor_ix
);
2489 if (SecCertificateVersion(anchor
) >= 3) {
2490 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckBasicConstraints
,
2491 ix
, kCFBooleanFalse
, true)) {
2496 /* (l) max_path_length is checked elsewhere. */
2498 /* (n) If a key usage extension is present, verify that the keyCertSign bit is set. */
2499 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
2500 if (keyUsage
&& !(keyUsage
& kSecKeyUsageKeyCertSign
)) {
2501 if (!SecPVCSetResultForced(pvc
, kSecPolicyCheckKeyUsage
,
2502 ix
, kCFBooleanFalse
, true)) {
2509 return SecPVCIsOkResult(pvc
);
2512 static bool SecPVCBlackListedKeyChecks(SecPVCRef pvc
, CFIndex ix
) {
2513 /* Check stuff common to intermediate and anchors. */
2515 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2516 if (NULL
!= otapkiRef
)
2518 CFSetRef blackListedKeys
= SecOTAPKICopyBlackListSet(otapkiRef
);
2519 CFRelease(otapkiRef
);
2520 if (NULL
!= blackListedKeys
)
2522 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2523 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2524 bool is_last
= (ix
== count
- 1);
2525 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2527 /* Check for blacklisted intermediate issuer keys. */
2528 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2530 /* Check dgst against blacklist. */
2531 if (CFSetContainsValue(blackListedKeys
, dgst
)) {
2532 /* Check allow list for this blacklisted issuer key,
2533 which is the authority key of the issued cert at ix-1.
2535 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2536 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2538 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
,
2539 ix
, kCFBooleanFalse
, true);
2545 CFRelease(blackListedKeys
);
2546 return SecPVCIsOkResult(pvc
);
2553 static bool SecPVCGrayListedKeyChecks(SecPVCRef pvc
, CFIndex ix
)
2555 /* Check stuff common to intermediate and anchors. */
2556 SecOTAPKIRef otapkiRef
= SecOTAPKICopyCurrentOTAPKIRef();
2557 if (NULL
!= otapkiRef
)
2559 CFSetRef grayListKeys
= SecOTAPKICopyGrayList(otapkiRef
);
2560 CFRelease(otapkiRef
);
2561 if (NULL
!= grayListKeys
)
2563 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, ix
);
2564 CFIndex count
= SecPVCGetCertificateCount(pvc
);
2565 bool is_last
= (ix
== count
- 1);
2566 bool is_anchor
= (is_last
&& SecPathBuilderIsAnchored(pvc
->builder
));
2568 /* Check for gray listed intermediate issuer keys. */
2569 CFDataRef dgst
= SecCertificateCopyPublicKeySHA1Digest(cert
);
2571 /* Check dgst against gray list. */
2572 if (CFSetContainsValue(grayListKeys
, dgst
)) {
2573 /* Check allow list for this graylisted issuer key,
2574 which is the authority key of the issued cert at ix-1.
2576 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2577 bool allowed
= path
&& SecCertificatePathVCIsAllowlisted(path
);
2579 SecPVCSetResultForced(pvc
, kSecPolicyCheckGrayListedKey
,
2580 ix
, kCFBooleanFalse
, true);
2586 CFRelease(grayListKeys
);
2587 return SecPVCIsOkResult(pvc
);
2594 static bool SecPVCContainsPolicy(SecPVCRef pvc
, CFStringRef searchOid
, CFStringRef searchName
, CFIndex
*policyIX
) {
2595 if (!isString(searchName
) && !isString(searchOid
)) {
2598 CFArrayRef policies
= pvc
->policies
;
2599 CFIndex ix
, count
= CFArrayGetCount(policies
);
2600 for (ix
= 0; ix
< count
; ++ix
) {
2601 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
2602 CFStringRef policyName
= SecPolicyGetName(policy
);
2603 CFStringRef policyOid
= SecPolicyGetOidString(policy
);
2604 /* Prefer a match of both name and OID */
2605 if (searchOid
&& searchName
&& policyOid
&& policyName
) {
2606 if (CFEqual(searchOid
, policyOid
) &&
2607 CFEqual(searchName
, policyName
)) {
2608 if (policyIX
) { *policyIX
= ix
; }
2612 /* Next best is just OID. */
2613 if (!searchName
&& searchOid
&& policyOid
) {
2614 if (CFEqual(searchOid
, policyOid
)) {
2615 if (policyIX
) { *policyIX
= ix
; }
2619 if (!searchOid
&& searchName
&& policyName
) {
2620 if (CFEqual(searchName
, policyName
)) {
2621 if (policyIX
) { *policyIX
= ix
; }
2629 static bool SecPVCContainsString(SecPVCRef pvc
, CFIndex policyIX
, CFStringRef stringValue
) {
2630 if (!isString(stringValue
)) {
2633 bool result
= false;
2635 CFStringRef tmpStringValue
= NULL
;
2636 if (CFStringGetCharacterAtIndex(stringValue
, CFStringGetLength(stringValue
) -1) == (UniChar
)0x0000) {
2637 tmpStringValue
= CFStringCreateTruncatedCopy(stringValue
, CFStringGetLength(stringValue
) - 1);
2639 tmpStringValue
= CFStringCreateCopy(NULL
, stringValue
);
2641 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
2642 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
2643 /* Have to look for all the possible locations of name string */
2644 CFStringRef policyString
= NULL
;
2645 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
2646 if (!policyString
) {
2647 policyString
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEmail
);
2649 if (policyString
&& (CFStringCompare(tmpStringValue
, policyString
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
)) {
2654 CFArrayRef policyStrings
= NULL
;
2655 policyStrings
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckEAPTrustedServerNames
);
2656 if (policyStrings
&& CFArrayContainsValue(policyStrings
,
2657 CFRangeMake(0, CFArrayGetCount(policyStrings
)),
2665 CFReleaseNull(tmpStringValue
);
2670 static uint32_t ts_key_usage_for_kuNumber(CFNumberRef keyUsageNumber
) {
2671 uint32_t ourTSKeyUsage
= 0;
2672 uint32_t keyUsage
= 0;
2673 if (keyUsageNumber
&&
2674 CFNumberGetValue(keyUsageNumber
, kCFNumberSInt32Type
, &keyUsage
)) {
2675 if (keyUsage
& kSecKeyUsageDigitalSignature
) {
2676 ourTSKeyUsage
|= kSecTrustSettingsKeyUseSignature
;
2678 if (keyUsage
& kSecKeyUsageDataEncipherment
) {
2679 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptData
;
2681 if (keyUsage
& kSecKeyUsageKeyEncipherment
) {
2682 ourTSKeyUsage
|= kSecTrustSettingsKeyUseEnDecryptKey
;
2684 if (keyUsage
& kSecKeyUsageKeyAgreement
) {
2685 ourTSKeyUsage
|= kSecTrustSettingsKeyUseKeyExchange
;
2687 if (keyUsage
== kSecKeyUsageAll
) {
2688 ourTSKeyUsage
= kSecTrustSettingsKeyUseAny
;
2691 return ourTSKeyUsage
;
2694 static uint32_t ts_key_usage_for_policy(SecPolicyRef policy
) {
2695 uint32_t ourTSKeyUsage
= 0;
2696 CFTypeRef policyKeyUsageType
= NULL
;
2698 policyKeyUsageType
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckKeyUsage
);
2699 if (isArray(policyKeyUsageType
)) {
2700 CFIndex ix
, count
= CFArrayGetCount(policyKeyUsageType
);
2701 for (ix
= 0; ix
< count
; ix
++) {
2702 CFNumberRef policyKeyUsageNumber
= NULL
;
2703 policyKeyUsageNumber
= (CFNumberRef
)CFArrayGetValueAtIndex(policyKeyUsageType
, ix
);
2704 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageNumber
);
2706 } else if (isNumber(policyKeyUsageType
)) {
2707 ourTSKeyUsage
|= ts_key_usage_for_kuNumber(policyKeyUsageType
);
2710 return ourTSKeyUsage
;
2713 static bool SecPVCContainsTrustSettingsKeyUsage(SecPVCRef pvc
,
2714 SecCertificateRef certificate
, CFIndex policyIX
, CFNumberRef keyUsageNumber
) {
2715 int64_t keyUsageValue
= 0;
2716 uint32_t ourKeyUsage
= 0;
2718 if (!isNumber(keyUsageNumber
) || !CFNumberGetValue(keyUsageNumber
, kCFNumberSInt64Type
, &keyUsageValue
)) {
2722 if (keyUsageValue
== kSecTrustSettingsKeyUseAny
) {
2726 /* We're using the key for revocation if we have the OCSPSigner policy.
2727 * @@@ If we support CRLs, we'd need to check for that policy here too.
2729 if (SecPVCContainsPolicy(pvc
, kSecPolicyAppleOCSPSigner
, NULL
, NULL
)) {
2730 ourKeyUsage
|= kSecTrustSettingsKeyUseSignRevocation
;
2733 /* We're using the key for verifying a cert if it's a root/intermediate
2734 * in the chain. If the cert isn't in the path yet, we're about to add it,
2735 * so it's a root/intermediate. If there is no path, this is the leaf.
2737 CFIndex pathIndex
= -1;
2738 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2740 pathIndex
= SecCertificatePathVCGetIndexOfCertificate(path
, certificate
);
2744 if (pathIndex
!= 0) {
2745 ourKeyUsage
|= kSecTrustSettingsKeyUseSignCert
;
2748 /* The rest of the key usages may be specified by the policy(ies). */
2749 if (policyIX
>= 0 && policyIX
< CFArrayGetCount(pvc
->policies
)) {
2750 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, policyIX
);
2751 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
2753 /* Get key usage from ALL policies */
2754 CFIndex ix
, count
= CFArrayGetCount(pvc
->policies
);
2755 for (ix
= 0; ix
< count
; ix
++) {
2756 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, ix
);
2757 ourKeyUsage
|= ts_key_usage_for_policy(policy
);
2761 if (ourKeyUsage
== (uint32_t)(keyUsageValue
& 0x00ffffffff)) {
2769 #include <Security/SecTrustedApplicationPriv.h>
2770 #include <Security/SecTask.h>
2771 #include <Security/SecTaskPriv.h>
2772 #include <bsm/libbsm.h>
2773 #include <libproc.h>
2775 extern OSStatus
SecTaskValidateForRequirement(SecTaskRef task
, CFStringRef requirement
);
2777 static bool SecPVCCallerIsApplication(CFDataRef clientAuditToken
, CFTypeRef appRef
) {
2778 bool result
= false;
2779 audit_token_t auditToken
= {};
2780 SecTaskRef task
= NULL
;
2781 SecRequirementRef requirement
= NULL
;
2782 CFStringRef stringRequirement
= NULL
;
2784 require(appRef
&& clientAuditToken
, out
);
2785 require(CFGetTypeID(appRef
) == SecTrustedApplicationGetTypeID(), out
);
2786 require_noerr(SecTrustedApplicationCopyRequirement((SecTrustedApplicationRef
)appRef
, &requirement
), out
);
2787 require(requirement
, out
);
2788 require_noerr(SecRequirementsCopyString(requirement
, kSecCSDefaultFlags
, &stringRequirement
), out
);
2789 require(stringRequirement
, out
);
2791 require(sizeof(auditToken
) == CFDataGetLength(clientAuditToken
), out
);
2792 CFDataGetBytes(clientAuditToken
, CFRangeMake(0, sizeof(auditToken
)), (uint8_t *)&auditToken
);
2793 require(task
= SecTaskCreateWithAuditToken(NULL
, auditToken
), out
);
2795 if(errSecSuccess
== SecTaskValidateForRequirement(task
, stringRequirement
)) {
2800 CFReleaseNull(task
);
2801 CFReleaseNull(requirement
);
2802 CFReleaseNull(stringRequirement
);
2807 static bool SecPVCContainsTrustSettingsPolicyOption(SecPVCRef pvc
, CFDictionaryRef options
) {
2808 if (!isDictionary(options
)) {
2813 CFDictionaryRef currentCallbacks
= pvc
->callbacks
;
2815 /* We need to run the leaf and path checks using these options. */
2816 pvc
->callbacks
= gSecPolicyLeafCallbacks
;
2817 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
2819 pvc
->callbacks
= gSecPolicyPathCallbacks
;
2820 CFDictionaryApplyFunction(options
, SecPVCValidateKey
, pvc
);
2823 pvc
->callbacks
= currentCallbacks
;
2825 /* Our work here is done; no need to claim a match */
2829 static bool SecPVCMeetsConstraint(SecPVCRef pvc
, SecCertificateRef certificate
, CFDictionaryRef constraint
) {
2830 CFStringRef policyOid
= NULL
, policyString
= NULL
, policyName
= NULL
;
2831 CFNumberRef keyUsageNumber
= NULL
;
2832 CFTypeRef trustedApplicationData
= NULL
;
2833 CFDictionaryRef policyOptions
= NULL
;
2835 bool policyMatch
= false, policyStringMatch
= false, applicationMatch
= false ,
2836 keyUsageMatch
= false, policyOptionMatch
= false;
2837 bool result
= false;
2839 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
2840 /* OS X returns a SecPolicyRef in the constraints. Convert to the oid string. */
2841 SecPolicyRef policy
= NULL
;
2842 policy
= (SecPolicyRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
2843 policyOid
= (policy
) ? policy
->_oid
: NULL
;
2845 policyOid
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicy
);
2847 policyName
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyName
);
2848 policyString
= (CFStringRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyString
);
2849 keyUsageNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsKeyUsage
);
2850 policyOptions
= (CFDictionaryRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsPolicyOptions
);
2852 CFIndex policyIX
= -1;
2853 policyMatch
= SecPVCContainsPolicy(pvc
, policyOid
, policyName
, &policyIX
);
2854 policyStringMatch
= SecPVCContainsString(pvc
, policyIX
, policyString
);
2855 keyUsageMatch
= SecPVCContainsTrustSettingsKeyUsage(pvc
, certificate
, policyIX
, keyUsageNumber
);
2856 policyOptionMatch
= SecPVCContainsTrustSettingsPolicyOption(pvc
, policyOptions
);
2858 #if TARGET_OS_MAC && !TARGET_OS_IPHONE
2859 trustedApplicationData
= CFDictionaryGetValue(constraint
, kSecTrustSettingsApplication
);
2860 CFDataRef clientAuditToken
= SecPathBuilderCopyClientAuditToken(pvc
->builder
);
2861 applicationMatch
= SecPVCCallerIsApplication(clientAuditToken
, trustedApplicationData
);
2862 CFReleaseNull(clientAuditToken
);
2864 if(CFDictionaryContainsKey(constraint
, kSecTrustSettingsApplication
)) {
2865 secerror("kSecTrustSettingsApplication is not yet supported on this platform");
2869 /* If we either didn't find the parameter in the dictionary or we got a match
2870 * against that parameter, for all possible parameters in the dictionary, then
2871 * this trust setting result applies to the output. */
2872 if (((!policyOid
&& !policyName
) || policyMatch
) &&
2873 (!policyString
|| policyStringMatch
) &&
2874 (!trustedApplicationData
|| applicationMatch
) &&
2875 (!keyUsageNumber
|| keyUsageMatch
) &&
2876 (!policyOptions
|| policyOptionMatch
)) {
2883 SecTrustSettingsResult
SecPVCGetTrustSettingsResult(SecPVCRef pvc
, SecCertificateRef certificate
, CFArrayRef constraints
) {
2884 SecTrustSettingsResult result
= kSecTrustSettingsResultInvalid
;
2885 CFIndex constraintIX
, constraintCount
= CFArrayGetCount(constraints
);
2886 for (constraintIX
= 0; constraintIX
< constraintCount
; constraintIX
++) {
2887 CFDictionaryRef constraint
= (CFDictionaryRef
)CFArrayGetValueAtIndex(constraints
, constraintIX
);
2888 if (!isDictionary(constraint
)) {
2892 CFNumberRef resultNumber
= NULL
;
2893 resultNumber
= (CFNumberRef
)CFDictionaryGetValue(constraint
, kSecTrustSettingsResult
);
2894 uint32_t resultValue
= kSecTrustSettingsResultInvalid
;
2895 if (!isNumber(resultNumber
) || !CFNumberGetValue(resultNumber
, kCFNumberSInt32Type
, &resultValue
)) {
2896 /* no SecTrustSettingsResult entry defaults to TrustRoot*/
2897 resultValue
= kSecTrustSettingsResultTrustRoot
;
2900 if (SecPVCMeetsConstraint(pvc
, certificate
, constraint
)) {
2901 result
= resultValue
;
2908 static void SecPVCCheckUsageConstraints(SecPVCRef pvc
) {
2909 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2910 for (certIX
= 0; certIX
< certCount
; certIX
++) {
2911 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
2912 CFArrayRef constraints
= SecCertificatePathVCGetUsageConstraintsAtIndex(path
, certIX
);
2913 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2914 SecTrustSettingsResult result
= SecPVCGetTrustSettingsResult(pvc
, cert
, constraints
);
2916 /* Set the pvc trust result based on the usage constraints and anchor source. */
2917 if (result
== kSecTrustSettingsResultDeny
) {
2918 SecPVCSetResultForced(pvc
, kSecPolicyCheckUsageConstraints
, certIX
, kCFBooleanFalse
, true);
2919 } else if ((result
== kSecTrustSettingsResultTrustRoot
|| result
== kSecTrustSettingsResultTrustAsRoot
||
2920 result
== kSecTrustSettingsResultInvalid
) && SecPVCIsOkResult(pvc
)) {
2921 /* If we already think the PVC is ok and this cert is from one of the user/
2922 * admin anchor sources, trustRoot, trustAsRoot, and Invalid (no constraints),
2923 * all mean we should use the special "Proceed" trust result. */
2924 #if TARGET_OS_IPHONE
2925 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecUserAnchorSource
) &&
2926 SecCertificateSourceContains(kSecUserAnchorSource
, cert
)) {
2928 if (SecPathBuilderIsAnchorSource(pvc
->builder
, kSecLegacyAnchorSource
) &&
2929 SecCertificateSourceContains(kSecLegacyAnchorSource
, cert
)) {
2931 pvc
->result
= kSecTrustResultProceed
;
2937 static const UInt8 kTestDateConstraintsRoot
[kSecPolicySHA256Size
] = {
2938 0x51,0xA0,0xF3,0x1F,0xC0,0x1D,0xEC,0x87,0x32,0xB6,0xFD,0x13,0x6A,0x43,0x4D,0x6C,
2939 0x87,0xCD,0x62,0xE0,0x38,0xB4,0xFB,0xD6,0x40,0xB0,0xFD,0x62,0x4D,0x1F,0xCF,0x6D
2941 static const UInt8 kWS_CA1_G2
[kSecPolicySHA256Size
] = {
2942 0xD4,0x87,0xA5,0x6F,0x83,0xB0,0x74,0x82,0xE8,0x5E,0x96,0x33,0x94,0xC1,0xEC,0xC2,
2943 0xC9,0xE5,0x1D,0x09,0x03,0xEE,0x94,0x6B,0x02,0xC3,0x01,0x58,0x1E,0xD9,0x9E,0x16
2945 static const UInt8 kWS_CA1_NEW
[kSecPolicySHA256Size
] = {
2946 0x4B,0x22,0xD5,0xA6,0xAE,0xC9,0x9F,0x3C,0xDB,0x79,0xAA,0x5E,0xC0,0x68,0x38,0x47,
2947 0x9C,0xD5,0xEC,0xBA,0x71,0x64,0xF7,0xF2,0x2D,0xC1,0xD6,0x5F,0x63,0xD8,0x57,0x08
2949 static const UInt8 kWS_CA2_NEW
[kSecPolicySHA256Size
] = {
2950 0xD6,0xF0,0x34,0xBD,0x94,0xAA,0x23,0x3F,0x02,0x97,0xEC,0xA4,0x24,0x5B,0x28,0x39,
2951 0x73,0xE4,0x47,0xAA,0x59,0x0F,0x31,0x0C,0x77,0xF4,0x8F,0xDF,0x83,0x11,0x22,0x54
2953 static const UInt8 kWS_ECC
[kSecPolicySHA256Size
] = {
2954 0x8B,0x45,0xDA,0x1C,0x06,0xF7,0x91,0xEB,0x0C,0xAB,0xF2,0x6B,0xE5,0x88,0xF5,0xFB,
2955 0x23,0x16,0x5C,0x2E,0x61,0x4B,0xF8,0x85,0x56,0x2D,0x0D,0xCE,0x50,0xB2,0x9B,0x02
2957 static const UInt8 kSC_SFSCA
[kSecPolicySHA256Size
] = {
2958 0xC7,0x66,0xA9,0xBE,0xF2,0xD4,0x07,0x1C,0x86,0x3A,0x31,0xAA,0x49,0x20,0xE8,0x13,
2959 0xB2,0xD1,0x98,0x60,0x8C,0xB7,0xB7,0xCF,0xE2,0x11,0x43,0xB8,0x36,0xDF,0x09,0xEA
2961 static const UInt8 kSC_SHA2
[kSecPolicySHA256Size
] = {
2962 0xE1,0x78,0x90,0xEE,0x09,0xA3,0xFB,0xF4,0xF4,0x8B,0x9C,0x41,0x4A,0x17,0xD6,0x37,
2963 0xB7,0xA5,0x06,0x47,0xE9,0xBC,0x75,0x23,0x22,0x72,0x7F,0xCC,0x17,0x42,0xA9,0x11
2965 static const UInt8 kSC_G2
[kSecPolicySHA256Size
] = {
2966 0xC7,0xBA,0x65,0x67,0xDE,0x93,0xA7,0x98,0xAE,0x1F,0xAA,0x79,0x1E,0x71,0x2D,0x37,
2967 0x8F,0xAE,0x1F,0x93,0xC4,0x39,0x7F,0xEA,0x44,0x1B,0xB7,0xCB,0xE6,0xFD,0x59,0x95
2970 static void SecPVCCheckIssuerDateConstraints(SecPVCRef pvc
) {
2971 static CFSetRef sConstrainedRoots
= NULL
;
2972 static dispatch_once_t _t
;
2973 dispatch_once(&_t
, ^{
2974 const UInt8
*v_hashes
[] = {
2975 kWS_CA1_G2
, kWS_CA1_NEW
, kWS_CA2_NEW
, kWS_ECC
,
2976 kSC_SFSCA
, kSC_SHA2
, kSC_G2
, kTestDateConstraintsRoot
2978 CFMutableSetRef set
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
2979 CFIndex ix
, count
= sizeof(v_hashes
)/sizeof(*v_hashes
);
2980 for (ix
=0; ix
<count
; ix
++) {
2981 CFDataRef hash
= CFDataCreateWithBytesNoCopy(NULL
, v_hashes
[ix
],
2982 kSecPolicySHA256Size
, kCFAllocatorNull
);
2984 CFSetAddValue(set
, hash
);
2988 sConstrainedRoots
= set
;
2991 bool shouldDeny
= false;
2992 CFIndex certIX
, certCount
= SecPVCGetCertificateCount(pvc
);
2993 for (certIX
= certCount
- 1; certIX
>= 0 && !shouldDeny
; certIX
--) {
2994 SecCertificateRef cert
= SecPVCGetCertificateAtIndex(pvc
, certIX
);
2995 CFDataRef sha256
= SecCertificateCopySHA256Digest(cert
);
2996 if (sha256
&& CFSetContainsValue(sConstrainedRoots
, sha256
)) {
2997 /* matched a constrained root; check notBefore dates on all its children. */
2998 CFIndex childIX
= certIX
;
2999 while (--childIX
>= 0) {
3000 SecCertificateRef child
= SecPVCGetCertificateAtIndex(pvc
, childIX
);
3001 /* 1 Dec 2016 00:00:00 GMT */
3002 if (child
&& (CFAbsoluteTime
)502243200.0 <= SecCertificateNotValidBefore(child
)) {
3003 SecPVCSetResultForced(pvc
, kSecPolicyCheckBlackListedKey
, certIX
, kCFBooleanFalse
, true);
3009 CFReleaseNull(sha256
);
3013 /* AUDIT[securityd](done):
3014 policy->_options is a caller provided dictionary, only its cf type has
3017 void SecPVCPathChecks(SecPVCRef pvc
) {
3018 secdebug("policy", "begin path: %@", SecPathBuilderGetPath(pvc
->builder
));
3019 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3020 /* This needs to be initialized before we call any function that might call
3021 SecPVCSetResultForced(). */
3023 SecPolicyCheckIdLinkage(pvc
, kSecPolicyCheckIdLinkage
);
3024 if (SecPVCIsOkResult(pvc
) || pvc
->details
) {
3025 SecPolicyCheckBasicCertificateProcessing(pvc
,
3026 kSecPolicyCheckBasicCertificateProcessing
);
3029 CFArrayRef policies
= pvc
->policies
;
3030 CFIndex count
= CFArrayGetCount(policies
);
3031 for (; pvc
->policyIX
< count
; ++pvc
->policyIX
) {
3032 /* Validate all keys for all policies. */
3033 pvc
->callbacks
= gSecPolicyPathCallbacks
;
3034 SecPolicyRef policy
= SecPVCGetPolicy(pvc
);
3035 CFDictionaryApplyFunction(policy
->_options
, SecPVCValidateKey
, pvc
);
3036 if (!SecPVCIsOkResult(pvc
) && !pvc
->details
)
3043 /* Check whether the TrustSettings say to deny a cert in the path. */
3044 SecPVCCheckUsageConstraints(pvc
);
3046 /* Check for Blocklisted certs */
3047 SecPVCCheckIssuerDateConstraints(pvc
);
3049 count
= SecCertificatePathVCGetCount(path
);
3050 for (ix
= 1; ix
< count
; ix
++) {
3051 SecPVCGrayListedKeyChecks(pvc
, ix
);
3052 SecPVCBlackListedKeyChecks(pvc
, ix
);
3055 /* Path-based check tests. */
3056 if (!SecCertificatePathVCIsPathValidated(path
)) {
3057 bool ev_check_ok
= false;
3058 if (SecCertificatePathVCIsOptionallyEV(path
)) {
3059 SecTrustResultType pre_ev_check_result
= pvc
->result
;
3060 SecPolicyCheckEV(pvc
, kSecPolicyCheckExtendedValidation
);
3061 ev_check_ok
= SecPVCIsOkResult(pvc
);
3062 /* If ev checking failed, we still want to accept this chain
3063 as a non EV one, if it was valid as such. */
3064 pvc
->result
= pre_ev_check_result
;
3068 /* This call will set the value of pvc->is_ct, but won't change the result (pvc->result) */
3069 SecPolicyCheckCT(pvc
);
3071 /* Certs are only EV if they are also CT verified */
3072 if (ev_check_ok
&& SecCertificatePathVCIsCT(path
)) {
3073 SecCertificatePathVCSetIsEV(path
, true);
3077 /* Check that this path meets CT constraints. */
3078 if (!SecCertificatePathVCIsCT(path
)) {
3079 SecPathCTPolicy ctp
= SecCertificatePathVCRequiresCT(path
);
3080 if (ctp
> kSecPathCTNotRequired
) {
3081 /* CT was required. Error is always set on leaf certificate. */
3082 SecPVCSetResultForced(pvc
, kSecPolicyCheckCTRequired
,
3083 0, kCFBooleanFalse
, true);
3084 if (ctp
!= kSecPathCTRequiredOverridable
) {
3085 /* Normally kSecPolicyCheckCTRequired is recoverable,
3086 so need to manually change trust result here. */
3087 pvc
->result
= kSecTrustResultFatalTrustFailure
;
3092 secdebug("policy", "end %strusted path: %@",
3093 (SecPVCIsOkResult(pvc
) ? "" : "not "), SecPathBuilderGetPath(pvc
->builder
));
3095 SecCertificatePathVCSetPathValidated(SecPathBuilderGetPath(pvc
->builder
));
3099 void SecPVCPathCheckRevocationResponsesReceived(SecPVCRef pvc
) {
3101 /* Since we don't currently allow networking on watchOS,
3102 * don't enforce the revocation-required check here. (32728029) */
3103 bool required
= false;
3105 bool required
= true;
3107 SecCertificatePathVCRef path
= SecPathBuilderGetPath(pvc
->builder
);
3108 CFIndex ix
, certCount
= SecCertificatePathVCGetCount(path
);
3109 for (ix
= 0; ix
< certCount
; ix
++) {
3110 SecRVCRef rvc
= SecCertificatePathVCGetRVCAtIndex(path
, ix
);
3111 /* Do we have a valid revocation response? */
3112 if (SecRVCGetEarliestNextUpdate(rvc
) == NULL_TIME
) {
3113 /* No valid revocation response.
3114 * Do we require revocation (for that cert per the
3115 * SecCertificateVCRef, or per the pvc)? */
3116 if (required
&& (SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(path
, ix
) ||
3117 ((ix
== 0) && pvc
->require_revocation_response
))) {
3118 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
3119 ix
, kCFBooleanFalse
, true);
3121 /* Do we have a definitive Valid revocation result for this cert? */
3122 if (SecRVCHasDefinitiveValidInfo(rvc
) && SecRVCHasRevokedValidInfo(rvc
)) {
3123 SecRVCSetRevokedResult(rvc
);