]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPolicyLeafCallbacks.c
Security-57740.31.2.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 SecPolicyCheckCertEAPTrustedServerNames(SecCertificateRef cert, CFTypeRef pvcValue) {
472 CFArrayRef trustedServerNames = pvcValue;
473 /* No names specified means we accept any name. */
474 if (!trustedServerNames)
475 return true;
476 if (!isArray(trustedServerNames)) {
477 /* @@@ We can't return an error here and making the evaluation fail
478 won't help much either. */
479 return false;
480 }
481
482 CFIndex tsnCount = CFArrayGetCount(trustedServerNames);
483 bool dnsMatch = false;
484 CFArrayRef dnsNames = SecCertificateCopyDNSNames(cert);
485 if (dnsNames) {
486 CFIndex ix, count = CFArrayGetCount(dnsNames);
487 // @@@ This is O(N^2) unfortunately we can't do better easily unless
488 // we don't do wildcard matching. */
489 for (ix = 0; !dnsMatch && ix < count; ++ix) {
490 CFStringRef dns = (CFStringRef)CFArrayGetValueAtIndex(dnsNames, ix);
491 CFIndex tix;
492 for (tix = 0; tix < tsnCount; ++tix) {
493 CFStringRef serverName =
494 (CFStringRef)CFArrayGetValueAtIndex(trustedServerNames, tix);
495 if (!isString(serverName)) {
496 /* @@@ We can't return an error here and making the
497 evaluation fail won't help much either. */
498 CFReleaseSafe(dnsNames);
499 return false;
500 }
501 /* we purposefully reverse the arguments here such that dns names
502 from the cert are matched against a server name list, where
503 the server names list can contain wildcards and the dns name
504 cannot. References: http://support.microsoft.com/kb/941123
505 It's easy to find occurrences where people tried to use
506 wildcard certificates and were told that those don't work
507 in this context. */
508 if (SecDNSMatch(dns, serverName)) {
509 dnsMatch = true;
510 break;
511 }
512 }
513 }
514 CFRelease(dnsNames);
515 }
516
517 return dnsMatch;
518 }
519
520 bool SecPolicyCheckCertLeafMarkerOid(SecCertificateRef cert, CFTypeRef pvcValue) {
521 if (pvcValue && SecCertificateHasMarkerExtension(cert, pvcValue)) {
522 return true;
523 }
524
525 return false;
526 }
527
528 bool SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(SecCertificateRef cert,
529 CFTypeRef pvcValue) {
530 if (CFGetTypeID(pvcValue) == CFArrayGetTypeID()) {
531 CFIndex ix, length = CFArrayGetCount(pvcValue);
532 for (ix = 0; ix < length; ix++)
533 if (SecPolicyCheckCertLeafMarkerOidWithoutValueCheck(cert,
534 CFArrayGetValueAtIndex((CFArrayRef)pvcValue, ix))) {
535 return true;
536 }
537 } else if (CFGetTypeID(pvcValue) == CFDataGetTypeID() ||
538 CFGetTypeID(pvcValue) == CFStringGetTypeID()) {
539 return (NULL != SecCertificateGetExtensionValue(cert, pvcValue));
540 }
541 return false;
542 }
543
544 static CFSetRef copyCertificatePolicies(SecCertificateRef cert) {
545 CFMutableSetRef policies = NULL;
546 policies = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
547 if (!policies) return NULL;
548
549 const SecCECertificatePolicies *cp = SecCertificateGetCertificatePolicies(cert);
550 size_t policy_ix, policy_count = cp ? cp->numPolicies : 0;
551 for (policy_ix = 0; policy_ix < policy_count; ++policy_ix) {
552 CFDataRef oidData = NULL;
553 DERItem *policyOID = &cp->policies[policy_ix].policyIdentifier;
554 oidData = CFDataCreate(kCFAllocatorDefault, policyOID->data, policyOID->length);
555 CFSetAddValue(policies, oidData);
556 CFReleaseSafe(oidData);
557 }
558 return policies;
559 }
560
561 static bool checkPolicyOidData(SecCertificateRef cert , CFDataRef oid) {
562 CFSetRef policies = copyCertificatePolicies(cert);
563 bool found = false;
564 if (policies && CFSetContainsValue(policies, oid)) {
565 found = true;
566 }
567 CFReleaseSafe(policies);
568 return found;
569 }
570
571 /* This one is different from SecPolicyCheckCertificatePolicyOid because
572 that one checks the whole chain. (And uses policy_set_t...) */
573 static bool SecPolicyCheckCertCertificatePolicyOid(SecCertificateRef cert, CFTypeRef pvcValue) {
574 CFTypeRef value = pvcValue;
575 bool result = false;
576
577 if (CFGetTypeID(value) == CFDataGetTypeID())
578 {
579 result = checkPolicyOidData(cert, value);
580 } else if (CFGetTypeID(value) == CFStringGetTypeID()) {
581 CFDataRef dataOid = SecCertificateCreateOidDataFromString(NULL, value);
582 if (dataOid) {
583 result = checkPolicyOidData(cert, dataOid);
584 CFRelease(dataOid);
585 }
586 }
587 return result;
588 }
589
590 static bool SecPolicyCheckCertWeak(SecCertificateRef cert, CFTypeRef __unused pvcValue) {
591 if (cert && SecCertificateIsWeakKey(cert)) {
592 /* Leaf certificate has a weak key. */
593 return false;
594 }
595 return true;
596 }
597
598 static bool SecPolicyCheckCertKeySize(SecCertificateRef cert, CFTypeRef pvcValue) {
599 CFDictionaryRef keySizes = pvcValue;
600 if (!SecCertificateIsAtLeastMinKeySize(cert, keySizes)) {
601 return false;
602 }
603 return true;
604 }
605
606 static CFStringRef convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum) {
607 const void *digests[] = { kSecSignatureDigestAlgorithmUnknown,
608 kSecSignatureDigestAlgorithmMD2,
609 kSecSignatureDigestAlgorithmMD4,
610 kSecSignatureDigestAlgorithmMD5,
611 kSecSignatureDigestAlgorithmSHA1,
612 kSecSignatureDigestAlgorithmSHA224,
613 kSecSignatureDigestAlgorithmSHA256,
614 kSecSignatureDigestAlgorithmSHA384,
615 kSecSignatureDigestAlgorithmSHA512,
616 };
617 return digests[algorithmEnum];
618 }
619
620 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert, CFTypeRef pvcValue) {
621 CFSetRef disallowedHashAlgorithms = pvcValue;
622 CFStringRef certAlg = convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert));
623 if (CFSetContainsValue(disallowedHashAlgorithms, certAlg)) {
624 return false;
625 }
626 return true;
627 }
628
629 /*
630 * MARK: SecLeafPVC functions
631 */
632 static CFDictionaryRef SecLeafPVCCopyCallbacks(void) {
633 CFMutableDictionaryRef leafCallbacks = NULL;
634 leafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
635 &kCFTypeDictionaryKeyCallBacks, NULL);
636 CFDictionaryAddValue(leafCallbacks,
637 kSecPolicyCheckCriticalExtensions,
638 SecPolicyCheckCertCriticalExtensions);
639 CFDictionaryAddValue(leafCallbacks,
640 kSecPolicyCheckKeyUsage,
641 SecPolicyCheckCertKeyUsage);
642 CFDictionaryAddValue(leafCallbacks,
643 kSecPolicyCheckExtendedKeyUsage,
644 SecPolicyCheckCertExtendedKeyUsage);
645 CFDictionaryAddValue(leafCallbacks,
646 kSecPolicyCheckNonEmptySubject,
647 SecPolicyCheckCertNonEmptySubject);
648 CFDictionaryAddValue(leafCallbacks,
649 kSecPolicyCheckQualifiedCertStatements,
650 SecPolicyCheckCertQualifiedCertStatements);
651 CFDictionaryAddValue(leafCallbacks,
652 kSecPolicyCheckSSLHostname,
653 SecPolicyCheckCertSSLHostname);
654 CFDictionaryAddValue(leafCallbacks,
655 kSecPolicyCheckEmail,
656 SecPolicyCheckCertEmail);
657 CFDictionaryAddValue(leafCallbacks,
658 kSecPolicyCheckValidLeaf,
659 SecPolicyCheckCertValidLeaf);
660 CFDictionaryAddValue(leafCallbacks,
661 kSecPolicyCheckSubjectCommonNamePrefix,
662 SecPolicyCheckCertSubjectCommonNamePrefix);
663 CFDictionaryAddValue(leafCallbacks,
664 kSecPolicyCheckSubjectCommonName,
665 SecPolicyCheckCertSubjectCommonName);
666 CFDictionaryAddValue(leafCallbacks,
667 kSecPolicyCheckNotValidBefore,
668 SecPolicyCheckCertNotValidBefore);
669 CFDictionaryAddValue(leafCallbacks,
670 kSecPolicyCheckSubjectOrganization,
671 SecPolicyCheckCertSubjectOrganization);
672 CFDictionaryAddValue(leafCallbacks,
673 kSecPolicyCheckSubjectOrganizationalUnit,
674 SecPolicyCheckCertSubjectOrganizationalUnit);
675 CFDictionaryAddValue(leafCallbacks,
676 kSecPolicyCheckEAPTrustedServerNames,
677 SecPolicyCheckCertEAPTrustedServerNames);
678 CFDictionaryAddValue(leafCallbacks,
679 kSecPolicyCheckSubjectCommonNameTEST,
680 SecPolicyCheckCertSubjectCommonNameTEST);
681 CFDictionaryAddValue(leafCallbacks,
682 kSecPolicyCheckLeafMarkerOid,
683 SecPolicyCheckCertLeafMarkerOid);
684 CFDictionaryAddValue(leafCallbacks,
685 kSecPolicyCheckLeafMarkerOidWithoutValueCheck,
686 SecPolicyCheckCertLeafMarkerOidWithoutValueCheck);
687 CFDictionaryAddValue(leafCallbacks,
688 kSecPolicyCheckCertificatePolicy,
689 SecPolicyCheckCertCertificatePolicyOid);
690 CFDictionaryAddValue(leafCallbacks,
691 kSecPolicyCheckWeakLeaf,
692 SecPolicyCheckCertWeak);
693 CFDictionaryAddValue(leafCallbacks,
694 kSecPolicyCheckKeySize,
695 SecPolicyCheckCertKeySize);
696 CFDictionaryAddValue(leafCallbacks,
697 kSecPolicyCheckSignatureHashAlgorithms,
698 SecPolicyCheckCertSignatureHashAlgorithms);
699
700 return leafCallbacks;
701 }
702
703 void SecLeafPVCInit(SecLeafPVCRef pvc, SecCertificateRef leaf, CFArrayRef policies,
704 CFAbsoluteTime verifyTime) {
705 secdebug("alloc", "%p", pvc);
706 // Weird logging policies crashes.
707 //secdebug("policy", "%@", policies);
708 pvc->leaf = CFRetainSafe(leaf);
709 pvc->policies = CFRetainSafe(policies);
710 pvc->verifyTime = verifyTime;
711 pvc->callbacks = SecLeafPVCCopyCallbacks();
712 pvc->policyIX = 0;
713 pvc->result = true;
714
715 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
716 &kCFTypeDictionaryValueCallBacks);
717 pvc->details = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail, 1,
718 &kCFTypeArrayCallBacks);
719 CFRelease(certDetail);
720 }
721
722
723 void SecLeafPVCDelete(SecLeafPVCRef pvc) {
724 secdebug("alloc", "%p", pvc);
725 CFReleaseNull(pvc->policies);
726 CFReleaseNull(pvc->details);
727 CFReleaseNull(pvc->callbacks);
728 CFReleaseNull(pvc->leaf);
729 }
730
731 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc,
732 CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
733
734 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, "leaf",
735 (force ? "force" : ""), result);
736
737 /* If this is not something the current policy cares about ignore
738 this error and return true so our caller continues evaluation. */
739 if (!force) {
740 /* @@@ The right long term fix might be to check if none of the passed
741 in policies contain this key, since not all checks are run for all
742 policies. */
743 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
744 if (policy && !CFDictionaryContainsKey(policy->_options, key))
745 return true;
746 }
747
748 /* @@@ Check to see if the SecTrustSettings for the certificate in question
749 tell us to ignore this error. */
750 pvc->result = false;
751 if (!pvc->details)
752 return false;
753
754 CFMutableDictionaryRef detail =
755 (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
756
757 /* Perhaps detail should have an array of results per key? As it stands
758 in the case of multiple policy failures the last failure stands. */
759 CFDictionarySetValue(detail, key, result);
760
761 return true;
762 }
763
764 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc,
765 CFStringRef key, CFIndex ix, CFTypeRef result) {
766 return SecLeafPVCSetResultForced(pvc, key, ix, result, false);
767 }
768
769 static void SecLeafPVCValidateKey(const void *key, const void *value,
770 void *context) {
771 SecLeafPVCRef pvc = (SecLeafPVCRef)context;
772
773 /* If our caller doesn't want full details and we failed earlier there is
774 no point in doing additional checks. */
775 if (!pvc->result && !pvc->details)
776 return;
777
778 SecPolicyCheckCertFunction fcn = (SecPolicyCheckCertFunction) CFDictionaryGetValue(pvc->callbacks, key);
779 if (!fcn) {
780 pvc->result = false;
781 return;
782 }
783
784 /* kSecPolicyCheckValidLeaf is special */
785 if (CFEqual(key, kSecPolicyCheckValidLeaf)) {
786 CFDateRef verifyDate = CFDateCreate(NULL, pvc->verifyTime);
787 if(!fcn(pvc->leaf, verifyDate)) {
788 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
789 }
790 CFReleaseSafe(verifyDate);
791 } else {
792 /* get pvcValue from current policy */
793 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
794 if (!policy) {
795 pvc->result = false;
796 return;
797 }
798 CFTypeRef pvcValue = (CFTypeRef)CFDictionaryGetValue(policy->_options, key);
799 if(!fcn(pvc->leaf, pvcValue)) {
800 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
801 }
802 }
803 }
804
805 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc) {
806 pvc->result = true;
807 CFArrayRef policies = pvc->policies;
808 CFIndex ix, count = CFArrayGetCount(policies);
809 for (ix = 0; ix < count; ++ix) {
810 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
811 pvc->policyIX = ix;
812 /* Validate all keys for all policies. */
813 CFDictionaryApplyFunction(policy->_options, SecLeafPVCValidateKey, pvc);
814 if (!pvc->result && !pvc->details)
815 return pvc->result;
816 }
817 return pvc->result;
818 }