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 CFStringRef revocation_check_method
;
138 SecCertificatePathVCRef bestPath
;
139 CFMutableDictionaryRef info
;
142 bool (*state
)(SecPathBuilderRef
);
143 SecPathBuilderCompleted completed
;
145 TrustAnalyticsBuilder
* analyticsData
;
148 /* State functions. Return false if a async job was scheduled, return
149 true to execute the next state. */
150 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
);
151 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
152 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
153 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
154 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
155 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
157 /* Forward declarations. */
158 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
159 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
160 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
);
162 static void SecPathBuilderInit(SecPathBuilderRef builder
,
163 CFDataRef clientAuditToken
, CFArrayRef certificates
,
164 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
165 CFArrayRef policies
, CFArrayRef ocspResponses
,
166 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
167 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
168 SecPathBuilderCompleted completed
, const void *context
) {
169 secdebug("alloc", "%p", builder
);
170 CFAllocatorRef allocator
= kCFAllocatorDefault
;
172 builder
->analyticsData
= calloc(1, sizeof(TrustAnalyticsBuilder
));
173 builder
->analyticsData
->start_time
= mach_absolute_time();
175 builder
->clientAuditToken
= (CFDataRef
)
176 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
177 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
179 builder
->nextParentSource
= 1;
181 /* <rdar://32728029> */
182 builder
->canAccessNetwork
= true;
185 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
186 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
188 builder
->allPaths
= CFSetCreateMutable(allocator
, 0, &kCFTypeSetCallBacks
);
189 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
190 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
191 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
); // Does not retain, allPaths retains members. See declaration.
193 /* Init the policy verification context. */
194 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
195 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
196 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
197 builder
->pvcCount
= 1;
198 builder
->verifyTime
= verifyTime
;
199 builder
->exceptions
= CFRetainSafe(exceptions
);
201 /* Let's create all the certificate sources we might want to use. */
202 builder
->certificateSource
=
203 SecMemoryCertificateSourceCreate(certificates
);
205 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
208 bool allowNonProduction
= false;
209 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
213 ** The order here avoids the most expensive methods if the cheaper methods
214 ** produce an acceptable chain: client-provided, keychains, network-fetched.
216 #if !TARGET_OS_BRIDGE
217 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
218 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
219 if (keychainsAllowed
) {
220 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
222 /* On OS X, need additional parent source to search legacy keychain files. */
223 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
224 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
229 /* Add the Apple, system, and user anchor certificate db to the search list
230 if we don't explicitly trust them. */
231 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
232 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
234 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
237 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
238 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
240 #else /* TARGET_OS_BRIDGE */
241 /* Bridge can only access memory sources. */
242 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
244 /* Add the Apple, system, and user anchor certificate db to the search list
245 if we don't explicitly trust them. */
246 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
248 #endif /* !TARGET_OS_BRIDGE */
251 ** The order here allows a client-provided anchor to overrule
252 ** a user or admin trust setting which can overrule the system anchors.
253 ** Apple's anchors cannot be overriden by a trust setting.
255 #if !TARGET_OS_BRIDGE
256 if (builder
->anchorSource
) {
257 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
260 /* Only add the system and user anchor certificate db to the
261 anchorSources if we are supposed to trust them. */
262 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
264 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
265 #else /* TARGET_OS_OSX */
266 if (keychainsAllowed
&& kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
267 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
270 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
272 #else /* TARGET_OS_BRIDGE */
273 /* Bridge can only access memory sources. */
274 if (builder
->anchorSource
) {
275 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
278 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
280 #endif /* !TARGET_OS_BRIDGE */
282 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
283 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
286 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
288 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
289 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
290 CFReleaseSafe(otapkiref
);
293 /* Now let's get the leaf cert and turn it into a path. */
294 SecCertificateRef leaf
=
295 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
296 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
297 CFSetAddValue(builder
->allPaths
, path
);
298 CFArrayAppendValue(builder
->partialPaths
, path
);
300 builder
->path
= CFRetainSafe(path
);
301 SecPathBuilderSetPath(builder
, path
);
304 /* Next step is to process the leaf. We do that work on the builder queue
305 * to avoid blocking the main thread with database lookups. */
306 builder
->state
= SecPathBuilderProcessLeaf
;
307 builder
->completed
= completed
;
308 builder
->context
= context
;
311 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
312 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
313 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
314 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
315 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
316 SecPathBuilderCompleted completed
, const void *context
) {
317 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
318 memset(builder
, 0, sizeof(*builder
));
319 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
320 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
321 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
322 accessGroups
, exceptions
, completed
, context
);
326 /* Don't use this if you're going to modify the PVC array in the operation. */
327 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
328 if (!builder
->pvcs
) { return; }
331 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
332 if (!builder
->pvcs
[ix
]) { continue; }
333 operation(builder
->pvcs
[ix
], &stop
);
338 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
339 secdebug("alloc", "%p", builder
);
340 dispatch_release_null(builder
->queue
);
341 if (builder
->anchorSource
) {
342 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
343 builder
->anchorSource
= NULL
;
345 if (builder
->certificateSource
) {
346 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
347 builder
->certificateSource
= NULL
;
349 if (builder
->itemCertificateSource
) {
350 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
351 builder
->itemCertificateSource
= NULL
;
353 if (builder
->appleAnchorSource
) {
354 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
355 builder
->appleAnchorSource
= NULL
;
357 CFReleaseNull(builder
->clientAuditToken
);
358 CFReleaseNull(builder
->anchorSources
);
359 CFReleaseNull(builder
->parentSources
);
360 CFReleaseNull(builder
->allPaths
);
361 CFReleaseNull(builder
->partialPaths
);
362 CFReleaseNull(builder
->rejectedPaths
);
363 CFReleaseNull(builder
->candidatePaths
);
364 CFReleaseNull(builder
->ocspResponses
);
365 CFReleaseNull(builder
->signedCertificateTimestamps
);
366 CFReleaseNull(builder
->trustedLogs
);
367 CFReleaseNull(builder
->path
);
368 CFReleaseNull(builder
->revocation_check_method
);
369 CFReleaseNull(builder
->info
);
370 CFReleaseNull(builder
->exceptions
);
372 free(builder
->analyticsData
);
373 builder
->analyticsData
= NULL
;
377 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
378 if (builder
->pvcs
[ix
]) {
379 SecPVCDelete(builder
->pvcs
[ix
]);
380 free(builder
->pvcs
[ix
]);
384 builder
->pvcs
= NULL
;
388 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
389 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
391 CFRetainAssign(builder
->path
, path
);
393 CFReleaseNull(builder
->info
);
395 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
396 SecPVCSetPath(pvc
, path
);
401 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
402 return builder
->canAccessNetwork
;
405 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
406 if (builder
->canAccessNetwork
!= allow
) {
407 builder
->canAccessNetwork
= allow
;
410 secinfo("http", "network access re-enabled by policy");
411 /* re-enabling network_access re-adds kSecCAIssuerSource as
413 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
415 /* <rdar://32728029> */
416 secnotice("http", "network access not allowed on WatchOS");
417 builder
->canAccessNetwork
= false;
420 secinfo("http", "network access disabled by policy");
421 /* disabling network_access removes kSecCAIssuerSource from
422 the list of parent sources. */
423 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
424 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
427 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
432 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
434 return CFRetainSafe(builder
->ocspResponses
);
437 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
439 return CFRetainSafe(builder
->signedCertificateTimestamps
);
442 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
444 return CFRetainSafe(builder
->trustedLogs
);
447 SecCertificateSourceRef
SecPathBuilderGetAppAnchorSource(SecPathBuilderRef builder
)
449 return builder
->anchorSource
;
452 CFSetRef
SecPathBuilderGetAllPaths(SecPathBuilderRef builder
)
454 return builder
->allPaths
;
457 TrustAnalyticsBuilder
*SecPathBuilderGetAnalyticsData(SecPathBuilderRef builder
)
459 return builder
->analyticsData
;
462 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
464 return builder
->bestPath
;
467 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
468 return builder
->path
;
471 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
472 return builder
->verifyTime
;
475 bool SecPathBuilderHasTemporalParentChecks(SecPathBuilderRef builder
) {
476 __block
bool validIntermediates
= false;
477 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
478 CFArrayForEach(pvc
->policies
, ^(const void *value
) {
479 SecPolicyRef policy
= (SecPolicyRef
)value
;
480 if (CFDictionaryContainsKey(policy
->_options
, kSecPolicyCheckTemporalValidity
)) {
481 validIntermediates
= true;
486 return validIntermediates
;
489 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
490 return SecCertificatePathVCGetCount(builder
->path
);
493 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
494 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
497 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
498 return SecCertificatePathVCIsAnchored(builder
->path
);
501 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
502 return --builder
->asyncJobCount
;
505 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
506 builder
->asyncJobCount
= jobCount
;
507 secdebug("rvc", "set asyncJobCount to %d", builder
->asyncJobCount
);
510 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
511 return builder
->info
;
514 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
515 return builder
->revocation_check_method
;
518 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
519 CFRetainAssign(builder
->revocation_check_method
, method
);
520 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
523 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
524 return builder
->online_revocation
;
527 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
528 builder
->online_revocation
= true;
529 secdebug("rvc", "revocation force online check");
532 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
533 return builder
->exceptions
;
536 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
537 return builder
->pvcCount
;
540 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
541 if (ix
> (builder
->pvcCount
- 1)) {
544 return builder
->pvcs
[ix
];
547 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
548 CFIndex ix
, CFTypeRef result
, bool force
) {
549 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
550 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
554 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
555 /* If any of the PVCs passed, we accept the path. */
556 __block
bool acceptPath
= false;
557 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
558 acceptPath
|= SecPVCIsOkResult(pvc
);
563 SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
564 /* Return the first PVC that passed */
565 __block SecPVCRef resultPVC
= NULL
;
566 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
567 if (SecPVCIsOkResult(pvc
)) {
572 if (resultPVC
) { return resultPVC
; }
574 /* If we didn't return a passing PVC, return the first PVC. */
575 return builder
->pvcs
[0];
578 /* This function assumes that the input source is an anchor source */
579 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
580 SecCertificateRef certificate
) {
581 __block
bool result
= false;
582 CFArrayRef constraints
= NULL
;
583 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
585 /* Unrestricted certificates:
586 * -those that come from anchor sources with no constraints
587 * -self-signed certificates with empty contraints arrays
589 Boolean selfSigned
= false;
590 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
591 if ((NULL
== source
->copyUsageConstraints
) ||
592 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
593 secinfo("trust", "unrestricted anchor%s",
594 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
599 /* Get the trust settings result for the PVCs. Only one PVC need match to
600 * trigger the anchor behavior -- policy validation will handle whether the
601 * path is truly anchored for that PVC. */
602 require(constraints
, out
);
603 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
604 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
605 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
608 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
609 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
610 // For our purposes, this is an anchor.
611 secinfo("trust", "complex trust settings anchor");
616 if (settingsResult
== kSecTrustSettingsResultDeny
) {
617 /* We consider denied certs "anchors" because the trust decision
618 is set regardless of building the chain further. The policy
619 validation will handle rejecting this chain. */
620 secinfo("trust", "complex trust settings denied anchor");
627 CFReleaseNull(constraints
);
631 /* Source returned in foundInSource has the same lifetime as the builder. */
632 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
633 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
634 /* We look through the anchor sources in order. They are ordered in
635 SecPathBuilderInit so that process anchors override user anchors which
636 override system anchors. */
637 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
639 for (ix
= 0; ix
< count
; ++ix
) {
640 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
641 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
642 if (SecCertificateSourceContains(source
, certificate
)) {
644 *foundInSource
= source
;
645 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
653 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
654 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
655 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
658 /* Return false if path is not a partial, if path was a valid candidate it
659 will have been added to builder->candidatePaths, if path was rejected
660 by the parent certificate checks (because it's expired or some other
661 static chaining check failed) it will have been added to rejectedPaths.
662 Return true path if path is a partial. */
663 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
664 SecCertificatePathVCRef path
) {
665 SecPathBuilderSetPath(builder
, path
);
666 __block
bool parentChecksFail
= true;
668 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
669 /* The parent checks aren't actually PVC-dependent, so theoretically,
670 * we only need to run this once per path, but we want to set the
671 * results in all PVCs. */
672 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
673 SecCertificatePathVCGetCount(path
) - 1);
676 if (!builder
->considerRejected
&& parentChecksFail
) {
677 secdebug("trust", "Found rejected path %@", path
);
678 CFArrayAppendValue(builder
->rejectedPaths
, path
);
682 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
683 /* Candidate paths with failed signatures are discarded. */
684 if (vstatus
== kSecPathVerifyFailed
) {
685 secdebug("trust", "Verify failed for path %@", path
);
689 if (vstatus
== kSecPathVerifySuccess
) {
690 /* The signature chain verified sucessfully, now let's find
691 out if we have an anchor for path. */
692 if (SecCertificatePathVCIsAnchored(path
)) {
693 secdebug("trust", "Adding candidate %@", path
);
694 CFArrayAppendValue(builder
->candidatePaths
, path
);
696 /* The path is not partial if the last cert is self-signed.
697 * The path is also not partial if the issuer of the last cert was the subject
698 * of a previous cert in the chain, indicating a cycle in the graph. See <rdar://33136765>. */
699 if (((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
700 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) ||
701 SecCertificatePathVCIsCycleInGraph(path
)) {
702 if (!builder
->considerRejected
) {
703 secdebug("trust", "Adding non-partial non-anchored reject %@", path
);
704 CFArrayAppendValue(builder
->rejectedPaths
, path
);
706 /* This path was previously rejected as unanchored non-partial, but now that
707 * we're considering rejected paths, this is a candidate. */
708 secdebug("trust", "Adding non-partial non-anchored candidate %@", path
);
709 CFArrayAppendValue(builder
->candidatePaths
, path
);
718 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
719 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
720 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
721 CFDictionaryAddValue(oldOptions
, key
, value
);
723 CFAssignRetained(policy
->_options
, oldOptions
);
726 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
727 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
728 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
729 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
731 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
732 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
733 CFStringRef policyName
= SecPolicyGetName(policy
);
734 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
735 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
737 /* Query the pinning database for this policy */
738 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
739 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
740 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
741 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
742 CFReleaseNull(query
);
743 if (!results
) { continue; } //No rules for this hostname or policyName
745 /* Found pinning policies. Apply them to the path builder. */
746 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
747 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
748 secinfo("SecPinningDb", "found pinning %lu %@ policies for hostname %@, policyName %@",
749 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
751 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
752 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
756 /* Create the new policies with pinning rules (preserving other ANDed policies). */
757 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
758 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
759 if (!newPolicy
) { continue; }
760 addOptionsToPolicy(newPolicy
, newOptions
);
761 SecPolicySetName(newPolicy
, dbPolicyName
);
762 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
763 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
764 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
766 if (newRulesIX
== 0) {
767 /* For the first set of pinning rules, replace this PVC's policies */
768 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
770 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
771 * Create another PVC for this dicitionary. */
772 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
773 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
774 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
777 CFReleaseNull(newPolicy
);
778 CFReleaseNull(newPolicies
);
780 CFReleaseNull(results
);
782 CFReleaseNull(policies
);
786 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
787 SecPathBuilderAddPinningPolicies(builder
);
789 /* We need to find and set constraints on the leaf-only path */
790 SecCertificatePathVCRef path
= builder
->path
;
791 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
793 SecCertificateSourceRef source
= NULL
;
794 bool isAnchor
= false;
795 CFArrayRef constraints
= NULL
;
796 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
800 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
802 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
803 CFReleaseSafe(constraints
);
805 SecCertificatePathVCSetIsAnchored(path
);
806 CFArrayAppendValue(builder
->candidatePaths
, path
);
809 __block
bool leafChecksFail
= true;
810 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
811 SecPVCLeafChecks(pvc
);
812 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
814 builder
->considerRejected
= leafChecksFail
;
816 builder
->state
= SecPathBuilderGetNext
;
820 /* Given the builder, a partial chain partial and the parents array, construct
821 a SecCertificatePath for each parent. After discarding previously
822 considered paths and paths with cycles, sort out which array each path
823 should go in, if any. */
824 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
825 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
826 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
827 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
829 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
830 SecCertificateRef parent
= (SecCertificateRef
)
831 CFArrayGetValueAtIndex(parents
, parentIX
);
832 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
834 if (ixOfParent
!= kCFNotFound
) {
835 /* partial already contains parent. Let's not add the same
836 certificate again. */
837 if (ixOfParent
== rootIX
) {
838 /* parent is equal to the root of the partial, so partial
839 looks to be self issued. */
840 SecCertificatePathVCSetSelfIssued(partial
);
845 /* FIXME Add more sanity checks to see that parent really can be
846 a parent of partial_root. subjectKeyID == authorityKeyID,
847 signature algorithm matches public key algorithm, etc. */
848 SecCertificateSourceRef source
= NULL
;
849 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
850 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
851 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
852 CFReleaseSafe(constraints
);
855 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
856 CFSetAddValue(builder
->allPaths
, path
);
858 SecCertificatePathVCSetIsAnchored(path
);
859 if (SecPathBuilderIsPartial(builder
, path
)) {
860 /* Insert path right at the current position since it's a new
862 CFArrayInsertValueAtIndex(builder
->partialPaths
,
863 ++builder
->partialIX
, path
);
864 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
865 parentIX
+ 1, num_parents
, path
);
867 secdebug("trust", "found new path %@", path
);
873 /* Callback for the SecPathBuilderGetNext() functions call to
874 SecCertificateSourceCopyParents(). */
875 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
876 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
877 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
878 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
879 secdebug("async", "%@ parents %@", partial
, parents
);
880 SecPathBuilderProcessParents(builder
, partial
, parents
);
882 builder
->state
= SecPathBuilderGetNext
;
883 SecPathBuilderStep(builder
);
886 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
887 /* If we have any candidates left to go return those first. */
888 if (CFArrayGetCount(builder
->candidatePaths
)) {
889 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
890 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
891 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
892 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
894 SecPathBuilderSetPath(builder
, path
);
895 builder
->state
= SecPathBuilderValidatePath
;
899 /* If we are considering rejected chains we check each rejected path
900 with SecPathBuilderIsPartial() which checks the signature chain and
901 either drops the path if it's not properly signed, add it as a
902 candidate if it has a trusted anchor, or adds it as a partial
903 to be considered once we finish considering all the rejects. */
904 if (builder
->considerRejected
) {
905 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
908 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
909 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
910 if (SecPathBuilderIsPartial(builder
, path
)) {
911 CFArrayInsertValueAtIndex(builder
->partialPaths
,
912 ++builder
->partialIX
, path
);
914 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
916 /* Keep going until we have moved all rejected partials into
917 the regular partials or candidates array. */
922 /* If builder->partialIX is < 0 we have considered all partial chains
923 this block must ensure partialIX >= 0 if execution continues past
925 if (builder
->partialIX
< 0) {
926 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
927 if (builder
->nextParentSource
< num_sources
) {
928 builder
->nextParentSource
++;
929 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
930 builder
->nextParentSource
, num_sources
);
932 /* We've run out of new sources to consider so let's look at
933 rejected chains and after that even consider partials
935 FIXME we might not want to consider partial paths that
936 are subsets of other partial paths, or not consider them
937 at all if we already have an (unpreferred) accept or anchored reject */
938 if (!builder
->considerRejected
) {
939 builder
->considerRejected
= true;
940 secdebug("trust", "considering rejected paths");
941 } else if (!builder
->considerPartials
) {
942 builder
->considerPartials
= true;
943 secdebug("trust", "considering partials");
945 /* We're all out of options, so we can't produce any more
946 candidates. Let's calculate details and return the best
948 builder
->state
= SecPathBuilderComputeDetails
;
952 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
953 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
957 /* We know builder->partialIX >= 0 if we get here. */
958 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
959 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
960 /* Don't try to extend partials anymore once we are in the considerPartials
961 state, since at this point every partial has been extended with every
962 possible parentSource already. */
963 if (builder
->considerPartials
) {
964 --builder
->partialIX
;
965 SecPathBuilderSetPath(builder
, partial
);
966 builder
->state
= SecPathBuilderValidatePath
;
970 /* Don't try to extend partials anymore if we already have too many chains. */
971 if (CFSetGetCount(builder
->allPaths
) > MAX_NUM_CHAINS
) {
972 secnotice("trust", "not building any more paths, already have %" PRIdCFIndex
,
973 CFSetGetCount(builder
->allPaths
));
974 builder
->partialIX
= -1;
978 /* Attempt to extend this partial path with another certificate. This
979 should give us a list of potential parents to consider. */
980 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
981 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
984 /* Attempt to extend partial, leaving all possible extended versions
985 of partial in builder->extendedPaths. */
986 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
987 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
988 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
989 SecCertificateSourceRef source
;
990 if (sourceIX
< num_anchor_sources
) {
991 source
= (SecCertificateSourceRef
)
992 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
993 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
996 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
997 source
= (SecCertificateSourceRef
)
998 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
999 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
1000 builder
->nextParentSource
);
1002 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
1003 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
1004 return SecCertificateSourceCopyParents(source
, root
,
1005 builder
, SecPathBuilderExtendPaths
);
1007 --builder
->partialIX
;
1013 /* One or more of the policies did not accept the candidate path. */
1014 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
1017 builder
->state
= SecPathBuilderGetNext
;
1019 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
1020 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1022 if (bestPathIsEV
&& !isEV
) {
1023 /* We never replace an ev reject with a non ev reject. */
1027 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1028 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
1029 SecCertificatePathVCSetScore(builder
->path
, score
);
1031 /* The current chain is valid for EV, but revocation checking failed. We
1032 replace any previously accepted or rejected non EV chains with the
1034 if (isEV
&& !bestPathIsEV
) {
1037 if (!builder
->bestPath
|| score
> bestPathScore
) {
1038 if (builder
->bestPath
) {
1040 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1041 (bestPathIsEV
? "" : "non "),
1042 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1044 (isEV
? "" : "non "), (long)score
, builder
->path
);
1046 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
1047 (isEV
? "" : "non "), score
, builder
->path
);
1050 builder
->bestPath
= builder
->path
;
1052 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
1053 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1057 /* All policies accepted the candidate path. */
1058 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1059 if (!builder
) { return; }
1060 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1061 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1062 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1063 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1064 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1065 /* Score this path. Note that all points awarded or deducted in
1066 * SecCertificatePathScore are < 100,000 */
1067 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1068 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1069 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1070 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1071 if (currScore
> bestScore
) {
1072 // current path is better than existing best path
1073 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1074 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1075 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1077 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1079 builder
->bestPath
= builder
->path
;
1082 /* If we found the best accept we can, we want to switch directly to the
1083 SecPathBuilderComputeDetails state here, since we're done. */
1084 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1085 builder
->state
= SecPathBuilderComputeDetails
;
1087 builder
->state
= SecPathBuilderGetNext
;
1090 /* Return true iff a given path satisfies all the specified policies at
1092 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1094 if (builder
->considerRejected
) {
1095 SecPathBuilderReject(builder
);
1099 builder
->state
= SecPathBuilderDidValidatePath
;
1101 /* Revocation checking is now done before path checks, to ensure that
1102 we have OCSP responses for CT checking and that isAllowlisted is
1103 appropriately set for other checks. */
1104 bool completed
= SecPathBuilderCheckRevocation(builder
);
1106 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1107 SecPVCPathChecks(pvc
);
1113 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1114 /* We perform the revocation required policy checks here because
1115 * this is the state we call back into once all the asynchronous
1116 * revocation check calls are done. */
1117 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1118 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1121 if (SecPathBuilderIsOkResult(builder
)) {
1122 SecPathBuilderAccept(builder
);
1124 SecPathBuilderReject(builder
);
1126 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1130 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1131 /* We have to re-do all the checks so that the results get set in the
1132 * PVC for the best path, as the last path checked may not have been the best. */
1133 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1134 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1136 __block
bool completed
= true;
1137 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1138 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1139 completed
&= SecPathBuilderCheckRevocation(builder
);
1140 for (ix
= 1; ix
< pathLength
; ++ix
) {
1141 SecPVCParentCertificateChecks(pvc
, ix
);
1143 SecPVCPathChecks(pvc
);
1146 builder
->state
= SecPathBuilderReportResult
;
1148 /* Check revocation responses. */
1149 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1150 SecPVCPathCheckRevocationResponsesReceived(pvc
);
1153 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1154 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1155 SecCertificatePathVCResetScore(builder
->bestPath
);
1156 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1162 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1163 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1164 0, &kCFTypeDictionaryKeyCallBacks
,
1165 &kCFTypeDictionaryValueCallBacks
);
1168 /* isEV is not set unless also CT verified. Here, we need to check that we
1169 * got a revocation response as well. */
1170 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1171 #if !TARGET_OS_WATCH
1172 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1173 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1174 if (nextUpdate
!= 0) {
1176 /* <rdar://32728029> We don't do networking on watchOS, so we can't require OCSP for EV */
1180 /* Successful revocation check, so this cert is EV */
1181 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1182 kCFBooleanTrue
); /* iOS key */
1183 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1184 kCFBooleanTrue
); /* unified API key */
1185 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1186 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1187 if (leafCompanyName
) {
1188 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1189 leafCompanyName
); /* iOS key */
1190 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1191 leafCompanyName
); /* unified API key */
1192 CFRelease(leafCompanyName
);
1198 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1199 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1200 if (nextUpdate
!= 0) {
1201 /* always populate revocation info for successful revocation check */
1202 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1203 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1204 validUntil
); /* iOS key */
1205 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1206 validUntil
); /* unified API key */
1207 CFRelease(validUntil
);
1208 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1209 kCFBooleanTrue
); /* iOS key */
1210 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1211 kCFBooleanTrue
); /* unified API key */
1212 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1213 /* populate revocation info for failed revocation check with EV */
1214 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1215 kCFBooleanFalse
); /* iOS key */
1216 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1217 kCFBooleanFalse
); /* unified API key */
1221 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1222 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1227 /* This will trigger the outer step function to call the completion
1229 builder
->state
= NULL
;
1233 /* @function SecPathBuilderStep
1234 @summary This is the core of the async engine.
1235 @description Return false iff job is complete, true if a network request
1237 builder->state is a function pointer which is to be invoked.
1238 If you call this function from within a builder->state invocation it
1239 immediately returns true.
1240 Otherwise the following steps are repeated endlessly (unless a step returns)
1241 builder->state is invoked. If it returns true and builder->state is still
1242 non NULL this proccess is repeated.
1243 If a state returns false, SecPathBuilder will return true
1244 if builder->state is non NULL.
1245 If builder->state is NULL then regardless of what the state function returns
1246 the completion callback will be invoked and the builder will be deallocated.
1248 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1249 if (builder
->activations
) {
1250 secdebug("async", "activations: %lu returning true",
1251 builder
->activations
);
1255 secdebug("async", "activations: %lu", builder
->activations
);
1256 builder
->activations
++;
1257 while (builder
->state
&& builder
->state(builder
));
1258 --builder
->activations
;
1260 if (builder
->state
) {
1261 secdebug("async", "waiting for async reply, exiting");
1262 /* A state returned false, it's waiting for network traffic. Let's
1267 if (builder
->activations
) {
1268 /* There is still at least one other running instance of this builder
1269 somewhere on the stack, we let that instance take care of sending
1270 the client a response. */
1274 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1275 SecTrustResultType result
= pvc
->result
;
1277 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1278 result
= kSecTrustResultProceed
;
1281 secinfo("trust", "completed: %@ details: %@ result: %d",
1282 builder
->bestPath
, pvc
->details
, result
);
1284 if (builder
->completed
) {
1285 CFArrayRef resultPath
= SecCertificatePathVCCopyCertificates(builder
->bestPath
);
1286 builder
->completed(builder
->context
, resultPath
,
1287 pvc
->details
, builder
->info
, result
);
1288 CFReleaseNull(resultPath
);
1291 /* Finally, destroy the builder and free it. */
1292 SecPathBuilderDestroy(builder
);
1298 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1299 return (builder
) ? builder
->queue
: NULL
;
1302 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1303 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1307 // MARK: SecTrustServer
1308 /********************************************************
1309 ****************** SecTrustServer **********************
1310 ********************************************************/
1312 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
);
1315 SecTrustServerEvaluateCompleted(const void *userData
,
1316 CFArrayRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1317 SecTrustResultType result
) {
1318 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1319 TrustdHealthAnalyticsLogSuccess(TAEventEvaluationCompleted
);
1320 evaluated(result
, details
, info
, chain
, NULL
);
1321 Block_release(evaluated
);
1325 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
)) {
1326 /* We need an array containing at least one certificate to proceed. */
1327 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1328 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1329 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1330 CFReleaseSafe(certError
);
1333 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1334 /* Call the actual evaluator function. */
1335 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1336 certificates
, anchors
,
1337 anchorsOnly
, keychainsAllowed
, policies
,
1338 responses
, SCTs
, trustedLogs
,
1339 verifyTime
, accessGroups
, exceptions
,
1340 SecTrustServerEvaluateCompleted
, userData
);
1341 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1345 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1346 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
) {
1347 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1348 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1349 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, CFArrayRef chain
, CFErrorRef error
) {
1351 if (tr
== kSecTrustResultInvalid
) {
1354 CFRetainSafe(error
);
1358 *pdetails
= details
;
1359 CFRetainSafe(details
);
1367 CFRetainSafe(chain
);
1370 dispatch_semaphore_signal(done
);
1372 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1373 dispatch_release(done
);