return kSecCS_TOPDIRECTORYFILE;
case cdEntitlementSlot:
return kSecCS_ENTITLEMENTFILE;
+ case cdEntitlementDERSlot:
+ return kSecCS_ENTITLEMENTDERFILE;
case cdRepSpecificSlot:
return kSecCS_REPSPECIFICFILE;
default:
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
}
"info",
"requirements",
"resources",
- "application",
+ "rep-specific",
"entitlement"
};
#endif //NDEBUG
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
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;
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);
}
//
// 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;
}
// 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;
}
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
void CodeDirectory::multipleHashFileData(FileDesc fd, size_t limit, CodeDirectory::HashAlgorithms types, void (^action)(HashAlgorithm type, DynamicHash* hasher))
{
assert(!types.empty());
- vector<RefPointer<DynamicHash> > hashers;
+ map<HashAlgorithm, RefPointer<DynamicHash> > hashes;
for (auto it = types.begin(); it != types.end(); ++it) {
if (CodeDirectory::viableHash(*it))
- hashers.push_back(CodeDirectory::hashFor(*it));
+ hashes[*it] = CodeDirectory::hashFor(*it);
}
scanFileData(fd, limit, ^(const void *buffer, size_t size) {
- unsigned n = 0;
- for (auto it = types.begin(); it != types.end(); ++it, ++n) {
- hashers[n]->update(buffer, size);
+ for (auto it = hashes.begin(); it != hashes.end(); ++it) {
+ it->second->update(buffer, size);
}
});
CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
- unsigned n = 0;
- for (auto it = types.begin(); it != types.end(); ++it, ++n) {
- action(*it, hashers[n]);
+ 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());
}
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((*this)[-cdRepSpecificSlot]); // use Info.plist hash
+ 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
}
{ "restrict", kSecCodeSignatureRestrict, true },
{ "enforcement", kSecCodeSignatureEnforcement, true },
{ "library-validation", kSecCodeSignatureLibraryValidation, true },
+ { "runtime", kSecCodeSignatureRuntime, true },
{ NULL }
};