#include "cdbuilder.h"
#include <security_utilities/memutils.h>
#include <cmath>
using namespace UnixPlusPlus;
using LowLevelMemoryUtilities::alignUp;
namespace Security {
namespace CodeSigning {
CodeDirectory::Builder::Builder()
: mFlags(0), mSpecialSlots(0), mCodeSlots(0), mScatter(NULL), mScatterSize(0), mDir(NULL)
{
memset(mSpecial, 0, sizeof(mSpecial));
}
CodeDirectory::Builder::~Builder()
{
::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::special(size_t slot, CFDataRef data)
{
assert(slot <= cdSlotMax);
Hash hash;
hash(CFDataGetBytePtr(data), CFDataGetLength(data));
hash.finish(mSpecial[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;
}
size_t CodeDirectory::Builder::size()
{
assert(mExec); if (mExecLength == 0)
mExecLength = mExec.fileSize() - mExecOffset;
if (mPageSize == 0) { mCodeSlots = (mExecLength > 0);
} else { mCodeSlots = (mExecLength + mPageSize - 1) / mPageSize; }
size_t offset = sizeof(CodeDirectory);
offset += mScatterSize; offset += mIdentifier.size() + 1; offset += (mCodeSlots + mSpecialSlots) * Hash::digestLength; return offset;
}
CodeDirectory *CodeDirectory::Builder::build()
{
assert(mExec);
size_t identLength = mIdentifier.size() + 1;
size_t total = size();
if (!(mDir = (CodeDirectory *)calloc(1, total))) UnixError::throwMe(ENOMEM);
mDir->initialize(total);
mDir->version = currentVersion;
mDir->flags = mFlags;
mDir->nSpecialSlots = mSpecialSlots;
mDir->nCodeSlots = mCodeSlots;
mDir->codeLimit = mExecLength;
mDir->hashSize = Hash::digestLength;
mDir->hashType = cdHashTypeDefault;
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 = sizeof(CodeDirectory);
if (mScatter) {
mDir->scatterOffset = offset;
memcpy(mDir->scatterVector(), mScatter, mScatterSize);
offset += mScatterSize;
}
mDir->identOffset = offset;
memcpy(mDir->identifier(), mIdentifier.c_str(), identLength);
offset += identLength;
mDir->hashOffset = offset + mSpecialSlots * Hash::digestLength;
offset += (mSpecialSlots + mCodeSlots) * Hash::digestLength;
assert(offset == total);
memset((*mDir)[-mSpecialSlots], 0, mDir->hashSize * mSpecialSlots);
for (size_t slot = 1; slot <= mSpecialSlots; ++slot)
memcpy((*mDir)[-slot], &mSpecial[slot], Hash::digestLength);
mExec.seek(mExecOffset);
size_t remaining = mExecLength;
for (unsigned int slot = 0; slot < mCodeSlots; ++slot) {
size_t thisPage = min(mPageSize, remaining);
hash(mExec, (*mDir)[slot], thisPage);
remaining -= thisPage;
}
return mDir;
}
} }