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