#include <securityd_client/ucsp.h> // MIG ucsp service
#include "self.h" // MIG self service
#include <security_utilities/logging.h>
#include <security_cdsa_client/mdsclient.h>
#include "server.h"
#include "session.h"
#include "acls.h"
#include "notifications.h"
#include "child.h"
#include <mach/mach_error.h>
#include <security_utilities/ccaudit.h>
#include <security_utilities/casts.h>
#include "pcscmonitor.h"
#include "agentquery.h"
using namespace MachPlusPlus;
Server::Server(CodeSignatures &signatures, const char *bootstrapName)
: MachServer(bootstrapName),
mBootstrapName(bootstrapName),
mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
mCodeSignatures(signatures),
mVerbosity(0),
mWaitForClients(true), mShuttingDown(false)
{
ref();
add(sleepWatcher);
}
Server::~Server()
{
}
Connection &Server::connection(mach_port_t port, audit_token_t &auditToken)
{
Server &server = active();
StLock<Mutex> _(server);
Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
conn->process().checkSession(auditToken);
active().mCurrentConnection() = conn;
conn->beginWork(auditToken);
return *conn;
}
Connection &Server::connection(bool tolerant)
{
Connection *conn = active().mCurrentConnection();
assert(conn); if (!tolerant)
conn->checkWork();
return *conn;
}
void Server::requestComplete(CSSM_RETURN &rcode)
{
if (RefPointer<Connection> &conn = active().mCurrentConnection()) {
conn->endWork(rcode);
conn = NULL;
}
IFDUMPING("state", NodeCore::dumpAll());
}
Process &Server::process()
{
return connection().process();
}
Session &Server::session()
{
return connection().process().session();
}
RefPointer<Key> Server::key(KeyHandle key)
{
return U32HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
}
RefPointer<Database> Server::database(DbHandle db)
{
return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
}
RefPointer<KeychainDatabase> Server::keychain(DbHandle db)
{
return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
}
RefPointer<Database> Server::optionalDatabase(DbHandle db, bool persistent)
{
if (persistent && db != noDb)
return database(db);
else
return &process().localStore();
}
AclSource &Server::aclBearer(AclKind kind, U32HandleObject::Handle handle)
{
AclSource &bearer = U32HandleObject::find<AclSource>(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
if (kind != bearer.acl().aclKind())
CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE);
return bearer;
}
void Server::run()
{
MachServer::run(0x10000,
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT));
}
void Server::threadLimitReached(UInt32 limit)
{
Syslog::notice("securityd has reached its thread limit (%d) - service deadlock is possible",
(uint32_t) limit);
}
boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *);
boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *);
boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
{
return ucsp_server(in, out) || self_server(in, out);
}
void Server::setupConnection(ConnectLevel type, Port replyPort, Port taskPort,
const audit_token_t &auditToken, const ClientSetupInfo *info)
{
Security::CommonCriteria::AuditToken audit(auditToken);
StLock<Mutex> _(*this);
RefPointer<Process> &proc = mProcesses[taskPort];
if (proc && proc->session().sessionId() != audit.sessionId())
proc->changeSession(audit.sessionId());
if (proc && type == connectNewProcess) {
assert(info);
proc->reset(taskPort, info, audit);
proc->changeSession(audit.sessionId());
}
if (!proc) {
if (type == connectNewThread) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
assert(info);
proc = new Process(taskPort, info, audit);
notifyIfDead(taskPort);
mPids[proc->pid()] = proc;
}
Connection *connection = new Connection(*proc, replyPort);
if (mConnections.contains(replyPort)) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); mConnections[replyPort] = connection;
notifyIfDead(replyPort);
}
void Server::endConnection(Port replyPort)
{
StLock<Mutex> _(*this);
PortMap<Connection>::iterator it = mConnections.find(replyPort);
assert(it != mConnections.end());
it->second->terminate();
mConnections.erase(it);
}
void Server::notifyDeadName(Port port)
{
StLock<Mutex> serverLock(*this);
PortMap<Connection>::iterator conIt = mConnections.find(port);
if (conIt != mConnections.end()) {
secinfo("SS", "%p dead connection %d", this, port.port());
RefPointer<Connection> con = conIt->second;
mConnections.erase(conIt);
serverLock.unlock();
con->abort();
return;
}
PortMap<Process>::iterator procIt = mProcesses.find(port);
if (procIt != mProcesses.end()) {
secinfo("SS", "%p dead process %d", this, port.port());
RefPointer<Process> proc = procIt->second;
mPids.erase(proc->pid());
mProcesses.erase(procIt);
serverLock.unlock();
StLock<MachServer, &Server::busy, &Server::idle> _(*this);
proc->kill();
return;
}
secnotice("server", "spurious dead port notification for port %d", port.port());
}
void Server::notifyNoSenders(Port port, mach_port_mscount_t)
{
secinfo("SS", "%p dead session %d", this, port.port());
}
kern_return_t self_server_handleSignal(mach_port_t sport,
mach_port_t taskPort, int sig)
{
try {
secnotice("SS", "signal handled %d", sig);
if (taskPort != mach_task_self()) {
Syslog::error("handleSignal: received from someone other than myself");
return KERN_SUCCESS;
}
switch (sig) {
case SIGCHLD:
ServerChild::checkChildren();
break;
case SIGINT:
secnotice("SS", "shutdown due to SIGINT");
Syslog::notice("securityd terminated due to SIGINT");
_exit(0);
case SIGTERM:
Server::active().beginShutdown();
break;
case SIGPIPE:
fprintf(stderr, "securityd ignoring SIGPIPE received");
break;
#if defined(DEBUGDUMP)
case SIGUSR1:
NodeCore::dumpAll();
break;
#endif //DEBUGDUMP
case SIGUSR2:
{
extern PCSCMonitor *gPCSC;
gPCSC->startSoftTokens();
break;
}
default:
assert(false);
}
} catch(...) {
secnotice("SS", "exception handling a signal (ignored)");
}
mach_port_deallocate(mach_task_self(), taskPort);
return KERN_SUCCESS;
}
kern_return_t self_server_handleSession(mach_port_t sport,
mach_port_t taskPort, uint32_t event, uint64_t ident)
{
try {
if (taskPort != mach_task_self()) {
Syslog::error("handleSession: received from someone other than myself");
return KERN_SUCCESS;
}
if (event == AUE_SESSION_END)
Session::destroy(int_cast<uint64_t, Session::SessionId>(ident));
} catch(...) {
secnotice("SS", "exception handling a signal (ignored)");
}
mach_port_deallocate(mach_task_self(), taskPort);
return KERN_SUCCESS;
}
void Server::SleepWatcher::systemWillSleep()
{
secnotice("SS", "%p will sleep", this);
Session::processSystemSleep();
for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
(*it)->systemWillSleep();
}
void Server::SleepWatcher::systemIsWaking()
{
secnotice("SS", "%p is waking", this);
for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
(*it)->systemIsWaking();
}
void Server::SleepWatcher::systemWillPowerOn()
{
secnotice("SS", "%p will power on", this);
Server::active().longTermActivity();
for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
(*it)->systemWillPowerOn();
}
void Server::SleepWatcher::add(PowerWatcher *client)
{
assert(mPowerClients.find(client) == mPowerClients.end());
mPowerClients.insert(client);
}
void Server::SleepWatcher::remove(PowerWatcher *client)
{
assert(mPowerClients.find(client) != mPowerClients.end());
mPowerClients.erase(client);
}
Process *Server::findPid(pid_t pid) const
{
PidMap::const_iterator it = mPids.find(pid);
return (it == mPids.end()) ? NULL : it->second;
}
void Server::waitForClients(bool waiting)
{
mWaitForClients = waiting;
}
static FILE *reportFile;
void Server::beginShutdown()
{
StLock<Mutex> _(*this);
if (!mWaitForClients) {
secnotice("SS", "%p shutting down now", this);
_exit(0);
} else {
if (!mShuttingDown) {
mShuttingDown = true;
Session::invalidateAuthHosts();
secnotice("SS", "%p beginning shutdown", this);
if (verbosity() >= 2) {
reportFile = fopen("/var/log/securityd-shutdown.log", "w");
shutdownSnitch();
}
}
}
}
void Server::eventDone()
{
if (this->shuttingDown()) {
StLock<Mutex> lazy(*this, false); if (verbosity() >= 2) {
lazy.lock();
secnotice("SS", "shutting down with %ld processes and %ld transactions", mProcesses.size(), VProc::Transaction::debugCount());
shutdownSnitch();
}
IFDUMPING("shutdown", NodeCore::dumpAll());
}
}
void Server::shutdownSnitch()
{
time_t now;
time(&now);
fprintf(reportFile, "%.24s %d residual clients:\n", ctime(&now), int(mPids.size()));
for (PidMap::const_iterator it = mPids.begin(); it != mPids.end(); ++it) {
string path = it->second->getPath();
fprintf(reportFile, " %s (%d)\n", path.c_str(), it->first);
}
fprintf(reportFile, "\n");
fflush(reportFile);
}
bool Server::inDarkWake()
{
return IOPMIsADarkWake(IOPMConnectionGetSystemCapabilities());
}
void Server::loadCssm(bool mdsIsInstalled)
{
if (!mCssm->isActive()) {
StLock<Mutex> _(*this);
VProc::Transaction xact;
if (!mCssm->isActive()) {
if (!mdsIsInstalled) { secnotice("SS", "Installing MDS");
IFDEBUG(if (geteuid() == 0))
MDSClient::mds().install();
}
secnotice("SS", "CSSM initializing");
mCssm->init();
mCSP->attach();
secnotice("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
}
}
}
LongtermStLock::LongtermStLock(Mutex &lck)
: StLock<Mutex>(lck, false) {
if (lck.tryLock()) { this->mActive = true;
} else { Server::active().longTermActivity();
this->lock();
}
}