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