#include "codesigdb.h"
#include "process.h"
#include "server.h"
#include "agentquery.h"
#include <security_utilities/memutils.h>
#include <security_utilities/logging.h>
#include <Security/SecRequirementPriv.h>
CodeSignatures::CodeSignatures()
{
}
CodeSignatures::~CodeSignatures()
{
}
void CodeSignatures::open(const char *path)
{
}
CodeSignatures::Identity::Identity() : mState(untried)
{ }
CodeSignatures::Identity::~Identity()
{ }
bool CodeSignatures::verify(Process &process,
const OSXVerifier &verifier, const AclValidationContext &context)
{
secdebug("codesign", "start verify");
StLock<Mutex> _(process);
SecCodeRef code = process.currentGuest();
if (!code) {
secdebug("codesign", "no code base: fail");
return false;
}
if (SecRequirementRef requirement = verifier.requirement()) {
secdebug("codesign", "CS requirement present; ignoring legacy hashes");
Server::active().longTermActivity();
switch (OSStatus rc = SecCodeCheckValidity(code, kSecCSDefaultFlags, requirement)) {
case noErr:
secdebug("codesign", "CS verify passed");
return true;
case errSecCSUnsigned:
secdebug("codesign", "CS verify against unsigned binary failed");
return false;
default:
secdebug("codesign", "CS verify failed OSStatus=%d", int32_t(rc));
return false;
}
}
switch (matchSignedClientToLegacyACL(process, code, verifier, context)) {
case noErr: return true;
case errSecCSUnsigned: { secdebug("codesign", "no CS requirement - using legacy hash");
Identity &clientIdentity = process;
try {
if (clientIdentity.getHash() == CssmData::wrap(verifier.legacyHash(), SHA1::digestLength)) {
secdebug("codesign", "direct match: pass");
return true;
}
} catch (...) {
secdebug("codesign", "exception getting client code hash: fail");
return false;
}
return false;
}
default: return false;
}
}
static string trim(string s, char delimiter)
{
string::size_type p = s.rfind(delimiter);
if (p != string::npos)
s = s.substr(p + 1);
return s;
}
static string trim(string s, char delimiter, string suffix)
{
s = trim(s, delimiter);
int preLength = s.length() - suffix.length();
if (preLength > 0 && s.substr(preLength) == suffix)
s = s.substr(0, preLength);
return s;
}
OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process,
SecCodeRef code, const OSXVerifier &verifier, const AclValidationContext &context)
{
if (SecurityServerAcl::looksLikeLegacyDotMac(context)) {
Server::active().longTermActivity();
CFRef<SecRequirementRef> dotmac;
MacOSError::check(SecRequirementCreateGroup(CFSTR("dot-mac"), NULL, kSecCSDefaultFlags, &dotmac.aref()));
if (SecCodeCheckValidity(code, kSecCSDefaultFlags, dotmac) == noErr) {
secdebug("codesign", "client is a dot-mac application; update the ACL accordingly");
CFRef<CFDataRef> reqdata;
MacOSError::check(SecRequirementCopyData(dotmac, kSecCSDefaultFlags, &reqdata.aref()));
RefPointer<CodeSignatureAclSubject> subject = new CodeSignatureAclSubject(NULL, "group://dot-mac");
subject->add((const BlobCore *)CFDataGetBytePtr(reqdata));
SecurityServerAcl::addToStandardACL(context, subject);
return noErr;
}
}
CFRef<CFDictionaryRef> info;
MacOSError::check(SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()));
CFStringRef signingIdentity = CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoIdentifier));
if (!signingIdentity) return errSecCSUnsigned;
string bundleName; if (CFDictionaryRef infoList = CFDictionaryRef(CFDictionaryGetValue(info, kSecCodeInfoPList)))
if (CFStringRef name = CFStringRef(CFDictionaryGetValue(infoList, kCFBundleNameKey)))
bundleName = trim(cfString(name), '.');
if (bundleName.empty()) bundleName = trim(cfString(signingIdentity), '.');
string aclName = trim(verifier.path(), '/', ".app");
secdebug("codesign", "matching signed client \"%s\" against legacy ACL \"%s\"",
bundleName.c_str(), aclName.c_str());
if (bundleName == aclName) {
const unsigned char reqData[] = { 0xfa, 0xde, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03
};
CFRef<SecRequirementRef> apple;
MacOSError::check(SecRequirementCreateWithData(CFTempData(reqData, sizeof(reqData)),
kSecCSDefaultFlags, &apple.aref()));
Server::active().longTermActivity();
switch (OSStatus rc = SecCodeCheckValidity(code, kSecCSDefaultFlags, apple)) {
case noErr:
{
secdebug("codesign", "withstands strict scrutiny; quietly adding new ACL");
RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
SecurityServerAcl::addToStandardACL(context, subject);
return noErr;
}
default:
secdebug("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
return rc;
}
secdebug("codesign", "does not withstand strict scrutiny; ask the user");
QueryCodeCheck query;
query.inferHints(process);
if (!query(verifier.path().c_str())) {
secdebug("codesign", "user declined equivalence: cancel the access");
CssmError::throwMe(CSSM_ERRCODE_USER_CANCELED);
}
RefPointer<OSXCode> wrap = new OSXCodeWrap(code);
RefPointer<AclSubject> subject = new CodeSignatureAclSubject(OSXVerifier(wrap));
SecurityServerAcl::addToStandardACL(context, subject);
return noErr;
}
return errSecCSReqFailed;
}