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