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