#include "server.h"
#include "session.h"
#include "acls.h"
#include "notifications.h"
#include "ucsp.h"
#include <mach/mach_error.h>
#include <bsm/audit.h>
#include <bsm/audit_kevents.h>
#include <bsm/audit_record.h>
#include <bsm/audit_uevents.h>
#include <bsm/libbsm.h>
#include "ccaudit.h"
using namespace MachPlusPlus;
Server::Server(Authority &authority, CodeSignatures &signatures, const char *bootstrapName)
: MachServer(bootstrapName),
mBootstrapName(bootstrapName),
mCurrentConnection(false),
mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
mAuthority(authority),
mCodeSignatures(signatures)
{
initAudit();
add(sleepWatcher);
}
Server::~Server()
{
}
Connection &Server::connection(mach_port_t port)
{
Server &server = active();
StLock<Mutex> _(server.lock);
ConnectionMap::iterator it = server.connections.find(port);
if (it == server.connections.end()) CssmError::throwMe(CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
Connection *conn = it->second;
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 (Connection *conn = active().mCurrentConnection) {
if (conn->endWork())
delete conn;
active().mCurrentConnection = NULL;
}
}
SecurityServerAcl &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
{
SecurityServerAcl &bearer = findHandle<SecurityServerAcl>(handle);
if (kind != bearer.kind())
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));
}
boolean_t ucsp_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);
}
#else //NDEBUG
static const struct IPCName { const char *name; int ipc; } ipcNames[] =
{ subsystem_to_name_map_ucsp };
boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
{
const int first = ipcNames[0].ipc;
const char *name = (in->msgh_id >= first && in->msgh_id < first + ucsp_MSG_COUNT) ?
ipcNames[in->msgh_id - first].name : "OUT OF BOUNDS";
secdebug("SSreq", "begin %s (%d)", name, in->msgh_id);
boolean_t result = ucsp_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> _(lock);
Process * &proc = processes[taskPort];
if (type == connectNewSession && proc) {
secdebug("server", "session setup - marooning old process %p(%d) of session %p",
proc, proc->pid(), &proc->session);
if (proc->kill(true))
delete proc;
proc = NULL;
}
if (!proc) {
if (type == connectNewThread) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
assert(info && identity);
proc = new Process(servicePort, taskPort, info, identity,
auditToken.val[1], auditToken.val[2]);
notifyIfDead(taskPort);
}
Connection *connection = new Connection(*proc, replyPort);
if (connections[replyPort]) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); connections[replyPort] = connection;
notifyIfDead(replyPort);
}
void Server::endConnection(Port replyPort)
{
StLock<Mutex> _(lock);
Connection *connection = connections[replyPort];
assert(connection);
connections.erase(replyPort);
connection->terminate();
delete connection;
}
void Server::notifyDeadName(Port port)
{
StLock<Mutex> _(lock);
secdebug("SSports", "port %d is dead", port.port());
ConnectionMap::iterator conIt = connections.find(port);
if (conIt != connections.end()) {
Connection *connection = conIt->second;
if (connection->abort())
delete connection;
connections.erase(conIt);
return;
}
ProcessMap::iterator procIt = processes.find(port);
if (procIt != processes.end()) {
Process *process = procIt->second;
if (process->kill())
delete process;
processes.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::eliminate(port);
}
void Server::SleepWatcher::systemWillSleep()
{
secdebug("SS", "sleep notification received");
Session::lockAllDatabases(true);
}
void Server::initAudit(void)
{
secdebug("SS", "initializing Common Criteria auditing");
mAudit.auditId(geteuid());
mAudit.eventMask().set(AUE_NULL, AUE_NULL);
mAudit.terminalId().set();
mAudit.sessionId(getpid());
mAudit.registerSession();
}
CssmClient::CSP &Server::getCsp()
{
if (!mCssm->isActive())
loadCssm();
return mCSP;
}
static void initMds();
void Server::loadCssm()
{
if (!mCssm->isActive()) {
StLock<Mutex> _(lock);
if (!mCssm->isActive()) {
try {
initMds();
} catch (const CssmError &error) {
switch (error.cssmError()) {
case CSSMERR_DL_MDS_ERROR:
case CSSMERR_DL_OS_ACCESS_DENIED:
secdebug("SS", "MDS initialization failed; continuing");
Syslog::warning("MDS initialization failed; continuing");
break;
default:
throw;
}
}
secdebug("SS", "CSSM initializing");
mCssm->init();
mCSP->attach();
IFDEBUG(char guids[Guid::stringRepLength+1]);
secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString(guids));
}
}
}
#include <Security/mds.h>
static void initMds()
{
secdebug("SS", "MDS initializing");
CssmAllocatorMemoryFunctions memory(CssmAllocator::standard());
MDS_FUNCS functions;
MDS_HANDLE handle;
CssmError::check(MDS_Initialize(NULL, &memory, &functions, &handle));
CssmError::check(MDS_Install(handle));
CssmError::check(MDS_Terminate(handle));
}