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