2 * Copyright (c) 2006-2014 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/SecItemPriv.h>
29 #include <Security/SecCertificateInternal.h>
30 #include <Security/SecCertificatePath.h>
31 #include <Security/SecFramework.h>
32 #include <Security/SecPolicyCerts.h>
33 #include <Security/SecPolicyInternal.h>
34 #include <Security/SecPolicyPriv.h>
35 #include <Security/SecuritydXPC.h>
36 #include <Security/SecInternal.h>
37 #include <Security/SecBasePriv.h>
38 #include <CoreFoundation/CFRuntime.h>
39 #include <CoreFoundation/CFSet.h>
40 #include <CoreFoundation/CFString.h>
41 #include <CoreFoundation/CFNumber.h>
42 #include <CoreFoundation/CFArray.h>
43 #include <CoreFoundation/CFPropertyList.h>
44 #include <AssertMacros.h>
50 #include <utilities/SecIOFormat.h>
51 #include <utilities/SecCFError.h>
52 #include <utilities/SecCFWrappers.h>
53 #include <utilities/SecCertificateTrace.h>
54 #include <utilities/debugging.h>
56 #include "SecRSAKey.h"
57 #include <libDER/oids.h>
59 #include <ipc/securityd_client.h>
61 #include <securityd/SecTrustServer.h>
63 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
65 SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey
, "ExtendedValidation");
66 SEC_CONST_DECL (kSecTrustInfoCompanyNameKey
, "CompanyName");
67 SEC_CONST_DECL (kSecTrustInfoRevocationKey
, "Revocation");
68 SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey
, "RevocationValidUntil");
70 /* Public trust result constants */
71 SEC_CONST_DECL (kSecTrustEvaluationDate
, "TrustEvaluationDate");
72 SEC_CONST_DECL (kSecTrustExtendedValidation
, "TrustExtendedValidation");
73 SEC_CONST_DECL (kSecTrustOrganizationName
, "Organization");
74 SEC_CONST_DECL (kSecTrustResultValue
, "TrustResultValue");
75 SEC_CONST_DECL (kSecTrustRevocationChecked
, "TrustRevocationChecked");
76 SEC_CONST_DECL (kSecTrustRevocationValidUntilDate
, "TrustExpirationDate");
77 SEC_CONST_DECL (kSecTrustResultDetails
, "TrustResultDetails");
82 /********************************************************
83 ****************** SecTrust object *********************
84 ********************************************************/
87 CFArrayRef _certificates
;
90 CFArrayRef _responses
;
91 CFDateRef _verifyDate
;
92 SecCertificatePathRef _chain
;
95 CFDictionaryRef _info
;
96 CFArrayRef _exceptions
;
98 /* Note that a value of kSecTrustResultInvalid (0)
99 * indicates the trust must be (re)evaluated; any
100 * functions which modify trust parameters in a way
101 * that would invalidate the current result must set
102 * this value back to kSecTrustResultInvalid.
104 SecTrustResultType _trustResult
;
106 /* If true we don't trust any anchors other than the ones in _anchors. */
109 /* Master switch to permit or disable network use in policy evaluation */
110 SecNetworkPolicy _networkPolicy
;
113 /* Forward declarations of static functions. */
114 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
);
116 /* Static functions. */
117 static CFStringRef
SecTrustCopyDescription(CFTypeRef cf
) {
118 SecTrustRef trust
= (SecTrustRef
)cf
;
119 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
120 CFSTR("<SecTrustRef: %p>"), trust
);
123 static void SecTrustDestroy(CFTypeRef cf
) {
124 SecTrustRef trust
= (SecTrustRef
)cf
;
125 CFReleaseSafe(trust
->_certificates
);
126 CFReleaseSafe(trust
->_policies
);
127 CFReleaseSafe(trust
->_responses
);
128 CFReleaseSafe(trust
->_verifyDate
);
129 CFReleaseSafe(trust
->_anchors
);
130 CFReleaseSafe(trust
->_chain
);
131 CFReleaseSafe(trust
->_publicKey
);
132 CFReleaseSafe(trust
->_details
);
133 CFReleaseSafe(trust
->_info
);
134 CFReleaseSafe(trust
->_exceptions
);
137 /* Public API functions. */
138 CFGiblisFor(SecTrust
)
140 OSStatus
SecTrustCreateWithCertificates(CFTypeRef certificates
,
141 CFTypeRef policies
, SecTrustRef
*trust
) {
142 OSStatus status
= errSecParam
;
143 CFAllocatorRef allocator
= kCFAllocatorDefault
;
144 CFArrayRef l_certs
= NULL
, l_policies
= NULL
;
145 SecTrustRef result
= NULL
;
149 CFTypeID certType
= CFGetTypeID(certificates
);
150 if (certType
== CFArrayGetTypeID()) {
151 /* We need at least 1 certificate. */
152 require_quiet(CFArrayGetCount(certificates
) > 0, errOut
);
153 l_certs
= CFArrayCreateCopy(allocator
, certificates
);
154 } else if (certType
== SecCertificateGetTypeID()) {
155 l_certs
= CFArrayCreate(allocator
, &certificates
, 1,
156 &kCFTypeArrayCallBacks
);
161 status
= errSecAllocate
;
166 CFTypeRef policy
= SecPolicyCreateBasicX509();
167 l_policies
= CFArrayCreate(allocator
, &policy
, 1,
168 &kCFTypeArrayCallBacks
);
170 } else if (CFGetTypeID(policies
) == CFArrayGetTypeID()) {
171 l_policies
= CFArrayCreateCopy(allocator
, policies
);
172 } else if (CFGetTypeID(policies
) == SecPolicyGetTypeID()) {
173 l_policies
= CFArrayCreate(allocator
, &policies
, 1,
174 &kCFTypeArrayCallBacks
);
179 status
= errSecAllocate
;
183 CFIndex size
= sizeof(struct __SecTrust
);
184 require_quiet(result
= (SecTrustRef
)_CFRuntimeCreateInstance(allocator
,
185 SecTrustGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
186 memset((char*)result
+ sizeof(result
->_base
), 0,
187 sizeof(*result
) - sizeof(result
->_base
));
188 status
= errSecSuccess
;
192 CFReleaseSafe(result
);
193 CFReleaseSafe(l_certs
);
194 CFReleaseSafe(l_policies
);
196 result
->_certificates
= l_certs
;
197 result
->_policies
= l_policies
;
201 CFReleaseSafe(result
);
206 static void SetTrustSetNeedsEvaluation(SecTrustRef trust
) {
209 trust
->_trustResult
= kSecTrustResultInvalid
;
213 OSStatus
SecTrustSetAnchorCertificatesOnly(SecTrustRef trust
,
214 Boolean anchorCertificatesOnly
) {
218 SetTrustSetNeedsEvaluation(trust
);
219 trust
->_anchorsOnly
= anchorCertificatesOnly
;
221 return errSecSuccess
;
224 OSStatus
SecTrustSetAnchorCertificates(SecTrustRef trust
,
225 CFArrayRef anchorCertificates
) {
229 SetTrustSetNeedsEvaluation(trust
);
230 if (anchorCertificates
)
231 CFRetain(anchorCertificates
);
233 CFRelease(trust
->_anchors
);
234 trust
->_anchors
= anchorCertificates
;
235 trust
->_anchorsOnly
= (anchorCertificates
!= NULL
);
237 return errSecSuccess
;
240 OSStatus
SecTrustCopyCustomAnchorCertificates(SecTrustRef trust
,
241 CFArrayRef
*anchors
) {
242 if (!trust
|| !anchors
) {
245 CFArrayRef anchorsArray
= NULL
;
246 if (trust
->_anchors
) {
247 anchorsArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_anchors
);
249 return errSecAllocate
;
252 *anchors
= anchorsArray
;
253 return errSecSuccess
;
256 OSStatus
SecTrustSetOCSPResponse(SecTrustRef trust
, CFTypeRef responseData
) {
260 SetTrustSetNeedsEvaluation(trust
);
261 CFArrayRef responseArray
= NULL
;
263 if (CFGetTypeID(responseData
) == CFArrayGetTypeID()) {
264 responseArray
= CFArrayCreateCopy(kCFAllocatorDefault
, responseData
);
265 } else if (CFGetTypeID(responseData
) == CFDataGetTypeID()) {
266 responseArray
= CFArrayCreate(kCFAllocatorDefault
, &responseData
, 1,
267 &kCFTypeArrayCallBacks
);
272 if (trust
->_responses
)
273 CFRelease(trust
->_responses
);
274 trust
->_responses
= responseArray
;
276 return errSecSuccess
;
279 OSStatus
SecTrustSetVerifyDate(SecTrustRef trust
, CFDateRef verifyDate
) {
283 SetTrustSetNeedsEvaluation(trust
);
285 CFRetainSafe(verifyDate
);
286 if (trust
->_verifyDate
)
287 CFRelease(trust
->_verifyDate
);
288 trust
->_verifyDate
= verifyDate
;
290 return errSecSuccess
;
293 OSStatus
SecTrustSetPolicies(SecTrustRef trust
, CFTypeRef newPolicies
) {
294 if (!trust
|| !newPolicies
) {
297 SetTrustSetNeedsEvaluation(trust
);
300 CFArrayRef policyArray
= NULL
;
301 if (CFGetTypeID(newPolicies
) == CFArrayGetTypeID()) {
302 policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, newPolicies
);
303 } else if (CFGetTypeID(newPolicies
) == SecPolicyGetTypeID()) {
304 policyArray
= CFArrayCreate(kCFAllocatorDefault
, &newPolicies
, 1,
305 &kCFTypeArrayCallBacks
);
310 if (trust
->_policies
)
311 CFRelease(trust
->_policies
);
312 trust
->_policies
= policyArray
;
314 return errSecSuccess
;
317 OSStatus
SecTrustCopyPolicies(SecTrustRef trust
, CFArrayRef
*policies
) {
318 if (!trust
|| !policies
) {
321 if (!trust
->_policies
) {
322 return errSecInternal
;
324 CFArrayRef policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_policies
);
326 return errSecAllocate
;
328 *policies
= policyArray
;
329 return errSecSuccess
;
332 OSStatus
SecTrustSetNetworkFetchAllowed(SecTrustRef trust
, Boolean allowFetch
) {
336 trust
->_networkPolicy
= (allowFetch
) ? useNetworkEnabled
: useNetworkDisabled
;
337 return errSecSuccess
;
340 OSStatus
SecTrustGetNetworkFetchAllowed(SecTrustRef trust
, Boolean
*allowFetch
) {
341 if (!trust
|| !allowFetch
) {
344 Boolean allowed
= false;
345 SecNetworkPolicy netPolicy
= trust
->_networkPolicy
;
346 if (netPolicy
== useNetworkDefault
) {
347 // network fetch is enabled by default for SSL only
348 CFIndex idx
, count
= (trust
->_policies
) ? CFArrayGetCount(trust
->_policies
) : 0;
349 for (idx
=0; idx
<count
; idx
++) {
350 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(trust
->_policies
, idx
);
352 CFDictionaryRef props
= SecPolicyCopyProperties(policy
);
354 CFTypeRef value
= (CFTypeRef
)CFDictionaryGetValue(props
, kSecPolicyOid
);
356 if (CFEqual(value
, kSecPolicyAppleSSL
)) {
365 // caller has explicitly set the network policy
366 allowed
= (netPolicy
== useNetworkEnabled
);
368 *allowFetch
= allowed
;
369 return errSecSuccess
;
372 CFAbsoluteTime
SecTrustGetVerifyTime(SecTrustRef trust
) {
373 CFAbsoluteTime verifyTime
;
374 if (trust
&& trust
->_verifyDate
) {
375 verifyTime
= CFDateGetAbsoluteTime(trust
->_verifyDate
);
377 verifyTime
= CFAbsoluteTimeGetCurrent();
378 /* Record the verifyDate we ended up using. */
380 trust
->_verifyDate
= CFDateCreate(CFGetAllocator(trust
), verifyTime
);
386 CFArrayRef
SecTrustGetDetails(SecTrustRef trust
) {
390 SecTrustEvaluateIfNecessary(trust
);
391 return trust
->_details
;
394 OSStatus
SecTrustGetTrustResult(SecTrustRef trust
,
395 SecTrustResultType
*result
) {
396 if (!trust
|| !result
) {
399 *result
= trust
->_trustResult
;
400 return errSecSuccess
;
403 static CFStringRef kSecCertificateDetailSHA1Digest
= CFSTR("SHA1Digest");
405 static CFDictionaryRef
SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust
, CFIndex ix
) {
406 if (!trust
->_exceptions
|| ix
>= CFArrayGetCount(trust
->_exceptions
))
408 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_exceptions
, ix
);
409 if (CFGetTypeID(exception
) != CFDictionaryGetTypeID())
412 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
416 /* If the exception contains the current certificates sha1Digest in the
417 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
418 CFDataRef sha1Digest
= SecCertificateGetSHA1Digest(certificate
);
419 CFTypeRef digestValue
= CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
);
420 if (!digestValue
|| !CFEqual(sha1Digest
, digestValue
))
426 struct SecTrustCheckExceptionContext
{
427 CFDictionaryRef exception
;
428 bool exceptionNotFound
;
431 static void SecTrustCheckException(const void *key
, const void *value
, void *context
) {
432 struct SecTrustCheckExceptionContext
*cec
= (struct SecTrustCheckExceptionContext
*)context
;
433 if (cec
->exception
) {
434 CFTypeRef exceptionValue
= CFDictionaryGetValue(cec
->exception
, key
);
435 if (!exceptionValue
|| !CFEqual(value
, exceptionValue
)) {
436 cec
->exceptionNotFound
= true;
439 cec
->exceptionNotFound
= true;
444 static CFArrayRef
SecTrustCreatePolicyAnchorsArray(const UInt8
* certData
, CFIndex certLength
)
446 CFArrayRef array
= NULL
;
447 CFAllocatorRef allocator
= kCFAllocatorDefault
;
448 SecCertificateRef cert
= SecCertificateCreateWithBytes(allocator
, certData
, certLength
);
450 array
= CFArrayCreate(allocator
, (const void **)&cert
, 1, &kCFTypeArrayCallBacks
);
457 static void SecTrustAddPolicyAnchors(SecTrustRef trust
)
459 /* Provide anchor certificates specifically required by certain policies.
460 This is used to evaluate test policies where the anchor is not provided
461 in the root store and may not be able to be supplied by the caller.
463 CFArrayRef policies
= (trust
) ? trust
->_policies
: NULL
;
467 CFIndex ix
, count
= CFArrayGetCount(policies
);
468 for (ix
= 0; ix
< count
; ++ix
) {
469 SecPolicyRef policy
= (SecPolicyRef
) CFArrayGetValueAtIndex(policies
, ix
);
472 if (CFEqual(policy
->_oid
, kSecPolicyAppleTestSMPEncryption
)) {
473 CFReleaseSafe(trust
->_anchors
);
474 trust
->_anchors
= SecTrustCreatePolicyAnchorsArray(_SEC_TestAppleRootCAECC
, sizeof(_SEC_TestAppleRootCAECC
));
475 trust
->_anchorsOnly
= true;
484 OSStatus
SecTrustEvaluate(SecTrustRef trust
, SecTrustResultType
*result
) {
488 OSStatus status
= SecTrustEvaluateIfNecessary(trust
);
489 if (status
|| !result
)
492 /* post-process trust result based on exceptions */
493 SecTrustResultType trustResult
= trust
->_trustResult
;
494 if (trustResult
== kSecTrustResultUnspecified
) {
495 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
496 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
497 trustResult
= kSecTrustResultProceed
;
498 } else if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
499 /* If we have exceptions get details and match to exceptions. */
500 CFIndex pathLength
= CFArrayGetCount(trust
->_details
);
501 struct SecTrustCheckExceptionContext context
= {};
503 for (ix
= 0; ix
< pathLength
; ++ix
) {
504 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_details
, ix
);
506 if ((ix
== 0) && CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedLeaf
))
508 trustResult
= kSecTrustResultFatalTrustFailure
;
509 goto DoneCheckingTrust
;
512 if (CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedKey
))
514 trustResult
= kSecTrustResultFatalTrustFailure
;
515 goto DoneCheckingTrust
;
518 context
.exception
= SecTrustGetExceptionForCertificateAtIndex(trust
, ix
);
519 CFDictionaryApplyFunction(detail
, SecTrustCheckException
, &context
);
520 if (context
.exceptionNotFound
) {
524 if (!context
.exceptionNotFound
)
525 trustResult
= kSecTrustResultProceed
;
528 trust
->_trustResult
= trustResult
;
530 /* log to syslog when there is a trust failure */
531 if (trustResult
!= kSecTrustResultProceed
&&
532 trustResult
!= kSecTrustResultConfirm
&&
533 trustResult
!= kSecTrustResultUnspecified
) {
534 CFStringRef failureDesc
= SecTrustCopyFailureDescription(trust
);
535 secerror("%@", failureDesc
);
536 CFRelease(failureDesc
);
540 *result
= trustResult
;
545 OSStatus
SecTrustEvaluateAsync(SecTrustRef trust
,
546 dispatch_queue_t queue
, SecTrustCallback result
)
548 dispatch_async(queue
, ^{
549 SecTrustResultType trustResult
;
550 if (errSecSuccess
!= SecTrustEvaluate(trust
, &trustResult
)) {
551 trustResult
= kSecTrustResultInvalid
;
553 result(trust
, trustResult
);
555 return errSecSuccess
;
558 static bool SecXPCDictionarySetCertificates(xpc_object_t message
, const char *key
, CFArrayRef certificates
, CFErrorRef
*error
) {
559 xpc_object_t xpc_certificates
= SecCertificateArrayCopyXPCArray(certificates
, error
);
560 if (!xpc_certificates
)
563 xpc_dictionary_set_value(message
, key
, xpc_certificates
);
564 xpc_release(xpc_certificates
);
569 static bool SecXPCDictionarySetPolicies(xpc_object_t message
, const char *key
, CFArrayRef policies
, CFErrorRef
*error
) {
570 xpc_object_t xpc_policies
= SecPolicyArrayCopyXPCArray(policies
, error
);
573 xpc_dictionary_set_value(message
, key
, xpc_policies
);
574 xpc_release(xpc_policies
);
578 static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message
, const char *key
, SecCertificatePathRef
*path
, CFErrorRef
*error
) {
579 xpc_object_t xpc_path
= xpc_dictionary_get_value(message
, key
);
584 *path
= SecCertificatePathCreateWithXPCArray(xpc_path
, error
);
588 static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
589 int64_t value
= xpc_dictionary_get_int64(message
, key
);
591 SecError(errSecInternal
, error
, CFSTR("object for key %s is 0"), key
);
596 static SecTrustResultType
certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request(enum SecXPCOperation op
, CFArrayRef certificates
, CFArrayRef anchors
, bool anchorsOnly
, CFArrayRef policies
, CFAbsoluteTime verifyTime
, __unused CFArrayRef accessGroups
, CFArrayRef
*details
, CFDictionaryRef
*info
, SecCertificatePathRef
*chain
, CFErrorRef
*error
)
598 __block SecTrustResultType tr
= kSecTrustResultInvalid
;
599 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
600 if (!SecXPCDictionarySetCertificates(message
, kSecTrustCertificatesKey
, certificates
, error
))
602 if (anchors
&& !SecXPCDictionarySetCertificates(message
, kSecTrustAnchorsKey
, anchors
, error
))
605 xpc_dictionary_set_bool(message
, kSecTrustAnchorsOnlyKey
, anchorsOnly
);
606 if (!SecXPCDictionarySetPolicies(message
, kSecTrustPoliciesKey
, policies
, error
))
608 xpc_dictionary_set_double(message
, kSecTrustVerifyDateKey
, verifyTime
);
610 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
611 secdebug("trust", "response: %@", response
);
612 return SecXPCDictionaryCopyArrayOptional(response
, kSecTrustDetailsKey
, details
, error
) &&
613 SecXPCDictionaryCopyDictionaryOptional(response
, kSecTrustInfoKey
, info
, error
) &&
614 SecXPCDictionaryCopyChainOptional(response
, kSecTrustChainKey
, chain
, error
) &&
615 ((tr
= SecXPCDictionaryGetNonZeroInteger(response
, kSecTrustResultKey
, error
)) != kSecTrustResultInvalid
);
620 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
) {
625 if (trust
->_trustResult
!= kSecTrustResultInvalid
)
626 return errSecSuccess
;
628 trust
->_trustResult
= kSecTrustResultOtherError
; /* to avoid potential recursion */
630 CFReleaseNull(trust
->_chain
);
631 CFReleaseNull(trust
->_details
);
632 CFReleaseNull(trust
->_info
);
634 SecTrustAddPolicyAnchors(trust
);
636 /* @@@ 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. */
637 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
638 trust
->_trustResult
= SECURITYD_XPC(sec_trust_evaluate
, certs_anchors_bool_policies_date_ag_to_details_info_chain_int_error_request
, trust
->_certificates
, trust
->_anchors
, trust
->_anchorsOnly
, trust
->_policies
, SecTrustGetVerifyTime(trust
), SecAccessGroupsGetCurrent(), &trust
->_details
, &trust
->_info
, &trust
->_chain
, error
);
639 if (trust
->_trustResult
== kSecTrustResultInvalid
/* TODO check domain */ &&
640 SecErrorGetOSStatus(*error
) == errSecNotAvailable
&&
641 CFArrayGetCount(trust
->_certificates
)) {
642 /* We failed to talk to securityd. The only time this should
643 happen is when we are running prior to launchd enabling
644 registration of services. This currently happens when we
645 are running from the ramdisk. To make ASR happy we initialize
646 _chain and return success with a failure as the trustResult, to
647 make it seem like we did a cert evaluation, so ASR can extract
648 the public key from the leaf. */
649 trust
->_chain
= SecCertificatePathCreate(NULL
, (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0));
651 CFReleaseNull(*error
);
654 return trust
->_trustResult
!= kSecTrustResultInvalid
;
658 /* Helper for the qsort below. */
659 static int compare_strings(const void *a1
, const void *a2
) {
660 CFStringRef s1
= *(CFStringRef
*)a1
;
661 CFStringRef s2
= *(CFStringRef
*)a2
;
662 return (int) CFStringCompare(s1
, s2
, kCFCompareForcedOrdering
);
665 CFStringRef
SecTrustCopyFailureDescription(SecTrustRef trust
) {
666 CFMutableStringRef reason
= CFStringCreateMutable(NULL
, 0);
667 CFArrayRef details
= SecTrustGetDetails(trust
);
668 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
669 for (CFIndex ix
= 0; ix
< pathLength
; ++ix
) {
670 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
671 CFIndex dCount
= CFDictionaryGetCount(detail
);
674 CFStringAppend(reason
, CFSTR(" [leaf"));
675 else if (ix
== pathLength
- 1)
676 CFStringAppend(reason
, CFSTR(" [root"));
678 CFStringAppendFormat(reason
, NULL
, CFSTR(" [ca%" PRIdCFIndex
), ix
);
680 const void *keys
[dCount
];
681 CFDictionaryGetKeysAndValues(detail
, &keys
[0], NULL
);
682 qsort(&keys
[0], dCount
, sizeof(keys
[0]), compare_strings
);
683 for (CFIndex kix
= 0; kix
< dCount
; ++kix
) {
684 CFStringRef key
= keys
[kix
];
685 const void *value
= CFDictionaryGetValue(detail
, key
);
686 CFStringAppendFormat(reason
, NULL
, CFSTR(" %@%@"), key
,
687 (CFGetTypeID(value
) == CFBooleanGetTypeID()
688 ? CFSTR("") : value
));
690 CFStringAppend(reason
, CFSTR("]"));
696 SecKeyRef
SecTrustCopyPublicKey(SecTrustRef trust
) {
700 if (!trust
->_publicKey
) {
701 if (!trust
->_chain
) {
702 /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */
703 trust
->_publicKey
= SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust
, 0));
705 if (!trust
->_publicKey
) {
706 /* If this fails use the passed in certs in order as if they are a valid cert path an attempt to extract the key. */
707 SecCertificatePathRef path
;
708 // SecCertificatePathCreateWithArray Would have crashed if this code was every called
709 // since it expected an array of CFDataRefs not an array of certificates.
710 path
= SecCertificatePathCreateWithArray(trust
->_certificates
);
711 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
715 if (!trust
->_publicKey
) {
716 /* Last resort, we evaluate the trust to get a _chain. */
717 SecTrustEvaluateIfNecessary(trust
);
721 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(trust
->_chain
, 0);
725 if (trust
->_publicKey
)
726 CFRetain(trust
->_publicKey
);
728 return trust
->_publicKey
;
731 CFIndex
SecTrustGetCertificateCount(SecTrustRef trust
) {
735 SecTrustEvaluateIfNecessary(trust
);
736 return (trust
->_chain
) ? SecCertificatePathGetCount(trust
->_chain
) : 1;
739 SecCertificateRef
SecTrustGetCertificateAtIndex(SecTrustRef trust
,
745 return (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0);
747 SecTrustEvaluateIfNecessary(trust
);
748 return (trust
->_chain
) ? SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
) : NULL
;
751 CFDictionaryRef
SecTrustCopyInfo(SecTrustRef trust
) {
755 SecTrustEvaluateIfNecessary(trust
);
756 CFDictionaryRef info
= trust
->_info
;
762 CFDataRef
SecTrustCopyExceptions(SecTrustRef trust
) {
763 CFArrayRef details
= SecTrustGetDetails(trust
);
764 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
765 CFMutableArrayRef exceptions
= CFArrayCreateMutable(kCFAllocatorDefault
, pathLength
, &kCFTypeArrayCallBacks
);
767 for (ix
= 0; ix
< pathLength
; ++ix
) {
768 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
769 CFIndex detailCount
= CFDictionaryGetCount(detail
);
770 CFMutableDictionaryRef exception
;
771 if (ix
== 0 || detailCount
> 0) {
772 exception
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, detailCount
+ 1, detail
);
773 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
774 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
775 CFDictionaryAddValue(exception
, kSecCertificateDetailSHA1Digest
, digest
);
777 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
778 exception
= (CFMutableDictionaryRef
)CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0,
779 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
781 CFArrayAppendValue(exceptions
, exception
);
782 CFRelease(exception
);
785 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
786 since it will never be empty). */
787 for (ix
= pathLength
; ix
-- > 1;) {
788 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(exceptions
, ix
);
789 if (CFDictionaryGetCount(exception
) == 0) {
790 CFArrayRemoveValueAtIndex(exceptions
, ix
);
796 CFDataRef encodedExceptions
= CFPropertyListCreateData(kCFAllocatorDefault
,
797 exceptions
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
798 CFRelease(exceptions
);
800 return encodedExceptions
;
803 bool SecTrustSetExceptions(SecTrustRef trust
, CFDataRef encodedExceptions
) {
807 CFArrayRef exceptions
= NULL
;
809 if (NULL
!= encodedExceptions
) {
810 exceptions
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
811 encodedExceptions
, kCFPropertyListImmutable
, NULL
, NULL
);
814 if (exceptions
&& CFGetTypeID(exceptions
) != CFArrayGetTypeID()) {
815 CFRelease(exceptions
);
818 CFReleaseSafe(trust
->_exceptions
);
819 trust
->_exceptions
= exceptions
;
821 /* If there is a valid exception entry for our current leaf we're golden. */
822 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
825 /* The passed in exceptions didn't match our current leaf, so we discard it. */
826 CFReleaseNull(trust
->_exceptions
);
830 CFArrayRef
SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
831 CFMutableArrayRef summary
;
832 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
833 summary
= SecCertificateCopySummaryProperties(certificate
,
834 SecTrustGetVerifyTime(trust
));
835 /* FIXME Add more details in the failure case. */
840 CFArrayRef
SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
842 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
843 summary
= SecCertificateCopyProperties(certificate
);
853 Can be on any non root cert in the chain.
855 Short circuit: Yes (No other errors matter after this one)
856 Non recoverable error
857 Trust UI: Invalid certificate chain linkage
858 Cert UI: Invalid linkage to parent certificate
860 CFStringRef kSecPolicyCheckIdLinkage
= CFSTR("IdLinkage");
862 /* X.509 required checks.
863 Can be on any cert in the chain
865 Short circuit: Yes (No other errors matter after this one)
866 Non recoverable error
867 Trust UI: (One or more) unsupported critical extensions found.
869 /* If we have no names for the extention oids use:
870 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
871 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
873 CFStringRef kSecPolicyCheckCriticalExtensions
= CFSTR("CriticalExtensions");
874 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
875 CFStringRef kSecPolicyCheckQualifiedCertStatements
= CFSTR("QualifiedCertStatements");
876 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
879 Only apply to the anchor.
881 Short circuit: No (Under discussion)
883 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
884 Cert UI: Not a valid anchor
886 CFStringRef kSecPolicyCheckAnchorTrusted
= CFSTR("AnchorTrusted");
887 CFStringRef kSecPolicyCheckAnchorSHA1
= CFSTR("AnchorSHA1");
894 Trust UI: (Hostname|email address) mismatch
896 CFStringRef kSecPolicyCheckSSLHostname
= CFSTR("SSLHostname");
898 /* Policy specific checks.
899 Can be on any cert in the chain
903 Trust UI: Certificate chain is not valid for the current policy.
904 OR: (One or more) certificates in the chain are not valid for the current policy/application
906 CFStringRef kSecPolicyCheckNonEmptySubject
= CFSTR("NonEmptySubject");
907 /* Cert UI: Non CA certificate used as CA.
908 Cert UI: CA certificate used as leaf.
909 Cert UI: Cert chain length exceeded.
910 Cert UI: Basic constraints extension not critical (non fatal).
911 Cert UI: Leaf certificate has basic constraints extension (non fatal).
913 CFStringRef kSecPolicyCheckBasicContraints
= CFSTR("BasicContraints");
914 CFStringRef kSecPolicyCheckKeyUsage
= CFSTR("KeyUsage");
915 CFStringRef kSecPolicyCheckExtendedKeyUsage
= CFSTR("ExtendedKeyUsage");
916 /* Checks that the issuer of the leaf has exactly one Common Name and that it
917 matches the specified string. */
918 CFStringRef kSecPolicyCheckIssuerCommonName
= CFSTR("IssuerCommonName");
919 /* Checks that the leaf has exactly one Common Name and that it has the
920 specified string as a prefix. */
921 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix
= CFSTR("SubjectCommonNamePrefix");
922 /* Check that the certificate chain length matches the specificed CFNumberRef
924 CFStringRef kSecPolicyCheckChainLength
= CFSTR("ChainLength");
925 CFStringRef kSecPolicyCheckNotValidBefore
= CFSTR("NotValidBefore");
928 Can be on any cert in the chain
932 Trust UI: One or more certificates have expired or are not valid yet.
933 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
934 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
936 CFStringRef kSecPolicyCheckValidIntermediates
= CFSTR("ValidIntermediates");
937 CFStringRef kSecPolicyCheckValidLeaf
= CFSTR("ValidLeaf");
938 CFStringRef kSecPolicyCheckValidRoot
= CFSTR("ValidRoot");
942 struct TrustFailures
{
944 bool unknownCritExtn
;
945 bool untrustedAnchor
;
946 bool hostnameMismatch
;
951 static void applyDetailProperty(const void *_key
, const void *_value
,
953 CFStringRef key
= (CFStringRef
)_key
;
954 struct TrustFailures
*tf
= (struct TrustFailures
*)context
;
955 if (CFGetTypeID(_value
) != CFBooleanGetTypeID()) {
956 /* Value isn't a CFBooleanRef, oh no! */
959 CFBooleanRef value
= (CFBooleanRef
)_value
;
960 if (CFBooleanGetValue(value
)) {
961 /* Not an actual failure so we don't report it. */
965 /* @@@ FIXME: Report a different return value when something is in the
966 details but masked out by an exception and use that below for display
968 if (CFEqual(key
, kSecPolicyCheckIdLinkage
)) {
969 tf
->badLinkage
= true;
970 } else if (CFEqual(key
, kSecPolicyCheckCriticalExtensions
)
971 || CFEqual(key
, kSecPolicyCheckQualifiedCertStatements
)) {
972 tf
->unknownCritExtn
= true;
973 } else if (CFEqual(key
, kSecPolicyCheckAnchorTrusted
)
974 || CFEqual(key
, kSecPolicyCheckAnchorSHA1
)) {
975 tf
->untrustedAnchor
= true;
976 } else if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
977 tf
->hostnameMismatch
= true;
978 } else if (CFEqual(key
, kSecPolicyCheckValidIntermediates
)
979 || CFEqual(key
, kSecPolicyCheckValidLeaf
)
980 || CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
981 tf
->invalidCert
= true;
983 /* Anything else is a policy failure. */
985 if (CFEqual(key
, kSecPolicyCheckNonEmptySubject
)
986 || CFEqual(key
, kSecPolicyCheckBasicContraints
)
987 || CFEqual(key
, kSecPolicyCheckKeyUsage
)
988 || CFEqual(key
, kSecPolicyCheckExtendedKeyUsage
)
989 || CFEqual(key
, kSecPolicyCheckIssuerCommonName
)
990 || CFEqual(key
, kSecPolicyCheckSubjectCommonNamePrefix
)
991 || CFEqual(key
, kSecPolicyCheckChainLength
)
992 || CFEqual(key
, kSecPolicyCheckNotValidBefore
))
995 tf
->policyFail
= true;
999 static void appendError(CFMutableArrayRef properties
, CFStringRef error
) {
1000 CFStringRef localizedError
= SecFrameworkCopyLocalizedString(error
,
1001 CFSTR("SecCertificate"));
1002 appendProperty(properties
, kSecPropertyTypeError
, NULL
, NULL
,
1004 CFReleaseNull(localizedError
);
1007 CFArrayRef
SecTrustCopyProperties(SecTrustRef trust
) {
1008 CFArrayRef details
= SecTrustGetDetails(trust
);
1012 struct TrustFailures tf
= {};
1014 CFIndex ix
, count
= CFArrayGetCount(details
);
1015 for (ix
= 0; ix
< count
; ++ix
) {
1016 CFDictionaryRef detail
= (CFDictionaryRef
)
1017 CFArrayGetValueAtIndex(details
, ix
);
1018 /* We now have a detail dictionary for certificate at index ix, with
1019 a key value pair for each failed policy check. Let's convert it
1020 from Ro-Man form into something a Hu-Man can understand. */
1021 CFDictionaryApplyFunction(detail
, applyDetailProperty
, &tf
);
1024 CFMutableArrayRef properties
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1025 &kCFTypeArrayCallBacks
);
1026 /* The badLinkage and unknownCritExtn failures are short circuited, since
1027 you can't recover from those errors. */
1028 if (tf
.badLinkage
) {
1029 appendError(properties
, CFSTR("Invalid certificate chain linkage."));
1030 } else if (tf
.unknownCritExtn
) {
1031 appendError(properties
, CFSTR("One or more unsupported critical extensions found."));
1033 if (tf
.untrustedAnchor
) {
1034 appendError(properties
, CFSTR("Root certificate is not trusted."));
1036 if (tf
.hostnameMismatch
) {
1037 appendError(properties
, CFSTR("Hostname mismatch."));
1039 if (tf
.policyFail
) {
1040 appendError(properties
, CFSTR("Policy requirements not met."));
1042 if (tf
.invalidCert
) {
1043 appendError(properties
, CFSTR("One or more certificates have expired or are not valid yet."));
1047 if (CFArrayGetCount(properties
) == 0) {
1048 /* The certificate chain is trusted, return an empty plist */
1049 CFReleaseNull(properties
);
1055 CFDictionaryRef
SecTrustCopyResult(SecTrustRef trust
) {
1056 // Builds and returns a dictionary of evaluation results.
1060 CFMutableDictionaryRef results
= CFDictionaryCreateMutable(NULL
, 0,
1061 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1063 // kSecTrustResultDetails (per-cert results)
1064 CFArrayRef details
= SecTrustGetDetails(trust
);
1066 CFDictionarySetValue(results
, (const void *)kSecTrustResultDetails
, (const void *)details
);
1069 // kSecTrustResultValue (overall trust result)
1070 CFNumberRef numValue
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trust
->_trustResult
);
1072 CFDictionarySetValue(results
, (const void *)kSecTrustResultValue
, (const void *)numValue
);
1073 CFRelease(numValue
);
1075 if (trust
->_trustResult
== kSecTrustResultInvalid
|| !trust
->_info
)
1076 return results
; // we have nothing more to add
1078 // kSecTrustEvaluationDate
1079 CFDateRef evaluationDate
= trust
->_verifyDate
;
1080 if (evaluationDate
) {
1081 CFDictionarySetValue(results
, (const void *)kSecTrustEvaluationDate
, (const void *)evaluationDate
);
1084 // kSecTrustExtendedValidation, kSecTrustOrganizationName
1085 CFDictionaryRef info
= trust
->_info
;
1087 CFBooleanRef evValue
;
1088 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoExtendedValidationKey
, (const void **)&evValue
)) {
1089 CFDictionarySetValue(results
, (const void *)kSecTrustExtendedValidation
, (const void *)evValue
);
1091 CFStringRef organizationName
;
1092 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoCompanyNameKey
, (const void **)&organizationName
)) {
1093 CFDictionarySetValue(results
, (const void *)kSecTrustOrganizationName
, (const void *)organizationName
);
1098 //FIXME: need to add revocation results here
1099 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate
1100 CFTypeRef expirationDate
;
1101 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecTrustExpirationDate
, (const void **)&expirationDate
)) {
1102 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationValidUntilDate
, (const void *)expirationDate
);
1103 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationChecked
, (const void *)kCFBooleanTrue
);
1110 // Return 0 upon error.
1111 static int to_int_error_request(enum SecXPCOperation op
, CFErrorRef
*error
) {
1112 __block
int64_t result
= 0;
1113 securityd_send_sync_and_do(op
, error
, NULL
, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1114 result
= xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1116 return SecError(errSecInternal
, error
, CFSTR("int64 missing in response"));
1122 // version 0 -> error, so we need to start at version 1 or later.
1123 OSStatus
SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber
)
1125 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1127 return SecError(errSecParam
, error
, CFSTR("versionNumber is NULL"));
1129 return (*versionNumber
= SECURITYD_XPC(sec_ota_pki_asset_version
, to_int_error_request
, error
)) != 0;
1133 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1135 static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary
, const char *key
, xpc_type_t type
)
1137 xpc_object_t value
= xpc_dictionary_get_value(dictionary
, key
);
1139 return value
&& (xpc_get_type(value
) == type
);
1142 OSStatus
SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset
)
1144 CFErrorRef error
= NULL
;
1145 do_if_registered(sec_ota_pki_get_new_asset
, &error
);
1148 xpc_object_t message
= securityd_create_message(kSecXPCOpOTAPKIGetNewAsset
, &error
);
1151 xpc_object_t response
= securityd_message_with_reply_sync(message
, &error
);
1153 if (response
&& xpc_dictionary_entry_is_type(response
, kSecXPCKeyResult
, XPC_TYPE_INT64
))
1155 num
= (int64_t) xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1156 xpc_release(response
);
1159 xpc_release(message
);
1162 if (NULL
!= didUpdateAsset
)
1164 *didUpdateAsset
= (int)num
;
1172 // MARK: SecTrustNode
1173 /********************************************************
1174 **************** SecTrustNode object *******************
1175 ********************************************************/
1176 typedef uint8_t SecFetchingState
;
1178 kSecFetchingStatePassedIn
= 0,
1179 kSecFetchingStateLocal
,
1180 kSecFetchingStateFromURL
,
1181 kSecFetchingStateDone
,
1184 typedef uint8_t SecTrustState
;
1186 kSecTrustStateUnknown
= 0,
1187 kSecTrustStateNotSigner
,
1188 kSecTrustStateValidSigner
,
1191 typedef struct __SecTrustNode
*SecTrustNodeRef
;
1192 struct __SecTrustNode
{
1193 SecTrustNodeRef _child
;
1194 SecCertificateRef _certificate
;
1196 /* Cached information about _certificate */
1200 /* Set of all certificates we have ever considered as a parent. We use
1201 this to avoid having to recheck certs when we go to the next phase. */
1202 CFMutableSet _certificates
;
1204 /* Parents that are still partial chains we haven't yet considered. */
1205 CFMutableSet _partials
;
1206 /* Parents that are still partial chains we have rejected. We reconsider
1207 these if we get to the final phase and we still haven't found a valid
1209 CFMutableSet _rejected_partials
;
1211 /* Parents that are complete chains we haven't yet considered. */
1212 CFMutableSet _candidates
;
1213 /* Parents that are complete chains we have rejected. */
1214 CFMutableSet _rejected_candidates
;
1216 /* State of candidate fetching. */
1217 SecFetchingState _fetchingState
;
1219 /* Trust state of _candidates[_candidateIndex] */
1220 SecTrustState _trustState
;
1222 typedef struct __SecTrustNode SecTrustNode
;
1224 /* Forward declarations of static functions. */
1225 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
);
1226 static void SecTrustNodeDestroy(CFTypeRef cf
);
1228 /* Static functions. */
1229 static CFStringRef
SecTrustNodeCopyDescription(CFTypeRef cf
) {
1230 SecTrustNodeRef node
= (SecTrustNodeRef
)cf
;
1231 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
1232 CFSTR("<SecTrustNodeRef: %p>"), node
);
1235 static void SecTrustNodeDestroy(CFTypeRef cf
) {
1236 SecTrustNodeRef trust
= (SecTrustNodeRef
)cf
;
1237 if (trust
->_child
) {
1238 free(trust
->_child
);
1240 if (trust
->_certificate
) {
1241 free(trust
->_certificate
);
1243 if (trust
->_candidates
) {
1244 CFRelease(trust
->_candidates
);
1248 /* SecTrustNode API functions. */
1249 CFGiblisFor(SecTrustNode
)
1251 SecTrustNodeRef
SecTrustNodeCreate(SecTrustRef trust
,
1252 SecCertificateRef certificate
, SecTrustNodeRef child
) {
1253 CFAllocatorRef allocator
= kCFAllocatorDefault
;
1257 CFIndex size
= sizeof(struct __SecTrustNode
);
1258 SecTrustNodeRef result
= (SecTrustNodeRef
)_CFRuntimeCreateInstance(
1259 allocator
, SecTrustNodeGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1263 memset((char*)result
+ sizeof(result
->_base
), 0,
1264 sizeof(*result
) - sizeof(result
->_base
));
1267 result
->_child
= child
;
1269 CFRetain(certificate
);
1270 result
->_certificate
= certificate
;
1271 result
->_isAnchor
= SecTrustCertificateIsAnchor(certificate
);
1276 SecCertificateRef
SecTrustGetCertificate(SecTrustNodeRef node
) {
1278 return node
->_certificate
;
1281 CFArrayRef
SecTrustNodeCopyProperties(SecTrustNodeRef node
,
1282 SecTrustRef trust
) {
1285 CFMutableArrayRef summary
= SecCertificateCopySummaryProperties(
1286 node
->_certificate
, SecTrustGetVerifyTime(trust
));
1287 /* FIXME Add more details in the failure case. */
1291 /* Attempt to verify this node's signature chain down to the child. */
1292 SecTrustState
SecTrustNodeVerifySignatureChain(SecTrustNodeRef node
) {
1294 return kSecTrustStateUnknown
;
1298 /* See if the next candidate works. */
1299 SecTrustNodeRef
SecTrustNodeCopyNextCandidate(SecTrustNodeRef node
,
1300 SecTrustRef trust
, SecFetchingState fetchingState
) {
1304 CFAbsoluteTime verifyTime
= SecTrustGetVerifyTime(trust
);
1307 /* If we have any unconsidered candidates left check those first. */
1308 while (node
->_candidateIndex
< CFArrayGetCount(node
->_candidates
)) {
1309 SecCertificateRef candidate
= (SecCertificateRef
)
1310 CFArrayGetValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1311 if (node
->_fetchingState
!= kSecFetchingStateDone
) {
1312 /* If we still have potential sources to fetch other candidates
1313 from we ignore expired candidates. */
1314 if (!SecCertificateIsValidOn(candidate
, verifyTime
)) {
1315 node
->_candidateIndex
++;
1320 SecTrustNodeRef parent
= SecTrustNodeCreate(candidate
, node
);
1321 CFArrayRemoveValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1322 if (SecTrustNodeVerifySignatureChain(parent
) ==
1323 kSecTrustStateNotSigner
) {
1324 /* This candidate parent is not a valid signer of its
1327 /* If another signature failed further down the chain we need
1328 to backtrack down to whatever child is still a valid
1329 candidate and has additional candidates to consider.
1330 @@@ We really want to make the fetchingState a global of
1331 SecTrust itself as well and not have any node go beyond the
1332 current state of SecTrust if there are other (read cheap)
1333 options to consider. */
1339 /* We've run out of candidates in our current state so let's try to
1340 find some more. Note we fetch candidates in increasing order of
1341 cost in the hope we won't ever get to the more expensive fetching
1343 SecCertificateRef certificate
= node
->_certificate
;
1344 switch (node
->_fetchingState
) {
1345 case kSecFetchingStatePassedIn
:
1346 /* Get the list of candidates from SecTrust. */
1347 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1349 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1352 SecCertificateGetNormalizedIssuerContent(certificate
);
1353 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1355 node
->_fetchingState
= kSecFetchingStateLocal
;
1357 case kSecFetchingStateLocal
:
1358 /* Lookup candidates in the local database. */
1359 node
->_fetchingState
= kSecFetchingStateFromURL
;
1361 case kSecFetchingStateFromURL
:
1362 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1364 case kSecFetchingStateCheckExpired
:
1365 /* Time to start considering expired candidates as well. */
1366 node
->_candidateIndex
= 0;
1367 node
->_fetchingState
= kSecFetchingStateDone
;
1369 case kSecFetchingStateDone
;
1374 CFAllocatorRef allocator
= CFGetAllocator(node
);
1376 /* A trust node has a number of states.
1377 1) Look for issuing certificates by asking SecTrust about known
1378 parent certificates.
1379 2) Look for issuing certificates in certificate databases (keychains)
1380 3) Look for issuing certificates by going out to the web if the nodes
1381 certificate has a issuer location URL.
1382 4) Look through expired or not yet valid candidates we have put aside.
1384 We go though the stages 1 though 3 looking for candidate issuer
1385 certificates. If a candidate certificate is not valid at verifyTime
1386 we put it in a to be examined later queue. If a candidate certificate
1387 is valid we verify if it actually signed our certificate (if possible).
1388 If not we discard it and continue on to the next candidate certificate.
1389 If it is we return a new SecTrustNodeRef for that certificate. */
1391 CFMutableArrayRef issuers
= CFArrayCreateMutable(allocator
, 0,
1392 &kCFTypeArrayCallBacks
);
1394 /* Find a node's parent. */
1395 certificate
= node
->_certificate
;
1396 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1397 CFTypeRef candidates
= NULL
;
1399 candidates
= (CFTypeRef
)CFDictionaryGetValueForKey(skidDict
, akid
);
1401 addValidIssuersFrom(issuers
, certificate
, candidates
, true);
1406 SecCertificateGetNormalizedIssuerContent(certificate
);
1407 candidates
= (CFTypeRef
)
1408 CFDictionaryGetValueForKey(subjectDict
, issuer
);
1409 addValidIssuersFrom(issuers
, certificate
, candidates
, false);
1412 if (CFArrayGetCount(issuers
) == 0) {
1413 /* O no! we can't find an issuer for this certificate. Let's see
1414 if we can find one in the local database. */
1417 return errSecSuccess
;
1420 CFArrayRef
SecTrustNodeCopyNextChain(SecTrustNodeRef node
,
1421 SecTrustRef trust
) {
1422 /* Return the next full chain that isn't a reject unless we are in
1423 a state where we consider returning rejects. */
1425 switch (node
->_fetchingState
) {
1426 case kSecFetchingStatePassedIn
:
1427 /* Get the list of candidates from SecTrust. */
1428 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1430 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1433 SecCertificateGetNormalizedIssuerContent(certificate
);
1434 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1436 node
->_fetchingState
= kSecFetchingStateLocal
;
1438 case kSecFetchingStateLocal
:
1439 /* Lookup candidates in the local database. */
1440 node
->_fetchingState
= kSecFetchingStateFromURL
;
1442 case kSecFetchingStateFromURL
:
1443 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1445 case kSecFetchingStateCheckExpired
:
1446 /* Time to start considering expired candidates as well. */
1447 node
->_candidateIndex
= 0;
1448 node
->_fetchingState
= kSecFetchingStateDone
;
1450 case kSecFetchingStateDone
;
1456 Iterator
parentIterator(Cert
);
1462 static bool unique(Node node
) {
1463 if (nodes
.contains(node
))
1469 static bool isAnchor(Cert cert
);
1476 Iterator parentIterator
; /* For current source of parents. */
1478 Node(Cert inCert
) : child(nil
), cert(inCert
), nextSource(0) {}
1479 Node(Node inChild
, Cert inCert
) : child(inChild
), cert(inCert
),
1482 CertPath
certPath() {
1486 path
.add(node
.cert
);
1492 void contains(Cert cert
) {
1495 if (cert
== node
.cert
)
1502 Node
nextParent(Array currentSources
) {
1505 parentIterator
== currentSources
[nextSource
- 1].end()) {
1506 if (nextSource
== currentSources
.count
) {
1507 /* We ran out of parent sources. */
1510 parentIterator
= currentSources
[nextSource
++].begin();
1512 Certificate cert
= *parentIterator
++;
1513 /* Check for cycles and self signed chains. */
1514 if (!contains(cert
)) {
1515 Node node
= Node(this, parent
);
1516 if (!NodeCache
.unique(node
))
1527 Array currentSources
;
1533 PathBuilder(Cert cert
) {
1534 nodes
.append(Node(cert
));
1535 nit
= nodes
.begin();
1536 sourceIT
= allSources
.begin();
1539 nextAnchoredPath() {
1540 if (nit
== nodes
.end()) {
1541 /* We should add another source to the list of sources to
1543 if (sourceIT
== allSources
.end()) {
1544 /* No more sources to add. */
1546 currentSources
+= *sourceIT
++;
1547 /* Resort nodes by size. */
1549 nit
= nodes
.begin();
1550 /* Set the source list for all nodes. */
1553 while (Node node
= *nit
) {
1554 Node candidate
= node
.nextParent(currentSources
);
1556 /* The current node has no more candidate parents so move
1562 if (candidate
.isAnchored
) {
1563 candidates
.append(candidate
);
1565 nodes
.insert(candidate
, nit
);
1570 while (Node node
= nextAnchoredPath()) {
1571 if (node
.isValid()) {
1572 chain
= node
.certPath
;
1575 rejects
.append(node
);