2 * Copyright (c) 2006-2010,2012-2015 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 <CoreFoundation/CFRuntime.h>
44 #include <CoreFoundation/CFSet.h>
45 #include <CoreFoundation/CFString.h>
46 #include <CoreFoundation/CFNumber.h>
47 #include <CoreFoundation/CFArray.h>
48 #include <CoreFoundation/CFPropertyList.h>
49 #include <AssertMacros.h>
54 #include <Security/SecBase.h>
55 #include "SecRSAKey.h"
56 #include <libDER/oids.h>
57 #include <utilities/debugging.h>
58 #include <utilities/SecCFWrappers.h>
59 #include <Security/SecInternal.h>
60 #include <ipc/securityd_client.h>
61 #include <CommonCrypto/CommonDigest.h>
62 #include "OTATrustUtilities.h"
65 /********************************************************
66 ***************** OTA Trust support ********************
67 ********************************************************/
70 //#ifndef SECITEM_SHIM_OSX
72 static CFArrayRef
subject_to_anchors(CFDataRef nic
);
73 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
);
75 static CFArrayRef
subject_to_anchors(CFDataRef nic
)
77 CFArrayRef result
= NULL
;
84 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
85 if (NULL
== otapkiref
)
90 CFDictionaryRef lookupTable
= SecOTAPKICopyAnchorLookupTable(otapkiref
);
93 if (NULL
== lookupTable
)
98 unsigned char subject_digest
[CC_SHA1_DIGEST_LENGTH
];
99 memset(subject_digest
, 0, CC_SHA1_DIGEST_LENGTH
);
101 (void)CC_SHA1(CFDataGetBytePtr(nic
), (CC_LONG
)CFDataGetLength(nic
), subject_digest
);
102 CFDataRef sha1Digest
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, subject_digest
, CC_SHA1_DIGEST_LENGTH
, kCFAllocatorNull
);
105 result
= (CFArrayRef
)CFDictionaryGetValue(lookupTable
, sha1Digest
);
106 CFReleaseSafe(lookupTable
);
107 CFReleaseSafe(sha1Digest
);
112 static CFArrayRef
CopyCertDataFromIndices(CFArrayRef offsets
)
114 CFMutableArrayRef result
= NULL
;
116 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
117 if (NULL
== otapkiref
)
122 const char* anchorTable
= SecOTAPKIGetAnchorTable(otapkiref
);
123 if (NULL
== anchorTable
)
125 CFReleaseSafe(otapkiref
);
129 CFIndex num_offsets
= CFArrayGetCount(offsets
);
131 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
133 for (CFIndex idx
= 0; idx
< num_offsets
; idx
++)
135 CFNumberRef offset
= (CFNumberRef
)CFArrayGetValueAtIndex(offsets
, idx
);
136 uint32_t offset_value
= 0;
137 if (CFNumberGetValue(offset
, kCFNumberSInt32Type
, &offset_value
))
139 char* pDataPtr
= (char *)(anchorTable
+ offset_value
);
140 //int32_t record_length = *((int32_t * )pDataPtr);
141 //record_length = record_length;
142 pDataPtr
+= sizeof(uint32_t);
144 int32_t cert_data_length
= *((int32_t * )pDataPtr
);
145 pDataPtr
+= sizeof(uint32_t);
147 CFDataRef cert_data
= CFDataCreateWithBytesNoCopy(kCFAllocatorDefault
, (const UInt8
*)pDataPtr
,
148 cert_data_length
, kCFAllocatorNull
);
149 if (NULL
!= cert_data
)
151 CFArrayAppendValue(result
, cert_data
);
152 CFReleaseSafe(cert_data
);
156 CFReleaseSafe(otapkiref
);
160 static CFArrayRef
CopyCertsFromIndices(CFArrayRef offsets
)
162 CFMutableArrayRef result
= NULL
;
164 CFArrayRef cert_data_array
= CopyCertDataFromIndices(offsets
);
166 if (NULL
!= cert_data_array
)
168 result
= CFArrayCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeArrayCallBacks
);
169 CFIndex num_cert_datas
= CFArrayGetCount(cert_data_array
);
170 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
172 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_data_array
, idx
);
173 if (NULL
!= cert_data
)
175 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, cert_data
);
178 CFArrayAppendValue(result
, cert
);
183 CFRelease(cert_data_array
);
188 //#endif // SECITEM_SHIM_OSX
190 /********************************************************
191 *************** END OTA Trust support ******************
192 ********************************************************/
194 #define MAX_CHAIN_LENGTH 15
196 /* Forward declaration for use in SecCertificateSource. */
197 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
201 // MARK: SecCertificateSource
202 /********************************************************
203 ************ SecCertificateSource object ***************
204 ********************************************************/
206 typedef struct SecCertificateSource
*SecCertificateSourceRef
;
207 typedef void(*SecCertificateSourceParents
)(void *, CFArrayRef
);
208 typedef bool(*CopyParents
)(SecCertificateSourceRef source
,
209 SecCertificateRef certificate
, void *context
, SecCertificateSourceParents
);
210 typedef bool(*Contains
)(SecCertificateSourceRef source
,
211 SecCertificateRef certificate
);
213 struct SecCertificateSource
{
214 CopyParents copyParents
;
218 static bool SecCertificateSourceCopyParents(SecCertificateSourceRef source
,
219 SecCertificateRef certificate
,
220 void *context
, SecCertificateSourceParents callback
) {
221 return source
->copyParents(source
, certificate
, context
, callback
);
224 static bool SecCertificateSourceContains(SecCertificateSourceRef source
,
225 SecCertificateRef certificate
) {
226 return source
->contains(source
, certificate
);
230 // MARK: SecItemCertificateSource
231 /********************************************************
232 *********** SecItemCertificateSource object ************
233 ********************************************************/
234 struct SecItemCertificateSource
{
235 struct SecCertificateSource base
;
236 CFArrayRef accessGroups
;
238 typedef struct SecItemCertificateSource
*SecItemCertificateSourceRef
;
240 static CFTypeRef
SecItemCertificateSourceResultsPost(CFTypeRef raw_results
) {
241 if (isArray(raw_results
)) {
242 CFMutableArrayRef result
= CFArrayCreateMutable(kCFAllocatorDefault
, CFArrayGetCount(raw_results
), &kCFTypeArrayCallBacks
);
243 CFArrayForEach(raw_results
, ^(const void *value
) {
244 SecCertificateRef cert
= SecCertificateCreateWithData(kCFAllocatorDefault
, value
);
246 CFArrayAppendValue(result
, cert
);
251 } else if (isData(raw_results
)) {
252 return SecCertificateCreateWithData(kCFAllocatorDefault
, (CFDataRef
)raw_results
);
257 static bool SecItemCertificateSourceCopyParents(
258 SecCertificateSourceRef source
, SecCertificateRef certificate
,
259 void *context
, SecCertificateSourceParents callback
) {
260 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
261 /* FIXME: Search for things other than just subject of our issuer if we
262 have a subjectID or authorityKeyIdentifier. */
263 CFDataRef normalizedIssuer
=
264 SecCertificateGetNormalizedIssuerContent(certificate
);
265 const void *keys
[] = {
272 kSecClassCertificate
,
277 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 4,
279 CFTypeRef results
= NULL
;
280 SecurityClient client
= {
282 .accessGroups
= msource
->accessGroups
,
283 .allowSystemKeychain
= true,
284 .allowSyncBubbleKeychain
= false,
285 .isNetworkExtension
= false,
288 /* We can make this async or run this on a queue now easily. */
289 CFErrorRef localError
= NULL
;
290 if (!_SecItemCopyMatching(query
, &client
, &results
, &localError
)) {
291 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
292 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
294 CFRelease(localError
);
297 CFTypeRef certs
= SecItemCertificateSourceResultsPost(results
);
298 CFReleaseSafe(results
);
299 callback(context
, certs
);
300 CFReleaseSafe(certs
);
304 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source
,
305 SecCertificateRef certificate
) {
306 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
307 /* Lookup a certificate by issuer and serial number. */
308 CFDataRef normalizedSubject
=
309 SecCertificateGetNormalizedSubjectContent(certificate
);
310 CFDataRef serialNumber
=
311 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
312 SecCertificateCopySerialNumber(certificate
, NULL
);
314 SecCertificateCopySerialNumber(certificate
);
316 const void *keys
[] = {
323 kSecClassCertificate
,
328 SecurityClient client
= {
330 .accessGroups
= msource
->accessGroups
,
331 .allowSystemKeychain
= true,
332 .allowSyncBubbleKeychain
= false,
333 .isNetworkExtension
= false,
335 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 5,
337 CFErrorRef localError
= NULL
;
338 CFTypeRef results
= NULL
;
339 bool ok
= _SecItemCopyMatching(query
, &client
, &results
, &localError
);
341 CFRelease(serialNumber
);
342 CFReleaseSafe(results
);
344 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
345 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
347 CFRelease(localError
);
353 static SecCertificateSourceRef
SecItemCertificateSourceCreate(CFArrayRef accessGroups
) {
354 SecItemCertificateSourceRef result
= (SecItemCertificateSourceRef
)malloc(sizeof(*result
));
355 result
->base
.copyParents
= SecItemCertificateSourceCopyParents
;
356 result
->base
.contains
= SecItemCertificateSourceContains
;
357 result
->accessGroups
= accessGroups
;
358 CFRetainSafe(accessGroups
);
359 return (SecCertificateSourceRef
)result
;
362 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source
) {
363 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
364 CFReleaseSafe(msource
->accessGroups
);
369 // MARK: SecSystemAnchorSource
370 /********************************************************
371 *********** SecSystemAnchorSource object ************
372 ********************************************************/
374 static bool SecSystemAnchorSourceCopyParents(
375 SecCertificateSourceRef source
, SecCertificateRef certificate
,
376 void *context
, SecCertificateSourceParents callback
) {
377 //#ifndef SECITEM_SHIM_OSX
378 CFArrayRef parents
= NULL
;
379 CFArrayRef anchors
= NULL
;
380 SecOTAPKIRef otapkiref
= NULL
;
382 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
383 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
384 It does not matter since we would be returning the wrong anchors */
385 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
387 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
388 require_quiet(otapkiref
, errOut
);
389 anchors
= subject_to_anchors(nic
);
390 require_quiet(anchors
, errOut
);
391 parents
= CopyCertsFromIndices(anchors
);
394 callback(context
, parents
);
395 CFReleaseSafe(parents
);
396 CFReleaseSafe(otapkiref
);
397 //#endif // SECITEM_SHIM_OSX
401 /* Quick thought: we can eliminate this method if we search anchor sources
402 before all others and we remember if we got a cert from an anchorsource. */
403 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source
,
404 SecCertificateRef certificate
) {
406 //#ifndef SECITEM_SHIM_OSX
407 CFArrayRef anchors
= NULL
;
408 SecOTAPKIRef otapkiref
= NULL
;
409 CFArrayRef cert_datas
= NULL
;
411 CFDataRef nic
= SecCertificateGetNormalizedSubjectContent(certificate
);
412 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
413 It does not matter since we would be returning the wrong anchors */
414 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
416 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
417 require_quiet(otapkiref
, errOut
);
418 anchors
= subject_to_anchors(nic
);
419 require_quiet(anchors
, errOut
);
420 cert_datas
= CopyCertDataFromIndices(anchors
);
421 require_quiet(cert_datas
, errOut
);
423 CFIndex cert_length
= SecCertificateGetLength(certificate
);
424 const UInt8
*cert_data_ptr
= SecCertificateGetBytePtr(certificate
);
426 CFIndex num_cert_datas
= CFArrayGetCount(cert_datas
);
427 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
429 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, idx
);
431 if (NULL
!= cert_data
)
433 if (CFGetTypeID(cert_data
) == CFDataGetTypeID())
435 CFIndex aCert_Length
= CFDataGetLength(cert_data
);
436 const UInt8
* aCert_Data_Ptr
= CFDataGetBytePtr(cert_data
);
438 if (aCert_Length
== cert_length
)
440 if (!memcmp(cert_data_ptr
, aCert_Data_Ptr
, cert_length
))
451 CFReleaseSafe(cert_datas
);
452 CFReleaseSafe(otapkiref
);
453 //#endif // SECITEM_SHIM_OSX
459 struct SecCertificateSource kSecSystemAnchorSource
= {
460 SecSystemAnchorSourceCopyParents
,
461 SecSystemAnchorSourceContains
465 // MARK: SecUserAnchorSource
466 /********************************************************
467 *********** SecUserAnchorSource object ************
468 ********************************************************/
469 static bool SecUserAnchorSourceCopyParents(
470 SecCertificateSourceRef source
, SecCertificateRef certificate
,
471 void *context
, SecCertificateSourceParents callback
) {
472 CFArrayRef parents
= SecTrustStoreCopyParents(
473 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
, NULL
);
474 callback(context
, parents
);
475 CFReleaseSafe(parents
);
479 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source
,
480 SecCertificateRef certificate
) {
481 return SecTrustStoreContains(
482 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
);
485 struct SecCertificateSource kSecUserAnchorSource
= {
486 SecUserAnchorSourceCopyParents
,
487 SecUserAnchorSourceContains
491 // MARK: SecMemoryCertificateSource
492 /********************************************************
493 *********** SecMemoryCertificateSource object ************
494 ********************************************************/
495 struct SecMemoryCertificateSource
{
496 struct SecCertificateSource base
;
497 CFMutableSetRef certificates
;
498 CFMutableDictionaryRef subjects
;
500 typedef struct SecMemoryCertificateSource
*SecMemoryCertificateSourceRef
;
502 static bool SecMemoryCertificateSourceCopyParents(
503 SecCertificateSourceRef source
, SecCertificateRef certificate
,
504 void *context
, SecCertificateSourceParents callback
) {
505 SecMemoryCertificateSourceRef msource
=
506 (SecMemoryCertificateSourceRef
)source
;
507 CFDataRef normalizedIssuer
=
508 SecCertificateGetNormalizedIssuerContent(certificate
);
509 CFArrayRef parents
= (normalizedIssuer
) ? CFDictionaryGetValue(msource
->subjects
,
510 normalizedIssuer
) : NULL
;
511 /* FIXME filter parents by subjectID if certificate has an
512 authorityKeyIdentifier. */
513 secdebug("trust", "%@ parents -> %@", certificate
, parents
);
514 callback(context
, parents
);
518 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source
,
519 SecCertificateRef certificate
) {
520 SecMemoryCertificateSourceRef msource
=
521 (SecMemoryCertificateSourceRef
)source
;
522 return CFSetContainsValue(msource
->certificates
, certificate
);
525 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict
,
526 const void *key
, const void *value
) {
530 CFMutableArrayRef values
=
531 (CFMutableArrayRef
)CFDictionaryGetValue(dict
, key
);
533 values
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
534 &kCFTypeArrayCallBacks
);
535 CFDictionaryAddValue(dict
, key
, values
);
540 CFArrayAppendValue(values
, value
);
543 static void SecMemoryCertificateSourceApplierFunction(const void *value
,
545 SecMemoryCertificateSourceRef msource
=
546 (SecMemoryCertificateSourceRef
)context
;
547 SecCertificateRef certificate
= (SecCertificateRef
)value
;
549 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
550 if (CFSetContainsValue(msource
->certificates
, certificate
))
552 CFSetAddValue(msource
->certificates
, certificate
);
554 CFDataRef key
= SecCertificateGetNormalizedSubjectContent(certificate
);
555 dictAddValueToArrayForKey(msource
->subjects
, key
, value
);
558 static SecCertificateSourceRef
SecMemoryCertificateSourceCreate(
559 CFArrayRef certificates
) {
560 SecMemoryCertificateSourceRef result
= (SecMemoryCertificateSourceRef
)
561 malloc(sizeof(*result
));
562 result
->base
.copyParents
= SecMemoryCertificateSourceCopyParents
;
563 result
->base
.contains
= SecMemoryCertificateSourceContains
;
564 CFIndex count
= CFArrayGetCount(certificates
);
565 result
->certificates
= CFSetCreateMutable(kCFAllocatorDefault
, count
,
566 &kCFTypeSetCallBacks
);
567 result
->subjects
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
568 count
, &kCFTypeDictionaryKeyCallBacks
,
569 &kCFTypeDictionaryValueCallBacks
);
570 CFRange range
= { 0, count
};
571 CFArrayApplyFunction(certificates
, range
,
572 SecMemoryCertificateSourceApplierFunction
, result
);
574 return (SecCertificateSourceRef
)result
;
577 static void SecMemoryCertificateSourceDestroy(
578 SecCertificateSourceRef source
) {
579 SecMemoryCertificateSourceRef msource
=
580 (SecMemoryCertificateSourceRef
)source
;
581 CFRelease(msource
->certificates
);
582 CFRelease(msource
->subjects
);
587 // MARK: SecCAIssuerCertificateSource
588 /********************************************************
589 ********* SecCAIssuerCertificateSource object **********
590 ********************************************************/
591 static bool SecCAIssuerCertificateSourceCopyParents(
592 SecCertificateSourceRef source
, SecCertificateRef certificate
,
593 void *context
, SecCertificateSourceParents callback
) {
594 return SecCAIssuerCopyParents(certificate
, SecPathBuilderGetQueue((SecPathBuilderRef
)context
), context
, callback
);
597 static bool SecCAIssuerCertificateSourceContains(
598 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
602 struct SecCertificateSource kSecCAIssuerSource
= {
603 SecCAIssuerCertificateSourceCopyParents
,
604 SecCAIssuerCertificateSourceContains
608 // MARK: SecPathBuilder
609 /********************************************************
610 *************** SecPathBuilder object ******************
611 ********************************************************/
612 struct SecPathBuilder
{
613 dispatch_queue_t queue
;
614 CFDataRef clientAuditToken
;
615 SecCertificateSourceRef certificateSource
;
616 SecCertificateSourceRef itemCertificateSource
;
617 SecCertificateSourceRef anchorSource
;
618 CFMutableArrayRef anchorSources
;
619 CFIndex nextParentSource
;
620 CFMutableArrayRef parentSources
;
621 CFArrayRef ocspResponses
; // Stapled OCSP responses
622 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
623 CFArrayRef trustedLogs
; // Trusted CT logs
625 /* Hashed set of all paths we've constructed so far, used to prevent
626 re-considering a path that was already constructed once before.
627 Note that this is the only container in which certificatePath
628 objects are retained.
629 Every certificatePath being considered is always in allPaths and in at
630 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
631 all of which don't retain their values. */
632 CFMutableSetRef allPaths
;
634 /* No trusted anchor, satisfies the linking to intermediates for all
635 policies (unless considerRejected is true). */
636 CFMutableArrayRef partialPaths
;
637 /* No trusted anchor, does not satisfy linking to intermediates for all
639 CFMutableArrayRef rejectedPaths
;
640 /* Trusted anchor, satisfies the policies so far. */
641 CFMutableArrayRef candidatePaths
;
645 CFArrayRef leafDetails
;
649 bool considerRejected
;
650 bool considerPartials
;
651 bool canAccessNetwork
;
653 struct OpaqueSecPVC path
;
654 SecCertificatePathRef bestPath
;
658 bool (*state
)(SecPathBuilderRef
);
659 SecPathBuilderCompleted completed
;
663 /* State functions. Return false if a async job was scheduled, return
664 true to execute the next state. */
665 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
666 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
667 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
668 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
669 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
671 /* Forward declarations. */
672 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
673 SecCertificateRef certificate
);
675 /* IDEA: policies could be made cabable of replacing incoming anchors and
676 anchorsOnly argument values. For example some policies require the
677 Apple Inc. CA and not any other anchor. This can be done in
678 SecPathBuilderLeafCertificateChecks since this only runs once. */
679 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
680 SecCertificatePathRef path
) {
681 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
682 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
683 &kCFTypeDictionaryValueCallBacks
);
684 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
685 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
686 CFRelease(certDetail
);
687 SecPVCRef pvc
= &builder
->path
;
688 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
689 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
692 static void SecPathBuilderInit(SecPathBuilderRef builder
,
693 CFDataRef clientAuditToken
, CFArrayRef certificates
,
694 CFArrayRef anchors
, bool anchorsOnly
,
695 CFArrayRef policies
, CFArrayRef ocspResponses
,
696 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
697 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
698 SecPathBuilderCompleted completed
, const void *context
) {
699 secdebug("alloc", "%p", builder
);
700 CFAllocatorRef allocator
= kCFAllocatorDefault
;
702 builder
->clientAuditToken
= (CFDataRef
)
703 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
704 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
706 builder
->nextParentSource
= 1;
707 builder
->considerPartials
= false;
708 builder
->canAccessNetwork
= true;
710 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
711 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
712 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
713 &kCFTypeSetCallBacks
);
715 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
716 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
717 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
718 builder
->partialIX
= 0;
720 /* Init the policy verification context. */
721 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
722 builder
->bestPath
= NULL
;
723 builder
->bestPathIsEV
= false;
724 builder
->rejectScore
= 0;
726 /* Let's create all the certificate sources we might want to use. */
727 builder
->certificateSource
=
728 SecMemoryCertificateSourceCreate(certificates
);
730 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
732 builder
->anchorSource
= NULL
;
734 /* We always search certificateSource for parents since it includes the
735 leaf itself and it might be self signed. */
736 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
737 if (builder
->anchorSource
) {
738 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
740 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
741 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
743 /* Add the system and user anchor certificate db to the search list
744 if we don't explicitly trust them. */
745 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
746 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
748 /* Only add the system and user anchor certificate db to the
749 anchorSources if we are supposed to trust them. */
750 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
751 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
753 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
755 /* Now let's get the leaf cert and turn it into a path. */
756 SecCertificateRef leaf
=
757 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
758 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
);
759 CFSetAddValue(builder
->allPaths
, path
);
760 CFArrayAppendValue(builder
->partialPaths
, path
);
761 if (SecPathBuilderIsAnchor(builder
, leaf
)) {
762 SecCertificatePathSetIsAnchored(path
);
763 CFArrayAppendValue(builder
->candidatePaths
, path
);
765 SecPathBuilderLeafCertificateChecks(builder
, path
);
768 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
769 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
772 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
774 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
775 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
776 CFReleaseSafe(otapkiref
);
779 builder
->activations
= 0;
780 builder
->state
= SecPathBuilderGetNext
;
781 builder
->completed
= completed
;
782 builder
->context
= context
;
785 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
786 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
787 CFArrayRef policies
, CFArrayRef ocspResponses
,
788 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
789 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
790 SecPathBuilderCompleted completed
, const void *context
) {
791 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
792 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
793 anchors
, anchorsOnly
, policies
, ocspResponses
,
794 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
795 accessGroups
, completed
, context
);
799 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
800 secdebug("alloc", "%p", builder
);
801 dispatch_release_null(builder
->queue
);
802 if (builder
->anchorSource
)
803 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
804 if (builder
->certificateSource
)
805 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
806 if (builder
->itemCertificateSource
)
807 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
808 CFReleaseSafe(builder
->clientAuditToken
);
809 CFReleaseSafe(builder
->anchorSources
);
810 CFReleaseSafe(builder
->parentSources
);
811 CFReleaseSafe(builder
->allPaths
);
812 CFReleaseSafe(builder
->partialPaths
);
813 CFReleaseSafe(builder
->rejectedPaths
);
814 CFReleaseSafe(builder
->candidatePaths
);
815 CFReleaseSafe(builder
->leafDetails
);
817 SecPVCDelete(&builder
->path
);
820 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
821 return builder
->canAccessNetwork
;
824 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
825 if (builder
->canAccessNetwork
!= allow
) {
826 builder
->canAccessNetwork
= allow
;
828 secdebug("http", "network access re-enabled by policy");
829 /* re-enabling network_access re-adds kSecCAIssuerSource as
831 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
833 secdebug("http", "network access disabled by policy");
834 /* disabling network_access removes kSecCAIssuerSource from
835 the list of parent sources. */
836 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
837 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
838 &kSecCAIssuerSource
);
840 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
845 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
847 return CFRetainSafe(builder
->ocspResponses
);
850 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
852 return CFRetainSafe(builder
->signedCertificateTimestamps
);
855 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
857 return CFRetainSafe(builder
->trustedLogs
);
860 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
861 SecCertificateRef certificate
) {
862 /* We always look through all anchor sources. */
863 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
865 for (ix
= 0; ix
< count
; ++ix
) {
866 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
867 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
868 if (SecCertificateSourceContains(source
, certificate
)) {
875 /* Return false if path is not a partial, if path was a valid candidate it
876 will have been added to builder->candidatePaths, if path was rejected
877 by the parent certificate checks (because it's expired or some other
878 static chaining check failed) it will have been added to rejectedPaths.
879 Return true path if path is a partial. */
880 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
881 SecCertificatePathRef path
) {
882 SecPVCRef pvc
= &builder
->path
;
883 SecPVCSetPath(pvc
, path
, NULL
);
885 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
886 SecPVCGetCertificateCount(pvc
) - 1)) {
887 secdebug("trust", "Found rejected path %@", path
);
888 CFArrayAppendValue(builder
->rejectedPaths
, path
);
892 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
893 /* Candidate paths with failed signatures are discarded. */
894 if (vstatus
== kSecPathVerifyFailed
) {
895 secdebug("trust", "Verify failed for path %@", path
);
899 if (vstatus
== kSecPathVerifySuccess
) {
900 /* The signature chain verified sucessfully, now let's find
901 out if we have an anchor for path. */
902 if (SecCertificatePathIsAnchored(path
)) {
903 secdebug("trust", "Adding candidate %@", path
);
904 CFArrayAppendValue(builder
->candidatePaths
, path
);
912 /* Given the builder, a partial chain partial and the parents array, construct
913 a SecCertificatePath for each parent. After discarding previously
914 considered paths and paths with cycles, sort out which array each path
915 should go in, if any. */
916 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
917 SecCertificatePathRef partial
, CFArrayRef parents
) {
918 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
919 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
921 bool is_anchor
= SecCertificatePathGetNextSourceIndex(partial
) <=
922 CFArrayGetCount(builder
->anchorSources
);
923 secdebug("trust", "found %" PRIdCFIndex
" candidate %s", num_parents
,
924 (is_anchor
? "anchors" : "parents"));
925 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
926 SecCertificateRef parent
= (SecCertificateRef
)
927 CFArrayGetValueAtIndex(parents
, parentIX
);
928 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
930 if (ixOfParent
!= kCFNotFound
) {
931 /* partial already contains parent. Let's not add the same
932 certificate again. */
933 if (ixOfParent
== rootIX
) {
934 /* parent is equal to the root of the partial, so partial
935 looks to be self issued. */
936 SecCertificatePathSetSelfIssued(partial
);
941 /* FIXME Add more sanity checks to see that parent really can be
942 a parent of partial_root. subjectKeyID == authorityKeyID,
943 signature algorithm matches public key algorithm, etc. */
944 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
);
947 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
948 CFSetAddValue(builder
->allPaths
, path
);
950 SecCertificatePathSetIsAnchored(path
);
951 if (SecPathBuilderIsPartial(builder
, path
)) {
952 /* Insert path right at the current position since it's a new
954 CFArrayInsertValueAtIndex(builder
->partialPaths
,
955 ++builder
->partialIX
, path
);
956 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
957 parentIX
+ 1, num_parents
, path
);
959 secdebug("trust", "found new path %@", path
);
965 /* Callback for the SecPathBuilderGetNext() functions call to
966 SecCertificateSourceCopyParents(). */
967 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
968 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
969 SecCertificatePathRef partial
= (SecCertificatePathRef
)
970 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
971 secdebug("async", "%@ parents %@", partial
, parents
);
972 SecPathBuilderProcessParents(builder
, partial
, parents
);
974 builder
->state
= SecPathBuilderGetNext
;
975 SecPathBuilderStep(builder
);
978 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
979 /* If we have any candidates left to go return those first. */
980 if (CFArrayGetCount(builder
->candidatePaths
)) {
981 SecCertificatePathRef path
= (SecCertificatePathRef
)
982 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
983 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
984 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
986 SecPVCSetPath(&builder
->path
, path
, NULL
);
987 builder
->state
= SecPathBuilderValidatePath
;
991 /* If we are considering rejected chains we check each rejected path
992 with SecPathBuilderIsPartial() which checks the signature chain and
993 either drops the path if it's not properly signed, add it as a
994 candidate if it has a trusted anchor, or adds it as a partial
995 to be considered once we finish considering all the rejects. */
996 if (builder
->considerRejected
) {
997 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
1000 SecCertificatePathRef path
= (SecCertificatePathRef
)
1001 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1002 if (SecPathBuilderIsPartial(builder
, path
)) {
1003 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1004 ++builder
->partialIX
, path
);
1006 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1008 /* Keep going until we have moved all rejected partials into
1009 the regular partials or candidates array. */
1014 /* If builder->partialIX is < 0 we have considered all partial chains
1015 this block must ensure partialIX >= 0 if execution continues past
1017 if (builder
->partialIX
< 0) {
1018 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1019 if (builder
->nextParentSource
< num_sources
) {
1020 builder
->nextParentSource
++;
1021 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1022 builder
->nextParentSource
, num_sources
);
1024 /* We've run out of new sources to consider so let's look at
1025 rejected chains and after that even consider partials
1027 FIXME we might not want to consider partial paths that
1028 are subsets of other partial paths, or not consider them
1029 at all if we already have an anchored reject. */
1030 if (!builder
->considerRejected
) {
1031 builder
->considerRejected
= true;
1032 secdebug("trust", "considering rejected paths");
1033 } else if (!builder
->considerPartials
) {
1034 builder
->considerPartials
= true;
1035 secdebug("trust", "considering partials");
1037 /* We're all out of options, so we can't produce any more
1038 candidates. Let's calculate details and return the best
1040 builder
->state
= SecPathBuilderComputeDetails
;
1044 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1045 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1049 /* We know builder->partialIX >= 0 if we get here. */
1050 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1051 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1052 /* Don't try to extend partials anymore once we are in the considerPartials
1053 state, since at this point every partial has been extended with every
1054 possible parentSource already. */
1055 if (builder
->considerPartials
) {
1056 --builder
->partialIX
;
1057 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1058 builder
->state
= SecPathBuilderValidatePath
;
1062 /* Attempt to extend this partial path with another certificate. This
1063 should give us a list of potential parents to consider. */
1064 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1065 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1068 /* Attempt to extend partial, leaving all possible extended versions
1069 of partial in builder->extendedPaths. */
1070 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1071 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1072 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1073 SecCertificateSourceRef source
;
1074 if (sourceIX
< num_anchor_sources
) {
1075 source
= (SecCertificateSourceRef
)
1076 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1077 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1078 num_anchor_sources
);
1080 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1081 source
= (SecCertificateSourceRef
)
1082 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1083 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1084 builder
->nextParentSource
);
1086 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1087 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1088 return SecCertificateSourceCopyParents(source
, root
,
1089 builder
, SecPathBuilderExtendPaths
);
1091 --builder
->partialIX
;
1097 /* One or more of the policies did not accept the candidate path. */
1098 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1100 SecPVCRef pvc
= &builder
->path
;
1102 builder
->state
= SecPathBuilderGetNext
;
1104 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1105 /* We never replace an ev reject with a non ev reject. */
1109 CFIndex rejectScore
= builder
->rejectScore
;
1110 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1111 SecPVCGetVerifyTime(&builder
->path
));
1113 /* The current chain is valid for EV, but revocation checking failed. We
1114 replace any previously accepted or rejected non EV chains with the
1116 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1122 /* Since this means we found a valid ev chain that was revoked,
1123 we might want to switch directly to the
1124 SecPathBuilderComputeDetails state here if we think further
1125 searching for new chains is pointless. For now we'll keep
1126 going, since we could accept an alternate EV certification
1127 path that isn't revoked. */
1128 builder
->state
= SecPathBuilderComputeDetails
;
1132 /* Do this last so that changes to rejectScore above will take affect. */
1133 if (!builder
->bestPath
|| score
> rejectScore
) {
1134 if (builder
->bestPath
) {
1136 "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex
" %@",
1137 (builder
->bestPathIsEV
? "" : "non "),
1138 (builder
->rejectScore
== INTPTR_MAX
? "accept" : "reject"),
1139 builder
->rejectScore
,
1140 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1142 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" %@",
1143 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1146 builder
->rejectScore
= score
;
1147 builder
->bestPath
= pvc
->path
;
1148 builder
->bestPathIsEV
= pvc
->is_ev
;
1150 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1151 (pvc
->is_ev
? "" : "non "), score
, rejectScore
, builder
->path
.path
);
1155 /* All policies accepted the candidate path. */
1156 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1158 SecPVCRef pvc
= &builder
->path
;
1159 if (pvc
->is_ev
|| !builder
->bestPathIsEV
) {
1160 secdebug("accept", "replacing %sev accept with %sev %@",
1161 (builder
->bestPathIsEV
? "" : "non "),
1162 (pvc
->is_ev
? "" : "non "), builder
->path
.path
);
1163 builder
->rejectScore
= INTPTR_MAX
; /* CFIndex is signed long which is INTPTR_T */
1164 builder
->bestPathIsEV
= pvc
->is_ev
;
1165 builder
->bestPath
= pvc
->path
;
1168 /* If we found the best accept we can we want to switch directly to the
1169 SecPathBuilderComputeDetails state here, since we're done. */
1170 if (pvc
->is_ev
|| !pvc
->optionally_ev
)
1171 builder
->state
= SecPathBuilderComputeDetails
;
1173 builder
->state
= SecPathBuilderGetNext
;
1176 /* Return true iff a given path satisfies all the specified policies at
1178 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1179 SecPVCRef pvc
= &builder
->path
;
1181 if (builder
->considerRejected
) {
1182 SecPathBuilderReject(builder
);
1186 builder
->state
= SecPathBuilderDidValidatePath
;
1187 return SecPVCPathChecks(pvc
);
1190 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1191 SecPVCRef pvc
= &builder
->path
;
1193 SecPathBuilderAccept(builder
);
1195 SecPathBuilderReject(builder
);
1197 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1201 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1203 SecPVCRef pvc
= &builder
->path
;
1205 if (!builder
->caller_wants_details
) {
1206 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1207 pvc
->result
= builder
->rejectScore
== INTPTR_MAX
;
1208 builder
->state
= SecPathBuilderReportResult
;
1212 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1213 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1214 pathLength
, builder
->leafDetails
);
1215 CFRetainSafe(details
);
1216 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1217 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1218 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1219 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1220 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1221 for (ix
= 1; ix
< pathLength
; ++ix
) {
1222 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1223 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1224 &kCFTypeDictionaryValueCallBacks
);
1225 CFArrayAppendValue(details
, certDetail
);
1226 CFRelease(certDetail
);
1227 SecPVCParentCertificateChecks(pvc
, ix
);
1228 SecPVCGrayListedKeyChecks(pvc
, ix
);
1229 SecPVCBlackListedKeyChecks(pvc
, ix
);
1231 builder
->state
= SecPathBuilderReportResult
;
1232 bool completed
= SecPVCPathChecks(pvc
);
1234 /* Reject the certificate if it was accepted before but we failed it now. */
1235 if (builder
->rejectScore
== INTPTR_MAX
&& !pvc
->result
) {
1236 builder
->rejectScore
= 0;
1239 CFReleaseSafe(details
);
1244 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1245 SecPVCRef pvc
= &builder
->path
;
1246 bool haveRevocationResponse
= false;
1247 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1248 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1249 kCFBooleanTrue
); /* iOS key */
1250 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1251 kCFBooleanTrue
); /* unified API key */
1252 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1253 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1254 if (leafCompanyName
) {
1255 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1256 leafCompanyName
); /* iOS key */
1257 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1258 leafCompanyName
); /* unified API key */
1259 CFRelease(leafCompanyName
);
1262 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1263 if (nextUpdate
== 0) {
1264 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1265 kCFBooleanFalse
); /* iOS key */
1266 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1267 kCFBooleanFalse
); /* unified API key */
1269 haveRevocationResponse
= true;
1270 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1271 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1272 validUntil
); /* iOS key */
1273 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1274 validUntil
); /* unified API key */
1275 CFRelease(validUntil
);
1276 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1277 kCFBooleanTrue
); /* iOS key */
1278 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1279 kCFBooleanTrue
); /* unified API key */
1284 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1285 builder
->rejectScore
= 0;
1286 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1287 0, kCFBooleanFalse
, true);
1290 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1291 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1296 /* This will trigger the outer step function to call the completion
1298 builder
->state
= NULL
;
1302 /* @function SecPathBuilderStep
1303 @summary This is the core of the async engine.
1304 @description Return false iff job is complete, true if a network request
1306 builder->state is a function pointer which is to be invoked.
1307 If you call this function from within a builder->state invocation it
1308 immediately returns true.
1309 Otherwise the following steps are repeated endlessly (unless a step returns)
1310 builder->state is invoked. If it returns true and builder->state is still
1311 non NULL this proccess is repeated.
1312 If a state returns false, SecPathBuilder will return true
1313 if builder->state is non NULL.
1314 If builder->state is NULL then regardless of what the state function returns
1315 the completion callback will be invoked and the builder will be deallocated.
1317 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1318 if (builder
->activations
) {
1319 secdebug("async", "activations: %lu returning true",
1320 builder
->activations
);
1324 secdebug("async", "activations: %lu", builder
->activations
);
1325 builder
->activations
++;
1326 while (builder
->state
&& builder
->state(builder
));
1327 --builder
->activations
;
1329 if (builder
->state
) {
1330 secdebug("async", "waiting for async reply, exiting");
1331 /* A state returned false, it's waiting for network traffic. Let's
1336 if (builder
->activations
) {
1337 /* There is still at least one other running instance of this builder
1338 somewhere on the stack, we let that instance take care of sending
1339 the client a response. */
1343 SecTrustResultType result
= (builder
->rejectScore
== INTPTR_MAX
1344 ? kSecTrustResultUnspecified
: kSecTrustResultRecoverableTrustFailure
);
1346 secdebug("trust", "completed: %@ details: %@ result: %d",
1347 builder
->bestPath
, builder
->path
.details
, result
);
1349 if (builder
->completed
) {
1350 builder
->completed(builder
->context
, builder
->bestPath
,
1351 builder
->path
.details
, builder
->path
.info
, result
);
1354 /* Finally, destroy the builder and free it. */
1355 SecPathBuilderDestroy(builder
);
1361 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1362 return (builder
) ? builder
->queue
: NULL
;
1365 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1366 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1370 // MARK: SecTrustServer
1371 /********************************************************
1372 ****************** SecTrustServer **********************
1373 ********************************************************/
1375 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1378 SecTrustServerEvaluateCompleted(const void *userData
,
1379 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1380 SecTrustResultType result
) {
1381 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1382 evaluated(result
, details
, info
, chain
, NULL
);
1383 Block_release(evaluated
);
1387 SecTrustServerEvaluateBlock(CFDataRef clientAuditToken
, CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, 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
)) {
1388 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1389 /* Call the actual evaluator function. */
1390 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1391 certificates
, anchors
,
1392 anchorsOnly
, policies
,
1393 responses
, SCTs
, trustedLogs
,
1394 verifyTime
, accessGroups
,
1395 SecTrustServerEvaluateCompleted
, userData
);
1396 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1400 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1401 SecTrustResultType
SecTrustServerEvaluate(CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, CFArrayRef
*pdetails
, CFDictionaryRef
*pinfo
, SecCertificatePathRef
*pchain
, CFErrorRef
*perror
) {
1402 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1403 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1404 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1406 if (tr
== kSecTrustResultInvalid
) {
1409 CFRetainSafe(error
);
1413 *pdetails
= details
;
1414 CFRetainSafe(details
);
1422 CFRetainSafe(chain
);
1425 dispatch_semaphore_signal(done
);
1427 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);