]> git.saurik.com Git - apple/securityd.git/blobdiff - src/server.cpp
securityd-55199.3.tar.gz
[apple/securityd.git] / src / server.cpp
index f07241d870948cedc08c462da452128b2066058e..017767901c18f2537b6d925497b85f605d024c49 100644 (file)
@@ -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@
  * 
@@ -36,6 +36,7 @@
 #include "child.h"
 #include <mach/mach_error.h>
 #include <security_utilities/ccaudit.h>
+#include "pcscmonitor.h"
 
 #include "agentquery.h"
 
@@ -54,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);
 }
@@ -91,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<Mutex> _(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;
 }
 
@@ -137,7 +138,7 @@ Session &Server::session()
 
 RefPointer<Key> Server::key(KeyHandle key)
 {
-       return HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
+       return U32HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
 }
 
 RefPointer<Database> Server::database(DbHandle db)
@@ -161,10 +162,11 @@ RefPointer<Database> 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<AclSource>(handle, CSSMERR_CSSM_INVALID_ADDIN_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;
@@ -204,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);
 }
 
 
@@ -239,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<Mutex> _(*this);
        RefPointer<Process> &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;
        }
@@ -295,28 +275,41 @@ void Server::endConnection(Port replyPort)
 //
 void Server::notifyDeadName(Port port)
 {
-       StLock<Mutex> _(*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<Mutex> serverLock(*this);
        secdebug("SSports", "port %d is dead", port.port());
     
     // is it a connection?
     PortMap<Connection>::iterator conIt = mConnections.find(port);
     if (conIt != mConnections.end()) {
-               conIt->second->abort();
+               SECURITYD_PORTS_DEAD_CONNECTION(port);
+        RefPointer<Connection> con = conIt->second;
                mConnections.erase(conIt);
+        serverLock.unlock();
+               con->abort();        
         return;
     }
     
     // is it a process?
     PortMap<Process>::iterator procIt = mProcesses.find(port);
     if (procIt != mProcesses.end()) {
-               Process *proc = procIt->second;
-               proc->kill();
+               SECURITYD_PORTS_DEAD_PROCESS(port);
+        RefPointer<Process> 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<MachServer, &Server::busy, &Server::idle> _(*this);
+               proc->kill();
         return;
     }
     
        // well, what IS IT?!
+       SECURITYD_PORTS_DEAD_ORPHAN(port);
        secdebug("server", "spurious dead port notification for port %d", port.port());
 }
 
@@ -327,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);
 }
 
 
@@ -341,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)
@@ -374,6 +358,14 @@ kern_return_t self_server_handleSignal(mach_port_t sport,
                        NodeCore::dumpAll();
                        break;
 #endif //DEBUGDUMP
+
+               case SIGUSR2:
+                       {
+                               extern PCSCMonitor *gPCSC;
+                               gPCSC->startSoftTokens();
+                               break;
+                       }
+
                default:
                        assert(false);
         }
@@ -385,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<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());
+       SECURITYD_POWER_WAKE();
        for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
                (*it)->systemIsWaking();
 }
 
+void Server::SleepWatcher::systemWillPowerOn()
+{
+       SECURITYD_POWER_ON();
+       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());
@@ -432,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;
+static FILE *reportFile;
 
-       secdebug("server", "beginning shutdown with %d client(s)", int(mProcesses.size()));
-       mShutdown = shuttingDown;
-
-#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<Mutex> _(*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<Mutex> 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<CFURLRef> 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.
@@ -502,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<Mutex> _(*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();