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>
50 #include "SecRSAKey.h"
51 #include <libDER/oids.h>
52 #include <utilities/debugging.h>
53 #include <Security/SecInternal.h>
54 #include <ipc/securityd_client.h>
55 #include <SecuritydXPC.h>
56 #include <securityd/SecTrustServer.h>
58 #define SEC_CONST_DECL(k,v) CFTypeRef k = (CFTypeRef)(CFSTR(v));
60 SEC_CONST_DECL (kSecTrustInfoExtendedValidationKey
, "ExtendedValidation");
61 SEC_CONST_DECL (kSecTrustInfoCompanyNameKey
, "CompanyName");
62 SEC_CONST_DECL (kSecTrustInfoRevocationKey
, "Revocation");
63 SEC_CONST_DECL (kSecTrustInfoRevocationValidUntilKey
, "RevocationValidUntil");
65 /* Public trust result constants */
66 SEC_CONST_DECL (kSecTrustEvaluationDate
, "TrustEvaluationDate");
67 SEC_CONST_DECL (kSecTrustExtendedValidation
, "TrustExtendedValidation");
68 SEC_CONST_DECL (kSecTrustOrganizationName
, "Organization");
69 SEC_CONST_DECL (kSecTrustResultValue
, "TrustResultValue");
70 SEC_CONST_DECL (kSecTrustRevocationChecked
, "TrustRevocationChecked");
71 SEC_CONST_DECL (kSecTrustRevocationValidUntilDate
, "TrustExpirationDate");
72 SEC_CONST_DECL (kSecTrustResultDetails
, "TrustResultDetails");
77 /********************************************************
78 ****************** SecTrust object *********************
79 ********************************************************/
82 CFArrayRef _certificates
;
85 CFArrayRef _responses
;
86 CFDateRef _verifyDate
;
87 SecCertificatePathRef _chain
;
90 CFDictionaryRef _info
;
91 CFArrayRef _exceptions
;
93 /* Note that a value of kSecTrustResultInvalid (0)
94 * indicates the trust must be (re)evaluated; any
95 * functions which modify trust parameters in a way
96 * that would invalidate the current result must set
97 * this value back to kSecTrustResultInvalid.
99 SecTrustResultType _trustResult
;
101 /* If true we don't trust any anchors other than the ones in _anchors. */
104 /* Master switch to permit or disable network use in policy evaluation */
105 SecNetworkPolicy _networkPolicy
;
108 /* CFRuntime registration data. */
109 static pthread_once_t kSecTrustRegisterClass
= PTHREAD_ONCE_INIT
;
110 static CFTypeID kSecTrustTypeID
= _kCFRuntimeNotATypeID
;
112 /* Forward declarations of static functions. */
113 static CFStringRef
SecTrustDescribe(CFTypeRef cf
);
114 static void SecTrustDestroy(CFTypeRef cf
);
115 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
);
117 /* Static functions. */
118 static CF_RETURNS_RETAINED CFStringRef
SecTrustDescribe(CFTypeRef cf
) {
119 SecTrustRef trust
= (SecTrustRef
)cf
;
120 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
121 CFSTR("<SecTrustRef: %p>"), trust
);
124 static void SecTrustDestroy(CFTypeRef cf
) {
125 SecTrustRef trust
= (SecTrustRef
)cf
;
126 CFReleaseSafe(trust
->_certificates
);
127 CFReleaseSafe(trust
->_policies
);
128 CFReleaseSafe(trust
->_responses
);
129 CFReleaseSafe(trust
->_verifyDate
);
130 CFReleaseSafe(trust
->_anchors
);
131 CFReleaseSafe(trust
->_chain
);
132 CFReleaseSafe(trust
->_publicKey
);
133 CFReleaseSafe(trust
->_details
);
134 CFReleaseSafe(trust
->_info
);
135 CFReleaseSafe(trust
->_exceptions
);
138 static void SecTrustRegisterClass(void) {
139 static const CFRuntimeClass kSecTrustClass
= {
141 "SecTrust", /* class name */
144 SecTrustDestroy
, /* dealloc */
147 NULL
, /* copyFormattingDesc */
148 SecTrustDescribe
/* copyDebugDesc */
151 kSecTrustTypeID
= _CFRuntimeRegisterClass(&kSecTrustClass
);
154 /* Public API functions. */
155 CFTypeID
SecTrustGetTypeID(void) {
156 pthread_once(&kSecTrustRegisterClass
, SecTrustRegisterClass
);
157 return kSecTrustTypeID
;
160 OSStatus
SecTrustCreateWithCertificates(CFTypeRef certificates
,
161 CFTypeRef policies
, SecTrustRef
*trust
) {
162 OSStatus status
= errSecParam
;
163 CFAllocatorRef allocator
= kCFAllocatorDefault
;
164 CFArrayRef l_certs
= NULL
, l_policies
= NULL
;
165 SecTrustRef result
= NULL
;
169 CFTypeID certType
= CFGetTypeID(certificates
);
170 if (certType
== CFArrayGetTypeID()) {
171 /* We need at least 1 certificate. */
172 require_quiet(CFArrayGetCount(certificates
) > 0, errOut
);
173 l_certs
= CFArrayCreateCopy(allocator
, certificates
);
174 } else if (certType
== SecCertificateGetTypeID()) {
175 l_certs
= CFArrayCreate(allocator
, &certificates
, 1,
176 &kCFTypeArrayCallBacks
);
181 status
= errSecAllocate
;
186 CFTypeRef policy
= SecPolicyCreateBasicX509();
187 l_policies
= CFArrayCreate(allocator
, &policy
, 1,
188 &kCFTypeArrayCallBacks
);
190 } else if (CFGetTypeID(policies
) == CFArrayGetTypeID()) {
191 l_policies
= CFArrayCreateCopy(allocator
, policies
);
192 } else if (CFGetTypeID(policies
) == SecPolicyGetTypeID()) {
193 l_policies
= CFArrayCreate(allocator
, &policies
, 1,
194 &kCFTypeArrayCallBacks
);
199 status
= errSecAllocate
;
203 CFIndex size
= sizeof(struct __SecTrust
);
204 require_quiet(result
= (SecTrustRef
)_CFRuntimeCreateInstance(allocator
,
205 SecTrustGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
206 memset((char*)result
+ sizeof(result
->_base
), 0,
207 sizeof(*result
) - sizeof(result
->_base
));
208 status
= errSecSuccess
;
212 CFReleaseSafe(result
);
213 CFReleaseSafe(l_certs
);
214 CFReleaseSafe(l_policies
);
216 result
->_certificates
= l_certs
;
217 result
->_policies
= l_policies
;
223 static void SetTrustSetNeedsEvaluation(SecTrustRef trust
) {
226 trust
->_trustResult
= kSecTrustResultInvalid
;
230 OSStatus
SecTrustSetAnchorCertificatesOnly(SecTrustRef trust
,
231 Boolean anchorCertificatesOnly
) {
235 SetTrustSetNeedsEvaluation(trust
);
236 trust
->_anchorsOnly
= anchorCertificatesOnly
;
238 return errSecSuccess
;
241 OSStatus
SecTrustSetAnchorCertificates(SecTrustRef trust
,
242 CFArrayRef anchorCertificates
) {
246 SetTrustSetNeedsEvaluation(trust
);
247 if (anchorCertificates
)
248 CFRetain(anchorCertificates
);
250 CFRelease(trust
->_anchors
);
251 trust
->_anchors
= anchorCertificates
;
252 trust
->_anchorsOnly
= (anchorCertificates
!= NULL
);
254 return errSecSuccess
;
257 OSStatus
SecTrustCopyCustomAnchorCertificates(SecTrustRef trust
,
258 CFArrayRef
*anchors
) {
259 if (!trust
|| !anchors
) {
262 CFArrayRef anchorsArray
= NULL
;
263 if (trust
->_anchors
) {
264 anchorsArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_anchors
);
266 return errSecAllocate
;
269 *anchors
= anchorsArray
;
270 return errSecSuccess
;
273 OSStatus
SecTrustSetOCSPResponse(SecTrustRef trust
, CFTypeRef responseData
) {
277 SetTrustSetNeedsEvaluation(trust
);
278 CFArrayRef responseArray
= NULL
;
280 if (CFGetTypeID(responseData
) == CFArrayGetTypeID()) {
281 responseArray
= CFArrayCreateCopy(kCFAllocatorDefault
, responseData
);
282 } else if (CFGetTypeID(responseData
) == CFDataGetTypeID()) {
283 responseArray
= CFArrayCreate(kCFAllocatorDefault
, &responseData
, 1,
284 &kCFTypeArrayCallBacks
);
289 if (trust
->_responses
)
290 CFRelease(trust
->_responses
);
291 trust
->_responses
= responseArray
;
293 return errSecSuccess
;
296 OSStatus
SecTrustSetVerifyDate(SecTrustRef trust
, CFDateRef verifyDate
) {
300 SetTrustSetNeedsEvaluation(trust
);
302 CFRetainSafe(verifyDate
);
303 if (trust
->_verifyDate
)
304 CFRelease(trust
->_verifyDate
);
305 trust
->_verifyDate
= verifyDate
;
307 return errSecSuccess
;
310 OSStatus
SecTrustSetPolicies(SecTrustRef trust
, CFTypeRef newPolicies
) {
311 if (!trust
|| !newPolicies
) {
314 SetTrustSetNeedsEvaluation(trust
);
317 CFArrayRef policyArray
= NULL
;
318 if (CFGetTypeID(newPolicies
) == CFArrayGetTypeID()) {
319 policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, newPolicies
);
320 } else if (CFGetTypeID(newPolicies
) == SecPolicyGetTypeID()) {
321 policyArray
= CFArrayCreate(kCFAllocatorDefault
, &newPolicies
, 1,
322 &kCFTypeArrayCallBacks
);
327 if (trust
->_policies
)
328 CFRelease(trust
->_policies
);
329 trust
->_policies
= policyArray
;
331 return errSecSuccess
;
334 OSStatus
SecTrustCopyPolicies(SecTrustRef trust
, CFArrayRef
*policies
) {
335 if (!trust
|| !policies
) {
338 if (!trust
->_policies
) {
339 return errSecInternal
;
341 CFArrayRef policyArray
= CFArrayCreateCopy(kCFAllocatorDefault
, trust
->_policies
);
343 return errSecAllocate
;
345 *policies
= policyArray
;
346 return errSecSuccess
;
349 OSStatus
SecTrustSetNetworkFetchAllowed(SecTrustRef trust
, Boolean allowFetch
) {
353 trust
->_networkPolicy
= (allowFetch
) ? useNetworkEnabled
: useNetworkDisabled
;
354 return errSecSuccess
;
357 OSStatus
SecTrustGetNetworkFetchAllowed(SecTrustRef trust
, Boolean
*allowFetch
) {
358 if (!trust
|| !allowFetch
) {
361 Boolean allowed
= false;
362 SecNetworkPolicy netPolicy
= trust
->_networkPolicy
;
363 if (netPolicy
== useNetworkDefault
) {
364 // network fetch is enabled by default for SSL only
365 CFIndex idx
, count
= (trust
->_policies
) ? CFArrayGetCount(trust
->_policies
) : 0;
366 for (idx
=0; idx
<count
; idx
++) {
367 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(trust
->_policies
, idx
);
369 CFDictionaryRef props
= SecPolicyCopyProperties(policy
);
371 CFTypeRef value
= (CFTypeRef
)CFDictionaryGetValue(props
, kSecPolicyOid
);
373 if (CFEqual(value
, kSecPolicyAppleSSL
)) {
382 // caller has explicitly set the network policy
383 allowed
= (netPolicy
== useNetworkEnabled
);
385 *allowFetch
= allowed
;
386 return errSecSuccess
;
389 CFAbsoluteTime
SecTrustGetVerifyTime(SecTrustRef trust
) {
390 CFAbsoluteTime verifyTime
;
391 if (trust
&& trust
->_verifyDate
) {
392 verifyTime
= CFDateGetAbsoluteTime(trust
->_verifyDate
);
394 verifyTime
= CFAbsoluteTimeGetCurrent();
395 /* Record the verifyDate we ended up using. */
397 trust
->_verifyDate
= CFDateCreate(CFGetAllocator(trust
), verifyTime
);
403 CFArrayRef
SecTrustGetDetails(SecTrustRef trust
) {
407 SecTrustEvaluateIfNecessary(trust
);
408 return trust
->_details
;
411 OSStatus
SecTrustGetTrustResult(SecTrustRef trust
,
412 SecTrustResultType
*result
) {
413 if (!trust
|| !result
) {
416 *result
= trust
->_trustResult
;
417 return errSecSuccess
;
420 static CFStringRef kSecCertificateDetailSHA1Digest
= CFSTR("SHA1Digest");
422 static CFDictionaryRef
SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust
, CFIndex ix
) {
423 if (!trust
->_exceptions
|| ix
>= CFArrayGetCount(trust
->_exceptions
))
425 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_exceptions
, ix
);
426 if (CFGetTypeID(exception
) != CFDictionaryGetTypeID())
429 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
433 /* If the exception contains the current certificates sha1Digest in the
434 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
435 CFDataRef sha1Digest
= SecCertificateGetSHA1Digest(certificate
);
436 CFTypeRef digestValue
= CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
);
437 if (!digestValue
|| !CFEqual(sha1Digest
, digestValue
))
443 struct SecTrustCheckExceptionContext
{
444 CFDictionaryRef exception
;
445 bool exceptionNotFound
;
448 static void SecTrustCheckException(const void *key
, const void *value
, void *context
) {
449 struct SecTrustCheckExceptionContext
*cec
= (struct SecTrustCheckExceptionContext
*)context
;
450 if (cec
->exception
) {
451 CFTypeRef exceptionValue
= CFDictionaryGetValue(cec
->exception
, key
);
452 if (!exceptionValue
|| !CFEqual(value
, exceptionValue
)) {
453 cec
->exceptionNotFound
= true;
456 cec
->exceptionNotFound
= true;
460 OSStatus
SecTrustEvaluate(SecTrustRef trust
, SecTrustResultType
*result
) {
464 OSStatus status
= SecTrustEvaluateIfNecessary(trust
);
465 if (status
|| !result
)
468 /* post-process trust result based on exceptions */
469 SecTrustResultType trustResult
= trust
->_trustResult
;
470 if (trustResult
== kSecTrustResultUnspecified
) {
471 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
472 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
473 trustResult
= kSecTrustResultProceed
;
474 } else if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
475 /* If we have exceptions get details and match to exceptions. */
476 CFIndex pathLength
= CFArrayGetCount(trust
->_details
);
477 struct SecTrustCheckExceptionContext context
= {};
479 for (ix
= 0; ix
< pathLength
; ++ix
) {
480 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_details
, ix
);
482 if ((ix
== 0) && CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedLeaf
))
484 trustResult
= kSecTrustResultFatalTrustFailure
;
485 goto DoneCheckingTrust
;
488 if (CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedKey
))
490 trustResult
= kSecTrustResultFatalTrustFailure
;
491 goto DoneCheckingTrust
;
494 context
.exception
= SecTrustGetExceptionForCertificateAtIndex(trust
, ix
);
495 CFDictionaryApplyFunction(detail
, SecTrustCheckException
, &context
);
496 if (context
.exceptionNotFound
) {
500 if (!context
.exceptionNotFound
)
501 trustResult
= kSecTrustResultProceed
;
504 trust
->_trustResult
= trustResult
;
507 /* log to syslog when there is a trust failure */
508 if (trustResult
!= kSecTrustResultProceed
&&
509 trustResult
!= kSecTrustResultConfirm
&&
510 trustResult
!= kSecTrustResultUnspecified
) {
511 CFStringRef failureDesc
= SecTrustCopyFailureDescription(trust
);
512 secerror("%@", failureDesc
);
513 CFRelease(failureDesc
);
517 *result
= trustResult
;
522 OSStatus
SecTrustEvaluateAsync(SecTrustRef trust
,
523 dispatch_queue_t queue
, SecTrustCallback result
)
525 dispatch_async(queue
, ^{
526 SecTrustResultType trustResult
;
527 if (errSecSuccess
!= SecTrustEvaluate(trust
, &trustResult
)) {
528 trustResult
= kSecTrustResultInvalid
;
530 result(trust
, trustResult
);
532 return errSecSuccess
;
535 static bool SecXPCDictionarySetCertificates(xpc_object_t message
, const char *key
, CFArrayRef certificates
, CFErrorRef
*error
) {
536 xpc_object_t xpc_certificates
= SecCertificateArrayCopyXPCArray(certificates
, error
);
537 if (!xpc_certificates
)
540 xpc_dictionary_set_value(message
, key
, xpc_certificates
);
541 xpc_release(xpc_certificates
);
546 static bool SecXPCDictionarySetPolicies(xpc_object_t message
, const char *key
, CFArrayRef policies
, CFErrorRef
*error
) {
547 xpc_object_t xpc_policies
= SecPolicyArrayCopyXPCArray(policies
, error
);
550 xpc_dictionary_set_value(message
, key
, xpc_policies
);
551 xpc_release(xpc_policies
);
555 static bool SecXPCDictionaryCopyChainOptional(xpc_object_t message
, const char *key
, SecCertificatePathRef
*path
, CFErrorRef
*error
) {
556 xpc_object_t xpc_path
= xpc_dictionary_get_value(message
, key
);
561 *path
= SecCertificatePathCreateWithXPCArray(xpc_path
, error
);
565 static int SecXPCDictionaryGetNonZeroInteger(xpc_object_t message
, const char *key
, CFErrorRef
*error
) {
566 int64_t value
= xpc_dictionary_get_int64(message
, key
);
568 SecError(errSecInternal
, error
, CFSTR("object for key %s is 0"), key
);
573 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
)
575 __block SecTrustResultType tr
= kSecTrustResultInvalid
;
576 securityd_send_sync_and_do(op
, error
, ^bool(xpc_object_t message
, CFErrorRef
*error
) {
577 if (!SecXPCDictionarySetCertificates(message
, kSecTrustCertificatesKey
, certificates
, error
))
579 if (anchors
&& !SecXPCDictionarySetCertificates(message
, kSecTrustAnchorsKey
, anchors
, error
))
582 xpc_dictionary_set_bool(message
, kSecTrustAnchorsOnlyKey
, anchorsOnly
);
583 if (!SecXPCDictionarySetPolicies(message
, kSecTrustPoliciesKey
, policies
, error
))
585 xpc_dictionary_set_double(message
, kSecTrustVerifyDateKey
, verifyTime
);
587 }, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
588 secdebug("trust", "response: %@", response
);
589 return SecXPCDictionaryCopyArrayOptional(response
, kSecTrustDetailsKey
, details
, error
) &&
590 SecXPCDictionaryCopyDictionaryOptional(response
, kSecTrustInfoKey
, info
, error
) &&
591 SecXPCDictionaryCopyChainOptional(response
, kSecTrustChainKey
, chain
, error
) &&
592 ((tr
= SecXPCDictionaryGetNonZeroInteger(response
, kSecTrustResultKey
, error
)) != kSecTrustResultInvalid
);
597 static OSStatus
SecTrustEvaluateIfNecessary(SecTrustRef trust
) {
602 if (trust
->_trustResult
!= kSecTrustResultInvalid
)
603 return errSecSuccess
;
605 trust
->_trustResult
= kSecTrustResultOtherError
; /* to avoid potential recursion */
607 CFReleaseNull(trust
->_chain
);
608 CFReleaseNull(trust
->_details
);
609 CFReleaseNull(trust
->_info
);
611 /* @@@ 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. */
612 return SecOSStatusWith(^bool (CFErrorRef
*error
) {
613 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
);
614 if (trust
->_trustResult
== kSecTrustResultInvalid
/* TODO check domain */ &&
615 SecErrorGetOSStatus(*error
) == errSecNotAvailable
&&
616 CFArrayGetCount(trust
->_certificates
)) {
617 /* We failed to talk to securityd. The only time this should
618 happen is when we are running prior to launchd enabling
619 registration of services. This currently happens when we
620 are running from the ramdisk. To make ASR happy we initialize
621 _chain and return success with a failure as the trustResult, to
622 make it seem like we did a cert evaluation, so ASR can extract
623 the public key from the leaf. */
624 trust
->_chain
= SecCertificatePathCreate(NULL
, (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0));
626 CFReleaseNull(*error
);
629 return trust
->_trustResult
!= kSecTrustResultInvalid
;
633 /* Helper for the qsort below. */
634 static int compare_strings(const void *a1
, const void *a2
) {
635 CFStringRef s1
= *(CFStringRef
*)a1
;
636 CFStringRef s2
= *(CFStringRef
*)a2
;
637 return (int) CFStringCompare(s1
, s2
, kCFCompareForcedOrdering
);
640 CFStringRef
SecTrustCopyFailureDescription(SecTrustRef trust
) {
641 CFMutableStringRef reason
= CFStringCreateMutable(NULL
, 0);
642 CFArrayRef details
= SecTrustGetDetails(trust
);
643 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
644 for (CFIndex ix
= 0; ix
< pathLength
; ++ix
) {
645 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
646 CFIndex dCount
= CFDictionaryGetCount(detail
);
649 CFStringAppend(reason
, CFSTR(" [leaf"));
650 else if (ix
== pathLength
- 1)
651 CFStringAppend(reason
, CFSTR(" [root"));
653 CFStringAppendFormat(reason
, NULL
, CFSTR(" [ca%" PRIdCFIndex
), ix
);
655 const void *keys
[dCount
];
656 CFDictionaryGetKeysAndValues(detail
, &keys
[0], NULL
);
657 qsort(&keys
[0], dCount
, sizeof(keys
[0]), compare_strings
);
658 for (CFIndex kix
= 0; kix
< dCount
; ++kix
) {
659 CFStringRef key
= keys
[kix
];
660 const void *value
= CFDictionaryGetValue(detail
, key
);
661 CFStringAppendFormat(reason
, NULL
, CFSTR(" %@%@"), key
,
662 (CFGetTypeID(value
) == CFBooleanGetTypeID()
663 ? CFSTR("") : value
));
665 CFStringAppend(reason
, CFSTR("]"));
671 SecKeyRef
SecTrustCopyPublicKey(SecTrustRef trust
) {
675 if (!trust
->_publicKey
) {
676 if (!trust
->_chain
) {
677 /* Trust hasn't been evaluated yet, first attempt to retrieve public key from leaf cert as is. */
678 trust
->_publicKey
= SecCertificateCopyPublicKey(SecTrustGetCertificateAtIndex(trust
, 0));
680 if (!trust
->_publicKey
) {
681 /* If this fails use the passed in certs in order as if they are a valid cert path an attempt to extract the key. */
682 SecCertificatePathRef path
;
683 // SecCertificatePathCreateWithArray Would have crashed if this code was every called
684 // since it expected an array of CFDataRefs not an array of certificates.
685 path
= SecCertificatePathCreateWithArray(trust
->_certificates
);
686 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(path
, 0);
690 if (!trust
->_publicKey
) {
691 /* Last resort, we evaluate the trust to get a _chain. */
692 SecTrustEvaluateIfNecessary(trust
);
696 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(trust
->_chain
, 0);
700 if (trust
->_publicKey
)
701 CFRetain(trust
->_publicKey
);
703 return trust
->_publicKey
;
706 CFIndex
SecTrustGetCertificateCount(SecTrustRef trust
) {
710 SecTrustEvaluateIfNecessary(trust
);
711 return (trust
->_chain
) ? SecCertificatePathGetCount(trust
->_chain
) : 1;
714 SecCertificateRef
SecTrustGetCertificateAtIndex(SecTrustRef trust
,
720 return (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0);
722 SecTrustEvaluateIfNecessary(trust
);
723 return (trust
->_chain
) ? SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
) : NULL
;
726 CFDictionaryRef
SecTrustCopyInfo(SecTrustRef trust
) {
730 SecTrustEvaluateIfNecessary(trust
);
731 CFDictionaryRef info
= trust
->_info
;
737 CFDataRef
SecTrustCopyExceptions(SecTrustRef trust
) {
738 CFArrayRef details
= SecTrustGetDetails(trust
);
739 CFIndex pathLength
= details
? CFArrayGetCount(details
) : 0;
740 CFMutableArrayRef exceptions
= CFArrayCreateMutable(kCFAllocatorDefault
, pathLength
, &kCFTypeArrayCallBacks
);
742 for (ix
= 0; ix
< pathLength
; ++ix
) {
743 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
744 CFIndex detailCount
= CFDictionaryGetCount(detail
);
745 CFMutableDictionaryRef exception
;
746 if (ix
== 0 || detailCount
> 0) {
747 exception
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, detailCount
+ 1, detail
);
748 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
749 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
750 CFDictionaryAddValue(exception
, kSecCertificateDetailSHA1Digest
, digest
);
752 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
753 exception
= (CFMutableDictionaryRef
)CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0,
754 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
756 CFArrayAppendValue(exceptions
, exception
);
757 CFRelease(exception
);
760 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
761 since it will never be empty). */
762 for (ix
= pathLength
; ix
-- > 1;) {
763 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(exceptions
, ix
);
764 if (CFDictionaryGetCount(exception
) == 0) {
765 CFArrayRemoveValueAtIndex(exceptions
, ix
);
771 CFDataRef encodedExceptions
= CFPropertyListCreateData(kCFAllocatorDefault
,
772 exceptions
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
773 CFRelease(exceptions
);
775 return encodedExceptions
;
778 bool SecTrustSetExceptions(SecTrustRef trust
, CFDataRef encodedExceptions
) {
782 CFArrayRef exceptions
= NULL
;
784 if (NULL
!= encodedExceptions
) {
785 exceptions
= CFPropertyListCreateWithData(kCFAllocatorDefault
,
786 encodedExceptions
, kCFPropertyListImmutable
, NULL
, NULL
);
789 if (exceptions
&& CFGetTypeID(exceptions
) != CFArrayGetTypeID()) {
790 CFRelease(exceptions
);
793 CFReleaseSafe(trust
->_exceptions
);
794 trust
->_exceptions
= exceptions
;
796 /* If there is a valid exception entry for our current leaf we're golden. */
797 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
800 /* The passed in exceptions didn't match our current leaf, so we discard it. */
801 CFReleaseNull(trust
->_exceptions
);
805 CFArrayRef
SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
806 CFMutableArrayRef summary
;
807 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
808 summary
= SecCertificateCopySummaryProperties(certificate
,
809 SecTrustGetVerifyTime(trust
));
810 /* FIXME Add more details in the failure case. */
815 CFArrayRef
SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
817 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
818 summary
= SecCertificateCopyProperties(certificate
);
828 Can be on any non root cert in the chain.
830 Short circuit: Yes (No other errors matter after this one)
831 Non recoverable error
832 Trust UI: Invalid certificate chain linkage
833 Cert UI: Invalid linkage to parent certificate
835 CFStringRef kSecPolicyCheckIdLinkage
= CFSTR("IdLinkage");
837 /* X.509 required checks.
838 Can be on any cert in the chain
840 Short circuit: Yes (No other errors matter after this one)
841 Non recoverable error
842 Trust UI: (One or more) unsupported critical extensions found.
844 /* If we have no names for the extention oids use:
845 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
846 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
848 CFStringRef kSecPolicyCheckCriticalExtensions
= CFSTR("CriticalExtensions");
849 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
850 CFStringRef kSecPolicyCheckQualifiedCertStatements
= CFSTR("QualifiedCertStatements");
851 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
854 Only apply to the anchor.
856 Short circuit: No (Under discussion)
858 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
859 Cert UI: Not a valid anchor
861 CFStringRef kSecPolicyCheckAnchorTrusted
= CFSTR("AnchorTrusted");
862 CFStringRef kSecPolicyCheckAnchorSHA1
= CFSTR("AnchorSHA1");
869 Trust UI: (Hostname|email address) mismatch
871 CFStringRef kSecPolicyCheckSSLHostname
= CFSTR("SSLHostname");
873 /* Policy specific checks.
874 Can be on any cert in the chain
878 Trust UI: Certificate chain is not valid for the current policy.
879 OR: (One or more) certificates in the chain are not valid for the current policy/application
881 CFStringRef kSecPolicyCheckNonEmptySubject
= CFSTR("NonEmptySubject");
882 /* Cert UI: Non CA certificate used as CA.
883 Cert UI: CA certificate used as leaf.
884 Cert UI: Cert chain length exceeded.
885 Cert UI: Basic constraints extension not critical (non fatal).
886 Cert UI: Leaf certificate has basic constraints extension (non fatal).
888 CFStringRef kSecPolicyCheckBasicContraints
= CFSTR("BasicContraints");
889 CFStringRef kSecPolicyCheckKeyUsage
= CFSTR("KeyUsage");
890 CFStringRef kSecPolicyCheckExtendedKeyUsage
= CFSTR("ExtendedKeyUsage");
891 /* Checks that the issuer of the leaf has exactly one Common Name and that it
892 matches the specified string. */
893 CFStringRef kSecPolicyCheckIssuerCommonName
= CFSTR("IssuerCommonName");
894 /* Checks that the leaf has exactly one Common Name and that it has the
895 specified string as a prefix. */
896 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix
= CFSTR("SubjectCommonNamePrefix");
897 /* Check that the certificate chain length matches the specificed CFNumberRef
899 CFStringRef kSecPolicyCheckChainLength
= CFSTR("ChainLength");
900 CFStringRef kSecPolicyCheckNotValidBefore
= CFSTR("NotValidBefore");
903 Can be on any cert in the chain
907 Trust UI: One or more certificates have expired or are not valid yet.
908 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
909 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
911 CFStringRef kSecPolicyCheckValidIntermediates
= CFSTR("ValidIntermediates");
912 CFStringRef kSecPolicyCheckValidLeaf
= CFSTR("ValidLeaf");
913 CFStringRef kSecPolicyCheckValidRoot
= CFSTR("ValidRoot");
917 struct TrustFailures
{
919 bool unknownCritExtn
;
920 bool untrustedAnchor
;
921 bool hostnameMismatch
;
926 static void applyDetailProperty(const void *_key
, const void *_value
,
928 CFStringRef key
= (CFStringRef
)_key
;
929 struct TrustFailures
*tf
= (struct TrustFailures
*)context
;
930 if (CFGetTypeID(_value
) != CFBooleanGetTypeID()) {
931 /* Value isn't a CFBooleanRef, oh no! */
934 CFBooleanRef value
= (CFBooleanRef
)_value
;
935 if (CFBooleanGetValue(value
)) {
936 /* Not an actual failure so we don't report it. */
940 /* @@@ FIXME: Report a different return value when something is in the
941 details but masked out by an exception and use that below for display
943 if (CFEqual(key
, kSecPolicyCheckIdLinkage
)) {
944 tf
->badLinkage
= true;
945 } else if (CFEqual(key
, kSecPolicyCheckCriticalExtensions
)
946 || CFEqual(key
, kSecPolicyCheckQualifiedCertStatements
)) {
947 tf
->unknownCritExtn
= true;
948 } else if (CFEqual(key
, kSecPolicyCheckAnchorTrusted
)
949 || CFEqual(key
, kSecPolicyCheckAnchorSHA1
)) {
950 tf
->untrustedAnchor
= true;
951 } else if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
952 tf
->hostnameMismatch
= true;
953 } else if (CFEqual(key
, kSecPolicyCheckValidIntermediates
)
954 || CFEqual(key
, kSecPolicyCheckValidLeaf
)
955 || CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
956 tf
->invalidCert
= true;
958 /* Anything else is a policy failure. */
960 if (CFEqual(key
, kSecPolicyCheckNonEmptySubject
)
961 || CFEqual(key
, kSecPolicyCheckBasicContraints
)
962 || CFEqual(key
, kSecPolicyCheckKeyUsage
)
963 || CFEqual(key
, kSecPolicyCheckExtendedKeyUsage
)
964 || CFEqual(key
, kSecPolicyCheckIssuerCommonName
)
965 || CFEqual(key
, kSecPolicyCheckSubjectCommonNamePrefix
)
966 || CFEqual(key
, kSecPolicyCheckChainLength
)
967 || CFEqual(key
, kSecPolicyCheckNotValidBefore
))
970 tf
->policyFail
= true;
974 static void appendError(CFMutableArrayRef properties
, CFStringRef error
) {
975 CFStringRef localizedError
= SecFrameworkCopyLocalizedString(error
,
976 CFSTR("SecCertificate"));
977 appendProperty(properties
, kSecPropertyTypeError
, NULL
, NULL
,
979 CFReleaseNull(localizedError
);
982 CFArrayRef
SecTrustCopyProperties(SecTrustRef trust
) {
983 CFArrayRef details
= SecTrustGetDetails(trust
);
987 struct TrustFailures tf
= {};
989 CFIndex ix
, count
= CFArrayGetCount(details
);
990 for (ix
= 0; ix
< count
; ++ix
) {
991 CFDictionaryRef detail
= (CFDictionaryRef
)
992 CFArrayGetValueAtIndex(details
, ix
);
993 /* We now have a detail dictionary for certificate at index ix, with
994 a key value pair for each failed policy check. Let's convert it
995 from Ro-Man form into something a Hu-Man can understand. */
996 CFDictionaryApplyFunction(detail
, applyDetailProperty
, &tf
);
999 CFMutableArrayRef properties
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
1000 &kCFTypeArrayCallBacks
);
1001 /* The badLinkage and unknownCritExtn failures are short circuited, since
1002 you can't recover from those errors. */
1003 if (tf
.badLinkage
) {
1004 appendError(properties
, CFSTR("Invalid certificate chain linkage."));
1005 } else if (tf
.unknownCritExtn
) {
1006 appendError(properties
, CFSTR("One or more unsupported critical extensions found."));
1008 if (tf
.untrustedAnchor
) {
1009 appendError(properties
, CFSTR("Root certificate is not trusted."));
1011 if (tf
.hostnameMismatch
) {
1012 appendError(properties
, CFSTR("Hostname mismatch."));
1014 if (tf
.policyFail
) {
1015 appendError(properties
, CFSTR("Policy requirements not met."));
1017 if (tf
.invalidCert
) {
1018 appendError(properties
, CFSTR("One or more certificates have expired or are not valid yet."));
1022 if (CFArrayGetCount(properties
) == 0) {
1023 /* The certificate chain is trusted, return an empty plist */
1024 CFReleaseNull(properties
);
1030 CFDictionaryRef
SecTrustCopyResult(SecTrustRef trust
) {
1031 // Builds and returns a dictionary of evaluation results.
1035 CFMutableDictionaryRef results
= CFDictionaryCreateMutable(NULL
, 0,
1036 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
1038 // kSecTrustResultDetails (per-cert results)
1039 CFArrayRef details
= SecTrustGetDetails(trust
);
1041 CFDictionarySetValue(results
, (const void *)kSecTrustResultDetails
, (const void *)details
);
1044 // kSecTrustResultValue (overall trust result)
1045 CFNumberRef numValue
= CFNumberCreate(NULL
, kCFNumberSInt32Type
, &trust
->_trustResult
);
1047 CFDictionarySetValue(results
, (const void *)kSecTrustResultValue
, (const void *)numValue
);
1048 CFRelease(numValue
);
1050 if (trust
->_trustResult
== kSecTrustResultInvalid
|| !trust
->_info
)
1051 return results
; // we have nothing more to add
1053 // kSecTrustEvaluationDate
1054 CFDateRef evaluationDate
= trust
->_verifyDate
;
1055 if (evaluationDate
) {
1056 CFDictionarySetValue(results
, (const void *)kSecTrustEvaluationDate
, (const void *)evaluationDate
);
1059 // kSecTrustExtendedValidation, kSecTrustOrganizationName
1060 CFDictionaryRef info
= trust
->_info
;
1062 CFBooleanRef evValue
;
1063 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoExtendedValidationKey
, (const void **)&evValue
)) {
1064 CFDictionarySetValue(results
, (const void *)kSecTrustExtendedValidation
, (const void *)evValue
);
1066 CFStringRef organizationName
;
1067 if (CFDictionaryGetValueIfPresent(info
, kSecTrustInfoCompanyNameKey
, (const void **)&organizationName
)) {
1068 CFDictionarySetValue(results
, (const void *)kSecTrustOrganizationName
, (const void *)organizationName
);
1073 //FIXME: need to add revocation results here
1074 // kSecTrustRevocationChecked, kSecTrustRevocationValidUntilDate
1075 CFTypeRef expirationDate
;
1076 if (CFDictionaryGetValueIfPresent(mExtendedResult
, kSecTrustExpirationDate
, (const void **)&expirationDate
)) {
1077 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationValidUntilDate
, (const void *)expirationDate
);
1078 CFDictionarySetValue(results
, (const void *)kSecTrustRevocationChecked
, (const void *)kCFBooleanTrue
);
1085 // Return 0 upon error.
1086 static int to_int_error_request(enum SecXPCOperation op
, CFErrorRef
*error
) {
1087 __block
int64_t result
= 0;
1088 securityd_send_sync_and_do(op
, error
, NULL
, ^bool(xpc_object_t response
, CFErrorRef
*error
) {
1089 result
= xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1091 return SecError(errSecInternal
, error
, CFSTR("int64 missing in response"));
1097 // version 0 -> error, so we need to start at version 1 or later.
1098 OSStatus
SecTrustGetOTAPKIAssetVersionNumber(int* versionNumber
)
1100 return SecOSStatusWith(^bool(CFErrorRef
*error
) {
1102 return SecError(errSecParam
, error
, CFSTR("versionNumber is NULL"));
1104 return (*versionNumber
= SECURITYD_XPC(sec_ota_pki_asset_version
, to_int_error_request
, error
)) != 0;
1108 #define do_if_registered(sdp, ...) if (gSecurityd && gSecurityd->sdp) { return gSecurityd->sdp(__VA_ARGS__); }
1110 static bool xpc_dictionary_entry_is_type(xpc_object_t dictionary
, const char *key
, xpc_type_t type
)
1112 xpc_object_t value
= xpc_dictionary_get_value(dictionary
, key
);
1114 return value
&& (xpc_get_type(value
) == type
);
1117 OSStatus
SecTrustOTAPKIGetUpdatedAsset(int* didUpdateAsset
)
1119 CFErrorRef error
= NULL
;
1120 do_if_registered(sec_ota_pki_get_new_asset
, &error
);
1123 xpc_object_t message
= securityd_create_message(kSecXPCOpOTAPKIGetNewAsset
, &error
);
1126 xpc_object_t response
= securityd_message_with_reply_sync(message
, &error
);
1128 if (response
&& xpc_dictionary_entry_is_type(response
, kSecXPCKeyResult
, XPC_TYPE_INT64
))
1130 num
= (int64_t) xpc_dictionary_get_int64(response
, kSecXPCKeyResult
);
1131 xpc_release(response
);
1134 xpc_release(message
);
1137 if (NULL
!= didUpdateAsset
)
1139 *didUpdateAsset
= (int)num
;
1147 // MARK: SecTrustNode
1148 /********************************************************
1149 **************** SecTrustNode object *******************
1150 ********************************************************/
1151 typedef uint8_t SecFetchingState
;
1153 kSecFetchingStatePassedIn
= 0,
1154 kSecFetchingStateLocal
,
1155 kSecFetchingStateFromURL
,
1156 kSecFetchingStateDone
,
1159 typedef uint8_t SecTrustState
;
1161 kSecTrustStateUnknown
= 0,
1162 kSecTrustStateNotSigner
,
1163 kSecTrustStateValidSigner
,
1166 typedef struct __SecTrustNode
*SecTrustNodeRef
;
1167 struct __SecTrustNode
{
1168 SecTrustNodeRef _child
;
1169 SecCertificateRef _certificate
;
1171 /* Cached information about _certificate */
1175 /* Set of all certificates we have ever considered as a parent. We use
1176 this to avoid having to recheck certs when we go to the next phase. */
1177 CFMutableSet _certificates
;
1179 /* Parents that are still partial chains we haven't yet considered. */
1180 CFMutableSet _partials
;
1181 /* Parents that are still partial chains we have rejected. We reconsider
1182 these if we get to the final phase and we still haven't found a valid
1184 CFMutableSet _rejected_partials
;
1186 /* Parents that are complete chains we haven't yet considered. */
1187 CFMutableSet _candidates
;
1188 /* Parents that are complete chains we have rejected. */
1189 CFMutableSet _rejected_candidates
;
1191 /* State of candidate fetching. */
1192 SecFetchingState _fetchingState
;
1194 /* Trust state of _candidates[_candidateIndex] */
1195 SecTrustState _trustState
;
1197 typedef struct __SecTrustNode SecTrustNode
;
1199 /* CFRuntime registration data. */
1200 static pthread_once_t kSecTrustNodeRegisterClass
= PTHREAD_ONCE_INIT
;
1201 static CFTypeID kSecTrustNodeTypeID
= _kCFRuntimeNotATypeID
;
1203 /* Forward declarations of static functions. */
1204 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
);
1205 static void SecTrustNodeDestroy(CFTypeRef cf
);
1207 /* Static functions. */
1208 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
) {
1209 SecTrustNodeRef node
= (SecTrustNodeRef
)cf
;
1210 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
1211 CFSTR("<SecTrustNodeRef: %p>"), node
);
1214 static void SecTrustNodeDestroy(CFTypeRef cf
) {
1215 SecTrustNodeRef trust
= (SecTrustNodeRef
)cf
;
1216 if (trust
->_child
) {
1217 free(trust
->_child
);
1219 if (trust
->_certificate
) {
1220 free(trust
->_certificate
);
1222 if (trust
->_candidates
) {
1223 CFRelease(trust
->_candidates
);
1227 static void SecTrustNodeRegisterClass(void) {
1228 static const CFRuntimeClass kSecTrustNodeClass
= {
1230 "SecTrustNode", /* class name */
1233 SecTrustNodeDestroy
, /* dealloc */
1236 NULL
, /* copyFormattingDesc */
1237 SecTrustNodeDescribe
/* copyDebugDesc */
1240 kSecTrustNodeTypeID
= _CFRuntimeRegisterClass(&kSecTrustNodeClass
);
1243 /* SecTrustNode API functions. */
1244 CFTypeID
SecTrustNodeGetTypeID(void) {
1245 pthread_once(&kSecTrustNodeRegisterClass
, SecTrustNodeRegisterClass
);
1246 return kSecTrustNodeTypeID
;
1249 SecTrustNodeRef
SecTrustNodeCreate(SecTrustRef trust
,
1250 SecCertificateRef certificate
, SecTrustNodeRef child
) {
1251 CFAllocatorRef allocator
= kCFAllocatorDefault
;
1255 CFIndex size
= sizeof(struct __SecTrustNode
);
1256 SecTrustNodeRef result
= (SecTrustNodeRef
)_CFRuntimeCreateInstance(
1257 allocator
, SecTrustNodeGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
1261 memset((char*)result
+ sizeof(result
->_base
), 0,
1262 sizeof(*result
) - sizeof(result
->_base
));
1265 result
->_child
= child
;
1267 CFRetain(certificate
);
1268 result
->_certificate
= certificate
;
1269 result
->_isAnchor
= SecTrustCertificateIsAnchor(certificate
);
1274 SecCertificateRef
SecTrustGetCertificate(SecTrustNodeRef node
) {
1276 return node
->_certificate
;
1279 CFArrayRef
SecTrustNodeCopyProperties(SecTrustNodeRef node
,
1280 SecTrustRef trust
) {
1283 CFMutableArrayRef summary
= SecCertificateCopySummaryProperties(
1284 node
->_certificate
, SecTrustGetVerifyTime(trust
));
1285 /* FIXME Add more details in the failure case. */
1289 /* Attempt to verify this node's signature chain down to the child. */
1290 SecTrustState
SecTrustNodeVerifySignatureChain(SecTrustNodeRef node
) {
1292 return kSecTrustStateUnknown
;
1296 /* See if the next candidate works. */
1297 SecTrustNodeRef
SecTrustNodeCopyNextCandidate(SecTrustNodeRef node
,
1298 SecTrustRef trust
, SecFetchingState fetchingState
) {
1302 CFAbsoluteTime verifyTime
= SecTrustGetVerifyTime(trust
);
1305 /* If we have any unconsidered candidates left check those first. */
1306 while (node
->_candidateIndex
< CFArrayGetCount(node
->_candidates
)) {
1307 SecCertificateRef candidate
= (SecCertificateRef
)
1308 CFArrayGetValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1309 if (node
->_fetchingState
!= kSecFetchingStateDone
) {
1310 /* If we still have potential sources to fetch other candidates
1311 from we ignore expired candidates. */
1312 if (!SecCertificateIsValidOn(candidate
, verifyTime
)) {
1313 node
->_candidateIndex
++;
1318 SecTrustNodeRef parent
= SecTrustNodeCreate(candidate
, node
);
1319 CFArrayRemoveValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
1320 if (SecTrustNodeVerifySignatureChain(parent
) ==
1321 kSecTrustStateNotSigner
) {
1322 /* This candidate parent is not a valid signer of its
1325 /* If another signature failed further down the chain we need
1326 to backtrack down to whatever child is still a valid
1327 candidate and has additional candidates to consider.
1328 @@@ We really want to make the fetchingState a global of
1329 SecTrust itself as well and not have any node go beyond the
1330 current state of SecTrust if there are other (read cheap)
1331 options to consider. */
1337 /* We've run out of candidates in our current state so let's try to
1338 find some more. Note we fetch candidates in increasing order of
1339 cost in the hope we won't ever get to the more expensive fetching
1341 SecCertificateRef certificate
= node
->_certificate
;
1342 switch (node
->_fetchingState
) {
1343 case kSecFetchingStatePassedIn
:
1344 /* Get the list of candidates from SecTrust. */
1345 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1347 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1350 SecCertificateGetNormalizedIssuerContent(certificate
);
1351 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1353 node
->_fetchingState
= kSecFetchingStateLocal
;
1355 case kSecFetchingStateLocal
:
1356 /* Lookup candidates in the local database. */
1357 node
->_fetchingState
= kSecFetchingStateFromURL
;
1359 case kSecFetchingStateFromURL
:
1360 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1362 case kSecFetchingStateCheckExpired
:
1363 /* Time to start considering expired candidates as well. */
1364 node
->_candidateIndex
= 0;
1365 node
->_fetchingState
= kSecFetchingStateDone
;
1367 case kSecFetchingStateDone
;
1372 CFAllocatorRef allocator
= CFGetAllocator(node
);
1374 /* A trust node has a number of states.
1375 1) Look for issuing certificates by asking SecTrust about known
1376 parent certificates.
1377 2) Look for issuing certificates in certificate databases (keychains)
1378 3) Look for issuing certificates by going out to the web if the nodes
1379 certificate has a issuer location URL.
1380 4) Look through expired or not yet valid candidates we have put aside.
1382 We go though the stages 1 though 3 looking for candidate issuer
1383 certificates. If a candidate certificate is not valid at verifyTime
1384 we put it in a to be examined later queue. If a candidate certificate
1385 is valid we verify if it actually signed our certificate (if possible).
1386 If not we discard it and continue on to the next candidate certificate.
1387 If it is we return a new SecTrustNodeRef for that certificate. */
1389 CFMutableArrayRef issuers
= CFArrayCreateMutable(allocator
, 0,
1390 &kCFTypeArrayCallBacks
);
1392 /* Find a node's parent. */
1393 certificate
= node
->_certificate
;
1394 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1395 CFTypeRef candidates
= NULL
;
1397 candidates
= (CFTypeRef
)CFDictionaryGetValueForKey(skidDict
, akid
);
1399 addValidIssuersFrom(issuers
, certificate
, candidates
, true);
1404 SecCertificateGetNormalizedIssuerContent(certificate
);
1405 candidates
= (CFTypeRef
)
1406 CFDictionaryGetValueForKey(subjectDict
, issuer
);
1407 addValidIssuersFrom(issuers
, certificate
, candidates
, false);
1410 if (CFArrayGetCount(issuers
) == 0) {
1411 /* O no! we can't find an issuer for this certificate. Let's see
1412 if we can find one in the local database. */
1415 return errSecSuccess
;
1418 CFArrayRef
SecTrustNodeCopyNextChain(SecTrustNodeRef node
,
1419 SecTrustRef trust
) {
1420 /* Return the next full chain that isn't a reject unless we are in
1421 a state where we consider returning rejects. */
1423 switch (node
->_fetchingState
) {
1424 case kSecFetchingStatePassedIn
:
1425 /* Get the list of candidates from SecTrust. */
1426 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1428 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1431 SecCertificateGetNormalizedIssuerContent(certificate
);
1432 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1434 node
->_fetchingState
= kSecFetchingStateLocal
;
1436 case kSecFetchingStateLocal
:
1437 /* Lookup candidates in the local database. */
1438 node
->_fetchingState
= kSecFetchingStateFromURL
;
1440 case kSecFetchingStateFromURL
:
1441 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1443 case kSecFetchingStateCheckExpired
:
1444 /* Time to start considering expired candidates as well. */
1445 node
->_candidateIndex
= 0;
1446 node
->_fetchingState
= kSecFetchingStateDone
;
1448 case kSecFetchingStateDone
;
1454 Iterator
parentIterator(Cert
);
1460 static bool unique(Node node
) {
1461 if (nodes
.contains(node
))
1467 static bool isAnchor(Cert cert
);
1474 Iterator parentIterator
; /* For current source of parents. */
1476 Node(Cert inCert
) : child(nil
), cert(inCert
), nextSource(0) {}
1477 Node(Node inChild
, Cert inCert
) : child(inChild
), cert(inCert
),
1480 CertPath
certPath() {
1484 path
.add(node
.cert
);
1490 void contains(Cert cert
) {
1493 if (cert
== node
.cert
)
1500 Node
nextParent(Array currentSources
) {
1503 parentIterator
== currentSources
[nextSource
- 1].end()) {
1504 if (nextSource
== currentSources
.count
) {
1505 /* We ran out of parent sources. */
1508 parentIterator
= currentSources
[nextSource
++].begin();
1510 Certificate cert
= *parentIterator
++;
1511 /* Check for cycles and self signed chains. */
1512 if (!contains(cert
)) {
1513 Node node
= Node(this, parent
);
1514 if (!NodeCache
.unique(node
))
1525 Array currentSources
;
1531 PathBuilder(Cert cert
) {
1532 nodes
.append(Node(cert
));
1533 nit
= nodes
.begin();
1534 sourceIT
= allSources
.begin();
1537 nextAnchoredPath() {
1538 if (nit
== nodes
.end()) {
1539 /* We should add another source to the list of sources to
1541 if (sourceIT
== allSources
.end()) {
1542 /* No more sources to add. */
1544 currentSources
+= *sourceIT
++;
1545 /* Resort nodes by size. */
1547 nit
= nodes
.begin();
1548 /* Set the source list for all nodes. */
1551 while (Node node
= *nit
) {
1552 Node candidate
= node
.nextParent(currentSources
);
1554 /* The current node has no more candidate parents so move
1560 if (candidate
.isAnchored
) {
1561 candidates
.append(candidate
);
1563 nodes
.insert(candidate
, nit
);
1568 while (Node node
= nextAnchoredPath()) {
1569 if (node
.isValid()) {
1570 chain
= node
.certPath
;
1573 rejects
.append(node
);