]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecTrustServer.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / sec / securityd / SecTrustServer.c
1 /*
2 * Copyright (c) 2006-2010,2012-2017 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 * SecTrustServer.c - certificate trust evaluation engine
24 *
25 *
26 */
27
28 #include <securityd/SecTrustServer.h>
29 #include <securityd/SecPolicyServer.h>
30 #include <securityd/SecTrustLoggingServer.h>
31 #include <securityd/SecCertificateSource.h>
32 #include <securityd/SecRevocationServer.h>
33 #include <securityd/SecCertificateServer.h>
34 #include <securityd/SecPinningDb.h>
35
36 #include <utilities/SecIOFormat.h>
37 #include <utilities/SecDispatchRelease.h>
38 #include <utilities/SecAppleAnchorPriv.h>
39
40 #include <Security/SecTrustPriv.h>
41 #include <Security/SecItem.h>
42 #include <Security/SecCertificateInternal.h>
43 #include <Security/SecCertificatePath.h>
44 #include <Security/SecFramework.h>
45 #include <Security/SecPolicyPriv.h>
46 #include <Security/SecPolicyInternal.h>
47 #include <Security/SecTrustSettingsPriv.h>
48 #include <Security/SecTask.h>
49 #include <CoreFoundation/CFRuntime.h>
50 #include <CoreFoundation/CFSet.h>
51 #include <CoreFoundation/CFString.h>
52 #include <CoreFoundation/CFNumber.h>
53 #include <CoreFoundation/CFArray.h>
54 #include <CoreFoundation/CFPropertyList.h>
55 #include <AssertMacros.h>
56 #include <stdbool.h>
57 #include <string.h>
58 #include <stdlib.h>
59 #include <limits.h>
60 #include <sys/codesign.h>
61 #include <Security/SecBase.h>
62 #include "SecRSAKey.h"
63 #include <libDER/oids.h>
64 #include <utilities/debugging.h>
65 #include <utilities/SecCFWrappers.h>
66 #include <Security/SecInternal.h>
67 #include <ipc/securityd_client.h>
68 #include <CommonCrypto/CommonDigest.h>
69 #include "OTATrustUtilities.h"
70 #include "personalization.h"
71 #include <utilities/SecInternalReleasePriv.h>
72
73 #if TARGET_OS_OSX
74 #include <Security/SecTaskPriv.h>
75 #endif
76
77 #define MAX_CHAIN_LENGTH 15
78 #define ACCEPT_PATH_SCORE 10000000
79
80 /* Forward declaration for use in SecCertificateSource. */
81 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
82
83 // MARK: -
84 // MARK: SecPathBuilder
85 /********************************************************
86 *************** SecPathBuilder object ******************
87 ********************************************************/
88 struct SecPathBuilder {
89 dispatch_queue_t queue;
90 CFDataRef clientAuditToken;
91 SecCertificateSourceRef certificateSource;
92 SecCertificateSourceRef itemCertificateSource;
93 SecCertificateSourceRef anchorSource;
94 SecCertificateSourceRef appleAnchorSource;
95 CFMutableArrayRef anchorSources;
96 CFIndex nextParentSource;
97 CFMutableArrayRef parentSources;
98 CFArrayRef ocspResponses; // Stapled OCSP responses
99 CFArrayRef signedCertificateTimestamps; // Stapled SCTs
100 CFArrayRef trustedLogs; // Trusted CT logs
101 CFAbsoluteTime verifyTime;
102 CFArrayRef exceptions;
103
104 /* Hashed set of all paths we've constructed so far, used to prevent
105 re-considering a path that was already constructed once before.
106 Note that this is the only container in which certificatePath
107 objects are retained.
108 Every certificatePath being considered is always in allPaths and in at
109 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
110 all of which don't retain their values. */
111 CFMutableSetRef allPaths;
112
113 /* No trusted anchor, satisfies the linking to intermediates for all
114 policies (unless considerRejected is true). */
115 CFMutableArrayRef partialPaths;
116 /* No trusted anchor, does not satisfy linking to intermediates for all
117 policies. */
118 CFMutableArrayRef rejectedPaths;
119 /* Trusted anchor, satisfies the policies so far. */
120 CFMutableArrayRef candidatePaths;
121
122 CFIndex partialIX;
123
124 bool considerRejected;
125 bool considerPartials;
126 bool canAccessNetwork;
127
128 SecPVCRef * pvcs;
129 CFIndex pvcCount;
130
131 SecCertificatePathVCRef path;
132 unsigned int asyncJobCount;
133 bool online_revocation;
134 CFStringRef revocation_check_method;
135
136 SecCertificatePathVCRef bestPath;
137 CFMutableDictionaryRef info;
138
139 CFIndex activations;
140 bool (*state)(SecPathBuilderRef);
141 SecPathBuilderCompleted completed;
142 const void *context;
143 };
144
145 /* State functions. Return false if a async job was scheduled, return
146 true to execute the next state. */
147 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder);
148 static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
149 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
150 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
151 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
152 static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
153
154 /* Forward declarations. */
155 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
156 SecCertificateRef certificate, SecCertificateSourceRef *foundInSource);
157 static void SecPathBuilderSetPath(SecPathBuilderRef builder, SecCertificatePathVCRef path);
158
159 static void SecPathBuilderInit(SecPathBuilderRef builder,
160 CFDataRef clientAuditToken, CFArrayRef certificates,
161 CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed,
162 CFArrayRef policies, CFArrayRef ocspResponses,
163 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
164 CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions,
165 SecPathBuilderCompleted completed, const void *context) {
166 secdebug("alloc", "%p", builder);
167 CFAllocatorRef allocator = kCFAllocatorDefault;
168
169 builder->clientAuditToken = (CFDataRef)
170 ((clientAuditToken) ? CFRetain(clientAuditToken) : NULL);
171 builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
172
173 builder->nextParentSource = 1;
174 #if !TARGET_OS_WATCH
175 builder->canAccessNetwork = true;
176 #endif
177
178 builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
179 builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
180 builder->allPaths = CFSetCreateMutable(allocator, 0,
181 &kCFTypeSetCallBacks);
182
183 builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
184 builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
185 builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
186
187 /* Init the policy verification context. */
188 builder->pvcs = malloc(sizeof(SecPVCRef));
189 builder->pvcs[0] = malloc(sizeof(struct OpaqueSecPVC));
190 SecPVCInit(builder->pvcs[0], builder, policies);
191 builder->pvcCount = 1;
192 builder->verifyTime = verifyTime;
193 builder->exceptions = CFRetainSafe(exceptions);
194
195 /* Let's create all the certificate sources we might want to use. */
196 builder->certificateSource =
197 SecMemoryCertificateSourceCreate(certificates);
198 if (anchors) {
199 builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
200 }
201
202 bool allowNonProduction = false;
203 builder->appleAnchorSource = SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction));
204
205
206 /** Parent Sources
207 ** The order here avoids the most expensive methods if the cheaper methods
208 ** produce an acceptable chain: client-provided, keychains, network-fetched.
209 **/
210 #if !TARGET_OS_BRIDGE
211 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
212 builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
213 if (keychainsAllowed) {
214 CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
215 #if TARGET_OS_OSX
216 /* On OS X, need additional parent source to search legacy keychain files. */
217 if (kSecLegacyCertificateSource->contains && kSecLegacyCertificateSource->copyParents) {
218 CFArrayAppendValue(builder->parentSources, kSecLegacyCertificateSource);
219 }
220 #endif
221 }
222 if (anchorsOnly) {
223 /* Add the Apple, system, and user anchor certificate db to the search list
224 if we don't explicitly trust them. */
225 CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
226 CFArrayAppendValue(builder->parentSources, kSecSystemAnchorSource);
227 #if TARGET_OS_IPHONE
228 CFArrayAppendValue(builder->parentSources, kSecUserAnchorSource);
229 #endif
230 }
231 if (keychainsAllowed && builder->canAccessNetwork) {
232 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
233 }
234 #else /* TARGET_OS_BRIDGE */
235 /* Bridge can only access memory sources. */
236 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
237 if (anchorsOnly) {
238 /* Add the Apple, system, and user anchor certificate db to the search list
239 if we don't explicitly trust them. */
240 CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
241 }
242 #endif /* !TARGET_OS_BRIDGE */
243
244 /** Anchor Sources
245 ** The order here allows a client-provided anchor to overrule
246 ** a user or admin trust setting which can overrule the system anchors.
247 ** Apple's anchors cannot be overriden by a trust setting.
248 **/
249 #if !TARGET_OS_BRIDGE
250 if (builder->anchorSource) {
251 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
252 }
253 if (!anchorsOnly) {
254 /* Only add the system and user anchor certificate db to the
255 anchorSources if we are supposed to trust them. */
256 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
257 #if TARGET_OS_IPHONE
258 CFArrayAppendValue(builder->anchorSources, kSecUserAnchorSource);
259 #else /* TARGET_OS_OSX */
260 if (keychainsAllowed && kSecLegacyAnchorSource->contains && kSecLegacyAnchorSource->copyParents) {
261 CFArrayAppendValue(builder->anchorSources, kSecLegacyAnchorSource);
262 }
263 #endif
264 CFArrayAppendValue(builder->anchorSources, kSecSystemAnchorSource);
265 }
266 #else /* TARGET_OS_BRIDGE */
267 /* Bridge can only access memory sources. */
268 if (builder->anchorSource) {
269 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
270 }
271 if (!anchorsOnly) {
272 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
273 }
274 #endif /* !TARGET_OS_BRIDGE */
275
276 builder->ocspResponses = CFRetainSafe(ocspResponses);
277 builder->signedCertificateTimestamps = CFRetainSafe(signedCertificateTimestamps);
278
279 if(trustedLogs) {
280 builder->trustedLogs = CFRetainSafe(trustedLogs);
281 } else {
282 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
283 builder->trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
284 CFReleaseSafe(otapkiref);
285 }
286
287 /* Now let's get the leaf cert and turn it into a path. */
288 SecCertificateRef leaf =
289 (SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
290 SecCertificatePathVCRef path = SecCertificatePathVCCreate(NULL, leaf, NULL);
291 CFSetAddValue(builder->allPaths, path);
292 CFArrayAppendValue(builder->partialPaths, path);
293
294 builder->path = CFRetainSafe(path);
295 SecPathBuilderSetPath(builder, path);
296 CFRelease(path);
297
298 /* Set the revocation context */
299
300 /* Next step is to process the leaf. We do that work on the builder queue
301 * to avoid blocking the main thread with database lookups. */
302 builder->state = SecPathBuilderProcessLeaf;
303 builder->completed = completed;
304 builder->context = context;
305 }
306
307 SecPathBuilderRef SecPathBuilderCreate(CFDataRef clientAuditToken,
308 CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
309 bool keychainsAllowed, CFArrayRef policies, CFArrayRef ocspResponses,
310 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
311 CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions,
312 SecPathBuilderCompleted completed, const void *context) {
313 SecPathBuilderRef builder = malloc(sizeof(*builder));
314 memset(builder, 0, sizeof(*builder));
315 SecPathBuilderInit(builder, clientAuditToken, certificates,
316 anchors, anchorsOnly, keychainsAllowed, policies, ocspResponses,
317 signedCertificateTimestamps, trustedLogs, verifyTime,
318 accessGroups, exceptions, completed, context);
319 return builder;
320 }
321
322 /* Don't use this if you're going to modify the PVC array in the operation. */
323 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder,void (^operation)(SecPVCRef pvc, bool *stop)) {
324 if (!builder->pvcs) { return; }
325 bool stop = false;
326 CFIndex ix;
327 for (ix = 0; ix < builder->pvcCount; ix++) {
328 if (!builder->pvcs[ix]) { continue; }
329 operation(builder->pvcs[ix], &stop);
330 if (stop) { break; }
331 }
332 }
333
334 static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
335 secdebug("alloc", "%p", builder);
336 dispatch_release_null(builder->queue);
337 if (builder->anchorSource) {
338 SecMemoryCertificateSourceDestroy(builder->anchorSource);
339 builder->anchorSource = NULL;
340 }
341 if (builder->certificateSource) {
342 SecMemoryCertificateSourceDestroy(builder->certificateSource);
343 builder->certificateSource = NULL;
344 }
345 if (builder->itemCertificateSource) {
346 SecItemCertificateSourceDestroy(builder->itemCertificateSource);
347 builder->itemCertificateSource = NULL;
348 }
349 if (builder->appleAnchorSource) {
350 SecMemoryCertificateSourceDestroy(builder->appleAnchorSource);
351 builder->appleAnchorSource = NULL;
352 }
353 CFReleaseNull(builder->clientAuditToken);
354 CFReleaseNull(builder->anchorSources);
355 CFReleaseNull(builder->parentSources);
356 CFReleaseNull(builder->allPaths);
357 CFReleaseNull(builder->partialPaths);
358 CFReleaseNull(builder->rejectedPaths);
359 CFReleaseNull(builder->candidatePaths);
360 CFReleaseNull(builder->ocspResponses);
361 CFReleaseNull(builder->signedCertificateTimestamps);
362 CFReleaseNull(builder->trustedLogs);
363 CFReleaseNull(builder->path);
364 CFReleaseNull(builder->revocation_check_method);
365 CFReleaseNull(builder->info);
366 CFReleaseNull(builder->exceptions);
367
368 if (builder->pvcs) {
369 CFIndex ix;
370 for (ix = 0; ix < builder->pvcCount; ix++) {
371 if (builder->pvcs[ix]) {
372 SecPVCDelete(builder->pvcs[ix]);
373 free(builder->pvcs[ix]);
374 }
375 }
376 free(builder->pvcs);
377 builder->pvcs = NULL;
378 }
379 }
380
381 static void SecPathBuilderSetPath(SecPathBuilderRef builder, SecCertificatePathVCRef path) {
382 bool samePath = ((!path && !builder->path) || (path && builder->path && CFEqual(path, builder->path)));
383 if (!samePath) {
384 CFRetainAssign(builder->path, path);
385 }
386 CFReleaseNull(builder->info);
387
388 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
389 SecPVCSetPath(pvc, path);
390 });
391 }
392
393
394 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
395 return builder->canAccessNetwork;
396 }
397
398 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
399 if (builder->canAccessNetwork != allow) {
400 builder->canAccessNetwork = allow;
401 if (allow) {
402 #if !TARGET_OS_WATCH
403 secinfo("http", "network access re-enabled by policy");
404 /* re-enabling network_access re-adds kSecCAIssuerSource as
405 a parent source. */
406 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
407 #else
408 secnotice("http", "network access not allowed on WatchOS");
409 builder->canAccessNetwork = false;
410 #endif
411 } else {
412 secinfo("http", "network access disabled by policy");
413 /* disabling network_access removes kSecCAIssuerSource from
414 the list of parent sources. */
415 CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
416 CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
417 kSecCAIssuerSource);
418 if (ix >= 0)
419 CFArrayRemoveValueAtIndex(builder->parentSources, ix);
420 }
421 }
422 }
423
424 CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
425 {
426 return CFRetainSafe(builder->ocspResponses);
427 }
428
429 CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
430 {
431 return CFRetainSafe(builder->signedCertificateTimestamps);
432 }
433
434 CFArrayRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
435 {
436 return CFRetainSafe(builder->trustedLogs);
437 }
438
439 SecCertificatePathVCRef SecPathBuilderGetBestPath(SecPathBuilderRef builder)
440 {
441 return builder->bestPath;
442 }
443
444 SecCertificatePathVCRef SecPathBuilderGetPath(SecPathBuilderRef builder) {
445 return builder->path;
446 }
447
448 CFAbsoluteTime SecPathBuilderGetVerifyTime(SecPathBuilderRef builder) {
449 return builder->verifyTime;
450 }
451
452 CFIndex SecPathBuilderGetCertificateCount(SecPathBuilderRef builder) {
453 return SecCertificatePathVCGetCount(builder->path);
454 }
455
456 SecCertificateRef SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder, CFIndex ix) {
457 return SecCertificatePathVCGetCertificateAtIndex(builder->path, ix);
458 }
459
460 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder) {
461 return SecCertificatePathVCIsAnchored(builder->path);
462 }
463
464 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder) {
465 return --builder->asyncJobCount;
466 }
467
468 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder, unsigned int jobCount) {
469 builder->asyncJobCount = jobCount;
470 secdebug("rvc", "set asyncJobCount to %d", builder->asyncJobCount);
471 }
472
473 CFMutableDictionaryRef SecPathBuilderGetInfo(SecPathBuilderRef builder) {
474 return builder->info;
475 }
476
477 CFStringRef SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder) {
478 return builder->revocation_check_method;
479 }
480
481 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder, CFStringRef method) {
482 CFRetainAssign(builder->revocation_check_method, method);
483 secdebug("rvc", "deferred revocation checking enabled using %@ method", method);
484 }
485
486 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder) {
487 return builder->online_revocation;
488 }
489
490 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder) {
491 builder->online_revocation = true;
492 secdebug("rvc", "revocation force online check");
493 }
494
495 CFArrayRef SecPathBuilderGetExceptions(SecPathBuilderRef builder) {
496 return builder->exceptions;
497 }
498
499 CFIndex SecPathBuilderGetPVCCount(SecPathBuilderRef builder) {
500 return builder->pvcCount;
501 }
502
503 SecPVCRef SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder, CFIndex ix) {
504 if (ix > (builder->pvcCount - 1)) {
505 return NULL;
506 }
507 return builder->pvcs[ix];
508 }
509
510 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder, CFStringRef key,
511 CFIndex ix, CFTypeRef result, bool force,
512 SecTrustResultType resultType) {
513 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
514 SecPVCSetResultForced(pvc, key, ix, result, force);
515 pvc->result = resultType;
516 });
517 }
518
519 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder) {
520 /* If any of the PVCs passed, we accept the path. */
521 __block bool acceptPath = false;
522 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
523 acceptPath |= SecPVCIsOkResult(pvc);
524 });
525 return acceptPath;
526 }
527
528 static SecPVCRef SecPathBuilderGetResultPVC(SecPathBuilderRef builder) {
529 /* Return the first PVC that passed */
530 __block SecPVCRef resultPVC = NULL;
531 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool *stop) {
532 if (SecPVCIsOkResult(pvc)) {
533 resultPVC = pvc;
534 *stop = true;
535 }
536 });
537 if (resultPVC) { return resultPVC; }
538
539 /* If we didn't return a passing PVC, return the first PVC. */
540 return builder->pvcs[0];
541 }
542
543 /* This function assumes that the input source is an anchor source */
544 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder, SecCertificateSourceRef source,
545 SecCertificateRef certificate) {
546 __block bool result = false;
547 CFArrayRef constraints = NULL;
548 constraints = SecCertificateSourceCopyUsageConstraints(source, certificate);
549
550 /* Unrestricted certificates:
551 * -those that come from anchor sources with no constraints
552 * -self-signed certificates with empty contraints arrays
553 */
554 Boolean selfSigned = false;
555 require(errSecSuccess == SecCertificateIsSelfSigned(certificate, &selfSigned), out);
556 if ((NULL == source->copyUsageConstraints) ||
557 (constraints && (CFArrayGetCount(constraints) == 0) && selfSigned)) {
558 secinfo("trust", "unrestricted anchor%s",
559 (NULL == source->copyUsageConstraints) ? " source" : "");
560 result = true;
561 goto out;
562 }
563
564 /* Get the trust settings result for the PVCs. Only one PVC need match to
565 * trigger the anchor behavior -- policy validation will handle whether the
566 * path is truly anchored for that PVC. */
567 require(constraints, out);
568 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
569 SecTrustSettingsResult settingsResult = kSecTrustSettingsResultInvalid;
570 settingsResult = SecPVCGetTrustSettingsResult(pvc,
571 certificate,
572 constraints);
573 if ((selfSigned && settingsResult == kSecTrustSettingsResultTrustRoot) ||
574 (!selfSigned && settingsResult == kSecTrustSettingsResultTrustAsRoot)) {
575 // For our purposes, this is an anchor.
576 secinfo("trust", "complex trust settings anchor");
577 result = true;
578 *stop = true;
579 }
580
581 if (settingsResult == kSecTrustSettingsResultDeny) {
582 /* We consider denied certs "anchors" because the trust decision
583 is set regardless of building the chain further. The policy
584 validation will handle rejecting this chain. */
585 secinfo("trust", "complex trust settings denied anchor");
586 result = true;
587 *stop = true;
588 }
589 });
590
591 out:
592 CFReleaseNull(constraints);
593 return result;
594 }
595
596 /* Source returned in foundInSource has the same lifetime as the builder. */
597 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
598 SecCertificateRef certificate, SecCertificateSourceRef *foundInSource) {
599 /* We look through the anchor sources in order. They are ordered in
600 SecPathBuilderInit so that process anchors override user anchors which
601 override system anchors. */
602 CFIndex count = CFArrayGetCount(builder->anchorSources);
603 CFIndex ix;
604 for (ix = 0; ix < count; ++ix) {
605 SecCertificateSourceRef source = (SecCertificateSourceRef)
606 CFArrayGetValueAtIndex(builder->anchorSources, ix);
607 if (SecCertificateSourceContains(source, certificate)) {
608 if (foundInSource)
609 *foundInSource = source;
610 if (SecPathBuilderIsAnchorPerConstraints(builder, source, certificate)) {
611 return true;
612 }
613 }
614 }
615 return false;
616 }
617
618 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder, SecCertificateSourceRef source) {
619 CFIndex anchorCount = CFArrayGetCount(builder->anchorSources);
620 return CFArrayContainsValue(builder->anchorSources, CFRangeMake(0,anchorCount), source);
621 }
622
623 /* Return false if path is not a partial, if path was a valid candidate it
624 will have been added to builder->candidatePaths, if path was rejected
625 by the parent certificate checks (because it's expired or some other
626 static chaining check failed) it will have been added to rejectedPaths.
627 Return true path if path is a partial. */
628 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
629 SecCertificatePathVCRef path) {
630 SecPathBuilderSetPath(builder, path);
631 __block bool parentChecksFail = true;
632
633 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
634 /* The parent checks aren't actually PVC-dependent, so theoretically,
635 * we only need to run this once per path, but we want to set the
636 * results in all PVCs. */
637 parentChecksFail &= !SecPVCParentCertificateChecks(pvc,
638 SecCertificatePathVCGetCount(path) - 1);
639 });
640
641 if (!builder->considerRejected && parentChecksFail) {
642 secdebug("trust", "Found rejected path %@", path);
643 CFArrayAppendValue(builder->rejectedPaths, path);
644 return false;
645 }
646
647 SecPathVerifyStatus vstatus = SecCertificatePathVCVerify(path);
648 /* Candidate paths with failed signatures are discarded. */
649 if (vstatus == kSecPathVerifyFailed) {
650 secdebug("trust", "Verify failed for path %@", path);
651 return false;
652 }
653
654 if (vstatus == kSecPathVerifySuccess) {
655 /* The signature chain verified sucessfully, now let's find
656 out if we have an anchor for path. */
657 if (SecCertificatePathVCIsAnchored(path)) {
658 secdebug("trust", "Adding candidate %@", path);
659 CFArrayAppendValue(builder->candidatePaths, path);
660 }
661 /* The path is not partial if the last cert is self-signed. */
662 if ((SecCertificatePathVCSelfSignedIndex(path) >= 0) &&
663 (SecCertificatePathVCSelfSignedIndex(path) == SecCertificatePathVCGetCount(path)-1)) {
664 return false;
665 }
666 }
667
668 return true;
669 }
670
671 static void addOptionsToPolicy(SecPolicyRef policy, CFDictionaryRef newOptions) {
672 __block CFMutableDictionaryRef oldOptions = CFDictionaryCreateMutableCopy(NULL, 0, policy->_options);
673 CFDictionaryForEach(newOptions, ^(const void *key, const void *value) {
674 CFDictionaryAddValue(oldOptions, key, value);
675 });
676 CFAssignRetained(policy->_options, oldOptions);
677 }
678
679 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder) {
680 CFIndex ix, initialPVCCount = builder->pvcCount;
681 for (ix = 0; ix < initialPVCCount; ix++) {
682 CFArrayRef policies = CFRetainSafe(builder->pvcs[ix]->policies);
683 CFIndex policyIX;
684 for (policyIX = 0; policyIX < CFArrayGetCount(policies); policyIX++) {
685 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, policyIX);
686 CFStringRef policyName = SecPolicyGetName(policy);
687 CFStringRef hostname = CFDictionaryGetValue(policy->_options, kSecPolicyCheckSSLHostname);
688 if (!hostname) { continue; } //No hostname to look up; probably not an SSL policy, skip
689
690 /* Query the pinning database for this policy */
691 CFMutableDictionaryRef query = CFDictionaryCreateMutable(NULL, 2, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
692 CFDictionaryAddValue(query, kSecPinningDbKeyPolicyName, policyName);
693 CFDictionaryAddValue(query, kSecPinningDbKeyHostname, hostname);
694 CFDictionaryRef results = SecPinningDbCopyMatching(query);
695 CFReleaseNull(query);
696 if (!results) { continue; } //No rules for this hostname or policyName
697
698 /* Found pinning policies. Apply them to the path builder. */
699 CFArrayRef newRules = CFDictionaryGetValue(results, kSecPinningDbKeyRules);
700 CFStringRef dbPolicyName = CFDictionaryGetValue(results, kSecPinningDbKeyPolicyName);
701 secinfo("trust", "found pinning %lu %@ policies for hostname %@, policyName %@",
702 (unsigned long)CFArrayGetCount(newRules), dbPolicyName, hostname, policyName);
703 CFIndex newRulesIX;
704 for (newRulesIX = 0; newRulesIX < CFArrayGetCount(newRules); newRulesIX++) {
705 if (!isDictionary(CFArrayGetValueAtIndex(newRules, newRulesIX))) {
706 continue;
707 }
708
709 /* Create the new policies with pinning rules (preserving other ANDed policies). */
710 CFDictionaryRef newOptions = (CFDictionaryRef)CFArrayGetValueAtIndex(newRules, newRulesIX);
711 SecPolicyRef newPolicy = SecPolicyCreateSSL(true, hostname);
712 if (!newPolicy) { continue; }
713 addOptionsToPolicy(newPolicy, newOptions);
714 SecPolicySetName(newPolicy, dbPolicyName);
715 CFMutableArrayRef newPolicies = CFArrayCreateMutableCopy(NULL, 0, policies);
716 if (!newPolicies) { CFReleaseNull(newPolicy); continue; }
717 CFArrayReplaceValues(newPolicies, CFRangeMake(policyIX, 1), (const void **)&newPolicy, 1);
718
719 if (newRulesIX == 0) {
720 /* For the first set of pinning rules, replace this PVC's policies */
721 CFRetainAssign(builder->pvcs[ix]->policies, newPolicies);
722 } else {
723 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
724 * Create another PVC for this dicitionary. */
725 builder->pvcs = realloc(builder->pvcs, (builder->pvcCount + 1) * sizeof(SecPVCRef));
726 builder->pvcs[builder->pvcCount] = malloc(sizeof(struct OpaqueSecPVC));
727 SecPVCInit(builder->pvcs[builder->pvcCount], builder, newPolicies);
728 builder->pvcCount++;
729 }
730 CFReleaseNull(newPolicy);
731 CFReleaseNull(newPolicies);
732 }
733 CFReleaseNull(results);
734 }
735 CFReleaseNull(policies);
736 }
737 }
738
739 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder) {
740 SecPathBuilderAddPinningPolicies(builder);
741
742 /* We need to find and set constraints on the leaf-only path */
743 SecCertificatePathVCRef path = builder->path;
744 SecCertificateRef leaf = SecCertificatePathVCGetCertificateAtIndex(path, 0);
745
746 SecCertificateSourceRef source = NULL;
747 bool isAnchor = false;
748 CFArrayRef constraints = NULL;
749 if (SecPathBuilderIsAnchor(builder, leaf, &source)) {
750 isAnchor = true;
751 }
752 if (source) {
753 constraints = SecCertificateSourceCopyUsageConstraints(source, leaf);
754 }
755 SecCertificatePathVCSetUsageConstraintsAtIndex(path, constraints, 0);
756 CFReleaseSafe(constraints);
757 if (isAnchor) {
758 SecCertificatePathVCSetIsAnchored(path);
759 CFArrayAppendValue(builder->candidatePaths, path);
760 }
761
762 __block bool leafChecksFail = true;
763 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
764 SecPVCLeafChecks(pvc);
765 leafChecksFail &= !SecPVCIsOkResult(pvc);
766 });
767 builder->considerRejected = leafChecksFail;
768
769 builder->state = SecPathBuilderGetNext;
770 return true;
771 }
772
773 /* Given the builder, a partial chain partial and the parents array, construct
774 a SecCertificatePath for each parent. After discarding previously
775 considered paths and paths with cycles, sort out which array each path
776 should go in, if any. */
777 static void SecPathBuilderProcessParents(SecPathBuilderRef builder,
778 SecCertificatePathVCRef partial, CFArrayRef parents) {
779 CFIndex rootIX = SecCertificatePathVCGetCount(partial) - 1;
780 CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
781 CFIndex parentIX;
782 for (parentIX = 0; parentIX < num_parents; ++parentIX) {
783 SecCertificateRef parent = (SecCertificateRef)
784 CFArrayGetValueAtIndex(parents, parentIX);
785 CFIndex ixOfParent = SecCertificatePathVCGetIndexOfCertificate(partial,
786 parent);
787 if (ixOfParent != kCFNotFound) {
788 /* partial already contains parent. Let's not add the same
789 certificate again. */
790 if (ixOfParent == rootIX) {
791 /* parent is equal to the root of the partial, so partial
792 looks to be self issued. */
793 SecCertificatePathVCSetSelfIssued(partial);
794 }
795 continue;
796 }
797
798 /* FIXME Add more sanity checks to see that parent really can be
799 a parent of partial_root. subjectKeyID == authorityKeyID,
800 signature algorithm matches public key algorithm, etc. */
801 SecCertificateSourceRef source = NULL;
802 bool is_anchor = SecPathBuilderIsAnchor(builder, parent, &source);
803 CFArrayRef constraints = (source) ? SecCertificateSourceCopyUsageConstraints(source, parent) : NULL;
804 SecCertificatePathVCRef path = SecCertificatePathVCCreate(partial, parent, constraints);
805 CFReleaseSafe(constraints);
806 if (!path)
807 continue;
808 if (!CFSetContainsValue(builder->allPaths, path)) {
809 CFSetAddValue(builder->allPaths, path);
810 if (is_anchor)
811 SecCertificatePathVCSetIsAnchored(path);
812 if (SecPathBuilderIsPartial(builder, path)) {
813 /* Insert path right at the current position since it's a new
814 candiate partial. */
815 CFArrayInsertValueAtIndex(builder->partialPaths,
816 ++builder->partialIX, path);
817 secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
818 parentIX + 1, num_parents, path);
819 }
820 secdebug("trust", "found new path %@", path);
821 }
822 CFRelease(path);
823 }
824 }
825
826 /* Callback for the SecPathBuilderGetNext() functions call to
827 SecCertificateSourceCopyParents(). */
828 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
829 SecPathBuilderRef builder = (SecPathBuilderRef)context;
830 SecCertificatePathVCRef partial = (SecCertificatePathVCRef)
831 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
832 secdebug("async", "%@ parents %@", partial, parents);
833 SecPathBuilderProcessParents(builder, partial, parents);
834
835 builder->state = SecPathBuilderGetNext;
836 SecPathBuilderStep(builder);
837 }
838
839 static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
840 /* If we have any candidates left to go return those first. */
841 if (CFArrayGetCount(builder->candidatePaths)) {
842 SecCertificatePathVCRef path = (SecCertificatePathVCRef)
843 CFArrayGetValueAtIndex(builder->candidatePaths, 0);
844 CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
845 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
846 path);
847 SecPathBuilderSetPath(builder, path);
848 builder->state = SecPathBuilderValidatePath;
849 return true;
850 }
851
852 /* If we are considering rejected chains we check each rejected path
853 with SecPathBuilderIsPartial() which checks the signature chain and
854 either drops the path if it's not properly signed, add it as a
855 candidate if it has a trusted anchor, or adds it as a partial
856 to be considered once we finish considering all the rejects. */
857 if (builder->considerRejected) {
858 CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
859 if (rejectedIX) {
860 rejectedIX--;
861 SecCertificatePathVCRef path = (SecCertificatePathVCRef)
862 CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
863 if (SecPathBuilderIsPartial(builder, path)) {
864 CFArrayInsertValueAtIndex(builder->partialPaths,
865 ++builder->partialIX, path);
866 }
867 CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
868
869 /* Keep going until we have moved all rejected partials into
870 the regular partials or candidates array. */
871 return true;
872 }
873 }
874
875 /* If builder->partialIX is < 0 we have considered all partial chains
876 this block must ensure partialIX >= 0 if execution continues past
877 it's end. */
878 if (builder->partialIX < 0) {
879 CFIndex num_sources = CFArrayGetCount(builder->parentSources);
880 if (builder->nextParentSource < num_sources) {
881 builder->nextParentSource++;
882 secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
883 builder->nextParentSource, num_sources);
884 } else {
885 /* We've run out of new sources to consider so let's look at
886 rejected chains and after that even consider partials
887 directly.
888 FIXME we might not want to consider partial paths that
889 are subsets of other partial paths, or not consider them
890 at all if we already have an anchored reject. */
891 if (!builder->considerRejected) {
892 builder->considerRejected = true;
893 secdebug("trust", "considering rejected paths");
894 } else if (!builder->considerPartials) {
895 builder->considerPartials = true;
896 secdebug("trust", "considering partials");
897 } else {
898 /* We're all out of options, so we can't produce any more
899 candidates. Let's calculate details and return the best
900 path we found. */
901 builder->state = SecPathBuilderComputeDetails;
902 return true;
903 }
904 }
905 builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
906 secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
907 return true;
908 }
909
910 /* We know builder->partialIX >= 0 if we get here. */
911 SecCertificatePathVCRef partial = (SecCertificatePathVCRef)
912 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
913 /* Don't try to extend partials anymore once we are in the considerPartials
914 state, since at this point every partial has been extended with every
915 possible parentSource already. */
916 if (builder->considerPartials) {
917 --builder->partialIX;
918 SecPathBuilderSetPath(builder, partial);
919 builder->state = SecPathBuilderValidatePath;
920 return true;
921 }
922
923 /* Attempt to extend this partial path with another certificate. This
924 should give us a list of potential parents to consider. */
925 secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
926 builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
927 partial);
928
929 /* Attempt to extend partial, leaving all possible extended versions
930 of partial in builder->extendedPaths. */
931 CFIndex sourceIX = SecCertificatePathVCGetNextSourceIndex(partial);
932 CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
933 if (sourceIX < num_anchor_sources + builder->nextParentSource) {
934 SecCertificateSourceRef source;
935 if (sourceIX < num_anchor_sources) {
936 source = (SecCertificateSourceRef)
937 CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
938 secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
939 num_anchor_sources);
940 } else {
941 CFIndex parentIX = sourceIX - num_anchor_sources;
942 source = (SecCertificateSourceRef)
943 CFArrayGetValueAtIndex(builder->parentSources, parentIX);
944 secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
945 builder->nextParentSource);
946 }
947 SecCertificatePathVCSetNextSourceIndex(partial, sourceIX + 1);
948 SecCertificateRef root = SecCertificatePathVCGetRoot(partial);
949 return SecCertificateSourceCopyParents(source, root,
950 builder, SecPathBuilderExtendPaths);
951 } else {
952 --builder->partialIX;
953 }
954
955 return true;
956 }
957
958 /* One or more of the policies did not accept the candidate path. */
959 static void SecPathBuilderReject(SecPathBuilderRef builder) {
960 check(builder);
961
962 builder->state = SecPathBuilderGetNext;
963
964 bool bestPathIsEV = SecCertificatePathVCIsEV(builder->bestPath);
965 bool isEV = SecCertificatePathVCIsEV(builder->path);
966
967 if (bestPathIsEV && !isEV) {
968 /* We never replace an ev reject with a non ev reject. */
969 return;
970 }
971
972 CFIndex bestPathScore = SecCertificatePathVCGetScore(builder->bestPath);
973 CFIndex score = SecCertificatePathVCScore(builder->path, builder->verifyTime);
974 SecCertificatePathVCSetScore(builder->path, score);
975
976 /* The current chain is valid for EV, but revocation checking failed. We
977 replace any previously accepted or rejected non EV chains with the
978 current one. */
979 if (isEV && !bestPathIsEV) {
980 bestPathScore = 0;
981 }
982 if (!builder->bestPath || score > bestPathScore) {
983 if (builder->bestPath) {
984 secinfo("reject",
985 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
986 (bestPathIsEV ? "" : "non "),
987 (bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
988 bestPathScore,
989 (isEV ? "" : "non "), (long)score, builder->path);
990 } else {
991 secinfo("reject", "%sev score: %" PRIdCFIndex " %@",
992 (isEV ? "" : "non "), score, builder->path);
993 }
994
995 builder->bestPath = builder->path;
996 } else {
997 secinfo("reject", "%sev score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
998 (isEV ? "" : "non "), score, bestPathScore, builder->path);
999 }
1000 }
1001
1002 /* All policies accepted the candidate path. */
1003 static void SecPathBuilderAccept(SecPathBuilderRef builder) {
1004 if (!builder) { return; }
1005 bool isSHA2 = !SecCertificatePathVCHasWeakHash(builder->path);
1006 bool isOptionallySHA2 = !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder, 0));
1007 bool isEV = SecCertificatePathVCIsEV(builder->path);
1008 bool isOptionallyEV = SecCertificatePathVCIsOptionallyEV(builder->path);
1009 CFIndex bestScore = SecCertificatePathVCGetScore(builder->bestPath);
1010 /* Score this path. Note that all points awarded or deducted in
1011 * SecCertificatePathScore are < 100,000 */
1012 CFIndex currScore = (SecCertificatePathVCScore(builder->path, builder->verifyTime) +
1013 ACCEPT_PATH_SCORE + // 10,000,000 points for accepting
1014 (isEV ? 1000000 : 0)); //1,000,000 points for EV
1015 SecCertificatePathVCSetScore(builder->path, currScore);
1016 if (currScore > bestScore) {
1017 // current path is better than existing best path
1018 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
1019 (SecCertificatePathVCIsEV(builder->bestPath) ? "" : "non "),
1020 (bestScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
1021 bestScore,
1022 (isEV ? "" : "non "), (long)currScore, builder->path);
1023
1024 builder->bestPath = builder->path;
1025 }
1026
1027 /* If we found the best accept we can, we want to switch directly to the
1028 SecPathBuilderComputeDetails state here, since we're done. */
1029 if ((isEV || !isOptionallyEV) && (isSHA2 || !isOptionallySHA2))
1030 builder->state = SecPathBuilderComputeDetails;
1031 else
1032 builder->state = SecPathBuilderGetNext;
1033 }
1034
1035 /* Return true iff a given path satisfies all the specified policies at
1036 verifyTime. */
1037 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
1038
1039 if (builder->considerRejected) {
1040 SecPathBuilderReject(builder);
1041 return true;
1042 }
1043
1044 builder->state = SecPathBuilderDidValidatePath;
1045
1046 /* Revocation checking is now done before path checks, to ensure that
1047 isAllowlisted will be set correctly for the subsequent path checks. */
1048 bool completed = SecPathBuilderCheckRevocation(builder);
1049
1050 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
1051 SecPVCPathChecks(pvc);
1052 });
1053
1054 return completed;
1055 }
1056
1057 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
1058 /* We perform the revocation required policy checks here because
1059 * this is the state we call back into once all the asynchronous
1060 * revocation check calls are done. */
1061 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
1062 SecPVCPathCheckRevocationRequired(pvc);
1063 });
1064
1065 if (SecPathBuilderIsOkResult(builder)) {
1066 SecPathBuilderAccept(builder);
1067 } else {
1068 SecPathBuilderReject(builder);
1069 }
1070 assert(builder->state != SecPathBuilderDidValidatePath);
1071 return true;
1072 }
1073
1074 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
1075 /* We have to re-do all the checks so that the results get set in the
1076 * PVC for the best path, as the last path checked may not have been the best. */
1077 SecPathBuilderSetPath(builder, builder->bestPath);
1078 __block CFIndex ix, pathLength = SecCertificatePathVCGetCount(builder->bestPath);
1079
1080 __block bool completed = true;
1081
1082 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
1083 SecPVCComputeDetails(pvc, builder->bestPath);
1084 completed &= SecPathBuilderCheckRevocation(builder);
1085 for (ix = 1; ix < pathLength; ++ix) {
1086 SecPVCParentCertificateChecks(pvc, ix);
1087 }
1088 SecPVCPathChecks(pvc);
1089 });
1090
1091 builder->state = SecPathBuilderReportResult;
1092
1093 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
1094 SecPVCPathCheckRevocationRequired(pvc);
1095 });
1096
1097 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1098 if (SecCertificatePathVCGetScore(builder->bestPath) > ACCEPT_PATH_SCORE && !SecPathBuilderIsOkResult(builder)) {
1099 SecCertificatePathVCResetScore(builder->bestPath);
1100 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1101 }
1102
1103 return completed;
1104 }
1105
1106 static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
1107 builder->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
1108 0, &kCFTypeDictionaryKeyCallBacks,
1109 &kCFTypeDictionaryValueCallBacks);
1110
1111
1112 /* isEV is not set unless also CT verified. Here, we need to check that we
1113 * got a revocation response as well. */
1114 if (builder->info && SecCertificatePathVCIsEV(builder->bestPath) && SecPathBuilderIsOkResult(builder)) {
1115 #if !TARGET_OS_WATCH
1116 if (SecCertificatePathVCIsRevocationDone(builder->bestPath)) {
1117 CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
1118 if (nextUpdate != 0) {
1119 #else
1120 /* We don't do networking on watchOS, so we can't require OCSP for EV */
1121 {
1122 {
1123 #endif
1124 /* Successful revocation check, so this cert is EV */
1125 CFDictionarySetValue(builder->info, kSecTrustInfoExtendedValidationKey,
1126 kCFBooleanTrue); /* iOS key */
1127 CFDictionarySetValue(builder->info, kSecTrustExtendedValidation,
1128 kCFBooleanTrue); /* unified API key */
1129 SecCertificateRef leaf = SecPathBuilderGetCertificateAtIndex(builder, 0);
1130 CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
1131 if (leafCompanyName) {
1132 CFDictionarySetValue(builder->info, kSecTrustInfoCompanyNameKey,
1133 leafCompanyName); /* iOS key */
1134 CFDictionarySetValue(builder->info, kSecTrustOrganizationName,
1135 leafCompanyName); /* unified API key */
1136 CFRelease(leafCompanyName);
1137 }
1138 }
1139 }
1140 }
1141
1142 if (builder->info && SecPathBuilderIsOkResult(builder) && SecCertificatePathVCIsRevocationDone(builder->bestPath)) {
1143 CFAbsoluteTime nextUpdate = SecCertificatePathVCGetEarliestNextUpdate(builder->bestPath);
1144 if (nextUpdate != 0) {
1145 /* always populate revocation info for successful revocation check */
1146 CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
1147 CFDictionarySetValue(builder->info, kSecTrustInfoRevocationValidUntilKey,
1148 validUntil); /* iOS key */
1149 CFDictionarySetValue(builder->info, kSecTrustRevocationValidUntilDate,
1150 validUntil); /* unified API key */
1151 CFRelease(validUntil);
1152 CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
1153 kCFBooleanTrue); /* iOS key */
1154 CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
1155 kCFBooleanTrue); /* unified API key */
1156 } else if (SecCertificatePathVCIsEV(builder->bestPath)) {
1157 /* populate revocation info for failed revocation check with EV */
1158 CFDictionarySetValue(builder->info, kSecTrustInfoRevocationKey,
1159 kCFBooleanFalse); /* iOS key */
1160 CFDictionarySetValue(builder->info, kSecTrustRevocationChecked,
1161 kCFBooleanFalse); /* unified API key */
1162 }
1163 }
1164
1165 if (builder->info && SecCertificatePathVCIsCT(builder->bestPath) && SecPathBuilderIsOkResult(builder)) {
1166 CFDictionarySetValue(builder->info, kSecTrustInfoCertificateTransparencyKey,
1167 kCFBooleanTrue);
1168 }
1169
1170
1171 /* This will trigger the outer step function to call the completion
1172 function. */
1173 builder->state = NULL;
1174 return false;
1175 }
1176
1177 /* @function SecPathBuilderStep
1178 @summary This is the core of the async engine.
1179 @description Return false iff job is complete, true if a network request
1180 is pending.
1181 builder->state is a function pointer which is to be invoked.
1182 If you call this function from within a builder->state invocation it
1183 immediately returns true.
1184 Otherwise the following steps are repeated endlessly (unless a step returns)
1185 builder->state is invoked. If it returns true and builder->state is still
1186 non NULL this proccess is repeated.
1187 If a state returns false, SecPathBuilder will return true
1188 if builder->state is non NULL.
1189 If builder->state is NULL then regardless of what the state function returns
1190 the completion callback will be invoked and the builder will be deallocated.
1191 */
1192 bool SecPathBuilderStep(SecPathBuilderRef builder) {
1193 if (builder->activations) {
1194 secdebug("async", "activations: %lu returning true",
1195 builder->activations);
1196 return true;
1197 }
1198
1199 secdebug("async", "activations: %lu", builder->activations);
1200 builder->activations++;
1201 while (builder->state && builder->state(builder));
1202 --builder->activations;
1203
1204 if (builder->state) {
1205 secdebug("async", "waiting for async reply, exiting");
1206 /* A state returned false, it's waiting for network traffic. Let's
1207 return. */
1208 return true;
1209 }
1210
1211 if (builder->activations) {
1212 /* There is still at least one other running instance of this builder
1213 somewhere on the stack, we let that instance take care of sending
1214 the client a response. */
1215 return false;
1216 }
1217
1218 SecPVCRef pvc = SecPathBuilderGetResultPVC(builder);
1219 SecTrustResultType result = pvc->result;
1220
1221 if (builder->exceptions && pvc->result == kSecTrustResultUnspecified) {
1222 result = kSecTrustResultProceed;
1223 }
1224
1225 secinfo("trust", "completed: %@ details: %@ result: %d",
1226 builder->bestPath, pvc->details, result);
1227
1228 if (builder->completed) {
1229 SecCertificatePathRef resultPath = SecCertificatePathVCCopyCertificatePath(builder->bestPath);
1230 builder->completed(builder->context, resultPath,
1231 pvc->details, builder->info, result);
1232 CFReleaseNull(resultPath);
1233 }
1234
1235 /* Finally, destroy the builder and free it. */
1236 SecPathBuilderDestroy(builder);
1237 free(builder);
1238
1239 return false;
1240 }
1241
1242 dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
1243 return (builder) ? builder->queue : NULL;
1244 }
1245
1246 CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
1247 return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
1248 }
1249
1250 // MARK: -
1251 // MARK: SecTrustServer
1252 /********************************************************
1253 ****************** SecTrustServer **********************
1254 ********************************************************/
1255
1256 typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
1257
1258 static void
1259 SecTrustServerEvaluateCompleted(const void *userData,
1260 SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
1261 SecTrustResultType result) {
1262 SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
1263 evaluated(result, details, info, chain, NULL);
1264 Block_release(evaluated);
1265 }
1266
1267 void
1268 SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
1269 /* We need an array containing at least one certificate to proceed. */
1270 if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
1271 CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
1272 evaluated(kSecTrustResultInvalid, NULL, NULL, NULL, certError);
1273 CFReleaseSafe(certError);
1274 return;
1275 }
1276 SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
1277 /* Call the actual evaluator function. */
1278 SecPathBuilderRef builder = SecPathBuilderCreate(clientAuditToken,
1279 certificates, anchors,
1280 anchorsOnly, keychainsAllowed, policies,
1281 responses, SCTs, trustedLogs,
1282 verifyTime, accessGroups, exceptions,
1283 SecTrustServerEvaluateCompleted, userData);
1284 dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
1285 }
1286
1287
1288 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1289 SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef exceptions, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
1290 dispatch_semaphore_t done = dispatch_semaphore_create(0);
1291 __block SecTrustResultType result = kSecTrustResultInvalid;
1292 SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, exceptions, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
1293 result = tr;
1294 if (tr == kSecTrustResultInvalid) {
1295 if (perror) {
1296 *perror = error;
1297 CFRetainSafe(error);
1298 }
1299 } else {
1300 if (pdetails) {
1301 *pdetails = details;
1302 CFRetainSafe(details);
1303 }
1304 if (pinfo) {
1305 *pinfo = info;
1306 CFRetainSafe(info);
1307 }
1308 if (pchain) {
1309 *pchain = chain;
1310 CFRetainSafe(chain);
1311 }
1312 }
1313 dispatch_semaphore_signal(done);
1314 });
1315 dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
1316 dispatch_release(done);
1317
1318 return result;
1319 }