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 <securityd/SecTrustServer.h>
29 #include <securityd/SecPolicyServer.h>
30 #include <securityd/SecTrustLoggingServer.h>
31 #include <securityd/SecCertificateSource.h>
32 #include <securityd/SecRevocationServer.h>
33 #include <securityd/SecCertificateServer.h>
34 #include <securityd/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 CFArrayRef 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
,
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
);
181 /* Put all trust evaluations on the same workloop in order to avoid
182 * high thread fanout and frequent expensive context switches */
183 static dispatch_workloop_t workloop
= NULL
;
184 static dispatch_once_t onceToken
;
185 static const char *recursion_key
= "trust-evaluation-recursion-token";
186 dispatch_once(&onceToken
, ^{
187 workloop
= dispatch_workloop_create("com.apple.trustd.evaluation");
189 dispatch_queue_t builderQueue
= NULL
;
190 if (dispatch_workloop_is_current(workloop
) || dispatch_get_specific(recursion_key
)) {
191 /* If we're on the workloop already or are in a recursive trust evaluation, make a
192 * new thread so that the new path builder block will get scheduled and the blocked
193 * trust evaluation can proceed. */
194 builderQueue
= dispatch_queue_create("com.apple.trustd.evaluation.recursive", DISPATCH_QUEUE_SERIAL
);
195 dispatch_queue_set_specific(builderQueue
, recursion_key
, (void *)1, NULL
);
197 builderQueue
= workloop
;
198 dispatch_retain_safe(builderQueue
);
200 builder
->queue
= builderQueue
;
202 builder
->nextParentSource
= 1;
204 /* <rdar://32728029> */
205 builder
->canAccessNetwork
= true;
207 atomic_init(&builder
->asyncJobCount
, 0);
209 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
210 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
212 builder
->allPaths
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
213 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
214 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
215 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
217 /* Init the policy verification context. */
218 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
219 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
220 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
221 builder
->pvcCount
= 1;
222 builder
->verifyTime
= verifyTime
;
223 builder
->exceptions
= CFRetainSafe(exceptions
);
225 /* Let's create all the certificate sources we might want to use. */
226 builder
->certificateSource
=
227 SecMemoryCertificateSourceCreate(certificates
);
229 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
232 bool allowNonProduction
= false;
233 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
237 ** The order here avoids the most expensive methods if the cheaper methods
238 ** produce an acceptable chain: client-provided, keychains, network-fetched.
240 #if !TARGET_OS_BRIDGE
241 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
242 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
243 if (keychainsAllowed
) {
244 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
246 /* On OS X, need additional parent source to search legacy keychain files. */
247 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
248 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
253 /* Add the Apple, system, and user anchor certificate db to the search list
254 if we don't explicitly trust them. */
255 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
256 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
258 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
261 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
262 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
264 #else /* TARGET_OS_BRIDGE */
265 /* Bridge can only access memory sources. */
266 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
268 /* Add the Apple, system, and user anchor certificate db to the search list
269 if we don't explicitly trust them. */
270 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
272 #endif /* !TARGET_OS_BRIDGE */
275 ** The order here allows a client-provided anchor to overrule
276 ** a user or admin trust setting which can overrule the system anchors.
277 ** Apple's anchors cannot be overriden by a trust setting.
279 #if !TARGET_OS_BRIDGE
280 if (builder
->anchorSource
) {
281 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
284 /* Only add the system and user anchor certificate db to the
285 anchorSources if we are supposed to trust them. */
286 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
288 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
289 #else /* TARGET_OS_OSX */
290 if (keychainsAllowed
&& kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
291 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
294 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
296 #else /* TARGET_OS_BRIDGE */
297 /* Bridge can only access memory sources. */
298 if (builder
->anchorSource
) {
299 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
302 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
304 #endif /* !TARGET_OS_BRIDGE */
306 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
307 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
310 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
313 /* Now let's get the leaf cert and turn it into a path. */
314 SecCertificateRef leaf
=
315 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
316 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
317 CFSetAddValue(builder
->allPaths
, path
);
318 CFArrayAppendValue(builder
->partialPaths
, path
);
320 builder
->path
= CFRetainSafe(path
);
321 SecPathBuilderSetPath(builder
, path
);
324 /* Next step is to process the leaf. We do that work on the builder queue
325 * to avoid blocking the main thread with database lookups. */
326 builder
->state
= SecPathBuilderProcessLeaf
;
327 builder
->completed
= completed
;
328 builder
->context
= context
;
331 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
332 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
333 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
334 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
335 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
336 SecPathBuilderCompleted completed
, const void *context
) {
337 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
338 memset(builder
, 0, sizeof(*builder
));
339 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
340 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
341 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
342 accessGroups
, exceptions
, completed
, context
);
346 /* Don't use this if you're going to modify the PVC array in the operation. */
347 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
348 if (!builder
->pvcs
) { return; }
351 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
352 if (!builder
->pvcs
[ix
]) { continue; }
353 operation(builder
->pvcs
[ix
], &stop
);
358 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
359 secdebug("alloc", "destroy builder %p", builder
);
360 dispatch_release_null(builder
->queue
);
361 if (builder
->anchorSource
) {
362 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
363 builder
->anchorSource
= NULL
;
365 if (builder
->certificateSource
) {
366 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
367 builder
->certificateSource
= NULL
;
369 if (builder
->itemCertificateSource
) {
370 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
371 builder
->itemCertificateSource
= NULL
;
373 if (builder
->appleAnchorSource
) {
374 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
375 builder
->appleAnchorSource
= NULL
;
377 CFReleaseNull(builder
->clientAuditToken
);
378 CFReleaseNull(builder
->anchorSources
);
379 CFReleaseNull(builder
->parentSources
);
380 CFReleaseNull(builder
->allPaths
);
381 CFReleaseNull(builder
->partialPaths
);
382 CFReleaseNull(builder
->rejectedPaths
);
383 CFReleaseNull(builder
->candidatePaths
);
384 CFReleaseNull(builder
->ocspResponses
);
385 CFReleaseNull(builder
->signedCertificateTimestamps
);
386 CFReleaseNull(builder
->trustedLogs
);
387 CFReleaseNull(builder
->path
);
388 CFReleaseNull(builder
->revocation_check_method
);
389 CFReleaseNull(builder
->info
);
390 CFReleaseNull(builder
->exceptions
);
392 free(builder
->analyticsData
);
393 builder
->analyticsData
= NULL
;
397 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
398 if (builder
->pvcs
[ix
]) {
399 SecPVCDelete(builder
->pvcs
[ix
]);
400 free(builder
->pvcs
[ix
]);
404 builder
->pvcs
= NULL
;
408 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
409 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
411 CFRetainAssign(builder
->path
, path
);
413 CFReleaseNull(builder
->info
);
415 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
416 SecPVCSetPath(pvc
, path
);
421 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
422 return builder
->canAccessNetwork
;
425 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
426 if (builder
->canAccessNetwork
!= allow
) {
427 builder
->canAccessNetwork
= allow
;
430 secinfo("http", "network access re-enabled by policy");
431 /* re-enabling network_access re-adds kSecCAIssuerSource as
433 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
435 /* <rdar://32728029> */
436 secnotice("http", "network access not allowed on WatchOS");
437 builder
->canAccessNetwork
= false;
440 secinfo("http", "network access disabled by policy");
441 /* disabling network_access removes kSecCAIssuerSource from
442 the list of parent sources. */
443 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
444 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
447 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
452 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
454 return CFRetainSafe(builder
->ocspResponses
);
457 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
459 return CFRetainSafe(builder
->signedCertificateTimestamps
);
462 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
464 return CFRetainSafe(builder
->trustedLogs
);
467 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
469 return builder
->anchorSource
;
472 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
474 return builder
->allPaths
;
477 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
479 return builder
->analyticsData
;
482 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
484 return builder
->bestPath
;
487 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
488 return builder
->path
;
491 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
492 return builder
->verifyTime
;
495 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
496 __block
bool validIntermediates
= false;
497 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
498 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
499 SecPolicyRef policy
= (SecPolicyRef
)value
;
500 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
501 validIntermediates
= true;
506 return validIntermediates
;
509 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
510 return SecCertificatePathVCGetCount(builder
->path
);
513 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
514 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
517 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
518 return SecCertificatePathVCIsAnchored(builder
->path
);
521 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
522 unsigned int result
= atomic_fetch_sub(&builder
->asyncJobCount
, 1);
523 secdebug("rvc", "%p: decrement asyncJobCount from %d", builder
, result
);
524 /* atomic_fetch_sub returns the original value, but we want this function to return the
525 * value after the operation. */
529 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
530 atomic_store(&builder
->asyncJobCount
, jobCount
);
531 secdebug("rvc", "%p: set asyncJobCount to %d", builder
, jobCount
);
534 unsigned int SecPathBuilderGetAsyncJobCount(SecPathBuilderRef builder
) {
535 unsigned int count
= atomic_load(&builder
->asyncJobCount
);
536 secdebug("rvc", "%p: current asyncJobCount is %d", builder
, count
);
540 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
541 return builder
->info
;
544 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
545 return builder
->revocation_check_method
;
548 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
549 CFRetainAssign(builder
->revocation_check_method
, method
);
550 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
553 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
554 return builder
->online_revocation
;
557 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
558 builder
->online_revocation
= true;
559 secdebug("rvc", "revocation force online check");
562 bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
563 return builder
->trusted_revocation
;
566 void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
567 builder
->trusted_revocation
= true;
568 secdebug("rvc", "revocation check only if trusted");
571 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
572 return builder
->exceptions
;
575 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
576 return builder
->pvcCount
;
579 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
580 if (ix
> (builder
->pvcCount
- 1)) {
583 return builder
->pvcs
[ix
];
586 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
587 CFIndex ix
, CFTypeRef result
, bool force
) {
588 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
589 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
593 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
594 /* If any of the PVCs passed, we accept the path. */
595 __block
bool acceptPath
= false;
596 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
597 acceptPath
|= SecPVCIsOkResult(pvc
);
602 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
603 /* Return the first PVC that passed */
604 __block SecPVCRef resultPVC
= NULL
;
605 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
606 if (SecPVCIsOkResult(pvc
)) {
611 if (resultPVC
) { return resultPVC
; }
613 /* If we didn't return a passing PVC, return the first PVC. */
614 return builder
->pvcs
[0];
617 /* This function assumes that the input source is an anchor source */
618 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
619 SecCertificateRef certificate
) {
620 __block
bool result
= false;
621 CFArrayRef constraints
= NULL
;
622 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
624 /* Unrestricted certificates:
625 * -those that come from anchor sources with no constraints
626 * -self-signed certificates with empty contraints arrays
628 Boolean selfSigned
= false;
629 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
630 if ((NULL
== source
->copyUsageConstraints
) ||
631 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
632 secinfo("trust", "unrestricted anchor%s",
633 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
638 /* Get the trust settings result for the PVCs. Only one PVC need match to
639 * trigger the anchor behavior -- policy validation will handle whether the
640 * path is truly anchored for that PVC. */
641 require(constraints
, out
);
642 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
643 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
644 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
647 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
648 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
649 // For our purposes, this is an anchor.
650 secinfo("trust", "complex trust settings anchor");
655 if (settingsResult
== kSecTrustSettingsResultDeny
) {
656 /* We consider denied certs "anchors" because the trust decision
657 is set regardless of building the chain further. The policy
658 validation will handle rejecting this chain. */
659 secinfo("trust", "complex trust settings denied anchor");
666 CFReleaseNull(constraints
);
670 /* Source returned in foundInSource has the same lifetime as the builder. */
671 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
672 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
673 /* We look through the anchor sources in order. They are ordered in
674 SecPathBuilderInit so that process anchors override user anchors which
675 override system anchors. */
676 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
678 for (ix
= 0; ix
< count
; ++ix
) {
679 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
680 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
681 if (SecCertificateSourceContains(source
, certificate
)) {
683 *foundInSource
= source
;
684 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
692 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
693 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
694 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
697 /* Return false if path is not a partial, if path was a valid candidate it
698 will have been added to builder->candidatePaths, if path was rejected
699 by the parent certificate checks (because it's expired or some other
700 static chaining check failed) it will have been added to rejectedPaths.
701 Return true path if path is a partial. */
702 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
703 SecCertificatePathVCRef path
) {
704 SecPathBuilderSetPath(builder
, path
);
705 __block
bool parentChecksFail
= true;
707 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
708 /* The parent checks aren't actually PVC-dependent, so theoretically,
709 * we only need to run this once per path, but we want to set the
710 * results in all PVCs. */
711 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
712 SecCertificatePathVCGetCount(path
) - 1);
715 if (!builder
->considerRejected
&& parentChecksFail
) {
716 secdebug("trust", "Found rejected path %@", path
);
717 CFArrayAppendValue(builder
->rejectedPaths
, path
);
721 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
722 /* Candidate paths with failed signatures are discarded. */
723 if (vstatus
== kSecPathVerifyFailed
) {
724 secdebug("trust", "Verify failed for path %@", path
);
728 if (vstatus
== kSecPathVerifySuccess
) {
729 /* The signature chain verified sucessfully, now let's find
730 out if we have an anchor for path. */
731 if (SecCertificatePathVCIsAnchored(path
)) {
732 secdebug("trust", "Adding candidate %@", path
);
733 CFArrayAppendValue(builder
->candidatePaths
, path
);
735 /* The path is not partial if the last cert is self-signed.
736 * The path is also not partial if the issuer of the last cert was the subject
737 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
738 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
739 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
740 SecCertificatePathVCIsCycleInGraph(path
)) {
741 if (!builder
->considerRejected
) {
742 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
743 CFArrayAppendValue(builder
->rejectedPaths
, path
);
745 /* This path was previously rejected as unanchored non-partial, but now that
746 * we're considering rejected paths, this is a candidate. */
747 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
748 CFArrayAppendValue(builder
->candidatePaths
, path
);
757 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
758 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
759 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
760 CFDictionaryAddValue(oldOptions
, key
, value
);
762 CFAssignRetained(policy
->_options
, oldOptions
);
765 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
766 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
767 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
768 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
770 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
771 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
772 CFStringRef policyName
= SecPolicyGetName(policy
);
773 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
774 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
776 /* Query the pinning database for this policy */
777 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
778 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
779 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
780 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
781 CFReleaseNull(query
);
782 if (!results
) { continue; } //No rules for this hostname or policyName
784 /* Found pinning policies. Apply them to the path builder. */
785 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
786 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
787 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
788 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
790 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
791 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
795 /* Create the new policies with pinning rules (preserving other ANDed policies). */
796 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
797 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
798 if (!newPolicy
) { continue; }
799 addOptionsToPolicy(newPolicy
, newOptions
);
800 SecPolicySetName(newPolicy
, dbPolicyName
);
801 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
802 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
803 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
805 if (newRulesIX
== 0) {
806 /* For the first set of pinning rules, replace this PVC's policies */
807 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
809 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
810 * Create another PVC for this dicitionary. */
811 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
812 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
813 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
816 CFReleaseNull(newPolicy
);
817 CFReleaseNull(newPolicies
);
819 CFReleaseNull(results
);
821 CFReleaseNull(policies
);
825 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
826 SecPathBuilderAddPinningPolicies(builder
);
828 /* We need to find and set constraints on the leaf-only path */
829 SecCertificatePathVCRef path
= builder
->path
;
830 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
832 SecCertificateSourceRef source
= NULL
;
833 bool isAnchor
= false;
834 CFArrayRef constraints
= NULL
;
835 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
839 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
841 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
842 CFReleaseSafe(constraints
);
844 SecCertificatePathVCSetIsAnchored(path
);
845 CFArrayAppendValue(builder
->candidatePaths
, path
);
848 __block
bool leafChecksFail
= true;
849 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
850 SecPVCLeafChecks(pvc
);
851 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
853 builder
->considerRejected
= leafChecksFail
;
855 builder
->state
= SecPathBuilderGetNext
;
859 /* Given the builder, a partial chain partial and the parents array, construct
860 a SecCertificatePath for each parent. After discarding previously
861 considered paths and paths with cycles, sort out which array each path
862 should go in, if any. */
863 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
864 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
865 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
866 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
868 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
869 SecCertificateRef parent
= (SecCertificateRef
)
870 CFArrayGetValueAtIndex(parents
, parentIX
);
871 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
873 if (ixOfParent
!= kCFNotFound
) {
874 /* partial already contains parent. Let's not add the same
875 certificate again. */
876 if (ixOfParent
== rootIX
) {
877 /* parent is equal to the root of the partial, so partial
878 looks to be self issued. */
879 SecCertificatePathVCSetSelfIssued(partial
);
884 /* FIXME Add more sanity checks to see that parent really can be
885 a parent of partial_root. subjectKeyID == authorityKeyID,
886 signature algorithm matches public key algorithm, etc. */
887 SecCertificateSourceRef source
= NULL
;
888 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
889 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
890 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
891 CFReleaseSafe(constraints
);
894 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
895 CFSetAddValue(builder
->allPaths
, path
);
897 SecCertificatePathVCSetIsAnchored(path
);
898 if (SecPathBuilderIsPartial(builder
, path
)) {
899 /* Insert path right at the current position since it's a new
901 CFArrayInsertValueAtIndex(builder
->partialPaths
,
902 ++builder
->partialIX
, path
);
903 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
904 parentIX
+ 1, num_parents
, path
);
906 secdebug("trust", "found new path %@", path
);
912 /* Callback for the SecPathBuilderGetNext() functions call to
913 SecCertificateSourceCopyParents(). */
914 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
915 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
916 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
917 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
918 secdebug("async", "%@ parents %@", partial
, parents
);
919 SecPathBuilderProcessParents(builder
, partial
, parents
);
921 builder
->state
= SecPathBuilderGetNext
;
922 SecPathBuilderStep(builder
);
925 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
926 /* If we have any candidates left to go return those first. */
927 if (CFArrayGetCount(builder
->candidatePaths
)) {
928 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
929 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
930 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
931 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
933 SecPathBuilderSetPath(builder
, path
);
934 builder
->state
= SecPathBuilderValidatePath
;
938 /* If we are considering rejected chains we check each rejected path
939 with SecPathBuilderIsPartial() which checks the signature chain and
940 either drops the path if it's not properly signed, add it as a
941 candidate if it has a trusted anchor, or adds it as a partial
942 to be considered once we finish considering all the rejects. */
943 if (builder
->considerRejected
) {
944 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
947 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
948 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
949 if (SecPathBuilderIsPartial(builder
, path
)) {
950 CFArrayInsertValueAtIndex(builder
->partialPaths
,
951 ++builder
->partialIX
, path
);
953 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
955 /* Keep going until we have moved all rejected partials into
956 the regular partials or candidates array. */
961 /* If builder->partialIX is < 0 we have considered all partial chains
962 this block must ensure partialIX >= 0 if execution continues past
964 if (builder
->partialIX
< 0) {
965 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
966 if (builder
->nextParentSource
< num_sources
) {
967 builder
->nextParentSource
++;
968 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
969 builder
->nextParentSource
, num_sources
);
971 /* We've run out of new sources to consider so let's look at
972 rejected chains and after that even consider partials
974 FIXME we might not want to consider partial paths that
975 are subsets of other partial paths, or not consider them
976 at all if we already have an (unpreferred) accept or anchored reject */
977 if (!builder
->considerRejected
) {
978 builder
->considerRejected
= true;
979 secdebug("trust", "considering rejected paths");
980 } else if (!builder
->considerPartials
) {
981 builder
->considerPartials
= true;
982 secdebug("trust", "considering partials");
984 /* We're all out of options, so we can't produce any more
985 candidates. Let's calculate details and return the best
987 builder
->state
= SecPathBuilderComputeDetails
;
991 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
992 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
996 /* We know builder->partialIX >= 0 if we get here. */
997 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
998 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
999 /* Don't try to extend partials anymore once we are in the considerPartials
1000 state, since at this point every partial has been extended with every
1001 possible parentSource already. */
1002 if (builder
->considerPartials
) {
1003 --builder
->partialIX
;
1004 SecPathBuilderSetPath(builder
, partial
);
1005 builder
->state
= SecPathBuilderValidatePath
;
1009 /* Don't try to extend partials anymore if we already have too many chains. */
1010 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
1011 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
1012 CFSetGetCount(builder
->allPaths
));
1013 builder
->partialIX
= -1;
1017 /* Attempt to extend this partial path with another certificate. This
1018 should give us a list of potential parents to consider. */
1019 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1020 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1023 /* Attempt to extend partial, leaving all possible extended versions
1024 of partial in builder->extendedPaths. */
1025 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
1026 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1027 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1028 SecCertificateSourceRef source
;
1029 if (sourceIX
< num_anchor_sources
) {
1030 source
= (SecCertificateSourceRef
)
1031 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1032 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1033 num_anchor_sources
);
1035 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1036 source
= (SecCertificateSourceRef
)
1037 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1038 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1039 builder
->nextParentSource
);
1041 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
1042 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
1043 return SecCertificateSourceCopyParents(source
, root
,
1044 builder
, SecPathBuilderExtendPaths
);
1046 --builder
->partialIX
;
1052 /* One or more of the policies did not accept the candidate path. */
1053 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1056 builder
->state
= SecPathBuilderGetNext
;
1058 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1059 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1061 if (bestPathIsEV
&& !isEV
) {
1062 /* We never replace an ev reject with a non ev reject. */
1066 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1067 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1068 SecCertificatePathVCSetScore(builder
->path
, score
);
1070 /* The current chain is valid for EV, but revocation checking failed. We
1071 replace any previously accepted or rejected non EV chains with the
1073 if (isEV
&& !bestPathIsEV
) {
1076 if (!builder
->bestPath
|| score
> bestPathScore
) {
1077 if (builder
->bestPath
) {
1079 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1080 (bestPathIsEV
? "" : "non "),
1081 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1083 (isEV
? "" : "non "), (long)score
, builder
->path
);
1085 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1086 (isEV
? "" : "non "), score
, builder
->path
);
1089 builder
->bestPath
= builder
->path
;
1091 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1092 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1096 /* All policies accepted the candidate path. */
1097 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1098 if (!builder
) { return; }
1099 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1100 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1101 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1102 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1103 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1104 /* Score this path. Note that all points awarded or deducted in
1105 * SecCertificatePathScore are < 100,000 */
1106 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1107 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1108 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1109 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1110 if (currScore
> bestScore
) {
1111 // current path is better than existing best path
1112 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1113 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1114 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1116 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1118 builder
->bestPath
= builder
->path
;
1121 /* If we found the best accept we can, we want to switch directly to the
1122 SecPathBuilderComputeDetails state here, since we're done. */
1123 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1124 builder
->state
= SecPathBuilderComputeDetails
;
1126 builder
->state
= SecPathBuilderGetNext
;
1129 /* Return true iff a given path satisfies all the specified policies at
1131 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1133 if (builder
->considerRejected
) {
1134 SecPathBuilderReject(builder
);
1138 builder
->state
= SecPathBuilderDidValidatePath
;
1140 /* Revocation checking is now done before path checks, to ensure that
1141 we have OCSP responses for CT checking and that isAllowlisted is
1142 appropriately set for other checks. */
1143 bool completed
= SecPathBuilderCheckRevocation(builder
);
1145 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1146 SecPVCPathChecks(pvc
);
1152 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1153 /* We perform the revocation required policy checks here because
1154 * this is the state we call back into once all the asynchronous
1155 * revocation check calls are done. */
1156 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1157 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1160 if (SecPathBuilderIsOkResult(builder
)) {
1161 SecPathBuilderAccept(builder
);
1163 SecPathBuilderReject(builder
);
1165 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1169 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1170 /* We have to re-do all the checks so that the results get set in the
1171 * PVC for the best path, as the last path checked may not have been the best. */
1172 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1173 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1175 __block
bool completed
= true;
1176 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1177 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1178 completed
&= SecPathBuilderCheckRevocation(builder
);
1179 for (ix
= 1; ix
< pathLength
; ++ix
) {
1180 SecPVCParentCertificateChecks(pvc
, ix
);
1182 SecPVCPathChecks(pvc
);
1185 builder
->state
= SecPathBuilderReportResult
;
1187 /* Check revocation responses. */
1188 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1189 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1192 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1193 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1194 SecCertificatePathVCResetScore(builder
->bestPath
);
1195 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1201 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1202 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1203 0, &kCFTypeDictionaryKeyCallBacks
,
1204 &kCFTypeDictionaryValueCallBacks
);
1207 /* isEV is not set unless also CT verified. Here, we need to check that we
1208 * got a revocation response as well. */
1209 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1210 #if !TARGET_OS_WATCH
1211 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1212 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1213 if (nextUpdate
!= 0) {
1215 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1219 /* Successful revocation check, so this cert is EV */
1220 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1221 kCFBooleanTrue
); /* iOS key */
1222 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1223 kCFBooleanTrue
); /* unified API key */
1224 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1225 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1226 if (leafCompanyName
) {
1227 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1228 leafCompanyName
); /* iOS key */
1229 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1230 leafCompanyName
); /* unified API key */
1231 CFRelease(leafCompanyName
);
1237 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1238 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1239 if (nextUpdate
!= 0) {
1240 /* always populate revocation info for successful revocation check */
1241 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1242 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1243 validUntil
); /* iOS key */
1244 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1245 validUntil
); /* unified API key */
1246 CFRelease(validUntil
);
1247 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1248 kCFBooleanTrue
); /* iOS key */
1249 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1250 kCFBooleanTrue
); /* unified API key */
1251 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1252 /* populate revocation info for failed revocation check with EV */
1253 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1254 kCFBooleanFalse
); /* iOS key */
1255 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1256 kCFBooleanFalse
); /* unified API key */
1260 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1261 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1266 /* This will trigger the outer step function to call the completion
1268 builder
->state
= NULL
;
1272 /* @function SecPathBuilderStep
1273 @summary This is the core of the async engine.
1274 @description Return false iff job is complete, true if a network request
1276 builder->state is a function pointer which is to be invoked.
1277 If you call this function from within a builder->state invocation it
1278 immediately returns true.
1279 Otherwise the following steps are repeated endlessly (unless a step returns)
1280 builder->state is invoked. If it returns true and builder->state is still
1281 non NULL this proccess is repeated.
1282 If a state returns false, SecPathBuilder will return true
1283 if builder->state is non NULL.
1284 If builder->state is NULL then regardless of what the state function returns
1285 the completion callback will be invoked and the builder will be deallocated.
1287 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1288 secdebug("async", "step builder %p", builder
);
1289 if (builder
->activations
) {
1290 secdebug("async", "activations: %lu returning true",
1291 builder
->activations
);
1295 secdebug("async", "activations: %lu", builder
->activations
);
1296 builder
->activations
++;
1297 while (builder
->state
&& builder
->state(builder
));
1298 --builder
->activations
;
1300 if (builder
->state
) {
1301 secdebug("async", "waiting for async reply, exiting");
1302 /* A state returned false, it's waiting for network traffic. Let's
1307 if (builder
->activations
) {
1308 /* There is still at least one other running instance of this builder
1309 somewhere on the stack, we let that instance take care of sending
1310 the client a response. */
1314 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1315 SecTrustResultType result
= pvc
->result
;
1317 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1318 result
= kSecTrustResultProceed
;
1321 secinfo("trust", "completed: %@ details: %@ result: %d",
1322 builder
->bestPath
, pvc
->details
, result
);
1324 if (builder
->completed
) {
1325 /* We want to retain just the data we need to return to our caller
1326 * and free the rest of the builder before doing the callback.
1327 * Since the callback may end an XPC transaction that made us active, we
1328 * want to retain as little residual memory as possible. */
1329 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1330 CFDictionaryRef info
= CFRetainSafe(builder
->info
);
1331 CFArrayRef details
= CFRetainSafe(pvc
->details
);
1332 const void *context
= builder
->context
;
1333 SecPathBuilderCompleted completed
= builder
->completed
;
1335 secdebug("async", "free builder");
1336 SecPathBuilderDestroy(builder
);
1339 secdebug("async", "returning to caller");
1340 completed(context
, resultPath
, details
, info
, result
);
1341 CFReleaseNull(resultPath
);
1342 CFReleaseNull(info
);
1343 CFReleaseNull(details
);
1345 SecPathBuilderDestroy(builder
);
1352 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1353 return (builder
) ? builder
->queue
: NULL
;
1356 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1357 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1361 // MARK: SecTrustServer
1362 /********************************************************
1363 ****************** SecTrustServer **********************
1364 ********************************************************/
1366 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1369 SecTrustServerEvaluateCompleted(const void *userData
,
1370 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1371 SecTrustResultType result
) {
1372 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1373 TrustdHealthAnalyticsLogEvaluationCompleted();
1374 evaluated(result
, details
, info
, chain
, NULL
);
1375 Block_release(evaluated
);
1379 SecTrustServerEvaluateBlock(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
)) {
1380 /* We need an array containing at least one certificate to proceed. */
1381 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1382 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1383 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1384 CFReleaseSafe(certError
);
1387 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1388 /* Call the actual evaluator function. */
1389 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1390 certificates
, anchors
,
1391 anchorsOnly
, keychainsAllowed
, policies
,
1392 responses
, SCTs
, trustedLogs
,
1393 verifyTime
, accessGroups
, exceptions
,
1394 SecTrustServerEvaluateCompleted
, userData
);
1395 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1399 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1400 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
) {
1401 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1402 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1403 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1405 if (tr
== kSecTrustResultInvalid
) {
1408 CFRetainSafe(error
);
1412 *pdetails
= details
;
1413 CFRetainSafe(details
);
1421 CFRetainSafe(chain
);
1424 dispatch_semaphore_signal(done
);
1426 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1427 dispatch_release(done
);