#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)
{
secinfo("codesign", "start verify");
StLock<Mutex> _(process);
if (SecRequirementRef requirement = verifier.requirement()) {
secinfo("codesign", "CS requirement present; ignoring legacy hashes");
Server::active().longTermActivity();
switch (OSStatus rc = process.checkValidity(kSecCSDefaultFlags, requirement)) {
case noErr:
secinfo("codesign", "CS verify passed");
return true;
case errSecCSUnsigned:
secinfo("codesign", "CS verify against unsigned binary failed");
return false;
default:
secinfo("codesign", "CS verify failed OSStatus=%d", int32_t(rc));
return false;
}
}
switch (matchSignedClientToLegacyACL(process, verifier, context)) {
case noErr: return true;
case errSecCSUnsigned: { secinfo("codesign", "no CS requirement - using legacy hash");
Identity &clientIdentity = process;
try {
if (clientIdentity.getHash() == CssmData::wrap(verifier.legacyHash(), SHA1::digestLength)) {
secinfo("codesign", "direct match: pass");
return true;
}
} catch (...) {
secinfo("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);
size_t preLength = s.length() - suffix.length();
if (preLength > 0 && s.substr(preLength) == suffix)
s = s.substr(0, preLength);
return s;
}
OSStatus CodeSignatures::matchSignedClientToLegacyACL(Process &process,
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 (process.checkValidity(kSecCSDefaultFlags, dotmac) == noErr) {
secinfo("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(process.copySigningInfo(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");
secinfo("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 = process.checkValidity(kSecCSDefaultFlags, apple)) {
case noErr:
{
secinfo("codesign", "withstands strict scrutiny; quietly adding new ACL");
RefPointer<AclSubject> subject = process.copyAclSubject();
SecurityServerAcl::addToStandardACL(context, subject);
return noErr;
}
default:
secinfo("codesign", "validation fails with rc=%d, rejecting", int32_t(rc));
return rc;
}
}
return errSecCSReqFailed;
}