]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_codesigning/lib/codedirectory.cpp
Security-59306.61.1.tar.gz
[apple/security.git] / OSX / libsecurity_codesigning / lib / codedirectory.cpp
index 7697e273455dad7c583899f6f1112344a241de58..4d3c1c80015884dcc02a2837b9ade80189e8310b 100644 (file)
@@ -27,6 +27,7 @@
 #include "codedirectory.h"
 #include "csutilities.h"
 #include "CSCommonPriv.h"
+#include <vector>
 
 using namespace UnixPlusPlus;
 
@@ -58,16 +59,30 @@ const char *CodeDirectory::canonicalSlotName(SpecialSlot slot)
        switch (slot) {
        case cdRequirementsSlot:
                return kSecCS_REQUIREMENTSFILE;
+       case cdAlternateCodeDirectorySlots:
+               return kSecCS_REQUIREMENTSFILE "-1";
+       case cdAlternateCodeDirectorySlots+1:
+               return kSecCS_REQUIREMENTSFILE "-2";
+       case cdAlternateCodeDirectorySlots+2:
+               return kSecCS_REQUIREMENTSFILE "-3";
+       case cdAlternateCodeDirectorySlots+3:
+               return kSecCS_REQUIREMENTSFILE "-4";
+       case cdAlternateCodeDirectorySlots+4:
+               return kSecCS_REQUIREMENTSFILE "-5";
        case cdResourceDirSlot:
                return kSecCS_RESOURCEDIRFILE;
        case cdCodeDirectorySlot:
                return kSecCS_CODEDIRECTORYFILE;
        case cdSignatureSlot:
                return kSecCS_SIGNATUREFILE;
-       case cdApplicationSlot:
-               return kSecCS_APPLICATIONFILE;
+       case cdTopDirectorySlot:
+               return kSecCS_TOPDIRECTORYFILE;
        case cdEntitlementSlot:
                return kSecCS_ENTITLEMENTFILE;
+       case cdEntitlementDERSlot:
+               return kSecCS_ENTITLEMENTDERFILE;
+       case cdRepSpecificSlot:
+               return kSecCS_REPSPECIFICFILE;
        default:
                return NULL;
        }
@@ -83,13 +98,21 @@ unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
        case cdRequirementsSlot:
                return cdComponentIsBlob; // global
        case cdCodeDirectorySlot:
-               return cdComponentPerArchitecture | cdComponentIsBlob;
+       case cdAlternateCodeDirectorySlots:
+       case cdAlternateCodeDirectorySlots+1:
+       case cdAlternateCodeDirectorySlots+2:
+       case cdAlternateCodeDirectorySlots+3:
+       case cdAlternateCodeDirectorySlots+4:
+                       return cdComponentPerArchitecture | cdComponentIsBlob;
        case cdSignatureSlot:
                return cdComponentPerArchitecture; // raw
        case cdEntitlementSlot:
+       case cdEntitlementDERSlot:
                return cdComponentIsBlob; // global
        case cdIdentificationSlot:
                return cdComponentPerArchitecture; // raw
+       case cdTicketSlot:
+               return 0; // global, raw
        default:
                return 0; // global, raw
        }
@@ -107,7 +130,7 @@ const char * const CodeDirectory::debugSlotName[] = {
        "info",
        "requirements",
        "resources",
-       "application",
+       "rep-specific",
        "entitlement"
 };
 #endif //NDEBUG
@@ -137,9 +160,11 @@ void CodeDirectory::checkIntegrity() const
        if (version < earliestVersion)
                MacOSError::throwMe(errSecCSSignatureUnsupported);      // too old - can't support
        if (version > currentVersion)
-               secdebug("codedir", "%p version 0x%x newer than current 0x%x",
+               secinfo("codedir", "%p version 0x%x newer than current 0x%x",
                        this, uint32_t(version), currentVersion);
-       
+
+       bool hasPreEncryptHashes = version >= supportsPreEncrypt && preEncryptOffset != 0;
+
        // now check interior offsets for validity
        if (!stringAt(identOffset))
                MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
@@ -147,6 +172,8 @@ void CodeDirectory::checkIntegrity() const
                        MacOSError::throwMe(errSecCSSignatureFailed); // identifier out of blob range
        if (!contains(hashOffset - int64_t(hashSize) * nSpecialSlots, hashSize * (int64_t(nSpecialSlots) + nCodeSlots)))
                MacOSError::throwMe(errSecCSSignatureFailed); // hash array out of blob range
+       if (hasPreEncryptHashes && !contains(preEncryptOffset, hashSize * (int64_t(nCodeSlots))))
+               MacOSError::throwMe(errSecCSSignatureFailed); // pre-encrypt array out of blob range
        if (const Scatter *scatter = this->scatterVector()) {
                // the optional scatter vector is terminated with an element having (count == 0)
                unsigned int pagesConsumed = 0;
@@ -157,19 +184,21 @@ void CodeDirectory::checkIntegrity() const
                                break;
                        pagesConsumed += scatter->count;
                }
-               if (!contains((*this)[pagesConsumed-1], hashSize))      // referenced too many main hash slots
+               if (!contains(getSlot(pagesConsumed-1, false), hashSize) ||
+                       (hasPreEncryptHashes && !contains(getSlot(pagesConsumed-1, true), hashSize)))   // referenced too many main hash slots
                        MacOSError::throwMe(errSecCSSignatureFailed);
        }
        
        // check consistency between the page-coverage fields
+       size_t limit = signingLimit();
        if (pageSize) {
-               if (codeLimit == 0)                                                                     // can't have paged signatures with no covered data
+               if (limit == 0)                                                                 // can't have paged signatures with no covered data
                        MacOSError::throwMe(errSecCSSignatureFailed);
-               size_t coveredPages = ((codeLimit-1) >> pageSize) + 1; // page slots required to cover codeLimit
+               size_t coveredPages = ((limit-1) >> pageSize) + 1; // page slots required to cover signingLimit
                if (coveredPages != nCodeSlots)
                        MacOSError::throwMe(errSecCSSignatureFailed);
        } else {
-               if ((codeLimit > 0) != nCodeSlots)      // must have one code slot, or none if no code
+               if ((limit > 0) != nCodeSlots)  // must have one code slot, or none if no code
                        MacOSError::throwMe(errSecCSSignatureFailed);
        }
 }
@@ -178,13 +207,13 @@ void CodeDirectory::checkIntegrity() const
 //
 // Validate a slot against data in memory.
 //
-bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
+bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot, bool preEncrypt) const
 {
-       secdebug("codedir", "%p validating slot %d", this, int(slot));
+       secinfo("codedir", "%p validating slot %d", this, int(slot));
        MakeHash<CodeDirectory> hasher(this);
-       Hashing::Byte digest[hasher->digestLength()];
-       generateHash(hasher, data, length, digest);
-       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
+       vector<Hashing::Byte> digest_vector(hasher->digestLength());
+       generateHash(hasher, data, length, digest_vector.data());
+       return memcmp(digest_vector.data(), getSlot(slot, preEncrypt), hasher->digestLength()) == 0;
 }
 
 
@@ -192,12 +221,12 @@ bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) con
 // Validate a slot against the contents of an open file. At most 'length' bytes
 // will be read from the file.
 //
-bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
+bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot, bool preEncrypt) const
 {
        MakeHash<CodeDirectory> hasher(this);
-       Hashing::Byte digest[hasher->digestLength()];
-       generateHash(hasher, fd, digest, length);
-       return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
+       vector<Hashing::Byte> digest_vector(hasher->digestLength());
+       generateHash(hasher, fd, digest_vector.data(), length);
+       return memcmp(digest_vector.data(), getSlot(slot, preEncrypt), hasher->digestLength()) == 0;
 }
 
 
@@ -209,7 +238,7 @@ bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
 bool CodeDirectory::slotIsPresent(Slot slot) const
 {
        if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
-               const Hashing::Byte *digest = (*this)[slot];
+               const Hashing::Byte *digest = getSlot(slot, false);
                for (unsigned n = 0; n < hashSize; n++)
                        if (digest[n])
                                return true;    // non-zero digest => present
@@ -229,6 +258,7 @@ DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
        switch (hashType) {
        case kSecCodeSignatureHashSHA1:                                         return new CCHashInstance(kCCDigestSHA1);
        case kSecCodeSignatureHashSHA256:                                       return new CCHashInstance(kCCDigestSHA256);
+       case kSecCodeSignatureHashSHA384:                                       return new CCHashInstance(kCCDigestSHA384);
        case kSecCodeSignatureHashSHA256Truncated:                      return new CCHashInstance(kCCDigestSHA256, SHA1::digestLength);
        default:
                MacOSError::throwMe(errSecCSSignatureUnsupported);
@@ -236,17 +266,83 @@ DynamicHash *CodeDirectory::hashFor(HashAlgorithm hashType)
 }
        
        
+//
+// Determine which of a set of possible digest types should be chosen as the "best" one
+//
+static const CodeDirectory::HashAlgorithm hashPriorities[] = {
+       kSecCodeSignatureHashSHA384,
+       kSecCodeSignatureHashSHA256,
+       kSecCodeSignatureHashSHA256Truncated,
+       kSecCodeSignatureHashSHA1,
+       kSecCodeSignatureNoHash         // sentinel
+};
+       
+bool CodeDirectory::viableHash(HashAlgorithm type)
+{
+       for (const HashAlgorithm* tp = hashPriorities; *tp != kSecCodeSignatureNoHash; tp++)
+               if (*tp == type)
+                       return true;
+       return false;
+
+}
+
+CodeDirectory::HashAlgorithm CodeDirectory::bestHashOf(const HashAlgorithms &types)
+{
+       for (const HashAlgorithm* type = hashPriorities; *type != kSecCodeSignatureNoHash; type++)
+               if (types.find(*type) != types.end())
+                       return *type;
+       MacOSError::throwMe(errSecCSUnsupportedDigestAlgorithm);
+}
+       
+
+//
+// Hash a file range with multiple digest algorithms and then pass the resulting
+// digests to a per-algorithm block.
+//
+void CodeDirectory::multipleHashFileData(FileDesc fd, size_t limit, CodeDirectory::HashAlgorithms types, void (^action)(HashAlgorithm type, DynamicHash* hasher))
+{
+       assert(!types.empty());
+       map<HashAlgorithm, RefPointer<DynamicHash> > hashes;
+       for (auto it = types.begin(); it != types.end(); ++it) {
+               if (CodeDirectory::viableHash(*it))
+                       hashes[*it] = CodeDirectory::hashFor(*it);
+       }
+       scanFileData(fd, limit, ^(const void *buffer, size_t size) {
+               for (auto it = hashes.begin(); it != hashes.end(); ++it) {
+            it->second->update(buffer, size);
+               }
+       });
+       CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
+       for (auto it = hashes.begin(); it != hashes.end(); ++it) {
+               action(it->first, it->second);
+       }
+}
+    
+    
+    //
+    // Hash data in memory using our hashAlgorithm()
+    //
+bool CodeDirectory::verifyMemoryContent(CFDataRef data, const Byte* digest) const
+{
+    RefPointer<DynamicHash> hasher = CodeDirectory::hashFor(this->hashType);
+    hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
+    return hasher->verify(digest);
+}
+       
+       
 //
 // Generate the canonical cdhash - the internal hash of the CodeDirectory itself.
-// We currently truncate to 20 bytes because that's what the kernel can deal with.
+// With 'truncate' truncates to 20 bytes, because that's what's commonly used.
 //
-CFDataRef CodeDirectory::cdhash() const
+CFDataRef CodeDirectory::cdhash(bool truncate) const
 {
        MakeHash<CodeDirectory> hash(this);
-       Hashing::Byte digest[hash->digestLength()];
+       vector<Hashing::Byte> digest_vector(hash->digestLength());
        hash->update(this, this->length());
-       hash->finish(digest);
-       return makeCFData(digest, min(hash->digestLength(), size_t(kSecCodeCDHashLength)));
+       hash->finish(digest_vector.data());
+       return makeCFData(digest_vector.data(),
+                                         truncate ? min(hash->digestLength(), size_t(kSecCodeCDHashLength)) :
+                                         hash->digestLength());
 }
 
 
@@ -296,9 +392,11 @@ std::string CodeDirectory::hexHash(const unsigned char *hash) const
 std::string CodeDirectory::screeningCode() const
 {
        if (slotIsPresent(-cdInfoSlot))         // has Info.plist
-               return "I" + hexHash((*this)[-cdInfoSlot]); // use Info.plist hash
+               return "I" + hexHash(getSlot(-cdInfoSlot, false)); // use Info.plist hash
+       if (slotIsPresent(-cdRepSpecificSlot))          // has Info.plist
+               return "R" + hexHash(getSlot(-cdRepSpecificSlot, false)); // use Info.plist hash
        if (pageSize == 0)                                      // good-enough proxy for "not a Mach-O file"
-               return "M" + hexHash((*this)[0]); // use hash of main executable
+               return "M" + hexHash(getSlot(0, false)); // use hash of main executable
        return "N";                                                     // no suitable screening code
 }
 
@@ -320,5 +418,6 @@ const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
        { "restrict",           kSecCodeSignatureRestrict,              true },
        { "enforcement",        kSecCodeSignatureEnforcement,           true },
        { "library-validation", kSecCodeSignatureLibraryValidation,             true },
+       { "runtime",    kSecCodeSignatureRuntime,               true },
        { NULL }
 };