X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..0d4552ce43ff8bf2e8666a9c5c44c3590eb117a8:/OSX/libsecurity_codesigning/lib/cdbuilder.cpp diff --git a/OSX/libsecurity_codesigning/lib/cdbuilder.cpp b/OSX/libsecurity_codesigning/lib/cdbuilder.cpp index 719a01b3..7fefc1c5 100644 --- a/OSX/libsecurity_codesigning/lib/cdbuilder.cpp +++ b/OSX/libsecurity_codesigning/lib/cdbuilder.cpp @@ -47,6 +47,11 @@ CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm) mCodeSlots(0), mScatter(NULL), mScatterSize(0), + mExecSegOffset(0), + mExecSegLimit(0), + mExecSegFlags(0), + mGeneratePreEncryptHashes(false), + mRuntimeVersion(0), mDir(NULL) { mDigestLength = (uint32_t)MakeHash(this)->digestLength(); @@ -75,13 +80,18 @@ void CodeDirectory::Builder::executable(string path, void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length) { - assert(mExec); // already called executable() + assert(opened()); // already called executable() mExec.close(); mExec.open(path); mExecOffset = offset; mExecLength = length; } +bool CodeDirectory::Builder::opened() +{ + return bool(mExec); +} + // // Set the source for one special slot @@ -92,6 +102,7 @@ void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data) MakeHash hash(this); hash->update(CFDataGetBytePtr(data), CFDataGetLength(data)); hash->finish(specialSlot(slot)); + mFilledSpecialSlots.insert(slot); if (slot >= mSpecialSlots) mSpecialSlots = slot; } @@ -109,14 +120,20 @@ CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count) return mScatter; } -// This calculates the fixed size of the code directory -// Because of , if the team ID -// field is not used, we leave out the team ID offset -// as well, to keep cd hashes consistent between -// versions. -const size_t CodeDirectory::Builder::fixedSize(const uint32_t version) +// +// Keep the allocated size of the (static) CodeDirectory consistent with +// the version chosen. We dynamically picked the least-needed version +// to provide stability of virtual signatures. +// +size_t CodeDirectory::Builder::fixedSize(const uint32_t version) { size_t cdSize = sizeof(CodeDirectory); + if (version < supportsPreEncrypt) + cdSize -= sizeof(mDir->runtime) + sizeof(mDir->preEncryptOffset); + if (version < supportsExecSegment) + cdSize -= sizeof(mDir->execSegBase) + sizeof(mDir->execSegLimit) + sizeof(mDir->execSegFlags); + if (version < supportsCodeLimit64) + cdSize -= sizeof(mDir->spare3) + sizeof(mDir->codeLimit64); if (version < supportsTeamID) cdSize -= sizeof(mDir->teamIDOffset); @@ -149,6 +166,11 @@ size_t CodeDirectory::Builder::size(const uint32_t version) if (mTeamID.size()) offset += mTeamID.size() + 1; // size of teamID (with null byte) offset += (mCodeSlots + mSpecialSlots) * mDigestLength; // hash vector + + if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) { + offset += mCodeSlots * mDigestLength; + } + if (offset <= offset0) UnixError::throwMe(ENOEXEC); @@ -179,26 +201,37 @@ CodeDirectory *CodeDirectory::Builder::build() size_t teamIDLength = mTeamID.size() + 1; // Determine the version - if (mTeamID.size()) { + if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty() || mRuntimeVersion) { version = currentVersion; + } else if (mExecSegLimit > 0) { + version = supportsExecSegment; + } else if (mExecLength > UINT32_MAX) { + version = supportsCodeLimit64; + } else if (mTeamID.size()) { + version = supportsTeamID; } else { version = supportsScatter; } + if (mCodeSlots > UINT32_MAX) // (still limited to 32 bits) + MacOSError::throwMe(errSecCSTooBig); + size_t total = size(version); if (!(mDir = (CodeDirectory *)calloc(1, total))) // initialize to zero UnixError::throwMe(ENOMEM); - - if (mExecLength > UINT32_MAX) - MacOSError::throwMe(errSecCSTooBig); - + // fill header mDir->initialize(total); mDir->version = version; mDir->flags = mFlags; mDir->nSpecialSlots = (uint32_t)mSpecialSlots; mDir->nCodeSlots = (uint32_t)mCodeSlots; - mDir->codeLimit = (uint32_t)mExecLength; + if (mExecLength > UINT32_MAX) { + mDir->codeLimit = UINT32_MAX; + mDir->codeLimit64 = mExecLength; + } else { + mDir->codeLimit = uint32_t(mExecLength); + } mDir->hashType = mHashType; mDir->platform = mPlatform; mDir->hashSize = mDigestLength; @@ -211,6 +244,11 @@ CodeDirectory *CodeDirectory::Builder::build() } else mDir->pageSize = 0; // means infinite page size + mDir->execSegBase = mExecSegOffset; + mDir->execSegLimit = mExecSegLimit; + mDir->execSegFlags = mExecSegFlags; + mDir->runtime = mRuntimeVersion; + // locate and fill flex fields size_t offset = fixedSize(mDir->version); @@ -229,26 +267,53 @@ CodeDirectory *CodeDirectory::Builder::build() memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength); offset += teamIDLength; } + // (add new flexibly-allocated fields here) + /* Pre-encrypt hashes come before normal hashes, so that the kernel can free + * the normal, potentially post-encrypt hashes away easily. */ + if (mGeneratePreEncryptHashes || !mPreservedPreEncryptHashMap.empty()) { + mDir->preEncryptOffset = (uint32_t)offset; + offset += mCodeSlots * mDigestLength; + } + mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength); offset += (mSpecialSlots + mCodeSlots) * mDigestLength; + assert(offset == total); // matches allocated size + + (void)offset; // fill special slots - memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots); + memset(mDir->getSlotMutable((int)-mSpecialSlots, false), 0, mDigestLength * mSpecialSlots); for (size_t slot = 1; slot <= mSpecialSlots; ++slot) - memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength); + memcpy(mDir->getSlotMutable((int)-slot, false), specialSlot((SpecialSlot)slot), mDigestLength); // fill code slots mExec.seek(mExecOffset); size_t remaining = mExecLength; for (unsigned int slot = 0; slot < mCodeSlots; ++slot) { - size_t thisPage = min(mPageSize, remaining); + size_t thisPage = remaining; + if (mPageSize) + thisPage = min(thisPage, mPageSize); MakeHash hasher(this); - generateHash(hasher, mExec, (*mDir)[slot], thisPage); + generateHash(hasher, mExec, mDir->getSlotMutable(slot, false), thisPage); + if (mGeneratePreEncryptHashes && mPreservedPreEncryptHashMap.empty()) { + memcpy(mDir->getSlotMutable(slot, true), mDir->getSlot(slot, false), + mDir->hashSize); + } remaining -= thisPage; } + assert(remaining == 0); + + PreEncryptHashMap::iterator preEncrypt = + mPreservedPreEncryptHashMap.find(mHashType); + if (preEncrypt != mPreservedPreEncryptHashMap.end()) { + memcpy(mDir->getSlotMutable(0, true), + CFDataGetBytePtr(preEncrypt->second), + mCodeSlots * mDigestLength); + mPreservedPreEncryptHashMap.erase(preEncrypt->first); // Releases the CFData memory. + } // all done. Pass ownership to caller return mDir;