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 return (keyUsage
& ku
) == ku
;
68 bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
69 SecKeyUsage keyUsage
= SecCertificateGetKeyUsage(cert
);
71 CFTypeRef xku
= pvcValue
;
73 CFIndex ix
, count
= CFArrayGetCount(xku
);
74 for (ix
= 0; ix
< count
; ++ix
) {
75 CFTypeRef ku
= CFArrayGetValueAtIndex(xku
, ix
);
76 if (keyusage_allows(keyUsage
, ku
)) {
82 match
= keyusage_allows(keyUsage
, xku
);
87 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage
,
91 if (extendedKeyUsage
) {
92 CFRange all
= { 0, CFArrayGetCount(extendedKeyUsage
) };
93 return CFArrayContainsValue(extendedKeyUsage
, all
, xeku
);
95 /* Certificate has no extended key usage, only a match if the policy
96 contains a 0 length CFDataRef. */
97 return CFDataGetLength((CFDataRef
)xeku
) == 0;
101 static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage
,
106 if(CFGetTypeID(xeku
) == CFDataGetTypeID()) {
107 return extendedkeyusage_allows(extendedKeyUsage
, xeku
);
108 } else if (CFGetTypeID(xeku
) == CFStringGetTypeID()) {
109 CFDataRef eku
= SecCertificateCreateOidDataFromString(NULL
, xeku
);
111 bool result
= extendedkeyusage_allows(extendedKeyUsage
, eku
);
119 bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert
, CFTypeRef pvcValue
) {
120 CFArrayRef certExtendedKeyUsage
= SecCertificateCopyExtendedKeyUsage(cert
);
122 CFTypeRef xeku
= pvcValue
;
124 CFIndex ix
, count
= CFArrayGetCount(xeku
);
125 for (ix
= 0; ix
< count
; ix
++) {
126 CFTypeRef eku
= CFArrayGetValueAtIndex(xeku
, ix
);
127 if (isExtendedKeyUsageAllowed(certExtendedKeyUsage
, eku
)) {
133 match
= isExtendedKeyUsageAllowed(certExtendedKeyUsage
, xeku
);
135 CFReleaseSafe(certExtendedKeyUsage
);
139 bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
140 /* If the certificate has a subject, or
141 if it doesn't, and it's the leaf and not a CA,
142 and also has a critical subjectAltName extension it's valid. */
143 if (!SecCertificateHasSubject(cert
)) {
144 if (SecCertificateIsCA(cert
)) {
145 /* CA certificate has empty subject. */
148 if (!SecCertificateHasCriticalSubjectAltName(cert
)) {
149 /* Leaf certificate with empty subject does not have
150 a critical subject alt name extension. */
158 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
159 more labels. Use CFNetwork's function for determining if those labels comprise
160 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
161 typedef bool (*CFNIsTLD_f
)(CFStringRef domain
);
162 bool SecDNSIsTLD(CFStringRef reference
) {
163 bool result
= false; /* fail open for allocation and symbol lookup failures */
164 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr
= NULL
;
165 static dispatch_once_t onceToken
;
166 CFStringRef presentedDomain
= NULL
;
168 dispatch_once(&onceToken
, ^{
169 void *framework
= dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY
);
171 CFNIsDomainTopLevelFunctionPtr
= dlsym(framework
, "_CFHostIsDomainTopLevelForCertificatePolicy");
175 require_quiet(CFNIsDomainTopLevelFunctionPtr
, out
);
176 CFIndex referenceLen
= CFStringGetLength(reference
);
178 /* reference identifier is too short, we should fail it */
179 require_action_quiet(referenceLen
> 2, out
, result
= true);
181 require_quiet(presentedDomain
= CFStringCreateWithSubstring(NULL
, reference
,
182 CFRangeMake(2, referenceLen
- 2)),
184 result
= CFNIsDomainTopLevelFunctionPtr(presentedDomain
);
187 CFReleaseNull(presentedDomain
);
191 /* Compare hostname, to a server name obtained from the server's cert
192 Obtained from the SubjectAltName or the CommonName entry in the Subject.
193 Limited wildcard checking is performed here as outlined in RFC 6125
196 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
197 never to accept partial-label wildcards even though they are allowed by
200 We use the language from RFC 6125, particularly the following definitions:
202 presented identifier: An identifier that is presented by a server to
203 a client within a PKIX certificate when the client attempts to
204 establish secure communication with the server; the certificate
205 can include one or more presented identifiers of different types,
206 and if the server hosts more than one domain then the certificate
207 might present distinct identifiers for each domain.
209 reference identifier: An identifier, constructed from a source
210 domain and optionally an application service type, used by the
211 client for matching purposes when examining presented identifiers.
214 static bool SecDNSMatch(CFStringRef reference
, CFStringRef presented
) {
215 CFArrayRef referenceLabels
= NULL
, presentedLabels
= NULL
;
218 /* A trailing '.' in the reference identifier is allowed as a mechanism
219 to force TLS renegotiation. Strip it before parsing labels. */
220 CFIndex referenceLen
= CFStringGetLength(reference
);
221 require_quiet(referenceLen
> 0, noMatch
);
222 if ('.' == CFStringGetCharacterAtIndex(reference
, referenceLen
- 1)) {
223 CFStringRef truncatedReference
= CFStringCreateWithSubstring(NULL
, reference
,
224 CFRangeMake(0, referenceLen
- 1));
225 referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, truncatedReference
, CFSTR("."));
226 CFReleaseNull(truncatedReference
);
227 require_quiet(referenceLabels
, noMatch
);
229 require_quiet(referenceLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, reference
, CFSTR(".")),
233 require_quiet(presentedLabels
= CFStringCreateArrayBySeparatingStrings(NULL
, presented
, CFSTR(".")),
236 /* Reference Identifier and Presented Identifier must have the same number of labels
237 because a wildcard in the presented identifier can only match a single label in the
238 reference identifier. */
239 require_quiet(CFArrayGetCount(referenceLabels
) == CFArrayGetCount(presentedLabels
), noMatch
);
241 CFIndex ix
, count
= CFArrayGetCount(referenceLabels
);
242 for (ix
= count
- 1; ix
>= 0; ix
--) {
243 CFStringRef rlabel
= NULL
, plabel
= NULL
;
244 require_quiet(rlabel
= CFArrayGetValueAtIndex(referenceLabels
, ix
), noMatch
);
245 require_quiet(plabel
= CFArrayGetValueAtIndex(presentedLabels
, ix
), noMatch
);
246 if (CFEqual(plabel
, CFSTR("*"))) {
247 /* must only occur in left-most label */
248 require_quiet(ix
== 0, noMatch
);
250 /* must not occur before single-label TLD */
251 require_quiet(count
> 2 && ix
!= count
- 2, noMatch
);
253 /* must not occur before a multi-label gTLD */
254 require_quiet(!SecDNSIsTLD(presented
), noMatch
);
256 /* partial-label wildcards are disallowed */
257 CFRange partialRange
= CFStringFind(plabel
, CFSTR("*"), 0);
258 require_quiet(partialRange
.location
== kCFNotFound
&& partialRange
.length
== 0 ,
261 /* not a wildcard, so labels must match exactly */
262 require_quiet(CFStringCompare(rlabel
, plabel
, kCFCompareCaseInsensitive
) == kCFCompareEqualTo
, noMatch
);
269 CFReleaseNull(referenceLabels
);
270 CFReleaseNull(presentedLabels
);
274 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert
, CFTypeRef pvcValue
) {
275 /* @@@ Consider what to do if the caller passes in no hostname. Should
276 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
277 CFStringRef hostName
= pvcValue
;
278 if (!isString(hostName
)) {
279 /* @@@ We can't return an error here and making the evaluation fail
280 won't help much either. */
284 bool dnsMatch
= false;
285 CFArrayRef dnsNames
= SecCertificateCopyDNSNamesFromSAN(cert
);
287 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
288 for (ix
= 0; ix
< count
; ++ix
) {
289 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
290 if (SecDNSMatch(hostName
, dns
)) {
299 /* Check whether hostname is an IPv4 or IPv6 address */
300 CFDataRef hostIPData
= SecFrameworkCopyIPAddressData(hostName
);
302 /* Check address against IP addresses in the SAN extension,
303 obtained by SecCertificateCopyIPAddresses. Comparisons
304 must always use the canonical data representation of the
305 address, since string notation may omit zeros, etc. */
306 CFArrayRef ipAddresses
= SecCertificateCopyIPAddresses(cert
);
307 CFIndex ix
, count
= (ipAddresses
) ? CFArrayGetCount(ipAddresses
) : 0;
308 for (ix
= 0; ix
< count
&& !dnsMatch
; ++ix
) {
309 CFStringRef ipAddress
= (CFStringRef
)CFArrayGetValueAtIndex(ipAddresses
, ix
);
310 CFDataRef addrData
= SecFrameworkCopyIPAddressData(ipAddress
);
311 if (CFEqualSafe(hostIPData
, addrData
)) {
314 CFReleaseSafe(addrData
);
316 CFReleaseSafe(ipAddresses
);
317 CFReleaseSafe(hostIPData
);
324 bool SecPolicyCheckCertEmail(SecCertificateRef cert
, CFTypeRef pvcValue
) {
325 CFStringRef email
= pvcValue
;
327 if (!isString(email
)) {
328 /* We can't return an error here and making the evaluation fail
329 won't help much either. */
333 CFArrayRef addrs
= SecCertificateCopyRFC822Names(cert
);
335 CFIndex ix
, count
= CFArrayGetCount(addrs
);
336 for (ix
= 0; ix
< count
; ++ix
) {
337 CFStringRef addr
= (CFStringRef
)CFArrayGetValueAtIndex(addrs
, ix
);
338 if (!CFStringCompare(email
, addr
, kCFCompareCaseInsensitive
)) {
349 bool SecPolicyCheckCertTemporalValidity(SecCertificateRef cert
, CFTypeRef pvcValue
) {
350 CFAbsoluteTime verifyTime
= CFDateGetAbsoluteTime(pvcValue
);
351 if (!SecCertificateIsValid(cert
, verifyTime
)) {
352 /* Leaf certificate has expired. */
358 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert
, CFTypeRef pvcValue
) {
359 CFStringRef prefix
= pvcValue
;
361 if (!isString(prefix
)) {
362 /* @@@ We can't return an error here and making the evaluation fail
363 won't help much either. */
366 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
367 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
368 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames
, 0), prefix
)) {
369 /* Common Name prefix mismatch. */
372 CFReleaseSafe(commonNames
);
376 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert
, CFTypeRef pvcValue
) {
377 CFStringRef common_name
= pvcValue
;
379 if (!isString(common_name
)) {
380 /* @@@ We can't return an error here and making the evaluation fail
381 won't help much either. */
384 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
385 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1 ||
386 !CFEqual(common_name
, CFArrayGetValueAtIndex(commonNames
, 0))) {
387 /* Common Name mismatch. */
390 CFReleaseSafe(commonNames
);
394 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert
, CFTypeRef pvcValue
) {
395 CFStringRef common_name
= pvcValue
;
397 if (!isString(common_name
)) {
398 /* @@@ We can't return an error here and making the evaluation fail
399 won't help much either. */
402 CFArrayRef commonNames
= SecCertificateCopyCommonNames(cert
);
403 if (!commonNames
|| CFArrayGetCount(commonNames
) != 1) {
404 CFStringRef cert_common_name
= CFArrayGetValueAtIndex(commonNames
, 0);
405 CFStringRef test_common_name
= common_name
?
406 CFStringCreateWithFormat(kCFAllocatorDefault
,
407 NULL
, CFSTR("TEST %@ TEST"), common_name
) :
409 if (!CFEqual(common_name
, cert_common_name
) &&
410 (!test_common_name
|| !CFEqual(test_common_name
, cert_common_name
)))
411 /* Common Name mismatch. */
413 CFReleaseSafe(test_common_name
);
415 CFReleaseSafe(commonNames
);
419 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert
, CFTypeRef pvcValue
) {
420 CFDateRef date
= pvcValue
;
422 /* @@@ We can't return an error here and making the evaluation fail
423 won't help much either. */
426 CFAbsoluteTime at
= CFDateGetAbsoluteTime(date
);
427 if (SecCertificateNotValidBefore(cert
) <= at
) {
428 /* Leaf certificate has not valid before that is too old. */
434 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert
, CFTypeRef pvcValue
) {
435 CFStringRef org
= pvcValue
;
437 if (!isString(org
)) {
438 /* @@@ We can't return an error here and making the evaluation fail
439 won't help much either. */
442 CFArrayRef organization
= SecCertificateCopyOrganization(cert
);
443 if (!organization
|| CFArrayGetCount(organization
) != 1 ||
444 !CFEqual(org
, CFArrayGetValueAtIndex(organization
, 0))) {
445 /* Leaf Subject Organization mismatch. */
448 CFReleaseSafe(organization
);
452 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert
, CFTypeRef pvcValue
) {
453 CFStringRef orgUnit
= pvcValue
;
455 if (!isString(orgUnit
)) {
456 /* @@@ We can't return an error here and making the evaluation fail
457 won't help much either. */
460 CFArrayRef organizationalUnit
= SecCertificateCopyOrganizationalUnit(cert
);
461 if (!organizationalUnit
|| CFArrayGetCount(organizationalUnit
) != 1 ||
462 !CFEqual(orgUnit
, CFArrayGetValueAtIndex(organizationalUnit
, 0))) {
463 /* Leaf Subject Organizational Unit mismatch. */
466 CFReleaseSafe(organizationalUnit
);
470 bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert
, CFTypeRef pvcValue
) {
471 CFStringRef country
= pvcValue
;
473 if (!isString(country
)) {
474 /* @@@ We can't return an error here and making the evaluation fail
475 won't help much either. */
478 CFArrayRef certCountry
= SecCertificateCopyCountry(cert
);
479 if (!certCountry
|| CFArrayGetCount(certCountry
) != 1 ||
480 !CFEqual(country
, CFArrayGetValueAtIndex(certCountry
, 0))) {
481 /* Subject Country mismatch. */
484 CFReleaseSafe(certCountry
);
488 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert
, CFTypeRef pvcValue
) {
489 CFArrayRef trustedServerNames
= pvcValue
;
490 /* No names specified means we accept any name. */
491 if (!trustedServerNames
)
493 if (!isArray(trustedServerNames
)) {
494 /* @@@ We can't return an error here and making the evaluation fail
495 won't help much either. */
499 CFIndex tsnCount
= CFArrayGetCount(trustedServerNames
);
500 bool dnsMatch
= false;
501 CFArrayRef dnsNames
= SecCertificateCopyDNSNames(cert
);
503 CFIndex ix
, count
= CFArrayGetCount(dnsNames
);
504 // @@@ This is O(N^2) unfortunately we can't do better easily unless
505 // we don't do wildcard matching. */
506 for (ix
= 0; !dnsMatch
&& ix
< count
; ++ix
) {
507 CFStringRef dns
= (CFStringRef
)CFArrayGetValueAtIndex(dnsNames
, ix
);
509 for (tix
= 0; tix
< tsnCount
; ++tix
) {
510 CFStringRef serverName
=
511 (CFStringRef
)CFArrayGetValueAtIndex(trustedServerNames
, tix
);
512 if (!isString(serverName
)) {
513 /* @@@ We can't return an error here and making the
514 evaluation fail won't help much either. */
515 CFReleaseSafe(dnsNames
);
518 /* we purposefully reverse the arguments here such that dns names
519 from the cert are matched against a server name list, where
520 the server names list can contain wildcards and the dns name
521 cannot. References: http://support.microsoft.com/kb/941123
522 It's easy to find occurrences where people tried to use
523 wildcard certificates and were told that those don't work
525 if (SecDNSMatch(dns
, serverName
)) {
537 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert
, CFTypeRef pvcValue
) {
538 if (pvcValue
&& SecCertificateHasMarkerExtension(cert
, pvcValue
)) {
545 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert
,
546 CFTypeRef pvcValue
) {
547 if (CFGetTypeID(pvcValue
) == CFArrayGetTypeID()) {
548 CFIndex ix
, length
= CFArrayGetCount(pvcValue
);
549 for (ix
= 0; ix
< length
; ix
++)
550 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert
,
551 CFArrayGetValueAtIndex((CFArrayRef
)pvcValue
, ix
))) {
554 } else if (CFGetTypeID(pvcValue
) == CFDataGetTypeID() ||
555 CFGetTypeID(pvcValue
) == CFStringGetTypeID()) {
556 return (NULL
!= SecCertificateGetExtensionValue(cert
, pvcValue
));
562 * The value is a dictionary. The dictionary contains keys indicating
563 * whether the value is for Prod or QA. The values are the same as
564 * in the options dictionary for SecPolicyCheckLeafMarkerOid.
566 bool SecPolicyCheckCertLeafMarkersProdAndQA(SecCertificateRef cert
, CFTypeRef pvcValue
)
568 CFTypeRef prodValue
= CFDictionaryGetValue(pvcValue
, kSecPolicyLeafMarkerProd
);
570 if (!SecPolicyCheckCertLeafMarkerOid(cert
, prodValue
)) {
577 static CFSetRef
copyCertificatePolicies(SecCertificateRef cert
) {
578 CFMutableSetRef policies
= NULL
;
579 policies
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
580 if (!policies
) return NULL
;
582 const SecCECertificatePolicies
*cp
= SecCertificateGetCertificatePolicies(cert
);
583 size_t policy_ix
, policy_count
= cp
? cp
->numPolicies
: 0;
584 for (policy_ix
= 0; policy_ix
< policy_count
; ++policy_ix
) {
585 CFDataRef oidData
= NULL
;
586 DERItem
*policyOID
= &cp
->policies
[policy_ix
].policyIdentifier
;
587 oidData
= CFDataCreate(kCFAllocatorDefault
, policyOID
->data
, policyOID
->length
);
588 CFSetAddValue(policies
, oidData
);
589 CFReleaseSafe(oidData
);
594 static bool checkPolicyOidData(SecCertificateRef cert
, CFDataRef oid
) {
595 CFSetRef policies
= copyCertificatePolicies(cert
);
597 if (policies
&& CFSetContainsValue(policies
, oid
)) {
600 CFReleaseSafe(policies
);
604 /* This one is different from SecPolicyCheckCertificatePolicyOid because
605 that one checks the whole chain. (And uses policy_set_t...) */
606 bool SecPolicyCheckCertCertificatePolicy(SecCertificateRef cert
, CFTypeRef pvcValue
) {
607 CFTypeRef value
= pvcValue
;
610 if (CFGetTypeID(value
) == CFDataGetTypeID())
612 result
= checkPolicyOidData(cert
, value
);
613 } else if (CFGetTypeID(value
) == CFStringGetTypeID()) {
614 CFDataRef dataOid
= SecCertificateCreateOidDataFromString(NULL
, value
);
616 result
= checkPolicyOidData(cert
, dataOid
);
623 bool SecPolicyCheckCertWeakKeySize(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
624 if (cert
&& SecCertificateIsWeakKey(cert
)) {
625 /* Leaf certificate has a weak key. */
631 bool SecPolicyCheckCertKeySize(SecCertificateRef cert
, CFTypeRef pvcValue
) {
632 CFDictionaryRef keySizes
= pvcValue
;
633 if (!SecCertificateIsAtLeastMinKeySize(cert
, keySizes
)) {
639 bool SecPolicyCheckCertWeakSignature(SecCertificateRef cert
, CFTypeRef __unused pvcValue
) {
641 CFMutableArrayRef disallowedHashes
= CFArrayCreateMutable(NULL
, 3, &kCFTypeArrayCallBacks
);
642 if (!disallowedHashes
) {
645 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD2
);
646 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD4
);
647 CFArrayAppendValue(disallowedHashes
, kSecSignatureDigestAlgorithmMD5
);
649 /* Weak Signature failures only for non-self-signed certs */
650 Boolean isSelfSigned
= false;
651 OSStatus status
= SecCertificateIsSelfSigned(cert
, &isSelfSigned
);
652 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert
, disallowedHashes
) && (status
!= errSecSuccess
|| !isSelfSigned
)) {
655 CFReleaseSafe(disallowedHashes
);
659 static CFStringRef
convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum
) {
660 const void *digests
[] = { kSecSignatureDigestAlgorithmUnknown
,
661 kSecSignatureDigestAlgorithmMD2
,
662 kSecSignatureDigestAlgorithmMD4
,
663 kSecSignatureDigestAlgorithmMD5
,
664 kSecSignatureDigestAlgorithmSHA1
,
665 kSecSignatureDigestAlgorithmSHA224
,
666 kSecSignatureDigestAlgorithmSHA256
,
667 kSecSignatureDigestAlgorithmSHA384
,
668 kSecSignatureDigestAlgorithmSHA512
,
670 return digests
[algorithmEnum
];
673 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert
, CFTypeRef pvcValue
) {
674 /* skip signature algorithm check on self-signed certs */
675 Boolean isSelfSigned
= false;
676 OSStatus status
= SecCertificateIsSelfSigned(cert
, &isSelfSigned
);
677 if ((status
== errSecSuccess
) && isSelfSigned
) {
681 CFArrayRef disallowedHashAlgorithms
= pvcValue
;
682 CFStringRef certAlg
= convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert
));
683 if (CFArrayContainsValue(disallowedHashAlgorithms
, CFRangeMake(0,CFArrayGetCount(disallowedHashAlgorithms
)), certAlg
)) {
689 bool SecPolicyCheckCertUnparseableExtension(SecCertificateRef cert
, CFTypeRef pvcValue
) {
690 if (SecCertificateGetUnparseableKnownExtension(cert
) != kCFNotFound
) {
697 * MARK: SecLeafPVC functions
699 static CFDictionaryRef
SecLeafPVCCopyCallbacks(void) {
700 CFMutableDictionaryRef leafCallbacks
= NULL
;
701 leafCallbacks
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0,
702 &kCFTypeDictionaryKeyCallBacks
, NULL
);
704 #undef POLICYCHECKMACRO
705 #define __PC_ADD_CHECK_(NAME)
706 #define __PC_ADD_CHECK_O(NAME) CFDictionaryAddValue(leafCallbacks, \
707 kSecPolicyCheck##NAME, SecPolicyCheckCert##NAME);
709 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
710 __PC_ADD_CHECK_##LEAFONLY(NAME)
711 #include "SecPolicyChecks.list"
713 return leafCallbacks
;
716 void SecLeafPVCInit(SecLeafPVCRef pvc
, SecCertificateRef leaf
, CFArrayRef policies
,
717 CFAbsoluteTime verifyTime
) {
718 secdebug("alloc", "leafpvc %p", pvc
);
719 // Weird logging policies crashes.
720 //secdebug("policy", "%@", policies);
721 pvc
->leaf
= CFRetainSafe(leaf
);
722 pvc
->policies
= CFRetainSafe(policies
);
723 pvc
->verifyTime
= verifyTime
;
724 pvc
->callbacks
= SecLeafPVCCopyCallbacks();
728 CFMutableDictionaryRef certDetail
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
,
729 &kCFTypeDictionaryValueCallBacks
);
730 pvc
->details
= CFArrayCreate(kCFAllocatorDefault
, (const void **)&certDetail
, 1,
731 &kCFTypeArrayCallBacks
);
732 CFRelease(certDetail
);
736 void SecLeafPVCDelete(SecLeafPVCRef pvc
) {
737 secdebug("alloc", "delete leaf pvc %p", pvc
);
738 CFReleaseNull(pvc
->policies
);
739 CFReleaseNull(pvc
->details
);
740 CFReleaseNull(pvc
->callbacks
);
741 CFReleaseNull(pvc
->leaf
);
744 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc
,
745 CFStringRef key
, CFIndex ix
, CFTypeRef result
, bool force
) {
747 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix
, key
, "leaf",
748 (force
? "force" : ""), result
);
750 /* If this is not something the current policy cares about ignore
751 this error and return true so our caller continues evaluation. */
753 /* @@@ The right long term fix might be to check if none of the passed
754 in policies contain this key, since not all checks are run for all
756 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
757 if (policy
&& !CFDictionaryContainsKey(policy
->_options
, key
))
761 /* @@@ Check to see if the SecTrustSettings for the certificate in question
762 tell us to ignore this error. */
767 CFMutableDictionaryRef detail
=
768 (CFMutableDictionaryRef
)CFArrayGetValueAtIndex(pvc
->details
, ix
);
770 /* Perhaps detail should have an array of results per key? As it stands
771 in the case of multiple policy failures the last failure stands. */
772 CFDictionarySetValue(detail
, key
, result
);
777 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc
,
778 CFStringRef key
, CFIndex ix
, CFTypeRef result
) {
779 return SecLeafPVCSetResultForced(pvc
, key
, ix
, result
, false);
782 static void SecLeafPVCValidateKey(const void *key
, const void *value
,
784 SecLeafPVCRef pvc
= (SecLeafPVCRef
)context
;
786 /* If our caller doesn't want full details and we failed earlier there is
787 no point in doing additional checks. */
788 if (!pvc
->result
&& !pvc
->details
)
791 SecPolicyCheckCertFunction fcn
= (SecPolicyCheckCertFunction
) CFDictionaryGetValue(pvc
->callbacks
, key
);
797 /* kSecPolicyCheckTemporalValidity is special */
798 if (CFEqual(key
, kSecPolicyCheckTemporalValidity
)) {
799 CFDateRef verifyDate
= CFDateCreate(NULL
, pvc
->verifyTime
);
800 if(!fcn(pvc
->leaf
, verifyDate
)) {
801 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
803 CFReleaseSafe(verifyDate
);
805 /* get pvcValue from current policy */
806 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(pvc
->policies
, pvc
->policyIX
);
811 CFTypeRef pvcValue
= (CFTypeRef
)CFDictionaryGetValue(policy
->_options
, key
);
812 if(!fcn(pvc
->leaf
, pvcValue
)) {
813 SecLeafPVCSetResult(pvc
, key
, 0, kCFBooleanFalse
);
818 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc
) {
820 CFArrayRef policies
= pvc
->policies
;
821 CFIndex ix
, count
= CFArrayGetCount(policies
);
822 for (ix
= 0; ix
< count
; ++ix
) {
823 SecPolicyRef policy
= (SecPolicyRef
)CFArrayGetValueAtIndex(policies
, ix
);
825 /* Validate all keys for all policies. */
826 CFDictionaryApplyFunction(policy
->_options
, SecLeafPVCValidateKey
, pvc
);
827 if (!pvc
->result
&& !pvc
->details
)