#include "diskimagerep.h"
#include "notarization.h"
#include "sigblob.h"
#include "CodeSigner.h"
#include <security_utilities/endian.h>
#include <algorithm>
namespace Security {
namespace CodeSigning {
using Security::n2h;
using Security::h2n;
using namespace UnixPlusPlus;
static const int32_t udifVersion = 4;
bool DiskImageRep::readHeader(FileDesc& fd, UDIFFileHeader& header)
{
static const size_t headerLength = sizeof(header);
size_t length = fd.fileSize();
if (length < sizeof(UDIFFileHeader) + sizeof(BlobCore))
return false;
size_t headerOffset = length - sizeof(UDIFFileHeader);
if (fd.read(&header, headerLength, headerOffset) != headerLength)
return false;
if (n2h(header.fUDIFSignature) != kUDIFSignature)
return false;
if (n2h(header.fUDIFVersion) != udifVersion) return false;
return true;
}
DiskImageRep::DiskImageRep(const char *path)
: SingleDiskRep(path)
{
this->setup();
}
void DiskImageRep::setup()
{
mSigningData = NULL;
if (!readHeader(fd(), this->mHeader))
UnixError::throwMe(errSecCSBadDiskImageFormat);
mHeaderOffset = fd().fileSize() - sizeof(UDIFFileHeader);
size_t signatureOffset = size_t(n2h(this->mHeader.fUDIFCodeSignOffset));
size_t signatureLength = size_t(n2h(this->mHeader.fUDIFCodeSignLength));
this->mHeader.fUDIFCodeSignLength = 0; if (signatureOffset == 0) {
mEndOfDataOffset = mHeaderOffset;
mHeader.fUDIFCodeSignOffset = h2n(mHeaderOffset);
return; } else {
mEndOfDataOffset = signatureOffset;
}
const size_t frameLength = mHeaderOffset - signatureOffset; if (EmbeddedSignatureBlob* blob = EmbeddedSignatureBlob::readBlob(fd(), signatureOffset, frameLength)) {
if (blob->length() != frameLength
|| frameLength != signatureLength
|| !blob->strictValidateBlob(frameLength)) {
free(blob);
MacOSError::throwMe(errSecCSBadDiskImageFormat);
}
mSigningData = blob;
}
}
CFDataRef DiskImageRep::identification()
{
SHA1 hash; hash(&mHeader, sizeof(mHeader));
SHA1::Digest digest;
hash.finish(digest);
return makeCFData(digest, sizeof(digest));
}
bool DiskImageRep::candidate(FileDesc &fd)
{
UDIFFileHeader header;
return readHeader(fd, header) == true;
}
size_t DiskImageRep::signingLimit()
{
return mEndOfDataOffset;
}
void DiskImageRep::strictValidate(const CodeDirectory* cd, const ToleratedErrors& tolerated, SecCSFlags flags)
{
DiskRep::strictValidate(cd, tolerated, flags);
if (cd) {
size_t cd_limit = cd->signingLimit();
size_t dr_limit = signingLimit();
if (cd_limit != dr_limit && cd_limit != fd().fileSize()) MacOSError::throwMe(errSecCSSignatureInvalid);
}
}
CFDataRef DiskImageRep::component(CodeDirectory::SpecialSlot slot)
{
switch (slot) {
case cdRepSpecificSlot:
return makeCFData(&mHeader, sizeof(mHeader));
default:
return mSigningData ? mSigningData->component(slot) : NULL;
}
}
string DiskImageRep::format()
{
return "disk image";
}
void DiskImageRep::prepareForSigning(SigningContext& context)
{
if (context.digestAlgorithms().empty())
context.setDigestAlgorithm(kSecCodeSignatureHashSHA256);
}
DiskRep::Writer *DiskImageRep::writer()
{
return new Writer(this);
}
void DiskImageRep::Writer::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
{
assert(slot != cdRepSpecificSlot);
EmbeddedSignatureBlob::Maker::component(slot, data);
}
void DiskImageRep::Writer::flush()
{
delete mSigningData; mSigningData = Maker::make();
size_t location = rep->mEndOfDataOffset;
assert(location);
fd().seek(location);
fd().writeAll(*mSigningData);
UDIFFileHeader fullHeader = rep->mHeader;
fullHeader.fUDIFCodeSignOffset = h2n(location);
fullHeader.fUDIFCodeSignLength = h2n(mSigningData->length());
fd().writeAll(&fullHeader, sizeof(rep->mHeader));
fd().truncate(fd().position());
}
void DiskImageRep::Writer::addDiscretionary(CodeDirectory::Builder &builder)
{
}
void DiskImageRep::registerStapledTicket()
{
CFRef<CFDataRef> data = NULL;
if (mSigningData) {
data.take(mSigningData->component(cdTicketSlot));
registerStapledTicketInDMG(data);
}
}
} }