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 /* Is the the issuer of the last cert a subject of a previous cert in the chain.See <rdar://33136765>. */
687 bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path
) {
688 bool isCircle
= false;
689 CFDataRef issuer
= SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path
));
690 if (!issuer
) { return isCircle
; }
691 CFIndex ix
= path
->count
- 2;
692 for (; ix
>= 0; ix
--) {
693 SecCertificateVCRef cvc
= path
->certificates
[ix
];
694 CFDataRef subject
= SecCertificateGetNormalizedSubjectContent(cvc
->certificate
);
695 if (subject
&& CFEqual(issuer
, subject
)) {
703 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
704 __block
bool result
= true;
705 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
706 if (!SecCertificateIsValid(certificate
, verifyTime
)) {
713 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath
) {
714 CFIndex ix
, count
= certificatePath
->count
;
716 if (certificatePath
->hasStrongHashes
) {
720 if (SecCertificatePathVCIsAnchored(certificatePath
)) {
721 /* For anchored paths, don't check the hash algorithm of the anchored cert,
722 * since we already decided to trust it. */
725 for (ix
= 0; ix
< count
; ++ix
) {
726 if (certificatePath
->certificates
[ix
]->isWeakHash
) {
730 certificatePath
->hasStrongHashes
= true;
734 bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath
) {
735 __block CFDictionaryRef keySizes
= NULL
;
736 CFNumberRef rsaSize
= NULL
, ecSize
= NULL
;
737 __block
bool result
= false;
739 /* RSA key sizes are 2048-bit or larger. EC key sizes are P-224 or larger. */
740 require(rsaSize
= CFNumberCreateWithCFIndex(NULL
, 2048), errOut
);
741 require(ecSize
= CFNumberCreateWithCFIndex(NULL
, 224), errOut
);
742 const void *keys
[] = { kSecAttrKeyTypeRSA
, kSecAttrKeyTypeEC
};
743 const void *values
[] = { rsaSize
, ecSize
};
744 require(keySizes
= CFDictionaryCreate(NULL
, keys
, values
, 2,
745 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
), errOut
);
746 SecCertificatePathVCForEachCertificate(certificatePath
, ^(SecCertificateRef certificate
, bool *stop
) {
747 if (!SecCertificateIsAtLeastMinKeySize(certificate
, keySizes
)) {
754 CFReleaseSafe(keySizes
);
755 CFReleaseSafe(rsaSize
);
756 CFReleaseSafe(ecSize
);
760 /* Return a score for this certificate chain. */
761 CFIndex
SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath
, CFAbsoluteTime verifyTime
) {
764 /* Paths that don't verify score terribly.c */
765 if (certificatePath
->lastVerifiedSigner
!= certificatePath
->count
- 1) {
766 secdebug("trust", "lvs: %" PRIdCFIndex
" count: %" PRIdCFIndex
,
767 certificatePath
->lastVerifiedSigner
, certificatePath
->count
);
771 if (certificatePath
->isAnchored
) {
772 /* Anchored paths for the win! */
776 if (certificatePath
->isSelfSigned
&& (certificatePath
->selfIssued
== certificatePath
->count
- 1)) {
777 /* Chains that terminate in a self-signed certificate are preferred,
778 even if they don't end in an anchor. */
780 /* Shorter chains ending in a self-signed cert are preferred. */
781 score
-= 1 * certificatePath
->count
;
783 /* Longer chains are preferred when the chain doesn't end in a self-signed cert. */
784 score
+= 1 * certificatePath
->count
;
787 if (SecCertificatePathVCIsValid(certificatePath
, verifyTime
)) {
791 if (!SecCertificatePathVCHasWeakHash(certificatePath
)) {
795 if (!SecCertificatePathVCHasWeakKeySize(certificatePath
)) {
802 CFIndex
SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath
) {
803 if (!certificatePath
) { return 0; }
804 return certificatePath
->score
;
807 void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath
, CFIndex score
) {
808 /* We may "score" the same path twice -- if we "accept" a path but then
809 * decide to keep looking for a better one, we we process the same path
810 * again in "reject" which creates a lower score. Don't replace a higher
811 * score with a lower score. Use reset below to post-reject a path. */
812 if (score
> certificatePath
->score
) {
813 certificatePath
->score
= score
;
817 void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath
) {
818 certificatePath
->score
= 0;
821 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath
, CFIndex ix
) {
822 if (ix
>= certificatePath
->rvcCount
) {
825 return &((SecRVCRef
)certificatePath
->rvcs
)[ix
];
828 bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath
) {
829 return (bool)certificatePath
->rvcs
;
832 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath
, CFIndex certCount
) {
833 certificatePath
->rvcs
= calloc(sizeof(struct OpaqueSecRVC
), certCount
);
834 certificatePath
->rvcCount
= certCount
;
837 /* Return 0 if any certs revocation checking failed, or the earliest date on
838 which one of the used revocation validation tokens (ocsp response or
840 /* This function returns 0 to indicate revocation checking was not completed
841 for this certificate chain, otherwise returns the date at which the first
842 piece of revocation checking info we used expires. */
843 CFAbsoluteTime
SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path
) {
844 CFIndex certIX
, certCount
= path
->count
;
845 CFAbsoluteTime enu
= NULL_TIME
;
846 if (certCount
<= 1 || !path
->rvcs
) {
850 for (certIX
= 0; certIX
< path
->rvcCount
; ++certIX
) {
851 SecRVCRef rvc
= &((SecRVCRef
)path
->rvcs
)[certIX
];
852 CFAbsoluteTime thisCertNextUpdate
= SecRVCGetEarliestNextUpdate(rvc
);
853 if (thisCertNextUpdate
== 0) {
855 /* We allow for CA certs to not be revocation checked if they
856 have no ocspResponders nor CRLDPs to check against, but the leaf
857 must be checked in order for us to claim we did revocation
859 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, rvc
->certIX
);
860 CFArrayRef ocspResponders
= NULL
;
861 ocspResponders
= SecCertificateGetOCSPResponders(cert
);
863 CFArrayRef crlDPs
= NULL
;
864 crlDPs
= SecCertificateGetCRLDistributionPoints(cert
);
866 if ((!ocspResponders
|| CFArrayGetCount(ocspResponders
) == 0)
868 && (!crlDPs
|| CFArrayGetCount(crlDPs
) == 0)
871 /* We can't check this cert so we don't consider it a soft
872 failure that we didn't. Ideally we should support crl
873 checking and remove this workaround, since that more
878 secdebug("rvc", "revocation checking soft failure for cert: %ld",
880 enu
= thisCertNextUpdate
;
883 if (enu
== 0 || thisCertNextUpdate
< enu
) {
884 enu
= thisCertNextUpdate
;
888 secdebug("rvc", "revocation valid until: %lg", enu
);
892 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
894 if (ix
> certificatePath
->count
- 1) { return false; }
895 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
896 return cvc
->require_revocation_response
;
899 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath
,
901 if (ix
> certificatePath
->count
- 1) { return; }
902 SecCertificateVCRef cvc
= certificatePath
->certificates
[ix
];
903 cvc
->require_revocation_response
= true;
906 bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath
) {
907 if (!certificatePath
) { return false; }
908 return certificatePath
->pathValidated
;
911 void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath
) {
912 certificatePath
->pathValidated
= true;
915 bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath
) {
916 if (!certificatePath
) { return false; }
917 return certificatePath
->isEV
;
920 void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath
, bool isEV
) {
921 certificatePath
->isEV
= isEV
;
924 bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath
) {
925 if (!certificatePath
) { return false; }
926 return certificatePath
->certificates
[0]->optionallyEV
;
929 bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath
) {
930 if (!certificatePath
) { return false; }
931 return certificatePath
->isCT
;
934 void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath
, bool isCT
) {
935 certificatePath
->isCT
= isCT
;
938 bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath
) {
939 if (!certificatePath
) { return false; }
940 return certificatePath
->is_allowlisted
;
943 void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath
, bool isAllowlisted
) {
944 certificatePath
->is_allowlisted
= isAllowlisted
;
947 /* MARK: policy_tree path verification */
948 struct policy_tree_add_ctx
{
950 policy_qualifier_t p_q
;
953 /* 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}. */
954 static bool policy_tree_add_if_match(policy_tree_t node
, void *ctx
) {
955 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
956 policy_set_t policy_set
;
957 for (policy_set
= node
->expected_policy_set
;
959 policy_set
= policy_set
->oid_next
) {
960 if (oid_equal(policy_set
->oid
, info
->p_oid
)) {
961 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
968 /* 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}. */
969 static bool policy_tree_add_if_any(policy_tree_t node
, void *ctx
) {
970 struct policy_tree_add_ctx
*info
= (struct policy_tree_add_ctx
*)ctx
;
971 if (oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
972 policy_tree_add_child(node
, &info
->p_oid
, info
->p_q
);
978 /* Return true iff node has a child with a valid_policy equal to oid. */
979 static bool policy_tree_has_child_with_oid(policy_tree_t node
,
982 for (child
= node
->children
; child
; child
= child
->siblings
) {
983 if (oid_equal(child
->valid_policy
, (*oid
))) {
990 /* 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. */
991 static bool policy_tree_add_expected(policy_tree_t node
, void *ctx
) {
992 policy_qualifier_t p_q
= (policy_qualifier_t
)ctx
;
993 policy_set_t policy_set
;
994 bool added_node
= false;
995 for (policy_set
= node
->expected_policy_set
;
997 policy_set
= policy_set
->oid_next
) {
998 if (!policy_tree_has_child_with_oid(node
, &policy_set
->oid
)) {
999 policy_tree_add_child(node
, &policy_set
->oid
, p_q
);
1006 /* 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. */
1007 static bool policy_tree_map_if_match(policy_tree_t node
, void *ctx
) {
1008 /* Can't map oidAnyPolicy. */
1009 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1012 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1013 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1014 policy_set_t policy_set
= NULL
;
1015 /* Generate the policy_set of sdps for matching idp */
1016 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1017 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1018 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1019 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*policy_set
));
1020 p_node
->oid
= mapping
->subjectDomainPolicy
;
1021 p_node
->oid_next
= policy_set
? policy_set
: NULL
;
1022 policy_set
= p_node
;
1026 policy_tree_set_expected_policy(node
, policy_set
);
1032 /* 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:
1033 (i) set the valid_policy to ID-P;
1034 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1035 (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. */
1036 static bool policy_tree_map_if_any(policy_tree_t node
, void *ctx
) {
1037 if (!oid_equal(node
->valid_policy
, oidAnyPolicy
)) {
1041 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1042 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1043 CFMutableDictionaryRef mappings
= NULL
;
1044 CFDataRef idp
= NULL
;
1045 CFDataRef sdp
= NULL
;
1046 require_quiet(mappings
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
1047 &kCFTypeDictionaryValueCallBacks
),
1049 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1050 for (mapping_ix
= 0; mapping_ix
< mapping_count
; mapping_ix
++) {
1051 oid_t issuerDomainPolicy
= pm
->mappings
[mapping_ix
].issuerDomainPolicy
;
1052 oid_t subjectDomainPolicy
= pm
->mappings
[mapping_ix
].subjectDomainPolicy
;
1053 idp
= CFDataCreateWithBytesNoCopy(NULL
, issuerDomainPolicy
.data
, issuerDomainPolicy
.length
, kCFAllocatorNull
);
1054 sdp
= CFDataCreateWithBytesNoCopy(NULL
, subjectDomainPolicy
.data
, subjectDomainPolicy
.length
, kCFAllocatorNull
);
1055 CFMutableArrayRef sdps
= (CFMutableArrayRef
)CFDictionaryGetValue(mappings
, idp
);
1057 CFArrayAppendValue(sdps
, sdp
);
1059 require_quiet(sdps
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1060 &kCFTypeArrayCallBacks
), errOut
);
1061 CFArrayAppendValue(sdps
, sdp
);
1062 CFDictionarySetValue(mappings
, idp
, sdps
);
1069 /* Now we use the dictionary to generate the new nodes */
1070 CFDictionaryForEach(mappings
, ^(const void *key
, const void *value
) {
1071 CFDataRef idp
= key
;
1072 CFArrayRef sdps
= value
;
1074 /* (i) set the valid_policy to ID-P; */
1076 p_oid
.data
= (uint8_t *)CFDataGetBytePtr(idp
);
1077 p_oid
.length
= CFDataGetLength(idp
);
1079 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1080 policy_qualifier_t p_q
= node
->qualifier_set
;
1082 /* (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. */
1083 __block policy_set_t p_expected
= NULL
;
1084 CFArrayForEach(sdps
, ^(const void *value
) {
1085 policy_set_t p_node
= (policy_set_t
)malloc(sizeof(*p_expected
));
1086 p_node
->oid
.data
= (void *)CFDataGetBytePtr(value
);
1087 p_node
->oid
.length
= CFDataGetLength(value
);
1088 p_node
->oid_next
= p_expected
? p_expected
: NULL
;
1089 p_expected
= p_node
;
1092 policy_tree_add_sibling(node
, &p_oid
, p_q
, p_expected
);
1094 CFReleaseNull(mappings
);
1098 CFReleaseNull(mappings
);
1104 static bool policy_tree_map_delete_if_match(policy_tree_t node
, void *ctx
) {
1105 /* Can't map oidAnyPolicy. */
1106 if (oid_equal(node
->valid_policy
, oidAnyPolicy
))
1109 const SecCEPolicyMappings
*pm
= (const SecCEPolicyMappings
*)ctx
;
1110 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1111 /* If this node matches any of the idps, delete it. */
1112 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1113 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1114 if (oid_equal(node
->valid_policy
, mapping
->issuerDomainPolicy
)) {
1115 policy_tree_remove_node(&node
);
1122 bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path
, CFIndex ix
) {
1123 /* The SecCertificatePath only tells us the last self-issued cert.
1124 * The chain may have more than one self-issued cert, so we need to
1125 * do the comparison. */
1126 bool result
= false;
1127 SecCertificateRef cert
= SecCertificatePathVCGetCertificateAtIndex(path
, ix
);
1128 CFDataRef issuer
= SecCertificateCopyNormalizedIssuerSequence(cert
);
1129 CFDataRef subject
= SecCertificateCopyNormalizedSubjectSequence(cert
);
1130 if (issuer
&& subject
&& CFEqual(issuer
, subject
)) {
1133 CFReleaseNull(issuer
);
1134 CFReleaseNull(subject
);
1139 kSecPolicyTreeVerificationUnknown
= 0,
1140 kSecPolicyTreeVerificationFalse
,
1141 kSecPolicyTreeVerificationTrue
,
1144 /* RFC 5280 policy tree processing */
1145 bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path
, bool anchor_trusted
) {
1146 if (!path
) { return false; }
1147 if (path
->policy_tree_verification_result
!= kSecPolicyTreeVerificationUnknown
) {
1148 return (path
->policy_tree_verification_result
== kSecPolicyTreeVerificationTrue
);
1151 /* Path Validation initialization */
1152 bool result
= false;
1153 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationFalse
;
1154 bool initial_policy_mapping_inhibit
= false;
1155 bool initial_explicit_policy
= false;
1156 bool initial_any_policy_inhibit
= false;
1158 SecCertificatePathVCPrunePolicyTree(path
);
1159 path
->policy_tree
= policy_tree_create(&oidAnyPolicy
, NULL
);
1161 assert((unsigned long)path
->count
<=UINT32_MAX
); /* Debug check. Correct as long as CFIndex is long */
1162 uint32_t n
= (uint32_t)path
->count
;
1163 if (anchor_trusted
) {
1167 uint32_t explicit_policy
= initial_explicit_policy
? 0 : n
+ 1;
1168 uint32_t inhibit_any_policy
= initial_any_policy_inhibit
? 0 : n
+ 1;
1169 uint32_t policy_mapping
= initial_policy_mapping_inhibit
? 0 : n
+ 1;
1171 SecCertificateRef cert
= NULL
;
1173 for (i
= 1; i
<= n
; ++i
) {
1175 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, n
- i
);
1176 bool is_self_issued
= SecCertificatePathVCIsCertificateAtIndexSelfIssued(path
, n
- i
);
1179 if (path
->policy_tree
) {
1180 const SecCECertificatePolicies
*cp
=
1181 SecCertificateGetCertificatePolicies(cert
);
1182 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
1183 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1184 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1185 oid_t p_oid
= policy
->policyIdentifier
;
1186 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1187 struct policy_tree_add_ctx ctx
= { p_oid
, p_q
};
1188 if (!oid_equal(p_oid
, oidAnyPolicy
)) {
1189 if (!policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1190 policy_tree_add_if_match
, &ctx
)) {
1191 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1192 policy_tree_add_if_any
, &ctx
);
1196 /* The certificate policies extension includes the policy
1197 anyPolicy with the qualifier set AP-Q and either
1198 (a) inhibit_anyPolicy is greater than 0 or
1199 (b) i < n and the certificate is self-issued. */
1200 if (inhibit_any_policy
> 0 || (i
< n
&& is_self_issued
)) {
1201 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
1202 const SecCEPolicyInformation
*policy
= &cp
->policies
[policy_ix
];
1203 oid_t p_oid
= policy
->policyIdentifier
;
1204 policy_qualifier_t p_q
= &policy
->policyQualifiers
;
1205 if (oid_equal(p_oid
, oidAnyPolicy
)) {
1206 policy_tree_walk_depth(path
->policy_tree
, i
- 1,
1207 policy_tree_add_expected
, (void *)p_q
);
1212 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1215 SecCertificatePathVCPrunePolicyTree(path
);
1219 /* (f) Verify that either explicit_policy is greater than 0 or the
1220 valid_policy_tree is not equal to NULL. */
1221 if (!path
->policy_tree
&& explicit_policy
== 0) {
1222 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1223 secnotice("policy", "policy tree failure on cert %u", n
- i
);
1226 /* If Last Cert in Path */
1230 /* Prepare for Next Cert */
1231 /* (a) verify that anyPolicy does not appear as an
1232 issuerDomainPolicy or a subjectDomainPolicy */
1233 const SecCEPolicyMappings
*pm
= SecCertificateGetPolicyMappings(cert
);
1234 if (pm
&& pm
->present
) {
1235 size_t mapping_ix
, mapping_count
= pm
->numMappings
;
1236 for (mapping_ix
= 0; mapping_ix
< mapping_count
; ++mapping_ix
) {
1237 const SecCEPolicyMapping
*mapping
= &pm
->mappings
[mapping_ix
];
1238 if (oid_equal(mapping
->issuerDomainPolicy
, oidAnyPolicy
)
1239 || oid_equal(mapping
->subjectDomainPolicy
, oidAnyPolicy
)) {
1240 /* Policy mapping uses anyPolicy, illegal. */
1241 secnotice("policy", "policy mapping anyPolicy failure %u", n
- i
);
1247 /* (1) If the policy_mapping variable is greater than 0 */
1248 if (policy_mapping
> 0 && path
->policy_tree
) {
1249 if (!policy_tree_walk_depth(path
->policy_tree
, i
,
1250 policy_tree_map_if_match
, (void *)pm
)) {
1251 /* 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. */
1252 policy_tree_walk_depth(path
->policy_tree
, i
, policy_tree_map_if_any
, (void *)pm
);
1254 } else if (path
->policy_tree
) {
1255 /* (i) delete each node of depth i in the valid_policy_tree
1256 where ID-P is the valid_policy. */
1257 policy_tree_walk_depth(path
->policy_tree
, i
,
1258 policy_tree_map_delete_if_match
, (void *)pm
);
1259 /* (ii) If there is a node in the valid_policy_tree of depth
1260 i-1 or less without any child nodes, delete that
1261 node. Repeat this step until there are no nodes of
1262 depth i-1 or less without children. */
1263 policy_tree_prune_childless(&path
->policy_tree
, i
- 1);
1268 if (!is_self_issued
) {
1269 if (explicit_policy
)
1273 if (inhibit_any_policy
)
1274 inhibit_any_policy
--;
1277 const SecCEPolicyConstraints
*pc
=
1278 SecCertificateGetPolicyConstraints(cert
);
1280 if (pc
->requireExplicitPolicyPresent
1281 && pc
->requireExplicitPolicy
< explicit_policy
) {
1282 explicit_policy
= pc
->requireExplicitPolicy
;
1284 if (pc
->inhibitPolicyMappingPresent
1285 && pc
->inhibitPolicyMapping
< policy_mapping
) {
1286 policy_mapping
= pc
->inhibitPolicyMapping
;
1290 const SecCEInhibitAnyPolicy
*iap
= SecCertificateGetInhibitAnyPolicySkipCerts(cert
);
1291 if (iap
&& iap
->skipCerts
< inhibit_any_policy
) {
1292 inhibit_any_policy
= iap
->skipCerts
;
1295 } /* end of path for loop */
1298 cert
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
1300 if (explicit_policy
)
1303 const SecCEPolicyConstraints
*pc
= SecCertificateGetPolicyConstraints(cert
);
1305 if (pc
->requireExplicitPolicyPresent
1306 && pc
->requireExplicitPolicy
== 0) {
1307 explicit_policy
= 0;
1311 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1313 if (path
->policy_tree
) {
1314 #if !defined(NDEBUG)
1315 policy_tree_dump(path
->policy_tree
);
1318 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1321 /* If either (1) the value of explicit_policy variable is greater than
1322 zero or (2) the valid_policy_tree is not NULL, then path processing
1324 if (!path
->policy_tree
&& explicit_policy
== 0) {
1325 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1326 secnotice("policy", "policy tree failure on leaf");
1330 path
->policy_tree_verification_result
= kSecPolicyTreeVerificationTrue
;