]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPolicyLeafCallbacks.c
Security-58286.1.32.tar.gz
[apple/security.git] / OSX / sec / Security / SecPolicyLeafCallbacks.c
1 /*
2 * Copyright (c) 2008-2016 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * SecPolicyLeafCallbacks.c - Callbacks for SecPolicy for verifying leafs
26 */
27
28 #include <AssertMacros.h>
29 #include <CoreFoundation/CFDictionary.h>
30 #include <Security/SecPolicyInternal.h>
31 #include <Security/SecCertificateInternal.h>
32 #include <utilities/SecCFWrappers.h>
33 #include <wctype.h>
34 #include <dlfcn.h>
35 #include <libDER/oidsPriv.h>
36
37 /*
38 * MARK: SecPolicyCheckCert Functions
39 * All SecPolicyCheckCert* return false if the cert fails the check and true if it succeeds.
40 */
41
42 typedef bool (*SecPolicyCheckCertFunction)(SecCertificateRef cert, CFTypeRef pvcValue);
43
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. */
50 return false;
51 }
52 return true;
53 }
54
55 static bool keyusage_allows(SecKeyUsage keyUsage, CFTypeRef xku) {
56 if (!xku || CFGetTypeID(xku) != CFNumberGetTypeID())
57 return false;
58
59 SInt32 dku;
60 CFNumberGetValue((CFNumberRef)xku, kCFNumberSInt32Type, &dku);
61 SecKeyUsage ku = (SecKeyUsage)dku;
62 return (keyUsage & ku) == ku;
63 }
64
65 bool SecPolicyCheckCertKeyUsage(SecCertificateRef cert, CFTypeRef pvcValue) {
66 SecKeyUsage keyUsage = SecCertificateGetKeyUsage(cert);
67 bool match = false;
68 CFTypeRef xku = pvcValue;
69 if (isArray(xku)) {
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)) {
74 match = true;
75 break;
76 }
77 }
78 } else {
79 match = keyusage_allows(keyUsage, xku);
80 }
81 return match;
82 }
83
84 static bool extendedkeyusage_allows(CFArrayRef extendedKeyUsage,
85 CFDataRef xeku) {
86 if (!xeku)
87 return false;
88 if (extendedKeyUsage) {
89 CFRange all = { 0, CFArrayGetCount(extendedKeyUsage) };
90 return CFArrayContainsValue(extendedKeyUsage, all, xeku);
91 } else {
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;
95 }
96 }
97
98 static bool isExtendedKeyUsageAllowed(CFArrayRef extendedKeyUsage,
99 CFTypeRef xeku) {
100 if (!xeku) {
101 return false;
102 }
103 if(CFGetTypeID(xeku) == CFDataGetTypeID()) {
104 return extendedkeyusage_allows(extendedKeyUsage, xeku);
105 } else if (CFGetTypeID(xeku) == CFStringGetTypeID()) {
106 CFDataRef eku = SecCertificateCreateOidDataFromString(NULL, xeku);
107 if (eku) {
108 bool result = extendedkeyusage_allows(extendedKeyUsage, eku);
109 CFRelease(eku);
110 return result;
111 }
112 }
113 return false;
114 }
115
116 bool SecPolicyCheckCertExtendedKeyUsage(SecCertificateRef cert, CFTypeRef pvcValue) {
117 CFArrayRef certExtendedKeyUsage = SecCertificateCopyExtendedKeyUsage(cert);
118 bool match = false;
119 CFTypeRef xeku = pvcValue;
120 if (isArray(xeku)) {
121 CFIndex ix, count = CFArrayGetCount(xeku);
122 for (ix = 0; ix < count; ix++) {
123 CFTypeRef eku = CFArrayGetValueAtIndex(xeku, ix);
124 if (isExtendedKeyUsageAllowed(certExtendedKeyUsage, eku)) {
125 match = true;
126 break;
127 }
128 }
129 } else {
130 match = isExtendedKeyUsageAllowed(certExtendedKeyUsage, xeku);
131 }
132 CFReleaseSafe(certExtendedKeyUsage);
133 return match;
134 }
135
136 bool SecPolicyCheckCertNonEmptySubject(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
137 /* If the certificate has a subject, or
138 if it doesn't, and it's the leaf and not a CA,
139 and also has a critical subjectAltName extension it's valid. */
140 if (!SecCertificateHasSubject(cert)) {
141 if (SecCertificateIsCA(cert)) {
142 /* CA certificate has empty subject. */
143 return false;
144 } else {
145 if (!SecCertificateHasCriticalSubjectAltName(cert)) {
146 /* Leaf certificate with empty subject does not have
147 a critical subject alt name extension. */
148 return false;
149 }
150 }
151 }
152 return true;
153 }
154
155
156 /* This one is different from SecPolicyCheckQualifiedCertStatements because
157 both are empty stubs. */
158 static bool SecPolicyCheckCertQualifiedCertStatements(SecCertificateRef __unused cert,
159 CFTypeRef __unused pvcValue) {
160 return true;
161 }
162
163 /* We have a wildcard reference identifier that looks like "*." followed by 2 or
164 more labels. Use CFNetwork's function for determining if those labels comprise
165 a top-level domain. We need to dlopen since CFNetwork is a client of ours. */
166 typedef bool (*CFNIsTLD_f)(CFStringRef domain);
167 static bool SecDNSIsTLD(CFStringRef reference) {
168 bool result = false; /* fail open for allocation and symbol lookup failures */
169 static CFNIsTLD_f CFNIsDomainTopLevelFunctionPtr = NULL;
170 static dispatch_once_t onceToken;
171 CFStringRef presentedDomain = NULL;
172
173 dispatch_once(&onceToken, ^{
174 void *framework = dlopen("/System/Library/Frameworks/CFNetwork.framework/CFNetwork", RTLD_LAZY);
175 if (framework) {
176 CFNIsDomainTopLevelFunctionPtr = dlsym(framework, "_CFHostIsDomainTopLevelForCertificatePolicy");
177 }
178 });
179
180 require_quiet(CFNIsDomainTopLevelFunctionPtr, out);
181 CFIndex referenceLen = CFStringGetLength(reference);
182
183 /* reference identifier is too short, we should fail it */
184 require_action_quiet(referenceLen > 2, out, result = true);
185
186 require_quiet(presentedDomain = CFStringCreateWithSubstring(NULL, reference,
187 CFRangeMake(2, referenceLen - 2)),
188 out);
189 result = CFNIsDomainTopLevelFunctionPtr(presentedDomain);
190
191 out:
192 CFReleaseNull(presentedDomain);
193 return result;
194 }
195
196 /* Compare hostname, to a server name obtained from the server's cert
197 Obtained from the SubjectAltName or the CommonName entry in the Subject.
198 Limited wildcard checking is performed here as outlined in RFC 6125
199 Section 6.4.3.
200
201 We adhere to the (SHOULD NOT) guidance in rules 1 and 2, and we choose
202 never to accept partial-label wildcards even though they are allowed by
203 rule 3.
204
205 We use the language from RFC 6125, particularly the following definitions:
206
207 presented identifier: An identifier that is presented by a server to
208 a client within a PKIX certificate when the client attempts to
209 establish secure communication with the server; the certificate
210 can include one or more presented identifiers of different types,
211 and if the server hosts more than one domain then the certificate
212 might present distinct identifiers for each domain.
213
214 reference identifier: An identifier, constructed from a source
215 domain and optionally an application service type, used by the
216 client for matching purposes when examining presented identifiers.
217
218 */
219 static bool SecDNSMatch(CFStringRef reference, CFStringRef presented) {
220 CFArrayRef referenceLabels = NULL, presentedLabels = NULL;
221 bool result = false;
222
223 /* A trailing '.' in the reference identifier is allowed as a mechanism
224 to force TLS renegotiation. Strip it before parsing labels. */
225 CFIndex referenceLen = CFStringGetLength(reference);
226 require_quiet(referenceLen > 0, noMatch);
227 if ('.' == CFStringGetCharacterAtIndex(reference, referenceLen - 1)) {
228 CFStringRef truncatedReference = CFStringCreateWithSubstring(NULL, reference,
229 CFRangeMake(0, referenceLen - 1));
230 referenceLabels = CFStringCreateArrayBySeparatingStrings(NULL, truncatedReference, CFSTR("."));
231 CFReleaseNull(truncatedReference);
232 require_quiet(referenceLabels, noMatch);
233 } else {
234 require_quiet(referenceLabels = CFStringCreateArrayBySeparatingStrings(NULL, reference, CFSTR(".")),
235 noMatch);
236 }
237
238 require_quiet(presentedLabels = CFStringCreateArrayBySeparatingStrings(NULL, presented, CFSTR(".")),
239 noMatch);
240
241 /* Reference Identifier and Presented Identifier must have the same number of labels
242 because a wildcard in the presented identifier can only match a single label in the
243 reference identifier. */
244 require_quiet(CFArrayGetCount(referenceLabels) == CFArrayGetCount(presentedLabels), noMatch);
245
246 CFIndex ix, count = CFArrayGetCount(referenceLabels);
247 for (ix = count - 1; ix >= 0; ix--) {
248 CFStringRef rlabel = NULL, plabel = NULL;
249 require_quiet(rlabel = CFArrayGetValueAtIndex(referenceLabels, ix), noMatch);
250 require_quiet(plabel = CFArrayGetValueAtIndex(presentedLabels, ix), noMatch);
251 if (CFEqual(plabel, CFSTR("*"))) {
252 /* must only occur in left-most label */
253 require_quiet(ix == 0, noMatch);
254
255 /* must not occur before single-label TLD */
256 require_quiet(count > 2 && ix != count - 2, noMatch);
257
258 /* must not occur before a multi-label gTLD */
259 require_quiet(!SecDNSIsTLD(presented), noMatch);
260 } else {
261 /* partial-label wildcards are disallowed */
262 CFRange partialRange = CFStringFind(plabel, CFSTR("*"), 0);
263 require_quiet(partialRange.location == kCFNotFound && partialRange.length == 0 ,
264 noMatch);
265
266 /* not a wildcard, so labels must match exactly */
267 require_quiet(CFStringCompare(rlabel, plabel, kCFCompareCaseInsensitive) == kCFCompareEqualTo, noMatch);
268 }
269 }
270
271 result = true;
272
273 noMatch:
274 CFReleaseNull(referenceLabels);
275 CFReleaseNull(presentedLabels);
276 return result;
277 }
278
279 bool SecPolicyCheckCertSSLHostname(SecCertificateRef cert, CFTypeRef pvcValue) {
280 /* @@@ Consider what to do if the caller passes in no hostname. Should
281 we then still fail if the leaf has no dnsNames or IPAddresses at all? */
282 CFStringRef hostName = pvcValue;
283 if (!isString(hostName)) {
284 /* @@@ We can't return an error here and making the evaluation fail
285 won't help much either. */
286 return false;
287 }
288
289 bool dnsMatch = false;
290 CFArrayRef dnsNames = SecCertificateCopyDNSNames(cert);
291 if (dnsNames) {
292 CFIndex ix, count = CFArrayGetCount(dnsNames);
293 for (ix = 0; ix < count; ++ix) {
294 CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
295 if (SecDNSMatch(hostName, dns)) {
296 dnsMatch = true;
297 break;
298 }
299 }
300 CFRelease(dnsNames);
301 }
302
303 if (!dnsMatch) {
304 /* Maybe hostname is an IPv4 or IPv6 address, let's compare against
305 the values returned by SecCertificateCopyIPAddresses() instead. */
306 CFArrayRef ipAddresses = SecCertificateCopyIPAddresses(cert);
307 if (ipAddresses) {
308 CFIndex ix, count = CFArrayGetCount(ipAddresses);
309 for (ix = 0; ix < count; ++ix) {
310 CFStringRef ipAddress = (CFStringRef)CFArrayGetValueAtIndex(ipAddresses, ix);
311 if (!CFStringCompare(hostName, ipAddress, kCFCompareCaseInsensitive)) {
312 dnsMatch = true;
313 break;
314 }
315 }
316 CFRelease(ipAddresses);
317 }
318 }
319
320 return dnsMatch;
321 }
322
323 bool SecPolicyCheckCertEmail(SecCertificateRef cert, CFTypeRef pvcValue) {
324 CFStringRef email = pvcValue;
325 bool match = false;
326 if (!isString(email)) {
327 /* We can't return an error here and making the evaluation fail
328 won't help much either. */
329 return false;
330 }
331
332 CFArrayRef addrs = SecCertificateCopyRFC822Names(cert);
333 if (addrs) {
334 CFIndex ix, count = CFArrayGetCount(addrs);
335 for (ix = 0; ix < count; ++ix) {
336 CFStringRef addr = (CFStringRef)CFArrayGetValueAtIndex(addrs, ix);
337 if (!CFStringCompare(email, addr, kCFCompareCaseInsensitive)) {
338 match = true;
339 break;
340 }
341 }
342 CFRelease(addrs);
343 }
344
345 return match;
346 }
347
348 static bool SecPolicyCheckCertValidLeaf(SecCertificateRef cert, CFTypeRef pvcValue) {
349 CFAbsoluteTime verifyTime = CFDateGetAbsoluteTime(pvcValue);
350 if (!SecCertificateIsValid(cert, verifyTime)) {
351 /* Leaf certificate has expired. */
352 return false;
353 }
354 return true;
355 }
356
357 bool SecPolicyCheckCertSubjectCommonNamePrefix(SecCertificateRef cert, CFTypeRef pvcValue) {
358 CFStringRef prefix = pvcValue;
359 bool match = true;
360 if (!isString(prefix)) {
361 /* @@@ We can't return an error here and making the evaluation fail
362 won't help much either. */
363 return false;
364 }
365 CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
366 if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
367 !CFStringHasPrefix(CFArrayGetValueAtIndex(commonNames, 0), prefix)) {
368 /* Common Name prefix mismatch. */
369 match = false;
370 }
371 CFReleaseSafe(commonNames);
372 return match;
373 }
374
375 bool SecPolicyCheckCertSubjectCommonName(SecCertificateRef cert, CFTypeRef pvcValue) {
376 CFStringRef common_name = pvcValue;
377 bool match = true;
378 if (!isString(common_name)) {
379 /* @@@ We can't return an error here and making the evaluation fail
380 won't help much either. */
381 return false;
382 }
383 CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
384 if (!commonNames || CFArrayGetCount(commonNames) != 1 ||
385 !CFEqual(common_name, CFArrayGetValueAtIndex(commonNames, 0))) {
386 /* Common Name mismatch. */
387 match = false;
388 }
389 CFReleaseSafe(commonNames);
390 return match;
391 }
392
393 bool SecPolicyCheckCertSubjectCommonNameTEST(SecCertificateRef cert, CFTypeRef pvcValue) {
394 CFStringRef common_name = pvcValue;
395 bool match = true;
396 if (!isString(common_name)) {
397 /* @@@ We can't return an error here and making the evaluation fail
398 won't help much either. */
399 return false;
400 }
401 CFArrayRef commonNames = SecCertificateCopyCommonNames(cert);
402 if (!commonNames || CFArrayGetCount(commonNames) != 1) {
403 CFStringRef cert_common_name = CFArrayGetValueAtIndex(commonNames, 0);
404 CFStringRef test_common_name = common_name ?
405 CFStringCreateWithFormat(kCFAllocatorDefault,
406 NULL, CFSTR("TEST %@ TEST"), common_name) :
407 NULL;
408 if (!CFEqual(common_name, cert_common_name) &&
409 (!test_common_name || !CFEqual(test_common_name, cert_common_name)))
410 /* Common Name mismatch. */
411 match = false;
412 CFReleaseSafe(test_common_name);
413 }
414 CFReleaseSafe(commonNames);
415 return match;
416 }
417
418 bool SecPolicyCheckCertNotValidBefore(SecCertificateRef cert, CFTypeRef pvcValue) {
419 CFDateRef date = pvcValue;
420 if (!isDate(date)) {
421 /* @@@ We can't return an error here and making the evaluation fail
422 won't help much either. */
423 return false;
424 }
425 CFAbsoluteTime at = CFDateGetAbsoluteTime(date);
426 if (SecCertificateNotValidBefore(cert) <= at) {
427 /* Leaf certificate has not valid before that is too old. */
428 return false;
429 }
430 return true;
431 }
432
433 bool SecPolicyCheckCertSubjectOrganization(SecCertificateRef cert, CFTypeRef pvcValue) {
434 CFStringRef org = pvcValue;
435 bool match = true;
436 if (!isString(org)) {
437 /* @@@ We can't return an error here and making the evaluation fail
438 won't help much either. */
439 return false;
440 }
441 CFArrayRef organization = SecCertificateCopyOrganization(cert);
442 if (!organization || CFArrayGetCount(organization) != 1 ||
443 !CFEqual(org, CFArrayGetValueAtIndex(organization, 0))) {
444 /* Leaf Subject Organization mismatch. */
445 match = false;
446 }
447 CFReleaseSafe(organization);
448 return match;
449 }
450
451 bool SecPolicyCheckCertSubjectOrganizationalUnit(SecCertificateRef cert, CFTypeRef pvcValue) {
452 CFStringRef orgUnit = pvcValue;
453 bool match = true;
454 if (!isString(orgUnit)) {
455 /* @@@ We can't return an error here and making the evaluation fail
456 won't help much either. */
457 return false;
458 }
459 CFArrayRef organizationalUnit = SecCertificateCopyOrganizationalUnit(cert);
460 if (!organizationalUnit || CFArrayGetCount(organizationalUnit) != 1 ||
461 !CFEqual(orgUnit, CFArrayGetValueAtIndex(organizationalUnit, 0))) {
462 /* Leaf Subject Organizational Unit mismatch. */
463 match = false;
464 }
465 CFReleaseSafe(organizationalUnit);
466 return match;
467 }
468
469 bool SecPolicyCheckCertSubjectCountry(SecCertificateRef cert, CFTypeRef pvcValue) {
470 CFStringRef country = pvcValue;
471 bool match = true;
472 if (!isString(country)) {
473 /* @@@ We can't return an error here and making the evaluation fail
474 won't help much either. */
475 return false;
476 }
477 CFArrayRef certCountry = SecCertificateCopyCountry(cert);
478 if (!certCountry || CFArrayGetCount(certCountry) != 1 ||
479 !CFEqual(country, CFArrayGetValueAtIndex(certCountry, 0))) {
480 /* Subject Country mismatch. */
481 match = false;
482 }
483 CFReleaseSafe(certCountry);
484 return match;
485 }
486
487 bool SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert, CFTypeRef pvcValue) {
488 CFArrayRef trustedServerNames = pvcValue;
489 /* No names specified means we accept any name. */
490 if (!trustedServerNames)
491 return true;
492 if (!isArray(trustedServerNames)) {
493 /* @@@ We can't return an error here and making the evaluation fail
494 won't help much either. */
495 return false;
496 }
497
498 CFIndex tsnCount = CFArrayGetCount(trustedServerNames);
499 bool dnsMatch = false;
500 CFArrayRef dnsNames = SecCertificateCopyDNSNames(cert);
501 if (dnsNames) {
502 CFIndex ix, count = CFArrayGetCount(dnsNames);
503 // @@@ This is O(N^2) unfortunately we can't do better easily unless
504 // we don't do wildcard matching. */
505 for (ix = 0; !dnsMatch && ix < count; ++ix) {
506 CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
507 CFIndex tix;
508 for (tix = 0; tix < tsnCount; ++tix) {
509 CFStringRef serverName =
510 (CFStringRef)CFArrayGetValueAtIndex(trustedServerNames, tix);
511 if (!isString(serverName)) {
512 /* @@@ We can't return an error here and making the
513 evaluation fail won't help much either. */
514 CFReleaseSafe(dnsNames);
515 return false;
516 }
517 /* we purposefully reverse the arguments here such that dns names
518 from the cert are matched against a server name list, where
519 the server names list can contain wildcards and the dns name
520 cannot. References: http://support.microsoft.com/kb/941123
521 It's easy to find occurrences where people tried to use
522 wildcard certificates and were told that those don't work
523 in this context. */
524 if (SecDNSMatch(dns, serverName)) {
525 dnsMatch = true;
526 break;
527 }
528 }
529 }
530 CFRelease(dnsNames);
531 }
532
533 return dnsMatch;
534 }
535
536 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert, CFTypeRef pvcValue) {
537 if (pvcValue && SecCertificateHasMarkerExtension(cert, pvcValue)) {
538 return true;
539 }
540
541 return false;
542 }
543
544 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert,
545 CFTypeRef pvcValue) {
546 if (CFGetTypeID(pvcValue) == CFArrayGetTypeID()) {
547 CFIndex ix, length = CFArrayGetCount(pvcValue);
548 for (ix = 0; ix < length; ix++)
549 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert,
550 CFArrayGetValueAtIndex((CFArrayRef)pvcValue, ix))) {
551 return true;
552 }
553 } else if (CFGetTypeID(pvcValue) == CFDataGetTypeID() ||
554 CFGetTypeID(pvcValue) == CFStringGetTypeID()) {
555 return (NULL != SecCertificateGetExtensionValue(cert, pvcValue));
556 }
557 return false;
558 }
559
560 static CFSetRef copyCertificatePolicies(SecCertificateRef cert) {
561 CFMutableSetRef policies = NULL;
562 policies = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
563 if (!policies) return NULL;
564
565 const SecCECertificatePolicies *cp = SecCertificateGetCertificatePolicies(cert);
566 size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
567 for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
568 CFDataRef oidData = NULL;
569 DERItem *policyOID = &cp->policies[policy_ix].policyIdentifier;
570 oidData = CFDataCreate(kCFAllocatorDefault, policyOID->data, policyOID->length);
571 CFSetAddValue(policies, oidData);
572 CFReleaseSafe(oidData);
573 }
574 return policies;
575 }
576
577 static bool checkPolicyOidData(SecCertificateRef cert , CFDataRef oid) {
578 CFSetRef policies = copyCertificatePolicies(cert);
579 bool found = false;
580 if (policies && CFSetContainsValue(policies, oid)) {
581 found = true;
582 }
583 CFReleaseSafe(policies);
584 return found;
585 }
586
587 /* This one is different from SecPolicyCheckCertificatePolicyOid because
588 that one checks the whole chain. (And uses policy_set_t...) */
589 static bool SecPolicyCheckCertCertificatePolicyOid(SecCertificateRef cert, CFTypeRef pvcValue) {
590 CFTypeRef value = pvcValue;
591 bool result = false;
592
593 if (CFGetTypeID(value) == CFDataGetTypeID())
594 {
595 result = checkPolicyOidData(cert, value);
596 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
597 CFDataRef dataOid = SecCertificateCreateOidDataFromString(NULL, value);
598 if (dataOid) {
599 result = checkPolicyOidData(cert, dataOid);
600 CFRelease(dataOid);
601 }
602 }
603 return result;
604 }
605
606 static bool SecPolicyCheckCertWeak(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
607 if (cert && SecCertificateIsWeakKey(cert)) {
608 /* Leaf certificate has a weak key. */
609 return false;
610 }
611 return true;
612 }
613
614 static bool SecPolicyCheckCertKeySize(SecCertificateRef cert, CFTypeRef pvcValue) {
615 CFDictionaryRef keySizes = pvcValue;
616 if (!SecCertificateIsAtLeastMinKeySize(cert, keySizes)) {
617 return false;
618 }
619 return true;
620 }
621
622 static CFStringRef convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum) {
623 const void *digests[] = { kSecSignatureDigestAlgorithmUnknown,
624 kSecSignatureDigestAlgorithmMD2,
625 kSecSignatureDigestAlgorithmMD4,
626 kSecSignatureDigestAlgorithmMD5,
627 kSecSignatureDigestAlgorithmSHA1,
628 kSecSignatureDigestAlgorithmSHA224,
629 kSecSignatureDigestAlgorithmSHA256,
630 kSecSignatureDigestAlgorithmSHA384,
631 kSecSignatureDigestAlgorithmSHA512,
632 };
633 return digests[algorithmEnum];
634 }
635
636 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert, CFTypeRef pvcValue) {
637 CFSetRef disallowedHashAlgorithms = pvcValue;
638 CFStringRef certAlg = convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert));
639 if (CFSetContainsValue(disallowedHashAlgorithms, certAlg)) {
640 return false;
641 }
642 return true;
643 }
644
645 /*
646 * MARK: SecLeafPVC functions
647 */
648 static CFDictionaryRef SecLeafPVCCopyCallbacks(void) {
649 CFMutableDictionaryRef leafCallbacks = NULL;
650 leafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
651 &kCFTypeDictionaryKeyCallBacks, NULL);
652 CFDictionaryAddValue(leafCallbacks,
653 kSecPolicyCheckCriticalExtensions,
654 SecPolicyCheckCertCriticalExtensions);
655 CFDictionaryAddValue(leafCallbacks,
656 kSecPolicyCheckKeyUsage,
657 SecPolicyCheckCertKeyUsage);
658 CFDictionaryAddValue(leafCallbacks,
659 kSecPolicyCheckExtendedKeyUsage,
660 SecPolicyCheckCertExtendedKeyUsage);
661 CFDictionaryAddValue(leafCallbacks,
662 kSecPolicyCheckNonEmptySubject,
663 SecPolicyCheckCertNonEmptySubject);
664 CFDictionaryAddValue(leafCallbacks,
665 kSecPolicyCheckQualifiedCertStatements,
666 SecPolicyCheckCertQualifiedCertStatements);
667 CFDictionaryAddValue(leafCallbacks,
668 kSecPolicyCheckSSLHostname,
669 SecPolicyCheckCertSSLHostname);
670 CFDictionaryAddValue(leafCallbacks,
671 kSecPolicyCheckEmail,
672 SecPolicyCheckCertEmail);
673 CFDictionaryAddValue(leafCallbacks,
674 kSecPolicyCheckValidLeaf,
675 SecPolicyCheckCertValidLeaf);
676 CFDictionaryAddValue(leafCallbacks,
677 kSecPolicyCheckSubjectCommonNamePrefix,
678 SecPolicyCheckCertSubjectCommonNamePrefix);
679 CFDictionaryAddValue(leafCallbacks,
680 kSecPolicyCheckSubjectCommonName,
681 SecPolicyCheckCertSubjectCommonName);
682 CFDictionaryAddValue(leafCallbacks,
683 kSecPolicyCheckNotValidBefore,
684 SecPolicyCheckCertNotValidBefore);
685 CFDictionaryAddValue(leafCallbacks,
686 kSecPolicyCheckSubjectOrganization,
687 SecPolicyCheckCertSubjectOrganization);
688 CFDictionaryAddValue(leafCallbacks,
689 kSecPolicyCheckSubjectOrganizationalUnit,
690 SecPolicyCheckCertSubjectOrganizationalUnit);
691 CFDictionaryAddValue(leafCallbacks,
692 kSecPolicyCheckEAPTrustedServerNames,
693 SecPolicyCheckCertEAPTrustedServerNames);
694 CFDictionaryAddValue(leafCallbacks,
695 kSecPolicyCheckSubjectCommonNameTEST,
696 SecPolicyCheckCertSubjectCommonNameTEST);
697 CFDictionaryAddValue(leafCallbacks,
698 kSecPolicyCheckLeafMarkerOid,
699 SecPolicyCheckCertLeafMarkerOid);
700 CFDictionaryAddValue(leafCallbacks,
701 kSecPolicyCheckLeafMarkerOidWithoutValueCheck,
702 SecPolicyCheckCertLeafMarkerOidWithoutValueCheck);
703 CFDictionaryAddValue(leafCallbacks,
704 kSecPolicyCheckCertificatePolicy,
705 SecPolicyCheckCertCertificatePolicyOid);
706 CFDictionaryAddValue(leafCallbacks,
707 kSecPolicyCheckWeakLeaf,
708 SecPolicyCheckCertWeak);
709 CFDictionaryAddValue(leafCallbacks,
710 kSecPolicyCheckKeySize,
711 SecPolicyCheckCertKeySize);
712 CFDictionaryAddValue(leafCallbacks,
713 kSecPolicyCheckSignatureHashAlgorithms,
714 SecPolicyCheckCertSignatureHashAlgorithms);
715
716 return leafCallbacks;
717 }
718
719 void SecLeafPVCInit(SecLeafPVCRef pvc, SecCertificateRef leaf, CFArrayRef policies,
720 CFAbsoluteTime verifyTime) {
721 secdebug("alloc", "%p", pvc);
722 // Weird logging policies crashes.
723 //secdebug("policy", "%@", policies);
724 pvc->leaf = CFRetainSafe(leaf);
725 pvc->policies = CFRetainSafe(policies);
726 pvc->verifyTime = verifyTime;
727 pvc->callbacks = SecLeafPVCCopyCallbacks();
728 pvc->policyIX = 0;
729 pvc->result = true;
730
731 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
732 &kCFTypeDictionaryValueCallBacks);
733 pvc->details = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail, 1,
734 &kCFTypeArrayCallBacks);
735 CFRelease(certDetail);
736 }
737
738
739 void SecLeafPVCDelete(SecLeafPVCRef pvc) {
740 secdebug("alloc", "%p", pvc);
741 CFReleaseNull(pvc->policies);
742 CFReleaseNull(pvc->details);
743 CFReleaseNull(pvc->callbacks);
744 CFReleaseNull(pvc->leaf);
745 }
746
747 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc,
748 CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
749
750 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, "leaf",
751 (force ? "force" : ""), result);
752
753 /* If this is not something the current policy cares about ignore
754 this error and return true so our caller continues evaluation. */
755 if (!force) {
756 /* @@@ The right long term fix might be to check if none of the passed
757 in policies contain this key, since not all checks are run for all
758 policies. */
759 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
760 if (policy && !CFDictionaryContainsKey(policy->_options, key))
761 return true;
762 }
763
764 /* @@@ Check to see if the SecTrustSettings for the certificate in question
765 tell us to ignore this error. */
766 pvc->result = false;
767 if (!pvc->details)
768 return false;
769
770 CFMutableDictionaryRef detail =
771 (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
772
773 /* Perhaps detail should have an array of results per key? As it stands
774 in the case of multiple policy failures the last failure stands. */
775 CFDictionarySetValue(detail, key, result);
776
777 return true;
778 }
779
780 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc,
781 CFStringRef key, CFIndex ix, CFTypeRef result) {
782 return SecLeafPVCSetResultForced(pvc, key, ix, result, false);
783 }
784
785 static void SecLeafPVCValidateKey(const void *key, const void *value,
786 void *context) {
787 SecLeafPVCRef pvc = (SecLeafPVCRef)context;
788
789 /* If our caller doesn't want full details and we failed earlier there is
790 no point in doing additional checks. */
791 if (!pvc->result && !pvc->details)
792 return;
793
794 SecPolicyCheckCertFunction fcn = (SecPolicyCheckCertFunction) CFDictionaryGetValue(pvc->callbacks, key);
795 if (!fcn) {
796 pvc->result = false;
797 return;
798 }
799
800 /* kSecPolicyCheckValidLeaf is special */
801 if (CFEqual(key, kSecPolicyCheckValidLeaf)) {
802 CFDateRef verifyDate = CFDateCreate(NULL, pvc->verifyTime);
803 if(!fcn(pvc->leaf, verifyDate)) {
804 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
805 }
806 CFReleaseSafe(verifyDate);
807 } else {
808 /* get pvcValue from current policy */
809 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
810 if (!policy) {
811 pvc->result = false;
812 return;
813 }
814 CFTypeRef pvcValue = (CFTypeRef)CFDictionaryGetValue(policy->_options, key);
815 if(!fcn(pvc->leaf, pvcValue)) {
816 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
817 }
818 }
819 }
820
821 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc) {
822 pvc->result = true;
823 CFArrayRef policies = pvc->policies;
824 CFIndex ix, count = CFArrayGetCount(policies);
825 for (ix = 0; ix < count; ++ix) {
826 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
827 pvc->policyIX = ix;
828 /* Validate all keys for all policies. */
829 CFDictionaryApplyFunction(policy->_options, SecLeafPVCValidateKey, pvc);
830 if (!pvc->result && !pvc->details)
831 return pvc->result;
832 }
833 return pvc->result;
834 }