2 * Copyright (c) 2006-2015 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 * SecTrust.c - CoreFoundation based certificate trust evaluator
27 #include <Security/SecTrustPriv.h>
28 #include <Security/SecTrustInternal.h>
29 #include <Security/SecItemPriv.h>
30 #include <Security/SecCertificateInternal.h>
31 #include <Security/SecCertificatePath.h>
32 #include <Security/SecFramework.h>
33 #include <Security/SecPolicyCerts.h>
34 #include <Security/SecPolicyInternal.h>
35 #include <Security/SecPolicyPriv.h>
36 #include <Security/SecuritydXPC.h>
37 #include <Security/SecInternal.h>
38 #include <Security/SecBasePriv.h>
39 #include <CoreFoundation/CFRuntime.h>
40 #include <CoreFoundation/CFSet.h>
41 #include <CoreFoundation/CFString.h>
42 #include <CoreFoundation/CFNumber.h>
43 #include <CoreFoundation/CFArray.h>
44 #include <CoreFoundation/CFPropertyList.h>
45 #include <AssertMacros.h>
51 #include <os/activity.h>
53 #include <utilities/SecIOFormat.h>
54 #include <utilities/SecCFError.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <utilities/SecCertificateTrace.h>
57 #include <utilities/debugging.h>
59 #include "SecRSAKey.h"
60 #include <libDER/oids.h>
62 #include <ipc/securityd_client.h>
64 #include <securityd/SecTrustServer.h>
66 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
68 SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey
, "ExtendedValidation");
69 SEC_CONST_DECL (kSecTrustInfoCompanyNameKey
, "CompanyName");
70 SEC_CONST_DECL (kSecTrustInfoRevocationKey
, "Revocation");
71 SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey
, "RevocationValidUntil");
72 SEC_CONST_DECL (kSecTrustInfoCertificateTransparencyKey
, "CertificateTransparency");
74 /* Public trust result constants */
75 SEC_CONST_DECL (kSecTrustEvaluationDate
, "TrustEvaluationDate");
76 SEC_CONST_DECL (kSecTrustExtendedValidation
, "TrustExtendedValidation");
77 SEC_CONST_DECL (kSecTrustOrganizationName
, "Organization");
78 SEC_CONST_DECL (kSecTrustResultValue
, "TrustResultValue");
79 SEC_CONST_DECL (kSecTrustRevocationChecked
, "TrustRevocationChecked");
80 SEC_CONST_DECL (kSecTrustRevocationReason
, "TrustRevocationReason");
81 SEC_CONST_DECL (kSecTrustRevocationValidUntilDate
, "TrustExpirationDate");
82 SEC_CONST_DECL (kSecTrustResultDetails
, "TrustResultDetails");
83 SEC_CONST_DECL (kSecTrustCertificateTransparency
, "TrustCertificateTransparency");
88 /********************************************************
89 ****************** SecTrust object *********************
90 ********************************************************/
93 CFArrayRef _certificates
;
96 CFArrayRef _responses
;
98 CFArrayRef _trustedLogs
;
99 CFDateRef _verifyDate
;
100 SecCertificatePathRef _chain
;
101 SecKeyRef _publicKey
;
103 CFDictionaryRef _info
;
104 CFArrayRef _exceptions
;
106 /* Note that a value of kSecTrustResultInvalid (0)
107 * indicates the trust must be (re)evaluated; any
108 * functions which modify trust parameters in a way
109 * that would invalidate the current result must set
110 * this value back to kSecTrustResultInvalid.
112 SecTrustResultType _trustResult
;
114 /* If true we don't trust any anchors other than the ones in _anchors. */
117 /* Master switch to permit or disable network use in policy evaluation */
118 SecNetworkPolicy _networkPolicy
;
121 /* Forward declarations of static functions. */
122 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
);
124 /* Static functions. */
125 static CFStringRef
SecTrustCopyFormatDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
) {
126 SecTrustRef trust
= (SecTrustRef
)cf
;
127 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
128 CFSTR("<SecTrustRef: %p>"), trust
);
131 static void SecTrustDestroy(CFTypeRef cf
) {
132 SecTrustRef trust
= (SecTrustRef
)cf
;
133 CFReleaseSafe(trust
->_certificates
);
134 CFReleaseSafe(trust
->_policies
);
135 CFReleaseSafe(trust
->_responses
);
136 CFReleaseSafe(trust
->_SCTs
);
137 CFReleaseSafe(trust
->_trustedLogs
);
138 CFReleaseSafe(trust
->_verifyDate
);
139 CFReleaseSafe(trust
->_anchors
);
140 CFReleaseSafe(trust
->_chain
);
141 CFReleaseSafe(trust
->_publicKey
);
142 CFReleaseSafe(trust
->_details
);
143 CFReleaseSafe(trust
->_info
);
144 CFReleaseSafe(trust
->_exceptions
);
147 /* Public API functions. */
148 CFGiblisFor(SecTrust
)
150 OSStatus
SecTrustCreateWithCertificates(CFTypeRef certificates
,
151 CFTypeRef policies
, SecTrustRef
*trust
) {
152 OSStatus status
= errSecParam
;
153 CFAllocatorRef allocator
= kCFAllocatorDefault
;
154 CFArrayRef l_certs
= NULL
, l_policies
= NULL
;
155 SecTrustRef result
= NULL
;
159 CFTypeID certType
= CFGetTypeID(certificates
);
160 if (certType
== CFArrayGetTypeID()) {
161 /* We need at least 1 certificate. */
162 require_quiet(CFArrayGetCount(certificates
) > 0, errOut
);
163 l_certs
= CFArrayCreateCopy(allocator
, certificates
);
164 } else if (certType
== SecCertificateGetTypeID()) {
165 l_certs
= CFArrayCreate(allocator
, &certificates
, 1,
166 &kCFTypeArrayCallBacks
);
171 status
= errSecAllocate
;
176 CFTypeRef policy
= SecPolicyCreateBasicX509();
177 l_policies
= CFArrayCreate(allocator
, &policy
, 1,
178 &kCFTypeArrayCallBacks
);
181 else if (CFGetTypeID(policies
) == CFArrayGetTypeID()) {
182 #if (SECTRUST_OSX && TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
183 #warning STU: <rdar://21330613>
184 // On OS X, passing an array consisting of the ssl and
185 // revocation policies is causing us not to return all the
186 // expected EV keys from SecTrustCopyResult, whereas if the
187 // revocation policy is omitted, they are present.
189 CFMutableArrayRef t_policies
= CFArrayCreateMutable(allocator
, 0,
190 &kCFTypeArrayCallBacks
);
191 CFIndex ix
, count
= CFArrayGetCount(policies
);
192 SecPolicyRef revocationPolicy
= NULL
, sslServerPolicy
= NULL
;
193 for (ix
=0; ix
<count
; ix
++) {
194 SecPolicyRef t_policy
= (SecPolicyRef
) CFArrayGetValueAtIndex(policies
, ix
);
195 CFStringRef oidstr
= SecPolicyGetOidString(t_policy
);
196 if (oidstr
&& CFEqual(oidstr
, CFSTR("sslServer"))) {
197 sslServerPolicy
= t_policy
;
199 if (oidstr
&& CFEqual(oidstr
, CFSTR("revocation"))) {
200 revocationPolicy
= t_policy
;
201 } else if (t_policy
) {
202 CFArrayAppendValue(t_policies
, t_policy
);
205 if (revocationPolicy
&& !(sslServerPolicy
&& count
==2)) {
206 CFArrayAppendValue(t_policies
, revocationPolicy
);
208 l_policies
= CFArrayCreateCopy(allocator
, t_policies
);
209 CFReleaseSafe(t_policies
);
211 l_policies
= CFArrayCreateCopy(allocator
, policies
);
214 else if (CFGetTypeID(policies
) == SecPolicyGetTypeID()) {
215 l_policies
= CFArrayCreate(allocator
, &policies
, 1,
216 &kCFTypeArrayCallBacks
);
221 status
= errSecAllocate
;
225 CFIndex size
= sizeof(struct __SecTrust
);
226 require_quiet(result
= (SecTrustRef
)_CFRuntimeCreateInstance(allocator
,
227 SecTrustGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
228 memset((char*)result
+ sizeof(result
->_base
), 0,
229 sizeof(*result
) - sizeof(result
->_base
));
230 status
= errSecSuccess
;
234 CFReleaseSafe(result
);
235 CFReleaseSafe(l_certs
);
236 CFReleaseSafe(l_policies
);
238 result
->_certificates
= l_certs
;
239 result
->_policies
= l_policies
;
243 CFReleaseSafe(result
);
248 static void SetTrustSetNeedsEvaluation(SecTrustRef trust
) {
251 trust
->_trustResult
= kSecTrustResultInvalid
;
255 OSStatus
SecTrustSetAnchorCertificatesOnly(SecTrustRef trust
,
256 Boolean anchorCertificatesOnly
) {
260 SetTrustSetNeedsEvaluation(trust
);
261 trust
->_anchorsOnly
= anchorCertificatesOnly
;
263 return errSecSuccess
;
266 OSStatus
SecTrustSetAnchorCertificates(SecTrustRef trust
,
267 CFArrayRef anchorCertificates
) {
271 SetTrustSetNeedsEvaluation(trust
);
272 if (anchorCertificates
)
273 CFRetain(anchorCertificates
);
275 CFRelease(trust
->_anchors
);
276 trust
->_anchors
= anchorCertificates
;
277 trust
->_anchorsOnly
= (anchorCertificates
!= NULL
);
279 return errSecSuccess
;
282 OSStatus
SecTrustCopyCustomAnchorCertificates(SecTrustRef trust
,
283 CFArrayRef
*anchors
) {
284 if (!trust
|| !anchors
) {
287 CFArrayRef anchorsArray
= NULL
;
288 if (trust
->_anchors
) {
289 anchorsArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_anchors
);
291 return errSecAllocate
;
294 *anchors
= anchorsArray
;
295 return errSecSuccess
;
298 OSStatus
SecTrustSetOCSPResponse(SecTrustRef trust
, CFTypeRef responseData
) {
302 SetTrustSetNeedsEvaluation(trust
);
303 CFArrayRef responseArray
= NULL
;
305 if (CFGetTypeID(responseData
) == CFArrayGetTypeID()) {
306 responseArray
= CFArrayCreateCopy(kCFAllocatorDefault
, responseData
);
307 } else if (CFGetTypeID(responseData
) == CFDataGetTypeID()) {
308 responseArray
= CFArrayCreate(kCFAllocatorDefault
, &responseData
, 1,
309 &kCFTypeArrayCallBacks
);
314 if (trust
->_responses
)
315 CFRelease(trust
->_responses
);
316 trust
->_responses
= responseArray
;
318 return errSecSuccess
;
321 OSStatus
SecTrustSetSignedCertificateTimestamps(SecTrustRef trust
, CFArrayRef sctArray
) {
325 SetTrustSetNeedsEvaluation(trust
);
326 CFRetainAssign(trust
->_SCTs
, sctArray
);
328 return errSecSuccess
;
331 OSStatus
SecTrustSetTrustedLogs(SecTrustRef trust
, CFArrayRef trustedLogs
) {
335 SetTrustSetNeedsEvaluation(trust
);
336 CFRetainAssign(trust
->_trustedLogs
, trustedLogs
);
338 return errSecSuccess
;
341 OSStatus
SecTrustSetVerifyDate(SecTrustRef trust
, CFDateRef verifyDate
) {
345 SetTrustSetNeedsEvaluation(trust
);
347 CFRetainAssign(trust
->_verifyDate
, verifyDate
);
349 return errSecSuccess
;
352 OSStatus
SecTrustSetPolicies(SecTrustRef trust
, CFTypeRef newPolicies
) {
353 if (!trust
|| !newPolicies
) {
356 SetTrustSetNeedsEvaluation(trust
);
359 CFArrayRef policyArray
= NULL
;
360 if (CFGetTypeID(newPolicies
) == CFArrayGetTypeID()) {
361 policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, newPolicies
);
362 } else if (CFGetTypeID(newPolicies
) == SecPolicyGetTypeID()) {
363 policyArray
= CFArrayCreate(kCFAllocatorDefault
, &newPolicies
, 1,
364 &kCFTypeArrayCallBacks
);
369 if (trust
->_policies
)
370 CFRelease(trust
->_policies
);
371 trust
->_policies
= policyArray
;
373 return errSecSuccess
;
376 OSStatus
SecTrustCopyPolicies(SecTrustRef trust
, CFArrayRef
*policies
) {
377 if (!trust
|| !policies
) {
380 if (!trust
->_policies
) {
381 return errSecInternal
;
383 CFArrayRef policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_policies
);
385 return errSecAllocate
;
387 *policies
= policyArray
;
388 return errSecSuccess
;
391 OSStatus
SecTrustSetNetworkFetchAllowed(SecTrustRef trust
, Boolean allowFetch
) {
395 trust
->_networkPolicy
= (allowFetch
) ? useNetworkEnabled
: useNetworkDisabled
;
396 return errSecSuccess
;
399 OSStatus
SecTrustGetNetworkFetchAllowed(SecTrustRef trust
, Boolean
*allowFetch
) {
400 if (!trust
|| !allowFetch
) {
403 Boolean allowed
= false;
404 SecNetworkPolicy netPolicy
= trust
->_networkPolicy
;
405 if (netPolicy
== useNetworkDefault
) {
406 // network fetch is enabled by default for SSL only
407 CFIndex idx
, count
= (trust
->_policies
) ? CFArrayGetCount(trust
->_policies
) : 0;
408 for (idx
=0; idx
<count
; idx
++) {
409 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(trust
->_policies
, idx
);
411 CFDictionaryRef props
= SecPolicyCopyProperties(policy
);
413 CFTypeRef value
= (CFTypeRef
)CFDictionaryGetValue(props
, kSecPolicyOid
);
415 if (CFEqual(value
, kSecPolicyAppleSSL
)) {
424 // caller has explicitly set the network policy
425 allowed
= (netPolicy
== useNetworkEnabled
);
427 *allowFetch
= allowed
;
428 return errSecSuccess
;
431 CFAbsoluteTime
SecTrustGetVerifyTime(SecTrustRef trust
) {
432 CFAbsoluteTime verifyTime
;
433 if (trust
&& trust
->_verifyDate
) {
434 verifyTime
= CFDateGetAbsoluteTime(trust
->_verifyDate
);
436 verifyTime
= CFAbsoluteTimeGetCurrent();
437 /* Record the verifyDate we ended up using. */
439 trust
->_verifyDate
= CFDateCreate(CFGetAllocator(trust
), verifyTime
);
445 CFArrayRef
SecTrustGetDetails(SecTrustRef trust
) {
449 SecTrustEvaluateIfNecessary(trust
);
450 return trust
->_details
;
453 OSStatus
SecTrustGetTrustResult(SecTrustRef trust
,
454 SecTrustResultType
*result
) {
455 if (!trust
|| !result
) {
458 *result
= trust
->_trustResult
;
459 return errSecSuccess
;
462 static CFStringRef kSecCertificateDetailSHA1Digest
= CFSTR("SHA1Digest");
464 static CFDictionaryRef
SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust
, CFIndex ix
) {
465 if (!trust
->_exceptions
|| ix
>= CFArrayGetCount(trust
->_exceptions
))
467 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_exceptions
, ix
);
468 if (CFGetTypeID(exception
) != CFDictionaryGetTypeID())
471 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
475 /* If the exception contains the current certificates sha1Digest in the
476 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
477 CFDataRef sha1Digest
= SecCertificateGetSHA1Digest(certificate
);
478 CFTypeRef digestValue
= CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
);
479 if (!digestValue
|| !CFEqual(sha1Digest
, digestValue
))
485 struct SecTrustCheckExceptionContext
{
486 CFDictionaryRef exception
;
487 bool exceptionNotFound
;
491 static void SecTrustCheckException(const void *key
, const void *value
, void *context
) {
492 struct SecTrustCheckExceptionContext
*cec
= (struct SecTrustCheckExceptionContext
*)context
;
493 if (cec
->exception
) {
494 CFTypeRef exceptionValue
= CFDictionaryGetValue(cec
->exception
, key
);
495 if (!exceptionValue
|| !CFEqual(value
, exceptionValue
)) {
496 cec
->exceptionNotFound
= true;
499 cec
->exceptionNotFound
= true;
504 static CFArrayRef
SecTrustCreatePolicyAnchorsArray(const UInt8
* certData
, CFIndex certLength
)
506 CFArrayRef array
= NULL
;
507 CFAllocatorRef allocator
= kCFAllocatorDefault
;
508 SecCertificateRef cert
= SecCertificateCreateWithBytes(allocator
, certData
, certLength
);
510 array
= CFArrayCreate(allocator
, (const void **)&cert
, 1, &kCFTypeArrayCallBacks
);
517 static void SecTrustAddPolicyAnchors(SecTrustRef trust
)
519 /* Provide anchor certificates specifically required by certain policies.
520 This is used to evaluate test policies where the anchor is not provided
521 in the root store and may not be able to be supplied by the caller.
523 CFArrayRef policies
= (trust
) ? trust
->_policies
: NULL
;
527 CFIndex ix
, count
= CFArrayGetCount(policies
);
528 for (ix
= 0; ix
< count
; ++ix
) {
529 SecPolicyRef policy
= (SecPolicyRef
) CFArrayGetValueAtIndex(policies
, ix
);
532 if (CFEqual(policy
->_oid
, kSecPolicyAppleTestSMPEncryption
)) {
533 CFReleaseSafe(trust
->_anchors
);
534 trust
->_anchors
= SecTrustCreatePolicyAnchorsArray(_SEC_TestAppleRootCAECC
, sizeof(_SEC_TestAppleRootCAECC
));
535 trust
->_anchorsOnly
= true;
544 // uncomment for verbose debug logging (debug builds only)
545 //#define CERT_TRUST_DUMP 1
548 static void sectrustlog(int priority
, const char *format
, ...)
553 if (priority
< LOG_NOTICE
) // log warnings and errors
557 va_start(list
, format
);
558 vsyslog(priority
, format
, list
);
563 static void sectrustshow(CFTypeRef obj
, const char *context
)
566 CFStringRef desc
= CFCopyDescription(obj
);
569 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(desc
), kCFStringEncodingUTF8
) + 1;
570 char* buffer
= (char*) malloc(length
);
572 Boolean converted
= CFStringGetCString(desc
, buffer
, length
, kCFStringEncodingUTF8
);
574 const char *prefix
= (context
) ? context
: "";
575 const char *separator
= (context
) ? " " : "";
576 sectrustlog(LOG_NOTICE
, "%s%s%s", prefix
, separator
, buffer
);
584 static void cert_trust_dump(SecTrustRef trust
) {
585 SecCertificateRef leaf
= (SecCertificateRef
) CFArrayGetValueAtIndex(trust
->_certificates
, 0);
586 CFStringRef name
= (leaf
) ? SecCertificateCopySubjectSummary(leaf
) : NULL
;
587 secerror("leaf \"%@\"", name
);
588 secerror(": result = %d", (int) trust
->_trustResult
);
590 CFIndex ix
, count
= SecCertificatePathGetCount(trust
->_chain
);
591 CFMutableArrayRef chain
= CFArrayCreateMutable(kCFAllocatorDefault
, count
, &kCFTypeArrayCallBacks
);
592 for (ix
= 0; ix
< count
; ix
++) {
593 SecCertificateRef cert
= SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
);
595 CFArrayAppendValue(chain
, cert
);
598 sectrustshow(chain
, "chain:");
599 CFReleaseSafe(chain
);
601 secerror(": %ld certificates, %ld anchors, %ld policies, %ld details",
602 (trust
->_certificates
) ? (long)CFArrayGetCount(trust
->_certificates
) : 0,
603 (trust
->_anchors
) ? (long)CFArrayGetCount(trust
->_anchors
) : 0,
604 (trust
->_policies
) ? (long)CFArrayGetCount(trust
->_policies
) : 0,
605 (trust
->_details
) ? (long)CFArrayGetCount(trust
->_details
) : 0);
607 sectrustshow(trust
->_verifyDate
, "verify date:");
608 sectrustshow(trust
->_certificates
, "certificates:");
609 sectrustshow(trust
->_anchors
, "anchors:");
610 sectrustshow(trust
->_policies
, "policies:");
611 sectrustshow(trust
->_details
, "details:");
612 sectrustshow(trust
->_info
, "info:");
617 static void cert_trust_dump(SecTrustRef trust
) {}
621 OSStatus
SecTrustEvaluate(SecTrustRef trust
, SecTrustResultType
*result
) {
625 OSStatus status
= SecTrustEvaluateIfNecessary(trust
);
629 /* post-process trust result based on exceptions */
630 SecTrustResultType trustResult
= trust
->_trustResult
;
631 if (trustResult
== kSecTrustResultUnspecified
) {
632 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
633 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
634 trustResult
= kSecTrustResultProceed
;
635 } else if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
636 /* If we have exceptions get details and match to exceptions. */
637 CFIndex pathLength
= CFArrayGetCount(trust
->_details
);
638 struct SecTrustCheckExceptionContext context
= {};
640 for (ix
= 0; ix
< pathLength
; ++ix
) {
641 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_details
, ix
);
643 if ((ix
== 0) && CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedLeaf
))
645 trustResult
= kSecTrustResultFatalTrustFailure
;
646 goto DoneCheckingTrust
;
649 if (CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedKey
))
651 trustResult
= kSecTrustResultFatalTrustFailure
;
652 goto DoneCheckingTrust
;
655 context
.exception
= SecTrustGetExceptionForCertificateAtIndex(trust
, ix
);
656 CFDictionaryApplyFunction(detail
, SecTrustCheckException
, &context
);
657 if (context
.exceptionNotFound
) {
661 if (!context
.exceptionNotFound
)
662 trustResult
= kSecTrustResultProceed
;
666 #warning STU: <rdar://21014749>
667 // may be fixed with rdar://21014749
668 if (trustResult
== kSecTrustResultProceed
)
669 trustResult
= kSecTrustResultUnspecified
;
671 trust
->_trustResult
= trustResult
;
673 /* log to syslog when there is a trust failure */
674 if (trustResult
!= kSecTrustResultProceed
&&
675 trustResult
!= kSecTrustResultConfirm
&&
676 trustResult
!= kSecTrustResultUnspecified
) {
677 CFStringRef failureDesc
= SecTrustCopyFailureDescription(trust
);
678 secerror("%@", failureDesc
);
679 CFRelease(failureDesc
);
684 *result
= trustResult
;
690 OSStatus
SecTrustEvaluateAsync(SecTrustRef trust
,
691 dispatch_queue_t queue
, SecTrustCallback result
)
693 dispatch_async(queue
, ^{
694 SecTrustResultType trustResult
;
695 if (errSecSuccess
!= SecTrustEvaluate(trust
, &trustResult
)) {
696 trustResult
= kSecTrustResultInvalid
;
698 result(trust
, trustResult
);
700 return errSecSuccess
;
703 static bool append_certificate_to_xpc_array(SecCertificateRef certificate
, xpc_object_t xpc_certificates
);
704 static xpc_object_t
copy_xpc_certificates_array(CFArrayRef certificates
);
705 xpc_object_t
copy_xpc_policies_array(CFArrayRef policies
);
706 OSStatus
validate_array_of_items(CFArrayRef array
, CFStringRef arrayItemType
, CFTypeID itemTypeID
, bool required
);
708 static bool append_certificate_to_xpc_array(SecCertificateRef certificate
, xpc_object_t xpc_certificates
) {
712 size_t length
= SecCertificateGetLength(certificate
);
713 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
714 if (!length
|| !bytes
) {
717 xpc_array_set_data(xpc_certificates
, XPC_ARRAY_APPEND
, bytes
, length
);
721 static xpc_object_t
copy_xpc_certificates_array(CFArrayRef certificates
) {
722 xpc_object_t xpc_certificates
= xpc_array_create(NULL
, 0);
723 if (!xpc_certificates
) {
726 CFIndex ix
, count
= CFArrayGetCount(certificates
);
727 for (ix
= 0; ix
< count
; ++ix
) {
728 SecCertificateRef certificate
= (SecCertificateRef
) CFArrayGetValueAtIndex(certificates
, ix
);
729 #if SECTRUST_VERBOSE_DEBUG
730 size_t length
= SecCertificateGetLength(certificate
);
731 const uint8_t *bytes
= SecCertificateGetBytePtr(certificate
);
732 secerror("idx=%d of %d; cert=0x%lX length=%ld bytes=0x%lX", (int)ix
, (int)count
, (uintptr_t)certificate
, (size_t)length
, (uintptr_t)bytes
);
734 if (!append_certificate_to_xpc_array(certificate
, xpc_certificates
)) {
735 xpc_release(xpc_certificates
);
736 xpc_certificates
= NULL
;
740 return xpc_certificates
;
743 static bool SecXPCDictionarySetCertificates(xpc_object_t message
, const char *key
, CFArrayRef certificates
, CFErrorRef
*error
) {
744 xpc_object_t xpc_certificates
= copy_xpc_certificates_array(certificates
);
745 if (!xpc_certificates
) {
746 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array of certificates"));
749 xpc_dictionary_set_value(message
, key
, xpc_certificates
);
750 xpc_release(xpc_certificates
);
754 static bool SecXPCDictionarySetPolicies(xpc_object_t message
, const char *key
, CFArrayRef policies
, CFErrorRef
*error
) {
755 xpc_object_t xpc_policies
= copy_xpc_policies_array(policies
);
757 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array of policies"));
760 xpc_dictionary_set_value(message
, key
, xpc_policies
);
761 xpc_release(xpc_policies
);
766 static bool CFDataAppendToXPCArray(CFDataRef data
, xpc_object_t xpc_data_array
, CFErrorRef
*error
) {
770 size_t length
= CFDataGetLength(data
);
771 const uint8_t *bytes
= CFDataGetBytePtr(data
);
772 if (!length
|| !bytes
)
773 return SecError(errSecParam
, error
, CFSTR("invalid CFDataRef"));
775 xpc_array_set_data(xpc_data_array
, XPC_ARRAY_APPEND
, bytes
, length
);
780 static xpc_object_t
CFDataArrayCopyXPCArray(CFArrayRef data_array
, CFErrorRef
*error
) {
781 xpc_object_t xpc_data_array
;
782 require_action_quiet(xpc_data_array
= xpc_array_create(NULL
, 0), exit
,
783 SecError(errSecAllocate
, error
, CFSTR("failed to create xpc_array")));
784 CFIndex ix
, count
= CFArrayGetCount(data_array
);
785 for (ix
= 0; ix
< count
; ++ix
) {
786 if (!CFDataAppendToXPCArray((CFDataRef
)CFArrayGetValueAtIndex(data_array
, ix
), xpc_data_array
, error
)) {
787 xpc_release(xpc_data_array
);
793 return xpc_data_array
;
796 static bool SecXPCDictionarySetDataArray(xpc_object_t message
, const char *key
, CFArrayRef data_array
, CFErrorRef
*error
) {
797 xpc_object_t xpc_data_array
= CFDataArrayCopyXPCArray(data_array
, error
);
800 xpc_dictionary_set_value(message
, key
, xpc_data_array
);
801 xpc_release(xpc_data_array
);
805 static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message
, const char *key
, SecCertificatePathRef
*path
, CFErrorRef
*error
) {
806 xpc_object_t xpc_path
= xpc_dictionary_get_value(message
, key
);
811 *path
= SecCertificatePathCreateWithXPCArray(xpc_path
, error
);
815 static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
816 int64_t value
= xpc_dictionary_get_int64(message
, key
);
818 SecError(errSecInternal
, error
, CFSTR("object for key %s is 0"), key
);
823 static SecTrustResultType
certs_anchors_bool_policies_responses_scts_logs_date_ag_to_details_info_chain_int_error_request(enum SecXPCOperation op
, CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, CFArrayRef policies
, CFArrayRef responses
, CFArrayRef SCTs
, CFArrayRef trustedLogs
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, CFArrayRef
*details
, CFDictionaryRef
*info
, SecCertificatePathRef
*chain
, CFErrorRef
*error
)
825 __block SecTrustResultType tr
= kSecTrustResultInvalid
;
826 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
827 if (!SecXPCDictionarySetCertificates(message
, kSecTrustCertificatesKey
, certificates
, error
))
829 if (anchors
&& !SecXPCDictionarySetCertificates(message
, kSecTrustAnchorsKey
, anchors
, error
))
832 xpc_dictionary_set_bool(message
, kSecTrustAnchorsOnlyKey
, anchorsOnly
);
833 if (!SecXPCDictionarySetPolicies(message
, kSecTrustPoliciesKey
, policies
, error
))
835 if (responses
&& !SecXPCDictionarySetDataArray(message
, kSecTrustResponsesKey
, responses
, error
))
837 if (SCTs
&& !SecXPCDictionarySetDataArray(message
, kSecTrustSCTsKey
, SCTs
, error
))
839 if (trustedLogs
&& !SecXPCDictionarySetPList(message
, kSecTrustTrustedLogsKey
, trustedLogs
, error
))
841 xpc_dictionary_set_double(message
, kSecTrustVerifyDateKey
, verifyTime
);
843 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
844 secdebug("trust", "response: %@", response
);
845 return SecXPCDictionaryCopyArrayOptional(response
, kSecTrustDetailsKey
, details
, error
) &&
846 SecXPCDictionaryCopyDictionaryOptional(response
, kSecTrustInfoKey
, info
, error
) &&
847 SecXPCDictionaryCopyChainOptional(response
, kSecTrustChainKey
, chain
, error
) &&
848 ((tr
= SecXPCDictionaryGetNonZeroInteger(response
, kSecTrustResultKey
, error
)) != kSecTrustResultInvalid
);
853 OSStatus
validate_array_of_items(CFArrayRef array
, CFStringRef arrayItemType
, CFTypeID itemTypeID
, bool required
) {
854 OSStatus result
= errSecSuccess
;
855 CFIndex index
, count
;
856 count
= (array
) ? CFArrayGetCount(array
) : 0;
857 if (!count
&& required
) {
858 secerror("no %@ in array!", arrayItemType
);
859 result
= errSecParam
;
861 for (index
= 0; index
< count
; index
++) {
862 CFTypeRef item
= (CFTypeRef
) CFArrayGetValueAtIndex(array
, index
);
864 secerror("%@ %@ (index %d)", arrayItemType
, CFSTR("reference is nil"), (int)index
);
865 result
= errSecParam
;
868 if (CFGetTypeID(item
) != itemTypeID
) {
869 secerror("%@ %@ (index %d)", arrayItemType
, CFSTR("is not the expected CF type"), (int)index
);
870 result
= errSecParam
;
873 if (CFGetTypeID(item
) == SecCertificateGetTypeID()) {
874 SecCertificateRef certificate
= (SecCertificateRef
) item
;
875 CFIndex length
= SecCertificateGetLength(certificate
);
876 const UInt8
*bytes
= SecCertificateGetBytePtr(certificate
);
878 secerror("%@ %@ (index %d)", arrayItemType
, CFSTR("has zero length"), (int)index
);
879 result
= errSecParam
;
882 secerror("%@ %@ (index %d)", arrayItemType
, CFSTR("has nil bytes"), (int)index
);
883 result
= errSecParam
;
885 #if SECTRUST_VERBOSE_DEBUG
886 secerror("%@[%d] of %d = %ld bytes @ 0x%lX", arrayItemType
, (int)index
, (int)count
, (size_t)length
, (uintptr_t)bytes
);
890 if (CFGetTypeID(item
) == SecPolicyGetTypeID()) {
891 SecPolicyRef policy
= (SecPolicyRef
) item
;
892 CFStringRef oidStr
= policy
->_oid
;
893 if (!oidStr
|| (CFGetTypeID(oidStr
) != CFStringGetTypeID())) {
894 oidStr
= CFSTR("has invalid OID string!");
895 secerror("%@ %@ (index %d)", arrayItemType
, oidStr
, (int)index
);
897 #if SECTRUST_VERBOSE_DEBUG
898 secerror("%@[%d] of %d = \"%@\" 0x%lX", arrayItemType
, (int)index
, (int)count
, oidStr
, (uintptr_t)policy
);
905 static OSStatus
SecTrustValidateInput(SecTrustRef trust
) {
906 OSStatus status
, result
= errSecSuccess
;
908 // certificates (required)
909 status
= validate_array_of_items(trust
->_certificates
, CFSTR("certificate"), SecCertificateGetTypeID(), true);
910 if (status
) result
= status
;
911 // anchors (optional)
912 status
= validate_array_of_items(trust
->_anchors
, CFSTR("input anchor"), SecCertificateGetTypeID(), false);
913 if (status
) result
= status
;
914 // policies (required??)
915 status
= validate_array_of_items(trust
->_policies
, CFSTR("policy"), SecPolicyGetTypeID(), true);
916 if (status
) result
= status
;
917 // _responses, _SCTs, _trustedLogs, ...
918 // verify time: SecTrustGetVerifyTime(trust)
919 // access groups: SecAccessGroupsGetCurrent()
925 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
) {
926 __block OSStatus result
;
931 if (trust
->_trustResult
!= kSecTrustResultInvalid
)
932 return errSecSuccess
;
934 trust
->_trustResult
= kSecTrustResultOtherError
; /* to avoid potential recursion */
936 CFReleaseNull(trust
->_chain
);
937 CFReleaseNull(trust
->_details
);
938 CFReleaseNull(trust
->_info
);
940 os_activity_initiate("SecTrustEvaluateIfNecessary", OS_ACTIVITY_FLAG_DEFAULT
, ^{
941 SecTrustAddPolicyAnchors(trust
);
942 SecTrustValidateInput(trust
);
944 /* @@@ Consider an optimization where we keep a side dictionary with the SHA1 hash of ever SecCertificateRef we send, so we only send potential duplicates once, and have the server respond with either just the SHA1 hash of a certificate, or the complete certificate in the response depending on whether the client already sent it, so we don't send back certificates to the client it already has. */
945 result
= SecOSStatusWith(^bool (CFErrorRef
*error
) {
946 trust
->_trustResult
= SECURITYD_XPC(sec_trust_evaluate
,
947 certs_anchors_bool_policies_responses_scts_logs_date_ag_to_details_info_chain_int_error_request
,
948 trust
->_certificates
, trust
->_anchors
, trust
->_anchorsOnly
,
949 trust
->_policies
, trust
->_responses
, trust
->_SCTs
, trust
->_trustedLogs
,
950 SecTrustGetVerifyTime(trust
), SecAccessGroupsGetCurrent(),
951 &trust
->_details
, &trust
->_info
, &trust
->_chain
, error
);
952 if (trust
->_trustResult
== kSecTrustResultInvalid
/* TODO check domain */ &&
953 SecErrorGetOSStatus(*error
) == errSecNotAvailable
&&
954 CFArrayGetCount(trust
->_certificates
)) {
955 /* We failed to talk to securityd. The only time this should
956 happen is when we are running prior to launchd enabling
957 registration of services. This currently happens when we
958 are running from the ramdisk. To make ASR happy we initialize
959 _chain and return success with a failure as the trustResult, to
960 make it seem like we did a cert evaluation, so ASR can extract
961 the public key from the leaf. */
962 trust
->_chain
= SecCertificatePathCreate(NULL
, (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0));
964 CFReleaseNull(*error
);
967 return trust
->_trustResult
!= kSecTrustResultInvalid
;
974 /* Helper for the qsort below. */
975 static int compare_strings(const void *a1
, const void *a2
) {
976 CFStringRef s1
= *(CFStringRef
*)a1
;
977 CFStringRef s2
= *(CFStringRef
*)a2
;
978 return (int) CFStringCompare(s1
, s2
, kCFCompareForcedOrdering
);
981 CFStringRef
SecTrustCopyFailureDescription(SecTrustRef trust
) {
982 CFMutableStringRef reason
= CFStringCreateMutable(NULL
, 0);
983 CFArrayRef details
= SecTrustGetDetails(trust
);
984 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
985 for (CFIndex ix
= 0; ix
< pathLength
; ++ix
) {
986 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
987 CFIndex dCount
= CFDictionaryGetCount(detail
);
990 CFStringAppend(reason
, CFSTR(" [leaf"));
991 else if (ix
== pathLength
- 1)
992 CFStringAppend(reason
, CFSTR(" [root"));
994 CFStringAppendFormat(reason
, NULL
, CFSTR(" [ca%" PRIdCFIndex
), ix
);
996 const void *keys
[dCount
];
997 CFDictionaryGetKeysAndValues(detail
, &keys
[0], NULL
);
998 qsort(&keys
[0], dCount
, sizeof(keys
[0]), compare_strings
);
999 for (CFIndex kix
= 0; kix
< dCount
; ++kix
) {
1000 CFStringRef key
= keys
[kix
];
1001 const void *value
= CFDictionaryGetValue(detail
, key
);
1002 CFStringAppendFormat(reason
, NULL
, CFSTR(" %@%@"), key
,
1003 (CFGetTypeID(value
) == CFBooleanGetTypeID()
1004 ? CFSTR("") : value
));
1006 CFStringAppend(reason
, CFSTR("]"));
1013 /* On OS X we need SecTrustCopyPublicKey to give us a CDSA-based SecKeyRef,
1014 so we will refer to this one internally as SecTrustCopyPublicKey_ios,
1015 and call it from SecTrustCopyPublicKey.
1017 SecKeyRef
SecTrustCopyPublicKey_ios(SecTrustRef trust
)
1019 SecKeyRef
SecTrustCopyPublicKey(SecTrustRef trust
)
1025 if (!trust
->_publicKey
) {
1026 if (!trust
->_chain
) {
1027 /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */
1029 trust
->_publicKey
= SecCertificateCopyPublicKey_ios(SecTrustGetCertificateAtIndex(trust
, 0));
1031 trust
->_publicKey
= SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust
, 0));
1034 if (!trust
->_publicKey
) {
1035 /* If this fails, use the passed-in certs in order as if they are a valid cert path,
1036 and attempt to extract the key. */
1037 SecCertificatePathRef path
;
1038 // SecCertificatePathCreateWithArray would have crashed if this code was ever called,
1039 // since it expected an array of CFDataRefs, not an array of certificates.
1040 path
= SecCertificatePathCreateWithArray(trust
->_certificates
);
1041 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
1045 if (!trust
->_publicKey
) {
1046 /* Last resort, we evaluate the trust to get a _chain. */
1047 SecTrustEvaluateIfNecessary(trust
);
1050 if (trust
->_chain
) {
1051 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(trust
->_chain
, 0);
1055 if (trust
->_publicKey
)
1056 CFRetain(trust
->_publicKey
);
1058 return trust
->_publicKey
;
1061 CFIndex
SecTrustGetCertificateCount(SecTrustRef trust
) {
1065 SecTrustEvaluateIfNecessary(trust
);
1066 return (trust
->_chain
) ? SecCertificatePathGetCount(trust
->_chain
) : 1;
1069 SecCertificateRef
SecTrustGetCertificateAtIndex(SecTrustRef trust
,
1075 return (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0);
1077 SecTrustEvaluateIfNecessary(trust
);
1078 return (trust
->_chain
) ? SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
) : NULL
;
1081 CFDictionaryRef
SecTrustCopyInfo(SecTrustRef trust
) {
1085 SecTrustEvaluateIfNecessary(trust
);
1086 CFDictionaryRef info
= trust
->_info
;
1092 CFDataRef
SecTrustCopyExceptions(SecTrustRef trust
) {
1093 CFArrayRef details
= SecTrustGetDetails(trust
);
1094 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
1095 CFMutableArrayRef exceptions
= CFArrayCreateMutable(kCFAllocatorDefault
, pathLength
, &kCFTypeArrayCallBacks
);
1097 for (ix
= 0; ix
< pathLength
; ++ix
) {
1098 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
1099 CFIndex detailCount
= CFDictionaryGetCount(detail
);
1100 CFMutableDictionaryRef exception
;
1101 if (ix
== 0 || detailCount
> 0) {
1102 exception
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, detailCount
+ 1, detail
);
1103 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
1104 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
1105 CFDictionaryAddValue(exception
, kSecCertificateDetailSHA1Digest
, digest
);
1107 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
1108 exception
= (CFMutableDictionaryRef
)CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0,
1109 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1111 CFArrayAppendValue(exceptions
, exception
);
1112 CFRelease(exception
);
1115 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
1116 since it will never be empty). */
1117 for (ix
= pathLength
; ix
-- > 1;) {
1118 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(exceptions
, ix
);
1119 if (CFDictionaryGetCount(exception
) == 0) {
1120 CFArrayRemoveValueAtIndex(exceptions
, ix
);
1126 CFDataRef encodedExceptions
= CFPropertyListCreateData(kCFAllocatorDefault
,
1127 exceptions
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
1128 CFRelease(exceptions
);
1130 return encodedExceptions
;
1133 bool SecTrustSetExceptions(SecTrustRef trust
, CFDataRef encodedExceptions
) {
1137 CFArrayRef exceptions
= NULL
;
1139 if (NULL
!= encodedExceptions
) {
1140 exceptions
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
1141 encodedExceptions
, kCFPropertyListImmutable
, NULL
, NULL
);
1144 if (exceptions
&& CFGetTypeID(exceptions
) != CFArrayGetTypeID()) {
1145 CFRelease(exceptions
);
1148 CFReleaseSafe(trust
->_exceptions
);
1149 trust
->_exceptions
= exceptions
;
1151 /* If there is a valid exception entry for our current leaf we're golden. */
1152 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
1155 /* The passed in exceptions didn't match our current leaf, so we discard it. */
1156 CFReleaseNull(trust
->_exceptions
);
1160 CFArrayRef
SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
1161 CFMutableArrayRef summary
;
1162 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
1163 summary
= SecCertificateCopySummaryProperties(certificate
,
1164 SecTrustGetVerifyTime(trust
));
1165 /* FIXME Add more details in the failure case. */
1170 CFArrayRef
SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
1172 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
1173 summary
= SecCertificateCopyProperties(certificate
);
1183 Can be on any non root cert in the chain.
1185 Short circuit: Yes (No other errors matter after this one)
1186 Non recoverable error
1187 Trust UI: Invalid certificate chain linkage
1188 Cert UI: Invalid linkage to parent certificate
1190 CFStringRef kSecPolicyCheckIdLinkage
= CFSTR("IdLinkage");
1192 /* X.509 required checks.
1193 Can be on any cert in the chain
1195 Short circuit: Yes (No other errors matter after this one)
1196 Non recoverable error
1197 Trust UI: (One or more) unsupported critical extensions found.
1199 /* If we have no names for the extention oids use:
1200 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
1201 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
1203 CFStringRef kSecPolicyCheckCriticalExtensions
= CFSTR("CriticalExtensions");
1204 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
1205 CFStringRef kSecPolicyCheckQualifiedCertStatements
= CFSTR("QualifiedCertStatements");
1206 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
1209 Only apply to the anchor.
1211 Short circuit: No (Under discussion)
1213 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
1214 Cert UI: Not a valid anchor
1216 CFStringRef kSecPolicyCheckAnchorTrusted
= CFSTR("AnchorTrusted");
1217 CFStringRef kSecPolicyCheckAnchorSHA1
= CFSTR("AnchorSHA1");
1219 CFStringRef kSecPolicyCheckAnchorApple
= CFSTR("AnchorApple");
1220 CFStringRef kSecPolicyAppleAnchorIncludeTestRoots
= CFSTR("AnchorAppleTestRoots");
1223 Only applies to leaf
1227 Trust UI: (Hostname|email address) mismatch
1229 CFStringRef kSecPolicyCheckSSLHostname
= CFSTR("SSLHostname");
1231 /* Policy specific checks.
1232 Can be on any cert in the chain
1236 Trust UI: Certificate chain is not valid for the current policy.
1237 OR: (One or more) certificates in the chain are not valid for the current policy/application
1239 CFStringRef kSecPolicyCheckNonEmptySubject
= CFSTR("NonEmptySubject");
1240 /* Cert UI: Non CA certificate used as CA.
1241 Cert UI: CA certificate used as leaf.
1242 Cert UI: Cert chain length exceeded.
1243 Cert UI: Basic constraints extension not critical (non fatal).
1244 Cert UI: Leaf certificate has basic constraints extension (non fatal).
1246 CFStringRef kSecPolicyCheckBasicContraints
= CFSTR("BasicContraints");
1247 CFStringRef kSecPolicyCheckKeyUsage
= CFSTR("KeyUsage");
1248 CFStringRef kSecPolicyCheckExtendedKeyUsage
= CFSTR("ExtendedKeyUsage");
1249 /* Checks that the issuer of the leaf has exactly one Common Name and that it
1250 matches the specified string. */
1251 CFStringRef kSecPolicyCheckIssuerCommonName
= CFSTR("IssuerCommonName");
1252 /* Checks that the leaf has exactly one Common Name and that it has the
1253 specified string as a prefix. */
1254 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix
= CFSTR("SubjectCommonNamePrefix");
1255 /* Check that the certificate chain length matches the specificed CFNumberRef
1257 CFStringRef kSecPolicyCheckChainLength
= CFSTR("ChainLength");
1258 CFStringRef kSecPolicyCheckNotValidBefore
= CFSTR("NotValidBefore");
1261 Can be on any cert in the chain
1265 Trust UI: One or more certificates have expired or are not valid yet.
1266 OS: The (root|intermediate|leaf) certificate (expired on 'date'|is not valid until 'date')
1267 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
1269 CFStringRef kSecPolicyCheckValidIntermediates
= CFSTR("ValidIntermediates");
1270 CFStringRef kSecPolicyCheckValidLeaf
= CFSTR("ValidLeaf");
1271 CFStringRef kSecPolicyCheckValidRoot
= CFSTR("ValidRoot");
1275 struct TrustFailures
{
1277 bool unknownCritExtn
;
1278 bool untrustedAnchor
;
1279 bool hostnameMismatch
;
1284 static void applyDetailProperty(const void *_key
, const void *_value
,
1286 CFStringRef key
= (CFStringRef
)_key
;
1287 struct TrustFailures
*tf
= (struct TrustFailures
*)context
;
1288 if (CFGetTypeID(_value
) != CFBooleanGetTypeID()) {
1289 /* Value isn't a CFBooleanRef, oh no! */
1292 CFBooleanRef value
= (CFBooleanRef
)_value
;
1293 if (CFBooleanGetValue(value
)) {
1294 /* Not an actual failure so we don't report it. */
1298 /* @@@ FIXME: Report a different return value when something is in the
1299 details but masked out by an exception and use that below for display
1301 if (CFEqual(key
, kSecPolicyCheckIdLinkage
)) {
1302 tf
->badLinkage
= true;
1303 } else if (CFEqual(key
, kSecPolicyCheckCriticalExtensions
)
1304 || CFEqual(key
, kSecPolicyCheckQualifiedCertStatements
)) {
1305 tf
->unknownCritExtn
= true;
1306 } else if (CFEqual(key
, kSecPolicyCheckAnchorTrusted
)
1307 || CFEqual(key
, kSecPolicyCheckAnchorSHA1
)
1308 || CFEqual(key
, kSecPolicyCheckAnchorApple
)) {
1309 tf
->untrustedAnchor
= true;
1310 } else if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
1311 tf
->hostnameMismatch
= true;
1312 } else if (CFEqual(key
, kSecPolicyCheckValidIntermediates
)
1313 || CFEqual(key
, kSecPolicyCheckValidLeaf
)
1314 || CFEqual(key
, kSecPolicyCheckValidRoot
)) {
1315 tf
->invalidCert
= true;
1317 /* Anything else is a policy failure. */
1319 if (CFEqual(key
, kSecPolicyCheckNonEmptySubject
)
1320 || CFEqual(key
, kSecPolicyCheckBasicContraints
)
1321 || CFEqual(key
, kSecPolicyCheckKeyUsage
)
1322 || CFEqual(key
, kSecPolicyCheckExtendedKeyUsage
)
1323 || CFEqual(key
, kSecPolicyCheckIssuerCommonName
)
1324 || CFEqual(key
, kSecPolicyCheckSubjectCommonNamePrefix
)
1325 || CFEqual(key
, kSecPolicyCheckChainLength
)
1326 || CFEqual(key
, kSecPolicyCheckNotValidBefore
))
1329 tf
->policyFail
= true;
1333 static void appendError(CFMutableArrayRef properties
, CFStringRef error
) {
1334 CFStringRef localizedError
= SecFrameworkCopyLocalizedString(error
,
1335 CFSTR("SecCertificate"));
1336 if (!localizedError
) {
1337 //secerror("WARNING: localized error string was not found in Security.framework");
1338 localizedError
= CFRetain(error
);
1340 appendProperty(properties
, kSecPropertyTypeError
, NULL
, NULL
,
1342 CFReleaseNull(localizedError
);
1345 CFArrayRef
SecTrustCopyProperties(SecTrustRef trust
) {
1346 CFArrayRef details
= SecTrustGetDetails(trust
);
1350 struct TrustFailures tf
= {};
1352 CFIndex ix
, count
= CFArrayGetCount(details
);
1353 for (ix
= 0; ix
< count
; ++ix
) {
1354 CFDictionaryRef detail
= (CFDictionaryRef
)
1355 CFArrayGetValueAtIndex(details
, ix
);
1356 /* We now have a detail dictionary for certificate at index ix, with
1357 a key value pair for each failed policy check. Let's convert it
1358 from Ro-Man form into something a Hu-Man can understand. */
1359 CFDictionaryApplyFunction(detail
, applyDetailProperty
, &tf
);
1362 CFMutableArrayRef properties
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1363 &kCFTypeArrayCallBacks
);
1364 /* The badLinkage and unknownCritExtn failures are short circuited, since
1365 you can't recover from those errors. */
1366 if (tf
.badLinkage
) {
1367 appendError(properties
, CFSTR("Invalid certificate chain linkage."));
1368 } else if (tf
.unknownCritExtn
) {
1369 appendError(properties
, CFSTR("One or more unsupported critical extensions found."));
1371 if (tf
.untrustedAnchor
) {
1372 appendError(properties
, CFSTR("Root certificate is not trusted."));
1374 if (tf
.hostnameMismatch
) {
1375 appendError(properties
, CFSTR("Hostname mismatch."));
1377 if (tf
.policyFail
) {
1378 appendError(properties
, CFSTR("Policy requirements not met."));
1380 if (tf
.invalidCert
) {
1381 appendError(properties
, CFSTR("One or more certificates have expired or are not valid yet."));
1385 if (CFArrayGetCount(properties
) == 0) {
1386 /* The certificate chain is trusted, return an empty plist */
1387 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
1388 // return empty (non-null) plist
1390 // return NULL plist
1391 CFReleaseNull(properties
);
1398 CFDictionaryRef
SecTrustCopyResult(SecTrustRef trust
) {
1399 // Builds and returns a dictionary of evaluation results.
1403 CFMutableDictionaryRef results
= CFDictionaryCreateMutable(NULL
, 0,
1404 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1406 // kSecTrustResultDetails (per-cert results)
1407 CFArrayRef details
= SecTrustGetDetails(trust
);
1409 CFDictionarySetValue(results
, (const void *)kSecTrustResultDetails
, (const void *)details
);
1412 // kSecTrustResultValue (overall trust result)
1413 CFNumberRef numValue
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trust
->_trustResult
);
1415 CFDictionarySetValue(results
, (const void *)kSecTrustResultValue
, (const void *)numValue
);
1416 CFRelease(numValue
);
1418 CFDictionaryRef info
= trust
->_info
;
1419 if (trust
->_trustResult
== kSecTrustResultInvalid
|| !info
) {
1420 return results
; // we have nothing more to add
1423 // kSecTrustEvaluationDate
1424 CFDateRef evaluationDate
= trust
->_verifyDate
;
1425 if (evaluationDate
) {
1426 CFDictionarySetValue(results
, (const void *)kSecTrustEvaluationDate
, (const void *)evaluationDate
);
1429 // kSecTrustCertificateTransparency
1430 CFBooleanRef ctValue
;
1431 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoCertificateTransparencyKey
, (const void **)&ctValue
)) {
1432 CFDictionarySetValue(results
, (const void *)kSecTrustCertificateTransparency
, (const void *)ctValue
);
1435 // kSecTrustExtendedValidation
1436 CFBooleanRef evValue
;
1437 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoExtendedValidationKey
, (const void **)&evValue
)) {
1438 CFDictionarySetValue(results
, (const void *)kSecTrustExtendedValidation
, (const void *)evValue
);
1441 // kSecTrustOrganizationName
1442 CFStringRef organizationName
;
1443 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoCompanyNameKey
, (const void **)&organizationName
)) {
1444 CFDictionarySetValue(results
, (const void *)kSecTrustOrganizationName
, (const void *)organizationName
);
1447 // kSecTrustRevocationChecked
1448 CFBooleanRef revocationChecked
;
1449 if (CFDictionaryGetValueIfPresent(info
, kSecTrustRevocationChecked
, (const void **)&revocationChecked
)) {
1450 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationChecked
, (const void *)revocationChecked
);
1453 // kSecTrustRevocationReason
1454 CFNumberRef revocationReason
;
1455 if (CFDictionaryGetValueIfPresent(info
, kSecTrustRevocationReason
, (const void **)&revocationReason
)) {
1456 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationReason
, (const void *)revocationReason
);
1459 // kSecTrustRevocationValidUntilDate
1460 CFDateRef validUntilDate
;
1461 if (CFDictionaryGetValueIfPresent(info
, kSecTrustRevocationValidUntilDate
, (const void **)&validUntilDate
)) {
1462 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationValidUntilDate
, (const void *)validUntilDate
);
1468 // Return 0 upon error.
1469 static int to_int_error_request(enum SecXPCOperation op
, CFErrorRef
*error
) {
1470 __block
int64_t result
= 0;
1471 securityd_send_sync_and_do(op
, error
, NULL
, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1472 result
= xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1474 return SecError(errSecInternal
, error
, CFSTR("int64 missing in response"));
1480 // version 0 -> error, so we need to start at version 1 or later.
1481 OSStatus
SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber
)
1484 os_activity_t trace_activity
= os_activity_start("SecTrustGetOTAPKIAssetVersionNumber", OS_ACTIVITY_FLAG_DEFAULT
);
1485 result
= SecOSStatusWith(^bool(CFErrorRef
*error
) {
1487 return SecError(errSecParam
, error
, CFSTR("versionNumber is NULL"));
1489 return (*versionNumber
= SECURITYD_XPC(sec_ota_pki_asset_version
, to_int_error_request
, error
)) != 0;
1492 os_activity_end(trace_activity
);
1496 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1498 static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary
, const char *key
, xpc_type_t type
)
1500 xpc_object_t value
= xpc_dictionary_get_value(dictionary
, key
);
1502 return value
&& (xpc_get_type(value
) == type
);
1505 OSStatus
SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset
)
1507 CFErrorRef error
= NULL
;
1508 do_if_registered(sec_ota_pki_get_new_asset
, &error
);
1511 xpc_object_t message
= securityd_create_message(kSecXPCOpOTAPKIGetNewAsset
, &error
);
1514 xpc_object_t response
= securityd_message_with_reply_sync(message
, &error
);
1516 if (response
&& xpc_dictionary_entry_is_type(response
, kSecXPCKeyResult
, XPC_TYPE_INT64
))
1518 num
= (int64_t) xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1519 xpc_release(response
);
1522 xpc_release(message
);
1525 if (NULL
!= didUpdateAsset
)
1527 *didUpdateAsset
= (int)num
;
1535 // MARK: SecTrustNode
1536 /********************************************************
1537 **************** SecTrustNode object *******************
1538 ********************************************************/
1539 typedef uint8_t SecFetchingState
;
1541 kSecFetchingStatePassedIn
= 0,
1542 kSecFetchingStateLocal
,
1543 kSecFetchingStateFromURL
,
1544 kSecFetchingStateDone
,
1547 typedef uint8_t SecTrustState
;
1549 kSecTrustStateUnknown
= 0,
1550 kSecTrustStateNotSigner
,
1551 kSecTrustStateValidSigner
,
1554 typedef struct __SecTrustNode
*SecTrustNodeRef
;
1555 struct __SecTrustNode
{
1556 SecTrustNodeRef _child
;
1557 SecCertificateRef _certificate
;
1559 /* Cached information about _certificate */
1563 /* Set of all certificates we have ever considered as a parent. We use
1564 this to avoid having to recheck certs when we go to the next phase. */
1565 CFMutableSet _certificates
;
1567 /* Parents that are still partial chains we haven't yet considered. */
1568 CFMutableSet _partials
;
1569 /* Parents that are still partial chains we have rejected. We reconsider
1570 these if we get to the final phase and we still haven't found a valid
1572 CFMutableSet _rejected_partials
;
1574 /* Parents that are complete chains we haven't yet considered. */
1575 CFMutableSet _candidates
;
1576 /* Parents that are complete chains we have rejected. */
1577 CFMutableSet _rejected_candidates
;
1579 /* State of candidate fetching. */
1580 SecFetchingState _fetchingState
;
1582 /* Trust state of _candidates[_candidateIndex] */
1583 SecTrustState _trustState
;
1585 typedef struct __SecTrustNode SecTrustNode
;
1587 /* Forward declarations of static functions. */
1588 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
);
1589 static void SecTrustNodeDestroy(CFTypeRef cf
);
1591 /* Static functions. */
1592 static CFStringRef
SecTrustNodeCopyDescription(CFTypeRef cf
) {
1593 SecTrustNodeRef node
= (SecTrustNodeRef
)cf
;
1594 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
1595 CFSTR("<SecTrustNodeRef: %p>"), node
);
1598 static void SecTrustNodeDestroy(CFTypeRef cf
) {
1599 SecTrustNodeRef trust
= (SecTrustNodeRef
)cf
;
1600 if (trust
->_child
) {
1601 free(trust
->_child
);
1603 if (trust
->_certificate
) {
1604 free(trust
->_certificate
);
1606 if (trust
->_candidates
) {
1607 CFRelease(trust
->_candidates
);
1611 /* SecTrustNode API functions. */
1612 CFGiblisFor(SecTrustNode
)
1614 SecTrustNodeRef
SecTrustNodeCreate(SecTrustRef trust
,
1615 SecCertificateRef certificate
, SecTrustNodeRef child
) {
1616 CFAllocatorRef allocator
= kCFAllocatorDefault
;
1620 CFIndex size
= sizeof(struct __SecTrustNode
);
1621 SecTrustNodeRef result
= (SecTrustNodeRef
)_CFRuntimeCreateInstance(
1622 allocator
, SecTrustNodeGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1626 memset((char*)result
+ sizeof(result
->_base
), 0,
1627 sizeof(*result
) - sizeof(result
->_base
));
1630 result
->_child
= child
;
1632 CFRetain(certificate
);
1633 result
->_certificate
= certificate
;
1634 result
->_isAnchor
= SecTrustCertificateIsAnchor(certificate
);
1639 SecCertificateRef
SecTrustGetCertificate(SecTrustNodeRef node
) {
1641 return node
->_certificate
;
1644 CFArrayRef
SecTrustNodeCopyProperties(SecTrustNodeRef node
,
1645 SecTrustRef trust
) {
1648 CFMutableArrayRef summary
= SecCertificateCopySummaryProperties(
1649 node
->_certificate
, SecTrustGetVerifyTime(trust
));
1650 /* FIXME Add more details in the failure case. */
1654 /* Attempt to verify this node's signature chain down to the child. */
1655 SecTrustState
SecTrustNodeVerifySignatureChain(SecTrustNodeRef node
) {
1657 return kSecTrustStateUnknown
;
1661 /* See if the next candidate works. */
1662 SecTrustNodeRef
SecTrustNodeCopyNextCandidate(SecTrustNodeRef node
,
1663 SecTrustRef trust
, SecFetchingState fetchingState
) {
1667 CFAbsoluteTime verifyTime
= SecTrustGetVerifyTime(trust
);
1670 /* If we have any unconsidered candidates left check those first. */
1671 while (node
->_candidateIndex
< CFArrayGetCount(node
->_candidates
)) {
1672 SecCertificateRef candidate
= (SecCertificateRef
)
1673 CFArrayGetValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1674 if (node
->_fetchingState
!= kSecFetchingStateDone
) {
1675 /* If we still have potential sources to fetch other candidates
1676 from we ignore expired candidates. */
1677 if (!SecCertificateIsValidOn(candidate
, verifyTime
)) {
1678 node
->_candidateIndex
++;
1683 SecTrustNodeRef parent
= SecTrustNodeCreate(candidate
, node
);
1684 CFArrayRemoveValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1685 if (SecTrustNodeVerifySignatureChain(parent
) ==
1686 kSecTrustStateNotSigner
) {
1687 /* This candidate parent is not a valid signer of its
1690 /* If another signature failed further down the chain we need
1691 to backtrack down to whatever child is still a valid
1692 candidate and has additional candidates to consider.
1693 @@@ We really want to make the fetchingState a global of
1694 SecTrust itself as well and not have any node go beyond the
1695 current state of SecTrust if there are other (read cheap)
1696 options to consider. */
1702 /* We've run out of candidates in our current state so let's try to
1703 find some more. Note we fetch candidates in increasing order of
1704 cost in the hope we won't ever get to the more expensive fetching
1706 SecCertificateRef certificate
= node
->_certificate
;
1707 switch (node
->_fetchingState
) {
1708 case kSecFetchingStatePassedIn
:
1709 /* Get the list of candidates from SecTrust. */
1710 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1712 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1715 SecCertificateGetNormalizedIssuerContent(certificate
);
1716 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1718 node
->_fetchingState
= kSecFetchingStateLocal
;
1720 case kSecFetchingStateLocal
:
1721 /* Lookup candidates in the local database. */
1722 node
->_fetchingState
= kSecFetchingStateFromURL
;
1724 case kSecFetchingStateFromURL
:
1725 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1727 case kSecFetchingStateCheckExpired
:
1728 /* Time to start considering expired candidates as well. */
1729 node
->_candidateIndex
= 0;
1730 node
->_fetchingState
= kSecFetchingStateDone
;
1732 case kSecFetchingStateDone
;
1737 CFAllocatorRef allocator
= CFGetAllocator(node
);
1739 /* A trust node has a number of states.
1740 1) Look for issuing certificates by asking SecTrust about known
1741 parent certificates.
1742 2) Look for issuing certificates in certificate databases (keychains)
1743 3) Look for issuing certificates by going out to the web if the nodes
1744 certificate has a issuer location URL.
1745 4) Look through expired or not yet valid candidates we have put aside.
1747 We go though the stages 1 though 3 looking for candidate issuer
1748 certificates. If a candidate certificate is not valid at verifyTime
1749 we put it in a to be examined later queue. If a candidate certificate
1750 is valid we verify if it actually signed our certificate (if possible).
1751 If not we discard it and continue on to the next candidate certificate.
1752 If it is we return a new SecTrustNodeRef for that certificate. */
1754 CFMutableArrayRef issuers
= CFArrayCreateMutable(allocator
, 0,
1755 &kCFTypeArrayCallBacks
);
1757 /* Find a node's parent. */
1758 certificate
= node
->_certificate
;
1759 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1760 CFTypeRef candidates
= NULL
;
1762 candidates
= (CFTypeRef
)CFDictionaryGetValueForKey(skidDict
, akid
);
1764 addValidIssuersFrom(issuers
, certificate
, candidates
, true);
1769 SecCertificateGetNormalizedIssuerContent(certificate
);
1770 candidates
= (CFTypeRef
)
1771 CFDictionaryGetValueForKey(subjectDict
, issuer
);
1772 addValidIssuersFrom(issuers
, certificate
, candidates
, false);
1775 if (CFArrayGetCount(issuers
) == 0) {
1776 /* O no! we can't find an issuer for this certificate. Let's see
1777 if we can find one in the local database. */
1780 return errSecSuccess
;
1783 CFArrayRef
SecTrustNodeCopyNextChain(SecTrustNodeRef node
,
1784 SecTrustRef trust
) {
1785 /* Return the next full chain that isn't a reject unless we are in
1786 a state where we consider returning rejects. */
1788 switch (node
->_fetchingState
) {
1789 case kSecFetchingStatePassedIn
:
1790 /* Get the list of candidates from SecTrust. */
1791 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1793 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1796 SecCertificateGetNormalizedIssuerContent(certificate
);
1797 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1799 node
->_fetchingState
= kSecFetchingStateLocal
;
1801 case kSecFetchingStateLocal
:
1802 /* Lookup candidates in the local database. */
1803 node
->_fetchingState
= kSecFetchingStateFromURL
;
1805 case kSecFetchingStateFromURL
:
1806 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1808 case kSecFetchingStateCheckExpired
:
1809 /* Time to start considering expired candidates as well. */
1810 node
->_candidateIndex
= 0;
1811 node
->_fetchingState
= kSecFetchingStateDone
;
1813 case kSecFetchingStateDone
;
1819 Iterator
parentIterator(Cert
);
1825 static bool unique(Node node
) {
1826 if (nodes
.contains(node
))
1832 static bool isAnchor(Cert cert
);
1839 Iterator parentIterator
; /* For current source of parents. */
1841 Node(Cert inCert
) : child(nil
), cert(inCert
), nextSource(0) {}
1842 Node(Node inChild
, Cert inCert
) : child(inChild
), cert(inCert
),
1845 CertPath
certPath() {
1849 path
.add(node
.cert
);
1855 void contains(Cert cert
) {
1858 if (cert
== node
.cert
)
1865 Node
nextParent(Array currentSources
) {
1868 parentIterator
== currentSources
[nextSource
- 1].end()) {
1869 if (nextSource
== currentSources
.count
) {
1870 /* We ran out of parent sources. */
1873 parentIterator
= currentSources
[nextSource
++].begin();
1875 Certificate cert
= *parentIterator
++;
1876 /* Check for cycles and self signed chains. */
1877 if (!contains(cert
)) {
1878 Node node
= Node(this, parent
);
1879 if (!NodeCache
.unique(node
))
1890 Array currentSources
;
1896 PathBuilder(Cert cert
) {
1897 nodes
.append(Node(cert
));
1898 nit
= nodes
.begin();
1899 sourceIT
= allSources
.begin();
1902 nextAnchoredPath() {
1903 if (nit
== nodes
.end()) {
1904 /* We should add another source to the list of sources to
1906 if (sourceIT
== allSources
.end()) {
1907 /* No more sources to add. */
1909 currentSources
+= *sourceIT
++;
1910 /* Resort nodes by size. */
1912 nit
= nodes
.begin();
1913 /* Set the source list for all nodes. */
1916 while (Node node
= *nit
) {
1917 Node candidate
= node
.nextParent(currentSources
);
1919 /* The current node has no more candidate parents so move
1925 if (candidate
.isAnchored
) {
1926 candidates
.append(candidate
);
1928 nodes
.insert(candidate
, nit
);
1933 while (Node node
= nextAnchoredPath()) {
1934 if (node
.isValid()) {
1935 chain
= node
.certPath
;
1938 rejects
.append(node
);