#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>
using namespace MachPlusPlus;
Authority::Authority(const char *configFile)
: Authorization::Engine(configFile)
{
}
Authority::~Authority()
{
}
Server::Server(Authority &authority, CodeSignatures &signatures, const char *bootstrapName)
: MachServer(bootstrapName),
mBootstrapName(bootstrapName),
mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
mAuthority(authority),
mCodeSignatures(signatures),
mAudit(geteuid(), getpid())
{
ref();
mAudit.registerSession();
add(sleepWatcher);
}
Server::~Server()
{
}
Connection &Server::connection(mach_port_t port)
{
Server &server = active();
StLock<Mutex> _(server);
Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
active().mCurrentConnection() = conn;
conn->beginWork();
return *conn;
}
Connection &Server::connection(bool tolerant)
{
Connection *conn = active().mCurrentConnection();
assert(conn); if (!tolerant)
conn->checkWork();
return *conn;
}
void Server::requestComplete()
{
if (RefPointer<Connection> &conn = active().mCurrentConnection()) {
conn->endWork();
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 HandleObject::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, CSSM_HANDLE handle)
{
AclSource &bearer = HandleObject::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 (%ld) - service deadlock is possible",
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 *);
#if defined(NDEBUG)
boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
{
return ucsp_server(in, out) || self_server(in, out);
}
#else //NDEBUG
struct IPCName { const char *name; int ipc; };
static IPCName ucspNames[] = { subsystem_to_name_map_ucsp }; static IPCName selfNames[] = { subsystem_to_name_map_self };
boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
{
const int id = in->msgh_id;
const int ucspBase = ucspNames[0].ipc;
const int selfBase = selfNames[0].ipc;
const char *name =
(id >= ucspBase && id < ucspBase + ucsp_MSG_COUNT) ? ucspNames[id - ucspBase].name :
(id >= selfBase && id < selfBase + self_MSG_COUNT) ? selfNames[id - selfBase].name :
"OUT OF BOUNDS";
secdebug("SSreq", "begin %s (%d)", name, in->msgh_id);
boolean_t result = ucsp_server(in, out) || self_server(in, out);
secdebug("SSreq", "end %s (%d)", name, in->msgh_id);
return result;
}
#endif //NDEBUG
void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort,
const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity)
{
StLock<Mutex> _(*this);
RefPointer<Process> &proc = mProcesses[taskPort];
if (type == connectNewSession && proc) {
proc->changeSession(servicePort);
}
if (proc && type == connectNewProcess) {
assert(info && identity);
proc->reset(servicePort, taskPort, info, identity, AuditToken(auditToken));
}
if (!proc) {
if (type == connectNewThread) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
assert(info && identity);
proc = new Process(servicePort, taskPort, info, identity, AuditToken(auditToken));
notifyIfDead(taskPort);
}
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> _(*this);
secdebug("SSports", "port %d is dead", port.port());
PortMap<Connection>::iterator conIt = mConnections.find(port);
if (conIt != mConnections.end()) {
conIt->second->abort();
mConnections.erase(conIt);
return;
}
PortMap<Process>::iterator procIt = mProcesses.find(port);
if (procIt != mProcesses.end()) {
procIt->second->kill();
mProcesses.erase(procIt);
return;
}
if (Listener::remove(port))
return;
secdebug("server", "spurious dead port notification for port %d", port.port());
}
void Server::notifyNoSenders(Port port, mach_port_mscount_t)
{
secdebug("SSports", "port %d no senders", port.port());
Session::destroy(port);
}
kern_return_t self_server_handleSignal(mach_port_t sport,
mach_port_t taskPort, int sig)
{
try {
if (taskPort != mach_task_self()) {
Syslog::error("handleSignal: received from someone other than myself");
secdebug("SS", "unauthorized handleSignal");
return 0;
}
secdebug("SS", "dispatching indirect signal %d", sig);
switch (sig) {
case SIGCHLD:
ServerChild::checkChildren();
break;
case SIGINT:
case SIGTERM:
secdebug("SS", "signal %d received: terminating", sig);
Syslog::notice("securityd terminating due to signal %d", sig);
exit(0);
#if defined(DEBUGDUMP)
case SIGUSR1:
NodeCore::dumpAll();
break;
#endif //DEBUGDUMP
default:
assert(false);
}
} catch(...) {
secdebug("SS", "exception handling a signal (ignored)");
}
mach_port_deallocate(mach_task_self(), taskPort);
return 0;
}
void Server::SleepWatcher::systemWillSleep()
{
secdebug("SS", "sleep notification received");
Session::processSystemSleep();
secdebug("server", "distributing sleep event to %ld clients", mPowerClients.size());
for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
(*it)->systemWillSleep();
}
void Server::SleepWatcher::systemIsWaking()
{
secdebug("server", "distributing wakeup event to %ld clients", mPowerClients.size());
for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
(*it)->systemIsWaking();
}
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);
}
void Server::loadCssm()
{
if (!mCssm->isActive()) {
StLock<Mutex> _(*this);
if (!mCssm->isActive()) {
secdebug("SS", "Installing MDS");
MDSClient::mds().install();
secdebug("SS", "CSSM initializing");
mCssm->init();
mCSP->attach();
secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
}
}
}