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