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