X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..0d4552ce43ff8bf2e8666a9c5c44c3590eb117a8:/OSX/libsecurity_codesigning/lib/signerutils.cpp diff --git a/OSX/libsecurity_codesigning/lib/signerutils.cpp b/OSX/libsecurity_codesigning/lib/signerutils.cpp index eb19d2d3..f6da5e93 100644 --- a/OSX/libsecurity_codesigning/lib/signerutils.cpp +++ b/OSX/libsecurity_codesigning/lib/signerutils.cpp @@ -24,16 +24,22 @@ // // signerutils - utilities for signature generation // +#include "csutilities.h" +#include "drmaker.h" +#include "resources.h" #include "signerutils.h" #include "signer.h" -#include "SecCodeSigner.h" + +#include #include #include -#include "resources.h" -#include "csutilities.h" -#include "drmaker.h" + +#include "SecCodeSigner.h" + #include +#include #include + #include // for helper validation @@ -76,14 +82,14 @@ void DetachedBlobWriter::flush() // // ArchEditor // -ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, uint32_t attrs) +ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithms hashTypes, uint32_t attrs) : DiskRep::Writer(attrs) { Universal::Architectures archList; code.architectures(archList); for (Universal::Architectures::const_iterator it = archList.begin(); it != archList.end(); ++it) - architecture[*it] = new Arch(*it, hashType); + architecture[*it] = new Arch(*it, hashTypes); } @@ -92,13 +98,22 @@ ArchEditor::~ArchEditor() for (ArchMap::iterator it = begin(); it != end(); ++it) delete it->second; } + + +ArchEditor::Arch::Arch(const Architecture &arch, CodeDirectory::HashAlgorithms hashTypes) + : architecture(arch) +{ + blobSize = 0; + for (auto type = hashTypes.begin(); type != hashTypes.end(); ++type) + cdBuilders.insert(make_pair(*type, new CodeDirectory::Builder(*type))); +} // // BlobEditor // BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s) - : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s) + : ArchEditor(fat, s.digestAlgorithms(), 0), signer(s) { } @@ -130,11 +145,12 @@ void BlobEditor::commit() // "drill up" the Mach-O binary for insertion of Code Signing signature data. // After the tool succeeds, we open the new file and are ready to write it. // -MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath) - : ArchEditor(code, hashType, w->attributes()), +MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithms hashTypes, std::string srcPath) + : ArchEditor(code, hashTypes, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"), + mHashTypes(hashTypes), mNewCode(NULL), mTempMayExist(false) { @@ -152,6 +168,7 @@ MachOEditor::~MachOEditor() delete mNewCode; if (mTempMayExist) ::remove(tempPath.c_str()); // ignore error (can't do anything about it) + this->kill(); } @@ -235,15 +252,23 @@ void MachOEditor::childAction() if (mHelperOverridden) ::csops(0, CS_OPS_MARKKILL, NULL, 0); // force code integrity - ::seteuid(0); // activate privilege if caller has it; ignore error if not + (void)::seteuid(0); // activate privilege if caller has it; ignore error if not execv(mHelperPath, (char * const *)&arguments[0]); } void MachOEditor::reset(Arch &arch) { arch.source.reset(mNewCode->architecture(arch.architecture)); - arch.cdbuilder.reopen(tempPath, - arch.source->offset(), arch.source->signingOffset()); + + for (auto type = mHashTypes.begin(); type != mHashTypes.end(); ++type) { + arch.eachDigest(^(CodeDirectory::Builder& builder) { + /* Signature editing may have no need for cd builders, and not + * have opened them, so only reopen them conditionally. */ + if (builder.opened()) { + builder.reopen(tempPath, arch.source->offset(), arch.source->signingOffset()); + } + }); + } } @@ -262,7 +287,7 @@ void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob) arch.source->writeAll(*blob); ::free(blob); // done with it } else { - secdebug("signer", "%p cannot find CODESIGNING section", this); + secinfo("signer", "%p cannot find CODESIGNING data in Mach-O", this); MacOSError::throwMe(errSecCSInternalError); } } @@ -288,11 +313,23 @@ void MachOEditor::commit() // perform copy under root or file-owner privileges if available UidGuard guard; if (!guard.seteuid(0)) - guard.seteuid(st.st_uid); + (void)guard.seteuid(st.st_uid); // copy metadata from original file... copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA); - + +#if TARGET_OS_OSX + // determine AFSC status if we are told to preserve compression + bool conductCompression = false; + cmpInfo cInfo; + if (writer->getPreserveAFSC()) { + if (queryCompressionInfo(sourcePath.c_str(), &cInfo) == 0) { + if (cInfo.compressionType != 0 && cInfo.compressedSize > 0) + conductCompression = true; + } + } +#endif + // ... but explicitly update the timestamps since we did change the file char buf; mFd.read(&buf, sizeof(buf), 0); @@ -301,7 +338,30 @@ void MachOEditor::commit() // move the new file into place UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str())); mTempMayExist = false; // we renamed it away + +#if TARGET_OS_OSX + // if the original file was compressed, compress the new file after move + if (conductCompression) { + CFMutableDictionaryRef options = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFStringRef val = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%d"), cInfo.compressionType); + CFDictionarySetValue(options, kAFSCCompressionTypes, val); + CFRelease(val); + + CompressionQueueContext compressionQueue = CreateCompressionQueue(NULL, NULL, NULL, NULL, options); + + if (!CompressFile(compressionQueue, sourcePath.c_str(), NULL)) { + secinfo("signer", "%p Failed to queue compression of file %s", this, sourcePath.c_str()); + MacOSError::throwMe(errSecCSInternalError); + } + FinishCompressionAndCleanUp(compressionQueue); + + compressionQueue = NULL; + CFRelease(options); + } +#endif + } + this->writer->flush(); } @@ -355,6 +415,102 @@ PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer) // other stuff this->identifier = signer.signingIdentifier(); } + + +// +// A collector of CodeDirectories for hash-agile construction of signatures. +// +CodeDirectorySet::~CodeDirectorySet() +{ + for (auto it = begin(); it != end(); ++it) + ::free(const_cast(it->second)); +} + + +void CodeDirectorySet::add(const Security::CodeSigning::CodeDirectory *cd) +{ + insert(make_pair(cd->hashType, cd)); + if (cd->hashType == kSecCodeSignatureHashSHA1) + mPrimary = cd; +} + + +void CodeDirectorySet::populate(DiskRep::Writer *writer) const +{ + assert(!empty()); + + if (mPrimary == NULL) // didn't add SHA-1; pick another occupant for this slot + mPrimary = begin()->second; + + // reserve slot zero for a SHA-1 digest if present; else pick something else + CodeDirectory::SpecialSlot nextAlternate = cdAlternateCodeDirectorySlots; + for (auto it = begin(); it != end(); ++it) { + if (it->second == mPrimary) { + writer->codeDirectory(it->second, cdCodeDirectorySlot); + } else { + writer->codeDirectory(it->second, nextAlternate++); + } + } +} + + +const CodeDirectory* CodeDirectorySet::primary() const +{ + if (mPrimary == NULL) + mPrimary = begin()->second; + return mPrimary; +} + +CFArrayRef CodeDirectorySet::hashList() const +{ + CFRef hashList = makeCFMutableArray(0); + for (auto it = begin(); it != end(); ++it) { + CFRef cdhash = it->second->cdhash(true); + CFArrayAppendValue(hashList, cdhash); + } + return hashList.yield(); +} + +CFDictionaryRef CodeDirectorySet::hashDict() const +{ + CFRef hashDict = makeCFMutableDictionary(); + + for (auto it = begin(); it != end(); ++it) { + SECOidTag tag = CodeDirectorySet::SECOidTagForAlgorithm(it->first); + + if (tag == SEC_OID_UNKNOWN) { + MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm); + } + + CFRef hashType = makeCFNumber(int(tag)); + CFRef fullCdhash = it->second->cdhash(false); // Full-length cdhash! + CFDictionarySetValue(hashDict, hashType, fullCdhash); + } + + return hashDict.yield(); +} + +SECOidTag CodeDirectorySet::SECOidTagForAlgorithm(CodeDirectory::HashAlgorithm algorithm) { + SECOidTag tag; + + switch (algorithm) { + case kSecCodeSignatureHashSHA1: + tag = SEC_OID_SHA1; + break; + case kSecCodeSignatureHashSHA256: + case kSecCodeSignatureHashSHA256Truncated: // truncated *page* hashes, not cdhash + tag = SEC_OID_SHA256; + break; + case kSecCodeSignatureHashSHA384: + tag = SEC_OID_SHA384; + break; + default: + tag = SEC_OID_UNKNOWN; + } + + return tag; +} + } // end namespace CodeSigning