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
);
312 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
313 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
314 CFReleaseSafe(otapkiref
);
317 /* Now let's get the leaf cert and turn it into a path. */
318 SecCertificateRef leaf
=
319 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
320 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
321 CFSetAddValue(builder
->allPaths
, path
);
322 CFArrayAppendValue(builder
->partialPaths
, path
);
324 builder
->path
= CFRetainSafe(path
);
325 SecPathBuilderSetPath(builder
, path
);
328 /* Next step is to process the leaf. We do that work on the builder queue
329 * to avoid blocking the main thread with database lookups. */
330 builder
->state
= SecPathBuilderProcessLeaf
;
331 builder
->completed
= completed
;
332 builder
->context
= context
;
335 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
336 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
337 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
338 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
339 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
340 SecPathBuilderCompleted completed
, const void *context
) {
341 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
342 memset(builder
, 0, sizeof(*builder
));
343 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
344 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
345 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
346 accessGroups
, exceptions
, completed
, context
);
350 /* Don't use this if you're going to modify the PVC array in the operation. */
351 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
352 if (!builder
->pvcs
) { return; }
355 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
356 if (!builder
->pvcs
[ix
]) { continue; }
357 operation(builder
->pvcs
[ix
], &stop
);
362 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
363 secdebug("alloc", "destroy builder %p", builder
);
364 dispatch_release_null(builder
->queue
);
365 if (builder
->anchorSource
) {
366 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
367 builder
->anchorSource
= NULL
;
369 if (builder
->certificateSource
) {
370 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
371 builder
->certificateSource
= NULL
;
373 if (builder
->itemCertificateSource
) {
374 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
375 builder
->itemCertificateSource
= NULL
;
377 if (builder
->appleAnchorSource
) {
378 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
379 builder
->appleAnchorSource
= NULL
;
381 CFReleaseNull(builder
->clientAuditToken
);
382 CFReleaseNull(builder
->anchorSources
);
383 CFReleaseNull(builder
->parentSources
);
384 CFReleaseNull(builder
->allPaths
);
385 CFReleaseNull(builder
->partialPaths
);
386 CFReleaseNull(builder
->rejectedPaths
);
387 CFReleaseNull(builder
->candidatePaths
);
388 CFReleaseNull(builder
->ocspResponses
);
389 CFReleaseNull(builder
->signedCertificateTimestamps
);
390 CFReleaseNull(builder
->trustedLogs
);
391 CFReleaseNull(builder
->path
);
392 CFReleaseNull(builder
->revocation_check_method
);
393 CFReleaseNull(builder
->info
);
394 CFReleaseNull(builder
->exceptions
);
396 free(builder
->analyticsData
);
397 builder
->analyticsData
= NULL
;
401 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
402 if (builder
->pvcs
[ix
]) {
403 SecPVCDelete(builder
->pvcs
[ix
]);
404 free(builder
->pvcs
[ix
]);
408 builder
->pvcs
= NULL
;
412 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
413 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
415 CFRetainAssign(builder
->path
, path
);
417 CFReleaseNull(builder
->info
);
419 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
420 SecPVCSetPath(pvc
, path
);
425 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
426 return builder
->canAccessNetwork
;
429 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
430 if (builder
->canAccessNetwork
!= allow
) {
431 builder
->canAccessNetwork
= allow
;
434 secinfo("http", "network access re-enabled by policy");
435 /* re-enabling network_access re-adds kSecCAIssuerSource as
437 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
439 /* <rdar://32728029> */
440 secnotice("http", "network access not allowed on WatchOS");
441 builder
->canAccessNetwork
= false;
444 secinfo("http", "network access disabled by policy");
445 /* disabling network_access removes kSecCAIssuerSource from
446 the list of parent sources. */
447 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
448 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
451 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
456 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
458 return CFRetainSafe(builder
->ocspResponses
);
461 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
463 return CFRetainSafe(builder
->signedCertificateTimestamps
);
466 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
468 return CFRetainSafe(builder
->trustedLogs
);
471 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
473 return builder
->anchorSource
;
476 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
478 return builder
->allPaths
;
481 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
483 return builder
->analyticsData
;
486 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
488 return builder
->bestPath
;
491 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
492 return builder
->path
;
495 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
496 return builder
->verifyTime
;
499 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
500 __block
bool validIntermediates
= false;
501 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
502 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
503 SecPolicyRef policy
= (SecPolicyRef
)value
;
504 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
505 validIntermediates
= true;
510 return validIntermediates
;
513 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
514 return SecCertificatePathVCGetCount(builder
->path
);
517 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
518 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
521 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
522 return SecCertificatePathVCIsAnchored(builder
->path
);
525 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
526 unsigned int result
= atomic_fetch_sub(&builder
->asyncJobCount
, 1);
527 secdebug("rvc", "%p: decrement asyncJobCount from %d", builder
, result
);
528 /* atomic_fetch_sub returns the original value, but we want this function to return the
529 * value after the operation. */
533 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
534 atomic_store(&builder
->asyncJobCount
, jobCount
);
535 secdebug("rvc", "%p: set asyncJobCount to %d", builder
, jobCount
);
538 unsigned int SecPathBuilderGetAsyncJobCount(SecPathBuilderRef builder
) {
539 unsigned int count
= atomic_load(&builder
->asyncJobCount
);
540 secdebug("rvc", "%p: current asyncJobCount is %d", builder
, count
);
544 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
545 return builder
->info
;
548 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
549 return builder
->revocation_check_method
;
552 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
553 CFRetainAssign(builder
->revocation_check_method
, method
);
554 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
557 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
558 return builder
->online_revocation
;
561 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
562 builder
->online_revocation
= true;
563 secdebug("rvc", "revocation force online check");
566 bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
567 return builder
->trusted_revocation
;
570 void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
571 builder
->trusted_revocation
= true;
572 secdebug("rvc", "revocation check only if trusted");
575 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
576 return builder
->exceptions
;
579 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
580 return builder
->pvcCount
;
583 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
584 if (ix
> (builder
->pvcCount
- 1)) {
587 return builder
->pvcs
[ix
];
590 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
591 CFIndex ix
, CFTypeRef result
, bool force
) {
592 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
593 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
597 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
598 /* If any of the PVCs passed, we accept the path. */
599 __block
bool acceptPath
= false;
600 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
601 acceptPath
|= SecPVCIsOkResult(pvc
);
606 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
607 /* Return the first PVC that passed */
608 __block SecPVCRef resultPVC
= NULL
;
609 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
610 if (SecPVCIsOkResult(pvc
)) {
615 if (resultPVC
) { return resultPVC
; }
617 /* If we didn't return a passing PVC, return the first PVC. */
618 return builder
->pvcs
[0];
621 /* This function assumes that the input source is an anchor source */
622 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
623 SecCertificateRef certificate
) {
624 __block
bool result
= false;
625 CFArrayRef constraints
= NULL
;
626 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
628 /* Unrestricted certificates:
629 * -those that come from anchor sources with no constraints
630 * -self-signed certificates with empty contraints arrays
632 Boolean selfSigned
= false;
633 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
634 if ((NULL
== source
->copyUsageConstraints
) ||
635 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
636 secinfo("trust", "unrestricted anchor%s",
637 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
642 /* Get the trust settings result for the PVCs. Only one PVC need match to
643 * trigger the anchor behavior -- policy validation will handle whether the
644 * path is truly anchored for that PVC. */
645 require(constraints
, out
);
646 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
647 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
648 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
651 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
652 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
653 // For our purposes, this is an anchor.
654 secinfo("trust", "complex trust settings anchor");
659 if (settingsResult
== kSecTrustSettingsResultDeny
) {
660 /* We consider denied certs "anchors" because the trust decision
661 is set regardless of building the chain further. The policy
662 validation will handle rejecting this chain. */
663 secinfo("trust", "complex trust settings denied anchor");
670 CFReleaseNull(constraints
);
674 /* Source returned in foundInSource has the same lifetime as the builder. */
675 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
676 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
677 /* We look through the anchor sources in order. They are ordered in
678 SecPathBuilderInit so that process anchors override user anchors which
679 override system anchors. */
680 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
682 for (ix
= 0; ix
< count
; ++ix
) {
683 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
684 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
685 if (SecCertificateSourceContains(source
, certificate
)) {
687 *foundInSource
= source
;
688 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
696 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
697 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
698 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
701 /* Return false if path is not a partial, if path was a valid candidate it
702 will have been added to builder->candidatePaths, if path was rejected
703 by the parent certificate checks (because it's expired or some other
704 static chaining check failed) it will have been added to rejectedPaths.
705 Return true path if path is a partial. */
706 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
707 SecCertificatePathVCRef path
) {
708 SecPathBuilderSetPath(builder
, path
);
709 __block
bool parentChecksFail
= true;
711 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
712 /* The parent checks aren't actually PVC-dependent, so theoretically,
713 * we only need to run this once per path, but we want to set the
714 * results in all PVCs. */
715 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
716 SecCertificatePathVCGetCount(path
) - 1);
719 if (!builder
->considerRejected
&& parentChecksFail
) {
720 secdebug("trust", "Found rejected path %@", path
);
721 CFArrayAppendValue(builder
->rejectedPaths
, path
);
725 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
726 /* Candidate paths with failed signatures are discarded. */
727 if (vstatus
== kSecPathVerifyFailed
) {
728 secdebug("trust", "Verify failed for path %@", path
);
732 if (vstatus
== kSecPathVerifySuccess
) {
733 /* The signature chain verified sucessfully, now let's find
734 out if we have an anchor for path. */
735 if (SecCertificatePathVCIsAnchored(path
)) {
736 secdebug("trust", "Adding candidate %@", path
);
737 CFArrayAppendValue(builder
->candidatePaths
, path
);
739 /* The path is not partial if the last cert is self-signed.
740 * The path is also not partial if the issuer of the last cert was the subject
741 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
742 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
743 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
744 SecCertificatePathVCIsCycleInGraph(path
)) {
745 if (!builder
->considerRejected
) {
746 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
747 CFArrayAppendValue(builder
->rejectedPaths
, path
);
749 /* This path was previously rejected as unanchored non-partial, but now that
750 * we're considering rejected paths, this is a candidate. */
751 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
752 CFArrayAppendValue(builder
->candidatePaths
, path
);
761 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
762 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
763 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
764 CFDictionaryAddValue(oldOptions
, key
, value
);
766 CFAssignRetained(policy
->_options
, oldOptions
);
769 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
770 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
771 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
772 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
774 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
775 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
776 CFStringRef policyName
= SecPolicyGetName(policy
);
777 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
778 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
780 /* Query the pinning database for this policy */
781 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
782 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
783 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
784 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
785 CFReleaseNull(query
);
786 if (!results
) { continue; } //No rules for this hostname or policyName
788 /* Found pinning policies. Apply them to the path builder. */
789 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
790 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
791 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
792 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
794 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
795 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
799 /* Create the new policies with pinning rules (preserving other ANDed policies). */
800 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
801 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
802 if (!newPolicy
) { continue; }
803 addOptionsToPolicy(newPolicy
, newOptions
);
804 SecPolicySetName(newPolicy
, dbPolicyName
);
805 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
806 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
807 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
809 if (newRulesIX
== 0) {
810 /* For the first set of pinning rules, replace this PVC's policies */
811 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
813 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
814 * Create another PVC for this dicitionary. */
815 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
816 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
817 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
820 CFReleaseNull(newPolicy
);
821 CFReleaseNull(newPolicies
);
823 CFReleaseNull(results
);
825 CFReleaseNull(policies
);
829 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
830 SecPathBuilderAddPinningPolicies(builder
);
832 /* We need to find and set constraints on the leaf-only path */
833 SecCertificatePathVCRef path
= builder
->path
;
834 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
836 SecCertificateSourceRef source
= NULL
;
837 bool isAnchor
= false;
838 CFArrayRef constraints
= NULL
;
839 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
843 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
845 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
846 CFReleaseSafe(constraints
);
848 SecCertificatePathVCSetIsAnchored(path
);
849 CFArrayAppendValue(builder
->candidatePaths
, path
);
852 __block
bool leafChecksFail
= true;
853 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
854 SecPVCLeafChecks(pvc
);
855 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
857 builder
->considerRejected
= leafChecksFail
;
859 builder
->state
= SecPathBuilderGetNext
;
863 /* Given the builder, a partial chain partial and the parents array, construct
864 a SecCertificatePath for each parent. After discarding previously
865 considered paths and paths with cycles, sort out which array each path
866 should go in, if any. */
867 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
868 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
869 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
870 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
872 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
873 SecCertificateRef parent
= (SecCertificateRef
)
874 CFArrayGetValueAtIndex(parents
, parentIX
);
875 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
877 if (ixOfParent
!= kCFNotFound
) {
878 /* partial already contains parent. Let's not add the same
879 certificate again. */
880 if (ixOfParent
== rootIX
) {
881 /* parent is equal to the root of the partial, so partial
882 looks to be self issued. */
883 SecCertificatePathVCSetSelfIssued(partial
);
888 /* FIXME Add more sanity checks to see that parent really can be
889 a parent of partial_root. subjectKeyID == authorityKeyID,
890 signature algorithm matches public key algorithm, etc. */
891 SecCertificateSourceRef source
= NULL
;
892 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
893 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
894 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
895 CFReleaseSafe(constraints
);
898 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
899 CFSetAddValue(builder
->allPaths
, path
);
901 SecCertificatePathVCSetIsAnchored(path
);
902 if (SecPathBuilderIsPartial(builder
, path
)) {
903 /* Insert path right at the current position since it's a new
905 CFArrayInsertValueAtIndex(builder
->partialPaths
,
906 ++builder
->partialIX
, path
);
907 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
908 parentIX
+ 1, num_parents
, path
);
910 secdebug("trust", "found new path %@", path
);
916 /* Callback for the SecPathBuilderGetNext() functions call to
917 SecCertificateSourceCopyParents(). */
918 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
919 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
920 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
921 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
922 secdebug("async", "%@ parents %@", partial
, parents
);
923 SecPathBuilderProcessParents(builder
, partial
, parents
);
925 builder
->state
= SecPathBuilderGetNext
;
926 SecPathBuilderStep(builder
);
929 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
930 /* If we have any candidates left to go return those first. */
931 if (CFArrayGetCount(builder
->candidatePaths
)) {
932 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
933 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
934 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
935 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
937 SecPathBuilderSetPath(builder
, path
);
938 builder
->state
= SecPathBuilderValidatePath
;
942 /* If we are considering rejected chains we check each rejected path
943 with SecPathBuilderIsPartial() which checks the signature chain and
944 either drops the path if it's not properly signed, add it as a
945 candidate if it has a trusted anchor, or adds it as a partial
946 to be considered once we finish considering all the rejects. */
947 if (builder
->considerRejected
) {
948 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
951 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
952 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
953 if (SecPathBuilderIsPartial(builder
, path
)) {
954 CFArrayInsertValueAtIndex(builder
->partialPaths
,
955 ++builder
->partialIX
, path
);
957 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
959 /* Keep going until we have moved all rejected partials into
960 the regular partials or candidates array. */
965 /* If builder->partialIX is < 0 we have considered all partial chains
966 this block must ensure partialIX >= 0 if execution continues past
968 if (builder
->partialIX
< 0) {
969 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
970 if (builder
->nextParentSource
< num_sources
) {
971 builder
->nextParentSource
++;
972 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
973 builder
->nextParentSource
, num_sources
);
975 /* We've run out of new sources to consider so let's look at
976 rejected chains and after that even consider partials
978 FIXME we might not want to consider partial paths that
979 are subsets of other partial paths, or not consider them
980 at all if we already have an (unpreferred) accept or anchored reject */
981 if (!builder
->considerRejected
) {
982 builder
->considerRejected
= true;
983 secdebug("trust", "considering rejected paths");
984 } else if (!builder
->considerPartials
) {
985 builder
->considerPartials
= true;
986 secdebug("trust", "considering partials");
988 /* We're all out of options, so we can't produce any more
989 candidates. Let's calculate details and return the best
991 builder
->state
= SecPathBuilderComputeDetails
;
995 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
996 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
1000 /* We know builder->partialIX >= 0 if we get here. */
1001 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
1002 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
1003 /* Don't try to extend partials anymore once we are in the considerPartials
1004 state, since at this point every partial has been extended with every
1005 possible parentSource already. */
1006 if (builder
->considerPartials
) {
1007 --builder
->partialIX
;
1008 SecPathBuilderSetPath(builder
, partial
);
1009 builder
->state
= SecPathBuilderValidatePath
;
1013 /* Don't try to extend partials anymore if we already have too many chains. */
1014 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
1015 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
1016 CFSetGetCount(builder
->allPaths
));
1017 builder
->partialIX
= -1;
1021 /* Attempt to extend this partial path with another certificate. This
1022 should give us a list of potential parents to consider. */
1023 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
1024 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
1027 /* Attempt to extend partial, leaving all possible extended versions
1028 of partial in builder->extendedPaths. */
1029 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
1030 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
1031 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
1032 SecCertificateSourceRef source
;
1033 if (sourceIX
< num_anchor_sources
) {
1034 source
= (SecCertificateSourceRef
)
1035 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1036 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1037 num_anchor_sources
);
1039 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1040 source
= (SecCertificateSourceRef
)
1041 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1042 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1043 builder
->nextParentSource
);
1045 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
1046 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
1047 return SecCertificateSourceCopyParents(source
, root
,
1048 builder
, SecPathBuilderExtendPaths
);
1050 --builder
->partialIX
;
1056 /* One or more of the policies did not accept the candidate path. */
1057 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1060 builder
->state
= SecPathBuilderGetNext
;
1062 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1063 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1065 if (bestPathIsEV
&& !isEV
) {
1066 /* We never replace an ev reject with a non ev reject. */
1070 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1071 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1072 SecCertificatePathVCSetScore(builder
->path
, score
);
1074 /* The current chain is valid for EV, but revocation checking failed. We
1075 replace any previously accepted or rejected non EV chains with the
1077 if (isEV
&& !bestPathIsEV
) {
1080 if (!builder
->bestPath
|| score
> bestPathScore
) {
1081 if (builder
->bestPath
) {
1083 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1084 (bestPathIsEV
? "" : "non "),
1085 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1087 (isEV
? "" : "non "), (long)score
, builder
->path
);
1089 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1090 (isEV
? "" : "non "), score
, builder
->path
);
1093 builder
->bestPath
= builder
->path
;
1095 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1096 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1100 /* All policies accepted the candidate path. */
1101 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1102 if (!builder
) { return; }
1103 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1104 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1105 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1106 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1107 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1108 /* Score this path. Note that all points awarded or deducted in
1109 * SecCertificatePathScore are < 100,000 */
1110 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1111 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1112 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1113 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1114 if (currScore
> bestScore
) {
1115 // current path is better than existing best path
1116 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1117 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1118 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1120 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1122 builder
->bestPath
= builder
->path
;
1125 /* If we found the best accept we can, we want to switch directly to the
1126 SecPathBuilderComputeDetails state here, since we're done. */
1127 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1128 builder
->state
= SecPathBuilderComputeDetails
;
1130 builder
->state
= SecPathBuilderGetNext
;
1133 /* Return true iff a given path satisfies all the specified policies at
1135 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1137 if (builder
->considerRejected
) {
1138 SecPathBuilderReject(builder
);
1142 builder
->state
= SecPathBuilderDidValidatePath
;
1144 /* Revocation checking is now done before path checks, to ensure that
1145 we have OCSP responses for CT checking and that isAllowlisted is
1146 appropriately set for other checks. */
1147 bool completed
= SecPathBuilderCheckRevocation(builder
);
1149 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1150 SecPVCPathChecks(pvc
);
1156 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1157 /* We perform the revocation required policy checks here because
1158 * this is the state we call back into once all the asynchronous
1159 * revocation check calls are done. */
1160 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1161 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1164 if (SecPathBuilderIsOkResult(builder
)) {
1165 SecPathBuilderAccept(builder
);
1167 SecPathBuilderReject(builder
);
1169 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1173 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1174 /* We have to re-do all the checks so that the results get set in the
1175 * PVC for the best path, as the last path checked may not have been the best. */
1176 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1177 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1179 __block
bool completed
= true;
1180 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1181 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1182 completed
&= SecPathBuilderCheckRevocation(builder
);
1183 for (ix
= 1; ix
< pathLength
; ++ix
) {
1184 SecPVCParentCertificateChecks(pvc
, ix
);
1186 SecPVCPathChecks(pvc
);
1189 builder
->state
= SecPathBuilderReportResult
;
1191 /* Check revocation responses. */
1192 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1193 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1196 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1197 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1198 SecCertificatePathVCResetScore(builder
->bestPath
);
1199 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1205 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1206 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1207 0, &kCFTypeDictionaryKeyCallBacks
,
1208 &kCFTypeDictionaryValueCallBacks
);
1211 /* isEV is not set unless also CT verified. Here, we need to check that we
1212 * got a revocation response as well. */
1213 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1214 #if !TARGET_OS_WATCH
1215 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1216 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1217 if (nextUpdate
!= 0) {
1219 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1223 /* Successful revocation check, so this cert is EV */
1224 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1225 kCFBooleanTrue
); /* iOS key */
1226 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1227 kCFBooleanTrue
); /* unified API key */
1228 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1229 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1230 if (leafCompanyName
) {
1231 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1232 leafCompanyName
); /* iOS key */
1233 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1234 leafCompanyName
); /* unified API key */
1235 CFRelease(leafCompanyName
);
1241 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1242 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1243 if (nextUpdate
!= 0) {
1244 /* always populate revocation info for successful revocation check */
1245 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1246 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1247 validUntil
); /* iOS key */
1248 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1249 validUntil
); /* unified API key */
1250 CFRelease(validUntil
);
1251 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1252 kCFBooleanTrue
); /* iOS key */
1253 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1254 kCFBooleanTrue
); /* unified API key */
1255 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1256 /* populate revocation info for failed revocation check with EV */
1257 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1258 kCFBooleanFalse
); /* iOS key */
1259 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1260 kCFBooleanFalse
); /* unified API key */
1264 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1265 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1270 /* This will trigger the outer step function to call the completion
1272 builder
->state
= NULL
;
1276 /* @function SecPathBuilderStep
1277 @summary This is the core of the async engine.
1278 @description Return false iff job is complete, true if a network request
1280 builder->state is a function pointer which is to be invoked.
1281 If you call this function from within a builder->state invocation it
1282 immediately returns true.
1283 Otherwise the following steps are repeated endlessly (unless a step returns)
1284 builder->state is invoked. If it returns true and builder->state is still
1285 non NULL this proccess is repeated.
1286 If a state returns false, SecPathBuilder will return true
1287 if builder->state is non NULL.
1288 If builder->state is NULL then regardless of what the state function returns
1289 the completion callback will be invoked and the builder will be deallocated.
1291 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1292 secdebug("async", "step builder %p", builder
);
1293 if (builder
->activations
) {
1294 secdebug("async", "activations: %lu returning true",
1295 builder
->activations
);
1299 secdebug("async", "activations: %lu", builder
->activations
);
1300 builder
->activations
++;
1301 while (builder
->state
&& builder
->state(builder
));
1302 --builder
->activations
;
1304 if (builder
->state
) {
1305 secdebug("async", "waiting for async reply, exiting");
1306 /* A state returned false, it's waiting for network traffic. Let's
1311 if (builder
->activations
) {
1312 /* There is still at least one other running instance of this builder
1313 somewhere on the stack, we let that instance take care of sending
1314 the client a response. */
1318 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1319 SecTrustResultType result
= pvc
->result
;
1321 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1322 result
= kSecTrustResultProceed
;
1325 secinfo("trust", "completed: %@ details: %@ result: %d",
1326 builder
->bestPath
, pvc
->details
, result
);
1328 if (builder
->completed
) {
1329 /* We want to retain just the data we need to return to our caller
1330 * and free the rest of the builder before doing the callback.
1331 * Since the callback may end an XPC transaction that made us active, we
1332 * want to retain as little residual memory as possible. */
1333 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1334 CFDictionaryRef info
= CFRetainSafe(builder
->info
);
1335 CFArrayRef details
= CFRetainSafe(pvc
->details
);
1336 const void *context
= builder
->context
;
1337 SecPathBuilderCompleted completed
= builder
->completed
;
1339 secdebug("async", "free builder");
1340 SecPathBuilderDestroy(builder
);
1343 secdebug("async", "returning to caller");
1344 completed(context
, resultPath
, details
, info
, result
);
1345 CFReleaseNull(resultPath
);
1346 CFReleaseNull(info
);
1347 CFReleaseNull(details
);
1349 SecPathBuilderDestroy(builder
);
1356 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1357 return (builder
) ? builder
->queue
: NULL
;
1360 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1361 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1365 // MARK: SecTrustServer
1366 /********************************************************
1367 ****************** SecTrustServer **********************
1368 ********************************************************/
1370 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1373 SecTrustServerEvaluateCompleted(const void *userData
,
1374 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1375 SecTrustResultType result
) {
1376 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1377 TrustdHealthAnalyticsLogEvaluationCompleted();
1378 evaluated(result
, details
, info
, chain
, NULL
);
1379 Block_release(evaluated
);
1383 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
)) {
1384 /* We need an array containing at least one certificate to proceed. */
1385 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1386 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1387 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1388 CFReleaseSafe(certError
);
1391 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1392 /* Call the actual evaluator function. */
1393 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1394 certificates
, anchors
,
1395 anchorsOnly
, keychainsAllowed
, policies
,
1396 responses
, SCTs
, trustedLogs
,
1397 verifyTime
, accessGroups
, exceptions
,
1398 SecTrustServerEvaluateCompleted
, userData
);
1399 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1403 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1404 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
) {
1405 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1406 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1407 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1409 if (tr
== kSecTrustResultInvalid
) {
1412 CFRetainSafe(error
);
1416 *pdetails
= details
;
1417 CFRetainSafe(details
);
1425 CFRetainSafe(chain
);
1428 dispatch_semaphore_signal(done
);
1430 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1431 dispatch_release(done
);