2 * Copyright (c) 2017-2020 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * SecCertificateServer.c - SecCertificate and SecCertificatePathVC types
26 * with additonal validation context.
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <AssertMacros.h>
32 #include <libDER/libDER.h>
33 #include <libDER/oids.h>
35 #include <Security/SecCertificate.h>
36 #include <Security/SecCertificatePriv.h>
37 #include <Security/SecCertificateInternal.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecInternal.h>
40 #include <Security/SecTrustSettingsPriv.h>
42 #include <utilities/SecIOFormat.h>
43 #include <utilities/SecCFError.h>
44 #include <utilities/SecCFWrappers.h>
45 #include <utilities/debugging.h>
47 #include "trust/trustd/policytree.h"
48 #include "trust/trustd/SecPolicyServer.h"
49 #include "trust/trustd/SecCertificateServer.h"
50 #include "trust/trustd/SecRevocationServer.h"
51 #include "trust/trustd/SecTrustStoreServer.h"
54 // MARK: SecCertificateVC
55 /********************************************************
56 ************* SecCertificateVC object ***************
57 ********************************************************/
59 struct SecCertificateVC
{
61 SecCertificateRef certificate
;
62 CFArrayRef usageConstraints
;
63 CFNumberRef revocationReason
;
66 bool require_revocation_response
;
68 CFGiblisWithHashFor(SecCertificateVC
)
70 static void SecCertificateVCDestroy(CFTypeRef cf
) {
71 SecCertificateVCRef cvc
= (SecCertificateVCRef
) cf
;
72 CFReleaseNull(cvc
->certificate
);
73 CFReleaseNull(cvc
->usageConstraints
);
74 CFReleaseNull(cvc
->revocationReason
);
77 static Boolean
SecCertificateVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
78 SecCertificateVCRef cv1
= (SecCertificateVCRef
) cf1
;
79 SecCertificateVCRef cv2
= (SecCertificateVCRef
) cf2
;
80 if (!CFEqual(cv1
->certificate
, cv2
->certificate
)) {
83 /* CertificateVCs are the same if either does not have usage constraints. */
84 if (cv1
->usageConstraints
&& cv2
->usageConstraints
&&
85 !CFEqual(cv1
->usageConstraints
, cv2
->usageConstraints
)) {
92 static CFHashCode
SecCertificateVCHash(CFTypeRef cf
) {
93 SecCertificateVCRef cvc
= (SecCertificateVCRef
) cf
;
94 CFHashCode hashCode
= 0;
95 hashCode
+= CFHash(cvc
->certificate
);
96 if (cvc
->usageConstraints
) {
97 hashCode
+= CFHash(cvc
->usageConstraints
);
102 static CFStringRef
SecCertificateVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
103 SecCertificateVCRef cvc
= (SecCertificateVCRef
)cf
;
104 return CFCopyDescription(cvc
->certificate
);
107 static bool SecCertificateVCCouldBeEV(SecCertificateRef certificate
) {
108 CFMutableDictionaryRef keySizes
= NULL
;
109 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
112 /* 3. Subscriber Certificate. */
114 /* (a) certificate Policies */
115 const SecCECertificatePolicies
*cp
;
116 cp
= SecCertificateGetCertificatePolicies(certificate
);
117 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
118 /* Now find at least one policy in here that has a qualifierID of id-qt 2
119 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
121 bool found_ev_anchor_for_leaf_policy
= false;
122 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
123 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
124 found_ev_anchor_for_leaf_policy
= true;
127 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
129 /* (b) cRLDistributionPoint
130 (c) authorityInformationAccess
131 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
134 /* (d) basicConstraints
135 If present, the cA field MUST be set false. */
136 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
138 require_action_quiet(bc
->isCA
== false, notEV
,
139 secnotice("ev", "Leaf has invalid basic constraints"));
143 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
145 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
,
146 secnotice("ev", "Leaf has invalid key usage %u", ku
));
150 /* The EV Cert Spec errata specifies this, though this is a check for SSL
151 not specifically EV. */
155 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
156 SecCertificateCopyExtendedKeyUsage(certificate
);
159 /* 6.1.5 Key Sizes */
160 CFAbsoluteTime jan2014
= 410227200;
161 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
162 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
163 &kCFTypeDictionaryValueCallBacks
), notEV
);
164 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
165 if (SecCertificateNotValidBefore(certificate
) < jan2014
) {
166 /* At least RSA 1024 or ECC NIST P-256. */
167 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
168 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
169 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
170 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
172 /* At least RSA 2028 or ECC NIST P-256. */
173 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
174 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
175 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
176 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
179 /* 6.3.2 Validity Periods */
180 // Will be checked by the policy server (see SecPolicyCheckValidityPeriodMaximums)
182 /* 7.1.3 Algorithm Object Identifiers */
183 CFAbsoluteTime jan2016
= 473299200;
184 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
186 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
187 notEV
, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
193 CFReleaseNull(rsaSize
);
194 CFReleaseNull(ecSize
);
195 CFReleaseNull(keySizes
);
200 SecCertificateVCRef
SecCertificateVCCreate(SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
201 if (!certificate
) { return NULL
; }
202 CFIndex size
= sizeof(struct SecCertificateVC
);
203 SecCertificateVCRef result
=
204 (SecCertificateVCRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
,
205 SecCertificateVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
208 result
->certificate
= CFRetainSafe(certificate
);
209 result
->isWeakHash
= SecCertificateIsWeakHash(certificate
);
210 result
->optionallyEV
= SecCertificateVCCouldBeEV(certificate
);
212 CFArrayRef emptyArray
= NULL
;
213 if (!usageConstraints
) {
214 require_action_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
, CFReleaseNull(result
));
215 usageConstraints
= emptyArray
;
217 result
->usageConstraints
= CFRetainSafe(usageConstraints
);
219 CFReleaseNull(emptyArray
);
224 // MARK: SecCertificatePathVC
225 /********************************************************
226 ************* SecCertificatePathVC object ***************
227 ********************************************************/
228 struct SecCertificatePathVC
{
232 /* Index of next parent source to search for parents. */
233 CFIndex nextParentSource
;
235 /* Index of last certificate in chain whose signature has been verified.
236 0 means nothing has been checked. 1 means the leaf has been verified
237 against its issuer, etc. */
238 CFIndex lastVerifiedSigner
;
240 /* Index of first self issued certificate in the chain. -1 mean there is
241 none. 0 means the leaf is self signed. */
244 /* True iff cert at index selfIssued does in fact self verify. */
247 /* True if the root of this path is an anchor. Trustedness of the
248 * anchor is determined by the PVC. */
251 policy_tree_t policy_tree
;
252 uint8_t policy_tree_verification_result
;
257 bool hasStrongHashes
;
259 /* revocationCAIndex contains the index of the last CA with a SPKI-based
260 * revocation match, or -1 (kCFNotFound) if no CA match was found.
261 * A value of 0 means the path has not yet been checked for a match. */
262 CFIndex revocationCAIndex
;
267 /* This is the score of the path after determining acceptance. */
272 /* If checkedIssuers is true, then the value of unknownCAIndex contains
273 * the index of the first CA which violates known-only constraints, or
274 * -1 if all CA certificates are either known or not constrained. */
276 CFIndex unknownCAIndex
;
278 /* Enumerated value to determine whether CT is required for the leaf
279 * certificate (because a CA in the path has a require-ct constraint).
280 * If non-zero, CT is required; value indicates overridable status. */
281 SecPathCTPolicy requiresCT
;
283 /* Issuance time, as determined by earliest SCT timestamp for leaf. */
284 CFAbsoluteTime issuanceTime
;
286 SecCertificateVCRef certificates
[];
288 CFGiblisWithHashFor(SecCertificatePathVC
)
290 static void SecCertificatePathVCPrunePolicyTree(SecCertificatePathVCRef certificatePath
) {
291 if (certificatePath
->policy_tree
) {
292 policy_tree_prune(&certificatePath
->policy_tree
);
296 void SecCertificatePathVCDeleteRVCs(SecCertificatePathVCRef path
) {
298 CFIndex certIX
, certCount
= path
->rvcCount
;
299 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
300 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
308 static void SecCertificatePathVCDestroy(CFTypeRef cf
) {
309 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
311 SecCertificatePathVCDeleteRVCs(certificatePath
);
312 SecCertificatePathVCPrunePolicyTree(certificatePath
);
313 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
314 CFReleaseNull(certificatePath
->certificates
[ix
]);
318 static Boolean
SecCertificatePathVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
319 SecCertificatePathVCRef cp1
= (SecCertificatePathVCRef
) cf1
;
320 SecCertificatePathVCRef cp2
= (SecCertificatePathVCRef
) cf2
;
321 if (cp1
->count
!= cp2
->count
)
324 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
325 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
332 static CFHashCode
SecCertificatePathVCHash(CFTypeRef cf
) {
333 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
334 CFHashCode hashCode
= 0;
336 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
337 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
342 static CFStringRef
SecCertificatePathVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
343 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
344 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
345 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
346 CFStringAppendFormat(desc
, NULL
,
347 CFSTR("<%@ certs: "), typeStr
);
350 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
352 CFStringAppend(desc
, CFSTR(", "));
354 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
355 CFStringAppend(desc
, str
);
358 CFStringAppend(desc
, CFSTR(" >"));
363 /* Create a new certificate path from an old one. */
364 SecCertificatePathVCRef
SecCertificatePathVCCreate(SecCertificatePathVCRef path
,
365 SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
366 CFAllocatorRef allocator
= kCFAllocatorDefault
;
369 CFIndex selfIssued
, lastVerifiedSigner
;
372 count
= path
->count
+ 1;
373 lastVerifiedSigner
= path
->lastVerifiedSigner
;
374 selfIssued
= path
->selfIssued
;
375 isSelfSigned
= path
->isSelfSigned
;
378 lastVerifiedSigner
= 0;
380 isSelfSigned
= false;
383 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
384 count
* sizeof(SecCertificateRef
);
385 SecCertificatePathVCRef result
=
386 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
387 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
391 memset((char*)result
+ sizeof(result
->_base
), 0,
392 sizeof(*result
) - sizeof(result
->_base
));
394 result
->count
= count
;
395 result
->lastVerifiedSigner
= lastVerifiedSigner
;
396 result
->selfIssued
= selfIssued
;
397 result
->isSelfSigned
= isSelfSigned
;
399 for (ix
= 0; ix
< count
- 1; ++ix
) {
400 result
->certificates
[ix
] = path
->certificates
[ix
];
401 CFRetain(result
->certificates
[ix
]);
404 SecCertificateVCRef cvc
= SecCertificateVCCreate(certificate
, usageConstraints
);
405 result
->certificates
[count
- 1] = cvc
;
410 SecCertificatePathVCRef
SecCertificatePathVCCopyFromParent(
411 SecCertificatePathVCRef path
, CFIndex skipCount
) {
412 CFAllocatorRef allocator
= kCFAllocatorDefault
;
414 CFIndex selfIssued
, lastVerifiedSigner
;
417 /* Ensure we are at least returning a path of length 1. */
418 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
421 count
= path
->count
- skipCount
;
422 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
423 ? path
->lastVerifiedSigner
- skipCount
: 0;
424 selfIssued
= path
->selfIssued
>= skipCount
425 ? path
->selfIssued
- skipCount
: -1;
426 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
428 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
429 count
* sizeof(SecCertificateRef
);
430 SecCertificatePathVCRef result
=
431 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
432 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
436 memset((char*)result
+ sizeof(result
->_base
), 0,
437 sizeof(*result
) - sizeof(result
->_base
));
439 result
->count
= count
;
440 result
->lastVerifiedSigner
= lastVerifiedSigner
;
441 result
->selfIssued
= selfIssued
;
442 result
->isSelfSigned
= isSelfSigned
;
443 result
->isAnchored
= path
->isAnchored
;
445 for (ix
= 0; ix
< count
; ++ix
) {
446 CFIndex pathIX
= ix
+ skipCount
;
447 result
->certificates
[ix
] = path
->certificates
[pathIX
];
448 CFRetain(result
->certificates
[ix
]);
454 SecCertificatePathVCRef
SecCertificatePathVCCopyAddingLeaf(SecCertificatePathVCRef path
,
455 SecCertificateRef leaf
) {
456 CFAllocatorRef allocator
= kCFAllocatorDefault
;
458 CFIndex selfIssued
, lastVerifiedSigner
;
461 /* First make sure the new leaf is signed by path's current leaf. */
462 SecKeyRef issuerKey
= SecCertificatePathVCCopyPublicKeyAtIndex(path
, 0);
465 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
466 CFRelease(issuerKey
);
470 count
= path
->count
+ 1;
471 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
472 selfIssued
= path
->selfIssued
;
473 isSelfSigned
= path
->isSelfSigned
;
475 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
476 count
* sizeof(SecCertificateRef
);
477 SecCertificatePathVCRef result
=
478 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
479 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
483 memset((char*)result
+ sizeof(result
->_base
), 0,
484 sizeof(*result
) - sizeof(result
->_base
));
486 result
->count
= count
;
487 result
->lastVerifiedSigner
= lastVerifiedSigner
;
488 result
->selfIssued
= selfIssued
;
489 result
->isSelfSigned
= isSelfSigned
;
490 result
->isAnchored
= path
->isAnchored
;
493 for (ix
= 1; ix
< count
; ++ix
) {
494 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
495 CFRetain(result
->certificates
[ix
]);
497 SecCertificateVCRef leafVC
= SecCertificateVCCreate(leaf
, NULL
);
498 result
->certificates
[0] = leafVC
;
503 CFArrayRef
SecCertificatePathVCCopyCertificates(SecCertificatePathVCRef path
) {
504 CFMutableArrayRef outCerts
= NULL
;
505 size_t count
= path
->count
;
506 require_quiet(outCerts
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
), exit
);
507 SecCertificatePathVCForEachCertificate(path
, ^(SecCertificateRef cert
, bool * __unused stop
) {
508 CFArrayAppendValue(outCerts
, cert
);
514 CFArrayRef
SecCertificatePathVCCreateSerialized(SecCertificatePathVCRef path
) {
515 CFMutableArrayRef serializedCerts
= NULL
;
516 require_quiet(path
, exit
);
517 size_t count
= path
->count
;
518 require_quiet(serializedCerts
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
), exit
);
519 SecCertificatePathVCForEachCertificate(path
, ^(SecCertificateRef cert
, bool * __unused stop
) {
520 CFDataRef certData
= SecCertificateCopyData(cert
);
522 CFArrayAppendValue(serializedCerts
, certData
);
527 return serializedCerts
;
531 /* Record the fact that we found our own root cert as our parent
533 void SecCertificatePathVCSetSelfIssued(
534 SecCertificatePathVCRef certificatePath
) {
535 if (certificatePath
->selfIssued
>= 0) {
536 secdebug("trust", "%@ is already issued at %" PRIdCFIndex
, certificatePath
,
537 certificatePath
->selfIssued
);
540 secdebug("trust", "%@ is self issued", certificatePath
);
541 certificatePath
->selfIssued
= certificatePath
->count
- 1;
543 /* now check that the selfIssued cert was actually self-signed */
544 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
545 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->selfIssued
];
546 Boolean isSelfSigned
= false;
547 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
548 if ((status
== errSecSuccess
) && isSelfSigned
) {
549 certificatePath
->isSelfSigned
= true;
551 certificatePath
->selfIssued
= -1;
556 void SecCertificatePathVCSetIsAnchored(
557 SecCertificatePathVCRef certificatePath
) {
558 secdebug("trust", "%@ is anchored", certificatePath
);
559 certificatePath
->isAnchored
= true;
561 /* Now check if that anchor (last cert) was actually self-signed.
562 * In the non-anchor case, this is handled by SecCertificatePathVCSetSelfIssued.
563 * Because anchored chains immediately go into the candidate bucket in the trust
564 * server, we need to ensure that the self-signed/self-issued members are set
565 * for the purposes of scoring. */
566 if (!certificatePath
->isSelfSigned
&& certificatePath
->count
> 0) {
567 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->count
- 1];
568 Boolean isSelfSigned
= false;
569 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
570 if ((status
== errSecSuccess
) && isSelfSigned
) {
571 certificatePath
->isSelfSigned
= true;
572 if (certificatePath
->selfIssued
== -1) {
573 certificatePath
->selfIssued
= certificatePath
->count
- 1;
579 /* Return the index of the first non anchor certificate in the chain that is
580 self signed counting from the leaf up. Return -1 if there is none. */
581 CFIndex
SecCertificatePathVCSelfSignedIndex(
582 SecCertificatePathVCRef certificatePath
) {
583 if (certificatePath
->isSelfSigned
)
584 return certificatePath
->selfIssued
;
588 Boolean
SecCertificatePathVCIsAnchored(
589 SecCertificatePathVCRef certificatePath
) {
590 return certificatePath
->isAnchored
;
594 void SecCertificatePathVCSetNextSourceIndex(
595 SecCertificatePathVCRef certificatePath
, CFIndex sourceIndex
) {
596 certificatePath
->nextParentSource
= sourceIndex
;
599 CFIndex
SecCertificatePathVCGetNextSourceIndex(
600 SecCertificatePathVCRef certificatePath
) {
601 return certificatePath
->nextParentSource
;
604 CFIndex
SecCertificatePathVCGetCount(
605 SecCertificatePathVCRef certificatePath
) {
606 check(certificatePath
);
607 return certificatePath
? certificatePath
->count
: 0;
610 SecCertificateRef
SecCertificatePathVCGetCertificateAtIndex(
611 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
612 if (!certificatePath
|| ix
< 0 || ix
>= certificatePath
->count
) {
615 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
616 return cvc
? cvc
->certificate
: NULL
;
619 void SecCertificatePathVCForEachCertificate(SecCertificatePathVCRef path
, void(^operation
)(SecCertificateRef certificate
, bool *stop
)) {
621 CFIndex ix
, count
= path
->count
;
622 for (ix
= 0; ix
< count
; ++ix
) {
623 SecCertificateVCRef cvc
= path
->certificates
[ix
];
624 operation(cvc
->certificate
, &stop
);
629 CFIndex
SecCertificatePathVCGetIndexOfCertificate(SecCertificatePathVCRef path
,
630 SecCertificateRef certificate
) {
631 CFIndex ix
, count
= path
->count
;
632 for (ix
= 0; ix
< count
; ++ix
) {
633 SecCertificateVCRef cvc
= path
->certificates
[ix
];
634 if (CFEqual(cvc
->certificate
, certificate
))
640 /* Return the root certificate for certificatePath. Note that root is just
641 the top of the path as far as it is constructed. It may or may not be
642 trusted or self signed. */
643 SecCertificateRef
SecCertificatePathVCGetRoot(
644 SecCertificatePathVCRef certificatePath
) {
645 return SecCertificatePathVCGetCertificateAtIndex(certificatePath
,
646 SecCertificatePathVCGetCount(certificatePath
) - 1);
649 SecKeyRef
SecCertificatePathVCCopyPublicKeyAtIndex(
650 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
651 SecCertificateRef certificate
=
652 SecCertificatePathVCGetCertificateAtIndex(certificatePath
, ix
);
653 return SecCertificateCopyKey(certificate
);
656 CFArrayRef
SecCertificatePathVCGetUsageConstraintsAtIndex(
657 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
658 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
659 return cvc
->usageConstraints
;
662 void SecCertificatePathVCSetUsageConstraintsAtIndex(SecCertificatePathVCRef certificatePath
,
663 CFArrayRef newConstraints
, CFIndex ix
) {
664 CFArrayRef emptyArray
= NULL
;
665 if (!newConstraints
) {
666 require_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
);
667 newConstraints
= emptyArray
;
670 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
671 cvc
->usageConstraints
= CFRetainSafe(newConstraints
);
673 CFReleaseNull(emptyArray
);
677 SecPathVerifyStatus
SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath
) {
678 check(certificatePath
);
679 if (!certificatePath
)
680 return kSecPathVerifyFailed
;
682 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
683 ++certificatePath
->lastVerifiedSigner
) {
684 SecKeyRef issuerKey
=
685 SecCertificatePathVCCopyPublicKeyAtIndex(certificatePath
,
686 certificatePath
->lastVerifiedSigner
+ 1);
688 return kSecPathVerifiesUnknown
;
689 SecCertificateVCRef cvc
= certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
];
690 OSStatus status
= SecCertificateIsSignedBy(cvc
->certificate
,
692 CFRelease(issuerKey
);
694 return kSecPathVerifyFailed
;
698 return kSecPathVerifySuccess
;
701 /* Is the the issuer of the last cert a subject of a previous cert in the chain.See <rdar://33136765>. */
702 bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path
) {
703 bool isCircle
= false;
704 CFDataRef issuer
= SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path
));
705 if (!issuer
) { return isCircle
; }
706 CFIndex ix
= path
->count
- 2;
707 for (; ix
>= 0; ix
--) {
708 SecCertificateVCRef cvc
= path
->certificates
[ix
];
709 CFDataRef subject
= SecCertificateGetNormalizedSubjectContent(cvc
->certificate
);
710 if (subject
&& CFEqual(issuer
, subject
)) {
718 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
719 __block
bool result
= true;
720 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
721 if (!SecCertificateIsValid(certificate
, verifyTime
)) {
728 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath
) {
729 CFIndex ix
, count
= certificatePath
->count
;
731 if (certificatePath
->hasStrongHashes
) {
735 if (SecCertificatePathVCIsAnchored(certificatePath
)) {
736 /* For anchored paths, don't check the hash algorithm of the anchored cert,
737 * since we already decided to trust it. */
740 for (ix
= 0; ix
< count
; ++ix
) {
741 if (certificatePath
->certificates
[ix
]->isWeakHash
) {
745 certificatePath
->hasStrongHashes
= true;
749 bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath
) {
750 __block CFDictionaryRef keySizes
= NULL
;
751 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
752 __block
bool result
= false;
754 /* RSA key sizes are 2048-bit or larger. EC key sizes are P-224 or larger. */
755 require(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), errOut
);
756 require(ecSize
= CFNumberCreateWithCFIndex(NULL
, 224), errOut
);
757 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
758 const void *values
[] = { rsaSize
, ecSize
};
759 require(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
760 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
761 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
762 if (!SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
)) {
769 CFReleaseSafe(keySizes
);
770 CFReleaseSafe(rsaSize
);
771 CFReleaseSafe(ecSize
);
775 /* Return a score for this certificate chain. */
776 CFIndex
SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
779 /* Paths that don't verify score terribly.c */
780 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
781 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
782 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
786 if (certificatePath
->isAnchored
) {
787 /* Anchored paths for the win! */
791 if (certificatePath
->isSelfSigned
&& (certificatePath
->selfIssued
== certificatePath
->count
- 1)) {
792 /* Chains that terminate in a self-signed certificate are preferred,
793 even if they don't end in an anchor. */
795 /* Shorter chains ending in a self-signed cert are preferred. */
796 score
-= 1 * certificatePath
->count
;
798 /* Longer chains are preferred when the chain doesn't end in a self-signed cert. */
799 score
+= 1 * certificatePath
->count
;
802 if (SecCertificatePathVCIsValid(certificatePath
, verifyTime
)) {
806 if (!SecCertificatePathVCHasWeakHash(certificatePath
)) {
810 if (!SecCertificatePathVCHasWeakKeySize(certificatePath
)) {
817 CFIndex
SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath
) {
818 if (!certificatePath
) { return 0; }
819 return certificatePath
->score
;
822 void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath
, CFIndex score
) {
823 /* We may "score" the same path twice -- if we "accept" a path but then
824 * decide to keep looking for a better one, we we process the same path
825 * again in "reject" which creates a lower score. Don't replace a higher
826 * score with a lower score. Use reset below to post-reject a path. */
827 if (score
> certificatePath
->score
) {
828 certificatePath
->score
= score
;
832 void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath
) {
833 certificatePath
->score
= 0;
836 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
837 if (ix
>= certificatePath
->rvcCount
) {
840 return &((SecRVCRef
)certificatePath
->rvcs
)[ix
];
843 bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath
) {
844 return (bool)certificatePath
->rvcs
;
847 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath
, CFIndex certCount
) {
848 certificatePath
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
849 certificatePath
->rvcCount
= certCount
;
852 /* Return 0 if any certs revocation checking failed, or the earliest date on
853 which one of the used revocation validation tokens (ocsp response or
855 /* This function returns 0 to indicate revocation checking was not completed
856 for this certificate chain, otherwise returns the date at which the first
857 piece of revocation checking info we used expires. */
858 CFAbsoluteTime
SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path
) {
859 CFIndex certIX
, certCount
= path
->count
;
860 CFAbsoluteTime enu
= NULL_TIME
;
861 if (certCount
<= 1 || !path
->rvcs
) {
865 for (certIX
= 0; certIX
< path
->rvcCount
; ++certIX
) {
866 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
867 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
868 if (thisCertNextUpdate
== 0) {
870 /* We allow for CA certs to not be revocation checked if they
871 have no ocspResponders to check against, but the leaf
872 must be checked in order for us to claim we did revocation
874 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
875 CFArrayRef ocspResponders
= NULL
;
876 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
877 if (!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) {
878 /* We can't check this cert so we don't consider it a soft
879 failure that we didn't. */
883 /* Make sure to always skip roots for whom we can't check revocation */
884 if (certIX
== certCount
- 1) {
887 secdebug("rvc", "revocation checking soft failure for cert: %ld",
889 enu
= thisCertNextUpdate
;
892 if (enu
== 0 || thisCertNextUpdate
< enu
) {
893 enu
= thisCertNextUpdate
;
897 secdebug("rvc", "revocation valid until: %lg", enu
);
901 bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path
) {
902 CFIndex certIX
, certCount
= path
->count
;
903 if (certCount
<= 1 || !path
->rvcs
) {
904 /* If there is only one certificate, it's the root, so revocation checking is irrelevant. */
908 for (certIX
= 0; certIX
< path
->rvcCount
- 1; ++certIX
) {
909 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
910 if (!SecRVCRevocationChecked(rvc
)) {
911 secdebug("rvc", "revocation has not been checked for all certs (not checked for cert %ld)", certIX
);
916 secdebug("rvc", "revocation has been checked for all certs");
920 void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
921 CFIndex ix
, CFNumberRef revocationReason
) {
922 if (ix
> certificatePath
->count
- 1) { return; }
923 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
924 cvc
->revocationReason
= CFRetainSafe(revocationReason
);
927 CFNumberRef
SecCertificatePathVCGetRevocationReason(SecCertificatePathVCRef certificatePath
) {
928 for (CFIndex ix
= 0; ix
< certificatePath
->count
; ix
++) {
929 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
930 if (cvc
->revocationReason
) {
931 return cvc
->revocationReason
;
937 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
939 if (ix
> certificatePath
->count
- 1) { return false; }
940 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
941 return cvc
->require_revocation_response
;
944 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
946 if (ix
> certificatePath
->count
- 1) { return; }
947 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
948 cvc
->require_revocation_response
= true;
951 bool SecCertificatePathVCCheckedIssuers(SecCertificatePathVCRef certificatePath
) {
952 return certificatePath
->checkedIssuers
;
955 void SecCertificatePathVCSetCheckedIssuers(SecCertificatePathVCRef certificatePath
, bool checked
) {
956 certificatePath
->checkedIssuers
= checked
;
959 CFIndex
SecCertificatePathVCUnknownCAIndex(SecCertificatePathVCRef certificatePath
) {
960 return certificatePath
->unknownCAIndex
;
963 void SecCertificatePathVCSetUnknownCAIndex(SecCertificatePathVCRef certificatePath
, CFIndex index
) {
964 certificatePath
->unknownCAIndex
= index
;
967 bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath
) {
968 if (!certificatePath
) { return false; }
969 return certificatePath
->pathValidated
;
972 void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath
) {
973 certificatePath
->pathValidated
= true;
976 bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath
) {
977 if (!certificatePath
) { return false; }
978 return certificatePath
->isEV
;
981 void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath
, bool isEV
) {
982 certificatePath
->isEV
= isEV
;
985 bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath
) {
986 if (!certificatePath
) { return false; }
987 return certificatePath
->certificates
[0]->optionallyEV
;
990 bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath
) {
991 if (!certificatePath
) { return false; }
992 return certificatePath
->isCT
;
995 void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath
, bool isCT
) {
996 certificatePath
->isCT
= isCT
;
999 SecPathCTPolicy
SecCertificatePathVCRequiresCT(SecCertificatePathVCRef certificatePath
) {
1000 if (!certificatePath
) { return kSecPathCTNotRequired
; }
1001 return certificatePath
->requiresCT
;
1004 void SecCertificatePathVCSetRequiresCT(SecCertificatePathVCRef certificatePath
, SecPathCTPolicy requiresCT
) {
1005 if (certificatePath
->requiresCT
> requiresCT
) {
1006 return; /* once set, CT policy may be only be changed to a more strict value */
1008 certificatePath
->requiresCT
= requiresCT
;
1011 static bool has_ca_additions_key(SecCertificatePathVCRef path
, CFDictionaryRef ca_entry
) {
1012 bool result
= false;
1013 CFDataRef hash
= CFDictionaryGetValue(ca_entry
, kSecCARevocationSPKIHashKey
);
1017 /* only check issuing CAs and not the leaf */
1018 for (CFIndex certIX
= 1; certIX
< SecCertificatePathVCGetCount(path
); certIX
++) {
1019 SecCertificateRef ca
= SecCertificatePathVCGetCertificateAtIndex(path
, certIX
);
1020 CFDataRef spkiHash
= SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca
);
1021 bool matched
= CFEqualSafe(hash
, spkiHash
);
1022 CFReleaseNull(spkiHash
);
1026 /* this SPKI is a match; remember highest index */
1027 if (certIX
> path
->revocationCAIndex
) {
1028 path
->revocationCAIndex
= certIX
;
1035 static void SecCertificatePathVCCheckCARevocationAdditions(SecCertificatePathVCRef path
) {
1036 CFDictionaryRef additions
= _SecTrustStoreCopyCARevocationAdditions(NULL
, NULL
);
1037 path
->revocationCAIndex
= kCFNotFound
;
1042 __block
bool result
= false;
1043 CFArrayRef ca_list
= CFDictionaryGetValue(additions
, kSecCARevocationAdditionsKey
);
1045 CFArrayForEach(ca_list
, ^(const void *value
) {
1046 result
= result
|| has_ca_additions_key(path
, value
);
1051 secinfo("ocsp", "key-based CA revocation applies at index %lld",
1052 (long long)path
->revocationCAIndex
);
1055 CFReleaseNull(additions
);
1059 CFIndex
SecCertificatePathVCIndexOfCAWithRevocationAdditions(SecCertificatePathVCRef certificatePath
) {
1060 if (!certificatePath
) { return kCFNotFound
; }
1061 if (0 == certificatePath
->revocationCAIndex
) {
1062 /* we haven't checked this path yet, do it now */
1063 SecCertificatePathVCCheckCARevocationAdditions(certificatePath
);
1065 return certificatePath
->revocationCAIndex
;
1068 CFAbsoluteTime
SecCertificatePathVCIssuanceTime(SecCertificatePathVCRef certificatePath
) {
1069 if (!certificatePath
) { return 0; }
1070 return certificatePath
->issuanceTime
;
1073 void SecCertificatePathVCSetIssuanceTime(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime issuanceTime
) {
1074 certificatePath
->issuanceTime
= issuanceTime
;
1077 bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath
) {
1078 if (!certificatePath
) { return false; }
1079 return certificatePath
->is_allowlisted
;
1082 void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath
, bool isAllowlisted
) {
1083 certificatePath
->is_allowlisted
= isAllowlisted
;
1086 /* MARK: policy_tree path verification */
1087 struct policy_tree_add_ctx
{
1089 policy_qualifier_t p_q
;
1092 /* For each node of depth i-1 in the valid_policy_tree where P-OID is in the expected_policy_set, create a child node as follows: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1093 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1094 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1095 policy_set_t policy_set
;
1096 for (policy_set
= node
->expected_policy_set
;
1098 policy_set
= policy_set
->oid_next
) {
1099 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1100 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1107 /* If the valid_policy_tree includes a node of depth i-1 with the valid_policy anyPolicy, generate a child node with the following values: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1108 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1109 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1110 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1111 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1117 /* Return true iff node has a child with a valid_policy equal to oid. */
1118 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1120 policy_tree_t child
;
1121 for (child
= node
->children
; child
; child
= child
->siblings
) {
1122 if (oid_equal(child
->valid_policy
, (*oid
))) {
1129 /* For each node in the valid_policy_tree of depth i-1, for each value in the expected_policy_set (including anyPolicy) that does not appear in a child node, create a child node with the following values: set the valid_policy to the value from the expected_policy_set in the parent node, set the qualifier_set to AP-Q, and set the expected_policy_set to the value in the valid_policy from this node. */
1130 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1131 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1132 policy_set_t policy_set
;
1133 bool added_node
= false;
1134 for (policy_set
= node
->expected_policy_set
;
1136 policy_set
= policy_set
->oid_next
) {
1137 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1138 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1145 /* For each node where ID-P is the valid_policy, set expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1146 static bool policy_tree_map_if_match(policy_tree_t node
, void *ctx
) {
1147 /* Can't map oidAnyPolicy. */
1148 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1151 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1152 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1153 policy_set_t policy_set
= NULL
;
1154 /* Generate the policy_set of sdps for matching idp */
1155 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1156 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1157 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1158 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1159 p_node
->oid
= mapping
->subjectDomainPolicy
;
1160 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1161 policy_set
= p_node
;
1165 policy_tree_set_expected_policy(node
, policy_set
);
1171 /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1 that has a valid_policy of anyPolicy as follows:
1172 (i) set the valid_policy to ID-P;
1173 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1174 (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1175 static bool policy_tree_map_if_any(policy_tree_t node
, void *ctx
) {
1176 if (!oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1180 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1181 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1182 CFMutableDictionaryRef mappings
= NULL
;
1183 CFDataRef idp
= NULL
;
1184 CFDataRef sdp
= NULL
;
1185 require_quiet(mappings
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1186 &kCFTypeDictionaryValueCallBacks
),
1188 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1189 for (mapping_ix
= 0; mapping_ix
< mapping_count
; mapping_ix
++) {
1190 oid_t issuerDomainPolicy
= pm
->mappings
[mapping_ix
].issuerDomainPolicy
;
1191 oid_t subjectDomainPolicy
= pm
->mappings
[mapping_ix
].subjectDomainPolicy
;
1192 idp
= CFDataCreateWithBytesNoCopy(NULL
, issuerDomainPolicy
.data
, issuerDomainPolicy
.length
, kCFAllocatorNull
);
1193 sdp
= CFDataCreateWithBytesNoCopy(NULL
, subjectDomainPolicy
.data
, subjectDomainPolicy
.length
, kCFAllocatorNull
);
1194 CFMutableArrayRef sdps
= (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
1196 CFArrayAppendValue(sdps
, sdp
);
1198 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1199 &kCFTypeArrayCallBacks
), errOut
);
1200 CFArrayAppendValue(sdps
, sdp
);
1201 CFDictionarySetValue(mappings
, idp
, sdps
);
1208 /* Now we use the dictionary to generate the new nodes */
1209 CFDictionaryForEach(mappings
, ^(const void *key
, const void *value
) {
1210 CFDataRef idp
= key
;
1211 CFArrayRef sdps
= value
;
1213 /* (i) set the valid_policy to ID-P; */
1215 p_oid
.data
= (uint8_t *)CFDataGetBytePtr(idp
);
1216 p_oid
.length
= CFDataGetLength(idp
);
1218 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1219 policy_qualifier_t p_q
= node
->qualifier_set
;
1221 /* (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1222 __block policy_set_t p_expected
= NULL
;
1223 CFArrayForEach(sdps
, ^(const void *value
) {
1224 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*p_expected
));
1225 p_node
->oid
.data
= (void *)CFDataGetBytePtr(value
);
1226 p_node
->oid
.length
= CFDataGetLength(value
);
1227 p_node
->oid_next
= p_expected
? p_expected
: NULL
;
1228 p_expected
= p_node
;
1231 policy_tree_add_sibling(node
, &p_oid
, p_q
, p_expected
);
1233 CFReleaseNull(mappings
);
1237 CFReleaseNull(mappings
);
1243 static bool policy_tree_map_delete_if_match(policy_tree_t node
, void *ctx
) {
1244 /* Can't map oidAnyPolicy. */
1245 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1248 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1249 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1250 /* If this node matches any of the idps, delete it. */
1251 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1252 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1253 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1254 policy_tree_remove_node(&node
);
1261 bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path
, CFIndex ix
) {
1262 /* The SecCertificatePath only tells us the last self-issued cert.
1263 * The chain may have more than one self-issued cert, so we need to
1264 * do the comparison. */
1265 bool result
= false;
1266 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, ix
);
1267 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
1268 CFDataRef subject
= SecCertificateCopyNormalizedSubjectSequence(cert
);
1269 if (issuer
&& subject
&& CFEqual(issuer
, subject
)) {
1272 CFReleaseNull(issuer
);
1273 CFReleaseNull(subject
);
1278 kSecPolicyTreeVerificationUnknown
= 0,
1279 kSecPolicyTreeVerificationFalse
,
1280 kSecPolicyTreeVerificationTrue
,
1283 /* RFC 5280 policy tree processing */
1284 bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path
, bool anchor_trusted
) {
1285 if (!path
) { return false; }
1286 if (path
->policy_tree_verification_result
!= kSecPolicyTreeVerificationUnknown
) {
1287 return (path
->policy_tree_verification_result
== kSecPolicyTreeVerificationTrue
);
1290 /* Path Validation initialization */
1291 bool result
= false;
1292 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationFalse
;
1293 bool initial_policy_mapping_inhibit
= false;
1294 bool initial_explicit_policy
= false;
1295 bool initial_any_policy_inhibit
= false;
1297 SecCertificatePathVCPrunePolicyTree(path
);
1298 path
->policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1300 assert((unsigned long)path
->count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1301 uint32_t n
= (uint32_t)path
->count
;
1302 if (anchor_trusted
) {
1306 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1307 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1308 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1310 SecCertificateRef cert
= NULL
;
1312 for (i
= 1; i
<= n
; ++i
) {
1314 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, n
- i
);
1315 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(path
, n
- i
);
1318 if (path
->policy_tree
) {
1319 const SecCECertificatePolicies
*cp
=
1320 SecCertificateGetCertificatePolicies(cert
);
1321 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1322 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1323 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1324 oid_t p_oid
= policy
->policyIdentifier
;
1325 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1326 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1327 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1328 if (!policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1329 policy_tree_add_if_match
, &ctx
)) {
1330 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1331 policy_tree_add_if_any
, &ctx
);
1335 /* The certificate policies extension includes the policy
1336 anyPolicy with the qualifier set AP-Q and either
1337 (a) inhibit_anyPolicy is greater than 0 or
1338 (b) i < n and the certificate is self-issued. */
1339 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1340 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1341 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1342 oid_t p_oid
= policy
->policyIdentifier
;
1343 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1344 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1345 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1346 policy_tree_add_expected
, (void *)p_q
);
1351 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1354 SecCertificatePathVCPrunePolicyTree(path
);
1358 /* (f) Verify that either explicit_policy is greater than 0 or the
1359 valid_policy_tree is not equal to NULL. */
1360 if (!path
->policy_tree
&& explicit_policy
== 0) {
1361 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1362 secnotice("policy", "policy tree failure on cert %u", n
- i
);
1365 /* If Last Cert in Path */
1369 /* Prepare for Next Cert */
1370 /* (a) verify that anyPolicy does not appear as an
1371 issuerDomainPolicy or a subjectDomainPolicy */
1372 const SecCEPolicyMappings
*pm
= SecCertificateGetPolicyMappings(cert
);
1373 if (pm
&& pm
->present
) {
1374 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1375 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1376 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1377 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1378 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1379 /* Policy mapping uses anyPolicy, illegal. */
1380 secnotice("policy", "policy mapping anyPolicy failure %u", n
- i
);
1386 /* (1) If the policy_mapping variable is greater than 0 */
1387 if (policy_mapping
> 0 && path
->policy_tree
) {
1388 if (!policy_tree_walk_depth(path
->policy_tree
, i
,
1389 policy_tree_map_if_match
, (void *)pm
)) {
1390 /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1. */
1391 policy_tree_walk_depth(path
->policy_tree
, i
, policy_tree_map_if_any
, (void *)pm
);
1393 } else if (path
->policy_tree
) {
1394 /* (i) delete each node of depth i in the valid_policy_tree
1395 where ID-P is the valid_policy. */
1396 policy_tree_walk_depth(path
->policy_tree
, i
,
1397 policy_tree_map_delete_if_match
, (void *)pm
);
1398 /* (ii) If there is a node in the valid_policy_tree of depth
1399 i-1 or less without any child nodes, delete that
1400 node. Repeat this step until there are no nodes of
1401 depth i-1 or less without children. */
1402 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1407 if (!is_self_issued
) {
1408 if (explicit_policy
)
1412 if (inhibit_any_policy
)
1413 inhibit_any_policy
--;
1416 const SecCEPolicyConstraints
*pc
=
1417 SecCertificateGetPolicyConstraints(cert
);
1419 if (pc
->requireExplicitPolicyPresent
1420 && pc
->requireExplicitPolicy
< explicit_policy
) {
1421 explicit_policy
= pc
->requireExplicitPolicy
;
1423 if (pc
->inhibitPolicyMappingPresent
1424 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1425 policy_mapping
= pc
->inhibitPolicyMapping
;
1429 const SecCEInhibitAnyPolicy
*iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1430 if (iap
&& iap
->skipCerts
< inhibit_any_policy
) {
1431 inhibit_any_policy
= iap
->skipCerts
;
1434 } /* end of path for loop */
1437 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
1439 if (explicit_policy
)
1442 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1444 if (pc
->requireExplicitPolicyPresent
1445 && pc
->requireExplicitPolicy
== 0) {
1446 explicit_policy
= 0;
1450 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1452 if (path
->policy_tree
) {
1453 #if !defined(NDEBUG)
1454 policy_tree_dump(path
->policy_tree
);
1457 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1460 /* If either (1) the value of explicit_policy variable is greater than
1461 zero or (2) the valid_policy_tree is not NULL, then path processing
1463 if (!path
->policy_tree
&& explicit_policy
== 0) {
1464 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1465 secnotice("policy", "policy tree failure on leaf");
1469 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationTrue
;