1/* 2 * Copyright (c) 2007-2010 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 * SecCertificatePath.c - CoreFoundation based certificate path object 26 */ 27 28#include "SecCertificatePath.h" 29 30#include <Security/SecTrust.h> 31#include <Security/SecTrustStore.h> 32#include <Security/SecItem.h> 33#include <Security/SecCertificateInternal.h> 34#include <Security/SecFramework.h> 35#include <utilities/SecIOFormat.h> 36#include <CoreFoundation/CFRuntime.h> 37#include <CoreFoundation/CFSet.h> 38#include <CoreFoundation/CFString.h> 39#include <CoreFoundation/CFNumber.h> 40#include <CoreFoundation/CFArray.h> 41#include <CoreFoundation/CFPropertyList.h> 42#include <AssertMacros.h> 43#include <stdbool.h> 44#include <string.h> 45#include <stdlib.h> 46#include <pthread.h> 47#include <Security/SecBase.h> 48#include "SecRSAKey.h" 49#include <libDER/oids.h> 50#include <utilities/debugging.h> 51#include <Security/SecInternal.h> 52#include <AssertMacros.h> 53#include <utilities/SecCFError.h> 54 55// MARK: - 56// MARK: SecCertificatePath 57/******************************************************** 58 ************* SecCertificatePath object **************** 59 ********************************************************/ 60struct SecCertificatePath { 61 CFRuntimeBase _base; 62 CFIndex count; 63 64 /* Index of next parent source to search for parents. */ 65 CFIndex nextParentSource; 66 67 /* Index of last certificate in chain who's signature has been verified. 68 0 means nothing has been checked. 1 means the leaf has been verified 69 against it's issuer, etc. */ 70 CFIndex lastVerifiedSigner; 71 72 /* Index of first self issued certificate in the chain. -1 mean there is 73 none. 0 means the leaf is self signed. */ 74 CFIndex selfIssued; 75 76 /* True iff cert at index selfIssued does in fact self verify. */ 77 bool isSelfSigned; 78 79 /* True if the root of this path is a trusted anchor. 80 FIXME get rid of this since it's a property of the evaluation, not a 81 static feature of a certificate path? */ 82 bool isAnchored; 83 SecCertificateRef certificates[]; 84}; 85 86/* CFRuntime regsitration data. */ 87static pthread_once_t kSecCertificatePathRegisterClass = PTHREAD_ONCE_INIT; 88static CFTypeID kSecCertificatePathTypeID = _kCFRuntimeNotATypeID; 89 90static void SecCertificatePathDestroy(CFTypeRef cf) { 91 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf; 92 CFIndex ix; 93 for (ix = 0; ix < certificatePath->count; ++ix) { 94 CFRelease(certificatePath->certificates[ix]); 95 } 96} 97 98static Boolean SecCertificatePathEqual(CFTypeRef cf1, CFTypeRef cf2) { 99 SecCertificatePathRef cp1 = (SecCertificatePathRef) cf1; 100 SecCertificatePathRef cp2 = (SecCertificatePathRef) cf2; 101 if (cp1->count != cp2->count) 102 return false; 103 CFIndex ix; 104 for (ix = 0; ix < cp1->count; ++ix) { 105 if (!CFEqual(cp1->certificates[ix], cp2->certificates[ix])) 106 return false; 107 } 108 109 return true; 110} 111 112static CFHashCode SecCertificatePathHash(CFTypeRef cf) { 113 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf; 114 CFHashCode hashCode = 0; 115 // hashCode = 31 * SecCertificatePathGetTypeID(); 116 CFIndex ix; 117 for (ix = 0; ix < certificatePath->count; ++ix) { 118 hashCode += CFHash(certificatePath->certificates[ix]); 119 } 120 return hashCode; 121} 122 123static CFStringRef SecCertificateCopyPathDescription(CFTypeRef cf) { 124 SecCertificatePathRef certificatePath = (SecCertificatePathRef) cf; 125 CFMutableStringRef desc = CFStringCreateMutable(kCFAllocatorDefault, 0); 126 CFStringRef typeStr = CFCopyTypeIDDescription(CFGetTypeID(cf)); 127 CFStringAppendFormat(desc, NULL, 128 CFSTR("<%@ lvs: %" PRIdCFIndex " certs: "), typeStr, 129 certificatePath->lastVerifiedSigner); 130 CFRelease(typeStr); 131 CFIndex ix; 132 for (ix = 0; ix < certificatePath->count; ++ix) { 133 if (ix > 0) { 134 CFStringAppend(desc, CFSTR(", ")); 135 } 136 CFStringRef str = CFCopyDescription(certificatePath->certificates[ix]); 137 CFStringAppend(desc, str); 138 CFRelease(str); 139 } 140 CFStringAppend(desc, CFSTR(" >")); 141 142 return desc; 143} 144 145static void SecCertificatePathRegisterClass(void) { 146 static const CFRuntimeClass kSecCertificatePathClass = { 147 0, /* version */ 148 "SecCertificatePath", /* class name */ 149 NULL, /* init */ 150 NULL, /* copy */ 151 SecCertificatePathDestroy, /* dealloc */ 152 SecCertificatePathEqual, /* equal */ 153 SecCertificatePathHash, /* hash */ 154 NULL, /* copyFormattingDesc */ 155 SecCertificateCopyPathDescription /* copyDebugDesc */ 156 }; 157 158 kSecCertificatePathTypeID = 159 _CFRuntimeRegisterClass(&kSecCertificatePathClass); 160} 161 162/* SecCertificatePath API functions. */ 163CFTypeID SecCertificatePathGetTypeID(void) { 164 pthread_once(&kSecCertificatePathRegisterClass, 165 SecCertificatePathRegisterClass); 166 return kSecCertificatePathTypeID; 167} 168 169/* Create a new certificate path from an old one. */ 170SecCertificatePathRef SecCertificatePathCreate(SecCertificatePathRef path, 171 SecCertificateRef certificate) { 172 CFAllocatorRef allocator = kCFAllocatorDefault; 173 check(certificate); 174 CFIndex count; 175 CFIndex selfIssued, lastVerifiedSigner; 176 bool isSelfSigned; 177 if (path) { 178 count = path->count + 1; 179 lastVerifiedSigner = path->lastVerifiedSigner; 180 selfIssued = path->selfIssued; 181 isSelfSigned = path->isSelfSigned; 182 } else { 183 count = 1; 184 lastVerifiedSigner = 0; 185 selfIssued = -1; 186 isSelfSigned = false; 187 } 188 189 CFIndex size = sizeof(struct SecCertificatePath) + 190 count * sizeof(SecCertificateRef); 191 SecCertificatePathRef result = 192 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator, 193 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0); 194 if (!result) 195 return NULL; 196 197 result->count = count; 198 result->nextParentSource = 0; 199 result->lastVerifiedSigner = lastVerifiedSigner; 200 result->selfIssued = selfIssued; 201 result->isSelfSigned = isSelfSigned; 202 result->isAnchored = false; 203 CFIndex ix; 204 for (ix = 0; ix < count - 1; ++ix) { 205 result->certificates[ix] = path->certificates[ix]; 206 CFRetain(result->certificates[ix]); 207 } 208 result->certificates[count - 1] = certificate; 209 CFRetainSafe(certificate); 210 211 return result; 212} 213 214/* Create a new certificate path from an xpc_array of data. */ 215SecCertificatePathRef SecCertificatePathCreateWithXPCArray(xpc_object_t xpc_path, CFErrorRef *error) { 216 SecCertificatePathRef result = NULL; 217 require_action_quiet(xpc_path, exit, SecError(errSecParam, error, CFSTR("xpc_path is NULL"))); 218 require_action_quiet(xpc_get_type(xpc_path) == XPC_TYPE_ARRAY, exit, SecError(errSecDecode, error, CFSTR("xpc_path value is not an array"))); 219 size_t count; 220 require_action_quiet(count = xpc_array_get_count(xpc_path), exit, SecError(errSecDecode, error, CFSTR("xpc_path array count == 0"))); 221 size_t size = sizeof(struct SecCertificatePath) + count * sizeof(SecCertificateRef); 222 require_action_quiet(result = (SecCertificatePathRef)_CFRuntimeCreateInstance(kCFAllocatorDefault, SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0), exit, SecError(errSecDecode, error, CFSTR("_CFRuntimeCreateInstance returned NULL"))); 223 224 result->count = count; 225 result->nextParentSource = 0; 226 result->lastVerifiedSigner = count; 227 result->selfIssued = -1; 228 result->isSelfSigned = false; 229 result->isAnchored = false; 230 size_t ix; 231 for (ix = 0; ix < count; ++ix) { 232 SecCertificateRef certificate = SecCertificateCreateWithXPCArrayAtIndex(xpc_path, ix, error); 233 if (certificate) { 234 result->certificates[ix] = certificate; 235 } else { 236 result->count = ix; // total allocated 237 CFReleaseNull(result); 238 break; 239 } 240 } 241 242exit: 243 return result; 244} 245 246SecCertificatePathRef SecCertificatePathCopyFromParent( 247 SecCertificatePathRef path, CFIndex skipCount) { 248 CFAllocatorRef allocator = kCFAllocatorDefault; 249 CFIndex count; 250 CFIndex selfIssued, lastVerifiedSigner; 251 bool isSelfSigned; 252 253 /* Ensure we are at least returning a path of length 1. */ 254 if (skipCount < 0 || path->count < 1 + skipCount) 255 return NULL; 256 257 count = path->count - skipCount; 258 lastVerifiedSigner = path->lastVerifiedSigner > skipCount 259 ? path->lastVerifiedSigner - skipCount : 0; 260 selfIssued = path->selfIssued >= skipCount 261 ? path->selfIssued - skipCount : -1; 262 isSelfSigned = path->selfIssued >= 0 ? path->isSelfSigned : false; 263 264 CFIndex size = sizeof(struct SecCertificatePath) + 265 count * sizeof(SecCertificateRef); 266 SecCertificatePathRef result = 267 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator, 268 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0); 269 if (!result) 270 return NULL; 271 272 result->count = count; 273 result->nextParentSource = 0; 274 result->lastVerifiedSigner = lastVerifiedSigner; 275 result->selfIssued = selfIssued; 276 result->isSelfSigned = isSelfSigned; 277 result->isAnchored = path->isAnchored; 278 CFIndex ix; 279 for (ix = 0; ix < count; ++ix) { 280 result->certificates[ix] = path->certificates[ix + skipCount]; 281 CFRetain(result->certificates[ix]); 282 } 283 284 return result; 285} 286 287SecCertificatePathRef SecCertificatePathCopyAddingLeaf(SecCertificatePathRef path, 288 SecCertificateRef leaf) { 289 CFAllocatorRef allocator = kCFAllocatorDefault; 290 CFIndex count; 291 CFIndex selfIssued, lastVerifiedSigner; 292 bool isSelfSigned; 293 294 /* First make sure the new leaf is signed by path's current leaf. */ 295 SecKeyRef issuerKey = SecCertificatePathCopyPublicKeyAtIndex(path, 0); 296 if (!issuerKey) 297 return NULL; 298 OSStatus status = SecCertificateIsSignedBy(leaf, issuerKey); 299 CFRelease(issuerKey); 300 if (status) 301 return NULL; 302 303 count = path->count + 1; 304 lastVerifiedSigner = path->lastVerifiedSigner + 1; 305 selfIssued = path->selfIssued; 306 isSelfSigned = path->isSelfSigned; 307 308 CFIndex size = sizeof(struct SecCertificatePath) + 309 count * sizeof(SecCertificateRef); 310 SecCertificatePathRef result = 311 (SecCertificatePathRef)_CFRuntimeCreateInstance(allocator, 312 SecCertificatePathGetTypeID(), size - sizeof(CFRuntimeBase), 0); 313 if (!result) 314 return NULL; 315 316 result->count = count; 317 result->nextParentSource = 0; 318 result->lastVerifiedSigner = lastVerifiedSigner; 319 result->selfIssued = selfIssued; 320 result->isSelfSigned = isSelfSigned; 321 result->isAnchored = path->isAnchored; 322 CFIndex ix; 323 for (ix = 1; ix < count; ++ix) { 324 result->certificates[ix] = path->certificates[ix - 1]; 325 CFRetain(result->certificates[ix]); 326 } 327 result->certificates[0] = leaf; 328 CFRetain(leaf); 329 330 return result; 331} 332 333/* Create an array of CFDataRefs from a certificate path. */ 334xpc_object_t SecCertificatePathCopyXPCArray(SecCertificatePathRef path, CFErrorRef *error) { 335 xpc_object_t xpc_chain = NULL; 336 size_t ix, count = path->count; 337 require_action_quiet(xpc_chain = xpc_array_create(NULL, 0), exit, SecError(errSecParam, error, CFSTR("xpc_array_create failed"))); 338 for (ix = 0; ix < count; ++ix) { 339 SecCertificateRef cert = SecCertificatePathGetCertificateAtIndex(path, ix); 340 if (!SecCertificateAppendToXPCArray(cert, xpc_chain, error)) { 341 xpc_release(xpc_chain); 342 return NULL; 343 } 344 } 345 346exit: 347 return xpc_chain; 348} 349 350/* Record the fact that we found our own root cert as our parent 351 certificate. */ 352void SecCertificatePathSetSelfIssued( 353 SecCertificatePathRef certificatePath) { 354 if (certificatePath->selfIssued >= 0) { 355 secdebug("trust", "%@ is already issued at %" PRIdCFIndex, certificatePath, 356 certificatePath->selfIssued); 357 return; 358 } 359 secdebug("trust", "%@ is self issued", certificatePath); 360 certificatePath->selfIssued = certificatePath->count - 1; 361} 362 363void SecCertificatePathSetIsAnchored( 364 SecCertificatePathRef certificatePath) { 365 secdebug("trust", "%@ is anchored", certificatePath); 366 certificatePath->isAnchored = true; 367} 368 369/* Return the index of the first non anchor certificate in the chain that is 370 self signed counting from the leaf up. Return -1 if there is none. */ 371CFIndex SecCertificatePathSelfSignedIndex( 372 SecCertificatePathRef certificatePath) { 373 if (certificatePath->isSelfSigned) 374 return certificatePath->selfIssued; 375 return -1; 376} 377 378Boolean SecCertificatePathIsAnchored( 379 SecCertificatePathRef certificatePath) { 380 return certificatePath->isAnchored; 381} 382 383void SecCertificatePathSetNextSourceIndex( 384 SecCertificatePathRef certificatePath, CFIndex sourceIndex) { 385 certificatePath->nextParentSource = sourceIndex; 386} 387 388CFIndex SecCertificatePathGetNextSourceIndex( 389 SecCertificatePathRef certificatePath) { 390 return certificatePath->nextParentSource; 391} 392 393CFIndex SecCertificatePathGetCount( 394 SecCertificatePathRef certificatePath) { 395 check(certificatePath); 396 return certificatePath ? certificatePath->count : 0; 397} 398 399SecCertificateRef SecCertificatePathGetCertificateAtIndex( 400 SecCertificatePathRef certificatePath, CFIndex ix) { 401 check(certificatePath); 402 check(ix >= 0 && ix < certificatePath->count); 403 return certificatePath->certificates[ix]; 404} 405 406CFIndex SecCertificatePathGetIndexOfCertificate(SecCertificatePathRef path, 407 SecCertificateRef certificate) { 408 CFIndex ix, count = path->count; 409 for (ix = 0; ix < count; ++ix) { 410 if (CFEqual(path->certificates[ix], certificate)) 411 return ix; 412 } 413 return kCFNotFound; 414} 415 416#if 0 417/* Return the leaf certificate for certificatePath. */ 418SecCertificateRef SecCertificatePathGetLeaf( 419 SecCertificatePathRef certificatePath) { 420 return SecCertificatePathGetCertificateAtIndex(certificatePath, 0); 421} 422#endif 423 424/* Return the root certificate for certificatePath. Note that root is just 425 the top of the path as far as it is constructed. It may or may not be 426 trusted or self signed. */ 427SecCertificateRef SecCertificatePathGetRoot( 428 SecCertificatePathRef certificatePath) { 429 return SecCertificatePathGetCertificateAtIndex(certificatePath, 430 SecCertificatePathGetCount(certificatePath) - 1); 431} 432 433SecKeyRef SecCertificatePathCopyPublicKeyAtIndex( 434 SecCertificatePathRef certificatePath, CFIndex ix) { 435 SecCertificateRef certificate = 436 SecCertificatePathGetCertificateAtIndex(certificatePath, ix); 437 const DERAlgorithmId *algId = 438 SecCertificateGetPublicKeyAlgorithm(certificate); 439 const DERItem *params = NULL; 440 if (algId->params.length != 0) { 441 params = &algId->params; 442 } else { 443 CFIndex count = certificatePath->count; 444 for (++ix; ix < count; ++ix) { 445 certificate = certificatePath->certificates[ix]; 446 const DERAlgorithmId *chain_algId = 447 SecCertificateGetPublicKeyAlgorithm(certificate); 448 if (!DEROidCompare(&algId->oid, &chain_algId->oid)) { 449 /* Algorithm oids differ, params stay NULL. */ 450 break; 451 } 452 if (chain_algId->params.length != 0) { 453 params = &chain_algId->params; 454 break; 455 } 456 } 457 } 458 const DERItem *keyData = SecCertificateGetPublicKeyData(certificate); 459 SecAsn1Oid oid1 = { .Data = algId->oid.data, .Length = algId->oid.length }; 460 SecAsn1Item params1 = { 461 .Data = params ? params->data : NULL, 462 .Length = params ? params->length : 0 463 }; 464 SecAsn1Item keyData1 = { 465 .Data = keyData ? keyData->data : NULL, 466 .Length = keyData ? keyData->length : 0 467 }; 468 return SecKeyCreatePublicFromDER(kCFAllocatorDefault, &oid1, ¶ms1, 469 &keyData1); 470} 471 472SecPathVerifyStatus SecCertificatePathVerify( 473 SecCertificatePathRef certificatePath) { 474 check(certificatePath); 475 if (!certificatePath) 476 return kSecPathVerifyFailed; 477 for (; 478 certificatePath->lastVerifiedSigner < certificatePath->count - 1; 479 ++certificatePath->lastVerifiedSigner) { 480 SecKeyRef issuerKey = 481 SecCertificatePathCopyPublicKeyAtIndex(certificatePath, 482 certificatePath->lastVerifiedSigner + 1); 483 if (!issuerKey) 484 return kSecPathVerifiesUnknown; 485 OSStatus status = SecCertificateIsSignedBy( 486 certificatePath->certificates[certificatePath->lastVerifiedSigner], 487 issuerKey); 488 CFRelease(issuerKey); 489 if (status) { 490 return kSecPathVerifyFailed; 491 } 492 } 493 494 if (certificatePath->selfIssued >= 0 && !certificatePath->isSelfSigned) { 495 SecKeyRef issuerKey = 496 SecCertificatePathCopyPublicKeyAtIndex(certificatePath, 497 certificatePath->selfIssued); 498 if (!issuerKey) { 499 certificatePath->selfIssued = -1; 500 } else { 501 OSStatus status = SecCertificateIsSignedBy( 502 certificatePath->certificates[certificatePath->selfIssued], 503 issuerKey); 504 CFRelease(issuerKey); 505 if (!status) { 506 certificatePath->isSelfSigned = true; 507 } else { 508 certificatePath->selfIssued = -1; 509 } 510 } 511 } 512 513 return kSecPathVerifySuccess; 514} 515 516/* Return a score for this certificate chain. */ 517CFIndex SecCertificatePathScore( 518 SecCertificatePathRef certificatePath, CFAbsoluteTime verifyTime) { 519 CFIndex score = 0; 520 if (certificatePath->isAnchored) { 521 /* Anchored paths for the win! */ 522 score += 10000; 523 } 524 525 /* Score points for each certificate in the chain. */ 526 score += 10 * certificatePath->count; 527 528 if (certificatePath->isSelfSigned) { 529 /* If there is a self signed certificate at the end ofthe chain we 530 count it as an extra certificate. If there is one in the middle 531 of the chain we count it for half. */ 532 if (certificatePath->selfIssued == certificatePath->count - 1) 533 score += 10; 534 else 535 score += 5; 536 } 537 538 /* Paths that don't verify score terribly. */ 539 if (certificatePath->lastVerifiedSigner != certificatePath->count - 1) { 540 secdebug("trust", "lvs: %" PRIdCFIndex " count: %" PRIdCFIndex, 541 certificatePath->lastVerifiedSigner, certificatePath->count); 542 score -= 100000; 543 } 544 545 /* Subtract 1 point for each not valid certificate, make sure we 546 subtract less than the amount we add per certificate, since 547 regardless of temporal validity we still prefer longer chains 548 to shorter ones. This distinction is just to ensure that when 549 everything else is equal we prefer the chain with the most 550 certificates that are valid at the given verifyTime. */ 551 CFIndex ix; 552 for (ix = 0; ix < certificatePath->count - 1; ++ix) { 553 if (!SecCertificateIsValid(certificatePath->certificates[ix], 554 verifyTime)) 555 score -= 1; 556 } 557 558 return score; 559} 560