]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecPolicyLeafCallbacks.c
Security-58286.260.20.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 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
642 /* Weak Signature failures only for non-self-signed certs */
643 Boolean isSelfSigned = false;
644 OSStatus status = SecCertificateIsSelfSigned(cert, &isSelfSigned);
645 if (!SecPolicyCheckCertSignatureHashAlgorithms(cert, disallowedHashes) && (status != errSecSuccess || !isSelfSigned)) {
646 result = false;
647 }
648 CFReleaseSafe(disallowedHashes);
649 return result;
650 }
651
652 static CFStringRef convertSignatureHashAlgorithm(SecSignatureHashAlgorithm algorithmEnum) {
653 const void *digests[] = { kSecSignatureDigestAlgorithmUnknown,
654 kSecSignatureDigestAlgorithmMD2,
655 kSecSignatureDigestAlgorithmMD4,
656 kSecSignatureDigestAlgorithmMD5,
657 kSecSignatureDigestAlgorithmSHA1,
658 kSecSignatureDigestAlgorithmSHA224,
659 kSecSignatureDigestAlgorithmSHA256,
660 kSecSignatureDigestAlgorithmSHA384,
661 kSecSignatureDigestAlgorithmSHA512,
662 };
663 return digests[algorithmEnum];
664 }
665
666 bool SecPolicyCheckCertSignatureHashAlgorithms(SecCertificateRef cert, CFTypeRef pvcValue) {
667 CFSetRef disallowedHashAlgorithms = pvcValue;
668 CFStringRef certAlg = convertSignatureHashAlgorithm(SecCertificateGetSignatureHashAlgorithm(cert));
669 if (CFSetContainsValue(disallowedHashAlgorithms, certAlg)) {
670 return false;
671 }
672 return true;
673 }
674
675 /*
676 * MARK: SecLeafPVC functions
677 */
678 static CFDictionaryRef SecLeafPVCCopyCallbacks(void) {
679 CFMutableDictionaryRef leafCallbacks = NULL;
680 leafCallbacks = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
681 &kCFTypeDictionaryKeyCallBacks, NULL);
682
683 #undef POLICYCHECKMACRO
684 #define __PC_ADD_CHECK_(NAME)
685 #define __PC_ADD_CHECK_O(NAME) CFDictionaryAddValue(leafCallbacks, \
686 kSecPolicyCheck##NAME, SecPolicyCheckCert##NAME);
687
688 #define POLICYCHECKMACRO(NAME, TRUSTRESULT, SUBTYPE, LEAFCHECK, PATHCHECK, LEAFONLY, CSSMERR, OSSTATUS) \
689 __PC_ADD_CHECK_##LEAFONLY(NAME)
690 #include "SecPolicyChecks.list"
691
692 return leafCallbacks;
693 }
694
695 void SecLeafPVCInit(SecLeafPVCRef pvc, SecCertificateRef leaf, CFArrayRef policies,
696 CFAbsoluteTime verifyTime) {
697 secdebug("alloc", "leafpvc %p", pvc);
698 // Weird logging policies crashes.
699 //secdebug("policy", "%@", policies);
700 pvc->leaf = CFRetainSafe(leaf);
701 pvc->policies = CFRetainSafe(policies);
702 pvc->verifyTime = verifyTime;
703 pvc->callbacks = SecLeafPVCCopyCallbacks();
704 pvc->policyIX = 0;
705 pvc->result = true;
706
707 CFMutableDictionaryRef certDetail = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
708 &kCFTypeDictionaryValueCallBacks);
709 pvc->details = CFArrayCreate(kCFAllocatorDefault, (const void **)&certDetail, 1,
710 &kCFTypeArrayCallBacks);
711 CFRelease(certDetail);
712 }
713
714
715 void SecLeafPVCDelete(SecLeafPVCRef pvc) {
716 secdebug("alloc", "delete leaf pvc %p", pvc);
717 CFReleaseNull(pvc->policies);
718 CFReleaseNull(pvc->details);
719 CFReleaseNull(pvc->callbacks);
720 CFReleaseNull(pvc->leaf);
721 }
722
723 static bool SecLeafPVCSetResultForced(SecLeafPVCRef pvc,
724 CFStringRef key, CFIndex ix, CFTypeRef result, bool force) {
725
726 secdebug("policy", "cert[%d]: %@ =(%s)[%s]> %@", (int) ix, key, "leaf",
727 (force ? "force" : ""), result);
728
729 /* If this is not something the current policy cares about ignore
730 this error and return true so our caller continues evaluation. */
731 if (!force) {
732 /* @@@ The right long term fix might be to check if none of the passed
733 in policies contain this key, since not all checks are run for all
734 policies. */
735 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
736 if (policy && !CFDictionaryContainsKey(policy->_options, key))
737 return true;
738 }
739
740 /* @@@ Check to see if the SecTrustSettings for the certificate in question
741 tell us to ignore this error. */
742 pvc->result = false;
743 if (!pvc->details)
744 return false;
745
746 CFMutableDictionaryRef detail =
747 (CFMutableDictionaryRef)CFArrayGetValueAtIndex(pvc->details, ix);
748
749 /* Perhaps detail should have an array of results per key? As it stands
750 in the case of multiple policy failures the last failure stands. */
751 CFDictionarySetValue(detail, key, result);
752
753 return true;
754 }
755
756 static bool SecLeafPVCSetResult(SecLeafPVCRef pvc,
757 CFStringRef key, CFIndex ix, CFTypeRef result) {
758 return SecLeafPVCSetResultForced(pvc, key, ix, result, false);
759 }
760
761 static void SecLeafPVCValidateKey(const void *key, const void *value,
762 void *context) {
763 SecLeafPVCRef pvc = (SecLeafPVCRef)context;
764
765 /* If our caller doesn't want full details and we failed earlier there is
766 no point in doing additional checks. */
767 if (!pvc->result && !pvc->details)
768 return;
769
770 SecPolicyCheckCertFunction fcn = (SecPolicyCheckCertFunction) CFDictionaryGetValue(pvc->callbacks, key);
771 if (!fcn) {
772 pvc->result = false;
773 return;
774 }
775
776 /* kSecPolicyCheckTemporalValidity is special */
777 if (CFEqual(key, kSecPolicyCheckTemporalValidity)) {
778 CFDateRef verifyDate = CFDateCreate(NULL, pvc->verifyTime);
779 if(!fcn(pvc->leaf, verifyDate)) {
780 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
781 }
782 CFReleaseSafe(verifyDate);
783 } else {
784 /* get pvcValue from current policy */
785 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(pvc->policies, pvc->policyIX);
786 if (!policy) {
787 pvc->result = false;
788 return;
789 }
790 CFTypeRef pvcValue = (CFTypeRef)CFDictionaryGetValue(policy->_options, key);
791 if(!fcn(pvc->leaf, pvcValue)) {
792 SecLeafPVCSetResult(pvc, key, 0, kCFBooleanFalse);
793 }
794 }
795 }
796
797 bool SecLeafPVCLeafChecks(SecLeafPVCRef pvc) {
798 pvc->result = true;
799 CFArrayRef policies = pvc->policies;
800 CFIndex ix, count = CFArrayGetCount(policies);
801 for (ix = 0; ix < count; ++ix) {
802 SecPolicyRef policy = (SecPolicyRef)CFArrayGetValueAtIndex(policies, ix);
803 pvc->policyIX = ix;
804 /* Validate all keys for all policies. */
805 CFDictionaryApplyFunction(policy->_options, SecLeafPVCValidateKey, pvc);
806 if (!pvc->result && !pvc->details)
807 return pvc->result;
808 }
809 return pvc->result;
810 }