#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>
#include <agentquery.h>
using namespace CommonCriteria;
Session::SessionMap Session::mSessions;
Mutex Session::mSessionLock(Mutex::recursive);
const char Session::kUsername[] = "username";
const char Session::kRealname[] = "realname";
Session::Session(const AuditInfo &audit, Server &server)
: mAudit(audit), mSecurityAgent(NULL), mKeybagState(0)
{
parent(server);
StLock<Mutex> _(mSessionLock);
assert(!mSessions[audit.sessionId()]);
mSessions[audit.sessionId()] = this;
secnotice("SS", "%p Session %d created, uid:%d sessionId:%d", this, this->sessionId(), mAudit.uid(), mAudit.sessionId());
Syslog::notice("Session %d created", this->sessionId());
}
Session::~Session()
{
secnotice("SS", "%p Session %d destroyed", this, this->sessionId());
Syslog::notice("Session %d destroyed", this->sessionId());
}
Server &Session::server() const
{
return parent<Server>();
}
Session &Session::find(pid_t id, bool create)
{
if (id == (pid_t)callerSecuritySession)
return Server::session();
StLock<Mutex> _(mSessionLock);
SessionMap::iterator it = mSessions.find(id);
if (it != mSessions.end())
return *it->second;
if (!create)
CssmError::throwMe(errSessionInvalidId);
AuditInfo info;
info.get(id);
assert(info.sessionId() == id);
RefPointer<Session> session = new Session(info, Server::active());
mSessions.insert(make_pair(id, session));
return *session;
}
void Session::destroy(SessionId id)
{
RefPointer<Session> session = NULL;
{
StLock<Mutex> _(mSessionLock);
SessionMap::iterator it = mSessions.find(id);
if (it != mSessions.end()) {
session = it->second;
assert(session->sessionId() == id);
mSessions.erase(it);
}
}
if (session.get()) {
session->kill();
}
}
void Session::kill()
{
StLock<Mutex> _(*this); secnotice("SS", "%p killing session %d", this, this->sessionId());
invalidateSessionAuthHosts();
PerSession::kill();
}
void Session::updateAudit() const
{
CommonCriteria::AuditInfo info;
try {
info.get(mAudit.sessionId());
} catch (...) {
return;
}
mAudit = info;
}
void Session::verifyKeyStorePassphrase(int32_t retries, bool useForACLFallback, const char *itemname)
{
QueryKeybagPassphrase keybagQuery(*this, retries);
keybagQuery.inferHints(Server::process());
if (useForACLFallback) {
keybagQuery.addHint("acl-fallback", &useForACLFallback, sizeof(useForACLFallback));
keybagQuery.addHint("keychain-item-name", itemname, itemname ? (uint32_t)strlen(itemname) : 0, 0);
}
if (keybagQuery.query() != SecurityAgent::noReason) {
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
}
void Session::changeKeyStorePassphrase()
{
service_context_t context = get_current_service_context();
QueryKeybagNewPassphrase keybagQuery(*this);
keybagQuery.inferHints(Server::process());
CssmAutoData pass(Allocator::standard(Allocator::sensitive));
CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
if (queryReason == SecurityAgent::noReason) {
service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
} else {
CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
}
}
void Session::resetKeyStorePassphrase(const CssmData &passphrase)
{
service_context_t context = get_current_service_context();
service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length());
}
service_context_t Session::get_current_service_context()
{
service_context_t context = { sessionId(), originatorUid(), *Server::connection().auditToken() };
return context;
}
void Session::keybagClearState(int state)
{
mKeybagState &= ~state;
}
void Session::keybagSetState(int state)
{
mKeybagState |= state;
}
bool Session::keybagGetState(int state)
{
return mKeybagState & state;
}
void Session::invalidateSessionAuthHosts()
{
StLock<Mutex> _(mAuthHostLock);
Syslog::warning("Killing auth hosts");
if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
mSecurityAgent = NULL;
}
void Session::invalidateAuthHosts()
{
StLock<Mutex> _(mSessionLock);
for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
it->second->invalidateSessionAuthHosts();
}
void Session::processSystemSleep()
{
SecurityAgentXPCQuery::killAllXPCClients();
StLock<Mutex> _(mSessionLock);
for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
it->second->allReferences(&DbCommon::sleepProcessing);
}
void Session::processLockAll()
{
allReferences(&DbCommon::lockProcessing);
}
RootSession::RootSession(uint64_t attributes, Server &server)
: Session(AuditInfo::current(), server)
{
ref(); mAudit.ai_flags |= attributes; }
void Session::setAttributes(SessionAttributeBits bits)
{
StLock<Mutex> _(*this);
updateAudit();
mAudit.ai_flags = bits;
mAudit.set();
}
void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
{
MacOSError::throwMe(errSessionAuthorizationDenied);
}
uid_t Session::originatorUid()
{
if (mAudit.uid() == AU_DEFAUDITID) {
StLock<Mutex> _(*this);
updateAudit();
}
return mAudit.uid();
}
RefPointer<AuthHostInstance>
Session::authhost(const bool restart)
{
StLock<Mutex> _(mAuthHostLock);
if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
{
if (mSecurityAgent)
PerSession::kill(*mSecurityAgent);
mSecurityAgent = new AuthHostInstance(*this);
}
return mSecurityAgent;
}
#if defined(DEBUGDUMP)
void Session::dumpNode()
{
PerSession::dumpNode();
Debug::dump(" auid=%d attrs=%#x securityagent=%p",
this->sessionId(), uint32_t(this->attributes()), mSecurityAgent);
}
#endif //DEBUGDUMP