]> git.saurik.com Git - apple/security.git/blob - trust/trustd/SecCertificateServer.c
Security-59754.80.3.tar.gz
[apple/security.git] / trust / trustd / SecCertificateServer.c
1 /*
2 * Copyright (c) 2017-2020 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecCertificateServer.c - SecCertificate and SecCertificatePathVC types
26 * with additonal validation context.
27 */
28
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <AssertMacros.h>
31
32 #include <libDER/libDER.h>
33 #include <libDER/oids.h>
34
35 #include <Security/SecCertificate.h>
36 #include <Security/SecCertificatePriv.h>
37 #include <Security/SecCertificateInternal.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecInternal.h>
40 #include <Security/SecTrustSettingsPriv.h>
41
42 #include <utilities/SecIOFormat.h>
43 #include <utilities/SecCFError.h>
44 #include <utilities/SecCFWrappers.h>
45 #include <utilities/debugging.h>
46
47 #include "trust/trustd/policytree.h"
48 #include "trust/trustd/SecPolicyServer.h"
49 #include "trust/trustd/SecCertificateServer.h"
50 #include "trust/trustd/SecRevocationServer.h"
51 #include "trust/trustd/SecTrustStoreServer.h"
52
53 // MARK: -
54 // MARK: SecCertificateVC
55 /********************************************************
56 ************* SecCertificateVC object ***************
57 ********************************************************/
58
59 struct SecCertificateVC {
60 CFRuntimeBase _base;
61 SecCertificateRef certificate;
62 CFArrayRef usageConstraints;
63 CFNumberRef revocationReason;
64 bool optionallyEV;
65 bool isWeakHash;
66 bool require_revocation_response;
67 };
68 CFGiblisWithHashFor(SecCertificateVC)
69
70 static void SecCertificateVCDestroy(CFTypeRef cf) {
71 SecCertificateVCRef cvc = (SecCertificateVCRef) cf;
72 CFReleaseNull(cvc->certificate);
73 CFReleaseNull(cvc->usageConstraints);
74 CFReleaseNull(cvc->revocationReason);
75 }
76
77 static Boolean SecCertificateVCCompare(CFTypeRef cf1, CFTypeRef cf2) {
78 SecCertificateVCRef cv1 = (SecCertificateVCRef) cf1;
79 SecCertificateVCRef cv2 = (SecCertificateVCRef) cf2;
80 if (!CFEqual(cv1->certificate, cv2->certificate)) {
81 return false;
82 }
83 /* CertificateVCs are the same if either does not have usage constraints. */
84 if (cv1->usageConstraints && cv2->usageConstraints &&
85 !CFEqual(cv1->usageConstraints, cv2->usageConstraints)) {
86 return false;
87 }
88
89 return true;
90 }
91
92 static CFHashCode SecCertificateVCHash(CFTypeRef cf) {
93 SecCertificateVCRef cvc = (SecCertificateVCRef) cf;
94 CFHashCode hashCode = 0;
95 hashCode += CFHash(cvc->certificate);
96 if (cvc->usageConstraints) {
97 hashCode += CFHash(cvc->usageConstraints);
98 }
99 return hashCode;
100 }
101
102 static CFStringRef SecCertificateVCCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
103 SecCertificateVCRef cvc = (SecCertificateVCRef)cf;
104 return CFCopyDescription(cvc->certificate);
105 }
106
107 static bool SecCertificateVCCouldBeEV(SecCertificateRef certificate) {
108 CFMutableDictionaryRef keySizes = NULL;
109 CFNumberRef rsaSize = NULL, ecSize = NULL;
110 bool isEV = false;
111
112 /* 3. Subscriber Certificate. */
113
114 /* (a) certificate Policies */
115 const SecCECertificatePolicies *cp;
116 cp = SecCertificateGetCertificatePolicies(certificate);
117 require_quiet(cp && cp->numPolicies > 0, notEV);
118 /* Now find at least one policy in here that has a qualifierID of id-qt 2
119 and a policyQualifier that is a URI to the CPS and an EV policy OID. */
120 uint32_t ix = 0;
121 bool found_ev_anchor_for_leaf_policy = false;
122 for (ix = 0; ix < cp->numPolicies; ++ix) {
123 if (SecPolicyIsEVPolicy(&cp->policies[ix].policyIdentifier)) {
124 found_ev_anchor_for_leaf_policy = true;
125 }
126 }
127 require_quiet(found_ev_anchor_for_leaf_policy, notEV);
128
129 /* (b) cRLDistributionPoint
130 (c) authorityInformationAccess
131 BRv1.3.4: MUST be present with OCSP Responder unless stapled response.
132 */
133
134 /* (d) basicConstraints
135 If present, the cA field MUST be set false. */
136 const SecCEBasicConstraints *bc = SecCertificateGetBasicConstraints(certificate);
137 if (bc) {
138 require_action_quiet(bc->isCA == false, notEV,
139 secnotice("ev", "Leaf has invalid basic constraints"));
140 }
141
142 /* (e) keyUsage. */
143 SecKeyUsage ku = SecCertificateGetKeyUsage(certificate);
144 if (ku) {
145 require_action_quiet((ku & (kSecKeyUsageKeyCertSign | kSecKeyUsageCRLSign)) == 0, notEV,
146 secnotice("ev", "Leaf has invalid key usage %u", ku));
147 }
148
149 #if 0
150 /* The EV Cert Spec errata specifies this, though this is a check for SSL
151 not specifically EV. */
152
153 /* (e) extKeyUsage
154
155 Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. Other values SHOULD NOT be present. */
156 SecCertificateCopyExtendedKeyUsage(certificate);
157 #endif
158
159 /* 6.1.5 Key Sizes */
160 CFAbsoluteTime jan2014 = 410227200;
161 require_quiet(ecSize = CFNumberCreateWithCFIndex(NULL, 256), notEV);
162 require_quiet(keySizes = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks,
163 &kCFTypeDictionaryValueCallBacks), notEV);
164 CFDictionaryAddValue(keySizes, kSecAttrKeyTypeEC, ecSize);
165 if (SecCertificateNotValidBefore(certificate) < jan2014) {
166 /* At least RSA 1024 or ECC NIST P-256. */
167 require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 1024), notEV);
168 CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
169 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
170 secnotice("ev", "Leaf's public key is too small for issuance before 2014"));
171 } else {
172 /* At least RSA 2028 or ECC NIST P-256. */
173 require_quiet(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), notEV);
174 CFDictionaryAddValue(keySizes, kSecAttrKeyTypeRSA, rsaSize);
175 require_action_quiet(SecCertificateIsAtLeastMinKeySize(certificate, keySizes), notEV,
176 secnotice("ev", "Leaf's public key is too small for issuance after 2013"));
177 }
178
179 /* 6.3.2 Validity Periods */
180 // Will be checked by the policy server (see SecPolicyCheckValidityPeriodMaximums)
181
182 /* 7.1.3 Algorithm Object Identifiers */
183 CFAbsoluteTime jan2016 = 473299200;
184 if (SecCertificateNotValidBefore(certificate) > jan2016) {
185 /* SHA-2 only */
186 require_action_quiet(SecCertificateGetSignatureHashAlgorithm(certificate) > kSecSignatureHashAlgorithmSHA1,
187 notEV, secnotice("ev", "Leaf was issued with SHA-1 after 2015"));
188 }
189
190 isEV = true;
191
192 notEV:
193 CFReleaseNull(rsaSize);
194 CFReleaseNull(ecSize);
195 CFReleaseNull(keySizes);
196 return isEV;
197 }
198
199
200 SecCertificateVCRef SecCertificateVCCreate(SecCertificateRef certificate, CFArrayRef usageConstraints) {
201 if (!certificate) { return NULL; }
202 CFIndex size = sizeof(struct SecCertificateVC);
203 SecCertificateVCRef result =
204 (SecCertificateVCRef)_CFRuntimeCreateInstance(kCFAllocatorDefault,
205 SecCertificateVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
206 if (!result)
207 return NULL;
208 result->certificate = CFRetainSafe(certificate);
209 result->isWeakHash = SecCertificateIsWeakHash(certificate);
210 result->optionallyEV = SecCertificateVCCouldBeEV(certificate);
211
212 CFArrayRef emptyArray = NULL;
213 if (!usageConstraints) {
214 require_action_quiet(emptyArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks), exit, CFReleaseNull(result));
215 usageConstraints = emptyArray;
216 }
217 result->usageConstraints = CFRetainSafe(usageConstraints);
218 exit:
219 CFReleaseNull(emptyArray);
220 return result;
221 }
222
223 // MARK: -
224 // MARK: SecCertificatePathVC
225 /********************************************************
226 ************* SecCertificatePathVC object ***************
227 ********************************************************/
228 struct SecCertificatePathVC {
229 CFRuntimeBase _base;
230 CFIndex count;
231
232 /* Index of next parent source to search for parents. */
233 CFIndex nextParentSource;
234
235 /* Index of last certificate in chain whose signature has been verified.
236 0 means nothing has been checked. 1 means the leaf has been verified
237 against its issuer, etc. */
238 CFIndex lastVerifiedSigner;
239
240 /* Index of first self issued certificate in the chain. -1 mean there is
241 none. 0 means the leaf is self signed. */
242 CFIndex selfIssued;
243
244 /* True iff cert at index selfIssued does in fact self verify. */
245 bool isSelfSigned;
246
247 /* True if the root of this path is an anchor. Trustedness of the
248 * anchor is determined by the PVC. */
249 bool isAnchored;
250
251 policy_tree_t policy_tree;
252 uint8_t policy_tree_verification_result;
253
254 bool isEV;
255 bool isCT;
256 bool is_allowlisted;
257 bool hasStrongHashes;
258
259 /* revocationCAIndex contains the index of the last CA with a SPKI-based
260 * revocation match, or -1 (kCFNotFound) if no CA match was found.
261 * A value of 0 means the path has not yet been checked for a match. */
262 CFIndex revocationCAIndex;
263
264 void * rvcs;
265 CFIndex rvcCount;
266
267 /* This is the score of the path after determining acceptance. */
268 CFIndex score;
269
270 bool pathValidated;
271
272 /* If checkedIssuers is true, then the value of unknownCAIndex contains
273 * the index of the first CA which violates known-only constraints, or
274 * -1 if all CA certificates are either known or not constrained. */
275 bool checkedIssuers;
276 CFIndex unknownCAIndex;
277
278 /* Enumerated value to determine whether CT is required for the leaf
279 * certificate (because a CA in the path has a require-ct constraint).
280 * If non-zero, CT is required; value indicates overridable status. */
281 SecPathCTPolicy requiresCT;
282
283 /* Issuance time, as determined by earliest SCT timestamp for leaf. */
284 CFAbsoluteTime issuanceTime;
285
286 SecCertificateVCRef certificates[];
287 };
288 CFGiblisWithHashFor(SecCertificatePathVC)
289
290 static void SecCertificatePathVCPrunePolicyTree(SecCertificatePathVCRef certificatePath) {
291 if (certificatePath->policy_tree) {
292 policy_tree_prune(&certificatePath->policy_tree);
293 }
294 }
295
296 void SecCertificatePathVCDeleteRVCs(SecCertificatePathVCRef path) {
297 if (path->rvcs) {
298 CFIndex certIX, certCount = path->rvcCount;
299 for (certIX = 0; certIX < certCount; ++certIX) {
300 SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
301 SecRVCDelete(rvc);
302 }
303 free(path->rvcs);
304 path->rvcs = NULL;
305 }
306 }
307
308 static void SecCertificatePathVCDestroy(CFTypeRef cf) {
309 SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
310 CFIndex ix;
311 SecCertificatePathVCDeleteRVCs(certificatePath);
312 SecCertificatePathVCPrunePolicyTree(certificatePath);
313 for (ix = 0; ix < certificatePath->count; ++ix) {
314 CFReleaseNull(certificatePath->certificates[ix]);
315 }
316 }
317
318 static Boolean SecCertificatePathVCCompare(CFTypeRef cf1, CFTypeRef cf2) {
319 SecCertificatePathVCRef cp1 = (SecCertificatePathVCRef) cf1;
320 SecCertificatePathVCRef cp2 = (SecCertificatePathVCRef) cf2;
321 if (cp1->count != cp2->count)
322 return false;
323 CFIndex ix;
324 for (ix = 0; ix < cp1->count; ++ix) {
325 if (!CFEqual(cp1->certificates[ix], cp2->certificates[ix]))
326 return false;
327 }
328
329 return true;
330 }
331
332 static CFHashCode SecCertificatePathVCHash(CFTypeRef cf) {
333 SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
334 CFHashCode hashCode = 0;
335 CFIndex ix;
336 for (ix = 0; ix < certificatePath->count; ++ix) {
337 hashCode += CFHash(certificatePath->certificates[ix]);
338 }
339 return hashCode;
340 }
341
342 static CFStringRef SecCertificatePathVCCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) {
343 SecCertificatePathVCRef certificatePath = (SecCertificatePathVCRef) cf;
344 CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0);
345 CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf));
346 CFStringAppendFormat(desc, NULL,
347 CFSTR("<%@ certs: "), typeStr);
348 CFRelease(typeStr);
349 CFIndex ix;
350 for (ix = 0; ix < certificatePath->count; ++ix) {
351 if (ix > 0) {
352 CFStringAppend(desc, CFSTR(", "));
353 }
354 CFStringRef str = CFCopyDescription(certificatePath->certificates[ix]);
355 CFStringAppend(desc, str);
356 CFRelease(str);
357 }
358 CFStringAppend(desc, CFSTR(" >"));
359
360 return desc;
361 }
362
363 /* Create a new certificate path from an old one. */
364 SecCertificatePathVCRef SecCertificatePathVCCreate(SecCertificatePathVCRef path,
365 SecCertificateRef certificate, CFArrayRef usageConstraints) {
366 CFAllocatorRef allocator = kCFAllocatorDefault;
367 check(certificate);
368 CFIndex count;
369 CFIndex selfIssued, lastVerifiedSigner;
370 bool isSelfSigned;
371 if (path) {
372 count = path->count + 1;
373 lastVerifiedSigner = path->lastVerifiedSigner;
374 selfIssued = path->selfIssued;
375 isSelfSigned = path->isSelfSigned;
376 } else {
377 count = 1;
378 lastVerifiedSigner = 0;
379 selfIssued = -1;
380 isSelfSigned = false;
381 }
382
383 CFIndex size = sizeof(struct SecCertificatePathVC) +
384 count * sizeof(SecCertificateRef);
385 SecCertificatePathVCRef result =
386 (SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
387 SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
388 if (!result)
389 return NULL;
390
391 memset((char*)result + sizeof(result->_base), 0,
392 sizeof(*result) - sizeof(result->_base));
393
394 result->count = count;
395 result->lastVerifiedSigner = lastVerifiedSigner;
396 result->selfIssued = selfIssued;
397 result->isSelfSigned = isSelfSigned;
398 CFIndex ix;
399 for (ix = 0; ix < count - 1; ++ix) {
400 result->certificates[ix] = path->certificates[ix];
401 CFRetain(result->certificates[ix]);
402 }
403
404 SecCertificateVCRef cvc = SecCertificateVCCreate(certificate, usageConstraints);
405 result->certificates[count - 1] = cvc;
406
407 return result;
408 }
409
410 SecCertificatePathVCRef SecCertificatePathVCCopyFromParent(
411 SecCertificatePathVCRef path, CFIndex skipCount) {
412 CFAllocatorRef allocator = kCFAllocatorDefault;
413 CFIndex count;
414 CFIndex selfIssued, lastVerifiedSigner;
415 bool isSelfSigned;
416
417 /* Ensure we are at least returning a path of length 1. */
418 if (skipCount < 0 || path->count < 1 + skipCount)
419 return NULL;
420
421 count = path->count - skipCount;
422 lastVerifiedSigner = path->lastVerifiedSigner > skipCount
423 ? path->lastVerifiedSigner - skipCount : 0;
424 selfIssued = path->selfIssued >= skipCount
425 ? path->selfIssued - skipCount : -1;
426 isSelfSigned = path->selfIssued >= 0 ? path->isSelfSigned : false;
427
428 CFIndex size = sizeof(struct SecCertificatePathVC) +
429 count * sizeof(SecCertificateRef);
430 SecCertificatePathVCRef result =
431 (SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
432 SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
433 if (!result)
434 return NULL;
435
436 memset((char*)result + sizeof(result->_base), 0,
437 sizeof(*result) - sizeof(result->_base));
438
439 result->count = count;
440 result->lastVerifiedSigner = lastVerifiedSigner;
441 result->selfIssued = selfIssued;
442 result->isSelfSigned = isSelfSigned;
443 result->isAnchored = path->isAnchored;
444 CFIndex ix;
445 for (ix = 0; ix < count; ++ix) {
446 CFIndex pathIX = ix + skipCount;
447 result->certificates[ix] = path->certificates[pathIX];
448 CFRetain(result->certificates[ix]);
449 }
450
451 return result;
452 }
453
454 SecCertificatePathVCRef SecCertificatePathVCCopyAddingLeaf(SecCertificatePathVCRef path,
455 SecCertificateRef leaf) {
456 CFAllocatorRef allocator = kCFAllocatorDefault;
457 CFIndex count;
458 CFIndex selfIssued, lastVerifiedSigner;
459 bool isSelfSigned;
460
461 /* First make sure the new leaf is signed by path's current leaf. */
462 SecKeyRef issuerKey = SecCertificatePathVCCopyPublicKeyAtIndex(path, 0);
463 if (!issuerKey)
464 return NULL;
465 OSStatus status = SecCertificateIsSignedBy(leaf, issuerKey);
466 CFRelease(issuerKey);
467 if (status)
468 return NULL;
469
470 count = path->count + 1;
471 lastVerifiedSigner = path->lastVerifiedSigner + 1;
472 selfIssued = path->selfIssued;
473 isSelfSigned = path->isSelfSigned;
474
475 CFIndex size = sizeof(struct SecCertificatePathVC) +
476 count * sizeof(SecCertificateRef);
477 SecCertificatePathVCRef result =
478 (SecCertificatePathVCRef)_CFRuntimeCreateInstance(allocator,
479 SecCertificatePathVCGetTypeID(), size - sizeof(CFRuntimeBase), 0);
480 if (!result)
481 return NULL;
482
483 memset((char*)result + sizeof(result->_base), 0,
484 sizeof(*result) - sizeof(result->_base));
485
486 result->count = count;
487 result->lastVerifiedSigner = lastVerifiedSigner;
488 result->selfIssued = selfIssued;
489 result->isSelfSigned = isSelfSigned;
490 result->isAnchored = path->isAnchored;
491
492 CFIndex ix;
493 for (ix = 1; ix < count; ++ix) {
494 result->certificates[ix] = path->certificates[ix - 1];
495 CFRetain(result->certificates[ix]);
496 }
497 SecCertificateVCRef leafVC = SecCertificateVCCreate(leaf, NULL);
498 result->certificates[0] = leafVC;
499
500 return result;
501 }
502
503 CFArrayRef SecCertificatePathVCCopyCertificates(SecCertificatePathVCRef path) {
504 CFMutableArrayRef outCerts = NULL;
505 size_t count = path->count;
506 require_quiet(outCerts = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks), exit);
507 SecCertificatePathVCForEachCertificate(path, ^(SecCertificateRef cert, bool * __unused stop) {
508 CFArrayAppendValue(outCerts, cert);
509 });
510 exit:
511 return outCerts;
512 }
513
514 CFArrayRef SecCertificatePathVCCreateSerialized(SecCertificatePathVCRef path) {
515 CFMutableArrayRef serializedCerts = NULL;
516 require_quiet(path, exit);
517 size_t count = path->count;
518 require_quiet(serializedCerts = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks), exit);
519 SecCertificatePathVCForEachCertificate(path, ^(SecCertificateRef cert, bool * __unused stop) {
520 CFDataRef certData = SecCertificateCopyData(cert);
521 if (certData) {
522 CFArrayAppendValue(serializedCerts, certData);
523 CFRelease(certData);
524 }
525 });
526 exit:
527 return serializedCerts;
528 }
529
530
531 /* Record the fact that we found our own root cert as our parent
532 certificate. */
533 void SecCertificatePathVCSetSelfIssued(
534 SecCertificatePathVCRef certificatePath) {
535 if (certificatePath->selfIssued >= 0) {
536 secdebug("trust", "%@ is already issued at %" PRIdCFIndex, certificatePath,
537 certificatePath->selfIssued);
538 return;
539 }
540 secdebug("trust", "%@ is self issued", certificatePath);
541 certificatePath->selfIssued = certificatePath->count - 1;
542
543 /* now check that the selfIssued cert was actually self-signed */
544 if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) {
545 SecCertificateVCRef certVC = certificatePath->certificates[certificatePath->selfIssued];
546 Boolean isSelfSigned = false;
547 OSStatus status = SecCertificateIsSelfSigned(certVC->certificate, &isSelfSigned);
548 if ((status == errSecSuccess) && isSelfSigned) {
549 certificatePath->isSelfSigned = true;
550 } else {
551 certificatePath->selfIssued = -1;
552 }
553 }
554 }
555
556 void SecCertificatePathVCSetIsAnchored(
557 SecCertificatePathVCRef certificatePath) {
558 secdebug("trust", "%@ is anchored", certificatePath);
559 certificatePath->isAnchored = true;
560
561 /* Now check if that anchor (last cert) was actually self-signed.
562 * In the non-anchor case, this is handled by SecCertificatePathVCSetSelfIssued.
563 * Because anchored chains immediately go into the candidate bucket in the trust
564 * server, we need to ensure that the self-signed/self-issued members are set
565 * for the purposes of scoring. */
566 if (!certificatePath->isSelfSigned && certificatePath->count > 0) {
567 SecCertificateVCRef certVC = certificatePath->certificates[certificatePath->count - 1];
568 Boolean isSelfSigned = false;
569 OSStatus status = SecCertificateIsSelfSigned(certVC->certificate, &isSelfSigned);
570 if ((status == errSecSuccess) && isSelfSigned) {
571 certificatePath->isSelfSigned = true;
572 if (certificatePath->selfIssued == -1) {
573 certificatePath->selfIssued = certificatePath->count - 1;
574 }
575 }
576 }
577 }
578
579 /* Return the index of the first non anchor certificate in the chain that is
580 self signed counting from the leaf up. Return -1 if there is none. */
581 CFIndex SecCertificatePathVCSelfSignedIndex(
582 SecCertificatePathVCRef certificatePath) {
583 if (certificatePath->isSelfSigned)
584 return certificatePath->selfIssued;
585 return -1;
586 }
587
588 Boolean SecCertificatePathVCIsAnchored(
589 SecCertificatePathVCRef certificatePath) {
590 return certificatePath->isAnchored;
591 }
592
593
594 void SecCertificatePathVCSetNextSourceIndex(
595 SecCertificatePathVCRef certificatePath, CFIndex sourceIndex) {
596 certificatePath->nextParentSource = sourceIndex;
597 }
598
599 CFIndex SecCertificatePathVCGetNextSourceIndex(
600 SecCertificatePathVCRef certificatePath) {
601 return certificatePath->nextParentSource;
602 }
603
604 CFIndex SecCertificatePathVCGetCount(
605 SecCertificatePathVCRef certificatePath) {
606 check(certificatePath);
607 return certificatePath ? certificatePath->count : 0;
608 }
609
610 SecCertificateRef SecCertificatePathVCGetCertificateAtIndex(
611 SecCertificatePathVCRef certificatePath, CFIndex ix) {
612 if (!certificatePath || ix < 0 || ix >= certificatePath->count) {
613 return NULL;
614 }
615 SecCertificateVCRef cvc = certificatePath->certificates[ix];
616 return cvc ? cvc->certificate : NULL;
617 }
618
619 void SecCertificatePathVCForEachCertificate(SecCertificatePathVCRef path, void(^operation)(SecCertificateRef certificate, bool *stop)) {
620 bool stop = false;
621 CFIndex ix, count = path->count;
622 for (ix = 0; ix < count; ++ix) {
623 SecCertificateVCRef cvc = path->certificates[ix];
624 operation(cvc->certificate, &stop);
625 if (stop) { break; }
626 }
627 }
628
629 CFIndex SecCertificatePathVCGetIndexOfCertificate(SecCertificatePathVCRef path,
630 SecCertificateRef certificate) {
631 CFIndex ix, count = path->count;
632 for (ix = 0; ix < count; ++ix) {
633 SecCertificateVCRef cvc = path->certificates[ix];
634 if (CFEqual(cvc->certificate, certificate))
635 return ix;
636 }
637 return kCFNotFound;
638 }
639
640 /* Return the root certificate for certificatePath. Note that root is just
641 the top of the path as far as it is constructed. It may or may not be
642 trusted or self signed. */
643 SecCertificateRef SecCertificatePathVCGetRoot(
644 SecCertificatePathVCRef certificatePath) {
645 return SecCertificatePathVCGetCertificateAtIndex(certificatePath,
646 SecCertificatePathVCGetCount(certificatePath) - 1);
647 }
648
649 SecKeyRef SecCertificatePathVCCopyPublicKeyAtIndex(
650 SecCertificatePathVCRef certificatePath, CFIndex ix) {
651 SecCertificateRef certificate =
652 SecCertificatePathVCGetCertificateAtIndex(certificatePath, ix);
653 return SecCertificateCopyKey(certificate);
654 }
655
656 CFArrayRef SecCertificatePathVCGetUsageConstraintsAtIndex(
657 SecCertificatePathVCRef certificatePath, CFIndex ix) {
658 SecCertificateVCRef cvc = certificatePath->certificates[ix];
659 return cvc->usageConstraints;
660 }
661
662 void SecCertificatePathVCSetUsageConstraintsAtIndex(SecCertificatePathVCRef certificatePath,
663 CFArrayRef newConstraints, CFIndex ix) {
664 CFArrayRef emptyArray = NULL;
665 if (!newConstraints) {
666 require_quiet(emptyArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks), exit);
667 newConstraints = emptyArray;
668 }
669
670 SecCertificateVCRef cvc = certificatePath->certificates[ix];
671 cvc->usageConstraints = CFRetainSafe(newConstraints);
672 exit:
673 CFReleaseNull(emptyArray);
674 return;
675 }
676
677 SecPathVerifyStatus SecCertificatePathVCVerify(SecCertificatePathVCRef certificatePath) {
678 check(certificatePath);
679 if (!certificatePath)
680 return kSecPathVerifyFailed;
681 for (;
682 certificatePath->lastVerifiedSigner < certificatePath->count - 1;
683 ++certificatePath->lastVerifiedSigner) {
684 SecKeyRef issuerKey =
685 SecCertificatePathVCCopyPublicKeyAtIndex(certificatePath,
686 certificatePath->lastVerifiedSigner + 1);
687 if (!issuerKey)
688 return kSecPathVerifiesUnknown;
689 SecCertificateVCRef cvc = certificatePath->certificates[certificatePath->lastVerifiedSigner];
690 OSStatus status = SecCertificateIsSignedBy(cvc->certificate,
691 issuerKey);
692 CFRelease(issuerKey);
693 if (status) {
694 return kSecPathVerifyFailed;
695 }
696 }
697
698 return kSecPathVerifySuccess;
699 }
700
701 /* Is the the issuer of the last cert a subject of a previous cert in the chain.See <rdar://33136765>. */
702 bool SecCertificatePathVCIsCycleInGraph(SecCertificatePathVCRef path) {
703 bool isCircle = false;
704 CFDataRef issuer = SecCertificateGetNormalizedIssuerContent(SecCertificatePathVCGetRoot(path));
705 if (!issuer) { return isCircle; }
706 CFIndex ix = path->count - 2;
707 for (; ix >= 0; ix--) {
708 SecCertificateVCRef cvc = path->certificates[ix];
709 CFDataRef subject = SecCertificateGetNormalizedSubjectContent(cvc->certificate);
710 if (subject && CFEqual(issuer, subject)) {
711 isCircle = true;
712 break;
713 }
714 }
715 return isCircle;
716 }
717
718 bool SecCertificatePathVCIsValid(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
719 __block bool result = true;
720 SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
721 if (!SecCertificateIsValid(certificate, verifyTime)) {
722 result = false;
723 }
724 });
725 return result;
726 }
727
728 bool SecCertificatePathVCHasWeakHash(SecCertificatePathVCRef certificatePath) {
729 CFIndex ix, count = certificatePath->count;
730
731 if (certificatePath->hasStrongHashes) {
732 return false;
733 }
734
735 if (SecCertificatePathVCIsAnchored(certificatePath)) {
736 /* For anchored paths, don't check the hash algorithm of the anchored cert,
737 * since we already decided to trust it. */
738 count--;
739 }
740 for (ix = 0; ix < count; ++ix) {
741 if (certificatePath->certificates[ix]->isWeakHash) {
742 return true;
743 }
744 }
745 certificatePath->hasStrongHashes = true;
746 return false;
747 }
748
749 bool SecCertificatePathVCHasWeakKeySize(SecCertificatePathVCRef certificatePath) {
750 __block CFDictionaryRef keySizes = NULL;
751 CFNumberRef rsaSize = NULL, ecSize = NULL;
752 __block bool result = false;
753
754 /* RSA key sizes are 2048-bit or larger. EC key sizes are P-224 or larger. */
755 require(rsaSize = CFNumberCreateWithCFIndex(NULL, 2048), errOut);
756 require(ecSize = CFNumberCreateWithCFIndex(NULL, 224), errOut);
757 const void *keys[] = { kSecAttrKeyTypeRSA, kSecAttrKeyTypeEC };
758 const void *values[] = { rsaSize, ecSize };
759 require(keySizes = CFDictionaryCreate(NULL, keys, values, 2,
760 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks), errOut);
761 SecCertificatePathVCForEachCertificate(certificatePath, ^(SecCertificateRef certificate, bool *stop) {
762 if (!SecCertificateIsAtLeastMinKeySize(certificate, keySizes)) {
763 result = true;
764 *stop = true;
765 }
766 });
767
768 errOut:
769 CFReleaseSafe(keySizes);
770 CFReleaseSafe(rsaSize);
771 CFReleaseSafe(ecSize);
772 return result;
773 }
774
775 /* Return a score for this certificate chain. */
776 CFIndex SecCertificatePathVCScore(SecCertificatePathVCRef certificatePath, CFAbsoluteTime verifyTime) {
777 CFIndex score = 0;
778
779 /* Paths that don't verify score terribly.c */
780 if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) {
781 secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex,
782 certificatePath->lastVerifiedSigner, certificatePath->count);
783 score -= 100000;
784 }
785
786 if (certificatePath->isAnchored) {
787 /* Anchored paths for the win! */
788 score += 10000;
789 }
790
791 if (certificatePath->isSelfSigned && (certificatePath->selfIssued == certificatePath->count - 1)) {
792 /* Chains that terminate in a self-signed certificate are preferred,
793 even if they don't end in an anchor. */
794 score += 1000;
795 /* Shorter chains ending in a self-signed cert are preferred. */
796 score -= 1 * certificatePath->count;
797 } else {
798 /* Longer chains are preferred when the chain doesn't end in a self-signed cert. */
799 score += 1 * certificatePath->count;
800 }
801
802 if (SecCertificatePathVCIsValid(certificatePath, verifyTime)) {
803 score += 100;
804 }
805
806 if (!SecCertificatePathVCHasWeakHash(certificatePath)) {
807 score += 10;
808 }
809
810 if (!SecCertificatePathVCHasWeakKeySize(certificatePath)) {
811 score += 10;
812 }
813
814 return score;
815 }
816
817 CFIndex SecCertificatePathVCGetScore(SecCertificatePathVCRef certificatePath) {
818 if (!certificatePath) { return 0; }
819 return certificatePath->score;
820 }
821
822 void SecCertificatePathVCSetScore(SecCertificatePathVCRef certificatePath, CFIndex score) {
823 /* We may "score" the same path twice -- if we "accept" a path but then
824 * decide to keep looking for a better one, we we process the same path
825 * again in "reject" which creates a lower score. Don't replace a higher
826 * score with a lower score. Use reset below to post-reject a path. */
827 if (score > certificatePath->score) {
828 certificatePath->score = score;
829 }
830 }
831
832 void SecCertificatePathVCResetScore(SecCertificatePathVCRef certificatePath) {
833 certificatePath->score = 0;
834 }
835
836 void *SecCertificatePathVCGetRVCAtIndex(SecCertificatePathVCRef certificatePath, CFIndex ix) {
837 if (ix >= certificatePath->rvcCount) {
838 return NULL;
839 }
840 return &((SecRVCRef)certificatePath->rvcs)[ix];
841 }
842
843 bool SecCertificatePathVCIsRevocationDone(SecCertificatePathVCRef certificatePath) {
844 return (bool)certificatePath->rvcs;
845 }
846
847 void SecCertificatePathVCAllocateRVCs(SecCertificatePathVCRef certificatePath, CFIndex certCount) {
848 certificatePath->rvcs = calloc(sizeof(struct OpaqueSecRVC), certCount);
849 certificatePath->rvcCount = certCount;
850 }
851
852 /* Return 0 if any certs revocation checking failed, or the earliest date on
853 which one of the used revocation validation tokens (ocsp response or
854 crl) expires. */
855 /* This function returns 0 to indicate revocation checking was not completed
856 for this certificate chain, otherwise returns the date at which the first
857 piece of revocation checking info we used expires. */
858 CFAbsoluteTime SecCertificatePathVCGetEarliestNextUpdate(SecCertificatePathVCRef path) {
859 CFIndex certIX, certCount = path->count;
860 CFAbsoluteTime enu = NULL_TIME;
861 if (certCount <= 1 || !path->rvcs) {
862 return enu;
863 }
864
865 for (certIX = 0; certIX < path->rvcCount; ++certIX) {
866 SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
867 CFAbsoluteTime thisCertNextUpdate = SecRVCGetEarliestNextUpdate(rvc);
868 if (thisCertNextUpdate == 0) {
869 if (certIX > 0) {
870 /* We allow for CA certs to not be revocation checked if they
871 have no ocspResponders to check against, but the leaf
872 must be checked in order for us to claim we did revocation
873 checking. */
874 SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, rvc->certIX);
875 CFArrayRef ocspResponders = NULL;
876 ocspResponders = SecCertificateGetOCSPResponders(cert);
877 if (!ocspResponders || CFArrayGetCount(ocspResponders) == 0) {
878 /* We can't check this cert so we don't consider it a soft
879 failure that we didn't. */
880 continue;
881 }
882 }
883 /* Make sure to always skip roots for whom we can't check revocation */
884 if (certIX == certCount - 1) {
885 continue;
886 }
887 secdebug("rvc", "revocation checking soft failure for cert: %ld",
888 certIX);
889 enu = thisCertNextUpdate;
890 break;
891 }
892 if (enu == 0 || thisCertNextUpdate < enu) {
893 enu = thisCertNextUpdate;
894 }
895 }
896
897 secdebug("rvc", "revocation valid until: %lg", enu);
898 return enu;
899 }
900
901 bool SecCertificatePathVCRevocationCheckedAllCerts(SecCertificatePathVCRef path) {
902 CFIndex certIX, certCount = path->count;
903 if (certCount <= 1 || !path->rvcs) {
904 /* If there is only one certificate, it's the root, so revocation checking is irrelevant. */
905 return false;
906 }
907
908 for (certIX = 0; certIX < path->rvcCount - 1; ++certIX) {
909 SecRVCRef rvc = &((SecRVCRef)path->rvcs)[certIX];
910 if (!SecRVCRevocationChecked(rvc)) {
911 secdebug("rvc", "revocation has not been checked for all certs (not checked for cert %ld)", certIX);
912 return false;
913 }
914 }
915
916 secdebug("rvc", "revocation has been checked for all certs");
917 return true;
918 }
919
920 void SecCertificatePathVCSetRevocationReasonForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
921 CFIndex ix, CFNumberRef revocationReason) {
922 if (ix > certificatePath->count - 1) { return; }
923 SecCertificateVCRef cvc = certificatePath->certificates[ix];
924 cvc->revocationReason = CFRetainSafe(revocationReason);
925 }
926
927 CFNumberRef SecCertificatePathVCGetRevocationReason(SecCertificatePathVCRef certificatePath) {
928 for (CFIndex ix = 0; ix < certificatePath->count; ix++) {
929 SecCertificateVCRef cvc = certificatePath->certificates[ix];
930 if (cvc->revocationReason) {
931 return cvc->revocationReason;
932 }
933 }
934 return NULL;
935 }
936
937 bool SecCertificatePathVCIsRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
938 CFIndex ix) {
939 if (ix > certificatePath->count - 1) { return false; }
940 SecCertificateVCRef cvc = certificatePath->certificates[ix];
941 return cvc->require_revocation_response;
942 }
943
944 void SecCertificatePathVCSetRevocationRequiredForCertificateAtIndex(SecCertificatePathVCRef certificatePath,
945 CFIndex ix) {
946 if (ix > certificatePath->count - 1) { return; }
947 SecCertificateVCRef cvc = certificatePath->certificates[ix];
948 cvc->require_revocation_response = true;
949 }
950
951 bool SecCertificatePathVCCheckedIssuers(SecCertificatePathVCRef certificatePath) {
952 return certificatePath->checkedIssuers;
953 }
954
955 void SecCertificatePathVCSetCheckedIssuers(SecCertificatePathVCRef certificatePath, bool checked) {
956 certificatePath->checkedIssuers = checked;
957 }
958
959 CFIndex SecCertificatePathVCUnknownCAIndex(SecCertificatePathVCRef certificatePath) {
960 return certificatePath->unknownCAIndex;
961 }
962
963 void SecCertificatePathVCSetUnknownCAIndex(SecCertificatePathVCRef certificatePath, CFIndex index) {
964 certificatePath->unknownCAIndex = index;
965 }
966
967 bool SecCertificatePathVCIsPathValidated(SecCertificatePathVCRef certificatePath) {
968 if (!certificatePath) { return false; }
969 return certificatePath->pathValidated;
970 }
971
972 void SecCertificatePathVCSetPathValidated(SecCertificatePathVCRef certificatePath) {
973 certificatePath->pathValidated = true;
974 }
975
976 bool SecCertificatePathVCIsEV(SecCertificatePathVCRef certificatePath) {
977 if (!certificatePath) { return false; }
978 return certificatePath->isEV;
979 }
980
981 void SecCertificatePathVCSetIsEV(SecCertificatePathVCRef certificatePath, bool isEV) {
982 certificatePath->isEV = isEV;
983 }
984
985 bool SecCertificatePathVCIsOptionallyEV(SecCertificatePathVCRef certificatePath) {
986 if (!certificatePath) { return false; }
987 return certificatePath->certificates[0]->optionallyEV;
988 }
989
990 bool SecCertificatePathVCIsCT(SecCertificatePathVCRef certificatePath) {
991 if (!certificatePath) { return false; }
992 return certificatePath->isCT;
993 }
994
995 void SecCertificatePathVCSetIsCT(SecCertificatePathVCRef certificatePath, bool isCT) {
996 certificatePath->isCT = isCT;
997 }
998
999 SecPathCTPolicy SecCertificatePathVCRequiresCT(SecCertificatePathVCRef certificatePath) {
1000 if (!certificatePath) { return kSecPathCTNotRequired; }
1001 return certificatePath->requiresCT;
1002 }
1003
1004 void SecCertificatePathVCSetRequiresCT(SecCertificatePathVCRef certificatePath, SecPathCTPolicy requiresCT) {
1005 if (certificatePath->requiresCT > requiresCT) {
1006 return; /* once set, CT policy may be only be changed to a more strict value */
1007 }
1008 certificatePath->requiresCT = requiresCT;
1009 }
1010
1011 static bool has_ca_additions_key(SecCertificatePathVCRef path, CFDictionaryRef ca_entry) {
1012 bool result = false;
1013 CFDataRef hash = CFDictionaryGetValue(ca_entry, kSecCARevocationSPKIHashKey);
1014 if (!hash) {
1015 return false;
1016 }
1017 /* only check issuing CAs and not the leaf */
1018 for (CFIndex certIX = 1; certIX < SecCertificatePathVCGetCount(path); certIX++) {
1019 SecCertificateRef ca = SecCertificatePathVCGetCertificateAtIndex(path, certIX);
1020 CFDataRef spkiHash = SecCertificateCopySubjectPublicKeyInfoSHA256Digest(ca);
1021 bool matched = CFEqualSafe(hash, spkiHash);
1022 CFReleaseNull(spkiHash);
1023 if (!matched) {
1024 continue;
1025 }
1026 /* this SPKI is a match; remember highest index */
1027 if (certIX > path->revocationCAIndex) {
1028 path->revocationCAIndex = certIX;
1029 }
1030 result = true;
1031 }
1032 return result;
1033 }
1034
1035 static void SecCertificatePathVCCheckCARevocationAdditions(SecCertificatePathVCRef path) {
1036 CFDictionaryRef additions = _SecTrustStoreCopyCARevocationAdditions(NULL, NULL);
1037 path->revocationCAIndex = kCFNotFound;
1038 if (!additions) {
1039 return;
1040 }
1041
1042 __block bool result = false;
1043 CFArrayRef ca_list = CFDictionaryGetValue(additions, kSecCARevocationAdditionsKey);
1044 if (ca_list) {
1045 CFArrayForEach(ca_list, ^(const void *value) {
1046 result = result || has_ca_additions_key(path, value);
1047 });
1048 }
1049
1050 if (result) {
1051 secinfo("ocsp", "key-based CA revocation applies at index %lld",
1052 (long long)path->revocationCAIndex);
1053 }
1054
1055 CFReleaseNull(additions);
1056 return;
1057 }
1058
1059 CFIndex SecCertificatePathVCIndexOfCAWithRevocationAdditions(SecCertificatePathVCRef certificatePath) {
1060 if (!certificatePath) { return kCFNotFound; }
1061 if (0 == certificatePath->revocationCAIndex) {
1062 /* we haven't checked this path yet, do it now */
1063 SecCertificatePathVCCheckCARevocationAdditions(certificatePath);
1064 }
1065 return certificatePath->revocationCAIndex;
1066 }
1067
1068 CFAbsoluteTime SecCertificatePathVCIssuanceTime(SecCertificatePathVCRef certificatePath) {
1069 if (!certificatePath) { return 0; }
1070 return certificatePath->issuanceTime;
1071 }
1072
1073 void SecCertificatePathVCSetIssuanceTime(SecCertificatePathVCRef certificatePath, CFAbsoluteTime issuanceTime) {
1074 certificatePath->issuanceTime = issuanceTime;
1075 }
1076
1077 bool SecCertificatePathVCIsAllowlisted(SecCertificatePathVCRef certificatePath) {
1078 if (!certificatePath) { return false; }
1079 return certificatePath->is_allowlisted;
1080 }
1081
1082 void SecCertificatePathVCSetIsAllowlisted(SecCertificatePathVCRef certificatePath, bool isAllowlisted) {
1083 certificatePath->is_allowlisted = isAllowlisted;
1084 }
1085
1086 /* MARK: policy_tree path verification */
1087 struct policy_tree_add_ctx {
1088 oid_t p_oid;
1089 policy_qualifier_t p_q;
1090 };
1091
1092 /* For each node of depth i-1 in the valid_policy_tree where P-OID is in the expected_policy_set, create a child node as follows: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1093 static bool policy_tree_add_if_match(policy_tree_t node, void *ctx) {
1094 struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
1095 policy_set_t policy_set;
1096 for (policy_set = node->expected_policy_set;
1097 policy_set;
1098 policy_set = policy_set->oid_next) {
1099 if (oid_equal(policy_set->oid, info->p_oid)) {
1100 policy_tree_add_child(node, &info->p_oid, info->p_q);
1101 return true;
1102 }
1103 }
1104 return false;
1105 }
1106
1107 /* If the valid_policy_tree includes a node of depth i-1 with the valid_policy anyPolicy, generate a child node with the following values: set the valid_policy to P-OID, set the qualifier_set to P-Q, and set the expected_policy_set to {P-OID}. */
1108 static bool policy_tree_add_if_any(policy_tree_t node, void *ctx) {
1109 struct policy_tree_add_ctx *info = (struct policy_tree_add_ctx *)ctx;
1110 if (oid_equal(node->valid_policy, oidAnyPolicy)) {
1111 policy_tree_add_child(node, &info->p_oid, info->p_q);
1112 return true;
1113 }
1114 return false;
1115 }
1116
1117 /* Return true iff node has a child with a valid_policy equal to oid. */
1118 static bool policy_tree_has_child_with_oid(policy_tree_t node,
1119 const oid_t *oid) {
1120 policy_tree_t child;
1121 for (child = node->children; child; child = child->siblings) {
1122 if (oid_equal(child->valid_policy, (*oid))) {
1123 return true;
1124 }
1125 }
1126 return false;
1127 }
1128
1129 /* For each node in the valid_policy_tree of depth i-1, for each value in the expected_policy_set (including anyPolicy) that does not appear in a child node, create a child node with the following values: set the valid_policy to the value from the expected_policy_set in the parent node, set the qualifier_set to AP-Q, and set the expected_policy_set to the value in the valid_policy from this node. */
1130 static bool policy_tree_add_expected(policy_tree_t node, void *ctx) {
1131 policy_qualifier_t p_q = (policy_qualifier_t)ctx;
1132 policy_set_t policy_set;
1133 bool added_node = false;
1134 for (policy_set = node->expected_policy_set;
1135 policy_set;
1136 policy_set = policy_set->oid_next) {
1137 if (!policy_tree_has_child_with_oid(node, &policy_set->oid)) {
1138 policy_tree_add_child(node, &policy_set->oid, p_q);
1139 added_node = true;
1140 }
1141 }
1142 return added_node;
1143 }
1144
1145 /* For each node where ID-P is the valid_policy, set expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1146 static bool policy_tree_map_if_match(policy_tree_t node, void *ctx) {
1147 /* Can't map oidAnyPolicy. */
1148 if (oid_equal(node->valid_policy, oidAnyPolicy))
1149 return false;
1150
1151 const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
1152 size_t mapping_ix, mapping_count = pm->numMappings;
1153 policy_set_t policy_set = NULL;
1154 /* Generate the policy_set of sdps for matching idp */
1155 for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
1156 const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
1157 if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) {
1158 policy_set_t p_node = (policy_set_t)malloc(sizeof(*policy_set));
1159 p_node->oid = mapping->subjectDomainPolicy;
1160 p_node->oid_next = policy_set ? policy_set : NULL;
1161 policy_set = p_node;
1162 }
1163 }
1164 if (policy_set) {
1165 policy_tree_set_expected_policy(node, policy_set);
1166 return true;
1167 }
1168 return false;
1169 }
1170
1171 /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1 that has a valid_policy of anyPolicy as follows:
1172 (i) set the valid_policy to ID-P;
1173 (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i; and
1174 (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1175 static bool policy_tree_map_if_any(policy_tree_t node, void *ctx) {
1176 if (!oid_equal(node->valid_policy, oidAnyPolicy)) {
1177 return false;
1178 }
1179
1180 const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
1181 size_t mapping_ix, mapping_count = pm->numMappings;
1182 CFMutableDictionaryRef mappings = NULL;
1183 CFDataRef idp = NULL;
1184 CFDataRef sdp = NULL;
1185 require_quiet(mappings = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,
1186 &kCFTypeDictionaryValueCallBacks),
1187 errOut);
1188 /* First we need to walk the mappings to generate the dictionary idp->sdps */
1189 for (mapping_ix = 0; mapping_ix < mapping_count; mapping_ix++) {
1190 oid_t issuerDomainPolicy = pm->mappings[mapping_ix].issuerDomainPolicy;
1191 oid_t subjectDomainPolicy = pm->mappings[mapping_ix].subjectDomainPolicy;
1192 idp = CFDataCreateWithBytesNoCopy(NULL, issuerDomainPolicy.data, issuerDomainPolicy.length, kCFAllocatorNull);
1193 sdp = CFDataCreateWithBytesNoCopy(NULL, subjectDomainPolicy.data, subjectDomainPolicy.length, kCFAllocatorNull);
1194 CFMutableArrayRef sdps = (CFMutableArrayRef)CFDictionaryGetValue(mappings, idp);
1195 if (sdps) {
1196 CFArrayAppendValue(sdps, sdp);
1197 } else {
1198 require_quiet(sdps = CFArrayCreateMutable(kCFAllocatorDefault, 0,
1199 &kCFTypeArrayCallBacks), errOut);
1200 CFArrayAppendValue(sdps, sdp);
1201 CFDictionarySetValue(mappings, idp, sdps);
1202 CFRelease(sdps);
1203 }
1204 CFReleaseNull(idp);
1205 CFReleaseNull(sdp);
1206 }
1207
1208 /* Now we use the dictionary to generate the new nodes */
1209 CFDictionaryForEach(mappings, ^(const void *key, const void *value) {
1210 CFDataRef idp = key;
1211 CFArrayRef sdps = value;
1212
1213 /* (i) set the valid_policy to ID-P; */
1214 oid_t p_oid;
1215 p_oid.data = (uint8_t *)CFDataGetBytePtr(idp);
1216 p_oid.length = CFDataGetLength(idp);
1217
1218 /* (ii) set the qualifier_set to the qualifier set of the policy anyPolicy in the certificate policies extension of certificate i */
1219 policy_qualifier_t p_q = node->qualifier_set;
1220
1221 /* (iii) set the expected_policy_set to the set of subjectDomainPolicy values that are specified as equivalent to ID-P by the policy mappings extension. */
1222 __block policy_set_t p_expected = NULL;
1223 CFArrayForEach(sdps, ^(const void *value) {
1224 policy_set_t p_node = (policy_set_t)malloc(sizeof(*p_expected));
1225 p_node->oid.data = (void *)CFDataGetBytePtr(value);
1226 p_node->oid.length = CFDataGetLength(value);
1227 p_node->oid_next = p_expected ? p_expected : NULL;
1228 p_expected = p_node;
1229 });
1230
1231 policy_tree_add_sibling(node, &p_oid, p_q, p_expected);
1232 });
1233 CFReleaseNull(mappings);
1234 return true;
1235
1236 errOut:
1237 CFReleaseNull(mappings);
1238 CFReleaseNull(idp);
1239 CFReleaseNull(sdp);
1240 return false;
1241 }
1242
1243 static bool policy_tree_map_delete_if_match(policy_tree_t node, void *ctx) {
1244 /* Can't map oidAnyPolicy. */
1245 if (oid_equal(node->valid_policy, oidAnyPolicy))
1246 return false;
1247
1248 const SecCEPolicyMappings *pm = (const SecCEPolicyMappings *)ctx;
1249 size_t mapping_ix, mapping_count = pm->numMappings;
1250 /* If this node matches any of the idps, delete it. */
1251 for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
1252 const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
1253 if (oid_equal(node->valid_policy, mapping->issuerDomainPolicy)) {
1254 policy_tree_remove_node(&node);
1255 break;
1256 }
1257 }
1258 return true;
1259 }
1260
1261 bool SecCertificatePathVCIsCertificateAtIndexSelfIssued(SecCertificatePathVCRef path, CFIndex ix) {
1262 /* The SecCertificatePath only tells us the last self-issued cert.
1263 * The chain may have more than one self-issued cert, so we need to
1264 * do the comparison. */
1265 bool result = false;
1266 SecCertificateRef cert = SecCertificatePathVCGetCertificateAtIndex(path, ix);
1267 CFDataRef issuer = SecCertificateCopyNormalizedIssuerSequence(cert);
1268 CFDataRef subject = SecCertificateCopyNormalizedSubjectSequence(cert);
1269 if (issuer && subject && CFEqual(issuer, subject)) {
1270 result = true;
1271 }
1272 CFReleaseNull(issuer);
1273 CFReleaseNull(subject);
1274 return result;
1275 }
1276
1277 enum {
1278 kSecPolicyTreeVerificationUnknown = 0,
1279 kSecPolicyTreeVerificationFalse,
1280 kSecPolicyTreeVerificationTrue,
1281 };
1282
1283 /* RFC 5280 policy tree processing */
1284 bool SecCertificatePathVCVerifyPolicyTree(SecCertificatePathVCRef path, bool anchor_trusted) {
1285 if (!path) { return false; }
1286 if (path->policy_tree_verification_result != kSecPolicyTreeVerificationUnknown) {
1287 return (path->policy_tree_verification_result == kSecPolicyTreeVerificationTrue);
1288 }
1289
1290 /* Path Validation initialization */
1291 bool result = false;
1292 path->policy_tree_verification_result = kSecPolicyTreeVerificationFalse;
1293 bool initial_policy_mapping_inhibit = false;
1294 bool initial_explicit_policy = false;
1295 bool initial_any_policy_inhibit = false;
1296
1297 SecCertificatePathVCPrunePolicyTree(path);
1298 path->policy_tree = policy_tree_create(&oidAnyPolicy, NULL);
1299
1300 assert((unsigned long)path->count<=UINT32_MAX); /* Debug check. Correct as long as CFIndex is long */
1301 uint32_t n = (uint32_t)path->count;
1302 if (anchor_trusted) {
1303 n--;
1304 }
1305
1306 uint32_t explicit_policy = initial_explicit_policy ? 0 : n + 1;
1307 uint32_t inhibit_any_policy = initial_any_policy_inhibit ? 0 : n + 1;
1308 uint32_t policy_mapping = initial_policy_mapping_inhibit ? 0 : n + 1;
1309
1310 SecCertificateRef cert = NULL;
1311 uint32_t i;
1312 for (i = 1; i <= n; ++i) {
1313 /* Process Cert */
1314 cert = SecCertificatePathVCGetCertificateAtIndex(path, n - i);
1315 bool is_self_issued = SecCertificatePathVCIsCertificateAtIndexSelfIssued(path, n - i);
1316
1317 /* (d) */
1318 if (path->policy_tree) {
1319 const SecCECertificatePolicies *cp =
1320 SecCertificateGetCertificatePolicies(cert);
1321 size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
1322 for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
1323 const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
1324 oid_t p_oid = policy->policyIdentifier;
1325 policy_qualifier_t p_q = &policy->policyQualifiers;
1326 struct policy_tree_add_ctx ctx = { p_oid, p_q };
1327 if (!oid_equal(p_oid, oidAnyPolicy)) {
1328 if (!policy_tree_walk_depth(path->policy_tree, i - 1,
1329 policy_tree_add_if_match, &ctx)) {
1330 policy_tree_walk_depth(path->policy_tree, i - 1,
1331 policy_tree_add_if_any, &ctx);
1332 }
1333 }
1334 }
1335 /* The certificate policies extension includes the policy
1336 anyPolicy with the qualifier set AP-Q and either
1337 (a) inhibit_anyPolicy is greater than 0 or
1338 (b) i < n and the certificate is self-issued. */
1339 if (inhibit_any_policy > 0 || (i < n && is_self_issued)) {
1340 for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
1341 const SecCEPolicyInformation *policy = &cp->policies[policy_ix];
1342 oid_t p_oid = policy->policyIdentifier;
1343 policy_qualifier_t p_q = &policy->policyQualifiers;
1344 if (oid_equal(p_oid, oidAnyPolicy)) {
1345 policy_tree_walk_depth(path->policy_tree, i - 1,
1346 policy_tree_add_expected, (void *)p_q);
1347 }
1348 }
1349 }
1350
1351 policy_tree_prune_childless(&path->policy_tree, i - 1);
1352 /* (e) */
1353 if (!cp) {
1354 SecCertificatePathVCPrunePolicyTree(path);
1355 }
1356 }
1357
1358 /* (f) Verify that either explicit_policy is greater than 0 or the
1359 valid_policy_tree is not equal to NULL. */
1360 if (!path->policy_tree && explicit_policy == 0) {
1361 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1362 secnotice("policy", "policy tree failure on cert %u", n - i);
1363 goto errOut;
1364 }
1365 /* If Last Cert in Path */
1366 if (i == n)
1367 break;
1368
1369 /* Prepare for Next Cert */
1370 /* (a) verify that anyPolicy does not appear as an
1371 issuerDomainPolicy or a subjectDomainPolicy */
1372 const SecCEPolicyMappings *pm = SecCertificateGetPolicyMappings(cert);
1373 if (pm && pm->present) {
1374 size_t mapping_ix, mapping_count = pm->numMappings;
1375 for (mapping_ix = 0; mapping_ix < mapping_count; ++mapping_ix) {
1376 const SecCEPolicyMapping *mapping = &pm->mappings[mapping_ix];
1377 if (oid_equal(mapping->issuerDomainPolicy, oidAnyPolicy)
1378 || oid_equal(mapping->subjectDomainPolicy, oidAnyPolicy)) {
1379 /* Policy mapping uses anyPolicy, illegal. */
1380 secnotice("policy", "policy mapping anyPolicy failure %u", n - i);
1381 goto errOut;
1382 }
1383 }
1384
1385 /* (b) */
1386 /* (1) If the policy_mapping variable is greater than 0 */
1387 if (policy_mapping > 0 && path->policy_tree) {
1388 if (!policy_tree_walk_depth(path->policy_tree, i,
1389 policy_tree_map_if_match, (void *)pm)) {
1390 /* If no node of depth i in the valid_policy_tree has a valid_policy of ID-P but there is a node of depth i with a valid_policy of anyPolicy, then generate a child node of the node of depth i-1. */
1391 policy_tree_walk_depth(path->policy_tree, i, policy_tree_map_if_any, (void *)pm);
1392 }
1393 } else if (path->policy_tree) {
1394 /* (i) delete each node of depth i in the valid_policy_tree
1395 where ID-P is the valid_policy. */
1396 policy_tree_walk_depth(path->policy_tree, i,
1397 policy_tree_map_delete_if_match, (void *)pm);
1398 /* (ii) If there is a node in the valid_policy_tree of depth
1399 i-1 or less without any child nodes, delete that
1400 node. Repeat this step until there are no nodes of
1401 depth i-1 or less without children. */
1402 policy_tree_prune_childless(&path->policy_tree, i - 1);
1403 }
1404 }
1405
1406 /* (h) */
1407 if (!is_self_issued) {
1408 if (explicit_policy)
1409 explicit_policy--;
1410 if (policy_mapping)
1411 policy_mapping--;
1412 if (inhibit_any_policy)
1413 inhibit_any_policy--;
1414 }
1415 /* (i) */
1416 const SecCEPolicyConstraints *pc =
1417 SecCertificateGetPolicyConstraints(cert);
1418 if (pc) {
1419 if (pc->requireExplicitPolicyPresent
1420 && pc->requireExplicitPolicy < explicit_policy) {
1421 explicit_policy = pc->requireExplicitPolicy;
1422 }
1423 if (pc->inhibitPolicyMappingPresent
1424 && pc->inhibitPolicyMapping < policy_mapping) {
1425 policy_mapping = pc->inhibitPolicyMapping;
1426 }
1427 }
1428 /* (j) */
1429 const SecCEInhibitAnyPolicy *iap = SecCertificateGetInhibitAnyPolicySkipCerts(cert);
1430 if (iap && iap->skipCerts < inhibit_any_policy) {
1431 inhibit_any_policy = iap->skipCerts;
1432 }
1433
1434 } /* end of path for loop */
1435
1436 /* Wrap up */
1437 cert = SecCertificatePathVCGetCertificateAtIndex(path, 0);
1438 /* (a) */
1439 if (explicit_policy)
1440 explicit_policy--;
1441 /* (b) */
1442 const SecCEPolicyConstraints *pc = SecCertificateGetPolicyConstraints(cert);
1443 if (pc) {
1444 if (pc->requireExplicitPolicyPresent
1445 && pc->requireExplicitPolicy == 0) {
1446 explicit_policy = 0;
1447 }
1448 }
1449
1450 /* (g) Calculate the intersection of the valid_policy_tree and the user-initial-policy-set, as follows */
1451
1452 if (path->policy_tree) {
1453 #if !defined(NDEBUG)
1454 policy_tree_dump(path->policy_tree);
1455 #endif
1456 /* (g3c4) */
1457 //policy_tree_prune_childless(&pvc->valid_policy_tree, n - 1);
1458 }
1459
1460 /* If either (1) the value of explicit_policy variable is greater than
1461 zero or (2) the valid_policy_tree is not NULL, then path processing
1462 has succeeded. */
1463 if (!path->policy_tree && explicit_policy == 0) {
1464 /* valid_policy_tree is empty and explicit policy is 0, illegal. */
1465 secnotice("policy", "policy tree failure on leaf");
1466 goto errOut;
1467 }
1468
1469 path->policy_tree_verification_result = kSecPolicyTreeVerificationTrue;
1470 result = true;
1471
1472 errOut:
1473 return result;
1474 }