2 * Copyright (c) 2008-2019 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 <Security/SecFramework.h>
34 #include <utilities/SecCFWrappers.h>
35 #include <utilities/SecInternalReleasePriv.h>
38 #include <libDER/oids.h>
41 * MARK: SecPolicyCheckCert Functions
42 * All SecPolicyCheckCert* return false if the cert fails the check and true if it succeeds.
45 typedef bool (*SecPolicyCheckCertFunction
)(SecCertificateRef cert
, CFTypeRef pvcValue
);
47 /* This one is different from SecPolicyCheckCriticalExtensions because
48 that one is an empty stub. The CriticalExtensions check is done in
49 SecPolicyCheckBasicCertificateProcessing. */
50 bool SecPolicyCheckCertCriticalExtensions(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
51 if (SecCertificateHasUnknownCriticalExtension(cert
)) {
52 /* Certificate contains one or more unknown critical extensions. */
58 static bool keyusage_allows(SecKeyUsage keyUsage
, CFTypeRef xku
) {
59 if (!xku
|| CFGetTypeID(xku
) != CFNumberGetTypeID())
63 CFNumberGetValue((CFNumberRef
)xku
, kCFNumberSInt32Type
, &dku
);
64 SecKeyUsage ku
= (SecKeyUsage
)dku
;
65 /* kSecKeyUsageUnspecified is a special sentinel for allowing a missing KU extension */
66 if (ku
== kSecKeyUsageUnspecified
) {
67 return (keyUsage
== ku
);
69 return (keyUsage
& ku
) == ku
;
72 bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
73 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
75 CFTypeRef xku
= pvcValue
;
77 CFIndex ix
, count
= CFArrayGetCount(xku
);
78 for (ix
= 0; ix
< count
; ++ix
) {
79 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
80 if (keyusage_allows(keyUsage
, ku
)) {
86 match
= keyusage_allows(keyUsage
, xku
);
91 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
95 if (extendedKeyUsage
) {
96 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
97 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
99 /* Certificate has no extended key usage, only a match if the policy
100 contains a 0 length CFDataRef. */
101 return CFDataGetLength((CFDataRef
)xeku
) == 0;
105 static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage
,
110 if(CFGetTypeID(xeku
) == CFDataGetTypeID()) {
111 return extendedkeyusage_allows(extendedKeyUsage
, xeku
);
112 } else if (CFGetTypeID(xeku
) == CFStringGetTypeID()) {
113 CFDataRef eku
= SecCertificateCreateOidDataFromString(NULL
, xeku
);
115 bool result
= extendedkeyusage_allows(extendedKeyUsage
, eku
);
123 bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
124 CFArrayRef certExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(cert
);
126 CFTypeRef xeku
= pvcValue
;
128 CFIndex ix
, count
= CFArrayGetCount(xeku
);
129 for (ix
= 0; ix
< count
; ix
++) {
130 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
131 if (isExtendedKeyUsageAllowed(certExtendedKeyUsage
, eku
)) {
137 match
= isExtendedKeyUsageAllowed(certExtendedKeyUsage
, xeku
);
139 CFReleaseSafe(certExtendedKeyUsage
);
143 bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
144 /* If the certificate has a subject, or
145 if it doesn't, and it's the leaf and not a CA,
146 and also has a critical subjectAltName extension it's valid. */
147 if (!SecCertificateHasSubject(cert
)) {
148 if (SecCertificateIsCA(cert
)) {
149 /* CA certificate has empty subject. */
152 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
153 /* Leaf certificate with empty subject does not have
154 a critical subject alt name extension. */
162 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
163 more labels. Use CFNetwork's function for determining if those labels comprise
164 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
165 typedef bool (*CFNIsTLD_f
)(CFStringRef domain
);
166 bool SecDNSIsTLD(CFStringRef reference
) {
167 bool result
= false; /* fail open for allocation and symbol lookup failures */
168 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr
= NULL
;
169 static dispatch_once_t onceToken
;
170 CFStringRef presentedDomain
= NULL
;
172 dispatch_once(&onceToken
, ^{
173 void *framework
= dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY
);
175 CFNIsDomainTopLevelFunctionPtr
= dlsym(framework
, "_CFHostIsDomainTopLevelForCertificatePolicy");
179 require_quiet(CFNIsDomainTopLevelFunctionPtr
, out
);
180 CFIndex referenceLen
= CFStringGetLength(reference
);
182 /* reference identifier is too short, we should fail it */
183 require_action_quiet(referenceLen
> 2, out
, result
= true);
185 require_quiet(presentedDomain
= CFStringCreateWithSubstring(NULL
, reference
,
186 CFRangeMake(2, referenceLen
- 2)),
188 result
= CFNIsDomainTopLevelFunctionPtr(presentedDomain
);
191 CFReleaseNull(presentedDomain
);
195 /* Compare hostname, to a server name obtained from the server's cert
196 Obtained from the SubjectAltName or the CommonName entry in the Subject.
197 Limited wildcard checking is performed here as outlined in RFC 6125
200 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
201 never to accept partial-label wildcards even though they are allowed by
204 We use the language from RFC 6125, particularly the following definitions:
206 presented identifier: An identifier that is presented by a server to
207 a client within a PKIX certificate when the client attempts to
208 establish secure communication with the server; the certificate
209 can include one or more presented identifiers of different types,
210 and if the server hosts more than one domain then the certificate
211 might present distinct identifiers for each domain.
213 reference identifier: An identifier, constructed from a source
214 domain and optionally an application service type, used by the
215 client for matching purposes when examining presented identifiers.
218 static bool SecDNSMatch(CFStringRef reference
, CFStringRef presented
) {
219 CFArrayRef referenceLabels
= NULL
, presentedLabels
= NULL
;
222 /* A trailing '.' in the reference identifier is allowed as a mechanism
223 to force TLS renegotiation. Strip it before parsing labels. */
224 CFIndex referenceLen
= CFStringGetLength(reference
);
225 require_quiet(referenceLen
> 0, noMatch
);
226 if ('.' == CFStringGetCharacterAtIndex(reference
, referenceLen
- 1)) {
227 CFStringRef truncatedReference
= CFStringCreateWithSubstring(NULL
, reference
,
228 CFRangeMake(0, referenceLen
- 1));
229 referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, truncatedReference
, CFSTR("."));
230 CFReleaseNull(truncatedReference
);
231 require_quiet(referenceLabels
, noMatch
);
233 require_quiet(referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, reference
, CFSTR(".")),
237 require_quiet(presentedLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, presented
, CFSTR(".")),
240 /* Reference Identifier and Presented Identifier must have the same number of labels
241 because a wildcard in the presented identifier can only match a single label in the
242 reference identifier. */
243 require_quiet(CFArrayGetCount(referenceLabels
) == CFArrayGetCount(presentedLabels
), noMatch
);
245 CFIndex ix
, count
= CFArrayGetCount(referenceLabels
);
246 for (ix
= count
- 1; ix
>= 0; ix
--) {
247 CFStringRef rlabel
= NULL
, plabel
= NULL
;
248 require_quiet(rlabel
= CFArrayGetValueAtIndex(referenceLabels
, ix
), noMatch
);
249 require_quiet(plabel
= CFArrayGetValueAtIndex(presentedLabels
, ix
), noMatch
);
250 if (CFEqual(plabel
, CFSTR("*"))) {
251 /* must only occur in left-most label */
252 require_quiet(ix
== 0, noMatch
);
254 /* must not occur before single-label TLD */
255 require_quiet(count
> 2 && ix
!= count
- 2, noMatch
);
257 /* must not occur before a multi-label gTLD */
258 require_quiet(!SecDNSIsTLD(presented
), noMatch
);
260 /* partial-label wildcards are disallowed */
261 CFRange partialRange
= CFStringFind(plabel
, CFSTR("*"), 0);
262 require_quiet(partialRange
.location
== kCFNotFound
&& partialRange
.length
== 0 ,
265 /* not a wildcard, so labels must match exactly */
266 require_quiet(CFStringCompare(rlabel
, plabel
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
, noMatch
);
273 CFReleaseNull(referenceLabels
);
274 CFReleaseNull(presentedLabels
);
278 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert
, CFTypeRef pvcValue
) {
279 /* @@@ Consider what to do if the caller passes in no hostname. Should
280 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
281 CFStringRef hostName
= pvcValue
;
282 if (!isString(hostName
)) {
283 /* @@@ We can't return an error here and making the evaluation fail
284 won't help much either. */
288 bool dnsMatch
= false;
289 CFArrayRef dnsNames
= SecCertificateCopyDNSNamesFromSAN(cert
);
291 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
292 for (ix
= 0; ix
< count
; ++ix
) {
293 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
294 if (SecDNSMatch(hostName
, dns
)) {
303 /* Check whether hostname is an IPv4 or IPv6 address */
304 CFDataRef hostIPData
= SecFrameworkCopyIPAddressData(hostName
);
306 /* Check address against IP addresses in the SAN extension,
307 obtained by SecCertificateCopyIPAddresses. Comparisons
308 must always use the canonical data representation of the
309 address, since string notation may omit zeros, etc. */
310 CFArrayRef ipAddresses
= SecCertificateCopyIPAddressDatas(cert
);
311 CFIndex ix
, count
= (ipAddresses
) ? CFArrayGetCount(ipAddresses
) : 0;
312 for (ix
= 0; ix
< count
&& !dnsMatch
; ++ix
) {
313 CFDataRef ipAddress
= (CFDataRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
314 if (CFEqualSafe(hostIPData
, ipAddress
)) {
318 CFReleaseSafe(ipAddresses
);
319 CFReleaseSafe(hostIPData
);
326 bool SecPolicyCheckCertEmail(SecCertificateRef cert
, CFTypeRef pvcValue
) {
327 CFStringRef email
= pvcValue
;
329 if (!isString(email
)) {
330 /* We can't return an error here and making the evaluation fail
331 won't help much either. */
335 CFArrayRef addrs
= SecCertificateCopyRFC822Names(cert
);
337 CFIndex ix
, count
= CFArrayGetCount(addrs
);
338 for (ix
= 0; ix
< count
; ++ix
) {
339 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
340 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
351 bool SecPolicyCheckCertTemporalValidity(SecCertificateRef cert
, CFTypeRef pvcValue
) {
352 CFAbsoluteTime verifyTime
= CFDateGetAbsoluteTime(pvcValue
);
353 if (!SecCertificateIsValid(cert
, verifyTime
)) {
354 /* Leaf certificate has expired. */
360 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert
, CFTypeRef pvcValue
) {
361 CFStringRef prefix
= pvcValue
;
363 if (!isString(prefix
)) {
364 /* @@@ We can't return an error here and making the evaluation fail
365 won't help much either. */
368 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
369 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
370 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
371 /* Common Name prefix mismatch. */
374 CFReleaseSafe(commonNames
);
378 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert
, CFTypeRef pvcValue
) {
379 CFStringRef common_name
= pvcValue
;
381 if (!isString(common_name
)) {
382 /* @@@ We can't return an error here and making the evaluation fail
383 won't help much either. */
386 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
387 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
388 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
389 /* Common Name mismatch. */
392 CFReleaseSafe(commonNames
);
396 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert
, CFTypeRef pvcValue
) {
397 CFStringRef common_name
= pvcValue
;
399 if (!isString(common_name
)) {
400 /* @@@ We can't return an error here and making the evaluation fail
401 won't help much either. */
404 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
405 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
406 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
407 CFStringRef test_common_name
= common_name
?
408 CFStringCreateWithFormat(kCFAllocatorDefault
,
409 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
411 if (!CFEqual(common_name
, cert_common_name
) &&
412 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
413 /* Common Name mismatch. */
415 CFReleaseSafe(test_common_name
);
417 CFReleaseSafe(commonNames
);
421 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert
, CFTypeRef pvcValue
) {
422 CFDateRef date
= pvcValue
;
424 /* @@@ We can't return an error here and making the evaluation fail
425 won't help much either. */
428 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
429 if (SecCertificateNotValidBefore(cert
) <= at
) {
430 /* Leaf certificate has not valid before that is too old. */
436 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert
, CFTypeRef pvcValue
) {
437 CFStringRef org
= pvcValue
;
439 if (!isString(org
)) {
440 /* @@@ We can't return an error here and making the evaluation fail
441 won't help much either. */
444 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
445 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
446 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
447 /* Leaf Subject Organization mismatch. */
450 CFReleaseSafe(organization
);
454 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert
, CFTypeRef pvcValue
) {
455 CFStringRef orgUnit
= pvcValue
;
457 if (!isString(orgUnit
)) {
458 /* @@@ We can't return an error here and making the evaluation fail
459 won't help much either. */
462 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
463 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
464 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
465 /* Leaf Subject Organizational Unit mismatch. */
468 CFReleaseSafe(organizationalUnit
);
472 bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert
, CFTypeRef pvcValue
) {
473 CFStringRef country
= pvcValue
;
475 if (!isString(country
)) {
476 /* @@@ We can't return an error here and making the evaluation fail
477 won't help much either. */
480 CFArrayRef certCountry
= SecCertificateCopyCountry(cert
);
481 if (!certCountry
|| CFArrayGetCount(certCountry
) != 1 ||
482 !CFEqual(country
, CFArrayGetValueAtIndex(certCountry
, 0))) {
483 /* Subject Country mismatch. */
486 CFReleaseSafe(certCountry
);
490 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert
, CFTypeRef pvcValue
) {
491 CFArrayRef trustedServerNames
= pvcValue
;
492 /* No names specified means we accept any name. */
493 if (!trustedServerNames
)
495 if (!isArray(trustedServerNames
)) {
496 /* @@@ We can't return an error here and making the evaluation fail
497 won't help much either. */
501 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
502 bool dnsMatch
= false;
503 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
505 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
506 // @@@ This is O(N^2) unfortunately we can't do better easily unless
507 // we don't do wildcard matching. */
508 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
509 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
511 for (tix
= 0; tix
< tsnCount
; ++tix
) {
512 CFStringRef serverName
=
513 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
514 if (!isString(serverName
)) {
515 /* @@@ We can't return an error here and making the
516 evaluation fail won't help much either. */
517 CFReleaseSafe(dnsNames
);
520 /* we purposefully reverse the arguments here such that dns names
521 from the cert are matched against a server name list, where
522 the server names list can contain wildcards and the dns name
523 cannot. References: http://support.microsoft.com/kb/941123
524 It's easy to find occurrences where people tried to use
525 wildcard certificates and were told that those don't work
527 if (SecDNSMatch(dns
, serverName
)) {
539 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
540 if (pvcValue
&& SecCertificateHasMarkerExtension(cert
, pvcValue
)) {
547 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert
,
548 CFTypeRef pvcValue
) {
549 if (CFGetTypeID(pvcValue
) == CFArrayGetTypeID()) {
550 CFIndex ix
, length
= CFArrayGetCount(pvcValue
);
551 for (ix
= 0; ix
< length
; ix
++)
552 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
,
553 CFArrayGetValueAtIndex((CFArrayRef
)pvcValue
, ix
))) {
556 } else if (CFGetTypeID(pvcValue
) == CFDataGetTypeID() ||
557 CFGetTypeID(pvcValue
) == CFStringGetTypeID()) {
558 return (NULL
!= SecCertificateGetExtensionValue(cert
, pvcValue
));
564 * The value is a dictionary. The dictionary contains keys indicating
565 * whether the value is for Prod or QA. The values are the same as
566 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
568 bool SecPolicyCheckCertLeafMarkersProdAndQA(SecCertificateRef cert
, CFTypeRef pvcValue
)
570 CFTypeRef prodValue
= CFDictionaryGetValue(pvcValue
, kSecPolicyLeafMarkerProd
);
572 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
579 static CFSetRef
copyCertificatePolicies(SecCertificateRef cert
) {
580 CFMutableSetRef policies
= NULL
;
581 policies
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
582 if (!policies
) return NULL
;
584 const SecCECertificatePolicies
*cp
= SecCertificateGetCertificatePolicies(cert
);
585 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
586 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
587 CFDataRef oidData
= NULL
;
588 DERItem
*policyOID
= &cp
->policies
[policy_ix
].policyIdentifier
;
589 oidData
= CFDataCreate(kCFAllocatorDefault
, policyOID
->data
, policyOID
->length
);
590 CFSetAddValue(policies
, oidData
);
591 CFReleaseSafe(oidData
);
596 static bool checkPolicyOidData(SecCertificateRef cert
, CFDataRef oid
) {
597 CFSetRef policies
= copyCertificatePolicies(cert
);
599 if (policies
&& CFSetContainsValue(policies
, oid
)) {
602 CFReleaseSafe(policies
);
606 /* This one is different from SecPolicyCheckCertificatePolicyOid because
607 that one checks the whole chain. (And uses policy_set_t...) */
608 bool SecPolicyCheckCertCertificatePolicy(SecCertificateRef cert
, CFTypeRef pvcValue
) {
609 CFTypeRef value
= pvcValue
;
612 if (CFGetTypeID(value
) == CFDataGetTypeID())
614 result
= checkPolicyOidData(cert
, value
);
615 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
616 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
618 result
= checkPolicyOidData(cert
, dataOid
);
625 bool SecPolicyCheckCertWeakKeySize(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
626 if (cert
&& SecCertificateIsWeakKey(cert
)) {
627 /* Leaf certificate has a weak key. */
633 bool SecPolicyCheckCertKeySize(SecCertificateRef cert
, CFTypeRef pvcValue
) {
634 CFDictionaryRef keySizes
= pvcValue
;
635 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
641 bool SecPolicyCheckCertWeakSignature(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
643 CFMutableArrayRef disallowedHashes
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
);
644 if (!disallowedHashes
) {
647 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD2
);
648 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD4
);
649 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD5
);
651 /* Weak Signature failures only for non-self-signed certs */
652 Boolean isSelfSigned
= false;
653 OSStatus status
= SecCertificateIsSelfSigned(cert
, &isSelfSigned
);
654 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashes
) && (status
!= errSecSuccess
|| !isSelfSigned
)) {
657 CFReleaseSafe(disallowedHashes
);
661 static CFStringRef
convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum
) {
662 const void *digests
[] = { kSecSignatureDigestAlgorithmUnknown
,
663 kSecSignatureDigestAlgorithmMD2
,
664 kSecSignatureDigestAlgorithmMD4
,
665 kSecSignatureDigestAlgorithmMD5
,
666 kSecSignatureDigestAlgorithmSHA1
,
667 kSecSignatureDigestAlgorithmSHA224
,
668 kSecSignatureDigestAlgorithmSHA256
,
669 kSecSignatureDigestAlgorithmSHA384
,
670 kSecSignatureDigestAlgorithmSHA512
,
672 return digests
[algorithmEnum
];
675 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert
, CFTypeRef pvcValue
) {
676 /* skip signature algorithm check on self-signed certs */
677 Boolean isSelfSigned
= false;
678 OSStatus status
= SecCertificateIsSelfSigned(cert
, &isSelfSigned
);
679 if ((status
== errSecSuccess
) && isSelfSigned
) {
683 CFArrayRef disallowedHashAlgorithms
= pvcValue
;
684 CFStringRef certAlg
= convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert
));
685 if (CFArrayContainsValue(disallowedHashAlgorithms
, CFRangeMake(0,CFArrayGetCount(disallowedHashAlgorithms
)), certAlg
)) {
691 bool SecPolicyCheckCertUnparseableExtension(SecCertificateRef cert
, CFTypeRef pvcValue
) {
692 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
698 bool SecPolicyCheckCertNotCA(SecCertificateRef cert
, CFTypeRef pvcValue
) {
699 if (SecCertificateIsCA(cert
)) {
706 * MARK: SecLeafPVC functions
708 static CFDictionaryRef
SecLeafPVCCopyCallbacks(void) {
709 CFMutableDictionaryRef leafCallbacks
= NULL
;
710 leafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
711 &kCFTypeDictionaryKeyCallBacks
, NULL
);
713 #undef POLICYCHECKMACRO
714 #define __PC_ADD_CHECK_(NAME)
715 #define __PC_ADD_CHECK_O(NAME) CFDictionaryAddValue(leafCallbacks, \
716 kSecPolicyCheck##NAME, SecPolicyCheckCert##NAME);
718 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
719 __PC_ADD_CHECK_##LEAFONLY(NAME)
720 #include "SecPolicyChecks.list"
722 return leafCallbacks
;
725 void SecLeafPVCInit(SecLeafPVCRef pvc
, SecCertificateRef leaf
, CFArrayRef policies
,
726 CFAbsoluteTime verifyTime
) {
727 secdebug("alloc", "leafpvc %p", pvc
);
728 // Weird logging policies crashes.
729 //secdebug("policy", "%@", policies);
730 pvc
->leaf
= CFRetainSafe(leaf
);
731 pvc
->policies
= CFRetainSafe(policies
);
732 pvc
->verifyTime
= verifyTime
;
733 pvc
->callbacks
= SecLeafPVCCopyCallbacks();
737 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
738 &kCFTypeDictionaryValueCallBacks
);
739 pvc
->details
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
, 1,
740 &kCFTypeArrayCallBacks
);
741 CFRelease(certDetail
);
745 void SecLeafPVCDelete(SecLeafPVCRef pvc
) {
746 secdebug("alloc", "delete leaf pvc %p", pvc
);
747 CFReleaseNull(pvc
->policies
);
748 CFReleaseNull(pvc
->details
);
749 CFReleaseNull(pvc
->callbacks
);
750 CFReleaseNull(pvc
->leaf
);
753 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc
,
754 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
756 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
, "leaf",
757 (force
? "force" : ""), result
);
759 /* If this is not something the current policy cares about ignore
760 this error and return true so our caller continues evaluation. */
762 /* @@@ The right long term fix might be to check if none of the passed
763 in policies contain this key, since not all checks are run for all
765 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
766 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
770 /* @@@ Check to see if the SecTrustSettings for the certificate in question
771 tell us to ignore this error. */
776 CFMutableDictionaryRef detail
=
777 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
779 /* Perhaps detail should have an array of results per key? As it stands
780 in the case of multiple policy failures the last failure stands. */
781 CFDictionarySetValue(detail
, key
, result
);
786 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc
,
787 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
788 return SecLeafPVCSetResultForced(pvc
, key
, ix
, result
, false);
791 static void SecLeafPVCValidateKey(const void *key
, const void *value
,
793 SecLeafPVCRef pvc
= (SecLeafPVCRef
)context
;
795 /* If our caller doesn't want full details and we failed earlier there is
796 no point in doing additional checks. */
797 if (!pvc
->result
&& !pvc
->details
)
800 SecPolicyCheckCertFunction fcn
= (SecPolicyCheckCertFunction
) CFDictionaryGetValue(pvc
->callbacks
, key
);
806 /* kSecPolicyCheckTemporalValidity is special */
807 if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
808 CFDateRef verifyDate
= CFDateCreate(NULL
, pvc
->verifyTime
);
809 if(!fcn(pvc
->leaf
, verifyDate
)) {
810 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
812 CFReleaseSafe(verifyDate
);
814 /* get pvcValue from current policy */
815 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
820 CFTypeRef pvcValue
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, key
);
821 if(!fcn(pvc
->leaf
, pvcValue
)) {
822 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
827 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc
) {
829 CFArrayRef policies
= pvc
->policies
;
830 CFIndex ix
, count
= CFArrayGetCount(policies
);
831 for (ix
= 0; ix
< count
; ++ix
) {
832 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
834 /* Validate all keys for all policies. */
835 CFDictionaryApplyFunction(policy
->_options
, SecLeafPVCValidateKey
, pvc
);
836 if (!pvc
->result
&& !pvc
->details
)