signerutils.cpp
1 /* 2 * Copyright (c) 2006-2013 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 // signerutils - utilities for signature generation 26 // 27 #include "csutilities.h" 28 #include "drmaker.h" 29 #include "resources.h" 30 #include "signerutils.h" 31 #include "signer.h" 32 33 #include <Security/SecCmsBase.h> 34 #include <Security/SecIdentity.h> 35 #include <Security/CMSEncoder.h> 36 37 #include "SecCodeSigner.h" 38 39 #include <security_utilities/unix++.h> 40 #include <security_utilities/logging.h> 41 #include <security_utilities/unixchild.h> 42 43 #include <vector> 44 45 // for helper validation 46 #include "Code.h" 47 #include <security_utilities/cfmunge.h> 48 #include <sys/codesign.h> 49 50 51 namespace Security { 52 namespace CodeSigning { 53 54 55 // 56 // About the Mach-O allocation helper 57 // 58 static const char helperName[] = "codesign_allocate"; 59 static const char helperPath[] = "/usr/bin/codesign_allocate"; 60 static const char helperOverride[] = "CODESIGN_ALLOCATE"; 61 static const size_t csAlign = 16; 62 63 64 // 65 // BlobWriters 66 // 67 void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 68 { 69 return EmbeddedSignatureBlob::Maker::component(slot, data); 70 } 71 72 73 void DetachedBlobWriter::flush() 74 { 75 EmbeddedSignatureBlob *blob = this->make(); 76 signer.code->detachedSignature(CFTempData(*blob)); 77 signer.state.returnDetachedSignature(blob, signer); 78 ::free(blob); 79 } 80 81 82 // 83 // ArchEditor 84 // 85 ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, uint32_t attrs) 86 : DiskRep::Writer(attrs) 87 { 88 Universal::Architectures archList; 89 code.architectures(archList); 90 for (Universal::Architectures::const_iterator it = archList.begin(); 91 it != archList.end(); ++it) 92 architecture[*it] = new Arch(*it, hashTypes); 93 } 94 95 96 ArchEditor::~ArchEditor() 97 { 98 for (ArchMap::iterator it = begin(); it != end(); ++it) 99 delete it->second; 100 } 101 102 103 ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes) 104 : architecture(arch) 105 { 106 blobSize = 0; 107 for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type) 108 cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type))); 109 } 110 111 112 // 113 // BlobEditor 114 // 115 BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s) 116 : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s) 117 { } 118 119 120 void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 121 { 122 mGlobal.component(slot, data); 123 } 124 125 void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) 126 { 127 mMaker.add(arch.architecture.cpuType(), blob); 128 } 129 130 131 void BlobEditor::commit() 132 { 133 // create the architecture-global blob and store it into the superblob 134 mMaker.add(0, mGlobal.make()); // takes ownership of blob 135 136 // finish up the superblob and deliver it 137 DetachedSignatureBlob *blob = mMaker.make(); 138 signer.state.returnDetachedSignature(blob, signer); 139 ::free(blob); 140 } 141 142 143 // 144 // MachOEditor's allocate() method spawns the codesign_allocate helper tool to 145 // "drill up" the Mach-O binary for insertion of Code Signing signature data. 146 // After the tool succeeds, we open the new file and are ready to write it. 147 // 148 MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath) 149 : ArchEditor(code, hashTypes, w->attributes()), 150 writer(w), 151 sourcePath(srcPath), 152 tempPath(srcPath + ".cstemp"), 153 mHashTypes(hashTypes), 154 mNewCode(NULL), 155 mTempMayExist(false) 156 { 157 if (const char *path = getenv(helperOverride)) { 158 mHelperPath = path; 159 mHelperOverridden = true; 160 } else { 161 mHelperPath = helperPath; 162 mHelperOverridden = false; 163 } 164 } 165 166 MachOEditor::~MachOEditor() 167 { 168 delete mNewCode; 169 if (mTempMayExist) 170 ::remove(tempPath.c_str()); // ignore error (can't do anything about it) 171 172 this->kill(); 173 } 174 175 176 void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data) 177 { 178 writer->component(slot, data); 179 } 180 181 182 void MachOEditor::allocate() 183 { 184 // note that we may have a temporary file from now on (for cleanup in the error case) 185 mTempMayExist = true; 186 187 // run codesign_allocate to make room in the executable file 188 fork(); 189 wait(); 190 if (!Child::succeeded()) 191 MacOSError::throwMe(errSecCSHelperFailed); 192 193 // open the new (temporary) Universal file 194 { 195 UidGuard guard(0); 196 mFd.open(tempPath, O_RDWR); 197 } 198 mNewCode = new Universal(mFd); 199 } 200 201 static const unsigned char appleReq[] = { 202 // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate" 203 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 204 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c, 205 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00, 206 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 207 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f, 208 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 209 }; 210 211 void MachOEditor::parentAction() 212 { 213 if (mHelperOverridden) { 214 CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid()); 215 // check code identity of an overridden allocation helper 216 SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath)); 217 code->staticValidate(kSecCSDefaultFlags, NULL); 218 code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed); 219 } 220 } 221 222 void MachOEditor::childAction() 223 { 224 vector<const char *> arguments; 225 arguments.push_back(helperName); 226 arguments.push_back("-i"); 227 arguments.push_back(sourcePath.c_str()); 228 arguments.push_back("-o"); 229 arguments.push_back(tempPath.c_str()); 230 231 for (Iterator it = architecture.begin(); it != architecture.end(); ++it) { 232 size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign); 233 char *ssize; // we'll leak this (execv is coming soon) 234 asprintf(&ssize, "%zd", size); 235 236 if (const char *arch = it->first.name()) { 237 CODESIGN_ALLOCATE_ARCH((char*)arch, (unsigned int)size); 238 arguments.push_back("-a"); 239 arguments.push_back(arch); 240 } else { 241 CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), (unsigned int)size); 242 arguments.push_back("-A"); 243 char *anum; 244 asprintf(&anum, "%d", it->first.cpuType()); 245 arguments.push_back(anum); 246 asprintf(&anum, "%d", it->first.cpuSubtype()); 247 arguments.push_back(anum); 248 } 249 arguments.push_back(ssize); 250 } 251 arguments.push_back(NULL); 252 253 if (mHelperOverridden) 254 ::csops(0, CS_OPS_MARKKILL, NULL, 0); // force code integrity 255 (void)::seteuid(0); // activate privilege if caller has it; ignore error if not 256 execv(mHelperPath, (char * const *)&arguments[0]); 257 } 258 259 void MachOEditor::reset(Arch &arch) 260 { 261 arch.source.reset(mNewCode->architecture(arch.architecture)); 262 263 for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) { 264 arch.eachDigest(^(CodeDirectory::Builder& builder) { 265 /* Signature editing may have no need for cd builders, and not 266 * have opened them, so only reopen them conditionally. */ 267 if (builder.opened()) { 268 builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset()); 269 } 270 }); 271 } 272 } 273 274 275 // 276 // MachOEditor's write() method actually writes the blob into the CODESIGNING section 277 // of the executable image file. 278 // 279 void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) 280 { 281 if (size_t offset = arch.source->signingOffset()) { 282 size_t signingLength = arch.source->signingLength(); 283 CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, (unsigned)blob->length(), (unsigned)signingLength); 284 if (signingLength < blob->length()) 285 MacOSError::throwMe(errSecCSCMSTooLarge); 286 arch.source->seek(offset); 287 arch.source->writeAll(*blob); 288 ::free(blob); // done with it 289 } else { 290 secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this); 291 MacOSError::throwMe(errSecCSInternalError); 292 } 293 } 294 295 296 // 297 // Commit the edit. 298 // This moves the temporary editor copy over the source image file. 299 // Note that the Universal object returned by allocate() is still open 300 // and valid; the caller owns it. 301 // 302 void MachOEditor::commit() 303 { 304 // if the file's owned by someone else *and* we can become root... 305 struct stat st; 306 UnixError::check(::stat(sourcePath.c_str(), &st)); 307 308 // copy over all the *other* stuff 309 Copyfile copy; 310 int fd = mFd; 311 copy.set(COPYFILE_STATE_DST_FD, &fd); 312 { 313 // perform copy under root or file-owner privileges if available 314 UidGuard guard; 315 if (!guard.seteuid(0)) 316 (void)guard.seteuid(st.st_uid); 317 318 // copy metadata from original file... 319 copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA); 320 321 #if TARGET_OS_OSX 322 // determine AFSC status if we are told to preserve compression 323 bool conductCompression = false; 324 cmpInfo cInfo; 325 if (writer->getPreserveAFSC()) { 326 if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) { 327 if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) 328 conductCompression = true; 329 } 330 } 331 #endif 332 333 // ... but explicitly update the timestamps since we did change the file 334 char buf; 335 mFd.read(&buf, sizeof(buf), 0); 336 mFd.write(&buf, sizeof(buf), 0); 337 338 // move the new file into place 339 UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str())); 340 mTempMayExist = false; // we renamed it away 341 342 #if TARGET_OS_OSX 343 // if the original file was compressed, compress the new file after move 344 if (conductCompression) { 345 CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 346 CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType); 347 CFDictionarySetValue(options, kAFSCCompressionTypes, val); 348 CFRelease(val); 349 350 CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options); 351 352 if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) { 353 secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str()); 354 MacOSError::throwMe(errSecCSInternalError); 355 } 356 FinishCompressionAndCleanUp(compressionQueue); 357 358 compressionQueue = NULL; 359 CFRelease(options); 360 } 361 #endif 362 363 } 364 this->writer->flush(); 365 } 366 367 368 // 369 // InternalRequirements 370 // 371 void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context) 372 { 373 // first add the default internal requirements 374 if (defaulted) { 375 this->add(defaulted); 376 ::free((void *)defaulted); // was malloc(3)ed by DiskRep 377 } 378 379 // now override them with any requirements explicitly given by the signer 380 if (given) 381 this->add(given); 382 383 // now add the Designated Requirement, if we can make it and it's not been provided 384 if (!this->contains(kSecDesignatedRequirementType)) { 385 DRMaker maker(context); 386 if (Requirement *dr = maker.make()) { 387 this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr 388 } 389 } 390 391 // return the result 392 mReqs = this->make(); 393 } 394 395 396 // 397 // Pre-Signing contexts 398 // 399 PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer) 400 { 401 // construct a cert chain 402 if (signer.signingIdentity() != SecIdentityRef(kCFNull)) { 403 CFRef<SecCertificateRef> signingCert; 404 MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref())); 405 CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning); 406 CFRef<SecTrustRef> trust; 407 MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref())); 408 SecTrustResultType result; 409 MacOSError::check(SecTrustEvaluate(trust, &result)); 410 CSSM_TP_APPLE_EVIDENCE_INFO *info; 411 MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info)); 412 this->certs = mCerts; 413 } 414 415 // other stuff 416 this->identifier = signer.signingIdentifier(); 417 } 418 419 420 // 421 // A collector of CodeDirectories for hash-agile construction of signatures. 422 // 423 CodeDirectorySet::~CodeDirectorySet() 424 { 425 for (auto it = begin(); it != end(); ++it) 426 ::free(const_cast<CodeDirectory*>(it->second)); 427 } 428 429 430 void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd) 431 { 432 insert(make_pair(cd->hashType, cd)); 433 if (cd->hashType == kSecCodeSignatureHashSHA1) 434 mPrimary = cd; 435 } 436 437 438 void CodeDirectorySet::populate(DiskRep::Writer *writer) const 439 { 440 assert(!empty()); 441 442 if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot 443 mPrimary = begin()->second; 444 445 // reserve slot zero for a SHA-1 digest if present; else pick something else 446 CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots; 447 for (auto it = begin(); it != end(); ++it) { 448 if (it->second == mPrimary) { 449 writer->codeDirectory(it->second, cdCodeDirectorySlot); 450 } else { 451 writer->codeDirectory(it->second, nextAlternate++); 452 } 453 } 454 } 455 456 457 const CodeDirectory* CodeDirectorySet::primary() const 458 { 459 if (mPrimary == NULL) 460 mPrimary = begin()->second; 461 return mPrimary; 462 } 463 464 CFArrayRef CodeDirectorySet::hashList() const 465 { 466 CFRef<CFMutableArrayRef> hashList = makeCFMutableArray(0); 467 for (auto it = begin(); it != end(); ++it) { 468 CFRef<CFDataRef> cdhash = it->second->cdhash(true); 469 CFArrayAppendValue(hashList, cdhash); 470 } 471 return hashList.yield(); 472 } 473 474 CFDictionaryRef CodeDirectorySet::hashDict() const 475 { 476 CFRef<CFMutableDictionaryRef> hashDict = makeCFMutableDictionary(); 477 478 for (auto it = begin(); it != end(); ++it) { 479 SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first); 480 481 if (tag == SEC_OID_UNKNOWN) { 482 MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm); 483 } 484 485 CFRef<CFNumberRef> hashType = makeCFNumber(int(tag)); 486 CFRef<CFDataRef> fullCdhash = it->second->cdhash(false); // Full-length cdhash! 487 CFDictionarySetValue(hashDict, hashType, fullCdhash); 488 } 489 490 return hashDict.yield(); 491 } 492 493 SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) { 494 SECOidTag tag; 495 496 switch (algorithm) { 497 case kSecCodeSignatureHashSHA1: 498 tag = SEC_OID_SHA1; 499 break; 500 case kSecCodeSignatureHashSHA256: 501 case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash 502 tag = SEC_OID_SHA256; 503 break; 504 case kSecCodeSignatureHashSHA384: 505 tag = SEC_OID_SHA384; 506 break; 507 default: 508 tag = SEC_OID_UNKNOWN; 509 } 510 511 return tag; 512 } 513 514 515 516 } // end namespace CodeSigning 517 } // end namespace Security