2 * Copyright (c) 2006-2010,2012-2016 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>
33 #include <utilities/SecIOFormat.h>
34 #include <utilities/SecDispatchRelease.h>
35 #include <utilities/SecAppleAnchorPriv.h>
37 #include <Security/SecTrustPriv.h>
38 #include <Security/SecItem.h>
39 #include <Security/SecCertificateInternal.h>
40 #include <Security/SecCertificatePath.h>
41 #include <Security/SecFramework.h>
42 #include <Security/SecPolicyPriv.h>
43 #include <Security/SecPolicyInternal.h>
44 #include <Security/SecTrustSettingsPriv.h>
45 #include <Security/SecTask.h>
46 #include <CoreFoundation/CFRuntime.h>
47 #include <CoreFoundation/CFSet.h>
48 #include <CoreFoundation/CFString.h>
49 #include <CoreFoundation/CFNumber.h>
50 #include <CoreFoundation/CFArray.h>
51 #include <CoreFoundation/CFPropertyList.h>
52 #include <AssertMacros.h>
57 #include <sys/codesign.h>
58 #include <Security/SecBase.h>
59 #include "SecRSAKey.h"
60 #include <libDER/oids.h>
61 #include <utilities/debugging.h>
62 #include <utilities/SecCFWrappers.h>
63 #include <Security/SecInternal.h>
64 #include <ipc/securityd_client.h>
65 #include <CommonCrypto/CommonDigest.h>
66 #include "OTATrustUtilities.h"
67 #include "personalization.h"
68 #include <utilities/SecInternalReleasePriv.h>
71 #include <Security/SecTaskPriv.h>
74 #define MAX_CHAIN_LENGTH 15
75 #define ACCEPT_PATH_SCORE 10000000
77 /* Forward declaration for use in SecCertificateSource. */
78 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
);
81 // MARK: SecPathBuilder
82 /********************************************************
83 *************** SecPathBuilder object ******************
84 ********************************************************/
85 struct SecPathBuilder
{
86 dispatch_queue_t queue
;
87 CFDataRef clientAuditToken
;
88 SecCertificateSourceRef certificateSource
;
89 SecCertificateSourceRef itemCertificateSource
;
90 SecCertificateSourceRef anchorSource
;
91 SecCertificateSourceRef appleAnchorSource
;
92 CFMutableArrayRef anchorSources
;
93 CFIndex nextParentSource
;
94 CFMutableArrayRef parentSources
;
95 CFArrayRef ocspResponses
; // Stapled OCSP responses
96 CFArrayRef signedCertificateTimestamps
; // Stapled SCTs
97 CFArrayRef trustedLogs
; // Trusted CT logs
99 /* Hashed set of all paths we've constructed so far, used to prevent
100 re-considering a path that was already constructed once before.
101 Note that this is the only container in which certificatePath
102 objects are retained.
103 Every certificatePath being considered is always in allPaths and in at
104 most one of partialPaths, rejectedPaths, candidatePath or extendedPaths
105 all of which don't retain their values. */
106 CFMutableSetRef allPaths
;
108 /* No trusted anchor, satisfies the linking to intermediates for all
109 policies (unless considerRejected is true). */
110 CFMutableArrayRef partialPaths
;
111 /* No trusted anchor, does not satisfy linking to intermediates for all
113 CFMutableArrayRef rejectedPaths
;
114 /* Trusted anchor, satisfies the policies so far. */
115 CFMutableArrayRef candidatePaths
;
119 CFArrayRef leafDetails
;
121 CFIndex bestPathScore
;
123 bool considerRejected
;
124 bool considerPartials
;
125 bool canAccessNetwork
;
127 struct OpaqueSecPVC path
;
128 SecCertificatePathRef bestPath
;
134 bool (*state
)(SecPathBuilderRef
);
135 SecPathBuilderCompleted completed
;
139 /* State functions. Return false if a async job was scheduled, return
140 true to execute the next state. */
141 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
);
142 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
);
143 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
);
144 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
);
145 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
);
147 /* Forward declarations. */
148 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
149 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
);
151 /* IDEA: policies could be made capable of replacing incoming anchors and
152 anchorsOnly argument values. For example, some policies require the
153 Apple Inc. CA and not any other anchor. This can be done in
154 SecPathBuilderLeafCertificateChecks since this only runs once. */
155 static void SecPathBuilderLeafCertificateChecks(SecPathBuilderRef builder
,
156 SecCertificatePathRef path
) {
157 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
158 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
159 &kCFTypeDictionaryValueCallBacks
);
160 builder
->leafDetails
= CFArrayCreate(kCFAllocatorDefault
,
161 (const void **)&certDetail
, 1, &kCFTypeArrayCallBacks
);
162 CFRelease(certDetail
);
163 SecPVCRef pvc
= &builder
->path
;
164 SecPVCSetPath(pvc
, path
, builder
->leafDetails
);
165 builder
->considerRejected
= !SecPVCLeafChecks(pvc
);
168 static void SecPathBuilderInit(SecPathBuilderRef builder
,
169 CFDataRef clientAuditToken
, CFArrayRef certificates
,
170 CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
,
171 CFArrayRef policies
, CFArrayRef ocspResponses
,
172 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
173 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
174 SecPathBuilderCompleted completed
, const void *context
) {
175 secdebug("alloc", "%p", builder
);
176 CFAllocatorRef allocator
= kCFAllocatorDefault
;
178 builder
->clientAuditToken
= (CFDataRef
)
179 ((clientAuditToken
) ? CFRetain(clientAuditToken
) : NULL
);
180 builder
->queue
= dispatch_queue_create("builder", DISPATCH_QUEUE_SERIAL
);
182 builder
->nextParentSource
= 1;
184 builder
->canAccessNetwork
= true;
187 builder
->anchorSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
188 builder
->parentSources
= CFArrayCreateMutable(allocator
, 0, NULL
);
189 builder
->allPaths
= CFSetCreateMutable(allocator
, 0,
190 &kCFTypeSetCallBacks
);
192 builder
->partialPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
193 builder
->rejectedPaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
194 builder
->candidatePaths
= CFArrayCreateMutable(allocator
, 0, NULL
);
196 /* Init the policy verification context. */
197 SecPVCInit(&builder
->path
, builder
, policies
, verifyTime
);
199 /* Let's create all the certificate sources we might want to use. */
200 builder
->certificateSource
=
201 SecMemoryCertificateSourceCreate(certificates
);
203 builder
->anchorSource
= SecMemoryCertificateSourceCreate(anchors
);
206 bool allowNonProduction
= false;
207 builder
->appleAnchorSource
= SecMemoryCertificateSourceCreate(SecGetAppleTrustAnchors(allowNonProduction
));
211 ** The order here avoids the most expensive methods if the cheaper methods
212 ** produce an acceptable chain: client-provided, keychains, network-fetched.
214 #if !TARGET_OS_BRIDGE
215 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
216 builder
->itemCertificateSource
= SecItemCertificateSourceCreate(accessGroups
);
217 if (keychainsAllowed
) {
218 CFArrayAppendValue(builder
->parentSources
, builder
->itemCertificateSource
);
220 /* On OS X, need additional parent source to search legacy keychain files. */
221 if (kSecLegacyCertificateSource
->contains
&& kSecLegacyCertificateSource
->copyParents
) {
222 CFArrayAppendValue(builder
->parentSources
, kSecLegacyCertificateSource
);
227 /* Add the Apple, system, and user anchor certificate db to the search list
228 if we don't explicitly trust them. */
229 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
230 CFArrayAppendValue(builder
->parentSources
, kSecSystemAnchorSource
);
232 CFArrayAppendValue(builder
->parentSources
, kSecUserAnchorSource
);
235 if (keychainsAllowed
&& builder
->canAccessNetwork
) {
236 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
238 #else /* TARGET_OS_BRIDGE */
239 /* Bridge can only access memory sources. */
240 CFArrayAppendValue(builder
->parentSources
, builder
->certificateSource
);
242 /* Add the Apple, system, and user anchor certificate db to the search list
243 if we don't explicitly trust them. */
244 CFArrayAppendValue(builder
->parentSources
, builder
->appleAnchorSource
);
246 #endif /* !TARGET_OS_BRIDGE */
249 ** The order here allows a client-provided anchor to overrule
250 ** a user or admin trust setting which can overrule the system anchors.
251 ** Apple's anchors cannot be overriden by a trust setting.
253 #if !TARGET_OS_BRIDGE
254 if (builder
->anchorSource
) {
255 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
258 /* Only add the system and user anchor certificate db to the
259 anchorSources if we are supposed to trust them. */
260 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
262 CFArrayAppendValue(builder
->anchorSources
, kSecUserAnchorSource
);
263 #else /* TARGET_OS_OSX */
264 if (keychainsAllowed
&& kSecLegacyAnchorSource
->contains
&& kSecLegacyAnchorSource
->copyParents
) {
265 CFArrayAppendValue(builder
->anchorSources
, kSecLegacyAnchorSource
);
268 CFArrayAppendValue(builder
->anchorSources
, kSecSystemAnchorSource
);
270 #else /* TARGET_OS_BRIDGE */
271 /* Bridge can only access memory sources. */
272 if (builder
->anchorSource
) {
273 CFArrayAppendValue(builder
->anchorSources
, builder
->anchorSource
);
276 CFArrayAppendValue(builder
->anchorSources
, builder
->appleAnchorSource
);
278 #endif /* !TARGET_OS_BRIDGE */
280 /* Now let's get the leaf cert and turn it into a path. */
281 SecCertificateRef leaf
=
282 (SecCertificateRef
)CFArrayGetValueAtIndex(certificates
, 0);
283 SecCertificateSourceRef source
= NULL
;
284 bool isAnchor
= false;
285 CFArrayRef constraints
= NULL
;
286 if (SecPathBuilderIsAnchor(builder
, leaf
, &source
)) {
290 constraints
= SecCertificateSourceCopyUsageConstraints(source
, leaf
);
292 SecCertificatePathRef path
= SecCertificatePathCreate(NULL
, leaf
, constraints
);
293 CFReleaseSafe(constraints
);
294 CFSetAddValue(builder
->allPaths
, path
);
295 CFArrayAppendValue(builder
->partialPaths
, path
);
297 SecCertificatePathSetIsAnchored(path
);
298 CFArrayAppendValue(builder
->candidatePaths
, path
);
300 SecPathBuilderLeafCertificateChecks(builder
, path
);
303 builder
->ocspResponses
= CFRetainSafe(ocspResponses
);
304 builder
->signedCertificateTimestamps
= CFRetainSafe(signedCertificateTimestamps
);
307 builder
->trustedLogs
= CFRetainSafe(trustedLogs
);
309 SecOTAPKIRef otapkiref
= SecOTAPKICopyCurrentOTAPKIRef();
310 builder
->trustedLogs
= SecOTAPKICopyTrustedCTLogs(otapkiref
);
311 CFReleaseSafe(otapkiref
);
314 builder
->state
= SecPathBuilderGetNext
;
315 builder
->completed
= completed
;
316 builder
->context
= context
;
319 SecPathBuilderRef
SecPathBuilderCreate(CFDataRef clientAuditToken
,
320 CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
,
321 bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef ocspResponses
,
322 CFArrayRef signedCertificateTimestamps
, CFArrayRef trustedLogs
,
323 CFAbsoluteTime verifyTime
, CFArrayRef accessGroups
,
324 SecPathBuilderCompleted completed
, const void *context
) {
325 SecPathBuilderRef builder
= malloc(sizeof(*builder
));
326 memset(builder
, 0, sizeof(*builder
));
327 SecPathBuilderInit(builder
, clientAuditToken
, certificates
,
328 anchors
, anchorsOnly
, keychainsAllowed
, policies
, ocspResponses
,
329 signedCertificateTimestamps
, trustedLogs
, verifyTime
,
330 accessGroups
, completed
, context
);
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
->leafDetails
);
361 CFReleaseNull(builder
->ocspResponses
);
362 CFReleaseNull(builder
->signedCertificateTimestamps
);
363 CFReleaseNull(builder
->trustedLogs
);
365 SecPVCDelete(&builder
->path
);
368 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
369 return builder
->canAccessNetwork
;
372 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
373 if (builder
->canAccessNetwork
!= allow
) {
374 builder
->canAccessNetwork
= allow
;
377 secinfo("http", "network access re-enabled by policy");
378 /* re-enabling network_access re-adds kSecCAIssuerSource as
380 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
382 secnotice("http", "network access not allowed on WatchOS");
383 builder
->canAccessNetwork
= false;
386 secinfo("http", "network access disabled by policy");
387 /* disabling network_access removes kSecCAIssuerSource from
388 the list of parent sources. */
389 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
390 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
393 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
398 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
400 return CFRetainSafe(builder
->ocspResponses
);
403 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
405 return CFRetainSafe(builder
->signedCertificateTimestamps
);
408 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
410 return CFRetainSafe(builder
->trustedLogs
);
413 SecCertificatePathRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
415 return builder
->bestPath
;
418 /* This function assumes that the input source is an anchor source */
419 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
420 SecCertificateRef certificate
) {
422 CFArrayRef constraints
= NULL
;
423 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
425 /* Unrestricted certificates:
426 * -those that come from anchor sources with no constraints
427 * -self-signed certificates with empty contraints arrays
429 Boolean selfSigned
= false;
430 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
431 if ((NULL
== source
->copyUsageConstraints
) ||
432 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
433 secinfo("trust", "unrestricted anchor%s",
434 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
439 /* Get the trust settings result for the PVC */
440 require(constraints
, out
);
441 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
442 settingsResult
= SecPVCGetTrustSettingsResult(&builder
->path
,
445 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
446 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
447 // For our purposes, this is an anchor.
448 secinfo("trust", "complex trust settings anchor");
452 if (settingsResult
== kSecTrustSettingsResultDeny
) {
453 /* We consider denied certs "anchors" because the trust decision
454 is set regardless of building the chain further. The policy
455 validation will handle rejecting this chain. */
456 secinfo("trust", "complex trust settings denied anchor");
461 CFReleaseNull(constraints
);
465 /* Source returned in foundInSource has the same lifetime as the builder. */
466 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
467 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
468 /* We look through the anchor sources in order. They are ordered in
469 SecPathBuilderInit so that process anchors override user anchors which
470 override system anchors. */
471 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
473 for (ix
= 0; ix
< count
; ++ix
) {
474 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
475 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
476 if (SecCertificateSourceContains(source
, certificate
)) {
478 *foundInSource
= source
;
479 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
487 /* Return false if path is not a partial, if path was a valid candidate it
488 will have been added to builder->candidatePaths, if path was rejected
489 by the parent certificate checks (because it's expired or some other
490 static chaining check failed) it will have been added to rejectedPaths.
491 Return true path if path is a partial. */
492 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
493 SecCertificatePathRef path
) {
494 SecPVCRef pvc
= &builder
->path
;
495 SecPVCSetPath(pvc
, path
, NULL
);
497 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
498 SecPVCGetCertificateCount(pvc
) - 1)) {
499 secdebug("trust", "Found rejected path %@", path
);
500 CFArrayAppendValue(builder
->rejectedPaths
, path
);
504 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
505 /* Candidate paths with failed signatures are discarded. */
506 if (vstatus
== kSecPathVerifyFailed
) {
507 secdebug("trust", "Verify failed for path %@", path
);
511 if (vstatus
== kSecPathVerifySuccess
) {
512 /* The signature chain verified sucessfully, now let's find
513 out if we have an anchor for path. */
514 if (SecCertificatePathIsAnchored(path
)) {
515 secdebug("trust", "Adding candidate %@", path
);
516 CFArrayAppendValue(builder
->candidatePaths
, path
);
518 /* The path is not partial if the last cert is self-signed. */
519 if ((SecCertificatePathSelfSignedIndex(path
) >= 0) &&
520 (SecCertificatePathSelfSignedIndex(path
) == SecCertificatePathGetCount(path
)-1)) {
528 /* Given the builder, a partial chain partial and the parents array, construct
529 a SecCertificatePath for each parent. After discarding previously
530 considered paths and paths with cycles, sort out which array each path
531 should go in, if any. */
532 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
533 SecCertificatePathRef partial
, CFArrayRef parents
) {
534 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
535 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
537 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
538 SecCertificateRef parent
= (SecCertificateRef
)
539 CFArrayGetValueAtIndex(parents
, parentIX
);
540 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
542 if (ixOfParent
!= kCFNotFound
) {
543 /* partial already contains parent. Let's not add the same
544 certificate again. */
545 if (ixOfParent
== rootIX
) {
546 /* parent is equal to the root of the partial, so partial
547 looks to be self issued. */
548 SecCertificatePathSetSelfIssued(partial
);
553 /* FIXME Add more sanity checks to see that parent really can be
554 a parent of partial_root. subjectKeyID == authorityKeyID,
555 signature algorithm matches public key algorithm, etc. */
556 SecCertificateSourceRef source
= NULL
;
557 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
558 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
559 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
, constraints
);
560 CFReleaseSafe(constraints
);
563 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
564 CFSetAddValue(builder
->allPaths
, path
);
566 SecCertificatePathSetIsAnchored(path
);
567 if (SecPathBuilderIsPartial(builder
, path
)) {
568 /* Insert path right at the current position since it's a new
570 CFArrayInsertValueAtIndex(builder
->partialPaths
,
571 ++builder
->partialIX
, path
);
572 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
573 parentIX
+ 1, num_parents
, path
);
575 secdebug("trust", "found new path %@", path
);
581 /* Callback for the SecPathBuilderGetNext() functions call to
582 SecCertificateSourceCopyParents(). */
583 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
584 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
585 SecCertificatePathRef partial
= (SecCertificatePathRef
)
586 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
587 secdebug("async", "%@ parents %@", partial
, parents
);
588 SecPathBuilderProcessParents(builder
, partial
, parents
);
590 builder
->state
= SecPathBuilderGetNext
;
591 SecPathBuilderStep(builder
);
594 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
595 /* If we have any candidates left to go return those first. */
596 if (CFArrayGetCount(builder
->candidatePaths
)) {
597 SecCertificatePathRef path
= (SecCertificatePathRef
)
598 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
599 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
600 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
602 SecPVCSetPath(&builder
->path
, path
, NULL
);
603 builder
->state
= SecPathBuilderValidatePath
;
607 /* If we are considering rejected chains we check each rejected path
608 with SecPathBuilderIsPartial() which checks the signature chain and
609 either drops the path if it's not properly signed, add it as a
610 candidate if it has a trusted anchor, or adds it as a partial
611 to be considered once we finish considering all the rejects. */
612 if (builder
->considerRejected
) {
613 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
616 SecCertificatePathRef path
= (SecCertificatePathRef
)
617 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
618 if (SecPathBuilderIsPartial(builder
, path
)) {
619 CFArrayInsertValueAtIndex(builder
->partialPaths
,
620 ++builder
->partialIX
, path
);
622 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
624 /* Keep going until we have moved all rejected partials into
625 the regular partials or candidates array. */
630 /* If builder->partialIX is < 0 we have considered all partial chains
631 this block must ensure partialIX >= 0 if execution continues past
633 if (builder
->partialIX
< 0) {
634 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
635 if (builder
->nextParentSource
< num_sources
) {
636 builder
->nextParentSource
++;
637 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
638 builder
->nextParentSource
, num_sources
);
640 /* We've run out of new sources to consider so let's look at
641 rejected chains and after that even consider partials
643 FIXME we might not want to consider partial paths that
644 are subsets of other partial paths, or not consider them
645 at all if we already have an anchored reject. */
646 if (!builder
->considerRejected
) {
647 builder
->considerRejected
= true;
648 secdebug("trust", "considering rejected paths");
649 } else if (!builder
->considerPartials
) {
650 builder
->considerPartials
= true;
651 secdebug("trust", "considering partials");
653 /* We're all out of options, so we can't produce any more
654 candidates. Let's calculate details and return the best
656 builder
->state
= SecPathBuilderComputeDetails
;
660 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
661 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
665 /* We know builder->partialIX >= 0 if we get here. */
666 SecCertificatePathRef partial
= (SecCertificatePathRef
)
667 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
668 /* Don't try to extend partials anymore once we are in the considerPartials
669 state, since at this point every partial has been extended with every
670 possible parentSource already. */
671 if (builder
->considerPartials
) {
672 --builder
->partialIX
;
673 SecPVCSetPath(&builder
->path
, partial
, NULL
);
674 builder
->state
= SecPathBuilderValidatePath
;
678 /* Attempt to extend this partial path with another certificate. This
679 should give us a list of potential parents to consider. */
680 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
681 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
684 /* Attempt to extend partial, leaving all possible extended versions
685 of partial in builder->extendedPaths. */
686 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
687 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
688 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
689 SecCertificateSourceRef source
;
690 if (sourceIX
< num_anchor_sources
) {
691 source
= (SecCertificateSourceRef
)
692 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
693 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
696 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
697 source
= (SecCertificateSourceRef
)
698 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
699 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
700 builder
->nextParentSource
);
702 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
703 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
704 return SecCertificateSourceCopyParents(source
, root
,
705 builder
, SecPathBuilderExtendPaths
);
707 --builder
->partialIX
;
713 /* One or more of the policies did not accept the candidate path. */
714 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
716 SecPVCRef pvc
= &builder
->path
;
718 builder
->state
= SecPathBuilderGetNext
;
720 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
721 /* We never replace an ev reject with a non ev reject. */
725 CFIndex bestPathScore
= builder
->bestPathScore
;
726 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
727 SecPVCGetVerifyTime(&builder
->path
));
729 /* The current chain is valid for EV, but revocation checking failed. We
730 replace any previously accepted or rejected non EV chains with the
732 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
738 /* Since this means we found a valid ev chain that was revoked,
739 we might want to switch directly to the
740 SecPathBuilderComputeDetails state here if we think further
741 searching for new chains is pointless. For now we'll keep
742 going, since we could accept an alternate EV certification
743 path that isn't revoked. */
744 builder
->state
= SecPathBuilderComputeDetails
;
748 /* Do this last so that changes to bestPathScore above will take effect. */
749 if (!builder
->bestPath
|| score
> bestPathScore
) {
750 if (builder
->bestPath
) {
752 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
753 (builder
->bestPathIsEV
? "" : "non "),
754 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
755 builder
->bestPathScore
,
756 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
758 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
759 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
762 builder
->bestPathScore
= score
;
763 builder
->bestPath
= pvc
->path
;
764 builder
->bestPathIsEV
= pvc
->is_ev
;
765 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
);
767 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
768 (pvc
->is_ev
? "" : "non "), score
, bestPathScore
, builder
->path
.path
);
772 /* All policies accepted the candidate path. */
773 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
774 if (!builder
) { return; }
775 SecPVCRef pvc
= &builder
->path
;
776 if (!pvc
) { return; }
777 bool isSHA2
= !SecCertificatePathHasWeakHash(pvc
->path
);
778 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc
, 0));
779 CFIndex bestScore
= builder
->bestPathScore
;
780 /* Score this path. Note that all points awarded or deducted in
781 * SecCertificatePathScore are < 100,000 */
782 CFIndex currScore
= (SecCertificatePathScore(pvc
->path
, pvc
->verifyTime
) +
783 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
784 ((pvc
->is_ev
) ? 1000000 : 0)); //1,000,000 points for EV
785 if (currScore
> bestScore
) {
786 // current path is better than existing best path
787 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
788 (builder
->bestPathIsEV
? "" : "non "),
789 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
790 builder
->bestPathScore
,
791 (pvc
->is_ev
? "" : "non "), (long)currScore
, builder
->path
.path
);
793 builder
->bestPathScore
= currScore
;
794 builder
->bestPathIsEV
= pvc
->is_ev
;
795 builder
->bestPathIsSHA2
= isSHA2
;
796 builder
->bestPath
= pvc
->path
;
797 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
); /* should always be false */
800 /* If we found the best accept we can, we want to switch directly to the
801 SecPathBuilderComputeDetails state here, since we're done. */
802 if ((pvc
->is_ev
|| !pvc
->optionally_ev
) && (isSHA2
|| !isOptionallySHA2
))
803 builder
->state
= SecPathBuilderComputeDetails
;
805 builder
->state
= SecPathBuilderGetNext
;
808 /* Return true iff a given path satisfies all the specified policies at
810 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
811 SecPVCRef pvc
= &builder
->path
;
813 if (builder
->considerRejected
) {
814 SecPathBuilderReject(builder
);
818 builder
->state
= SecPathBuilderDidValidatePath
;
819 return SecPVCPathChecks(pvc
);
822 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
823 SecPVCRef pvc
= &builder
->path
;
825 SecPathBuilderAccept(builder
);
827 SecPathBuilderReject(builder
);
829 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
833 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
835 SecPVCRef pvc
= &builder
->path
;
837 if (!builder
->caller_wants_details
) {
838 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
839 pvc
->result
= builder
->bestPathScore
> ACCEPT_PATH_SCORE
;
840 builder
->state
= SecPathBuilderReportResult
;
844 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
845 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
846 pathLength
, builder
->leafDetails
);
847 CFRetainSafe(details
);
848 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
849 /* Only report on EV stuff if the bestPath actually was valid for EV. */
850 pvc
->optionally_ev
= builder
->bestPathIsEV
;
851 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
852 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
853 for (ix
= 1; ix
< pathLength
; ++ix
) {
854 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
855 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
856 &kCFTypeDictionaryValueCallBacks
);
857 CFArrayAppendValue(details
, certDetail
);
858 CFRelease(certDetail
);
859 SecPVCParentCertificateChecks(pvc
, ix
);
860 SecPVCGrayListedKeyChecks(pvc
, ix
);
861 SecPVCBlackListedKeyChecks(pvc
, ix
);
863 builder
->state
= SecPathBuilderReportResult
;
864 bool completed
= SecPVCPathChecks(pvc
);
866 /* Reject the certificate if it was accepted before but we failed it now. */
867 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
&& !pvc
->result
) {
868 builder
->bestPathScore
= 0;
871 /* Accept a partial path if certificate is on the allow list
872 and is temporally valid and passed all PVC checks. */
873 if (completed
&& pvc
->is_allowlisted
&& pvc
->result
&&
874 builder
->bestPathScore
< ACCEPT_PATH_SCORE
&&
875 SecCertificatePathIsValid(pvc
->path
, pvc
->verifyTime
)) {
876 builder
->bestPathScore
+= ACCEPT_PATH_SCORE
;
879 CFReleaseSafe(details
);
884 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
885 SecPVCRef pvc
= &builder
->path
;
886 bool haveRevocationResponse
= false;
887 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
888 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
889 kCFBooleanTrue
); /* iOS key */
890 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
891 kCFBooleanTrue
); /* unified API key */
892 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
893 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
894 if (leafCompanyName
) {
895 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
896 leafCompanyName
); /* iOS key */
897 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
898 leafCompanyName
); /* unified API key */
899 CFRelease(leafCompanyName
);
902 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
903 if (nextUpdate
== 0) {
904 /* populate revocation info for failed revocation check */
905 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
906 kCFBooleanFalse
); /* iOS key */
907 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
908 kCFBooleanFalse
); /* unified API key */
913 if (pvc
->info
&& pvc
->result
&& pvc
->rvcs
) {
914 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
915 if (nextUpdate
!= 0) {
916 /* always populate revocation info for successful revocation check */
917 haveRevocationResponse
= true;
918 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
919 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
920 validUntil
); /* iOS key */
921 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
922 validUntil
); /* unified API key */
923 CFRelease(validUntil
);
924 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
925 kCFBooleanTrue
); /* iOS key */
926 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
927 kCFBooleanTrue
); /* unified API key */
931 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
932 builder
->bestPathScore
= 0;
933 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
934 0, kCFBooleanFalse
, true);
937 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
938 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
942 if (pvc
->info
&& pvc
->is_ct_whitelisted
&& pvc
->result
) {
943 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyWhiteListKey
,
948 /* This will trigger the outer step function to call the completion
950 builder
->state
= NULL
;
954 /* @function SecPathBuilderStep
955 @summary This is the core of the async engine.
956 @description Return false iff job is complete, true if a network request
958 builder->state is a function pointer which is to be invoked.
959 If you call this function from within a builder->state invocation it
960 immediately returns true.
961 Otherwise the following steps are repeated endlessly (unless a step returns)
962 builder->state is invoked. If it returns true and builder->state is still
963 non NULL this proccess is repeated.
964 If a state returns false, SecPathBuilder will return true
965 if builder->state is non NULL.
966 If builder->state is NULL then regardless of what the state function returns
967 the completion callback will be invoked and the builder will be deallocated.
969 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
970 if (builder
->activations
) {
971 secdebug("async", "activations: %lu returning true",
972 builder
->activations
);
976 secdebug("async", "activations: %lu", builder
->activations
);
977 builder
->activations
++;
978 while (builder
->state
&& builder
->state(builder
));
979 --builder
->activations
;
981 if (builder
->state
) {
982 secdebug("async", "waiting for async reply, exiting");
983 /* A state returned false, it's waiting for network traffic. Let's
988 if (builder
->activations
) {
989 /* There is still at least one other running instance of this builder
990 somewhere on the stack, we let that instance take care of sending
991 the client a response. */
995 SecTrustResultType result
= kSecTrustResultInvalid
;
996 if (builder
->denyBestPath
) {
997 result
= kSecTrustResultDeny
;
998 } else if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
) {
999 result
= kSecTrustResultUnspecified
;
1001 result
= kSecTrustResultRecoverableTrustFailure
;
1004 secinfo("trust", "completed: %@ details: %@ result: %d",
1005 builder
->bestPath
, builder
->path
.details
, result
);
1007 if (builder
->completed
) {
1008 builder
->completed(builder
->context
, builder
->bestPath
,
1009 builder
->path
.details
, builder
->path
.info
, result
);
1012 /* Finally, destroy the builder and free it. */
1013 SecPathBuilderDestroy(builder
);
1019 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1020 return (builder
) ? builder
->queue
: NULL
;
1023 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1024 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1028 // MARK: SecTrustServer
1029 /********************************************************
1030 ****************** SecTrustServer **********************
1031 ********************************************************/
1033 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1036 SecTrustServerEvaluateCompleted(const void *userData
,
1037 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1038 SecTrustResultType result
) {
1039 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1040 evaluated(result
, details
, info
, chain
, NULL
);
1041 Block_release(evaluated
);
1045 SecTrustServerEvaluateBlock(CFDataRef clientAuditToken
, CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, void (^evaluated
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
)) {
1046 /* We need an array containing at least one certificate to proceed. */
1047 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1048 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1049 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1050 CFReleaseSafe(certError
);
1053 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1054 /* Call the actual evaluator function. */
1055 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1056 certificates
, anchors
,
1057 anchorsOnly
, keychainsAllowed
, policies
,
1058 responses
, SCTs
, trustedLogs
,
1059 verifyTime
, accessGroups
,
1060 SecTrustServerEvaluateCompleted
, userData
);
1061 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1065 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1066 SecTrustResultType
SecTrustServerEvaluate(CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, bool keychainsAllowed
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, CFArrayRef
*pdetails
, CFDictionaryRef
*pinfo
, SecCertificatePathRef
*pchain
, CFErrorRef
*perror
) {
1067 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1068 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1069 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1071 if (tr
== kSecTrustResultInvalid
) {
1074 CFRetainSafe(error
);
1078 *pdetails
= details
;
1079 CFRetainSafe(details
);
1087 CFRetainSafe(chain
);
1090 dispatch_semaphore_signal(done
);
1092 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);