#include <pwd.h>
#include <signal.h> // SIGTERM
#include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
#include "session.h"
#include "connection.h"
#include "database.h"
#include "server.h"
#include <security_utilities/logging.h>
PortMap<Session> Session::mSessions;
std::string Session::kUsername = "username";
std::string Session::kRealname = "realname";
Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs)
: mBootstrap(bootstrap), mServicePort(servicePort),
mAttributes(attrs), mSecurityAgent(NULL), mAuthHost(NULL)
{
secdebug("SSsession", "%p CREATED: handle=%#x bootstrap=%d service=%d attrs=%#x",
this, handle(), mBootstrap.port(), mServicePort.port(), uint32_t(mAttributes));
SECURITYD_SESSION_CREATE(this, attrs, servicePort);
Syslog::notice("Session 0x%lx created", this->handle());
}
Session::~Session()
{
secdebug("SSsession", "%p DESTROYED: handle=%#x bootstrap=%d",
this, handle(), mBootstrap.port());
Syslog::notice("Session 0x%lx destroyed", this->handle());
}
Session &Session::find(Port servicePort)
{
StLock<Mutex> _(mSessions);
PortMap<Session>::const_iterator it = mSessions.find(servicePort);
assert(it != mSessions.end());
return *it->second;
}
Session &Session::find(SecuritySessionId id)
{
switch (id) {
case callerSecuritySession:
return Server::session();
default:
try {
return U32HandleObject::find<Session>(id, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
} catch (const CommonError &err) {
Syslog::warning("Session::find(%#x) failed rcode=%d", id, err.osStatus());
for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); ++it)
Syslog::notice(" Valid sessions include %#x attrs=%#x",
it->second->handle(), it->second->attributes());
throw;
}
}
}
void Session::destroy(Port servPort)
{
StLock<Mutex> _(mSessions);
PortMap<Session>::iterator it = mSessions.find(servPort);
assert(it != mSessions.end());
RefPointer<Session> session = it->second;
SECURITYD_SESSION_DESTROY(session);
Syslog::notice("Session 0x%lx dead", session->handle());
mSessions.erase(it);
session->kill();
}
void Session::kill()
{
StLock<Mutex> _(*this);
invalidateSessionAuthHosts();
{
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();
}
PerSession::kill();
}
void Session::invalidateSessionAuthHosts()
{
StLock<Mutex> _(mAuthHostLock);
Syslog::warning("Killing auth hosts");
if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
if (mAuthHost) mAuthHost->UnixPlusPlus::Child::kill(SIGTERM);
mSecurityAgent = NULL;
mAuthHost = NULL;
}
void Session::invalidateAuthHosts()
{
StLock<Mutex> _(mSessions);
for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
it->second->invalidateSessionAuthHosts();
}
void Session::processSystemSleep()
{
StLock<Mutex> _(mSessions);
for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
it->second->allReferences(&DbCommon::sleepProcessing);
}
void Session::processLockAll()
{
allReferences(&DbCommon::lockProcessing);
}
RootSession::RootSession(Server &server, SessionAttributeBits attrs)
: Session(Bootstrap(), server.primaryServicePort(),
sessionIsRoot | sessionWasInitialized | attrs)
{
parent(server); ref();
mSessions[mServicePort] = this;
}
DynamicSession::DynamicSession(TaskPort taskPort)
: ReceivePort(Server::active().bootstrapName(), taskPort.bootstrap(), false),
Session(taskPort.bootstrap(), *this),
mOriginatorTask(taskPort), mHaveOriginatorUid(false)
{
parent(Server::active());
Server::active().add(*this);
Server::active().notifyIfDead(bootstrapPort()); Server::active().notifyIfUnused(*this);
StLock<Mutex> _(mSessions);
assert(!mSessions[*this]); mSessions[*this] = this;
secdebug("SSsession", "%p dynamic session originator=%d (pid=%d)",
this, mOriginatorTask.port(), taskPort.pid());
}
DynamicSession::~DynamicSession()
{
Server::active().remove(*this);
}
void DynamicSession::kill()
{
StLock<Mutex> _(*this);
mBootstrap.destroy(); Session::kill(); }
void DynamicSession::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
{
StLock<Mutex> _(*this);
SECURITYD_SESSION_SETATTR(this, attrs);
Syslog::notice("Session 0x%lx attributes 0x%x", this->handle(), attrs);
secdebug("SSsession", "%p setup flags=%#x attrs=%#x", this, uint32_t(flags), uint32_t(attrs));
if (attrs & ~settableAttributes)
MacOSError::throwMe(errSessionInvalidAttributes);
checkOriginator();
if (attribute(sessionWasInitialized))
MacOSError::throwMe(errSessionAuthorizationDenied);
setAttributes(attrs | sessionWasInitialized);
}
void DynamicSession::checkOriginator()
{
if (mOriginatorTask != Server::process().taskPort())
MacOSError::throwMe(errSessionAuthorizationDenied);
}
uid_t DynamicSession::originatorUid() const
{
if (mHaveOriginatorUid)
return mOriginatorUid;
else
MacOSError::throwMe(errSessionValueNotSet);
}
void DynamicSession::originatorUid(uid_t uid)
{
checkOriginator();
if (mHaveOriginatorUid) MacOSError::throwMe(errSessionAuthorizationDenied);
mHaveOriginatorUid = true;
mOriginatorUid = uid;
Server::active().longTermActivity();
struct passwd *pw = getpwuid(uid);
if (pw != NULL) {
mOriginatorCredential = Credential(uid, pw->pw_name ? pw->pw_name : "", pw->pw_gecos ? pw->pw_gecos : "", "", true);
endpwent();
}
secdebug("SSsession", "%p session uid set to %d", this, uid);
}
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, (flags&kAuthorizationFlagLeastPrivileged)));
SECURITYD_AUTH_CREATE(this, auth.get());
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::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::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)
{
AuthorizationToken &auth = authorization(authBlob);
return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
}
OSStatus Session::authGetRights(AuthorizationToken &auth,
const AuthItemSet &rights, const AuthItemSet &environment,
AuthorizationFlags flags,
AuthItemSet &grantedRights)
{
CredentialSet resultCreds;
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",
&auth, 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> _(*this);
if (auth.mayExternalize(Server::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::process(), true)) {
StLock<Mutex> _(*this);
authBlob = extBlob.blob;
Server::process().addAuthorization(&sourceAuth);
secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
return noErr;
} else
return errAuthorizationInternalizeNotAllowed;
}
void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
{
MacOSError::throwMe(errSessionAuthorizationDenied);
}
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=%d)",
&authorization(authBlob), inRightName, int32_t(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=%d)",
&authorization(authBlob), inRightName, int32_t(result));
return result;
}
void Session::mergeCredentials(CredentialSet &creds)
{
secdebug("SSsession", "%p merge creds @%p", this, &creds);
CredentialSet updatedCredentials = 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);
updatedCredentials.erase(*it);
updatedCredentials.insert(*old);
}
}
creds.swap(updatedCredentials);
}
AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
{
AuthorizationToken &auth = AuthorizationToken::find(blob);
Server::process().checkAuthorization(&auth);
return auth;
}
OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
{
AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
AuthorizationItemSet rightItemSet = { 1, &rightItem };
AuthItemSet rightAuthItemSet(&rightItemSet);
AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
if (true == allowUI)
flags |= kAuthorizationFlagInteractionAllowed;
AuthorizationBlob dummyHandle;
const audit_token_t *at = connection.auditToken();
return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
}
bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
{
bool isAuthorized = false;
try {
OSStatus status = authCheckRight(rightName, connection, allowUI);
if (errAuthorizationSuccess == status)
isAuthorized = true;
}
catch (...) {
}
return isAuthorized;
}
RefPointer<AuthHostInstance>
Session::authhost(const AuthHostType hostType, const bool restart)
{
StLock<Mutex> _(mAuthHostLock);
if (hostType == privilegedAuthHost)
{
if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
{
if (mAuthHost)
PerSession::kill(*mAuthHost);
mAuthHost = new AuthHostInstance(*this, hostType);
}
return mAuthHost;
}
else
{
if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
{
if (mSecurityAgent)
PerSession::kill(*mSecurityAgent);
mSecurityAgent = new AuthHostInstance(*this, hostType);
}
return mSecurityAgent;
}
}
void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
{
checkOriginator();
if (Server::process().uid() != 0)
MacOSError::throwMe(errSessionAuthorizationDenied);
StLock<Mutex> _(*this);
mSessionAgentPrefs = userPrefsDict;
}
CFDataRef DynamicSession::copyUserPrefs()
{
StLock<Mutex> _(*this);
if (mSessionAgentPrefs)
CFRetain(mSessionAgentPrefs);
return mSessionAgentPrefs;
}
#if defined(DEBUGDUMP)
void Session::dumpNode()
{
PerSession::dumpNode();
Debug::dump(" boot=%d service=%d attrs=%#x authhost=%p securityagent=%p",
mBootstrap.port(), mServicePort.port(), uint32_t(mAttributes), mAuthHost, mSecurityAgent);
}
#endif //DEBUGDUMP