+++ /dev/null
-/*
- * 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 <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;
-
-//
-// 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<Mutex> _(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<SecurityServerAcl>(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<Mutex> _(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<Mutex> _(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<Mutex> _(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<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));
-}