/*
- * Copyright (c) 2006-2007 Apple Inc. All Rights Reserved.
+ * Copyright (c) 2006-2010 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
#include <Security/SecIdentity.h>
#include <Security/CMSEncoder.h>
#include "renum.h"
+#include "csutilities.h"
+#include "drmaker.h"
#include <security_utilities/unix++.h>
#include <security_utilities/unixchild.h>
#include <vector>
+// for helper validation
+#include "Code.h"
+#include <security_utilities/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;
-//
-// InternalRequirements
-//
-void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted)
-{
- if (defaulted) {
- this->add(defaulted);
- ::free((void *)defaulted); // was malloc(3)ed by DiskRep
- }
- if (given)
- this->add(given);
- mReqs = make();
-}
-
-
//
// BlobWriters
//
void DetachedBlobWriter::flush()
{
EmbeddedSignatureBlob *blob = this->make();
- signer.code->detachedSignature(CFTempData(*blob));
- signer.state.returnDetachedSignature(blob);
+ signer.code->detachedSignature(makeCFData(*blob));
+ signer.state.returnDetachedSignature(blob, signer);
::free(blob);
}
//
// ArchEditor
//
-ArchEditor::ArchEditor(Universal &code, uint32_t attrs /* = 0 */)
+ArchEditor::ArchEditor(Universal &code, CodeDirectory::HashAlgorithm hashType, 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);
+ architecture[*it] = new Arch(*it, hashType);
}
//
// BlobEditor
//
+BlobEditor::BlobEditor(Universal &fat, SecCodeSigner::Signer &s)
+ : ArchEditor(fat, s.digestAlgorithm(), 0), signer(s)
+{ }
+
+
void BlobEditor::component(CodeDirectory::SpecialSlot slot, CFDataRef data)
{
mGlobal.component(slot, data);
// finish up the superblob and deliver it
DetachedSignatureBlob *blob = mMaker.make();
- signer.state.returnDetachedSignature(blob);
+ signer.state.returnDetachedSignature(blob, signer);
::free(blob);
}
// "drill up" the Mach-O binary for insertion of Code Signing signature data.
// After the tool succeeds, we open the new file and are ready to write it.
//
-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)
+MachOEditor::MachOEditor(DiskRep::Writer *w, Universal &code, CodeDirectory::HashAlgorithm hashType, std::string srcPath)
+ : ArchEditor(code, hashType, 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()); // ignore error (can't do anything about it)
+ this->kill();
}
mNewCode = new Universal(mFd);
}
+static const unsigned char appleReq[] = {
+ // anchor apple and info["Application-Group"] = "com.apple.tool.codesign_allocate"
+ 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x11, 0x41, 0x70, 0x70, 0x6c,
+ 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2d, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x70, 0x70, 0x6c,
+ 0x65, 0x2e, 0x74, 0x6f, 0x6f, 0x6c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x73, 0x69, 0x67, 0x6e, 0x5f,
+ 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65,
+};
+
+void MachOEditor::parentAction()
+{
+ if (mHelperOverridden) {
+ CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
+ // check code identity of an overridden allocation helper
+ SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
+ code->validateDirectory();
+ code->validateExecutable();
+ code->validateResources();
+ code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
+ }
+}
+
void MachOEditor::childAction()
{
vector<const char *> arguments;
arguments.push_back(tempPath.c_str());
for (Iterator it = architecture.begin(); it != architecture.end(); ++it) {
- char *size; // we'll leak this (execv is coming soon)
- asprintf(&size, "%d", LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign));
- secdebug("machoedit", "preparing %s size=%s", it->first.name(), size);
- arguments.push_back("-a");
- arguments.push_back(it->first.name());
- arguments.push_back(size);
+ size_t size = LowLevelMemoryUtilities::alignUp(it->second->blobSize, csAlign);
+ char *ssize; // we'll leak this (execv is coming soon)
+ asprintf(&ssize, "%zd", size);
+
+ if (const char *arch = it->first.name()) {
+ CODESIGN_ALLOCATE_ARCH((char*)arch, size);
+ arguments.push_back("-a");
+ arguments.push_back(arch);
+ } else {
+ CODESIGN_ALLOCATE_ARCHN(it->first.cpuType(), it->first.cpuSubtype(), size);
+ 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(ssize);
}
arguments.push_back(NULL);
+
+ if (mHelperOverridden)
+ ::csops(0, CS_EXEC_SET_KILL, NULL, 0); // force code integrity
::seteuid(0); // activate privilege if caller has it; ignore error if not
- execv(helperPath, (char * const *)&arguments[0]);
+ execv(mHelperPath, (char * const *)&arguments[0]);
}
void MachOEditor::reset(Arch &arch)
{
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()) {
- secdebug("codesign", "trying to write %zd bytes into %zd area",
- blob->length(), signingLength);
- MacOSError::throwMe(errSecCSInternalError);
- }
+ CODESIGN_ALLOCATE_WRITE((char*)arch.architecture.name(), offset, blob->length(), signingLength);
+ if (signingLength < blob->length())
+ MacOSError::throwMe(errSecCSCMSTooLarge);
arch.source->seek(offset);
arch.source->writeAll(*blob);
::free(blob); // done with it
UidGuard guard;
if (!guard.seteuid(0))
guard.seteuid(st.st_uid);
+
+ // copy metadata from original file...
copy(sourcePath.c_str(), NULL, COPYFILE_SECURITY | COPYFILE_METADATA);
+
+ // ... but explicitly update the timestamps since we did change the file
+ char buf;
+ mFd.read(&buf, sizeof(buf), 0);
+ mFd.write(&buf, sizeof(buf), 0);
// move the new file into place
UnixError::check(::rename(tempPath.c_str(), sourcePath.c_str()));
//
-// Copyfile
+// InternalRequirements
//
-Copyfile::Copyfile()
+void InternalRequirements::operator () (const Requirements *given, const Requirements *defaulted, const Requirement::Context &context)
{
- if (!(mState = copyfile_state_alloc()))
- UnixError::throwMe();
-}
+ // first add the default internal requirements
+ if (defaulted) {
+ this->add(defaulted);
+ ::free((void *)defaulted); // was malloc(3)ed by DiskRep
+ }
-void Copyfile::set(uint32_t flag, const void *value)
-{
- check(::copyfile_state_set(mState, flag, value));
-}
+ // now override them with any requirements explicitly given by the signer
+ if (given)
+ this->add(given);
-void Copyfile::get(uint32_t flag, void *value)
-{
- check(::copyfile_state_set(mState, flag, value));
-}
+ // now add the Designated Requirement, if we can make it and it's not been provided
+ if (!this->contains(kSecDesignatedRequirementType)) {
+ DRMaker maker(context);
+ if (Requirement *dr = maker.make()) {
+ this->add(kSecDesignatedRequirementType, dr); // takes ownership of dr
+ }
+ }
-void Copyfile::operator () (const char *src, const char *dst, copyfile_flags_t flags)
-{
- check(::copyfile(src, dst, mState, flags));
+ // return the result
+ mReqs = this->make();
}
-void Copyfile::check(int rc)
+
+//
+// Pre-Signing contexts
+//
+PreSigningContext::PreSigningContext(const SecCodeSigner::Signer &signer)
{
- if (rc < 0)
- UnixError::throwMe();
+ // construct a cert chain
+ if (signer.signingIdentity() != SecIdentityRef(kCFNull)) {
+ CFRef<SecCertificateRef> signingCert;
+ MacOSError::check(SecIdentityCopyCertificate(signer.signingIdentity(), &signingCert.aref()));
+ CFRef<SecPolicyRef> policy = SecPolicyCreateWithOID(kSecPolicyAppleCodeSigning);
+ CFRef<SecTrustRef> trust;
+ MacOSError::check(SecTrustCreateWithCertificates(CFArrayRef(signingCert.get()), policy, &trust.aref()));
+ SecTrustResultType result;
+ MacOSError::check(SecTrustEvaluate(trust, &result));
+ CSSM_TP_APPLE_EVIDENCE_INFO *info;
+ MacOSError::check(SecTrustGetResult(trust, &result, &mCerts.aref(), &info));
+ this->certs = mCerts;
+ }
+
+ // other stuff
+ this->identifier = signer.signingIdentifier();
}