]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/SecTrustServer.c
Security-57740.60.18.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 builder->anchorSource = NULL;
340 }
341 if (builder->certificateSource) {
342 SecMemoryCertificateSourceDestroy(builder->certificateSource);
343 builder->certificateSource = NULL;
344 }
345 if (builder->itemCertificateSource) {
346 SecItemCertificateSourceDestroy(builder->itemCertificateSource);
347 builder->itemCertificateSource = NULL;
348 }
349 if (builder->appleAnchorSource) {
350 SecMemoryCertificateSourceDestroy(builder->appleAnchorSource);
351 builder->appleAnchorSource = NULL;
352 }
353 CFReleaseNull(builder->clientAuditToken);
354 CFReleaseNull(builder->anchorSources);
355 CFReleaseNull(builder->parentSources);
356 CFReleaseNull(builder->allPaths);
357 CFReleaseNull(builder->partialPaths);
358 CFReleaseNull(builder->rejectedPaths);
359 CFReleaseNull(builder->candidatePaths);
360 CFReleaseNull(builder->leafDetails);
361 CFReleaseNull(builder->ocspResponses);
362 CFReleaseNull(builder->signedCertificateTimestamps);
363 CFReleaseNull(builder->trustedLogs);
364
365 SecPVCDelete(&builder->path);
366 }
367
368 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder) {
369 return builder->canAccessNetwork;
370 }
371
372 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder, bool allow) {
373 if (builder->canAccessNetwork != allow) {
374 builder->canAccessNetwork = allow;
375 if (allow) {
376 #if !TARGET_OS_WATCH
377 secinfo("http", "network access re-enabled by policy");
378 /* re-enabling network_access re-adds kSecCAIssuerSource as
379 a parent source. */
380 CFArrayAppendValue(builder->parentSources, kSecCAIssuerSource);
381 #else
382 secnotice("http", "network access not allowed on WatchOS");
383 builder->canAccessNetwork = false;
384 #endif
385 } else {
386 secinfo("http", "network access disabled by policy");
387 /* disabling network_access removes kSecCAIssuerSource from
388 the list of parent sources. */
389 CFIndex ix = CFArrayGetFirstIndexOfValue(builder->parentSources,
390 CFRangeMake(0, CFArrayGetCount(builder->parentSources)),
391 kSecCAIssuerSource);
392 if (ix >= 0)
393 CFArrayRemoveValueAtIndex(builder->parentSources, ix);
394 }
395 }
396 }
397
398 CFArrayRef SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder)
399 {
400 return CFRetainSafe(builder->ocspResponses);
401 }
402
403 CFArrayRef SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder)
404 {
405 return CFRetainSafe(builder->signedCertificateTimestamps);
406 }
407
408 CFArrayRef SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder)
409 {
410 return CFRetainSafe(builder->trustedLogs);
411 }
412
413 SecCertificatePathRef SecPathBuilderGetBestPath(SecPathBuilderRef builder)
414 {
415 return builder->bestPath;
416 }
417
418 /* This function assumes that the input source is an anchor source */
419 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder, SecCertificateSourceRef source,
420 SecCertificateRef certificate) {
421 bool result = false;
422 CFArrayRef constraints = NULL;
423 constraints = SecCertificateSourceCopyUsageConstraints(source, certificate);
424
425 /* Unrestricted certificates:
426 * -those that come from anchor sources with no constraints
427 * -self-signed certificates with empty contraints arrays
428 */
429 Boolean selfSigned = false;
430 require(errSecSuccess == SecCertificateIsSelfSigned(certificate, &selfSigned), out);
431 if ((NULL == source->copyUsageConstraints) ||
432 (constraints && (CFArrayGetCount(constraints) == 0) && selfSigned)) {
433 secinfo("trust", "unrestricted anchor%s",
434 (NULL == source->copyUsageConstraints) ? " source" : "");
435 result = true;
436 goto out;
437 }
438
439 /* Get the trust settings result for the PVC */
440 require(constraints, out);
441 SecTrustSettingsResult settingsResult = kSecTrustSettingsResultInvalid;
442 settingsResult = SecPVCGetTrustSettingsResult(&builder->path,
443 certificate,
444 constraints);
445 if ((selfSigned && settingsResult == kSecTrustSettingsResultTrustRoot) ||
446 (!selfSigned && settingsResult == kSecTrustSettingsResultTrustAsRoot)) {
447 // For our purposes, this is an anchor.
448 secinfo("trust", "complex trust settings anchor");
449 result = true;
450 }
451
452 if (settingsResult == kSecTrustSettingsResultDeny) {
453 /* We consider denied certs "anchors" because the trust decision
454 is set regardless of building the chain further. The policy
455 validation will handle rejecting this chain. */
456 secinfo("trust", "complex trust settings denied anchor");
457 result = true;
458 }
459
460 out:
461 CFReleaseNull(constraints);
462 return result;
463 }
464
465 /* Source returned in foundInSource has the same lifetime as the builder. */
466 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder,
467 SecCertificateRef certificate, SecCertificateSourceRef *foundInSource) {
468 /* We look through the anchor sources in order. They are ordered in
469 SecPathBuilderInit so that process anchors override user anchors which
470 override system anchors. */
471 CFIndex count = CFArrayGetCount(builder->anchorSources);
472 CFIndex ix;
473 for (ix = 0; ix < count; ++ix) {
474 SecCertificateSourceRef source = (SecCertificateSourceRef)
475 CFArrayGetValueAtIndex(builder->anchorSources, ix);
476 if (SecCertificateSourceContains(source, certificate)) {
477 if (foundInSource)
478 *foundInSource = source;
479 if (SecPathBuilderIsAnchorPerConstraints(builder, source, certificate)) {
480 return true;
481 }
482 }
483 }
484 return false;
485 }
486
487 /* Return false if path is not a partial, if path was a valid candidate it
488 will have been added to builder->candidatePaths, if path was rejected
489 by the parent certificate checks (because it's expired or some other
490 static chaining check failed) it will have been added to rejectedPaths.
491 Return true path if path is a partial. */
492 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder,
493 SecCertificatePathRef path) {
494 SecPVCRef pvc = &builder->path;
495 SecPVCSetPath(pvc, path, NULL);
496
497 if (!builder->considerRejected && !SecPVCParentCertificateChecks(pvc,
498 SecPVCGetCertificateCount(pvc) - 1)) {
499 secdebug("trust", "Found rejected path %@", path);
500 CFArrayAppendValue(builder->rejectedPaths, path);
501 return false;
502 }
503
504 SecPathVerifyStatus vstatus = SecCertificatePathVerify(path);
505 /* Candidate paths with failed signatures are discarded. */
506 if (vstatus == kSecPathVerifyFailed) {
507 secdebug("trust", "Verify failed for path %@", path);
508 return false;
509 }
510
511 if (vstatus == kSecPathVerifySuccess) {
512 /* The signature chain verified sucessfully, now let's find
513 out if we have an anchor for path. */
514 if (SecCertificatePathIsAnchored(path)) {
515 secdebug("trust", "Adding candidate %@", path);
516 CFArrayAppendValue(builder->candidatePaths, path);
517 }
518 /* The path is not partial if the last cert is self-signed. */
519 if ((SecCertificatePathSelfSignedIndex(path) >= 0) &&
520 (SecCertificatePathSelfSignedIndex(path) == SecCertificatePathGetCount(path)-1)) {
521 return false;
522 }
523 }
524
525 return true;
526 }
527
528 /* Given the builder, a partial chain partial and the parents array, construct
529 a SecCertificatePath for each parent. After discarding previously
530 considered paths and paths with cycles, sort out which array each path
531 should go in, if any. */
532 static void SecPathBuilderProcessParents(SecPathBuilderRef builder,
533 SecCertificatePathRef partial, CFArrayRef parents) {
534 CFIndex rootIX = SecCertificatePathGetCount(partial) - 1;
535 CFIndex num_parents = parents ? CFArrayGetCount(parents) : 0;
536 CFIndex parentIX;
537 for (parentIX = 0; parentIX < num_parents; ++parentIX) {
538 SecCertificateRef parent = (SecCertificateRef)
539 CFArrayGetValueAtIndex(parents, parentIX);
540 CFIndex ixOfParent = SecCertificatePathGetIndexOfCertificate(partial,
541 parent);
542 if (ixOfParent != kCFNotFound) {
543 /* partial already contains parent. Let's not add the same
544 certificate again. */
545 if (ixOfParent == rootIX) {
546 /* parent is equal to the root of the partial, so partial
547 looks to be self issued. */
548 SecCertificatePathSetSelfIssued(partial);
549 }
550 continue;
551 }
552
553 /* FIXME Add more sanity checks to see that parent really can be
554 a parent of partial_root. subjectKeyID == authorityKeyID,
555 signature algorithm matches public key algorithm, etc. */
556 SecCertificateSourceRef source = NULL;
557 bool is_anchor = SecPathBuilderIsAnchor(builder, parent, &source);
558 CFArrayRef constraints = (source) ? SecCertificateSourceCopyUsageConstraints(source, parent) : NULL;
559 SecCertificatePathRef path = SecCertificatePathCreate(partial, parent, constraints);
560 CFReleaseSafe(constraints);
561 if (!path)
562 continue;
563 if (!CFSetContainsValue(builder->allPaths, path)) {
564 CFSetAddValue(builder->allPaths, path);
565 if (is_anchor)
566 SecCertificatePathSetIsAnchored(path);
567 if (SecPathBuilderIsPartial(builder, path)) {
568 /* Insert path right at the current position since it's a new
569 candiate partial. */
570 CFArrayInsertValueAtIndex(builder->partialPaths,
571 ++builder->partialIX, path);
572 secdebug("trust", "Adding partial for parent %" PRIdCFIndex "/%" PRIdCFIndex " %@",
573 parentIX + 1, num_parents, path);
574 }
575 secdebug("trust", "found new path %@", path);
576 }
577 CFRelease(path);
578 }
579 }
580
581 /* Callback for the SecPathBuilderGetNext() functions call to
582 SecCertificateSourceCopyParents(). */
583 static void SecPathBuilderExtendPaths(void *context, CFArrayRef parents) {
584 SecPathBuilderRef builder = (SecPathBuilderRef)context;
585 SecCertificatePathRef partial = (SecCertificatePathRef)
586 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
587 secdebug("async", "%@ parents %@", partial, parents);
588 SecPathBuilderProcessParents(builder, partial, parents);
589
590 builder->state = SecPathBuilderGetNext;
591 SecPathBuilderStep(builder);
592 }
593
594 static bool SecPathBuilderGetNext(SecPathBuilderRef builder) {
595 /* If we have any candidates left to go return those first. */
596 if (CFArrayGetCount(builder->candidatePaths)) {
597 SecCertificatePathRef path = (SecCertificatePathRef)
598 CFArrayGetValueAtIndex(builder->candidatePaths, 0);
599 CFArrayRemoveValueAtIndex(builder->candidatePaths, 0);
600 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
601 path);
602 SecPVCSetPath(&builder->path, path, NULL);
603 builder->state = SecPathBuilderValidatePath;
604 return true;
605 }
606
607 /* If we are considering rejected chains we check each rejected path
608 with SecPathBuilderIsPartial() which checks the signature chain and
609 either drops the path if it's not properly signed, add it as a
610 candidate if it has a trusted anchor, or adds it as a partial
611 to be considered once we finish considering all the rejects. */
612 if (builder->considerRejected) {
613 CFIndex rejectedIX = CFArrayGetCount(builder->rejectedPaths);
614 if (rejectedIX) {
615 rejectedIX--;
616 SecCertificatePathRef path = (SecCertificatePathRef)
617 CFArrayGetValueAtIndex(builder->rejectedPaths, rejectedIX);
618 if (SecPathBuilderIsPartial(builder, path)) {
619 CFArrayInsertValueAtIndex(builder->partialPaths,
620 ++builder->partialIX, path);
621 }
622 CFArrayRemoveValueAtIndex(builder->rejectedPaths, rejectedIX);
623
624 /* Keep going until we have moved all rejected partials into
625 the regular partials or candidates array. */
626 return true;
627 }
628 }
629
630 /* If builder->partialIX is < 0 we have considered all partial chains
631 this block must ensure partialIX >= 0 if execution continues past
632 it's end. */
633 if (builder->partialIX < 0) {
634 CFIndex num_sources = CFArrayGetCount(builder->parentSources);
635 if (builder->nextParentSource < num_sources) {
636 builder->nextParentSource++;
637 secdebug("trust", "broading search to %" PRIdCFIndex "/%" PRIdCFIndex " sources",
638 builder->nextParentSource, num_sources);
639 } else {
640 /* We've run out of new sources to consider so let's look at
641 rejected chains and after that even consider partials
642 directly.
643 FIXME we might not want to consider partial paths that
644 are subsets of other partial paths, or not consider them
645 at all if we already have an anchored reject. */
646 if (!builder->considerRejected) {
647 builder->considerRejected = true;
648 secdebug("trust", "considering rejected paths");
649 } else if (!builder->considerPartials) {
650 builder->considerPartials = true;
651 secdebug("trust", "considering partials");
652 } else {
653 /* We're all out of options, so we can't produce any more
654 candidates. Let's calculate details and return the best
655 path we found. */
656 builder->state = SecPathBuilderComputeDetails;
657 return true;
658 }
659 }
660 builder->partialIX = CFArrayGetCount(builder->partialPaths) - 1;
661 secdebug("trust", "re-checking %" PRIdCFIndex " partials", builder->partialIX + 1);
662 return true;
663 }
664
665 /* We know builder->partialIX >= 0 if we get here. */
666 SecCertificatePathRef partial = (SecCertificatePathRef)
667 CFArrayGetValueAtIndex(builder->partialPaths, builder->partialIX);
668 /* Don't try to extend partials anymore once we are in the considerPartials
669 state, since at this point every partial has been extended with every
670 possible parentSource already. */
671 if (builder->considerPartials) {
672 --builder->partialIX;
673 SecPVCSetPath(&builder->path, partial, NULL);
674 builder->state = SecPathBuilderValidatePath;
675 return true;
676 }
677
678 /* Attempt to extend this partial path with another certificate. This
679 should give us a list of potential parents to consider. */
680 secdebug("trust", "looking for parents of partial %" PRIdCFIndex "/%" PRIdCFIndex ": %@",
681 builder->partialIX + 1, CFArrayGetCount(builder->partialPaths),
682 partial);
683
684 /* Attempt to extend partial, leaving all possible extended versions
685 of partial in builder->extendedPaths. */
686 CFIndex sourceIX = SecCertificatePathGetNextSourceIndex(partial);
687 CFIndex num_anchor_sources = CFArrayGetCount(builder->anchorSources);
688 if (sourceIX < num_anchor_sources + builder->nextParentSource) {
689 SecCertificateSourceRef source;
690 if (sourceIX < num_anchor_sources) {
691 source = (SecCertificateSourceRef)
692 CFArrayGetValueAtIndex(builder->anchorSources, sourceIX);
693 secdebug("trust", "searching anchor source %" PRIdCFIndex "/%" PRIdCFIndex, sourceIX + 1,
694 num_anchor_sources);
695 } else {
696 CFIndex parentIX = sourceIX - num_anchor_sources;
697 source = (SecCertificateSourceRef)
698 CFArrayGetValueAtIndex(builder->parentSources, parentIX);
699 secdebug("trust", "searching parent source %" PRIdCFIndex "/%" PRIdCFIndex, parentIX + 1,
700 builder->nextParentSource);
701 }
702 SecCertificatePathSetNextSourceIndex(partial, sourceIX + 1);
703 SecCertificateRef root = SecCertificatePathGetRoot(partial);
704 return SecCertificateSourceCopyParents(source, root,
705 builder, SecPathBuilderExtendPaths);
706 } else {
707 --builder->partialIX;
708 }
709
710 return true;
711 }
712
713 /* One or more of the policies did not accept the candidate path. */
714 static void SecPathBuilderReject(SecPathBuilderRef builder) {
715 check(builder);
716 SecPVCRef pvc = &builder->path;
717
718 builder->state = SecPathBuilderGetNext;
719
720 if (builder->bestPathIsEV && !pvc->is_ev) {
721 /* We never replace an ev reject with a non ev reject. */
722 return;
723 }
724
725 CFIndex bestPathScore = builder->bestPathScore;
726 CFIndex score = SecCertificatePathScore(builder->path.path,
727 SecPVCGetVerifyTime(&builder->path));
728
729 /* The current chain is valid for EV, but revocation checking failed. We
730 replace any previously accepted or rejected non EV chains with the
731 current one. */
732 if (pvc->is_ev && !builder->bestPathIsEV) {
733 bestPathScore = 0;
734 }
735
736 #if 0
737 if (pvc->is_ev) {
738 /* Since this means we found a valid ev chain that was revoked,
739 we might want to switch directly to the
740 SecPathBuilderComputeDetails state here if we think further
741 searching for new chains is pointless. For now we'll keep
742 going, since we could accept an alternate EV certification
743 path that isn't revoked. */
744 builder->state = SecPathBuilderComputeDetails;
745 }
746 #endif
747
748 /* Do this last so that changes to bestPathScore above will take effect. */
749 if (!builder->bestPath || score > bestPathScore) {
750 if (builder->bestPath) {
751 secinfo("reject",
752 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
753 (builder->bestPathIsEV ? "" : "non "),
754 (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
755 builder->bestPathScore,
756 (pvc->is_ev ? "" : "non "), (long)score, builder->path.path);
757 } else {
758 secinfo("reject", "%sev score: %" PRIdCFIndex " %@",
759 (pvc->is_ev ? "" : "non "), score, builder->path.path);
760 }
761
762 builder->bestPathScore = score;
763 builder->bestPath = pvc->path;
764 builder->bestPathIsEV = pvc->is_ev;
765 builder->denyBestPath = SecPVCCheckUsageConstraints(pvc);
766 } else {
767 secinfo("reject", "%sev score: %" PRIdCFIndex " lower than %" PRIdCFIndex " %@",
768 (pvc->is_ev ? "" : "non "), score, bestPathScore, builder->path.path);
769 }
770 }
771
772 /* All policies accepted the candidate path. */
773 static void SecPathBuilderAccept(SecPathBuilderRef builder) {
774 if (!builder) { return; }
775 SecPVCRef pvc = &builder->path;
776 if (!pvc) { return; }
777 bool isSHA2 = !SecCertificatePathHasWeakHash(pvc->path);
778 bool isOptionallySHA2 = !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc, 0));
779 CFIndex bestScore = builder->bestPathScore;
780 /* Score this path. Note that all points awarded or deducted in
781 * SecCertificatePathScore are < 100,000 */
782 CFIndex currScore = (SecCertificatePathScore(pvc->path, pvc->verifyTime) +
783 ACCEPT_PATH_SCORE + // 10,000,000 points for accepting
784 ((pvc->is_ev) ? 1000000 : 0)); //1,000,000 points for EV
785 if (currScore > bestScore) {
786 // current path is better than existing best path
787 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex " %@",
788 (builder->bestPathIsEV ? "" : "non "),
789 (builder->bestPathScore > ACCEPT_PATH_SCORE ? "accept" : "reject"),
790 builder->bestPathScore,
791 (pvc->is_ev ? "" : "non "), (long)currScore, builder->path.path);
792
793 builder->bestPathScore = currScore;
794 builder->bestPathIsEV = pvc->is_ev;
795 builder->bestPathIsSHA2 = isSHA2;
796 builder->bestPath = pvc->path;
797 builder->denyBestPath = SecPVCCheckUsageConstraints(pvc); /* should always be false */
798 }
799
800 /* If we found the best accept we can, we want to switch directly to the
801 SecPathBuilderComputeDetails state here, since we're done. */
802 if ((pvc->is_ev || !pvc->optionally_ev) && (isSHA2 || !isOptionallySHA2))
803 builder->state = SecPathBuilderComputeDetails;
804 else
805 builder->state = SecPathBuilderGetNext;
806 }
807
808 /* Return true iff a given path satisfies all the specified policies at
809 verifyTime. */
810 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder) {
811 SecPVCRef pvc = &builder->path;
812
813 if (builder->considerRejected) {
814 SecPathBuilderReject(builder);
815 return true;
816 }
817
818 builder->state = SecPathBuilderDidValidatePath;
819 return SecPVCPathChecks(pvc);
820 }
821
822 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder) {
823 SecPVCRef pvc = &builder->path;
824 if (pvc->result) {
825 SecPathBuilderAccept(builder);
826 } else {
827 SecPathBuilderReject(builder);
828 }
829 assert(builder->state != SecPathBuilderDidValidatePath);
830 return true;
831 }
832
833 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder) {
834 // foobar
835 SecPVCRef pvc = &builder->path;
836 #if 0
837 if (!builder->caller_wants_details) {
838 SecPVCSetPath(pvc, builder->bestPath, NULL);
839 pvc->result = builder->bestPathScore > ACCEPT_PATH_SCORE;
840 builder->state = SecPathBuilderReportResult;
841 return true;
842 }
843 #endif
844 CFIndex ix, pathLength = SecCertificatePathGetCount(builder->bestPath);
845 CFMutableArrayRef details = CFArrayCreateMutableCopy(kCFAllocatorDefault,
846 pathLength, builder->leafDetails);
847 CFRetainSafe(details);
848 SecPVCSetPath(pvc, builder->bestPath, details);
849 /* Only report on EV stuff if the bestPath actually was valid for EV. */
850 pvc->optionally_ev = builder->bestPathIsEV;
851 pvc->info = CFDictionaryCreateMutable(kCFAllocatorDefault,
852 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
853 for (ix = 1; ix < pathLength; ++ix) {
854 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(
855 kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
856 &kCFTypeDictionaryValueCallBacks);
857 CFArrayAppendValue(details, certDetail);
858 CFRelease(certDetail);
859 SecPVCParentCertificateChecks(pvc, ix);
860 SecPVCGrayListedKeyChecks(pvc, ix);
861 SecPVCBlackListedKeyChecks(pvc, ix);
862 }
863 builder->state = SecPathBuilderReportResult;
864 bool completed = SecPVCPathChecks(pvc);
865
866 /* Reject the certificate if it was accepted before but we failed it now. */
867 if (builder->bestPathScore > ACCEPT_PATH_SCORE && !pvc->result) {
868 builder->bestPathScore = 0;
869 }
870
871 /* Accept a partial path if certificate is on the allow list
872 and is temporally valid and passed all PVC checks. */
873 if (completed && pvc->is_allowlisted && pvc->result &&
874 builder->bestPathScore < ACCEPT_PATH_SCORE &&
875 SecCertificatePathIsValid(pvc->path, pvc->verifyTime)) {
876 builder->bestPathScore += ACCEPT_PATH_SCORE;
877 }
878
879 CFReleaseSafe(details);
880
881 return completed;
882 }
883
884 static bool SecPathBuilderReportResult(SecPathBuilderRef builder) {
885 SecPVCRef pvc = &builder->path;
886 bool haveRevocationResponse = false;
887 if (pvc->info && pvc->is_ev && pvc->result) {
888 CFDictionarySetValue(pvc->info, kSecTrustInfoExtendedValidationKey,
889 kCFBooleanTrue); /* iOS key */
890 CFDictionarySetValue(pvc->info, kSecTrustExtendedValidation,
891 kCFBooleanTrue); /* unified API key */
892 SecCertificateRef leaf = SecPVCGetCertificateAtIndex(pvc, 0);
893 CFStringRef leafCompanyName = SecCertificateCopyCompanyName(leaf);
894 if (leafCompanyName) {
895 CFDictionarySetValue(pvc->info, kSecTrustInfoCompanyNameKey,
896 leafCompanyName); /* iOS key */
897 CFDictionarySetValue(pvc->info, kSecTrustOrganizationName,
898 leafCompanyName); /* unified API key */
899 CFRelease(leafCompanyName);
900 }
901 if (pvc->rvcs) {
902 CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
903 if (nextUpdate == 0) {
904 /* populate revocation info for failed revocation check */
905 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
906 kCFBooleanFalse); /* iOS key */
907 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
908 kCFBooleanFalse); /* unified API key */
909 }
910 }
911 }
912
913 if (pvc->info && pvc->result && pvc->rvcs) {
914 CFAbsoluteTime nextUpdate = SecPVCGetEarliestNextUpdate(pvc);
915 if (nextUpdate != 0) {
916 /* always populate revocation info for successful revocation check */
917 haveRevocationResponse = true;
918 CFDateRef validUntil = CFDateCreate(kCFAllocatorDefault, nextUpdate);
919 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationValidUntilKey,
920 validUntil); /* iOS key */
921 CFDictionarySetValue(pvc->info, kSecTrustRevocationValidUntilDate,
922 validUntil); /* unified API key */
923 CFRelease(validUntil);
924 CFDictionarySetValue(pvc->info, kSecTrustInfoRevocationKey,
925 kCFBooleanTrue); /* iOS key */
926 CFDictionarySetValue(pvc->info, kSecTrustRevocationChecked,
927 kCFBooleanTrue); /* unified API key */
928 }
929 }
930
931 if (pvc->info && pvc->result && pvc->response_required && !haveRevocationResponse) {
932 builder->bestPathScore = 0;
933 SecPVCSetResultForced(pvc, kSecPolicyCheckRevocationResponseRequired,
934 0, kCFBooleanFalse, true);
935 }
936
937 if (pvc->info && pvc->is_ct && pvc->result) {
938 CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyKey,
939 kCFBooleanTrue);
940 }
941
942 if (pvc->info && pvc->is_ct_whitelisted && pvc->result) {
943 CFDictionarySetValue(pvc->info, kSecTrustInfoCertificateTransparencyWhiteListKey,
944 kCFBooleanTrue);
945 }
946
947
948 /* This will trigger the outer step function to call the completion
949 function. */
950 builder->state = NULL;
951 return false;
952 }
953
954 /* @function SecPathBuilderStep
955 @summary This is the core of the async engine.
956 @description Return false iff job is complete, true if a network request
957 is pending.
958 builder->state is a function pointer which is to be invoked.
959 If you call this function from within a builder->state invocation it
960 immediately returns true.
961 Otherwise the following steps are repeated endlessly (unless a step returns)
962 builder->state is invoked. If it returns true and builder->state is still
963 non NULL this proccess is repeated.
964 If a state returns false, SecPathBuilder will return true
965 if builder->state is non NULL.
966 If builder->state is NULL then regardless of what the state function returns
967 the completion callback will be invoked and the builder will be deallocated.
968 */
969 bool SecPathBuilderStep(SecPathBuilderRef builder) {
970 if (builder->activations) {
971 secdebug("async", "activations: %lu returning true",
972 builder->activations);
973 return true;
974 }
975
976 secdebug("async", "activations: %lu", builder->activations);
977 builder->activations++;
978 while (builder->state && builder->state(builder));
979 --builder->activations;
980
981 if (builder->state) {
982 secdebug("async", "waiting for async reply, exiting");
983 /* A state returned false, it's waiting for network traffic. Let's
984 return. */
985 return true;
986 }
987
988 if (builder->activations) {
989 /* There is still at least one other running instance of this builder
990 somewhere on the stack, we let that instance take care of sending
991 the client a response. */
992 return false;
993 }
994
995 SecTrustResultType result = kSecTrustResultInvalid;
996 if (builder->denyBestPath) {
997 result = kSecTrustResultDeny;
998 } else if (builder->bestPathScore > ACCEPT_PATH_SCORE) {
999 result = kSecTrustResultUnspecified;
1000 } else {
1001 result = kSecTrustResultRecoverableTrustFailure;
1002 }
1003
1004 secinfo("trust", "completed: %@ details: %@ result: %d",
1005 builder->bestPath, builder->path.details, result);
1006
1007 if (builder->completed) {
1008 builder->completed(builder->context, builder->bestPath,
1009 builder->path.details, builder->path.info, result);
1010 }
1011
1012 /* Finally, destroy the builder and free it. */
1013 SecPathBuilderDestroy(builder);
1014 free(builder);
1015
1016 return false;
1017 }
1018
1019 dispatch_queue_t SecPathBuilderGetQueue(SecPathBuilderRef builder) {
1020 return (builder) ? builder->queue : NULL;
1021 }
1022
1023 CFDataRef SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder) {
1024 return (builder) ? (CFDataRef)CFRetainSafe(builder->clientAuditToken) : NULL;
1025 }
1026
1027 // MARK: -
1028 // MARK: SecTrustServer
1029 /********************************************************
1030 ****************** SecTrustServer **********************
1031 ********************************************************/
1032
1033 typedef void (^SecTrustServerEvaluationCompleted)(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error);
1034
1035 static void
1036 SecTrustServerEvaluateCompleted(const void *userData,
1037 SecCertificatePathRef chain, CFArrayRef details, CFDictionaryRef info,
1038 SecTrustResultType result) {
1039 SecTrustServerEvaluationCompleted evaluated = (SecTrustServerEvaluationCompleted)userData;
1040 evaluated(result, details, info, chain, NULL);
1041 Block_release(evaluated);
1042 }
1043
1044 void
1045 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)) {
1046 /* We need an array containing at least one certificate to proceed. */
1047 if (!isArray(certificates) || !(CFArrayGetCount(certificates) > 0)) {
1048 CFErrorRef certError = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidCertificate, NULL);
1049 evaluated(kSecTrustResultInvalid, NULL, NULL, NULL, certError);
1050 CFReleaseSafe(certError);
1051 return;
1052 }
1053 SecTrustServerEvaluationCompleted userData = Block_copy(evaluated);
1054 /* Call the actual evaluator function. */
1055 SecPathBuilderRef builder = SecPathBuilderCreate(clientAuditToken,
1056 certificates, anchors,
1057 anchorsOnly, keychainsAllowed, policies,
1058 responses, SCTs, trustedLogs,
1059 verifyTime, accessGroups,
1060 SecTrustServerEvaluateCompleted, userData);
1061 dispatch_async(builder->queue, ^{ SecPathBuilderStep(builder); });
1062 }
1063
1064
1065 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1066 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) {
1067 dispatch_semaphore_t done = dispatch_semaphore_create(0);
1068 __block SecTrustResultType result = kSecTrustResultInvalid;
1069 SecTrustServerEvaluateBlock(NULL, certificates, anchors, anchorsOnly, keychainsAllowed, policies, responses, SCTs, trustedLogs, verifyTime, accessGroups, ^(SecTrustResultType tr, CFArrayRef details, CFDictionaryRef info, SecCertificatePathRef chain, CFErrorRef error) {
1070 result = tr;
1071 if (tr == kSecTrustResultInvalid) {
1072 if (perror) {
1073 *perror = error;
1074 CFRetainSafe(error);
1075 }
1076 } else {
1077 if (pdetails) {
1078 *pdetails = details;
1079 CFRetainSafe(details);
1080 }
1081 if (pinfo) {
1082 *pinfo = info;
1083 CFRetainSafe(info);
1084 }
1085 if (pchain) {
1086 *pchain = chain;
1087 CFRetainSafe(chain);
1088 }
1089 }
1090 dispatch_semaphore_signal(done);
1091 });
1092 dispatch_semaphore_wait(done, DISPATCH_TIME_FOREVER);
1093
1094 return result;
1095 }