]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecTrustServer.c
Security-57740.51.3.tar.gz
[apple/security.git] / OSX / sec / securityd / SecTrustServer.c
1 /*
2 * Copyright (c) 2006-2010,2012-2016 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
33 #include <utilities/SecIOFormat.h>
34 #include <utilities/SecDispatchRelease.h>
35 #include <utilities/SecAppleAnchorPriv.h>
36
37 #include <Security/SecTrustPriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <Security/SecCertificatePath.h>
41 #include <Security/SecFramework.h>
42 #include <Security/SecPolicyPriv.h>
43 #include <Security/SecPolicyInternal.h>
44 #include <Security/SecTrustSettingsPriv.h>
45 #include <Security/SecTask.h>
46 #include <CoreFoundation/CFRuntime.h>
47 #include <CoreFoundation/CFSet.h>
48 #include <CoreFoundation/CFString.h>
49 #include <CoreFoundation/CFNumber.h>
50 #include <CoreFoundation/CFArray.h>
51 #include <CoreFoundation/CFPropertyList.h>
52 #include <AssertMacros.h>
53 #include <stdbool.h>
54 #include <string.h>
55 #include <stdlib.h>
56 #include <limits.h>
57 #include <sys/codesign.h>
58 #include <Security/SecBase.h>
59 #include "SecRSAKey.h"
60 #include <libDER/oids.h>
61 #include <utilities/debugging.h>
62 #include <utilities/SecCFWrappers.h>
63 #include <Security/SecInternal.h>
64 #include <ipc/securityd_client.h>
65 #include <CommonCrypto/CommonDigest.h>
66 #include "OTATrustUtilities.h"
67 #include "personalization.h"
68 #include <utilities/SecInternalReleasePriv.h>
69
70 #if TARGET_OS_OSX
71 #include <Security/SecTaskPriv.h>
72 #endif
73
74 #define MAX_CHAIN_LENGTH 15
75 #define ACCEPT_PATH_SCORE 10000000
76
77 /* Forward declaration for use in SecCertificateSource. */
78 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents);
79
80 // MARK: -
81 // MARK: SecPathBuilder
82 /********************************************************
83 *************** SecPathBuilder object ******************
84 ********************************************************/
85 struct SecPathBuilder {
86 dispatch_queue_t queue;
87 CFDataRef clientAuditToken;
88 SecCertificateSourceRef certificateSource;
89 SecCertificateSourceRef itemCertificateSource;
90 SecCertificateSourceRef anchorSource;
91 SecCertificateSourceRef appleAnchorSource;
92 CFMutableArrayRef anchorSources;
93 CFIndex nextParentSource;
94 CFMutableArrayRef parentSources;
95 CFArrayRef ocspResponses; // Stapled OCSP responses
96 CFArrayRef signedCertificateTimestamps; // Stapled SCTs
97 CFArrayRef trustedLogs; // Trusted CT logs
98
99 /* Hashed set of all paths we've constructed so far, used to prevent
100 re-considering a path that was already constructed once before.
101 Note that this is the only container in which certificatePath
102 objects are retained.
103 Every certificatePath being considered is always in allPaths and in at
104 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
105 all of which don't retain their values. */
106 CFMutableSetRef allPaths;
107
108 /* No trusted anchor, satisfies the linking to intermediates for all
109 policies (unless considerRejected is true). */
110 CFMutableArrayRef partialPaths;
111 /* No trusted anchor, does not satisfy linking to intermediates for all
112 policies. */
113 CFMutableArrayRef rejectedPaths;
114 /* Trusted anchor, satisfies the policies so far. */
115 CFMutableArrayRef candidatePaths;
116
117 CFIndex partialIX;
118
119 CFArrayRef leafDetails;
120
121 CFIndex bestPathScore;
122
123 bool considerRejected;
124 bool considerPartials;
125 bool canAccessNetwork;
126
127 struct OpaqueSecPVC path;
128 SecCertificatePathRef bestPath;
129 bool bestPathIsEV;
130 bool bestPathIsSHA2;
131 bool denyBestPath;
132
133 CFIndex activations;
134 bool (*state)(SecPathBuilderRef);
135 SecPathBuilderCompleted completed;
136 const void *context;
137 };
138
139 /* State functions. Return false if a async job was scheduled, return
140 true to execute the next state. */
141 static bool SecPathBuilderGetNext(SecPathBuilderRef builder);
142 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder);
143 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder);
144 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder);
145 static bool SecPathBuilderReportResult(SecPathBuilderRef builder);
146
147 /* Forward declarations. */
148 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
149 SecCertificateRef certificate, SecCertificateSourceRef *foundInSource);
150
151 /* IDEA: policies could be made capable of replacing incoming anchors and
152 anchorsOnly argument values. For example, some policies require the
153 Apple Inc. CA and not any other anchor. This can be done in
154 SecPathBuilderLeafCertificateChecks since this only runs once. */
155 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder,
156 SecCertificatePathRef path) {
157 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
158 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
159 &kCFTypeDictionaryValueCallBacks);
160 builder->leafDetails = CFArrayCreate(kCFAllocatorDefault,
161 (const void **)&certDetail, 1, &kCFTypeArrayCallBacks);
162 CFRelease(certDetail);
163 SecPVCRef pvc = &builder->path;
164 SecPVCSetPath(pvc, path, builder->leafDetails);
165 builder->considerRejected = !SecPVCLeafChecks(pvc);
166 }
167
168 static void SecPathBuilderInit(SecPathBuilderRef builder,
169 CFDataRef clientAuditToken, CFArrayRef certificates,
170 CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed,
171 CFArrayRef policies, CFArrayRef ocspResponses,
172 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
173 CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
174 SecPathBuilderCompleted completed, const void *context) {
175 secdebug("alloc", "%p", builder);
176 CFAllocatorRef allocator = kCFAllocatorDefault;
177
178 builder->clientAuditToken = (CFDataRef)
179 ((clientAuditToken) ? CFRetain(clientAuditToken) : NULL);
180 builder->queue = dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL);
181
182 builder->nextParentSource = 1;
183 #if !TARGET_OS_WATCH
184 builder->canAccessNetwork = true;
185 #endif
186
187 builder->anchorSources = CFArrayCreateMutable(allocator, 0, NULL);
188 builder->parentSources = CFArrayCreateMutable(allocator, 0, NULL);
189 builder->allPaths = CFSetCreateMutable(allocator, 0,
190 &kCFTypeSetCallBacks);
191
192 builder->partialPaths = CFArrayCreateMutable(allocator, 0, NULL);
193 builder->rejectedPaths = CFArrayCreateMutable(allocator, 0, NULL);
194 builder->candidatePaths = CFArrayCreateMutable(allocator, 0, NULL);
195
196 /* Init the policy verification context. */
197 SecPVCInit(&builder->path, builder, policies, verifyTime);
198
199 /* Let's create all the certificate sources we might want to use. */
200 builder->certificateSource =
201 SecMemoryCertificateSourceCreate(certificates);
202 if (anchors) {
203 builder->anchorSource = SecMemoryCertificateSourceCreate(anchors);
204 }
205
206 bool allowNonProduction = false;
207 builder->appleAnchorSource = SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction));
208
209
210 /** Parent Sources
211 ** The order here avoids the most expensive methods if the cheaper methods
212 ** produce an acceptable chain: client-provided, keychains, network-fetched.
213 **/
214 #if !TARGET_OS_BRIDGE
215 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
216 builder->itemCertificateSource = SecItemCertificateSourceCreate(accessGroups);
217 if (keychainsAllowed) {
218 CFArrayAppendValue(builder->parentSources, builder->itemCertificateSource);
219 #if TARGET_OS_OSX
220 /* On OS X, need additional parent source to search legacy keychain files. */
221 if (kSecLegacyCertificateSource->contains && kSecLegacyCertificateSource->copyParents) {
222 CFArrayAppendValue(builder->parentSources, kSecLegacyCertificateSource);
223 }
224 #endif
225 }
226 if (anchorsOnly) {
227 /* Add the Apple, system, and user anchor certificate db to the search list
228 if we don't explicitly trust them. */
229 CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
230 CFArrayAppendValue(builder->parentSources, kSecSystemAnchorSource);
231 #if TARGET_OS_IPHONE
232 CFArrayAppendValue(builder->parentSources, kSecUserAnchorSource);
233 #endif
234 }
235 if (keychainsAllowed && builder->canAccessNetwork) {
236 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
237 }
238 #else /* TARGET_OS_BRIDGE */
239 /* Bridge can only access memory sources. */
240 CFArrayAppendValue(builder->parentSources, builder->certificateSource);
241 if (anchorsOnly) {
242 /* Add the Apple, system, and user anchor certificate db to the search list
243 if we don't explicitly trust them. */
244 CFArrayAppendValue(builder->parentSources, builder->appleAnchorSource);
245 }
246 #endif /* !TARGET_OS_BRIDGE */
247
248 /** Anchor Sources
249 ** The order here allows a client-provided anchor to overrule
250 ** a user or admin trust setting which can overrule the system anchors.
251 ** Apple's anchors cannot be overriden by a trust setting.
252 **/
253 #if !TARGET_OS_BRIDGE
254 if (builder->anchorSource) {
255 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
256 }
257 if (!anchorsOnly) {
258 /* Only add the system and user anchor certificate db to the
259 anchorSources if we are supposed to trust them. */
260 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
261 #if TARGET_OS_IPHONE
262 CFArrayAppendValue(builder->anchorSources, kSecUserAnchorSource);
263 #else /* TARGET_OS_OSX */
264 if (keychainsAllowed && kSecLegacyAnchorSource->contains && kSecLegacyAnchorSource->copyParents) {
265 CFArrayAppendValue(builder->anchorSources, kSecLegacyAnchorSource);
266 }
267 #endif
268 CFArrayAppendValue(builder->anchorSources, kSecSystemAnchorSource);
269 }
270 #else /* TARGET_OS_BRIDGE */
271 /* Bridge can only access memory sources. */
272 if (builder->anchorSource) {
273 CFArrayAppendValue(builder->anchorSources, builder->anchorSource);
274 }
275 if (!anchorsOnly) {
276 CFArrayAppendValue(builder->anchorSources, builder->appleAnchorSource);
277 }
278 #endif /* !TARGET_OS_BRIDGE */
279
280 /* Now let's get the leaf cert and turn it into a path. */
281 SecCertificateRef leaf =
282 (SecCertificateRef)CFArrayGetValueAtIndex(certificates, 0);
283 SecCertificateSourceRef source = NULL;
284 bool isAnchor = false;
285 CFArrayRef constraints = NULL;
286 if (SecPathBuilderIsAnchor(builder, leaf, &source)) {
287 isAnchor = true;
288 }
289 if (source) {
290 constraints = SecCertificateSourceCopyUsageConstraints(source, leaf);
291 }
292 SecCertificatePathRef path = SecCertificatePathCreate(NULL, leaf, constraints);
293 CFReleaseSafe(constraints);
294 CFSetAddValue(builder->allPaths, path);
295 CFArrayAppendValue(builder->partialPaths, path);
296 if (isAnchor) {
297 SecCertificatePathSetIsAnchored(path);
298 CFArrayAppendValue(builder->candidatePaths, path);
299 }
300 SecPathBuilderLeafCertificateChecks(builder, path);
301 CFRelease(path);
302
303 builder->ocspResponses = CFRetainSafe(ocspResponses);
304 builder->signedCertificateTimestamps = CFRetainSafe(signedCertificateTimestamps);
305
306 if(trustedLogs) {
307 builder->trustedLogs = CFRetainSafe(trustedLogs);
308 } else {
309 SecOTAPKIRef otapkiref = SecOTAPKICopyCurrentOTAPKIRef();
310 builder->trustedLogs = SecOTAPKICopyTrustedCTLogs(otapkiref);
311 CFReleaseSafe(otapkiref);
312 }
313
314 builder->state = SecPathBuilderGetNext;
315 builder->completed = completed;
316 builder->context = context;
317 }
318
319 SecPathBuilderRef SecPathBuilderCreate(CFDataRef clientAuditToken,
320 CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly,
321 bool keychainsAllowed, CFArrayRef policies, CFArrayRef ocspResponses,
322 CFArrayRef signedCertificateTimestamps, CFArrayRef trustedLogs,
323 CFAbsoluteTime verifyTime, CFArrayRef accessGroups,
324 SecPathBuilderCompleted completed, const void *context) {
325 SecPathBuilderRef builder = malloc(sizeof(*builder));
326 memset(builder, 0, sizeof(*builder));
327 SecPathBuilderInit(builder, clientAuditToken, certificates,
328 anchors, anchorsOnly, keychainsAllowed, policies, ocspResponses,
329 signedCertificateTimestamps, trustedLogs, verifyTime,
330 accessGroups, completed, context);
331 return builder;
332 }
333
334 static void SecPathBuilderDestroy(SecPathBuilderRef builder) {
335 secdebug("alloc", "%p", builder);
336 dispatch_release_null(builder->queue);
337 if (builder->anchorSource) {
338 SecMemoryCertificateSourceDestroy(builder->anchorSource); }
339 if (builder->certificateSource) {
340 SecMemoryCertificateSourceDestroy(builder->certificateSource); }
341 if (builder->itemCertificateSource) {
342 SecItemCertificateSourceDestroy(builder->itemCertificateSource); }
343 if (builder->appleAnchorSource) {
344 SecMemoryCertificateSourceDestroy(builder->appleAnchorSource); }
345 CFReleaseSafe(builder->clientAuditToken);
346 CFReleaseSafe(builder->anchorSources);
347 CFReleaseSafe(builder->parentSources);
348 CFReleaseSafe(builder->allPaths);
349 CFReleaseSafe(builder->partialPaths);
350 CFReleaseSafe(builder->rejectedPaths);
351 CFReleaseSafe(builder->candidatePaths);
352 CFReleaseSafe(builder->leafDetails);
353 CFReleaseSafe(builder->ocspResponses);
354 CFReleaseSafe(builder->signedCertificateTimestamps);
355 CFReleaseSafe(builder->trustedLogs);
356
357 SecPVCDelete(&builder->path);
358 }
359
360 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
361 return builder->canAccessNetwork;
362 }
363
364 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
365 if (builder->canAccessNetwork != allow) {
366 builder->canAccessNetwork = allow;
367 if (allow) {
368 #if !TARGET_OS_WATCH
369 secinfo("http", "network access re-enabled by policy");
370 /* re-enabling network_access re-adds kSecCAIssuerSource as
371 a parent source. */
372 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
373 #else
374 secnotice("http", "network access not allowed on WatchOS");
375 builder->canAccessNetwork = false;
376 #endif
377 } else {
378 secinfo("http", "network access disabled by policy");
379 /* disabling network_access removes kSecCAIssuerSource from
380 the list of parent sources. */
381 CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
382 CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
383 kSecCAIssuerSource);
384 if (ix >= 0)
385 CFArrayRemoveValueAtIndex(builder->parentSources, ix);
386 }
387 }
388 }
389
390 CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
391 {
392 return CFRetainSafe(builder->ocspResponses);
393 }
394
395 CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
396 {
397 return CFRetainSafe(builder->signedCertificateTimestamps);
398 }
399
400 CFArrayRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
401 {
402 return CFRetainSafe(builder->trustedLogs);
403 }
404
405 SecCertificatePathRef SecPathBuilderGetBestPath(SecPathBuilderRef builder)
406 {
407 return builder->bestPath;
408 }
409
410 /* This function assumes that the input source is an anchor source */
411 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder, SecCertificateSourceRef source,
412 SecCertificateRef certificate) {
413 bool result = false;
414 CFArrayRef constraints = NULL;
415 constraints = SecCertificateSourceCopyUsageConstraints(source, certificate);
416
417 /* Unrestricted certificates:
418 * -those that come from anchor sources with no constraints
419 * -self-signed certificates with empty contraints arrays
420 */
421 Boolean selfSigned = false;
422 require(errSecSuccess == SecCertificateIsSelfSigned(certificate, &selfSigned), out);
423 if ((NULL == source->copyUsageConstraints) ||
424 (constraints && (CFArrayGetCount(constraints) == 0) && selfSigned)) {
425 secinfo("trust", "unrestricted anchor%s",
426 (NULL == source->copyUsageConstraints) ? " source" : "");
427 result = true;
428 goto out;
429 }
430
431 /* Get the trust settings result for the PVC */
432 require(constraints, out);
433 SecTrustSettingsResult settingsResult = kSecTrustSettingsResultInvalid;
434 settingsResult = SecPVCGetTrustSettingsResult(&builder->path,
435 certificate,
436 constraints);
437 if ((selfSigned && settingsResult == kSecTrustSettingsResultTrustRoot) ||
438 (!selfSigned && settingsResult == kSecTrustSettingsResultTrustAsRoot)) {
439 // For our purposes, this is an anchor.
440 secinfo("trust", "complex trust settings anchor");
441 result = true;
442 }
443
444 if (settingsResult == kSecTrustSettingsResultDeny) {
445 /* We consider denied certs "anchors" because the trust decision
446 is set regardless of building the chain further. The policy
447 validation will handle rejecting this chain. */
448 secinfo("trust", "complex trust settings denied anchor");
449 result = true;
450 }
451
452 out:
453 CFReleaseNull(constraints);
454 return result;
455 }
456
457 /* Source returned in foundInSource has the same lifetime as the builder. */
458 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
459 SecCertificateRef certificate, SecCertificateSourceRef *foundInSource) {
460 /* We look through the anchor sources in order. They are ordered in
461 SecPathBuilderInit so that process anchors override user anchors which
462 override system anchors. */
463 CFIndex count = CFArrayGetCount(builder->anchorSources);
464 CFIndex ix;
465 for (ix = 0; ix < count; ++ix) {
466 SecCertificateSourceRef source = (SecCertificateSourceRef)
467 CFArrayGetValueAtIndex(builder->anchorSources, ix);
468 if (SecCertificateSourceContains(source, certificate)) {
469 if (foundInSource)
470 *foundInSource = source;
471 if (SecPathBuilderIsAnchorPerConstraints(builder, source, certificate)) {
472 return true;
473 }
474 }
475 }
476 return false;
477 }
478
479 /* Return false if path is not a partial, if path was a valid candidate it
480 will have been added to builder->candidatePaths, if path was rejected
481 by the parent certificate checks (because it's expired or some other
482 static chaining check failed) it will have been added to rejectedPaths.
483 Return true path if path is a partial. */
484 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
485 SecCertificatePathRef path) {
486 SecPVCRef pvc = &builder->path;
487 SecPVCSetPath(pvc, path, NULL);
488
489 if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
490 SecPVCGetCertificateCount(pvc) - 1)) {
491 secdebug("trust", "Found rejected path %@", path);
492 CFArrayAppendValue(builder->rejectedPaths, path);
493 return false;
494 }
495
496 SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
497 /* Candidate paths with failed signatures are discarded. */
498 if (vstatus == kSecPathVerifyFailed) {
499 secdebug("trust", "Verify failed for path %@", path);
500 return false;
501 }
502
503 if (vstatus == kSecPathVerifySuccess) {
504 /* The signature chain verified sucessfully, now let's find
505 out if we have an anchor for path. */
506 if (SecCertificatePathIsAnchored(path)) {
507 secdebug("trust", "Adding candidate %@", path);
508 CFArrayAppendValue(builder->candidatePaths, path);
509 }
510 /* The path is not partial if the last cert is self-signed. */
511 if ((SecCertificatePathSelfSignedIndex(path) >= 0) &&
512 (SecCertificatePathSelfSignedIndex(path) == SecCertificatePathGetCount(path)-1)) {
513 return false;
514 }
515 }
516
517 return true;
518 }
519
520 /* Given the builder, a partial chain partial and the parents array, construct
521 a SecCertificatePath for each parent. After discarding previously
522 considered paths and paths with cycles, sort out which array each path
523 should go in, if any. */
524 static void SecPathBuilderProcessParents(SecPathBuilderRef builder,
525 SecCertificatePathRef partial, CFArrayRef parents) {
526 CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
527 CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
528 CFIndex parentIX;
529 for (parentIX = 0; parentIX < num_parents; ++parentIX) {
530 SecCertificateRef parent = (SecCertificateRef)
531 CFArrayGetValueAtIndex(parents, parentIX);
532 CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
533 parent);
534 if (ixOfParent != kCFNotFound) {
535 /* partial already contains parent. Let's not add the same
536 certificate again. */
537 if (ixOfParent == rootIX) {
538 /* parent is equal to the root of the partial, so partial
539 looks to be self issued. */
540 SecCertificatePathSetSelfIssued(partial);
541 }
542 continue;
543 }
544
545 /* FIXME Add more sanity checks to see that parent really can be
546 a parent of partial_root. subjectKeyID == authorityKeyID,
547 signature algorithm matches public key algorithm, etc. */
548 SecCertificateSourceRef source = NULL;
549 bool is_anchor = SecPathBuilderIsAnchor(builder, parent, &source);
550 CFArrayRef constraints = (source) ? SecCertificateSourceCopyUsageConstraints(source, parent) : NULL;
551 SecCertificatePathRef path = SecCertificatePathCreate(partial, parent, constraints);
552 CFReleaseSafe(constraints);
553 if (!path)
554 continue;
555 if (!CFSetContainsValue(builder->allPaths, path)) {
556 CFSetAddValue(builder->allPaths, path);
557 if (is_anchor)
558 SecCertificatePathSetIsAnchored(path);
559 if (SecPathBuilderIsPartial(builder, path)) {
560 /* Insert path right at the current position since it's a new
561 candiate partial. */
562 CFArrayInsertValueAtIndex(builder->partialPaths,
563 ++builder->partialIX, path);
564 secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
565 parentIX + 1, num_parents, path);
566 }
567 secdebug("trust", "found new path %@", path);
568 }
569 CFRelease(path);
570 }
571 }
572
573 /* Callback for the SecPathBuilderGetNext() functions call to
574 SecCertificateSourceCopyParents(). */
575 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
576 SecPathBuilderRef builder = (SecPathBuilderRef)context;
577 SecCertificatePathRef partial = (SecCertificatePathRef)
578 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
579 secdebug("async", "%@ parents %@", partial, parents);
580 SecPathBuilderProcessParents(builder, partial, parents);
581
582 builder->state = SecPathBuilderGetNext;
583 SecPathBuilderStep(builder);
584 }
585
586 static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
587 /* If we have any candidates left to go return those first. */
588 if (CFArrayGetCount(builder->candidatePaths)) {
589 SecCertificatePathRef path = (SecCertificatePathRef)
590 CFArrayGetValueAtIndex(builder->candidatePaths, 0);
591 CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
592 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
593 path);
594 SecPVCSetPath(&builder->path, path, NULL);
595 builder->state = SecPathBuilderValidatePath;
596 return true;
597 }
598
599 /* If we are considering rejected chains we check each rejected path
600 with SecPathBuilderIsPartial() which checks the signature chain and
601 either drops the path if it's not properly signed, add it as a
602 candidate if it has a trusted anchor, or adds it as a partial
603 to be considered once we finish considering all the rejects. */
604 if (builder->considerRejected) {
605 CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
606 if (rejectedIX) {
607 rejectedIX--;
608 SecCertificatePathRef path = (SecCertificatePathRef)
609 CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
610 if (SecPathBuilderIsPartial(builder, path)) {
611 CFArrayInsertValueAtIndex(builder->partialPaths,
612 ++builder->partialIX, path);
613 }
614 CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
615
616 /* Keep going until we have moved all rejected partials into
617 the regular partials or candidates array. */
618 return true;
619 }
620 }
621
622 /* If builder->partialIX is < 0 we have considered all partial chains
623 this block must ensure partialIX >= 0 if execution continues past
624 it's end. */
625 if (builder->partialIX < 0) {
626 CFIndex num_sources = CFArrayGetCount(builder->parentSources);
627 if (builder->nextParentSource < num_sources) {
628 builder->nextParentSource++;
629 secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
630 builder->nextParentSource, num_sources);
631 } else {
632 /* We've run out of new sources to consider so let's look at
633 rejected chains and after that even consider partials
634 directly.
635 FIXME we might not want to consider partial paths that
636 are subsets of other partial paths, or not consider them
637 at all if we already have an anchored reject. */
638 if (!builder->considerRejected) {
639 builder->considerRejected = true;
640 secdebug("trust", "considering rejected paths");
641 } else if (!builder->considerPartials) {
642 builder->considerPartials = true;
643 secdebug("trust", "considering partials");
644 } else {
645 /* We're all out of options, so we can't produce any more
646 candidates. Let's calculate details and return the best
647 path we found. */
648 builder->state = SecPathBuilderComputeDetails;
649 return true;
650 }
651 }
652 builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
653 secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
654 return true;
655 }
656
657 /* We know builder->partialIX >= 0 if we get here. */
658 SecCertificatePathRef partial = (SecCertificatePathRef)
659 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
660 /* Don't try to extend partials anymore once we are in the considerPartials
661 state, since at this point every partial has been extended with every
662 possible parentSource already. */
663 if (builder->considerPartials) {
664 --builder->partialIX;
665 SecPVCSetPath(&builder->path, partial, NULL);
666 builder->state = SecPathBuilderValidatePath;
667 return true;
668 }
669
670 /* Attempt to extend this partial path with another certificate. This
671 should give us a list of potential parents to consider. */
672 secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
673 builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
674 partial);
675
676 /* Attempt to extend partial, leaving all possible extended versions
677 of partial in builder->extendedPaths. */
678 CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
679 CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
680 if (sourceIX < num_anchor_sources + builder->nextParentSource) {
681 SecCertificateSourceRef source;
682 if (sourceIX < num_anchor_sources) {
683 source = (SecCertificateSourceRef)
684 CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
685 secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
686 num_anchor_sources);
687 } else {
688 CFIndex parentIX = sourceIX - num_anchor_sources;
689 source = (SecCertificateSourceRef)
690 CFArrayGetValueAtIndex(builder->parentSources, parentIX);
691 secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
692 builder->nextParentSource);
693 }
694 SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
695 SecCertificateRef root = SecCertificatePathGetRoot(partial);
696 return SecCertificateSourceCopyParents(source, root,
697 builder, SecPathBuilderExtendPaths);
698 } else {
699 --builder->partialIX;
700 }
701
702 return true;
703 }
704
705 /* One or more of the policies did not accept the candidate path. */
706 static void SecPathBuilderReject(SecPathBuilderRef builder) {
707 check(builder);
708 SecPVCRef pvc = &builder->path;
709
710 builder->state = SecPathBuilderGetNext;
711
712 if (builder->bestPathIsEV && !pvc->is_ev) {
713 /* We never replace an ev reject with a non ev reject. */
714 return;
715 }
716
717 CFIndex bestPathScore = builder->bestPathScore;
718 CFIndex score = SecCertificatePathScore(builder->path.path,
719 SecPVCGetVerifyTime(&builder->path));
720
721 /* The current chain is valid for EV, but revocation checking failed. We
722 replace any previously accepted or rejected non EV chains with the
723 current one. */
724 if (pvc->is_ev && !builder->bestPathIsEV) {
725 bestPathScore = 0;
726 }
727
728 #if 0
729 if (pvc->is_ev) {
730 /* Since this means we found a valid ev chain that was revoked,
731 we might want to switch directly to the
732 SecPathBuilderComputeDetails state here if we think further
733 searching for new chains is pointless. For now we'll keep
734 going, since we could accept an alternate EV certification
735 path that isn't revoked. */
736 builder->state = SecPathBuilderComputeDetails;
737 }
738 #endif
739
740 /* Do this last so that changes to bestPathScore above will take effect. */
741 if (!builder->bestPath || score > bestPathScore) {
742 if (builder->bestPath) {
743 secinfo("reject",
744 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
745 (builder->bestPathIsEV ? "" : "non "),
746 (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
747 builder->bestPathScore,
748 (pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
749 } else {
750 secinfo("reject", "%sev score: %" PRIdCFIndex " %@",
751 (pvc->is_ev ? "" : "non "), score, builder->path.path);
752 }
753
754 builder->bestPathScore = score;
755 builder->bestPath = pvc->path;
756 builder->bestPathIsEV = pvc->is_ev;
757 builder->denyBestPath = SecPVCCheckUsageConstraints(pvc);
758 } else {
759 secinfo("reject", "%sev score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
760 (pvc->is_ev ? "" : "non "), score, bestPathScore, builder->path.path);
761 }
762 }
763
764 /* All policies accepted the candidate path. */
765 static void SecPathBuilderAccept(SecPathBuilderRef builder) {
766 if (!builder) { return; }
767 SecPVCRef pvc = &builder->path;
768 if (!pvc) { return; }
769 bool isSHA2 = !SecCertificatePathHasWeakHash(pvc->path);
770 bool isOptionallySHA2 = !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc, 0));
771 CFIndex bestScore = builder->bestPathScore;
772 /* Score this path. Note that all points awarded or deducted in
773 * SecCertificatePathScore are < 100,000 */
774 CFIndex currScore = (SecCertificatePathScore(pvc->path, pvc->verifyTime) +
775 ACCEPT_PATH_SCORE + // 10,000,000 points for accepting
776 ((pvc->is_ev) ? 1000000 : 0)); //1,000,000 points for EV
777 if (currScore > bestScore) {
778 // current path is better than existing best path
779 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
780 (builder->bestPathIsEV ? "" : "non "),
781 (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
782 builder->bestPathScore,
783 (pvc->is_ev ? "" : "non "), (long)currScore, builder->path.path);
784
785 builder->bestPathScore = currScore;
786 builder->bestPathIsEV = pvc->is_ev;
787 builder->bestPathIsSHA2 = isSHA2;
788 builder->bestPath = pvc->path;
789 builder->denyBestPath = SecPVCCheckUsageConstraints(pvc); /* should always be false */
790 }
791
792 /* If we found the best accept we can, we want to switch directly to the
793 SecPathBuilderComputeDetails state here, since we're done. */
794 if ((pvc->is_ev || !pvc->optionally_ev) && (isSHA2 || !isOptionallySHA2))
795 builder->state = SecPathBuilderComputeDetails;
796 else
797 builder->state = SecPathBuilderGetNext;
798 }
799
800 /* Return true iff a given path satisfies all the specified policies at
801 verifyTime. */
802 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
803 SecPVCRef pvc = &builder->path;
804
805 if (builder->considerRejected) {
806 SecPathBuilderReject(builder);
807 return true;
808 }
809
810 builder->state = SecPathBuilderDidValidatePath;
811 return SecPVCPathChecks(pvc);
812 }
813
814 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
815 SecPVCRef pvc = &builder->path;
816 if (pvc->result) {
817 SecPathBuilderAccept(builder);
818 } else {
819 SecPathBuilderReject(builder);
820 }
821 assert(builder->state != SecPathBuilderDidValidatePath);
822 return true;
823 }
824
825 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
826 // foobar
827 SecPVCRef pvc = &builder->path;
828 #if 0
829 if (!builder->caller_wants_details) {
830 SecPVCSetPath(pvc, builder->bestPath, NULL);
831 pvc->result = builder->bestPathScore > ACCEPT_PATH_SCORE;
832 builder->state = SecPathBuilderReportResult;
833 return true;
834 }
835 #endif
836 CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
837 CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
838 pathLength, builder->leafDetails);
839 CFRetainSafe(details);
840 SecPVCSetPath(pvc, builder->bestPath, details);
841 /* Only report on EV stuff if the bestPath actually was valid for EV. */
842 pvc->optionally_ev = builder->bestPathIsEV;
843 pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
844 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
845 for (ix = 1; ix < pathLength; ++ix) {
846 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
847 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
848 &kCFTypeDictionaryValueCallBacks);
849 CFArrayAppendValue(details, certDetail);
850 CFRelease(certDetail);
851 SecPVCParentCertificateChecks(pvc, ix);
852 SecPVCGrayListedKeyChecks(pvc, ix);
853 SecPVCBlackListedKeyChecks(pvc, ix);
854 }
855 builder->state = SecPathBuilderReportResult;
856 bool completed = SecPVCPathChecks(pvc);
857
858 /* Reject the certificate if it was accepted before but we failed it now. */
859 if (builder->bestPathScore > ACCEPT_PATH_SCORE && !pvc->result) {
860 builder->bestPathScore = 0;
861 }
862
863 /* Accept a partial path if certificate is on the allow list
864 and is temporally valid and passed all PVC checks. */
865 if (completed && pvc->is_allowlisted && pvc->result &&
866 builder->bestPathScore < ACCEPT_PATH_SCORE &&
867 SecCertificatePathIsValid(pvc->path, pvc->verifyTime)) {
868 builder->bestPathScore += ACCEPT_PATH_SCORE;
869 }
870
871 CFReleaseSafe(details);
872
873 return completed;
874 }
875
876 static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
877 SecPVCRef pvc = &builder->path;
878 bool haveRevocationResponse = false;
879 if (pvc->info && pvc->is_ev && pvc->result) {
880 CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
881 kCFBooleanTrue); /* iOS key */
882 CFDictionarySetValue(pvc->info, kSecTrustExtendedValidation,
883 kCFBooleanTrue); /* unified API key */
884 SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
885 CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
886 if (leafCompanyName) {
887 CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
888 leafCompanyName); /* iOS key */
889 CFDictionarySetValue(pvc->info, kSecTrustOrganizationName,
890 leafCompanyName); /* unified API key */
891 CFRelease(leafCompanyName);
892 }
893 if (pvc->rvcs) {
894 CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
895 if (nextUpdate == 0) {
896 /* populate revocation info for failed revocation check */
897 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
898 kCFBooleanFalse); /* iOS key */
899 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
900 kCFBooleanFalse); /* unified API key */
901 }
902 }
903 }
904
905 if (pvc->info && pvc->result && pvc->rvcs) {
906 CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
907 if (nextUpdate != 0) {
908 /* always populate revocation info for successful revocation check */
909 haveRevocationResponse = true;
910 CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
911 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
912 validUntil); /* iOS key */
913 CFDictionarySetValue(pvc->info, kSecTrustRevocationValidUntilDate,
914 validUntil); /* unified API key */
915 CFRelease(validUntil);
916 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
917 kCFBooleanTrue); /* iOS key */
918 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
919 kCFBooleanTrue); /* unified API key */
920 }
921 }
922
923 if (pvc->info && pvc->result && pvc->response_required && !haveRevocationResponse) {
924 builder->bestPathScore = 0;
925 SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired,
926 0, kCFBooleanFalse, true);
927 }
928
929 if (pvc->info && pvc->is_ct && pvc->result) {
930 CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyKey,
931 kCFBooleanTrue);
932 }
933
934 if (pvc->info && pvc->is_ct_whitelisted && pvc->result) {
935 CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyWhiteListKey,
936 kCFBooleanTrue);
937 }
938
939
940 /* This will trigger the outer step function to call the completion
941 function. */
942 builder->state = NULL;
943 return false;
944 }
945
946 /* @function SecPathBuilderStep
947 @summary This is the core of the async engine.
948 @description Return false iff job is complete, true if a network request
949 is pending.
950 builder->state is a function pointer which is to be invoked.
951 If you call this function from within a builder->state invocation it
952 immediately returns true.
953 Otherwise the following steps are repeated endlessly (unless a step returns)
954 builder->state is invoked. If it returns true and builder->state is still
955 non NULL this proccess is repeated.
956 If a state returns false, SecPathBuilder will return true
957 if builder->state is non NULL.
958 If builder->state is NULL then regardless of what the state function returns
959 the completion callback will be invoked and the builder will be deallocated.
960 */
961 bool SecPathBuilderStep(SecPathBuilderRef builder) {
962 if (builder->activations) {
963 secdebug("async", "activations: %lu returning true",
964 builder->activations);
965 return true;
966 }
967
968 secdebug("async", "activations: %lu", builder->activations);
969 builder->activations++;
970 while (builder->state && builder->state(builder));
971 --builder->activations;
972
973 if (builder->state) {
974 secdebug("async", "waiting for async reply, exiting");
975 /* A state returned false, it's waiting for network traffic. Let's
976 return. */
977 return true;
978 }
979
980 if (builder->activations) {
981 /* There is still at least one other running instance of this builder
982 somewhere on the stack, we let that instance take care of sending
983 the client a response. */
984 return false;
985 }
986
987 SecTrustResultType result = kSecTrustResultInvalid;
988 if (builder->denyBestPath) {
989 result = kSecTrustResultDeny;
990 } else if (builder->bestPathScore > ACCEPT_PATH_SCORE) {
991 result = kSecTrustResultUnspecified;
992 } else {
993 result = kSecTrustResultRecoverableTrustFailure;
994 }
995
996 secinfo("trust", "completed: %@ details: %@ result: %d",
997 builder->bestPath, builder->path.details, result);
998
999 if (builder->completed) {
1000 builder->completed(builder->context, builder->bestPath,
1001 builder->path.details, builder->path.info, result);
1002 }
1003
1004 /* Finally, destroy the builder and free it. */
1005 SecPathBuilderDestroy(builder);
1006 free(builder);
1007
1008 return false;
1009 }
1010
1011 dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
1012 return (builder) ? builder->queue : NULL;
1013 }
1014
1015 CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
1016 return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
1017 }
1018
1019 // MARK: -
1020 // MARK: SecTrustServer
1021 /********************************************************
1022 ****************** SecTrustServer **********************
1023 ********************************************************/
1024
1025 typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
1026
1027 static void
1028 SecTrustServerEvaluateCompleted(const void *userData,
1029 SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
1030 SecTrustResultType result) {
1031 SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
1032 evaluated(result, details, info, chain, NULL);
1033 Block_release(evaluated);
1034 }
1035
1036 void
1037 SecTrustServerEvaluateBlock(CFDataRef clientAuditToken, CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, void (^evaluated)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error)) {
1038 /* We need an array containing at least one certificate to proceed. */
1039 if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
1040 CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
1041 evaluated(kSecTrustResultInvalid, NULL, NULL, NULL, certError);
1042 CFReleaseSafe(certError);
1043 return;
1044 }
1045 SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
1046 /* Call the actual evaluator function. */
1047 SecPathBuilderRef builder = SecPathBuilderCreate(clientAuditToken,
1048 certificates, anchors,
1049 anchorsOnly, keychainsAllowed, policies,
1050 responses, SCTs, trustedLogs,
1051 verifyTime, accessGroups,
1052 SecTrustServerEvaluateCompleted, userData);
1053 dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
1054 }
1055
1056
1057 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1058 SecTrustResultType SecTrustServerEvaluate(CFArrayRef certificates, CFArrayRef anchors, bool anchorsOnly, bool keychainsAllowed, CFArrayRef policies, CFArrayRef responses, CFArrayRef SCTs, CFArrayRef trustedLogs, CFAbsoluteTime verifyTime, __unused CFArrayRef accessGroups, CFArrayRef *pdetails, CFDictionaryRef *pinfo, SecCertificatePathRef *pchain, CFErrorRef *perror) {
1059 dispatch_semaphore_t done = dispatch_semaphore_create(0);
1060 __block SecTrustResultType result = kSecTrustResultInvalid;
1061 SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
1062 result = tr;
1063 if (tr == kSecTrustResultInvalid) {
1064 if (perror) {
1065 *perror = error;
1066 CFRetainSafe(error);
1067 }
1068 } else {
1069 if (pdetails) {
1070 *pdetails = details;
1071 CFRetainSafe(details);
1072 }
1073 if (pinfo) {
1074 *pinfo = info;
1075 CFRetainSafe(info);
1076 }
1077 if (pchain) {
1078 *pchain = chain;
1079 CFRetainSafe(chain);
1080 }
1081 }
1082 dispatch_semaphore_signal(done);
1083 });
1084 dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
1085
1086 return result;
1087 }