#include "process.h"
#include "server.h"
#include "session.h"
#include "authority.h"
#include "flippers.h"
Process::Process(Port servicePort, TaskPort taskPort,
const ClientSetupInfo *info, const char *identity, uid_t uid, gid_t gid)
: session(Session::find(servicePort)), mBusyCount(0), mDying(false),
mTaskPort(taskPort), mByteFlipped(false), mUid(uid), mGid(gid),
mClientIdent(deferred)
{
assert(info);
uint32 pversion = info->version;
if (pversion == SSPROTOVERSION) {
} else {
Flippers::flip(pversion);
if (pversion == SSPROTOVERSION) {
mByteFlipped = true;
} else {
CssmError::throwMe(CSSM_ERRCODE_INCOMPATIBLE_VERSION);
}
}
mPid = mTaskPort.pid();
session.addProcess(this);
secdebug("SS", "New process %p(%d) uid=%d gid=%d session=%p TP=%d %sfor %s",
this, mPid, mUid, mGid, &session,
mTaskPort.port(),
mByteFlipped ? "FLIP " : "",
(identity && identity[0]) ? identity : "(unknown)");
try {
mClientCode = CodeSigning::OSXCode::decode(identity);
} catch (...) {
secdebug("SS", "process %p(%d) identity decode threw exception", this, pid());
}
if (!mClientCode) {
mClientIdent = unknown; secdebug("SS", "process %p(%d) no clientCode - marked anonymous", this, pid());
}
}
Process::~Process()
{
assert(mBusyCount == 0);
IFDEBUG(if (!mAuthorizations.empty())
secdebug("SS", "Process %p(%d) clearing %d authorizations",
this, mPid, int(mAuthorizations.size())));
for (AuthorizationSet::iterator it = mAuthorizations.begin();
it != mAuthorizations.end(); ) {
AuthorizationToken *auth = *it;
while (++it != mAuthorizations.end() && *it == auth) ; if (auth->endProcess(*this))
delete auth;
}
IFDEBUG(if (!mDatabases.empty())
secdebug("SS", "Process %p(%d) clearing %d database handles",
this, mPid, int(mDatabases.size())));
for (DatabaseSet::iterator it = mDatabases.begin();
it != mDatabases.end(); it++)
delete *it;
secdebug("SS", "Process %p(%d) has died", this, mPid);
if (mTaskPort)
mTaskPort.destroy();
if (session.removeProcess(this))
delete &session;
}
bool Process::kill(bool keepTaskPort)
{
StLock<Mutex> _(mLock);
if (keepTaskPort)
mTaskPort = Port(); if (mBusyCount == 0) {
return true; } else {
secdebug("SS", "Process %p(%d) destruction deferred for %d busy connections",
this, mPid, int(mBusyCount));
mDying = true;
return false; }
}
void Process::beginConnection(Connection &)
{
StLock<Mutex> _(mLock);
mBusyCount++;
}
bool Process::endConnection(Connection &)
{
StLock<Mutex> _(mLock);
return --mBusyCount == 0 && mDying;
}
void Process::addDatabase(Database *database)
{
StLock<Mutex> _(mLock);
mDatabases.insert(database);
}
void Process::removeDatabase(Database *database)
{
StLock<Mutex> _(mLock);
assert(mDatabases.find(database) != mDatabases.end());
mDatabases.erase(database);
}
string Process::getPath() const
{
assert(mClientCode);
return mClientCode->canonicalPath();
}
const CssmData Process::getHash(CodeSigning::OSXSigner &signer) const
{
switch (mClientIdent) {
case deferred:
try {
mCachedSignature.reset(mClientCode->sign(signer));
assert(mCachedSignature.get());
mClientIdent = known;
secdebug("SS", "process %p(%d) code signature computed", this, pid());
break;
} catch (...) {
mClientIdent = unknown;
secdebug("SS", "process %p(%d) no code signature - anonymous", this, pid());
CssmError::throwMe(CSSM_ERRCODE_INSUFFICIENT_CLIENT_IDENTIFICATION);
}
case known:
assert(mCachedSignature.get());
break;
case unknown:
CssmError::throwMe(CSSM_ERRCODE_INSUFFICIENT_CLIENT_IDENTIFICATION);
}
return CssmData(*mCachedSignature);
}
void Process::addAuthorization(AuthorizationToken *auth)
{
assert(auth);
StLock<Mutex> _(mLock);
mAuthorizations.insert(auth);
auth->addProcess(*this);
}
void Process::checkAuthorization(AuthorizationToken *auth)
{
assert(auth);
StLock<Mutex> _(mLock);
if (mAuthorizations.find(auth) == mAuthorizations.end())
MacOSError::throwMe(errAuthorizationInvalidRef);
}
bool Process::removeAuthorization(AuthorizationToken *auth)
{
assert(auth);
StLock<Mutex> _(mLock);
typedef AuthorizationSet::iterator Iter;
Iter it = mAuthorizations.lower_bound(auth);
bool isLast;
if (it == mAuthorizations.end() || auth != *it) {
Syslog::error("process is missing authorization to remove"); isLast = true;
} else {
Iter next = it; ++next; isLast = (next == mAuthorizations.end()) || auth != *next;
mAuthorizations.erase(it); }
if (isLast) {
if (auth->endProcess(*this)) return true; }
return false; }
void Process::requestNotifications(Port port, Listener::Domain domain, Listener::EventMask events)
{
new Listener(*this, port, domain, events);
}
void Process::stopNotifications(Port port)
{
if (!Listener::remove(port))
CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); }
void Process::postNotification(Listener::Domain domain, Listener::Event event, const CssmData &data)
{
Listener::notify(domain, event, data);
}