2 * Copyright (c) 2006-2010,2012-2016 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
23 * SecTrustServer.c - certificate trust evaluation engine
28 #include <securityd/SecTrustServer.h>
29 #include <securityd/SecPolicyServer.h>
30 #include <securityd/SecTrustStoreServer.h>
31 #include <securityd/SecCAIssuerRequest.h>
32 #include <securityd/SecItemServer.h>
34 #include <utilities/SecIOFormat.h>
35 #include <utilities/SecDispatchRelease.h>
36 #include <utilities/SecAppleAnchorPriv.h>
38 #include <Security/SecTrustPriv.h>
39 #include <Security/SecItem.h>
40 #include <Security/SecCertificateInternal.h>
41 #include <Security/SecCertificatePath.h>
42 #include <Security/SecFramework.h>
43 #include <Security/SecPolicyPriv.h>
44 #include <Security/SecPolicyInternal.h>
45 #include <Security/SecTrustSettingsPriv.h>
46 #include <Security/SecTask.h>
47 #include <CoreFoundation/CFRuntime.h>
48 #include <CoreFoundation/CFSet.h>
49 #include <CoreFoundation/CFString.h>
50 #include <CoreFoundation/CFNumber.h>
51 #include <CoreFoundation/CFArray.h>
52 #include <CoreFoundation/CFPropertyList.h>
53 #include <AssertMacros.h>
58 #include <sys/codesign.h>
59 #include <Security/SecBase.h>
60 #include "SecRSAKey.h"
61 #include <libDER/oids.h>
62 #include <utilities/debugging.h>
63 #include <utilities/SecCFWrappers.h>
64 #include <Security/SecInternal.h>
65 #include <ipc/securityd_client.h>
66 #include <CommonCrypto/CommonDigest.h>
67 #include "OTATrustUtilities.h"
68 #include "personalization.h"
69 #include <utilities/SecInternalReleasePriv.h>
72 #include <Security/SecTaskPriv.h>
76 /********************************************************
77 ***************** OTA Trust support ********************
78 ********************************************************/
81 //#ifndef SECITEM_SHIM_OSX
83 static CFArrayRef
subject_to_anchors(CFDataRef nic
);
84 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
);
86 static CFArrayRef
subject_to_anchors(CFDataRef nic
)
88 CFArrayRef result
= NULL
;
95 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
96 if (NULL
== otapkiref
)
101 CFDictionaryRef lookupTable
= SecOTAPKICopyAnchorLookupTable(otapkiref
);
102 CFRelease(otapkiref
);
104 if (NULL
== lookupTable
)
109 unsigned char subject_digest
[CC_SHA1_DIGEST_LENGTH
];
110 memset(subject_digest
, 0, CC_SHA1_DIGEST_LENGTH
);
112 (void)CC_SHA1(CFDataGetBytePtr(nic
), (CC_LONG
)CFDataGetLength(nic
), subject_digest
);
113 CFDataRef sha1Digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, subject_digest
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
116 result
= (CFArrayRef
)CFDictionaryGetValue(lookupTable
, sha1Digest
);
117 CFReleaseSafe(lookupTable
);
118 CFReleaseSafe(sha1Digest
);
123 static CFArrayRef
CopyCertDataFromIndices(CFArrayRef offsets
)
125 CFMutableArrayRef result
= NULL
;
127 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
128 if (NULL
== otapkiref
)
133 const char* anchorTable
= SecOTAPKIGetAnchorTable(otapkiref
);
134 if (NULL
== anchorTable
)
136 CFReleaseSafe(otapkiref
);
140 CFIndex num_offsets
= CFArrayGetCount(offsets
);
142 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
144 for (CFIndex idx
= 0; idx
< num_offsets
; idx
++)
146 CFNumberRef offset
= (CFNumberRef
)CFArrayGetValueAtIndex(offsets
, idx
);
147 uint32_t offset_value
= 0;
148 if (CFNumberGetValue(offset
, kCFNumberSInt32Type
, &offset_value
))
150 char* pDataPtr
= (char *)(anchorTable
+ offset_value
);
151 //int32_t record_length = *((int32_t * )pDataPtr);
152 //record_length = record_length;
153 pDataPtr
+= sizeof(uint32_t);
155 int32_t cert_data_length
= *((int32_t * )pDataPtr
);
156 pDataPtr
+= sizeof(uint32_t);
158 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)pDataPtr
,
159 cert_data_length
, kCFAllocatorNull
);
160 if (NULL
!= cert_data
)
162 CFArrayAppendValue(result
, cert_data
);
163 CFReleaseSafe(cert_data
);
167 CFReleaseSafe(otapkiref
);
171 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
)
173 CFMutableArrayRef result
= NULL
;
175 CFArrayRef cert_data_array
= CopyCertDataFromIndices(offsets
);
177 if (NULL
!= cert_data_array
)
179 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
180 CFIndex num_cert_datas
= CFArrayGetCount(cert_data_array
);
181 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
183 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_data_array
, idx
);
184 if (NULL
!= cert_data
)
186 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, cert_data
);
189 CFArrayAppendValue(result
, cert
);
194 CFRelease(cert_data_array
);
199 //#endif // SECITEM_SHIM_OSX
201 /********************************************************
202 *************** END OTA Trust support ******************
203 ********************************************************/
205 #define MAX_CHAIN_LENGTH 15
206 #define ACCEPT_PATH_SCORE 10000000
208 /* Forward declaration for use in SecCertificateSource. */
209 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
213 // MARK: SecCertificateSource
214 /********************************************************
215 ************ SecCertificateSource object ***************
216 ********************************************************/
218 typedef struct SecCertificateSource
*SecCertificateSourceRef
;
219 typedef void(*SecCertificateSourceParents
)(void *, CFArrayRef
);
220 typedef bool(*CopyParents
)(SecCertificateSourceRef source
,
221 SecCertificateRef certificate
, void *context
, SecCertificateSourceParents
);
222 typedef CFArrayRef(*CopyConstraints
)(SecCertificateSourceRef source
,
223 SecCertificateRef certificate
);
224 typedef bool(*Contains
)(SecCertificateSourceRef source
,
225 SecCertificateRef certificate
);
227 struct SecCertificateSource
{
228 CopyParents copyParents
;
229 CopyConstraints copyUsageConstraints
;
233 static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source
,
234 SecCertificateRef certificate
,
235 void *context
, SecCertificateSourceParents callback
) {
236 return source
->copyParents(source
, certificate
, context
, callback
);
239 static CFArrayRef
SecCertificateSourceCopyUsageConstraints(
240 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
241 if (source
->copyUsageConstraints
) {
242 return source
->copyUsageConstraints(source
, certificate
);
248 static bool SecCertificateSourceContains(SecCertificateSourceRef source
,
249 SecCertificateRef certificate
) {
250 return source
->contains(source
, certificate
);
254 // MARK: SecItemCertificateSource
255 /********************************************************
256 *********** SecItemCertificateSource object ************
257 ********************************************************/
258 struct SecItemCertificateSource
{
259 struct SecCertificateSource base
;
260 CFArrayRef accessGroups
;
262 typedef struct SecItemCertificateSource
*SecItemCertificateSourceRef
;
264 static CFTypeRef
SecItemCertificateSourceResultsPost(CFTypeRef raw_results
) {
265 if (isArray(raw_results
)) {
266 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(raw_results
), &kCFTypeArrayCallBacks
);
267 CFArrayForEach(raw_results
, ^(const void *value
) {
268 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, value
);
270 CFArrayAppendValue(result
, cert
);
275 } else if (isData(raw_results
)) {
276 return SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)raw_results
);
281 static bool SecItemCertificateSourceCopyParents(
282 SecCertificateSourceRef source
, SecCertificateRef certificate
,
283 void *context
, SecCertificateSourceParents callback
) {
284 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
285 /* FIXME: Search for things other than just subject of our issuer if we
286 have a subjectID or authorityKeyIdentifier. */
287 CFDataRef normalizedIssuer
=
288 SecCertificateGetNormalizedIssuerContent(certificate
);
289 const void *keys
[] = {
296 kSecClassCertificate
,
301 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
303 CFTypeRef results
= NULL
;
304 SecurityClient client
= {
306 .accessGroups
= msource
->accessGroups
,
307 .allowSystemKeychain
= true,
308 .allowSyncBubbleKeychain
= false,
309 .isNetworkExtension
= false,
312 /* We can make this async or run this on a queue now easily. */
313 CFErrorRef localError
= NULL
;
314 if (!_SecItemCopyMatching(query
, &client
, &results
, &localError
)) {
315 if (localError
&& (CFErrorGetCode(localError
) != errSecItemNotFound
)) {
316 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
318 CFReleaseSafe(localError
);
321 CFTypeRef certs
= SecItemCertificateSourceResultsPost(results
);
322 CFReleaseSafe(results
);
323 callback(context
, certs
);
324 CFReleaseSafe(certs
);
328 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source
,
329 SecCertificateRef certificate
) {
330 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
331 /* Look up a certificate by issuer and serial number. */
332 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
333 CFRetainSafe(normalizedIssuer
);
334 CFDataRef serialNumber
=
335 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
336 SecCertificateCopySerialNumber(certificate
, NULL
);
338 SecCertificateCopySerialNumber(certificate
);
340 const void *keys
[] = {
347 kSecClassCertificate
,
352 SecurityClient client
= {
354 .accessGroups
= msource
->accessGroups
,
355 .allowSystemKeychain
= true,
356 .allowSyncBubbleKeychain
= false,
357 .isNetworkExtension
= false,
359 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4, NULL
, NULL
);
360 CFErrorRef localError
= NULL
;
361 CFTypeRef results
= NULL
;
362 bool ok
= _SecItemCopyMatching(query
, &client
, &results
, &localError
);
363 CFReleaseSafe(query
);
364 CFReleaseSafe(serialNumber
);
365 CFReleaseSafe(normalizedIssuer
);
366 CFReleaseSafe(results
);
368 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
369 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
371 CFReleaseSafe(localError
);
377 static SecCertificateSourceRef
SecItemCertificateSourceCreate(CFArrayRef accessGroups
) {
378 SecItemCertificateSourceRef result
= (SecItemCertificateSourceRef
)malloc(sizeof(*result
));
379 result
->base
.copyParents
= SecItemCertificateSourceCopyParents
;
380 result
->base
.copyUsageConstraints
= NULL
;
381 result
->base
.contains
= SecItemCertificateSourceContains
;
382 result
->accessGroups
= accessGroups
;
383 CFRetainSafe(accessGroups
);
384 return (SecCertificateSourceRef
)result
;
387 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source
) {
388 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
389 CFReleaseSafe(msource
->accessGroups
);
394 // MARK: SecSystemAnchorSource
395 /********************************************************
396 *********** SecSystemAnchorSource object ************
397 ********************************************************/
399 static bool SecSystemAnchorSourceCopyParents(
400 SecCertificateSourceRef source
, SecCertificateRef certificate
,
401 void *context
, SecCertificateSourceParents callback
) {
402 //#ifndef SECITEM_SHIM_OSX
403 CFArrayRef parents
= NULL
;
404 CFArrayRef anchors
= NULL
;
405 SecOTAPKIRef otapkiref
= NULL
;
407 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
408 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
409 It does not matter since we would be returning the wrong anchors */
410 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
412 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
413 require_quiet(otapkiref
, errOut
);
414 anchors
= subject_to_anchors(nic
);
415 require_quiet(anchors
, errOut
);
416 parents
= CopyCertsFromIndices(anchors
);
419 callback(context
, parents
);
420 CFReleaseSafe(parents
);
421 CFReleaseSafe(otapkiref
);
422 //#endif // SECITEM_SHIM_OSX
426 /* Quick thought: we can eliminate this method if we search anchor sources
427 before all others and we remember if we got a cert from an anchorsource. */
428 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source
,
429 SecCertificateRef certificate
) {
431 CFArrayRef anchors
= NULL
;
432 SecOTAPKIRef otapkiref
= NULL
;
433 CFArrayRef cert_datas
= NULL
;
435 CFDataRef nic
= SecCertificateGetNormalizedSubjectContent(certificate
);
436 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
437 It does not matter since we would be returning the wrong anchors */
438 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
440 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
441 require_quiet(otapkiref
, errOut
);
442 anchors
= subject_to_anchors(nic
);
443 require_quiet(anchors
, errOut
);
444 cert_datas
= CopyCertDataFromIndices(anchors
);
445 require_quiet(cert_datas
, errOut
);
447 CFIndex cert_length
= SecCertificateGetLength(certificate
);
448 const UInt8
*cert_data_ptr
= SecCertificateGetBytePtr(certificate
);
450 CFIndex num_cert_datas
= CFArrayGetCount(cert_datas
);
451 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
453 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, idx
);
455 if (NULL
!= cert_data
)
457 if (CFGetTypeID(cert_data
) == CFDataGetTypeID())
459 CFIndex aCert_Length
= CFDataGetLength(cert_data
);
460 const UInt8
* aCert_Data_Ptr
= CFDataGetBytePtr(cert_data
);
462 if (aCert_Length
== cert_length
)
464 if (!memcmp(cert_data_ptr
, aCert_Data_Ptr
, cert_length
))
475 CFReleaseSafe(cert_datas
);
476 CFReleaseSafe(otapkiref
);
482 struct SecCertificateSource kSecSystemAnchorSource
= {
483 SecSystemAnchorSourceCopyParents
,
485 SecSystemAnchorSourceContains
490 // MARK: SecUserAnchorSource
491 /********************************************************
492 ************* SecUserAnchorSource object ***************
493 ********************************************************/
494 static bool SecUserAnchorSourceCopyParents(
495 SecCertificateSourceRef source
, SecCertificateRef certificate
,
496 void *context
, SecCertificateSourceParents callback
) {
497 CFArrayRef parents
= SecTrustStoreCopyParents(
498 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
, NULL
);
499 callback(context
, parents
);
500 CFReleaseSafe(parents
);
504 static CFArrayRef
SecUserAnchorSourceCopyUsageConstraints(
505 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
506 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
509 CFArrayRef usageConstraints
= NULL
;
510 bool ok
= _SecTrustStoreCopyUsageConstraints(
511 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), digest
, &usageConstraints
, NULL
);
512 return (ok
) ? usageConstraints
: NULL
;
515 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source
,
516 SecCertificateRef certificate
) {
517 return SecTrustStoreContains(
518 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
);
521 struct SecCertificateSource kSecUserAnchorSource
= {
522 SecUserAnchorSourceCopyParents
,
523 SecUserAnchorSourceCopyUsageConstraints
,
524 SecUserAnchorSourceContains
529 // MARK: SecMemoryCertificateSource
530 /********************************************************
531 ********** SecMemoryCertificateSource object ***********
532 ********************************************************/
533 struct SecMemoryCertificateSource
{
534 struct SecCertificateSource base
;
535 CFMutableSetRef certificates
;
536 CFMutableDictionaryRef subjects
;
538 typedef struct SecMemoryCertificateSource
*SecMemoryCertificateSourceRef
;
540 static bool SecMemoryCertificateSourceCopyParents(
541 SecCertificateSourceRef source
, SecCertificateRef certificate
,
542 void *context
, SecCertificateSourceParents callback
) {
543 SecMemoryCertificateSourceRef msource
=
544 (SecMemoryCertificateSourceRef
)source
;
545 CFDataRef normalizedIssuer
=
546 SecCertificateGetNormalizedIssuerContent(certificate
);
547 CFArrayRef parents
= (normalizedIssuer
) ? CFDictionaryGetValue(msource
->subjects
,
548 normalizedIssuer
) : NULL
;
549 /* FIXME filter parents by subjectID if certificate has an
550 authorityKeyIdentifier. */
551 secdebug("trust", "%@ parents -> %@", certificate
, parents
);
552 callback(context
, parents
);
556 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source
,
557 SecCertificateRef certificate
) {
558 SecMemoryCertificateSourceRef msource
=
559 (SecMemoryCertificateSourceRef
)source
;
560 return CFSetContainsValue(msource
->certificates
, certificate
);
563 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict
,
564 const void *key
, const void *value
) {
568 CFMutableArrayRef values
=
569 (CFMutableArrayRef
)CFDictionaryGetValue(dict
, key
);
571 values
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
572 &kCFTypeArrayCallBacks
);
573 CFDictionaryAddValue(dict
, key
, values
);
578 CFArrayAppendValue(values
, value
);
581 static void SecMemoryCertificateSourceApplierFunction(const void *value
,
583 SecMemoryCertificateSourceRef msource
=
584 (SecMemoryCertificateSourceRef
)context
;
585 SecCertificateRef certificate
= (SecCertificateRef
)value
;
587 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
588 if (CFSetContainsValue(msource
->certificates
, certificate
))
590 CFSetAddValue(msource
->certificates
, certificate
);
592 CFDataRef key
= SecCertificateGetNormalizedSubjectContent(certificate
);
593 dictAddValueToArrayForKey(msource
->subjects
, key
, value
);
596 static SecCertificateSourceRef
SecMemoryCertificateSourceCreate(
597 CFArrayRef certificates
) {
598 SecMemoryCertificateSourceRef result
= (SecMemoryCertificateSourceRef
)
599 malloc(sizeof(*result
));
600 result
->base
.copyParents
= SecMemoryCertificateSourceCopyParents
;
601 result
->base
.copyUsageConstraints
= NULL
;
602 result
->base
.contains
= SecMemoryCertificateSourceContains
;
603 CFIndex count
= CFArrayGetCount(certificates
);
604 result
->certificates
= CFSetCreateMutable(kCFAllocatorDefault
, count
,
605 &kCFTypeSetCallBacks
);
606 result
->subjects
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
607 count
, &kCFTypeDictionaryKeyCallBacks
,
608 &kCFTypeDictionaryValueCallBacks
);
609 CFRange range
= { 0, count
};
610 CFArrayApplyFunction(certificates
, range
,
611 SecMemoryCertificateSourceApplierFunction
, result
);
613 return (SecCertificateSourceRef
)result
;
616 static void SecMemoryCertificateSourceDestroy(
617 SecCertificateSourceRef source
) {
618 SecMemoryCertificateSourceRef msource
=
619 (SecMemoryCertificateSourceRef
)source
;
620 CFRelease(msource
->certificates
);
621 CFRelease(msource
->subjects
);
626 // MARK: SecCAIssuerCertificateSource
627 /********************************************************
628 ********* SecCAIssuerCertificateSource object **********
629 ********************************************************/
630 static bool SecCAIssuerCertificateSourceCopyParents(
631 SecCertificateSourceRef source
, SecCertificateRef certificate
,
632 void *context
, SecCertificateSourceParents callback
) {
633 return SecCAIssuerCopyParents(certificate
, SecPathBuilderGetQueue((SecPathBuilderRef
)context
), context
, callback
);
636 static bool SecCAIssuerCertificateSourceContains(
637 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
641 struct SecCertificateSource kSecCAIssuerSource
= {
642 SecCAIssuerCertificateSourceCopyParents
,
644 SecCAIssuerCertificateSourceContains
647 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
648 #include <Security/SecItemPriv.h>
650 // MARK: SecLegacyCertificateSource
651 /********************************************************
652 ********** SecLegacyCertificateSource object ***********
653 ********************************************************/
655 static bool SecLegacyCertificateSourceCopyParents(
656 SecCertificateSourceRef source
, SecCertificateRef certificate
,
657 void *context
, SecCertificateSourceParents callback
) {
658 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
659 callback(context
, parents
);
660 CFReleaseSafe(parents
);
664 static bool SecLegacyCertificateSourceContains(
665 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
666 SecCertificateRef cert
= SecItemCopyStoredCertificate(certificate
, NULL
);
667 bool result
= (cert
) ? true : false;
672 struct SecCertificateSource kSecLegacyCertificateSource
= {
673 SecLegacyCertificateSourceCopyParents
,
675 SecLegacyCertificateSourceContains
677 #endif /* SecLegacyCertificateSource */
679 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
681 // MARK: SecLegacyAnchorSource
682 /********************************************************
683 ************ SecLegacyAnchorSource object **************
684 ********************************************************/
686 static bool SecLegacyAnchorSourceCopyParents(
687 SecCertificateSourceRef source
, SecCertificateRef certificate
,
688 void *context
, SecCertificateSourceParents callback
) {
689 CFMutableArrayRef anchors
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
690 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
691 CFArrayRef trusted
= NULL
;
692 if (parents
== NULL
) {
695 /* Get the custom anchors which have been trusted in the user and admin domains.
696 * We don't need system domain roots here, since SecSystemAnchorSource provides those.
698 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
699 if (status
== errSecSuccess
&& trusted
) {
700 CFIndex index
, count
= CFArrayGetCount(parents
);
701 for (index
= 0; index
< count
; index
++) {
702 SecCertificateRef parent
= (SecCertificateRef
)CFArrayGetValueAtIndex(parents
, index
);
703 if (parent
&& CFArrayContainsValue(trusted
, CFRangeMake(0, CFArrayGetCount(trusted
)), parent
)) {
704 CFArrayAppendValue(anchors
, parent
);
710 callback(context
, anchors
);
711 CFReleaseSafe(anchors
);
712 CFReleaseSafe(parents
);
713 CFReleaseSafe(trusted
);
717 static CFArrayRef
SecLegacyAnchorSourceCopyUsageConstraints(
718 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
720 CFArrayRef result
= NULL
;
721 CFArrayRef userTrustSettings
= NULL
, adminTrustSettings
= NULL
;
723 OSStatus status
= SecTrustSettingsCopyTrustSettings(certificate
,
724 kSecTrustSettingsDomainUser
,
726 if ((status
== errSecSuccess
) && (userTrustSettings
!= NULL
)) {
727 result
= CFRetain(userTrustSettings
);
730 status
= SecTrustSettingsCopyTrustSettings(certificate
,
731 kSecTrustSettingsDomainAdmin
,
732 &adminTrustSettings
);
733 /* user trust settings overrule admin trust settings */
734 if ((status
== errSecSuccess
) && (adminTrustSettings
!= NULL
) && (result
== NULL
)) {
735 result
= CFRetain(adminTrustSettings
);
738 CFReleaseNull(userTrustSettings
);
739 CFReleaseNull(adminTrustSettings
);
743 static bool SecLegacyAnchorSourceContains(
744 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
745 if (certificate
== NULL
) {
748 CFArrayRef trusted
= NULL
;
750 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
751 if ((status
== errSecSuccess
) && (trusted
!= NULL
)) {
752 CFIndex index
, count
= CFArrayGetCount(trusted
);
753 for (index
= 0; index
< count
; index
++) {
754 SecCertificateRef anchor
= (SecCertificateRef
)CFArrayGetValueAtIndex(trusted
, index
);
755 if (anchor
&& CFEqual(anchor
, certificate
)) {
761 CFReleaseSafe(trusted
);
765 struct SecCertificateSource kSecLegacyAnchorSource
= {
766 SecLegacyAnchorSourceCopyParents
,
767 SecLegacyAnchorSourceCopyUsageConstraints
,
768 SecLegacyAnchorSourceContains
770 #endif /* SecLegacyAnchorSource */
773 // MARK: SecPathBuilder
774 /********************************************************
775 *************** SecPathBuilder object ******************
776 ********************************************************/
777 struct SecPathBuilder
{
778 dispatch_queue_t queue
;
779 CFDataRef clientAuditToken
;
780 SecCertificateSourceRef certificateSource
;
781 SecCertificateSourceRef itemCertificateSource
;
782 SecCertificateSourceRef anchorSource
;
783 SecCertificateSourceRef appleAnchorSource
;
784 CFMutableArrayRef anchorSources
;
785 CFIndex nextParentSource
;
786 CFMutableArrayRef parentSources
;
787 CFArrayRef ocspResponses
; // Stapled OCSP responses
788 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
789 CFArrayRef trustedLogs
; // Trusted CT logs
791 /* Hashed set of all paths we've constructed so far, used to prevent
792 re-considering a path that was already constructed once before.
793 Note that this is the only container in which certificatePath
794 objects are retained.
795 Every certificatePath being considered is always in allPaths and in at
796 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
797 all of which don't retain their values. */
798 CFMutableSetRef allPaths
;
800 /* No trusted anchor, satisfies the linking to intermediates for all
801 policies (unless considerRejected is true). */
802 CFMutableArrayRef partialPaths
;
803 /* No trusted anchor, does not satisfy linking to intermediates for all
805 CFMutableArrayRef rejectedPaths
;
806 /* Trusted anchor, satisfies the policies so far. */
807 CFMutableArrayRef candidatePaths
;
811 CFArrayRef leafDetails
;
813 CFIndex bestPathScore
;
815 bool considerRejected
;
816 bool considerPartials
;
817 bool canAccessNetwork
;
819 struct OpaqueSecPVC path
;
820 SecCertificatePathRef bestPath
;
826 bool (*state
)(SecPathBuilderRef
);
827 SecPathBuilderCompleted completed
;
831 /* State functions. Return false if a async job was scheduled, return
832 true to execute the next state. */
833 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
834 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
835 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
836 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
837 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
839 /* Forward declarations. */
840 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
841 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
843 /* IDEA: policies could be made capable of replacing incoming anchors and
844 anchorsOnly argument values. For example, some policies require the
845 Apple Inc. CA and not any other anchor. This can be done in
846 SecPathBuilderLeafCertificateChecks since this only runs once. */
847 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
848 SecCertificatePathRef path
) {
849 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
850 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
851 &kCFTypeDictionaryValueCallBacks
);
852 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
853 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
854 CFRelease(certDetail
);
855 SecPVCRef pvc
= &builder
->path
;
856 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
857 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
860 static void SecPathBuilderInit(SecPathBuilderRef builder
,
861 CFDataRef clientAuditToken
, CFArrayRef certificates
,
862 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
863 CFArrayRef policies
, CFArrayRef ocspResponses
,
864 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
865 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
866 SecPathBuilderCompleted completed
, const void *context
) {
867 secdebug("alloc", "%p", builder
);
868 CFAllocatorRef allocator
= kCFAllocatorDefault
;
870 builder
->clientAuditToken
= (CFDataRef
)
871 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
872 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
874 builder
->nextParentSource
= 1;
876 builder
->canAccessNetwork
= true;
879 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
880 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
881 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
882 &kCFTypeSetCallBacks
);
884 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
885 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
886 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
888 /* Init the policy verification context. */
889 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
891 /* Let's create all the certificate sources we might want to use. */
892 builder
->certificateSource
=
893 SecMemoryCertificateSourceCreate(certificates
);
895 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
898 bool allowNonProduction
= false;
899 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
903 ** The order here avoids the most expensive methods if the cheaper methods
904 ** produce an acceptable chain: client-provided, keychains, network-fetched.
906 #if !TARGET_OS_BRIDGE
907 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
908 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
909 if (keychainsAllowed
) {
910 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
912 /* On OS X, need additional parent source to search legacy keychain files. */
913 if (kSecLegacyCertificateSource
.contains
&& kSecLegacyCertificateSource
.copyParents
) {
914 CFArrayAppendValue(builder
->parentSources
, &kSecLegacyCertificateSource
);
919 /* Add the Apple, system, and user anchor certificate db to the search list
920 if we don't explicitly trust them. */
921 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
922 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
924 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
927 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
928 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
930 #else /* TARGET_OS_BRIDGE */
931 /* Bridge can only access memory sources. */
932 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
934 /* Add the Apple, system, and user anchor certificate db to the search list
935 if we don't explicitly trust them. */
936 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
938 #endif /* !TARGET_OS_BRIDGE */
941 ** The order here allows a client-provided anchor to overrule
942 ** a user or admin trust setting which can overrule the system anchors.
943 ** Apple's anchors cannot be overriden by a trust setting.
945 #if !TARGET_OS_BRIDGE
946 if (builder
->anchorSource
) {
947 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
950 /* Only add the system and user anchor certificate db to the
951 anchorSources if we are supposed to trust them. */
952 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
954 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
955 #else /* TARGET_OS_OSX */
956 if (keychainsAllowed
&& kSecLegacyAnchorSource
.contains
&& kSecLegacyAnchorSource
.copyParents
) {
957 CFArrayAppendValue(builder
->anchorSources
, &kSecLegacyAnchorSource
);
960 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
962 #else /* TARGET_OS_BRIDGE */
963 /* Bridge can only access memory sources. */
964 if (builder
->anchorSource
) {
965 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
968 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
970 #endif /* !TARGET_OS_BRIDGE */
972 /* Now let's get the leaf cert and turn it into a path. */
973 SecCertificateRef leaf
=
974 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
975 SecCertificateSourceRef source
= NULL
;
976 bool isAnchor
= false;
977 CFArrayRef constraints
= NULL
;
978 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
982 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
984 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
, constraints
);
985 CFReleaseSafe(constraints
);
986 CFSetAddValue(builder
->allPaths
, path
);
987 CFArrayAppendValue(builder
->partialPaths
, path
);
989 SecCertificatePathSetIsAnchored(path
);
990 CFArrayAppendValue(builder
->candidatePaths
, path
);
992 SecPathBuilderLeafCertificateChecks(builder
, path
);
995 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
996 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
999 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
1001 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
1002 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
1003 CFReleaseSafe(otapkiref
);
1006 builder
->state
= SecPathBuilderGetNext
;
1007 builder
->completed
= completed
;
1008 builder
->context
= context
;
1011 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
1012 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
1013 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
1014 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
1015 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
1016 SecPathBuilderCompleted completed
, const void *context
) {
1017 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
1018 memset(builder
, 0, sizeof(*builder
));
1019 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
1020 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
1021 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
1022 accessGroups
, completed
, context
);
1026 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
1027 secdebug("alloc", "%p", builder
);
1028 dispatch_release_null(builder
->queue
);
1029 if (builder
->anchorSource
) {
1030 SecMemoryCertificateSourceDestroy(builder
->anchorSource
); }
1031 if (builder
->certificateSource
) {
1032 SecMemoryCertificateSourceDestroy(builder
->certificateSource
); }
1033 if (builder
->itemCertificateSource
) {
1034 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
); }
1035 if (builder
->appleAnchorSource
) {
1036 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
); }
1037 CFReleaseSafe(builder
->clientAuditToken
);
1038 CFReleaseSafe(builder
->anchorSources
);
1039 CFReleaseSafe(builder
->parentSources
);
1040 CFReleaseSafe(builder
->allPaths
);
1041 CFReleaseSafe(builder
->partialPaths
);
1042 CFReleaseSafe(builder
->rejectedPaths
);
1043 CFReleaseSafe(builder
->candidatePaths
);
1044 CFReleaseSafe(builder
->leafDetails
);
1045 CFReleaseSafe(builder
->ocspResponses
);
1046 CFReleaseSafe(builder
->signedCertificateTimestamps
);
1047 CFReleaseSafe(builder
->trustedLogs
);
1049 SecPVCDelete(&builder
->path
);
1052 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
1053 return builder
->canAccessNetwork
;
1056 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
1057 if (builder
->canAccessNetwork
!= allow
) {
1058 builder
->canAccessNetwork
= allow
;
1060 #if !TARGET_OS_WATCH
1061 secinfo("http", "network access re-enabled by policy");
1062 /* re-enabling network_access re-adds kSecCAIssuerSource as
1064 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
1066 secnotice("http", "network access not allowed on WatchOS");
1067 builder
->canAccessNetwork
= false;
1070 secinfo("http", "network access disabled by policy");
1071 /* disabling network_access removes kSecCAIssuerSource from
1072 the list of parent sources. */
1073 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
1074 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
1075 &kSecCAIssuerSource
);
1077 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
1082 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
1084 return CFRetainSafe(builder
->ocspResponses
);
1087 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
1089 return CFRetainSafe(builder
->signedCertificateTimestamps
);
1092 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
1094 return CFRetainSafe(builder
->trustedLogs
);
1097 /* This function assumes that the input source is an anchor source */
1098 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
1099 SecCertificateRef certificate
) {
1100 bool result
= false;
1101 CFArrayRef constraints
= NULL
;
1102 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
1104 /* Unrestricted certificates:
1105 * -those that come from anchor sources with no constraints
1106 * -self-signed certificates with empty contraints arrays
1108 Boolean selfSigned
= false;
1109 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
1110 if ((NULL
== source
->copyUsageConstraints
) ||
1111 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
1112 secinfo("trust", "unrestricted anchor%s",
1113 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
1118 /* Get the trust settings result for the PVC */
1119 require(constraints
, out
);
1120 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
1121 settingsResult
= SecPVCGetTrustSettingsResult(&builder
->path
,
1124 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
1125 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
1126 // For our purposes, this is an anchor.
1127 secinfo("trust", "complex trust settings anchor");
1131 if (settingsResult
== kSecTrustSettingsResultDeny
) {
1132 /* We consider denied certs "anchors" because the trust decision
1133 is set regardless of building the chain further. The policy
1134 validation will handle rejecting this chain. */
1135 secinfo("trust", "complex trust settings denied anchor");
1140 CFReleaseNull(constraints
);
1144 /* Source returned in foundInSource has the same lifetime as the builder. */
1145 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
1146 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
1147 /* We look through the anchor sources in order. They are ordered in
1148 SecPathBuilderInit so that process anchors override user anchors which
1149 override system anchors. */
1150 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
1152 for (ix
= 0; ix
< count
; ++ix
) {
1153 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
1154 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
1155 if (SecCertificateSourceContains(source
, certificate
)) {
1157 *foundInSource
= source
;
1158 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
1166 /* Return false if path is not a partial, if path was a valid candidate it
1167 will have been added to builder->candidatePaths, if path was rejected
1168 by the parent certificate checks (because it's expired or some other
1169 static chaining check failed) it will have been added to rejectedPaths.
1170 Return true path if path is a partial. */
1171 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
1172 SecCertificatePathRef path
) {
1173 SecPVCRef pvc
= &builder
->path
;
1174 SecPVCSetPath(pvc
, path
, NULL
);
1176 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
1177 SecPVCGetCertificateCount(pvc
) - 1)) {
1178 secdebug("trust", "Found rejected path %@", path
);
1179 CFArrayAppendValue(builder
->rejectedPaths
, path
);
1183 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
1184 /* Candidate paths with failed signatures are discarded. */
1185 if (vstatus
== kSecPathVerifyFailed
) {
1186 secdebug("trust", "Verify failed for path %@", path
);
1190 if (vstatus
== kSecPathVerifySuccess
) {
1191 /* The signature chain verified sucessfully, now let's find
1192 out if we have an anchor for path. */
1193 if (SecCertificatePathIsAnchored(path
)) {
1194 secdebug("trust", "Adding candidate %@", path
);
1195 CFArrayAppendValue(builder
->candidatePaths
, path
);
1197 /* The path is not partial if the last cert is self-signed. */
1198 if ((SecCertificatePathSelfSignedIndex(path
) >= 0) &&
1199 (SecCertificatePathSelfSignedIndex(path
) == SecCertificatePathGetCount(path
)-1)) {
1207 /* Given the builder, a partial chain partial and the parents array, construct
1208 a SecCertificatePath for each parent. After discarding previously
1209 considered paths and paths with cycles, sort out which array each path
1210 should go in, if any. */
1211 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
1212 SecCertificatePathRef partial
, CFArrayRef parents
) {
1213 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
1214 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
1216 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
1217 SecCertificateRef parent
= (SecCertificateRef
)
1218 CFArrayGetValueAtIndex(parents
, parentIX
);
1219 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
1221 if (ixOfParent
!= kCFNotFound
) {
1222 /* partial already contains parent. Let's not add the same
1223 certificate again. */
1224 if (ixOfParent
== rootIX
) {
1225 /* parent is equal to the root of the partial, so partial
1226 looks to be self issued. */
1227 SecCertificatePathSetSelfIssued(partial
);
1232 /* FIXME Add more sanity checks to see that parent really can be
1233 a parent of partial_root. subjectKeyID == authorityKeyID,
1234 signature algorithm matches public key algorithm, etc. */
1235 SecCertificateSourceRef source
= NULL
;
1236 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
1237 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
1238 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
, constraints
);
1239 CFReleaseSafe(constraints
);
1242 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
1243 CFSetAddValue(builder
->allPaths
, path
);
1245 SecCertificatePathSetIsAnchored(path
);
1246 if (SecPathBuilderIsPartial(builder
, path
)) {
1247 /* Insert path right at the current position since it's a new
1248 candiate partial. */
1249 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1250 ++builder
->partialIX
, path
);
1251 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
1252 parentIX
+ 1, num_parents
, path
);
1254 secdebug("trust", "found new path %@", path
);
1260 /* Callback for the SecPathBuilderGetNext() functions call to
1261 SecCertificateSourceCopyParents(). */
1262 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
1263 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
1264 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1265 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1266 secdebug("async", "%@ parents %@", partial
, parents
);
1267 SecPathBuilderProcessParents(builder
, partial
, parents
);
1269 builder
->state
= SecPathBuilderGetNext
;
1270 SecPathBuilderStep(builder
);
1273 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
1274 /* If we have any candidates left to go return those first. */
1275 if (CFArrayGetCount(builder
->candidatePaths
)) {
1276 SecCertificatePathRef path
= (SecCertificatePathRef
)
1277 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
1278 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
1279 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
1281 SecPVCSetPath(&builder
->path
, path
, NULL
);
1282 builder
->state
= SecPathBuilderValidatePath
;
1286 /* If we are considering rejected chains we check each rejected path
1287 with SecPathBuilderIsPartial() which checks the signature chain and
1288 either drops the path if it's not properly signed, add it as a
1289 candidate if it has a trusted anchor, or adds it as a partial
1290 to be considered once we finish considering all the rejects. */
1291 if (builder
->considerRejected
) {
1292 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
1295 SecCertificatePathRef path
= (SecCertificatePathRef
)
1296 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1297 if (SecPathBuilderIsPartial(builder
, path
)) {
1298 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1299 ++builder
->partialIX
, path
);
1301 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1303 /* Keep going until we have moved all rejected partials into
1304 the regular partials or candidates array. */
1309 /* If builder->partialIX is < 0 we have considered all partial chains
1310 this block must ensure partialIX >= 0 if execution continues past
1312 if (builder
->partialIX
< 0) {
1313 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1314 if (builder
->nextParentSource
< num_sources
) {
1315 builder
->nextParentSource
++;
1316 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1317 builder
->nextParentSource
, num_sources
);
1319 /* We've run out of new sources to consider so let's look at
1320 rejected chains and after that even consider partials
1322 FIXME we might not want to consider partial paths that
1323 are subsets of other partial paths, or not consider them
1324 at all if we already have an anchored reject. */
1325 if (!builder
->considerRejected
) {
1326 builder
->considerRejected
= true;
1327 secdebug("trust", "considering rejected paths");
1328 } else if (!builder
->considerPartials
) {
1329 builder
->considerPartials
= true;
1330 secdebug("trust", "considering partials");
1332 /* We're all out of options, so we can't produce any more
1333 candidates. Let's calculate details and return the best
1335 builder
->state
= SecPathBuilderComputeDetails
;
1339 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1340 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1344 /* We know builder->partialIX >= 0 if we get here. */
1345 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1346 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1347 /* Don't try to extend partials anymore once we are in the considerPartials
1348 state, since at this point every partial has been extended with every
1349 possible parentSource already. */
1350 if (builder
->considerPartials
) {
1351 --builder
->partialIX
;
1352 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1353 builder
->state
= SecPathBuilderValidatePath
;
1357 /* Attempt to extend this partial path with another certificate. This
1358 should give us a list of potential parents to consider. */
1359 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1360 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1363 /* Attempt to extend partial, leaving all possible extended versions
1364 of partial in builder->extendedPaths. */
1365 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1366 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1367 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1368 SecCertificateSourceRef source
;
1369 if (sourceIX
< num_anchor_sources
) {
1370 source
= (SecCertificateSourceRef
)
1371 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1372 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1373 num_anchor_sources
);
1375 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1376 source
= (SecCertificateSourceRef
)
1377 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1378 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1379 builder
->nextParentSource
);
1381 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1382 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1383 return SecCertificateSourceCopyParents(source
, root
,
1384 builder
, SecPathBuilderExtendPaths
);
1386 --builder
->partialIX
;
1392 /* One or more of the policies did not accept the candidate path. */
1393 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1395 SecPVCRef pvc
= &builder
->path
;
1397 builder
->state
= SecPathBuilderGetNext
;
1399 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1400 /* We never replace an ev reject with a non ev reject. */
1404 CFIndex bestPathScore
= builder
->bestPathScore
;
1405 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1406 SecPVCGetVerifyTime(&builder
->path
));
1408 /* The current chain is valid for EV, but revocation checking failed. We
1409 replace any previously accepted or rejected non EV chains with the
1411 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1417 /* Since this means we found a valid ev chain that was revoked,
1418 we might want to switch directly to the
1419 SecPathBuilderComputeDetails state here if we think further
1420 searching for new chains is pointless. For now we'll keep
1421 going, since we could accept an alternate EV certification
1422 path that isn't revoked. */
1423 builder
->state
= SecPathBuilderComputeDetails
;
1427 /* Do this last so that changes to bestPathScore above will take effect. */
1428 if (!builder
->bestPath
|| score
> bestPathScore
) {
1429 if (builder
->bestPath
) {
1431 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1432 (builder
->bestPathIsEV
? "" : "non "),
1433 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1434 builder
->bestPathScore
,
1435 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1437 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1438 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1441 builder
->bestPathScore
= score
;
1442 builder
->bestPath
= pvc
->path
;
1443 builder
->bestPathIsEV
= pvc
->is_ev
;
1444 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
);
1446 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1447 (pvc
->is_ev
? "" : "non "), score
, bestPathScore
, builder
->path
.path
);
1451 /* All policies accepted the candidate path. */
1452 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1454 SecPVCRef pvc
= &builder
->path
;
1455 bool isSHA2
= !SecCertificatePathHasWeakHash(pvc
->path
);
1456 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc
, 0));
1457 CFIndex bestScore
= builder
->bestPathScore
;
1458 /* Score this path. Note that all points awarded or deducted in
1459 * SecCertificatePathScore are < 100,000 */
1460 CFIndex currScore
= (SecCertificatePathScore(pvc
->path
, pvc
->verifyTime
) +
1461 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1462 ((pvc
->is_ev
) ? 1000000 : 0)); //1,000,000 points for EV
1463 if (currScore
> bestScore
) {
1464 // current path is better than existing best path
1465 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1466 (builder
->bestPathIsEV
? "" : "non "),
1467 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1468 builder
->bestPathScore
,
1469 (pvc
->is_ev
? "" : "non "), (long)currScore
, builder
->path
.path
);
1471 builder
->bestPathScore
= currScore
;
1472 builder
->bestPathIsEV
= pvc
->is_ev
;
1473 builder
->bestPathIsSHA2
= isSHA2
;
1474 builder
->bestPath
= pvc
->path
;
1475 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
); /* should always be false */
1478 /* If we found the best accept we can, we want to switch directly to the
1479 SecPathBuilderComputeDetails state here, since we're done. */
1480 if ((pvc
->is_ev
|| !pvc
->optionally_ev
) && (isSHA2
|| !isOptionallySHA2
))
1481 builder
->state
= SecPathBuilderComputeDetails
;
1483 builder
->state
= SecPathBuilderGetNext
;
1486 /* Return true iff a given path satisfies all the specified policies at
1488 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1489 SecPVCRef pvc
= &builder
->path
;
1491 if (builder
->considerRejected
) {
1492 SecPathBuilderReject(builder
);
1496 builder
->state
= SecPathBuilderDidValidatePath
;
1497 return SecPVCPathChecks(pvc
);
1500 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1501 SecPVCRef pvc
= &builder
->path
;
1503 SecPathBuilderAccept(builder
);
1505 SecPathBuilderReject(builder
);
1507 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1511 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1513 SecPVCRef pvc
= &builder
->path
;
1515 if (!builder
->caller_wants_details
) {
1516 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1517 pvc
->result
= builder
->bestPathScore
> ACCEPT_PATH_SCORE
;
1518 builder
->state
= SecPathBuilderReportResult
;
1522 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1523 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1524 pathLength
, builder
->leafDetails
);
1525 CFRetainSafe(details
);
1526 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1527 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1528 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1529 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1530 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1531 for (ix
= 1; ix
< pathLength
; ++ix
) {
1532 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1533 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1534 &kCFTypeDictionaryValueCallBacks
);
1535 CFArrayAppendValue(details
, certDetail
);
1536 CFRelease(certDetail
);
1537 SecPVCParentCertificateChecks(pvc
, ix
);
1538 SecPVCGrayListedKeyChecks(pvc
, ix
);
1539 SecPVCBlackListedKeyChecks(pvc
, ix
);
1541 builder
->state
= SecPathBuilderReportResult
;
1542 bool completed
= SecPVCPathChecks(pvc
);
1544 /* Reject the certificate if it was accepted before but we failed it now. */
1545 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
&& !pvc
->result
) {
1546 builder
->bestPathScore
= 0;
1549 /* Accept a partial path if certificate is on the allow list
1550 and is temporally valid and passed all PVC checks. */
1551 if (completed
&& pvc
->is_allowlisted
&& pvc
->result
&&
1552 builder
->bestPathScore
< ACCEPT_PATH_SCORE
&&
1553 SecCertificatePathIsValid(pvc
->path
, pvc
->verifyTime
)) {
1554 builder
->bestPathScore
+= ACCEPT_PATH_SCORE
;
1557 CFReleaseSafe(details
);
1563 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1564 SecPVCRef pvc
= &builder
->path
;
1565 bool haveRevocationResponse
= false;
1566 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1567 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1568 kCFBooleanTrue
); /* iOS key */
1569 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1570 kCFBooleanTrue
); /* unified API key */
1571 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1572 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1573 if (leafCompanyName
) {
1574 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1575 leafCompanyName
); /* iOS key */
1576 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1577 leafCompanyName
); /* unified API key */
1578 CFRelease(leafCompanyName
);
1581 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1582 if (nextUpdate
== 0) {
1583 /* populate revocation info for failed revocation check */
1584 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1585 kCFBooleanFalse
); /* iOS key */
1586 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1587 kCFBooleanFalse
); /* unified API key */
1592 if (pvc
->info
&& pvc
->result
&& pvc
->rvcs
) {
1593 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1594 if (nextUpdate
!= 0) {
1595 /* always populate revocation info for successful revocation check */
1596 haveRevocationResponse
= true;
1597 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1598 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1599 validUntil
); /* iOS key */
1600 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1601 validUntil
); /* unified API key */
1602 CFRelease(validUntil
);
1603 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1604 kCFBooleanTrue
); /* iOS key */
1605 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1606 kCFBooleanTrue
); /* unified API key */
1610 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1611 builder
->bestPathScore
= 0;
1612 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1613 0, kCFBooleanFalse
, true);
1616 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1617 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1621 if (pvc
->info
&& pvc
->is_ct_whitelisted
&& pvc
->result
) {
1622 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyWhiteListKey
,
1627 /* This will trigger the outer step function to call the completion
1629 builder
->state
= NULL
;
1633 /* @function SecPathBuilderStep
1634 @summary This is the core of the async engine.
1635 @description Return false iff job is complete, true if a network request
1637 builder->state is a function pointer which is to be invoked.
1638 If you call this function from within a builder->state invocation it
1639 immediately returns true.
1640 Otherwise the following steps are repeated endlessly (unless a step returns)
1641 builder->state is invoked. If it returns true and builder->state is still
1642 non NULL this proccess is repeated.
1643 If a state returns false, SecPathBuilder will return true
1644 if builder->state is non NULL.
1645 If builder->state is NULL then regardless of what the state function returns
1646 the completion callback will be invoked and the builder will be deallocated.
1648 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1649 if (builder
->activations
) {
1650 secdebug("async", "activations: %lu returning true",
1651 builder
->activations
);
1655 secdebug("async", "activations: %lu", builder
->activations
);
1656 builder
->activations
++;
1657 while (builder
->state
&& builder
->state(builder
));
1658 --builder
->activations
;
1660 if (builder
->state
) {
1661 secdebug("async", "waiting for async reply, exiting");
1662 /* A state returned false, it's waiting for network traffic. Let's
1667 if (builder
->activations
) {
1668 /* There is still at least one other running instance of this builder
1669 somewhere on the stack, we let that instance take care of sending
1670 the client a response. */
1674 SecTrustResultType result
= kSecTrustResultInvalid
;
1675 if (builder
->denyBestPath
) {
1676 result
= kSecTrustResultDeny
;
1677 } else if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
) {
1678 result
= kSecTrustResultUnspecified
;
1680 result
= kSecTrustResultRecoverableTrustFailure
;
1683 secinfo("trust", "completed: %@ details: %@ result: %d",
1684 builder
->bestPath
, builder
->path
.details
, result
);
1686 if (builder
->completed
) {
1687 builder
->completed(builder
->context
, builder
->bestPath
,
1688 builder
->path
.details
, builder
->path
.info
, result
);
1691 /* Finally, destroy the builder and free it. */
1692 SecPathBuilderDestroy(builder
);
1698 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1699 return (builder
) ? builder
->queue
: NULL
;
1702 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1703 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1707 // MARK: SecTrustServer
1708 /********************************************************
1709 ****************** SecTrustServer **********************
1710 ********************************************************/
1712 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1715 SecTrustServerEvaluateCompleted(const void *userData
,
1716 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1717 SecTrustResultType result
) {
1718 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1719 evaluated(result
, details
, info
, chain
, NULL
);
1720 Block_release(evaluated
);
1724 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
)) {
1725 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1726 /* Call the actual evaluator function. */
1727 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1728 certificates
, anchors
,
1729 anchorsOnly
, keychainsAllowed
, policies
,
1730 responses
, SCTs
, trustedLogs
,
1731 verifyTime
, accessGroups
,
1732 SecTrustServerEvaluateCompleted
, userData
);
1733 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1737 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1738 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
) {
1739 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1740 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1741 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1743 if (tr
== kSecTrustResultInvalid
) {
1746 CFRetainSafe(error
);
1750 *pdetails
= details
;
1751 CFRetainSafe(details
);
1759 CFRetainSafe(chain
);
1762 dispatch_semaphore_signal(done
);
1764 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);