]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/cdbuilder.cpp
Security-59306.120.7.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / cdbuilder.cpp
index 719a01b3364cdd72f1924266f7440195c8342119..7fefc1c5e190a2363215dbb455340450a7bec8db 100644 (file)
@@ -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<Builder>(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<Builder> 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 <rdar://problem/16102695>, 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<Builder> 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;