2 * Copyright (c) 2006-2010 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/SecItem.h>
30 #include <Security/SecCertificateInternal.h>
31 #include <Security/SecCertificatePath.h>
32 #include <Security/SecFramework.h>
33 #include <Security/SecPolicyInternal.h>
34 #include <CoreFoundation/CFRuntime.h>
35 #include <CoreFoundation/CFSet.h>
36 #include <CoreFoundation/CFString.h>
37 #include <CoreFoundation/CFNumber.h>
38 #include <CoreFoundation/CFArray.h>
39 #include <CoreFoundation/CFPropertyList.h>
40 #include <AssertMacros.h>
45 #include <MacErrors.h>
46 #include "SecRSAKey.h"
47 #include <libDER/oids.h>
48 #include <security_utilities/debugging.h>
49 #include <Security/SecInternal.h>
50 #include <ipc/securityd_client.h>
51 #include "securityd_server.h"
53 CFStringRef kSecTrustInfoExtendedValidationKey
= CFSTR("ExtendedValidation");
54 CFStringRef kSecTrustInfoCompanyNameKey
= CFSTR("CompanyName");
55 CFStringRef kSecTrustInfoRevocationKey
= CFSTR("Revocation");
56 CFStringRef kSecTrustInfoRevocationValidUntilKey
=
57 CFSTR("RevocationValidUntil");
61 /********************************************************
62 ****************** SecTrust object *********************
63 ********************************************************/
66 CFArrayRef _certificates
;
69 CFDateRef _verifyDate
;
70 SecCertificatePathRef _chain
;
73 CFDictionaryRef _info
;
74 CFArrayRef _exceptions
;
76 /* If true we don't trust any anchors other than the ones in _anchors. */
80 /* CFRuntime regsitration data. */
81 static pthread_once_t kSecTrustRegisterClass
= PTHREAD_ONCE_INIT
;
82 static CFTypeID kSecTrustTypeID
= _kCFRuntimeNotATypeID
;
84 /* Forward declartions of static functions. */
85 static CFStringRef
SecTrustDescribe(CFTypeRef cf
);
86 static void SecTrustDestroy(CFTypeRef cf
);
88 /* Static functions. */
89 static CFStringRef
SecTrustDescribe(CFTypeRef cf
) {
90 SecTrustRef certificate
= (SecTrustRef
)cf
;
91 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
92 CFSTR("<SecTrustRef: %p>"), certificate
);
95 static void SecTrustDestroy(CFTypeRef cf
) {
96 SecTrustRef trust
= (SecTrustRef
)cf
;
97 CFReleaseSafe(trust
->_certificates
);
98 CFReleaseSafe(trust
->_policies
);
99 CFReleaseSafe(trust
->_verifyDate
);
100 CFReleaseSafe(trust
->_anchors
);
101 CFReleaseSafe(trust
->_chain
);
102 CFReleaseSafe(trust
->_publicKey
);
103 CFReleaseSafe(trust
->_details
);
104 CFReleaseSafe(trust
->_info
);
105 CFReleaseSafe(trust
->_exceptions
);
108 static void SecTrustRegisterClass(void) {
109 static const CFRuntimeClass kSecTrustClass
= {
111 "SecTrust", /* class name */
114 SecTrustDestroy
, /* dealloc */
117 NULL
, /* copyFormattingDesc */
118 SecTrustDescribe
/* copyDebugDesc */
121 kSecTrustTypeID
= _CFRuntimeRegisterClass(&kSecTrustClass
);
124 /* Public API functions. */
125 CFTypeID
SecTrustGetTypeID(void) {
126 pthread_once(&kSecTrustRegisterClass
, SecTrustRegisterClass
);
127 return kSecTrustTypeID
;
130 OSStatus
SecTrustCreateWithCertificates(CFTypeRef certificates
,
131 CFTypeRef policies
, SecTrustRef
*trustRef
) {
132 OSStatus status
= errSecParam
;
133 CFAllocatorRef allocator
= kCFAllocatorDefault
;
134 CFArrayRef l_certs
= NULL
, l_policies
= NULL
;
135 SecTrustRef result
= NULL
;
139 CFTypeID certType
= CFGetTypeID(certificates
);
140 if (certType
== CFArrayGetTypeID()) {
141 /* We need at least 1 certificate. */
142 require_quiet(CFArrayGetCount(certificates
) > 0, errOut
);
143 l_certs
= CFArrayCreateCopy(allocator
, certificates
);
144 } else if (certType
== SecCertificateGetTypeID()) {
145 l_certs
= CFArrayCreate(allocator
, &certificates
, 1,
146 &kCFTypeArrayCallBacks
);
151 status
= errSecAllocate
;
156 CFTypeRef policy
= SecPolicyCreateBasicX509();
157 l_policies
= CFArrayCreate(allocator
, &policy
, 1,
158 &kCFTypeArrayCallBacks
);
160 } else if (CFGetTypeID(policies
) == CFArrayGetTypeID()) {
161 l_policies
= CFArrayCreateCopy(allocator
, policies
);
162 } else if (CFGetTypeID(policies
) == SecPolicyGetTypeID()) {
163 l_policies
= CFArrayCreate(allocator
, &policies
, 1,
164 &kCFTypeArrayCallBacks
);
169 status
= errSecAllocate
;
173 CFIndex size
= sizeof(struct __SecTrust
);
174 require_quiet(result
= (SecTrustRef
)_CFRuntimeCreateInstance(allocator
,
175 SecTrustGetTypeID(), size
- sizeof(CFRuntimeBase
), 0), errOut
);
176 memset((char*)result
+ sizeof(result
->_base
), 0,
177 sizeof(*result
) - sizeof(result
->_base
));
182 CFReleaseSafe(result
);
183 CFReleaseSafe(l_certs
);
184 CFReleaseSafe(l_policies
);
186 result
->_certificates
= l_certs
;
187 result
->_policies
= l_policies
;
193 OSStatus
SecTrustSetAnchorCertificatesOnly(SecTrustRef trust
,
194 Boolean anchorCertificatesOnly
) {
196 trust
->_anchorsOnly
= anchorCertificatesOnly
;
198 /* FIXME changing this options should (potentially) invalidate the chain. */
202 OSStatus
SecTrustSetAnchorCertificates(SecTrustRef trust
,
203 CFArrayRef anchorCertificates
) {
205 check(anchorCertificates
);
206 CFRetain(anchorCertificates
);
208 CFRelease(trust
->_anchors
);
209 trust
->_anchors
= anchorCertificates
;
210 trust
->_anchorsOnly
= true;
212 /* FIXME changing the anchor set should invalidate the chain. */
216 OSStatus
SecTrustSetVerifyDate(SecTrustRef trust
, CFDateRef verifyDate
) {
219 CFRetain(verifyDate
);
220 if (trust
->_verifyDate
)
221 CFRelease(trust
->_verifyDate
);
222 trust
->_verifyDate
= verifyDate
;
224 /* FIXME changing the verifydate should invalidate the chain. */
228 CFAbsoluteTime
SecTrustGetVerifyTime(SecTrustRef trust
) {
229 CFAbsoluteTime verifyTime
;
230 if (trust
->_verifyDate
) {
231 verifyTime
= CFDateGetAbsoluteTime(trust
->_verifyDate
);
233 verifyTime
= CFAbsoluteTimeGetCurrent();
234 /* Record the verifyDate we ended up using. */
235 trust
->_verifyDate
= CFDateCreate(CFGetAllocator(trust
), verifyTime
);
241 CFArrayRef
SecTrustGetDetails(SecTrustRef trust
) {
242 return trust
->_details
;
245 static CFStringRef kSecCertificateDetailSHA1Digest
= CFSTR("SHA1Digest");
247 static CFDictionaryRef
SecTrustGetExceptionForCertificateAtIndex(SecTrustRef trust
, CFIndex ix
) {
248 if (!trust
->_exceptions
|| ix
>= CFArrayGetCount(trust
->_exceptions
))
250 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(trust
->_exceptions
, ix
);
251 if (CFGetTypeID(exception
) != CFDictionaryGetTypeID())
254 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
258 /* If the exception contains the current certificates sha1Digest in the
259 kSecCertificateDetailSHA1Digest key then we use it otherwise we ignore it. */
260 CFDataRef sha1Digest
= SecCertificateGetSHA1Digest(certificate
);
261 CFTypeRef digestValue
= CFDictionaryGetValue(exception
, kSecCertificateDetailSHA1Digest
);
262 if (!digestValue
|| !CFEqual(sha1Digest
, digestValue
))
268 struct SecTrustCheckExceptionContext
{
269 CFDictionaryRef exception
;
270 bool exceptionNotFound
;
273 static void SecTrustCheckException(const void *key
, const void *value
, void *context
) {
274 struct SecTrustCheckExceptionContext
*cec
= (struct SecTrustCheckExceptionContext
*)context
;
275 if (cec
->exception
) {
276 CFTypeRef exceptionValue
= CFDictionaryGetValue(cec
->exception
, key
);
277 if (!exceptionValue
|| !CFEqual(value
, exceptionValue
)) {
278 cec
->exceptionNotFound
= true;
281 cec
->exceptionNotFound
= true;
285 OSStatus
SecTrustEvaluate(SecTrustRef trust
, SecTrustResultType
*result
) {
286 CFMutableDictionaryRef args_in
= NULL
;
287 CFTypeRef args_out
= NULL
;
292 CFReleaseNull(trust
->_chain
);
293 CFReleaseNull(trust
->_details
);
294 CFReleaseNull(trust
->_info
);
296 args_in
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
297 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
299 /* Translate certificates to CFDataRefs. */
300 CFArrayRef certificates
= SecCertificateArrayCopyDataArray(trust
->_certificates
);
301 CFDictionaryAddValue(args_in
, kSecTrustCertificatesKey
, certificates
);
302 CFRelease(certificates
);
304 if (trust
->_anchors
) {
305 /* Translate anchors to CFDataRefs. */
306 CFArrayRef anchors
= SecCertificateArrayCopyDataArray(trust
->_anchors
);
307 CFDictionaryAddValue(args_in
, kSecTrustAnchorsKey
, anchors
);
310 if (trust
->_anchorsOnly
)
311 CFDictionaryAddValue(args_in
, kSecTrustAnchorsOnlyKey
, kCFBooleanTrue
);
313 /* Translate policies to plist capable CFTypeRefs. */
314 CFArrayRef serializedPolicies
= SecPolicyArraySerialize(trust
->_policies
);
315 CFDictionaryAddValue(args_in
, kSecTrustPoliciesKey
, serializedPolicies
);
316 CFRelease(serializedPolicies
);
318 /* Ensure trust->_verifyDate is initialized. */
319 SecTrustGetVerifyTime(trust
);
320 CFDictionaryAddValue(args_in
, kSecTrustVerifyDateKey
, trust
->_verifyDate
);
322 /* @@@ 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. */
323 //status = SECURITYD(sec_trust_evaluate, args_in, &args_out);
325 status
= gSecurityd
->sec_trust_evaluate(args_in
, &args_out
);
327 status
= ServerCommandSendReceive(sec_trust_evaluate_id
,
330 if (status
== errSecNotAvailable
&& CFArrayGetCount(trust
->_certificates
)) {
331 /* We failed to talk to securityd. The only time this should
332 happen is when we are running prior to launchd enabling
333 registration of services. This currently happens when we
334 are running from the ramdisk. To make ASR happy we initialize
335 _chain and return success with a failure as the trustResult, to
336 make it seem like we did a cert evaluation, so ASR can extract
337 the public key from the leaf. */
338 trust
->_chain
= SecCertificatePathCreate(NULL
,
339 (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0));
341 *result
= kSecTrustResultOtherError
;
346 require_quiet(args_out
, errOut
);
348 CFArrayRef details
= (CFArrayRef
)CFDictionaryGetValue(args_out
, kSecTrustDetailsKey
);
350 require(CFGetTypeID(details
) == CFArrayGetTypeID(), errOut
);
352 trust
->_details
= details
;
355 CFDictionaryRef info
= (CFDictionaryRef
)CFDictionaryGetValue(args_out
, kSecTrustInfoKey
);
357 require(CFGetTypeID(info
) == CFDictionaryGetTypeID(), errOut
);
362 CFArrayRef chainArray
= (CFArrayRef
)CFDictionaryGetValue(args_out
, kSecTrustChainKey
);
363 require(chainArray
&& CFGetTypeID(chainArray
) == CFArrayGetTypeID(), errOut
);
364 trust
->_chain
= SecCertificatePathCreateWithArray(chainArray
);
365 require(trust
->_chain
, errOut
);
367 CFNumberRef cfResult
= (CFNumberRef
)CFDictionaryGetValue(args_out
, kSecTrustResultKey
);
368 require(cfResult
&& CFGetTypeID(cfResult
) == CFNumberGetTypeID(), errOut
);
371 CFNumberGetValue(cfResult
, kCFNumberSInt32Type
, &trustResult
);
372 if (trustResult
== kSecTrustResultUnspecified
) {
373 /* If leaf is in exceptions -> proceed, otherwise unspecified. */
374 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
375 trustResult
= kSecTrustResultProceed
;
376 } else if (trustResult
== kSecTrustResultRecoverableTrustFailure
) {
377 /* If we have exceptions get details and match to exceptions. */
378 CFIndex pathLength
= CFArrayGetCount(details
);
379 struct SecTrustCheckExceptionContext context
= {};
381 for (ix
= 0; ix
< pathLength
; ++ix
) {
382 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
383 if ((ix
== 0) && CFDictionaryContainsKey(detail
, kSecPolicyCheckBlackListedLeaf
))
384 trustResult
= kSecTrustResultFatalTrustFailure
;
385 context
.exception
= SecTrustGetExceptionForCertificateAtIndex(trust
, ix
);
386 CFDictionaryApplyFunction(detail
, SecTrustCheckException
, &context
);
387 if (context
.exceptionNotFound
) {
392 if (!context
.exceptionNotFound
)
393 trustResult
= kSecTrustResultProceed
;
396 *result
= trustResult
;
400 CFReleaseSafe(args_out
);
401 CFReleaseSafe(args_in
);
406 SecKeyRef
SecTrustCopyPublicKey(SecTrustRef trust
) {
407 if (!trust
->_publicKey
&& trust
->_chain
) {
408 trust
->_publicKey
= SecCertificatePathCopyPublicKeyAtIndex(
411 if (trust
->_publicKey
)
412 CFRetain(trust
->_publicKey
);
414 return trust
->_publicKey
;
417 CFIndex
SecTrustGetCertificateCount(SecTrustRef trust
) {
418 return trust
->_chain
? SecCertificatePathGetCount(trust
->_chain
) : 1;
421 SecCertificateRef
SecTrustGetCertificateAtIndex(SecTrustRef trust
,
424 return SecCertificatePathGetCertificateAtIndex(trust
->_chain
, ix
);
426 return (SecCertificateRef
)CFArrayGetValueAtIndex(trust
->_certificates
, 0);
431 CFDictionaryRef
SecTrustCopyInfo(SecTrustRef trust
) {
432 CFDictionaryRef info
= trust
->_info
;
438 CFDataRef
SecTrustCopyExceptions(SecTrustRef trust
) {
440 CFArrayRef details
= SecTrustGetDetails(trust
);
441 CFIndex pathLength
= CFArrayGetCount(details
);
442 CFMutableArrayRef exceptions
= CFArrayCreateMutable(kCFAllocatorDefault
, pathLength
, &kCFTypeArrayCallBacks
);
444 for (ix
= 0; ix
< pathLength
; ++ix
) {
445 CFDictionaryRef detail
= (CFDictionaryRef
)CFArrayGetValueAtIndex(details
, ix
);
446 CFIndex detailCount
= CFDictionaryGetCount(detail
);
447 CFMutableDictionaryRef exception
;
448 if (ix
== 0 || detailCount
> 0) {
449 exception
= CFDictionaryCreateMutableCopy(kCFAllocatorDefault
, detailCount
+ 1, detail
);
450 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
451 CFDataRef digest
= SecCertificateGetSHA1Digest(certificate
);
452 CFDictionaryAddValue(exception
, kSecCertificateDetailSHA1Digest
, digest
);
454 /* Add empty exception dictionaries for non leaf certs which have no exceptions to save space. */
455 exception
= (CFMutableDictionaryRef
)CFDictionaryCreate(kCFAllocatorDefault
, NULL
, NULL
, 0,
456 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
458 CFArrayAppendValue(exceptions
, exception
);
459 CFRelease(exception
);
462 /* Remove any trailing empty dictionaries to save even more space (we skip the leaf
463 since it will never be empty). */
464 for (ix
= pathLength
- 1; ix
> 0; --ix
) {
465 CFDictionaryRef exception
= (CFDictionaryRef
)CFArrayGetValueAtIndex(exceptions
, ix
);
466 if (CFDictionaryGetCount(exception
) == 0) {
467 CFArrayRemoveValueAtIndex(exceptions
, ix
);
473 CFDataRef encodedExceptions
= CFPropertyListCreateData(kCFAllocatorDefault
,
474 exceptions
, kCFPropertyListBinaryFormat_v1_0
, 0, NULL
);
475 CFRelease(exceptions
);
477 return encodedExceptions
;
480 bool SecTrustSetExceptions(SecTrustRef trust
, CFDataRef encodedExceptions
) {
481 CFArrayRef exceptions
;
482 exceptions
= CFPropertyListCreateFromXMLData(kCFAllocatorDefault
, encodedExceptions
, kCFPropertyListImmutable
, NULL
);
483 if (exceptions
&& CFGetTypeID(exceptions
) != CFArrayGetTypeID()) {
484 CFRelease(exceptions
);
488 CFReleaseSafe(trust
->_exceptions
);
489 trust
->_exceptions
= exceptions
;
491 /* If there is a valid exception entry for our current leaf we're golden. */
492 if (SecTrustGetExceptionForCertificateAtIndex(trust
, 0))
495 /* The passed in exceptions didn't match our current leaf, so we discard it. */
496 CFReleaseNull(trust
->_exceptions
);
500 CFArrayRef
SecTrustCopySummaryPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
501 CFMutableArrayRef summary
;
502 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
503 summary
= SecCertificateCopySummaryProperties(certificate
,
504 SecTrustGetVerifyTime(trust
));
505 /* FIXME Add more details in the failure case. */
510 CFArrayRef
SecTrustCopyDetailedPropertiesAtIndex(SecTrustRef trust
, CFIndex ix
) {
512 SecCertificateRef certificate
= SecTrustGetCertificateAtIndex(trust
, ix
);
513 summary
= SecCertificateCopyProperties(certificate
);
523 Can be on any non root cert in the chain.
525 Short circuit: Yes (No other errors matter after this one)
526 Non recoverable error
527 Trust UI: Invalid certificate chain linkage
528 Cert UI: Invalid linkage to parent certificate
530 CFStringRef kSecPolicyCheckIdLinkage
= CFSTR("IdLinkage");
532 /* X.509 required checks.
533 Can be on any cert in the chain
535 Short circuit: Yes (No other errors matter after this one)
536 Non recoverable error
537 Trust UI: (One or more) unsupported critical extensions found.
539 /* If we have no names for the extention oids use:
540 Cert UI: One or more unsupported critical extensions found (Non recoverable error).
541 Cert UI: Unsupported 'foo', 'bar', baz' critical extensions found.
543 CFStringRef kSecPolicyCheckCriticalExtensions
= CFSTR("CriticalExtensions");
544 /* Cert UI: Unsupported critical Qualified Certificate Statements extension found (Non recoverable error). */
545 CFStringRef kSecPolicyCheckQualifiedCertStatements
= CFSTR("QualifiedCertStatements");
546 /* Cert UI: Certificate has an empty subject (and no critial subjectAltname). */
549 Only apply to the anchor.
551 Short circuit: No (Under discussion)
553 Trust UI: Root certificate is not trusted (for this policy/app/host/whatever?)
554 Cert UI: Not a valid anchor
556 CFStringRef kSecPolicyCheckAnchorTrusted
= CFSTR("AnchorTrusted");
557 CFStringRef kSecPolicyCheckAnchorSHA1
= CFSTR("AnchorSHA1");
564 Trust UI: (Hostname|email address) mismatch
566 CFStringRef kSecPolicyCheckSSLHostname
= CFSTR("SSLHostname");
568 /* Policy specific checks.
569 Can be on any cert in the chain
573 Trust UI: Certificate chain is not valid for the current policy.
574 OR: (One or more) certificates in the chain are not valid for the current policy/application
576 CFStringRef kSecPolicyCheckNonEmptySubject
= CFSTR("NonEmptySubject");
577 /* Cert UI: Non CA certificate used as CA.
578 Cert UI: CA certificate used as leaf.
579 Cert UI: Cert chain length exceeded.
580 Cert UI: Basic constraints extension not critical (non fatal).
581 Cert UI: Leaf certificate has basic constraints extension (non fatal).
583 CFStringRef kSecPolicyCheckBasicContraints
= CFSTR("BasicContraints");
584 CFStringRef kSecPolicyCheckKeyUsage
= CFSTR("KeyUsage");
585 CFStringRef kSecPolicyCheckExtendedKeyUsage
= CFSTR("ExtendedKeyUsage");
586 /* Checks that the issuer of the leaf has exactly one Common Name and that it
587 matches the specified string. */
588 CFStringRef kSecPolicyCheckIssuerCommonName
= CFSTR("IssuerCommonName");
589 /* Checks that the leaf has exactly one Common Name and that it has the
590 specified string as a prefix. */
591 CFStringRef kSecPolicyCheckSubjectCommonNamePrefix
= CFSTR("SubjectCommonNamePrefix");
592 /* Check that the certificate chain length matches the specificed CFNumberRef
594 CFStringRef kSecPolicyCheckChainLength
= CFSTR("ChainLength");
595 CFStringRef kSecPolicyCheckNotValidBefore
= CFSTR("NotValidBefore");
598 Can be on any cert in the chain
602 Trust UI: One or more certificates have expired or are not valid yet.
603 OS: The (root|intermidate|leaf) certificate (expired on 'date'|is not valid until 'date')
604 Cert UI: Certificate (expired on 'date'|is not valid until 'date')
606 CFStringRef kSecPolicyCheckValidIntermediates
= CFSTR("ValidIntermediates");
607 CFStringRef kSecPolicyCheckValidLeaf
= CFSTR("ValidLeaf");
608 CFStringRef kSecPolicyCheckValidRoot
= CFSTR("ValidRoot");
612 struct TrustFailures
{
614 bool unknownCritExtn
;
615 bool untrustedAnchor
;
616 bool hostnameMismatch
;
621 static void applyDetailProperty(const void *_key
, const void *_value
,
623 CFStringRef key
= (CFStringRef
)_key
;
624 struct TrustFailures
*tf
= (struct TrustFailures
*)context
;
625 if (CFGetTypeID(_value
) != CFBooleanGetTypeID()) {
626 /* Value isn't a CFBooleanRef, oh no! */
629 CFBooleanRef value
= (CFBooleanRef
)_value
;
630 if (CFBooleanGetValue(value
)) {
631 /* Not an actual failure so we don't report it. */
635 /* @@@ FIXME: Report a different return value when something is in the
636 details but masked out by an exception and use that below for display
638 if (CFEqual(key
, kSecPolicyCheckIdLinkage
)) {
639 tf
->badLinkage
= true;
640 } else if (CFEqual(key
, kSecPolicyCheckCriticalExtensions
)
641 || CFEqual(key
, kSecPolicyCheckQualifiedCertStatements
)) {
642 tf
->unknownCritExtn
= true;
643 } else if (CFEqual(key
, kSecPolicyCheckAnchorTrusted
)
644 || CFEqual(key
, kSecPolicyCheckAnchorSHA1
)) {
645 tf
->untrustedAnchor
= true;
646 } else if (CFEqual(key
, kSecPolicyCheckSSLHostname
)) {
647 tf
->hostnameMismatch
= true;
648 } else if (CFEqual(key
, kSecPolicyCheckValidIntermediates
)
649 || CFEqual(key
, kSecPolicyCheckValidLeaf
)
650 || CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
651 tf
->invalidCert
= true;
653 /* Anything else is a policy failure. */
655 if (CFEqual(key
, kSecPolicyCheckNonEmptySubject
)
656 || CFEqual(key
, kSecPolicyCheckBasicContraints
)
657 || CFEqual(key
, kSecPolicyCheckKeyUsage
)
658 || CFEqual(key
, kSecPolicyCheckExtendedKeyUsage
)
659 || CFEqual(key
, kSecPolicyCheckIssuerCommonName
)
660 || CFEqual(key
, kSecPolicyCheckSubjectCommonNamePrefix
)
661 || CFEqual(key
, kSecPolicyCheckChainLength
)
662 || CFEqual(key
, kSecPolicyCheckNotValidBefore
))
665 tf
->policyFail
= true;
669 static void appendError(CFMutableArrayRef properties
, CFStringRef error
) {
670 CFStringRef localizedError
= SecFrameworkCopyLocalizedString(error
,
671 CFSTR("SecCertificate"));
672 appendProperty(properties
, kSecPropertyTypeError
, NULL
, NULL
,
676 CFArrayRef
SecTrustCopyProperties(SecTrustRef trust
) {
677 CFArrayRef details
= SecTrustGetDetails(trust
);
681 struct TrustFailures tf
= {};
683 CFIndex ix
, count
= CFArrayGetCount(details
);
684 for (ix
= 0; ix
< count
; ++ix
) {
685 CFDictionaryRef detail
= (CFDictionaryRef
)
686 CFArrayGetValueAtIndex(details
, ix
);
687 /* We now have a detail dictionary for certificate at index ix, with
688 a key value pair for each failed policy check. Let's convert it
689 from Ro-Man form into something a Hu-Man can understand. */
690 CFDictionaryApplyFunction(detail
, applyDetailProperty
, &tf
);
693 CFMutableArrayRef properties
= CFArrayCreateMutable(kCFAllocatorDefault
, 0,
694 &kCFTypeArrayCallBacks
);
695 /* The badLinkage and unknownCritExtn failures are short circuited, since
696 you can't recover from those errors. */
698 appendError(properties
, CFSTR("Invalid certificate chain linkage."));
699 } else if (tf
.unknownCritExtn
) {
700 appendError(properties
, CFSTR("One or more unsupported critical extensions found."));
702 if (tf
.untrustedAnchor
) {
703 appendError(properties
, CFSTR("Root certificate is not trusted."));
705 if (tf
.hostnameMismatch
) {
706 appendError(properties
, CFSTR("Hostname mismatch."));
709 appendError(properties
, CFSTR("Policy requirements not met."));
711 if (tf
.invalidCert
) {
712 appendError(properties
, CFSTR("One or more certificates have expired or are not valid yet."));
716 if (CFArrayGetCount(properties
) == 0) {
717 /* The certificate chain is trusted, return an empty plist */
718 CFReleaseNull(properties
);
727 #pragma mark SecTrustNode
728 /********************************************************
729 **************** SecTrustNode object *******************
730 ********************************************************/
731 typedef uint8_t SecFetchingState
;
733 kSecFetchingStatePassedIn
= 0,
734 kSecFetchingStateLocal
,
735 kSecFetchingStateFromURL
,
736 kSecFetchingStateDone
,
739 typedef uint8_t SecTrustState
;
741 kSecTrustStateUnknown
= 0,
742 kSecTrustStateNotSigner
,
743 kSecTrustStateValidSigner
,
746 typedef struct __SecTrustNode
*SecTrustNodeRef
;
747 struct __SecTrustNode
{
748 SecTrustNodeRef _child
;
749 SecCertificateRef _certificate
;
751 /* Cached information about _certificate */
755 /* Set of all certificates we have ever considered as a parent. We use
756 this to avoid having to recheck certs when we go to the next phase. */
757 CFMutableSet _certificates
;
759 /* Parents that are still partial chains we haven't yet considered. */
760 CFMutableSet _partials
;
761 /* Parents that are still partial chains we have rejected. We reconsider
762 these if we get to the final phase and we still haven't found a valid
764 CFMutableSet _rejected_partials
;
766 /* Parents that are complete chains we haven't yet considered. */
767 CFMutableSet _candidates
;
768 /* Parents that are complete chains we have rejected. */
769 CFMutableSet _rejected_candidates
;
771 /* State of candidate fetching. */
772 SecFetchingState _fetchingState
;
774 /* Trust state of _candidates[_candidateIndex] */
775 SecTrustState _trustState
;
777 typedef struct __SecTrustNode SecTrustNode
;
779 /* CFRuntime regsitration data. */
780 static pthread_once_t kSecTrustNodeRegisterClass
= PTHREAD_ONCE_INIT
;
781 static CFTypeID kSecTrustNodeTypeID
= _kCFRuntimeNotATypeID
;
783 /* Forward declartions of static functions. */
784 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
);
785 static void SecTrustNodeDestroy(CFTypeRef cf
);
787 /* Static functions. */
788 static CFStringRef
SecTrustNodeDescribe(CFTypeRef cf
) {
789 SecTrustNodeRef node
= (SecTrustNodeRef
)cf
;
790 return CFStringCreateWithFormat(kCFAllocatorDefault
, NULL
,
791 CFSTR("<SecTrustNodeRef: %p>"), node
);
794 static void SecTrustNodeDestroy(CFTypeRef cf
) {
795 SecTrustNodeRef trust
= (SecTrustNodeRef
)cf
;
799 if (trust
->_certificate
) {
800 free(trust
->_certificate
);
802 if (trust
->_candidates
) {
803 CFRelease(trust
->_candidates
);
807 static void SecTrustNodeRegisterClass(void) {
808 static const CFRuntimeClass kSecTrustNodeClass
= {
810 "SecTrustNode", /* class name */
813 SecTrustNodeDestroy
, /* dealloc */
816 NULL
, /* copyFormattingDesc */
817 SecTrustNodeDescribe
/* copyDebugDesc */
820 kSecTrustNodeTypeID
= _CFRuntimeRegisterClass(&kSecTrustNodeClass
);
823 /* SecTrustNode API functions. */
824 CFTypeID
SecTrustNodeGetTypeID(void) {
825 pthread_once(&kSecTrustNodeRegisterClass
, SecTrustNodeRegisterClass
);
826 return kSecTrustNodeTypeID
;
829 SecTrustNodeRef
SecTrustNodeCreate(SecTrustRef trust
,
830 SecCertificateRef certificate
, SecTrustNodeRef child
) {
831 CFAllocatorRef allocator
= kCFAllocatorDefault
;
835 CFIndex size
= sizeof(struct __SecTrustNode
);
836 SecTrustNodeRef result
= (SecTrustNodeRef
)_CFRuntimeCreateInstance(
837 allocator
, SecTrustNodeGetTypeID(), size
- sizeof(CFRuntimeBase
), 0);
841 memset((char*)result
+ sizeof(result
->_base
), 0,
842 sizeof(*result
) - sizeof(result
->_base
));
845 result
->_child
= child
;
847 CFRetain(certificate
);
848 result
->_certificate
= certificate
;
849 result
->_isAnchor
= SecTrustCertificateIsAnchor(certificate
);
854 SecCertificateRef
SecTrustGetCertificate(SecTrustNodeRef node
) {
856 return node
->_certificate
;
859 CFArrayRef
SecTrustNodeCopyProperties(SecTrustNodeRef node
,
863 CFMutableArrayRef summary
= SecCertificateCopySummaryProperties(
864 node
->_certificate
, SecTrustGetVerifyTime(trust
));
865 /* FIXME Add more details in the failure case. */
869 /* Attempt to verify this node's signature chain down to the child. */
870 SecTrustState
SecTrustNodeVerifySignatureChain(SecTrustNodeRef node
) {
872 return kSecTrustStateUnknown
;
876 /* See if the next candidate works. */
877 SecTrustNodeRef
SecTrustNodeCopyNextCandidate(SecTrustNodeRef node
,
878 SecTrustRef trust
, SecFetchingState fetchingState
) {
882 CFAbsoluteTime verifyTime
= SecTrustGetVerifyTime(trust
);
885 /* If we have any unconsidered candidates left check those first. */
886 while (node
->_candidateIndex
< CFArrayGetCount(node
->_candidates
)) {
887 SecCertificateRef candidate
= (SecCertificateRef
)
888 CFArrayGetValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
889 if (node
->_fetchingState
!= kSecFetchingStateDone
) {
890 /* If we still have potential sources to fetch other candidates
891 from we ignore expired candidates. */
892 if (!SecCertificateIsValidOn(candidate
, verifyTime
)) {
893 node
->_candidateIndex
++;
898 SecTrustNodeRef parent
= SecTrustNodeCreate(candidate
, node
);
899 CFArrayRemoveValueAtIndex(node
->_candidates
, node
->_candidateIndex
);
900 if (SecTrustNodeVerifySignatureChain(parent
) ==
901 kSecTrustStateNotSigner
) {
902 /* This candidate parent is not a valid signer of its
905 /* If another signature failed further down the chain we need
906 to backtrack down to whatever child is still a valid
907 candidate and has additional candidates to consider.
908 @@@ We really want to make the fetchingState a global of
909 SecTrust itself as well and not have any node go beyond the
910 current state of SecTrust if there are other (read cheap)
911 options to consider. */
917 /* We've run out of candidates in our current state so let's try to
918 find some more. Note we fetch candidates in increasing order of
919 cost in the hope we won't ever get to the more expensive fetching
921 SecCertificateRef certificate
= node
->_certificate
;
922 switch (node
->_fetchingState
) {
923 case kSecFetchingStatePassedIn
:
924 /* Get the list of candidates from SecTrust. */
925 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
927 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
930 SecCertificateGetNormalizedIssuerContent(certificate
);
931 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
933 node
->_fetchingState
= kSecFetchingStateLocal
;
935 case kSecFetchingStateLocal
:
936 /* Lookup candidates in the local database. */
937 node
->_fetchingState
= kSecFetchingStateFromURL
;
939 case kSecFetchingStateFromURL
:
940 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
942 case kSecFetchingStateCheckExpired
:
943 /* Time to start considering expired candidates as well. */
944 node
->_candidateIndex
= 0;
945 node
->_fetchingState
= kSecFetchingStateDone
;
947 case kSecFetchingStateDone
;
952 CFAllocatorRef allocator
= CFGetAllocator(node
);
954 /* A trust node has a number of states.
955 1) Look for issuing certificates by asking SecTrust about known
957 2) Look for issuing certificates in certificate databases (keychains)
958 3) Look for issuing certificates by going out to the web if the nodes
959 certificate has a issuer location URL.
960 4) Look through expired or not yet valid candidates we have put aside.
962 We go though the stages 1 though 3 looking for candidate issuer
963 certificates. If a candidate certificate is not valid at verifyTime
964 we put it in a to be examined later queue. If a candidate certificate
965 is valid we verify if it actually signed our certificate (if possible).
966 If not we discard it and continue on to the next candidate certificate.
967 If it is we return a new SecTrustNodeRef for that certificate. */
969 CFMutableArrayRef issuers
= CFArrayCreateMutable(allocator
, 0,
970 &kCFTypeArrayCallBacks
);
972 /* Find a node's parent. */
973 certificate
= node
->_certificate
;
974 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
975 CFTypeRef candidates
= NULL
;
977 candidates
= (CFTypeRef
)CFDictionaryGetValueForKey(skidDict
, akid
);
979 addValidIssuersFrom(issuers
, certificate
, candidates
, true);
984 SecCertificateGetNormalizedIssuerContent(certificate
);
985 candidates
= (CFTypeRef
)
986 CFDictionaryGetValueForKey(subjectDict
, issuer
);
987 addValidIssuersFrom(issuers
, certificate
, candidates
, false);
990 if (CFArrayGetCount(issuers
) == 0) {
991 /* O no! we can't find an issuer for this certificate. Let's see
992 if we can find one in the local database. */
998 CFArrayRef
SecTrustNodeCopyNextChain(SecTrustNodeRef node
,
1000 /* Return the next full chain that isn't a reject unless we are in
1001 a state where we consider returning rejects. */
1003 switch (node
->_fetchingState
) {
1004 case kSecFetchingStatePassedIn
:
1005 /* Get the list of candidates from SecTrust. */
1006 CFDataRef akid
= SecCertificateGetAuthorityKeyID(certificate
);
1008 SecTrustAppendCandidatesWithAuthorityKeyID(akid
, node
->_candidates
);
1011 SecCertificateGetNormalizedIssuerContent(certificate
);
1012 SecTrustAppendCandidatesWithSubject(issuer
, node
->_candidates
);
1014 node
->_fetchingState
= kSecFetchingStateLocal
;
1016 case kSecFetchingStateLocal
:
1017 /* Lookup candidates in the local database. */
1018 node
->_fetchingState
= kSecFetchingStateFromURL
;
1020 case kSecFetchingStateFromURL
:
1021 node
->_fetchingState
= kSecFetchingStateCheckExpired
;
1023 case kSecFetchingStateCheckExpired
:
1024 /* Time to start considering expired candidates as well. */
1025 node
->_candidateIndex
= 0;
1026 node
->_fetchingState
= kSecFetchingStateDone
;
1028 case kSecFetchingStateDone
;
1034 Iterator
parentIterator(Cert
);
1040 static bool unique(Node node
) {
1041 if (nodes
.contains(node
))
1047 static bool isAnchor(Cert cert
);
1054 Iterator parentIterator
; /* For current source of parents. */
1056 Node(Cert inCert
) : child(nil
), cert(inCert
), nextSource(0) {}
1057 Node(Node inChild
, Cert inCert
) : child(inChild
), cert(inCert
),
1060 CertPath
certPath() {
1064 path
.add(node
.cert
);
1070 void contains(Cert cert
) {
1073 if (cert
== node
.cert
)
1080 Node
nextParent(Array currentSources
) {
1083 parentIterator
== currentSources
[nextSource
- 1].end()) {
1084 if (nextSource
== currentSources
.count
) {
1085 /* We ran out of parent sources. */
1088 parentIterator
= currentSources
[nextSource
++].begin();
1090 Certificate cert
= *parentIterator
++;
1091 /* Check for cycles and self signed chains. */
1092 if (!contains(cert
)) {
1093 Node node
= Node(this, parent
);
1094 if (!NodeCache
.unique(node
))
1105 Array currentSources
;
1111 PathBuilder(Cert cert
) {
1112 nodes
.append(Node(cert
));
1113 nit
= nodes
.begin();
1114 sourceIT
= allSources
.begin();
1117 nextAnchoredPath() {
1118 if (nit
== nodes
.end()) {
1119 /* We should add another source to the list of sources to
1121 if (sourceIT
== allSources
.end()) {
1122 /* No more sources to add. */
1124 currentSources
+= *sourceIT
++;
1125 /* Resort nodes by size. */
1127 nit
= nodes
.begin();
1128 /* Set the source list for all nodes. */
1131 while (Node node
= *nit
) {
1132 Node candidate
= node
.nextParent(currentSources
);
1134 /* The current node has no more candidate parents so move
1140 if (candidate
.isAnchored
) {
1141 candidates
.append(candidate
);
1143 nodes
.insert(candidate
, nit
);
1148 while (Node node
= nextAnchoredPath()) {
1149 if (node
.isValid()) {
1150 chain
= node
.certPath
;
1153 rejects
.append(node
);