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 static bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert
, CFTypeRef pvcValue
) {
137 /* If the certificate has a subject, or
138 if it doesn't, and it's the leaf and not self signed,
139 and also has a critical subjectAltName extension it's valid. */
140 if (!SecCertificateHasSubject(cert
)) {
141 Boolean isSelfSigned
= true;
142 SecCertificateIsSelfSigned(cert
, &isSelfSigned
);
144 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
145 /* Leaf certificate with empty subject does not have
146 a critical subject alt name extension. */
150 /* CA certificate has empty subject. */
158 /* This one is different from SecPolicyCheckQualifiedCertStatements because
159 both are empty stubs. */
160 static bool SecPolicyCheckCertQualifiedCertStatements(SecCertificateRef __unused cert
,
161 CFTypeRef __unused pvcValue
) {
165 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
166 more labels. Use CFNetwork's function for determining if those labels comprise
167 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
168 typedef bool (*CFNIsTLD_f
)(CFStringRef domain
);
169 static bool SecDNSIsTLD(CFStringRef reference
) {
170 bool result
= false; /* fail open for allocation and symbol lookup failures */
171 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr
= NULL
;
172 static dispatch_once_t onceToken
;
173 CFStringRef presentedDomain
= NULL
;
175 dispatch_once(&onceToken
, ^{
176 void *framework
= dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY
);
178 CFNIsDomainTopLevelFunctionPtr
= dlsym(framework
, "_CFHostIsDomainTopLevelForCertificatePolicy");
182 require_quiet(CFNIsDomainTopLevelFunctionPtr
, out
);
183 CFIndex referenceLen
= CFStringGetLength(reference
);
185 /* reference identifier is too short, we should fail it */
186 require_action_quiet(referenceLen
> 2, out
, result
= true);
188 require_quiet(presentedDomain
= CFStringCreateWithSubstring(NULL
, reference
,
189 CFRangeMake(2, referenceLen
- 2)),
191 result
= CFNIsDomainTopLevelFunctionPtr(presentedDomain
);
194 CFReleaseNull(presentedDomain
);
198 /* Compare hostname, to a server name obtained from the server's cert
199 Obtained from the SubjectAltName or the CommonName entry in the Subject.
200 Limited wildcard checking is performed here as outlined in RFC 6125
203 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
204 never to accept partial-label wildcards even though they are allowed by
207 We use the language from RFC 6125, particularly the following definitions:
209 presented identifier: An identifier that is presented by a server to
210 a client within a PKIX certificate when the client attempts to
211 establish secure communication with the server; the certificate
212 can include one or more presented identifiers of different types,
213 and if the server hosts more than one domain then the certificate
214 might present distinct identifiers for each domain.
216 reference identifier: An identifier, constructed from a source
217 domain and optionally an application service type, used by the
218 client for matching purposes when examining presented identifiers.
221 static bool SecDNSMatch(CFStringRef reference
, CFStringRef presented
) {
222 CFArrayRef referenceLabels
= NULL
, presentedLabels
= NULL
;
225 /* A trailing '.' in the reference identifier is allowed as a mechanism
226 to force TLS renegotiation. Strip it before parsing labels. */
227 CFIndex referenceLen
= CFStringGetLength(reference
);
228 require_quiet(referenceLen
> 0, noMatch
);
229 if ('.' == CFStringGetCharacterAtIndex(reference
, referenceLen
- 1)) {
230 CFStringRef truncatedReference
= CFStringCreateWithSubstring(NULL
, reference
,
231 CFRangeMake(0, referenceLen
- 1));
232 referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, truncatedReference
, CFSTR("."));
233 CFReleaseNull(truncatedReference
);
234 require_quiet(referenceLabels
, noMatch
);
236 require_quiet(referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, reference
, CFSTR(".")),
240 require_quiet(presentedLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, presented
, CFSTR(".")),
243 /* Reference Identifier and Presented Identifier must have the same number of labels
244 because a wildcard in the presented identifier can only match a single label in the
245 reference identifier. */
246 require_quiet(CFArrayGetCount(referenceLabels
) == CFArrayGetCount(presentedLabels
), noMatch
);
248 CFIndex ix
, count
= CFArrayGetCount(referenceLabels
);
249 for (ix
= count
- 1; ix
>= 0; ix
--) {
250 CFStringRef rlabel
= NULL
, plabel
= NULL
;
251 require_quiet(rlabel
= CFArrayGetValueAtIndex(referenceLabels
, ix
), noMatch
);
252 require_quiet(plabel
= CFArrayGetValueAtIndex(presentedLabels
, ix
), noMatch
);
253 if (CFEqual(plabel
, CFSTR("*"))) {
254 /* must only occur in left-most label */
255 require_quiet(ix
== 0, noMatch
);
257 /* must not occur before single-label TLD */
258 require_quiet(count
> 2 && ix
!= count
- 2, noMatch
);
260 /* must not occur before a multi-label gTLD */
261 require_quiet(!SecDNSIsTLD(presented
), noMatch
);
263 /* partial-label wildcards are disallowed */
264 CFRange partialRange
= CFStringFind(plabel
, CFSTR("*"), 0);
265 require_quiet(partialRange
.location
== kCFNotFound
&& partialRange
.length
== 0 ,
268 /* not a wildcard, so labels must match exactly */
269 require_quiet(CFStringCompare(rlabel
, plabel
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
, noMatch
);
276 CFReleaseNull(referenceLabels
);
277 CFReleaseNull(presentedLabels
);
281 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert
, CFTypeRef pvcValue
) {
282 /* @@@ Consider what to do if the caller passes in no hostname. Should
283 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
284 CFStringRef hostName
= pvcValue
;
285 if (!isString(hostName
)) {
286 /* @@@ We can't return an error here and making the evaluation fail
287 won't help much either. */
291 bool dnsMatch
= false;
292 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
294 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
295 for (ix
= 0; ix
< count
; ++ix
) {
296 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
297 if (SecDNSMatch(hostName
, dns
)) {
306 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
307 the values returned by SecCertificateCopyIPAddresses() instead. */
308 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(cert
);
310 CFIndex ix
, count
= CFArrayGetCount(ipAddresses
);
311 for (ix
= 0; ix
< count
; ++ix
) {
312 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
313 if (!CFStringCompare(hostName
, ipAddress
, kCFCompareCaseInsensitive
)) {
318 CFRelease(ipAddresses
);
325 bool SecPolicyCheckCertEmail(SecCertificateRef cert
, CFTypeRef pvcValue
) {
326 CFStringRef email
= pvcValue
;
328 if (!isString(email
)) {
329 /* We can't return an error here and making the evaluation fail
330 won't help much either. */
334 CFArrayRef addrs
= SecCertificateCopyRFC822Names(cert
);
336 CFIndex ix
, count
= CFArrayGetCount(addrs
);
337 for (ix
= 0; ix
< count
; ++ix
) {
338 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
339 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
350 static bool SecPolicyCheckCertValidLeaf(SecCertificateRef cert
, CFTypeRef pvcValue
) {
351 CFAbsoluteTime verifyTime
= CFDateGetAbsoluteTime(pvcValue
);
352 if (!SecCertificateIsValid(cert
, verifyTime
)) {
353 /* Leaf certificate has expired. */
359 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert
, CFTypeRef pvcValue
) {
360 CFStringRef prefix
= pvcValue
;
362 if (!isString(prefix
)) {
363 /* @@@ We can't return an error here and making the evaluation fail
364 won't help much either. */
367 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
368 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
369 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
370 /* Common Name prefix mismatch. */
373 CFReleaseSafe(commonNames
);
377 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert
, CFTypeRef pvcValue
) {
378 CFStringRef common_name
= pvcValue
;
380 if (!isString(common_name
)) {
381 /* @@@ We can't return an error here and making the evaluation fail
382 won't help much either. */
385 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
386 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
387 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
388 /* Common Name mismatch. */
391 CFReleaseSafe(commonNames
);
395 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert
, CFTypeRef pvcValue
) {
396 CFStringRef common_name
= pvcValue
;
398 if (!isString(common_name
)) {
399 /* @@@ We can't return an error here and making the evaluation fail
400 won't help much either. */
403 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
404 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
405 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
406 CFStringRef test_common_name
= common_name
?
407 CFStringCreateWithFormat(kCFAllocatorDefault
,
408 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
410 if (!CFEqual(common_name
, cert_common_name
) &&
411 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
412 /* Common Name mismatch. */
414 CFReleaseSafe(test_common_name
);
416 CFReleaseSafe(commonNames
);
420 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert
, CFTypeRef pvcValue
) {
421 CFDateRef date
= pvcValue
;
423 /* @@@ We can't return an error here and making the evaluation fail
424 won't help much either. */
427 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
428 if (SecCertificateNotValidBefore(cert
) <= at
) {
429 /* Leaf certificate has not valid before that is too old. */
435 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert
, CFTypeRef pvcValue
) {
436 CFStringRef org
= pvcValue
;
438 if (!isString(org
)) {
439 /* @@@ We can't return an error here and making the evaluation fail
440 won't help much either. */
443 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
444 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
445 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
446 /* Leaf Subject Organization mismatch. */
449 CFReleaseSafe(organization
);
453 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert
, CFTypeRef pvcValue
) {
454 CFStringRef orgUnit
= pvcValue
;
456 if (!isString(orgUnit
)) {
457 /* @@@ We can't return an error here and making the evaluation fail
458 won't help much either. */
461 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
462 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
463 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
464 /* Leaf Subject Organizational Unit mismatch. */
467 CFReleaseSafe(organizationalUnit
);
471 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert
, CFTypeRef pvcValue
) {
472 CFArrayRef trustedServerNames
= pvcValue
;
473 /* No names specified means we accept any name. */
474 if (!trustedServerNames
)
476 if (!isArray(trustedServerNames
)) {
477 /* @@@ We can't return an error here and making the evaluation fail
478 won't help much either. */
482 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
483 bool dnsMatch
= false;
484 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
486 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
487 // @@@ This is O(N^2) unfortunately we can't do better easily unless
488 // we don't do wildcard matching. */
489 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
490 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
492 for (tix
= 0; tix
< tsnCount
; ++tix
) {
493 CFStringRef serverName
=
494 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
495 if (!isString(serverName
)) {
496 /* @@@ We can't return an error here and making the
497 evaluation fail won't help much either. */
498 CFReleaseSafe(dnsNames
);
501 /* we purposefully reverse the arguments here such that dns names
502 from the cert are matched against a server name list, where
503 the server names list can contain wildcards and the dns name
504 cannot. References: http://support.microsoft.com/kb/941123
505 It's easy to find occurrences where people tried to use
506 wildcard certificates and were told that those don't work
508 if (SecDNSMatch(dns
, serverName
)) {
520 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
521 if (pvcValue
&& SecCertificateHasMarkerExtension(cert
, pvcValue
)) {
528 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert
,
529 CFTypeRef pvcValue
) {
530 if (CFGetTypeID(pvcValue
) == CFArrayGetTypeID()) {
531 CFIndex ix
, length
= CFArrayGetCount(pvcValue
);
532 for (ix
= 0; ix
< length
; ix
++)
533 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
,
534 CFArrayGetValueAtIndex((CFArrayRef
)pvcValue
, ix
))) {
537 } else if (CFGetTypeID(pvcValue
) == CFDataGetTypeID() ||
538 CFGetTypeID(pvcValue
) == CFStringGetTypeID()) {
539 return (NULL
!= SecCertificateGetExtensionValue(cert
, pvcValue
));
544 static CFSetRef
copyCertificatePolicies(SecCertificateRef cert
) {
545 CFMutableSetRef policies
= NULL
;
546 policies
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
547 if (!policies
) return NULL
;
549 const SecCECertificatePolicies
*cp
= SecCertificateGetCertificatePolicies(cert
);
550 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
551 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
552 CFDataRef oidData
= NULL
;
553 DERItem
*policyOID
= &cp
->policies
[policy_ix
].policyIdentifier
;
554 oidData
= CFDataCreate(kCFAllocatorDefault
, policyOID
->data
, policyOID
->length
);
555 CFSetAddValue(policies
, oidData
);
556 CFReleaseSafe(oidData
);
561 static bool checkPolicyOidData(SecCertificateRef cert
, CFDataRef oid
) {
562 CFSetRef policies
= copyCertificatePolicies(cert
);
564 if (policies
&& CFSetContainsValue(policies
, oid
)) {
567 CFReleaseSafe(policies
);
571 /* This one is different from SecPolicyCheckCertificatePolicyOid because
572 that one checks the whole chain. (And uses policy_set_t...) */
573 static bool SecPolicyCheckCertCertificatePolicyOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
574 CFTypeRef value
= pvcValue
;
577 if (CFGetTypeID(value
) == CFDataGetTypeID())
579 result
= checkPolicyOidData(cert
, value
);
580 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
581 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
583 result
= checkPolicyOidData(cert
, dataOid
);
590 static bool SecPolicyCheckCertWeak(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
591 if (cert
&& SecCertificateIsWeakKey(cert
)) {
592 /* Leaf certificate has a weak key. */
598 static bool SecPolicyCheckCertKeySize(SecCertificateRef cert
, CFTypeRef pvcValue
) {
599 CFDictionaryRef keySizes
= pvcValue
;
600 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
606 static CFStringRef
convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum
) {
607 const void *digests
[] = { kSecSignatureDigestAlgorithmUnknown
,
608 kSecSignatureDigestAlgorithmMD2
,
609 kSecSignatureDigestAlgorithmMD4
,
610 kSecSignatureDigestAlgorithmMD5
,
611 kSecSignatureDigestAlgorithmSHA1
,
612 kSecSignatureDigestAlgorithmSHA224
,
613 kSecSignatureDigestAlgorithmSHA256
,
614 kSecSignatureDigestAlgorithmSHA384
,
615 kSecSignatureDigestAlgorithmSHA512
,
617 return digests
[algorithmEnum
];
620 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert
, CFTypeRef pvcValue
) {
621 CFSetRef disallowedHashAlgorithms
= pvcValue
;
622 CFStringRef certAlg
= convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert
));
623 if (CFSetContainsValue(disallowedHashAlgorithms
, certAlg
)) {
630 * MARK: SecLeafPVC functions
632 static CFDictionaryRef
SecLeafPVCCopyCallbacks(void) {
633 CFMutableDictionaryRef leafCallbacks
= NULL
;
634 leafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
635 &kCFTypeDictionaryKeyCallBacks
, NULL
);
636 CFDictionaryAddValue(leafCallbacks
,
637 kSecPolicyCheckCriticalExtensions
,
638 SecPolicyCheckCertCriticalExtensions
);
639 CFDictionaryAddValue(leafCallbacks
,
640 kSecPolicyCheckKeyUsage
,
641 SecPolicyCheckCertKeyUsage
);
642 CFDictionaryAddValue(leafCallbacks
,
643 kSecPolicyCheckExtendedKeyUsage
,
644 SecPolicyCheckCertExtendedKeyUsage
);
645 CFDictionaryAddValue(leafCallbacks
,
646 kSecPolicyCheckNonEmptySubject
,
647 SecPolicyCheckCertNonEmptySubject
);
648 CFDictionaryAddValue(leafCallbacks
,
649 kSecPolicyCheckQualifiedCertStatements
,
650 SecPolicyCheckCertQualifiedCertStatements
);
651 CFDictionaryAddValue(leafCallbacks
,
652 kSecPolicyCheckSSLHostname
,
653 SecPolicyCheckCertSSLHostname
);
654 CFDictionaryAddValue(leafCallbacks
,
655 kSecPolicyCheckEmail
,
656 SecPolicyCheckCertEmail
);
657 CFDictionaryAddValue(leafCallbacks
,
658 kSecPolicyCheckValidLeaf
,
659 SecPolicyCheckCertValidLeaf
);
660 CFDictionaryAddValue(leafCallbacks
,
661 kSecPolicyCheckSubjectCommonNamePrefix
,
662 SecPolicyCheckCertSubjectCommonNamePrefix
);
663 CFDictionaryAddValue(leafCallbacks
,
664 kSecPolicyCheckSubjectCommonName
,
665 SecPolicyCheckCertSubjectCommonName
);
666 CFDictionaryAddValue(leafCallbacks
,
667 kSecPolicyCheckNotValidBefore
,
668 SecPolicyCheckCertNotValidBefore
);
669 CFDictionaryAddValue(leafCallbacks
,
670 kSecPolicyCheckSubjectOrganization
,
671 SecPolicyCheckCertSubjectOrganization
);
672 CFDictionaryAddValue(leafCallbacks
,
673 kSecPolicyCheckSubjectOrganizationalUnit
,
674 SecPolicyCheckCertSubjectOrganizationalUnit
);
675 CFDictionaryAddValue(leafCallbacks
,
676 kSecPolicyCheckEAPTrustedServerNames
,
677 SecPolicyCheckCertEAPTrustedServerNames
);
678 CFDictionaryAddValue(leafCallbacks
,
679 kSecPolicyCheckSubjectCommonNameTEST
,
680 SecPolicyCheckCertSubjectCommonNameTEST
);
681 CFDictionaryAddValue(leafCallbacks
,
682 kSecPolicyCheckLeafMarkerOid
,
683 SecPolicyCheckCertLeafMarkerOid
);
684 CFDictionaryAddValue(leafCallbacks
,
685 kSecPolicyCheckLeafMarkerOidWithoutValueCheck
,
686 SecPolicyCheckCertLeafMarkerOidWithoutValueCheck
);
687 CFDictionaryAddValue(leafCallbacks
,
688 kSecPolicyCheckCertificatePolicy
,
689 SecPolicyCheckCertCertificatePolicyOid
);
690 CFDictionaryAddValue(leafCallbacks
,
691 kSecPolicyCheckWeakLeaf
,
692 SecPolicyCheckCertWeak
);
693 CFDictionaryAddValue(leafCallbacks
,
694 kSecPolicyCheckKeySize
,
695 SecPolicyCheckCertKeySize
);
696 CFDictionaryAddValue(leafCallbacks
,
697 kSecPolicyCheckSignatureHashAlgorithms
,
698 SecPolicyCheckCertSignatureHashAlgorithms
);
700 return leafCallbacks
;
703 void SecLeafPVCInit(SecLeafPVCRef pvc
, SecCertificateRef leaf
, CFArrayRef policies
,
704 CFAbsoluteTime verifyTime
) {
705 secdebug("alloc", "%p", pvc
);
706 // Weird logging policies crashes.
707 //secdebug("policy", "%@", policies);
708 pvc
->leaf
= CFRetainSafe(leaf
);
709 pvc
->policies
= CFRetainSafe(policies
);
710 pvc
->verifyTime
= verifyTime
;
711 pvc
->callbacks
= SecLeafPVCCopyCallbacks();
715 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
716 &kCFTypeDictionaryValueCallBacks
);
717 pvc
->details
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
, 1,
718 &kCFTypeArrayCallBacks
);
719 CFRelease(certDetail
);
723 void SecLeafPVCDelete(SecLeafPVCRef pvc
) {
724 secdebug("alloc", "%p", pvc
);
725 CFReleaseNull(pvc
->policies
);
726 CFReleaseNull(pvc
->details
);
727 CFReleaseNull(pvc
->callbacks
);
728 CFReleaseNull(pvc
->leaf
);
731 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc
,
732 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
734 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
, "leaf",
735 (force
? "force" : ""), result
);
737 /* If this is not something the current policy cares about ignore
738 this error and return true so our caller continues evaluation. */
740 /* @@@ The right long term fix might be to check if none of the passed
741 in policies contain this key, since not all checks are run for all
743 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
744 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
748 /* @@@ Check to see if the SecTrustSettings for the certificate in question
749 tell us to ignore this error. */
754 CFMutableDictionaryRef detail
=
755 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
757 /* Perhaps detail should have an array of results per key? As it stands
758 in the case of multiple policy failures the last failure stands. */
759 CFDictionarySetValue(detail
, key
, result
);
764 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc
,
765 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
766 return SecLeafPVCSetResultForced(pvc
, key
, ix
, result
, false);
769 static void SecLeafPVCValidateKey(const void *key
, const void *value
,
771 SecLeafPVCRef pvc
= (SecLeafPVCRef
)context
;
773 /* If our caller doesn't want full details and we failed earlier there is
774 no point in doing additional checks. */
775 if (!pvc
->result
&& !pvc
->details
)
778 SecPolicyCheckCertFunction fcn
= (SecPolicyCheckCertFunction
) CFDictionaryGetValue(pvc
->callbacks
, key
);
784 /* kSecPolicyCheckValidLeaf is special */
785 if (CFEqual(key
, kSecPolicyCheckValidLeaf
)) {
786 CFDateRef verifyDate
= CFDateCreate(NULL
, pvc
->verifyTime
);
787 if(!fcn(pvc
->leaf
, verifyDate
)) {
788 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
790 CFReleaseSafe(verifyDate
);
792 /* get pvcValue from current policy */
793 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
798 CFTypeRef pvcValue
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, key
);
799 if(!fcn(pvc
->leaf
, pvcValue
)) {
800 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
805 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc
) {
807 CFArrayRef policies
= pvc
->policies
;
808 CFIndex ix
, count
= CFArrayGetCount(policies
);
809 for (ix
= 0; ix
< count
; ++ix
) {
810 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
812 /* Validate all keys for all policies. */
813 CFDictionaryApplyFunction(policy
->_options
, SecLeafPVCValidateKey
, pvc
);
814 if (!pvc
->result
&& !pvc
->details
)