#include "signerutils.h"
#include "signer.h"
#include "SecCodeSigner.h"
#include <Security/SecIdentity.h>
#include <Security/CMSEncoder.h>
#include "renum.h"
#include <security_utilities/unix++.h>
#include <security_utilities/unixchild.h>
#include <vector>
#include "Code.h"
#include "cfmunge.h"
#include <sys/codesign.h>
namespace Security {
namespace CodeSigning {
static const char helperName[] = "codesign_allocate";
static const char helperPath[] = "/usr/bin/codesign_allocate";
static const char helperOverride[] = "CODESIGN_ALLOCATE";
static const size_t csAlign = 16;
void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted)
{
if (defaulted) {
this->add(defaulted);
::free((void *)defaulted); }
if (given)
this->add(given);
mReqs = make();
}
void BlobWriter::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
{
return EmbeddedSignatureBlob::Maker::component(slot, data);
}
void DetachedBlobWriter::flush()
{
EmbeddedSignatureBlob *blob = this->make();
signer.code->detachedSignature(CFTempData(*blob));
signer.state.returnDetachedSignature(blob);
::free(blob);
}
ArchEditor::ArchEditor(Universal &code, uint32_t attrs )
: DiskRep::Writer(attrs)
{
Universal::Architectures archList;
code.architectures(archList);
for (Universal::Architectures::const_iterator it = archList.begin();
it != archList.end(); ++it)
architecture[*it] = new Arch(*it);
}
ArchEditor::~ArchEditor()
{
for (ArchMap::iterator it = begin(); it != end(); ++it)
delete it->second;
}
void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
{
mGlobal.component(slot, data);
}
void BlobEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
{
mMaker.add(arch.architecture.cpuType(), blob);
}
void BlobEditor::commit()
{
mMaker.add(0, mGlobal.make());
DetachedSignatureBlob *blob = mMaker.make();
signer.state.returnDetachedSignature(blob);
::free(blob);
}
MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, std::string srcPath)
: ArchEditor(code, w->attributes()), writer(w), sourcePath(srcPath), tempPath(srcPath + ".cstemp"),
mNewCode(NULL), mTempMayExist(false)
{
if (const char *path = getenv(helperOverride)) {
mHelperPath = path;
mHelperOverridden = true;
} else {
mHelperPath = helperPath;
mHelperOverridden = false;
}
}
MachOEditor::~MachOEditor()
{
delete mNewCode;
if (mTempMayExist)
::remove(tempPath.c_str());
if (state() == alive) {
this->kill(SIGTERM); checkChildren(); if (state() == alive) {
usleep(500000); if (state() == alive) { checkChildren(); if (state() == alive) { this->kill(SIGKILL); checkChildren();
if (state() == alive) abandon(); }
}
}
}
}
void MachOEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
{
writer->component(slot, data);
}
void MachOEditor::allocate()
{
mTempMayExist = true;
fork();
wait();
if (!Child::succeeded())
UnixError::throwMe(ENOEXEC);
{
UidGuard guard(0);
mFd.open(tempPath, O_RDWR);
}
mNewCode = new Universal(mFd);
}
static const unsigned char appleReq[] = { 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
};
void MachOEditor::parentAction()
{
if (mHelperOverridden) {
secdebug("machoedit", "validating alternate codesign_allocate at %s (pid=%d)", mHelperPath, this->pid());
SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
code->validateDirectory();
code->validateExecutable();
code->validateResources();
code->validateRequirements((const Requirement *)appleReq, errSecCSReqFailed);
}
}
void MachOEditor::childAction()
{
vector<const char *> arguments;
arguments.push_back(helperName);
arguments.push_back("-i");
arguments.push_back(sourcePath.c_str());
arguments.push_back("-o");
arguments.push_back(tempPath.c_str());
for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
char *size; asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign));
secdebug("machoedit", "preparing %s size=%s", it->first.name(), size);
if (const char *arch = it->first.name()) {
arguments.push_back("-a");
arguments.push_back(arch);
} else {
arguments.push_back("-A");
char *anum;
asprintf(&anum, "%d", it->first.cpuType());
arguments.push_back(anum);
asprintf(&anum, "%d", it->first.cpuSubtype());
arguments.push_back(anum);
}
arguments.push_back(size);
}
arguments.push_back(NULL);
if (mHelperOverridden)
::csops(0, CS_EXEC_SET_KILL, NULL, 0); ::seteuid(0); execv(mHelperPath, (char * const *)&arguments[0]);
}
void MachOEditor::reset(Arch &arch)
{
arch.source.reset(mNewCode->architecture(arch.architecture));
arch.cdbuilder.reopen(tempPath,
arch.source->offset(), arch.source->signingOffset());
}
void MachOEditor::write(Arch &arch, EmbeddedSignatureBlob *blob)
{
if (size_t offset = arch.source->signingOffset()) {
size_t signingLength = arch.source->signingLength();
secdebug("codesign", "writing architecture %s at 0x%zx (%zd of %zd)",
arch.architecture.name(), offset, blob->length(), signingLength);
if (signingLength < blob->length())
MacOSError::throwMe(errSecCSCMSTooLarge);
arch.source->seek(offset);
arch.source->writeAll(*blob);
::free(blob); } else {
secdebug("signer", "%p cannot find CODESIGNING section", this);
MacOSError::throwMe(errSecCSInternalError);
}
}
void MachOEditor::commit()
{
struct stat st;
UnixError::check(::stat(sourcePath.c_str(), &st));
Copyfile copy;
int fd = mFd;
copy.set(COPYFILE_STATE_DST_FD, &fd);
{
UidGuard guard;
if (!guard.seteuid(0))
guard.seteuid(st.st_uid);
copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
mTempMayExist = false; }
}
Copyfile::Copyfile()
{
if (!(mState = copyfile_state_alloc()))
UnixError::throwMe();
}
void Copyfile::set(uint32_t flag, const void *value)
{
check(::copyfile_state_set(mState, flag, value));
}
void Copyfile::get(uint32_t flag, void *value)
{
check(::copyfile_state_set(mState, flag, value));
}
void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
{
check(::copyfile(src, dst, mState, flags));
}
void Copyfile::check(int rc)
{
if (rc < 0)
UnixError::throwMe();
}
} }