2 * Copyright (c) 2006-2013 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
25 * Created by Michael Brouwer on 10/17/06.
28 #include <Security/SecTrustPriv.h>
29 #include <Security/SecItemPriv.h>
30 #include <Security/SecCertificateInternal.h>
31 #include <Security/SecCertificatePath.h>
32 #include <Security/SecFramework.h>
33 #include <Security/SecPolicyInternal.h>
34 #include <utilities/SecIOFormat.h>
35 #include <CoreFoundation/CFRuntime.h>
36 #include <CoreFoundation/CFSet.h>
37 #include <CoreFoundation/CFString.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <CoreFoundation/CFArray.h>
40 #include <CoreFoundation/CFPropertyList.h>
41 #include <AssertMacros.h>
46 #include <Security/SecBasePriv.h>
47 #include <utilities/SecCFError.h>
48 #include <utilities/SecCFWrappers.h>
49 #include <utilities/SecCertificateTrace.h>
51 #include "SecRSAKey.h"
52 #include <libDER/oids.h>
53 #include <utilities/debugging.h>
54 #include <Security/SecInternal.h>
55 #include <ipc/securityd_client.h>
56 #include <SecuritydXPC.h>
57 #include <securityd/SecTrustServer.h>
59 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
61 SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey
, "ExtendedValidation");
62 SEC_CONST_DECL (kSecTrustInfoCompanyNameKey
, "CompanyName");
63 SEC_CONST_DECL (kSecTrustInfoRevocationKey
, "Revocation");
64 SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey
, "RevocationValidUntil");
66 /* Public trust result constants */
67 SEC_CONST_DECL (kSecTrustEvaluationDate
, "TrustEvaluationDate");
68 SEC_CONST_DECL (kSecTrustExtendedValidation
, "TrustExtendedValidation");
69 SEC_CONST_DECL (kSecTrustOrganizationName
, "Organization");
70 SEC_CONST_DECL (kSecTrustResultValue
, "TrustResultValue");
71 SEC_CONST_DECL (kSecTrustRevocationChecked
, "TrustRevocationChecked");
72 SEC_CONST_DECL (kSecTrustRevocationValidUntilDate
, "TrustExpirationDate");
73 SEC_CONST_DECL (kSecTrustResultDetails
, "TrustResultDetails");
78 /********************************************************
79 ****************** SecTrust object *********************
80 ********************************************************/
83 CFArrayRef _certificates
;
86 CFArrayRef _responses
;
87 CFDateRef _verifyDate
;
88 SecCertificatePathRef _chain
;
91 CFDictionaryRef _info
;
92 CFArrayRef _exceptions
;
94 /* Note that a value of kSecTrustResultInvalid (0)
95 * indicates the trust must be (re)evaluated; any
96 * functions which modify trust parameters in a way
97 * that would invalidate the current result must set
98 * this value back to kSecTrustResultInvalid.
100 SecTrustResultType _trustResult
;
102 /* If true we don't trust any anchors other than the ones in _anchors. */
105 /* Master switch to permit or disable network use in policy evaluation */
106 SecNetworkPolicy _networkPolicy
;
109 /* CFRuntime registration data. */
110 static pthread_once_t kSecTrustRegisterClass
= PTHREAD_ONCE_INIT
;
111 static CFTypeID kSecTrustTypeID
= _kCFRuntimeNotATypeID
;
113 /* Forward declarations of static functions. */
114 static CFStringRef
SecTrustDescribe(CFTypeRef cf
);
115 static void SecTrustDestroy(CFTypeRef cf
);
116 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
);
118 /* Static functions. */
119 static CF_RETURNS_RETAINED CFStringRef
SecTrustDescribe(CFTypeRef cf
) {
120 SecTrustRef trust
= (SecTrustRef
)cf
;
121 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
122 CFSTR("<SecTrustRef: %p>"), trust
);
125 static void SecTrustDestroy(CFTypeRef cf
) {
126 SecTrustRef trust
= (SecTrustRef
)cf
;
127 CFReleaseSafe(trust
->_certificates
);
128 CFReleaseSafe(trust
->_policies
);
129 CFReleaseSafe(trust
->_responses
);
130 CFReleaseSafe(trust
->_verifyDate
);
131 CFReleaseSafe(trust
->_anchors
);
132 CFReleaseSafe(trust
->_chain
);
133 CFReleaseSafe(trust
->_publicKey
);
134 CFReleaseSafe(trust
->_details
);
135 CFReleaseSafe(trust
->_info
);
136 CFReleaseSafe(trust
->_exceptions
);
139 static void SecTrustRegisterClass(void) {
140 static const CFRuntimeClass kSecTrustClass
= {
142 "SecTrust", /* class name */
145 SecTrustDestroy
, /* dealloc */
148 NULL
, /* copyFormattingDesc */
149 SecTrustDescribe
/* copyDebugDesc */
152 kSecTrustTypeID
= _CFRuntimeRegisterClass(&kSecTrustClass
);
155 /* Public API functions. */
156 CFTypeID
SecTrustGetTypeID(void) {
157 pthread_once(&kSecTrustRegisterClass
, SecTrustRegisterClass
);
158 return kSecTrustTypeID
;
161 OSStatus
SecTrustCreateWithCertificates(CFTypeRef certificates
,
162 CFTypeRef policies
, SecTrustRef
*trust
) {
163 OSStatus status
= errSecParam
;
164 CFAllocatorRef allocator
= kCFAllocatorDefault
;
165 CFArrayRef l_certs
= NULL
, l_policies
= NULL
;
166 SecTrustRef result
= NULL
;
170 CFTypeID certType
= CFGetTypeID(certificates
);
171 if (certType
== CFArrayGetTypeID()) {
172 /* We need at least 1 certificate. */
173 require_quiet(CFArrayGetCount(certificates
) > 0, errOut
);
174 l_certs
= CFArrayCreateCopy(allocator
, certificates
);
175 } else if (certType
== SecCertificateGetTypeID()) {
176 l_certs
= CFArrayCreate(allocator
, &certificates
, 1,
177 &kCFTypeArrayCallBacks
);
182 status
= errSecAllocate
;
187 CFTypeRef policy
= SecPolicyCreateBasicX509();
188 l_policies
= CFArrayCreate(allocator
, &policy
, 1,
189 &kCFTypeArrayCallBacks
);
191 } else if (CFGetTypeID(policies
) == CFArrayGetTypeID()) {
192 l_policies
= CFArrayCreateCopy(allocator
, policies
);
193 } else if (CFGetTypeID(policies
) == SecPolicyGetTypeID()) {
194 l_policies
= CFArrayCreate(allocator
, &policies
, 1,
195 &kCFTypeArrayCallBacks
);
200 status
= errSecAllocate
;
204 CFIndex size
= sizeof(struct __SecTrust
);
205 require_quiet(result
= (SecTrustRef
)_CFRuntimeCreateInstance(allocator
,
206 SecTrustGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
207 memset((char*)result
+ sizeof(result
->_base
), 0,
208 sizeof(*result
) - sizeof(result
->_base
));
209 status
= errSecSuccess
;
213 CFReleaseSafe(result
);
214 CFReleaseSafe(l_certs
);
215 CFReleaseSafe(l_policies
);
217 result
->_certificates
= l_certs
;
218 result
->_policies
= l_policies
;
224 static void SetTrustSetNeedsEvaluation(SecTrustRef trust
) {
227 trust
->_trustResult
= kSecTrustResultInvalid
;
231 OSStatus
SecTrustSetAnchorCertificatesOnly(SecTrustRef trust
,
232 Boolean anchorCertificatesOnly
) {
236 SetTrustSetNeedsEvaluation(trust
);
237 trust
->_anchorsOnly
= anchorCertificatesOnly
;
239 return errSecSuccess
;
242 OSStatus
SecTrustSetAnchorCertificates(SecTrustRef trust
,
243 CFArrayRef anchorCertificates
) {
247 SetTrustSetNeedsEvaluation(trust
);
248 if (anchorCertificates
)
249 CFRetain(anchorCertificates
);
251 CFRelease(trust
->_anchors
);
252 trust
->_anchors
= anchorCertificates
;
253 trust
->_anchorsOnly
= (anchorCertificates
!= NULL
);
255 return errSecSuccess
;
258 OSStatus
SecTrustCopyCustomAnchorCertificates(SecTrustRef trust
,
259 CFArrayRef
*anchors
) {
260 if (!trust
|| !anchors
) {
263 CFArrayRef anchorsArray
= NULL
;
264 if (trust
->_anchors
) {
265 anchorsArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_anchors
);
267 return errSecAllocate
;
270 *anchors
= anchorsArray
;
271 return errSecSuccess
;
274 OSStatus
SecTrustSetOCSPResponse(SecTrustRef trust
, CFTypeRef responseData
) {
278 SetTrustSetNeedsEvaluation(trust
);
279 CFArrayRef responseArray
= NULL
;
281 if (CFGetTypeID(responseData
) == CFArrayGetTypeID()) {
282 responseArray
= CFArrayCreateCopy(kCFAllocatorDefault
, responseData
);
283 } else if (CFGetTypeID(responseData
) == CFDataGetTypeID()) {
284 responseArray
= CFArrayCreate(kCFAllocatorDefault
, &responseData
, 1,
285 &kCFTypeArrayCallBacks
);
290 if (trust
->_responses
)
291 CFRelease(trust
->_responses
);
292 trust
->_responses
= responseArray
;
294 return errSecSuccess
;
297 OSStatus
SecTrustSetVerifyDate(SecTrustRef trust
, CFDateRef verifyDate
) {
301 SetTrustSetNeedsEvaluation(trust
);
303 CFRetainSafe(verifyDate
);
304 if (trust
->_verifyDate
)
305 CFRelease(trust
->_verifyDate
);
306 trust
->_verifyDate
= verifyDate
;
308 return errSecSuccess
;
311 OSStatus
SecTrustSetPolicies(SecTrustRef trust
, CFTypeRef newPolicies
) {
312 if (!trust
|| !newPolicies
) {
315 SetTrustSetNeedsEvaluation(trust
);
318 CFArrayRef policyArray
= NULL
;
319 if (CFGetTypeID(newPolicies
) == CFArrayGetTypeID()) {
320 policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, newPolicies
);
321 } else if (CFGetTypeID(newPolicies
) == SecPolicyGetTypeID()) {
322 policyArray
= CFArrayCreate(kCFAllocatorDefault
, &newPolicies
, 1,
323 &kCFTypeArrayCallBacks
);
328 if (trust
->_policies
)
329 CFRelease(trust
->_policies
);
330 trust
->_policies
= policyArray
;
332 return errSecSuccess
;
335 OSStatus
SecTrustCopyPolicies(SecTrustRef trust
, CFArrayRef
*policies
) {
336 if (!trust
|| !policies
) {
339 if (!trust
->_policies
) {
340 return errSecInternal
;
342 CFArrayRef policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_policies
);
344 return errSecAllocate
;
346 *policies
= policyArray
;
347 return errSecSuccess
;
350 OSStatus
SecTrustSetNetworkFetchAllowed(SecTrustRef trust
, Boolean allowFetch
) {
354 trust
->_networkPolicy
= (allowFetch
) ? useNetworkEnabled
: useNetworkDisabled
;
355 return errSecSuccess
;
358 OSStatus
SecTrustGetNetworkFetchAllowed(SecTrustRef trust
, Boolean
*allowFetch
) {
359 if (!trust
|| !allowFetch
) {
362 Boolean allowed
= false;
363 SecNetworkPolicy netPolicy
= trust
->_networkPolicy
;
364 if (netPolicy
== useNetworkDefault
) {
365 // network fetch is enabled by default for SSL only
366 CFIndex idx
, count
= (trust
->_policies
) ? CFArrayGetCount(trust
->_policies
) : 0;
367 for (idx
=0; idx
<count
; idx
++) {
368 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(trust
->_policies
, idx
);
370 CFDictionaryRef props
= SecPolicyCopyProperties(policy
);
372 CFTypeRef value
= (CFTypeRef
)CFDictionaryGetValue(props
, kSecPolicyOid
);
374 if (CFEqual(value
, kSecPolicyAppleSSL
)) {
383 // caller has explicitly set the network policy
384 allowed
= (netPolicy
== useNetworkEnabled
);
386 *allowFetch
= allowed
;
387 return errSecSuccess
;
390 CFAbsoluteTime
SecTrustGetVerifyTime(SecTrustRef trust
) {
391 CFAbsoluteTime verifyTime
;
392 if (trust
&& trust
->_verifyDate
) {
393 verifyTime
= CFDateGetAbsoluteTime(trust
->_verifyDate
);
395 verifyTime
= CFAbsoluteTimeGetCurrent();
396 /* Record the verifyDate we ended up using. */
398 trust
->_verifyDate
= CFDateCreate(CFGetAllocator(trust
), verifyTime
);
404 CFArrayRef
SecTrustGetDetails(SecTrustRef trust
) {
408 SecTrustEvaluateIfNecessary(trust
);
409 return trust
->_details
;
412 OSStatus
SecTrustGetTrustResult(SecTrustRef trust
,
413 SecTrustResultType
*result
) {
414 if (!trust
|| !result
) {
417 *result
= trust
->_trustResult
;
418 return errSecSuccess
;
421 static CFStringRef kSecCertificateDetailSHA1Digest
= CFSTR("SHA1Digest");
423 static CFDictionaryRef
SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust
, CFIndex ix
) {
424 if (!trust
->_exceptions
|| ix
>= CFArrayGetCount(trust
->_exceptions
))
426 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_exceptions
, ix
);
427 if (CFGetTypeID(exception
) != CFDictionaryGetTypeID())
430 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
434 /* If the exception contains the current certificates sha1Digest in the
435 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
436 CFDataRef sha1Digest
= SecCertificateGetSHA1Digest(certificate
);
437 CFTypeRef digestValue
= CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
);
438 if (!digestValue
|| !CFEqual(sha1Digest
, digestValue
))
444 struct SecTrustCheckExceptionContext
{
445 CFDictionaryRef exception
;
446 bool exceptionNotFound
;
449 static void SecTrustCheckException(const void *key
, const void *value
, void *context
) {
450 struct SecTrustCheckExceptionContext
*cec
= (struct SecTrustCheckExceptionContext
*)context
;
451 if (cec
->exception
) {
452 CFTypeRef exceptionValue
= CFDictionaryGetValue(cec
->exception
, key
);
453 if (!exceptionValue
|| !CFEqual(value
, exceptionValue
)) {
454 cec
->exceptionNotFound
= true;
457 cec
->exceptionNotFound
= true;
462 OSStatus
SecTrustEvaluate(SecTrustRef trust
, SecTrustResultType
*result
) {
466 OSStatus status
= SecTrustEvaluateIfNecessary(trust
);
467 if (status
|| !result
)
470 /* post-process trust result based on exceptions */
471 SecTrustResultType trustResult
= trust
->_trustResult
;
472 if (trustResult
== kSecTrustResultUnspecified
) {
473 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
474 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
475 trustResult
= kSecTrustResultProceed
;
476 } else if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
477 /* If we have exceptions get details and match to exceptions. */
478 CFIndex pathLength
= CFArrayGetCount(trust
->_details
);
479 struct SecTrustCheckExceptionContext context
= {};
481 for (ix
= 0; ix
< pathLength
; ++ix
) {
482 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_details
, ix
);
484 if ((ix
== 0) && CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedLeaf
))
486 trustResult
= kSecTrustResultFatalTrustFailure
;
487 goto DoneCheckingTrust
;
490 if (CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedKey
))
492 trustResult
= kSecTrustResultFatalTrustFailure
;
493 goto DoneCheckingTrust
;
496 context
.exception
= SecTrustGetExceptionForCertificateAtIndex(trust
, ix
);
497 CFDictionaryApplyFunction(detail
, SecTrustCheckException
, &context
);
498 if (context
.exceptionNotFound
) {
502 if (!context
.exceptionNotFound
)
503 trustResult
= kSecTrustResultProceed
;
506 trust
->_trustResult
= trustResult
;
509 /* log to syslog when there is a trust failure */
510 if (trustResult
!= kSecTrustResultProceed
&&
511 trustResult
!= kSecTrustResultConfirm
&&
512 trustResult
!= kSecTrustResultUnspecified
) {
513 CFStringRef failureDesc
= SecTrustCopyFailureDescription(trust
);
514 secerror("%@", failureDesc
);
515 CFRelease(failureDesc
);
520 *result
= trustResult
;
525 OSStatus
SecTrustEvaluateAsync(SecTrustRef trust
,
526 dispatch_queue_t queue
, SecTrustCallback result
)
528 dispatch_async(queue
, ^{
529 SecTrustResultType trustResult
;
530 if (errSecSuccess
!= SecTrustEvaluate(trust
, &trustResult
)) {
531 trustResult
= kSecTrustResultInvalid
;
533 result(trust
, trustResult
);
535 return errSecSuccess
;
538 static bool SecXPCDictionarySetCertificates(xpc_object_t message
, const char *key
, CFArrayRef certificates
, CFErrorRef
*error
) {
539 xpc_object_t xpc_certificates
= SecCertificateArrayCopyXPCArray(certificates
, error
);
540 if (!xpc_certificates
)
543 xpc_dictionary_set_value(message
, key
, xpc_certificates
);
544 xpc_release(xpc_certificates
);
549 static bool SecXPCDictionarySetPolicies(xpc_object_t message
, const char *key
, CFArrayRef policies
, CFErrorRef
*error
) {
550 xpc_object_t xpc_policies
= SecPolicyArrayCopyXPCArray(policies
, error
);
553 xpc_dictionary_set_value(message
, key
, xpc_policies
);
554 xpc_release(xpc_policies
);
558 static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message
, const char *key
, SecCertificatePathRef
*path
, CFErrorRef
*error
) {
559 xpc_object_t xpc_path
= xpc_dictionary_get_value(message
, key
);
564 *path
= SecCertificatePathCreateWithXPCArray(xpc_path
, error
);
568 static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
569 int64_t value
= xpc_dictionary_get_int64(message
, key
);
571 SecError(errSecInternal
, error
, CFSTR("object for key %s is 0"), key
);
576 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
)
578 __block SecTrustResultType tr
= kSecTrustResultInvalid
;
579 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
580 if (!SecXPCDictionarySetCertificates(message
, kSecTrustCertificatesKey
, certificates
, error
))
582 if (anchors
&& !SecXPCDictionarySetCertificates(message
, kSecTrustAnchorsKey
, anchors
, error
))
585 xpc_dictionary_set_bool(message
, kSecTrustAnchorsOnlyKey
, anchorsOnly
);
586 if (!SecXPCDictionarySetPolicies(message
, kSecTrustPoliciesKey
, policies
, error
))
588 xpc_dictionary_set_double(message
, kSecTrustVerifyDateKey
, verifyTime
);
590 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
591 secdebug("trust", "response: %@", response
);
592 return SecXPCDictionaryCopyArrayOptional(response
, kSecTrustDetailsKey
, details
, error
) &&
593 SecXPCDictionaryCopyDictionaryOptional(response
, kSecTrustInfoKey
, info
, error
) &&
594 SecXPCDictionaryCopyChainOptional(response
, kSecTrustChainKey
, chain
, error
) &&
595 ((tr
= SecXPCDictionaryGetNonZeroInteger(response
, kSecTrustResultKey
, error
)) != kSecTrustResultInvalid
);
600 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
) {
605 if (trust
->_trustResult
!= kSecTrustResultInvalid
)
606 return errSecSuccess
;
608 trust
->_trustResult
= kSecTrustResultOtherError
; /* to avoid potential recursion */
610 CFReleaseNull(trust
->_chain
);
611 CFReleaseNull(trust
->_details
);
612 CFReleaseNull(trust
->_info
);
614 /* @@@ 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. */
615 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
616 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
);
617 if (trust
->_trustResult
== kSecTrustResultInvalid
/* TODO check domain */ &&
618 SecErrorGetOSStatus(*error
) == errSecNotAvailable
&&
619 CFArrayGetCount(trust
->_certificates
)) {
620 /* We failed to talk to securityd. The only time this should
621 happen is when we are running prior to launchd enabling
622 registration of services. This currently happens when we
623 are running from the ramdisk. To make ASR happy we initialize
624 _chain and return success with a failure as the trustResult, to
625 make it seem like we did a cert evaluation, so ASR can extract
626 the public key from the leaf. */
627 trust
->_chain
= SecCertificatePathCreate(NULL
, (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0));
629 CFReleaseNull(*error
);
632 return trust
->_trustResult
!= kSecTrustResultInvalid
;
636 /* Helper for the qsort below. */
637 static int compare_strings(const void *a1
, const void *a2
) {
638 CFStringRef s1
= *(CFStringRef
*)a1
;
639 CFStringRef s2
= *(CFStringRef
*)a2
;
640 return (int) CFStringCompare(s1
, s2
, kCFCompareForcedOrdering
);
643 CFStringRef
SecTrustCopyFailureDescription(SecTrustRef trust
) {
644 CFMutableStringRef reason
= CFStringCreateMutable(NULL
, 0);
645 CFArrayRef details
= SecTrustGetDetails(trust
);
646 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
647 for (CFIndex ix
= 0; ix
< pathLength
; ++ix
) {
648 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
649 CFIndex dCount
= CFDictionaryGetCount(detail
);
652 CFStringAppend(reason
, CFSTR(" [leaf"));
653 else if (ix
== pathLength
- 1)
654 CFStringAppend(reason
, CFSTR(" [root"));
656 CFStringAppendFormat(reason
, NULL
, CFSTR(" [ca%" PRIdCFIndex
), ix
);
658 const void *keys
[dCount
];
659 CFDictionaryGetKeysAndValues(detail
, &keys
[0], NULL
);
660 qsort(&keys
[0], dCount
, sizeof(keys
[0]), compare_strings
);
661 for (CFIndex kix
= 0; kix
< dCount
; ++kix
) {
662 CFStringRef key
= keys
[kix
];
663 const void *value
= CFDictionaryGetValue(detail
, key
);
664 CFStringAppendFormat(reason
, NULL
, CFSTR(" %@%@"), key
,
665 (CFGetTypeID(value
) == CFBooleanGetTypeID()
666 ? CFSTR("") : value
));
668 CFStringAppend(reason
, CFSTR("]"));
674 SecKeyRef
SecTrustCopyPublicKey(SecTrustRef trust
) {
678 if (!trust
->_publicKey
) {
679 if (!trust
->_chain
) {
680 /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */
681 trust
->_publicKey
= SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust
, 0));
683 if (!trust
->_publicKey
) {
684 /* If this fails use the passed in certs in order as if they are a valid cert path an attempt to extract the key. */
685 SecCertificatePathRef path
;
686 // SecCertificatePathCreateWithArray Would have crashed if this code was every called
687 // since it expected an array of CFDataRefs not an array of certificates.
688 path
= SecCertificatePathCreateWithArray(trust
->_certificates
);
689 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
693 if (!trust
->_publicKey
) {
694 /* Last resort, we evaluate the trust to get a _chain. */
695 SecTrustEvaluateIfNecessary(trust
);
699 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(trust
->_chain
, 0);
703 if (trust
->_publicKey
)
704 CFRetain(trust
->_publicKey
);
706 return trust
->_publicKey
;
709 CFIndex
SecTrustGetCertificateCount(SecTrustRef trust
) {
713 SecTrustEvaluateIfNecessary(trust
);
714 return (trust
->_chain
) ? SecCertificatePathGetCount(trust
->_chain
) : 1;
717 SecCertificateRef
SecTrustGetCertificateAtIndex(SecTrustRef trust
,
723 return (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0);
725 SecTrustEvaluateIfNecessary(trust
);
726 return (trust
->_chain
) ? SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
) : NULL
;
729 CFDictionaryRef
SecTrustCopyInfo(SecTrustRef trust
) {
733 SecTrustEvaluateIfNecessary(trust
);
734 CFDictionaryRef info
= trust
->_info
;
740 CFDataRef
SecTrustCopyExceptions(SecTrustRef trust
) {
741 CFArrayRef details
= SecTrustGetDetails(trust
);
742 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
743 CFMutableArrayRef exceptions
= CFArrayCreateMutable(kCFAllocatorDefault
, pathLength
, &kCFTypeArrayCallBacks
);
745 for (ix
= 0; ix
< pathLength
; ++ix
) {
746 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
747 CFIndex detailCount
= CFDictionaryGetCount(detail
);
748 CFMutableDictionaryRef exception
;
749 if (ix
== 0 || detailCount
> 0) {
750 exception
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, detailCount
+ 1, detail
);
751 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
752 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
753 CFDictionaryAddValue(exception
, kSecCertificateDetailSHA1Digest
, digest
);
755 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
756 exception
= (CFMutableDictionaryRef
)CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0,
757 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
759 CFArrayAppendValue(exceptions
, exception
);
760 CFRelease(exception
);
763 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
764 since it will never be empty). */
765 for (ix
= pathLength
; ix
-- > 1;) {
766 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(exceptions
, ix
);
767 if (CFDictionaryGetCount(exception
) == 0) {
768 CFArrayRemoveValueAtIndex(exceptions
, ix
);
774 CFDataRef encodedExceptions
= CFPropertyListCreateData(kCFAllocatorDefault
,
775 exceptions
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
776 CFRelease(exceptions
);
778 return encodedExceptions
;
781 bool SecTrustSetExceptions(SecTrustRef trust
, CFDataRef encodedExceptions
) {
785 CFArrayRef exceptions
= NULL
;
787 if (NULL
!= encodedExceptions
) {
788 exceptions
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
789 encodedExceptions
, kCFPropertyListImmutable
, NULL
, NULL
);
792 if (exceptions
&& CFGetTypeID(exceptions
) != CFArrayGetTypeID()) {
793 CFRelease(exceptions
);
796 CFReleaseSafe(trust
->_exceptions
);
797 trust
->_exceptions
= exceptions
;
799 /* If there is a valid exception entry for our current leaf we're golden. */
800 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
803 /* The passed in exceptions didn't match our current leaf, so we discard it. */
804 CFReleaseNull(trust
->_exceptions
);
808 CFArrayRef
SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
809 CFMutableArrayRef summary
;
810 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
811 summary
= SecCertificateCopySummaryProperties(certificate
,
812 SecTrustGetVerifyTime(trust
));
813 /* FIXME Add more details in the failure case. */
818 CFArrayRef
SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
820 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
821 summary
= SecCertificateCopyProperties(certificate
);
831 Can be on any non root cert in the chain.
833 Short circuit: Yes (No other errors matter after this one)
834 Non recoverable error
835 Trust UI: Invalid certificate chain linkage
836 Cert UI: Invalid linkage to parent certificate
838 CFStringRef kSecPolicyCheckIdLinkage
= CFSTR("IdLinkage");
840 /* X.509 required checks.
841 Can be on any cert in the chain
843 Short circuit: Yes (No other errors matter after this one)
844 Non recoverable error
845 Trust UI: (One or more) unsupported critical extensions found.
847 /* If we have no names for the extention oids use:
848 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
849 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
851 CFStringRef kSecPolicyCheckCriticalExtensions
= CFSTR("CriticalExtensions");
852 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
853 CFStringRef kSecPolicyCheckQualifiedCertStatements
= CFSTR("QualifiedCertStatements");
854 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
857 Only apply to the anchor.
859 Short circuit: No (Under discussion)
861 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
862 Cert UI: Not a valid anchor
864 CFStringRef kSecPolicyCheckAnchorTrusted
= CFSTR("AnchorTrusted");
865 CFStringRef kSecPolicyCheckAnchorSHA1
= CFSTR("AnchorSHA1");
872 Trust UI: (Hostname|email address) mismatch
874 CFStringRef kSecPolicyCheckSSLHostname
= CFSTR("SSLHostname");
876 /* Policy specific checks.
877 Can be on any cert in the chain
881 Trust UI: Certificate chain is not valid for the current policy.
882 OR: (One or more) certificates in the chain are not valid for the current policy/application
884 CFStringRef kSecPolicyCheckNonEmptySubject
= CFSTR("NonEmptySubject");
885 /* Cert UI: Non CA certificate used as CA.
886 Cert UI: CA certificate used as leaf.
887 Cert UI: Cert chain length exceeded.
888 Cert UI: Basic constraints extension not critical (non fatal).
889 Cert UI: Leaf certificate has basic constraints extension (non fatal).
891 CFStringRef kSecPolicyCheckBasicContraints
= CFSTR("BasicContraints");
892 CFStringRef kSecPolicyCheckKeyUsage
= CFSTR("KeyUsage");
893 CFStringRef kSecPolicyCheckExtendedKeyUsage
= CFSTR("ExtendedKeyUsage");
894 /* Checks that the issuer of the leaf has exactly one Common Name and that it
895 matches the specified string. */
896 CFStringRef kSecPolicyCheckIssuerCommonName
= CFSTR("IssuerCommonName");
897 /* Checks that the leaf has exactly one Common Name and that it has the
898 specified string as a prefix. */
899 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix
= CFSTR("SubjectCommonNamePrefix");
900 /* Check that the certificate chain length matches the specificed CFNumberRef
902 CFStringRef kSecPolicyCheckChainLength
= CFSTR("ChainLength");
903 CFStringRef kSecPolicyCheckNotValidBefore
= CFSTR("NotValidBefore");
906 Can be on any cert in the chain
910 Trust UI: One or more certificates have expired or are not valid yet.
911 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
912 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
914 CFStringRef kSecPolicyCheckValidIntermediates
= CFSTR("ValidIntermediates");
915 CFStringRef kSecPolicyCheckValidLeaf
= CFSTR("ValidLeaf");
916 CFStringRef kSecPolicyCheckValidRoot
= CFSTR("ValidRoot");
920 struct TrustFailures
{
922 bool unknownCritExtn
;
923 bool untrustedAnchor
;
924 bool hostnameMismatch
;
929 static void applyDetailProperty(const void *_key
, const void *_value
,
931 CFStringRef key
= (CFStringRef
)_key
;
932 struct TrustFailures
*tf
= (struct TrustFailures
*)context
;
933 if (CFGetTypeID(_value
) != CFBooleanGetTypeID()) {
934 /* Value isn't a CFBooleanRef, oh no! */
937 CFBooleanRef value
= (CFBooleanRef
)_value
;
938 if (CFBooleanGetValue(value
)) {
939 /* Not an actual failure so we don't report it. */
943 /* @@@ FIXME: Report a different return value when something is in the
944 details but masked out by an exception and use that below for display
946 if (CFEqual(key
, kSecPolicyCheckIdLinkage
)) {
947 tf
->badLinkage
= true;
948 } else if (CFEqual(key
, kSecPolicyCheckCriticalExtensions
)
949 || CFEqual(key
, kSecPolicyCheckQualifiedCertStatements
)) {
950 tf
->unknownCritExtn
= true;
951 } else if (CFEqual(key
, kSecPolicyCheckAnchorTrusted
)
952 || CFEqual(key
, kSecPolicyCheckAnchorSHA1
)) {
953 tf
->untrustedAnchor
= true;
954 } else if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
955 tf
->hostnameMismatch
= true;
956 } else if (CFEqual(key
, kSecPolicyCheckValidIntermediates
)
957 || CFEqual(key
, kSecPolicyCheckValidLeaf
)
958 || CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
959 tf
->invalidCert
= true;
961 /* Anything else is a policy failure. */
963 if (CFEqual(key
, kSecPolicyCheckNonEmptySubject
)
964 || CFEqual(key
, kSecPolicyCheckBasicContraints
)
965 || CFEqual(key
, kSecPolicyCheckKeyUsage
)
966 || CFEqual(key
, kSecPolicyCheckExtendedKeyUsage
)
967 || CFEqual(key
, kSecPolicyCheckIssuerCommonName
)
968 || CFEqual(key
, kSecPolicyCheckSubjectCommonNamePrefix
)
969 || CFEqual(key
, kSecPolicyCheckChainLength
)
970 || CFEqual(key
, kSecPolicyCheckNotValidBefore
))
973 tf
->policyFail
= true;
977 static void appendError(CFMutableArrayRef properties
, CFStringRef error
) {
978 CFStringRef localizedError
= SecFrameworkCopyLocalizedString(error
,
979 CFSTR("SecCertificate"));
980 appendProperty(properties
, kSecPropertyTypeError
, NULL
, NULL
,
982 CFReleaseNull(localizedError
);
985 CFArrayRef
SecTrustCopyProperties(SecTrustRef trust
) {
986 CFArrayRef details
= SecTrustGetDetails(trust
);
990 struct TrustFailures tf
= {};
992 CFIndex ix
, count
= CFArrayGetCount(details
);
993 for (ix
= 0; ix
< count
; ++ix
) {
994 CFDictionaryRef detail
= (CFDictionaryRef
)
995 CFArrayGetValueAtIndex(details
, ix
);
996 /* We now have a detail dictionary for certificate at index ix, with
997 a key value pair for each failed policy check. Let's convert it
998 from Ro-Man form into something a Hu-Man can understand. */
999 CFDictionaryApplyFunction(detail
, applyDetailProperty
, &tf
);
1002 CFMutableArrayRef properties
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1003 &kCFTypeArrayCallBacks
);
1004 /* The badLinkage and unknownCritExtn failures are short circuited, since
1005 you can't recover from those errors. */
1006 if (tf
.badLinkage
) {
1007 appendError(properties
, CFSTR("Invalid certificate chain linkage."));
1008 } else if (tf
.unknownCritExtn
) {
1009 appendError(properties
, CFSTR("One or more unsupported critical extensions found."));
1011 if (tf
.untrustedAnchor
) {
1012 appendError(properties
, CFSTR("Root certificate is not trusted."));
1014 if (tf
.hostnameMismatch
) {
1015 appendError(properties
, CFSTR("Hostname mismatch."));
1017 if (tf
.policyFail
) {
1018 appendError(properties
, CFSTR("Policy requirements not met."));
1020 if (tf
.invalidCert
) {
1021 appendError(properties
, CFSTR("One or more certificates have expired or are not valid yet."));
1025 if (CFArrayGetCount(properties
) == 0) {
1026 /* The certificate chain is trusted, return an empty plist */
1027 CFReleaseNull(properties
);
1033 CFDictionaryRef
SecTrustCopyResult(SecTrustRef trust
) {
1034 // Builds and returns a dictionary of evaluation results.
1038 CFMutableDictionaryRef results
= CFDictionaryCreateMutable(NULL
, 0,
1039 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1041 // kSecTrustResultDetails (per-cert results)
1042 CFArrayRef details
= SecTrustGetDetails(trust
);
1044 CFDictionarySetValue(results
, (const void *)kSecTrustResultDetails
, (const void *)details
);
1047 // kSecTrustResultValue (overall trust result)
1048 CFNumberRef numValue
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trust
->_trustResult
);
1050 CFDictionarySetValue(results
, (const void *)kSecTrustResultValue
, (const void *)numValue
);
1051 CFRelease(numValue
);
1053 if (trust
->_trustResult
== kSecTrustResultInvalid
|| !trust
->_info
)
1054 return results
; // we have nothing more to add
1056 // kSecTrustEvaluationDate
1057 CFDateRef evaluationDate
= trust
->_verifyDate
;
1058 if (evaluationDate
) {
1059 CFDictionarySetValue(results
, (const void *)kSecTrustEvaluationDate
, (const void *)evaluationDate
);
1062 // kSecTrustExtendedValidation, kSecTrustOrganizationName
1063 CFDictionaryRef info
= trust
->_info
;
1065 CFBooleanRef evValue
;
1066 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoExtendedValidationKey
, (const void **)&evValue
)) {
1067 CFDictionarySetValue(results
, (const void *)kSecTrustExtendedValidation
, (const void *)evValue
);
1069 CFStringRef organizationName
;
1070 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoCompanyNameKey
, (const void **)&organizationName
)) {
1071 CFDictionarySetValue(results
, (const void *)kSecTrustOrganizationName
, (const void *)organizationName
);
1076 //FIXME: need to add revocation results here
1077 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate
1078 CFTypeRef expirationDate
;
1079 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecTrustExpirationDate
, (const void **)&expirationDate
)) {
1080 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationValidUntilDate
, (const void *)expirationDate
);
1081 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationChecked
, (const void *)kCFBooleanTrue
);
1088 // Return 0 upon error.
1089 static int to_int_error_request(enum SecXPCOperation op
, CFErrorRef
*error
) {
1090 __block
int64_t result
= 0;
1091 securityd_send_sync_and_do(op
, error
, NULL
, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1092 result
= xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1094 return SecError(errSecInternal
, error
, CFSTR("int64 missing in response"));
1100 // version 0 -> error, so we need to start at version 1 or later.
1101 OSStatus
SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber
)
1103 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1105 return SecError(errSecParam
, error
, CFSTR("versionNumber is NULL"));
1107 return (*versionNumber
= SECURITYD_XPC(sec_ota_pki_asset_version
, to_int_error_request
, error
)) != 0;
1111 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1113 static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary
, const char *key
, xpc_type_t type
)
1115 xpc_object_t value
= xpc_dictionary_get_value(dictionary
, key
);
1117 return value
&& (xpc_get_type(value
) == type
);
1120 OSStatus
SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset
)
1122 CFErrorRef error
= NULL
;
1123 do_if_registered(sec_ota_pki_get_new_asset
, &error
);
1126 xpc_object_t message
= securityd_create_message(kSecXPCOpOTAPKIGetNewAsset
, &error
);
1129 xpc_object_t response
= securityd_message_with_reply_sync(message
, &error
);
1131 if (response
&& xpc_dictionary_entry_is_type(response
, kSecXPCKeyResult
, XPC_TYPE_INT64
))
1133 num
= (int64_t) xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1134 xpc_release(response
);
1137 xpc_release(message
);
1140 if (NULL
!= didUpdateAsset
)
1142 *didUpdateAsset
= (int)num
;
1150 // MARK: SecTrustNode
1151 /********************************************************
1152 **************** SecTrustNode object *******************
1153 ********************************************************/
1154 typedef uint8_t SecFetchingState
;
1156 kSecFetchingStatePassedIn
= 0,
1157 kSecFetchingStateLocal
,
1158 kSecFetchingStateFromURL
,
1159 kSecFetchingStateDone
,
1162 typedef uint8_t SecTrustState
;
1164 kSecTrustStateUnknown
= 0,
1165 kSecTrustStateNotSigner
,
1166 kSecTrustStateValidSigner
,
1169 typedef struct __SecTrustNode
*SecTrustNodeRef
;
1170 struct __SecTrustNode
{
1171 SecTrustNodeRef _child
;
1172 SecCertificateRef _certificate
;
1174 /* Cached information about _certificate */
1178 /* Set of all certificates we have ever considered as a parent. We use
1179 this to avoid having to recheck certs when we go to the next phase. */
1180 CFMutableSet _certificates
;
1182 /* Parents that are still partial chains we haven't yet considered. */
1183 CFMutableSet _partials
;
1184 /* Parents that are still partial chains we have rejected. We reconsider
1185 these if we get to the final phase and we still haven't found a valid
1187 CFMutableSet _rejected_partials
;
1189 /* Parents that are complete chains we haven't yet considered. */
1190 CFMutableSet _candidates
;
1191 /* Parents that are complete chains we have rejected. */
1192 CFMutableSet _rejected_candidates
;
1194 /* State of candidate fetching. */
1195 SecFetchingState _fetchingState
;
1197 /* Trust state of _candidates[_candidateIndex] */
1198 SecTrustState _trustState
;
1200 typedef struct __SecTrustNode SecTrustNode
;
1202 /* CFRuntime registration data. */
1203 static pthread_once_t kSecTrustNodeRegisterClass
= PTHREAD_ONCE_INIT
;
1204 static CFTypeID kSecTrustNodeTypeID
= _kCFRuntimeNotATypeID
;
1206 /* Forward declarations of static functions. */
1207 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
);
1208 static void SecTrustNodeDestroy(CFTypeRef cf
);
1210 /* Static functions. */
1211 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
) {
1212 SecTrustNodeRef node
= (SecTrustNodeRef
)cf
;
1213 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
1214 CFSTR("<SecTrustNodeRef: %p>"), node
);
1217 static void SecTrustNodeDestroy(CFTypeRef cf
) {
1218 SecTrustNodeRef trust
= (SecTrustNodeRef
)cf
;
1219 if (trust
->_child
) {
1220 free(trust
->_child
);
1222 if (trust
->_certificate
) {
1223 free(trust
->_certificate
);
1225 if (trust
->_candidates
) {
1226 CFRelease(trust
->_candidates
);
1230 static void SecTrustNodeRegisterClass(void) {
1231 static const CFRuntimeClass kSecTrustNodeClass
= {
1233 "SecTrustNode", /* class name */
1236 SecTrustNodeDestroy
, /* dealloc */
1239 NULL
, /* copyFormattingDesc */
1240 SecTrustNodeDescribe
/* copyDebugDesc */
1243 kSecTrustNodeTypeID
= _CFRuntimeRegisterClass(&kSecTrustNodeClass
);
1246 /* SecTrustNode API functions. */
1247 CFTypeID
SecTrustNodeGetTypeID(void) {
1248 pthread_once(&kSecTrustNodeRegisterClass
, SecTrustNodeRegisterClass
);
1249 return kSecTrustNodeTypeID
;
1252 SecTrustNodeRef
SecTrustNodeCreate(SecTrustRef trust
,
1253 SecCertificateRef certificate
, SecTrustNodeRef child
) {
1254 CFAllocatorRef allocator
= kCFAllocatorDefault
;
1258 CFIndex size
= sizeof(struct __SecTrustNode
);
1259 SecTrustNodeRef result
= (SecTrustNodeRef
)_CFRuntimeCreateInstance(
1260 allocator
, SecTrustNodeGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1264 memset((char*)result
+ sizeof(result
->_base
), 0,
1265 sizeof(*result
) - sizeof(result
->_base
));
1268 result
->_child
= child
;
1270 CFRetain(certificate
);
1271 result
->_certificate
= certificate
;
1272 result
->_isAnchor
= SecTrustCertificateIsAnchor(certificate
);
1277 SecCertificateRef
SecTrustGetCertificate(SecTrustNodeRef node
) {
1279 return node
->_certificate
;
1282 CFArrayRef
SecTrustNodeCopyProperties(SecTrustNodeRef node
,
1283 SecTrustRef trust
) {
1286 CFMutableArrayRef summary
= SecCertificateCopySummaryProperties(
1287 node
->_certificate
, SecTrustGetVerifyTime(trust
));
1288 /* FIXME Add more details in the failure case. */
1292 /* Attempt to verify this node's signature chain down to the child. */
1293 SecTrustState
SecTrustNodeVerifySignatureChain(SecTrustNodeRef node
) {
1295 return kSecTrustStateUnknown
;
1299 /* See if the next candidate works. */
1300 SecTrustNodeRef
SecTrustNodeCopyNextCandidate(SecTrustNodeRef node
,
1301 SecTrustRef trust
, SecFetchingState fetchingState
) {
1305 CFAbsoluteTime verifyTime
= SecTrustGetVerifyTime(trust
);
1308 /* If we have any unconsidered candidates left check those first. */
1309 while (node
->_candidateIndex
< CFArrayGetCount(node
->_candidates
)) {
1310 SecCertificateRef candidate
= (SecCertificateRef
)
1311 CFArrayGetValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1312 if (node
->_fetchingState
!= kSecFetchingStateDone
) {
1313 /* If we still have potential sources to fetch other candidates
1314 from we ignore expired candidates. */
1315 if (!SecCertificateIsValidOn(candidate
, verifyTime
)) {
1316 node
->_candidateIndex
++;
1321 SecTrustNodeRef parent
= SecTrustNodeCreate(candidate
, node
);
1322 CFArrayRemoveValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1323 if (SecTrustNodeVerifySignatureChain(parent
) ==
1324 kSecTrustStateNotSigner
) {
1325 /* This candidate parent is not a valid signer of its
1328 /* If another signature failed further down the chain we need
1329 to backtrack down to whatever child is still a valid
1330 candidate and has additional candidates to consider.
1331 @@@ We really want to make the fetchingState a global of
1332 SecTrust itself as well and not have any node go beyond the
1333 current state of SecTrust if there are other (read cheap)
1334 options to consider. */
1340 /* We've run out of candidates in our current state so let's try to
1341 find some more. Note we fetch candidates in increasing order of
1342 cost in the hope we won't ever get to the more expensive fetching
1344 SecCertificateRef certificate
= node
->_certificate
;
1345 switch (node
->_fetchingState
) {
1346 case kSecFetchingStatePassedIn
:
1347 /* Get the list of candidates from SecTrust. */
1348 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1350 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1353 SecCertificateGetNormalizedIssuerContent(certificate
);
1354 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1356 node
->_fetchingState
= kSecFetchingStateLocal
;
1358 case kSecFetchingStateLocal
:
1359 /* Lookup candidates in the local database. */
1360 node
->_fetchingState
= kSecFetchingStateFromURL
;
1362 case kSecFetchingStateFromURL
:
1363 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1365 case kSecFetchingStateCheckExpired
:
1366 /* Time to start considering expired candidates as well. */
1367 node
->_candidateIndex
= 0;
1368 node
->_fetchingState
= kSecFetchingStateDone
;
1370 case kSecFetchingStateDone
;
1375 CFAllocatorRef allocator
= CFGetAllocator(node
);
1377 /* A trust node has a number of states.
1378 1) Look for issuing certificates by asking SecTrust about known
1379 parent certificates.
1380 2) Look for issuing certificates in certificate databases (keychains)
1381 3) Look for issuing certificates by going out to the web if the nodes
1382 certificate has a issuer location URL.
1383 4) Look through expired or not yet valid candidates we have put aside.
1385 We go though the stages 1 though 3 looking for candidate issuer
1386 certificates. If a candidate certificate is not valid at verifyTime
1387 we put it in a to be examined later queue. If a candidate certificate
1388 is valid we verify if it actually signed our certificate (if possible).
1389 If not we discard it and continue on to the next candidate certificate.
1390 If it is we return a new SecTrustNodeRef for that certificate. */
1392 CFMutableArrayRef issuers
= CFArrayCreateMutable(allocator
, 0,
1393 &kCFTypeArrayCallBacks
);
1395 /* Find a node's parent. */
1396 certificate
= node
->_certificate
;
1397 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1398 CFTypeRef candidates
= NULL
;
1400 candidates
= (CFTypeRef
)CFDictionaryGetValueForKey(skidDict
, akid
);
1402 addValidIssuersFrom(issuers
, certificate
, candidates
, true);
1407 SecCertificateGetNormalizedIssuerContent(certificate
);
1408 candidates
= (CFTypeRef
)
1409 CFDictionaryGetValueForKey(subjectDict
, issuer
);
1410 addValidIssuersFrom(issuers
, certificate
, candidates
, false);
1413 if (CFArrayGetCount(issuers
) == 0) {
1414 /* O no! we can't find an issuer for this certificate. Let's see
1415 if we can find one in the local database. */
1418 return errSecSuccess
;
1421 CFArrayRef
SecTrustNodeCopyNextChain(SecTrustNodeRef node
,
1422 SecTrustRef trust
) {
1423 /* Return the next full chain that isn't a reject unless we are in
1424 a state where we consider returning rejects. */
1426 switch (node
->_fetchingState
) {
1427 case kSecFetchingStatePassedIn
:
1428 /* Get the list of candidates from SecTrust. */
1429 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1431 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1434 SecCertificateGetNormalizedIssuerContent(certificate
);
1435 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1437 node
->_fetchingState
= kSecFetchingStateLocal
;
1439 case kSecFetchingStateLocal
:
1440 /* Lookup candidates in the local database. */
1441 node
->_fetchingState
= kSecFetchingStateFromURL
;
1443 case kSecFetchingStateFromURL
:
1444 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1446 case kSecFetchingStateCheckExpired
:
1447 /* Time to start considering expired candidates as well. */
1448 node
->_candidateIndex
= 0;
1449 node
->_fetchingState
= kSecFetchingStateDone
;
1451 case kSecFetchingStateDone
;
1457 Iterator
parentIterator(Cert
);
1463 static bool unique(Node node
) {
1464 if (nodes
.contains(node
))
1470 static bool isAnchor(Cert cert
);
1477 Iterator parentIterator
; /* For current source of parents. */
1479 Node(Cert inCert
) : child(nil
), cert(inCert
), nextSource(0) {}
1480 Node(Node inChild
, Cert inCert
) : child(inChild
), cert(inCert
),
1483 CertPath
certPath() {
1487 path
.add(node
.cert
);
1493 void contains(Cert cert
) {
1496 if (cert
== node
.cert
)
1503 Node
nextParent(Array currentSources
) {
1506 parentIterator
== currentSources
[nextSource
- 1].end()) {
1507 if (nextSource
== currentSources
.count
) {
1508 /* We ran out of parent sources. */
1511 parentIterator
= currentSources
[nextSource
++].begin();
1513 Certificate cert
= *parentIterator
++;
1514 /* Check for cycles and self signed chains. */
1515 if (!contains(cert
)) {
1516 Node node
= Node(this, parent
);
1517 if (!NodeCache
.unique(node
))
1528 Array currentSources
;
1534 PathBuilder(Cert cert
) {
1535 nodes
.append(Node(cert
));
1536 nit
= nodes
.begin();
1537 sourceIT
= allSources
.begin();
1540 nextAnchoredPath() {
1541 if (nit
== nodes
.end()) {
1542 /* We should add another source to the list of sources to
1544 if (sourceIT
== allSources
.end()) {
1545 /* No more sources to add. */
1547 currentSources
+= *sourceIT
++;
1548 /* Resort nodes by size. */
1550 nit
= nodes
.begin();
1551 /* Set the source list for all nodes. */
1554 while (Node node
= *nit
) {
1555 Node candidate
= node
.nextParent(currentSources
);
1557 /* The current node has no more candidate parents so move
1563 if (candidate
.isAnchored
) {
1564 candidates
.append(candidate
);
1566 nodes
.insert(candidate
, nit
);
1571 while (Node node
= nextAnchoredPath()) {
1572 if (node
.isValid()) {
1573 chain
= node
.certPath
;
1576 rejects
.append(node
);