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>
37 #include <Security/SecTrustPriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <Security/SecCertificatePath.h>
41 #include <Security/SecFramework.h>
42 #include <Security/SecPolicyInternal.h>
43 #include <Security/SecTrustSettingsPriv.h>
44 #include <CoreFoundation/CFRuntime.h>
45 #include <CoreFoundation/CFSet.h>
46 #include <CoreFoundation/CFString.h>
47 #include <CoreFoundation/CFNumber.h>
48 #include <CoreFoundation/CFArray.h>
49 #include <CoreFoundation/CFPropertyList.h>
50 #include <AssertMacros.h>
55 #include <Security/SecBase.h>
56 #include "SecRSAKey.h"
57 #include <libDER/oids.h>
58 #include <utilities/debugging.h>
59 #include <utilities/SecCFWrappers.h>
60 #include <Security/SecInternal.h>
61 #include <ipc/securityd_client.h>
62 #include <CommonCrypto/CommonDigest.h>
63 #include "OTATrustUtilities.h"
66 /********************************************************
67 ***************** OTA Trust support ********************
68 ********************************************************/
71 //#ifndef SECITEM_SHIM_OSX
73 static CFArrayRef
subject_to_anchors(CFDataRef nic
);
74 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
);
76 static CFArrayRef
subject_to_anchors(CFDataRef nic
)
78 CFArrayRef result
= NULL
;
85 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
86 if (NULL
== otapkiref
)
91 CFDictionaryRef lookupTable
= SecOTAPKICopyAnchorLookupTable(otapkiref
);
94 if (NULL
== lookupTable
)
99 unsigned char subject_digest
[CC_SHA1_DIGEST_LENGTH
];
100 memset(subject_digest
, 0, CC_SHA1_DIGEST_LENGTH
);
102 (void)CC_SHA1(CFDataGetBytePtr(nic
), (CC_LONG
)CFDataGetLength(nic
), subject_digest
);
103 CFDataRef sha1Digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, subject_digest
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
106 result
= (CFArrayRef
)CFDictionaryGetValue(lookupTable
, sha1Digest
);
107 CFReleaseSafe(lookupTable
);
108 CFReleaseSafe(sha1Digest
);
113 static CFArrayRef
CopyCertDataFromIndices(CFArrayRef offsets
)
115 CFMutableArrayRef result
= NULL
;
117 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
118 if (NULL
== otapkiref
)
123 const char* anchorTable
= SecOTAPKIGetAnchorTable(otapkiref
);
124 if (NULL
== anchorTable
)
126 CFReleaseSafe(otapkiref
);
130 CFIndex num_offsets
= CFArrayGetCount(offsets
);
132 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
134 for (CFIndex idx
= 0; idx
< num_offsets
; idx
++)
136 CFNumberRef offset
= (CFNumberRef
)CFArrayGetValueAtIndex(offsets
, idx
);
137 uint32_t offset_value
= 0;
138 if (CFNumberGetValue(offset
, kCFNumberSInt32Type
, &offset_value
))
140 char* pDataPtr
= (char *)(anchorTable
+ offset_value
);
141 //int32_t record_length = *((int32_t * )pDataPtr);
142 //record_length = record_length;
143 pDataPtr
+= sizeof(uint32_t);
145 int32_t cert_data_length
= *((int32_t * )pDataPtr
);
146 pDataPtr
+= sizeof(uint32_t);
148 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)pDataPtr
,
149 cert_data_length
, kCFAllocatorNull
);
150 if (NULL
!= cert_data
)
152 CFArrayAppendValue(result
, cert_data
);
153 CFReleaseSafe(cert_data
);
157 CFReleaseSafe(otapkiref
);
161 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
)
163 CFMutableArrayRef result
= NULL
;
165 CFArrayRef cert_data_array
= CopyCertDataFromIndices(offsets
);
167 if (NULL
!= cert_data_array
)
169 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
170 CFIndex num_cert_datas
= CFArrayGetCount(cert_data_array
);
171 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
173 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_data_array
, idx
);
174 if (NULL
!= cert_data
)
176 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, cert_data
);
179 CFArrayAppendValue(result
, cert
);
184 CFRelease(cert_data_array
);
189 //#endif // SECITEM_SHIM_OSX
191 /********************************************************
192 *************** END OTA Trust support ******************
193 ********************************************************/
195 #define MAX_CHAIN_LENGTH 15
196 #define ACCEPT_PATH_SCORE 10000000
198 /* Forward declaration for use in SecCertificateSource. */
199 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
203 // MARK: SecCertificateSource
204 /********************************************************
205 ************ SecCertificateSource object ***************
206 ********************************************************/
208 typedef struct SecCertificateSource
*SecCertificateSourceRef
;
209 typedef void(*SecCertificateSourceParents
)(void *, CFArrayRef
);
210 typedef bool(*CopyParents
)(SecCertificateSourceRef source
,
211 SecCertificateRef certificate
, void *context
, SecCertificateSourceParents
);
212 typedef CFArrayRef(*CopyConstraints
)(SecCertificateSourceRef source
,
213 SecCertificateRef certificate
);
214 typedef bool(*Contains
)(SecCertificateSourceRef source
,
215 SecCertificateRef certificate
);
217 struct SecCertificateSource
{
218 CopyParents copyParents
;
219 CopyConstraints copyUsageConstraints
;
223 static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source
,
224 SecCertificateRef certificate
,
225 void *context
, SecCertificateSourceParents callback
) {
226 return source
->copyParents(source
, certificate
, context
, callback
);
229 static CFArrayRef
SecCertificateSourceCopyUsageConstraints(
230 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
231 if (source
->copyUsageConstraints
) {
232 return source
->copyUsageConstraints(source
, certificate
);
238 static bool SecCertificateSourceContains(SecCertificateSourceRef source
,
239 SecCertificateRef certificate
) {
240 return source
->contains(source
, certificate
);
244 // MARK: SecItemCertificateSource
245 /********************************************************
246 *********** SecItemCertificateSource object ************
247 ********************************************************/
248 struct SecItemCertificateSource
{
249 struct SecCertificateSource base
;
250 CFArrayRef accessGroups
;
252 typedef struct SecItemCertificateSource
*SecItemCertificateSourceRef
;
254 static CFTypeRef
SecItemCertificateSourceResultsPost(CFTypeRef raw_results
) {
255 if (isArray(raw_results
)) {
256 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(raw_results
), &kCFTypeArrayCallBacks
);
257 CFArrayForEach(raw_results
, ^(const void *value
) {
258 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, value
);
260 CFArrayAppendValue(result
, cert
);
265 } else if (isData(raw_results
)) {
266 return SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)raw_results
);
271 static bool SecItemCertificateSourceCopyParents(
272 SecCertificateSourceRef source
, SecCertificateRef certificate
,
273 void *context
, SecCertificateSourceParents callback
) {
274 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
275 /* FIXME: Search for things other than just subject of our issuer if we
276 have a subjectID or authorityKeyIdentifier. */
277 CFDataRef normalizedIssuer
=
278 SecCertificateGetNormalizedIssuerContent(certificate
);
279 const void *keys
[] = {
286 kSecClassCertificate
,
291 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
293 CFTypeRef results
= NULL
;
294 SecurityClient client
= {
296 .accessGroups
= msource
->accessGroups
,
297 .allowSystemKeychain
= true,
298 .allowSyncBubbleKeychain
= false,
299 .isNetworkExtension
= false,
302 /* We can make this async or run this on a queue now easily. */
303 CFErrorRef localError
= NULL
;
304 if (!_SecItemCopyMatching(query
, &client
, &results
, &localError
)) {
305 if (localError
&& (CFErrorGetCode(localError
) != errSecItemNotFound
)) {
306 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
308 CFReleaseSafe(localError
);
311 CFTypeRef certs
= SecItemCertificateSourceResultsPost(results
);
312 CFReleaseSafe(results
);
313 callback(context
, certs
);
314 CFReleaseSafe(certs
);
318 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source
,
319 SecCertificateRef certificate
) {
320 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
321 /* Look up a certificate by issuer and serial number. */
322 CFDataRef normalizedIssuer
= SecCertificateGetNormalizedIssuerContent(certificate
);
323 CFRetainSafe(normalizedIssuer
);
324 CFDataRef serialNumber
=
325 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
326 SecCertificateCopySerialNumber(certificate
, NULL
);
328 SecCertificateCopySerialNumber(certificate
);
330 const void *keys
[] = {
337 kSecClassCertificate
,
342 SecurityClient client
= {
344 .accessGroups
= msource
->accessGroups
,
345 .allowSystemKeychain
= true,
346 .allowSyncBubbleKeychain
= false,
347 .isNetworkExtension
= false,
349 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4, NULL
, NULL
);
350 CFErrorRef localError
= NULL
;
351 CFTypeRef results
= NULL
;
352 bool ok
= _SecItemCopyMatching(query
, &client
, &results
, &localError
);
353 CFReleaseSafe(query
);
354 CFReleaseSafe(serialNumber
);
355 CFReleaseSafe(normalizedIssuer
);
356 CFReleaseSafe(results
);
358 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
359 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
361 CFReleaseSafe(localError
);
367 static SecCertificateSourceRef
SecItemCertificateSourceCreate(CFArrayRef accessGroups
) {
368 SecItemCertificateSourceRef result
= (SecItemCertificateSourceRef
)malloc(sizeof(*result
));
369 result
->base
.copyParents
= SecItemCertificateSourceCopyParents
;
370 result
->base
.copyUsageConstraints
= NULL
;
371 result
->base
.contains
= SecItemCertificateSourceContains
;
372 result
->accessGroups
= accessGroups
;
373 CFRetainSafe(accessGroups
);
374 return (SecCertificateSourceRef
)result
;
377 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source
) {
378 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
379 CFReleaseSafe(msource
->accessGroups
);
384 // MARK: SecSystemAnchorSource
385 /********************************************************
386 *********** SecSystemAnchorSource object ************
387 ********************************************************/
389 static bool SecSystemAnchorSourceCopyParents(
390 SecCertificateSourceRef source
, SecCertificateRef certificate
,
391 void *context
, SecCertificateSourceParents callback
) {
392 //#ifndef SECITEM_SHIM_OSX
393 CFArrayRef parents
= NULL
;
394 CFArrayRef anchors
= NULL
;
395 SecOTAPKIRef otapkiref
= NULL
;
397 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
398 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
399 It does not matter since we would be returning the wrong anchors */
400 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
402 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
403 require_quiet(otapkiref
, errOut
);
404 anchors
= subject_to_anchors(nic
);
405 require_quiet(anchors
, errOut
);
406 parents
= CopyCertsFromIndices(anchors
);
409 callback(context
, parents
);
410 CFReleaseSafe(parents
);
411 CFReleaseSafe(otapkiref
);
412 //#endif // SECITEM_SHIM_OSX
416 /* Quick thought: we can eliminate this method if we search anchor sources
417 before all others and we remember if we got a cert from an anchorsource. */
418 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source
,
419 SecCertificateRef certificate
) {
421 CFArrayRef anchors
= NULL
;
422 SecOTAPKIRef otapkiref
= NULL
;
423 CFArrayRef cert_datas
= NULL
;
425 CFDataRef nic
= SecCertificateGetNormalizedSubjectContent(certificate
);
426 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
427 It does not matter since we would be returning the wrong anchors */
428 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
430 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
431 require_quiet(otapkiref
, errOut
);
432 anchors
= subject_to_anchors(nic
);
433 require_quiet(anchors
, errOut
);
434 cert_datas
= CopyCertDataFromIndices(anchors
);
435 require_quiet(cert_datas
, errOut
);
437 CFIndex cert_length
= SecCertificateGetLength(certificate
);
438 const UInt8
*cert_data_ptr
= SecCertificateGetBytePtr(certificate
);
440 CFIndex num_cert_datas
= CFArrayGetCount(cert_datas
);
441 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
443 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, idx
);
445 if (NULL
!= cert_data
)
447 if (CFGetTypeID(cert_data
) == CFDataGetTypeID())
449 CFIndex aCert_Length
= CFDataGetLength(cert_data
);
450 const UInt8
* aCert_Data_Ptr
= CFDataGetBytePtr(cert_data
);
452 if (aCert_Length
== cert_length
)
454 if (!memcmp(cert_data_ptr
, aCert_Data_Ptr
, cert_length
))
465 CFReleaseSafe(cert_datas
);
466 CFReleaseSafe(otapkiref
);
472 struct SecCertificateSource kSecSystemAnchorSource
= {
473 SecSystemAnchorSourceCopyParents
,
475 SecSystemAnchorSourceContains
480 // MARK: SecUserAnchorSource
481 /********************************************************
482 ************* SecUserAnchorSource object ***************
483 ********************************************************/
484 static bool SecUserAnchorSourceCopyParents(
485 SecCertificateSourceRef source
, SecCertificateRef certificate
,
486 void *context
, SecCertificateSourceParents callback
) {
487 CFArrayRef parents
= SecTrustStoreCopyParents(
488 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
, NULL
);
489 callback(context
, parents
);
490 CFReleaseSafe(parents
);
494 static CFArrayRef
SecUserAnchorSourceCopyUsageConstraints(
495 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
496 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
499 CFArrayRef usageConstraints
= NULL
;
500 bool ok
= _SecTrustStoreCopyUsageConstraints(
501 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), digest
, &usageConstraints
, NULL
);
502 return (ok
) ? usageConstraints
: NULL
;
505 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source
,
506 SecCertificateRef certificate
) {
507 return SecTrustStoreContains(
508 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
);
511 struct SecCertificateSource kSecUserAnchorSource
= {
512 SecUserAnchorSourceCopyParents
,
513 SecUserAnchorSourceCopyUsageConstraints
,
514 SecUserAnchorSourceContains
519 // MARK: SecMemoryCertificateSource
520 /********************************************************
521 ********** SecMemoryCertificateSource object ***********
522 ********************************************************/
523 struct SecMemoryCertificateSource
{
524 struct SecCertificateSource base
;
525 CFMutableSetRef certificates
;
526 CFMutableDictionaryRef subjects
;
528 typedef struct SecMemoryCertificateSource
*SecMemoryCertificateSourceRef
;
530 static bool SecMemoryCertificateSourceCopyParents(
531 SecCertificateSourceRef source
, SecCertificateRef certificate
,
532 void *context
, SecCertificateSourceParents callback
) {
533 SecMemoryCertificateSourceRef msource
=
534 (SecMemoryCertificateSourceRef
)source
;
535 CFDataRef normalizedIssuer
=
536 SecCertificateGetNormalizedIssuerContent(certificate
);
537 CFArrayRef parents
= (normalizedIssuer
) ? CFDictionaryGetValue(msource
->subjects
,
538 normalizedIssuer
) : NULL
;
539 /* FIXME filter parents by subjectID if certificate has an
540 authorityKeyIdentifier. */
541 secdebug("trust", "%@ parents -> %@", certificate
, parents
);
542 callback(context
, parents
);
546 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source
,
547 SecCertificateRef certificate
) {
548 SecMemoryCertificateSourceRef msource
=
549 (SecMemoryCertificateSourceRef
)source
;
550 return CFSetContainsValue(msource
->certificates
, certificate
);
553 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict
,
554 const void *key
, const void *value
) {
558 CFMutableArrayRef values
=
559 (CFMutableArrayRef
)CFDictionaryGetValue(dict
, key
);
561 values
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
562 &kCFTypeArrayCallBacks
);
563 CFDictionaryAddValue(dict
, key
, values
);
568 CFArrayAppendValue(values
, value
);
571 static void SecMemoryCertificateSourceApplierFunction(const void *value
,
573 SecMemoryCertificateSourceRef msource
=
574 (SecMemoryCertificateSourceRef
)context
;
575 SecCertificateRef certificate
= (SecCertificateRef
)value
;
577 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
578 if (CFSetContainsValue(msource
->certificates
, certificate
))
580 CFSetAddValue(msource
->certificates
, certificate
);
582 CFDataRef key
= SecCertificateGetNormalizedSubjectContent(certificate
);
583 dictAddValueToArrayForKey(msource
->subjects
, key
, value
);
586 static SecCertificateSourceRef
SecMemoryCertificateSourceCreate(
587 CFArrayRef certificates
) {
588 SecMemoryCertificateSourceRef result
= (SecMemoryCertificateSourceRef
)
589 malloc(sizeof(*result
));
590 result
->base
.copyParents
= SecMemoryCertificateSourceCopyParents
;
591 result
->base
.copyUsageConstraints
= NULL
;
592 result
->base
.contains
= SecMemoryCertificateSourceContains
;
593 CFIndex count
= CFArrayGetCount(certificates
);
594 result
->certificates
= CFSetCreateMutable(kCFAllocatorDefault
, count
,
595 &kCFTypeSetCallBacks
);
596 result
->subjects
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
597 count
, &kCFTypeDictionaryKeyCallBacks
,
598 &kCFTypeDictionaryValueCallBacks
);
599 CFRange range
= { 0, count
};
600 CFArrayApplyFunction(certificates
, range
,
601 SecMemoryCertificateSourceApplierFunction
, result
);
603 return (SecCertificateSourceRef
)result
;
606 static void SecMemoryCertificateSourceDestroy(
607 SecCertificateSourceRef source
) {
608 SecMemoryCertificateSourceRef msource
=
609 (SecMemoryCertificateSourceRef
)source
;
610 CFRelease(msource
->certificates
);
611 CFRelease(msource
->subjects
);
616 // MARK: SecCAIssuerCertificateSource
617 /********************************************************
618 ********* SecCAIssuerCertificateSource object **********
619 ********************************************************/
620 static bool SecCAIssuerCertificateSourceCopyParents(
621 SecCertificateSourceRef source
, SecCertificateRef certificate
,
622 void *context
, SecCertificateSourceParents callback
) {
623 return SecCAIssuerCopyParents(certificate
, SecPathBuilderGetQueue((SecPathBuilderRef
)context
), context
, callback
);
626 static bool SecCAIssuerCertificateSourceContains(
627 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
631 struct SecCertificateSource kSecCAIssuerSource
= {
632 SecCAIssuerCertificateSourceCopyParents
,
634 SecCAIssuerCertificateSourceContains
637 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
638 #include <Security/SecItemPriv.h>
640 // MARK: SecLegacyCertificateSource
641 /********************************************************
642 ********** SecLegacyCertificateSource object ***********
643 ********************************************************/
645 static bool SecLegacyCertificateSourceCopyParents(
646 SecCertificateSourceRef source
, SecCertificateRef certificate
,
647 void *context
, SecCertificateSourceParents callback
) {
648 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
649 callback(context
, parents
);
650 CFReleaseSafe(parents
);
654 static bool SecLegacyCertificateSourceContains(
655 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
656 SecCertificateRef cert
= SecItemCopyStoredCertificate(certificate
, NULL
);
657 bool result
= (cert
) ? true : false;
662 struct SecCertificateSource kSecLegacyCertificateSource
= {
663 SecLegacyCertificateSourceCopyParents
,
665 SecLegacyCertificateSourceContains
667 #endif /* SecLegacyCertificateSource */
669 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
671 // MARK: SecLegacyAnchorSource
672 /********************************************************
673 ************ SecLegacyAnchorSource object **************
674 ********************************************************/
676 static bool SecLegacyAnchorSourceCopyParents(
677 SecCertificateSourceRef source
, SecCertificateRef certificate
,
678 void *context
, SecCertificateSourceParents callback
) {
679 CFMutableArrayRef anchors
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
680 CFArrayRef parents
= SecItemCopyParentCertificates(certificate
, NULL
);
681 CFArrayRef trusted
= NULL
;
682 if (parents
== NULL
) {
685 /* Get the custom anchors which have been trusted in the user and admin domains.
686 * We don't need system domain roots here, since SecSystemAnchorSource provides those.
688 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
689 if (status
== errSecSuccess
&& trusted
) {
690 CFIndex index
, count
= CFArrayGetCount(parents
);
691 for (index
= 0; index
< count
; index
++) {
692 SecCertificateRef parent
= (SecCertificateRef
)CFArrayGetValueAtIndex(parents
, index
);
693 if (parent
&& CFArrayContainsValue(trusted
, CFRangeMake(0, CFArrayGetCount(trusted
)), parent
)) {
694 CFArrayAppendValue(anchors
, parent
);
700 callback(context
, anchors
);
701 CFReleaseSafe(anchors
);
702 CFReleaseSafe(parents
);
703 CFReleaseSafe(trusted
);
707 static CFArrayRef
SecLegacyAnchorSourceCopyUsageConstraints(
708 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
710 CFArrayRef result
= NULL
;
711 CFArrayRef userTrustSettings
= NULL
, adminTrustSettings
= NULL
;
713 OSStatus status
= SecTrustSettingsCopyTrustSettings(certificate
,
714 kSecTrustSettingsDomainUser
,
716 if ((status
== errSecSuccess
) && (userTrustSettings
!= NULL
)) {
717 result
= CFRetain(userTrustSettings
);
720 status
= SecTrustSettingsCopyTrustSettings(certificate
,
721 kSecTrustSettingsDomainAdmin
,
722 &adminTrustSettings
);
723 /* user trust settings overrule admin trust settings */
724 if ((status
== errSecSuccess
) && (adminTrustSettings
!= NULL
) && (result
== NULL
)) {
725 result
= CFRetain(adminTrustSettings
);
728 CFReleaseNull(userTrustSettings
);
729 CFReleaseNull(adminTrustSettings
);
733 static bool SecLegacyAnchorSourceContains(
734 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
735 if (certificate
== NULL
) {
738 CFArrayRef trusted
= NULL
;
740 OSStatus status
= SecTrustSettingsCopyCertificatesForUserAdminDomains(&trusted
);
741 if ((status
== errSecSuccess
) && (trusted
!= NULL
)) {
742 CFIndex index
, count
= CFArrayGetCount(trusted
);
743 for (index
= 0; index
< count
; index
++) {
744 SecCertificateRef anchor
= (SecCertificateRef
)CFArrayGetValueAtIndex(trusted
, index
);
745 if (anchor
&& CFEqual(anchor
, certificate
)) {
751 CFReleaseSafe(trusted
);
755 struct SecCertificateSource kSecLegacyAnchorSource
= {
756 SecLegacyAnchorSourceCopyParents
,
757 SecLegacyAnchorSourceCopyUsageConstraints
,
758 SecLegacyAnchorSourceContains
760 #endif /* SecLegacyAnchorSource */
763 // MARK: SecPathBuilder
764 /********************************************************
765 *************** SecPathBuilder object ******************
766 ********************************************************/
767 struct SecPathBuilder
{
768 dispatch_queue_t queue
;
769 CFDataRef clientAuditToken
;
770 SecCertificateSourceRef certificateSource
;
771 SecCertificateSourceRef itemCertificateSource
;
772 SecCertificateSourceRef anchorSource
;
773 CFMutableArrayRef anchorSources
;
774 CFIndex nextParentSource
;
775 CFMutableArrayRef parentSources
;
776 CFArrayRef ocspResponses
; // Stapled OCSP responses
777 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
778 CFArrayRef trustedLogs
; // Trusted CT logs
780 /* Hashed set of all paths we've constructed so far, used to prevent
781 re-considering a path that was already constructed once before.
782 Note that this is the only container in which certificatePath
783 objects are retained.
784 Every certificatePath being considered is always in allPaths and in at
785 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
786 all of which don't retain their values. */
787 CFMutableSetRef allPaths
;
789 /* No trusted anchor, satisfies the linking to intermediates for all
790 policies (unless considerRejected is true). */
791 CFMutableArrayRef partialPaths
;
792 /* No trusted anchor, does not satisfy linking to intermediates for all
794 CFMutableArrayRef rejectedPaths
;
795 /* Trusted anchor, satisfies the policies so far. */
796 CFMutableArrayRef candidatePaths
;
800 CFArrayRef leafDetails
;
802 CFIndex bestPathScore
;
804 bool considerRejected
;
805 bool considerPartials
;
806 bool canAccessNetwork
;
808 struct OpaqueSecPVC path
;
809 SecCertificatePathRef bestPath
;
815 bool (*state
)(SecPathBuilderRef
);
816 SecPathBuilderCompleted completed
;
820 /* State functions. Return false if a async job was scheduled, return
821 true to execute the next state. */
822 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
823 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
824 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
825 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
826 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
828 /* Forward declarations. */
829 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
830 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
832 /* IDEA: policies could be made capable of replacing incoming anchors and
833 anchorsOnly argument values. For example, some policies require the
834 Apple Inc. CA and not any other anchor. This can be done in
835 SecPathBuilderLeafCertificateChecks since this only runs once. */
836 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
837 SecCertificatePathRef path
) {
838 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
839 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
840 &kCFTypeDictionaryValueCallBacks
);
841 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
842 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
843 CFRelease(certDetail
);
844 SecPVCRef pvc
= &builder
->path
;
845 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
846 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
849 static void SecPathBuilderInit(SecPathBuilderRef builder
,
850 CFDataRef clientAuditToken
, CFArrayRef certificates
,
851 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
852 CFArrayRef policies
, CFArrayRef ocspResponses
,
853 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
854 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
855 SecPathBuilderCompleted completed
, const void *context
) {
856 secdebug("alloc", "%p", builder
);
857 CFAllocatorRef allocator
= kCFAllocatorDefault
;
859 builder
->clientAuditToken
= (CFDataRef
)
860 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
861 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
863 builder
->nextParentSource
= 1;
864 builder
->considerPartials
= false;
866 builder
->canAccessNetwork
= true;
868 builder
->canAccessNetwork
= false;
871 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
872 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
873 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
874 &kCFTypeSetCallBacks
);
876 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
877 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
878 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
879 builder
->partialIX
= 0;
881 /* Init the policy verification context. */
882 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
883 builder
->bestPath
= NULL
;
884 builder
->bestPathIsEV
= false;
885 builder
->bestPathIsSHA2
= false;
886 builder
->denyBestPath
= false;
887 builder
->bestPathScore
= 0;
889 /* Let's create all the certificate sources we might want to use. */
890 builder
->certificateSource
=
891 SecMemoryCertificateSourceCreate(certificates
);
893 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
895 builder
->anchorSource
= NULL
;
898 ** The order here avoids the most expensive methods if the cheaper methods
899 ** produce an acceptable chain: client-provided, keychains, network-fetched.
901 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
902 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
903 if (keychainsAllowed
) {
904 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
905 #if !TARGET_OS_IPHONE
906 /* On OS X, need additional parent source to search legacy keychain files. */
907 if (kSecLegacyCertificateSource
.contains
&& kSecLegacyCertificateSource
.copyParents
) {
908 CFArrayAppendValue(builder
->parentSources
, &kSecLegacyCertificateSource
);
913 /* Add the system and user anchor certificate db to the search list
914 if we don't explicitly trust them. */
915 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
917 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
920 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
921 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
925 ** The order here allows a client-provided anchor to overrule
926 ** a user or admin trust setting which can overrule the system anchors.
928 if (builder
->anchorSource
) {
929 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
932 /* Only add the system and user anchor certificate db to the
933 anchorSources if we are supposed to trust them. */
935 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
937 if (keychainsAllowed
&& kSecLegacyAnchorSource
.contains
&& kSecLegacyAnchorSource
.copyParents
) {
938 CFArrayAppendValue(builder
->anchorSources
, &kSecLegacyAnchorSource
);
941 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
944 /* Now let's get the leaf cert and turn it into a path. */
945 SecCertificateRef leaf
=
946 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
947 SecCertificateSourceRef source
= NULL
;
948 bool isAnchor
= false;
949 CFArrayRef constraints
= NULL
;
950 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
954 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
956 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
, constraints
);
957 CFReleaseSafe(constraints
);
958 CFSetAddValue(builder
->allPaths
, path
);
959 CFArrayAppendValue(builder
->partialPaths
, path
);
961 SecCertificatePathSetIsAnchored(path
);
962 CFArrayAppendValue(builder
->candidatePaths
, path
);
964 SecPathBuilderLeafCertificateChecks(builder
, path
);
967 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
968 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
971 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
973 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
974 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
975 CFReleaseSafe(otapkiref
);
978 builder
->activations
= 0;
979 builder
->state
= SecPathBuilderGetNext
;
980 builder
->completed
= completed
;
981 builder
->context
= context
;
984 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
985 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
986 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
987 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
988 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
989 SecPathBuilderCompleted completed
, const void *context
) {
990 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
991 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
992 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
993 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
994 accessGroups
, completed
, context
);
998 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
999 secdebug("alloc", "%p", builder
);
1000 dispatch_release_null(builder
->queue
);
1001 if (builder
->anchorSource
)
1002 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
1003 if (builder
->certificateSource
)
1004 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
1005 if (builder
->itemCertificateSource
)
1006 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
1007 CFReleaseSafe(builder
->clientAuditToken
);
1008 CFReleaseSafe(builder
->anchorSources
);
1009 CFReleaseSafe(builder
->parentSources
);
1010 CFReleaseSafe(builder
->allPaths
);
1011 CFReleaseSafe(builder
->partialPaths
);
1012 CFReleaseSafe(builder
->rejectedPaths
);
1013 CFReleaseSafe(builder
->candidatePaths
);
1014 CFReleaseSafe(builder
->leafDetails
);
1015 CFReleaseSafe(builder
->ocspResponses
);
1016 CFReleaseSafe(builder
->signedCertificateTimestamps
);
1017 CFReleaseSafe(builder
->trustedLogs
);
1019 SecPVCDelete(&builder
->path
);
1022 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
1023 return builder
->canAccessNetwork
;
1026 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
1027 if (builder
->canAccessNetwork
!= allow
) {
1028 builder
->canAccessNetwork
= allow
;
1030 #if !TARGET_OS_WATCH
1031 secinfo("http", "network access re-enabled by policy");
1032 /* re-enabling network_access re-adds kSecCAIssuerSource as
1034 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
1036 secnotice("http", "network access not allowed on WatchOS");
1037 builder
->canAccessNetwork
= false;
1040 secinfo("http", "network access disabled by policy");
1041 /* disabling network_access removes kSecCAIssuerSource from
1042 the list of parent sources. */
1043 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
1044 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
1045 &kSecCAIssuerSource
);
1047 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
1052 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
1054 return CFRetainSafe(builder
->ocspResponses
);
1057 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
1059 return CFRetainSafe(builder
->signedCertificateTimestamps
);
1062 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
1064 return CFRetainSafe(builder
->trustedLogs
);
1067 /* This function assumes that the input source is an anchor source */
1068 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
1069 SecCertificateRef certificate
) {
1070 bool result
= false;
1071 CFArrayRef constraints
= NULL
;
1072 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
1074 /* Unrestricted certificates:
1075 * -those that come from anchor sources with no constraints
1076 * -self-signed certificates with empty contraints arrays
1078 Boolean selfSigned
= false;
1079 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
1080 if ((NULL
== source
->copyUsageConstraints
) ||
1081 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
1082 secinfo("trust", "unrestricted anchor%s",
1083 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
1088 /* Get the trust settings result for the PVC */
1089 require(constraints
, out
);
1090 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
1091 settingsResult
= SecPVCGetTrustSettingsResult(&builder
->path
,
1094 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
1095 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
1096 // For our purposes, this is an anchor.
1097 secinfo("trust", "complex trust settings anchor");
1101 if (settingsResult
== kSecTrustSettingsResultDeny
) {
1102 /* We consider denied certs "anchors" because the trust decision
1103 is set regardless of building the chain further. The policy
1104 validation will handle rejecting this chain. */
1105 secinfo("trust", "complex trust settings denied anchor");
1110 CFReleaseNull(constraints
);
1114 /* Source returned in foundInSource has the same lifetime as the builder. */
1115 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
1116 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
1117 /* We look through the anchor sources in order. They are ordered in
1118 SecPathBuilderInit so that process anchors override user anchors which
1119 override system anchors. */
1120 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
1122 for (ix
= 0; ix
< count
; ++ix
) {
1123 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
1124 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
1125 if (SecCertificateSourceContains(source
, certificate
)) {
1127 *foundInSource
= source
;
1128 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
1136 /* Return false if path is not a partial, if path was a valid candidate it
1137 will have been added to builder->candidatePaths, if path was rejected
1138 by the parent certificate checks (because it's expired or some other
1139 static chaining check failed) it will have been added to rejectedPaths.
1140 Return true path if path is a partial. */
1141 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
1142 SecCertificatePathRef path
) {
1143 SecPVCRef pvc
= &builder
->path
;
1144 SecPVCSetPath(pvc
, path
, NULL
);
1146 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
1147 SecPVCGetCertificateCount(pvc
) - 1)) {
1148 secdebug("trust", "Found rejected path %@", path
);
1149 CFArrayAppendValue(builder
->rejectedPaths
, path
);
1153 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
1154 /* Candidate paths with failed signatures are discarded. */
1155 if (vstatus
== kSecPathVerifyFailed
) {
1156 secdebug("trust", "Verify failed for path %@", path
);
1160 if (vstatus
== kSecPathVerifySuccess
) {
1161 /* The signature chain verified sucessfully, now let's find
1162 out if we have an anchor for path. */
1163 if (SecCertificatePathIsAnchored(path
)) {
1164 secdebug("trust", "Adding candidate %@", path
);
1165 CFArrayAppendValue(builder
->candidatePaths
, path
);
1173 /* Given the builder, a partial chain partial and the parents array, construct
1174 a SecCertificatePath for each parent. After discarding previously
1175 considered paths and paths with cycles, sort out which array each path
1176 should go in, if any. */
1177 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
1178 SecCertificatePathRef partial
, CFArrayRef parents
) {
1179 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
1180 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
1182 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
1183 SecCertificateRef parent
= (SecCertificateRef
)
1184 CFArrayGetValueAtIndex(parents
, parentIX
);
1185 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
1187 if (ixOfParent
!= kCFNotFound
) {
1188 /* partial already contains parent. Let's not add the same
1189 certificate again. */
1190 if (ixOfParent
== rootIX
) {
1191 /* parent is equal to the root of the partial, so partial
1192 looks to be self issued. */
1193 SecCertificatePathSetSelfIssued(partial
);
1198 /* FIXME Add more sanity checks to see that parent really can be
1199 a parent of partial_root. subjectKeyID == authorityKeyID,
1200 signature algorithm matches public key algorithm, etc. */
1201 SecCertificateSourceRef source
= NULL
;
1202 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
1203 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
1204 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
, constraints
);
1205 CFReleaseSafe(constraints
);
1208 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
1209 CFSetAddValue(builder
->allPaths
, path
);
1211 SecCertificatePathSetIsAnchored(path
);
1212 if (SecPathBuilderIsPartial(builder
, path
)) {
1213 /* Insert path right at the current position since it's a new
1214 candiate partial. */
1215 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1216 ++builder
->partialIX
, path
);
1217 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
1218 parentIX
+ 1, num_parents
, path
);
1220 secdebug("trust", "found new path %@", path
);
1226 /* Callback for the SecPathBuilderGetNext() functions call to
1227 SecCertificateSourceCopyParents(). */
1228 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
1229 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
1230 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1231 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1232 secdebug("async", "%@ parents %@", partial
, parents
);
1233 SecPathBuilderProcessParents(builder
, partial
, parents
);
1235 builder
->state
= SecPathBuilderGetNext
;
1236 SecPathBuilderStep(builder
);
1239 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
1240 /* If we have any candidates left to go return those first. */
1241 if (CFArrayGetCount(builder
->candidatePaths
)) {
1242 SecCertificatePathRef path
= (SecCertificatePathRef
)
1243 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
1244 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
1245 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
1247 SecPVCSetPath(&builder
->path
, path
, NULL
);
1248 builder
->state
= SecPathBuilderValidatePath
;
1252 /* If we are considering rejected chains we check each rejected path
1253 with SecPathBuilderIsPartial() which checks the signature chain and
1254 either drops the path if it's not properly signed, add it as a
1255 candidate if it has a trusted anchor, or adds it as a partial
1256 to be considered once we finish considering all the rejects. */
1257 if (builder
->considerRejected
) {
1258 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
1261 SecCertificatePathRef path
= (SecCertificatePathRef
)
1262 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1263 if (SecPathBuilderIsPartial(builder
, path
)) {
1264 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1265 ++builder
->partialIX
, path
);
1267 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1269 /* Keep going until we have moved all rejected partials into
1270 the regular partials or candidates array. */
1275 /* If builder->partialIX is < 0 we have considered all partial chains
1276 this block must ensure partialIX >= 0 if execution continues past
1278 if (builder
->partialIX
< 0) {
1279 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1280 if (builder
->nextParentSource
< num_sources
) {
1281 builder
->nextParentSource
++;
1282 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1283 builder
->nextParentSource
, num_sources
);
1285 /* We've run out of new sources to consider so let's look at
1286 rejected chains and after that even consider partials
1288 FIXME we might not want to consider partial paths that
1289 are subsets of other partial paths, or not consider them
1290 at all if we already have an anchored reject. */
1291 if (!builder
->considerRejected
) {
1292 builder
->considerRejected
= true;
1293 secdebug("trust", "considering rejected paths");
1294 } else if (!builder
->considerPartials
) {
1295 builder
->considerPartials
= true;
1296 secdebug("trust", "considering partials");
1298 /* We're all out of options, so we can't produce any more
1299 candidates. Let's calculate details and return the best
1301 builder
->state
= SecPathBuilderComputeDetails
;
1305 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1306 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1310 /* We know builder->partialIX >= 0 if we get here. */
1311 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1312 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1313 /* Don't try to extend partials anymore once we are in the considerPartials
1314 state, since at this point every partial has been extended with every
1315 possible parentSource already. */
1316 if (builder
->considerPartials
) {
1317 --builder
->partialIX
;
1318 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1319 builder
->state
= SecPathBuilderValidatePath
;
1323 /* Attempt to extend this partial path with another certificate. This
1324 should give us a list of potential parents to consider. */
1325 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1326 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1329 /* Attempt to extend partial, leaving all possible extended versions
1330 of partial in builder->extendedPaths. */
1331 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1332 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1333 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1334 SecCertificateSourceRef source
;
1335 if (sourceIX
< num_anchor_sources
) {
1336 source
= (SecCertificateSourceRef
)
1337 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1338 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1339 num_anchor_sources
);
1341 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1342 source
= (SecCertificateSourceRef
)
1343 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1344 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1345 builder
->nextParentSource
);
1347 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1348 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1349 return SecCertificateSourceCopyParents(source
, root
,
1350 builder
, SecPathBuilderExtendPaths
);
1352 --builder
->partialIX
;
1358 /* One or more of the policies did not accept the candidate path. */
1359 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1361 SecPVCRef pvc
= &builder
->path
;
1363 builder
->state
= SecPathBuilderGetNext
;
1365 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1366 /* We never replace an ev reject with a non ev reject. */
1370 CFIndex bestPathScore
= builder
->bestPathScore
;
1371 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1372 SecPVCGetVerifyTime(&builder
->path
));
1374 /* The current chain is valid for EV, but revocation checking failed. We
1375 replace any previously accepted or rejected non EV chains with the
1377 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1383 /* Since this means we found a valid ev chain that was revoked,
1384 we might want to switch directly to the
1385 SecPathBuilderComputeDetails state here if we think further
1386 searching for new chains is pointless. For now we'll keep
1387 going, since we could accept an alternate EV certification
1388 path that isn't revoked. */
1389 builder
->state
= SecPathBuilderComputeDetails
;
1393 /* Do this last so that changes to bestPathScore above will take effect. */
1394 if (!builder
->bestPath
|| score
> bestPathScore
) {
1395 if (builder
->bestPath
) {
1397 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1398 (builder
->bestPathIsEV
? "" : "non "),
1399 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1400 builder
->bestPathScore
,
1401 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1403 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1404 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1407 builder
->bestPathScore
= score
;
1408 builder
->bestPath
= pvc
->path
;
1409 builder
->bestPathIsEV
= pvc
->is_ev
;
1410 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
);
1412 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1413 (pvc
->is_ev
? "" : "non "), score
, bestPathScore
, builder
->path
.path
);
1417 /* All policies accepted the candidate path. */
1418 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1420 SecPVCRef pvc
= &builder
->path
;
1421 bool isSHA2
= !SecCertificatePathHasWeakHash(pvc
->path
);
1422 CFIndex bestScore
= builder
->bestPathScore
;
1423 /* Score this path. Note that all points awarded or deducted in
1424 * SecCertificatePathScore are < 100,000 */
1425 CFIndex currScore
= (SecCertificatePathScore(pvc
->path
, pvc
->verifyTime
) +
1426 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1427 ((pvc
->is_ev
) ? 1000000 : 0)); //1,000,000 points for EV
1428 if (currScore
> bestScore
) {
1429 // current path is better than existing best path
1430 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1431 (builder
->bestPathIsEV
? "" : "non "),
1432 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1433 builder
->bestPathScore
,
1434 (pvc
->is_ev
? "" : "non "), (long)currScore
, builder
->path
.path
);
1436 builder
->bestPathScore
= currScore
;
1437 builder
->bestPathIsEV
= pvc
->is_ev
;
1438 builder
->bestPathIsSHA2
= isSHA2
;
1439 builder
->bestPath
= pvc
->path
;
1440 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
); /* should always be false */
1443 /* If we found the best accept we can, we want to switch directly to the
1444 SecPathBuilderComputeDetails state here, since we're done. */
1445 if ((pvc
->is_ev
|| !pvc
->optionally_ev
) && isSHA2
)
1446 builder
->state
= SecPathBuilderComputeDetails
;
1448 builder
->state
= SecPathBuilderGetNext
;
1451 /* Return true iff a given path satisfies all the specified policies at
1453 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1454 SecPVCRef pvc
= &builder
->path
;
1456 if (builder
->considerRejected
) {
1457 SecPathBuilderReject(builder
);
1461 builder
->state
= SecPathBuilderDidValidatePath
;
1462 return SecPVCPathChecks(pvc
);
1465 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1466 SecPVCRef pvc
= &builder
->path
;
1468 SecPathBuilderAccept(builder
);
1470 SecPathBuilderReject(builder
);
1472 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1476 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1478 SecPVCRef pvc
= &builder
->path
;
1480 if (!builder
->caller_wants_details
) {
1481 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1482 pvc
->result
= builder
->bestPathScore
> ACCEPT_PATH_SCORE
;
1483 builder
->state
= SecPathBuilderReportResult
;
1487 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1488 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1489 pathLength
, builder
->leafDetails
);
1490 CFRetainSafe(details
);
1491 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1492 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1493 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1494 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1495 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1496 for (ix
= 1; ix
< pathLength
; ++ix
) {
1497 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1498 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1499 &kCFTypeDictionaryValueCallBacks
);
1500 CFArrayAppendValue(details
, certDetail
);
1501 CFRelease(certDetail
);
1502 SecPVCParentCertificateChecks(pvc
, ix
);
1503 SecPVCGrayListedKeyChecks(pvc
, ix
);
1504 SecPVCBlackListedKeyChecks(pvc
, ix
);
1506 builder
->state
= SecPathBuilderReportResult
;
1507 bool completed
= SecPVCPathChecks(pvc
);
1509 /* Reject the certificate if it was accepted before but we failed it now. */
1510 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
&& !pvc
->result
) {
1511 builder
->bestPathScore
= 0;
1514 CFReleaseSafe(details
);
1519 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1520 SecPVCRef pvc
= &builder
->path
;
1521 bool haveRevocationResponse
= false;
1522 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1523 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1524 kCFBooleanTrue
); /* iOS key */
1525 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1526 kCFBooleanTrue
); /* unified API key */
1527 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1528 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1529 if (leafCompanyName
) {
1530 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1531 leafCompanyName
); /* iOS key */
1532 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1533 leafCompanyName
); /* unified API key */
1534 CFRelease(leafCompanyName
);
1537 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1538 if (nextUpdate
== 0) {
1539 /* populate revocation info for failed revocation check */
1540 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1541 kCFBooleanFalse
); /* iOS key */
1542 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1543 kCFBooleanFalse
); /* unified API key */
1548 if (pvc
->info
&& pvc
->result
&& pvc
->rvcs
) {
1549 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1550 if (nextUpdate
!= 0) {
1551 /* always populate revocation info for successful revocation check */
1552 haveRevocationResponse
= true;
1553 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1554 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1555 validUntil
); /* iOS key */
1556 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1557 validUntil
); /* unified API key */
1558 CFRelease(validUntil
);
1559 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1560 kCFBooleanTrue
); /* iOS key */
1561 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1562 kCFBooleanTrue
); /* unified API key */
1566 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1567 builder
->bestPathScore
= 0;
1568 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1569 0, kCFBooleanFalse
, true);
1572 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1573 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1577 if (pvc
->info
&& pvc
->is_ct_whitelisted
&& pvc
->result
) {
1578 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyWhiteListKey
,
1583 /* This will trigger the outer step function to call the completion
1585 builder
->state
= NULL
;
1589 /* @function SecPathBuilderStep
1590 @summary This is the core of the async engine.
1591 @description Return false iff job is complete, true if a network request
1593 builder->state is a function pointer which is to be invoked.
1594 If you call this function from within a builder->state invocation it
1595 immediately returns true.
1596 Otherwise the following steps are repeated endlessly (unless a step returns)
1597 builder->state is invoked. If it returns true and builder->state is still
1598 non NULL this proccess is repeated.
1599 If a state returns false, SecPathBuilder will return true
1600 if builder->state is non NULL.
1601 If builder->state is NULL then regardless of what the state function returns
1602 the completion callback will be invoked and the builder will be deallocated.
1604 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1605 if (builder
->activations
) {
1606 secdebug("async", "activations: %lu returning true",
1607 builder
->activations
);
1611 secdebug("async", "activations: %lu", builder
->activations
);
1612 builder
->activations
++;
1613 while (builder
->state
&& builder
->state(builder
));
1614 --builder
->activations
;
1616 if (builder
->state
) {
1617 secdebug("async", "waiting for async reply, exiting");
1618 /* A state returned false, it's waiting for network traffic. Let's
1623 if (builder
->activations
) {
1624 /* There is still at least one other running instance of this builder
1625 somewhere on the stack, we let that instance take care of sending
1626 the client a response. */
1630 SecTrustResultType result
= kSecTrustResultInvalid
;
1631 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
) {
1632 result
= kSecTrustResultUnspecified
;
1633 } else if (builder
->denyBestPath
) {
1634 result
= kSecTrustResultDeny
;
1636 result
= kSecTrustResultRecoverableTrustFailure
;
1639 secinfo("trust", "completed: %@ details: %@ result: %d",
1640 builder
->bestPath
, builder
->path
.details
, result
);
1642 if (builder
->completed
) {
1643 builder
->completed(builder
->context
, builder
->bestPath
,
1644 builder
->path
.details
, builder
->path
.info
, result
);
1647 /* Finally, destroy the builder and free it. */
1648 SecPathBuilderDestroy(builder
);
1654 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1655 return (builder
) ? builder
->queue
: NULL
;
1658 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1659 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1663 // MARK: SecTrustServer
1664 /********************************************************
1665 ****************** SecTrustServer **********************
1666 ********************************************************/
1668 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1671 SecTrustServerEvaluateCompleted(const void *userData
,
1672 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1673 SecTrustResultType result
) {
1674 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1675 evaluated(result
, details
, info
, chain
, NULL
);
1676 Block_release(evaluated
);
1680 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
)) {
1681 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1682 /* Call the actual evaluator function. */
1683 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1684 certificates
, anchors
,
1685 anchorsOnly
, keychainsAllowed
, policies
,
1686 responses
, SCTs
, trustedLogs
,
1687 verifyTime
, accessGroups
,
1688 SecTrustServerEvaluateCompleted
, userData
);
1689 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1693 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1694 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
) {
1695 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1696 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1697 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1699 if (tr
== kSecTrustResultInvalid
) {
1702 CFRetainSafe(error
);
1706 *pdetails
= details
;
1707 CFRetainSafe(details
);
1715 CFRetainSafe(chain
);
1718 dispatch_semaphore_signal(done
);
1720 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);