]> git.saurik.com Git - apple/security.git/blob - trust/trustd/SecTrustServer.c
Security-59306.61.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 #if TARGET_OS_IPHONE
246 CFArrayAppendValue(builder->parentSources, kSecUserAnchorSource);
247 #endif
248 }
249 if (keychainsAllowed && builder->canAccessNetwork) {
250 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
251 }
252 #else /* TARGET_OS_BRIDGE */
253 /* Bridge can only access memory sources. */
254 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
255 if (anchorsOnly) {
256 /* Add the Apple, system, and user anchor certificate db to the search list
257 if we don't explicitly trust them. */
258 CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
259 }
260 #endif /* !TARGET_OS_BRIDGE */
261
262 /** Anchor Sources
263 ** The order here allows a client-provided anchor to overrule
264 ** a user or admin trust setting which can overrule the system anchors.
265 ** Apple's anchors cannot be overriden by a trust setting.
266 **/
267 #if !TARGET_OS_BRIDGE
268 if (builder->anchorSource) {
269 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
270 }
271 if (!anchorsOnly) {
272 /* Only add the system and user anchor certificate db to the
273 anchorSources if we are supposed to trust them. */
274 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
275 if (keychainsAllowed) {
276 #if TARGET_OS_IPHONE
277 CFArrayAppendValue(builder->anchorSources, kSecUserAnchorSource);
278 #else /* TARGET_OS_OSX */
279 if (kSecLegacyAnchorSource->contains && kSecLegacyAnchorSource->copyParents) {
280 CFArrayAppendValue(builder->anchorSources, kSecLegacyAnchorSource);
281 }
282 #endif
283 }
284 CFArrayAppendValue(builder->anchorSources, kSecSystemAnchorSource);
285 }
286 #else /* TARGET_OS_BRIDGE */
287 /* Bridge can only access memory sources. */
288 if (builder->anchorSource) {
289 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
290 }
291 if (!anchorsOnly) {
292 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
293 }
294 #endif /* !TARGET_OS_BRIDGE */
295
296 builder->ocspResponses = CFRetainSafe(ocspResponses);
297 builder->signedCertificateTimestamps = CFRetainSafe(signedCertificateTimestamps);
298
299 if(trustedLogs) {
300 builder->trustedLogs = SecOTAPKICreateTrustedCTLogsDictionaryFromArray(trustedLogs);
301 }
302
303 /* Now let's get the leaf cert and turn it into a path. */
304 SecCertificateRef leaf =
305 (SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
306 SecCertificatePathVCRef path = SecCertificatePathVCCreate(NULL, leaf, NULL);
307 CFSetAddValue(builder->allPaths, path);
308 CFArrayAppendValue(builder->partialPaths, path);
309
310 builder->path = CFRetainSafe(path);
311 SecPathBuilderSetPath(builder, path);
312 CFRelease(path);
313
314 /* Next step is to process the leaf. We do that work on the builder queue
315 * to avoid blocking the main thread with database lookups. */
316 builder->state = SecPathBuilderProcessLeaf;
317 builder->completed = completed;
318 builder->context = context;
319 }
320
321 SecPathBuilderRef SecPathBuilderCreate(dispatch_queue_t builderQueue, CFDataRef clientAuditToken,
322 CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
323 bool keychainsAllowed, CFArrayRef policies, CFArrayRef ocspResponses,
324 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
325 CFAbsoluteTime verifyTime, CFArrayRef accessGroups, CFArrayRef exceptions,
326 SecPathBuilderCompleted completed, const void *context) {
327 SecPathBuilderRef builder = malloc(sizeof(*builder));
328 memset(builder, 0, sizeof(*builder));
329 SecPathBuilderInit(builder, builderQueue, clientAuditToken, certificates,
330 anchors, anchorsOnly, keychainsAllowed, policies, ocspResponses,
331 signedCertificateTimestamps, trustedLogs, verifyTime,
332 accessGroups, exceptions, completed, context);
333 return builder;
334 }
335
336 /* Don't use this if you're going to modify the PVC array in the operation. */
337 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder,void (^operation)(SecPVCRef pvc, bool *stop)) {
338 if (!builder->pvcs) { return; }
339 bool stop = false;
340 CFIndex ix;
341 for (ix = 0; ix < builder->pvcCount; ix++) {
342 if (!builder->pvcs[ix]) { continue; }
343 operation(builder->pvcs[ix], &stop);
344 if (stop) { break; }
345 }
346 }
347
348 static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
349 secdebug("alloc", "destroy builder %p", builder);
350 dispatch_release_null(builder->queue);
351 if (builder->anchorSource) {
352 SecMemoryCertificateSourceDestroy(builder->anchorSource);
353 builder->anchorSource = NULL;
354 }
355 if (builder->certificateSource) {
356 SecMemoryCertificateSourceDestroy(builder->certificateSource);
357 builder->certificateSource = NULL;
358 }
359 if (builder->itemCertificateSource) {
360 SecItemCertificateSourceDestroy(builder->itemCertificateSource);
361 builder->itemCertificateSource = NULL;
362 }
363 if (builder->appleAnchorSource) {
364 SecMemoryCertificateSourceDestroy(builder->appleAnchorSource);
365 builder->appleAnchorSource = NULL;
366 }
367 CFReleaseNull(builder->clientAuditToken);
368 CFReleaseNull(builder->anchorSources);
369 CFReleaseNull(builder->parentSources);
370 CFReleaseNull(builder->allPaths);
371 CFReleaseNull(builder->partialPaths);
372 CFReleaseNull(builder->rejectedPaths);
373 CFReleaseNull(builder->candidatePaths);
374 CFReleaseNull(builder->ocspResponses);
375 CFReleaseNull(builder->signedCertificateTimestamps);
376 CFReleaseNull(builder->trustedLogs);
377 CFReleaseNull(builder->path);
378 CFReleaseNull(builder->revocation_check_method);
379 CFReleaseNull(builder->info);
380 CFReleaseNull(builder->exceptions);
381
382 free(builder->analyticsData);
383 builder->analyticsData = NULL;
384
385 if (builder->pvcs) {
386 CFIndex ix;
387 for (ix = 0; ix < builder->pvcCount; ix++) {
388 if (builder->pvcs[ix]) {
389 SecPVCDelete(builder->pvcs[ix]);
390 free(builder->pvcs[ix]);
391 }
392 }
393 free(builder->pvcs);
394 builder->pvcs = NULL;
395 }
396 }
397
398 static void SecPathBuilderSetPath(SecPathBuilderRef builder, SecCertificatePathVCRef path) {
399 bool samePath = ((!path && !builder->path) || (path && builder->path && CFEqual(path, builder->path)));
400 if (!samePath) {
401 CFRetainAssign(builder->path, path);
402 }
403 CFReleaseNull(builder->info);
404
405 SecPathBuilderForEachPVC(builder, ^(SecPVCRef pvc, bool * __unused stop) {
406 SecPVCSetPath(pvc, path);
407 });
408 }
409
410
411 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
412 return builder->canAccessNetwork;
413 }
414
415 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
416 if (builder->canAccessNetwork != allow) {
417 builder->canAccessNetwork = allow;
418 if (allow) {
419 #if !TARGET_OS_WATCH
420 secinfo("http", "network access re-enabled by policy");
421 /* re-enabling network_access re-adds kSecCAIssuerSource as
422 a parent source. */
423 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
424 #else
425 /* <rdar://32728029> */
426 secnotice("http", "network access not allowed on WatchOS");
427 builder->canAccessNetwork = false;
428 #endif
429 } else {
430 secinfo("http", "network access disabled by policy");
431 /* disabling network_access removes kSecCAIssuerSource from
432 the list of parent sources. */
433 CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
434 CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
435 kSecCAIssuerSource);
436 if (ix >= 0)
437 CFArrayRemoveValueAtIndex(builder->parentSources, ix);
438 }
439 }
440 }
441
442 CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
443 {
444 return CFRetainSafe(builder->ocspResponses);
445 }
446
447 CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
448 {
449 return CFRetainSafe(builder->signedCertificateTimestamps);
450 }
451
452 CFDictionaryRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
453 {
454 return CFRetainSafe(builder->trustedLogs);
455 }
456
457 SecCertificateSourceRef SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder)
458 {
459 return builder->anchorSource;
460 }
461
462 CFSetRef SecPathBuilderGetAllPaths(SecPathBuilderRef builder)
463 {
464 return builder->allPaths;
465 }
466
467 TrustAnalyticsBuilder *SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder)
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 }
1214 }
1215
1216 /* If revoked, set the revocation reason */
1217 if (builder->info && !SecPathBuilderIsOkResult(builder) && SecCertificatePathVCIsRevocationDone(builder->bestPath)
1218 && SecCertificatePathVCGetRevocationReason(builder->bestPath)) {
1219 CFNumberRef reason = SecCertificatePathVCGetRevocationReason(builder->bestPath);
1220 CFDictionarySetValue(builder->info, kSecTrustRevocationReason, reason);
1221 }
1222
1223 /* Set CT marker in the info */
1224 if (builder->info && SecCertificatePathVCIsCT(builder->bestPath) && SecPathBuilderIsOkResult(builder)) {
1225 CFDictionarySetValue(builder->info, kSecTrustInfoCertificateTransparencyKey,
1226 kCFBooleanTrue);
1227 }
1228
1229
1230 /* This will trigger the outer step function to call the completion
1231 function. */
1232 builder->state = NULL;
1233 return false;
1234 }
1235
1236 /* @function SecPathBuilderStep
1237 @summary This is the core of the async engine.
1238 @description Return false iff job is complete, true if a network request
1239 is pending.
1240 builder->state is a function pointer which is to be invoked.
1241 If you call this function from within a builder->state invocation it
1242 immediately returns true.
1243 Otherwise the following steps are repeated endlessly (unless a step returns)
1244 builder->state is invoked. If it returns true and builder->state is still
1245 non NULL this proccess is repeated.
1246 If a state returns false, SecPathBuilder will return true
1247 if builder->state is non NULL.
1248 If builder->state is NULL then regardless of what the state function returns
1249 the completion callback will be invoked and the builder will be deallocated.
1250 */
1251 bool SecPathBuilderStep(SecPathBuilderRef builder) {
1252 secdebug("async", "step builder %p", builder);
1253 if (builder->activations) {
1254 secdebug("async", "activations: %lu returning true",
1255 builder->activations);
1256 return true;
1257 }
1258
1259 secdebug("async", "activations: %lu", builder->activations);
1260 builder->activations++;
1261 while (builder->state && builder->state(builder));
1262 --builder->activations;
1263
1264 if (builder->state) {
1265 secdebug("async", "waiting for async reply, exiting");
1266 /* A state returned false, it's waiting for network traffic. Let's
1267 return. */
1268 return true;
1269 }
1270
1271 if (builder->activations) {
1272 /* There is still at least one other running instance of this builder
1273 somewhere on the stack, we let that instance take care of sending
1274 the client a response. */
1275 return false;
1276 }
1277
1278 SecPVCRef pvc = SecPathBuilderGetResultPVC(builder);
1279 SecTrustResultType result = pvc->result;
1280
1281 if (builder->exceptions && pvc->result == kSecTrustResultUnspecified) {
1282 result = kSecTrustResultProceed;
1283 }
1284
1285 secinfo("trust", "completed: %@ details: %@ result: %d",
1286 builder->bestPath, pvc->details, result);
1287
1288 if (builder->completed) {
1289 /* We want to retain just the data we need to return to our caller
1290 * and free the rest of the builder before doing the callback.
1291 * Since the callback may end an XPC transaction that made us active, we
1292 * want to retain as little residual memory as possible. */
1293 CFArrayRef resultPath = SecCertificatePathVCCopyCertificates(builder->bestPath);
1294 CFDictionaryRef info = CFRetainSafe(builder->info);
1295 CFArrayRef details = CFRetainSafe(pvc->details);
1296 const void *context = builder->context;
1297 SecPathBuilderCompleted completed = builder->completed;
1298
1299 secdebug("async", "free builder");
1300 SecPathBuilderDestroy(builder);
1301 free(builder);
1302
1303 secdebug("async", "returning to caller");
1304 completed(context, resultPath, details, info, result);
1305 CFReleaseNull(resultPath);
1306 CFReleaseNull(info);
1307 CFReleaseNull(details);
1308 } else {
1309 SecPathBuilderDestroy(builder);
1310 free(builder);
1311 }
1312
1313 return false;
1314 }
1315
1316 dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
1317 return (builder) ? builder->queue : NULL;
1318 }
1319
1320 CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
1321 return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
1322 }
1323
1324 // MARK: -
1325 // MARK: SecTrustServer
1326 /********************************************************
1327 ****************** SecTrustServer **********************
1328 ********************************************************/
1329
1330 typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error);
1331
1332 static void
1333 SecTrustServerEvaluateCompleted(const void *userData,
1334 CFArrayRef chain, CFArrayRef details, CFDictionaryRef info,
1335 SecTrustResultType result) {
1336 SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
1337 TrustdHealthAnalyticsLogEvaluationCompleted();
1338 evaluated(result, details, info, chain, NULL);
1339 Block_release(evaluated);
1340 }
1341
1342 void
1343 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)) {
1344 /* We need an array containing at least one certificate to proceed. */
1345 if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
1346 CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
1347 evaluated(kSecTrustResultInvalid, NULL, NULL, NULL, certError);
1348 CFReleaseSafe(certError);
1349 return;
1350 }
1351 SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
1352 /* Call the actual evaluator function. */
1353 SecPathBuilderRef builder = SecPathBuilderCreate(builderQueue, clientAuditToken,
1354 certificates, anchors,
1355 anchorsOnly, keychainsAllowed, policies,
1356 responses, SCTs, trustedLogs,
1357 verifyTime, accessGroups, exceptions,
1358 SecTrustServerEvaluateCompleted, userData);
1359 SecPathBuilderStep(builder);
1360 }
1361
1362
1363 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1364 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) {
1365 dispatch_semaphore_t done = dispatch_semaphore_create(0);
1366 __block SecTrustResultType result = kSecTrustResultInvalid;
1367 __block dispatch_queue_t queue = dispatch_queue_create("com.apple.trustd.evaluation.recursive", DISPATCH_QUEUE_SERIAL);
1368
1369 /* We need to use the async call with the semaphore here instead of a synchronous call because we may return from
1370 * SecPathBuilderStep while waiting for an asynchronous network call in order to complete the evaluation. That return
1371 * is necessary in the XPC interface in order to free up the workloop for other trust evaluations while we wait for
1372 * the networking to complete, but here, we need to make sure we wait for the network call (which will async back
1373 * onto our queue) to complete and signal us before we return to the "inline" caller. */
1374 dispatch_async(queue, ^{
1375 SecTrustServerEvaluateBlock(queue, NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, exceptions, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, CFArrayRef chain, CFErrorRef error) {
1376 result = tr;
1377 if (tr == kSecTrustResultInvalid) {
1378 if (perror) {
1379 *perror = error;
1380 CFRetainSafe(error);
1381 }
1382 } else {
1383 if (pdetails) {
1384 *pdetails = details;
1385 CFRetainSafe(details);
1386 }
1387 if (pinfo) {
1388 *pinfo = info;
1389 CFRetainSafe(info);
1390 }
1391 if (pchain) {
1392 *pchain = chain;
1393 CFRetainSafe(chain);
1394 }
1395 }
1396 dispatch_semaphore_signal(done);
1397 });
1398 });
1399 dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
1400 dispatch_release(done);
1401 dispatch_release_null(queue);
1402
1403 return result;
1404 }