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;
709 builder
->canAccessNetwork
= true;
711 builder
->canAccessNetwork
= false;
714 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
715 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
716 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
717 &kCFTypeSetCallBacks
);
719 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
720 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
721 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
722 builder
->partialIX
= 0;
724 /* Init the policy verification context. */
725 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
726 builder
->bestPath
= NULL
;
727 builder
->bestPathIsEV
= false;
728 builder
->rejectScore
= 0;
730 /* Let's create all the certificate sources we might want to use. */
731 builder
->certificateSource
=
732 SecMemoryCertificateSourceCreate(certificates
);
734 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
736 builder
->anchorSource
= NULL
;
738 /* We always search certificateSource for parents since it includes the
739 leaf itself and it might be self signed. */
740 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
741 if (builder
->anchorSource
) {
742 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
744 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
745 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
747 /* Add the system and user anchor certificate db to the search list
748 if we don't explicitly trust them. */
749 CFArrayAppendValue(builder
->parentSources
, &kSecSystemAnchorSource
);
750 CFArrayAppendValue(builder
->parentSources
, &kSecUserAnchorSource
);
752 /* Only add the system and user anchor certificate db to the
753 anchorSources if we are supposed to trust them. */
754 CFArrayAppendValue(builder
->anchorSources
, &kSecSystemAnchorSource
);
755 CFArrayAppendValue(builder
->anchorSources
, &kSecUserAnchorSource
);
757 if (builder
->canAccessNetwork
) {
758 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
761 /* Now let's get the leaf cert and turn it into a path. */
762 SecCertificateRef leaf
=
763 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
764 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
);
765 CFSetAddValue(builder
->allPaths
, path
);
766 CFArrayAppendValue(builder
->partialPaths
, path
);
767 if (SecPathBuilderIsAnchor(builder
, leaf
)) {
768 SecCertificatePathSetIsAnchored(path
);
769 CFArrayAppendValue(builder
->candidatePaths
, path
);
771 SecPathBuilderLeafCertificateChecks(builder
, path
);
774 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
775 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
778 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
780 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
781 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
782 CFReleaseSafe(otapkiref
);
785 builder
->activations
= 0;
786 builder
->state
= SecPathBuilderGetNext
;
787 builder
->completed
= completed
;
788 builder
->context
= context
;
791 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
792 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
793 CFArrayRef policies
, CFArrayRef ocspResponses
,
794 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
795 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
796 SecPathBuilderCompleted completed
, const void *context
) {
797 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
798 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
799 anchors
, anchorsOnly
, policies
, ocspResponses
,
800 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
801 accessGroups
, completed
, context
);
805 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
806 secdebug("alloc", "%p", builder
);
807 dispatch_release_null(builder
->queue
);
808 if (builder
->anchorSource
)
809 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
810 if (builder
->certificateSource
)
811 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
812 if (builder
->itemCertificateSource
)
813 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
814 CFReleaseSafe(builder
->clientAuditToken
);
815 CFReleaseSafe(builder
->anchorSources
);
816 CFReleaseSafe(builder
->parentSources
);
817 CFReleaseSafe(builder
->allPaths
);
818 CFReleaseSafe(builder
->partialPaths
);
819 CFReleaseSafe(builder
->rejectedPaths
);
820 CFReleaseSafe(builder
->candidatePaths
);
821 CFReleaseSafe(builder
->leafDetails
);
822 CFReleaseSafe(builder
->ocspResponses
);
823 CFReleaseSafe(builder
->signedCertificateTimestamps
);
824 CFReleaseSafe(builder
->trustedLogs
);
826 SecPVCDelete(&builder
->path
);
829 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
830 return builder
->canAccessNetwork
;
833 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
834 if (builder
->canAccessNetwork
!= allow
) {
835 builder
->canAccessNetwork
= allow
;
838 secdebug("http", "network access re-enabled by policy");
839 /* re-enabling network_access re-adds kSecCAIssuerSource as
841 CFArrayAppendValue(builder
->parentSources
, &kSecCAIssuerSource
);
843 secdebug("http", "network access not allowed on WatchOS");
844 builder
->canAccessNetwork
= false;
847 secdebug("http", "network access disabled by policy");
848 /* disabling network_access removes kSecCAIssuerSource from
849 the list of parent sources. */
850 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
851 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
852 &kSecCAIssuerSource
);
854 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
859 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
861 return CFRetainSafe(builder
->ocspResponses
);
864 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
866 return CFRetainSafe(builder
->signedCertificateTimestamps
);
869 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
871 return CFRetainSafe(builder
->trustedLogs
);
874 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
875 SecCertificateRef certificate
) {
876 /* We always look through all anchor sources. */
877 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
879 for (ix
= 0; ix
< count
; ++ix
) {
880 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
881 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
882 if (SecCertificateSourceContains(source
, certificate
)) {
889 /* Return false if path is not a partial, if path was a valid candidate it
890 will have been added to builder->candidatePaths, if path was rejected
891 by the parent certificate checks (because it's expired or some other
892 static chaining check failed) it will have been added to rejectedPaths.
893 Return true path if path is a partial. */
894 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
895 SecCertificatePathRef path
) {
896 SecPVCRef pvc
= &builder
->path
;
897 SecPVCSetPath(pvc
, path
, NULL
);
899 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
900 SecPVCGetCertificateCount(pvc
) - 1)) {
901 secdebug("trust", "Found rejected path %@", path
);
902 CFArrayAppendValue(builder
->rejectedPaths
, path
);
906 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
907 /* Candidate paths with failed signatures are discarded. */
908 if (vstatus
== kSecPathVerifyFailed
) {
909 secdebug("trust", "Verify failed for path %@", path
);
913 if (vstatus
== kSecPathVerifySuccess
) {
914 /* The signature chain verified sucessfully, now let's find
915 out if we have an anchor for path. */
916 if (SecCertificatePathIsAnchored(path
)) {
917 secdebug("trust", "Adding candidate %@", path
);
918 CFArrayAppendValue(builder
->candidatePaths
, path
);
926 /* Given the builder, a partial chain partial and the parents array, construct
927 a SecCertificatePath for each parent. After discarding previously
928 considered paths and paths with cycles, sort out which array each path
929 should go in, if any. */
930 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
931 SecCertificatePathRef partial
, CFArrayRef parents
) {
932 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
933 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
935 bool is_anchor
= SecCertificatePathGetNextSourceIndex(partial
) <=
936 CFArrayGetCount(builder
->anchorSources
);
937 secdebug("trust", "found %" PRIdCFIndex
" candidate %s", num_parents
,
938 (is_anchor
? "anchors" : "parents"));
939 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
940 SecCertificateRef parent
= (SecCertificateRef
)
941 CFArrayGetValueAtIndex(parents
, parentIX
);
942 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
944 if (ixOfParent
!= kCFNotFound
) {
945 /* partial already contains parent. Let's not add the same
946 certificate again. */
947 if (ixOfParent
== rootIX
) {
948 /* parent is equal to the root of the partial, so partial
949 looks to be self issued. */
950 SecCertificatePathSetSelfIssued(partial
);
955 /* FIXME Add more sanity checks to see that parent really can be
956 a parent of partial_root. subjectKeyID == authorityKeyID,
957 signature algorithm matches public key algorithm, etc. */
958 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
);
961 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
962 CFSetAddValue(builder
->allPaths
, path
);
964 SecCertificatePathSetIsAnchored(path
);
965 if (SecPathBuilderIsPartial(builder
, path
)) {
966 /* Insert path right at the current position since it's a new
968 CFArrayInsertValueAtIndex(builder
->partialPaths
,
969 ++builder
->partialIX
, path
);
970 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
971 parentIX
+ 1, num_parents
, path
);
973 secdebug("trust", "found new path %@", path
);
979 /* Callback for the SecPathBuilderGetNext() functions call to
980 SecCertificateSourceCopyParents(). */
981 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
982 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
983 SecCertificatePathRef partial
= (SecCertificatePathRef
)
984 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
985 secdebug("async", "%@ parents %@", partial
, parents
);
986 SecPathBuilderProcessParents(builder
, partial
, parents
);
988 builder
->state
= SecPathBuilderGetNext
;
989 SecPathBuilderStep(builder
);
992 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
993 /* If we have any candidates left to go return those first. */
994 if (CFArrayGetCount(builder
->candidatePaths
)) {
995 SecCertificatePathRef path
= (SecCertificatePathRef
)
996 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
997 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
998 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
1000 SecPVCSetPath(&builder
->path
, path
, NULL
);
1001 builder
->state
= SecPathBuilderValidatePath
;
1005 /* If we are considering rejected chains we check each rejected path
1006 with SecPathBuilderIsPartial() which checks the signature chain and
1007 either drops the path if it's not properly signed, add it as a
1008 candidate if it has a trusted anchor, or adds it as a partial
1009 to be considered once we finish considering all the rejects. */
1010 if (builder
->considerRejected
) {
1011 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
1014 SecCertificatePathRef path
= (SecCertificatePathRef
)
1015 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1016 if (SecPathBuilderIsPartial(builder
, path
)) {
1017 CFArrayInsertValueAtIndex(builder
->partialPaths
,
1018 ++builder
->partialIX
, path
);
1020 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
1022 /* Keep going until we have moved all rejected partials into
1023 the regular partials or candidates array. */
1028 /* If builder->partialIX is < 0 we have considered all partial chains
1029 this block must ensure partialIX >= 0 if execution continues past
1031 if (builder
->partialIX
< 0) {
1032 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
1033 if (builder
->nextParentSource
< num_sources
) {
1034 builder
->nextParentSource
++;
1035 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
1036 builder
->nextParentSource
, num_sources
);
1038 /* We've run out of new sources to consider so let's look at
1039 rejected chains and after that even consider partials
1041 FIXME we might not want to consider partial paths that
1042 are subsets of other partial paths, or not consider them
1043 at all if we already have an anchored reject. */
1044 if (!builder
->considerRejected
) {
1045 builder
->considerRejected
= true;
1046 secdebug("trust", "considering rejected paths");
1047 } else if (!builder
->considerPartials
) {
1048 builder
->considerPartials
= true;
1049 secdebug("trust", "considering partials");
1051 /* We're all out of options, so we can't produce any more
1052 candidates. Let's calculate details and return the best
1054 builder
->state
= SecPathBuilderComputeDetails
;
1058 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
1059 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1063 /* We know builder->partialIX >= 0 if we get here. */
1064 SecCertificatePathRef partial
= (SecCertificatePathRef
)
1065 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1066 /* Don't try to extend partials anymore once we are in the considerPartials
1067 state, since at this point every partial has been extended with every
1068 possible parentSource already. */
1069 if (builder
->considerPartials
) {
1070 --builder
->partialIX
;
1071 SecPVCSetPath(&builder
->path
, partial
, NULL
);
1072 builder
->state
= SecPathBuilderValidatePath
;
1076 /* Attempt to extend this partial path with another certificate. This
1077 should give us a list of potential parents to consider. */
1078 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1079 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1082 /* Attempt to extend partial, leaving all possible extended versions
1083 of partial in builder->extendedPaths. */
1084 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
1085 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1086 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1087 SecCertificateSourceRef source
;
1088 if (sourceIX
< num_anchor_sources
) {
1089 source
= (SecCertificateSourceRef
)
1090 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1091 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1092 num_anchor_sources
);
1094 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1095 source
= (SecCertificateSourceRef
)
1096 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1097 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1098 builder
->nextParentSource
);
1100 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
1101 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
1102 return SecCertificateSourceCopyParents(source
, root
,
1103 builder
, SecPathBuilderExtendPaths
);
1105 --builder
->partialIX
;
1111 /* One or more of the policies did not accept the candidate path. */
1112 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1114 SecPVCRef pvc
= &builder
->path
;
1116 builder
->state
= SecPathBuilderGetNext
;
1118 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
1119 /* We never replace an ev reject with a non ev reject. */
1123 CFIndex rejectScore
= builder
->rejectScore
;
1124 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
1125 SecPVCGetVerifyTime(&builder
->path
));
1127 /* The current chain is valid for EV, but revocation checking failed. We
1128 replace any previously accepted or rejected non EV chains with the
1130 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
1136 /* Since this means we found a valid ev chain that was revoked,
1137 we might want to switch directly to the
1138 SecPathBuilderComputeDetails state here if we think further
1139 searching for new chains is pointless. For now we'll keep
1140 going, since we could accept an alternate EV certification
1141 path that isn't revoked. */
1142 builder
->state
= SecPathBuilderComputeDetails
;
1146 /* Do this last so that changes to rejectScore above will take affect. */
1147 if (!builder
->bestPath
|| score
> rejectScore
) {
1148 if (builder
->bestPath
) {
1150 "replacing %sev %s score: %ld with %sev reject score: %" PRIdCFIndex
" %@",
1151 (builder
->bestPathIsEV
? "" : "non "),
1152 (builder
->rejectScore
== INTPTR_MAX
? "accept" : "reject"),
1153 builder
->rejectScore
,
1154 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
1156 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" %@",
1157 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
1160 builder
->rejectScore
= score
;
1161 builder
->bestPath
= pvc
->path
;
1162 builder
->bestPathIsEV
= pvc
->is_ev
;
1164 secdebug("reject", "%sev reject score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1165 (pvc
->is_ev
? "" : "non "), score
, rejectScore
, builder
->path
.path
);
1169 /* All policies accepted the candidate path. */
1170 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1172 SecPVCRef pvc
= &builder
->path
;
1173 if (pvc
->is_ev
|| !builder
->bestPathIsEV
) {
1174 secdebug("accept", "replacing %sev accept with %sev %@",
1175 (builder
->bestPathIsEV
? "" : "non "),
1176 (pvc
->is_ev
? "" : "non "), builder
->path
.path
);
1177 builder
->rejectScore
= INTPTR_MAX
; /* CFIndex is signed long which is INTPTR_T */
1178 builder
->bestPathIsEV
= pvc
->is_ev
;
1179 builder
->bestPath
= pvc
->path
;
1182 /* If we found the best accept we can we want to switch directly to the
1183 SecPathBuilderComputeDetails state here, since we're done. */
1184 if (pvc
->is_ev
|| !pvc
->optionally_ev
)
1185 builder
->state
= SecPathBuilderComputeDetails
;
1187 builder
->state
= SecPathBuilderGetNext
;
1190 /* Return true iff a given path satisfies all the specified policies at
1192 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1193 SecPVCRef pvc
= &builder
->path
;
1195 if (builder
->considerRejected
) {
1196 SecPathBuilderReject(builder
);
1200 builder
->state
= SecPathBuilderDidValidatePath
;
1201 return SecPVCPathChecks(pvc
);
1204 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1205 SecPVCRef pvc
= &builder
->path
;
1207 SecPathBuilderAccept(builder
);
1209 SecPathBuilderReject(builder
);
1211 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1215 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1217 SecPVCRef pvc
= &builder
->path
;
1219 if (!builder
->caller_wants_details
) {
1220 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
1221 pvc
->result
= builder
->rejectScore
== INTPTR_MAX
;
1222 builder
->state
= SecPathBuilderReportResult
;
1226 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
1227 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
1228 pathLength
, builder
->leafDetails
);
1229 CFRetainSafe(details
);
1230 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
1231 /* Only report on EV stuff if the bestPath actually was valid for EV. */
1232 pvc
->optionally_ev
= builder
->bestPathIsEV
;
1233 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1234 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1235 for (ix
= 1; ix
< pathLength
; ++ix
) {
1236 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
1237 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
1238 &kCFTypeDictionaryValueCallBacks
);
1239 CFArrayAppendValue(details
, certDetail
);
1240 CFRelease(certDetail
);
1241 SecPVCParentCertificateChecks(pvc
, ix
);
1242 SecPVCGrayListedKeyChecks(pvc
, ix
);
1243 SecPVCBlackListedKeyChecks(pvc
, ix
);
1245 builder
->state
= SecPathBuilderReportResult
;
1246 bool completed
= SecPVCPathChecks(pvc
);
1248 /* Reject the certificate if it was accepted before but we failed it now. */
1249 if (builder
->rejectScore
== INTPTR_MAX
&& !pvc
->result
) {
1250 builder
->rejectScore
= 0;
1253 CFReleaseSafe(details
);
1258 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1259 SecPVCRef pvc
= &builder
->path
;
1260 bool haveRevocationResponse
= false;
1261 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
1262 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
1263 kCFBooleanTrue
); /* iOS key */
1264 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
1265 kCFBooleanTrue
); /* unified API key */
1266 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
1267 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1268 if (leafCompanyName
) {
1269 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
1270 leafCompanyName
); /* iOS key */
1271 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
1272 leafCompanyName
); /* unified API key */
1273 CFRelease(leafCompanyName
);
1276 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
1277 if (nextUpdate
== 0) {
1278 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1279 kCFBooleanFalse
); /* iOS key */
1280 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1281 kCFBooleanFalse
); /* unified API key */
1283 haveRevocationResponse
= true;
1284 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1285 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
1286 validUntil
); /* iOS key */
1287 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
1288 validUntil
); /* unified API key */
1289 CFRelease(validUntil
);
1290 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
1291 kCFBooleanTrue
); /* iOS key */
1292 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
1293 kCFBooleanTrue
); /* unified API key */
1298 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
1299 builder
->rejectScore
= 0;
1300 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
1301 0, kCFBooleanFalse
, true);
1304 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
1305 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
1310 /* This will trigger the outer step function to call the completion
1312 builder
->state
= NULL
;
1316 /* @function SecPathBuilderStep
1317 @summary This is the core of the async engine.
1318 @description Return false iff job is complete, true if a network request
1320 builder->state is a function pointer which is to be invoked.
1321 If you call this function from within a builder->state invocation it
1322 immediately returns true.
1323 Otherwise the following steps are repeated endlessly (unless a step returns)
1324 builder->state is invoked. If it returns true and builder->state is still
1325 non NULL this proccess is repeated.
1326 If a state returns false, SecPathBuilder will return true
1327 if builder->state is non NULL.
1328 If builder->state is NULL then regardless of what the state function returns
1329 the completion callback will be invoked and the builder will be deallocated.
1331 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1332 if (builder
->activations
) {
1333 secdebug("async", "activations: %lu returning true",
1334 builder
->activations
);
1338 secdebug("async", "activations: %lu", builder
->activations
);
1339 builder
->activations
++;
1340 while (builder
->state
&& builder
->state(builder
));
1341 --builder
->activations
;
1343 if (builder
->state
) {
1344 secdebug("async", "waiting for async reply, exiting");
1345 /* A state returned false, it's waiting for network traffic. Let's
1350 if (builder
->activations
) {
1351 /* There is still at least one other running instance of this builder
1352 somewhere on the stack, we let that instance take care of sending
1353 the client a response. */
1357 SecTrustResultType result
= (builder
->rejectScore
== INTPTR_MAX
1358 ? kSecTrustResultUnspecified
: kSecTrustResultRecoverableTrustFailure
);
1360 secdebug("trust", "completed: %@ details: %@ result: %d",
1361 builder
->bestPath
, builder
->path
.details
, result
);
1363 if (builder
->completed
) {
1364 builder
->completed(builder
->context
, builder
->bestPath
,
1365 builder
->path
.details
, builder
->path
.info
, result
);
1368 /* Finally, destroy the builder and free it. */
1369 SecPathBuilderDestroy(builder
);
1375 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1376 return (builder
) ? builder
->queue
: NULL
;
1379 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1380 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1384 // MARK: SecTrustServer
1385 /********************************************************
1386 ****************** SecTrustServer **********************
1387 ********************************************************/
1389 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1392 SecTrustServerEvaluateCompleted(const void *userData
,
1393 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1394 SecTrustResultType result
) {
1395 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1396 evaluated(result
, details
, info
, chain
, NULL
);
1397 Block_release(evaluated
);
1401 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
)) {
1402 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1403 /* Call the actual evaluator function. */
1404 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1405 certificates
, anchors
,
1406 anchorsOnly
, policies
,
1407 responses
, SCTs
, trustedLogs
,
1408 verifyTime
, accessGroups
,
1409 SecTrustServerEvaluateCompleted
, userData
);
1410 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1414 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1415 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
) {
1416 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1417 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1418 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1420 if (tr
== kSecTrustResultInvalid
) {
1423 CFRetainSafe(error
);
1427 *pdetails
= details
;
1428 CFRetainSafe(details
);
1436 CFRetainSafe(chain
);
1439 dispatch_semaphore_signal(done
);
1441 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);