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 if (builder
->certificateSource
) {
340 SecMemoryCertificateSourceDestroy(builder
->certificateSource
); }
341 if (builder
->itemCertificateSource
) {
342 SecItemCertificateSourceDestroy(builder
->itemCertificateSource
); }
343 if (builder
->appleAnchorSource
) {
344 SecMemoryCertificateSourceDestroy(builder
->appleAnchorSource
); }
345 CFReleaseSafe(builder
->clientAuditToken
);
346 CFReleaseSafe(builder
->anchorSources
);
347 CFReleaseSafe(builder
->parentSources
);
348 CFReleaseSafe(builder
->allPaths
);
349 CFReleaseSafe(builder
->partialPaths
);
350 CFReleaseSafe(builder
->rejectedPaths
);
351 CFReleaseSafe(builder
->candidatePaths
);
352 CFReleaseSafe(builder
->leafDetails
);
353 CFReleaseSafe(builder
->ocspResponses
);
354 CFReleaseSafe(builder
->signedCertificateTimestamps
);
355 CFReleaseSafe(builder
->trustedLogs
);
357 SecPVCDelete(&builder
->path
);
360 bool SecPathBuilderCanAccessNetwork(SecPathBuilderRef builder
) {
361 return builder
->canAccessNetwork
;
364 void SecPathBuilderSetCanAccessNetwork(SecPathBuilderRef builder
, bool allow
) {
365 if (builder
->canAccessNetwork
!= allow
) {
366 builder
->canAccessNetwork
= allow
;
369 secinfo("http", "network access re-enabled by policy");
370 /* re-enabling network_access re-adds kSecCAIssuerSource as
372 CFArrayAppendValue(builder
->parentSources
, kSecCAIssuerSource
);
374 secnotice("http", "network access not allowed on WatchOS");
375 builder
->canAccessNetwork
= false;
378 secinfo("http", "network access disabled by policy");
379 /* disabling network_access removes kSecCAIssuerSource from
380 the list of parent sources. */
381 CFIndex ix
= CFArrayGetFirstIndexOfValue(builder
->parentSources
,
382 CFRangeMake(0, CFArrayGetCount(builder
->parentSources
)),
385 CFArrayRemoveValueAtIndex(builder
->parentSources
, ix
);
390 CFArrayRef
SecPathBuilderCopyOCSPResponses(SecPathBuilderRef builder
)
392 return CFRetainSafe(builder
->ocspResponses
);
395 CFArrayRef
SecPathBuilderCopySignedCertificateTimestamps(SecPathBuilderRef builder
)
397 return CFRetainSafe(builder
->signedCertificateTimestamps
);
400 CFArrayRef
SecPathBuilderCopyTrustedLogs(SecPathBuilderRef builder
)
402 return CFRetainSafe(builder
->trustedLogs
);
405 SecCertificatePathRef
SecPathBuilderGetBestPath(SecPathBuilderRef builder
)
407 return builder
->bestPath
;
410 /* This function assumes that the input source is an anchor source */
411 static bool SecPathBuilderIsAnchorPerConstraints(SecPathBuilderRef builder
, SecCertificateSourceRef source
,
412 SecCertificateRef certificate
) {
414 CFArrayRef constraints
= NULL
;
415 constraints
= SecCertificateSourceCopyUsageConstraints(source
, certificate
);
417 /* Unrestricted certificates:
418 * -those that come from anchor sources with no constraints
419 * -self-signed certificates with empty contraints arrays
421 Boolean selfSigned
= false;
422 require(errSecSuccess
== SecCertificateIsSelfSigned(certificate
, &selfSigned
), out
);
423 if ((NULL
== source
->copyUsageConstraints
) ||
424 (constraints
&& (CFArrayGetCount(constraints
) == 0) && selfSigned
)) {
425 secinfo("trust", "unrestricted anchor%s",
426 (NULL
== source
->copyUsageConstraints
) ? " source" : "");
431 /* Get the trust settings result for the PVC */
432 require(constraints
, out
);
433 SecTrustSettingsResult settingsResult
= kSecTrustSettingsResultInvalid
;
434 settingsResult
= SecPVCGetTrustSettingsResult(&builder
->path
,
437 if ((selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustRoot
) ||
438 (!selfSigned
&& settingsResult
== kSecTrustSettingsResultTrustAsRoot
)) {
439 // For our purposes, this is an anchor.
440 secinfo("trust", "complex trust settings anchor");
444 if (settingsResult
== kSecTrustSettingsResultDeny
) {
445 /* We consider denied certs "anchors" because the trust decision
446 is set regardless of building the chain further. The policy
447 validation will handle rejecting this chain. */
448 secinfo("trust", "complex trust settings denied anchor");
453 CFReleaseNull(constraints
);
457 /* Source returned in foundInSource has the same lifetime as the builder. */
458 static bool SecPathBuilderIsAnchor(SecPathBuilderRef builder
,
459 SecCertificateRef certificate
, SecCertificateSourceRef
*foundInSource
) {
460 /* We look through the anchor sources in order. They are ordered in
461 SecPathBuilderInit so that process anchors override user anchors which
462 override system anchors. */
463 CFIndex count
= CFArrayGetCount(builder
->anchorSources
);
465 for (ix
= 0; ix
< count
; ++ix
) {
466 SecCertificateSourceRef source
= (SecCertificateSourceRef
)
467 CFArrayGetValueAtIndex(builder
->anchorSources
, ix
);
468 if (SecCertificateSourceContains(source
, certificate
)) {
470 *foundInSource
= source
;
471 if (SecPathBuilderIsAnchorPerConstraints(builder
, source
, certificate
)) {
479 /* Return false if path is not a partial, if path was a valid candidate it
480 will have been added to builder->candidatePaths, if path was rejected
481 by the parent certificate checks (because it's expired or some other
482 static chaining check failed) it will have been added to rejectedPaths.
483 Return true path if path is a partial. */
484 static bool SecPathBuilderIsPartial(SecPathBuilderRef builder
,
485 SecCertificatePathRef path
) {
486 SecPVCRef pvc
= &builder
->path
;
487 SecPVCSetPath(pvc
, path
, NULL
);
489 if (!builder
->considerRejected
&& !SecPVCParentCertificateChecks(pvc
,
490 SecPVCGetCertificateCount(pvc
) - 1)) {
491 secdebug("trust", "Found rejected path %@", path
);
492 CFArrayAppendValue(builder
->rejectedPaths
, path
);
496 SecPathVerifyStatus vstatus
= SecCertificatePathVerify(path
);
497 /* Candidate paths with failed signatures are discarded. */
498 if (vstatus
== kSecPathVerifyFailed
) {
499 secdebug("trust", "Verify failed for path %@", path
);
503 if (vstatus
== kSecPathVerifySuccess
) {
504 /* The signature chain verified sucessfully, now let's find
505 out if we have an anchor for path. */
506 if (SecCertificatePathIsAnchored(path
)) {
507 secdebug("trust", "Adding candidate %@", path
);
508 CFArrayAppendValue(builder
->candidatePaths
, path
);
510 /* The path is not partial if the last cert is self-signed. */
511 if ((SecCertificatePathSelfSignedIndex(path
) >= 0) &&
512 (SecCertificatePathSelfSignedIndex(path
) == SecCertificatePathGetCount(path
)-1)) {
520 /* Given the builder, a partial chain partial and the parents array, construct
521 a SecCertificatePath for each parent. After discarding previously
522 considered paths and paths with cycles, sort out which array each path
523 should go in, if any. */
524 static void SecPathBuilderProcessParents(SecPathBuilderRef builder
,
525 SecCertificatePathRef partial
, CFArrayRef parents
) {
526 CFIndex rootIX
= SecCertificatePathGetCount(partial
) - 1;
527 CFIndex num_parents
= parents
? CFArrayGetCount(parents
) : 0;
529 for (parentIX
= 0; parentIX
< num_parents
; ++parentIX
) {
530 SecCertificateRef parent
= (SecCertificateRef
)
531 CFArrayGetValueAtIndex(parents
, parentIX
);
532 CFIndex ixOfParent
= SecCertificatePathGetIndexOfCertificate(partial
,
534 if (ixOfParent
!= kCFNotFound
) {
535 /* partial already contains parent. Let's not add the same
536 certificate again. */
537 if (ixOfParent
== rootIX
) {
538 /* parent is equal to the root of the partial, so partial
539 looks to be self issued. */
540 SecCertificatePathSetSelfIssued(partial
);
545 /* FIXME Add more sanity checks to see that parent really can be
546 a parent of partial_root. subjectKeyID == authorityKeyID,
547 signature algorithm matches public key algorithm, etc. */
548 SecCertificateSourceRef source
= NULL
;
549 bool is_anchor
= SecPathBuilderIsAnchor(builder
, parent
, &source
);
550 CFArrayRef constraints
= (source
) ? SecCertificateSourceCopyUsageConstraints(source
, parent
) : NULL
;
551 SecCertificatePathRef path
= SecCertificatePathCreate(partial
, parent
, constraints
);
552 CFReleaseSafe(constraints
);
555 if (!CFSetContainsValue(builder
->allPaths
, path
)) {
556 CFSetAddValue(builder
->allPaths
, path
);
558 SecCertificatePathSetIsAnchored(path
);
559 if (SecPathBuilderIsPartial(builder
, path
)) {
560 /* Insert path right at the current position since it's a new
562 CFArrayInsertValueAtIndex(builder
->partialPaths
,
563 ++builder
->partialIX
, path
);
564 secdebug("trust", "Adding partial for parent %" PRIdCFIndex
"/%" PRIdCFIndex
" %@",
565 parentIX
+ 1, num_parents
, path
);
567 secdebug("trust", "found new path %@", path
);
573 /* Callback for the SecPathBuilderGetNext() functions call to
574 SecCertificateSourceCopyParents(). */
575 static void SecPathBuilderExtendPaths(void *context
, CFArrayRef parents
) {
576 SecPathBuilderRef builder
= (SecPathBuilderRef
)context
;
577 SecCertificatePathRef partial
= (SecCertificatePathRef
)
578 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
579 secdebug("async", "%@ parents %@", partial
, parents
);
580 SecPathBuilderProcessParents(builder
, partial
, parents
);
582 builder
->state
= SecPathBuilderGetNext
;
583 SecPathBuilderStep(builder
);
586 static bool SecPathBuilderGetNext(SecPathBuilderRef builder
) {
587 /* If we have any candidates left to go return those first. */
588 if (CFArrayGetCount(builder
->candidatePaths
)) {
589 SecCertificatePathRef path
= (SecCertificatePathRef
)
590 CFArrayGetValueAtIndex(builder
->candidatePaths
, 0);
591 CFArrayRemoveValueAtIndex(builder
->candidatePaths
, 0);
592 secdebug("trust", "SecPathBuilderGetNext returning candidate %@",
594 SecPVCSetPath(&builder
->path
, path
, NULL
);
595 builder
->state
= SecPathBuilderValidatePath
;
599 /* If we are considering rejected chains we check each rejected path
600 with SecPathBuilderIsPartial() which checks the signature chain and
601 either drops the path if it's not properly signed, add it as a
602 candidate if it has a trusted anchor, or adds it as a partial
603 to be considered once we finish considering all the rejects. */
604 if (builder
->considerRejected
) {
605 CFIndex rejectedIX
= CFArrayGetCount(builder
->rejectedPaths
);
608 SecCertificatePathRef path
= (SecCertificatePathRef
)
609 CFArrayGetValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
610 if (SecPathBuilderIsPartial(builder
, path
)) {
611 CFArrayInsertValueAtIndex(builder
->partialPaths
,
612 ++builder
->partialIX
, path
);
614 CFArrayRemoveValueAtIndex(builder
->rejectedPaths
, rejectedIX
);
616 /* Keep going until we have moved all rejected partials into
617 the regular partials or candidates array. */
622 /* If builder->partialIX is < 0 we have considered all partial chains
623 this block must ensure partialIX >= 0 if execution continues past
625 if (builder
->partialIX
< 0) {
626 CFIndex num_sources
= CFArrayGetCount(builder
->parentSources
);
627 if (builder
->nextParentSource
< num_sources
) {
628 builder
->nextParentSource
++;
629 secdebug("trust", "broading search to %" PRIdCFIndex
"/%" PRIdCFIndex
" sources",
630 builder
->nextParentSource
, num_sources
);
632 /* We've run out of new sources to consider so let's look at
633 rejected chains and after that even consider partials
635 FIXME we might not want to consider partial paths that
636 are subsets of other partial paths, or not consider them
637 at all if we already have an anchored reject. */
638 if (!builder
->considerRejected
) {
639 builder
->considerRejected
= true;
640 secdebug("trust", "considering rejected paths");
641 } else if (!builder
->considerPartials
) {
642 builder
->considerPartials
= true;
643 secdebug("trust", "considering partials");
645 /* We're all out of options, so we can't produce any more
646 candidates. Let's calculate details and return the best
648 builder
->state
= SecPathBuilderComputeDetails
;
652 builder
->partialIX
= CFArrayGetCount(builder
->partialPaths
) - 1;
653 secdebug("trust", "re-checking %" PRIdCFIndex
" partials", builder
->partialIX
+ 1);
657 /* We know builder->partialIX >= 0 if we get here. */
658 SecCertificatePathRef partial
= (SecCertificatePathRef
)
659 CFArrayGetValueAtIndex(builder
->partialPaths
, builder
->partialIX
);
660 /* Don't try to extend partials anymore once we are in the considerPartials
661 state, since at this point every partial has been extended with every
662 possible parentSource already. */
663 if (builder
->considerPartials
) {
664 --builder
->partialIX
;
665 SecPVCSetPath(&builder
->path
, partial
, NULL
);
666 builder
->state
= SecPathBuilderValidatePath
;
670 /* Attempt to extend this partial path with another certificate. This
671 should give us a list of potential parents to consider. */
672 secdebug("trust", "looking for parents of partial %" PRIdCFIndex
"/%" PRIdCFIndex
": %@",
673 builder
->partialIX
+ 1, CFArrayGetCount(builder
->partialPaths
),
676 /* Attempt to extend partial, leaving all possible extended versions
677 of partial in builder->extendedPaths. */
678 CFIndex sourceIX
= SecCertificatePathGetNextSourceIndex(partial
);
679 CFIndex num_anchor_sources
= CFArrayGetCount(builder
->anchorSources
);
680 if (sourceIX
< num_anchor_sources
+ builder
->nextParentSource
) {
681 SecCertificateSourceRef source
;
682 if (sourceIX
< num_anchor_sources
) {
683 source
= (SecCertificateSourceRef
)
684 CFArrayGetValueAtIndex(builder
->anchorSources
, sourceIX
);
685 secdebug("trust", "searching anchor source %" PRIdCFIndex
"/%" PRIdCFIndex
, sourceIX
+ 1,
688 CFIndex parentIX
= sourceIX
- num_anchor_sources
;
689 source
= (SecCertificateSourceRef
)
690 CFArrayGetValueAtIndex(builder
->parentSources
, parentIX
);
691 secdebug("trust", "searching parent source %" PRIdCFIndex
"/%" PRIdCFIndex
, parentIX
+ 1,
692 builder
->nextParentSource
);
694 SecCertificatePathSetNextSourceIndex(partial
, sourceIX
+ 1);
695 SecCertificateRef root
= SecCertificatePathGetRoot(partial
);
696 return SecCertificateSourceCopyParents(source
, root
,
697 builder
, SecPathBuilderExtendPaths
);
699 --builder
->partialIX
;
705 /* One or more of the policies did not accept the candidate path. */
706 static void SecPathBuilderReject(SecPathBuilderRef builder
) {
708 SecPVCRef pvc
= &builder
->path
;
710 builder
->state
= SecPathBuilderGetNext
;
712 if (builder
->bestPathIsEV
&& !pvc
->is_ev
) {
713 /* We never replace an ev reject with a non ev reject. */
717 CFIndex bestPathScore
= builder
->bestPathScore
;
718 CFIndex score
= SecCertificatePathScore(builder
->path
.path
,
719 SecPVCGetVerifyTime(&builder
->path
));
721 /* The current chain is valid for EV, but revocation checking failed. We
722 replace any previously accepted or rejected non EV chains with the
724 if (pvc
->is_ev
&& !builder
->bestPathIsEV
) {
730 /* Since this means we found a valid ev chain that was revoked,
731 we might want to switch directly to the
732 SecPathBuilderComputeDetails state here if we think further
733 searching for new chains is pointless. For now we'll keep
734 going, since we could accept an alternate EV certification
735 path that isn't revoked. */
736 builder
->state
= SecPathBuilderComputeDetails
;
740 /* Do this last so that changes to bestPathScore above will take effect. */
741 if (!builder
->bestPath
|| score
> bestPathScore
) {
742 if (builder
->bestPath
) {
744 "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
745 (builder
->bestPathIsEV
? "" : "non "),
746 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
747 builder
->bestPathScore
,
748 (pvc
->is_ev
? "" : "non "), (long)score
, builder
->path
.path
);
750 secinfo("reject", "%sev score: %" PRIdCFIndex
" %@",
751 (pvc
->is_ev
? "" : "non "), score
, builder
->path
.path
);
754 builder
->bestPathScore
= score
;
755 builder
->bestPath
= pvc
->path
;
756 builder
->bestPathIsEV
= pvc
->is_ev
;
757 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
);
759 secinfo("reject", "%sev score: %" PRIdCFIndex
" lower than %" PRIdCFIndex
" %@",
760 (pvc
->is_ev
? "" : "non "), score
, bestPathScore
, builder
->path
.path
);
764 /* All policies accepted the candidate path. */
765 static void SecPathBuilderAccept(SecPathBuilderRef builder
) {
766 if (!builder
) { return; }
767 SecPVCRef pvc
= &builder
->path
;
768 if (!pvc
) { return; }
769 bool isSHA2
= !SecCertificatePathHasWeakHash(pvc
->path
);
770 bool isOptionallySHA2
= !SecCertificateIsWeakHash(SecPVCGetCertificateAtIndex(pvc
, 0));
771 CFIndex bestScore
= builder
->bestPathScore
;
772 /* Score this path. Note that all points awarded or deducted in
773 * SecCertificatePathScore are < 100,000 */
774 CFIndex currScore
= (SecCertificatePathScore(pvc
->path
, pvc
->verifyTime
) +
775 ACCEPT_PATH_SCORE
+ // 10,000,000 points for accepting
776 ((pvc
->is_ev
) ? 1000000 : 0)); //1,000,000 points for EV
777 if (currScore
> bestScore
) {
778 // current path is better than existing best path
779 secinfo("accept", "replacing %sev %s score: %ld with %sev score: %" PRIdCFIndex
" %@",
780 (builder
->bestPathIsEV
? "" : "non "),
781 (builder
->bestPathScore
> ACCEPT_PATH_SCORE
? "accept" : "reject"),
782 builder
->bestPathScore
,
783 (pvc
->is_ev
? "" : "non "), (long)currScore
, builder
->path
.path
);
785 builder
->bestPathScore
= currScore
;
786 builder
->bestPathIsEV
= pvc
->is_ev
;
787 builder
->bestPathIsSHA2
= isSHA2
;
788 builder
->bestPath
= pvc
->path
;
789 builder
->denyBestPath
= SecPVCCheckUsageConstraints(pvc
); /* should always be false */
792 /* If we found the best accept we can, we want to switch directly to the
793 SecPathBuilderComputeDetails state here, since we're done. */
794 if ((pvc
->is_ev
|| !pvc
->optionally_ev
) && (isSHA2
|| !isOptionallySHA2
))
795 builder
->state
= SecPathBuilderComputeDetails
;
797 builder
->state
= SecPathBuilderGetNext
;
800 /* Return true iff a given path satisfies all the specified policies at
802 static bool SecPathBuilderValidatePath(SecPathBuilderRef builder
) {
803 SecPVCRef pvc
= &builder
->path
;
805 if (builder
->considerRejected
) {
806 SecPathBuilderReject(builder
);
810 builder
->state
= SecPathBuilderDidValidatePath
;
811 return SecPVCPathChecks(pvc
);
814 static bool SecPathBuilderDidValidatePath(SecPathBuilderRef builder
) {
815 SecPVCRef pvc
= &builder
->path
;
817 SecPathBuilderAccept(builder
);
819 SecPathBuilderReject(builder
);
821 assert(builder
->state
!= SecPathBuilderDidValidatePath
);
825 static bool SecPathBuilderComputeDetails(SecPathBuilderRef builder
) {
827 SecPVCRef pvc
= &builder
->path
;
829 if (!builder
->caller_wants_details
) {
830 SecPVCSetPath(pvc
, builder
->bestPath
, NULL
);
831 pvc
->result
= builder
->bestPathScore
> ACCEPT_PATH_SCORE
;
832 builder
->state
= SecPathBuilderReportResult
;
836 CFIndex ix
, pathLength
= SecCertificatePathGetCount(builder
->bestPath
);
837 CFMutableArrayRef details
= CFArrayCreateMutableCopy(kCFAllocatorDefault
,
838 pathLength
, builder
->leafDetails
);
839 CFRetainSafe(details
);
840 SecPVCSetPath(pvc
, builder
->bestPath
, details
);
841 /* Only report on EV stuff if the bestPath actually was valid for EV. */
842 pvc
->optionally_ev
= builder
->bestPathIsEV
;
843 pvc
->info
= CFDictionaryCreateMutable(kCFAllocatorDefault
,
844 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
845 for (ix
= 1; ix
< pathLength
; ++ix
) {
846 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(
847 kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
848 &kCFTypeDictionaryValueCallBacks
);
849 CFArrayAppendValue(details
, certDetail
);
850 CFRelease(certDetail
);
851 SecPVCParentCertificateChecks(pvc
, ix
);
852 SecPVCGrayListedKeyChecks(pvc
, ix
);
853 SecPVCBlackListedKeyChecks(pvc
, ix
);
855 builder
->state
= SecPathBuilderReportResult
;
856 bool completed
= SecPVCPathChecks(pvc
);
858 /* Reject the certificate if it was accepted before but we failed it now. */
859 if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
&& !pvc
->result
) {
860 builder
->bestPathScore
= 0;
863 /* Accept a partial path if certificate is on the allow list
864 and is temporally valid and passed all PVC checks. */
865 if (completed
&& pvc
->is_allowlisted
&& pvc
->result
&&
866 builder
->bestPathScore
< ACCEPT_PATH_SCORE
&&
867 SecCertificatePathIsValid(pvc
->path
, pvc
->verifyTime
)) {
868 builder
->bestPathScore
+= ACCEPT_PATH_SCORE
;
871 CFReleaseSafe(details
);
876 static bool SecPathBuilderReportResult(SecPathBuilderRef builder
) {
877 SecPVCRef pvc
= &builder
->path
;
878 bool haveRevocationResponse
= false;
879 if (pvc
->info
&& pvc
->is_ev
&& pvc
->result
) {
880 CFDictionarySetValue(pvc
->info
, kSecTrustInfoExtendedValidationKey
,
881 kCFBooleanTrue
); /* iOS key */
882 CFDictionarySetValue(pvc
->info
, kSecTrustExtendedValidation
,
883 kCFBooleanTrue
); /* unified API key */
884 SecCertificateRef leaf
= SecPVCGetCertificateAtIndex(pvc
, 0);
885 CFStringRef leafCompanyName
= SecCertificateCopyCompanyName(leaf
);
886 if (leafCompanyName
) {
887 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCompanyNameKey
,
888 leafCompanyName
); /* iOS key */
889 CFDictionarySetValue(pvc
->info
, kSecTrustOrganizationName
,
890 leafCompanyName
); /* unified API key */
891 CFRelease(leafCompanyName
);
894 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
895 if (nextUpdate
== 0) {
896 /* populate revocation info for failed revocation check */
897 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
898 kCFBooleanFalse
); /* iOS key */
899 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
900 kCFBooleanFalse
); /* unified API key */
905 if (pvc
->info
&& pvc
->result
&& pvc
->rvcs
) {
906 CFAbsoluteTime nextUpdate
= SecPVCGetEarliestNextUpdate(pvc
);
907 if (nextUpdate
!= 0) {
908 /* always populate revocation info for successful revocation check */
909 haveRevocationResponse
= true;
910 CFDateRef validUntil
= CFDateCreate(kCFAllocatorDefault
, nextUpdate
);
911 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationValidUntilKey
,
912 validUntil
); /* iOS key */
913 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationValidUntilDate
,
914 validUntil
); /* unified API key */
915 CFRelease(validUntil
);
916 CFDictionarySetValue(pvc
->info
, kSecTrustInfoRevocationKey
,
917 kCFBooleanTrue
); /* iOS key */
918 CFDictionarySetValue(pvc
->info
, kSecTrustRevocationChecked
,
919 kCFBooleanTrue
); /* unified API key */
923 if (pvc
->info
&& pvc
->result
&& pvc
->response_required
&& !haveRevocationResponse
) {
924 builder
->bestPathScore
= 0;
925 SecPVCSetResultForced(pvc
, kSecPolicyCheckRevocationResponseRequired
,
926 0, kCFBooleanFalse
, true);
929 if (pvc
->info
&& pvc
->is_ct
&& pvc
->result
) {
930 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyKey
,
934 if (pvc
->info
&& pvc
->is_ct_whitelisted
&& pvc
->result
) {
935 CFDictionarySetValue(pvc
->info
, kSecTrustInfoCertificateTransparencyWhiteListKey
,
940 /* This will trigger the outer step function to call the completion
942 builder
->state
= NULL
;
946 /* @function SecPathBuilderStep
947 @summary This is the core of the async engine.
948 @description Return false iff job is complete, true if a network request
950 builder->state is a function pointer which is to be invoked.
951 If you call this function from within a builder->state invocation it
952 immediately returns true.
953 Otherwise the following steps are repeated endlessly (unless a step returns)
954 builder->state is invoked. If it returns true and builder->state is still
955 non NULL this proccess is repeated.
956 If a state returns false, SecPathBuilder will return true
957 if builder->state is non NULL.
958 If builder->state is NULL then regardless of what the state function returns
959 the completion callback will be invoked and the builder will be deallocated.
961 bool SecPathBuilderStep(SecPathBuilderRef builder
) {
962 if (builder
->activations
) {
963 secdebug("async", "activations: %lu returning true",
964 builder
->activations
);
968 secdebug("async", "activations: %lu", builder
->activations
);
969 builder
->activations
++;
970 while (builder
->state
&& builder
->state(builder
));
971 --builder
->activations
;
973 if (builder
->state
) {
974 secdebug("async", "waiting for async reply, exiting");
975 /* A state returned false, it's waiting for network traffic. Let's
980 if (builder
->activations
) {
981 /* There is still at least one other running instance of this builder
982 somewhere on the stack, we let that instance take care of sending
983 the client a response. */
987 SecTrustResultType result
= kSecTrustResultInvalid
;
988 if (builder
->denyBestPath
) {
989 result
= kSecTrustResultDeny
;
990 } else if (builder
->bestPathScore
> ACCEPT_PATH_SCORE
) {
991 result
= kSecTrustResultUnspecified
;
993 result
= kSecTrustResultRecoverableTrustFailure
;
996 secinfo("trust", "completed: %@ details: %@ result: %d",
997 builder
->bestPath
, builder
->path
.details
, result
);
999 if (builder
->completed
) {
1000 builder
->completed(builder
->context
, builder
->bestPath
,
1001 builder
->path
.details
, builder
->path
.info
, result
);
1004 /* Finally, destroy the builder and free it. */
1005 SecPathBuilderDestroy(builder
);
1011 dispatch_queue_t
SecPathBuilderGetQueue(SecPathBuilderRef builder
) {
1012 return (builder
) ? builder
->queue
: NULL
;
1015 CFDataRef
SecPathBuilderCopyClientAuditToken(SecPathBuilderRef builder
) {
1016 return (builder
) ? (CFDataRef
)CFRetainSafe(builder
->clientAuditToken
) : NULL
;
1020 // MARK: SecTrustServer
1021 /********************************************************
1022 ****************** SecTrustServer **********************
1023 ********************************************************/
1025 typedef void (^SecTrustServerEvaluationCompleted
)(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
);
1028 SecTrustServerEvaluateCompleted(const void *userData
,
1029 SecCertificatePathRef chain
, CFArrayRef details
, CFDictionaryRef info
,
1030 SecTrustResultType result
) {
1031 SecTrustServerEvaluationCompleted evaluated
= (SecTrustServerEvaluationCompleted
)userData
;
1032 evaluated(result
, details
, info
, chain
, NULL
);
1033 Block_release(evaluated
);
1037 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
)) {
1038 /* We need an array containing at least one certificate to proceed. */
1039 if (!isArray(certificates
) || !(CFArrayGetCount(certificates
) > 0)) {
1040 CFErrorRef certError
= CFErrorCreate(NULL
, kCFErrorDomainOSStatus
, errSecInvalidCertificate
, NULL
);
1041 evaluated(kSecTrustResultInvalid
, NULL
, NULL
, NULL
, certError
);
1042 CFReleaseSafe(certError
);
1045 SecTrustServerEvaluationCompleted userData
= Block_copy(evaluated
);
1046 /* Call the actual evaluator function. */
1047 SecPathBuilderRef builder
= SecPathBuilderCreate(clientAuditToken
,
1048 certificates
, anchors
,
1049 anchorsOnly
, keychainsAllowed
, policies
,
1050 responses
, SCTs
, trustedLogs
,
1051 verifyTime
, accessGroups
,
1052 SecTrustServerEvaluateCompleted
, userData
);
1053 dispatch_async(builder
->queue
, ^{ SecPathBuilderStep(builder
); });
1057 // NO_SERVER Shim code only, xpc interface should call SecTrustServerEvaluateBlock() directly
1058 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
) {
1059 dispatch_semaphore_t done
= dispatch_semaphore_create(0);
1060 __block SecTrustResultType result
= kSecTrustResultInvalid
;
1061 SecTrustServerEvaluateBlock(NULL
, certificates
, anchors
, anchorsOnly
, keychainsAllowed
, policies
, responses
, SCTs
, trustedLogs
, verifyTime
, accessGroups
, ^(SecTrustResultType tr
, CFArrayRef details
, CFDictionaryRef info
, SecCertificatePathRef chain
, CFErrorRef error
) {
1063 if (tr
== kSecTrustResultInvalid
) {
1066 CFRetainSafe(error
);
1070 *pdetails
= details
;
1071 CFRetainSafe(details
);
1079 CFRetainSafe(chain
);
1082 dispatch_semaphore_signal(done
);
1084 dispatch_semaphore_wait(done
, DISPATCH_TIME_FOREVER
);