/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the * specific language governing rights and limitations under the License. */ // // server - the actual SecurityServer server object // #include "server.h" #include "session.h" #include "acls.h" #include "notifications.h" #include "ucsp.h" #include #include #include #include #include #include #include "ccaudit.h" using namespace MachPlusPlus; // // Construct the server object // 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(); // engage the subsidiary port handler for sleep notifications add(sleepWatcher); } // // Clean up the server object // Server::~Server() { //@@@ more later } // // Locate a connection by reply port and make it the current connection // of this thread. The connection will be marked busy, and can be accessed // by calling Server::connection() [no argument] until it is released by // calling Connection::endWork(). // Connection &Server::connection(mach_port_t port) { Server &server = active(); StLock _(server.lock); ConnectionMap::iterator it = server.connections.find(port); if (it == server.connections.end()) // unknown client port -- could be a hack attempt 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); // have to have one if (!tolerant) conn->checkWork(); return *conn; } void Server::requestComplete() { // note: there may not be an active connection if connection setup failed if (Connection *conn = active().mCurrentConnection) { if (conn->endWork()) delete conn; active().mCurrentConnection = NULL; } } // // Locate an ACL bearer (database or key) by handle // SecurityServerAcl &Server::aclBearer(AclKind kind, CSSM_HANDLE handle) { SecurityServerAcl &bearer = findHandle(handle); if (kind != bearer.kind()) CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); return bearer; } // // Run the server. This will not return until the server is forced to exit. // void Server::run() { MachServer::run(0x10000, MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)); } // // The primary server run-loop function. // Invokes the MIG-generated main dispatch function (ucsp_server). // For debug builds, look up request names in a MIG-generated table // for better debug-log messages. // 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 }; // macro generated by MIG, from ucsp.h 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 // // Set up a new Connection. This establishes the environment (process et al) as needed // and registers a properly initialized Connection object to run with. // Type indicates how "deep" we need to initialize (new session, process, or connection). // Everything at and below that level is constructed. This is straight-forward except // in the case of session re-initialization (see below). // // audit_token_t.val[1] is the EUID, audit_token_t.val[2] is the EGID. // void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort, const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity) { // first, make or find the process based on task port StLock _(lock); Process * &proc = processes[taskPort]; if (type == connectNewSession && proc) { // The client has talked to us before and now wants to create a new session. // We'll unmoor the old process object and cast it adrift (it will die either now // or later following the usual deferred-death mechanics). // The connection object will die (it's probably already dead) because the client // has destroyed its replyPort. So we don't worry about this here. 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) // client error (or attack) CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); assert(info && identity); proc = new Process(servicePort, taskPort, info, identity, auditToken.val[1], auditToken.val[2]); notifyIfDead(taskPort); } // now, establish a connection and register it in the server Connection *connection = new Connection(*proc, replyPort); if (connections[replyPort]) // malicious re-entry attempt? CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error) connections[replyPort] = connection; notifyIfDead(replyPort); } // // Synchronously end a Connection. // This is due to a request from the client, so no thread races are possible. // void Server::endConnection(Port replyPort) { StLock _(lock); Connection *connection = connections[replyPort]; assert(connection); connections.erase(replyPort); connection->terminate(); delete connection; } // // Handling dead-port notifications. // This receives DPNs for all kinds of ports we're interested in. // void Server::notifyDeadName(Port port) { StLock _(lock); secdebug("SSports", "port %d is dead", port.port()); // is it a connection? ConnectionMap::iterator conIt = connections.find(port); if (conIt != connections.end()) { Connection *connection = conIt->second; if (connection->abort()) delete connection; connections.erase(conIt); return; } // is it a process? ProcessMap::iterator procIt = processes.find(port); if (procIt != processes.end()) { Process *process = procIt->second; if (process->kill()) delete process; processes.erase(procIt); return; } // is it a notification client? if (Listener::remove(port)) return; secdebug("server", "spurious dead port notification for port %d", port.port()); } // // Handling no-senders notifications. // This is currently only used for (subsidiary) service ports // void Server::notifyNoSenders(Port port, mach_port_mscount_t) { secdebug("SSports", "port %d no senders", port.port()); Session::eliminate(port); } // // Notifier for system sleep events // 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()); // Set the class mask so only the audit records we submit are written. mAudit.eventMask().set(AUE_NULL, AUE_NULL); mAudit.terminalId().set(); // XXX If we use SS session IDs instead, get the RootSession ID mAudit.sessionId(getpid()); mAudit.registerSession(); } // // Return the primary Cryptographic Service Provider. // This will be lazily loaded when it is first requested. // CssmClient::CSP &Server::getCsp() { if (!mCssm->isActive()) loadCssm(); return mCSP; } // // Initialize the CSSM/MDS subsystem. // This is thread-safe and can be done lazily. // static void initMds(); void Server::loadCssm() { if (!mCssm->isActive()) { StLock _(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 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)); }