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"
35 #include "trust/trustd/md.h"
37 #include <utilities/SecIOFormat.h>
38 #include <utilities/SecDispatchRelease.h>
39 #include <utilities/SecAppleAnchorPriv.h>
41 #include <Security/SecTrustPriv.h>
42 #include <Security/SecItem.h>
43 #include <Security/SecCertificateInternal.h>
44 #include <Security/SecFramework.h>
45 #include <Security/SecPolicyPriv.h>
46 #include <Security/SecPolicyInternal.h>
47 #include <Security/SecTrustSettingsPriv.h>
48 #include <Security/SecTask.h>
49 #include <CoreFoundation/CFRuntime.h>
50 #include <CoreFoundation/CFSet.h>
51 #include <CoreFoundation/CFString.h>
52 #include <CoreFoundation/CFNumber.h>
53 #include <CoreFoundation/CFArray.h>
54 #include <CoreFoundation/CFPropertyList.h>
55 #include <AssertMacros.h>
59 #include <stdatomic.h>
61 #include <sys/codesign.h>
62 #include <Security/SecBase.h>
63 #include "SecRSAKey.h"
64 #include <libDER/oids.h>
65 #include <utilities/debugging.h>
66 #include <utilities/SecCFWrappers.h>
67 #include <Security/SecInternal.h>
68 #include <ipc/securityd_client.h>
69 #include <CommonCrypto/CommonDigest.h>
70 #include "OTATrustUtilities.h"
71 #include "personalization.h"
72 #include <utilities/SecInternalReleasePriv.h>
73 #include <mach/mach_time.h>
74 #include <dispatch/private.h>
77 #include <Security/SecTaskPriv.h>
80 #define MAX_CHAIN_LENGTH 15
81 #define MAX_NUM_CHAINS 100
82 #define ACCEPT_PATH_SCORE 10000000
84 /* Forward declaration for use in SecCertificateSource. */
85 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
88 // MARK: SecPathBuilder
89 /********************************************************
90 *************** SecPathBuilder object ******************
91 ********************************************************/
92 struct SecPathBuilder
{
93 dispatch_queue_t queue
;
95 CFDataRef clientAuditToken
;
96 SecCertificateSourceRef certificateSource
;
97 SecCertificateSourceRef itemCertificateSource
;
98 SecCertificateSourceRef anchorSource
;
99 SecCertificateSourceRef appleAnchorSource
;
100 CFMutableArrayRef anchorSources
;
101 CFIndex nextParentSource
;
102 CFMutableArrayRef parentSources
;
103 CFArrayRef ocspResponses
; // Stapled OCSP responses
104 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
105 CFDictionaryRef trustedLogs
; // Trusted CT logs
106 CFAbsoluteTime verifyTime
;
107 CFArrayRef exceptions
;
109 /* Hashed set of all paths we've constructed so far, used to prevent
110 re-considering a path that was already constructed once before.
111 Note that this is the only container in which certificatePath
112 objects are retained.
113 Every certificatePath being considered is always in allPaths and in at
114 least one of partialPaths, rejectedPaths, or candidatePath,
115 all of which don't retain their values. */
116 CFMutableSetRef allPaths
;
118 /* No trusted anchor, satisfies the linking to intermediates for all
119 policies (unless considerRejected is true). */
120 CFMutableArrayRef partialPaths
;
121 /* No trusted anchor, does not satisfy linking to intermediates for all
123 CFMutableArrayRef rejectedPaths
;
124 /* Trusted anchor, satisfies the policies so far. */
125 CFMutableArrayRef candidatePaths
;
129 bool considerRejected
;
130 bool considerPartials
;
131 bool canAccessNetwork
;
136 SecCertificatePathVCRef path
;
137 _Atomic
unsigned int asyncJobCount
;
138 bool online_revocation
;
139 bool trusted_revocation
;
140 CFStringRef revocation_check_method
;
142 SecCertificatePathVCRef bestPath
;
143 CFMutableDictionaryRef info
;
146 bool (*state
)(SecPathBuilderRef
);
147 SecPathBuilderCompleted completed
;
149 TrustAnalyticsBuilder
* analyticsData
;
152 /* State functions. Return false if a async job was scheduled, return
153 true to execute the next state. */
154 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
);
155 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
156 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
157 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
158 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
159 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
161 /* Forward declarations. */
162 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
163 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
164 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
);
166 static void SecPathBuilderInit(SecPathBuilderRef builder
, dispatch_queue_t builderQueue
,
167 CFDataRef clientAuditToken
, CFArrayRef certificates
,
168 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
169 CFArrayRef policies
, CFArrayRef ocspResponses
,
170 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
171 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
172 SecPathBuilderCompleted completed
, const void *context
) {
173 secdebug("alloc", "builder %p", builder
);
174 CFAllocatorRef allocator
= kCFAllocatorDefault
;
176 builder
->analyticsData
= calloc(1, sizeof(TrustAnalyticsBuilder
));
177 builder
->analyticsData
->start_time
= mach_absolute_time();
179 builder
->clientAuditToken
= (CFDataRef
)
180 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
183 /* make our own queue if caller fails to provide one */
184 builder
->queue
= dispatch_queue_create("com.apple.trustd.evaluation.builder", DISPATCH_QUEUE_SERIAL
);
186 dispatch_retain_safe(builderQueue
);
187 builder
->queue
= builderQueue
;
190 builder
->nextParentSource
= 1;
192 /* <rdar://32728029> */
193 builder
->canAccessNetwork
= true;
195 atomic_init(&builder
->asyncJobCount
, 0);
197 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
198 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
200 builder
->allPaths
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
201 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
202 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
203 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
205 /* Init the policy verification context. */
206 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
207 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
208 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
209 builder
->pvcCount
= 1;
210 builder
->verifyTime
= verifyTime
;
211 builder
->exceptions
= CFRetainSafe(exceptions
);
213 /* Let's create all the certificate sources we might want to use. */
214 builder
->certificateSource
=
215 SecMemoryCertificateSourceCreate(certificates
);
217 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
220 bool allowNonProduction
= false;
221 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
225 ** The order here avoids the most expensive methods if the cheaper methods
226 ** produce an acceptable chain: client-provided, keychains, network-fetched.
228 #if !TARGET_OS_BRIDGE
229 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
230 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
231 if (keychainsAllowed
) {
232 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
234 /* On OS X, need additional parent source to search legacy keychain files. */
235 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
236 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
241 /* Add the Apple, system, and user anchor certificate db to the search list
242 if we don't explicitly trust them. */
243 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
244 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
246 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
249 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
250 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
252 #else /* TARGET_OS_BRIDGE */
253 /* Bridge can only access memory sources. */
254 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
256 /* Add the Apple, system, and user anchor certificate db to the search list
257 if we don't explicitly trust them. */
258 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
260 #endif /* !TARGET_OS_BRIDGE */
263 ** The order here allows a client-provided anchor to overrule
264 ** a user or admin trust setting which can overrule the system anchors.
265 ** Apple's anchors cannot be overriden by a trust setting.
267 #if !TARGET_OS_BRIDGE
268 if (builder
->anchorSource
) {
269 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
272 /* Only add the system and user anchor certificate db to the
273 anchorSources if we are supposed to trust them. */
274 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
275 if (keychainsAllowed
) {
277 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
278 #else /* TARGET_OS_OSX */
279 if (kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
280 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
284 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
286 #else /* TARGET_OS_BRIDGE */
287 /* Bridge can only access memory sources. */
288 if (builder
->anchorSource
) {
289 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
292 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
294 #endif /* !TARGET_OS_BRIDGE */
296 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
297 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
300 builder
->trustedLogs
= SecOTAPKICreateTrustedCTLogsDictionaryFromArray(trustedLogs
);
303 /* Now let's get the leaf cert and turn it into a path. */
304 SecCertificateRef leaf
=
305 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
306 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
307 CFSetAddValue(builder
->allPaths
, path
);
308 CFArrayAppendValue(builder
->partialPaths
, path
);
310 builder
->path
= CFRetainSafe(path
);
311 SecPathBuilderSetPath(builder
, path
);
314 /* Next step is to process the leaf. We do that work on the builder queue
315 * to avoid blocking the main thread with database lookups. */
316 builder
->state
= SecPathBuilderProcessLeaf
;
317 builder
->completed
= completed
;
318 builder
->context
= context
;
321 SecPathBuilderRef
SecPathBuilderCreate(dispatch_queue_t builderQueue
, CFDataRef clientAuditToken
,
322 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
323 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
324 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
325 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
326 SecPathBuilderCompleted completed
, const void *context
) {
327 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
328 memset(builder
, 0, sizeof(*builder
));
329 SecPathBuilderInit(builder
, builderQueue
, clientAuditToken
, certificates
,
330 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
331 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
332 accessGroups
, exceptions
, completed
, context
);
336 /* Don't use this if you're going to modify the PVC array in the operation. */
337 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
338 if (!builder
->pvcs
) { return; }
341 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
342 if (!builder
->pvcs
[ix
]) { continue; }
343 operation(builder
->pvcs
[ix
], &stop
);
348 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
349 secdebug("alloc", "destroy builder %p", builder
);
350 dispatch_release_null(builder
->queue
);
351 if (builder
->anchorSource
) {
352 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
353 builder
->anchorSource
= NULL
;
355 if (builder
->certificateSource
) {
356 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
357 builder
->certificateSource
= NULL
;
359 if (builder
->itemCertificateSource
) {
360 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
361 builder
->itemCertificateSource
= NULL
;
363 if (builder
->appleAnchorSource
) {
364 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
365 builder
->appleAnchorSource
= NULL
;
367 CFReleaseNull(builder
->clientAuditToken
);
368 CFReleaseNull(builder
->anchorSources
);
369 CFReleaseNull(builder
->parentSources
);
370 CFReleaseNull(builder
->allPaths
);
371 CFReleaseNull(builder
->partialPaths
);
372 CFReleaseNull(builder
->rejectedPaths
);
373 CFReleaseNull(builder
->candidatePaths
);
374 CFReleaseNull(builder
->ocspResponses
);
375 CFReleaseNull(builder
->signedCertificateTimestamps
);
376 CFReleaseNull(builder
->trustedLogs
);
377 CFReleaseNull(builder
->path
);
378 CFReleaseNull(builder
->revocation_check_method
);
379 CFReleaseNull(builder
->info
);
380 CFReleaseNull(builder
->exceptions
);
382 free(builder
->analyticsData
);
383 builder
->analyticsData
= NULL
;
387 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
388 if (builder
->pvcs
[ix
]) {
389 SecPVCDelete(builder
->pvcs
[ix
]);
390 free(builder
->pvcs
[ix
]);
394 builder
->pvcs
= NULL
;
398 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
399 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
401 CFRetainAssign(builder
->path
, path
);
403 CFReleaseNull(builder
->info
);
405 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
406 SecPVCSetPath(pvc
, path
);
411 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
412 return builder
->canAccessNetwork
;
415 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
416 if (builder
->canAccessNetwork
!= allow
) {
417 builder
->canAccessNetwork
= allow
;
420 secinfo("http", "network access re-enabled by policy");
421 /* re-enabling network_access re-adds kSecCAIssuerSource as
423 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
425 /* <rdar://32728029> */
426 secnotice("http", "network access not allowed on WatchOS");
427 builder
->canAccessNetwork
= false;
430 secinfo("http", "network access disabled by policy");
431 /* disabling network_access removes kSecCAIssuerSource from
432 the list of parent sources. */
433 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
434 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
437 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
442 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
444 return CFRetainSafe(builder
->ocspResponses
);
447 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
449 return CFRetainSafe(builder
->signedCertificateTimestamps
);
452 CFDictionaryRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
454 return CFRetainSafe(builder
->trustedLogs
);
457 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
459 return builder
->anchorSource
;
462 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
464 return builder
->allPaths
;
467 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
469 return builder
->analyticsData
;
472 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
474 return builder
->bestPath
;
477 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
478 return builder
->path
;
481 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
482 return builder
->verifyTime
;
485 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
486 __block
bool validIntermediates
= false;
487 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
488 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
489 SecPolicyRef policy
= (SecPolicyRef
)value
;
490 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
491 validIntermediates
= true;
496 return validIntermediates
;
499 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
500 return SecCertificatePathVCGetCount(builder
->path
);
503 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
504 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
507 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
508 return SecCertificatePathVCIsAnchored(builder
->path
);
511 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
512 unsigned int result
= atomic_fetch_sub(&builder
->asyncJobCount
, 1);
513 secdebug("rvc", "%p: decrement asyncJobCount from %d", builder
, result
);
514 /* atomic_fetch_sub returns the original value, but we want this function to return the
515 * value after the operation. */
519 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
520 atomic_store(&builder
->asyncJobCount
, jobCount
);
521 secdebug("rvc", "%p: set asyncJobCount to %d", builder
, jobCount
);
524 unsigned int SecPathBuilderGetAsyncJobCount(SecPathBuilderRef builder
) {
525 unsigned int count
= atomic_load(&builder
->asyncJobCount
);
526 secdebug("rvc", "%p: current asyncJobCount is %d", builder
, count
);
530 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
531 return builder
->info
;
534 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
535 return builder
->revocation_check_method
;
538 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
539 CFRetainAssign(builder
->revocation_check_method
, method
);
540 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
543 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
544 return builder
->online_revocation
;
547 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
548 builder
->online_revocation
= true;
549 secdebug("rvc", "revocation force online check");
552 bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
553 return builder
->trusted_revocation
;
556 void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
557 builder
->trusted_revocation
= true;
558 secdebug("rvc", "revocation check only if trusted");
561 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
562 return builder
->exceptions
;
565 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
566 return builder
->pvcCount
;
569 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
570 if (ix
> (builder
->pvcCount
- 1)) {
573 return builder
->pvcs
[ix
];
576 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
577 CFIndex ix
, CFTypeRef result
, bool force
) {
578 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
579 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
583 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
584 /* If any of the PVCs passed, we accept the path. */
585 __block
bool acceptPath
= false;
586 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
587 acceptPath
|= SecPVCIsOkResult(pvc
);
592 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
593 /* Return the first PVC that passed */
594 __block SecPVCRef resultPVC
= NULL
;
595 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
596 if (SecPVCIsOkResult(pvc
)) {
601 if (resultPVC
) { return resultPVC
; }
603 /* If we didn't return a passing PVC, return the first PVC. */
604 return builder
->pvcs
[0];
607 /* This function assumes that the input source is an anchor source */
608 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
609 SecCertificateRef certificate
) {
611 /* Get the trust settings result for the PVCs. Only one PVC need match to
612 * trigger the anchor behavior -- policy validation will handle whether the
613 * path is truly anchored for that PVC. */
614 __block
bool result
= false;
615 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
616 if (SecPVCIsAnchorPerConstraints(pvc
, source
, certificate
)) {
625 /* Source returned in foundInSource has the same lifetime as the builder. */
626 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
627 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
628 /* We look through the anchor sources in order. They are ordered in
629 SecPathBuilderInit so that process anchors override user anchors which
630 override system anchors. */
631 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
633 for (ix
= 0; ix
< count
; ++ix
) {
634 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
635 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
636 if (SecCertificateSourceContains(source
, certificate
)) {
638 *foundInSource
= source
;
639 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
647 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
648 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
649 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
652 /* Return false if path is not a partial, if path was a valid candidate it
653 will have been added to builder->candidatePaths, if path was rejected
654 by the parent certificate checks (because it's expired or some other
655 static chaining check failed) it will have been added to rejectedPaths.
656 Return true path if path is a partial. */
657 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
658 SecCertificatePathVCRef path
) {
659 SecPathBuilderSetPath(builder
, path
);
660 __block
bool parentChecksFail
= true;
662 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
663 /* The parent checks aren't actually PVC-dependent, so theoretically,
664 * we only need to run this once per path, but we want to set the
665 * results in all PVCs. */
666 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
667 SecCertificatePathVCGetCount(path
) - 1);
670 if (!builder
->considerRejected
&& parentChecksFail
) {
671 secdebug("trust", "Found rejected path %@", path
);
672 CFArrayAppendValue(builder
->rejectedPaths
, path
);
676 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
677 /* Candidate paths with failed signatures are discarded. */
678 if (vstatus
== kSecPathVerifyFailed
) {
679 secdebug("trust", "Verify failed for path %@", path
);
683 if (vstatus
== kSecPathVerifySuccess
) {
684 /* The signature chain verified successfully, now let's find
685 out if we have an anchor for path. */
686 if (SecCertificatePathVCIsAnchored(path
)) {
687 secdebug("trust", "Adding candidate %@", path
);
688 CFArrayAppendValue(builder
->candidatePaths
, path
);
690 /* The path is not partial if the last cert is self-signed.
691 * The path is also not partial if the issuer of the last cert was the subject
692 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
693 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
694 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
695 SecCertificatePathVCIsCycleInGraph(path
)) {
696 if (!builder
->considerRejected
) {
697 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
698 CFArrayAppendValue(builder
->rejectedPaths
, path
);
700 /* This path was previously rejected as unanchored non-partial, but now that
701 * we're considering rejected paths, this is a candidate. */
702 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
703 CFArrayAppendValue(builder
->candidatePaths
, path
);
712 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
713 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
714 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
715 CFDictionaryAddValue(oldOptions
, key
, value
);
717 CFAssignRetained(policy
->_options
, oldOptions
);
720 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
721 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
722 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
723 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
725 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
726 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
727 CFStringRef policyName
= SecPolicyGetName(policy
);
728 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
729 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
731 /* Query the pinning database for this policy */
732 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
733 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
734 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
735 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
736 CFReleaseNull(query
);
737 if (!results
) { continue; } //No rules for this hostname or policyName
739 /* Found pinning policies. Apply them to the path builder. */
740 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
741 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
742 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
743 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
745 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
746 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
750 /* Create the new policies with pinning rules (preserving other ANDed policies). */
751 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
752 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
753 if (!newPolicy
) { continue; }
754 addOptionsToPolicy(newPolicy
, newOptions
);
755 SecPolicySetName(newPolicy
, dbPolicyName
);
756 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
757 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
758 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
760 if (newRulesIX
== 0) {
761 /* For the first set of pinning rules, replace this PVC's policies */
762 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
764 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
765 * Create another PVC for this dicitionary. */
766 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
767 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
768 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
771 CFReleaseNull(newPolicy
);
772 CFReleaseNull(newPolicies
);
774 CFReleaseNull(results
);
776 CFReleaseNull(policies
);
780 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
781 SecPathBuilderAddPinningPolicies(builder
);
783 /* We need to find and set constraints on the leaf-only path */
784 SecCertificatePathVCRef path
= builder
->path
;
785 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
787 SecCertificateSourceRef source
= NULL
;
788 bool isAnchor
= false;
789 CFArrayRef constraints
= NULL
;
790 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
794 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
796 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
797 CFReleaseSafe(constraints
);
799 SecCertificatePathVCSetIsAnchored(path
);
800 CFArrayAppendValue(builder
->candidatePaths
, path
);
803 __block
bool leafChecksFail
= true;
804 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
805 SecPVCLeafChecks(pvc
);
806 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
808 builder
->considerRejected
= leafChecksFail
;
810 builder
->state
= SecPathBuilderGetNext
;
814 /* Given the builder, a partial chain partial and the parents array, construct
815 a SecCertificatePath for each parent. After discarding previously
816 considered paths and paths with cycles, sort out which array each path
817 should go in, if any. */
818 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
819 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
820 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
821 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
823 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
824 SecCertificateRef parent
= (SecCertificateRef
)
825 CFArrayGetValueAtIndex(parents
, parentIX
);
826 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
828 if (ixOfParent
!= kCFNotFound
) {
829 /* partial already contains parent. Let's not add the same
830 certificate again. */
831 if (ixOfParent
== rootIX
) {
832 /* parent is equal to the root of the partial, so partial
833 looks to be self issued. */
834 SecCertificatePathVCSetSelfIssued(partial
);
839 /* FIXME Add more sanity checks to see that parent really can be
840 a parent of partial_root. subjectKeyID == authorityKeyID,
841 signature algorithm matches public key algorithm, etc. */
842 SecCertificateSourceRef source
= NULL
;
843 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
844 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
845 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
846 CFReleaseSafe(constraints
);
849 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
850 CFSetAddValue(builder
->allPaths
, path
);
852 SecCertificatePathVCSetIsAnchored(path
);
853 if (SecPathBuilderIsPartial(builder
, path
)) {
854 /* Insert path right at the current position since it's a new
856 CFArrayInsertValueAtIndex(builder
->partialPaths
,
857 ++builder
->partialIX
, path
);
858 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
859 parentIX
+ 1, num_parents
, path
);
861 secdebug("trust", "found new path %@", path
);
867 /* Callback for the SecPathBuilderGetNext() functions call to
868 SecCertificateSourceCopyParents(). */
869 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
870 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
871 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
872 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
873 secdebug("async", "%@ parents %@", partial
, parents
);
874 SecPathBuilderProcessParents(builder
, partial
, parents
);
876 builder
->state
= SecPathBuilderGetNext
;
877 SecPathBuilderStep(builder
);
880 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
881 /* If we have any candidates left to go return those first. */
882 if (CFArrayGetCount(builder
->candidatePaths
)) {
883 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
884 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
885 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
886 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
888 SecPathBuilderSetPath(builder
, path
);
889 builder
->state
= SecPathBuilderValidatePath
;
893 /* If we are considering rejected chains we check each rejected path
894 with SecPathBuilderIsPartial() which checks the signature chain and
895 either drops the path if it's not properly signed, add it as a
896 candidate if it has a trusted anchor, or adds it as a partial
897 to be considered once we finish considering all the rejects. */
898 if (builder
->considerRejected
) {
899 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
902 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
903 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
904 if (SecPathBuilderIsPartial(builder
, path
)) {
905 CFArrayInsertValueAtIndex(builder
->partialPaths
,
906 ++builder
->partialIX
, path
);
908 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
910 /* Keep going until we have moved all rejected partials into
911 the regular partials or candidates array. */
916 /* If builder->partialIX is < 0 we have considered all partial chains
917 this block must ensure partialIX >= 0 if execution continues past
919 if (builder
->partialIX
< 0) {
920 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
921 if (builder
->nextParentSource
< num_sources
) {
922 builder
->nextParentSource
++;
923 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
924 builder
->nextParentSource
, num_sources
);
926 /* We've run out of new sources to consider so let's look at
927 rejected chains and after that even consider partials
929 FIXME we might not want to consider partial paths that
930 are subsets of other partial paths, or not consider them
931 at all if we already have an (unpreferred) accept or anchored reject */
932 if (!builder
->considerRejected
) {
933 builder
->considerRejected
= true;
934 secdebug("trust", "considering rejected paths");
935 } else if (!builder
->considerPartials
) {
936 builder
->considerPartials
= true;
937 secdebug("trust", "considering partials");
939 /* We're all out of options, so we can't produce any more
940 candidates. Let's calculate details and return the best
942 builder
->state
= SecPathBuilderComputeDetails
;
946 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
947 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
951 /* We know builder->partialIX >= 0 if we get here. */
952 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
953 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
954 /* Don't try to extend partials anymore once we are in the considerPartials
955 state, since at this point every partial has been extended with every
956 possible parentSource already. */
957 if (builder
->considerPartials
) {
958 --builder
->partialIX
;
959 SecPathBuilderSetPath(builder
, partial
);
960 builder
->state
= SecPathBuilderValidatePath
;
964 /* Don't try to extend partials anymore if we already have too many chains. */
965 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
966 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
967 CFSetGetCount(builder
->allPaths
));
968 builder
->partialIX
= -1;
972 /* Attempt to extend this partial path with another certificate. This
973 should give us a list of potential parents to consider. */
974 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
975 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
978 /* Attempt to extend partial, leaving all possible extended versions
979 of partial in builder->extendedPaths. */
980 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
981 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
982 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
983 SecCertificateSourceRef source
;
984 if (sourceIX
< num_anchor_sources
) {
985 source
= (SecCertificateSourceRef
)
986 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
987 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
990 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
991 source
= (SecCertificateSourceRef
)
992 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
993 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
994 builder
->nextParentSource
);
996 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
997 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
998 return SecCertificateSourceCopyParents(source
, root
,
999 builder
, SecPathBuilderExtendPaths
);
1001 --builder
->partialIX
;
1007 /* One or more of the policies did not accept the candidate path. */
1008 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1011 builder
->state
= SecPathBuilderGetNext
;
1013 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1014 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1016 if (bestPathIsEV
&& !isEV
) {
1017 /* We never replace an ev reject with a non ev reject. */
1021 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1022 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1023 SecCertificatePathVCSetScore(builder
->path
, score
);
1025 /* The current chain is valid for EV, but revocation checking failed. We
1026 replace any previously accepted or rejected non EV chains with the
1028 if (isEV
&& !bestPathIsEV
) {
1031 if (!builder
->bestPath
|| score
> bestPathScore
) {
1032 if (builder
->bestPath
) {
1034 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1035 (bestPathIsEV
? "" : "non "),
1036 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1038 (isEV
? "" : "non "), (long)score
, builder
->path
);
1040 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1041 (isEV
? "" : "non "), score
, builder
->path
);
1044 builder
->bestPath
= builder
->path
;
1046 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1047 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1051 /* All policies accepted the candidate path. */
1052 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1053 if (!builder
) { return; }
1054 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1055 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1056 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1057 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1058 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1059 /* Score this path. Note that all points awarded or deducted in
1060 * SecCertificatePathScore are < 100,000 */
1061 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1062 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1063 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1064 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1065 if (currScore
> bestScore
) {
1066 // current path is better than existing best path
1067 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1068 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1069 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1071 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1073 builder
->bestPath
= builder
->path
;
1076 /* If we found the best accept we can, we want to switch directly to the
1077 SecPathBuilderComputeDetails state here, since we're done. */
1078 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1079 builder
->state
= SecPathBuilderComputeDetails
;
1081 builder
->state
= SecPathBuilderGetNext
;
1084 /* Return true iff a given path satisfies all the specified policies at
1086 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1088 if (builder
->considerRejected
) {
1089 SecPathBuilderReject(builder
);
1093 builder
->state
= SecPathBuilderDidValidatePath
;
1095 /* Revocation checking is now done before path checks, to ensure that
1096 we have OCSP responses for CT checking and that isAllowlisted is
1097 appropriately set for other checks. */
1098 bool completed
= SecPathBuilderCheckRevocation(builder
);
1100 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1101 SecPVCPathChecks(pvc
);
1107 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1108 /* We perform the revocation required policy checks here because
1109 * this is the state we call back into once all the asynchronous
1110 * revocation check calls are done. */
1111 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1112 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1115 if (SecPathBuilderIsOkResult(builder
)) {
1116 SecPathBuilderAccept(builder
);
1118 SecPathBuilderReject(builder
);
1120 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1124 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1125 /* We have to re-do all the checks so that the results get set in the
1126 * PVC for the best path, as the last path checked may not have been the best. */
1127 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1128 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1130 __block
bool completed
= true;
1131 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1132 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1133 completed
&= SecPathBuilderCheckRevocation(builder
);
1134 for (ix
= 1; ix
< pathLength
; ++ix
) {
1135 SecPVCParentCertificateChecks(pvc
, ix
);
1137 SecPVCPathChecks(pvc
);
1140 builder
->state
= SecPathBuilderReportResult
;
1142 /* Check revocation responses. */
1143 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1144 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1147 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1148 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1149 SecCertificatePathVCResetScore(builder
->bestPath
);
1150 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1156 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1157 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1158 0, &kCFTypeDictionaryKeyCallBacks
,
1159 &kCFTypeDictionaryValueCallBacks
);
1162 /* isEV is not set unless also CT verified. Here, we need to check that we
1163 * got a revocation response as well. */
1164 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1165 #if !TARGET_OS_WATCH
1166 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1167 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
))
1170 #if !TARGET_OS_WATCH
1171 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1172 if (nextUpdate
!= 0)
1175 /* Successful revocation check, so this cert is EV */
1176 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1177 kCFBooleanTrue
); /* iOS key */
1178 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1179 kCFBooleanTrue
); /* unified API key */
1180 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1181 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1182 if (leafCompanyName
) {
1183 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1184 leafCompanyName
); /* iOS key */
1185 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1186 leafCompanyName
); /* unified API key */
1187 CFRelease(leafCompanyName
);
1193 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1194 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1195 if (nextUpdate
!= 0) {
1196 /* always populate revocation info for successful revocation check */
1197 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1198 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1199 validUntil
); /* iOS key */
1200 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1201 validUntil
); /* unified API key */
1202 CFRelease(validUntil
);
1203 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1204 kCFBooleanTrue
); /* iOS key */
1205 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1206 kCFBooleanTrue
); /* unified API key */
1207 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1208 /* populate revocation info for failed revocation check with EV */
1209 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1210 kCFBooleanFalse
); /* iOS key */
1211 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1212 kCFBooleanFalse
); /* unified API key */
1213 } else if (SecCertificatePathVCRevocationCheckedAllCerts(builder
->bestPath
)) {
1214 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1215 kCFBooleanTrue
); /* iOS key */
1216 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1217 kCFBooleanTrue
); /* unified API key */
1221 /* If revoked, set the revocation reason */
1222 if (builder
->info
&& !SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)
1223 && SecCertificatePathVCGetRevocationReason(builder
->bestPath
)) {
1224 CFNumberRef reason
= SecCertificatePathVCGetRevocationReason(builder
->bestPath
);
1225 CFDictionarySetValue(builder
->info
, kSecTrustRevocationReason
, reason
);
1228 /* Set CT marker in the info */
1229 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1230 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1235 /* This will trigger the outer step function to call the completion
1237 builder
->state
= NULL
;
1241 /* @function SecPathBuilderStep
1242 @summary This is the core of the async engine.
1243 @description Return false iff job is complete, true if a network request
1245 builder->state is a function pointer which is to be invoked.
1246 If you call this function from within a builder->state invocation it
1247 immediately returns true.
1248 Otherwise the following steps are repeated endlessly (unless a step returns)
1249 builder->state is invoked. If it returns true and builder->state is still
1250 non NULL this proccess is repeated.
1251 If a state returns false, SecPathBuilder will return true
1252 if builder->state is non NULL.
1253 If builder->state is NULL then regardless of what the state function returns
1254 the completion callback will be invoked and the builder will be deallocated.
1256 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1257 secdebug("async", "step builder %p", builder
);
1258 if (builder
->activations
) {
1259 secdebug("async", "activations: %lu returning true",
1260 builder
->activations
);
1264 secdebug("async", "activations: %lu", builder
->activations
);
1265 builder
->activations
++;
1266 while (builder
->state
&& builder
->state(builder
));
1267 --builder
->activations
;
1269 if (builder
->state
) {
1270 secdebug("async", "waiting for async reply, exiting");
1271 /* A state returned false, it's waiting for network traffic. Let's
1276 if (builder
->activations
) {
1277 /* There is still at least one other running instance of this builder
1278 somewhere on the stack, we let that instance take care of sending
1279 the client a response. */
1283 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1284 SecTrustResultType result
= pvc
->result
;
1286 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1287 result
= kSecTrustResultProceed
;
1290 secinfo("trust", "completed: %@ details: %@ result: %d",
1291 builder
->bestPath
, pvc
->details
, result
);
1293 if (builder
->completed
) {
1294 /* We want to retain just the data we need to return to our caller
1295 * and free the rest of the builder before doing the callback.
1296 * Since the callback may end an XPC transaction that made us active, we
1297 * want to retain as little residual memory as possible. */
1298 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1299 CFDictionaryRef info
= CFRetainSafe(builder
->info
);
1300 CFArrayRef details
= CFRetainSafe(pvc
->details
);
1301 const void *context
= builder
->context
;
1302 SecPathBuilderCompleted completed
= builder
->completed
;
1304 secdebug("async", "free builder");
1305 SecPathBuilderDestroy(builder
);
1308 secdebug("async", "returning to caller");
1309 completed(context
, resultPath
, details
, info
, result
);
1310 CFReleaseNull(resultPath
);
1311 CFReleaseNull(info
);
1312 CFReleaseNull(details
);
1314 SecPathBuilderDestroy(builder
);
1321 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1322 return (builder
) ? builder
->queue
: NULL
;
1325 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1326 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1330 // MARK: SecTrustServer
1331 /********************************************************
1332 ****************** SecTrustServer **********************
1333 ********************************************************/
1335 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1338 SecTrustServerEvaluateCompleted(const void *userData
,
1339 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1340 SecTrustResultType result
) {
1341 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1342 TrustdHealthAnalyticsLogEvaluationCompleted();
1343 evaluated(result
, details
, info
, chain
, NULL
);
1344 Block_release(evaluated
);
1348 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
)) {
1349 /* We need an array containing at least one certificate to proceed. */
1350 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1351 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1352 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1353 CFReleaseSafe(certError
);
1356 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1357 /* Call the actual evaluator function. */
1358 SecPathBuilderRef builder
= SecPathBuilderCreate(builderQueue
, clientAuditToken
,
1359 certificates
, anchors
,
1360 anchorsOnly
, keychainsAllowed
, policies
,
1361 responses
, SCTs
, trustedLogs
,
1362 verifyTime
, accessGroups
, exceptions
,
1363 SecTrustServerEvaluateCompleted
, userData
);
1364 SecPathBuilderStep(builder
);
1368 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1369 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
) {
1370 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1371 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1372 __block dispatch_queue_t queue
= dispatch_queue_create("com.apple.trustd.evaluation.recursive", DISPATCH_QUEUE_SERIAL
);
1374 /* We need to use the async call with the semaphore here instead of a synchronous call because we may return from
1375 * SecPathBuilderStep while waiting for an asynchronous network call in order to complete the evaluation. That return
1376 * is necessary in the XPC interface in order to free up the workloop for other trust evaluations while we wait for
1377 * the networking to complete, but here, we need to make sure we wait for the network call (which will async back
1378 * onto our queue) to complete and signal us before we return to the "inline" caller. */
1379 dispatch_async(queue
, ^{
1380 SecTrustServerEvaluateBlock(queue
, NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1382 if (tr
== kSecTrustResultInvalid
) {
1385 CFRetainSafe(error
);
1389 *pdetails
= details
;
1390 CFRetainSafe(details
);
1398 CFRetainSafe(chain
);
1401 dispatch_semaphore_signal(done
);
1404 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1405 dispatch_release(done
);
1406 dispatch_release_null(queue
);