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/SecPolicyInternal.h>
44 #include <Security/SecTrustSettingsPriv.h>
45 #include <CoreFoundation/CFRuntime.h>
46 #include <CoreFoundation/CFSet.h>
47 #include <CoreFoundation/CFString.h>
48 #include <CoreFoundation/CFNumber.h>
49 #include <CoreFoundation/CFArray.h>
50 #include <CoreFoundation/CFPropertyList.h>
51 #include <AssertMacros.h>
56 #include <Security/SecBase.h>
57 #include "SecRSAKey.h"
58 #include <libDER/oids.h>
59 #include <utilities/debugging.h>
60 #include <utilities/SecCFWrappers.h>
61 #include <Security/SecInternal.h>
62 #include <ipc/securityd_client.h>
63 #include <CommonCrypto/CommonDigest.h>
64 #include "OTATrustUtilities.h"
65 #include "personalization.h"
66 #include <utilities/SecInternalReleasePriv.h>
69 /********************************************************
70 ***************** OTA Trust support ********************
71 ********************************************************/
74 //#ifndef SECITEM_SHIM_OSX
76 static CFArrayRef
subject_to_anchors(CFDataRef nic
);
77 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
);
79 static CFArrayRef
subject_to_anchors(CFDataRef nic
)
81 CFArrayRef result
= NULL
;
88 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
89 if (NULL
== otapkiref
)
94 CFDictionaryRef lookupTable
= SecOTAPKICopyAnchorLookupTable(otapkiref
);
97 if (NULL
== lookupTable
)
102 unsigned char subject_digest
[CC_SHA1_DIGEST_LENGTH
];
103 memset(subject_digest
, 0, CC_SHA1_DIGEST_LENGTH
);
105 (void)CC_SHA1(CFDataGetBytePtr(nic
), (CC_LONG
)CFDataGetLength(nic
), subject_digest
);
106 CFDataRef sha1Digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, subject_digest
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
109 result
= (CFArrayRef
)CFDictionaryGetValue(lookupTable
, sha1Digest
);
110 CFReleaseSafe(lookupTable
);
111 CFReleaseSafe(sha1Digest
);
116 static CFArrayRef
CopyCertDataFromIndices(CFArrayRef offsets
)
118 CFMutableArrayRef result
= NULL
;
120 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
121 if (NULL
== otapkiref
)
126 const char* anchorTable
= SecOTAPKIGetAnchorTable(otapkiref
);
127 if (NULL
== anchorTable
)
129 CFReleaseSafe(otapkiref
);
133 CFIndex num_offsets
= CFArrayGetCount(offsets
);
135 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
137 for (CFIndex idx
= 0; idx
< num_offsets
; idx
++)
139 CFNumberRef offset
= (CFNumberRef
)CFArrayGetValueAtIndex(offsets
, idx
);
140 uint32_t offset_value
= 0;
141 if (CFNumberGetValue(offset
, kCFNumberSInt32Type
, &offset_value
))
143 char* pDataPtr
= (char *)(anchorTable
+ offset_value
);
144 //int32_t record_length = *((int32_t * )pDataPtr);
145 //record_length = record_length;
146 pDataPtr
+= sizeof(uint32_t);
148 int32_t cert_data_length
= *((int32_t * )pDataPtr
);
149 pDataPtr
+= sizeof(uint32_t);
151 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)pDataPtr
,
152 cert_data_length
, kCFAllocatorNull
);
153 if (NULL
!= cert_data
)
155 CFArrayAppendValue(result
, cert_data
);
156 CFReleaseSafe(cert_data
);
160 CFReleaseSafe(otapkiref
);
164 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
)
166 CFMutableArrayRef result
= NULL
;
168 CFArrayRef cert_data_array
= CopyCertDataFromIndices(offsets
);
170 if (NULL
!= cert_data_array
)
172 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
173 CFIndex num_cert_datas
= CFArrayGetCount(cert_data_array
);
174 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
176 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_data_array
, idx
);
177 if (NULL
!= cert_data
)
179 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, cert_data
);
182 CFArrayAppendValue(result
, cert
);
187 CFRelease(cert_data_array
);
192 //#endif // SECITEM_SHIM_OSX
194 /********************************************************
195 *************** END OTA Trust support ******************
196 ********************************************************/
198 #define MAX_CHAIN_LENGTH 15
199 #define ACCEPT_PATH_SCORE 10000000
201 /* Forward declaration for use in SecCertificateSource. */
202 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
206 // MARK: SecCertificateSource
207 /********************************************************
208 ************ SecCertificateSource object ***************
209 ********************************************************/
211 typedef struct SecCertificateSource
*SecCertificateSourceRef
;
212 typedef void(*SecCertificateSourceParents
)(void *, CFArrayRef
);
213 typedef bool(*CopyParents
)(SecCertificateSourceRef source
,
214 SecCertificateRef certificate
, void *context
, SecCertificateSourceParents
);
215 typedef CFArrayRef(*CopyConstraints
)(SecCertificateSourceRef source
,
216 SecCertificateRef certificate
);
217 typedef bool(*Contains
)(SecCertificateSourceRef source
,
218 SecCertificateRef certificate
);
220 struct SecCertificateSource
{
221 CopyParents copyParents
;
222 CopyConstraints copyUsageConstraints
;
226 static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source
,
227 SecCertificateRef certificate
,
228 void *context
, SecCertificateSourceParents callback
) {
229 return source
->copyParents(source
, certificate
, context
, callback
);
232 static CFArrayRef
SecCertificateSourceCopyUsageConstraints(
233 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
234 if (source
->copyUsageConstraints
) {
235 return source
->copyUsageConstraints(source
, certificate
);
241 static bool SecCertificateSourceContains(SecCertificateSourceRef source
,
242 SecCertificateRef certificate
) {
243 return source
->contains(source
, certificate
);
247 // MARK: SecItemCertificateSource
248 /********************************************************
249 *********** SecItemCertificateSource object ************
250 ********************************************************/
251 struct SecItemCertificateSource
{
252 struct SecCertificateSource base
;
253 CFArrayRef accessGroups
;
255 typedef struct SecItemCertificateSource
*SecItemCertificateSourceRef
;
257 static CFTypeRef
SecItemCertificateSourceResultsPost(CFTypeRef raw_results
) {
258 if (isArray(raw_results
)) {
259 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(raw_results
), &kCFTypeArrayCallBacks
);
260 CFArrayForEach(raw_results
, ^(const void *value
) {
261 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, value
);
263 CFArrayAppendValue(result
, cert
);
268 } else if (isData(raw_results
)) {
269 return SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)raw_results
);
274 static bool SecItemCertificateSourceCopyParents(
275 SecCertificateSourceRef source
, SecCertificateRef certificate
,
276 void *context
, SecCertificateSourceParents callback
) {
277 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
278 /* FIXME: Search for things other than just subject of our issuer if we
279 have a subjectID or authorityKeyIdentifier. */
280 CFDataRef normalizedIssuer
=
281 SecCertificateGetNormalizedIssuerContent(certificate
);
282 const void *keys
[] = {
289 kSecClassCertificate
,
294 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
296 CFTypeRef results
= NULL
;
297 SecurityClient client
= {
299 .accessGroups
= msource
->accessGroups
,
300 .allowSystemKeychain
= true,
301 .allowSyncBubbleKeychain
= false,
302 .isNetworkExtension
= false,
305 /* We can make this async or run this on a queue now easily. */
306 CFErrorRef localError
= NULL
;
307 if (!_SecItemCopyMatching(query
, &client
, &results
, &localError
)) {
308 if (localError
&& (CFErrorGetCode(localError
) != errSecItemNotFound
)) {
309 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
311 CFReleaseSafe(localError
);
314 CFTypeRef certs
= SecItemCertificateSourceResultsPost(results
);
315 CFReleaseSafe(results
);
316 callback(context
, certs
);
317 CFReleaseSafe(certs
);
321 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source
,
322 SecCertificateRef certificate
) {
323 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
324 /* Look up a certificate by issuer and serial number. */
325 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
326 CFRetainSafe(normalizedIssuer
);
327 CFDataRef serialNumber
=
328 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
329 SecCertificateCopySerialNumber(certificate
, NULL
);
331 SecCertificateCopySerialNumber(certificate
);
333 const void *keys
[] = {
340 kSecClassCertificate
,
345 SecurityClient client
= {
347 .accessGroups
= msource
->accessGroups
,
348 .allowSystemKeychain
= true,
349 .allowSyncBubbleKeychain
= false,
350 .isNetworkExtension
= false,
352 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4, NULL
, NULL
);
353 CFErrorRef localError
= NULL
;
354 CFTypeRef results
= NULL
;
355 bool ok
= _SecItemCopyMatching(query
, &client
, &results
, &localError
);
356 CFReleaseSafe(query
);
357 CFReleaseSafe(serialNumber
);
358 CFReleaseSafe(normalizedIssuer
);
359 CFReleaseSafe(results
);
361 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
362 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
364 CFReleaseSafe(localError
);
370 static SecCertificateSourceRef
SecItemCertificateSourceCreate(CFArrayRef accessGroups
) {
371 SecItemCertificateSourceRef result
= (SecItemCertificateSourceRef
)malloc(sizeof(*result
));
372 result
->base
.copyParents
= SecItemCertificateSourceCopyParents
;
373 result
->base
.copyUsageConstraints
= NULL
;
374 result
->base
.contains
= SecItemCertificateSourceContains
;
375 result
->accessGroups
= accessGroups
;
376 CFRetainSafe(accessGroups
);
377 return (SecCertificateSourceRef
)result
;
380 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source
) {
381 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
382 CFReleaseSafe(msource
->accessGroups
);
387 // MARK: SecSystemAnchorSource
388 /********************************************************
389 *********** SecSystemAnchorSource object ************
390 ********************************************************/
392 static bool SecSystemAnchorSourceCopyParents(
393 SecCertificateSourceRef source
, SecCertificateRef certificate
,
394 void *context
, SecCertificateSourceParents callback
) {
395 //#ifndef SECITEM_SHIM_OSX
396 CFArrayRef parents
= NULL
;
397 CFArrayRef anchors
= NULL
;
398 SecOTAPKIRef otapkiref
= NULL
;
400 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
401 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
402 It does not matter since we would be returning the wrong anchors */
403 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
405 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
406 require_quiet(otapkiref
, errOut
);
407 anchors
= subject_to_anchors(nic
);
408 require_quiet(anchors
, errOut
);
409 parents
= CopyCertsFromIndices(anchors
);
412 callback(context
, parents
);
413 CFReleaseSafe(parents
);
414 CFReleaseSafe(otapkiref
);
415 //#endif // SECITEM_SHIM_OSX
419 /* Quick thought: we can eliminate this method if we search anchor sources
420 before all others and we remember if we got a cert from an anchorsource. */
421 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source
,
422 SecCertificateRef certificate
) {
424 CFArrayRef anchors
= NULL
;
425 SecOTAPKIRef otapkiref
= NULL
;
426 CFArrayRef cert_datas
= NULL
;
428 CFDataRef nic
= SecCertificateGetNormalizedSubjectContent(certificate
);
429 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
430 It does not matter since we would be returning the wrong anchors */
431 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
433 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
434 require_quiet(otapkiref
, errOut
);
435 anchors
= subject_to_anchors(nic
);
436 require_quiet(anchors
, errOut
);
437 cert_datas
= CopyCertDataFromIndices(anchors
);
438 require_quiet(cert_datas
, errOut
);
440 CFIndex cert_length
= SecCertificateGetLength(certificate
);
441 const UInt8
*cert_data_ptr
= SecCertificateGetBytePtr(certificate
);
443 CFIndex num_cert_datas
= CFArrayGetCount(cert_datas
);
444 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
446 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, idx
);
448 if (NULL
!= cert_data
)
450 if (CFGetTypeID(cert_data
) == CFDataGetTypeID())
452 CFIndex aCert_Length
= CFDataGetLength(cert_data
);
453 const UInt8
* aCert_Data_Ptr
= CFDataGetBytePtr(cert_data
);
455 if (aCert_Length
== cert_length
)
457 if (!memcmp(cert_data_ptr
, aCert_Data_Ptr
, cert_length
))
468 CFReleaseSafe(cert_datas
);
469 CFReleaseSafe(otapkiref
);
475 struct SecCertificateSource kSecSystemAnchorSource
= {
476 SecSystemAnchorSourceCopyParents
,
478 SecSystemAnchorSourceContains
483 // MARK: SecUserAnchorSource
484 /********************************************************
485 ************* SecUserAnchorSource object ***************
486 ********************************************************/
487 static bool SecUserAnchorSourceCopyParents(
488 SecCertificateSourceRef source
, SecCertificateRef certificate
,
489 void *context
, SecCertificateSourceParents callback
) {
490 CFArrayRef parents
= SecTrustStoreCopyParents(
491 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
, NULL
);
492 callback(context
, parents
);
493 CFReleaseSafe(parents
);
497 static CFArrayRef
SecUserAnchorSourceCopyUsageConstraints(
498 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
499 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
502 CFArrayRef usageConstraints
= NULL
;
503 bool ok
= _SecTrustStoreCopyUsageConstraints(
504 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), digest
, &usageConstraints
, NULL
);
505 return (ok
) ? usageConstraints
: NULL
;
508 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source
,
509 SecCertificateRef certificate
) {
510 return SecTrustStoreContains(
511 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
);
514 struct SecCertificateSource kSecUserAnchorSource
= {
515 SecUserAnchorSourceCopyParents
,
516 SecUserAnchorSourceCopyUsageConstraints
,
517 SecUserAnchorSourceContains
522 // MARK: SecMemoryCertificateSource
523 /********************************************************
524 ********** SecMemoryCertificateSource object ***********
525 ********************************************************/
526 struct SecMemoryCertificateSource
{
527 struct SecCertificateSource base
;
528 CFMutableSetRef certificates
;
529 CFMutableDictionaryRef subjects
;
531 typedef struct SecMemoryCertificateSource
*SecMemoryCertificateSourceRef
;
533 static bool SecMemoryCertificateSourceCopyParents(
534 SecCertificateSourceRef source
, SecCertificateRef certificate
,
535 void *context
, SecCertificateSourceParents callback
) {
536 SecMemoryCertificateSourceRef msource
=
537 (SecMemoryCertificateSourceRef
)source
;
538 CFDataRef normalizedIssuer
=
539 SecCertificateGetNormalizedIssuerContent(certificate
);
540 CFArrayRef parents
= (normalizedIssuer
) ? CFDictionaryGetValue(msource
->subjects
,
541 normalizedIssuer
) : NULL
;
542 /* FIXME filter parents by subjectID if certificate has an
543 authorityKeyIdentifier. */
544 secdebug("trust", "%@ parents -> %@", certificate
, parents
);
545 callback(context
, parents
);
549 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source
,
550 SecCertificateRef certificate
) {
551 SecMemoryCertificateSourceRef msource
=
552 (SecMemoryCertificateSourceRef
)source
;
553 return CFSetContainsValue(msource
->certificates
, certificate
);
556 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict
,
557 const void *key
, const void *value
) {
561 CFMutableArrayRef values
=
562 (CFMutableArrayRef
)CFDictionaryGetValue(dict
, key
);
564 values
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
565 &kCFTypeArrayCallBacks
);
566 CFDictionaryAddValue(dict
, key
, values
);
571 CFArrayAppendValue(values
, value
);
574 static void SecMemoryCertificateSourceApplierFunction(const void *value
,
576 SecMemoryCertificateSourceRef msource
=
577 (SecMemoryCertificateSourceRef
)context
;
578 SecCertificateRef certificate
= (SecCertificateRef
)value
;
580 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
581 if (CFSetContainsValue(msource
->certificates
, certificate
))
583 CFSetAddValue(msource
->certificates
, certificate
);
585 CFDataRef key
= SecCertificateGetNormalizedSubjectContent(certificate
);
586 dictAddValueToArrayForKey(msource
->subjects
, key
, value
);
589 static SecCertificateSourceRef
SecMemoryCertificateSourceCreate(
590 CFArrayRef certificates
) {
591 SecMemoryCertificateSourceRef result
= (SecMemoryCertificateSourceRef
)
592 malloc(sizeof(*result
));
593 result
->base
.copyParents
= SecMemoryCertificateSourceCopyParents
;
594 result
->base
.copyUsageConstraints
= NULL
;
595 result
->base
.contains
= SecMemoryCertificateSourceContains
;
596 CFIndex count
= CFArrayGetCount(certificates
);
597 result
->certificates
= CFSetCreateMutable(kCFAllocatorDefault
, count
,
598 &kCFTypeSetCallBacks
);
599 result
->subjects
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
600 count
, &kCFTypeDictionaryKeyCallBacks
,
601 &kCFTypeDictionaryValueCallBacks
);
602 CFRange range
= { 0, count
};
603 CFArrayApplyFunction(certificates
, range
,
604 SecMemoryCertificateSourceApplierFunction
, result
);
606 return (SecCertificateSourceRef
)result
;
609 static void SecMemoryCertificateSourceDestroy(
610 SecCertificateSourceRef source
) {
611 SecMemoryCertificateSourceRef msource
=
612 (SecMemoryCertificateSourceRef
)source
;
613 CFRelease(msource
->certificates
);
614 CFRelease(msource
->subjects
);
619 // MARK: SecCAIssuerCertificateSource
620 /********************************************************
621 ********* SecCAIssuerCertificateSource object **********
622 ********************************************************/
623 static bool SecCAIssuerCertificateSourceCopyParents(
624 SecCertificateSourceRef source
, SecCertificateRef certificate
,
625 void *context
, SecCertificateSourceParents callback
) {
626 return SecCAIssuerCopyParents(certificate
, SecPathBuilderGetQueue((SecPathBuilderRef
)context
), context
, callback
);
629 static bool SecCAIssuerCertificateSourceContains(
630 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
634 struct SecCertificateSource kSecCAIssuerSource
= {
635 SecCAIssuerCertificateSourceCopyParents
,
637 SecCAIssuerCertificateSourceContains
640 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
641 #include <Security/SecItemPriv.h>
643 // MARK: SecLegacyCertificateSource
644 /********************************************************
645 ********** SecLegacyCertificateSource object ***********
646 ********************************************************/
648 static bool SecLegacyCertificateSourceCopyParents(
649 SecCertificateSourceRef source
, SecCertificateRef certificate
,
650 void *context
, SecCertificateSourceParents callback
) {
651 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
652 callback(context
, parents
);
653 CFReleaseSafe(parents
);
657 static bool SecLegacyCertificateSourceContains(
658 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
659 SecCertificateRef cert
= SecItemCopyStoredCertificate(certificate
, NULL
);
660 bool result
= (cert
) ? true : false;
665 struct SecCertificateSource kSecLegacyCertificateSource
= {
666 SecLegacyCertificateSourceCopyParents
,
668 SecLegacyCertificateSourceContains
670 #endif /* SecLegacyCertificateSource */
672 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
674 // MARK: SecLegacyAnchorSource
675 /********************************************************
676 ************ SecLegacyAnchorSource object **************
677 ********************************************************/
679 static bool SecLegacyAnchorSourceCopyParents(
680 SecCertificateSourceRef source
, SecCertificateRef certificate
,
681 void *context
, SecCertificateSourceParents callback
) {
682 CFMutableArrayRef anchors
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
683 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
684 CFArrayRef trusted
= NULL
;
685 if (parents
== NULL
) {
688 /* Get the custom anchors which have been trusted in the user and admin domains.
689 * We don't need system domain roots here, since SecSystemAnchorSource provides those.
691 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
692 if (status
== errSecSuccess
&& trusted
) {
693 CFIndex index
, count
= CFArrayGetCount(parents
);
694 for (index
= 0; index
< count
; index
++) {
695 SecCertificateRef parent
= (SecCertificateRef
)CFArrayGetValueAtIndex(parents
, index
);
696 if (parent
&& CFArrayContainsValue(trusted
, CFRangeMake(0, CFArrayGetCount(trusted
)), parent
)) {
697 CFArrayAppendValue(anchors
, parent
);
703 callback(context
, anchors
);
704 CFReleaseSafe(anchors
);
705 CFReleaseSafe(parents
);
706 CFReleaseSafe(trusted
);
710 static CFArrayRef
SecLegacyAnchorSourceCopyUsageConstraints(
711 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
713 CFArrayRef result
= NULL
;
714 CFArrayRef userTrustSettings
= NULL
, adminTrustSettings
= NULL
;
716 OSStatus status
= SecTrustSettingsCopyTrustSettings(certificate
,
717 kSecTrustSettingsDomainUser
,
719 if ((status
== errSecSuccess
) && (userTrustSettings
!= NULL
)) {
720 result
= CFRetain(userTrustSettings
);
723 status
= SecTrustSettingsCopyTrustSettings(certificate
,
724 kSecTrustSettingsDomainAdmin
,
725 &adminTrustSettings
);
726 /* user trust settings overrule admin trust settings */
727 if ((status
== errSecSuccess
) && (adminTrustSettings
!= NULL
) && (result
== NULL
)) {
728 result
= CFRetain(adminTrustSettings
);
731 CFReleaseNull(userTrustSettings
);
732 CFReleaseNull(adminTrustSettings
);
736 static bool SecLegacyAnchorSourceContains(
737 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
738 if (certificate
== NULL
) {
741 CFArrayRef trusted
= NULL
;
743 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
744 if ((status
== errSecSuccess
) && (trusted
!= NULL
)) {
745 CFIndex index
, count
= CFArrayGetCount(trusted
);
746 for (index
= 0; index
< count
; index
++) {
747 SecCertificateRef anchor
= (SecCertificateRef
)CFArrayGetValueAtIndex(trusted
, index
);
748 if (anchor
&& CFEqual(anchor
, certificate
)) {
754 CFReleaseSafe(trusted
);
758 struct SecCertificateSource kSecLegacyAnchorSource
= {
759 SecLegacyAnchorSourceCopyParents
,
760 SecLegacyAnchorSourceCopyUsageConstraints
,
761 SecLegacyAnchorSourceContains
763 #endif /* SecLegacyAnchorSource */
766 // MARK: SecPathBuilder
767 /********************************************************
768 *************** SecPathBuilder object ******************
769 ********************************************************/
770 struct SecPathBuilder
{
771 dispatch_queue_t queue
;
772 CFDataRef clientAuditToken
;
773 SecCertificateSourceRef certificateSource
;
774 SecCertificateSourceRef itemCertificateSource
;
775 SecCertificateSourceRef anchorSource
;
776 SecCertificateSourceRef appleAnchorSource
;
777 CFMutableArrayRef anchorSources
;
778 CFIndex nextParentSource
;
779 CFMutableArrayRef parentSources
;
780 CFArrayRef ocspResponses
; // Stapled OCSP responses
781 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
782 CFArrayRef trustedLogs
; // Trusted CT logs
784 /* Hashed set of all paths we've constructed so far, used to prevent
785 re-considering a path that was already constructed once before.
786 Note that this is the only container in which certificatePath
787 objects are retained.
788 Every certificatePath being considered is always in allPaths and in at
789 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
790 all of which don't retain their values. */
791 CFMutableSetRef allPaths
;
793 /* No trusted anchor, satisfies the linking to intermediates for all
794 policies (unless considerRejected is true). */
795 CFMutableArrayRef partialPaths
;
796 /* No trusted anchor, does not satisfy linking to intermediates for all
798 CFMutableArrayRef rejectedPaths
;
799 /* Trusted anchor, satisfies the policies so far. */
800 CFMutableArrayRef candidatePaths
;
804 CFArrayRef leafDetails
;
806 CFIndex bestPathScore
;
808 bool considerRejected
;
809 bool considerPartials
;
810 bool canAccessNetwork
;
812 struct OpaqueSecPVC path
;
813 SecCertificatePathRef bestPath
;
819 bool (*state
)(SecPathBuilderRef
);
820 SecPathBuilderCompleted completed
;
824 /* State functions. Return false if a async job was scheduled, return
825 true to execute the next state. */
826 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
827 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
828 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
829 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
830 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
832 /* Forward declarations. */
833 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
834 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
836 /* IDEA: policies could be made capable of replacing incoming anchors and
837 anchorsOnly argument values. For example, some policies require the
838 Apple Inc. CA and not any other anchor. This can be done in
839 SecPathBuilderLeafCertificateChecks since this only runs once. */
840 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
841 SecCertificatePathRef path
) {
842 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
843 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
844 &kCFTypeDictionaryValueCallBacks
);
845 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
846 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
847 CFRelease(certDetail
);
848 SecPVCRef pvc
= &builder
->path
;
849 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
850 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
853 static void SecPathBuilderInit(SecPathBuilderRef builder
,
854 CFDataRef clientAuditToken
, CFArrayRef certificates
,
855 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
856 CFArrayRef policies
, CFArrayRef ocspResponses
,
857 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
858 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
859 SecPathBuilderCompleted completed
, const void *context
) {
860 secdebug("alloc", "%p", builder
);
861 CFAllocatorRef allocator
= kCFAllocatorDefault
;
863 builder
->clientAuditToken
= (CFDataRef
)
864 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
865 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
867 builder
->nextParentSource
= 1;
869 builder
->canAccessNetwork
= true;
872 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
873 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
874 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
875 &kCFTypeSetCallBacks
);
877 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
878 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
879 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
881 /* Init the policy verification context. */
882 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
884 /* Let's create all the certificate sources we might want to use. */
885 builder
->certificateSource
=
886 SecMemoryCertificateSourceCreate(certificates
);
888 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
891 bool allowNonProduction
= false;
892 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
896 ** The order here avoids the most expensive methods if the cheaper methods
897 ** produce an acceptable chain: client-provided, keychains, network-fetched.
899 #if !TARGET_OS_BRIDGE
900 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
901 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
902 if (keychainsAllowed
) {
903 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
905 /* On OS X, need additional parent source to search legacy keychain files. */
906 if (kSecLegacyCertificateSource
.contains
&& kSecLegacyCertificateSource
.copyParents
) {
907 CFArrayAppendValue(builder
->parentSources
, &kSecLegacyCertificateSource
);
912 /* Add the Apple, system, and user anchor certificate db to the search list
913 if we don't explicitly trust them. */
914 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
915 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
917 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
920 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
921 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
923 #else /* TARGET_OS_BRIDGE */
924 /* Bridge can only access memory sources. */
925 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
927 /* Add the Apple, system, and user anchor certificate db to the search list
928 if we don't explicitly trust them. */
929 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
931 #endif /* !TARGET_OS_BRIDGE */
934 ** The order here allows a client-provided anchor to overrule
935 ** a user or admin trust setting which can overrule the system anchors.
936 ** Apple's anchors cannot be overriden by a trust setting.
938 #if !TARGET_OS_BRIDGE
939 if (builder
->anchorSource
) {
940 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
943 /* Only add the system and user anchor certificate db to the
944 anchorSources if we are supposed to trust them. */
945 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
947 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
948 #else /* TARGET_OS_OSX */
949 if (keychainsAllowed
&& kSecLegacyAnchorSource
.contains
&& kSecLegacyAnchorSource
.copyParents
) {
950 CFArrayAppendValue(builder
->anchorSources
, &kSecLegacyAnchorSource
);
953 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
955 #else /* TARGET_OS_BRIDGE */
956 /* Bridge can only access memory sources. */
957 if (builder
->anchorSource
) {
958 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
961 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
963 #endif /* !TARGET_OS_BRIDGE */
965 /* Now let's get the leaf cert and turn it into a path. */
966 SecCertificateRef leaf
=
967 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
968 SecCertificateSourceRef source
= NULL
;
969 bool isAnchor
= false;
970 CFArrayRef constraints
= NULL
;
971 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
975 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
977 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
, constraints
);
978 CFReleaseSafe(constraints
);
979 CFSetAddValue(builder
->allPaths
, path
);
980 CFArrayAppendValue(builder
->partialPaths
, path
);
982 SecCertificatePathSetIsAnchored(path
);
983 CFArrayAppendValue(builder
->candidatePaths
, path
);
985 SecPathBuilderLeafCertificateChecks(builder
, path
);
988 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
989 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
992 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
994 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
995 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
996 CFReleaseSafe(otapkiref
);
999 builder
->state
= SecPathBuilderGetNext
;
1000 builder
->completed
= completed
;
1001 builder
->context
= context
;
1004 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
1005 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
1006 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
1007 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
1008 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
1009 SecPathBuilderCompleted completed
, const void *context
) {
1010 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
1011 memset(builder
, 0, sizeof(*builder
));
1012 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
1013 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
1014 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
1015 accessGroups
, completed
, context
);
1019 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
1020 secdebug("alloc", "%p", builder
);
1021 dispatch_release_null(builder
->queue
);
1022 if (builder
->anchorSource
) {
1023 SecMemoryCertificateSourceDestroy(builder
->anchorSource
); }
1024 if (builder
->certificateSource
) {
1025 SecMemoryCertificateSourceDestroy(builder
->certificateSource
); }
1026 if (builder
->itemCertificateSource
) {
1027 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
); }
1028 if (builder
->appleAnchorSource
) {
1029 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
); }
1030 CFReleaseSafe(builder
->clientAuditToken
);
1031 CFReleaseSafe(builder
->anchorSources
);
1032 CFReleaseSafe(builder
->parentSources
);
1033 CFReleaseSafe(builder
->allPaths
);
1034 CFReleaseSafe(builder
->partialPaths
);
1035 CFReleaseSafe(builder
->rejectedPaths
);
1036 CFReleaseSafe(builder
->candidatePaths
);
1037 CFReleaseSafe(builder
->leafDetails
);
1038 CFReleaseSafe(builder
->ocspResponses
);
1039 CFReleaseSafe(builder
->signedCertificateTimestamps
);
1040 CFReleaseSafe(builder
->trustedLogs
);
1042 SecPVCDelete(&builder
->path
);
1045 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
1046 return builder
->canAccessNetwork
;
1049 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
1050 if (builder
->canAccessNetwork
!= allow
) {
1051 builder
->canAccessNetwork
= allow
;
1053 #if !TARGET_OS_WATCH
1054 secinfo("http", "network access re-enabled by policy");
1055 /* re-enabling network_access re-adds kSecCAIssuerSource as
1057 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
1059 secnotice("http", "network access not allowed on WatchOS");
1060 builder
->canAccessNetwork
= false;
1063 secinfo("http", "network access disabled by policy");
1064 /* disabling network_access removes kSecCAIssuerSource from
1065 the list of parent sources. */
1066 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
1067 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
1068 &kSecCAIssuerSource
);
1070 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
1075 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
1077 return CFRetainSafe(builder
->ocspResponses
);
1080 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
1082 return CFRetainSafe(builder
->signedCertificateTimestamps
);
1085 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
1087 return CFRetainSafe(builder
->trustedLogs
);
1090 /* This function assumes that the input source is an anchor source */
1091 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
1092 SecCertificateRef certificate
) {
1093 bool result
= false;
1094 CFArrayRef constraints
= NULL
;
1095 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
1097 /* Unrestricted certificates:
1098 * -those that come from anchor sources with no constraints
1099 * -self-signed certificates with empty contraints arrays
1101 Boolean selfSigned
= false;
1102 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
1103 if ((NULL
== source
->copyUsageConstraints
) ||
1104 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
1105 secinfo("trust", "unrestricted anchor%s",
1106 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
1111 /* Get the trust settings result for the PVC */
1112 require(constraints
, out
);
1113 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
1114 settingsResult
= SecPVCGetTrustSettingsResult(&builder
->path
,
1117 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
1118 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
1119 // For our purposes, this is an anchor.
1120 secinfo("trust", "complex trust settings anchor");
1124 if (settingsResult
== kSecTrustSettingsResultDeny
) {
1125 /* We consider denied certs "anchors" because the trust decision
1126 is set regardless of building the chain further. The policy
1127 validation will handle rejecting this chain. */
1128 secinfo("trust", "complex trust settings denied anchor");
1133 CFReleaseNull(constraints
);
1137 /* Source returned in foundInSource has the same lifetime as the builder. */
1138 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
1139 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
1140 /* We look through the anchor sources in order. They are ordered in
1141 SecPathBuilderInit so that process anchors override user anchors which
1142 override system anchors. */
1143 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
1145 for (ix
= 0; ix
< count
; ++ix
) {
1146 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
1147 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
1148 if (SecCertificateSourceContains(source
, certificate
)) {
1150 *foundInSource
= source
;
1151 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
1159 /* Return false if path is not a partial, if path was a valid candidate it
1160 will have been added to builder->candidatePaths, if path was rejected
1161 by the parent certificate checks (because it's expired or some other
1162 static chaining check failed) it will have been added to rejectedPaths.
1163 Return true path if path is a partial. */
1164 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
1165 SecCertificatePathRef path
) {
1166 SecPVCRef pvc
= &builder
->path
;
1167 SecPVCSetPath(pvc
, path
, NULL
);
1169 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
1170 SecPVCGetCertificateCount(pvc
) - 1)) {
1171 secdebug("trust", "Found rejected path %@", path
);
1172 CFArrayAppendValue(builder
->rejectedPaths
, path
);
1176 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
1177 /* Candidate paths with failed signatures are discarded. */
1178 if (vstatus
== kSecPathVerifyFailed
) {
1179 secdebug("trust", "Verify failed for path %@", path
);
1183 if (vstatus
== kSecPathVerifySuccess
) {
1184 /* The signature chain verified sucessfully, now let's find
1185 out if we have an anchor for path. */
1186 if (SecCertificatePathIsAnchored(path
)) {
1187 secdebug("trust", "Adding candidate %@", path
);
1188 CFArrayAppendValue(builder
->candidatePaths
, path
);
1196 /* Given the builder, a partial chain partial and the parents array, construct
1197 a SecCertificatePath for each parent. After discarding previously
1198 considered paths and paths with cycles, sort out which array each path
1199 should go in, if any. */
1200 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
1201 SecCertificatePathRef partial
, CFArrayRef parents
) {
1202 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
1203 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
1205 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
1206 SecCertificateRef parent
= (SecCertificateRef
)
1207 CFArrayGetValueAtIndex(parents
, parentIX
);
1208 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
1210 if (ixOfParent
!= kCFNotFound
) {
1211 /* partial already contains parent. Let's not add the same
1212 certificate again. */
1213 if (ixOfParent
== rootIX
) {
1214 /* parent is equal to the root of the partial, so partial
1215 looks to be self issued. */
1216 SecCertificatePathSetSelfIssued(partial
);
1221 /* FIXME Add more sanity checks to see that parent really can be
1222 a parent of partial_root. subjectKeyID == authorityKeyID,
1223 signature algorithm matches public key algorithm, etc. */
1224 SecCertificateSourceRef source
= NULL
;
1225 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
1226 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
1227 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
, constraints
);
1228 CFReleaseSafe(constraints
);
1231 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
1232 CFSetAddValue(builder
->allPaths
, path
);
1234 SecCertificatePathSetIsAnchored(path
);
1235 if (SecPathBuilderIsPartial(builder
, path
)) {
1236 /* Insert path right at the current position since it's a new
1237 candiate partial. */
1238 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1239 ++builder
->partialIX
, path
);
1240 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
1241 parentIX
+ 1, num_parents
, path
);
1243 secdebug("trust", "found new path %@", path
);
1249 /* Callback for the SecPathBuilderGetNext() functions call to
1250 SecCertificateSourceCopyParents(). */
1251 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
1252 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
1253 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1254 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1255 secdebug("async", "%@ parents %@", partial
, parents
);
1256 SecPathBuilderProcessParents(builder
, partial
, parents
);
1258 builder
->state
= SecPathBuilderGetNext
;
1259 SecPathBuilderStep(builder
);
1262 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
1263 /* If we have any candidates left to go return those first. */
1264 if (CFArrayGetCount(builder
->candidatePaths
)) {
1265 SecCertificatePathRef path
= (SecCertificatePathRef
)
1266 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
1267 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
1268 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
1270 SecPVCSetPath(&builder
->path
, path
, NULL
);
1271 builder
->state
= SecPathBuilderValidatePath
;
1275 /* If we are considering rejected chains we check each rejected path
1276 with SecPathBuilderIsPartial() which checks the signature chain and
1277 either drops the path if it's not properly signed, add it as a
1278 candidate if it has a trusted anchor, or adds it as a partial
1279 to be considered once we finish considering all the rejects. */
1280 if (builder
->considerRejected
) {
1281 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
1284 SecCertificatePathRef path
= (SecCertificatePathRef
)
1285 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1286 if (SecPathBuilderIsPartial(builder
, path
)) {
1287 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1288 ++builder
->partialIX
, path
);
1290 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1292 /* Keep going until we have moved all rejected partials into
1293 the regular partials or candidates array. */
1298 /* If builder->partialIX is < 0 we have considered all partial chains
1299 this block must ensure partialIX >= 0 if execution continues past
1301 if (builder
->partialIX
< 0) {
1302 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1303 if (builder
->nextParentSource
< num_sources
) {
1304 builder
->nextParentSource
++;
1305 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1306 builder
->nextParentSource
, num_sources
);
1308 /* We've run out of new sources to consider so let's look at
1309 rejected chains and after that even consider partials
1311 FIXME we might not want to consider partial paths that
1312 are subsets of other partial paths, or not consider them
1313 at all if we already have an anchored reject. */
1314 if (!builder
->considerRejected
) {
1315 builder
->considerRejected
= true;
1316 secdebug("trust", "considering rejected paths");
1317 } else if (!builder
->considerPartials
) {
1318 builder
->considerPartials
= true;
1319 secdebug("trust", "considering partials");
1321 /* We're all out of options, so we can't produce any more
1322 candidates. Let's calculate details and return the best
1324 builder
->state
= SecPathBuilderComputeDetails
;
1328 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1329 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1333 /* We know builder->partialIX >= 0 if we get here. */
1334 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1335 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1336 /* Don't try to extend partials anymore once we are in the considerPartials
1337 state, since at this point every partial has been extended with every
1338 possible parentSource already. */
1339 if (builder
->considerPartials
) {
1340 --builder
->partialIX
;
1341 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1342 builder
->state
= SecPathBuilderValidatePath
;
1346 /* Attempt to extend this partial path with another certificate. This
1347 should give us a list of potential parents to consider. */
1348 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1349 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1352 /* Attempt to extend partial, leaving all possible extended versions
1353 of partial in builder->extendedPaths. */
1354 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1355 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1356 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1357 SecCertificateSourceRef source
;
1358 if (sourceIX
< num_anchor_sources
) {
1359 source
= (SecCertificateSourceRef
)
1360 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1361 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1362 num_anchor_sources
);
1364 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1365 source
= (SecCertificateSourceRef
)
1366 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1367 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1368 builder
->nextParentSource
);
1370 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1371 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1372 return SecCertificateSourceCopyParents(source
, root
,
1373 builder
, SecPathBuilderExtendPaths
);
1375 --builder
->partialIX
;
1381 /* One or more of the policies did not accept the candidate path. */
1382 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1384 SecPVCRef pvc
= &builder
->path
;
1386 builder
->state
= SecPathBuilderGetNext
;
1388 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1389 /* We never replace an ev reject with a non ev reject. */
1393 CFIndex bestPathScore
= builder
->bestPathScore
;
1394 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1395 SecPVCGetVerifyTime(&builder
->path
));
1397 /* The current chain is valid for EV, but revocation checking failed. We
1398 replace any previously accepted or rejected non EV chains with the
1400 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1406 /* Since this means we found a valid ev chain that was revoked,
1407 we might want to switch directly to the
1408 SecPathBuilderComputeDetails state here if we think further
1409 searching for new chains is pointless. For now we'll keep
1410 going, since we could accept an alternate EV certification
1411 path that isn't revoked. */
1412 builder
->state
= SecPathBuilderComputeDetails
;
1416 /* Do this last so that changes to bestPathScore above will take effect. */
1417 if (!builder
->bestPath
|| score
> bestPathScore
) {
1418 if (builder
->bestPath
) {
1420 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1421 (builder
->bestPathIsEV
? "" : "non "),
1422 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1423 builder
->bestPathScore
,
1424 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1426 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1427 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1430 builder
->bestPathScore
= score
;
1431 builder
->bestPath
= pvc
->path
;
1432 builder
->bestPathIsEV
= pvc
->is_ev
;
1433 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
);
1435 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1436 (pvc
->is_ev
? "" : "non "), score
, bestPathScore
, builder
->path
.path
);
1440 /* All policies accepted the candidate path. */
1441 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1443 SecPVCRef pvc
= &builder
->path
;
1444 bool isSHA2
= !SecCertificatePathHasWeakHash(pvc
->path
);
1445 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc
, 0));
1446 CFIndex bestScore
= builder
->bestPathScore
;
1447 /* Score this path. Note that all points awarded or deducted in
1448 * SecCertificatePathScore are < 100,000 */
1449 CFIndex currScore
= (SecCertificatePathScore(pvc
->path
, pvc
->verifyTime
) +
1450 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1451 ((pvc
->is_ev
) ? 1000000 : 0)); //1,000,000 points for EV
1452 if (currScore
> bestScore
) {
1453 // current path is better than existing best path
1454 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1455 (builder
->bestPathIsEV
? "" : "non "),
1456 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1457 builder
->bestPathScore
,
1458 (pvc
->is_ev
? "" : "non "), (long)currScore
, builder
->path
.path
);
1460 builder
->bestPathScore
= currScore
;
1461 builder
->bestPathIsEV
= pvc
->is_ev
;
1462 builder
->bestPathIsSHA2
= isSHA2
;
1463 builder
->bestPath
= pvc
->path
;
1464 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
); /* should always be false */
1467 /* If we found the best accept we can, we want to switch directly to the
1468 SecPathBuilderComputeDetails state here, since we're done. */
1469 if ((pvc
->is_ev
|| !pvc
->optionally_ev
) && (isSHA2
|| !isOptionallySHA2
))
1470 builder
->state
= SecPathBuilderComputeDetails
;
1472 builder
->state
= SecPathBuilderGetNext
;
1475 /* Return true iff a given path satisfies all the specified policies at
1477 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1478 SecPVCRef pvc
= &builder
->path
;
1480 if (builder
->considerRejected
) {
1481 SecPathBuilderReject(builder
);
1485 builder
->state
= SecPathBuilderDidValidatePath
;
1486 return SecPVCPathChecks(pvc
);
1489 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1490 SecPVCRef pvc
= &builder
->path
;
1492 SecPathBuilderAccept(builder
);
1494 SecPathBuilderReject(builder
);
1496 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1500 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1502 SecPVCRef pvc
= &builder
->path
;
1504 if (!builder
->caller_wants_details
) {
1505 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1506 pvc
->result
= builder
->bestPathScore
> ACCEPT_PATH_SCORE
;
1507 builder
->state
= SecPathBuilderReportResult
;
1511 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1512 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1513 pathLength
, builder
->leafDetails
);
1514 CFRetainSafe(details
);
1515 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1516 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1517 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1518 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1519 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1520 for (ix
= 1; ix
< pathLength
; ++ix
) {
1521 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1522 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1523 &kCFTypeDictionaryValueCallBacks
);
1524 CFArrayAppendValue(details
, certDetail
);
1525 CFRelease(certDetail
);
1526 SecPVCParentCertificateChecks(pvc
, ix
);
1527 SecPVCGrayListedKeyChecks(pvc
, ix
);
1528 SecPVCBlackListedKeyChecks(pvc
, ix
);
1530 builder
->state
= SecPathBuilderReportResult
;
1531 bool completed
= SecPVCPathChecks(pvc
);
1533 /* Reject the certificate if it was accepted before but we failed it now. */
1534 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
&& !pvc
->result
) {
1535 builder
->bestPathScore
= 0;
1538 /* Accept a partial path if certificate is on the allow list
1539 and is temporally valid. */
1540 if (completed
&& pvc
->is_allowlisted
&&
1541 builder
->bestPathScore
< ACCEPT_PATH_SCORE
&&
1542 SecCertificatePathIsValid(pvc
->path
, pvc
->verifyTime
)) {
1543 builder
->bestPathScore
+= ACCEPT_PATH_SCORE
;
1546 CFReleaseSafe(details
);
1551 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1552 SecPVCRef pvc
= &builder
->path
;
1553 bool haveRevocationResponse
= false;
1554 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1555 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1556 kCFBooleanTrue
); /* iOS key */
1557 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1558 kCFBooleanTrue
); /* unified API key */
1559 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1560 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1561 if (leafCompanyName
) {
1562 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1563 leafCompanyName
); /* iOS key */
1564 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1565 leafCompanyName
); /* unified API key */
1566 CFRelease(leafCompanyName
);
1569 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1570 if (nextUpdate
== 0) {
1571 /* populate revocation info for failed revocation check */
1572 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1573 kCFBooleanFalse
); /* iOS key */
1574 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1575 kCFBooleanFalse
); /* unified API key */
1580 if (pvc
->info
&& pvc
->result
&& pvc
->rvcs
) {
1581 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1582 if (nextUpdate
!= 0) {
1583 /* always populate revocation info for successful revocation check */
1584 haveRevocationResponse
= true;
1585 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1586 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1587 validUntil
); /* iOS key */
1588 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1589 validUntil
); /* unified API key */
1590 CFRelease(validUntil
);
1591 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1592 kCFBooleanTrue
); /* iOS key */
1593 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1594 kCFBooleanTrue
); /* unified API key */
1598 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1599 builder
->bestPathScore
= 0;
1600 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1601 0, kCFBooleanFalse
, true);
1604 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1605 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1609 if (pvc
->info
&& pvc
->is_ct_whitelisted
&& pvc
->result
) {
1610 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyWhiteListKey
,
1615 /* This will trigger the outer step function to call the completion
1617 builder
->state
= NULL
;
1621 /* @function SecPathBuilderStep
1622 @summary This is the core of the async engine.
1623 @description Return false iff job is complete, true if a network request
1625 builder->state is a function pointer which is to be invoked.
1626 If you call this function from within a builder->state invocation it
1627 immediately returns true.
1628 Otherwise the following steps are repeated endlessly (unless a step returns)
1629 builder->state is invoked. If it returns true and builder->state is still
1630 non NULL this proccess is repeated.
1631 If a state returns false, SecPathBuilder will return true
1632 if builder->state is non NULL.
1633 If builder->state is NULL then regardless of what the state function returns
1634 the completion callback will be invoked and the builder will be deallocated.
1636 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1637 if (builder
->activations
) {
1638 secdebug("async", "activations: %lu returning true",
1639 builder
->activations
);
1643 secdebug("async", "activations: %lu", builder
->activations
);
1644 builder
->activations
++;
1645 while (builder
->state
&& builder
->state(builder
));
1646 --builder
->activations
;
1648 if (builder
->state
) {
1649 secdebug("async", "waiting for async reply, exiting");
1650 /* A state returned false, it's waiting for network traffic. Let's
1655 if (builder
->activations
) {
1656 /* There is still at least one other running instance of this builder
1657 somewhere on the stack, we let that instance take care of sending
1658 the client a response. */
1662 SecTrustResultType result
= kSecTrustResultInvalid
;
1663 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
) {
1664 result
= kSecTrustResultUnspecified
;
1665 } else if (builder
->denyBestPath
) {
1666 result
= kSecTrustResultDeny
;
1668 result
= kSecTrustResultRecoverableTrustFailure
;
1671 secinfo("trust", "completed: %@ details: %@ result: %d",
1672 builder
->bestPath
, builder
->path
.details
, result
);
1674 if (builder
->completed
) {
1675 builder
->completed(builder
->context
, builder
->bestPath
,
1676 builder
->path
.details
, builder
->path
.info
, result
);
1679 /* Finally, destroy the builder and free it. */
1680 SecPathBuilderDestroy(builder
);
1686 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1687 return (builder
) ? builder
->queue
: NULL
;
1690 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1691 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1695 // MARK: SecTrustServer
1696 /********************************************************
1697 ****************** SecTrustServer **********************
1698 ********************************************************/
1700 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1703 SecTrustServerEvaluateCompleted(const void *userData
,
1704 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1705 SecTrustResultType result
) {
1706 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1707 evaluated(result
, details
, info
, chain
, NULL
);
1708 Block_release(evaluated
);
1712 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
)) {
1713 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1714 /* Call the actual evaluator function. */
1715 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1716 certificates
, anchors
,
1717 anchorsOnly
, keychainsAllowed
, policies
,
1718 responses
, SCTs
, trustedLogs
,
1719 verifyTime
, accessGroups
,
1720 SecTrustServerEvaluateCompleted
, userData
);
1721 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1725 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1726 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
) {
1727 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1728 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1729 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1731 if (tr
== kSecTrustResultInvalid
) {
1734 CFRetainSafe(error
);
1738 *pdetails
= details
;
1739 CFRetainSafe(details
);
1747 CFRetainSafe(chain
);
1750 dispatch_semaphore_signal(done
);
1752 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);