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