#include "signer.h"
#include "resources.h"
#include "signerutils.h"
#include "SecCodeSigner.h"
#include <Security/SecIdentity.h>
#include <Security/CMSEncoder.h>
#include <Security/CMSPrivate.h>
#include <CoreFoundation/CFBundlePriv.h>
#include "renum.h"
#include <security_utilities/unix++.h>
#include <security_utilities/unixchild.h>
#include <security_codesigning/cfmunge.h>
namespace Security {
namespace CodeSigning {
void SecCodeSigner::Signer::sign(SecCSFlags flags)
{
rep = code->diskRep()->base();
CFRef<CFDictionaryRef> infoDict;
if (CFRef<CFDataRef> infoData = rep->component(cdInfoSlot))
infoDict.take(makeCFDictionaryFrom(infoData));
identifier = state.mIdentifier;
if (identifier.empty()) {
identifier = rep->recommendedIdentifier();
if (identifier.find('.') == string::npos && !state.mIdentifierPrefix.empty())
identifier = state.mIdentifierPrefix + identifier;
secdebug("signer", "using default identifier=%s", identifier.c_str());
} else
secdebug("signer", "using explicit identifier=%s", identifier.c_str());
if (state.mCdFlagsGiven) {
cdFlags = state.mCdFlags;
secdebug("signer", "using explicit cdFlags=0x%x", cdFlags);
} else {
cdFlags = 0;
if (infoDict)
if (CFTypeRef csflags = CFDictionaryGetValue(infoDict, CFSTR("CSFlags")))
if (CFGetTypeID(csflags) == CFNumberGetTypeID()) {
cdFlags = cfNumber<uint32_t>(CFNumberRef(csflags));
secdebug("signer", "using numeric cdFlags=0x%x from Info.dict", cdFlags);
} else if (CFGetTypeID(csflags) == CFStringGetTypeID()) {
cdFlags = CodeDirectory::textFlags(cfString(CFStringRef(csflags)));
secdebug("signer", "using text cdFlags=0x%x from Info.dict", cdFlags);
} else
MacOSError::throwMe(errSecCSBadDictionaryFormat);
}
if (state.mSigner == SecIdentityRef(kCFNull)) cdFlags |= kSecCodeSignatureAdhoc;
string rpath = rep->resourcesRootPath();
if (!rpath.empty()) {
CFCopyRef<CFDictionaryRef> resourceRules(state.mResourceRules);
if (!resourceRules)
if (CFTypeRef spec = CFDictionaryGetValue(infoDict, _kCFBundleResourceSpecificationKey))
if (CFGetTypeID(spec) == CFStringGetTypeID())
if (CFRef<CFDataRef> data = cfLoadFile(rpath + "/" + cfString(CFStringRef(spec))))
if (CFRef<CFDictionaryRef> dict = makeCFDictionaryFrom(data))
resourceRules = dict;
if (!resourceRules)
resourceRules.take(rep->defaultResourceRules());
ResourceBuilder resources(rpath, cfget<CFDictionaryRef>(resourceRules, "rules"));
rep->adjustResources(resources);
CFRef<CFDictionaryRef> rdir = resources.build();
resourceDirectory.take(CFPropertyListCreateXMLData(NULL, rdir));
}
CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
if (state.mSigningTime == CFDateRef(kCFNull)) {
signingTime = 0; } else if (!state.mSigningTime) {
signingTime = now; } else {
CFAbsoluteTime time = CFDateGetAbsoluteTime(state.mSigningTime);
if (time > now) MacOSError::throwMe(errSecCSBadDictionaryFormat);
signingTime = time;
}
pagesize = state.mPageSize ? cfNumber<size_t>(state.mPageSize) : rep->pageSize();
if (Universal *fat = state.mNoMachO ? NULL : rep->mainExecutableImage()) {
signMachO(fat);
} else {
signArchitectureAgnostic();
}
}
void SecCodeSigner::Signer::signMachO(Universal *fat)
{
auto_ptr<ArchEditor> editor(state.mDetached
? static_cast<ArchEditor *>(new BlobEditor(*fat, *this))
: new MachOEditor(rep->writer(), *fat, rep->mainExecutablePath()));
assert(editor->count() > 0);
if (!editor->attribute(writerNoGlobal)) populate(*editor);
for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
MachOEditor::Arch &arch = *it->second;
arch.source.reset(fat->architecture(it->first));
arch.ireqs(state.mRequirements, rep->defaultRequirements(&arch.architecture));
if (editor->attribute(writerNoGlobal)) populate(arch);
populate(arch.cdbuilder, arch, arch.ireqs,
arch.source->offset(), arch.source->signingExtent());
size_t cdSize = arch.cdbuilder.size();
arch.blobSize = arch.size(cdSize, state.mCMSSize, 0);
}
editor->allocate();
for (MachOEditor::Iterator it = editor->begin(); it != editor->end(); ++it) {
MachOEditor::Arch &arch = *it->second;
editor->reset(arch);
CodeDirectory *cd = arch.cdbuilder.build();
CFRef<CFDataRef> signature = signCodeDirectory(cd);
arch.add(cdCodeDirectorySlot, cd); arch.add(cdSignatureSlot, BlobWrapper::alloc(
CFDataGetBytePtr(signature), CFDataGetLength(signature)));
if (!state.mDryRun) {
EmbeddedSignatureBlob *blob = arch.make();
editor->write(arch, blob); }
}
if (!state.mDryRun)
editor->commit();
}
void SecCodeSigner::Signer::signArchitectureAgnostic()
{
RefPointer<DiskRep::Writer> writer = state.mDetached ?
(new DetachedBlobWriter(*this)) : rep->writer();
CodeDirectory::Builder builder;
InternalRequirements ireqs;
ireqs(state.mRequirements, rep->defaultRequirements(NULL));
populate(*writer);
populate(builder, *writer, ireqs, rep->signingBase(), rep->signingLimit());
CodeDirectory *cd = builder.build();
CFRef<CFDataRef> signature = signCodeDirectory(cd);
if (!state.mDryRun) {
writer->codeDirectory(cd);
writer->signature(signature);
writer->flush();
}
::free(cd);
}
void SecCodeSigner::Signer::populate(DiskRep::Writer &writer)
{
if (resourceDirectory)
writer.component(cdResourceDirSlot, resourceDirectory);
}
void SecCodeSigner::Signer::populate(CodeDirectory::Builder &builder, DiskRep::Writer &writer,
InternalRequirements &ireqs, size_t offset , size_t length )
{
builder.executable(rep->mainExecutablePath(), pagesize, offset, length);
builder.flags(cdFlags);
builder.identifier(identifier);
for (CodeDirectory::Slot slot = cdSlotMax; slot >= 1; --slot)
switch (slot) {
case cdRequirementsSlot:
if (ireqs) {
CFRef<CFDataRef> data = makeCFData(*ireqs);
writer.component(cdRequirementsSlot, data);
builder.special(slot, data);
}
break;
case cdResourceDirSlot:
if (resourceDirectory)
builder.special(slot, resourceDirectory);
break;
case cdApplicationSlot:
#if NOT_YET
if (state.mApplicationData)
builder.special(slot, state.mApplicationData);
#endif
break;
case cdEntitlementSlot:
if (state.mEntitlementData) {
writer.component(cdEntitlementSlot, state.mEntitlementData);
builder.special(slot, state.mEntitlementData);
}
break;
default:
if (CFDataRef data = rep->component(slot))
builder.special(slot, data);
break;
}
}
CFDataRef SecCodeSigner::Signer::signCodeDirectory(const CodeDirectory *cd)
{
assert(state.mSigner);
if (state.mSigner == SecIdentityRef(kCFNull))
return CFDataCreate(NULL, NULL, 0);
CFRef<CMSEncoderRef> cms;
MacOSError::check(CMSEncoderCreate(&cms.aref()));
MacOSError::check(CMSEncoderSetCertificateChainMode(cms, kCMSCertificateChainWithRoot));
CMSEncoderAddSigners(cms, state.mSigner);
MacOSError::check(CMSEncoderSetHasDetachedContent(cms, true));
if (signingTime) {
MacOSError::check(CMSEncoderAddSignedAttributes(cms, kCMSAttrSigningTime));
MacOSError::check(CMSEncoderSetSigningTime(cms, signingTime));
}
MacOSError::check(CMSEncoderUpdateContent(cms, cd, cd->length()));
CFDataRef signature;
MacOSError::check(CMSEncoderCopyEncodedContent(cms, &signature));
return signature;
}
} }