#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)
{
debug("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);
if (attribute(sessionHasGraphicAccess))
Database::lockAllDatabases();
}
void DynamicSession::release()
{
mBootstrap.destroy();
}
Session::~Session()
{
assert(mProcessCount == 0); debug("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
debug("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())
debug("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::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 RightSet &rights,
const AuthorizationEnvironment *environment,
AuthorizationFlags flags,
AuthorizationBlob &newHandle)
{
CredentialSet resultCreds;
auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds));
CredentialSet sessionCreds;
{
StLock<Mutex> _(mCredsLock);
sessionCreds = mSessionCreds;
}
OSStatus result = Server::authority().authorize(rights, environment, flags,
&sessionCreds, &resultCreds, NULL, *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;
if (flags & kAuthorizationFlagDestroyRights) {
for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
if ((*it)->isShared())
(*it)->invalidate();
}
if (Server::connection().process.removeAuthorization(&auth))
deleter.remove();
}
OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
const RightSet &rights, const AuthorizationEnvironment *environment,
AuthorizationFlags flags,
MutableRightSet &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);
}
IFDEBUG(debug("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,
AuthorizationItemSet *&contextInfo)
{
StLock<Mutex> _(mLock);
AuthorizationToken &auth = authorization(authBlob);
debug("SSauth", "Authorization %p get-info", &auth);
if (tag) { return errAuthorizationInvalidTag;
} else { contextInfo = &auth.infoSet();
return noErr;
}
}
OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
AuthorizationExternalForm &extForm)
{
StLock<Mutex> _(mLock);
const AuthorizationToken &auth = authorization(authBlob);
if (auth.mayExternalize(Server::connection().process)) {
memset(&extForm, 0, sizeof(extForm));
AuthorizationExternalBlob &extBlob =
reinterpret_cast<AuthorizationExternalBlob &>(extForm);
extBlob.blob = auth.handle();
extBlob.session = bootstrapPort();
debug("SSauth", "Authorization %p externalized", &auth);
return noErr;
} else
return errAuthorizationExternalizeNotAllowed;
}
OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
AuthorizationBlob &authBlob)
{
StLock<Mutex> _(mLock);
const AuthorizationExternalBlob &extBlob =
reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
if (sourceAuth.mayInternalize(Server::connection().process, true)) {
authBlob = extBlob.blob;
Server::connection().process.addAuthorization(&sourceAuth);
mAuthCount++;
debug("SSauth", "Authorization %p internalized", &sourceAuth);
return noErr;
} else
return errAuthorizationInternalizeNotAllowed;
}
void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs)
{
Process *process = &Server::connection().process;
#if 0
if (process->taskPort().bootstrap() != process->session.bootstrapPort())
process = Server::active().resetConnection();
#endif
process->session.setupAttributes(attrs);
}
void Session::setupAttributes(SessionAttributeBits attrs)
{
debug("SSsession", "%p setup attrs=0x%lx", this, attrs);
if (attrs & ~settableAttributes)
MacOSError::throwMe(errSessionInvalidAttributes);
if (attribute(sessionWasInitialized))
MacOSError::throwMe(errSessionAuthorizationDenied);
setAttributes(attrs | sessionWasInitialized);
}
void Session::mergeCredentials(CredentialSet &creds)
{
debug("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)
{
return AuthorizationToken::find(blob);
}