*
* @APPLE_LICENSE_HEADER_START@
*
- * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
- *
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
//
-// server - the actual SecurityServer server object
+// server - securityd main server object
//
-#include <securityd_client/ucsp.h> // knowledge of mig message sizes
+#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;
mBootstrapName(bootstrapName),
mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
mAuthority(authority),
- mCodeSignatures(signatures)
+ mCodeSignatures(signatures),
+ mAudit(geteuid(), getpid())
{
+ // make me eternal (in the object mesh)
+ ref();
+
+ mAudit.registerSession();
+
// engage the subsidiary port handler for sleep notifications
- add(sleepWatcher);
+ add(sleepWatcher);
}
RefPointer<Database> Server::database(DbHandle db)
{
- RefPointer<Database> database =
- HandleObject::findRef<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
- if (database->process() != process())
- CssmError::throwMe(CSSMERR_DL_INVALID_DB_HANDLE);
- return database;
+ return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
}
RefPointer<KeychainDatabase> Server::keychain(DbHandle db)
{
- RefPointer<KeychainDatabase> keychain =
- HandleObject::findRef<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
- if (keychain->process() != process())
- CssmError::throwMe(CSSMERR_DL_INVALID_DB_HANDLE);
- return keychain;
+ return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
}
-RefPointer<Database> Server::optionalDatabase(DbHandle db)
+RefPointer<Database> Server::optionalDatabase(DbHandle db, bool persistent)
{
- if (db == noDb)
- return &process().localStore();
- else
+ if (persistent && db != noDb)
return database(db);
+ else
+ return &process().localStore();
}
//
// Locate an ACL bearer (database or key) by handle
//
-SecurityServerAcl &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
+AclSource &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
{
- SecurityServerAcl &bearer =
- HandleObject::find<SecurityServerAcl>(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
- if (kind != bearer.kind())
+ 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;
}
{
MachServer::run(0x10000,
MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
- MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_SENDER));
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT));
+}
+
+
+//
+// Handle thread overflow. MachServer will call this if it has hit its thread
+// limit and yet still needs another thread.
+//
+void Server::threadLimitReached(UInt32 limit)
+{
+ Syslog::notice("securityd has reached its thread limit (%ld) - service deadlock is possible",
+ limit);
}
//
// The primary server run-loop function.
-// Invokes the MIG-generated main dispatch function (ucsp_server).
+// Invokes the MIG-generated main dispatch function (ucsp_server), as well
+// as the self-send dispatch (self_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 *);
+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);
+ return ucsp_server(in, out) || self_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
+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
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";
+ 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);
+ boolean_t result = ucsp_server(in, out) || self_server(in, out);
secdebug("SSreq", "end %s (%d)", name, in->msgh_id);
return result;
}
// in the case of session re-initialization (see below).
//
void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort,
- const security_token_t &securityToken, const ClientSetupInfo *info, const char *identity)
+ const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity)
{
// first, make or find the process based on task port
StLock<Mutex> _(*this);
// The client has talked to us before and now wants to create a new session.
proc->changeSession(servicePort);
}
+ if (proc && type == connectNewProcess) {
+ // the client has amnesia - reset it
+ assert(info && identity);
+ proc->reset(servicePort, taskPort, info, identity, AuditToken(auditToken));
+ }
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,
- securityToken.val[0], securityToken.val[1]);
+ proc = new Process(servicePort, taskPort, info, identity, AuditToken(auditToken));
notifyIfDead(taskPort);
}
if (Listener::remove(port))
return;
+ // well, what IS IT?!
secdebug("server", "spurious dead port notification for port %d", port.port());
}
}
+//
+// Handling signals.
+// These are sent as Mach messages from ourselves to escape the limitations of
+// the signal handler environment.
+//
+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;
+}
+
+
//
// Notifier for system sleep events
//
{
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();
+}
-//
-// Return the primary Cryptographic Service Provider.
-// This will be lazily loaded when it is first requested.
-//
-CssmClient::CSP &Server::getCsp()
+void Server::SleepWatcher::add(PowerWatcher *client)
{
- if (!mCssm->isActive())
- loadCssm();
- return mCSP;
+ assert(mPowerClients.find(client) == mPowerClients.end());
+ mPowerClients.insert(client);
+}
+
+void Server::SleepWatcher::remove(PowerWatcher *client)
+{
+ assert(mPowerClients.find(client) != mPowerClients.end());
+ mPowerClients.erase(client);
}
//
// Initialize the CSSM/MDS subsystem.
-// This is thread-safe and can be done lazily.
+// This was once done lazily on demand. These days, we are setting up the
+// 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.
//
-static void initMds();
-
void Server::loadCssm()
{
if (!mCssm->isActive()) {
StLock<Mutex> _(*this);
if (!mCssm->isActive()) {
- try {
- initMds();
- } catch (const CssmError &error) {
- switch (error.osStatus()) {
- 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", "Installing MDS");
+ MDSClient::mds().install();
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));
+ secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
}
}
}
-
-#include <Security/mds.h>
-
-static void initMds()
-{
- secdebug("SS", "MDS initializing");
- CssmAllocatorMemoryFunctions memory(Allocator::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));
-}