#include "session.h"
#include "connection.h"
#include "server.h"
Session::SessionMap Session::sessionMap;
Mutex Session::sessionMapLock;
Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
: mBootstrap(bootstrap), mServicePort(servicePort),
mAttributes(attrs), mProcessCount(0), mAuthCount(0), mDying(false)
{
secdebug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
}
void Session::release()
{
}
RootSession::RootSession(Port servicePort, SessionAttributeBits attrs)
: Session(Bootstrap(), servicePort, sessionIsRoot | sessionWasInitialized | attrs)
{
sessionMap[mServicePort] = this;
}
DynamicSession::DynamicSession(const Bootstrap &bootstrap)
: ReceivePort(Server::active().bootstrapName(), bootstrap),
Session(bootstrap, *this)
{
Server::active().add(*this);
Server::active().notifyIfDead(bootstrapPort()); Server::active().notifyIfUnused(*this);
StLock<Mutex> _(sessionMapLock);
sessionMap[*this] = this;
}
DynamicSession::~DynamicSession()
{
Server::active().remove(*this);
secdebug("session", "%p Locking all %ld databases",
this, databases().size());
Database::lockAllDatabases(databases());
}
void DynamicSession::release()
{
mBootstrap.destroy();
}
Session::~Session()
{
assert(mProcessCount == 0); secdebug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
this, handle(), mBootstrap.port());
}
Session &Session::find(Port servicePort)
{
StLock<Mutex> _(sessionMapLock);
SessionMap::const_iterator it = sessionMap.find(servicePort);
assert(it != sessionMap.end());
return *it->second;
}
Session &Session::find(SecuritySessionId id)
{
switch (id) {
case callerSecuritySession:
return Server::connection().process.session;
default:
return findHandle<Session>(id);
}
}
void Session::eliminate(Port servPort)
{
StLock<Mutex> _(sessionMapLock);
SessionMap::iterator it = sessionMap.find(servPort);
assert(it != sessionMap.end());
Session *session = it->second;
sessionMap.erase(it);
session->release();
if (session->clearResources())
delete session;
else
secdebug("SSsession", "session %p zombified for %d processes and %d auths",
session, int(session->mProcessCount), int(session->mAuthCount));
}
bool Session::clearResources()
{
StLock<Mutex> _(mLock);
mDying = true;
{
StLock<Mutex> _(mCredsLock);
IFDEBUG(if (!mSessionCreds.empty())
secdebug("SSauth", "session %p clearing %d shared credentials",
this, int(mSessionCreds.size())));
for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
(*it)->invalidate();
}
return mProcessCount == 0 && mAuthCount == 0;
}
void Session::lockAllDatabases(bool forSleep)
{
StLock<Mutex> _(sessionMapLock);
for (SessionMap::const_iterator it = begin(); it != end(); it++) {
secdebug("SSdb", "locking all %d known databases %s in session %p",
int(it->second->databases().size()), forSleep ? " for sleep" : "", it->second);
Database::lockAllDatabases(it->second->databases(), forSleep);
}
}
void Session::addProcess(Process *)
{
StLock<Mutex> _(mLock);
mProcessCount++;
}
bool Session::removeProcess(Process *)
{
StLock<Mutex> _(mLock);
assert(mProcessCount > 0);
return --mProcessCount == 0 && mDying && mAuthCount == 0;
}
void Session::addAuthorization(AuthorizationToken *)
{
StLock<Mutex> _(mLock);
mAuthCount++;
}
bool Session::removeAuthorization(AuthorizationToken *)
{
StLock<Mutex> _(mLock);
assert(mAuthCount > 0);
return --mAuthCount == 0 && mDying && mProcessCount == 0;
}
OSStatus Session::authCreate(const AuthItemSet &rights,
const AuthItemSet &environment,
AuthorizationFlags flags,
AuthorizationBlob &newHandle,
const audit_token_t &auditToken)
{
CredentialSet resultCreds;
auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken));
CredentialSet sessionCreds;
{
StLock<Mutex> _(mCredsLock);
sessionCreds = mSessionCreds;
}
AuthItemSet outRights;
OSStatus result = Server::authority().authorize(rights, environment, flags,
&sessionCreds, &resultCreds, outRights, *auth);
newHandle = auth->handle();
if ((flags & kAuthorizationFlagExtendRights) &&
!(flags & kAuthorizationFlagDestroyRights))
{
StLock<Mutex> _(mCredsLock);
mergeCredentials(resultCreds);
auth->mergeCredentials(resultCreds);
}
Server::connection().process.addAuthorization(auth.get());
auth.release();
return result;
}
void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
{
AuthorizationToken::Deleter deleter(authBlob);
AuthorizationToken &auth = deleter;
Process &process = Server::connection().process;
process.checkAuthorization(&auth);
if (flags & kAuthorizationFlagDestroyRights) {
for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
if ((*it)->isShared())
(*it)->invalidate();
}
if (process.removeAuthorization(&auth))
deleter.remove();
}
OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
const AuthItemSet &rights, const AuthItemSet &environment,
AuthorizationFlags flags,
AuthItemSet &grantedRights)
{
CredentialSet resultCreds;
AuthorizationToken &auth = authorization(authBlob);
CredentialSet effective;
{
StLock<Mutex> _(mCredsLock);
effective = auth.effectiveCreds();
}
OSStatus result = Server::authority().authorize(rights, environment, flags,
&effective, &resultCreds, grantedRights, auth);
if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
{
StLock<Mutex> _(mCredsLock);
mergeCredentials(resultCreds);
auth.mergeCredentials(resultCreds);
}
secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
&authorization(authBlob), int(rights.size()), int(grantedRights.size()));
return result;
}
OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
const char *tag,
AuthItemSet &contextInfo)
{
AuthorizationToken &auth = authorization(authBlob);
secdebug("SSauth", "Authorization %p get-info", &auth);
contextInfo = auth.infoSet(tag);
return noErr;
}
OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
AuthorizationExternalForm &extForm)
{
const AuthorizationToken &auth = authorization(authBlob);
StLock<Mutex> _(mLock);
if (auth.mayExternalize(Server::connection().process)) {
memset(&extForm, 0, sizeof(extForm));
AuthorizationExternalBlob &extBlob =
reinterpret_cast<AuthorizationExternalBlob &>(extForm);
extBlob.blob = auth.handle();
extBlob.session = bootstrapPort();
secdebug("SSauth", "Authorization %p externalized", &auth);
return noErr;
} else
return errAuthorizationExternalizeNotAllowed;
}
OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
AuthorizationBlob &authBlob)
{
const AuthorizationExternalBlob &extBlob =
reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
if (sourceAuth.mayInternalize(Server::connection().process, true)) {
StLock<Mutex> _(mLock);
authBlob = extBlob.blob;
Server::connection().process.addAuthorization(&sourceAuth);
mAuthCount++;
secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
return noErr;
} else
return errAuthorizationInternalizeNotAllowed;
}
void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs)
{
Process *process = &Server::connection().process;
process->session.setupAttributes(attrs);
}
void Session::setupAttributes(SessionAttributeBits attrs)
{
secdebug("SSsession", "%p setup attrs=0x%lx", this, attrs);
if (attrs & ~settableAttributes)
MacOSError::throwMe(errSessionInvalidAttributes);
if (attribute(sessionWasInitialized))
MacOSError::throwMe(errSessionAuthorizationDenied);
setAttributes(attrs | sessionWasInitialized);
}
OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
{
string rightName(inRightName);
return Server::authority().getRule(rightName, rightDict);
}
OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
{
CredentialSet resultCreds;
AuthorizationToken &auth = authorization(authBlob);
CredentialSet effective;
{
StLock<Mutex> _(mCredsLock);
effective = auth.effectiveCreds();
}
OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
{
StLock<Mutex> _(mCredsLock);
mergeCredentials(resultCreds);
auth.mergeCredentials(resultCreds);
}
secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%ld)",
&authorization(authBlob), inRightName, result);
return result;
}
OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
{
CredentialSet resultCreds;
AuthorizationToken &auth = authorization(authBlob);
CredentialSet effective;
{
StLock<Mutex> _(mCredsLock);
effective = auth.effectiveCreds();
}
OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
{
StLock<Mutex> _(mCredsLock);
mergeCredentials(resultCreds);
auth.mergeCredentials(resultCreds);
}
secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%ld)",
&authorization(authBlob), inRightName, result);
return result;
}
void Session::mergeCredentials(CredentialSet &creds)
{
secdebug("SSsession", "%p merge creds @%p", this, &creds);
for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
if (((*it)->isShared() && (*it)->isValid())) {
CredentialSet::iterator old = mSessionCreds.find(*it);
if (old == mSessionCreds.end()) {
mSessionCreds.insert(*it);
} else {
(*old)->merge(**it);
creds.erase(it);
creds.insert(*old);
}
}
}
AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
{
AuthorizationToken &auth = AuthorizationToken::find(blob);
Server::connection().process.checkAuthorization(&auth);
return auth;
}