machorep.cpp
1 /* 2 * Copyright (c) 2006,2011-2012,2014 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 // machorep - DiskRep mix-in for handling Mach-O main executables 26 // 27 #include "machorep.h" 28 #include "notarization.h" 29 #include "StaticCode.h" 30 #include "reqmaker.h" 31 #include <security_utilities/logging.h> 32 #include <security_utilities/cfmunge.h> 33 #include <security_utilities/casts.h> 34 35 36 37 namespace Security { 38 namespace CodeSigning { 39 40 using namespace UnixPlusPlus; 41 42 43 // 44 // Object management. 45 // We open the main executable lazily, so nothing much happens on construction. 46 // If the context specifies a file offset, we directly pick that Mach-O binary (only). 47 // if it specifies an architecture, we try to pick that. Otherwise, we deliver the whole 48 // Universal object (which will usually deliver the "native" architecture later). 49 // 50 MachORep::MachORep(const char *path, const Context *ctx) 51 : SingleDiskRep(path), mSigningData(NULL) 52 { 53 if (ctx) 54 if (ctx->offset) 55 mExecutable = new Universal(fd(), (size_t)ctx->offset, ctx->size); 56 else if (ctx->arch) { 57 unique_ptr<Universal> full(new Universal(fd())); 58 mExecutable = new Universal(fd(), full->archOffset(ctx->arch), full->archLength(ctx->arch)); 59 } else 60 mExecutable = new Universal(fd()); 61 else 62 mExecutable = new Universal(fd()); 63 64 assert(mExecutable); 65 CODESIGN_DISKREP_CREATE_MACHO(this, (char*)path, (void*)ctx); 66 } 67 68 MachORep::~MachORep() 69 { 70 delete mExecutable; 71 ::free(mSigningData); 72 } 73 74 75 // 76 // Sniffer function for "plausible Mach-O binary" 77 // 78 bool MachORep::candidate(FileDesc &fd) 79 { 80 switch (Universal::typeOf(fd)) { 81 case MH_EXECUTE: 82 case MH_DYLIB: 83 case MH_DYLINKER: 84 case MH_BUNDLE: 85 case MH_KEXT_BUNDLE: 86 case MH_PRELOAD: 87 return true; // dynamic image; supported 88 case MH_OBJECT: 89 return false; // maybe later... 90 default: 91 return false; // not Mach-O (or too exotic) 92 } 93 } 94 95 96 97 // 98 // Nowadays, the main executable object is created upon construction. 99 // 100 Universal *MachORep::mainExecutableImage() 101 { 102 return mExecutable; 103 } 104 105 106 // 107 // Explicitly default to SHA256 (only) digests if the minimum deployment 108 // target is young enough. 109 // 110 void MachORep::prepareForSigning(SigningContext &context) 111 { 112 if (context.digestAlgorithms().empty()) { 113 bool requiresAgileHashes = false; 114 115 Universal::Architectures architectures; 116 mExecutable->architectures(architectures); 117 118 for (Universal::Architectures::const_iterator arch = architectures.begin(); arch != architectures.end(); ++arch) { 119 unique_ptr<MachO> slice(mExecutable->architecture(*arch)); 120 121 uint32_t limit = 0; 122 switch (slice->platform()) { 123 case 0: 124 // If we don't know the platform, we stay agile. 125 requiresAgileHashes = true; 126 continue; 127 case PLATFORM_MACOS: 128 // 10.11.4 had first proper sha256 support. 129 limit = (10 << 16 | 11 << 8 | 4 << 0); 130 break; 131 case PLATFORM_TVOS: 132 case PLATFORM_IOS: 133 // iOS 11 and tvOS 11 had first proper sha256 support. 134 limit = (11 << 16 | 0 << 8 | 0 << 0); 135 break; 136 case PLATFORM_WATCHOS: 137 // We stay agile on the watch right now. 138 requiresAgileHashes = true; 139 continue; 140 default: 141 // All other platforms are assumed to be new and support SHA256. 142 continue; 143 } 144 if (slice->minVersion() < limit) { 145 // If any slice has a min version less than the limit, than we must remain agile. 146 requiresAgileHashes = true; 147 } 148 } 149 150 // Only if every slice met the minimum requirements can we set the digest algorithm to SHA256. 151 // Otherwise, we leave it empty and let it pick the default which will include legacy hash types. 152 if (!requiresAgileHashes) { 153 context.setDigestAlgorithm(kSecCodeSignatureHashSHA256); 154 } 155 } 156 } 157 158 159 // 160 // Signing base is the start of the Mach-O architecture we're using 161 // 162 size_t MachORep::signingBase() 163 { 164 return mainExecutableImage()->archOffset(); 165 } 166 167 size_t MachORep::signingLimit() 168 { 169 unique_ptr<MachO> macho(mExecutable->architecture()); 170 return macho->signingExtent(); 171 } 172 173 bool MachORep::needsExecSeg(const MachO& macho) { 174 uint32_t platform = macho.platform(); 175 176 // Everything gets an exec segment. This is ignored 177 // on non-PPL devices, and explicitly wastes some 178 // space on those devices, but is simpler logic. 179 return platform != 0; 180 } 181 182 size_t MachORep::execSegBase(const Architecture *arch) 183 { 184 unique_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture()); 185 186 if (!needsExecSeg(*macho)) { 187 return 0; 188 } 189 190 segment_command const * const text_cmd = macho->findSegment("__TEXT"); 191 192 if (text_cmd == NULL) { 193 return 0; 194 } 195 196 size_t off = 0; 197 198 if (macho->is64()) { 199 off = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->fileoff); 200 } else { 201 off = text_cmd->fileoff; 202 } 203 204 return off; 205 } 206 207 size_t MachORep::execSegLimit(const Architecture *arch) 208 { 209 unique_ptr<MachO> macho(arch ? mExecutable->architecture(*arch) : mExecutable->architecture()); 210 211 if (!needsExecSeg(*macho)) { 212 return 0; 213 } 214 215 segment_command const * const text_cmd = macho->findSegment("__TEXT"); 216 217 if (text_cmd == NULL) { 218 return 0; 219 } 220 221 size_t size = 0; 222 223 if (macho->is64()) { 224 size = int_cast<uint64_t,size_t>(reinterpret_cast<segment_command_64 const * const>(text_cmd)->filesize); 225 } else { 226 size = text_cmd->filesize; 227 } 228 229 return size; 230 } 231 232 233 // 234 // We choose the binary identifier for a Mach-O binary as follows: 235 // - If the Mach-O headers have a UUID command, use the UUID. 236 // - Otherwise, use the SHA-1 hash of the (entire) load commands. 237 // 238 CFDataRef MachORep::identification() 239 { 240 std::unique_ptr<MachO> macho(mainExecutableImage()->architecture()); 241 return identificationFor(macho.get()); 242 } 243 244 CFDataRef MachORep::identificationFor(MachO *macho) 245 { 246 // if there is a LC_UUID load command, use the UUID contained therein 247 if (const load_command *cmd = macho->findCommand(LC_UUID)) { 248 const uuid_command *uuidc = reinterpret_cast<const uuid_command *>(cmd); 249 // uuidc->cmdsize should be sizeof(uuid_command), so if it is not, 250 // something is wrong. Fail out. 251 if (macho->flip(uuidc->cmdsize) != sizeof(uuid_command)) 252 MacOSError::throwMe(errSecCSSignatureInvalid); 253 char result[4 + sizeof(uuidc->uuid)]; 254 memcpy(result, "UUID", 4); 255 memcpy(result+4, uuidc->uuid, sizeof(uuidc->uuid)); 256 return makeCFData(result, sizeof(result)); 257 } 258 259 // otherwise, use the SHA-1 hash of the entire load command area (this is way, way obsolete) 260 SHA1 hash; 261 hash(&macho->header(), sizeof(mach_header)); 262 hash(macho->loadCommands(), macho->commandLength()); 263 SHA1::Digest digest; 264 hash.finish(digest); 265 return makeCFData(digest, sizeof(digest)); 266 } 267 268 269 // 270 // Retrieve a component from the executable. 271 // This reads the entire signing SuperBlob when first called for an executable, 272 // and then caches it for further use. 273 // Note that we could read individual components directly off disk and only cache 274 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected 275 // to cache the pieces anyway. 276 // 277 CFDataRef MachORep::component(CodeDirectory::SpecialSlot slot) 278 { 279 switch (slot) { 280 case cdInfoSlot: 281 return infoPlist(); 282 default: 283 return embeddedComponent(slot); 284 } 285 } 286 287 // 288 // Retrieve all components, used for signature editing. 289 // 290 EditableDiskRep::RawComponentMap MachORep::createRawComponents() 291 { 292 EditableDiskRep::RawComponentMap blobMap; 293 294 // First call to signingData() caches the result, so this 295 // _should_ not cause performance issues. 296 if (NULL == signingData()) { 297 MacOSError::throwMe(errSecCSUnsigned); 298 } 299 const EmbeddedSignatureBlob &blobs = *signingData(); 300 301 for (unsigned int i = 0; i < blobs.count(); ++i) { 302 CodeDirectory::Slot slot = blobs.type(i); 303 const BlobCore *blob = blobs.blob(i); 304 blobMap[slot] = blobs.blobData(slot, blob); 305 } 306 return blobMap; 307 } 308 309 // Retrieve a component from the embedded signature SuperBlob (if present). 310 // This reads the entire signing SuperBlob when first called for an executable, 311 // and then caches it for further use. 312 // Note that we could read individual components directly off disk and only cache 313 // the SuperBlob Index directory. Our caller (usually SecStaticCode) is expected 314 // to cache the pieces anyway. But it's not clear that the resulting multiple I/O 315 // calls wouldn't be slower in the end. 316 // 317 CFDataRef MachORep::embeddedComponent(CodeDirectory::SpecialSlot slot) 318 { 319 if (signingData()) { 320 return signingData()->component(slot); 321 } 322 323 // not found 324 return NULL; 325 } 326 327 328 329 EmbeddedSignatureBlob *MachORep::signingData() 330 { 331 if (!mSigningData) { // fetch and cache 332 unique_ptr<MachO> macho(mainExecutableImage()->architecture()); 333 if (macho.get()) 334 if (const linkedit_data_command *cs = macho->findCodeSignature()) { 335 size_t offset = macho->flip(cs->dataoff); 336 size_t length = macho->flip(cs->datasize); 337 if ((mSigningData = EmbeddedSignatureBlob::readBlob(macho->fd(), macho->offset() + offset, length))) { 338 secinfo("machorep", "%zd signing bytes in %d blob(s) from %s(%s)", 339 mSigningData->length(), mSigningData->count(), 340 mainExecutablePath().c_str(), macho->architecture().name()); 341 } else { 342 secinfo("machorep", "failed to read signing bytes from %s(%s)", 343 mainExecutablePath().c_str(), macho->architecture().name()); 344 MacOSError::throwMe(errSecCSSignatureInvalid); 345 } 346 } 347 } 348 return mSigningData; 349 } 350 351 352 // 353 // Extract an embedded Info.plist from the file. 354 // Returns NULL if none is found. 355 // 356 CFDataRef MachORep::infoPlist() 357 { 358 CFRef<CFDataRef> info; 359 try { 360 unique_ptr<MachO> macho(mainExecutableImage()->architecture()); 361 if (const section *sect = macho->findSection("__TEXT", "__info_plist")) { 362 if (macho->is64()) { 363 const section_64 *sect64 = reinterpret_cast<const section_64 *>(sect); 364 info.take(macho->dataAt(macho->flip(sect64->offset), (size_t)macho->flip(sect64->size))); 365 } else { 366 info.take(macho->dataAt(macho->flip(sect->offset), macho->flip(sect->size))); 367 } 368 } 369 } catch (...) { 370 secinfo("machorep", "exception reading embedded Info.plist"); 371 } 372 return info.yield(); 373 } 374 375 376 // 377 // Provide a (vaguely) human readable characterization of this code 378 // 379 string MachORep::format() 380 { 381 if (Universal *fat = mainExecutableImage()) { 382 Universal::Architectures archs; 383 fat->architectures(archs); 384 if (fat->isUniversal()) { 385 string s = "Mach-O universal ("; 386 for (Universal::Architectures::const_iterator it = archs.begin(); 387 it != archs.end(); ++it) { 388 if (it != archs.begin()) 389 s += " "; 390 s += it->displayName(); 391 } 392 return s + ")"; 393 } else { 394 assert(archs.size() == 1); 395 return string("Mach-O thin (") + archs.begin()->displayName() + ")"; 396 } 397 } else 398 return "Mach-O (unrecognized format)"; 399 } 400 401 402 // 403 // Flush cached data 404 // 405 void MachORep::flush() 406 { 407 size_t offset = mExecutable->offset(); 408 size_t length = mExecutable->length(); 409 delete mExecutable; 410 mExecutable = NULL; 411 ::free(mSigningData); 412 mSigningData = NULL; 413 SingleDiskRep::flush(); 414 mExecutable = new Universal(fd(), offset, length); 415 } 416 417 CFDictionaryRef MachORep::copyDiskRepInformation() 418 { 419 unique_ptr<MachO> macho (mainExecutableImage()->architecture()); 420 CFRef<CFDictionaryRef> info; 421 422 uint32_t platform = 0; 423 uint32_t minVersion = 0; 424 uint32_t sdkVersion = 0; 425 426 if (macho->version(&platform, &minVersion, &sdkVersion)) { 427 428 /* These keys replace the old kSecCodeInfoDiskRepOSPlatform, kSecCodeInfoDiskRepOSVersionMin 429 * and kSecCodeInfoDiskRepOSSDKVersion. The keys were renamed because we changed what value 430 * "platform" represents: For the old key, the actual load command (e.g. LC_VERSION_MIN_MACOSX) 431 * was returned; for the new key, we return one of the PLATFORM_* values used by LC_BUILD_VERSION. 432 * 433 * The keys are private and undocumented, and maintaining a translation table between the old and 434 * new domain would provide little value at high cost, but we do remove the old keys to make 435 * the change obvious. 436 */ 437 438 info.take(cfmake<CFMutableDictionaryRef>("{%O = %d,%O = %d,%O = %d}", 439 kSecCodeInfoDiskRepVersionPlatform, platform, 440 kSecCodeInfoDiskRepVersionMin, minVersion, 441 kSecCodeInfoDiskRepVersionSDK, sdkVersion)); 442 443 if (platform == PLATFORM_MACOS && sdkVersion < (10 << 16 | 9 << 8)) 444 { 445 info.take(cfmake<CFMutableDictionaryRef>("{+%O, %O = 'OS X SDK version before 10.9 does not support Library Validation'}", 446 info.get(), 447 kSecCodeInfoDiskRepNoLibraryValidation)); 448 } 449 } 450 451 return info.yield(); 452 } 453 454 455 // 456 // Return a recommended unique identifier. 457 // If our file has an embedded Info.plist, use the CFBundleIdentifier from that. 458 // Otherwise, use the default. 459 // 460 string MachORep::recommendedIdentifier(const SigningContext &ctx) 461 { 462 if (CFDataRef info = infoPlist()) { 463 if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(info)) { 464 CFStringRef code = CFStringRef(CFDictionaryGetValue(dict, kCFBundleIdentifierKey)); 465 if (code && CFGetTypeID(code) != CFStringGetTypeID()) 466 MacOSError::throwMe(errSecCSBadDictionaryFormat); 467 if (code) 468 return cfString(code); 469 } else 470 MacOSError::throwMe(errSecCSBadDictionaryFormat); 471 } 472 473 // ah well. Use the default 474 return SingleDiskRep::recommendedIdentifier(ctx); 475 } 476 477 478 // 479 // The default suggested requirements for Mach-O binaries are as follows: 480 // Library requirement: Composed from dynamic load commands. 481 // 482 const Requirements *MachORep::defaultRequirements(const Architecture *arch, const SigningContext &ctx) 483 { 484 assert(arch); // enforced by signing infrastructure 485 Requirements::Maker maker; 486 487 // add library requirements from DYLIB commands (if any) 488 if (Requirement *libreq = libraryRequirements(arch, ctx)) 489 maker.add(kSecLibraryRequirementType, libreq); // takes ownership 490 491 // that's all 492 return maker.make(); 493 } 494 495 Requirement *MachORep::libraryRequirements(const Architecture *arch, const SigningContext &ctx) 496 { 497 unique_ptr<MachO> macho(mainExecutableImage()->architecture(*arch)); 498 Requirement::Maker maker; 499 Requirement::Maker::Chain chain(maker, opOr); 500 501 if (macho.get()) 502 if (const linkedit_data_command *ldep = macho->findLibraryDependencies()) { 503 size_t offset = macho->flip(ldep->dataoff); 504 size_t length = macho->flip(ldep->datasize); 505 if (LibraryDependencyBlob *deplist = LibraryDependencyBlob::readBlob(macho->fd(), macho->offset() + offset, length)) { 506 try { 507 secinfo("machorep", "%zd library dependency bytes in %d blob(s) from %s(%s)", 508 deplist->length(), deplist->count(), 509 mainExecutablePath().c_str(), macho->architecture().name()); 510 unsigned count = deplist->count(); 511 // we could walk through DYLIB load commands in parallel. We just don't need anything from them so far 512 for (unsigned n = 0; n < count; n++) { 513 const Requirement *req = NULL; 514 if (const BlobCore *dep = deplist->blob(n)) { 515 if ((req = Requirement::specific(dep))) { 516 // binary code requirement; good to go 517 } else if (const BlobWrapper *wrap = BlobWrapper::specific(dep)) { 518 // blob-wrapped text form - convert to binary requirement 519 std::string reqString = std::string((const char *)wrap->data(), wrap->length()); 520 CFRef<SecRequirementRef> areq; 521 MacOSError::check(SecRequirementCreateWithString(CFTempString(reqString), kSecCSDefaultFlags, &areq.aref())); 522 CFRef<CFDataRef> reqData; 523 MacOSError::check(SecRequirementCopyData(areq, kSecCSDefaultFlags, &reqData.aref())); 524 req = Requirement::specific((const BlobCore *)CFDataGetBytePtr(reqData)); 525 } else { 526 secinfo("machorep", "unexpected blob type 0x%x in slot %d of binary dependencies", dep->magic(), n); 527 continue; 528 } 529 chain.add(); 530 maker.copy(req); 531 } else 532 secinfo("machorep", "missing DR info for library index %d", n); 533 } 534 ::free(deplist); 535 } catch (...) { 536 ::free(deplist); 537 throw; 538 } 539 } 540 } 541 if (chain.empty()) 542 return NULL; 543 else 544 return maker.make(); 545 } 546 547 548 // 549 // Default to system page size for segmented (paged) signatures 550 // 551 size_t MachORep::pageSize(const SigningContext &) 552 { 553 return segmentedPageSize; 554 } 555 556 557 // 558 // Strict validation 559 // 560 void MachORep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags) 561 { 562 SingleDiskRep::strictValidate(cd, tolerated, flags); 563 564 // if the constructor found suspicious issues, fail a struct validation now 565 if (mExecutable->isSuspicious() && tolerated.find(errSecCSBadMainExecutable) == tolerated.end()) 566 MacOSError::throwMe(errSecCSBadMainExecutable); 567 } 568 569 570 // 571 // FileDiskRep::Writers 572 // 573 DiskRep::Writer *MachORep::writer() 574 { 575 return new Writer(this); 576 } 577 578 579 // 580 // Write a component. 581 // MachORep::Writers don't write to components directly; the signing code uses special 582 // knowledge of the Mach-O format to build embedded signatures and blasts them directly 583 // to disk. Thus this implementation will never be called (and, if called, will simply fail). 584 // 585 void MachORep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 586 { 587 assert(false); 588 Syslog::notice("code signing internal error: trying to write Mach-O component directly"); 589 MacOSError::throwMe(errSecCSInternalError); 590 } 591 592 void MachORep::registerStapledTicket() 593 { 594 CFRef<CFDataRef> data = NULL; 595 if (mSigningData) { 596 data.take(mSigningData->component(cdTicketSlot)); 597 registerStapledTicketInMachO(data); 598 } 599 } 600 601 } // end namespace CodeSigning 602 } // end namespace Security