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 }