#include "codedirectory.h"
#include "csutilities.h"
#include "CSCommonPriv.h"
#include <vector>
using namespace UnixPlusPlus;
namespace Security {
namespace CodeSigning {
CodeDirectory::SpecialSlot CodeDirectory::maxSpecialSlot() const
{
SpecialSlot slot = this->nSpecialSlots;
if (slot > cdSlotMax)
slot = cdSlotMax;
return slot;
}
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 cdTopDirectorySlot:
return kSecCS_TOPDIRECTORYFILE;
case cdEntitlementSlot:
return kSecCS_ENTITLEMENTFILE;
case cdRepSpecificSlot:
return kSecCS_REPSPECIFICFILE;
default:
return NULL;
}
}
unsigned CodeDirectory::slotAttributes(SpecialSlot slot)
{
switch (slot) {
case cdRequirementsSlot:
return cdComponentIsBlob; case cdCodeDirectorySlot:
case cdAlternateCodeDirectorySlots:
case cdAlternateCodeDirectorySlots+1:
case cdAlternateCodeDirectorySlots+2:
case cdAlternateCodeDirectorySlots+3:
case cdAlternateCodeDirectorySlots+4:
return cdComponentPerArchitecture | cdComponentIsBlob;
case cdSignatureSlot:
return cdComponentPerArchitecture; case cdEntitlementSlot:
return cdComponentIsBlob; case cdIdentificationSlot:
return cdComponentPerArchitecture; default:
return 0; }
}
#if !defined(NDEBUG)
const char * const CodeDirectory::debugSlotName[] = {
"codedirectory",
"info",
"requirements",
"resources",
"rep-specific",
"entitlement"
};
#endif //NDEBUG
void CodeDirectory::checkIntegrity() const
{
if (!this->validateBlob())
MacOSError::throwMe(errSecCSSignatureInvalid); if (version > compatibilityLimit)
MacOSError::throwMe(errSecCSSignatureUnsupported); if (version < earliestVersion)
MacOSError::throwMe(errSecCSSignatureUnsupported); if (version > currentVersion)
secinfo("codedir", "%p version 0x%x newer than current 0x%x",
this, uint32_t(version), currentVersion);
if (!stringAt(identOffset))
MacOSError::throwMe(errSecCSSignatureFailed); if (version >= supportsTeamID && teamIDOffset != 0 && !stringAt(teamIDOffset))
MacOSError::throwMe(errSecCSSignatureFailed); if (!contains(hashOffset - int64_t(hashSize) * nSpecialSlots, hashSize * (int64_t(nSpecialSlots) + nCodeSlots)))
MacOSError::throwMe(errSecCSSignatureFailed); if (const Scatter *scatter = this->scatterVector()) {
unsigned int pagesConsumed = 0;
for (;; scatter++) {
if (!contains(scatter, sizeof(Scatter)))
MacOSError::throwMe(errSecCSSignatureFailed);
if (scatter->count == 0)
break;
pagesConsumed += scatter->count;
}
if (!contains((*this)[pagesConsumed-1], hashSize)) MacOSError::throwMe(errSecCSSignatureFailed);
}
size_t limit = signingLimit();
if (pageSize) {
if (limit == 0) MacOSError::throwMe(errSecCSSignatureFailed);
size_t coveredPages = ((limit-1) >> pageSize) + 1; if (coveredPages != nCodeSlots)
MacOSError::throwMe(errSecCSSignatureFailed);
} else {
if ((limit > 0) != nCodeSlots) MacOSError::throwMe(errSecCSSignatureFailed);
}
}
bool CodeDirectory::validateSlot(const void *data, size_t length, Slot slot) const
{
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;
}
bool CodeDirectory::validateSlot(FileDesc fd, size_t length, Slot slot) const
{
MakeHash<CodeDirectory> hasher(this);
Hashing::Byte digest[hasher->digestLength()];
generateHash(hasher, fd, digest, length);
return memcmp(digest, (*this)[slot], hasher->digestLength()) == 0;
}
bool CodeDirectory::slotIsPresent(Slot slot) const
{
if (slot >= -Slot(nSpecialSlots) && slot < Slot(nCodeSlots)) {
const Hashing::Byte *digest = (*this)[slot];
for (unsigned n = 0; n < hashSize; n++)
if (digest[n])
return true; }
return false; }
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);
}
}
static const CodeDirectory::HashAlgorithm hashPriorities[] = {
kSecCodeSignatureHashSHA384,
kSecCodeSignatureHashSHA256,
kSecCodeSignatureHashSHA256Truncated,
kSecCodeSignatureHashSHA1,
kSecCodeSignatureNoHash };
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);
}
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);
}
}
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);
}
CFDataRef CodeDirectory::cdhash() const
{
MakeHash<CodeDirectory> hash(this);
Hashing::Byte digest[hash->digestLength()];
hash->update(this, this->length());
hash->finish(digest);
return makeCFData(digest, min(hash->digestLength(), size_t(kSecCodeCDHashLength)));
}
size_t CodeDirectory::generateHash(DynamicHash *hasher, FileDesc fd, Hashing::Byte *digest, size_t limit)
{
size_t size = hashFileData(fd, hasher, limit);
hasher->finish(digest);
return size;
}
size_t CodeDirectory::generateHash(DynamicHash *hasher, const void *data, size_t length, Hashing::Byte *digest)
{
hasher->update(data, length);
hasher->finish(digest);
return length;
}
std::string CodeDirectory::hexHash(const unsigned char *hash) const
{
size_t size = this->hashSize;
char result[2*size+1];
for (unsigned n = 0; n < size; n++)
sprintf(result+2*n, "%02.2x", hash[n]);
return result;
}
std::string CodeDirectory::screeningCode() const
{
if (slotIsPresent(-cdInfoSlot)) return "I" + hexHash((*this)[-cdInfoSlot]); if (slotIsPresent(-cdRepSpecificSlot)) return "R" + hexHash((*this)[-cdRepSpecificSlot]); if (pageSize == 0) return "M" + hexHash((*this)[0]); return "N"; }
} }
const SecCodeDirectoryFlagTable kSecCodeDirectoryFlagTable[] = {
{ "host", kSecCodeSignatureHost, true },
{ "adhoc", kSecCodeSignatureAdhoc, false },
{ "hard", kSecCodeSignatureForceHard, true },
{ "kill", kSecCodeSignatureForceKill, true },
{ "expires", kSecCodeSignatureForceExpiration, true },
{ "restrict", kSecCodeSignatureRestrict, true },
{ "enforcement", kSecCodeSignatureEnforcement, true },
{ "library-validation", kSecCodeSignatureLibraryValidation, true },
{ NULL }
};