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/SecPolicyPriv.h>
31 #include <Security/SecPolicyInternal.h>
32 #include <Security/SecCertificateInternal.h>
33 #include <utilities/SecCFWrappers.h>
34 #include <utilities/SecInternalReleasePriv.h>
37 #include <libDER/oids.h>
40 * MARK: SecPolicyCheckCert Functions
41 * All SecPolicyCheckCert* return false if the cert fails the check and true if it succeeds.
44 typedef bool (*SecPolicyCheckCertFunction
)(SecCertificateRef cert
, CFTypeRef pvcValue
);
46 /* This one is different from SecPolicyCheckCriticalExtensions because
47 that one is an empty stub. The CriticalExtensions check is done in
48 SecPolicyCheckBasicCertificateProcessing. */
49 bool SecPolicyCheckCertCriticalExtensions(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
50 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
51 /* Certificate contains one or more unknown critical extensions. */
57 static bool keyusage_allows(SecKeyUsage keyUsage
, CFTypeRef xku
) {
58 if (!xku
|| CFGetTypeID(xku
) != CFNumberGetTypeID())
62 CFNumberGetValue((CFNumberRef
)xku
, kCFNumberSInt32Type
, &dku
);
63 SecKeyUsage ku
= (SecKeyUsage
)dku
;
64 return (keyUsage
& ku
) == ku
;
67 bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
68 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
70 CFTypeRef xku
= pvcValue
;
72 CFIndex ix
, count
= CFArrayGetCount(xku
);
73 for (ix
= 0; ix
< count
; ++ix
) {
74 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
75 if (keyusage_allows(keyUsage
, ku
)) {
81 match
= keyusage_allows(keyUsage
, xku
);
86 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
90 if (extendedKeyUsage
) {
91 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
92 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
94 /* Certificate has no extended key usage, only a match if the policy
95 contains a 0 length CFDataRef. */
96 return CFDataGetLength((CFDataRef
)xeku
) == 0;
100 static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage
,
105 if(CFGetTypeID(xeku
) == CFDataGetTypeID()) {
106 return extendedkeyusage_allows(extendedKeyUsage
, xeku
);
107 } else if (CFGetTypeID(xeku
) == CFStringGetTypeID()) {
108 CFDataRef eku
= SecCertificateCreateOidDataFromString(NULL
, xeku
);
110 bool result
= extendedkeyusage_allows(extendedKeyUsage
, eku
);
118 bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
119 CFArrayRef certExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(cert
);
121 CFTypeRef xeku
= pvcValue
;
123 CFIndex ix
, count
= CFArrayGetCount(xeku
);
124 for (ix
= 0; ix
< count
; ix
++) {
125 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
126 if (isExtendedKeyUsageAllowed(certExtendedKeyUsage
, eku
)) {
132 match
= isExtendedKeyUsageAllowed(certExtendedKeyUsage
, xeku
);
134 CFReleaseSafe(certExtendedKeyUsage
);
138 bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
139 /* If the certificate has a subject, or
140 if it doesn't, and it's the leaf and not a CA,
141 and also has a critical subjectAltName extension it's valid. */
142 if (!SecCertificateHasSubject(cert
)) {
143 if (SecCertificateIsCA(cert
)) {
144 /* CA certificate has empty subject. */
147 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
148 /* Leaf certificate with empty subject does not have
149 a critical subject alt name extension. */
157 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
158 more labels. Use CFNetwork's function for determining if those labels comprise
159 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
160 typedef bool (*CFNIsTLD_f
)(CFStringRef domain
);
161 static bool SecDNSIsTLD(CFStringRef reference
) {
162 bool result
= false; /* fail open for allocation and symbol lookup failures */
163 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr
= NULL
;
164 static dispatch_once_t onceToken
;
165 CFStringRef presentedDomain
= NULL
;
167 dispatch_once(&onceToken
, ^{
168 void *framework
= dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY
);
170 CFNIsDomainTopLevelFunctionPtr
= dlsym(framework
, "_CFHostIsDomainTopLevelForCertificatePolicy");
174 require_quiet(CFNIsDomainTopLevelFunctionPtr
, out
);
175 CFIndex referenceLen
= CFStringGetLength(reference
);
177 /* reference identifier is too short, we should fail it */
178 require_action_quiet(referenceLen
> 2, out
, result
= true);
180 require_quiet(presentedDomain
= CFStringCreateWithSubstring(NULL
, reference
,
181 CFRangeMake(2, referenceLen
- 2)),
183 result
= CFNIsDomainTopLevelFunctionPtr(presentedDomain
);
186 CFReleaseNull(presentedDomain
);
190 /* Compare hostname, to a server name obtained from the server's cert
191 Obtained from the SubjectAltName or the CommonName entry in the Subject.
192 Limited wildcard checking is performed here as outlined in RFC 6125
195 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
196 never to accept partial-label wildcards even though they are allowed by
199 We use the language from RFC 6125, particularly the following definitions:
201 presented identifier: An identifier that is presented by a server to
202 a client within a PKIX certificate when the client attempts to
203 establish secure communication with the server; the certificate
204 can include one or more presented identifiers of different types,
205 and if the server hosts more than one domain then the certificate
206 might present distinct identifiers for each domain.
208 reference identifier: An identifier, constructed from a source
209 domain and optionally an application service type, used by the
210 client for matching purposes when examining presented identifiers.
213 static bool SecDNSMatch(CFStringRef reference
, CFStringRef presented
) {
214 CFArrayRef referenceLabels
= NULL
, presentedLabels
= NULL
;
217 /* A trailing '.' in the reference identifier is allowed as a mechanism
218 to force TLS renegotiation. Strip it before parsing labels. */
219 CFIndex referenceLen
= CFStringGetLength(reference
);
220 require_quiet(referenceLen
> 0, noMatch
);
221 if ('.' == CFStringGetCharacterAtIndex(reference
, referenceLen
- 1)) {
222 CFStringRef truncatedReference
= CFStringCreateWithSubstring(NULL
, reference
,
223 CFRangeMake(0, referenceLen
- 1));
224 referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, truncatedReference
, CFSTR("."));
225 CFReleaseNull(truncatedReference
);
226 require_quiet(referenceLabels
, noMatch
);
228 require_quiet(referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, reference
, CFSTR(".")),
232 require_quiet(presentedLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, presented
, CFSTR(".")),
235 /* Reference Identifier and Presented Identifier must have the same number of labels
236 because a wildcard in the presented identifier can only match a single label in the
237 reference identifier. */
238 require_quiet(CFArrayGetCount(referenceLabels
) == CFArrayGetCount(presentedLabels
), noMatch
);
240 CFIndex ix
, count
= CFArrayGetCount(referenceLabels
);
241 for (ix
= count
- 1; ix
>= 0; ix
--) {
242 CFStringRef rlabel
= NULL
, plabel
= NULL
;
243 require_quiet(rlabel
= CFArrayGetValueAtIndex(referenceLabels
, ix
), noMatch
);
244 require_quiet(plabel
= CFArrayGetValueAtIndex(presentedLabels
, ix
), noMatch
);
245 if (CFEqual(plabel
, CFSTR("*"))) {
246 /* must only occur in left-most label */
247 require_quiet(ix
== 0, noMatch
);
249 /* must not occur before single-label TLD */
250 require_quiet(count
> 2 && ix
!= count
- 2, noMatch
);
252 /* must not occur before a multi-label gTLD */
253 require_quiet(!SecDNSIsTLD(presented
), noMatch
);
255 /* partial-label wildcards are disallowed */
256 CFRange partialRange
= CFStringFind(plabel
, CFSTR("*"), 0);
257 require_quiet(partialRange
.location
== kCFNotFound
&& partialRange
.length
== 0 ,
260 /* not a wildcard, so labels must match exactly */
261 require_quiet(CFStringCompare(rlabel
, plabel
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
, noMatch
);
268 CFReleaseNull(referenceLabels
);
269 CFReleaseNull(presentedLabels
);
273 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert
, CFTypeRef pvcValue
) {
274 /* @@@ Consider what to do if the caller passes in no hostname. Should
275 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
276 CFStringRef hostName
= pvcValue
;
277 if (!isString(hostName
)) {
278 /* @@@ We can't return an error here and making the evaluation fail
279 won't help much either. */
283 bool dnsMatch
= false;
284 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
286 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
287 for (ix
= 0; ix
< count
; ++ix
) {
288 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
289 if (SecDNSMatch(hostName
, dns
)) {
298 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
299 the values returned by SecCertificateCopyIPAddresses() instead. */
300 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(cert
);
302 CFIndex ix
, count
= CFArrayGetCount(ipAddresses
);
303 for (ix
= 0; ix
< count
; ++ix
) {
304 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
305 if (!CFStringCompare(hostName
, ipAddress
, kCFCompareCaseInsensitive
)) {
310 CFRelease(ipAddresses
);
317 bool SecPolicyCheckCertEmail(SecCertificateRef cert
, CFTypeRef pvcValue
) {
318 CFStringRef email
= pvcValue
;
320 if (!isString(email
)) {
321 /* We can't return an error here and making the evaluation fail
322 won't help much either. */
326 CFArrayRef addrs
= SecCertificateCopyRFC822Names(cert
);
328 CFIndex ix
, count
= CFArrayGetCount(addrs
);
329 for (ix
= 0; ix
< count
; ++ix
) {
330 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
331 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
342 bool SecPolicyCheckCertTemporalValidity(SecCertificateRef cert
, CFTypeRef pvcValue
) {
343 CFAbsoluteTime verifyTime
= CFDateGetAbsoluteTime(pvcValue
);
344 if (!SecCertificateIsValid(cert
, verifyTime
)) {
345 /* Leaf certificate has expired. */
351 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert
, CFTypeRef pvcValue
) {
352 CFStringRef prefix
= pvcValue
;
354 if (!isString(prefix
)) {
355 /* @@@ We can't return an error here and making the evaluation fail
356 won't help much either. */
359 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
360 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
361 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
362 /* Common Name prefix mismatch. */
365 CFReleaseSafe(commonNames
);
369 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert
, CFTypeRef pvcValue
) {
370 CFStringRef common_name
= pvcValue
;
372 if (!isString(common_name
)) {
373 /* @@@ We can't return an error here and making the evaluation fail
374 won't help much either. */
377 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
378 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
379 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
380 /* Common Name mismatch. */
383 CFReleaseSafe(commonNames
);
387 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert
, CFTypeRef pvcValue
) {
388 CFStringRef common_name
= pvcValue
;
390 if (!isString(common_name
)) {
391 /* @@@ We can't return an error here and making the evaluation fail
392 won't help much either. */
395 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
396 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
397 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
398 CFStringRef test_common_name
= common_name
?
399 CFStringCreateWithFormat(kCFAllocatorDefault
,
400 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
402 if (!CFEqual(common_name
, cert_common_name
) &&
403 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
404 /* Common Name mismatch. */
406 CFReleaseSafe(test_common_name
);
408 CFReleaseSafe(commonNames
);
412 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert
, CFTypeRef pvcValue
) {
413 CFDateRef date
= pvcValue
;
415 /* @@@ We can't return an error here and making the evaluation fail
416 won't help much either. */
419 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
420 if (SecCertificateNotValidBefore(cert
) <= at
) {
421 /* Leaf certificate has not valid before that is too old. */
427 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert
, CFTypeRef pvcValue
) {
428 CFStringRef org
= pvcValue
;
430 if (!isString(org
)) {
431 /* @@@ We can't return an error here and making the evaluation fail
432 won't help much either. */
435 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
436 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
437 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
438 /* Leaf Subject Organization mismatch. */
441 CFReleaseSafe(organization
);
445 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert
, CFTypeRef pvcValue
) {
446 CFStringRef orgUnit
= pvcValue
;
448 if (!isString(orgUnit
)) {
449 /* @@@ We can't return an error here and making the evaluation fail
450 won't help much either. */
453 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
454 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
455 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
456 /* Leaf Subject Organizational Unit mismatch. */
459 CFReleaseSafe(organizationalUnit
);
463 bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert
, CFTypeRef pvcValue
) {
464 CFStringRef country
= pvcValue
;
466 if (!isString(country
)) {
467 /* @@@ We can't return an error here and making the evaluation fail
468 won't help much either. */
471 CFArrayRef certCountry
= SecCertificateCopyCountry(cert
);
472 if (!certCountry
|| CFArrayGetCount(certCountry
) != 1 ||
473 !CFEqual(country
, CFArrayGetValueAtIndex(certCountry
, 0))) {
474 /* Subject Country mismatch. */
477 CFReleaseSafe(certCountry
);
481 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert
, CFTypeRef pvcValue
) {
482 CFArrayRef trustedServerNames
= pvcValue
;
483 /* No names specified means we accept any name. */
484 if (!trustedServerNames
)
486 if (!isArray(trustedServerNames
)) {
487 /* @@@ We can't return an error here and making the evaluation fail
488 won't help much either. */
492 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
493 bool dnsMatch
= false;
494 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
496 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
497 // @@@ This is O(N^2) unfortunately we can't do better easily unless
498 // we don't do wildcard matching. */
499 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
500 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
502 for (tix
= 0; tix
< tsnCount
; ++tix
) {
503 CFStringRef serverName
=
504 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
505 if (!isString(serverName
)) {
506 /* @@@ We can't return an error here and making the
507 evaluation fail won't help much either. */
508 CFReleaseSafe(dnsNames
);
511 /* we purposefully reverse the arguments here such that dns names
512 from the cert are matched against a server name list, where
513 the server names list can contain wildcards and the dns name
514 cannot. References: http://support.microsoft.com/kb/941123
515 It's easy to find occurrences where people tried to use
516 wildcard certificates and were told that those don't work
518 if (SecDNSMatch(dns
, serverName
)) {
530 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
531 if (pvcValue
&& SecCertificateHasMarkerExtension(cert
, pvcValue
)) {
538 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert
,
539 CFTypeRef pvcValue
) {
540 if (CFGetTypeID(pvcValue
) == CFArrayGetTypeID()) {
541 CFIndex ix
, length
= CFArrayGetCount(pvcValue
);
542 for (ix
= 0; ix
< length
; ix
++)
543 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
,
544 CFArrayGetValueAtIndex((CFArrayRef
)pvcValue
, ix
))) {
547 } else if (CFGetTypeID(pvcValue
) == CFDataGetTypeID() ||
548 CFGetTypeID(pvcValue
) == CFStringGetTypeID()) {
549 return (NULL
!= SecCertificateGetExtensionValue(cert
, pvcValue
));
555 * The value is a dictionary. The dictionary contains keys indicating
556 * whether the value is for Prod or QA. The values are the same as
557 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
559 bool SecPolicyCheckCertLeafMarkersProdAndQA(SecCertificateRef cert
, CFTypeRef pvcValue
)
561 CFTypeRef prodValue
= CFDictionaryGetValue(pvcValue
, kSecPolicyLeafMarkerProd
);
563 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
570 static CFSetRef
copyCertificatePolicies(SecCertificateRef cert
) {
571 CFMutableSetRef policies
= NULL
;
572 policies
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
573 if (!policies
) return NULL
;
575 const SecCECertificatePolicies
*cp
= SecCertificateGetCertificatePolicies(cert
);
576 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
577 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
578 CFDataRef oidData
= NULL
;
579 DERItem
*policyOID
= &cp
->policies
[policy_ix
].policyIdentifier
;
580 oidData
= CFDataCreate(kCFAllocatorDefault
, policyOID
->data
, policyOID
->length
);
581 CFSetAddValue(policies
, oidData
);
582 CFReleaseSafe(oidData
);
587 static bool checkPolicyOidData(SecCertificateRef cert
, CFDataRef oid
) {
588 CFSetRef policies
= copyCertificatePolicies(cert
);
590 if (policies
&& CFSetContainsValue(policies
, oid
)) {
593 CFReleaseSafe(policies
);
597 /* This one is different from SecPolicyCheckCertificatePolicyOid because
598 that one checks the whole chain. (And uses policy_set_t...) */
599 bool SecPolicyCheckCertCertificatePolicy(SecCertificateRef cert
, CFTypeRef pvcValue
) {
600 CFTypeRef value
= pvcValue
;
603 if (CFGetTypeID(value
) == CFDataGetTypeID())
605 result
= checkPolicyOidData(cert
, value
);
606 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
607 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
609 result
= checkPolicyOidData(cert
, dataOid
);
616 bool SecPolicyCheckCertWeakKeySize(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
617 if (cert
&& SecCertificateIsWeakKey(cert
)) {
618 /* Leaf certificate has a weak key. */
624 bool SecPolicyCheckCertKeySize(SecCertificateRef cert
, CFTypeRef pvcValue
) {
625 CFDictionaryRef keySizes
= pvcValue
;
626 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
632 bool SecPolicyCheckCertWeakSignature(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
634 CFMutableSetRef disallowedHashes
= CFSetCreateMutable(NULL
, 3, &kCFTypeSetCallBacks
);
635 if (!disallowedHashes
) {
638 CFSetAddValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD2
);
639 CFSetAddValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD4
);
640 CFSetAddValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD5
);
641 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashes
)) {
644 CFReleaseSafe(disallowedHashes
);
648 static CFStringRef
convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum
) {
649 const void *digests
[] = { kSecSignatureDigestAlgorithmUnknown
,
650 kSecSignatureDigestAlgorithmMD2
,
651 kSecSignatureDigestAlgorithmMD4
,
652 kSecSignatureDigestAlgorithmMD5
,
653 kSecSignatureDigestAlgorithmSHA1
,
654 kSecSignatureDigestAlgorithmSHA224
,
655 kSecSignatureDigestAlgorithmSHA256
,
656 kSecSignatureDigestAlgorithmSHA384
,
657 kSecSignatureDigestAlgorithmSHA512
,
659 return digests
[algorithmEnum
];
662 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert
, CFTypeRef pvcValue
) {
663 CFSetRef disallowedHashAlgorithms
= pvcValue
;
664 CFStringRef certAlg
= convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert
));
665 if (CFSetContainsValue(disallowedHashAlgorithms
, certAlg
)) {
672 * MARK: SecLeafPVC functions
674 static CFDictionaryRef
SecLeafPVCCopyCallbacks(void) {
675 CFMutableDictionaryRef leafCallbacks
= NULL
;
676 leafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
677 &kCFTypeDictionaryKeyCallBacks
, NULL
);
679 #undef POLICYCHECKMACRO
680 #define __PC_ADD_CHECK_(NAME)
681 #define __PC_ADD_CHECK_O(NAME) CFDictionaryAddValue(leafCallbacks, \
682 kSecPolicyCheck##NAME, SecPolicyCheckCert##NAME);
684 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
685 __PC_ADD_CHECK_##LEAFONLY(NAME)
686 #include "SecPolicyChecks.list"
688 return leafCallbacks
;
691 void SecLeafPVCInit(SecLeafPVCRef pvc
, SecCertificateRef leaf
, CFArrayRef policies
,
692 CFAbsoluteTime verifyTime
) {
693 secdebug("alloc", "%p", pvc
);
694 // Weird logging policies crashes.
695 //secdebug("policy", "%@", policies);
696 pvc
->leaf
= CFRetainSafe(leaf
);
697 pvc
->policies
= CFRetainSafe(policies
);
698 pvc
->verifyTime
= verifyTime
;
699 pvc
->callbacks
= SecLeafPVCCopyCallbacks();
703 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
704 &kCFTypeDictionaryValueCallBacks
);
705 pvc
->details
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
, 1,
706 &kCFTypeArrayCallBacks
);
707 CFRelease(certDetail
);
711 void SecLeafPVCDelete(SecLeafPVCRef pvc
) {
712 secdebug("alloc", "%p", pvc
);
713 CFReleaseNull(pvc
->policies
);
714 CFReleaseNull(pvc
->details
);
715 CFReleaseNull(pvc
->callbacks
);
716 CFReleaseNull(pvc
->leaf
);
719 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc
,
720 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
722 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
, "leaf",
723 (force
? "force" : ""), result
);
725 /* If this is not something the current policy cares about ignore
726 this error and return true so our caller continues evaluation. */
728 /* @@@ The right long term fix might be to check if none of the passed
729 in policies contain this key, since not all checks are run for all
731 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
732 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
736 /* @@@ Check to see if the SecTrustSettings for the certificate in question
737 tell us to ignore this error. */
742 CFMutableDictionaryRef detail
=
743 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
745 /* Perhaps detail should have an array of results per key? As it stands
746 in the case of multiple policy failures the last failure stands. */
747 CFDictionarySetValue(detail
, key
, result
);
752 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc
,
753 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
754 return SecLeafPVCSetResultForced(pvc
, key
, ix
, result
, false);
757 static void SecLeafPVCValidateKey(const void *key
, const void *value
,
759 SecLeafPVCRef pvc
= (SecLeafPVCRef
)context
;
761 /* If our caller doesn't want full details and we failed earlier there is
762 no point in doing additional checks. */
763 if (!pvc
->result
&& !pvc
->details
)
766 SecPolicyCheckCertFunction fcn
= (SecPolicyCheckCertFunction
) CFDictionaryGetValue(pvc
->callbacks
, key
);
772 /* kSecPolicyCheckTemporalValidity is special */
773 if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
774 CFDateRef verifyDate
= CFDateCreate(NULL
, pvc
->verifyTime
);
775 if(!fcn(pvc
->leaf
, verifyDate
)) {
776 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
778 CFReleaseSafe(verifyDate
);
780 /* get pvcValue from current policy */
781 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
786 CFTypeRef pvcValue
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, key
);
787 if(!fcn(pvc
->leaf
, pvcValue
)) {
788 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
793 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc
) {
795 CFArrayRef policies
= pvc
->policies
;
796 CFIndex ix
, count
= CFArrayGetCount(policies
);
797 for (ix
= 0; ix
< count
; ++ix
) {
798 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
800 /* Validate all keys for all policies. */
801 CFDictionaryApplyFunction(policy
->_options
, SecLeafPVCValidateKey
, pvc
);
802 if (!pvc
->result
&& !pvc
->details
)