X-Git-Url: https://git.saurik.com/apple/securityd.git/blobdiff_plain/2f9dc5dc91d2adf097a030bc067f00235982c474..4cd1cad0dea00daa03e1b54fdf2797a02373ad5b:/src/server.cpp diff --git a/src/server.cpp b/src/server.cpp index 9f4fb6b..0177679 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved. + * Copyright (c) 2000-2004,2009 Apple Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -55,23 +55,22 @@ Authority::~Authority() { } + // // Construct the server object // Server::Server(Authority &authority, CodeSignatures &signatures, const char *bootstrapName) : MachServer(bootstrapName), mBootstrapName(bootstrapName), - mShutdown(shutdownImmediately), mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule), mAuthority(authority), mCodeSignatures(signatures), - mAudit(geteuid(), getpid()) + mVerbosity(0), + mWaitForClients(true), mShuttingDown(false) { // make me eternal (in the object mesh) ref(); - mAudit.registerSession(); - // engage the subsidiary port handler for sleep notifications add(sleepWatcher); } @@ -92,13 +91,14 @@ Server::~Server() // by calling Server::connection() [no argument] until it is released by // calling Connection::endWork(). // -Connection &Server::connection(mach_port_t port) +Connection &Server::connection(mach_port_t port, audit_token_t &auditToken) { Server &server = active(); StLock _(server); Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE); + conn->process().checkSession(auditToken); active().mCurrentConnection() = conn; - conn->beginWork(); + conn->beginWork(auditToken); return *conn; } @@ -138,7 +138,7 @@ Session &Server::session() RefPointer Server::key(KeyHandle key) { - return HandleObject::findRef(key, CSSMERR_CSP_INVALID_KEY_REFERENCE); + return U32HandleObject::findRef(key, CSSMERR_CSP_INVALID_KEY_REFERENCE); } RefPointer Server::database(DbHandle db) @@ -162,10 +162,11 @@ RefPointer Server::optionalDatabase(DbHandle db, bool persistent) // // Locate an ACL bearer (database or key) by handle +// The handle might be used across IPC, so we clamp it accordingly // -AclSource &Server::aclBearer(AclKind kind, CSSM_HANDLE handle) +AclSource &Server::aclBearer(AclKind kind, U32HandleObject::Handle handle) { - AclSource &bearer = HandleObject::find(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE); + AclSource &bearer = U32HandleObject::find(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE); if (kind != bearer.acl().aclKind()) CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); return bearer; @@ -205,31 +206,9 @@ 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) - -struct IPCName { const char *name; int ipc; }; -static IPCName ucspNames[] = { subsystem_to_name_map_ucsp }; // generated by MIG -static IPCName selfNames[] = { subsystem_to_name_map_self }; // generated by MIG - -#endif //NDEBUG - boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out) { -#if !defined(NDEBUG) - 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); -#endif //NDEBUG - - boolean_t result = ucsp_server(in, out) || self_server(in, out); - IFDEBUG(secdebug("SSreq", "end %s (%d)", name, in->msgh_id)); - - return result; + return ucsp_server(in, out) || self_server(in, out); } @@ -240,27 +219,27 @@ boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out) // Everything at and below that level is constructed. This is straight-forward except // in the case of session re-initialization (see below). // -void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort, - const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity) +void Server::setupConnection(ConnectLevel type, Port replyPort, Port taskPort, + const audit_token_t &auditToken, const ClientSetupInfo *info) { + AuditToken audit(auditToken); + // first, make or find the process based on task port StLock _(*this); RefPointer &proc = mProcesses[taskPort]; - if (type == connectNewSession && proc) { - // The client has talked to us before and now wants to create a new session. - proc->changeSession(servicePort); - } + if (proc && proc->session().sessionId() != audit.sessionId()) + proc->changeSession(audit.sessionId()); if (proc && type == connectNewProcess) { // the client has amnesia - reset it - assert(info && identity); - proc->reset(servicePort, taskPort, info, identity, AuditToken(auditToken)); - proc->changeSession(servicePort); + assert(info); + proc->reset(taskPort, info, audit); + proc->changeSession(audit.sessionId()); } 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(auditToken)); + assert(info); + proc = new Process(taskPort, info, audit); notifyIfDead(taskPort); mPids[proc->pid()] = proc; } @@ -296,28 +275,41 @@ void Server::endConnection(Port replyPort) // void Server::notifyDeadName(Port port) { - StLock _(*this); + // We need the lock to get a proper iterator on mConnections or mProcesses, + // but must release it before we call abort or kill, as these might take + // unbounded time, including calls out to token daemons etc. + + StLock serverLock(*this); secdebug("SSports", "port %d is dead", port.port()); // is it a connection? PortMap::iterator conIt = mConnections.find(port); if (conIt != mConnections.end()) { - conIt->second->abort(); + SECURITYD_PORTS_DEAD_CONNECTION(port); + RefPointer con = conIt->second; mConnections.erase(conIt); + serverLock.unlock(); + con->abort(); return; } // is it a process? PortMap::iterator procIt = mProcesses.find(port); if (procIt != mProcesses.end()) { - Process *proc = procIt->second; - proc->kill(); + SECURITYD_PORTS_DEAD_PROCESS(port); + RefPointer proc = procIt->second; mPids.erase(proc->pid()); mProcesses.erase(procIt); + serverLock.unlock(); + // The kill may take some time; make sure there is a spare thread around + // to prevent deadlocks + StLock _(*this); + proc->kill(); return; } // well, what IS IT?! + SECURITYD_PORTS_DEAD_ORPHAN(port); secdebug("server", "spurious dead port notification for port %d", port.port()); } @@ -328,8 +320,7 @@ void Server::notifyDeadName(Port port) // void Server::notifyNoSenders(Port port, mach_port_mscount_t) { - secdebug("SSports", "port %d no senders", port.port()); - Session::destroy(port); + SECURITYD_PORTS_DEAD_SESSION(port); } @@ -342,32 +333,24 @@ kern_return_t self_server_handleSignal(mach_port_t sport, mach_port_t taskPort, int sig) { try { + SECURITYD_SIGNAL_HANDLED(sig); if (taskPort != mach_task_self()) { Syslog::error("handleSignal: received from someone other than myself"); - secdebug("SS", "unauthorized handleSignal"); return KERN_SUCCESS; } - secdebug("SS", "dispatching indirect signal %d", sig); switch (sig) { case SIGCHLD: ServerChild::checkChildren(); break; case SIGINT: - secdebug("SS", "SIGINT received: terminating immediately"); + SECURITYD_SHUTDOWN_NOW(); Syslog::notice("securityd terminated due to SIGINT"); - exit(0); + _exit(0); case SIGTERM: - if (Server::active().beginShutdown()) { - Syslog::notice("securityd shutting down; lingering for remaining clients"); - } else { - secdebug("SS", "SIGTERM received: terminating immediately"); - Syslog::notice("securityd terminated due to SIGTERM"); - exit(0); - } + Server::active().beginShutdown(); break; case SIGPIPE: - secdebug("SS", "SIGPIPE received: ignoring"); - Syslog::notice("securityd ignoring SIGPIPE received"); + fprintf(stderr, "securityd ignoring SIGPIPE received"); break; #if defined(DEBUGDUMP) @@ -394,25 +377,50 @@ kern_return_t self_server_handleSignal(mach_port_t sport, } +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_CLOSE) + Session::destroy(ident); + } catch(...) { + secdebug("SS", "exception handling a signal (ignored)"); + } + mach_port_deallocate(mach_task_self(), taskPort); + return KERN_SUCCESS; +} + + // // Notifier for system sleep events // void Server::SleepWatcher::systemWillSleep() { - secdebug("SS", "sleep notification received"); + SECURITYD_POWER_SLEEP(); Session::processSystemSleep(); - secdebug("server", "distributing sleep event to %ld clients", mPowerClients.size()); for (set::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()); + SECURITYD_POWER_WAKE(); for (set::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) (*it)->systemIsWaking(); } +void Server::SleepWatcher::systemWillPowerOn() +{ + SECURITYD_POWER_ON(); + Server::active().longTermActivity(); + for (set::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) + (*it)->systemWillPowerOn(); +} + void Server::SleepWatcher::add(PowerWatcher *client) { assert(mPowerClients.find(client) == mPowerClients.end()); @@ -441,69 +449,78 @@ Process *Server::findPid(pid_t pid) const // void Server::waitForClients(bool waiting) { - if (mShutdown == shuttingDown) // too late to change your mind now - return; - if (waiting) - mShutdown = shutdownDelayed; - else - mShutdown = shutdownImmediately; + mWaitForClients = waiting; } // -// Shutdown processing +// Begin shutdown processing. +// We relinquish our primary state authority. From now on, we'll be +// kept alive (only) by our current clients. // -bool Server::beginShutdown() -{ - if (mShutdown != shutdownDelayed) - return false; - - secdebug("server", "beginning shutdown with %d client(s)", int(mProcesses.size())); - mShutdown = shuttingDown; +static FILE *reportFile; -#if defined(SHUTDOWN_SNITCH) - struct Snitch : public MachServer::Timer { - void action() { Server::active().shutdownSnitch(); } - }; - setTimer(new Snitch, Time::Interval(29)); // right before we get SIGKILLed -#endif - - return true; +void Server::beginShutdown() +{ + StLock _(*this); + if (!mWaitForClients) { + SECURITYD_SHUTDOWN_NOW(); + _exit(0); + } else { + if (!mShuttingDown) { + mShuttingDown = true; + Session::invalidateAuthHosts(); + SECURITYD_SHUTDOWN_BEGIN(); + if (verbosity() >= 2) { + reportFile = fopen("/var/log/securityd-shutdown.log", "w"); + shutdownSnitch(); + } + } + } } +// +// During shutdown, we report residual clients to dtrace, and allow a state dump +// for debugging. +// We don't bother locking for the shuttingDown() check; it's a latching boolean +// and we'll be good enough without a lock. +// void Server::eventDone() { - if (mShutdown == shuttingDown) { - if (mProcesses.empty()) { - secdebug("SS", "out of clients - shutdown complete"); - Syslog::notice("securityd has finished serving its clients - terminating now"); - exit(0); - } else { - secdebug("SS", "shutdown in progress - %d process(es) left", int(mProcesses.size())); - IFDUMPING("shutdown", NodeCore::dumpAll()); + if (this->shuttingDown()) { + StLock lazy(*this, false); // lazy lock acquisition + if (SECURITYD_SHUTDOWN_COUNT_ENABLED()) { + lazy.lock(); + SECURITYD_SHUTDOWN_COUNT(mProcesses.size(), VProc::Transaction::debugCount()); } + if (verbosity() >= 2) { + lazy.lock(); + shutdownSnitch(); + } + IFDUMPING("shutdown", NodeCore::dumpAll()); } } -#if defined(SHUTDOWN_SNITCH) void Server::shutdownSnitch() { - Syslog::notice("29 seconds after shutdown began, securityd still has %d clients:", int(mPids.size())); + 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) if (SecCodeRef clientCode = it->second->processCode()) { CFRef path; - SecCodeCopyPath(clientCode, kSecCSDefaultFlags, &path.aref()); + OSStatus rc = SecCodeCopyPath(clientCode, kSecCSDefaultFlags, &path.aref()); if (path) - Syslog::notice(" %s (%d)", cfString(path).c_str(), it->first); + fprintf(reportFile, " %s (%d)\n", cfString(path).c_str(), it->first); else - Syslog::notice(" pid=%d", it->first); + fprintf(reportFile, "pid=%d (error %d)\n", it->first, int32_t(rc)); } + fprintf(reportFile, "\n"); + fflush(reportFile); } -#endif //SHUTDOWN_SNITCH - // // Initialize the CSSM/MDS subsystem. @@ -511,14 +528,17 @@ void Server::shutdownSnitch() // system MDS here, and CSSM is pretty much always needed, so this is called // early during program startup. Do note that the server may not (yet) be running. // -void Server::loadCssm() +void Server::loadCssm(bool mdsIsInstalled) { if (!mCssm->isActive()) { StLock _(*this); + VProc::Transaction xact; if (!mCssm->isActive()) { - secdebug("SS", "Installing MDS"); - IFDEBUG(if (geteuid() == 0)) + if (!mdsIsInstalled) { // non-system securityd instance should not reinitialize MDS + secdebug("SS", "Installing MDS"); + IFDEBUG(if (geteuid() == 0)) MDSClient::mds().install(); + } secdebug("SS", "CSSM initializing"); mCssm->init(); mCSP->attach();