2 * Copyright (c) 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 * 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>
41 #include <utilities/SecIOFormat.h>
42 #include <utilities/SecCFError.h>
43 #include <utilities/SecCFWrappers.h>
44 #include <utilities/debugging.h>
46 #include "trust/trustd/policytree.h"
47 #include "trust/trustd/SecPolicyServer.h"
48 #include "trust/trustd/SecCertificateServer.h"
49 #include "trust/trustd/SecRevocationServer.h"
52 // MARK: SecCertificateVC
53 /********************************************************
54 ************* SecCertificateVC object ***************
55 ********************************************************/
57 struct SecCertificateVC
{
59 SecCertificateRef certificate
;
60 CFArrayRef usageConstraints
;
61 CFNumberRef revocationReason
;
64 bool require_revocation_response
;
66 CFGiblisWithHashFor(SecCertificateVC
)
68 static void SecCertificateVCDestroy(CFTypeRef cf
) {
69 SecCertificateVCRef cvc
= (SecCertificateVCRef
) cf
;
70 CFReleaseNull(cvc
->certificate
);
71 CFReleaseNull(cvc
->usageConstraints
);
72 CFReleaseNull(cvc
->revocationReason
);
75 static Boolean
SecCertificateVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
76 SecCertificateVCRef cv1
= (SecCertificateVCRef
) cf1
;
77 SecCertificateVCRef cv2
= (SecCertificateVCRef
) cf2
;
78 if (!CFEqual(cv1
->certificate
, cv2
->certificate
)) {
81 /* CertificateVCs are the same if either does not have usage constraints. */
82 if (cv1
->usageConstraints
&& cv2
->usageConstraints
&&
83 !CFEqual(cv1
->usageConstraints
, cv2
->usageConstraints
)) {
90 static CFHashCode
SecCertificateVCHash(CFTypeRef cf
) {
91 SecCertificateVCRef cvc
= (SecCertificateVCRef
) cf
;
92 CFHashCode hashCode
= 0;
93 hashCode
+= CFHash(cvc
->certificate
);
94 if (cvc
->usageConstraints
) {
95 hashCode
+= CFHash(cvc
->usageConstraints
);
100 static CFStringRef
SecCertificateVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
101 SecCertificateVCRef cvc
= (SecCertificateVCRef
)cf
;
102 return CFCopyDescription(cvc
->certificate
);
105 static bool SecCertificateVCCouldBeEV(SecCertificateRef certificate
) {
106 CFMutableDictionaryRef keySizes
= NULL
;
107 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
110 /* 3. Subscriber Certificate. */
112 /* (a) certificate Policies */
113 const SecCECertificatePolicies
*cp
;
114 cp
= SecCertificateGetCertificatePolicies(certificate
);
115 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
116 /* Now find at least one policy in here that has a qualifierID of id-qt 2
117 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
119 bool found_ev_anchor_for_leaf_policy
= false;
120 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
121 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
122 found_ev_anchor_for_leaf_policy
= true;
125 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
127 /* (b) cRLDistributionPoint
128 (c) authorityInformationAccess
129 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
132 /* (d) basicConstraints
133 If present, the cA field MUST be set false. */
134 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
136 require_action_quiet(bc
->isCA
== false, notEV
,
137 secnotice("ev", "Leaf has invalid basic constraints"));
141 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
143 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
,
144 secnotice("ev", "Leaf has invalid key usage %u", ku
));
148 /* The EV Cert Spec errata specifies this, though this is a check for SSL
149 not specifically EV. */
153 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
154 SecCertificateCopyExtendedKeyUsage(certificate
);
157 /* 6.1.5 Key Sizes */
158 CFAbsoluteTime jan2014
= 410227200;
159 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
160 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
161 &kCFTypeDictionaryValueCallBacks
), notEV
);
162 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
163 if (SecCertificateNotValidBefore(certificate
) < jan2014
) {
164 /* At least RSA 1024 or ECC NIST P-256. */
165 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
166 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
167 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
168 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
170 /* At least RSA 2028 or ECC NIST P-256. */
171 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
172 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
173 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
174 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
177 /* 6.3.2 Validity Periods */
178 // Will be checked by the policy server (see SecPolicyCheckValidityPeriodMaximums)
180 /* 7.1.3 Algorithm Object Identifiers */
181 CFAbsoluteTime jan2016
= 473299200;
182 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
184 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
185 notEV
, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
191 CFReleaseNull(rsaSize
);
192 CFReleaseNull(ecSize
);
193 CFReleaseNull(keySizes
);
198 SecCertificateVCRef
SecCertificateVCCreate(SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
199 if (!certificate
) { return NULL
; }
200 CFIndex size
= sizeof(struct SecCertificateVC
);
201 SecCertificateVCRef result
=
202 (SecCertificateVCRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
,
203 SecCertificateVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
206 result
->certificate
= CFRetainSafe(certificate
);
207 result
->isWeakHash
= SecCertificateIsWeakHash(certificate
);
208 result
->optionallyEV
= SecCertificateVCCouldBeEV(certificate
);
210 CFArrayRef emptyArray
= NULL
;
211 if (!usageConstraints
) {
212 require_action_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
, CFReleaseNull(result
));
213 usageConstraints
= emptyArray
;
215 result
->usageConstraints
= CFRetainSafe(usageConstraints
);
217 CFReleaseNull(emptyArray
);
222 // MARK: SecCertificatePathVC
223 /********************************************************
224 ************* SecCertificatePathVC object ***************
225 ********************************************************/
226 struct SecCertificatePathVC
{
230 /* Index of next parent source to search for parents. */
231 CFIndex nextParentSource
;
233 /* Index of last certificate in chain whose signature has been verified.
234 0 means nothing has been checked. 1 means the leaf has been verified
235 against its issuer, etc. */
236 CFIndex lastVerifiedSigner
;
238 /* Index of first self issued certificate in the chain. -1 mean there is
239 none. 0 means the leaf is self signed. */
242 /* True iff cert at index selfIssued does in fact self verify. */
245 /* True if the root of this path is an anchor. Trustedness of the
246 * anchor is determined by the PVC. */
249 policy_tree_t policy_tree
;
250 uint8_t policy_tree_verification_result
;
255 bool hasStrongHashes
;
260 /* This is the score of the path after determining acceptance. */
265 /* If checkedIssuers is true, then the value of unknownCAIndex contains
266 * the index of the first CA which violates known-only constraints, or
267 * -1 if all CA certificates are either known or not constrained. */
269 CFIndex unknownCAIndex
;
271 /* Enumerated value to determine whether CT is required for the leaf
272 * certificate (because a CA in the path has a require-ct constraint).
273 * If non-zero, CT is required; value indicates overridable status. */
274 SecPathCTPolicy requiresCT
;
276 /* Issuance time, as determined by earliest SCT timestamp for leaf. */
277 CFAbsoluteTime issuanceTime
;
279 SecCertificateVCRef certificates
[];
281 CFGiblisWithHashFor(SecCertificatePathVC
)
283 static void SecCertificatePathVCPrunePolicyTree(SecCertificatePathVCRef certificatePath
) {
284 if (certificatePath
->policy_tree
) {
285 policy_tree_prune(&certificatePath
->policy_tree
);
289 void SecCertificatePathVCDeleteRVCs(SecCertificatePathVCRef path
) {
291 CFIndex certIX
, certCount
= path
->rvcCount
;
292 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
293 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
301 static void SecCertificatePathVCDestroy(CFTypeRef cf
) {
302 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
304 SecCertificatePathVCDeleteRVCs(certificatePath
);
305 SecCertificatePathVCPrunePolicyTree(certificatePath
);
306 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
307 CFReleaseNull(certificatePath
->certificates
[ix
]);
311 static Boolean
SecCertificatePathVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
312 SecCertificatePathVCRef cp1
= (SecCertificatePathVCRef
) cf1
;
313 SecCertificatePathVCRef cp2
= (SecCertificatePathVCRef
) cf2
;
314 if (cp1
->count
!= cp2
->count
)
317 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
318 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
325 static CFHashCode
SecCertificatePathVCHash(CFTypeRef cf
) {
326 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
327 CFHashCode hashCode
= 0;
329 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
330 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
335 static CFStringRef
SecCertificatePathVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
336 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
337 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
338 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
339 CFStringAppendFormat(desc
, NULL
,
340 CFSTR("<%@ certs: "), typeStr
);
343 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
345 CFStringAppend(desc
, CFSTR(", "));
347 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
348 CFStringAppend(desc
, str
);
351 CFStringAppend(desc
, CFSTR(" >"));
356 /* Create a new certificate path from an old one. */
357 SecCertificatePathVCRef
SecCertificatePathVCCreate(SecCertificatePathVCRef path
,
358 SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
359 CFAllocatorRef allocator
= kCFAllocatorDefault
;
362 CFIndex selfIssued
, lastVerifiedSigner
;
365 count
= path
->count
+ 1;
366 lastVerifiedSigner
= path
->lastVerifiedSigner
;
367 selfIssued
= path
->selfIssued
;
368 isSelfSigned
= path
->isSelfSigned
;
371 lastVerifiedSigner
= 0;
373 isSelfSigned
= false;
376 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
377 count
* sizeof(SecCertificateRef
);
378 SecCertificatePathVCRef result
=
379 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
380 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
384 memset((char*)result
+ sizeof(result
->_base
), 0,
385 sizeof(*result
) - sizeof(result
->_base
));
387 result
->count
= count
;
388 result
->lastVerifiedSigner
= lastVerifiedSigner
;
389 result
->selfIssued
= selfIssued
;
390 result
->isSelfSigned
= isSelfSigned
;
392 for (ix
= 0; ix
< count
- 1; ++ix
) {
393 result
->certificates
[ix
] = path
->certificates
[ix
];
394 CFRetain(result
->certificates
[ix
]);
397 SecCertificateVCRef cvc
= SecCertificateVCCreate(certificate
, usageConstraints
);
398 result
->certificates
[count
- 1] = cvc
;
403 SecCertificatePathVCRef
SecCertificatePathVCCopyFromParent(
404 SecCertificatePathVCRef path
, CFIndex skipCount
) {
405 CFAllocatorRef allocator
= kCFAllocatorDefault
;
407 CFIndex selfIssued
, lastVerifiedSigner
;
410 /* Ensure we are at least returning a path of length 1. */
411 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
414 count
= path
->count
- skipCount
;
415 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
416 ? path
->lastVerifiedSigner
- skipCount
: 0;
417 selfIssued
= path
->selfIssued
>= skipCount
418 ? path
->selfIssued
- skipCount
: -1;
419 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
421 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
422 count
* sizeof(SecCertificateRef
);
423 SecCertificatePathVCRef result
=
424 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
425 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
429 memset((char*)result
+ sizeof(result
->_base
), 0,
430 sizeof(*result
) - sizeof(result
->_base
));
432 result
->count
= count
;
433 result
->lastVerifiedSigner
= lastVerifiedSigner
;
434 result
->selfIssued
= selfIssued
;
435 result
->isSelfSigned
= isSelfSigned
;
436 result
->isAnchored
= path
->isAnchored
;
438 for (ix
= 0; ix
< count
; ++ix
) {
439 CFIndex pathIX
= ix
+ skipCount
;
440 result
->certificates
[ix
] = path
->certificates
[pathIX
];
441 CFRetain(result
->certificates
[ix
]);
447 SecCertificatePathVCRef
SecCertificatePathVCCopyAddingLeaf(SecCertificatePathVCRef path
,
448 SecCertificateRef leaf
) {
449 CFAllocatorRef allocator
= kCFAllocatorDefault
;
451 CFIndex selfIssued
, lastVerifiedSigner
;
454 /* First make sure the new leaf is signed by path's current leaf. */
455 SecKeyRef issuerKey
= SecCertificatePathVCCopyPublicKeyAtIndex(path
, 0);
458 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
459 CFRelease(issuerKey
);
463 count
= path
->count
+ 1;
464 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
465 selfIssued
= path
->selfIssued
;
466 isSelfSigned
= path
->isSelfSigned
;
468 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
469 count
* sizeof(SecCertificateRef
);
470 SecCertificatePathVCRef result
=
471 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
472 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
476 memset((char*)result
+ sizeof(result
->_base
), 0,
477 sizeof(*result
) - sizeof(result
->_base
));
479 result
->count
= count
;
480 result
->lastVerifiedSigner
= lastVerifiedSigner
;
481 result
->selfIssued
= selfIssued
;
482 result
->isSelfSigned
= isSelfSigned
;
483 result
->isAnchored
= path
->isAnchored
;
486 for (ix
= 1; ix
< count
; ++ix
) {
487 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
488 CFRetain(result
->certificates
[ix
]);
490 SecCertificateVCRef leafVC
= SecCertificateVCCreate(leaf
, NULL
);
491 result
->certificates
[0] = leafVC
;
496 CFArrayRef
SecCertificatePathVCCopyCertificates(SecCertificatePathVCRef path
) {
497 CFMutableArrayRef outCerts
= NULL
;
498 size_t count
= path
->count
;
499 require_quiet(outCerts
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
), exit
);
500 SecCertificatePathVCForEachCertificate(path
, ^(SecCertificateRef cert
, bool * __unused stop
) {
501 CFArrayAppendValue(outCerts
, cert
);
507 CFArrayRef
SecCertificatePathVCCreateSerialized(SecCertificatePathVCRef path
) {
508 CFMutableArrayRef serializedCerts
= NULL
;
509 require_quiet(path
, exit
);
510 size_t count
= path
->count
;
511 require_quiet(serializedCerts
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
), exit
);
512 SecCertificatePathVCForEachCertificate(path
, ^(SecCertificateRef cert
, bool * __unused stop
) {
513 CFDataRef certData
= SecCertificateCopyData(cert
);
515 CFArrayAppendValue(serializedCerts
, certData
);
520 return serializedCerts
;
524 /* Record the fact that we found our own root cert as our parent
526 void SecCertificatePathVCSetSelfIssued(
527 SecCertificatePathVCRef certificatePath
) {
528 if (certificatePath
->selfIssued
>= 0) {
529 secdebug("trust", "%@ is already issued at %" PRIdCFIndex
, certificatePath
,
530 certificatePath
->selfIssued
);
533 secdebug("trust", "%@ is self issued", certificatePath
);
534 certificatePath
->selfIssued
= certificatePath
->count
- 1;
536 /* now check that the selfIssued cert was actually self-signed */
537 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
538 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->selfIssued
];
539 Boolean isSelfSigned
= false;
540 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
541 if ((status
== errSecSuccess
) && isSelfSigned
) {
542 certificatePath
->isSelfSigned
= true;
544 certificatePath
->selfIssued
= -1;
549 void SecCertificatePathVCSetIsAnchored(
550 SecCertificatePathVCRef certificatePath
) {
551 secdebug("trust", "%@ is anchored", certificatePath
);
552 certificatePath
->isAnchored
= true;
554 /* Now check if that anchor (last cert) was actually self-signed.
555 * In the non-anchor case, this is handled by SecCertificatePathVCSetSelfIssued.
556 * Because anchored chains immediately go into the candidate bucket in the trust
557 * server, we need to ensure that the self-signed/self-issued members are set
558 * for the purposes of scoring. */
559 if (!certificatePath
->isSelfSigned
&& certificatePath
->count
> 0) {
560 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->count
- 1];
561 Boolean isSelfSigned
= false;
562 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
563 if ((status
== errSecSuccess
) && isSelfSigned
) {
564 certificatePath
->isSelfSigned
= true;
565 if (certificatePath
->selfIssued
== -1) {
566 certificatePath
->selfIssued
= certificatePath
->count
- 1;
572 /* Return the index of the first non anchor certificate in the chain that is
573 self signed counting from the leaf up. Return -1 if there is none. */
574 CFIndex
SecCertificatePathVCSelfSignedIndex(
575 SecCertificatePathVCRef certificatePath
) {
576 if (certificatePath
->isSelfSigned
)
577 return certificatePath
->selfIssued
;
581 Boolean
SecCertificatePathVCIsAnchored(
582 SecCertificatePathVCRef certificatePath
) {
583 return certificatePath
->isAnchored
;
587 void SecCertificatePathVCSetNextSourceIndex(
588 SecCertificatePathVCRef certificatePath
, CFIndex sourceIndex
) {
589 certificatePath
->nextParentSource
= sourceIndex
;
592 CFIndex
SecCertificatePathVCGetNextSourceIndex(
593 SecCertificatePathVCRef certificatePath
) {
594 return certificatePath
->nextParentSource
;
597 CFIndex
SecCertificatePathVCGetCount(
598 SecCertificatePathVCRef certificatePath
) {
599 check(certificatePath
);
600 return certificatePath
? certificatePath
->count
: 0;
603 SecCertificateRef
SecCertificatePathVCGetCertificateAtIndex(
604 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
605 if (!certificatePath
|| ix
< 0 || ix
>= certificatePath
->count
) {
608 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
609 return cvc
? cvc
->certificate
: NULL
;
612 void SecCertificatePathVCForEachCertificate(SecCertificatePathVCRef path
, void(^operation
)(SecCertificateRef certificate
, bool *stop
)) {
614 CFIndex ix
, count
= path
->count
;
615 for (ix
= 0; ix
< count
; ++ix
) {
616 SecCertificateVCRef cvc
= path
->certificates
[ix
];
617 operation(cvc
->certificate
, &stop
);
622 CFIndex
SecCertificatePathVCGetIndexOfCertificate(SecCertificatePathVCRef path
,
623 SecCertificateRef certificate
) {
624 CFIndex ix
, count
= path
->count
;
625 for (ix
= 0; ix
< count
; ++ix
) {
626 SecCertificateVCRef cvc
= path
->certificates
[ix
];
627 if (CFEqual(cvc
->certificate
, certificate
))
633 /* Return the root certificate for certificatePath. Note that root is just
634 the top of the path as far as it is constructed. It may or may not be
635 trusted or self signed. */
636 SecCertificateRef
SecCertificatePathVCGetRoot(
637 SecCertificatePathVCRef certificatePath
) {
638 return SecCertificatePathVCGetCertificateAtIndex(certificatePath
,
639 SecCertificatePathVCGetCount(certificatePath
) - 1);
642 SecKeyRef
SecCertificatePathVCCopyPublicKeyAtIndex(
643 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
644 SecCertificateRef certificate
=
645 SecCertificatePathVCGetCertificateAtIndex(certificatePath
, ix
);
646 return SecCertificateCopyKey(certificate
);
649 CFArrayRef
SecCertificatePathVCGetUsageConstraintsAtIndex(
650 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
651 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
652 return cvc
->usageConstraints
;
655 void SecCertificatePathVCSetUsageConstraintsAtIndex(SecCertificatePathVCRef certificatePath
,
656 CFArrayRef newConstraints
, CFIndex ix
) {
657 CFArrayRef emptyArray
= NULL
;
658 if (!newConstraints
) {
659 require_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
);
660 newConstraints
= emptyArray
;
663 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
664 cvc
->usageConstraints
= CFRetainSafe(newConstraints
);
666 CFReleaseNull(emptyArray
);
670 SecPathVerifyStatus
SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath
) {
671 check(certificatePath
);
672 if (!certificatePath
)
673 return kSecPathVerifyFailed
;
675 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
676 ++certificatePath
->lastVerifiedSigner
) {
677 SecKeyRef issuerKey
=
678 SecCertificatePathVCCopyPublicKeyAtIndex(certificatePath
,
679 certificatePath
->lastVerifiedSigner
+ 1);
681 return kSecPathVerifiesUnknown
;
682 SecCertificateVCRef cvc
= certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
];
683 OSStatus status
= SecCertificateIsSignedBy(cvc
->certificate
,
685 CFRelease(issuerKey
);
687 return kSecPathVerifyFailed
;
691 return kSecPathVerifySuccess
;
694 /* Is the the issuer of the last cert a subject of a previous cert in the chain.See <rdar://33136765>. */
695 bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path
) {
696 bool isCircle
= false;
697 CFDataRef issuer
= SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path
));
698 if (!issuer
) { return isCircle
; }
699 CFIndex ix
= path
->count
- 2;
700 for (; ix
>= 0; ix
--) {
701 SecCertificateVCRef cvc
= path
->certificates
[ix
];
702 CFDataRef subject
= SecCertificateGetNormalizedSubjectContent(cvc
->certificate
);
703 if (subject
&& CFEqual(issuer
, subject
)) {
711 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
712 __block
bool result
= true;
713 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
714 if (!SecCertificateIsValid(certificate
, verifyTime
)) {
721 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath
) {
722 CFIndex ix
, count
= certificatePath
->count
;
724 if (certificatePath
->hasStrongHashes
) {
728 if (SecCertificatePathVCIsAnchored(certificatePath
)) {
729 /* For anchored paths, don't check the hash algorithm of the anchored cert,
730 * since we already decided to trust it. */
733 for (ix
= 0; ix
< count
; ++ix
) {
734 if (certificatePath
->certificates
[ix
]->isWeakHash
) {
738 certificatePath
->hasStrongHashes
= true;
742 bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath
) {
743 __block CFDictionaryRef keySizes
= NULL
;
744 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
745 __block
bool result
= false;
747 /* RSA key sizes are 2048-bit or larger. EC key sizes are P-224 or larger. */
748 require(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), errOut
);
749 require(ecSize
= CFNumberCreateWithCFIndex(NULL
, 224), errOut
);
750 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
751 const void *values
[] = { rsaSize
, ecSize
};
752 require(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
753 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
754 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
755 if (!SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
)) {
762 CFReleaseSafe(keySizes
);
763 CFReleaseSafe(rsaSize
);
764 CFReleaseSafe(ecSize
);
768 /* Return a score for this certificate chain. */
769 CFIndex
SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
772 /* Paths that don't verify score terribly.c */
773 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
774 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
775 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
779 if (certificatePath
->isAnchored
) {
780 /* Anchored paths for the win! */
784 if (certificatePath
->isSelfSigned
&& (certificatePath
->selfIssued
== certificatePath
->count
- 1)) {
785 /* Chains that terminate in a self-signed certificate are preferred,
786 even if they don't end in an anchor. */
788 /* Shorter chains ending in a self-signed cert are preferred. */
789 score
-= 1 * certificatePath
->count
;
791 /* Longer chains are preferred when the chain doesn't end in a self-signed cert. */
792 score
+= 1 * certificatePath
->count
;
795 if (SecCertificatePathVCIsValid(certificatePath
, verifyTime
)) {
799 if (!SecCertificatePathVCHasWeakHash(certificatePath
)) {
803 if (!SecCertificatePathVCHasWeakKeySize(certificatePath
)) {
810 CFIndex
SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath
) {
811 if (!certificatePath
) { return 0; }
812 return certificatePath
->score
;
815 void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath
, CFIndex score
) {
816 /* We may "score" the same path twice -- if we "accept" a path but then
817 * decide to keep looking for a better one, we we process the same path
818 * again in "reject" which creates a lower score. Don't replace a higher
819 * score with a lower score. Use reset below to post-reject a path. */
820 if (score
> certificatePath
->score
) {
821 certificatePath
->score
= score
;
825 void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath
) {
826 certificatePath
->score
= 0;
829 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
830 if (ix
>= certificatePath
->rvcCount
) {
833 return &((SecRVCRef
)certificatePath
->rvcs
)[ix
];
836 bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath
) {
837 return (bool)certificatePath
->rvcs
;
840 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath
, CFIndex certCount
) {
841 certificatePath
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
842 certificatePath
->rvcCount
= certCount
;
845 /* Return 0 if any certs revocation checking failed, or the earliest date on
846 which one of the used revocation validation tokens (ocsp response or
848 /* This function returns 0 to indicate revocation checking was not completed
849 for this certificate chain, otherwise returns the date at which the first
850 piece of revocation checking info we used expires. */
851 CFAbsoluteTime
SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path
) {
852 CFIndex certIX
, certCount
= path
->count
;
853 CFAbsoluteTime enu
= NULL_TIME
;
854 if (certCount
<= 1 || !path
->rvcs
) {
858 for (certIX
= 0; certIX
< path
->rvcCount
; ++certIX
) {
859 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
860 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
861 if (thisCertNextUpdate
== 0) {
863 /* We allow for CA certs to not be revocation checked if they
864 have no ocspResponders to check against, but the leaf
865 must be checked in order for us to claim we did revocation
867 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
868 CFArrayRef ocspResponders
= NULL
;
869 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
870 if (!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0) {
871 /* We can't check this cert so we don't consider it a soft
872 failure that we didn't. */
876 /* Make sure to always skip roots for whom we can't check revocation */
877 if (certIX
== certCount
- 1) {
880 secdebug("rvc", "revocation checking soft failure for cert: %ld",
882 enu
= thisCertNextUpdate
;
885 if (enu
== 0 || thisCertNextUpdate
< enu
) {
886 enu
= thisCertNextUpdate
;
890 secdebug("rvc", "revocation valid until: %lg", enu
);
894 void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
895 CFIndex ix
, CFNumberRef revocationReason
) {
896 if (ix
> certificatePath
->count
- 1) { return; }
897 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
898 cvc
->revocationReason
= CFRetainSafe(revocationReason
);
901 CFNumberRef
SecCertificatePathVCGetRevocationReason(SecCertificatePathVCRef certificatePath
) {
902 for (CFIndex ix
= 0; ix
< certificatePath
->count
; ix
++) {
903 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
904 if (cvc
->revocationReason
) {
905 return cvc
->revocationReason
;
911 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
913 if (ix
> certificatePath
->count
- 1) { return false; }
914 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
915 return cvc
->require_revocation_response
;
918 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
920 if (ix
> certificatePath
->count
- 1) { return; }
921 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
922 cvc
->require_revocation_response
= true;
925 bool SecCertificatePathVCCheckedIssuers(SecCertificatePathVCRef certificatePath
) {
926 return certificatePath
->checkedIssuers
;
929 void SecCertificatePathVCSetCheckedIssuers(SecCertificatePathVCRef certificatePath
, bool checked
) {
930 certificatePath
->checkedIssuers
= checked
;
933 CFIndex
SecCertificatePathVCUnknownCAIndex(SecCertificatePathVCRef certificatePath
) {
934 return certificatePath
->unknownCAIndex
;
937 void SecCertificatePathVCSetUnknownCAIndex(SecCertificatePathVCRef certificatePath
, CFIndex index
) {
938 certificatePath
->unknownCAIndex
= index
;
941 bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath
) {
942 if (!certificatePath
) { return false; }
943 return certificatePath
->pathValidated
;
946 void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath
) {
947 certificatePath
->pathValidated
= true;
950 bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath
) {
951 if (!certificatePath
) { return false; }
952 return certificatePath
->isEV
;
955 void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath
, bool isEV
) {
956 certificatePath
->isEV
= isEV
;
959 bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath
) {
960 if (!certificatePath
) { return false; }
961 return certificatePath
->certificates
[0]->optionallyEV
;
964 bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath
) {
965 if (!certificatePath
) { return false; }
966 return certificatePath
->isCT
;
969 void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath
, bool isCT
) {
970 certificatePath
->isCT
= isCT
;
973 SecPathCTPolicy
SecCertificatePathVCRequiresCT(SecCertificatePathVCRef certificatePath
) {
974 if (!certificatePath
) { return kSecPathCTNotRequired
; }
975 return certificatePath
->requiresCT
;
978 void SecCertificatePathVCSetRequiresCT(SecCertificatePathVCRef certificatePath
, SecPathCTPolicy requiresCT
) {
979 if (certificatePath
->requiresCT
> requiresCT
) {
980 return; /* once set, CT policy may be only be changed to a more strict value */
982 certificatePath
->requiresCT
= requiresCT
;
985 CFAbsoluteTime
SecCertificatePathVCIssuanceTime(SecCertificatePathVCRef certificatePath
) {
986 if (!certificatePath
) { return 0; }
987 return certificatePath
->issuanceTime
;
990 void SecCertificatePathVCSetIssuanceTime(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime issuanceTime
) {
991 certificatePath
->issuanceTime
= issuanceTime
;
994 bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath
) {
995 if (!certificatePath
) { return false; }
996 return certificatePath
->is_allowlisted
;
999 void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath
, bool isAllowlisted
) {
1000 certificatePath
->is_allowlisted
= isAllowlisted
;
1003 /* MARK: policy_tree path verification */
1004 struct policy_tree_add_ctx
{
1006 policy_qualifier_t p_q
;
1009 /* 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}. */
1010 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
1011 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1012 policy_set_t policy_set
;
1013 for (policy_set
= node
->expected_policy_set
;
1015 policy_set
= policy_set
->oid_next
) {
1016 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
1017 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1024 /* 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}. */
1025 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
1026 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
1027 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1028 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
1034 /* Return true iff node has a child with a valid_policy equal to oid. */
1035 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
1037 policy_tree_t child
;
1038 for (child
= node
->children
; child
; child
= child
->siblings
) {
1039 if (oid_equal(child
->valid_policy
, (*oid
))) {
1046 /* 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. */
1047 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
1048 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
1049 policy_set_t policy_set
;
1050 bool added_node
= false;
1051 for (policy_set
= node
->expected_policy_set
;
1053 policy_set
= policy_set
->oid_next
) {
1054 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
1055 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1062 /* 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. */
1063 static bool policy_tree_map_if_match(policy_tree_t node
, void *ctx
) {
1064 /* Can't map oidAnyPolicy. */
1065 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1068 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1069 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1070 policy_set_t policy_set
= NULL
;
1071 /* Generate the policy_set of sdps for matching idp */
1072 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1073 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1074 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1075 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1076 p_node
->oid
= mapping
->subjectDomainPolicy
;
1077 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1078 policy_set
= p_node
;
1082 policy_tree_set_expected_policy(node
, policy_set
);
1088 /* 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:
1089 (i) set the valid_policy to ID-P;
1090 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1091 (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. */
1092 static bool policy_tree_map_if_any(policy_tree_t node
, void *ctx
) {
1093 if (!oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1097 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1098 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1099 CFMutableDictionaryRef mappings
= NULL
;
1100 CFDataRef idp
= NULL
;
1101 CFDataRef sdp
= NULL
;
1102 require_quiet(mappings
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1103 &kCFTypeDictionaryValueCallBacks
),
1105 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1106 for (mapping_ix
= 0; mapping_ix
< mapping_count
; mapping_ix
++) {
1107 oid_t issuerDomainPolicy
= pm
->mappings
[mapping_ix
].issuerDomainPolicy
;
1108 oid_t subjectDomainPolicy
= pm
->mappings
[mapping_ix
].subjectDomainPolicy
;
1109 idp
= CFDataCreateWithBytesNoCopy(NULL
, issuerDomainPolicy
.data
, issuerDomainPolicy
.length
, kCFAllocatorNull
);
1110 sdp
= CFDataCreateWithBytesNoCopy(NULL
, subjectDomainPolicy
.data
, subjectDomainPolicy
.length
, kCFAllocatorNull
);
1111 CFMutableArrayRef sdps
= (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
1113 CFArrayAppendValue(sdps
, sdp
);
1115 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1116 &kCFTypeArrayCallBacks
), errOut
);
1117 CFArrayAppendValue(sdps
, sdp
);
1118 CFDictionarySetValue(mappings
, idp
, sdps
);
1125 /* Now we use the dictionary to generate the new nodes */
1126 CFDictionaryForEach(mappings
, ^(const void *key
, const void *value
) {
1127 CFDataRef idp
= key
;
1128 CFArrayRef sdps
= value
;
1130 /* (i) set the valid_policy to ID-P; */
1132 p_oid
.data
= (uint8_t *)CFDataGetBytePtr(idp
);
1133 p_oid
.length
= CFDataGetLength(idp
);
1135 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1136 policy_qualifier_t p_q
= node
->qualifier_set
;
1138 /* (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. */
1139 __block policy_set_t p_expected
= NULL
;
1140 CFArrayForEach(sdps
, ^(const void *value
) {
1141 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*p_expected
));
1142 p_node
->oid
.data
= (void *)CFDataGetBytePtr(value
);
1143 p_node
->oid
.length
= CFDataGetLength(value
);
1144 p_node
->oid_next
= p_expected
? p_expected
: NULL
;
1145 p_expected
= p_node
;
1148 policy_tree_add_sibling(node
, &p_oid
, p_q
, p_expected
);
1150 CFReleaseNull(mappings
);
1154 CFReleaseNull(mappings
);
1160 static bool policy_tree_map_delete_if_match(policy_tree_t node
, void *ctx
) {
1161 /* Can't map oidAnyPolicy. */
1162 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1165 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1166 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1167 /* If this node matches any of the idps, delete it. */
1168 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1169 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1170 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1171 policy_tree_remove_node(&node
);
1178 bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path
, CFIndex ix
) {
1179 /* The SecCertificatePath only tells us the last self-issued cert.
1180 * The chain may have more than one self-issued cert, so we need to
1181 * do the comparison. */
1182 bool result
= false;
1183 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, ix
);
1184 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
1185 CFDataRef subject
= SecCertificateCopyNormalizedSubjectSequence(cert
);
1186 if (issuer
&& subject
&& CFEqual(issuer
, subject
)) {
1189 CFReleaseNull(issuer
);
1190 CFReleaseNull(subject
);
1195 kSecPolicyTreeVerificationUnknown
= 0,
1196 kSecPolicyTreeVerificationFalse
,
1197 kSecPolicyTreeVerificationTrue
,
1200 /* RFC 5280 policy tree processing */
1201 bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path
, bool anchor_trusted
) {
1202 if (!path
) { return false; }
1203 if (path
->policy_tree_verification_result
!= kSecPolicyTreeVerificationUnknown
) {
1204 return (path
->policy_tree_verification_result
== kSecPolicyTreeVerificationTrue
);
1207 /* Path Validation initialization */
1208 bool result
= false;
1209 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationFalse
;
1210 bool initial_policy_mapping_inhibit
= false;
1211 bool initial_explicit_policy
= false;
1212 bool initial_any_policy_inhibit
= false;
1214 SecCertificatePathVCPrunePolicyTree(path
);
1215 path
->policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1217 assert((unsigned long)path
->count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1218 uint32_t n
= (uint32_t)path
->count
;
1219 if (anchor_trusted
) {
1223 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1224 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1225 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1227 SecCertificateRef cert
= NULL
;
1229 for (i
= 1; i
<= n
; ++i
) {
1231 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, n
- i
);
1232 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(path
, n
- i
);
1235 if (path
->policy_tree
) {
1236 const SecCECertificatePolicies
*cp
=
1237 SecCertificateGetCertificatePolicies(cert
);
1238 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1239 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1240 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1241 oid_t p_oid
= policy
->policyIdentifier
;
1242 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1243 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1244 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1245 if (!policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1246 policy_tree_add_if_match
, &ctx
)) {
1247 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1248 policy_tree_add_if_any
, &ctx
);
1252 /* The certificate policies extension includes the policy
1253 anyPolicy with the qualifier set AP-Q and either
1254 (a) inhibit_anyPolicy is greater than 0 or
1255 (b) i < n and the certificate is self-issued. */
1256 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1257 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1258 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1259 oid_t p_oid
= policy
->policyIdentifier
;
1260 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1261 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1262 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1263 policy_tree_add_expected
, (void *)p_q
);
1268 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1271 SecCertificatePathVCPrunePolicyTree(path
);
1275 /* (f) Verify that either explicit_policy is greater than 0 or the
1276 valid_policy_tree is not equal to NULL. */
1277 if (!path
->policy_tree
&& explicit_policy
== 0) {
1278 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1279 secnotice("policy", "policy tree failure on cert %u", n
- i
);
1282 /* If Last Cert in Path */
1286 /* Prepare for Next Cert */
1287 /* (a) verify that anyPolicy does not appear as an
1288 issuerDomainPolicy or a subjectDomainPolicy */
1289 const SecCEPolicyMappings
*pm
= SecCertificateGetPolicyMappings(cert
);
1290 if (pm
&& pm
->present
) {
1291 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1292 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1293 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1294 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1295 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1296 /* Policy mapping uses anyPolicy, illegal. */
1297 secnotice("policy", "policy mapping anyPolicy failure %u", n
- i
);
1303 /* (1) If the policy_mapping variable is greater than 0 */
1304 if (policy_mapping
> 0 && path
->policy_tree
) {
1305 if (!policy_tree_walk_depth(path
->policy_tree
, i
,
1306 policy_tree_map_if_match
, (void *)pm
)) {
1307 /* 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. */
1308 policy_tree_walk_depth(path
->policy_tree
, i
, policy_tree_map_if_any
, (void *)pm
);
1310 } else if (path
->policy_tree
) {
1311 /* (i) delete each node of depth i in the valid_policy_tree
1312 where ID-P is the valid_policy. */
1313 policy_tree_walk_depth(path
->policy_tree
, i
,
1314 policy_tree_map_delete_if_match
, (void *)pm
);
1315 /* (ii) If there is a node in the valid_policy_tree of depth
1316 i-1 or less without any child nodes, delete that
1317 node. Repeat this step until there are no nodes of
1318 depth i-1 or less without children. */
1319 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1324 if (!is_self_issued
) {
1325 if (explicit_policy
)
1329 if (inhibit_any_policy
)
1330 inhibit_any_policy
--;
1333 const SecCEPolicyConstraints
*pc
=
1334 SecCertificateGetPolicyConstraints(cert
);
1336 if (pc
->requireExplicitPolicyPresent
1337 && pc
->requireExplicitPolicy
< explicit_policy
) {
1338 explicit_policy
= pc
->requireExplicitPolicy
;
1340 if (pc
->inhibitPolicyMappingPresent
1341 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1342 policy_mapping
= pc
->inhibitPolicyMapping
;
1346 const SecCEInhibitAnyPolicy
*iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1347 if (iap
&& iap
->skipCerts
< inhibit_any_policy
) {
1348 inhibit_any_policy
= iap
->skipCerts
;
1351 } /* end of path for loop */
1354 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
1356 if (explicit_policy
)
1359 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1361 if (pc
->requireExplicitPolicyPresent
1362 && pc
->requireExplicitPolicy
== 0) {
1363 explicit_policy
= 0;
1367 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1369 if (path
->policy_tree
) {
1370 #if !defined(NDEBUG)
1371 policy_tree_dump(path
->policy_tree
);
1374 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1377 /* If either (1) the value of explicit_policy variable is greater than
1378 zero or (2) the valid_policy_tree is not NULL, then path processing
1380 if (!path
->policy_tree
&& explicit_policy
== 0) {
1381 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1382 secnotice("policy", "policy tree failure on leaf");
1386 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationTrue
;