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/oidsPriv.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/oids.h>
42 #include <utilities/SecIOFormat.h>
43 #include <utilities/SecCFError.h>
44 #include <utilities/SecCFWrappers.h>
45 #include <utilities/debugging.h>
47 #include <securityd/policytree.h>
48 #include <securityd/SecPolicyServer.h>
49 #include <securityd/SecCertificateServer.h>
50 #include <securityd/SecRevocationServer.h>
53 // MARK: SecCertificateVC
54 /********************************************************
55 ************* SecCertificateVC object ***************
56 ********************************************************/
58 struct SecCertificateVC
{
60 SecCertificateRef certificate
;
61 CFArrayRef usageConstraints
;
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
);
74 static Boolean
SecCertificateVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
75 SecCertificateVCRef cv1
= (SecCertificateVCRef
) cf1
;
76 SecCertificateVCRef cv2
= (SecCertificateVCRef
) cf2
;
77 if (!CFEqual(cv1
->certificate
, cv2
->certificate
)) {
80 /* CertificateVCs are the same if either does not have usage constraints. */
81 if (cv1
->usageConstraints
&& cv2
->usageConstraints
&&
82 !CFEqual(cv1
->usageConstraints
, cv2
->usageConstraints
)) {
89 static CFHashCode
SecCertificateVCHash(CFTypeRef cf
) {
90 SecCertificateVCRef cvc
= (SecCertificateVCRef
) cf
;
91 CFHashCode hashCode
= 0;
92 hashCode
+= CFHash(cvc
->certificate
);
93 if (cvc
->usageConstraints
) {
94 hashCode
+= CFHash(cvc
->usageConstraints
);
99 static CFStringRef
SecCertificateVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
100 SecCertificateVCRef cvc
= (SecCertificateVCRef
)cf
;
101 return CFCopyDescription(cvc
->certificate
);
104 static bool SecCertificateVCCouldBeEV(SecCertificateRef certificate
) {
105 CFMutableDictionaryRef keySizes
= NULL
;
106 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
109 /* 3. Subscriber Certificate. */
111 /* (a) certificate Policies */
112 const SecCECertificatePolicies
*cp
;
113 cp
= SecCertificateGetCertificatePolicies(certificate
);
114 require_quiet(cp
&& cp
->numPolicies
> 0, notEV
);
115 /* Now find at least one policy in here that has a qualifierID of id-qt 2
116 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
118 bool found_ev_anchor_for_leaf_policy
= false;
119 for (ix
= 0; ix
< cp
->numPolicies
; ++ix
) {
120 if (SecPolicyIsEVPolicy(&cp
->policies
[ix
].policyIdentifier
)) {
121 found_ev_anchor_for_leaf_policy
= true;
124 require_quiet(found_ev_anchor_for_leaf_policy
, notEV
);
126 /* (b) cRLDistributionPoint
127 (c) authorityInformationAccess
128 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
131 /* (d) basicConstraints
132 If present, the cA field MUST be set false. */
133 const SecCEBasicConstraints
*bc
= SecCertificateGetBasicConstraints(certificate
);
135 require_action_quiet(bc
->isCA
== false, notEV
,
136 secnotice("ev", "Leaf has invalid basic constraints"));
140 SecKeyUsage ku
= SecCertificateGetKeyUsage(certificate
);
142 require_action_quiet((ku
& (kSecKeyUsageKeyCertSign
| kSecKeyUsageCRLSign
)) == 0, notEV
,
143 secnotice("ev", "Leaf has invalid key usage %u", ku
));
147 /* The EV Cert Spec errata specifies this, though this is a check for SSL
148 not specifically EV. */
152 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
153 SecCertificateCopyExtendedKeyUsage(certificate
);
156 /* 6.1.5 Key Sizes */
157 CFAbsoluteTime jan2014
= 410227200;
158 require_quiet(ecSize
= CFNumberCreateWithCFIndex(NULL
, 256), notEV
);
159 require_quiet(keySizes
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
,
160 &kCFTypeDictionaryValueCallBacks
), notEV
);
161 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeEC
, ecSize
);
162 if (SecCertificateNotValidBefore(certificate
) < jan2014
) {
163 /* At least RSA 1024 or ECC NIST P-256. */
164 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 1024), notEV
);
165 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
166 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
167 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
169 /* At least RSA 2028 or ECC NIST P-256. */
170 require_quiet(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), notEV
);
171 CFDictionaryAddValue(keySizes
, kSecAttrKeyTypeRSA
, rsaSize
);
172 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
), notEV
,
173 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
176 /* 6.3.2 Validity Periods */
177 CFAbsoluteTime jul2016
= 489024000;
178 CFAbsoluteTime notAfter
= SecCertificateNotValidAfter(certificate
);
179 CFAbsoluteTime notBefore
= SecCertificateNotValidBefore(certificate
);
180 if (SecCertificateNotValidBefore(certificate
) < jul2016
) {
181 /* Validity Period no greater than 60 months.
182 60 months is no more than 5 years and 2 leap days. */
183 CFAbsoluteTime maxPeriod
= 60*60*24*(365*5+2);
184 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
185 secnotice("ev", "Leaf's validity period is more than 60 months"));
187 /* Validity Period no greater than 39 months.
188 39 months is no more than 3 years, 2 31-day months,
189 1 30-day month, and 1 leap day */
190 CFAbsoluteTime maxPeriod
= 60*60*24*(365*3+2*31+30+1);
191 require_action_quiet(notAfter
- notBefore
<= maxPeriod
, notEV
,
192 secnotice("ev", "Leaf has validity period longer than 39 months and issued after 30 June 2016"));
195 /* 7.1.3 Algorithm Object Identifiers */
196 CFAbsoluteTime jan2016
= 473299200;
197 if (SecCertificateNotValidBefore(certificate
) > jan2016
) {
199 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate
) > kSecSignatureHashAlgorithmSHA1
,
200 notEV
, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
206 CFReleaseNull(rsaSize
);
207 CFReleaseNull(ecSize
);
208 CFReleaseNull(keySizes
);
213 SecCertificateVCRef
SecCertificateVCCreate(SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
214 if (!certificate
) { return NULL
; }
215 CFIndex size
= sizeof(struct SecCertificateVC
);
216 SecCertificateVCRef result
=
217 (SecCertificateVCRef
)_CFRuntimeCreateInstance(kCFAllocatorDefault
,
218 SecCertificateVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
221 result
->certificate
= CFRetainSafe(certificate
);
222 result
->isWeakHash
= SecCertificateIsWeakHash(certificate
);
223 result
->optionallyEV
= SecCertificateVCCouldBeEV(certificate
);
225 CFArrayRef emptyArray
= NULL
;
226 if (!usageConstraints
) {
227 require_action_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
, CFReleaseNull(result
));
228 usageConstraints
= emptyArray
;
230 result
->usageConstraints
= CFRetainSafe(usageConstraints
);
232 CFReleaseNull(emptyArray
);
237 // MARK: SecCertificatePathVC
238 /********************************************************
239 ************* SecCertificatePathVC object ***************
240 ********************************************************/
241 struct SecCertificatePathVC
{
245 /* Index of next parent source to search for parents. */
246 CFIndex nextParentSource
;
248 /* Index of last certificate in chain who's signature has been verified.
249 0 means nothing has been checked. 1 means the leaf has been verified
250 against it's issuer, etc. */
251 CFIndex lastVerifiedSigner
;
253 /* Index of first self issued certificate in the chain. -1 mean there is
254 none. 0 means the leaf is self signed. */
257 /* True iff cert at index selfIssued does in fact self verify. */
260 /* True if the root of this path is an anchor. Trustedness of the
261 * anchor is determined by the PVC. */
264 policy_tree_t policy_tree
;
265 uint8_t policy_tree_verification_result
;
270 bool hasStrongHashes
;
275 /* This is the score of the path after determining acceptance. */
280 SecCertificateVCRef certificates
[];
282 CFGiblisWithHashFor(SecCertificatePathVC
)
284 static void SecCertificatePathVCPrunePolicyTree(SecCertificatePathVCRef certificatePath
) {
285 if (certificatePath
->policy_tree
) {
286 policy_tree_prune(&certificatePath
->policy_tree
);
290 static void SecCertificatePathVCDeleteRVCs(SecCertificatePathVCRef path
) {
292 CFIndex certIX
, certCount
= path
->rvcCount
;
293 for (certIX
= 0; certIX
< certCount
; ++certIX
) {
294 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
302 static void SecCertificatePathVCDestroy(CFTypeRef cf
) {
303 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
305 SecCertificatePathVCDeleteRVCs(certificatePath
);
306 SecCertificatePathVCPrunePolicyTree(certificatePath
);
307 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
308 CFReleaseNull(certificatePath
->certificates
[ix
]);
312 static Boolean
SecCertificatePathVCCompare(CFTypeRef cf1
, CFTypeRef cf2
) {
313 SecCertificatePathVCRef cp1
= (SecCertificatePathVCRef
) cf1
;
314 SecCertificatePathVCRef cp2
= (SecCertificatePathVCRef
) cf2
;
315 if (cp1
->count
!= cp2
->count
)
318 for (ix
= 0; ix
< cp1
->count
; ++ix
) {
319 if (!CFEqual(cp1
->certificates
[ix
], cp2
->certificates
[ix
]))
326 static CFHashCode
SecCertificatePathVCHash(CFTypeRef cf
) {
327 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
328 CFHashCode hashCode
= 0;
330 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
331 hashCode
+= CFHash(certificatePath
->certificates
[ix
]);
336 static CFStringRef
SecCertificatePathVCCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
337 SecCertificatePathVCRef certificatePath
= (SecCertificatePathVCRef
) cf
;
338 CFMutableStringRef desc
= CFStringCreateMutable(kCFAllocatorDefault
, 0);
339 CFStringRef typeStr
= CFCopyTypeIDDescription(CFGetTypeID(cf
));
340 CFStringAppendFormat(desc
, NULL
,
341 CFSTR("<%@ certs: "), typeStr
);
344 for (ix
= 0; ix
< certificatePath
->count
; ++ix
) {
346 CFStringAppend(desc
, CFSTR(", "));
348 CFStringRef str
= CFCopyDescription(certificatePath
->certificates
[ix
]);
349 CFStringAppend(desc
, str
);
352 CFStringAppend(desc
, CFSTR(" >"));
357 /* Create a new certificate path from an old one. */
358 SecCertificatePathVCRef
SecCertificatePathVCCreate(SecCertificatePathVCRef path
,
359 SecCertificateRef certificate
, CFArrayRef usageConstraints
) {
360 CFAllocatorRef allocator
= kCFAllocatorDefault
;
363 CFIndex selfIssued
, lastVerifiedSigner
;
366 count
= path
->count
+ 1;
367 lastVerifiedSigner
= path
->lastVerifiedSigner
;
368 selfIssued
= path
->selfIssued
;
369 isSelfSigned
= path
->isSelfSigned
;
372 lastVerifiedSigner
= 0;
374 isSelfSigned
= false;
377 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
378 count
* sizeof(SecCertificateRef
);
379 SecCertificatePathVCRef result
=
380 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
381 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
385 memset((char*)result
+ sizeof(result
->_base
), 0,
386 sizeof(*result
) - sizeof(result
->_base
));
388 result
->count
= count
;
389 result
->lastVerifiedSigner
= lastVerifiedSigner
;
390 result
->selfIssued
= selfIssued
;
391 result
->isSelfSigned
= isSelfSigned
;
393 for (ix
= 0; ix
< count
- 1; ++ix
) {
394 result
->certificates
[ix
] = path
->certificates
[ix
];
395 CFRetain(result
->certificates
[ix
]);
398 SecCertificateVCRef cvc
= SecCertificateVCCreate(certificate
, usageConstraints
);
399 result
->certificates
[count
- 1] = cvc
;
404 SecCertificatePathVCRef
SecCertificatePathVCCopyFromParent(
405 SecCertificatePathVCRef path
, CFIndex skipCount
) {
406 CFAllocatorRef allocator
= kCFAllocatorDefault
;
408 CFIndex selfIssued
, lastVerifiedSigner
;
411 /* Ensure we are at least returning a path of length 1. */
412 if (skipCount
< 0 || path
->count
< 1 + skipCount
)
415 count
= path
->count
- skipCount
;
416 lastVerifiedSigner
= path
->lastVerifiedSigner
> skipCount
417 ? path
->lastVerifiedSigner
- skipCount
: 0;
418 selfIssued
= path
->selfIssued
>= skipCount
419 ? path
->selfIssued
- skipCount
: -1;
420 isSelfSigned
= path
->selfIssued
>= 0 ? path
->isSelfSigned
: false;
422 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
423 count
* sizeof(SecCertificateRef
);
424 SecCertificatePathVCRef result
=
425 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
426 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
430 memset((char*)result
+ sizeof(result
->_base
), 0,
431 sizeof(*result
) - sizeof(result
->_base
));
433 result
->count
= count
;
434 result
->lastVerifiedSigner
= lastVerifiedSigner
;
435 result
->selfIssued
= selfIssued
;
436 result
->isSelfSigned
= isSelfSigned
;
437 result
->isAnchored
= path
->isAnchored
;
439 for (ix
= 0; ix
< count
; ++ix
) {
440 CFIndex pathIX
= ix
+ skipCount
;
441 result
->certificates
[ix
] = path
->certificates
[pathIX
];
442 CFRetain(result
->certificates
[ix
]);
448 SecCertificatePathVCRef
SecCertificatePathVCCopyAddingLeaf(SecCertificatePathVCRef path
,
449 SecCertificateRef leaf
) {
450 CFAllocatorRef allocator
= kCFAllocatorDefault
;
452 CFIndex selfIssued
, lastVerifiedSigner
;
455 /* First make sure the new leaf is signed by path's current leaf. */
456 SecKeyRef issuerKey
= SecCertificatePathVCCopyPublicKeyAtIndex(path
, 0);
459 OSStatus status
= SecCertificateIsSignedBy(leaf
, issuerKey
);
460 CFRelease(issuerKey
);
464 count
= path
->count
+ 1;
465 lastVerifiedSigner
= path
->lastVerifiedSigner
+ 1;
466 selfIssued
= path
->selfIssued
;
467 isSelfSigned
= path
->isSelfSigned
;
469 CFIndex size
= sizeof(struct SecCertificatePathVC
) +
470 count
* sizeof(SecCertificateRef
);
471 SecCertificatePathVCRef result
=
472 (SecCertificatePathVCRef
)_CFRuntimeCreateInstance(allocator
,
473 SecCertificatePathVCGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
477 memset((char*)result
+ sizeof(result
->_base
), 0,
478 sizeof(*result
) - sizeof(result
->_base
));
480 result
->count
= count
;
481 result
->lastVerifiedSigner
= lastVerifiedSigner
;
482 result
->selfIssued
= selfIssued
;
483 result
->isSelfSigned
= isSelfSigned
;
484 result
->isAnchored
= path
->isAnchored
;
487 for (ix
= 1; ix
< count
; ++ix
) {
488 result
->certificates
[ix
] = path
->certificates
[ix
- 1];
489 CFRetain(result
->certificates
[ix
]);
491 SecCertificateVCRef leafVC
= SecCertificateVCCreate(leaf
, NULL
);
492 result
->certificates
[0] = leafVC
;
497 CFArrayRef
SecCertificatePathVCCopyCertificates(SecCertificatePathVCRef path
) {
498 CFMutableArrayRef outCerts
= NULL
;
499 size_t count
= path
->count
;
500 require_quiet(outCerts
= CFArrayCreateMutable(NULL
, count
, &kCFTypeArrayCallBacks
), exit
);
501 SecCertificatePathVCForEachCertificate(path
, ^(SecCertificateRef cert
, bool * __unused stop
) {
502 CFArrayAppendValue(outCerts
, cert
);
508 SecCertificatePathRef
SecCertificatePathVCCopyCertificatePath(SecCertificatePathVCRef path
) {
509 CFArrayRef certs
= SecCertificatePathVCCopyCertificates(path
);
510 SecCertificatePathRef newPath
= SecCertificatePathCreateWithCertificates(certs
, NULL
);
511 CFReleaseNull(certs
);
515 /* Record the fact that we found our own root cert as our parent
517 void SecCertificatePathVCSetSelfIssued(
518 SecCertificatePathVCRef certificatePath
) {
519 if (certificatePath
->selfIssued
>= 0) {
520 secdebug("trust", "%@ is already issued at %" PRIdCFIndex
, certificatePath
,
521 certificatePath
->selfIssued
);
524 secdebug("trust", "%@ is self issued", certificatePath
);
525 certificatePath
->selfIssued
= certificatePath
->count
- 1;
527 /* now check that the selfIssued cert was actually self-signed */
528 if (certificatePath
->selfIssued
>= 0 && !certificatePath
->isSelfSigned
) {
529 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->selfIssued
];
530 Boolean isSelfSigned
= false;
531 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
532 if ((status
== errSecSuccess
) && isSelfSigned
) {
533 certificatePath
->isSelfSigned
= true;
535 certificatePath
->selfIssued
= -1;
540 void SecCertificatePathVCSetIsAnchored(
541 SecCertificatePathVCRef certificatePath
) {
542 secdebug("trust", "%@ is anchored", certificatePath
);
543 certificatePath
->isAnchored
= true;
545 /* Now check if that anchor (last cert) was actually self-signed.
546 * In the non-anchor case, this is handled by SecCertificatePathVCSetSelfIssued.
547 * Because anchored chains immediately go into the candidate bucket in the trust
548 * server, we need to ensure that the self-signed/self-issued members are set
549 * for the purposes of scoring. */
550 if (!certificatePath
->isSelfSigned
&& certificatePath
->count
> 0) {
551 SecCertificateVCRef certVC
= certificatePath
->certificates
[certificatePath
->count
- 1];
552 Boolean isSelfSigned
= false;
553 OSStatus status
= SecCertificateIsSelfSigned(certVC
->certificate
, &isSelfSigned
);
554 if ((status
== errSecSuccess
) && isSelfSigned
) {
555 certificatePath
->isSelfSigned
= true;
556 if (certificatePath
->selfIssued
== -1) {
557 certificatePath
->selfIssued
= certificatePath
->count
- 1;
563 /* Return the index of the first non anchor certificate in the chain that is
564 self signed counting from the leaf up. Return -1 if there is none. */
565 CFIndex
SecCertificatePathVCSelfSignedIndex(
566 SecCertificatePathVCRef certificatePath
) {
567 if (certificatePath
->isSelfSigned
)
568 return certificatePath
->selfIssued
;
572 Boolean
SecCertificatePathVCIsAnchored(
573 SecCertificatePathVCRef certificatePath
) {
574 return certificatePath
->isAnchored
;
578 void SecCertificatePathVCSetNextSourceIndex(
579 SecCertificatePathVCRef certificatePath
, CFIndex sourceIndex
) {
580 certificatePath
->nextParentSource
= sourceIndex
;
583 CFIndex
SecCertificatePathVCGetNextSourceIndex(
584 SecCertificatePathVCRef certificatePath
) {
585 return certificatePath
->nextParentSource
;
588 CFIndex
SecCertificatePathVCGetCount(
589 SecCertificatePathVCRef certificatePath
) {
590 check(certificatePath
);
591 return certificatePath
? certificatePath
->count
: 0;
594 SecCertificateRef
SecCertificatePathVCGetCertificateAtIndex(
595 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
596 check(certificatePath
&& ix
>= 0 && ix
< certificatePath
->count
);
597 return (certificatePath
->certificates
[ix
])->certificate
;
600 void SecCertificatePathVCForEachCertificate(SecCertificatePathVCRef path
, void(^operation
)(SecCertificateRef certificate
, bool *stop
)) {
602 CFIndex ix
, count
= path
->count
;
603 for (ix
= 0; ix
< count
; ++ix
) {
604 SecCertificateVCRef cvc
= path
->certificates
[ix
];
605 operation(cvc
->certificate
, &stop
);
610 CFIndex
SecCertificatePathVCGetIndexOfCertificate(SecCertificatePathVCRef path
,
611 SecCertificateRef certificate
) {
612 CFIndex ix
, count
= path
->count
;
613 for (ix
= 0; ix
< count
; ++ix
) {
614 SecCertificateVCRef cvc
= path
->certificates
[ix
];
615 if (CFEqual(cvc
->certificate
, certificate
))
621 /* Return the root certificate for certificatePath. Note that root is just
622 the top of the path as far as it is constructed. It may or may not be
623 trusted or self signed. */
624 SecCertificateRef
SecCertificatePathVCGetRoot(
625 SecCertificatePathVCRef certificatePath
) {
626 return SecCertificatePathVCGetCertificateAtIndex(certificatePath
,
627 SecCertificatePathVCGetCount(certificatePath
) - 1);
630 SecKeyRef
SecCertificatePathVCCopyPublicKeyAtIndex(
631 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
632 SecCertificateRef certificate
=
633 SecCertificatePathVCGetCertificateAtIndex(certificatePath
, ix
);
635 return SecCertificateCopyPublicKey_ios(certificate
);
637 return SecCertificateCopyPublicKey(certificate
);
641 CFArrayRef
SecCertificatePathVCGetUsageConstraintsAtIndex(
642 SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
643 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
644 return cvc
->usageConstraints
;
647 void SecCertificatePathVCSetUsageConstraintsAtIndex(SecCertificatePathVCRef certificatePath
,
648 CFArrayRef newConstraints
, CFIndex ix
) {
649 CFArrayRef emptyArray
= NULL
;
650 if (!newConstraints
) {
651 require_quiet(emptyArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
), exit
);
652 newConstraints
= emptyArray
;
655 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
656 cvc
->usageConstraints
= CFRetainSafe(newConstraints
);
658 CFReleaseNull(emptyArray
);
662 SecPathVerifyStatus
SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath
) {
663 check(certificatePath
);
664 if (!certificatePath
)
665 return kSecPathVerifyFailed
;
667 certificatePath
->lastVerifiedSigner
< certificatePath
->count
- 1;
668 ++certificatePath
->lastVerifiedSigner
) {
669 SecKeyRef issuerKey
=
670 SecCertificatePathVCCopyPublicKeyAtIndex(certificatePath
,
671 certificatePath
->lastVerifiedSigner
+ 1);
673 return kSecPathVerifiesUnknown
;
674 SecCertificateVCRef cvc
= certificatePath
->certificates
[certificatePath
->lastVerifiedSigner
];
675 OSStatus status
= SecCertificateIsSignedBy(cvc
->certificate
,
677 CFRelease(issuerKey
);
679 return kSecPathVerifyFailed
;
683 return kSecPathVerifySuccess
;
686 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
687 __block
bool result
= true;
688 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
689 if (!SecCertificateIsValid(certificate
, verifyTime
)) {
696 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath
) {
697 CFIndex ix
, count
= certificatePath
->count
;
699 if (certificatePath
->hasStrongHashes
) {
703 if (SecCertificatePathVCIsAnchored(certificatePath
)) {
704 /* For anchored paths, don't check the hash algorithm of the anchored cert,
705 * since we already decided to trust it. */
708 for (ix
= 0; ix
< count
; ++ix
) {
709 if (certificatePath
->certificates
[ix
]->isWeakHash
) {
713 certificatePath
->hasStrongHashes
= true;
717 bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath
) {
718 __block CFDictionaryRef keySizes
= NULL
;
719 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
720 __block
bool result
= false;
722 /* RSA key sizes are 2048-bit or larger. EC key sizes are P-224 or larger. */
723 require(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), errOut
);
724 require(ecSize
= CFNumberCreateWithCFIndex(NULL
, 224), errOut
);
725 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
726 const void *values
[] = { rsaSize
, ecSize
};
727 require(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
728 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
729 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
730 if (!SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
)) {
737 CFReleaseSafe(keySizes
);
738 CFReleaseSafe(rsaSize
);
739 CFReleaseSafe(ecSize
);
743 /* Return a score for this certificate chain. */
744 CFIndex
SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
747 /* Paths that don't verify score terribly.c */
748 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
749 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
750 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
754 if (certificatePath
->isAnchored
) {
755 /* Anchored paths for the win! */
759 if (certificatePath
->isSelfSigned
&& (certificatePath
->selfIssued
== certificatePath
->count
- 1)) {
760 /* Chains that terminate in a self-signed certificate are preferred,
761 even if they don't end in an anchor. */
763 /* Shorter chains ending in a self-signed cert are preferred. */
764 score
-= 1 * certificatePath
->count
;
766 /* Longer chains are preferred when the chain doesn't end in a self-signed cert. */
767 score
+= 1 * certificatePath
->count
;
770 if (SecCertificatePathVCIsValid(certificatePath
, verifyTime
)) {
774 if (!SecCertificatePathVCHasWeakHash(certificatePath
)) {
778 if (!SecCertificatePathVCHasWeakKeySize(certificatePath
)) {
785 CFIndex
SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath
) {
786 if (!certificatePath
) { return 0; }
787 return certificatePath
->score
;
790 void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath
, CFIndex score
) {
791 /* We may "score" the same path twice -- if we "accept" a path but then
792 * decide to keep looking for a better one, we we process the same path
793 * again in "reject" which creates a lower score. Don't replace a higher
794 * score with a lower score. Use reset below to post-reject a path. */
795 if (score
> certificatePath
->score
) {
796 certificatePath
->score
= score
;
800 void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath
) {
801 certificatePath
->score
= 0;
804 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
805 if (ix
>= certificatePath
->rvcCount
) {
808 return &((SecRVCRef
)certificatePath
->rvcs
)[ix
];
811 bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath
) {
812 return (bool)certificatePath
->rvcs
;
815 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath
, CFIndex certCount
) {
816 certificatePath
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
817 certificatePath
->rvcCount
= certCount
;
820 /* Return 0 if any certs revocation checking failed, or the earliest date on
821 which one of the used revocation validation tokens (ocsp response or
823 /* This function returns 0 to indicate revocation checking was not completed
824 for this certificate chain, otherwise returns the date at which the first
825 piece of revocation checking info we used expires. */
826 CFAbsoluteTime
SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path
) {
827 CFIndex certIX
, certCount
= path
->count
;
828 CFAbsoluteTime enu
= NULL_TIME
;
829 if (certCount
<= 1 || !path
->rvcs
) {
833 for (certIX
= 0; certIX
< path
->rvcCount
; ++certIX
) {
834 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
835 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
836 if (thisCertNextUpdate
== 0) {
838 /* We allow for CA certs to not be revocation checked if they
839 have no ocspResponders nor CRLDPs to check against, but the leaf
840 must be checked in order for us to claim we did revocation
842 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
843 CFArrayRef ocspResponders
= NULL
;
844 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
846 CFArrayRef crlDPs
= NULL
;
847 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
849 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
851 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
854 /* We can't check this cert so we don't consider it a soft
855 failure that we didn't. Ideally we should support crl
856 checking and remove this workaround, since that more
861 secdebug("rvc", "revocation checking soft failure for cert: %ld",
863 enu
= thisCertNextUpdate
;
866 if (enu
== 0 || thisCertNextUpdate
< enu
) {
867 enu
= thisCertNextUpdate
;
871 secdebug("rvc", "revocation valid until: %lg", enu
);
875 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
877 if (ix
> certificatePath
->count
- 1) { return false; }
878 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
879 return cvc
->require_revocation_response
;
882 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
884 if (ix
> certificatePath
->count
- 1) { return; }
885 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
886 cvc
->require_revocation_response
= true;
889 bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath
) {
890 if (!certificatePath
) { return false; }
891 return certificatePath
->pathValidated
;
894 void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath
) {
895 certificatePath
->pathValidated
= true;
898 bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath
) {
899 if (!certificatePath
) { return false; }
900 return certificatePath
->isEV
;
903 void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath
, bool isEV
) {
904 certificatePath
->isEV
= isEV
;
907 bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath
) {
908 if (!certificatePath
) { return false; }
909 return certificatePath
->certificates
[0]->optionallyEV
;
912 bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath
) {
913 if (!certificatePath
) { return false; }
914 return certificatePath
->isCT
;
917 void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath
, bool isCT
) {
918 certificatePath
->isCT
= isCT
;
921 bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath
) {
922 if (!certificatePath
) { return false; }
923 return certificatePath
->is_allowlisted
;
926 void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath
, bool isAllowlisted
) {
927 certificatePath
->is_allowlisted
= isAllowlisted
;
930 /* MARK: policy_tree path verification */
931 struct policy_tree_add_ctx
{
933 policy_qualifier_t p_q
;
936 /* 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}. */
937 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
938 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
939 policy_set_t policy_set
;
940 for (policy_set
= node
->expected_policy_set
;
942 policy_set
= policy_set
->oid_next
) {
943 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
944 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
951 /* 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}. */
952 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
953 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
954 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
955 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
961 /* Return true iff node has a child with a valid_policy equal to oid. */
962 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
965 for (child
= node
->children
; child
; child
= child
->siblings
) {
966 if (oid_equal(child
->valid_policy
, (*oid
))) {
973 /* 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. */
974 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
975 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
976 policy_set_t policy_set
;
977 bool added_node
= false;
978 for (policy_set
= node
->expected_policy_set
;
980 policy_set
= policy_set
->oid_next
) {
981 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
982 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
989 /* 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. */
990 static bool policy_tree_map_if_match(policy_tree_t node
, void *ctx
) {
991 /* Can't map oidAnyPolicy. */
992 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
995 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
996 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
997 policy_set_t policy_set
= NULL
;
998 /* Generate the policy_set of sdps for matching idp */
999 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1000 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1001 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1002 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1003 p_node
->oid
= mapping
->subjectDomainPolicy
;
1004 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1005 policy_set
= p_node
;
1009 policy_tree_set_expected_policy(node
, policy_set
);
1015 /* 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:
1016 (i) set the valid_policy to ID-P;
1017 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1018 (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. */
1019 static bool policy_tree_map_if_any(policy_tree_t node
, void *ctx
) {
1020 if (!oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1024 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1025 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1026 CFMutableDictionaryRef mappings
= NULL
;
1027 CFDataRef idp
= NULL
;
1028 CFDataRef sdp
= NULL
;
1029 require_quiet(mappings
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1030 &kCFTypeDictionaryValueCallBacks
),
1032 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1033 for (mapping_ix
= 0; mapping_ix
< mapping_count
; mapping_ix
++) {
1034 oid_t issuerDomainPolicy
= pm
->mappings
[mapping_ix
].issuerDomainPolicy
;
1035 oid_t subjectDomainPolicy
= pm
->mappings
[mapping_ix
].subjectDomainPolicy
;
1036 idp
= CFDataCreateWithBytesNoCopy(NULL
, issuerDomainPolicy
.data
, issuerDomainPolicy
.length
, kCFAllocatorNull
);
1037 sdp
= CFDataCreateWithBytesNoCopy(NULL
, subjectDomainPolicy
.data
, subjectDomainPolicy
.length
, kCFAllocatorNull
);
1038 CFMutableArrayRef sdps
= (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
1040 CFArrayAppendValue(sdps
, sdp
);
1042 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1043 &kCFTypeArrayCallBacks
), errOut
);
1044 CFArrayAppendValue(sdps
, sdp
);
1045 CFDictionarySetValue(mappings
, idp
, sdps
);
1052 /* Now we use the dictionary to generate the new nodes */
1053 CFDictionaryForEach(mappings
, ^(const void *key
, const void *value
) {
1054 CFDataRef idp
= key
;
1055 CFArrayRef sdps
= value
;
1057 /* (i) set the valid_policy to ID-P; */
1059 p_oid
.data
= (uint8_t *)CFDataGetBytePtr(idp
);
1060 p_oid
.length
= CFDataGetLength(idp
);
1062 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1063 policy_qualifier_t p_q
= node
->qualifier_set
;
1065 /* (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. */
1066 __block policy_set_t p_expected
= NULL
;
1067 CFArrayForEach(sdps
, ^(const void *value
) {
1068 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*p_expected
));
1069 p_node
->oid
.data
= (void *)CFDataGetBytePtr(value
);
1070 p_node
->oid
.length
= CFDataGetLength(value
);
1071 p_node
->oid_next
= p_expected
? p_expected
: NULL
;
1072 p_expected
= p_node
;
1075 policy_tree_add_sibling(node
, &p_oid
, p_q
, p_expected
);
1077 CFReleaseNull(mappings
);
1081 CFReleaseNull(mappings
);
1087 static bool policy_tree_map_delete_if_match(policy_tree_t node
, void *ctx
) {
1088 /* Can't map oidAnyPolicy. */
1089 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1092 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1093 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1094 /* If this node matches any of the idps, delete it. */
1095 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1096 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1097 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1098 policy_tree_remove_node(&node
);
1105 bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path
, CFIndex ix
) {
1106 /* The SecCertificatePath only tells us the last self-issued cert.
1107 * The chain may have more than one self-issued cert, so we need to
1108 * do the comparison. */
1109 bool result
= false;
1110 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, ix
);
1111 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
1112 CFDataRef subject
= SecCertificateCopyNormalizedSubjectSequence(cert
);
1113 if (issuer
&& subject
&& CFEqual(issuer
, subject
)) {
1116 CFReleaseNull(issuer
);
1117 CFReleaseNull(subject
);
1122 kSecPolicyTreeVerificationUnknown
= 0,
1123 kSecPolicyTreeVerificationFalse
,
1124 kSecPolicyTreeVerificationTrue
,
1127 /* RFC 5280 policy tree processing */
1128 bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path
, bool anchor_trusted
) {
1129 if (!path
) { return false; }
1130 if (path
->policy_tree_verification_result
!= kSecPolicyTreeVerificationUnknown
) {
1131 return (path
->policy_tree_verification_result
== kSecPolicyTreeVerificationTrue
);
1134 /* Path Validation initialization */
1135 bool result
= false;
1136 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationFalse
;
1137 bool initial_policy_mapping_inhibit
= false;
1138 bool initial_explicit_policy
= false;
1139 bool initial_any_policy_inhibit
= false;
1141 SecCertificatePathVCPrunePolicyTree(path
);
1142 path
->policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1144 assert((unsigned long)path
->count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1145 uint32_t n
= (uint32_t)path
->count
;
1146 if (anchor_trusted
) {
1150 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1151 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1152 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1154 SecCertificateRef cert
= NULL
;
1156 for (i
= 1; i
<= n
; ++i
) {
1158 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, n
- i
);
1159 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(path
, n
- i
);
1162 if (path
->policy_tree
) {
1163 const SecCECertificatePolicies
*cp
=
1164 SecCertificateGetCertificatePolicies(cert
);
1165 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1166 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1167 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1168 oid_t p_oid
= policy
->policyIdentifier
;
1169 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1170 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1171 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1172 if (!policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1173 policy_tree_add_if_match
, &ctx
)) {
1174 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1175 policy_tree_add_if_any
, &ctx
);
1179 /* The certificate policies extension includes the policy
1180 anyPolicy with the qualifier set AP-Q and either
1181 (a) inhibit_anyPolicy is greater than 0 or
1182 (b) i < n and the certificate is self-issued. */
1183 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1184 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1185 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1186 oid_t p_oid
= policy
->policyIdentifier
;
1187 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1188 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1189 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1190 policy_tree_add_expected
, (void *)p_q
);
1195 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1198 SecCertificatePathVCPrunePolicyTree(path
);
1202 /* (f) Verify that either explicit_policy is greater than 0 or the
1203 valid_policy_tree is not equal to NULL. */
1204 if (!path
->policy_tree
&& explicit_policy
== 0) {
1205 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1206 secnotice("policy", "policy tree failure on cert %u", n
- i
);
1209 /* If Last Cert in Path */
1213 /* Prepare for Next Cert */
1214 /* (a) verify that anyPolicy does not appear as an
1215 issuerDomainPolicy or a subjectDomainPolicy */
1216 const SecCEPolicyMappings
*pm
= SecCertificateGetPolicyMappings(cert
);
1217 if (pm
&& pm
->present
) {
1218 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1219 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1220 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1221 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1222 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1223 /* Policy mapping uses anyPolicy, illegal. */
1224 secnotice("policy", "policy mapping anyPolicy failure %u", n
- i
);
1230 /* (1) If the policy_mapping variable is greater than 0 */
1231 if (policy_mapping
> 0 && path
->policy_tree
) {
1232 if (!policy_tree_walk_depth(path
->policy_tree
, i
,
1233 policy_tree_map_if_match
, (void *)pm
)) {
1234 /* 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. */
1235 policy_tree_walk_depth(path
->policy_tree
, i
, policy_tree_map_if_any
, (void *)pm
);
1237 } else if (path
->policy_tree
) {
1238 /* (i) delete each node of depth i in the valid_policy_tree
1239 where ID-P is the valid_policy. */
1240 policy_tree_walk_depth(path
->policy_tree
, i
,
1241 policy_tree_map_delete_if_match
, (void *)pm
);
1242 /* (ii) If there is a node in the valid_policy_tree of depth
1243 i-1 or less without any child nodes, delete that
1244 node. Repeat this step until there are no nodes of
1245 depth i-1 or less without children. */
1246 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1251 if (!is_self_issued
) {
1252 if (explicit_policy
)
1256 if (inhibit_any_policy
)
1257 inhibit_any_policy
--;
1260 const SecCEPolicyConstraints
*pc
=
1261 SecCertificateGetPolicyConstraints(cert
);
1263 if (pc
->requireExplicitPolicyPresent
1264 && pc
->requireExplicitPolicy
< explicit_policy
) {
1265 explicit_policy
= pc
->requireExplicitPolicy
;
1267 if (pc
->inhibitPolicyMappingPresent
1268 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1269 policy_mapping
= pc
->inhibitPolicyMapping
;
1273 const SecCEInhibitAnyPolicy
*iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1274 if (iap
&& iap
->skipCerts
< inhibit_any_policy
) {
1275 inhibit_any_policy
= iap
->skipCerts
;
1278 } /* end of path for loop */
1281 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
1283 if (explicit_policy
)
1286 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1288 if (pc
->requireExplicitPolicyPresent
1289 && pc
->requireExplicitPolicy
== 0) {
1290 explicit_policy
= 0;
1294 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1296 if (path
->policy_tree
) {
1297 #if !defined(NDEBUG)
1298 policy_tree_dump(path
->policy_tree
);
1301 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1304 /* If either (1) the value of explicit_policy variable is greater than
1305 zero or (2) the valid_policy_tree is not NULL, then path processing
1307 if (!path
->policy_tree
&& explicit_policy
== 0) {
1308 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1309 secnotice("policy", "policy tree failure on leaf");
1313 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationTrue
;