#include "clientid.h"
#include "server.h"
#include <Security/SecCodePriv.h>
#include <Security/oidsattr.h>
#include <Security/SecCertificatePriv.h>
ClientIdentification::ClientIdentification()
: mGotPartitionId(false)
{
}
void ClientIdentification::setup(Security::CommonCriteria::AuditToken const &audit)
{
StLock<Mutex> _(mLock);
StLock<Mutex> __(mValidityCheckLock);
audit_token_t const token = audit.auditToken();
OSStatus rc = SecCodeCreateWithAuditToken(&token, kSecCSDefaultFlags, &mClientProcess.aref());
if (rc) {
secerror("could not get code for process %d: OSStatus=%d",
audit.pid(), int32_t(rc));
}
mGuests.erase(mGuests.begin(), mGuests.end());
}
SecCodeRef ClientIdentification::processCode() const
{
return mClientProcess;
}
SecCodeRef ClientIdentification::currentGuest() const
{
if (GuestState *guest = current())
return guest->code;
else
return mClientProcess;
}
ClientIdentification::GuestState *ClientIdentification::current() const
{
if (!processCode())
return NULL;
SecGuestRef guestRef = Server::connection().guestRef();
{
StLock<Mutex> _(mLock);
GuestMap::iterator it = mGuests.find(guestRef);
if (it != mGuests.end())
return &it->second;
}
CFRef<CFDictionaryRef> attributes = (guestRef == kSecNoGuest)
? NULL
: makeCFDictionary(1, kSecGuestAttributeCanonical, CFTempNumber(guestRef).get());
Server::active().longTermActivity();
CFRef<SecCodeRef> code;
switch (OSStatus rc = SecCodeCopyGuestWithAttributes(processCode(),
attributes, kSecCSDefaultFlags, &code.aref())) {
case noErr:
break;
case errSecCSUnsigned: case errSecCSNotAHost: code = mClientProcess;
break;
case errSecCSNoSuchCode: if (guestRef == kSecNoGuest) { code = mClientProcess;
break;
}
default:
MacOSError::throwMe(rc);
}
StLock<Mutex> _(mLock);
GuestState &slot = mGuests[guestRef];
if (!slot.code) slot.code = code;
return &slot;
}
std::string ClientIdentification::partitionId() const
{
if (!mGotPartitionId) {
StLock<Mutex> _(mValidityCheckLock);
mClientPartitionId = partitionIdForProcess(processCode());
mGotPartitionId = true;
}
return mClientPartitionId;
}
static std::string hashString(CFDataRef data)
{
CFIndex length = CFDataGetLength(data);
const unsigned char *hash = CFDataGetBytePtr(data);
char s[2 * length + 1];
for (CFIndex n = 0; n < length; n++)
sprintf(&s[2*n], "%2.2x", hash[n]);
return s;
}
std::string ClientIdentification::partitionIdForProcess(SecStaticCodeRef code)
{
static CFStringRef const appleReq = CFSTR("anchor apple");
static CFStringRef const masReq = CFSTR("anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]");
static CFStringRef const developmentOrDevIDReq = CFSTR("anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13]" " or "
"anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] and certificate leaf[field.1.2.840.113635.100.6.1.12]" " or "
"anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.1] and certificate leaf[field.1.2.840.113635.100.6.1.7]"); static SecRequirementRef apple;
static SecRequirementRef mas;
static SecRequirementRef developmentOrDevID;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (noErr != SecRequirementCreateWithString(appleReq, kSecCSDefaultFlags, &apple)
|| noErr != SecRequirementCreateWithString(masReq, kSecCSDefaultFlags, &mas)
|| noErr != SecRequirementCreateWithString(developmentOrDevIDReq, kSecCSDefaultFlags, &developmentOrDevID))
abort();
});
OSStatus rc;
switch (rc = SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, apple)) {
case noErr:
case errSecCSReqFailed:
break;
case errSecCSUnsigned:
return "unsigned:";
default:
MacOSError::throwMe(rc);
}
CFRef<CFDictionaryRef> info;
if (OSStatus irc = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref()))
MacOSError::throwMe(irc);
if (rc == noErr) {
if (CFEqual(CFDictionaryGetValue(info, kSecCodeInfoIdentifier), CFSTR("com.apple.security"))) {
return "apple-tool:"; } else {
return "apple:";
}
} else if (noErr == SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, mas)) {
return "teamid:" + cfString(CFStringRef(CFDictionaryGetValue(info, kSecCodeInfoTeamIdentifier)));
} else if (noErr == SecStaticCodeCheckValidity(code, kSecCSBasicValidateOnly, developmentOrDevID)) {
CFRef<CFDictionaryRef> info;
if (noErr != (rc = SecCodeCopySigningInformation(code, kSecCSSigningInformation, &info.aref())))
MacOSError::throwMe(rc);
CFArrayRef certChain = CFArrayRef(CFDictionaryGetValue(info, kSecCodeInfoCertificates));
SecCertificateRef signingCert = SecCertificateRef(CFArrayGetValueAtIndex(certChain, 0));
CFRef<CFStringRef> ou;
SecCertificateCopySubjectComponent(signingCert, &CSSMOID_OrganizationalUnitName, &ou.aref());
return "teamid:" + cfString(ou);
} else {
CFDataRef cdhashData = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
assert(cdhashData);
return "cdhash:" + hashString(cdhashData);
}
}
string ClientIdentification::getPath() const
{
assert(mClientProcess);
StLock<Mutex> _(mValidityCheckLock);
return codePath(currentGuest());
}
const CssmData ClientIdentification::getHash() const
{
if (GuestState *guest = current()) {
if (!guest->gotHash) {
RefPointer<OSXCode> clientCode = new OSXCodeWrap(guest->code);
OSXVerifier::makeLegacyHash(clientCode, guest->legacyHash);
guest->gotHash = true;
}
return CssmData::wrap(guest->legacyHash, SHA1::digestLength);
} else
return CssmData();
}
AclSubject* ClientIdentification::copyAclSubject() const
{
StLock<Mutex> _(mValidityCheckLock);
RefPointer<OSXCode> clientXCode = new OSXCodeWrap(currentGuest());
return new CodeSignatureAclSubject(OSXVerifier(clientXCode));
}
OSStatus ClientIdentification::copySigningInfo(SecCSFlags flags,
CFDictionaryRef *info) const
{
StLock<Mutex> _(mValidityCheckLock);
return SecCodeCopySigningInformation(currentGuest(), flags, info);
}
OSStatus ClientIdentification::checkValidity(SecCSFlags flags,
SecRequirementRef requirement) const
{
StLock<Mutex> _(mValidityCheckLock);
return SecCodeCheckValidityWithErrors(currentGuest(), flags, requirement, NULL);
}
bool ClientIdentification::checkAppleSigned() const
{
static CFStringRef const requirementString = CFSTR("(anchor apple) or (anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9])");
CFRef<SecRequirementRef> secRequirementRef = NULL;
OSStatus status = SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &secRequirementRef.aref());
if (status == errSecSuccess) {
status = checkValidity(kSecCSDefaultFlags, secRequirementRef);
if (status != errSecSuccess) {
secnotice("clientid", "code requirement check failed (%d), client is not Apple-signed", (int32_t)status);
} else {
return true;
}
}
return false;
}
bool ClientIdentification::hasEntitlement(const char *name) const
{
CFRef<CFDictionaryRef> info;
{
StLock<Mutex> _(mValidityCheckLock);
MacOSError::check(SecCodeCopySigningInformation(processCode(), kSecCSDefaultFlags, &info.aref()));
}
CFCopyRef<CFDictionaryRef> entitlements = (CFDictionaryRef)CFDictionaryGetValue(info, kSecCodeInfoEntitlementsDict);
if (entitlements && entitlements.is<CFDictionaryRef>()) {
CFTypeRef value = CFDictionaryGetValue(entitlements, CFTempString(name));
if (value && value != kCFBooleanFalse)
return true; }
return false;
}
std::string codePath(SecStaticCodeRef code)
{
CFRef<CFURLRef> path;
MacOSError::check(SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()));
return cfString(path);
}
#if defined(DEBUGDUMP)
static void dumpCode(SecCodeRef code)
{
CFRef<CFURLRef> path;
if (OSStatus rc = SecCodeCopyPath(code, kSecCSDefaultFlags, &path.aref()))
Debug::dump("unknown(rc=%d)", int32_t(rc));
else
Debug::dump("%s", cfString(path).c_str());
}
void ClientIdentification::dump()
{
Debug::dump(" client=");
dumpCode(mClientProcess);
for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) {
Debug::dump(" guest(0x%x)=", it->first);
dumpCode(it->second.code);
if (it->second.gotHash)
Debug::dump(" [got hash]");
}
}
#endif //DEBUGDUMP