#include "StaticCode.h"
#include "Code.h"
#include "reqmaker.h"
#include "drmaker.h"
#include "reqdumper.h"
#include "sigblob.h"
#include "resources.h"
#include "renum.h"
#include "detachedrep.h"
#include "csdatabase.h"
#include "csutilities.h"
#include <CoreFoundation/CFURLAccess.h>
#include <Security/SecPolicyPriv.h>
#include <Security/SecTrustPriv.h>
#include <Security/SecCertificatePriv.h>
#include <Security/CMSPrivate.h>
#include <Security/SecCmsContentInfo.h>
#include <Security/SecCmsSignerInfo.h>
#include <Security/SecCmsSignedData.h>
#include <Security/cssmapplePriv.h>
#include <security_utilities/unix++.h>
#include <security_utilities/cfmunge.h>
#include <Security/CMSDecoder.h>
namespace Security {
namespace CodeSigning {
using namespace UnixPlusPlus;
SecStaticCode::SecStaticCode(DiskRep *rep)
: mRep(rep),
mValidated(false), mExecutableValidated(false), mResourcesValidated(false), mResourcesValidContext(NULL),
mDesignatedReq(NULL), mGotResourceBase(false), mEvalDetails(NULL)
{
CODESIGN_STATIC_CREATE(this, rep);
checkForSystemSignature();
}
SecStaticCode::~SecStaticCode() throw()
try {
::free(const_cast<Requirement *>(mDesignatedReq));
if (mResourcesValidContext)
delete mResourcesValidContext;
} catch (...) {
return;
}
bool SecStaticCode::equal(SecCFObject &secOther)
{
SecStaticCode *other = static_cast<SecStaticCode *>(&secOther);
CFDataRef mine = this->cdHash();
CFDataRef his = other->cdHash();
if (mine || his)
return mine && his && CFEqual(mine, his);
else
return CFEqual(this->canonicalPath(), other->canonicalPath());
}
CFHashCode SecStaticCode::hash()
{
if (CFDataRef h = this->cdHash())
return CFHash(h);
else
return CFHash(this->canonicalPath());
}
void SecStaticCode::detachedSignature(CFDataRef sigData)
{
if (sigData) {
mRep = new DetachedRep(sigData, mRep->base(), "explicit detached");
CODESIGN_STATIC_ATTACH_EXPLICIT(this, mRep);
} else {
mRep = mRep->base();
CODESIGN_STATIC_ATTACH_EXPLICIT(this, NULL);
}
}
void SecStaticCode::checkForSystemSignature()
{
if (!this->isSigned())
try {
if (RefPointer<DiskRep> dsig = signatureDatabase().findCode(mRep)) {
CODESIGN_STATIC_ATTACH_SYSTEM(this, dsig);
mRep = dsig;
}
} catch (...) {
}
}
string SecStaticCode::signatureSource()
{
if (!isSigned())
return "unsigned";
if (DetachedRep *rep = dynamic_cast<DetachedRep *>(mRep.get()))
return rep->source();
return "embedded";
}
SecStaticCode *SecStaticCode::requiredStatic(SecStaticCodeRef ref)
{
SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
if (SecStaticCode *scode = dynamic_cast<SecStaticCode *>(object))
return scode;
else if (SecCode *code = dynamic_cast<SecCode *>(object))
return code->staticCode();
else MacOSError::throwMe(errSecCSInvalidObjectRef);
}
SecCode *SecStaticCode::optionalDynamic(SecStaticCodeRef ref)
{
SecCFObject *object = SecCFObject::required(ref, errSecCSInvalidObjectRef);
if (dynamic_cast<SecStaticCode *>(object))
return NULL;
else if (SecCode *code = dynamic_cast<SecCode *>(object))
return code;
else MacOSError::throwMe(errSecCSInvalidObjectRef);
}
void SecStaticCode::resetValidity()
{
CODESIGN_EVAL_STATIC_RESET(this);
mValidated = false;
mExecutableValidated = false;
mResourcesValidated = false;
if (mResourcesValidContext) {
delete mResourcesValidContext;
mResourcesValidContext = NULL;
}
mDir = NULL;
mSignature = NULL;
for (unsigned n = 0; n < cdSlotCount; n++)
mCache[n] = NULL;
mInfoDict = NULL;
mEntitlements = NULL;
mResourceDict = NULL;
mDesignatedReq = NULL;
mGotResourceBase = false;
mTrust = NULL;
mCertChain = NULL;
mEvalDetails = NULL;
mRep->flush();
checkForSystemSignature();
}
CFDataRef SecStaticCode::component(CodeDirectory::SpecialSlot slot, OSStatus fail )
{
assert(slot <= cdSlotMax);
CFRef<CFDataRef> &cache = mCache[slot];
if (!cache) {
if (CFRef<CFDataRef> data = mRep->component(slot)) {
if (validated()) if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
MacOSError::throwMe(fail); cache = data; } else { if (validated()) if (codeDirectory()->slotIsPresent(-slot)) MacOSError::throwMe(fail); cache = CFDataRef(kCFNull); }
}
return (cache == CFDataRef(kCFNull)) ? NULL : cache.get();
}
const CodeDirectory *SecStaticCode::codeDirectory(bool check )
{
if (!mDir) {
if (mDir.take(mRep->codeDirectory())) {
const CodeDirectory *dir = reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
dir->checkIntegrity();
}
}
if (mDir)
return reinterpret_cast<const CodeDirectory *>(CFDataGetBytePtr(mDir));
if (check)
MacOSError::throwMe(errSecCSUnsigned);
return NULL;
}
CFDataRef SecStaticCode::cdHash()
{
if (!mCDHash) {
if (const CodeDirectory *cd = codeDirectory(false)) {
SHA1 hash;
hash(cd, cd->length());
SHA1::Digest digest;
hash.finish(digest);
mCDHash.take(makeCFData(digest, sizeof(digest)));
CODESIGN_STATIC_CDHASH(this, digest, sizeof(digest));
}
}
return mCDHash;
}
CFDataRef SecStaticCode::signature()
{
if (!mSignature)
mSignature.take(mRep->signature());
if (mSignature)
return mSignature;
MacOSError::throwMe(errSecCSUnsigned);
}
void SecStaticCode::validateDirectory()
{
if (!validated())
try {
CODESIGN_EVAL_STATIC_DIRECTORY(this);
mValidationExpired = verifySignature();
component(cdInfoSlot, errSecCSInfoPlistFailed); for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
if (mCache[slot]) validateComponent(slot); mValidated = true; mValidationResult = noErr; } catch (const CommonError &err) {
mValidated = true;
mValidationResult = err.osStatus();
throw;
} catch (...) {
secdebug("staticCode", "%p validation threw non-common exception", this);
mValidated = true;
mValidationResult = errSecCSInternalError;
throw;
}
assert(validated());
if (mValidationResult == noErr) {
if (mValidationExpired)
if ((apiFlags() & kSecCSConsiderExpiration)
|| (codeDirectory()->flags & kSecCodeSignatureForceExpiration))
MacOSError::throwMe(CSSMERR_TP_CERT_EXPIRED);
} else
MacOSError::throwMe(mValidationResult);
}
void SecStaticCode::validateNonResourceComponents()
{
this->validateDirectory();
for (CodeDirectory::SpecialSlot slot = codeDirectory()->maxSpecialSlot(); slot >= 1; --slot)
switch (slot) {
case cdResourceDirSlot: break;
default:
this->component(slot); break;
}
}
CFAbsoluteTime SecStaticCode::signingTime()
{
validateDirectory();
return mSigningTime;
}
CFAbsoluteTime SecStaticCode::signingTimestamp()
{
validateDirectory();
return mSigningTimestamp;
}
bool SecStaticCode::verifySignature()
{
if (flag(kSecCodeSignatureAdhoc)) {
CODESIGN_EVAL_STATIC_SIGNATURE_ADHOC(this);
return false;
}
DTRACK(CODESIGN_EVAL_STATIC_SIGNATURE, this, (char*)this->mainExecutablePath().c_str());
CFRef<CMSDecoderRef> cms;
MacOSError::check(CMSDecoderCreate(&cms.aref())); CFDataRef sig = this->signature();
MacOSError::check(CMSDecoderUpdateMessage(cms, CFDataGetBytePtr(sig), CFDataGetLength(sig)));
this->codeDirectory(); MacOSError::check(CMSDecoderSetDetachedContent(cms, mDir));
MacOSError::check(CMSDecoderFinalizeMessage(cms));
MacOSError::check(CMSDecoderSetSearchKeychain(cms, cfEmptyArray()));
CFRef<CFTypeRef> policy = verificationPolicy(apiFlags());
CMSSignerStatus status;
MacOSError::check(CMSDecoderCopySignerStatus(cms, 0, policy,
false, &status, &mTrust.aref(), NULL));
if (status != kCMSSignerValid)
MacOSError::throwMe(errSecCSSignatureFailed);
mSigningTime = 0; switch (OSStatus rc = CMSDecoderCopySignerSigningTime(cms, 0, &mSigningTime)) {
case noErr:
case errSecSigningTimeMissing:
break;
default:
MacOSError::throwMe(rc);
}
mSigningTimestamp = 0;
switch (OSStatus rc = CMSDecoderCopySignerTimestamp(cms, 0, &mSigningTimestamp)) {
case noErr:
case errSecTimestampMissing:
break;
default:
MacOSError::throwMe(rc);
}
MacOSError::check(SecTrustSetAnchorCertificates(mTrust, cfEmptyArray())); MacOSError::check(SecTrustSetKeychains(mTrust, cfEmptyArray())); CSSM_APPLE_TP_ACTION_DATA actionData = {
CSSM_APPLE_TP_ACTION_VERSION, CSSM_TP_ACTION_IMPLICIT_ANCHORS };
for (;;) { MacOSError::check(SecTrustSetParameters(mTrust,
CSSM_TP_ACTION_DEFAULT, CFTempData(&actionData, sizeof(actionData))));
SecTrustResultType trustResult;
MacOSError::check(SecTrustEvaluate(mTrust, &trustResult));
MacOSError::check(SecTrustGetResult(mTrust, &trustResult, &mCertChain.aref(), &mEvalDetails));
CODESIGN_EVAL_STATIC_SIGNATURE_RESULT(this, trustResult, mCertChain ? CFArrayGetCount(mCertChain) : 0);
switch (trustResult) {
case kSecTrustResultProceed:
case kSecTrustResultUnspecified:
break; case kSecTrustResultDeny:
MacOSError::throwMe(CSSMERR_APPLETP_TRUST_SETTING_DENY); case kSecTrustResultInvalid:
assert(false); MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
default:
{
OSStatus result;
MacOSError::check(SecTrustGetCssmResultCode(mTrust, &result));
if (mSigningTimestamp == 0) if (((result == CSSMERR_TP_CERT_EXPIRED) || (result == CSSMERR_TP_CERT_NOT_VALID_YET))
&& !(actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED)) {
CODESIGN_EVAL_STATIC_SIGNATURE_EXPIRED(this);
actionData.ActionFlags |= CSSM_TP_ACTION_ALLOW_EXPIRED; continue; }
MacOSError::throwMe(result);
}
}
if (mSigningTimestamp) {
CFIndex rootix = CFArrayGetCount(mCertChain);
if (SecCertificateRef mainRoot = SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, rootix-1)))
if (isAppleCA(mainRoot)) {
CFRef<CFArrayRef> tsCerts;
MacOSError::check(CMSDecoderCopySignerTimestampCertificates(cms, 0, &tsCerts.aref()));
CFIndex tsn = CFArrayGetCount(tsCerts);
bool good = tsn > 0 && isAppleCA(SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, tsn-1)));
#ifndef WORKAROUND_12007637
for (CFIndex n = 0; n < tsn; n++)
if (SecCertificateRef tsRoot = SecCertificateRef(CFArrayGetValueAtIndex(tsCerts, n)))
if ((good = isAppleCA(tsRoot))) {
secdebug("BUG", "Apple root at TS cert %ld", n);
break;
}
#endif //WORKAROUND_12007637
if (!good)
MacOSError::throwMe(CSSMERR_TP_NOT_TRUSTED);
}
}
return actionData.ActionFlags & CSSM_TP_ACTION_ALLOW_EXPIRED;
}
}
static SecPolicyRef makeCRLPolicy()
{
CFRef<SecPolicyRef> policy;
MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_CRL, &policy.aref()));
CSSM_APPLE_TP_CRL_OPTIONS options;
memset(&options, 0, sizeof(options));
options.Version = CSSM_APPLE_TP_CRL_OPTS_VERSION;
options.CrlFlags = CSSM_TP_ACTION_FETCH_CRL_FROM_NET | CSSM_TP_ACTION_CRL_SUFFICIENT;
CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
MacOSError::check(SecPolicySetValue(policy, &optData));
return policy.yield();
}
static SecPolicyRef makeOCSPPolicy()
{
CFRef<SecPolicyRef> policy;
MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3, &CSSMOID_APPLE_TP_REVOCATION_OCSP, &policy.aref()));
CSSM_APPLE_TP_OCSP_OPTIONS options;
memset(&options, 0, sizeof(options));
options.Version = CSSM_APPLE_TP_OCSP_OPTS_VERSION;
options.Flags = CSSM_TP_ACTION_OCSP_SUFFICIENT;
CSSM_DATA optData = { sizeof(options), (uint8 *)&options };
MacOSError::check(SecPolicySetValue(policy, &optData));
return policy.yield();
}
CFTypeRef SecStaticCode::verificationPolicy(SecCSFlags flags)
{
CFRef<SecPolicyRef> core;
MacOSError::check(SecPolicyCopy(CSSM_CERT_X_509v3,
&CSSMOID_APPLE_TP_CODE_SIGNING, &core.aref()));
if (flags & kSecCSEnforceRevocationChecks) {
CFRef<SecPolicyRef> crl = makeCRLPolicy();
CFRef<SecPolicyRef> ocsp = makeOCSPPolicy();
return makeCFArray(3, core.get(), crl.get(), ocsp.get());
} else {
return core.yield();
}
}
void SecStaticCode::validateComponent(CodeDirectory::SpecialSlot slot, OSStatus fail )
{
assert(slot <= cdSlotMax);
CFDataRef data = mCache[slot];
assert(data); if (data == CFDataRef(kCFNull)) {
if (codeDirectory()->slotIsPresent(-slot)) MacOSError::throwMe(fail); } else {
if (!codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot))
MacOSError::throwMe(fail);
}
}
void SecStaticCode::validateExecutable()
{
if (!validatedExecutable()) {
try {
DTRACK(CODESIGN_EVAL_STATIC_EXECUTABLE, this,
(char*)this->mainExecutablePath().c_str(), codeDirectory()->nCodeSlots);
const CodeDirectory *cd = this->codeDirectory();
if (!cd)
MacOSError::throwMe(errSecCSUnsigned);
AutoFileDesc fd(mainExecutablePath(), O_RDONLY);
fd.fcntl(F_NOCACHE, true); if (Universal *fat = mRep->mainExecutableImage())
fd.seek(fat->archOffset());
size_t pageSize = cd->pageSize ? (1 << cd->pageSize) : 0;
size_t remaining = cd->codeLimit;
for (size_t slot = 0; slot < cd->nCodeSlots; ++slot) {
size_t size = min(remaining, pageSize);
if (!cd->validateSlot(fd, size, slot)) {
CODESIGN_EVAL_STATIC_EXECUTABLE_FAIL(this, slot);
MacOSError::throwMe(errSecCSSignatureFailed);
}
remaining -= size;
}
mExecutableValidated = true;
mExecutableValidResult = noErr;
} catch (const CommonError &err) {
mExecutableValidated = true;
mExecutableValidResult = err.osStatus();
throw;
} catch (...) {
secdebug("staticCode", "%p executable validation threw non-common exception", this);
mExecutableValidated = true;
mExecutableValidResult = errSecCSInternalError;
throw;
}
}
assert(validatedExecutable());
if (mExecutableValidResult != noErr)
MacOSError::throwMe(mExecutableValidResult);
}
void SecStaticCode::validateResources()
{
if (!validatedResources()) {
try {
CFDictionaryRef sealedResources = resourceDictionary();
if (this->resourceBase()) if (sealedResources)
;
else
MacOSError::throwMe(errSecCSResourcesNotFound);
else if (sealedResources)
MacOSError::throwMe(errSecCSResourcesNotFound);
else
return;
CFDictionaryRef rules = cfget<CFDictionaryRef>(sealedResources, "rules");
CFDictionaryRef files = cfget<CFDictionaryRef>(sealedResources, "files");
DTRACK(CODESIGN_EVAL_STATIC_RESOURCES, this,
(char*)this->mainExecutablePath().c_str(), int(CFDictionaryGetCount(files)));
CFRef<CFMutableDictionaryRef> resourceMap = makeCFMutableDictionary(files);
mResourcesValidContext = new CollectingContext(*this); ResourceBuilder resources(cfString(this->resourceBase()), rules, codeDirectory()->hashType);
mRep->adjustResources(resources);
string path;
ResourceBuilder::Rule *rule;
while (resources.next(path, rule)) {
validateResource(path, *mResourcesValidContext);
CFDictionaryRemoveValue(resourceMap, CFTempString(path));
}
if (CFDictionaryGetCount(resourceMap) > 0) {
secdebug("staticCode", "%p sealed resource(s) not found in code", this);
CFDictionaryApplyFunction(resourceMap, SecStaticCode::checkOptionalResource, mResourcesValidContext);
}
mResourcesValidated = true;
if (mResourcesValidContext->osStatus() != noErr)
mResourcesValidContext->throwMe();
} catch (const CommonError &err) {
mResourcesValidated = true;
mResourcesValidResult = err.osStatus();
throw;
} catch (...) {
secdebug("staticCode", "%p executable validation threw non-common exception", this);
mResourcesValidated = true;
mResourcesValidResult = errSecCSInternalError;
throw;
}
}
assert(validatedResources());
if (mResourcesValidResult)
MacOSError::throwMe(mResourcesValidResult);
if (mResourcesValidContext->osStatus() != noErr)
mResourcesValidContext->throwMe();
}
void SecStaticCode::checkOptionalResource(CFTypeRef key, CFTypeRef value, void *context)
{
CollectingContext *ctx = static_cast<CollectingContext *>(context);
ResourceSeal seal(value);
if (!seal.optional()) {
if (key && CFGetTypeID(key) == CFStringGetTypeID()) {
ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing,
CFTempURL(CFStringRef(key), false, ctx->code.resourceBase()));
} else {
ctx->reportProblem(errSecCSBadResource, kSecCFErrorResourceSeal, key);
}
}
}
CFDictionaryRef SecStaticCode::infoDictionary()
{
if (!mInfoDict) {
mInfoDict.take(getDictionary(cdInfoSlot, errSecCSInfoPlistFailed));
secdebug("staticCode", "%p loaded InfoDict %p", this, mInfoDict.get());
}
return mInfoDict;
}
CFDictionaryRef SecStaticCode::entitlements()
{
if (!mEntitlements) {
validateDirectory();
if (CFDataRef entitlementData = component(cdEntitlementSlot)) {
validateComponent(cdEntitlementSlot);
const EntitlementBlob *blob = reinterpret_cast<const EntitlementBlob *>(CFDataGetBytePtr(entitlementData));
if (blob->validateBlob()) {
mEntitlements.take(blob->entitlements());
secdebug("staticCode", "%p loaded Entitlements %p", this, mEntitlements.get());
}
}
}
return mEntitlements;
}
CFDictionaryRef SecStaticCode::resourceDictionary()
{
if (mResourceDict) return mResourceDict;
if (CFRef<CFDictionaryRef> dict = getDictionary(cdResourceDirSlot, errSecCSSignatureFailed))
if (cfscan(dict, "{rules=%Dn,files=%Dn}")) {
secdebug("staticCode", "%p loaded ResourceDict %p",
this, mResourceDict.get());
return mResourceDict = dict;
}
return NULL;
}
CFURLRef SecStaticCode::resourceBase()
{
if (!mGotResourceBase) {
string base = mRep->resourcesRootPath();
if (!base.empty())
mResourceBase.take(makeCFURL(base, true));
mGotResourceBase = true;
}
return mResourceBase;
}
CFDictionaryRef SecStaticCode::getDictionary(CodeDirectory::SpecialSlot slot, OSStatus fail )
{
validateDirectory();
if (CFDataRef infoData = component(slot, fail)) {
validateComponent(slot, fail);
if (CFDictionaryRef dict = makeCFDictionaryFrom(infoData))
return dict;
else
MacOSError::throwMe(errSecCSBadDictionaryFormat);
}
return NULL;
}
CFDataRef SecStaticCode::resource(string path, ValidationContext &ctx)
{
if (CFDictionaryRef rdict = resourceDictionary()) {
if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
ResourceSeal seal = file;
if (!resourceBase()) MacOSError::throwMe(errSecCSResourcesNotFound);
CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
if (CFRef<CFDataRef> data = cfLoadFile(fullpath)) {
MakeHash<CodeDirectory> hasher(this->codeDirectory());
hasher->update(CFDataGetBytePtr(data), CFDataGetLength(data));
if (hasher->verify(seal.hash()))
return data.yield(); else
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); } else {
if (!seal.optional())
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); else
return NULL; }
} else
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
return NULL;
} else
MacOSError::throwMe(errSecCSResourcesNotSealed);
}
CFDataRef SecStaticCode::resource(string path)
{
ValidationContext ctx;
return resource(path, ctx);
}
void SecStaticCode::validateResource(string path, ValidationContext &ctx)
{
if (CFDictionaryRef rdict = resourceDictionary()) {
if (CFTypeRef file = cfget(rdict, "files.%s", path.c_str())) {
ResourceSeal seal = file;
if (!resourceBase()) MacOSError::throwMe(errSecCSResourcesNotFound);
CFRef<CFURLRef> fullpath = makeCFURL(path, false, resourceBase());
AutoFileDesc fd(cfString(fullpath), O_RDONLY, FileDesc::modeMissingOk); if (fd) {
MakeHash<CodeDirectory> hasher(this->codeDirectory());
hashFileData(fd, hasher.get());
if (hasher->verify(seal.hash()))
return; else
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAltered, fullpath); } else {
if (!seal.optional())
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceMissing, fullpath); else
return; }
} else
ctx.reportProblem(errSecCSBadResource, kSecCFErrorResourceAdded, CFTempURL(path, false, resourceBase()));
} else
MacOSError::throwMe(errSecCSResourcesNotSealed);
}
bool SecStaticCode::flag(uint32_t tested)
{
if (const CodeDirectory *cd = this->codeDirectory(false))
return cd->flags & tested;
else
return false;
}
const Requirements *SecStaticCode::internalRequirements()
{
if (CFDataRef req = component(cdRequirementsSlot))
return (const Requirements *)CFDataGetBytePtr(req);
else
return NULL;
}
const Requirement *SecStaticCode::internalRequirement(SecRequirementType type)
{
if (const Requirements *reqs = internalRequirements())
return reqs->find<Requirement>(type);
else
return NULL;
}
const Requirement *SecStaticCode::designatedRequirement()
{
if (const Requirement *req = internalRequirement(kSecDesignatedRequirementType)) {
return req; } else {
if (!mDesignatedReq)
mDesignatedReq = defaultDesignatedRequirement();
return mDesignatedReq;
}
}
const Requirement *SecStaticCode::defaultDesignatedRequirement()
{
if (flag(kSecCodeSignatureAdhoc)) {
Requirement::Maker maker;
SHA1 hash;
hash(codeDirectory(), codeDirectory()->length());
SHA1::Digest digest;
hash.finish(digest);
maker.cdhash(digest);
return maker.make();
} else {
validateDirectory(); Requirement::Context context(this->certificates(),
this->infoDictionary(),
this->entitlements(),
this->identifier(),
this->codeDirectory()
);
return DRMaker(context).make();
}
}
void SecStaticCode::validateRequirements(SecRequirementType type, SecStaticCode *target,
OSStatus nullError )
{
DTRACK(CODESIGN_EVAL_STATIC_INTREQ, this, type, target, nullError);
if (const Requirement *req = internalRequirement(type))
target->validateRequirement(req, nullError ? nullError : errSecCSReqFailed);
else if (nullError)
MacOSError::throwMe(nullError);
else
;
}
bool SecStaticCode::satisfiesRequirement(const Requirement *req, OSStatus failure)
{
assert(req);
validateDirectory();
return req->validates(Requirement::Context(mCertChain, infoDictionary(), entitlements(), codeDirectory()->identifier(), codeDirectory()), failure);
}
void SecStaticCode::validateRequirement(const Requirement *req, OSStatus failure)
{
if (!this->satisfiesRequirement(req, failure))
MacOSError::throwMe(failure);
}
SecCertificateRef SecStaticCode::cert(int ix)
{
validateDirectory(); if (mCertChain) {
CFIndex length = CFArrayGetCount(mCertChain);
if (ix < 0)
ix += length;
if (ix >= 0 && ix < length)
return SecCertificateRef(CFArrayGetValueAtIndex(mCertChain, ix));
}
return NULL;
}
CFArrayRef SecStaticCode::certificates()
{
validateDirectory(); return mCertChain;
}
CFDictionaryRef SecStaticCode::signingInformation(SecCSFlags flags)
{
CFRef<CFMutableDictionaryRef> dict = makeCFMutableDictionary(1,
kSecCodeInfoMainExecutable, CFTempURL(this->mainExecutablePath()).get()
);
if (!this->isSigned())
return dict.yield();
CFDictionaryAddValue(dict, kSecCodeInfoIdentifier, CFTempString(this->identifier()));
CFDictionaryAddValue(dict, kSecCodeInfoFormat, CFTempString(this->format()));
CFDictionaryAddValue(dict, kSecCodeInfoSource, CFTempString(this->signatureSource()));
CFDictionaryAddValue(dict, kSecCodeInfoUnique, this->cdHash());
CFDictionaryAddValue(dict, kSecCodeInfoDigestAlgorithm, CFTempNumber(this->codeDirectory(false)->hashType));
try {
if (CFDictionaryRef info = this->infoDictionary())
CFDictionaryAddValue(dict, kSecCodeInfoPList, info);
} catch (...) { }
if (flags & kSecCSSigningInformation) {
if (CFArrayRef certs = this->certificates())
CFDictionaryAddValue(dict, kSecCodeInfoCertificates, certs);
if (CFDataRef sig = this->signature())
CFDictionaryAddValue(dict, kSecCodeInfoCMS, sig);
if (mTrust)
CFDictionaryAddValue(dict, kSecCodeInfoTrust, mTrust);
if (CFAbsoluteTime time = this->signingTime())
if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
CFDictionaryAddValue(dict, kSecCodeInfoTime, date);
if (CFAbsoluteTime time = this->signingTimestamp())
if (CFRef<CFDateRef> date = CFDateCreate(NULL, time))
CFDictionaryAddValue(dict, kSecCodeInfoTimestamp, date);
}
if (flags & kSecCSRequirementInformation) {
if (const Requirements *reqs = this->internalRequirements()) {
CFDictionaryAddValue(dict, kSecCodeInfoRequirements,
CFTempString(Dumper::dump(reqs)));
CFDictionaryAddValue(dict, kSecCodeInfoRequirementData, CFTempData(*reqs));
}
const Requirement *dreq = this->designatedRequirement();
CFRef<SecRequirementRef> dreqRef = (new SecRequirement(dreq))->handle();
CFDictionaryAddValue(dict, kSecCodeInfoDesignatedRequirement, dreqRef);
if (this->internalRequirement(kSecDesignatedRequirementType)) { CFRef<SecRequirementRef> ddreqRef = (new SecRequirement(this->defaultDesignatedRequirement(), true))->handle();
CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, ddreqRef);
} else { CFDictionaryAddValue(dict, kSecCodeInfoImplicitDesignatedRequirement, dreqRef);
}
if (CFDataRef ent = this->component(cdEntitlementSlot)) {
CFDictionaryAddValue(dict, kSecCodeInfoEntitlements, ent);
if (CFDictionaryRef entdict = this->entitlements())
CFDictionaryAddValue(dict, kSecCodeInfoEntitlementsDict, entdict);
}
}
if (flags & kSecCSInternalInformation) {
if (mDir)
CFDictionaryAddValue(dict, kSecCodeInfoCodeDirectory, mDir);
CFDictionaryAddValue(dict, kSecCodeInfoCodeOffset, CFTempNumber(mRep->signingBase()));
if (CFDictionaryRef resources = resourceDictionary())
CFDictionaryAddValue(dict, kSecCodeInfoResourceDirectory, resources);
}
if (flags & kSecCSContentInformation)
if (CFRef<CFArrayRef> files = mRep->modifiedFiles())
CFDictionaryAddValue(dict, kSecCodeInfoChangedFiles, files);
return dict.yield();
}
SecStaticCode::ValidationContext::~ValidationContext()
{ }
void SecStaticCode::ValidationContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
{
CSError::throwMe(rc, type, value);
}
void SecStaticCode::CollectingContext::reportProblem(OSStatus rc, CFStringRef type, CFTypeRef value)
{
if (mStatus == noErr)
mStatus = rc; if (type) {
if (!mCollection)
mCollection.take(makeCFMutableDictionary());
CFMutableArrayRef element = CFMutableArrayRef(CFDictionaryGetValue(mCollection, type));
if (!element) {
element = makeCFMutableArray(0);
if (!element)
CFError::throwMe();
CFDictionaryAddValue(mCollection, type, element);
CFRelease(element);
}
CFArrayAppendValue(element, value);
}
}
void SecStaticCode::CollectingContext::throwMe()
{
assert(mStatus != noErr);
throw CSError(mStatus, mCollection.retain());
}
SecStaticCode::AllArchitectures::AllArchitectures(SecStaticCode *code)
: mBase(code)
{
if (Universal *fat = code->diskRep()->mainExecutableImage()) {
fat->architectures(mArchitectures);
mCurrent = mArchitectures.begin();
mState = fatBinary;
} else {
mState = firstNonFat;
}
}
SecStaticCode *SecStaticCode::AllArchitectures::operator () ()
{
switch (mState) {
case firstNonFat:
mState = atEnd;
return mBase;
case fatBinary:
{
if (mCurrent == mArchitectures.end())
return NULL;
Architecture arch = *mCurrent++;
if (arch == mBase->diskRep()->mainExecutableImage()->bestNativeArch()) {
return mBase;
} else {
DiskRep::Context ctx;
ctx.arch = arch;
return new SecStaticCode(DiskRep::bestGuess(mBase->mainExecutablePath(), &ctx));
}
}
default:
return NULL;
}
}
} }