2 * Copyright (c) 2006-2010,2012-2017 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 "trust/trustd/SecTrustServer.h"
29 #include "trust/trustd/SecPolicyServer.h"
30 #include "trust/trustd/SecTrustLoggingServer.h"
31 #include "trust/trustd/SecCertificateSource.h"
32 #include "trust/trustd/SecRevocationServer.h"
33 #include "trust/trustd/SecCertificateServer.h"
34 #include "trust/trustd/SecPinningDb.h"
36 #include <utilities/SecIOFormat.h>
37 #include <utilities/SecDispatchRelease.h>
38 #include <utilities/SecAppleAnchorPriv.h>
40 #include <Security/SecTrustPriv.h>
41 #include <Security/SecItem.h>
42 #include <Security/SecCertificateInternal.h>
43 #include <Security/SecFramework.h>
44 #include <Security/SecPolicyPriv.h>
45 #include <Security/SecPolicyInternal.h>
46 #include <Security/SecTrustSettingsPriv.h>
47 #include <Security/SecTask.h>
48 #include <CoreFoundation/CFRuntime.h>
49 #include <CoreFoundation/CFSet.h>
50 #include <CoreFoundation/CFString.h>
51 #include <CoreFoundation/CFNumber.h>
52 #include <CoreFoundation/CFArray.h>
53 #include <CoreFoundation/CFPropertyList.h>
54 #include <AssertMacros.h>
58 #include <stdatomic.h>
60 #include <sys/codesign.h>
61 #include <Security/SecBase.h>
62 #include "SecRSAKey.h"
63 #include <libDER/oids.h>
64 #include <utilities/debugging.h>
65 #include <utilities/SecCFWrappers.h>
66 #include <Security/SecInternal.h>
67 #include <ipc/securityd_client.h>
68 #include <CommonCrypto/CommonDigest.h>
69 #include "OTATrustUtilities.h"
70 #include "personalization.h"
71 #include <utilities/SecInternalReleasePriv.h>
72 #include <mach/mach_time.h>
73 #include <dispatch/private.h>
76 #include <Security/SecTaskPriv.h>
79 #define MAX_CHAIN_LENGTH 15
80 #define MAX_NUM_CHAINS 100
81 #define ACCEPT_PATH_SCORE 10000000
83 /* Forward declaration for use in SecCertificateSource. */
84 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
87 // MARK: SecPathBuilder
88 /********************************************************
89 *************** SecPathBuilder object ******************
90 ********************************************************/
91 struct SecPathBuilder
{
92 dispatch_queue_t queue
;
94 CFDataRef clientAuditToken
;
95 SecCertificateSourceRef certificateSource
;
96 SecCertificateSourceRef itemCertificateSource
;
97 SecCertificateSourceRef anchorSource
;
98 SecCertificateSourceRef appleAnchorSource
;
99 CFMutableArrayRef anchorSources
;
100 CFIndex nextParentSource
;
101 CFMutableArrayRef parentSources
;
102 CFArrayRef ocspResponses
; // Stapled OCSP responses
103 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
104 CFDictionaryRef trustedLogs
; // Trusted CT logs
105 CFAbsoluteTime verifyTime
;
106 CFArrayRef exceptions
;
108 /* Hashed set of all paths we've constructed so far, used to prevent
109 re-considering a path that was already constructed once before.
110 Note that this is the only container in which certificatePath
111 objects are retained.
112 Every certificatePath being considered is always in allPaths and in at
113 least one of partialPaths, rejectedPaths, or candidatePath,
114 all of which don't retain their values. */
115 CFMutableSetRef allPaths
;
117 /* No trusted anchor, satisfies the linking to intermediates for all
118 policies (unless considerRejected is true). */
119 CFMutableArrayRef partialPaths
;
120 /* No trusted anchor, does not satisfy linking to intermediates for all
122 CFMutableArrayRef rejectedPaths
;
123 /* Trusted anchor, satisfies the policies so far. */
124 CFMutableArrayRef candidatePaths
;
128 bool considerRejected
;
129 bool considerPartials
;
130 bool canAccessNetwork
;
135 SecCertificatePathVCRef path
;
136 _Atomic
unsigned int asyncJobCount
;
137 bool online_revocation
;
138 bool trusted_revocation
;
139 CFStringRef revocation_check_method
;
141 SecCertificatePathVCRef bestPath
;
142 CFMutableDictionaryRef info
;
145 bool (*state
)(SecPathBuilderRef
);
146 SecPathBuilderCompleted completed
;
148 TrustAnalyticsBuilder
* analyticsData
;
151 /* State functions. Return false if a async job was scheduled, return
152 true to execute the next state. */
153 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
);
154 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
155 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
156 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
157 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
158 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
160 /* Forward declarations. */
161 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
162 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
163 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
);
165 static void SecPathBuilderInit(SecPathBuilderRef builder
, dispatch_queue_t builderQueue
,
166 CFDataRef clientAuditToken
, CFArrayRef certificates
,
167 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
168 CFArrayRef policies
, CFArrayRef ocspResponses
,
169 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
170 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
171 SecPathBuilderCompleted completed
, const void *context
) {
172 secdebug("alloc", "builder %p", builder
);
173 CFAllocatorRef allocator
= kCFAllocatorDefault
;
175 builder
->analyticsData
= calloc(1, sizeof(TrustAnalyticsBuilder
));
176 builder
->analyticsData
->start_time
= mach_absolute_time();
178 builder
->clientAuditToken
= (CFDataRef
)
179 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
182 /* make our own queue if caller fails to provide one */
183 builder
->queue
= dispatch_queue_create("com.apple.trustd.evaluation.builder", DISPATCH_QUEUE_SERIAL
);
185 dispatch_retain_safe(builderQueue
);
186 builder
->queue
= builderQueue
;
189 builder
->nextParentSource
= 1;
191 /* <rdar://32728029> */
192 builder
->canAccessNetwork
= true;
194 atomic_init(&builder
->asyncJobCount
, 0);
196 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
197 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
199 builder
->allPaths
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
200 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
201 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
202 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
204 /* Init the policy verification context. */
205 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
206 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
207 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
208 builder
->pvcCount
= 1;
209 builder
->verifyTime
= verifyTime
;
210 builder
->exceptions
= CFRetainSafe(exceptions
);
212 /* Let's create all the certificate sources we might want to use. */
213 builder
->certificateSource
=
214 SecMemoryCertificateSourceCreate(certificates
);
216 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
219 bool allowNonProduction
= false;
220 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
224 ** The order here avoids the most expensive methods if the cheaper methods
225 ** produce an acceptable chain: client-provided, keychains, network-fetched.
227 #if !TARGET_OS_BRIDGE
228 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
229 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
230 if (keychainsAllowed
) {
231 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
233 /* On OS X, need additional parent source to search legacy keychain files. */
234 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
235 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
240 /* Add the Apple, system, and user anchor certificate db to the search list
241 if we don't explicitly trust them. */
242 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
243 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
245 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
248 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
249 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
251 #else /* TARGET_OS_BRIDGE */
252 /* Bridge can only access memory sources. */
253 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
255 /* Add the Apple, system, and user anchor certificate db to the search list
256 if we don't explicitly trust them. */
257 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
259 #endif /* !TARGET_OS_BRIDGE */
262 ** The order here allows a client-provided anchor to overrule
263 ** a user or admin trust setting which can overrule the system anchors.
264 ** Apple's anchors cannot be overriden by a trust setting.
266 #if !TARGET_OS_BRIDGE
267 if (builder
->anchorSource
) {
268 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
271 /* Only add the system and user anchor certificate db to the
272 anchorSources if we are supposed to trust them. */
273 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
274 if (keychainsAllowed
) {
276 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
277 #else /* TARGET_OS_OSX */
278 if (kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
279 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
283 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
285 #else /* TARGET_OS_BRIDGE */
286 /* Bridge can only access memory sources. */
287 if (builder
->anchorSource
) {
288 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
291 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
293 #endif /* !TARGET_OS_BRIDGE */
295 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
296 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
299 builder
->trustedLogs
= SecOTAPKICreateTrustedCTLogsDictionaryFromArray(trustedLogs
);
302 /* Now let's get the leaf cert and turn it into a path. */
303 SecCertificateRef leaf
=
304 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
305 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
306 CFSetAddValue(builder
->allPaths
, path
);
307 CFArrayAppendValue(builder
->partialPaths
, path
);
309 builder
->path
= CFRetainSafe(path
);
310 SecPathBuilderSetPath(builder
, path
);
313 /* Next step is to process the leaf. We do that work on the builder queue
314 * to avoid blocking the main thread with database lookups. */
315 builder
->state
= SecPathBuilderProcessLeaf
;
316 builder
->completed
= completed
;
317 builder
->context
= context
;
320 SecPathBuilderRef
SecPathBuilderCreate(dispatch_queue_t builderQueue
, CFDataRef clientAuditToken
,
321 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
322 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
323 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
324 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
325 SecPathBuilderCompleted completed
, const void *context
) {
326 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
327 memset(builder
, 0, sizeof(*builder
));
328 SecPathBuilderInit(builder
, builderQueue
, clientAuditToken
, certificates
,
329 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
330 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
331 accessGroups
, exceptions
, completed
, context
);
335 /* Don't use this if you're going to modify the PVC array in the operation. */
336 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
337 if (!builder
->pvcs
) { return; }
340 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
341 if (!builder
->pvcs
[ix
]) { continue; }
342 operation(builder
->pvcs
[ix
], &stop
);
347 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
348 secdebug("alloc", "destroy builder %p", builder
);
349 dispatch_release_null(builder
->queue
);
350 if (builder
->anchorSource
) {
351 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
352 builder
->anchorSource
= NULL
;
354 if (builder
->certificateSource
) {
355 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
356 builder
->certificateSource
= NULL
;
358 if (builder
->itemCertificateSource
) {
359 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
360 builder
->itemCertificateSource
= NULL
;
362 if (builder
->appleAnchorSource
) {
363 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
364 builder
->appleAnchorSource
= NULL
;
366 CFReleaseNull(builder
->clientAuditToken
);
367 CFReleaseNull(builder
->anchorSources
);
368 CFReleaseNull(builder
->parentSources
);
369 CFReleaseNull(builder
->allPaths
);
370 CFReleaseNull(builder
->partialPaths
);
371 CFReleaseNull(builder
->rejectedPaths
);
372 CFReleaseNull(builder
->candidatePaths
);
373 CFReleaseNull(builder
->ocspResponses
);
374 CFReleaseNull(builder
->signedCertificateTimestamps
);
375 CFReleaseNull(builder
->trustedLogs
);
376 CFReleaseNull(builder
->path
);
377 CFReleaseNull(builder
->revocation_check_method
);
378 CFReleaseNull(builder
->info
);
379 CFReleaseNull(builder
->exceptions
);
381 free(builder
->analyticsData
);
382 builder
->analyticsData
= NULL
;
386 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
387 if (builder
->pvcs
[ix
]) {
388 SecPVCDelete(builder
->pvcs
[ix
]);
389 free(builder
->pvcs
[ix
]);
393 builder
->pvcs
= NULL
;
397 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
398 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
400 CFRetainAssign(builder
->path
, path
);
402 CFReleaseNull(builder
->info
);
404 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
405 SecPVCSetPath(pvc
, path
);
410 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
411 return builder
->canAccessNetwork
;
414 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
415 if (builder
->canAccessNetwork
!= allow
) {
416 builder
->canAccessNetwork
= allow
;
419 secinfo("http", "network access re-enabled by policy");
420 /* re-enabling network_access re-adds kSecCAIssuerSource as
422 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
424 /* <rdar://32728029> */
425 secnotice("http", "network access not allowed on WatchOS");
426 builder
->canAccessNetwork
= false;
429 secinfo("http", "network access disabled by policy");
430 /* disabling network_access removes kSecCAIssuerSource from
431 the list of parent sources. */
432 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
433 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
436 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
441 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
443 return CFRetainSafe(builder
->ocspResponses
);
446 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
448 return CFRetainSafe(builder
->signedCertificateTimestamps
);
451 CFDictionaryRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
453 return CFRetainSafe(builder
->trustedLogs
);
456 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
458 return builder
->anchorSource
;
461 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
463 return builder
->allPaths
;
466 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
468 return builder
->analyticsData
;
471 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
473 return builder
->bestPath
;
476 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
477 return builder
->path
;
480 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
481 return builder
->verifyTime
;
484 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
485 __block
bool validIntermediates
= false;
486 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
487 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
488 SecPolicyRef policy
= (SecPolicyRef
)value
;
489 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
490 validIntermediates
= true;
495 return validIntermediates
;
498 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
499 return SecCertificatePathVCGetCount(builder
->path
);
502 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
503 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
506 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
507 return SecCertificatePathVCIsAnchored(builder
->path
);
510 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
511 unsigned int result
= atomic_fetch_sub(&builder
->asyncJobCount
, 1);
512 secdebug("rvc", "%p: decrement asyncJobCount from %d", builder
, result
);
513 /* atomic_fetch_sub returns the original value, but we want this function to return the
514 * value after the operation. */
518 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
519 atomic_store(&builder
->asyncJobCount
, jobCount
);
520 secdebug("rvc", "%p: set asyncJobCount to %d", builder
, jobCount
);
523 unsigned int SecPathBuilderGetAsyncJobCount(SecPathBuilderRef builder
) {
524 unsigned int count
= atomic_load(&builder
->asyncJobCount
);
525 secdebug("rvc", "%p: current asyncJobCount is %d", builder
, count
);
529 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
530 return builder
->info
;
533 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
534 return builder
->revocation_check_method
;
537 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
538 CFRetainAssign(builder
->revocation_check_method
, method
);
539 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
542 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
543 return builder
->online_revocation
;
546 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
547 builder
->online_revocation
= true;
548 secdebug("rvc", "revocation force online check");
551 bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
552 return builder
->trusted_revocation
;
555 void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
556 builder
->trusted_revocation
= true;
557 secdebug("rvc", "revocation check only if trusted");
560 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
561 return builder
->exceptions
;
564 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
565 return builder
->pvcCount
;
568 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
569 if (ix
> (builder
->pvcCount
- 1)) {
572 return builder
->pvcs
[ix
];
575 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
576 CFIndex ix
, CFTypeRef result
, bool force
) {
577 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
578 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
582 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
583 /* If any of the PVCs passed, we accept the path. */
584 __block
bool acceptPath
= false;
585 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
586 acceptPath
|= SecPVCIsOkResult(pvc
);
591 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
592 /* Return the first PVC that passed */
593 __block SecPVCRef resultPVC
= NULL
;
594 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
595 if (SecPVCIsOkResult(pvc
)) {
600 if (resultPVC
) { return resultPVC
; }
602 /* If we didn't return a passing PVC, return the first PVC. */
603 return builder
->pvcs
[0];
606 /* This function assumes that the input source is an anchor source */
607 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
608 SecCertificateRef certificate
) {
610 /* Get the trust settings result for the PVCs. Only one PVC need match to
611 * trigger the anchor behavior -- policy validation will handle whether the
612 * path is truly anchored for that PVC. */
613 __block
bool result
= false;
614 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
615 if (SecPVCIsAnchorPerConstraints(pvc
, source
, certificate
)) {
624 /* Source returned in foundInSource has the same lifetime as the builder. */
625 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
626 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
627 /* We look through the anchor sources in order. They are ordered in
628 SecPathBuilderInit so that process anchors override user anchors which
629 override system anchors. */
630 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
632 for (ix
= 0; ix
< count
; ++ix
) {
633 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
634 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
635 if (SecCertificateSourceContains(source
, certificate
)) {
637 *foundInSource
= source
;
638 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
646 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
647 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
648 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
651 /* Return false if path is not a partial, if path was a valid candidate it
652 will have been added to builder->candidatePaths, if path was rejected
653 by the parent certificate checks (because it's expired or some other
654 static chaining check failed) it will have been added to rejectedPaths.
655 Return true path if path is a partial. */
656 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
657 SecCertificatePathVCRef path
) {
658 SecPathBuilderSetPath(builder
, path
);
659 __block
bool parentChecksFail
= true;
661 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
662 /* The parent checks aren't actually PVC-dependent, so theoretically,
663 * we only need to run this once per path, but we want to set the
664 * results in all PVCs. */
665 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
666 SecCertificatePathVCGetCount(path
) - 1);
669 if (!builder
->considerRejected
&& parentChecksFail
) {
670 secdebug("trust", "Found rejected path %@", path
);
671 CFArrayAppendValue(builder
->rejectedPaths
, path
);
675 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
676 /* Candidate paths with failed signatures are discarded. */
677 if (vstatus
== kSecPathVerifyFailed
) {
678 secdebug("trust", "Verify failed for path %@", path
);
682 if (vstatus
== kSecPathVerifySuccess
) {
683 /* The signature chain verified successfully, now let's find
684 out if we have an anchor for path. */
685 if (SecCertificatePathVCIsAnchored(path
)) {
686 secdebug("trust", "Adding candidate %@", path
);
687 CFArrayAppendValue(builder
->candidatePaths
, path
);
689 /* The path is not partial if the last cert is self-signed.
690 * The path is also not partial if the issuer of the last cert was the subject
691 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
692 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
693 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
694 SecCertificatePathVCIsCycleInGraph(path
)) {
695 if (!builder
->considerRejected
) {
696 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
697 CFArrayAppendValue(builder
->rejectedPaths
, path
);
699 /* This path was previously rejected as unanchored non-partial, but now that
700 * we're considering rejected paths, this is a candidate. */
701 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
702 CFArrayAppendValue(builder
->candidatePaths
, path
);
711 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
712 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
713 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
714 CFDictionaryAddValue(oldOptions
, key
, value
);
716 CFAssignRetained(policy
->_options
, oldOptions
);
719 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
720 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
721 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
722 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
724 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
725 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
726 CFStringRef policyName
= SecPolicyGetName(policy
);
727 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
728 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
730 /* Query the pinning database for this policy */
731 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
732 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
733 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
734 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
735 CFReleaseNull(query
);
736 if (!results
) { continue; } //No rules for this hostname or policyName
738 /* Found pinning policies. Apply them to the path builder. */
739 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
740 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
741 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
742 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
744 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
745 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
749 /* Create the new policies with pinning rules (preserving other ANDed policies). */
750 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
751 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
752 if (!newPolicy
) { continue; }
753 addOptionsToPolicy(newPolicy
, newOptions
);
754 SecPolicySetName(newPolicy
, dbPolicyName
);
755 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
756 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
757 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
759 if (newRulesIX
== 0) {
760 /* For the first set of pinning rules, replace this PVC's policies */
761 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
763 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
764 * Create another PVC for this dicitionary. */
765 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
766 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
767 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
770 CFReleaseNull(newPolicy
);
771 CFReleaseNull(newPolicies
);
773 CFReleaseNull(results
);
775 CFReleaseNull(policies
);
779 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
780 SecPathBuilderAddPinningPolicies(builder
);
782 /* We need to find and set constraints on the leaf-only path */
783 SecCertificatePathVCRef path
= builder
->path
;
784 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
786 SecCertificateSourceRef source
= NULL
;
787 bool isAnchor
= false;
788 CFArrayRef constraints
= NULL
;
789 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
793 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
795 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
796 CFReleaseSafe(constraints
);
798 SecCertificatePathVCSetIsAnchored(path
);
799 CFArrayAppendValue(builder
->candidatePaths
, path
);
802 __block
bool leafChecksFail
= true;
803 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
804 SecPVCLeafChecks(pvc
);
805 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
807 builder
->considerRejected
= leafChecksFail
;
809 builder
->state
= SecPathBuilderGetNext
;
813 /* Given the builder, a partial chain partial and the parents array, construct
814 a SecCertificatePath for each parent. After discarding previously
815 considered paths and paths with cycles, sort out which array each path
816 should go in, if any. */
817 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
818 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
819 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
820 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
822 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
823 SecCertificateRef parent
= (SecCertificateRef
)
824 CFArrayGetValueAtIndex(parents
, parentIX
);
825 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
827 if (ixOfParent
!= kCFNotFound
) {
828 /* partial already contains parent. Let's not add the same
829 certificate again. */
830 if (ixOfParent
== rootIX
) {
831 /* parent is equal to the root of the partial, so partial
832 looks to be self issued. */
833 SecCertificatePathVCSetSelfIssued(partial
);
838 /* FIXME Add more sanity checks to see that parent really can be
839 a parent of partial_root. subjectKeyID == authorityKeyID,
840 signature algorithm matches public key algorithm, etc. */
841 SecCertificateSourceRef source
= NULL
;
842 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
843 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
844 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
845 CFReleaseSafe(constraints
);
848 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
849 CFSetAddValue(builder
->allPaths
, path
);
851 SecCertificatePathVCSetIsAnchored(path
);
852 if (SecPathBuilderIsPartial(builder
, path
)) {
853 /* Insert path right at the current position since it's a new
855 CFArrayInsertValueAtIndex(builder
->partialPaths
,
856 ++builder
->partialIX
, path
);
857 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
858 parentIX
+ 1, num_parents
, path
);
860 secdebug("trust", "found new path %@", path
);
866 /* Callback for the SecPathBuilderGetNext() functions call to
867 SecCertificateSourceCopyParents(). */
868 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
869 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
870 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
871 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
872 secdebug("async", "%@ parents %@", partial
, parents
);
873 SecPathBuilderProcessParents(builder
, partial
, parents
);
875 builder
->state
= SecPathBuilderGetNext
;
876 SecPathBuilderStep(builder
);
879 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
880 /* If we have any candidates left to go return those first. */
881 if (CFArrayGetCount(builder
->candidatePaths
)) {
882 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
883 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
884 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
885 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
887 SecPathBuilderSetPath(builder
, path
);
888 builder
->state
= SecPathBuilderValidatePath
;
892 /* If we are considering rejected chains we check each rejected path
893 with SecPathBuilderIsPartial() which checks the signature chain and
894 either drops the path if it's not properly signed, add it as a
895 candidate if it has a trusted anchor, or adds it as a partial
896 to be considered once we finish considering all the rejects. */
897 if (builder
->considerRejected
) {
898 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
901 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
902 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
903 if (SecPathBuilderIsPartial(builder
, path
)) {
904 CFArrayInsertValueAtIndex(builder
->partialPaths
,
905 ++builder
->partialIX
, path
);
907 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
909 /* Keep going until we have moved all rejected partials into
910 the regular partials or candidates array. */
915 /* If builder->partialIX is < 0 we have considered all partial chains
916 this block must ensure partialIX >= 0 if execution continues past
918 if (builder
->partialIX
< 0) {
919 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
920 if (builder
->nextParentSource
< num_sources
) {
921 builder
->nextParentSource
++;
922 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
923 builder
->nextParentSource
, num_sources
);
925 /* We've run out of new sources to consider so let's look at
926 rejected chains and after that even consider partials
928 FIXME we might not want to consider partial paths that
929 are subsets of other partial paths, or not consider them
930 at all if we already have an (unpreferred) accept or anchored reject */
931 if (!builder
->considerRejected
) {
932 builder
->considerRejected
= true;
933 secdebug("trust", "considering rejected paths");
934 } else if (!builder
->considerPartials
) {
935 builder
->considerPartials
= true;
936 secdebug("trust", "considering partials");
938 /* We're all out of options, so we can't produce any more
939 candidates. Let's calculate details and return the best
941 builder
->state
= SecPathBuilderComputeDetails
;
945 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
946 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
950 /* We know builder->partialIX >= 0 if we get here. */
951 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
952 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
953 /* Don't try to extend partials anymore once we are in the considerPartials
954 state, since at this point every partial has been extended with every
955 possible parentSource already. */
956 if (builder
->considerPartials
) {
957 --builder
->partialIX
;
958 SecPathBuilderSetPath(builder
, partial
);
959 builder
->state
= SecPathBuilderValidatePath
;
963 /* Don't try to extend partials anymore if we already have too many chains. */
964 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
965 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
966 CFSetGetCount(builder
->allPaths
));
967 builder
->partialIX
= -1;
971 /* Attempt to extend this partial path with another certificate. This
972 should give us a list of potential parents to consider. */
973 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
974 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
977 /* Attempt to extend partial, leaving all possible extended versions
978 of partial in builder->extendedPaths. */
979 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
980 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
981 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
982 SecCertificateSourceRef source
;
983 if (sourceIX
< num_anchor_sources
) {
984 source
= (SecCertificateSourceRef
)
985 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
986 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
989 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
990 source
= (SecCertificateSourceRef
)
991 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
992 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
993 builder
->nextParentSource
);
995 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
996 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
997 return SecCertificateSourceCopyParents(source
, root
,
998 builder
, SecPathBuilderExtendPaths
);
1000 --builder
->partialIX
;
1006 /* One or more of the policies did not accept the candidate path. */
1007 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1010 builder
->state
= SecPathBuilderGetNext
;
1012 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1013 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1015 if (bestPathIsEV
&& !isEV
) {
1016 /* We never replace an ev reject with a non ev reject. */
1020 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1021 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1022 SecCertificatePathVCSetScore(builder
->path
, score
);
1024 /* The current chain is valid for EV, but revocation checking failed. We
1025 replace any previously accepted or rejected non EV chains with the
1027 if (isEV
&& !bestPathIsEV
) {
1030 if (!builder
->bestPath
|| score
> bestPathScore
) {
1031 if (builder
->bestPath
) {
1033 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1034 (bestPathIsEV
? "" : "non "),
1035 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1037 (isEV
? "" : "non "), (long)score
, builder
->path
);
1039 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1040 (isEV
? "" : "non "), score
, builder
->path
);
1043 builder
->bestPath
= builder
->path
;
1045 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1046 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1050 /* All policies accepted the candidate path. */
1051 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1052 if (!builder
) { return; }
1053 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1054 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1055 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1056 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1057 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1058 /* Score this path. Note that all points awarded or deducted in
1059 * SecCertificatePathScore are < 100,000 */
1060 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1061 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1062 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1063 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1064 if (currScore
> bestScore
) {
1065 // current path is better than existing best path
1066 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1067 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1068 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1070 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1072 builder
->bestPath
= builder
->path
;
1075 /* If we found the best accept we can, we want to switch directly to the
1076 SecPathBuilderComputeDetails state here, since we're done. */
1077 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1078 builder
->state
= SecPathBuilderComputeDetails
;
1080 builder
->state
= SecPathBuilderGetNext
;
1083 /* Return true iff a given path satisfies all the specified policies at
1085 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1087 if (builder
->considerRejected
) {
1088 SecPathBuilderReject(builder
);
1092 builder
->state
= SecPathBuilderDidValidatePath
;
1094 /* Revocation checking is now done before path checks, to ensure that
1095 we have OCSP responses for CT checking and that isAllowlisted is
1096 appropriately set for other checks. */
1097 bool completed
= SecPathBuilderCheckRevocation(builder
);
1099 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1100 SecPVCPathChecks(pvc
);
1106 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1107 /* We perform the revocation required policy checks here because
1108 * this is the state we call back into once all the asynchronous
1109 * revocation check calls are done. */
1110 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1111 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1114 if (SecPathBuilderIsOkResult(builder
)) {
1115 SecPathBuilderAccept(builder
);
1117 SecPathBuilderReject(builder
);
1119 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1123 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1124 /* We have to re-do all the checks so that the results get set in the
1125 * PVC for the best path, as the last path checked may not have been the best. */
1126 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1127 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1129 __block
bool completed
= true;
1130 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1131 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1132 completed
&= SecPathBuilderCheckRevocation(builder
);
1133 for (ix
= 1; ix
< pathLength
; ++ix
) {
1134 SecPVCParentCertificateChecks(pvc
, ix
);
1136 SecPVCPathChecks(pvc
);
1139 builder
->state
= SecPathBuilderReportResult
;
1141 /* Check revocation responses. */
1142 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1143 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1146 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1147 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1148 SecCertificatePathVCResetScore(builder
->bestPath
);
1149 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1155 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1156 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1157 0, &kCFTypeDictionaryKeyCallBacks
,
1158 &kCFTypeDictionaryValueCallBacks
);
1161 /* isEV is not set unless also CT verified. Here, we need to check that we
1162 * got a revocation response as well. */
1163 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1164 #if !TARGET_OS_WATCH
1165 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1166 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1167 if (nextUpdate
!= 0) {
1169 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1173 /* Successful revocation check, so this cert is EV */
1174 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1175 kCFBooleanTrue
); /* iOS key */
1176 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1177 kCFBooleanTrue
); /* unified API key */
1178 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1179 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1180 if (leafCompanyName
) {
1181 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1182 leafCompanyName
); /* iOS key */
1183 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1184 leafCompanyName
); /* unified API key */
1185 CFRelease(leafCompanyName
);
1191 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1192 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1193 if (nextUpdate
!= 0) {
1194 /* always populate revocation info for successful revocation check */
1195 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1196 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1197 validUntil
); /* iOS key */
1198 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1199 validUntil
); /* unified API key */
1200 CFRelease(validUntil
);
1201 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1202 kCFBooleanTrue
); /* iOS key */
1203 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1204 kCFBooleanTrue
); /* unified API key */
1205 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1206 /* populate revocation info for failed revocation check with EV */
1207 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1208 kCFBooleanFalse
); /* iOS key */
1209 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1210 kCFBooleanFalse
); /* unified API key */
1214 /* If revoked, set the revocation reason */
1215 if (builder
->info
&& !SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)
1216 && SecCertificatePathVCGetRevocationReason(builder
->bestPath
)) {
1217 CFNumberRef reason
= SecCertificatePathVCGetRevocationReason(builder
->bestPath
);
1218 CFDictionarySetValue(builder
->info
, kSecTrustRevocationReason
, reason
);
1221 /* Set CT marker in the info */
1222 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1223 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1228 /* This will trigger the outer step function to call the completion
1230 builder
->state
= NULL
;
1234 /* @function SecPathBuilderStep
1235 @summary This is the core of the async engine.
1236 @description Return false iff job is complete, true if a network request
1238 builder->state is a function pointer which is to be invoked.
1239 If you call this function from within a builder->state invocation it
1240 immediately returns true.
1241 Otherwise the following steps are repeated endlessly (unless a step returns)
1242 builder->state is invoked. If it returns true and builder->state is still
1243 non NULL this proccess is repeated.
1244 If a state returns false, SecPathBuilder will return true
1245 if builder->state is non NULL.
1246 If builder->state is NULL then regardless of what the state function returns
1247 the completion callback will be invoked and the builder will be deallocated.
1249 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1250 secdebug("async", "step builder %p", builder
);
1251 if (builder
->activations
) {
1252 secdebug("async", "activations: %lu returning true",
1253 builder
->activations
);
1257 secdebug("async", "activations: %lu", builder
->activations
);
1258 builder
->activations
++;
1259 while (builder
->state
&& builder
->state(builder
));
1260 --builder
->activations
;
1262 if (builder
->state
) {
1263 secdebug("async", "waiting for async reply, exiting");
1264 /* A state returned false, it's waiting for network traffic. Let's
1269 if (builder
->activations
) {
1270 /* There is still at least one other running instance of this builder
1271 somewhere on the stack, we let that instance take care of sending
1272 the client a response. */
1276 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1277 SecTrustResultType result
= pvc
->result
;
1279 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1280 result
= kSecTrustResultProceed
;
1283 secinfo("trust", "completed: %@ details: %@ result: %d",
1284 builder
->bestPath
, pvc
->details
, result
);
1286 if (builder
->completed
) {
1287 /* We want to retain just the data we need to return to our caller
1288 * and free the rest of the builder before doing the callback.
1289 * Since the callback may end an XPC transaction that made us active, we
1290 * want to retain as little residual memory as possible. */
1291 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1292 CFDictionaryRef info
= CFRetainSafe(builder
->info
);
1293 CFArrayRef details
= CFRetainSafe(pvc
->details
);
1294 const void *context
= builder
->context
;
1295 SecPathBuilderCompleted completed
= builder
->completed
;
1297 secdebug("async", "free builder");
1298 SecPathBuilderDestroy(builder
);
1301 secdebug("async", "returning to caller");
1302 completed(context
, resultPath
, details
, info
, result
);
1303 CFReleaseNull(resultPath
);
1304 CFReleaseNull(info
);
1305 CFReleaseNull(details
);
1307 SecPathBuilderDestroy(builder
);
1314 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1315 return (builder
) ? builder
->queue
: NULL
;
1318 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1319 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1323 // MARK: SecTrustServer
1324 /********************************************************
1325 ****************** SecTrustServer **********************
1326 ********************************************************/
1328 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1331 SecTrustServerEvaluateCompleted(const void *userData
,
1332 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1333 SecTrustResultType result
) {
1334 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1335 TrustdHealthAnalyticsLogEvaluationCompleted();
1336 evaluated(result
, details
, info
, chain
, NULL
);
1337 Block_release(evaluated
);
1341 SecTrustServerEvaluateBlock(dispatch_queue_t builderQueue
, CFDataRef clientAuditToken
, CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
, void (^evaluated
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
)) {
1342 /* We need an array containing at least one certificate to proceed. */
1343 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1344 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1345 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1346 CFReleaseSafe(certError
);
1349 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1350 /* Call the actual evaluator function. */
1351 SecPathBuilderRef builder
= SecPathBuilderCreate(builderQueue
, clientAuditToken
,
1352 certificates
, anchors
,
1353 anchorsOnly
, keychainsAllowed
, policies
,
1354 responses
, SCTs
, trustedLogs
,
1355 verifyTime
, accessGroups
, exceptions
,
1356 SecTrustServerEvaluateCompleted
, userData
);
1357 SecPathBuilderStep(builder
);
1361 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1362 SecTrustResultType
SecTrustServerEvaluate(CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, CFArrayRef exceptions
, CFArrayRef
*pdetails
, CFDictionaryRef
*pinfo
, CFArrayRef
*pchain
, CFErrorRef
*perror
) {
1363 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1364 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1365 __block dispatch_queue_t queue
= dispatch_queue_create("com.apple.trustd.evaluation.recursive", DISPATCH_QUEUE_SERIAL
);
1367 /* We need to use the async call with the semaphore here instead of a synchronous call because we may return from
1368 * SecPathBuilderStep while waiting for an asynchronous network call in order to complete the evaluation. That return
1369 * is necessary in the XPC interface in order to free up the workloop for other trust evaluations while we wait for
1370 * the networking to complete, but here, we need to make sure we wait for the network call (which will async back
1371 * onto our queue) to complete and signal us before we return to the "inline" caller. */
1372 dispatch_async(queue
, ^{
1373 SecTrustServerEvaluateBlock(queue
, NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1375 if (tr
== kSecTrustResultInvalid
) {
1378 CFRetainSafe(error
);
1382 *pdetails
= details
;
1383 CFRetainSafe(details
);
1391 CFRetainSafe(chain
);
1394 dispatch_semaphore_signal(done
);
1397 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1398 dispatch_release(done
);
1399 dispatch_release_null(queue
);