signer.cpp
1 /* 2 * Copyright (c) 2006-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 // signer - Signing operation supervisor and controller 26 // 27 #include "bundlediskrep.h" 28 #include "der_plist.h" 29 #include "signer.h" 30 #include "resources.h" 31 #include "signerutils.h" 32 #include "SecCodeSigner.h" 33 #include <Security/SecIdentity.h> 34 #include <Security/CMSEncoder.h> 35 #include <Security/CMSPrivate.h> 36 #include <Security/CSCommonPriv.h> 37 #include <CoreFoundation/CFBundlePriv.h> 38 #include "resources.h" 39 #include "machorep.h" 40 #include "reqparser.h" 41 #include "reqdumper.h" 42 #include "csutilities.h" 43 #include <security_utilities/unix++.h> 44 #include <security_utilities/unixchild.h> 45 #include <security_utilities/cfmunge.h> 46 #include <security_utilities/dispatch.h> 47 #include <IOKit/storage/IOStorageDeviceCharacteristics.h> 48 49 namespace Security { 50 namespace CodeSigning { 51 52 53 // 54 // Sign some code. 55 // 56 void SecCodeSigner::Signer::sign(SecCSFlags flags) 57 { 58 rep = code->diskRep()->base(); 59 this->prepare(flags); 60 61 PreSigningContext context(*this); 62 63 considerTeamID(context); 64 65 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { 66 signMachO(fat, context); 67 } else { 68 signArchitectureAgnostic(context); 69 } 70 } 71 72 73 void SecCodeSigner::Signer::considerTeamID(const PreSigningContext& context) 74 { 75 /* If an explicit teamID was passed in it must be 76 the same as what came from the cert */ 77 std::string teamIDFromCert = state.getTeamIDFromSigner(context.certs); 78 79 if (state.mPreserveMetadata & kSecCodeSignerPreserveTeamIdentifier) { 80 /* If preserving the team identifier, teamID is set previously when the 81 code object is still available */ 82 if (!teamIDFromCert.empty() && teamID != teamIDFromCert) 83 MacOSError::throwMe(errSecCSInvalidFlags); 84 } else { 85 if (teamIDFromCert.empty()) { 86 /* state.mTeamID is an explicitly passed teamID */ 87 teamID = state.mTeamID; 88 } else if (state.mTeamID.empty() || (state.mTeamID == teamIDFromCert)) { 89 /* If there was no explicit team ID set, or the explicit team ID matches 90 what is in the cert, use the team ID from the certificate */ 91 teamID = teamIDFromCert; 92 } else { 93 /* The caller passed in an explicit team ID that does not match what is 94 in the signing cert, which is an invalid usage */ 95 MacOSError::throwMe(errSecCSInvalidFlags); 96 } 97 } 98 } 99 100 101 // 102 // Remove any existing code signature from code 103 // 104 void SecCodeSigner::Signer::remove(SecCSFlags flags) 105 { 106 // can't remove a detached signature 107 if (state.mDetached) 108 MacOSError::throwMe(errSecCSNotSupported); 109 110 rep = code->diskRep(); 111 112 if (state.mPreserveAFSC) 113 rep->writer()->setPreserveAFSC(state.mPreserveAFSC); 114 115 if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) { 116 // architecture-sensitive removal 117 MachOEditor editor(rep->writer(), *fat, digestAlgorithms(), rep->mainExecutablePath()); 118 editor.allocate(); // create copy 119 editor.commit(); // commit change 120 } else { 121 // architecture-agnostic removal 122 RefPointer<DiskRep::Writer> writer = rep->writer(); 123 writer->remove(); 124 writer->flush(); 125 } 126 } 127 128 129 // 130 // Contemplate the object-to-be-signed and set up the Signer state accordingly. 131 // 132 void SecCodeSigner::Signer::prepare(SecCSFlags flags) 133 { 134 // make sure the rep passes strict validation 135 if (strict) 136 rep->strictValidate(NULL, MacOSErrorSet(), flags | (kSecCSQuickCheck|kSecCSRestrictSidebandData)); 137 138 // initialize progress/cancellation state 139 code->prepareProgress(0); // totally fake workload - we don't know how many files we'll encounter 140 141 // get the Info.plist out of the rep for some creative defaulting 142 CFRef<CFDictionaryRef> infoDict; 143 if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot)) 144 infoDict.take(makeCFDictionaryFrom(infoData)); 145 146 uint32_t inherit = 0; 147 148 if (code->isSigned() && (code->codeDirectory(false)->flags & kSecCodeSignatureLinkerSigned) == 0) { 149 inherit = state.mPreserveMetadata; 150 } 151 152 // work out the canonical identifier 153 identifier = state.mIdentifier; 154 if (identifier.empty() && (inherit & kSecCodeSignerPreserveIdentifier)) { 155 identifier = code->identifier(); 156 } 157 if (identifier.empty()) { 158 identifier = rep->recommendedIdentifier(*this); 159 if (identifier.find('.') == string::npos) 160 identifier = state.mIdentifierPrefix + identifier; 161 if (identifier.find('.') == string::npos && isAdhoc()) 162 identifier = identifier + "-" + uniqueName(); 163 secinfo("signer", "using default identifier=%s", identifier.c_str()); 164 } else 165 secinfo("signer", "using explicit identifier=%s", identifier.c_str()); 166 167 teamID = state.mTeamID; 168 if (teamID.empty() && (inherit & kSecCodeSignerPreserveTeamIdentifier)) { 169 const char *c_id = code->teamID(); 170 if (c_id) 171 teamID = c_id; 172 } 173 174 // Digest algorithms: explicit or preserved. Subject to diskRep defaults or final default later. 175 hashAlgorithms = state.mDigestAlgorithms; 176 if (hashAlgorithms.empty() && (inherit & kSecCodeSignerPreserveDigestAlgorithm)) 177 hashAlgorithms = code->hashAlgorithms(); 178 179 entitlements = state.mEntitlementData; 180 if (!entitlements && (inherit & kSecCodeSignerPreserveEntitlements)) 181 entitlements = code->component(cdEntitlementSlot); 182 183 // work out the CodeDirectory flags word 184 bool haveCdFlags = false; 185 if (!haveCdFlags && state.mCdFlagsGiven) { 186 cdFlags = state.mCdFlags; 187 secinfo("signer", "using explicit cdFlags=0x%x", cdFlags); 188 haveCdFlags = true; 189 } 190 if (!haveCdFlags) { 191 cdFlags = 0; 192 if (infoDict) 193 if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags"))) { 194 if (CFGetTypeID(csflags) == CFNumberGetTypeID()) { 195 cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags)); 196 secinfo("signer", "using numeric cdFlags=0x%x from Info.plist", cdFlags); 197 } else if (CFGetTypeID(csflags) == CFStringGetTypeID()) { 198 cdFlags = cdTextFlags(cfString(CFStringRef(csflags))); 199 secinfo("signer", "using text cdFlags=0x%x from Info.plist", cdFlags); 200 } else 201 MacOSError::throwMe(errSecCSBadDictionaryFormat); 202 haveCdFlags = true; 203 } 204 } 205 if (!haveCdFlags && (inherit & kSecCodeSignerPreserveFlags)) { 206 cdFlags = code->codeDirectory(false)->flags & ~kSecCodeSignatureAdhoc; 207 secinfo("signer", "using inherited cdFlags=0x%x", cdFlags); 208 haveCdFlags = true; 209 } 210 if (!haveCdFlags) 211 cdFlags = 0; 212 if ((state.mSigner == SecIdentityRef(kCFNull)) && 213 !state.mOmitAdhocFlag) // ad-hoc signing requested... 214 cdFlags |= kSecCodeSignatureAdhoc; // ... so note that 215 216 // prepare the internal requirements input 217 if (state.mRequirements) { 218 if (CFGetTypeID(state.mRequirements) == CFDataGetTypeID()) { // binary form 219 const Requirements *rp = (const Requirements *)CFDataGetBytePtr(state.mRequirements.as<CFDataRef>()); 220 if (!rp->validateBlob()) 221 MacOSError::throwMe(errSecCSReqInvalid); 222 requirements = rp->clone(); 223 } else if (CFGetTypeID(state.mRequirements) == CFStringGetTypeID()) { // text form 224 CFRef<CFMutableStringRef> reqText = CFStringCreateMutableCopy(NULL, 0, state.mRequirements.as<CFStringRef>()); 225 // substitute $ variable tokens 226 CFRange range = { 0, CFStringGetLength(reqText) }; 227 CFStringFindAndReplace(reqText, CFSTR("$self.identifier"), CFTempString(identifier), range, 0); 228 requirements = parseRequirements(cfString(reqText)); 229 } else 230 MacOSError::throwMe(errSecCSInvalidObjectRef); 231 } else if (inherit & kSecCodeSignerPreserveRequirements) 232 if (const Requirements *rp = code->internalRequirements()) 233 requirements = rp->clone(); 234 235 // prepare the resource directory, if any 236 string rpath = rep->resourcesRootPath(); 237 string rrpath; 238 CFCopyRef<CFDictionaryRef> resourceRules; 239 if (!rpath.empty()) { 240 // explicitly given resource rules always win 241 resourceRules = state.mResourceRules; 242 243 // inherited rules come next (overriding embedded ones!) 244 if (!resourceRules && (inherit & kSecCodeSignerPreserveResourceRules)) 245 if (CFDictionaryRef oldRules = code->resourceDictionary(false)) 246 resourceRules = oldRules; 247 248 // embedded resource rules come next 249 if (!resourceRules && infoDict) 250 if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey)) { 251 if (CFGetTypeID(spec) == CFStringGetTypeID()) 252 if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec)))) 253 if (CFDictionaryRef dict = makeCFDictionaryFrom(data)) 254 resourceRules.take(dict); 255 if (!resourceRules) // embedded rules present but unacceptable 256 MacOSError::throwMe(errSecCSResourceRulesInvalid); 257 } 258 259 // if we got one from anywhere (but the defaults), sanity-check it 260 if (resourceRules) { 261 CFTypeRef rules = CFDictionaryGetValue(resourceRules, CFSTR("rules")); 262 if (!rules || CFGetTypeID(rules) != CFDictionaryGetTypeID()) 263 MacOSError::throwMe(errSecCSResourceRulesInvalid); 264 } 265 266 // finally, ask the DiskRep for its default 267 if (!resourceRules) 268 resourceRules.take(rep->defaultResourceRules(*this)); 269 270 // resource root can optionally be the canonical bundle path, 271 // but sealed resource paths are always relative to rpath 272 rrpath = rpath; 273 if (signingFlags() & kSecCSSignBundleRoot) 274 rrpath = cfStringRelease(rep->copyCanonicalPath()); 275 } 276 277 // screen and set the signing time 278 if (state.mSigningTime == CFDateRef(kCFNull)) { 279 emitSigningTime = false; // no time at all 280 } else if (!state.mSigningTime) { 281 emitSigningTime = true; 282 signingTime = 0; // wall clock, established later 283 } else { 284 CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime); 285 if (time > CFAbsoluteTimeGetCurrent()) // not allowed to post-date a signature 286 MacOSError::throwMe(errSecCSBadDictionaryFormat); 287 emitSigningTime = true; 288 signingTime = time; 289 } 290 291 pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize(*this); 292 293 // Allow the DiskRep to modify the signing parameters. This sees explicit and inherited values but not defaults. 294 rep->prepareForSigning(*this); 295 296 // apply some defaults after diskRep intervention 297 if (hashAlgorithms.empty()) { // default to SHA256 + SHA-1 298 hashAlgorithms.insert(kSecCodeSignatureHashSHA1); 299 hashAlgorithms.insert(kSecCodeSignatureHashSHA256); 300 } 301 302 // build the resource directory (once and for all, using the digests determined above) 303 if (!rpath.empty()) { 304 buildResources(rrpath, rpath, resourceRules); 305 } 306 307 308 309 if (inherit & kSecCodeSignerPreservePEH) { 310 /* We need at least one architecture in all cases because we index our 311 * PreEncryptionMaps by architecture. However, only machOs have any 312 * architecture at all, for generic targets there will just be one 313 * PreEncryptionHashMap. 314 * So if the main executable is not a machO, we just choose the local 315 * (signer's) main architecture as dummy value for the first element in our pair. */ 316 preEncryptMainArch = (code->diskRep()->mainExecutableIsMachO() ? 317 code->diskRep()->mainExecutableImage()->bestNativeArch() : 318 Architecture::local()); 319 320 addPreEncryptHashes(preEncryptHashMaps[preEncryptMainArch], code); 321 322 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) { 323 Universal *fat = subcode->diskRep()->mainExecutableImage(); 324 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice. 325 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice. 326 addPreEncryptHashes(preEncryptHashMaps[arch], subcode); 327 }); 328 } 329 330 if (inherit & kSecCodeSignerPreserveRuntime) { 331 /* We need at least one architecture in all cases because we index our 332 * RuntimeVersionMaps by architecture. However, only machOs have any 333 * architecture at all, for generic targets there will just be one 334 * RuntimeVersionMap. 335 * So if the main executable is not a machO, we just choose the local 336 * (signer's) main architecture as dummy value for the first element in our pair. */ 337 runtimeVersionMainArch = (code->diskRep()->mainExecutableIsMachO() ? 338 code->diskRep()->mainExecutableImage()->bestNativeArch() : 339 Architecture::local()); 340 341 addRuntimeVersions(runtimeVersionMap[runtimeVersionMainArch], code); 342 343 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) { 344 Universal *fat = subcode->diskRep()->mainExecutableImage(); 345 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice. 346 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice. 347 addRuntimeVersions(runtimeVersionMap[arch], subcode); 348 }); 349 } 350 } 351 352 void SecCodeSigner::Signer::addPreEncryptHashes(PreEncryptHashMap &map, SecStaticCode const *code) { 353 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories(); 354 355 if (cds != NULL) { 356 for(auto const& pair : *cds) { 357 CodeDirectory::HashAlgorithm const alg = pair.first; 358 CFDataRef const cddata = pair.second; 359 360 CodeDirectory const * cd = 361 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata)); 362 if (cd->preEncryptHashes() != NULL) { 363 CFRef<CFDataRef> preEncrypt = makeCFData(cd->preEncryptHashes(), 364 cd->nCodeSlots * cd->hashSize); 365 map[alg] = preEncrypt; 366 } 367 } 368 } 369 } 370 371 void SecCodeSigner::Signer::addRuntimeVersions(RuntimeVersionMap &map, const SecStaticCode *code) 372 { 373 SecStaticCode::CodeDirectoryMap const *cds = code->codeDirectories(); 374 375 if (cds != NULL) { 376 for(auto const& pair : *cds) { 377 CodeDirectory::HashAlgorithm const alg = pair.first; 378 CFDataRef const cddata = pair.second; 379 380 CodeDirectory const * cd = 381 reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(cddata)); 382 if (cd->runtimeVersion()) { 383 map[alg] = cd->runtimeVersion(); 384 } 385 } 386 } 387 } 388 389 // 390 // Collect the resource seal for a program. 391 // This includes both sealed resources and information about nested code. 392 // 393 void SecCodeSigner::Signer::buildResources(std::string root, std::string relBase, CFDictionaryRef rulesDict) 394 { 395 typedef ResourceBuilder::Rule Rule; 396 397 secinfo("codesign", "start building resource directory"); 398 __block CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary(); 399 400 CFDictionaryRef rules = cfget<CFDictionaryRef>(rulesDict, "rules"); 401 assert(rules); 402 403 if (this->state.mLimitedAsync == NULL) { 404 this->state.mLimitedAsync = 405 /* rdar://problem/20299541: Async workers (i.e. parallelization) are currently 406 * turned off, because the paths for signing code are not ready for it yet. */ 407 // new LimitedAsync(rep->fd().mediumType() == kIOPropertyMediumTypeSolidStateKey); 408 new LimitedAsync(false); 409 } 410 411 CFDictionaryRef files2 = NULL; 412 if (!(signingFlags() & kSecCSSignV1)) { 413 CFCopyRef<CFDictionaryRef> rules2 = cfget<CFDictionaryRef>(rulesDict, "rules2"); 414 if (!rules2) { 415 // Clone V1 rules and add default nesting rules at weight 0 (overridden by anything in rules, 416 // because the default weight, according to ResourceBuilder::addRule(), is 1). 417 // V1 rules typically do not cover these places so we'll prevail, but if they do, we defer to them. 418 rules2.take(cfmake<CFDictionaryRef>("{+%O" 419 "'^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/' = {nested=#T, weight=0}" // exclude dynamic repositories 420 "}", rules)); 421 } 422 423 Dispatch::Group group; 424 Dispatch::Group &groupRef = group; // (into block) 425 426 // build the modern (V2) resource seal 427 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(); 428 CFMutableDictionaryRef filesRef = files.get(); // (into block) 429 ResourceBuilder resourceBuilder(root, relBase, rules2, strict, MacOSErrorSet()); 430 ResourceBuilder &resources = resourceBuilder; // (into block) 431 rep->adjustResources(resources); 432 433 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, const std::string relpath, Rule *rule) { 434 bool isSymlink = (ent->fts_info == FTS_SL); 435 bool isNested = (ruleFlags & ResourceBuilder::nested); 436 const std::string path(ent->fts_path); 437 const std::string accpath(ent->fts_accpath); 438 this->state.mLimitedAsync->perform(groupRef, ^{ 439 CFRef<CFMutableDictionaryRef> seal; 440 if (isNested) { 441 seal.take(signNested(path, relpath)); 442 } else if (isSymlink) { 443 char target[PATH_MAX]; 444 ssize_t len = ::readlink(accpath.c_str(), target, sizeof(target)-1); 445 if (len < 0) 446 UnixError::check(-1); 447 target[len] = '\0'; 448 seal.take(cfmake<CFMutableDictionaryRef>("{symlink=%s}", target)); 449 } else { 450 seal.take(resources.hashFile(accpath.c_str(), digestAlgorithms(), signingFlags() & kSecCSSignStrictPreflight)); 451 } 452 if (seal.get() == NULL) { 453 secerror("Failed to generate sealed resource: %d, %d, %s", isNested, isSymlink, accpath.c_str()); 454 MacOSError::throwMe(errSecCSBadResource); 455 } 456 if (ruleFlags & ResourceBuilder::optional) 457 CFDictionaryAddValue(seal, CFSTR("optional"), kCFBooleanTrue); 458 CFTypeRef hash; 459 StLock<Mutex> _(resourceLock); 460 if ((hash = CFDictionaryGetValue(seal, CFSTR("hash"))) && CFDictionaryGetCount(seal) == 1) // simple form 461 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), hash); 462 else 463 CFDictionaryAddValue(filesRef, CFTempString(relpath).get(), seal.get()); 464 code->reportProgress(); 465 }); 466 }); 467 group.wait(); 468 CFDictionaryAddValue(result, CFSTR("rules2"), resourceBuilder.rules()); 469 files2 = files; 470 CFDictionaryAddValue(result, CFSTR("files2"), files2); 471 } 472 473 CFDictionaryAddValue(result, CFSTR("rules"), rules); // preserve V1 rules in any case 474 if (!(signingFlags() & kSecCSSignNoV1)) { 475 // build the legacy (V1) resource seal 476 __block CFRef<CFMutableDictionaryRef> files = makeCFMutableDictionary(); 477 ResourceBuilder resourceBuilder(root, relBase, rules, strict, MacOSErrorSet()); 478 ResourceBuilder &resources = resourceBuilder; 479 rep->adjustResources(resources); // DiskRep-specific adjustments 480 resources.scan(^(FTSENT *ent, uint32_t ruleFlags, std::string relpath, Rule *rule) { 481 if (ent->fts_info == FTS_F) { 482 CFRef<CFDataRef> hash; 483 if (files2) // try to get the hash from a previously-made version 484 if (CFTypeRef seal = CFDictionaryGetValue(files2, CFTempString(relpath))) { 485 if (CFGetTypeID(seal) == CFDataGetTypeID()) 486 hash = CFDataRef(seal); 487 else 488 hash = CFDataRef(CFDictionaryGetValue(CFDictionaryRef(seal), CFSTR("hash"))); 489 } 490 if (!hash) 491 hash.take(resources.hashFile(ent->fts_accpath, kSecCodeSignatureHashSHA1)); 492 // The user controlled rule flag is a runtime only flag and shouldn't cause use of a more 493 // complex resource serialization as doing so would break serialized adhoc opaque hashes. 494 if ((ruleFlags & ~ResourceBuilder::user_controlled) == 0) { // default case - plain hash 495 cfadd(files, "{%s=%O}", relpath.c_str(), hash.get()); 496 secinfo("csresource", "%s added simple (rule %p)", relpath.c_str(), rule); 497 } else { // more complicated - use a sub-dictionary 498 cfadd(files, "{%s={hash=%O,optional=%B}}", 499 relpath.c_str(), hash.get(), ruleFlags & ResourceBuilder::optional); 500 secinfo("csresource", "%s added complex (rule %p)", relpath.c_str(), rule); 501 } 502 } 503 }); 504 CFDictionaryAddValue(result, CFSTR("files"), files.get()); 505 } 506 507 resourceDirectory = result.get(); 508 resourceDictData.take(makeCFData(resourceDirectory.get())); 509 } 510 511 512 // 513 // Deal with one piece of nested code 514 // 515 CFMutableDictionaryRef SecCodeSigner::Signer::signNested(const std::string &path, const std::string &relpath) 516 { 517 // sign nested code and collect nesting information 518 try { 519 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(path)); 520 if (signingFlags() & kSecCSSignNestedCode) 521 this->state.sign(code, signingFlags()); 522 std::string dr = Dumper::dump(code->designatedRequirement()); 523 if (CFDataRef hash = code->cdHash()) 524 return cfmake<CFMutableDictionaryRef>("{requirement=%s,cdhash=%O}", 525 Dumper::dump(code->designatedRequirement()).c_str(), 526 hash); 527 MacOSError::throwMe(errSecCSUnsigned); 528 } catch (const CommonError &err) { 529 CSError::throwMe(err.osStatus(), kSecCFErrorPath, CFTempURL(relpath, false, this->code->resourceBase())); 530 } 531 } 532 533 534 // 535 // Sign a Mach-O binary, using liberal dollops of that special Mach-O magic sauce. 536 // Note that this will deal just fine with non-fat Mach-O binaries, but it will 537 // treat them as architectural binaries containing (only) one architecture - that 538 // interpretation is courtesy of the Universal/MachO support classes. 539 // 540 void SecCodeSigner::Signer::signMachO(Universal *fat, const Requirement::Context &context) 541 { 542 // Mach-O executable at the core - perform multi-architecture signing 543 RefPointer<DiskRep::Writer> writer = rep->writer(); 544 545 if (state.mPreserveAFSC) 546 writer->setPreserveAFSC(state.mPreserveAFSC); 547 548 unique_ptr<ArchEditor> editor(state.mDetached 549 ? static_cast<ArchEditor *>(new BlobEditor(*fat, *this)) 550 : new MachOEditor(writer, *fat, this->digestAlgorithms(), rep->mainExecutablePath())); 551 assert(editor->count() > 0); 552 if (!editor->attribute(writerNoGlobal)) // can store architecture-common components 553 populate(*editor); 554 555 // pass 1: prepare signature blobs and calculate sizes 556 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 557 MachOEditor::Arch &arch = *it->second; 558 arch.source.reset(fat->architecture(it->first)); 559 560 // library validation is not compatible with i386 561 if (arch.architecture.cpuType() == CPU_TYPE_I386) { 562 if (cdFlags & kSecCodeSignatureLibraryValidation) { 563 MacOSError::throwMe(errSecCSBadLVArch); 564 } 565 } 566 567 bool generateEntitlementDER = false; 568 if (signingFlags() & kSecCSSignGenerateEntitlementDER) { 569 generateEntitlementDER = true; 570 } else { 571 uint32_t platform = arch.source->platform(); 572 switch (platform) { 573 case PLATFORM_WATCHOS: 574 case PLATFORM_BRIDGEOS: 575 generateEntitlementDER = false; 576 break; 577 default: 578 generateEntitlementDER = true; 579 break; 580 } 581 } 582 583 bool mainBinary = arch.source.get()->type() == MH_EXECUTE; 584 585 uint32_t runtimeVersion = 0; 586 if (cdFlags & kSecCodeSignatureRuntime) { 587 runtimeVersion = state.mRuntimeVersionOverride ? state.mRuntimeVersionOverride : arch.source.get()->sdkVersion(); 588 } 589 590 arch.ireqs(requirements, rep->defaultRequirements(&arch.architecture, *this), context); 591 if (editor->attribute(writerNoGlobal)) // can't store globally, add per-arch 592 populate(arch); 593 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) { 594 uint32_t runtimeVersionToUse = runtimeVersion; 595 if ((cdFlags & kSecCodeSignatureRuntime) && runtimeVersionMap.count(arch.architecture)) { 596 if (runtimeVersionMap[arch.architecture].count(*type)) { 597 runtimeVersionToUse = runtimeVersionMap[arch.architecture][*type]; 598 } 599 } 600 arch.eachDigest(^(CodeDirectory::Builder& builder) { 601 populate(builder, arch, arch.ireqs, 602 arch.source->offset(), arch.source->signingExtent(), 603 mainBinary, rep->execSegBase(&(arch.architecture)), rep->execSegLimit(&(arch.architecture)), 604 unsigned(digestAlgorithms().size()-1), 605 preEncryptHashMaps[arch.architecture], runtimeVersionToUse, generateEntitlementDER); 606 }); 607 } 608 609 // add identification blob (made from this architecture) only if we're making a detached signature 610 if (state.mDetached) { 611 CFRef<CFDataRef> identification = MachORep::identificationFor(arch.source.get()); 612 arch.add(cdIdentificationSlot, BlobWrapper::alloc( 613 CFDataGetBytePtr(identification), CFDataGetLength(identification))); 614 } 615 616 // prepare SuperBlob size estimate 617 __block std::vector<size_t> sizes; 618 arch.eachDigest(^(CodeDirectory::Builder& builder){ 619 sizes.push_back(builder.size(CodeDirectory::currentVersion)); 620 }); 621 arch.blobSize = arch.size(sizes, state.mCMSSize, 0); 622 } 623 624 editor->allocate(); 625 626 // pass 2: Finish and generate signatures, and write them 627 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 628 MachOEditor::Arch &arch = *it->second; 629 editor->reset(arch); 630 631 // finish CodeDirectories (off new binary) and sign it 632 __block CodeDirectorySet cdSet; 633 arch.eachDigest(^(CodeDirectory::Builder &builder) { 634 CodeDirectory *cd = builder.build(); 635 cdSet.add(cd); 636 }); 637 638 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict(); 639 CFRef<CFArrayRef> hashList = cdSet.hashList(); 640 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList); 641 642 // complete the SuperBlob 643 cdSet.populate(&arch); 644 arch.add(cdSignatureSlot, BlobWrapper::alloc( 645 CFDataGetBytePtr(signature), CFDataGetLength(signature))); 646 if (!state.mDryRun) { 647 EmbeddedSignatureBlob *blob = arch.make(); 648 editor->write(arch, blob); // takes ownership of blob 649 } 650 } 651 652 // done: write edit copy back over the original 653 if (!state.mDryRun) { 654 editor->commit(); 655 } 656 } 657 658 659 // 660 // Sign a binary that has no notion of architecture. 661 // That currently means anything that isn't Mach-O format. 662 // 663 void SecCodeSigner::Signer::signArchitectureAgnostic(const Requirement::Context &context) 664 { 665 // non-Mach-O executable - single-instance signing 666 RefPointer<DiskRep::Writer> writer = state.mDetached ? 667 (new DetachedBlobWriter(*this)) : rep->writer(); 668 669 if(state.mPreserveAFSC) 670 writer->setPreserveAFSC(state.mPreserveAFSC); 671 672 CodeDirectorySet cdSet; 673 674 for (auto type = digestAlgorithms().begin(); type != digestAlgorithms().end(); ++type) { 675 CodeDirectory::Builder builder(*type); 676 InternalRequirements ireqs; 677 ireqs(requirements, rep->defaultRequirements(NULL, *this), context); 678 populate(*writer); 679 populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit(), 680 false, // only machOs can currently be main binaries 681 rep->execSegBase(NULL), rep->execSegLimit(NULL), 682 unsigned(digestAlgorithms().size()-1), 683 preEncryptHashMaps[preEncryptMainArch], // Only one map, the default. 684 (cdFlags & kSecCodeSignatureRuntime) ? state.mRuntimeVersionOverride : 0, 685 signingFlags() & kSecCSSignGenerateEntitlementDER); 686 687 CodeDirectory *cd = builder.build(); 688 if (!state.mDryRun) 689 cdSet.add(cd); 690 } 691 692 // add identification blob (made from this architecture) only if we're making a detached signature 693 if (state.mDetached) { 694 CFRef<CFDataRef> identification = rep->identification(); 695 writer->component(cdIdentificationSlot, identification); 696 } 697 698 // write out all CodeDirectories 699 if (!state.mDryRun) 700 cdSet.populate(writer); 701 702 CFRef<CFDictionaryRef> hashDict = cdSet.hashDict(); 703 CFRef<CFArrayRef> hashList = cdSet.hashList(); 704 CFRef<CFDataRef> signature = signCodeDirectory(cdSet.primary(), hashDict, hashList); 705 writer->signature(signature); 706 707 // commit to storage 708 writer->flush(); 709 } 710 711 712 // 713 // Global populate - send components to destination buffers ONCE 714 // 715 void SecCodeSigner::Signer::populate(DiskRep::Writer &writer) 716 { 717 if (resourceDirectory && !state.mDryRun) 718 writer.component(cdResourceDirSlot, resourceDictData); 719 } 720 721 722 // 723 // Per-architecture populate - send components to per-architecture buffers 724 // and populate the CodeDirectory for an architecture. In architecture-agnostic 725 // signing operations, the non-architectural binary is considered one (arbitrary) architecture 726 // for the purposes of this call. 727 // 728 void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer, 729 InternalRequirements &ireqs, size_t offset, size_t length, 730 bool mainBinary, size_t execSegBase, size_t execSegLimit, 731 unsigned alternateDigestCount, 732 PreEncryptHashMap const &preEncryptHashMap, 733 uint32_t runtimeVersion, bool generateEntitlementDER) 734 { 735 // fill the CodeDirectory 736 builder.executable(rep->mainExecutablePath(), pagesize, offset, length); 737 builder.flags(cdFlags); 738 builder.identifier(identifier); 739 builder.teamID(teamID); 740 builder.platform(state.mPlatform); 741 builder.execSeg(execSegBase, execSegLimit, mainBinary ? kSecCodeExecSegMainBinary : 0); 742 builder.generatePreEncryptHashes(signingFlags() & kSecCSSignGeneratePEH); 743 builder.preservePreEncryptHashMap(preEncryptHashMap); 744 builder.runTimeVersion(runtimeVersion); 745 746 if (CFRef<CFDataRef> data = rep->component(cdInfoSlot)) 747 builder.specialSlot(cdInfoSlot, data); 748 if (ireqs) { 749 CFRef<CFDataRef> data = makeCFData(*ireqs); 750 writer.component(cdRequirementsSlot, data); 751 builder.specialSlot(cdRequirementsSlot, data); 752 } 753 if (resourceDirectory) 754 builder.specialSlot(cdResourceDirSlot, resourceDictData); 755 if (entitlements) { 756 writer.component(cdEntitlementSlot, entitlements); 757 builder.specialSlot(cdEntitlementSlot, entitlements); 758 759 if (mainBinary) { 760 CFRef<CFDataRef> entitlementDER; 761 uint64_t execSegFlags = 0; 762 cookEntitlements(entitlements, generateEntitlementDER, 763 &execSegFlags, &entitlementDER.aref()); 764 765 if (generateEntitlementDER) { 766 writer.component(cdEntitlementDERSlot, entitlementDER); 767 builder.specialSlot(cdEntitlementDERSlot, entitlementDER); 768 } 769 770 builder.addExecSegFlags(execSegFlags); 771 } 772 } 773 if (CFRef<CFDataRef> repSpecific = rep->component(cdRepSpecificSlot)) 774 builder.specialSlot(cdRepSpecificSlot, repSpecific); 775 776 writer.addDiscretionary(builder); 777 778 #if 0 // rdar://problem/25720754 779 if ((signingFlags() & (kSecCSSignOpaque|kSecCSSignV1)) == 0 && builder.hashType() != kSecCodeSignatureHashSHA1) { 780 // calculate sorted list of top SuperBlob keys in this EmbeddedSignatureBlob (if any) 781 // (but not for opaque or V1 construction, which must remain bit-for-bit compatible) 782 std::vector<Endian<uint32_t> > slotVector; 783 slotVector.push_back(cdCodeDirectorySlot); // mandatory 784 std::set<CodeDirectory::Slot> filledSlots = builder.filledSpecialSlots(); 785 filledSlots.insert(cdTopDirectorySlot); // will be added below 786 copy(filledSlots.begin(), filledSlots.end(), back_inserter(slotVector)); 787 for (unsigned n = 0; n < alternateDigestCount; n++) 788 slotVector.push_back(cdAlternateCodeDirectorySlots + n); 789 slotVector.push_back(cdSignatureSlot); 790 CFTempData cfSlotVector(&slotVector[0], slotVector.size() * sizeof(slotVector[0])); 791 writer.component(cdTopDirectorySlot, cfSlotVector); 792 builder.specialSlot(cdTopDirectorySlot, cfSlotVector); 793 } 794 #endif 795 } 796 797 798 #include <security_smime/tsaSupport.h> 799 800 // 801 // Generate the CMS signature for a (finished) CodeDirectory. 802 // 803 CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd, 804 CFDictionaryRef hashDict, 805 CFArrayRef hashList) 806 { 807 assert(state.mSigner); 808 CFRef<CFMutableDictionaryRef> defaultTSContext = NULL; 809 810 // a null signer generates a null signature blob 811 if (state.mSigner == SecIdentityRef(kCFNull)) 812 return CFDataCreate(NULL, NULL, 0); 813 814 // generate CMS signature 815 CFRef<CMSEncoderRef> cms; 816 MacOSError::check(CMSEncoderCreate(&cms.aref())); 817 MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRootOrFail)); 818 CMSEncoderAddSigners(cms, state.mSigner); 819 CMSEncoderSetSignerAlgorithm(cms, kCMSEncoderDigestAlgorithmSHA256); 820 MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true)); 821 822 if (emitSigningTime) { 823 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime)); 824 CFAbsoluteTime time = signingTime ? signingTime : CFAbsoluteTimeGetCurrent(); 825 MacOSError::check(CMSEncoderSetSigningTime(cms, time)); 826 } 827 828 if (hashDict != NULL) { 829 assert(hashList != NULL); 830 831 // V2 Hash Agility 832 833 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgilityV2)); 834 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgilityV2(cms, hashDict)); 835 836 // V1 Hash Agility 837 838 CFTemp<CFDictionaryRef> hashDict("{cdhashes=%O}", hashList); 839 CFRef<CFDataRef> hashAgilityV1Attribute = makeCFData(hashDict.get()); 840 841 MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrAppleCodesigningHashAgility)); 842 MacOSError::check(CMSEncoderSetAppleCodesigningHashAgility(cms, hashAgilityV1Attribute)); 843 } 844 845 MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length())); 846 847 // Set up to call Timestamp server if requested 848 if (state.mWantTimeStamp) 849 { 850 CFRef<CFErrorRef> error = NULL; 851 defaultTSContext = SecCmsTSAGetDefaultContext(&error.aref()); 852 if (error) 853 MacOSError::throwMe(errSecDataNotAvailable); 854 855 if (state.mNoTimeStampCerts || state.mTimestampService) { 856 if (state.mTimestampService) 857 CFDictionarySetValue(defaultTSContext, kTSAContextKeyURL, state.mTimestampService); 858 if (state.mNoTimeStampCerts) 859 CFDictionarySetValue(defaultTSContext, kTSAContextKeyNoCerts, kCFBooleanTrue); 860 } 861 862 CmsMessageSetTSAContext(cms, defaultTSContext); 863 } 864 865 CFDataRef signature; 866 MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature)); 867 868 return signature; 869 } 870 871 872 // 873 // Our DiskRep::signingContext methods communicate with the signing subsystem 874 // in terms those callers can easily understand. 875 // 876 string SecCodeSigner::Signer::sdkPath(const std::string &path) const 877 { 878 assert(path[0] == '/'); // need absolute path here 879 if (state.mSDKRoot) 880 return cfString(state.mSDKRoot) + path; 881 else 882 return path; 883 } 884 885 bool SecCodeSigner::Signer::isAdhoc() const 886 { 887 return state.mSigner == SecIdentityRef(kCFNull); 888 } 889 890 SecCSFlags SecCodeSigner::Signer::signingFlags() const 891 { 892 return state.mOpFlags; 893 } 894 895 896 // 897 // Parse a text of the form 898 // flag,...,flag 899 // where each flag is the canonical name of a signable CodeDirectory flag. 900 // No abbreviations are allowed, and internally set flags are not accepted. 901 // 902 uint32_t SecCodeSigner::Signer::cdTextFlags(std::string text) 903 { 904 uint32_t flags = 0; 905 for (string::size_type comma = text.find(','); ; text = text.substr(comma+1), comma = text.find(',')) { 906 string word = (comma == string::npos) ? text : text.substr(0, comma); 907 const SecCodeDirectoryFlagTable *item; 908 for (item = kSecCodeDirectoryFlagTable; item->name; item++) 909 if (item->signable && word == item->name) { 910 flags |= item->value; 911 break; 912 } 913 if (!item->name) // not found 914 MacOSError::throwMe(errSecCSInvalidFlags); 915 if (comma == string::npos) // last word 916 break; 917 } 918 return flags; 919 } 920 921 922 // 923 // Generate a unique string from our underlying DiskRep. 924 // We could get 90%+ of the uniquing benefit by just generating 925 // a random string here. Instead, we pick the (hex string encoding of) 926 // the source rep's unique identifier blob. For universal binaries, 927 // this is the canonical local architecture, which is a bit arbitrary. 928 // This provides us with a consistent unique string for all architectures 929 // of a fat binary, *and* (unlike a random string) is reproducible 930 // for identical inputs, even upon resigning. 931 // 932 std::string SecCodeSigner::Signer::uniqueName() const 933 { 934 CFRef<CFDataRef> identification = rep->identification(); 935 const UInt8 *ident = CFDataGetBytePtr(identification); 936 const CFIndex length = CFDataGetLength(identification); 937 string result; 938 for (CFIndex n = 0; n < length; n++) { 939 char hex[3]; 940 snprintf(hex, sizeof(hex), "%02x", ident[n]); 941 result += hex; 942 } 943 return result; 944 } 945 946 bool SecCodeSigner::Signer::booleanEntitlement(CFDictionaryRef entDict, CFStringRef key) { 947 CFBooleanRef entValue = (CFBooleanRef)CFDictionaryGetValue(entDict, key); 948 949 if (entValue == NULL || CFGetTypeID(entValue) != CFBooleanGetTypeID()) { 950 return false; 951 } 952 953 return CFBooleanGetValue(entValue); 954 } 955 956 void SecCodeSigner::Signer::cookEntitlements(CFDataRef entitlements, bool generateDER, 957 uint64_t *execSegFlags, CFDataRef *entitlementDER) 958 { 959 if (!entitlements) { 960 return; // nothing to do. 961 } 962 963 EntitlementDERBlob *derBlob = NULL; 964 965 try { 966 const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlements)); 967 968 if (blob == NULL || !blob->validateBlob(CFDataGetLength(entitlements))) { 969 MacOSError::throwMe(errSecCSInvalidEntitlements); 970 } 971 972 CFRef<CFDictionaryRef> entDict = blob->entitlements(); 973 974 if (generateDER) { 975 CFRef<CFErrorRef> error = NULL; 976 size_t const der_size = der_sizeof_plist(entDict, &error.aref()); 977 978 if (der_size == 0) { 979 secerror("Getting DER size for entitlement plist failed: %@", error.get()); 980 MacOSError::throwMe(errSecCSInvalidEntitlements); 981 } 982 983 derBlob = EntitlementDERBlob::alloc(der_size); 984 985 if (derBlob == NULL) { 986 secerror("Cannot allocate buffer for DER entitlements of size %zu", der_size); 987 MacOSError::throwMe(errSecCSInvalidEntitlements); 988 } 989 uint8_t * const der_end = derBlob->der() + der_size; 990 uint8_t * const der_start = der_encode_plist(entDict, &error.aref(), derBlob->der(), der_end); 991 992 if (der_start != derBlob->der()) { 993 secerror("Entitlement DER start mismatch (%zu)", (size_t)(der_start - derBlob->der())); 994 free(derBlob); 995 MacOSError::throwMe(errSecCSInvalidEntitlements); 996 } 997 998 *entitlementDER = makeCFData(derBlob, derBlob->length()); 999 free(derBlob); 1000 derBlob = NULL; 1001 } 1002 1003 if (execSegFlags != NULL) { 1004 uint64_t flags = 0; 1005 1006 flags |= booleanEntitlement(entDict, CFSTR("get-task-allow")) ? kSecCodeExecSegAllowUnsigned : 0; 1007 flags |= booleanEntitlement(entDict, CFSTR("run-unsigned-code")) ? kSecCodeExecSegAllowUnsigned : 0; 1008 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.cs.debugger")) ? kSecCodeExecSegDebugger : 0; 1009 flags |= booleanEntitlement(entDict, CFSTR("dynamic-codesigning")) ? kSecCodeExecSegJit : 0; 1010 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.skip-library-validation")) ? kSecCodeExecSegSkipLibraryVal : 0; 1011 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-load-cdhash")) ? kSecCodeExecSegCanLoadCdHash : 0; 1012 flags |= booleanEntitlement(entDict, CFSTR("com.apple.private.amfi.can-execute-cdhash")) ? kSecCodeExecSegCanExecCdHash : 0; 1013 1014 *execSegFlags = flags; 1015 } 1016 1017 } catch (const CommonError &err) { 1018 free(derBlob); 1019 // Not fatal if we're not asked to generate DER entitlements. 1020 1021 secwarning("failed to parse entitlements: %s", err.what()); 1022 if (generateDER) { 1023 throw; 1024 } 1025 } 1026 } 1027 1028 //// Signature Editing 1029 1030 void SecCodeSigner::Signer::edit(SecCSFlags flags) 1031 { 1032 rep = code->diskRep()->base(); 1033 1034 Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage(); 1035 1036 prepareForEdit(flags); 1037 1038 if (fat != NULL) { 1039 editMachO(fat); 1040 } else { 1041 editArchitectureAgnostic(); 1042 } 1043 } 1044 1045 EditableDiskRep *SecCodeSigner::Signer::editMainExecutableRep(DiskRep *rep) 1046 { 1047 EditableDiskRep *mainExecRep = NULL; 1048 BundleDiskRep *bundleDiskRep = dynamic_cast<BundleDiskRep*>(rep); 1049 1050 if (bundleDiskRep) { 1051 mainExecRep = dynamic_cast<EditableDiskRep*>(bundleDiskRep->mainExecRep()); 1052 } 1053 1054 return mainExecRep; 1055 } 1056 1057 void SecCodeSigner::Signer::prepareForEdit(SecCSFlags flags) { 1058 setDigestAlgorithms(code->hashAlgorithms()); 1059 1060 Universal *machO = (code->diskRep()->mainExecutableIsMachO() ? 1061 code->diskRep()->mainExecutableImage() : NULL); 1062 1063 /* We need at least one architecture in all cases because we index our 1064 * RawComponentMaps by architecture. However, only machOs have any 1065 * architecture at all, for generic targets there will just be one 1066 * RawComponentMap. 1067 * So if the main executable is not a machO, we just choose the local 1068 * (signer's) main architecture as dummy value for the first element in our pair. */ 1069 editMainArch = (machO != NULL ? machO->bestNativeArch() : Architecture::local()); 1070 1071 if (machO != NULL) { 1072 if (machO->narrowed()) { 1073 /* --arch gives us a narrowed SecStaticCode, but because 1074 * codesign_allocate always creates or replaces signatures 1075 * for all slices, we must operate on the universal 1076 * SecStaticCode. Instead, we provide --edit-arch to specify 1077 * which slices to edit, the others have their code signature 1078 * copied without modifications. 1079 */ 1080 MacOSError::throwMe(errSecCSNotSupported, 1081 "Signature editing must be performed on universal binary instead of narrow slice (using --edit-arch instead of --arch)."); 1082 } 1083 1084 if (state.mEditArch && !machO->isUniversal()) { 1085 MacOSError::throwMe(errSecCSInvalidFlags, 1086 "--edit-arch is only valid for universal binaries."); 1087 } 1088 1089 if (state.mEditCMS && machO->isUniversal() && !state.mEditArch) { 1090 /* Each slice has its own distinct code signature, 1091 * so a CMS blob is only valid for its one slice. 1092 * Therefore, replacing all CMS blobs in all slices 1093 * with the same blob is rather nonsensical, and we refuse. 1094 * 1095 * (Universal binaries with only one slice can exist, 1096 * and in that case the slice to operate on would be 1097 * umambiguous, but we are not treating those binaries 1098 * specially and still want --edit-arch for consistency.) 1099 */ 1100 MacOSError::throwMe(errSecCSNotSupported, 1101 "CMS editing must be performed on specific slice (specified with --edit-arch)."); 1102 } 1103 } 1104 1105 void (^editArch)(SecStaticCode *code, Architecture arch) = 1106 ^(SecStaticCode *code, Architecture arch) { 1107 EditableDiskRep *editRep = dynamic_cast<EditableDiskRep *>(code->diskRep()); 1108 1109 if (editRep == NULL) { 1110 MacOSError::throwMe(errSecCSNotSupported, 1111 "Signature editing not supported for code of this type."); 1112 } 1113 1114 EditableDiskRep *mainExecRep = editMainExecutableRep(code->diskRep()); 1115 1116 if (mainExecRep != NULL) { 1117 // Delegate editing to the main executable if it is an EditableDiskRep. 1118 //(Which is the case for machOs.) 1119 editRep = mainExecRep; 1120 } 1121 1122 editComponents[arch] = std::make_unique<RawComponentMap>(editRep->createRawComponents()); 1123 1124 if (!state.mEditArch || arch == state.mEditArch) { 1125 if (state.mEditCMS) { 1126 CFDataRef cms = state.mEditCMS.get(); 1127 (*editComponents[arch])[cdSignatureSlot] = cms; 1128 } 1129 } 1130 }; 1131 1132 editArch(code, editMainArch); 1133 1134 code->handleOtherArchitectures(^(Security::CodeSigning::SecStaticCode *subcode) { 1135 Universal *fat = subcode->diskRep()->mainExecutableImage(); 1136 assert(fat && fat->narrowed()); // handleOtherArchitectures gave us a focused architecture slice. 1137 Architecture arch = fat->bestNativeArch(); // actually, only architecture for this slice. 1138 editArch(subcode, arch); 1139 }); 1140 1141 /* The resource dictionary is special, because it is 1142 * considered "global" instead of per architecture. 1143 * For editing, that means it's usually not embedded 1144 * in the main executable's signature if it exists, 1145 * but in the containing disk rep (e.g. the 1146 * CodeResources file if the rep is a Bundle). 1147 */ 1148 resourceDictData = rep->component(cdResourceDirSlot); 1149 } 1150 1151 void SecCodeSigner::Signer::editMachO(Universal *fat) { 1152 // Mach-O executable at the core - perform multi-architecture signature editing 1153 RefPointer<DiskRep::Writer> writer = rep->writer(); 1154 1155 if (state.mPreserveAFSC) 1156 writer->setPreserveAFSC(state.mPreserveAFSC); 1157 1158 unique_ptr<ArchEditor> editor(new MachOEditor(writer, *fat, 1159 this->digestAlgorithms(), 1160 rep->mainExecutablePath())); 1161 assert(editor->count() > 0); 1162 1163 if (resourceDictData && !editor->attribute(writerNoGlobal)) { 1164 // For when the resource dict is "global", e.g. for bundles. 1165 editor->component(cdResourceDirSlot, resourceDictData); 1166 } 1167 1168 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 1169 MachOEditor::Arch &arch = *it->second; 1170 arch.source.reset(fat->architecture(it->first)); // transfer ownership 1171 1172 if (resourceDictData && editor->attribute(writerNoGlobal)) { 1173 // Technically possible to embed a resource dict in the embedded sig. 1174 arch.component(cdResourceDirSlot, resourceDictData); 1175 } 1176 1177 for (auto const &entry : *editComponents[arch.architecture]) { 1178 CodeDirectory::Slot slot = entry.first; 1179 CFDataRef data = entry.second.get(); 1180 arch.component(slot, data); 1181 } 1182 1183 /* We must preserve the original superblob's size, as the size is 1184 * also in the macho's load commands, which are itself covered 1185 * by the signature. */ 1186 arch.blobSize = arch.source->signingLength(); 1187 } 1188 1189 editor->allocate(); 1190 1191 for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) { 1192 MachOEditor::Arch &arch = *it->second; 1193 editor->reset(arch); 1194 1195 if (!state.mDryRun) { 1196 EmbeddedSignatureBlob *blob = arch.make(); 1197 editor->write(arch, blob); // takes ownership of blob 1198 } 1199 } 1200 1201 if (!state.mDryRun) { 1202 editor->commit(); 1203 } 1204 1205 } 1206 1207 void SecCodeSigner::Signer::editArchitectureAgnostic() 1208 { 1209 if (state.mDryRun) { 1210 return; 1211 1212 } 1213 // non-Mach-O executable - single-instance signature editing 1214 RefPointer<DiskRep::Writer> writer = rep->writer(); 1215 1216 if(state.mPreserveAFSC) 1217 writer->setPreserveAFSC(state.mPreserveAFSC); 1218 1219 for (auto const &entry : *editComponents[editMainArch]) { 1220 CodeDirectory::Slot slot = entry.first; 1221 CFDataRef data = entry.second.get(); 1222 1223 writer->component(slot, data); 1224 } 1225 1226 // commit to storage 1227 writer->flush(); 1228 } 1229 1230 } // end namespace CodeSigning 1231 } // end namespace Security