#include "cdbuilder.h"
#include <security_utilities/memutils.h>
#include <cmath>
using namespace UnixPlusPlus;
using LowLevelMemoryUtilities::alignUp;
namespace Security {
namespace CodeSigning {
CodeDirectory::Builder::Builder(HashAlgorithm digestAlgorithm)
: mFlags(0),
mHashType(digestAlgorithm),
mPlatform(0),
mSpecialSlots(0),
mCodeSlots(0),
mScatter(NULL),
mScatterSize(0),
mDir(NULL)
{
mDigestLength = (uint32_t)MakeHash<Builder>(this)->digestLength();
mSpecial = (unsigned char *)calloc(cdSlotMax, mDigestLength);
}
CodeDirectory::Builder::~Builder()
{
::free(mSpecial);
::free(mScatter);
}
void CodeDirectory::Builder::executable(string path,
size_t pagesize, size_t offset, size_t length)
{
mExec.close(); mExec.open(path);
mPageSize = pagesize;
mExecOffset = offset;
mExecLength = length;
}
void CodeDirectory::Builder::reopen(string path, size_t offset, size_t length)
{
assert(mExec); mExec.close();
mExec.open(path);
mExecOffset = offset;
mExecLength = length;
}
void CodeDirectory::Builder::specialSlot(SpecialSlot slot, CFDataRef data)
{
assert(slot <= cdSlotMax);
MakeHash<Builder> hash(this);
hash->update(CFDataGetBytePtr(data), CFDataGetLength(data));
hash->finish(specialSlot(slot));
mFilledSpecialSlots.insert(slot);
if (slot >= mSpecialSlots)
mSpecialSlots = slot;
}
CodeDirectory::Scatter *CodeDirectory::Builder::scatter(unsigned count)
{
mScatterSize = (count + 1) * sizeof(Scatter);
if (!(mScatter = (Scatter *)::realloc(mScatter, mScatterSize)))
UnixError::throwMe(ENOMEM);
::memset(mScatter, 0, mScatterSize);
return mScatter;
}
const size_t CodeDirectory::Builder::fixedSize(const uint32_t version)
{
size_t cdSize = sizeof(CodeDirectory);
if (version < supportsCodeLimit64)
cdSize -= sizeof(mDir->spare3) + sizeof(mDir->codeLimit64);
if (version < supportsTeamID)
cdSize -= sizeof(mDir->teamIDOffset);
return cdSize;
}
size_t CodeDirectory::Builder::size(const uint32_t version)
{
assert(mExec); if (mExecLength == 0)
mExecLength = mExec.fileSize() - mExecOffset;
if (mExecLength <= 0) { mCodeSlots = 0;
} else if (mPageSize == 0) { mCodeSlots = 1;
} else { mCodeSlots = (mExecLength - 1) / mPageSize + 1;
}
size_t offset = fixedSize(version);
size_t offset0 = offset;
offset += mScatterSize; offset += mIdentifier.size() + 1; if (mTeamID.size())
offset += mTeamID.size() + 1; offset += (mCodeSlots + mSpecialSlots) * mDigestLength; if (offset <= offset0)
UnixError::throwMe(ENOEXEC);
return offset;
}
CodeDirectory *CodeDirectory::Builder::build()
{
assert(mExec); uint32_t version;
size_t identLength = mIdentifier.size() + 1;
size_t teamIDLength = mTeamID.size() + 1;
if (mExecLength > UINT32_MAX) {
version = currentVersion;
} else if (mTeamID.size()) {
version = supportsTeamID;
} else {
version = supportsScatter;
}
if (mCodeSlots > UINT32_MAX) MacOSError::throwMe(errSecCSTooBig);
size_t total = size(version);
if (!(mDir = (CodeDirectory *)calloc(1, total))) UnixError::throwMe(ENOMEM);
mDir->initialize(total);
mDir->version = version;
mDir->flags = mFlags;
mDir->nSpecialSlots = (uint32_t)mSpecialSlots;
mDir->nCodeSlots = (uint32_t)mCodeSlots;
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;
if (mPageSize) {
int pglog;
assert(frexp(mPageSize, &pglog) == 0.5); frexp(mPageSize, &pglog);
assert(pglog < 256);
mDir->pageSize = pglog - 1;
} else
mDir->pageSize = 0;
size_t offset = fixedSize(mDir->version);
if (mScatter) {
mDir->scatterOffset = (uint32_t)offset;
memcpy(mDir->scatterVector(), mScatter, mScatterSize);
offset += mScatterSize;
}
mDir->identOffset = (uint32_t)offset;
memcpy(mDir->identifier(), mIdentifier.c_str(), identLength);
offset += identLength;
if (mTeamID.size()) {
mDir->teamIDOffset = (uint32_t)offset;
memcpy(mDir->teamID(), mTeamID.c_str(), teamIDLength);
offset += teamIDLength;
}
mDir->hashOffset = (uint32_t)(offset + mSpecialSlots * mDigestLength);
offset += (mSpecialSlots + mCodeSlots) * mDigestLength;
assert(offset == total);
memset((*mDir)[(int)-mSpecialSlots], 0, mDigestLength * mSpecialSlots);
for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
memcpy((*mDir)[(int)-slot], specialSlot((SpecialSlot)slot), mDigestLength);
mExec.seek(mExecOffset);
size_t remaining = mExecLength;
for (unsigned int slot = 0; slot < mCodeSlots; ++slot) {
size_t thisPage = remaining;
if (mPageSize)
thisPage = min(thisPage, mPageSize);
MakeHash<Builder> hasher(this);
generateHash(hasher, mExec, (*mDir)[slot], thisPage);
remaining -= thisPage;
}
assert(remaining == 0);
return mDir;
}
} }