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