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/SecCertificatePath.h>
44 #include <Security/SecFramework.h>
45 #include <Security/SecPolicyPriv.h>
46 #include <Security/SecPolicyInternal.h>
47 #include <Security/SecTrustSettingsPriv.h>
48 #include <Security/SecTask.h>
49 #include <CoreFoundation/CFRuntime.h>
50 #include <CoreFoundation/CFSet.h>
51 #include <CoreFoundation/CFString.h>
52 #include <CoreFoundation/CFNumber.h>
53 #include <CoreFoundation/CFArray.h>
54 #include <CoreFoundation/CFPropertyList.h>
55 #include <AssertMacros.h>
60 #include <sys/codesign.h>
61 #include <Security/SecBase.h>
62 #include "SecRSAKey.h"
63 #include <libDER/oids.h>
64 #include <utilities/debugging.h>
65 #include <utilities/SecCFWrappers.h>
66 #include <Security/SecInternal.h>
67 #include <ipc/securityd_client.h>
68 #include <CommonCrypto/CommonDigest.h>
69 #include "OTATrustUtilities.h"
70 #include "personalization.h"
71 #include <utilities/SecInternalReleasePriv.h>
74 #include <Security/SecTaskPriv.h>
77 #define MAX_CHAIN_LENGTH 15
78 #define ACCEPT_PATH_SCORE 10000000
80 /* Forward declaration for use in SecCertificateSource. */
81 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
84 // MARK: SecPathBuilder
85 /********************************************************
86 *************** SecPathBuilder object ******************
87 ********************************************************/
88 struct SecPathBuilder
{
89 dispatch_queue_t queue
;
90 CFDataRef clientAuditToken
;
91 SecCertificateSourceRef certificateSource
;
92 SecCertificateSourceRef itemCertificateSource
;
93 SecCertificateSourceRef anchorSource
;
94 SecCertificateSourceRef appleAnchorSource
;
95 CFMutableArrayRef anchorSources
;
96 CFIndex nextParentSource
;
97 CFMutableArrayRef parentSources
;
98 CFArrayRef ocspResponses
; // Stapled OCSP responses
99 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
100 CFArrayRef trustedLogs
; // Trusted CT logs
101 CFAbsoluteTime verifyTime
;
102 CFArrayRef exceptions
;
104 /* Hashed set of all paths we've constructed so far, used to prevent
105 re-considering a path that was already constructed once before.
106 Note that this is the only container in which certificatePath
107 objects are retained.
108 Every certificatePath being considered is always in allPaths and in at
109 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
110 all of which don't retain their values. */
111 CFMutableSetRef allPaths
;
113 /* No trusted anchor, satisfies the linking to intermediates for all
114 policies (unless considerRejected is true). */
115 CFMutableArrayRef partialPaths
;
116 /* No trusted anchor, does not satisfy linking to intermediates for all
118 CFMutableArrayRef rejectedPaths
;
119 /* Trusted anchor, satisfies the policies so far. */
120 CFMutableArrayRef candidatePaths
;
124 bool considerRejected
;
125 bool considerPartials
;
126 bool canAccessNetwork
;
131 SecCertificatePathVCRef path
;
132 unsigned int asyncJobCount
;
133 bool online_revocation
;
134 CFStringRef revocation_check_method
;
136 SecCertificatePathVCRef bestPath
;
137 CFMutableDictionaryRef info
;
140 bool (*state
)(SecPathBuilderRef
);
141 SecPathBuilderCompleted completed
;
145 /* State functions. Return false if a async job was scheduled, return
146 true to execute the next state. */
147 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
);
148 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
149 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
150 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
151 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
152 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
154 /* Forward declarations. */
155 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
156 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
157 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
);
159 static void SecPathBuilderInit(SecPathBuilderRef builder
,
160 CFDataRef clientAuditToken
, CFArrayRef certificates
,
161 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
162 CFArrayRef policies
, CFArrayRef ocspResponses
,
163 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
164 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
165 SecPathBuilderCompleted completed
, const void *context
) {
166 secdebug("alloc", "%p", builder
);
167 CFAllocatorRef allocator
= kCFAllocatorDefault
;
169 builder
->clientAuditToken
= (CFDataRef
)
170 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
171 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
173 builder
->nextParentSource
= 1;
175 builder
->canAccessNetwork
= true;
178 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
179 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
180 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
181 &kCFTypeSetCallBacks
);
183 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
184 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
185 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
187 /* Init the policy verification context. */
188 builder
->pvcs
= malloc(sizeof(SecPVCRef
));
189 builder
->pvcs
[0] = malloc(sizeof(struct OpaqueSecPVC
));
190 SecPVCInit(builder
->pvcs
[0], builder
, policies
);
191 builder
->pvcCount
= 1;
192 builder
->verifyTime
= verifyTime
;
193 builder
->exceptions
= CFRetainSafe(exceptions
);
195 /* Let's create all the certificate sources we might want to use. */
196 builder
->certificateSource
=
197 SecMemoryCertificateSourceCreate(certificates
);
199 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
202 bool allowNonProduction
= false;
203 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
207 ** The order here avoids the most expensive methods if the cheaper methods
208 ** produce an acceptable chain: client-provided, keychains, network-fetched.
210 #if !TARGET_OS_BRIDGE
211 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
212 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
213 if (keychainsAllowed
) {
214 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
216 /* On OS X, need additional parent source to search legacy keychain files. */
217 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
218 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
223 /* Add the Apple, system, and user anchor certificate db to the search list
224 if we don't explicitly trust them. */
225 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
226 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
228 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
231 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
232 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
234 #else /* TARGET_OS_BRIDGE */
235 /* Bridge can only access memory sources. */
236 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
238 /* Add the Apple, system, and user anchor certificate db to the search list
239 if we don't explicitly trust them. */
240 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
242 #endif /* !TARGET_OS_BRIDGE */
245 ** The order here allows a client-provided anchor to overrule
246 ** a user or admin trust setting which can overrule the system anchors.
247 ** Apple's anchors cannot be overriden by a trust setting.
249 #if !TARGET_OS_BRIDGE
250 if (builder
->anchorSource
) {
251 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
254 /* Only add the system and user anchor certificate db to the
255 anchorSources if we are supposed to trust them. */
256 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
258 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
259 #else /* TARGET_OS_OSX */
260 if (keychainsAllowed
&& kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
261 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
264 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
266 #else /* TARGET_OS_BRIDGE */
267 /* Bridge can only access memory sources. */
268 if (builder
->anchorSource
) {
269 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
272 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
274 #endif /* !TARGET_OS_BRIDGE */
276 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
277 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
280 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
282 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
283 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
284 CFReleaseSafe(otapkiref
);
287 /* Now let's get the leaf cert and turn it into a path. */
288 SecCertificateRef leaf
=
289 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
290 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(NULL
, leaf
, NULL
);
291 CFSetAddValue(builder
->allPaths
, path
);
292 CFArrayAppendValue(builder
->partialPaths
, path
);
294 builder
->path
= CFRetainSafe(path
);
295 SecPathBuilderSetPath(builder
, path
);
298 /* Set the revocation context */
300 /* Next step is to process the leaf. We do that work on the builder queue
301 * to avoid blocking the main thread with database lookups. */
302 builder
->state
= SecPathBuilderProcessLeaf
;
303 builder
->completed
= completed
;
304 builder
->context
= context
;
307 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
308 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
309 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
310 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
311 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
, CFArrayRef exceptions
,
312 SecPathBuilderCompleted completed
, const void *context
) {
313 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
314 memset(builder
, 0, sizeof(*builder
));
315 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
316 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
317 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
318 accessGroups
, exceptions
, completed
, context
);
322 /* Don't use this if you're going to modify the PVC array in the operation. */
323 static void SecPathBuilderForEachPVC(SecPathBuilderRef builder
,void (^operation
)(SecPVCRef pvc
, bool *stop
)) {
324 if (!builder
->pvcs
) { return; }
327 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
328 if (!builder
->pvcs
[ix
]) { continue; }
329 operation(builder
->pvcs
[ix
], &stop
);
334 static void SecPathBuilderDestroy(SecPathBuilderRef builder
) {
335 secdebug("alloc", "%p", builder
);
336 dispatch_release_null(builder
->queue
);
337 if (builder
->anchorSource
) {
338 SecMemoryCertificateSourceDestroy(builder
->anchorSource
);
339 builder
->anchorSource
= NULL
;
341 if (builder
->certificateSource
) {
342 SecMemoryCertificateSourceDestroy(builder
->certificateSource
);
343 builder
->certificateSource
= NULL
;
345 if (builder
->itemCertificateSource
) {
346 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
);
347 builder
->itemCertificateSource
= NULL
;
349 if (builder
->appleAnchorSource
) {
350 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
);
351 builder
->appleAnchorSource
= NULL
;
353 CFReleaseNull(builder
->clientAuditToken
);
354 CFReleaseNull(builder
->anchorSources
);
355 CFReleaseNull(builder
->parentSources
);
356 CFReleaseNull(builder
->allPaths
);
357 CFReleaseNull(builder
->partialPaths
);
358 CFReleaseNull(builder
->rejectedPaths
);
359 CFReleaseNull(builder
->candidatePaths
);
360 CFReleaseNull(builder
->ocspResponses
);
361 CFReleaseNull(builder
->signedCertificateTimestamps
);
362 CFReleaseNull(builder
->trustedLogs
);
363 CFReleaseNull(builder
->path
);
364 CFReleaseNull(builder
->revocation_check_method
);
365 CFReleaseNull(builder
->info
);
366 CFReleaseNull(builder
->exceptions
);
370 for (ix
= 0; ix
< builder
->pvcCount
; ix
++) {
371 if (builder
->pvcs
[ix
]) {
372 SecPVCDelete(builder
->pvcs
[ix
]);
373 free(builder
->pvcs
[ix
]);
377 builder
->pvcs
= NULL
;
381 static void SecPathBuilderSetPath(SecPathBuilderRef builder
, SecCertificatePathVCRef path
) {
382 bool samePath
= ((!path
&& !builder
->path
) || (path
&& builder
->path
&& CFEqual(path
, builder
->path
)));
384 CFRetainAssign(builder
->path
, path
);
386 CFReleaseNull(builder
->info
);
388 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
389 SecPVCSetPath(pvc
, path
);
394 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
395 return builder
->canAccessNetwork
;
398 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
399 if (builder
->canAccessNetwork
!= allow
) {
400 builder
->canAccessNetwork
= allow
;
403 secinfo("http", "network access re-enabled by policy");
404 /* re-enabling network_access re-adds kSecCAIssuerSource as
406 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
408 secnotice("http", "network access not allowed on WatchOS");
409 builder
->canAccessNetwork
= false;
412 secinfo("http", "network access disabled by policy");
413 /* disabling network_access removes kSecCAIssuerSource from
414 the list of parent sources. */
415 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
416 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
419 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
424 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
426 return CFRetainSafe(builder
->ocspResponses
);
429 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
431 return CFRetainSafe(builder
->signedCertificateTimestamps
);
434 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
436 return CFRetainSafe(builder
->trustedLogs
);
439 SecCertificatePathVCRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
441 return builder
->bestPath
;
444 SecCertificatePathVCRef
SecPathBuilderGetPath(SecPathBuilderRef builder
) {
445 return builder
->path
;
448 CFAbsoluteTime
SecPathBuilderGetVerifyTime(SecPathBuilderRef builder
) {
449 return builder
->verifyTime
;
452 CFIndex
SecPathBuilderGetCertificateCount(SecPathBuilderRef builder
) {
453 return SecCertificatePathVCGetCount(builder
->path
);
456 SecCertificateRef
SecPathBuilderGetCertificateAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
457 return SecCertificatePathVCGetCertificateAtIndex(builder
->path
, ix
);
460 bool SecPathBuilderIsAnchored(SecPathBuilderRef builder
) {
461 return SecCertificatePathVCIsAnchored(builder
->path
);
464 unsigned int SecPathBuilderDecrementAsyncJobCount(SecPathBuilderRef builder
) {
465 return --builder
->asyncJobCount
;
468 void SecPathBuilderSetAsyncJobCount(SecPathBuilderRef builder
, unsigned int jobCount
) {
469 builder
->asyncJobCount
= jobCount
;
470 secdebug("rvc", "set asyncJobCount to %d", builder
->asyncJobCount
);
473 CFMutableDictionaryRef
SecPathBuilderGetInfo(SecPathBuilderRef builder
) {
474 return builder
->info
;
477 CFStringRef
SecPathBuilderGetRevocationMethod(SecPathBuilderRef builder
) {
478 return builder
->revocation_check_method
;
481 void SecPathBuilderSetRevocationMethod(SecPathBuilderRef builder
, CFStringRef method
) {
482 CFRetainAssign(builder
->revocation_check_method
, method
);
483 secdebug("rvc", "deferred revocation checking enabled using %@ method", method
);
486 bool SecPathBuilderGetCheckRevocationOnline(SecPathBuilderRef builder
) {
487 return builder
->online_revocation
;
490 void SecPathBuilderSetCheckRevocationOnline(SecPathBuilderRef builder
) {
491 builder
->online_revocation
= true;
492 secdebug("rvc", "revocation force online check");
495 CFArrayRef
SecPathBuilderGetExceptions(SecPathBuilderRef builder
) {
496 return builder
->exceptions
;
499 CFIndex
SecPathBuilderGetPVCCount(SecPathBuilderRef builder
) {
500 return builder
->pvcCount
;
503 SecPVCRef
SecPathBuilderGetPVCAtIndex(SecPathBuilderRef builder
, CFIndex ix
) {
504 if (ix
> (builder
->pvcCount
- 1)) {
507 return builder
->pvcs
[ix
];
510 void SecPathBuilderSetResultInPVCs(SecPathBuilderRef builder
, CFStringRef key
,
511 CFIndex ix
, CFTypeRef result
, bool force
,
512 SecTrustResultType resultType
) {
513 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
514 SecPVCSetResultForced(pvc
, key
, ix
, result
, force
);
515 pvc
->result
= resultType
;
519 static bool SecPathBuilderIsOkResult(SecPathBuilderRef builder
) {
520 /* If any of the PVCs passed, we accept the path. */
521 __block
bool acceptPath
= false;
522 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
523 acceptPath
|= SecPVCIsOkResult(pvc
);
528 static SecPVCRef
SecPathBuilderGetResultPVC(SecPathBuilderRef builder
) {
529 /* Return the first PVC that passed */
530 __block SecPVCRef resultPVC
= NULL
;
531 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool *stop
) {
532 if (SecPVCIsOkResult(pvc
)) {
537 if (resultPVC
) { return resultPVC
; }
539 /* If we didn't return a passing PVC, return the first PVC. */
540 return builder
->pvcs
[0];
543 /* This function assumes that the input source is an anchor source */
544 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
545 SecCertificateRef certificate
) {
546 __block
bool result
= false;
547 CFArrayRef constraints
= NULL
;
548 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
550 /* Unrestricted certificates:
551 * -those that come from anchor sources with no constraints
552 * -self-signed certificates with empty contraints arrays
554 Boolean selfSigned
= false;
555 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
556 if ((NULL
== source
->copyUsageConstraints
) ||
557 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
558 secinfo("trust", "unrestricted anchor%s",
559 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
564 /* Get the trust settings result for the PVCs. Only one PVC need match to
565 * trigger the anchor behavior -- policy validation will handle whether the
566 * path is truly anchored for that PVC. */
567 require(constraints
, out
);
568 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
569 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
570 settingsResult
= SecPVCGetTrustSettingsResult(pvc
,
573 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
574 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
575 // For our purposes, this is an anchor.
576 secinfo("trust", "complex trust settings anchor");
581 if (settingsResult
== kSecTrustSettingsResultDeny
) {
582 /* We consider denied certs "anchors" because the trust decision
583 is set regardless of building the chain further. The policy
584 validation will handle rejecting this chain. */
585 secinfo("trust", "complex trust settings denied anchor");
592 CFReleaseNull(constraints
);
596 /* Source returned in foundInSource has the same lifetime as the builder. */
597 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
598 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
599 /* We look through the anchor sources in order. They are ordered in
600 SecPathBuilderInit so that process anchors override user anchors which
601 override system anchors. */
602 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
604 for (ix
= 0; ix
< count
; ++ix
) {
605 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
606 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
607 if (SecCertificateSourceContains(source
, certificate
)) {
609 *foundInSource
= source
;
610 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
618 bool SecPathBuilderIsAnchorSource(SecPathBuilderRef builder
, SecCertificateSourceRef source
) {
619 CFIndex anchorCount
= CFArrayGetCount(builder
->anchorSources
);
620 return CFArrayContainsValue(builder
->anchorSources
, CFRangeMake(0,anchorCount
), source
);
623 /* Return false if path is not a partial, if path was a valid candidate it
624 will have been added to builder->candidatePaths, if path was rejected
625 by the parent certificate checks (because it's expired or some other
626 static chaining check failed) it will have been added to rejectedPaths.
627 Return true path if path is a partial. */
628 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
629 SecCertificatePathVCRef path
) {
630 SecPathBuilderSetPath(builder
, path
);
631 __block
bool parentChecksFail
= true;
633 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
634 /* The parent checks aren't actually PVC-dependent, so theoretically,
635 * we only need to run this once per path, but we want to set the
636 * results in all PVCs. */
637 parentChecksFail
&= !SecPVCParentCertificateChecks(pvc
,
638 SecCertificatePathVCGetCount(path
) - 1);
641 if (!builder
->considerRejected
&& parentChecksFail
) {
642 secdebug("trust", "Found rejected path %@", path
);
643 CFArrayAppendValue(builder
->rejectedPaths
, path
);
647 SecPathVerifyStatus vstatus
= SecCertificatePathVCVerify(path
);
648 /* Candidate paths with failed signatures are discarded. */
649 if (vstatus
== kSecPathVerifyFailed
) {
650 secdebug("trust", "Verify failed for path %@", path
);
654 if (vstatus
== kSecPathVerifySuccess
) {
655 /* The signature chain verified sucessfully, now let's find
656 out if we have an anchor for path. */
657 if (SecCertificatePathVCIsAnchored(path
)) {
658 secdebug("trust", "Adding candidate %@", path
);
659 CFArrayAppendValue(builder
->candidatePaths
, path
);
661 /* The path is not partial if the last cert is self-signed. */
662 if ((SecCertificatePathVCSelfSignedIndex(path
) >= 0) &&
663 (SecCertificatePathVCSelfSignedIndex(path
) == SecCertificatePathVCGetCount(path
)-1)) {
671 static void addOptionsToPolicy(SecPolicyRef policy
, CFDictionaryRef newOptions
) {
672 __block CFMutableDictionaryRef oldOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, policy
->_options
);
673 CFDictionaryForEach(newOptions
, ^(const void *key
, const void *value
) {
674 CFDictionaryAddValue(oldOptions
, key
, value
);
676 CFAssignRetained(policy
->_options
, oldOptions
);
679 static void SecPathBuilderAddPinningPolicies(SecPathBuilderRef builder
) {
680 CFIndex ix
, initialPVCCount
= builder
->pvcCount
;
681 for (ix
= 0; ix
< initialPVCCount
; ix
++) {
682 CFArrayRef policies
= CFRetainSafe(builder
->pvcs
[ix
]->policies
);
684 for (policyIX
= 0; policyIX
< CFArrayGetCount(policies
); policyIX
++) {
685 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, policyIX
);
686 CFStringRef policyName
= SecPolicyGetName(policy
);
687 CFStringRef hostname
= CFDictionaryGetValue(policy
->_options
, kSecPolicyCheckSSLHostname
);
688 if (!hostname
) { continue; } //No hostname to look up; probably not an SSL policy, skip
690 /* Query the pinning database for this policy */
691 CFMutableDictionaryRef query
= CFDictionaryCreateMutable(NULL
, 2, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
692 CFDictionaryAddValue(query
, kSecPinningDbKeyPolicyName
, policyName
);
693 CFDictionaryAddValue(query
, kSecPinningDbKeyHostname
, hostname
);
694 CFDictionaryRef results
= SecPinningDbCopyMatching(query
);
695 CFReleaseNull(query
);
696 if (!results
) { continue; } //No rules for this hostname or policyName
698 /* Found pinning policies. Apply them to the path builder. */
699 CFArrayRef newRules
= CFDictionaryGetValue(results
, kSecPinningDbKeyRules
);
700 CFStringRef dbPolicyName
= CFDictionaryGetValue(results
, kSecPinningDbKeyPolicyName
);
701 secinfo("trust", "found pinning %lu %@ policies for hostname %@, policyName %@",
702 (unsigned long)CFArrayGetCount(newRules
), dbPolicyName
, hostname
, policyName
);
704 for (newRulesIX
= 0; newRulesIX
< CFArrayGetCount(newRules
); newRulesIX
++) {
705 if (!isDictionary(CFArrayGetValueAtIndex(newRules
, newRulesIX
))) {
709 /* Create the new policies with pinning rules (preserving other ANDed policies). */
710 CFDictionaryRef newOptions
= (CFDictionaryRef
)CFArrayGetValueAtIndex(newRules
, newRulesIX
);
711 SecPolicyRef newPolicy
= SecPolicyCreateSSL(true, hostname
);
712 if (!newPolicy
) { continue; }
713 addOptionsToPolicy(newPolicy
, newOptions
);
714 SecPolicySetName(newPolicy
, dbPolicyName
);
715 CFMutableArrayRef newPolicies
= CFArrayCreateMutableCopy(NULL
, 0, policies
);
716 if (!newPolicies
) { CFReleaseNull(newPolicy
); continue; }
717 CFArrayReplaceValues(newPolicies
, CFRangeMake(policyIX
, 1), (const void **)&newPolicy
, 1);
719 if (newRulesIX
== 0) {
720 /* For the first set of pinning rules, replace this PVC's policies */
721 CFRetainAssign(builder
->pvcs
[ix
]->policies
, newPolicies
);
723 /* If there were two or more dictionaries of rules, we need to treat them as an "OR".
724 * Create another PVC for this dicitionary. */
725 builder
->pvcs
= realloc(builder
->pvcs
, (builder
->pvcCount
+ 1) * sizeof(SecPVCRef
));
726 builder
->pvcs
[builder
->pvcCount
] = malloc(sizeof(struct OpaqueSecPVC
));
727 SecPVCInit(builder
->pvcs
[builder
->pvcCount
], builder
, newPolicies
);
730 CFReleaseNull(newPolicy
);
731 CFReleaseNull(newPolicies
);
733 CFReleaseNull(results
);
735 CFReleaseNull(policies
);
739 static bool SecPathBuilderProcessLeaf(SecPathBuilderRef builder
) {
740 SecPathBuilderAddPinningPolicies(builder
);
742 /* We need to find and set constraints on the leaf-only path */
743 SecCertificatePathVCRef path
= builder
->path
;
744 SecCertificateRef leaf
= SecCertificatePathVCGetCertificateAtIndex(path
, 0);
746 SecCertificateSourceRef source
= NULL
;
747 bool isAnchor
= false;
748 CFArrayRef constraints
= NULL
;
749 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
753 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
755 SecCertificatePathVCSetUsageConstraintsAtIndex(path
, constraints
, 0);
756 CFReleaseSafe(constraints
);
758 SecCertificatePathVCSetIsAnchored(path
);
759 CFArrayAppendValue(builder
->candidatePaths
, path
);
762 __block
bool leafChecksFail
= true;
763 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
764 SecPVCLeafChecks(pvc
);
765 leafChecksFail
&= !SecPVCIsOkResult(pvc
);
767 builder
->considerRejected
= leafChecksFail
;
769 builder
->state
= SecPathBuilderGetNext
;
773 /* Given the builder, a partial chain partial and the parents array, construct
774 a SecCertificatePath for each parent. After discarding previously
775 considered paths and paths with cycles, sort out which array each path
776 should go in, if any. */
777 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
778 SecCertificatePathVCRef partial
, CFArrayRef parents
) {
779 CFIndex rootIX
= SecCertificatePathVCGetCount(partial
) - 1;
780 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
782 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
783 SecCertificateRef parent
= (SecCertificateRef
)
784 CFArrayGetValueAtIndex(parents
, parentIX
);
785 CFIndex ixOfParent
= SecCertificatePathVCGetIndexOfCertificate(partial
,
787 if (ixOfParent
!= kCFNotFound
) {
788 /* partial already contains parent. Let's not add the same
789 certificate again. */
790 if (ixOfParent
== rootIX
) {
791 /* parent is equal to the root of the partial, so partial
792 looks to be self issued. */
793 SecCertificatePathVCSetSelfIssued(partial
);
798 /* FIXME Add more sanity checks to see that parent really can be
799 a parent of partial_root. subjectKeyID == authorityKeyID,
800 signature algorithm matches public key algorithm, etc. */
801 SecCertificateSourceRef source
= NULL
;
802 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
803 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
804 SecCertificatePathVCRef path
= SecCertificatePathVCCreate(partial
, parent
, constraints
);
805 CFReleaseSafe(constraints
);
808 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
809 CFSetAddValue(builder
->allPaths
, path
);
811 SecCertificatePathVCSetIsAnchored(path
);
812 if (SecPathBuilderIsPartial(builder
, path
)) {
813 /* Insert path right at the current position since it's a new
815 CFArrayInsertValueAtIndex(builder
->partialPaths
,
816 ++builder
->partialIX
, path
);
817 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
818 parentIX
+ 1, num_parents
, path
);
820 secdebug("trust", "found new path %@", path
);
826 /* Callback for the SecPathBuilderGetNext() functions call to
827 SecCertificateSourceCopyParents(). */
828 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
829 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
830 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
831 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
832 secdebug("async", "%@ parents %@", partial
, parents
);
833 SecPathBuilderProcessParents(builder
, partial
, parents
);
835 builder
->state
= SecPathBuilderGetNext
;
836 SecPathBuilderStep(builder
);
839 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
840 /* If we have any candidates left to go return those first. */
841 if (CFArrayGetCount(builder
->candidatePaths
)) {
842 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
843 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
844 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
845 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
847 SecPathBuilderSetPath(builder
, path
);
848 builder
->state
= SecPathBuilderValidatePath
;
852 /* If we are considering rejected chains we check each rejected path
853 with SecPathBuilderIsPartial() which checks the signature chain and
854 either drops the path if it's not properly signed, add it as a
855 candidate if it has a trusted anchor, or adds it as a partial
856 to be considered once we finish considering all the rejects. */
857 if (builder
->considerRejected
) {
858 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
861 SecCertificatePathVCRef path
= (SecCertificatePathVCRef
)
862 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
863 if (SecPathBuilderIsPartial(builder
, path
)) {
864 CFArrayInsertValueAtIndex(builder
->partialPaths
,
865 ++builder
->partialIX
, path
);
867 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
869 /* Keep going until we have moved all rejected partials into
870 the regular partials or candidates array. */
875 /* If builder->partialIX is < 0 we have considered all partial chains
876 this block must ensure partialIX >= 0 if execution continues past
878 if (builder
->partialIX
< 0) {
879 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
880 if (builder
->nextParentSource
< num_sources
) {
881 builder
->nextParentSource
++;
882 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
883 builder
->nextParentSource
, num_sources
);
885 /* We've run out of new sources to consider so let's look at
886 rejected chains and after that even consider partials
888 FIXME we might not want to consider partial paths that
889 are subsets of other partial paths, or not consider them
890 at all if we already have an anchored reject. */
891 if (!builder
->considerRejected
) {
892 builder
->considerRejected
= true;
893 secdebug("trust", "considering rejected paths");
894 } else if (!builder
->considerPartials
) {
895 builder
->considerPartials
= true;
896 secdebug("trust", "considering partials");
898 /* We're all out of options, so we can't produce any more
899 candidates. Let's calculate details and return the best
901 builder
->state
= SecPathBuilderComputeDetails
;
905 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
906 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
910 /* We know builder->partialIX >= 0 if we get here. */
911 SecCertificatePathVCRef partial
= (SecCertificatePathVCRef
)
912 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
913 /* Don't try to extend partials anymore once we are in the considerPartials
914 state, since at this point every partial has been extended with every
915 possible parentSource already. */
916 if (builder
->considerPartials
) {
917 --builder
->partialIX
;
918 SecPathBuilderSetPath(builder
, partial
);
919 builder
->state
= SecPathBuilderValidatePath
;
923 /* Attempt to extend this partial path with another certificate. This
924 should give us a list of potential parents to consider. */
925 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
926 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
929 /* Attempt to extend partial, leaving all possible extended versions
930 of partial in builder->extendedPaths. */
931 CFIndex sourceIX
= SecCertificatePathVCGetNextSourceIndex(partial
);
932 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
933 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
934 SecCertificateSourceRef source
;
935 if (sourceIX
< num_anchor_sources
) {
936 source
= (SecCertificateSourceRef
)
937 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
938 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
941 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
942 source
= (SecCertificateSourceRef
)
943 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
944 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
945 builder
->nextParentSource
);
947 SecCertificatePathVCSetNextSourceIndex(partial
, sourceIX
+ 1);
948 SecCertificateRef root
= SecCertificatePathVCGetRoot(partial
);
949 return SecCertificateSourceCopyParents(source
, root
,
950 builder
, SecPathBuilderExtendPaths
);
952 --builder
->partialIX
;
958 /* One or more of the policies did not accept the candidate path. */
959 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
962 builder
->state
= SecPathBuilderGetNext
;
964 bool bestPathIsEV
= SecCertificatePathVCIsEV(builder
->bestPath
);
965 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
967 if (bestPathIsEV
&& !isEV
) {
968 /* We never replace an ev reject with a non ev reject. */
972 CFIndex bestPathScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
973 CFIndex score
= SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
);
974 SecCertificatePathVCSetScore(builder
->path
, score
);
976 /* The current chain is valid for EV, but revocation checking failed. We
977 replace any previously accepted or rejected non EV chains with the
979 if (isEV
&& !bestPathIsEV
) {
982 if (!builder
->bestPath
|| score
> bestPathScore
) {
983 if (builder
->bestPath
) {
985 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
986 (bestPathIsEV
? "" : "non "),
987 (bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
989 (isEV
? "" : "non "), (long)score
, builder
->path
);
991 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
992 (isEV
? "" : "non "), score
, builder
->path
);
995 builder
->bestPath
= builder
->path
;
997 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
998 (isEV
? "" : "non "), score
, bestPathScore
, builder
->path
);
1002 /* All policies accepted the candidate path. */
1003 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
1004 if (!builder
) { return; }
1005 bool isSHA2
= !SecCertificatePathVCHasWeakHash(builder
->path
);
1006 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPathBuilderGetCertificateAtIndex(builder
, 0));
1007 bool isEV
= SecCertificatePathVCIsEV(builder
->path
);
1008 bool isOptionallyEV
= SecCertificatePathVCIsOptionallyEV(builder
->path
);
1009 CFIndex bestScore
= SecCertificatePathVCGetScore(builder
->bestPath
);
1010 /* Score this path. Note that all points awarded or deducted in
1011 * SecCertificatePathScore are < 100,000 */
1012 CFIndex currScore
= (SecCertificatePathVCScore(builder
->path
, builder
->verifyTime
) +
1013 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
1014 (isEV
? 1000000 : 0)); //1,000,000 points for EV
1015 SecCertificatePathVCSetScore(builder
->path
, currScore
);
1016 if (currScore
> bestScore
) {
1017 // current path is better than existing best path
1018 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
1019 (SecCertificatePathVCIsEV(builder
->bestPath
) ? "" : "non "),
1020 (bestScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
1022 (isEV
? "" : "non "), (long)currScore
, builder
->path
);
1024 builder
->bestPath
= builder
->path
;
1027 /* If we found the best accept we can, we want to switch directly to the
1028 SecPathBuilderComputeDetails state here, since we're done. */
1029 if ((isEV
|| !isOptionallyEV
) && (isSHA2
|| !isOptionallySHA2
))
1030 builder
->state
= SecPathBuilderComputeDetails
;
1032 builder
->state
= SecPathBuilderGetNext
;
1035 /* Return true iff a given path satisfies all the specified policies at
1037 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
1039 if (builder
->considerRejected
) {
1040 SecPathBuilderReject(builder
);
1044 builder
->state
= SecPathBuilderDidValidatePath
;
1046 /* Revocation checking is now done before path checks, to ensure that
1047 isAllowlisted will be set correctly for the subsequent path checks. */
1048 bool completed
= SecPathBuilderCheckRevocation(builder
);
1050 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1051 SecPVCPathChecks(pvc
);
1057 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
1058 /* We perform the revocation required policy checks here because
1059 * this is the state we call back into once all the asynchronous
1060 * revocation check calls are done. */
1061 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1062 SecPVCPathCheckRevocationRequired(pvc
);
1065 if (SecPathBuilderIsOkResult(builder
)) {
1066 SecPathBuilderAccept(builder
);
1068 SecPathBuilderReject(builder
);
1070 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
1074 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
1075 /* We have to re-do all the checks so that the results get set in the
1076 * PVC for the best path, as the last path checked may not have been the best. */
1077 SecPathBuilderSetPath(builder
, builder
->bestPath
);
1078 __block CFIndex ix
, pathLength
= SecCertificatePathVCGetCount(builder
->bestPath
);
1080 __block
bool completed
= true;
1082 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1083 SecPVCComputeDetails(pvc
, builder
->bestPath
);
1084 completed
&= SecPathBuilderCheckRevocation(builder
);
1085 for (ix
= 1; ix
< pathLength
; ++ix
) {
1086 SecPVCParentCertificateChecks(pvc
, ix
);
1088 SecPVCPathChecks(pvc
);
1091 builder
->state
= SecPathBuilderReportResult
;
1093 SecPathBuilderForEachPVC(builder
, ^(SecPVCRef pvc
, bool * __unused stop
) {
1094 SecPVCPathCheckRevocationRequired(pvc
);
1097 /* Reject the certificate if it was accepted before but we failed it now. (Should not happen anymore.) */
1098 if (SecCertificatePathVCGetScore(builder
->bestPath
) > ACCEPT_PATH_SCORE
&& !SecPathBuilderIsOkResult(builder
)) {
1099 SecCertificatePathVCResetScore(builder
->bestPath
);
1100 secwarning("In ComputeDetails, we got a reject after an accept in DidValidatePath.");
1106 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
1107 builder
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
1108 0, &kCFTypeDictionaryKeyCallBacks
,
1109 &kCFTypeDictionaryValueCallBacks
);
1112 /* isEV is not set unless also CT verified. Here, we need to check that we
1113 * got a revocation response as well. */
1114 if (builder
->info
&& SecCertificatePathVCIsEV(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1115 #if !TARGET_OS_WATCH
1116 if (SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1117 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1118 if (nextUpdate
!= 0) {
1120 /* We don't do networking on watchOS, so we can't require OCSP for EV */
1124 /* Successful revocation check, so this cert is EV */
1125 CFDictionarySetValue(builder
->info
, kSecTrustInfoExtendedValidationKey
,
1126 kCFBooleanTrue
); /* iOS key */
1127 CFDictionarySetValue(builder
->info
, kSecTrustExtendedValidation
,
1128 kCFBooleanTrue
); /* unified API key */
1129 SecCertificateRef leaf
= SecPathBuilderGetCertificateAtIndex(builder
, 0);
1130 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
1131 if (leafCompanyName
) {
1132 CFDictionarySetValue(builder
->info
, kSecTrustInfoCompanyNameKey
,
1133 leafCompanyName
); /* iOS key */
1134 CFDictionarySetValue(builder
->info
, kSecTrustOrganizationName
,
1135 leafCompanyName
); /* unified API key */
1136 CFRelease(leafCompanyName
);
1142 if (builder
->info
&& SecPathBuilderIsOkResult(builder
) && SecCertificatePathVCIsRevocationDone(builder
->bestPath
)) {
1143 CFAbsoluteTime nextUpdate
= SecCertificatePathVCGetEarliestNextUpdate(builder
->bestPath
);
1144 if (nextUpdate
!= 0) {
1145 /* always populate revocation info for successful revocation check */
1146 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
1147 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationValidUntilKey
,
1148 validUntil
); /* iOS key */
1149 CFDictionarySetValue(builder
->info
, kSecTrustRevocationValidUntilDate
,
1150 validUntil
); /* unified API key */
1151 CFRelease(validUntil
);
1152 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1153 kCFBooleanTrue
); /* iOS key */
1154 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1155 kCFBooleanTrue
); /* unified API key */
1156 } else if (SecCertificatePathVCIsEV(builder
->bestPath
)) {
1157 /* populate revocation info for failed revocation check with EV */
1158 CFDictionarySetValue(builder
->info
, kSecTrustInfoRevocationKey
,
1159 kCFBooleanFalse
); /* iOS key */
1160 CFDictionarySetValue(builder
->info
, kSecTrustRevocationChecked
,
1161 kCFBooleanFalse
); /* unified API key */
1165 if (builder
->info
&& SecCertificatePathVCIsCT(builder
->bestPath
) && SecPathBuilderIsOkResult(builder
)) {
1166 CFDictionarySetValue(builder
->info
, kSecTrustInfoCertificateTransparencyKey
,
1171 /* This will trigger the outer step function to call the completion
1173 builder
->state
= NULL
;
1177 /* @function SecPathBuilderStep
1178 @summary This is the core of the async engine.
1179 @description Return false iff job is complete, true if a network request
1181 builder->state is a function pointer which is to be invoked.
1182 If you call this function from within a builder->state invocation it
1183 immediately returns true.
1184 Otherwise the following steps are repeated endlessly (unless a step returns)
1185 builder->state is invoked. If it returns true and builder->state is still
1186 non NULL this proccess is repeated.
1187 If a state returns false, SecPathBuilder will return true
1188 if builder->state is non NULL.
1189 If builder->state is NULL then regardless of what the state function returns
1190 the completion callback will be invoked and the builder will be deallocated.
1192 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
1193 if (builder
->activations
) {
1194 secdebug("async", "activations: %lu returning true",
1195 builder
->activations
);
1199 secdebug("async", "activations: %lu", builder
->activations
);
1200 builder
->activations
++;
1201 while (builder
->state
&& builder
->state(builder
));
1202 --builder
->activations
;
1204 if (builder
->state
) {
1205 secdebug("async", "waiting for async reply, exiting");
1206 /* A state returned false, it's waiting for network traffic. Let's
1211 if (builder
->activations
) {
1212 /* There is still at least one other running instance of this builder
1213 somewhere on the stack, we let that instance take care of sending
1214 the client a response. */
1218 SecPVCRef pvc
= SecPathBuilderGetResultPVC(builder
);
1219 SecTrustResultType result
= pvc
->result
;
1221 if (builder
->exceptions
&& pvc
->result
== kSecTrustResultUnspecified
) {
1222 result
= kSecTrustResultProceed
;
1225 secinfo("trust", "completed: %@ details: %@ result: %d",
1226 builder
->bestPath
, pvc
->details
, result
);
1228 if (builder
->completed
) {
1229 SecCertificatePathRef resultPath
= SecCertificatePathVCCopyCertificatePath(builder
->bestPath
);
1230 builder
->completed(builder
->context
, resultPath
,
1231 pvc
->details
, builder
->info
, result
);
1232 CFReleaseNull(resultPath
);
1235 /* Finally, destroy the builder and free it. */
1236 SecPathBuilderDestroy(builder
);
1242 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1243 return (builder
) ? builder
->queue
: NULL
;
1246 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1247 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1251 // MARK: SecTrustServer
1252 /********************************************************
1253 ****************** SecTrustServer **********************
1254 ********************************************************/
1256 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1259 SecTrustServerEvaluateCompleted(const void *userData
,
1260 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1261 SecTrustResultType result
) {
1262 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1263 evaluated(result
, details
, info
, chain
, NULL
);
1264 Block_release(evaluated
);
1268 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
, SecCertificatePathRef chain
, CFErrorRef error
)) {
1269 /* We need an array containing at least one certificate to proceed. */
1270 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1271 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1272 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1273 CFReleaseSafe(certError
);
1276 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1277 /* Call the actual evaluator function. */
1278 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1279 certificates
, anchors
,
1280 anchorsOnly
, keychainsAllowed
, policies
,
1281 responses
, SCTs
, trustedLogs
,
1282 verifyTime
, accessGroups
, exceptions
,
1283 SecTrustServerEvaluateCompleted
, userData
);
1284 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1288 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1289 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
, SecCertificatePathRef
*pchain
, CFErrorRef
*perror
) {
1290 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1291 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1292 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, exceptions
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1294 if (tr
== kSecTrustResultInvalid
) {
1297 CFRetainSafe(error
);
1301 *pdetails
= details
;
1302 CFRetainSafe(details
);
1310 CFRetainSafe(chain
);
1313 dispatch_semaphore_signal(done
);
1315 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);
1316 dispatch_release(done
);