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