#include "csproxy.h"
#include "server.h"
#include <Security/SecStaticCode.h>
#include <securityd_client/cshosting.h>
#include <security_utilities/cfmunge.h>
CodeSigningHost::CodeSigningHost()
: mLock(Mutex::recursive), mHostingState(noHosting)
{
}
CodeSigningHost::~CodeSigningHost()
{
reset();
}
void CodeSigningHost::reset()
{
StLock<Mutex> _(mLock);
switch (mHostingState) {
case noHosting:
break; case dynamicHosting:
mHostingPort.destroy();
mHostingPort = MACH_PORT_NULL;
SECURITYD_HOST_UNREGISTER(DTSELF);
break;
case proxyHosting:
Server::active().remove(*this); mHostingPort.destroy(); mHostingState = noHosting;
mHostingPort = MACH_PORT_NULL;
mGuests.erase(mGuests.begin(), mGuests.end());
SECURITYD_HOST_UNREGISTER(DTSELF);
break;
}
}
CodeSigningHost::Guest *CodeSigningHost::findHost(SecGuestRef hostRef)
{
Guest *host = findGuest(hostRef, true);
for (;;) {
if (Guest *guest = findGuest(host))
if (guest->dedicated) {
host = guest;
continue;
}
return host;
}
}
CodeSigningHost::Guest *CodeSigningHost::findGuest(SecGuestRef guestRef, bool hostOk )
{
GuestMap::iterator it = mGuests.find(guestRef);
if (it == mGuests.end())
if (hostOk)
return NULL;
else
MacOSError::throwMe(errSecCSNoSuchCode);
assert(it->first == it->second->guestRef());
return it->second;
}
CodeSigningHost::Guest *CodeSigningHost::findGuest(Guest *host, const CssmData &attrData)
{
CFRef<CFDictionaryRef> attrDict = attrData
? makeCFDictionaryFrom(attrData.data(), attrData.length())
: makeCFDictionary(0);
CFDictionary attrs(attrDict, errSecCSInvalidAttributeValues);
if (CFNumberRef canonical = attrs.get<CFNumberRef>(kSecGuestAttributeCanonical)) {
SecGuestRef guestRef = cfNumber<SecGuestRef>(canonical);
if (Guest *guest = findGuest(guestRef, true)) if (guest->isGuestOf(host, loose))
host = guest; else
MacOSError::throwMe(errSecCSNoSuchCode); else
MacOSError::throwMe(errSecCSNoSuchCode); }
CFIndex count = CFDictionaryGetCount(attrs);
CFTypeRef keys[count], values[count];
CFDictionaryGetKeysAndValues(attrs, keys, values);
for (;;) {
Guest *match = NULL; for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it)
if (it->second->isGuestOf(host, strict))
if (it->second->matches(count, keys, values))
if (match)
MacOSError::throwMe(errSecCSMultipleGuests); else
match = it->second;
if (!match) return host;
else
host = match; }
}
CodeSigningHost::Guest *CodeSigningHost::findGuest(Guest *host)
{
for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it)
if (it->second->isGuestOf(host, strict))
return it->second;
return NULL;
}
void CodeSigningHost::registerCodeSigning(mach_port_t hostingPort, SecCSFlags flags)
{
StLock<Mutex> _(mLock);
switch (mHostingState) {
case noHosting:
mHostingPort = hostingPort;
mHostingState = dynamicHosting;
SECURITYD_HOST_REGISTER(DTSELF, mHostingPort);
break;
default:
MacOSError::throwMe(errSecCSHostProtocolContradiction);
}
}
SecGuestRef CodeSigningHost::createGuest(SecGuestRef hostRef,
uint32_t status, const char *path,
const CssmData &cdhash, const CssmData &attributes, SecCSFlags flags)
{
StLock<Mutex> _(mLock);
if (path[0] != '/') MacOSError::throwMe(errSecCSHostProtocolRelativePath);
if (cdhash.length() > maxUcspHashLength)
MacOSError::throwMe(errSecCSHostProtocolInvalidHash);
switch (mHostingState) {
case noHosting: mHostingPort.allocate(); MachServer::Handler::port(mHostingPort); MachServer::active().add(*this); mHostingState = proxyHosting; SECURITYD_HOST_PROXY(DTSELF, mHostingPort);
break;
case proxyHosting: break;
case dynamicHosting: MacOSError::throwMe(errSecCSHostProtocolContradiction);
}
RefPointer<Guest> host = findHost(hostRef);
if (RefPointer<Guest> knownGuest = findGuest(host)) if (flags & kSecCSDedicatedHost)
MacOSError::throwMe(errSecCSHostProtocolDedicationError); else if (knownGuest->dedicated)
MacOSError::throwMe(errSecCSHostProtocolDedicationError);
RefPointer<Guest> guest = new Guest;
if (host)
guest->guestPath = host->guestPath;
guest->guestPath.push_back(guest->handle());
guest->status = status;
guest->path = path;
guest->setAttributes(attributes);
guest->setHash(cdhash, flags & kSecCSGenerateGuestHash);
guest->dedicated = (flags & kSecCSDedicatedHost);
mGuests[guest->guestRef()] = guest;
SECURITYD_GUEST_CREATE(DTSELF, hostRef, guest->guestRef(), guest->status, flags, guest->path.c_str());
if (SECURITYD_GUEST_CDHASH_ENABLED())
SECURITYD_GUEST_CDHASH(DTSELF, guest->guestRef(),
(void*)CFDataGetBytePtr(guest->cdhash), CFDataGetLength(guest->cdhash));
return guest->guestRef();
}
void CodeSigningHost::setGuestStatus(SecGuestRef guestRef, uint32_t status, const CssmData &attributes)
{
StLock<Mutex> _(mLock);
if (mHostingState != proxyHosting)
MacOSError::throwMe(errSecCSHostProtocolNotProxy);
Guest *guest = findGuest(guestRef);
if ((status & ~guest->status) & kSecCodeStatusValid)
MacOSError::throwMe(errSecCSHostProtocolStateError); if ((~status & guest->status) & (kSecCodeStatusHard | kSecCodeStatusKill))
MacOSError::throwMe(errSecCSHostProtocolStateError); guest->status = status;
SECURITYD_GUEST_CHANGE(DTSELF, guestRef, status);
if (attributes)
guest->setAttributes(attributes);
}
void CodeSigningHost::removeGuest(SecGuestRef hostRef, SecGuestRef guestRef)
{
StLock<Mutex> _(mLock);
if (mHostingState != proxyHosting)
MacOSError::throwMe(errSecCSHostProtocolNotProxy);
RefPointer<Guest> host = findHost(hostRef);
RefPointer<Guest> guest = findGuest(guestRef);
if (guest->dedicated) MacOSError::throwMe(errSecCSHostProtocolDedicationError);
if (!guest->isGuestOf(host, strict))
MacOSError::throwMe(errSecCSHostProtocolUnrelated);
for (GuestMap::iterator it = mGuests.begin(); it != mGuests.end(); ++it)
if (it->second->isGuestOf(guest, loose)) {
SECURITYD_GUEST_DESTROY(DTSELF, it->first);
mGuests.erase(it);
}
}
CodeSigningHost::Guest::~Guest()
{ }
void CodeSigningHost::Guest::setAttributes(const CssmData &attrData)
{
CFRef<CFNumberRef> guest = makeCFNumber(guestRef());
if (attrData) {
attributes.take(cfmake<CFDictionaryRef>("{+%O,%O=%O}",
makeCFDictionaryFrom(attrData.data(), attrData.length()), kSecGuestAttributeCanonical, guest.get()));
} else {
attributes.take(makeCFDictionary(1, kSecGuestAttributeCanonical, guest.get()));
}
}
CFDataRef CodeSigningHost::Guest::attrData() const
{
if (!mAttrData)
mAttrData = makeCFData(this->attributes.get());
return mAttrData;
}
void CodeSigningHost::Guest::setHash(const CssmData &given, bool generate)
{
if (given.length()) this->cdhash.take(makeCFData(given));
else if (CFTypeRef hash = CFDictionaryGetValue(this->attributes, kSecGuestAttributeHash))
if (CFGetTypeID(hash) == CFDataGetTypeID())
this->cdhash = CFDataRef(hash);
else
MacOSError::throwMe(errSecCSHostProtocolInvalidHash);
else if (generate) { CFRef<SecStaticCodeRef> code;
MacOSError::check(SecStaticCodeCreateWithPath(CFTempURL(this->path), kSecCSDefaultFlags, &code.aref()));
CFRef<CFDictionaryRef> info;
MacOSError::check(SecCodeCopySigningInformation(code, kSecCSDefaultFlags, &info.aref()));
this->cdhash = CFDataRef(CFDictionaryGetValue(info, kSecCodeInfoUnique));
}
}
bool CodeSigningHost::Guest::isGuestOf(Guest *host, GuestCheck check) const
{
vector<SecGuestRef> hostPath;
if (host)
hostPath = host->guestPath;
if (hostPath.size() <= guestPath.size()
&& !memcmp(&hostPath[0], &guestPath[0], sizeof(SecGuestRef) * hostPath.size()))
switch (check) {
case loose:
return true;
case strict:
return guestPath.size() == hostPath.size() + 1; }
return false;
}
bool CodeSigningHost::Guest::matches(CFIndex count, CFTypeRef keys[], CFTypeRef values[]) const
{
if (dedicated)
return true;
for (CFIndex n = 0; n < count; n++) {
CFStringRef key = CFStringRef(keys[n]);
if (CFEqual(key, kSecGuestAttributeCanonical)) continue;
if (CFTypeRef value = CFDictionaryGetValue(attributes, key))
if (CFEqual(value, values[n]))
return true;
}
return false;
}
struct CodeSigningHost::Lock : private StLock<Mutex> {
Lock(CodeSigningHost *host) : StLock<Mutex>(host->mLock) { }
};
boolean_t cshosting_server(mach_msg_header_t *, mach_msg_header_t *);
static ThreadNexus<CodeSigningHost *> context;
boolean_t CodeSigningHost::handle(mach_msg_header_t *in, mach_msg_header_t *out)
{
CodeSigningHost::Lock _(this);
context() = this;
return cshosting_server(in, out);
}
#define CSH_ARGS mach_port_t servicePort, mach_port_t replyPort, OSStatus *rcode
#define BEGIN_IPC try {
#define END_IPC *rcode = noErr; } \
catch (const CommonError &err) { *rcode = err.osStatus(); } \
catch (...) { *rcode = errSecCSInternalError; } \
return KERN_SUCCESS;
#define DATA_IN(base) void *base, mach_msg_type_number_t base##Length
#define DATA_OUT(base) void **base, mach_msg_type_number_t *base##Length
#define DATA(base) CssmData(base, base##Length)
kern_return_t cshosting_server_findGuest(CSH_ARGS, SecGuestRef hostRef,
DATA_IN(attributes),
GuestChain *foundGuest, mach_msg_type_number_t *depth, mach_port_t *subhost)
{
BEGIN_IPC
*subhost = MACH_PORT_NULL;
Process::Guest *host = context()->findGuest(hostRef, true);
if (Process::Guest *guest = context()->findGuest(host, DATA(attributes))) {
*foundGuest = &guest->guestPath[0];
*depth = guest->guestPath.size();
} else {
*foundGuest = NULL;
*depth = 0;
}
END_IPC
}
kern_return_t cshosting_server_identifyGuest(CSH_ARGS, SecGuestRef guestRef,
char *path, char *hash, uint32_t *hashLength, DATA_OUT(attributes))
{
BEGIN_IPC
CodeSigningHost::Guest *guest = context()->findGuest(guestRef);
strncpy(path, guest->path.c_str(), MAXPATHLEN);
if (guest->cdhash) {
*hashLength = CFDataGetLength(guest->cdhash);
assert(*hashLength <= maxUcspHashLength);
memcpy(hash, CFDataGetBytePtr(guest->cdhash), *hashLength);
} else
*hashLength = 0;
CFDataRef attrData = guest->attrData(); *attributes = (void *)CFDataGetBytePtr(attrData); *attributesLength = CFDataGetLength(attrData);
END_IPC
}
kern_return_t cshosting_server_guestStatus(CSH_ARGS, SecGuestRef guestRef, uint32_t *status)
{
BEGIN_IPC
*status = context()->findGuest(guestRef)->status;
END_IPC
}
#if defined(DEBUGDUMP)
void CodeSigningHost::dump() const
{
StLock<Mutex> _(mLock);
switch (mHostingState) {
case noHosting:
break;
case dynamicHosting:
Debug::dump(" dynamic host port=%d", mHostingPort.port());
break;
case proxyHosting:
Debug::dump(" proxy-host port=%d", mHostingPort.port());
if (!mGuests.empty()) {
Debug::dump(" %d guests={", int(mGuests.size()));
for (GuestMap::const_iterator it = mGuests.begin(); it != mGuests.end(); ++it) {
if (it != mGuests.begin())
Debug::dump(", ");
it->second->dump();
}
Debug::dump("}");
}
break;
}
}
void CodeSigningHost::Guest::dump() const
{
Debug::dump("%s[", path.c_str());
for (vector<SecGuestRef>::const_iterator it = guestPath.begin(); it != guestPath.end(); ++it) {
if (it != guestPath.begin())
Debug::dump("/");
Debug::dump("0x%x", *it);
}
Debug::dump("; status=0x%x attrs=%s]",
status, cfString(CFCopyDescription(attributes), true).c_str());
}
#endif //DEBUGDUMP