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 /* We can make this async or run this on a queue now easily. */
281 CFErrorRef localError
= NULL
;
282 if (!_SecItemCopyMatching(query
, msource
->accessGroups
, &results
, &localError
)) {
283 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
284 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
286 CFRelease(localError
);
289 CFTypeRef certs
= SecItemCertificateSourceResultsPost(results
);
290 CFReleaseSafe(results
);
291 callback(context
, certs
);
292 CFReleaseSafe(certs
);
296 static bool SecItemCertificateSourceContains(SecCertificateSourceRef source
,
297 SecCertificateRef certificate
) {
298 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
299 /* Lookup a certificate by issuer and serial number. */
300 CFDataRef normalizedSubject
=
301 SecCertificateGetNormalizedSubjectContent(certificate
);
302 CFDataRef serialNumber
=
303 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
304 SecCertificateCopySerialNumber(certificate
, NULL
);
306 SecCertificateCopySerialNumber(certificate
);
308 const void *keys
[] = {
315 kSecClassCertificate
,
320 CFDictionaryRef query
= CFDictionaryCreate(NULL
, keys
, values
, 5,
322 CFErrorRef localError
= NULL
;
323 CFTypeRef results
= NULL
;
324 bool ok
= _SecItemCopyMatching(query
, msource
->accessGroups
, &results
, &localError
);
326 CFRelease(serialNumber
);
327 CFReleaseSafe(results
);
329 if (CFErrorGetCode(localError
) != errSecItemNotFound
) {
330 secdebug("trust", "_SecItemCopyMatching: %@", localError
);
332 CFRelease(localError
);
338 static SecCertificateSourceRef
SecItemCertificateSourceCreate(CFArrayRef accessGroups
) {
339 SecItemCertificateSourceRef result
= (SecItemCertificateSourceRef
)malloc(sizeof(*result
));
340 result
->base
.copyParents
= SecItemCertificateSourceCopyParents
;
341 result
->base
.contains
= SecItemCertificateSourceContains
;
342 result
->accessGroups
= accessGroups
;
343 CFRetainSafe(accessGroups
);
344 return (SecCertificateSourceRef
)result
;
347 static void SecItemCertificateSourceDestroy(SecCertificateSourceRef source
) {
348 SecItemCertificateSourceRef msource
= (SecItemCertificateSourceRef
)source
;
349 CFReleaseSafe(msource
->accessGroups
);
354 // MARK: SecSystemAnchorSource
355 /********************************************************
356 *********** SecSystemAnchorSource object ************
357 ********************************************************/
359 static bool SecSystemAnchorSourceCopyParents(
360 SecCertificateSourceRef source
, SecCertificateRef certificate
,
361 void *context
, SecCertificateSourceParents callback
) {
362 //#ifndef SECITEM_SHIM_OSX
363 CFArrayRef parents
= NULL
;
364 CFArrayRef anchors
= NULL
;
365 SecOTAPKIRef otapkiref
= NULL
;
367 CFDataRef nic
= SecCertificateGetNormalizedIssuerContent(certificate
);
368 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
369 It does not matter since we would be returning the wrong anchors */
370 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
372 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
373 require_quiet(otapkiref
, errOut
);
374 anchors
= subject_to_anchors(nic
);
375 require_quiet(anchors
, errOut
);
376 parents
= CopyCertsFromIndices(anchors
);
379 callback(context
, parents
);
380 CFReleaseSafe(parents
);
381 CFReleaseSafe(otapkiref
);
382 //#endif // SECITEM_SHIM_OSX
386 /* Quick thought: we can eliminate this method if we search anchor sources
387 before all others and we remember if we got a cert from an anchorsource. */
388 static bool SecSystemAnchorSourceContains(SecCertificateSourceRef source
,
389 SecCertificateRef certificate
) {
391 //#ifndef SECITEM_SHIM_OSX
392 CFArrayRef anchors
= NULL
;
393 SecOTAPKIRef otapkiref
= NULL
;
394 CFArrayRef cert_datas
= NULL
;
396 CFDataRef nic
= SecCertificateGetNormalizedSubjectContent(certificate
);
397 /* 64 bits cast: the worst that can happen here is we truncate the length and match an actual anchor.
398 It does not matter since we would be returning the wrong anchors */
399 assert((unsigned long)CFDataGetLength(nic
)<UINT_MAX
); /* Debug check. correct as long as CFIndex is signed long */
401 otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
402 require_quiet(otapkiref
, errOut
);
403 anchors
= subject_to_anchors(nic
);
404 require_quiet(anchors
, errOut
);
405 cert_datas
= CopyCertDataFromIndices(anchors
);
406 require_quiet(cert_datas
, errOut
);
408 CFIndex cert_length
= SecCertificateGetLength(certificate
);
409 const UInt8
*cert_data_ptr
= SecCertificateGetBytePtr(certificate
);
411 CFIndex num_cert_datas
= CFArrayGetCount(cert_datas
);
412 for (CFIndex idx
= 0; idx
< num_cert_datas
; idx
++)
414 CFDataRef cert_data
= (CFDataRef
)CFArrayGetValueAtIndex(cert_datas
, idx
);
416 if (NULL
!= cert_data
)
418 if (CFGetTypeID(cert_data
) == CFDataGetTypeID())
420 CFIndex aCert_Length
= CFDataGetLength(cert_data
);
421 const UInt8
* aCert_Data_Ptr
= CFDataGetBytePtr(cert_data
);
423 if (aCert_Length
== cert_length
)
425 if (!memcmp(cert_data_ptr
, aCert_Data_Ptr
, cert_length
))
436 CFReleaseSafe(cert_datas
);
437 CFReleaseSafe(otapkiref
);
438 //#endif // SECITEM_SHIM_OSX
444 struct SecCertificateSource kSecSystemAnchorSource
= {
445 SecSystemAnchorSourceCopyParents
,
446 SecSystemAnchorSourceContains
450 // MARK: SecUserAnchorSource
451 /********************************************************
452 *********** SecUserAnchorSource object ************
453 ********************************************************/
454 static bool SecUserAnchorSourceCopyParents(
455 SecCertificateSourceRef source
, SecCertificateRef certificate
,
456 void *context
, SecCertificateSourceParents callback
) {
457 CFArrayRef parents
= SecTrustStoreCopyParents(
458 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
, NULL
);
459 callback(context
, parents
);
460 CFReleaseSafe(parents
);
464 static bool SecUserAnchorSourceContains(SecCertificateSourceRef source
,
465 SecCertificateRef certificate
) {
466 return SecTrustStoreContains(
467 SecTrustStoreForDomain(kSecTrustStoreDomainUser
), certificate
);
470 struct SecCertificateSource kSecUserAnchorSource
= {
471 SecUserAnchorSourceCopyParents
,
472 SecUserAnchorSourceContains
476 // MARK: SecMemoryCertificateSource
477 /********************************************************
478 *********** SecMemoryCertificateSource object ************
479 ********************************************************/
480 struct SecMemoryCertificateSource
{
481 struct SecCertificateSource base
;
482 CFMutableSetRef certificates
;
483 CFMutableDictionaryRef subjects
;
485 typedef struct SecMemoryCertificateSource
*SecMemoryCertificateSourceRef
;
487 static bool SecMemoryCertificateSourceCopyParents(
488 SecCertificateSourceRef source
, SecCertificateRef certificate
,
489 void *context
, SecCertificateSourceParents callback
) {
490 SecMemoryCertificateSourceRef msource
=
491 (SecMemoryCertificateSourceRef
)source
;
492 CFDataRef normalizedIssuer
=
493 SecCertificateGetNormalizedIssuerContent(certificate
);
494 CFArrayRef parents
= (normalizedIssuer
) ? CFDictionaryGetValue(msource
->subjects
,
495 normalizedIssuer
) : NULL
;
496 /* FIXME filter parents by subjectID if certificate has an
497 authorityKeyIdentifier. */
498 secdebug("trust", "%@ parents -> %@", certificate
, parents
);
499 callback(context
, parents
);
503 static bool SecMemoryCertificateSourceContains(SecCertificateSourceRef source
,
504 SecCertificateRef certificate
) {
505 SecMemoryCertificateSourceRef msource
=
506 (SecMemoryCertificateSourceRef
)source
;
507 return CFSetContainsValue(msource
->certificates
, certificate
);
510 static void dictAddValueToArrayForKey(CFMutableDictionaryRef dict
,
511 const void *key
, const void *value
) {
515 CFMutableArrayRef values
=
516 (CFMutableArrayRef
)CFDictionaryGetValue(dict
, key
);
518 values
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
519 &kCFTypeArrayCallBacks
);
520 CFDictionaryAddValue(dict
, key
, values
);
525 CFArrayAppendValue(values
, value
);
528 static void SecMemoryCertificateSourceApplierFunction(const void *value
,
530 SecMemoryCertificateSourceRef msource
=
531 (SecMemoryCertificateSourceRef
)context
;
532 SecCertificateRef certificate
= (SecCertificateRef
)value
;
534 /* CFSet's API has no way to combine these 2 operations into 1 sadly. */
535 if (CFSetContainsValue(msource
->certificates
, certificate
))
537 CFSetAddValue(msource
->certificates
, certificate
);
539 CFDataRef key
= SecCertificateGetNormalizedSubjectContent(certificate
);
540 dictAddValueToArrayForKey(msource
->subjects
, key
, value
);
543 static SecCertificateSourceRef
SecMemoryCertificateSourceCreate(
544 CFArrayRef certificates
) {
545 SecMemoryCertificateSourceRef result
= (SecMemoryCertificateSourceRef
)
546 malloc(sizeof(*result
));
547 result
->base
.copyParents
= SecMemoryCertificateSourceCopyParents
;
548 result
->base
.contains
= SecMemoryCertificateSourceContains
;
549 CFIndex count
= CFArrayGetCount(certificates
);
550 result
->certificates
= CFSetCreateMutable(kCFAllocatorDefault
, count
,
551 &kCFTypeSetCallBacks
);
552 result
->subjects
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
553 count
, &kCFTypeDictionaryKeyCallBacks
,
554 &kCFTypeDictionaryValueCallBacks
);
555 CFRange range
= { 0, count
};
556 CFArrayApplyFunction(certificates
, range
,
557 SecMemoryCertificateSourceApplierFunction
, result
);
559 return (SecCertificateSourceRef
)result
;
562 static void SecMemoryCertificateSourceDestroy(
563 SecCertificateSourceRef source
) {
564 SecMemoryCertificateSourceRef msource
=
565 (SecMemoryCertificateSourceRef
)source
;
566 CFRelease(msource
->certificates
);
567 CFRelease(msource
->subjects
);
572 // MARK: SecCAIssuerCertificateSource
573 /********************************************************
574 ********* SecCAIssuerCertificateSource object **********
575 ********************************************************/
576 static bool SecCAIssuerCertificateSourceCopyParents(
577 SecCertificateSourceRef source
, SecCertificateRef certificate
,
578 void *context
, SecCertificateSourceParents callback
) {
579 return SecCAIssuerCopyParents(certificate
, SecPathBuilderGetQueue((SecPathBuilderRef
)context
), context
, callback
);
582 static bool SecCAIssuerCertificateSourceContains(
583 SecCertificateSourceRef source
, SecCertificateRef certificate
) {
587 struct SecCertificateSource kSecCAIssuerSource
= {
588 SecCAIssuerCertificateSourceCopyParents
,
589 SecCAIssuerCertificateSourceContains
593 // MARK: SecPathBuilder
594 /********************************************************
595 *************** SecPathBuilder object ******************
596 ********************************************************/
597 struct SecPathBuilder
{
598 dispatch_queue_t queue
;
599 CFDataRef clientAuditToken
;
600 SecCertificateSourceRef certificateSource
;
601 SecCertificateSourceRef itemCertificateSource
;
602 SecCertificateSourceRef anchorSource
;
603 CFMutableArrayRef anchorSources
;
604 CFIndex nextParentSource
;
605 CFMutableArrayRef parentSources
;
606 CFArrayRef ocspResponses
; // Stapled OCSP responses
607 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
608 CFArrayRef trustedLogs
; // Trusted CT logs
610 /* Hashed set of all paths we've constructed so far, used to prevent
611 re-considering a path that was already constructed once before.
612 Note that this is the only container in which certificatePath
613 objects are retained.
614 Every certificatePath being considered is always in allPaths and in at
615 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
616 all of which don't retain their values. */
617 CFMutableSetRef allPaths
;
619 /* No trusted anchor, satisfies the linking to intermediates for all
620 policies (unless considerRejected is true). */
621 CFMutableArrayRef partialPaths
;
622 /* No trusted anchor, does not satisfy linking to intermediates for all
624 CFMutableArrayRef rejectedPaths
;
625 /* Trusted anchor, satisfies the policies so far. */
626 CFMutableArrayRef candidatePaths
;
630 CFArrayRef leafDetails
;
634 bool considerRejected
;
635 bool considerPartials
;
636 bool canAccessNetwork
;
638 struct OpaqueSecPVC path
;
639 SecCertificatePathRef bestPath
;
643 bool (*state
)(SecPathBuilderRef
);
644 SecPathBuilderCompleted completed
;
648 /* State functions. Return false if a async job was scheduled, return
649 true to execute the next state. */
650 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
651 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
652 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
653 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
654 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
656 /* Forward declarations. */
657 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
658 SecCertificateRef certificate
);
660 /* IDEA: policies could be made cabable of replacing incoming anchors and
661 anchorsOnly argument values. For example some policies require the
662 Apple Inc. CA and not any other anchor. This can be done in
663 SecPathBuilderLeafCertificateChecks since this only runs once. */
664 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
665 SecCertificatePathRef path
) {
666 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
667 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
668 &kCFTypeDictionaryValueCallBacks
);
669 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
670 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
671 CFRelease(certDetail
);
672 SecPVCRef pvc
= &builder
->path
;
673 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
674 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
677 static void SecPathBuilderInit(SecPathBuilderRef builder
,
678 CFDataRef clientAuditToken
, CFArrayRef certificates
,
679 CFArrayRef anchors
, bool anchorsOnly
,
680 CFArrayRef policies
, CFArrayRef ocspResponses
,
681 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
682 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
683 SecPathBuilderCompleted completed
, const void *context
) {
684 secdebug("alloc", "%p", builder
);
685 CFAllocatorRef allocator
= kCFAllocatorDefault
;
687 builder
->clientAuditToken
= (CFDataRef
)
688 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
689 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
691 builder
->nextParentSource
= 1;
692 builder
->considerPartials
= false;
693 builder
->canAccessNetwork
= true;
695 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
696 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
697 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
698 &kCFTypeSetCallBacks
);
700 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
701 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
702 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
703 builder
->partialIX
= 0;
705 /* Init the policy verification context. */
706 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
707 builder
->bestPath
= NULL
;
708 builder
->bestPathIsEV
= false;
709 builder
->rejectScore
= 0;
711 /* Let's create all the certificate sources we might want to use. */
712 builder
->certificateSource
=
713 SecMemoryCertificateSourceCreate(certificates
);
715 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
717 builder
->anchorSource
= NULL
;
719 /* We always search certificateSource for parents since it includes the
720 leaf itself and it might be self signed. */
721 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
722 if (builder
->anchorSource
) {
723 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
725 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
726 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
728 /* Add the system and user anchor certificate db to the search list
729 if we don't explicitly trust them. */
730 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
731 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
733 /* Only add the system and user anchor certificate db to the
734 anchorSources if we are supposed to trust them. */
735 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
736 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
738 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
740 /* Now let's get the leaf cert and turn it into a path. */
741 SecCertificateRef leaf
=
742 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
743 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
);
744 CFSetAddValue(builder
->allPaths
, path
);
745 CFArrayAppendValue(builder
->partialPaths
, path
);
746 if (SecPathBuilderIsAnchor(builder
, leaf
)) {
747 SecCertificatePathSetIsAnchored(path
);
748 CFArrayAppendValue(builder
->candidatePaths
, path
);
750 SecPathBuilderLeafCertificateChecks(builder
, path
);
753 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
754 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
757 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
759 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
760 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
761 CFReleaseSafe(otapkiref
);
764 builder
->activations
= 0;
765 builder
->state
= SecPathBuilderGetNext
;
766 builder
->completed
= completed
;
767 builder
->context
= context
;
770 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
771 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
772 CFArrayRef policies
, CFArrayRef ocspResponses
,
773 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
774 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
775 SecPathBuilderCompleted completed
, const void *context
) {
776 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
777 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
778 anchors
, anchorsOnly
, policies
, ocspResponses
,
779 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
780 accessGroups
, completed
, context
);
784 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
785 secdebug("alloc", "%p", builder
);
786 dispatch_release_null(builder
->queue
);
787 if (builder
->anchorSource
)
788 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
789 if (builder
->certificateSource
)
790 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
791 if (builder
->itemCertificateSource
)
792 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
793 CFReleaseSafe(builder
->clientAuditToken
);
794 CFReleaseSafe(builder
->anchorSources
);
795 CFReleaseSafe(builder
->parentSources
);
796 CFReleaseSafe(builder
->allPaths
);
797 CFReleaseSafe(builder
->partialPaths
);
798 CFReleaseSafe(builder
->rejectedPaths
);
799 CFReleaseSafe(builder
->candidatePaths
);
800 CFReleaseSafe(builder
->leafDetails
);
802 SecPVCDelete(&builder
->path
);
805 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
806 return builder
->canAccessNetwork
;
809 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
810 if (builder
->canAccessNetwork
!= allow
) {
811 builder
->canAccessNetwork
= allow
;
813 secdebug("http", "network access re-enabled by policy");
814 /* re-enabling network_access re-adds kSecCAIssuerSource as
816 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
818 secdebug("http", "network access disabled by policy");
819 /* disabling network_access removes kSecCAIssuerSource from
820 the list of parent sources. */
821 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
822 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
823 &kSecCAIssuerSource
);
825 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
830 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
832 return CFRetainSafe(builder
->ocspResponses
);
835 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
837 return CFRetainSafe(builder
->signedCertificateTimestamps
);
840 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
842 return CFRetainSafe(builder
->trustedLogs
);
845 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
846 SecCertificateRef certificate
) {
847 /* We always look through all anchor sources. */
848 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
850 for (ix
= 0; ix
< count
; ++ix
) {
851 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
852 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
853 if (SecCertificateSourceContains(source
, certificate
)) {
860 /* Return false if path is not a partial, if path was a valid candidate it
861 will have been added to builder->candidatePaths, if path was rejected
862 by the parent certificate checks (because it's expired or some other
863 static chaining check failed) it will have been added to rejectedPaths.
864 Return true path if path is a partial. */
865 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
866 SecCertificatePathRef path
) {
867 SecPVCRef pvc
= &builder
->path
;
868 SecPVCSetPath(pvc
, path
, NULL
);
870 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
871 SecPVCGetCertificateCount(pvc
) - 1)) {
872 secdebug("trust", "Found rejected path %@", path
);
873 CFArrayAppendValue(builder
->rejectedPaths
, path
);
877 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
878 /* Candidate paths with failed signatures are discarded. */
879 if (vstatus
== kSecPathVerifyFailed
) {
880 secdebug("trust", "Verify failed for path %@", path
);
884 if (vstatus
== kSecPathVerifySuccess
) {
885 /* The signature chain verified sucessfully, now let's find
886 out if we have an anchor for path. */
887 if (SecCertificatePathIsAnchored(path
)) {
888 secdebug("trust", "Adding candidate %@", path
);
889 CFArrayAppendValue(builder
->candidatePaths
, path
);
897 /* Given the builder, a partial chain partial and the parents array, construct
898 a SecCertificatePath for each parent. After discarding previously
899 considered paths and paths with cycles, sort out which array each path
900 should go in, if any. */
901 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
902 SecCertificatePathRef partial
, CFArrayRef parents
) {
903 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
904 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
906 bool is_anchor
= SecCertificatePathGetNextSourceIndex(partial
) <=
907 CFArrayGetCount(builder
->anchorSources
);
908 secdebug("trust", "found %" PRIdCFIndex
" candidate %s", num_parents
,
909 (is_anchor
? "anchors" : "parents"));
910 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
911 SecCertificateRef parent
= (SecCertificateRef
)
912 CFArrayGetValueAtIndex(parents
, parentIX
);
913 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
915 if (ixOfParent
!= kCFNotFound
) {
916 /* partial already contains parent. Let's not add the same
917 certificate again. */
918 if (ixOfParent
== rootIX
) {
919 /* parent is equal to the root of the partial, so partial
920 looks to be self issued. */
921 SecCertificatePathSetSelfIssued(partial
);
926 /* FIXME Add more sanity checks to see that parent really can be
927 a parent of partial_root. subjectKeyID == authorityKeyID,
928 signature algorithm matches public key algorithm, etc. */
929 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
);
932 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
933 CFSetAddValue(builder
->allPaths
, path
);
935 SecCertificatePathSetIsAnchored(path
);
936 if (SecPathBuilderIsPartial(builder
, path
)) {
937 /* Insert path right at the current position since it's a new
939 CFArrayInsertValueAtIndex(builder
->partialPaths
,
940 ++builder
->partialIX
, path
);
941 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
942 parentIX
+ 1, num_parents
, path
);
944 secdebug("trust", "found new path %@", path
);
950 /* Callback for the SecPathBuilderGetNext() functions call to
951 SecCertificateSourceCopyParents(). */
952 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
953 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
954 SecCertificatePathRef partial
= (SecCertificatePathRef
)
955 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
956 secdebug("async", "%@ parents %@", partial
, parents
);
957 SecPathBuilderProcessParents(builder
, partial
, parents
);
959 builder
->state
= SecPathBuilderGetNext
;
960 SecPathBuilderStep(builder
);
963 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
964 /* If we have any candidates left to go return those first. */
965 if (CFArrayGetCount(builder
->candidatePaths
)) {
966 SecCertificatePathRef path
= (SecCertificatePathRef
)
967 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
968 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
969 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
971 SecPVCSetPath(&builder
->path
, path
, NULL
);
972 builder
->state
= SecPathBuilderValidatePath
;
976 /* If we are considering rejected chains we check each rejected path
977 with SecPathBuilderIsPartial() which checks the signature chain and
978 either drops the path if it's not properly signed, add it as a
979 candidate if it has a trusted anchor, or adds it as a partial
980 to be considered once we finish considering all the rejects. */
981 if (builder
->considerRejected
) {
982 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
985 SecCertificatePathRef path
= (SecCertificatePathRef
)
986 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
987 if (SecPathBuilderIsPartial(builder
, path
)) {
988 CFArrayInsertValueAtIndex(builder
->partialPaths
,
989 ++builder
->partialIX
, path
);
991 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
993 /* Keep going until we have moved all rejected partials into
994 the regular partials or candidates array. */
999 /* If builder->partialIX is < 0 we have considered all partial chains
1000 this block must ensure partialIX >= 0 if execution continues past
1002 if (builder
->partialIX
< 0) {
1003 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1004 if (builder
->nextParentSource
< num_sources
) {
1005 builder
->nextParentSource
++;
1006 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1007 builder
->nextParentSource
, num_sources
);
1009 /* We've run out of new sources to consider so let's look at
1010 rejected chains and after that even consider partials
1012 FIXME we might not want to consider partial paths that
1013 are subsets of other partial paths, or not consider them
1014 at all if we already have an anchored reject. */
1015 if (!builder
->considerRejected
) {
1016 builder
->considerRejected
= true;
1017 secdebug("trust", "considering rejected paths");
1018 } else if (!builder
->considerPartials
) {
1019 builder
->considerPartials
= true;
1020 secdebug("trust", "considering partials");
1022 /* We're all out of options, so we can't produce any more
1023 candidates. Let's calculate details and return the best
1025 builder
->state
= SecPathBuilderComputeDetails
;
1029 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1030 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1034 /* We know builder->partialIX >= 0 if we get here. */
1035 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1036 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1037 /* Don't try to extend partials anymore once we are in the considerPartials
1038 state, since at this point every partial has been extended with every
1039 possible parentSource already. */
1040 if (builder
->considerPartials
) {
1041 --builder
->partialIX
;
1042 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1043 builder
->state
= SecPathBuilderValidatePath
;
1047 /* Attempt to extend this partial path with another certificate. This
1048 should give us a list of potential parents to consider. */
1049 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1050 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1053 /* Attempt to extend partial, leaving all possible extended versions
1054 of partial in builder->extendedPaths. */
1055 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1056 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1057 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1058 SecCertificateSourceRef source
;
1059 if (sourceIX
< num_anchor_sources
) {
1060 source
= (SecCertificateSourceRef
)
1061 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1062 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1063 num_anchor_sources
);
1065 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1066 source
= (SecCertificateSourceRef
)
1067 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1068 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1069 builder
->nextParentSource
);
1071 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1072 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1073 return SecCertificateSourceCopyParents(source
, root
,
1074 builder
, SecPathBuilderExtendPaths
);
1076 --builder
->partialIX
;
1082 /* One or more of the policies did not accept the candidate path. */
1083 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1085 SecPVCRef pvc
= &builder
->path
;
1087 builder
->state
= SecPathBuilderGetNext
;
1089 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1090 /* We never replace an ev reject with a non ev reject. */
1094 CFIndex rejectScore
= builder
->rejectScore
;
1095 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1096 SecPVCGetVerifyTime(&builder
->path
));
1098 /* The current chain is valid for EV, but revocation checking failed. We
1099 replace any previously accepted or rejected non EV chains with the
1101 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1107 /* Since this means we found a valid ev chain that was revoked,
1108 we might want to switch directly to the
1109 SecPathBuilderComputeDetails state here if we think further
1110 searching for new chains is pointless. For now we'll keep
1111 going, since we could accept an alternate EV certification
1112 path that isn't revoked. */
1113 builder
->state
= SecPathBuilderComputeDetails
;
1117 /* Do this last so that changes to rejectScore above will take affect. */
1118 if (!builder
->bestPath
|| score
> rejectScore
) {
1119 if (builder
->bestPath
) {
1121 "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex
" %@",
1122 (builder
->bestPathIsEV
? "" : "non "),
1123 (builder
->rejectScore
== INTPTR_MAX
? "accept" : "reject"),
1124 builder
->rejectScore
,
1125 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1127 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" %@",
1128 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1131 builder
->rejectScore
= score
;
1132 builder
->bestPath
= pvc
->path
;
1133 builder
->bestPathIsEV
= pvc
->is_ev
;
1135 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1136 (pvc
->is_ev
? "" : "non "), score
, rejectScore
, builder
->path
.path
);
1140 /* All policies accepted the candidate path. */
1141 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1143 SecPVCRef pvc
= &builder
->path
;
1144 if (pvc
->is_ev
|| !builder
->bestPathIsEV
) {
1145 secdebug("accept", "replacing %sev accept with %sev %@",
1146 (builder
->bestPathIsEV
? "" : "non "),
1147 (pvc
->is_ev
? "" : "non "), builder
->path
.path
);
1148 builder
->rejectScore
= INTPTR_MAX
; /* CFIndex is signed long which is INTPTR_T */
1149 builder
->bestPathIsEV
= pvc
->is_ev
;
1150 builder
->bestPath
= pvc
->path
;
1153 /* If we found the best accept we can we want to switch directly to the
1154 SecPathBuilderComputeDetails state here, since we're done. */
1155 if (pvc
->is_ev
|| !pvc
->optionally_ev
)
1156 builder
->state
= SecPathBuilderComputeDetails
;
1158 builder
->state
= SecPathBuilderGetNext
;
1161 /* Return true iff a given path satisfies all the specified policies at
1163 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1164 SecPVCRef pvc
= &builder
->path
;
1166 if (builder
->considerRejected
) {
1167 SecPathBuilderReject(builder
);
1171 builder
->state
= SecPathBuilderDidValidatePath
;
1172 return SecPVCPathChecks(pvc
);
1175 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1176 SecPVCRef pvc
= &builder
->path
;
1178 SecPathBuilderAccept(builder
);
1180 SecPathBuilderReject(builder
);
1182 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1186 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1188 SecPVCRef pvc
= &builder
->path
;
1190 if (!builder
->caller_wants_details
) {
1191 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1192 pvc
->result
= builder
->rejectScore
== INTPTR_MAX
;
1193 builder
->state
= SecPathBuilderReportResult
;
1197 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1198 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1199 pathLength
, builder
->leafDetails
);
1200 CFRetainSafe(details
);
1201 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1202 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1203 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1204 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1205 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1206 for (ix
= 1; ix
< pathLength
; ++ix
) {
1207 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1208 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1209 &kCFTypeDictionaryValueCallBacks
);
1210 CFArrayAppendValue(details
, certDetail
);
1211 CFRelease(certDetail
);
1212 SecPVCParentCertificateChecks(pvc
, ix
);
1213 SecPVCGrayListedKeyChecks(pvc
, ix
);
1214 SecPVCBlackListedKeyChecks(pvc
, ix
);
1216 builder
->state
= SecPathBuilderReportResult
;
1217 bool completed
= SecPVCPathChecks(pvc
);
1219 /* Reject the certificate if it was accepted before but we failed it now. */
1220 if (builder
->rejectScore
== INTPTR_MAX
&& !pvc
->result
) {
1221 builder
->rejectScore
= 0;
1224 CFReleaseSafe(details
);
1229 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1230 SecPVCRef pvc
= &builder
->path
;
1231 bool haveRevocationResponse
= false;
1232 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1233 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1234 kCFBooleanTrue
); /* iOS key */
1235 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1236 kCFBooleanTrue
); /* unified API key */
1237 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1238 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1239 if (leafCompanyName
) {
1240 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1241 leafCompanyName
); /* iOS key */
1242 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1243 leafCompanyName
); /* unified API key */
1244 CFRelease(leafCompanyName
);
1247 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1248 if (nextUpdate
== 0) {
1249 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1250 kCFBooleanFalse
); /* iOS key */
1251 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1252 kCFBooleanFalse
); /* unified API key */
1254 haveRevocationResponse
= true;
1255 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1256 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1257 validUntil
); /* iOS key */
1258 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1259 validUntil
); /* unified API key */
1260 CFRelease(validUntil
);
1261 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1262 kCFBooleanTrue
); /* iOS key */
1263 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1264 kCFBooleanTrue
); /* unified API key */
1269 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1270 builder
->rejectScore
= 0;
1271 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1272 0, kCFBooleanFalse
, true);
1275 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1276 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1281 /* This will trigger the outer step function to call the completion
1283 builder
->state
= NULL
;
1287 /* @function SecPathBuilderStep
1288 @summary This is the core of the async engine.
1289 @description Return false iff job is complete, true if a network request
1291 builder->state is a function pointer which is to be invoked.
1292 If you call this function from within a builder->state invocation it
1293 immediately returns true.
1294 Otherwise the following steps are repeated endlessly (unless a step returns)
1295 builder->state is invoked. If it returns true and builder->state is still
1296 non NULL this proccess is repeated.
1297 If a state returns false, SecPathBuilder will return true
1298 if builder->state is non NULL.
1299 If builder->state is NULL then regardless of what the state function returns
1300 the completion callback will be invoked and the builder will be deallocated.
1302 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1303 if (builder
->activations
) {
1304 secdebug("async", "activations: %lu returning true",
1305 builder
->activations
);
1309 secdebug("async", "activations: %lu", builder
->activations
);
1310 builder
->activations
++;
1311 while (builder
->state
&& builder
->state(builder
));
1312 --builder
->activations
;
1314 if (builder
->state
) {
1315 secdebug("async", "waiting for async reply, exiting");
1316 /* A state returned false, it's waiting for network traffic. Let's
1321 if (builder
->activations
) {
1322 /* There is still at least one other running instance of this builder
1323 somewhere on the stack, we let that instance take care of sending
1324 the client a response. */
1328 SecTrustResultType result
= (builder
->rejectScore
== INTPTR_MAX
1329 ? kSecTrustResultUnspecified
: kSecTrustResultRecoverableTrustFailure
);
1331 secdebug("trust", "completed: %@ details: %@ result: %d",
1332 builder
->bestPath
, builder
->path
.details
, result
);
1334 if (builder
->completed
) {
1335 builder
->completed(builder
->context
, builder
->bestPath
,
1336 builder
->path
.details
, builder
->path
.info
, result
);
1339 /* Finally, destroy the builder and free it. */
1340 SecPathBuilderDestroy(builder
);
1346 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1347 return (builder
) ? builder
->queue
: NULL
;
1350 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1351 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1355 // MARK: SecTrustServer
1356 /********************************************************
1357 ****************** SecTrustServer **********************
1358 ********************************************************/
1360 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1363 SecTrustServerEvaluateCompleted(const void *userData
,
1364 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1365 SecTrustResultType result
) {
1366 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1367 evaluated(result
, details
, info
, chain
, NULL
);
1368 Block_release(evaluated
);
1372 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
)) {
1373 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1374 /* Call the actual evaluator function. */
1375 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1376 certificates
, anchors
,
1377 anchorsOnly
, policies
,
1378 responses
, SCTs
, trustedLogs
,
1379 verifyTime
, accessGroups
,
1380 SecTrustServerEvaluateCompleted
, userData
);
1381 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1385 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1386 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
) {
1387 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1388 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1389 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1391 if (tr
== kSecTrustResultInvalid
) {
1394 CFRetainSafe(error
);
1398 *pdetails
= details
;
1399 CFRetainSafe(details
);
1407 CFRetainSafe(chain
);
1410 dispatch_semaphore_signal(done
);
1412 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);