2 * Copyright (c) 2008-2016 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@
25 * SecPolicyLeafCallbacks.c - Callbacks for SecPolicy for verifying leafs
28 #include <AssertMacros.h>
29 #include <CoreFoundation/CFDictionary.h>
30 #include <Security/SecPolicyInternal.h>
31 #include <Security/SecCertificateInternal.h>
32 #include <utilities/SecCFWrappers.h>
35 #include <libDER/oidsPriv.h>
38 * MARK: SecPolicyCheckCert Functions
39 * All SecPolicyCheckCert* return false if the cert fails the check and true if it succeeds.
42 typedef bool (*SecPolicyCheckCertFunction
)(SecCertificateRef cert
, CFTypeRef pvcValue
);
44 /* This one is different from SecPolicyCheckCriticalExtensions because
45 that one is an empty stub. The CriticalExtensions check is done in
46 SecPolicyCheckBasicCertificateProcessing. */
47 static bool SecPolicyCheckCertCriticalExtensions(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
48 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
49 /* Certificate contains one or more unknown critical extensions. */
55 static bool keyusage_allows(SecKeyUsage keyUsage
, CFTypeRef xku
) {
56 if (!xku
|| CFGetTypeID(xku
) != CFNumberGetTypeID())
60 CFNumberGetValue((CFNumberRef
)xku
, kCFNumberSInt32Type
, &dku
);
61 SecKeyUsage ku
= (SecKeyUsage
)dku
;
62 return (keyUsage
& ku
) == ku
;
65 bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
66 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
68 CFTypeRef xku
= pvcValue
;
70 CFIndex ix
, count
= CFArrayGetCount(xku
);
71 for (ix
= 0; ix
< count
; ++ix
) {
72 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
73 if (keyusage_allows(keyUsage
, ku
)) {
79 match
= keyusage_allows(keyUsage
, xku
);
84 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
88 if (extendedKeyUsage
) {
89 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
90 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
92 /* Certificate has no extended key usage, only a match if the policy
93 contains a 0 length CFDataRef. */
94 return CFDataGetLength((CFDataRef
)xeku
) == 0;
98 static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage
,
103 if(CFGetTypeID(xeku
) == CFDataGetTypeID()) {
104 return extendedkeyusage_allows(extendedKeyUsage
, xeku
);
105 } else if (CFGetTypeID(xeku
) == CFStringGetTypeID()) {
106 CFDataRef eku
= SecCertificateCreateOidDataFromString(NULL
, xeku
);
108 bool result
= extendedkeyusage_allows(extendedKeyUsage
, eku
);
116 bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
117 CFArrayRef certExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(cert
);
119 CFTypeRef xeku
= pvcValue
;
121 CFIndex ix
, count
= CFArrayGetCount(xeku
);
122 for (ix
= 0; ix
< count
; ix
++) {
123 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
124 if (isExtendedKeyUsageAllowed(certExtendedKeyUsage
, eku
)) {
130 match
= isExtendedKeyUsageAllowed(certExtendedKeyUsage
, xeku
);
132 CFReleaseSafe(certExtendedKeyUsage
);
136 bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
137 /* If the certificate has a subject, or
138 if it doesn't, and it's the leaf and not a CA,
139 and also has a critical subjectAltName extension it's valid. */
140 if (!SecCertificateHasSubject(cert
)) {
141 if (SecCertificateIsCA(cert
)) {
142 /* CA certificate has empty subject. */
145 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
146 /* Leaf certificate with empty subject does not have
147 a critical subject alt name extension. */
156 /* This one is different from SecPolicyCheckQualifiedCertStatements because
157 both are empty stubs. */
158 static bool SecPolicyCheckCertQualifiedCertStatements(SecCertificateRef __unused cert
,
159 CFTypeRef __unused pvcValue
) {
163 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
164 more labels. Use CFNetwork's function for determining if those labels comprise
165 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
166 typedef bool (*CFNIsTLD_f
)(CFStringRef domain
);
167 static bool SecDNSIsTLD(CFStringRef reference
) {
168 bool result
= false; /* fail open for allocation and symbol lookup failures */
169 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr
= NULL
;
170 static dispatch_once_t onceToken
;
171 CFStringRef presentedDomain
= NULL
;
173 dispatch_once(&onceToken
, ^{
174 void *framework
= dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY
);
176 CFNIsDomainTopLevelFunctionPtr
= dlsym(framework
, "_CFHostIsDomainTopLevelForCertificatePolicy");
180 require_quiet(CFNIsDomainTopLevelFunctionPtr
, out
);
181 CFIndex referenceLen
= CFStringGetLength(reference
);
183 /* reference identifier is too short, we should fail it */
184 require_action_quiet(referenceLen
> 2, out
, result
= true);
186 require_quiet(presentedDomain
= CFStringCreateWithSubstring(NULL
, reference
,
187 CFRangeMake(2, referenceLen
- 2)),
189 result
= CFNIsDomainTopLevelFunctionPtr(presentedDomain
);
192 CFReleaseNull(presentedDomain
);
196 /* Compare hostname, to a server name obtained from the server's cert
197 Obtained from the SubjectAltName or the CommonName entry in the Subject.
198 Limited wildcard checking is performed here as outlined in RFC 6125
201 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
202 never to accept partial-label wildcards even though they are allowed by
205 We use the language from RFC 6125, particularly the following definitions:
207 presented identifier: An identifier that is presented by a server to
208 a client within a PKIX certificate when the client attempts to
209 establish secure communication with the server; the certificate
210 can include one or more presented identifiers of different types,
211 and if the server hosts more than one domain then the certificate
212 might present distinct identifiers for each domain.
214 reference identifier: An identifier, constructed from a source
215 domain and optionally an application service type, used by the
216 client for matching purposes when examining presented identifiers.
219 static bool SecDNSMatch(CFStringRef reference
, CFStringRef presented
) {
220 CFArrayRef referenceLabels
= NULL
, presentedLabels
= NULL
;
223 /* A trailing '.' in the reference identifier is allowed as a mechanism
224 to force TLS renegotiation. Strip it before parsing labels. */
225 CFIndex referenceLen
= CFStringGetLength(reference
);
226 require_quiet(referenceLen
> 0, noMatch
);
227 if ('.' == CFStringGetCharacterAtIndex(reference
, referenceLen
- 1)) {
228 CFStringRef truncatedReference
= CFStringCreateWithSubstring(NULL
, reference
,
229 CFRangeMake(0, referenceLen
- 1));
230 referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, truncatedReference
, CFSTR("."));
231 CFReleaseNull(truncatedReference
);
232 require_quiet(referenceLabels
, noMatch
);
234 require_quiet(referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, reference
, CFSTR(".")),
238 require_quiet(presentedLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, presented
, CFSTR(".")),
241 /* Reference Identifier and Presented Identifier must have the same number of labels
242 because a wildcard in the presented identifier can only match a single label in the
243 reference identifier. */
244 require_quiet(CFArrayGetCount(referenceLabels
) == CFArrayGetCount(presentedLabels
), noMatch
);
246 CFIndex ix
, count
= CFArrayGetCount(referenceLabels
);
247 for (ix
= count
- 1; ix
>= 0; ix
--) {
248 CFStringRef rlabel
= NULL
, plabel
= NULL
;
249 require_quiet(rlabel
= CFArrayGetValueAtIndex(referenceLabels
, ix
), noMatch
);
250 require_quiet(plabel
= CFArrayGetValueAtIndex(presentedLabels
, ix
), noMatch
);
251 if (CFEqual(plabel
, CFSTR("*"))) {
252 /* must only occur in left-most label */
253 require_quiet(ix
== 0, noMatch
);
255 /* must not occur before single-label TLD */
256 require_quiet(count
> 2 && ix
!= count
- 2, noMatch
);
258 /* must not occur before a multi-label gTLD */
259 require_quiet(!SecDNSIsTLD(presented
), noMatch
);
261 /* partial-label wildcards are disallowed */
262 CFRange partialRange
= CFStringFind(plabel
, CFSTR("*"), 0);
263 require_quiet(partialRange
.location
== kCFNotFound
&& partialRange
.length
== 0 ,
266 /* not a wildcard, so labels must match exactly */
267 require_quiet(CFStringCompare(rlabel
, plabel
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
, noMatch
);
274 CFReleaseNull(referenceLabels
);
275 CFReleaseNull(presentedLabels
);
279 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert
, CFTypeRef pvcValue
) {
280 /* @@@ Consider what to do if the caller passes in no hostname. Should
281 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
282 CFStringRef hostName
= pvcValue
;
283 if (!isString(hostName
)) {
284 /* @@@ We can't return an error here and making the evaluation fail
285 won't help much either. */
289 bool dnsMatch
= false;
290 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
292 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
293 for (ix
= 0; ix
< count
; ++ix
) {
294 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
295 if (SecDNSMatch(hostName
, dns
)) {
304 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
305 the values returned by SecCertificateCopyIPAddresses() instead. */
306 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(cert
);
308 CFIndex ix
, count
= CFArrayGetCount(ipAddresses
);
309 for (ix
= 0; ix
< count
; ++ix
) {
310 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
311 if (!CFStringCompare(hostName
, ipAddress
, kCFCompareCaseInsensitive
)) {
316 CFRelease(ipAddresses
);
323 bool SecPolicyCheckCertEmail(SecCertificateRef cert
, CFTypeRef pvcValue
) {
324 CFStringRef email
= pvcValue
;
326 if (!isString(email
)) {
327 /* We can't return an error here and making the evaluation fail
328 won't help much either. */
332 CFArrayRef addrs
= SecCertificateCopyRFC822Names(cert
);
334 CFIndex ix
, count
= CFArrayGetCount(addrs
);
335 for (ix
= 0; ix
< count
; ++ix
) {
336 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
337 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
348 static bool SecPolicyCheckCertValidLeaf(SecCertificateRef cert
, CFTypeRef pvcValue
) {
349 CFAbsoluteTime verifyTime
= CFDateGetAbsoluteTime(pvcValue
);
350 if (!SecCertificateIsValid(cert
, verifyTime
)) {
351 /* Leaf certificate has expired. */
357 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert
, CFTypeRef pvcValue
) {
358 CFStringRef prefix
= pvcValue
;
360 if (!isString(prefix
)) {
361 /* @@@ We can't return an error here and making the evaluation fail
362 won't help much either. */
365 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
366 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
367 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
368 /* Common Name prefix mismatch. */
371 CFReleaseSafe(commonNames
);
375 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert
, CFTypeRef pvcValue
) {
376 CFStringRef common_name
= pvcValue
;
378 if (!isString(common_name
)) {
379 /* @@@ We can't return an error here and making the evaluation fail
380 won't help much either. */
383 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
384 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
385 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
386 /* Common Name mismatch. */
389 CFReleaseSafe(commonNames
);
393 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert
, CFTypeRef pvcValue
) {
394 CFStringRef common_name
= pvcValue
;
396 if (!isString(common_name
)) {
397 /* @@@ We can't return an error here and making the evaluation fail
398 won't help much either. */
401 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
402 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
403 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
404 CFStringRef test_common_name
= common_name
?
405 CFStringCreateWithFormat(kCFAllocatorDefault
,
406 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
408 if (!CFEqual(common_name
, cert_common_name
) &&
409 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
410 /* Common Name mismatch. */
412 CFReleaseSafe(test_common_name
);
414 CFReleaseSafe(commonNames
);
418 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert
, CFTypeRef pvcValue
) {
419 CFDateRef date
= pvcValue
;
421 /* @@@ We can't return an error here and making the evaluation fail
422 won't help much either. */
425 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
426 if (SecCertificateNotValidBefore(cert
) <= at
) {
427 /* Leaf certificate has not valid before that is too old. */
433 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert
, CFTypeRef pvcValue
) {
434 CFStringRef org
= pvcValue
;
436 if (!isString(org
)) {
437 /* @@@ We can't return an error here and making the evaluation fail
438 won't help much either. */
441 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
442 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
443 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
444 /* Leaf Subject Organization mismatch. */
447 CFReleaseSafe(organization
);
451 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert
, CFTypeRef pvcValue
) {
452 CFStringRef orgUnit
= pvcValue
;
454 if (!isString(orgUnit
)) {
455 /* @@@ We can't return an error here and making the evaluation fail
456 won't help much either. */
459 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
460 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
461 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
462 /* Leaf Subject Organizational Unit mismatch. */
465 CFReleaseSafe(organizationalUnit
);
469 bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert
, CFTypeRef pvcValue
) {
470 CFStringRef country
= pvcValue
;
472 if (!isString(country
)) {
473 /* @@@ We can't return an error here and making the evaluation fail
474 won't help much either. */
477 CFArrayRef certCountry
= SecCertificateCopyCountry(cert
);
478 if (!certCountry
|| CFArrayGetCount(certCountry
) != 1 ||
479 !CFEqual(country
, CFArrayGetValueAtIndex(certCountry
, 0))) {
480 /* Subject Country mismatch. */
483 CFReleaseSafe(certCountry
);
487 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert
, CFTypeRef pvcValue
) {
488 CFArrayRef trustedServerNames
= pvcValue
;
489 /* No names specified means we accept any name. */
490 if (!trustedServerNames
)
492 if (!isArray(trustedServerNames
)) {
493 /* @@@ We can't return an error here and making the evaluation fail
494 won't help much either. */
498 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
499 bool dnsMatch
= false;
500 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
502 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
503 // @@@ This is O(N^2) unfortunately we can't do better easily unless
504 // we don't do wildcard matching. */
505 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
506 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
508 for (tix
= 0; tix
< tsnCount
; ++tix
) {
509 CFStringRef serverName
=
510 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
511 if (!isString(serverName
)) {
512 /* @@@ We can't return an error here and making the
513 evaluation fail won't help much either. */
514 CFReleaseSafe(dnsNames
);
517 /* we purposefully reverse the arguments here such that dns names
518 from the cert are matched against a server name list, where
519 the server names list can contain wildcards and the dns name
520 cannot. References: http://support.microsoft.com/kb/941123
521 It's easy to find occurrences where people tried to use
522 wildcard certificates and were told that those don't work
524 if (SecDNSMatch(dns
, serverName
)) {
536 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
537 if (pvcValue
&& SecCertificateHasMarkerExtension(cert
, pvcValue
)) {
544 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert
,
545 CFTypeRef pvcValue
) {
546 if (CFGetTypeID(pvcValue
) == CFArrayGetTypeID()) {
547 CFIndex ix
, length
= CFArrayGetCount(pvcValue
);
548 for (ix
= 0; ix
< length
; ix
++)
549 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
,
550 CFArrayGetValueAtIndex((CFArrayRef
)pvcValue
, ix
))) {
553 } else if (CFGetTypeID(pvcValue
) == CFDataGetTypeID() ||
554 CFGetTypeID(pvcValue
) == CFStringGetTypeID()) {
555 return (NULL
!= SecCertificateGetExtensionValue(cert
, pvcValue
));
560 static CFSetRef
copyCertificatePolicies(SecCertificateRef cert
) {
561 CFMutableSetRef policies
= NULL
;
562 policies
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
563 if (!policies
) return NULL
;
565 const SecCECertificatePolicies
*cp
= SecCertificateGetCertificatePolicies(cert
);
566 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
567 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
568 CFDataRef oidData
= NULL
;
569 DERItem
*policyOID
= &cp
->policies
[policy_ix
].policyIdentifier
;
570 oidData
= CFDataCreate(kCFAllocatorDefault
, policyOID
->data
, policyOID
->length
);
571 CFSetAddValue(policies
, oidData
);
572 CFReleaseSafe(oidData
);
577 static bool checkPolicyOidData(SecCertificateRef cert
, CFDataRef oid
) {
578 CFSetRef policies
= copyCertificatePolicies(cert
);
580 if (policies
&& CFSetContainsValue(policies
, oid
)) {
583 CFReleaseSafe(policies
);
587 /* This one is different from SecPolicyCheckCertificatePolicyOid because
588 that one checks the whole chain. (And uses policy_set_t...) */
589 static bool SecPolicyCheckCertCertificatePolicyOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
590 CFTypeRef value
= pvcValue
;
593 if (CFGetTypeID(value
) == CFDataGetTypeID())
595 result
= checkPolicyOidData(cert
, value
);
596 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
597 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
599 result
= checkPolicyOidData(cert
, dataOid
);
606 static bool SecPolicyCheckCertWeak(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
607 if (cert
&& SecCertificateIsWeakKey(cert
)) {
608 /* Leaf certificate has a weak key. */
614 static bool SecPolicyCheckCertKeySize(SecCertificateRef cert
, CFTypeRef pvcValue
) {
615 CFDictionaryRef keySizes
= pvcValue
;
616 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
622 static CFStringRef
convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum
) {
623 const void *digests
[] = { kSecSignatureDigestAlgorithmUnknown
,
624 kSecSignatureDigestAlgorithmMD2
,
625 kSecSignatureDigestAlgorithmMD4
,
626 kSecSignatureDigestAlgorithmMD5
,
627 kSecSignatureDigestAlgorithmSHA1
,
628 kSecSignatureDigestAlgorithmSHA224
,
629 kSecSignatureDigestAlgorithmSHA256
,
630 kSecSignatureDigestAlgorithmSHA384
,
631 kSecSignatureDigestAlgorithmSHA512
,
633 return digests
[algorithmEnum
];
636 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert
, CFTypeRef pvcValue
) {
637 CFSetRef disallowedHashAlgorithms
= pvcValue
;
638 CFStringRef certAlg
= convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert
));
639 if (CFSetContainsValue(disallowedHashAlgorithms
, certAlg
)) {
646 * MARK: SecLeafPVC functions
648 static CFDictionaryRef
SecLeafPVCCopyCallbacks(void) {
649 CFMutableDictionaryRef leafCallbacks
= NULL
;
650 leafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
651 &kCFTypeDictionaryKeyCallBacks
, NULL
);
652 CFDictionaryAddValue(leafCallbacks
,
653 kSecPolicyCheckCriticalExtensions
,
654 SecPolicyCheckCertCriticalExtensions
);
655 CFDictionaryAddValue(leafCallbacks
,
656 kSecPolicyCheckKeyUsage
,
657 SecPolicyCheckCertKeyUsage
);
658 CFDictionaryAddValue(leafCallbacks
,
659 kSecPolicyCheckExtendedKeyUsage
,
660 SecPolicyCheckCertExtendedKeyUsage
);
661 CFDictionaryAddValue(leafCallbacks
,
662 kSecPolicyCheckNonEmptySubject
,
663 SecPolicyCheckCertNonEmptySubject
);
664 CFDictionaryAddValue(leafCallbacks
,
665 kSecPolicyCheckQualifiedCertStatements
,
666 SecPolicyCheckCertQualifiedCertStatements
);
667 CFDictionaryAddValue(leafCallbacks
,
668 kSecPolicyCheckSSLHostname
,
669 SecPolicyCheckCertSSLHostname
);
670 CFDictionaryAddValue(leafCallbacks
,
671 kSecPolicyCheckEmail
,
672 SecPolicyCheckCertEmail
);
673 CFDictionaryAddValue(leafCallbacks
,
674 kSecPolicyCheckValidLeaf
,
675 SecPolicyCheckCertValidLeaf
);
676 CFDictionaryAddValue(leafCallbacks
,
677 kSecPolicyCheckSubjectCommonNamePrefix
,
678 SecPolicyCheckCertSubjectCommonNamePrefix
);
679 CFDictionaryAddValue(leafCallbacks
,
680 kSecPolicyCheckSubjectCommonName
,
681 SecPolicyCheckCertSubjectCommonName
);
682 CFDictionaryAddValue(leafCallbacks
,
683 kSecPolicyCheckNotValidBefore
,
684 SecPolicyCheckCertNotValidBefore
);
685 CFDictionaryAddValue(leafCallbacks
,
686 kSecPolicyCheckSubjectOrganization
,
687 SecPolicyCheckCertSubjectOrganization
);
688 CFDictionaryAddValue(leafCallbacks
,
689 kSecPolicyCheckSubjectOrganizationalUnit
,
690 SecPolicyCheckCertSubjectOrganizationalUnit
);
691 CFDictionaryAddValue(leafCallbacks
,
692 kSecPolicyCheckEAPTrustedServerNames
,
693 SecPolicyCheckCertEAPTrustedServerNames
);
694 CFDictionaryAddValue(leafCallbacks
,
695 kSecPolicyCheckSubjectCommonNameTEST
,
696 SecPolicyCheckCertSubjectCommonNameTEST
);
697 CFDictionaryAddValue(leafCallbacks
,
698 kSecPolicyCheckLeafMarkerOid
,
699 SecPolicyCheckCertLeafMarkerOid
);
700 CFDictionaryAddValue(leafCallbacks
,
701 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
702 SecPolicyCheckCertLeafMarkerOidWithoutValueCheck
);
703 CFDictionaryAddValue(leafCallbacks
,
704 kSecPolicyCheckCertificatePolicy
,
705 SecPolicyCheckCertCertificatePolicyOid
);
706 CFDictionaryAddValue(leafCallbacks
,
707 kSecPolicyCheckWeakLeaf
,
708 SecPolicyCheckCertWeak
);
709 CFDictionaryAddValue(leafCallbacks
,
710 kSecPolicyCheckKeySize
,
711 SecPolicyCheckCertKeySize
);
712 CFDictionaryAddValue(leafCallbacks
,
713 kSecPolicyCheckSignatureHashAlgorithms
,
714 SecPolicyCheckCertSignatureHashAlgorithms
);
716 return leafCallbacks
;
719 void SecLeafPVCInit(SecLeafPVCRef pvc
, SecCertificateRef leaf
, CFArrayRef policies
,
720 CFAbsoluteTime verifyTime
) {
721 secdebug("alloc", "%p", pvc
);
722 // Weird logging policies crashes.
723 //secdebug("policy", "%@", policies);
724 pvc
->leaf
= CFRetainSafe(leaf
);
725 pvc
->policies
= CFRetainSafe(policies
);
726 pvc
->verifyTime
= verifyTime
;
727 pvc
->callbacks
= SecLeafPVCCopyCallbacks();
731 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
732 &kCFTypeDictionaryValueCallBacks
);
733 pvc
->details
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
, 1,
734 &kCFTypeArrayCallBacks
);
735 CFRelease(certDetail
);
739 void SecLeafPVCDelete(SecLeafPVCRef pvc
) {
740 secdebug("alloc", "%p", pvc
);
741 CFReleaseNull(pvc
->policies
);
742 CFReleaseNull(pvc
->details
);
743 CFReleaseNull(pvc
->callbacks
);
744 CFReleaseNull(pvc
->leaf
);
747 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc
,
748 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
750 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
, "leaf",
751 (force
? "force" : ""), result
);
753 /* If this is not something the current policy cares about ignore
754 this error and return true so our caller continues evaluation. */
756 /* @@@ The right long term fix might be to check if none of the passed
757 in policies contain this key, since not all checks are run for all
759 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
760 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
764 /* @@@ Check to see if the SecTrustSettings for the certificate in question
765 tell us to ignore this error. */
770 CFMutableDictionaryRef detail
=
771 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
773 /* Perhaps detail should have an array of results per key? As it stands
774 in the case of multiple policy failures the last failure stands. */
775 CFDictionarySetValue(detail
, key
, result
);
780 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc
,
781 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
782 return SecLeafPVCSetResultForced(pvc
, key
, ix
, result
, false);
785 static void SecLeafPVCValidateKey(const void *key
, const void *value
,
787 SecLeafPVCRef pvc
= (SecLeafPVCRef
)context
;
789 /* If our caller doesn't want full details and we failed earlier there is
790 no point in doing additional checks. */
791 if (!pvc
->result
&& !pvc
->details
)
794 SecPolicyCheckCertFunction fcn
= (SecPolicyCheckCertFunction
) CFDictionaryGetValue(pvc
->callbacks
, key
);
800 /* kSecPolicyCheckValidLeaf is special */
801 if (CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
802 CFDateRef verifyDate
= CFDateCreate(NULL
, pvc
->verifyTime
);
803 if(!fcn(pvc
->leaf
, verifyDate
)) {
804 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
806 CFReleaseSafe(verifyDate
);
808 /* get pvcValue from current policy */
809 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
814 CFTypeRef pvcValue
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, key
);
815 if(!fcn(pvc
->leaf
, pvcValue
)) {
816 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
821 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc
) {
823 CFArrayRef policies
= pvc
->policies
;
824 CFIndex ix
, count
= CFArrayGetCount(policies
);
825 for (ix
= 0; ix
< count
; ++ix
) {
826 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
828 /* Validate all keys for all policies. */
829 CFDictionaryApplyFunction(policy
->_options
, SecLeafPVCValidateKey
, pvc
);
830 if (!pvc
->result
&& !pvc
->details
)