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