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>
59 #include <sys/codesign.h>
60 #include <Security/SecBase.h>
61 #include "SecRSAKey.h"
62 #include <libDER/oids.h>
63 #include <utilities/debugging.h>
64 #include <utilities/SecCFWrappers.h>
65 #include <Security/SecInternal.h>
66 #include <ipc/securityd_client.h>
67 #include <CommonCrypto/CommonDigest.h>
68 #include "OTATrustUtilities.h"
69 #include "personalization.h"
70 #include <utilities/SecInternalReleasePriv.h>
71 #include <mach/mach_time.h>
74 #include <Security/SecTaskPriv.h>
77 #define MAX_CHAIN_LENGTH 15
78 #define MAX_NUM_CHAINS 100
79 #define ACCEPT_PATH_SCORE 10000000
81 /* Forward declaration for use in SecCertificateSource. */
82 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
85 // MARK: SecPathBuilder
86 /********************************************************
87 *************** SecPathBuilder object ******************
88 ********************************************************/
89 struct SecPathBuilder
{
90 dispatch_queue_t queue
;
92 CFDataRef clientAuditToken
;
93 SecCertificateSourceRef certificateSource
;
94 SecCertificateSourceRef itemCertificateSource
;
95 SecCertificateSourceRef anchorSource
;
96 SecCertificateSourceRef appleAnchorSource
;
97 CFMutableArrayRef anchorSources
;
98 CFIndex nextParentSource
;
99 CFMutableArrayRef parentSources
;
100 CFArrayRef ocspResponses
; // Stapled OCSP responses
101 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
102 CFArrayRef trustedLogs
; // Trusted CT logs
103 CFAbsoluteTime verifyTime
;
104 CFArrayRef exceptions
;
106 /* Hashed set of all paths we've constructed so far, used to prevent
107 re-considering a path that was already constructed once before.
108 Note that this is the only container in which certificatePath
109 objects are retained.
110 Every certificatePath being considered is always in allPaths and in at
111 least one of partialPaths, rejectedPaths, or candidatePath,
112 all of which don't retain their values. */
113 CFMutableSetRef allPaths
;
115 /* No trusted anchor, satisfies the linking to intermediates for all
116 policies (unless considerRejected is true). */
117 CFMutableArrayRef partialPaths
;
118 /* No trusted anchor, does not satisfy linking to intermediates for all
120 CFMutableArrayRef rejectedPaths
;
121 /* Trusted anchor, satisfies the policies so far. */
122 CFMutableArrayRef candidatePaths
;
126 bool considerRejected
;
127 bool considerPartials
;
128 bool canAccessNetwork
;
133 SecCertificatePathVCRef path
;
134 unsigned int asyncJobCount
;
135 bool online_revocation
;
136 bool trusted_revocation
;
137 CFStringRef revocation_check_method
;
139 SecCertificatePathVCRef bestPath
;
140 CFMutableDictionaryRef info
;
143 bool (*state
)(SecPathBuilderRef
);
144 SecPathBuilderCompleted completed
;
146 TrustAnalyticsBuilder
* analyticsData
;
149 /* State functions. Return false if a async job was scheduled, return
150 true to execute the next state. */
151 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
);
152 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
153 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
154 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
155 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
156 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
158 /* Forward declarations. */
159 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
160 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
161 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
);
163 static void SecPathBuilderInit(SecPathBuilderRef builder
,
164 CFDataRef clientAuditToken
, CFArrayRef certificates
,
165 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
166 CFArrayRef policies
, CFArrayRef ocspResponses
,
167 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
168 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
169 SecPathBuilderCompleted completed
, const void *context
) {
170 secdebug("alloc", "%p", builder
);
171 CFAllocatorRef allocator
= kCFAllocatorDefault
;
173 builder
->analyticsData
= calloc(1, sizeof(TrustAnalyticsBuilder
));
174 builder
->analyticsData
->start_time
= mach_absolute_time();
176 builder
->clientAuditToken
= (CFDataRef
)
177 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
178 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
180 builder
->nextParentSource
= 1;
182 /* <rdar://32728029> */
183 builder
->canAccessNetwork
= true;
186 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
187 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
189 builder
->allPaths
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
190 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
191 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
192 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
194 /* Init the policy verification context. */
195 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
196 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
197 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
198 builder
->pvcCount
= 1;
199 builder
->verifyTime
= verifyTime
;
200 builder
->exceptions
= CFRetainSafe(exceptions
);
202 /* Let's create all the certificate sources we might want to use. */
203 builder
->certificateSource
=
204 SecMemoryCertificateSourceCreate(certificates
);
206 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
209 bool allowNonProduction
= false;
210 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
214 ** The order here avoids the most expensive methods if the cheaper methods
215 ** produce an acceptable chain: client-provided, keychains, network-fetched.
217 #if !TARGET_OS_BRIDGE
218 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
219 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
220 if (keychainsAllowed
) {
221 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
223 /* On OS X, need additional parent source to search legacy keychain files. */
224 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
225 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
230 /* Add the Apple, system, and user anchor certificate db to the search list
231 if we don't explicitly trust them. */
232 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
233 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
235 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
238 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
239 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
241 #else /* TARGET_OS_BRIDGE */
242 /* Bridge can only access memory sources. */
243 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
245 /* Add the Apple, system, and user anchor certificate db to the search list
246 if we don't explicitly trust them. */
247 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
249 #endif /* !TARGET_OS_BRIDGE */
252 ** The order here allows a client-provided anchor to overrule
253 ** a user or admin trust setting which can overrule the system anchors.
254 ** Apple's anchors cannot be overriden by a trust setting.
256 #if !TARGET_OS_BRIDGE
257 if (builder
->anchorSource
) {
258 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
261 /* Only add the system and user anchor certificate db to the
262 anchorSources if we are supposed to trust them. */
263 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
265 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
266 #else /* TARGET_OS_OSX */
267 if (keychainsAllowed
&& kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
268 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
271 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
273 #else /* TARGET_OS_BRIDGE */
274 /* Bridge can only access memory sources. */
275 if (builder
->anchorSource
) {
276 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
279 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
281 #endif /* !TARGET_OS_BRIDGE */
283 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
284 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
287 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
289 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
290 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
291 CFReleaseSafe(otapkiref
);
294 /* Now let's get the leaf cert and turn it into a path. */
295 SecCertificateRef leaf
=
296 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
297 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
298 CFSetAddValue(builder
->allPaths
, path
);
299 CFArrayAppendValue(builder
->partialPaths
, path
);
301 builder
->path
= CFRetainSafe(path
);
302 SecPathBuilderSetPath(builder
, path
);
305 /* Next step is to process the leaf. We do that work on the builder queue
306 * to avoid blocking the main thread with database lookups. */
307 builder
->state
= SecPathBuilderProcessLeaf
;
308 builder
->completed
= completed
;
309 builder
->context
= context
;
312 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
313 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
314 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
315 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
316 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
317 SecPathBuilderCompleted completed
, const void *context
) {
318 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
319 memset(builder
, 0, sizeof(*builder
));
320 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
321 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
322 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
323 accessGroups
, exceptions
, completed
, context
);
327 /* Don't use this if you're going to modify the PVC array in the operation. */
328 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
329 if (!builder
->pvcs
) { return; }
332 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
333 if (!builder
->pvcs
[ix
]) { continue; }
334 operation(builder
->pvcs
[ix
], &stop
);
339 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
340 secdebug("alloc", "%p", builder
);
341 dispatch_release_null(builder
->queue
);
342 if (builder
->anchorSource
) {
343 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
344 builder
->anchorSource
= NULL
;
346 if (builder
->certificateSource
) {
347 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
348 builder
->certificateSource
= NULL
;
350 if (builder
->itemCertificateSource
) {
351 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
352 builder
->itemCertificateSource
= NULL
;
354 if (builder
->appleAnchorSource
) {
355 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
356 builder
->appleAnchorSource
= NULL
;
358 CFReleaseNull(builder
->clientAuditToken
);
359 CFReleaseNull(builder
->anchorSources
);
360 CFReleaseNull(builder
->parentSources
);
361 CFReleaseNull(builder
->allPaths
);
362 CFReleaseNull(builder
->partialPaths
);
363 CFReleaseNull(builder
->rejectedPaths
);
364 CFReleaseNull(builder
->candidatePaths
);
365 CFReleaseNull(builder
->ocspResponses
);
366 CFReleaseNull(builder
->signedCertificateTimestamps
);
367 CFReleaseNull(builder
->trustedLogs
);
368 CFReleaseNull(builder
->path
);
369 CFReleaseNull(builder
->revocation_check_method
);
370 CFReleaseNull(builder
->info
);
371 CFReleaseNull(builder
->exceptions
);
373 free(builder
->analyticsData
);
374 builder
->analyticsData
= NULL
;
378 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
379 if (builder
->pvcs
[ix
]) {
380 SecPVCDelete(builder
->pvcs
[ix
]);
381 free(builder
->pvcs
[ix
]);
385 builder
->pvcs
= NULL
;
389 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
390 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
392 CFRetainAssign(builder
->path
, path
);
394 CFReleaseNull(builder
->info
);
396 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
397 SecPVCSetPath(pvc
, path
);
402 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
403 return builder
->canAccessNetwork
;
406 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
407 if (builder
->canAccessNetwork
!= allow
) {
408 builder
->canAccessNetwork
= allow
;
411 secinfo("http", "network access re-enabled by policy");
412 /* re-enabling network_access re-adds kSecCAIssuerSource as
414 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
416 /* <rdar://32728029> */
417 secnotice("http", "network access not allowed on WatchOS");
418 builder
->canAccessNetwork
= false;
421 secinfo("http", "network access disabled by policy");
422 /* disabling network_access removes kSecCAIssuerSource from
423 the list of parent sources. */
424 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
425 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
428 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
433 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
435 return CFRetainSafe(builder
->ocspResponses
);
438 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
440 return CFRetainSafe(builder
->signedCertificateTimestamps
);
443 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
445 return CFRetainSafe(builder
->trustedLogs
);
448 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
450 return builder
->anchorSource
;
453 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
455 return builder
->allPaths
;
458 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
460 return builder
->analyticsData
;
463 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
465 return builder
->bestPath
;
468 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
469 return builder
->path
;
472 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
473 return builder
->verifyTime
;
476 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
477 __block
bool validIntermediates
= false;
478 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
479 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
480 SecPolicyRef policy
= (SecPolicyRef
)value
;
481 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
482 validIntermediates
= true;
487 return validIntermediates
;
490 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
491 return SecCertificatePathVCGetCount(builder
->path
);
494 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
495 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
498 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
499 return SecCertificatePathVCIsAnchored(builder
->path
);
502 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
503 return --builder
->asyncJobCount
;
506 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
507 builder
->asyncJobCount
= jobCount
;
508 secdebug("rvc", "set asyncJobCount to %d", builder
->asyncJobCount
);
511 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
512 return builder
->info
;
515 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
516 return builder
->revocation_check_method
;
519 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
520 CFRetainAssign(builder
->revocation_check_method
, method
);
521 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
524 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
525 return builder
->online_revocation
;
528 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
529 builder
->online_revocation
= true;
530 secdebug("rvc", "revocation force online check");
533 bool SecPathBuilderGetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
534 return builder
->trusted_revocation
;
537 void SecPathBuilderSetCheckRevocationIfTrusted(SecPathBuilderRef builder
) {
538 builder
->trusted_revocation
= true;
539 secdebug("rvc", "revocation check only if trusted");
542 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
543 return builder
->exceptions
;
546 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
547 return builder
->pvcCount
;
550 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
551 if (ix
> (builder
->pvcCount
- 1)) {
554 return builder
->pvcs
[ix
];
557 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
558 CFIndex ix
, CFTypeRef result
, bool force
) {
559 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
560 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
564 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
565 /* If any of the PVCs passed, we accept the path. */
566 __block
bool acceptPath
= false;
567 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
568 acceptPath
|= SecPVCIsOkResult(pvc
);
573 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
574 /* Return the first PVC that passed */
575 __block SecPVCRef resultPVC
= NULL
;
576 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
577 if (SecPVCIsOkResult(pvc
)) {
582 if (resultPVC
) { return resultPVC
; }
584 /* If we didn't return a passing PVC, return the first PVC. */
585 return builder
->pvcs
[0];
588 /* This function assumes that the input source is an anchor source */
589 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
590 SecCertificateRef certificate
) {
591 __block
bool result
= false;
592 CFArrayRef constraints
= NULL
;
593 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
595 /* Unrestricted certificates:
596 * -those that come from anchor sources with no constraints
597 * -self-signed certificates with empty contraints arrays
599 Boolean selfSigned
= false;
600 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
601 if ((NULL
== source
->copyUsageConstraints
) ||
602 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
603 secinfo("trust", "unrestricted anchor%s",
604 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
609 /* Get the trust settings result for the PVCs. Only one PVC need match to
610 * trigger the anchor behavior -- policy validation will handle whether the
611 * path is truly anchored for that PVC. */
612 require(constraints
, out
);
613 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
614 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
615 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
618 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
619 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
620 // For our purposes, this is an anchor.
621 secinfo("trust", "complex trust settings anchor");
626 if (settingsResult
== kSecTrustSettingsResultDeny
) {
627 /* We consider denied certs "anchors" because the trust decision
628 is set regardless of building the chain further. The policy
629 validation will handle rejecting this chain. */
630 secinfo("trust", "complex trust settings denied anchor");
637 CFReleaseNull(constraints
);
641 /* Source returned in foundInSource has the same lifetime as the builder. */
642 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
643 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
644 /* We look through the anchor sources in order. They are ordered in
645 SecPathBuilderInit so that process anchors override user anchors which
646 override system anchors. */
647 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
649 for (ix
= 0; ix
< count
; ++ix
) {
650 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
651 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
652 if (SecCertificateSourceContains(source
, certificate
)) {
654 *foundInSource
= source
;
655 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
663 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
664 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
665 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
668 /* Return false if path is not a partial, if path was a valid candidate it
669 will have been added to builder->candidatePaths, if path was rejected
670 by the parent certificate checks (because it's expired or some other
671 static chaining check failed) it will have been added to rejectedPaths.
672 Return true path if path is a partial. */
673 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
674 SecCertificatePathVCRef path
) {
675 SecPathBuilderSetPath(builder
, path
);
676 __block
bool parentChecksFail
= true;
678 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
679 /* The parent checks aren't actually PVC-dependent, so theoretically,
680 * we only need to run this once per path, but we want to set the
681 * results in all PVCs. */
682 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
683 SecCertificatePathVCGetCount(path
) - 1);
686 if (!builder
->considerRejected
&& parentChecksFail
) {
687 secdebug("trust", "Found rejected path %@", path
);
688 CFArrayAppendValue(builder
->rejectedPaths
, path
);
692 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
693 /* Candidate paths with failed signatures are discarded. */
694 if (vstatus
== kSecPathVerifyFailed
) {
695 secdebug("trust", "Verify failed for path %@", path
);
699 if (vstatus
== kSecPathVerifySuccess
) {
700 /* The signature chain verified sucessfully, now let's find
701 out if we have an anchor for path. */
702 if (SecCertificatePathVCIsAnchored(path
)) {
703 secdebug("trust", "Adding candidate %@", path
);
704 CFArrayAppendValue(builder
->candidatePaths
, path
);
706 /* The path is not partial if the last cert is self-signed.
707 * The path is also not partial if the issuer of the last cert was the subject
708 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
709 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
710 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
711 SecCertificatePathVCIsCycleInGraph(path
)) {
712 if (!builder
->considerRejected
) {
713 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
714 CFArrayAppendValue(builder
->rejectedPaths
, path
);
716 /* This path was previously rejected as unanchored non-partial, but now that
717 * we're considering rejected paths, this is a candidate. */
718 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
719 CFArrayAppendValue(builder
->candidatePaths
, path
);
728 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
729 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
730 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
731 CFDictionaryAddValue(oldOptions
, key
, value
);
733 CFAssignRetained(policy
->_options
, oldOptions
);
736 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
737 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
738 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
739 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
741 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
742 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
743 CFStringRef policyName
= SecPolicyGetName(policy
);
744 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
745 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
747 /* Query the pinning database for this policy */
748 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
749 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
750 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
751 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
752 CFReleaseNull(query
);
753 if (!results
) { continue; } //No rules for this hostname or policyName
755 /* Found pinning policies. Apply them to the path builder. */
756 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
757 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
758 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
759 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
761 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
762 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
766 /* Create the new policies with pinning rules (preserving other ANDed policies). */
767 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
768 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
769 if (!newPolicy
) { continue; }
770 addOptionsToPolicy(newPolicy
, newOptions
);
771 SecPolicySetName(newPolicy
, dbPolicyName
);
772 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
773 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
774 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
776 if (newRulesIX
== 0) {
777 /* For the first set of pinning rules, replace this PVC's policies */
778 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
780 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
781 * Create another PVC for this dicitionary. */
782 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
783 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
784 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
787 CFReleaseNull(newPolicy
);
788 CFReleaseNull(newPolicies
);
790 CFReleaseNull(results
);
792 CFReleaseNull(policies
);
796 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
797 SecPathBuilderAddPinningPolicies(builder
);
799 /* We need to find and set constraints on the leaf-only path */
800 SecCertificatePathVCRef path
= builder
->path
;
801 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
803 SecCertificateSourceRef source
= NULL
;
804 bool isAnchor
= false;
805 CFArrayRef constraints
= NULL
;
806 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
810 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
812 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
813 CFReleaseSafe(constraints
);
815 SecCertificatePathVCSetIsAnchored(path
);
816 CFArrayAppendValue(builder
->candidatePaths
, path
);
819 __block
bool leafChecksFail
= true;
820 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
821 SecPVCLeafChecks(pvc
);
822 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
824 builder
->considerRejected
= leafChecksFail
;
826 builder
->state
= SecPathBuilderGetNext
;
830 /* Given the builder, a partial chain partial and the parents array, construct
831 a SecCertificatePath for each parent. After discarding previously
832 considered paths and paths with cycles, sort out which array each path
833 should go in, if any. */
834 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
835 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
836 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
837 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
839 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
840 SecCertificateRef parent
= (SecCertificateRef
)
841 CFArrayGetValueAtIndex(parents
, parentIX
);
842 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
844 if (ixOfParent
!= kCFNotFound
) {
845 /* partial already contains parent. Let's not add the same
846 certificate again. */
847 if (ixOfParent
== rootIX
) {
848 /* parent is equal to the root of the partial, so partial
849 looks to be self issued. */
850 SecCertificatePathVCSetSelfIssued(partial
);
855 /* FIXME Add more sanity checks to see that parent really can be
856 a parent of partial_root. subjectKeyID == authorityKeyID,
857 signature algorithm matches public key algorithm, etc. */
858 SecCertificateSourceRef source
= NULL
;
859 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
860 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
861 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
862 CFReleaseSafe(constraints
);
865 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
866 CFSetAddValue(builder
->allPaths
, path
);
868 SecCertificatePathVCSetIsAnchored(path
);
869 if (SecPathBuilderIsPartial(builder
, path
)) {
870 /* Insert path right at the current position since it's a new
872 CFArrayInsertValueAtIndex(builder
->partialPaths
,
873 ++builder
->partialIX
, path
);
874 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
875 parentIX
+ 1, num_parents
, path
);
877 secdebug("trust", "found new path %@", path
);
883 /* Callback for the SecPathBuilderGetNext() functions call to
884 SecCertificateSourceCopyParents(). */
885 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
886 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
887 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
888 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
889 secdebug("async", "%@ parents %@", partial
, parents
);
890 SecPathBuilderProcessParents(builder
, partial
, parents
);
892 builder
->state
= SecPathBuilderGetNext
;
893 SecPathBuilderStep(builder
);
896 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
897 /* If we have any candidates left to go return those first. */
898 if (CFArrayGetCount(builder
->candidatePaths
)) {
899 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
900 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
901 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
902 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
904 SecPathBuilderSetPath(builder
, path
);
905 builder
->state
= SecPathBuilderValidatePath
;
909 /* If we are considering rejected chains we check each rejected path
910 with SecPathBuilderIsPartial() which checks the signature chain and
911 either drops the path if it's not properly signed, add it as a
912 candidate if it has a trusted anchor, or adds it as a partial
913 to be considered once we finish considering all the rejects. */
914 if (builder
->considerRejected
) {
915 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
918 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
919 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
920 if (SecPathBuilderIsPartial(builder
, path
)) {
921 CFArrayInsertValueAtIndex(builder
->partialPaths
,
922 ++builder
->partialIX
, path
);
924 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
926 /* Keep going until we have moved all rejected partials into
927 the regular partials or candidates array. */
932 /* If builder->partialIX is < 0 we have considered all partial chains
933 this block must ensure partialIX >= 0 if execution continues past
935 if (builder
->partialIX
< 0) {
936 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
937 if (builder
->nextParentSource
< num_sources
) {
938 builder
->nextParentSource
++;
939 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
940 builder
->nextParentSource
, num_sources
);
942 /* We've run out of new sources to consider so let's look at
943 rejected chains and after that even consider partials
945 FIXME we might not want to consider partial paths that
946 are subsets of other partial paths, or not consider them
947 at all if we already have an (unpreferred) accept or anchored reject */
948 if (!builder
->considerRejected
) {
949 builder
->considerRejected
= true;
950 secdebug("trust", "considering rejected paths");
951 } else if (!builder
->considerPartials
) {
952 builder
->considerPartials
= true;
953 secdebug("trust", "considering partials");
955 /* We're all out of options, so we can't produce any more
956 candidates. Let's calculate details and return the best
958 builder
->state
= SecPathBuilderComputeDetails
;
962 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
963 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
967 /* We know builder->partialIX >= 0 if we get here. */
968 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
969 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
970 /* Don't try to extend partials anymore once we are in the considerPartials
971 state, since at this point every partial has been extended with every
972 possible parentSource already. */
973 if (builder
->considerPartials
) {
974 --builder
->partialIX
;
975 SecPathBuilderSetPath(builder
, partial
);
976 builder
->state
= SecPathBuilderValidatePath
;
980 /* Don't try to extend partials anymore if we already have too many chains. */
981 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
982 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
983 CFSetGetCount(builder
->allPaths
));
984 builder
->partialIX
= -1;
988 /* Attempt to extend this partial path with another certificate. This
989 should give us a list of potential parents to consider. */
990 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
991 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
994 /* Attempt to extend partial, leaving all possible extended versions
995 of partial in builder->extendedPaths. */
996 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
997 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
998 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
999 SecCertificateSourceRef source
;
1000 if (sourceIX
< num_anchor_sources
) {
1001 source
= (SecCertificateSourceRef
)
1002 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
1003 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
1004 num_anchor_sources
);
1006 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
1007 source
= (SecCertificateSourceRef
)
1008 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
1009 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1010 builder
->nextParentSource
);
1012 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
1013 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
1014 return SecCertificateSourceCopyParents(source
, root
,
1015 builder
, SecPathBuilderExtendPaths
);
1017 --builder
->partialIX
;
1023 /* One or more of the policies did not accept the candidate path. */
1024 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1027 builder
->state
= SecPathBuilderGetNext
;
1029 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1030 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1032 if (bestPathIsEV
&& !isEV
) {
1033 /* We never replace an ev reject with a non ev reject. */
1037 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1038 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1039 SecCertificatePathVCSetScore(builder
->path
, score
);
1041 /* The current chain is valid for EV, but revocation checking failed. We
1042 replace any previously accepted or rejected non EV chains with the
1044 if (isEV
&& !bestPathIsEV
) {
1047 if (!builder
->bestPath
|| score
> bestPathScore
) {
1048 if (builder
->bestPath
) {
1050 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1051 (bestPathIsEV
? "" : "non "),
1052 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1054 (isEV
? "" : "non "), (long)score
, builder
->path
);
1056 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1057 (isEV
? "" : "non "), score
, builder
->path
);
1060 builder
->bestPath
= builder
->path
;
1062 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1063 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1067 /* All policies accepted the candidate path. */
1068 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1069 if (!builder
) { return; }
1070 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1071 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1072 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1073 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1074 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1075 /* Score this path. Note that all points awarded or deducted in
1076 * SecCertificatePathScore are < 100,000 */
1077 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1078 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1079 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1080 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1081 if (currScore
> bestScore
) {
1082 // current path is better than existing best path
1083 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1084 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1085 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1087 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1089 builder
->bestPath
= builder
->path
;
1092 /* If we found the best accept we can, we want to switch directly to the
1093 SecPathBuilderComputeDetails state here, since we're done. */
1094 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1095 builder
->state
= SecPathBuilderComputeDetails
;
1097 builder
->state
= SecPathBuilderGetNext
;
1100 /* Return true iff a given path satisfies all the specified policies at
1102 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1104 if (builder
->considerRejected
) {
1105 SecPathBuilderReject(builder
);
1109 builder
->state
= SecPathBuilderDidValidatePath
;
1111 /* Revocation checking is now done before path checks, to ensure that
1112 we have OCSP responses for CT checking and that isAllowlisted is
1113 appropriately set for other checks. */
1114 bool completed
= SecPathBuilderCheckRevocation(builder
);
1116 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1117 SecPVCPathChecks(pvc
);
1123 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1124 /* We perform the revocation required policy checks here because
1125 * this is the state we call back into once all the asynchronous
1126 * revocation check calls are done. */
1127 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1128 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1131 if (SecPathBuilderIsOkResult(builder
)) {
1132 SecPathBuilderAccept(builder
);
1134 SecPathBuilderReject(builder
);
1136 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1140 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1141 /* We have to re-do all the checks so that the results get set in the
1142 * PVC for the best path, as the last path checked may not have been the best. */
1143 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1144 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1146 __block
bool completed
= true;
1147 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1148 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1149 completed
&= SecPathBuilderCheckRevocation(builder
);
1150 for (ix
= 1; ix
< pathLength
; ++ix
) {
1151 SecPVCParentCertificateChecks(pvc
, ix
);
1153 SecPVCPathChecks(pvc
);
1156 builder
->state
= SecPathBuilderReportResult
;
1158 /* Check revocation responses. */
1159 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1160 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1163 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1164 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1165 SecCertificatePathVCResetScore(builder
->bestPath
);
1166 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1172 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1173 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1174 0, &kCFTypeDictionaryKeyCallBacks
,
1175 &kCFTypeDictionaryValueCallBacks
);
1178 /* isEV is not set unless also CT verified. Here, we need to check that we
1179 * got a revocation response as well. */
1180 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1181 #if !TARGET_OS_WATCH
1182 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1183 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1184 if (nextUpdate
!= 0) {
1186 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1190 /* Successful revocation check, so this cert is EV */
1191 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1192 kCFBooleanTrue
); /* iOS key */
1193 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1194 kCFBooleanTrue
); /* unified API key */
1195 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1196 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1197 if (leafCompanyName
) {
1198 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1199 leafCompanyName
); /* iOS key */
1200 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1201 leafCompanyName
); /* unified API key */
1202 CFRelease(leafCompanyName
);
1208 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1209 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1210 if (nextUpdate
!= 0) {
1211 /* always populate revocation info for successful revocation check */
1212 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1213 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1214 validUntil
); /* iOS key */
1215 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1216 validUntil
); /* unified API key */
1217 CFRelease(validUntil
);
1218 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1219 kCFBooleanTrue
); /* iOS key */
1220 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1221 kCFBooleanTrue
); /* unified API key */
1222 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1223 /* populate revocation info for failed revocation check with EV */
1224 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1225 kCFBooleanFalse
); /* iOS key */
1226 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1227 kCFBooleanFalse
); /* unified API key */
1231 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1232 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1237 /* This will trigger the outer step function to call the completion
1239 builder
->state
= NULL
;
1243 /* @function SecPathBuilderStep
1244 @summary This is the core of the async engine.
1245 @description Return false iff job is complete, true if a network request
1247 builder->state is a function pointer which is to be invoked.
1248 If you call this function from within a builder->state invocation it
1249 immediately returns true.
1250 Otherwise the following steps are repeated endlessly (unless a step returns)
1251 builder->state is invoked. If it returns true and builder->state is still
1252 non NULL this proccess is repeated.
1253 If a state returns false, SecPathBuilder will return true
1254 if builder->state is non NULL.
1255 If builder->state is NULL then regardless of what the state function returns
1256 the completion callback will be invoked and the builder will be deallocated.
1258 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1259 if (builder
->activations
) {
1260 secdebug("async", "activations: %lu returning true",
1261 builder
->activations
);
1265 secdebug("async", "activations: %lu", builder
->activations
);
1266 builder
->activations
++;
1267 while (builder
->state
&& builder
->state(builder
));
1268 --builder
->activations
;
1270 if (builder
->state
) {
1271 secdebug("async", "waiting for async reply, exiting");
1272 /* A state returned false, it's waiting for network traffic. Let's
1277 if (builder
->activations
) {
1278 /* There is still at least one other running instance of this builder
1279 somewhere on the stack, we let that instance take care of sending
1280 the client a response. */
1284 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1285 SecTrustResultType result
= pvc
->result
;
1287 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1288 result
= kSecTrustResultProceed
;
1291 secinfo("trust", "completed: %@ details: %@ result: %d",
1292 builder
->bestPath
, pvc
->details
, result
);
1294 if (builder
->completed
) {
1295 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1296 builder
->completed(builder
->context
, resultPath
,
1297 pvc
->details
, builder
->info
, result
);
1298 CFReleaseNull(resultPath
);
1301 /* Finally, destroy the builder and free it. */
1302 SecPathBuilderDestroy(builder
);
1308 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1309 return (builder
) ? builder
->queue
: NULL
;
1312 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1313 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1317 // MARK: SecTrustServer
1318 /********************************************************
1319 ****************** SecTrustServer **********************
1320 ********************************************************/
1322 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1325 SecTrustServerEvaluateCompleted(const void *userData
,
1326 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1327 SecTrustResultType result
) {
1328 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1329 TrustdHealthAnalyticsLogEvaluationCompleted();
1330 evaluated(result
, details
, info
, chain
, NULL
);
1331 Block_release(evaluated
);
1335 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
)) {
1336 /* We need an array containing at least one certificate to proceed. */
1337 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1338 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1339 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1340 CFReleaseSafe(certError
);
1343 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1344 /* Call the actual evaluator function. */
1345 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1346 certificates
, anchors
,
1347 anchorsOnly
, keychainsAllowed
, policies
,
1348 responses
, SCTs
, trustedLogs
,
1349 verifyTime
, accessGroups
, exceptions
,
1350 SecTrustServerEvaluateCompleted
, userData
);
1351 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1355 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1356 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
) {
1357 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1358 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1359 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1361 if (tr
== kSecTrustResultInvalid
) {
1364 CFRetainSafe(error
);
1368 *pdetails
= details
;
1369 CFRetainSafe(details
);
1377 CFRetainSafe(chain
);
1380 dispatch_semaphore_signal(done
);
1382 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1383 dispatch_release(done
);