2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 // server - securityd main server object
28 #include <securityd_client/ucsp.h> // MIG ucsp service
29 #include "self.h" // MIG self service
30 #include <security_utilities/logging.h>
31 #include <security_cdsa_client/mdsclient.h>
35 #include "notifications.h"
37 #include <mach/mach_error.h>
38 #include <security_utilities/ccaudit.h>
40 #include "agentquery.h"
43 using namespace MachPlusPlus
;
46 // Construct an Authority
48 Authority::Authority(const char *configFile
)
49 : Authorization::Engine(configFile
)
53 Authority::~Authority()
58 // Construct the server object
60 Server::Server(Authority
&authority
, CodeSignatures
&signatures
, const char *bootstrapName
)
61 : MachServer(bootstrapName
),
62 mBootstrapName(bootstrapName
),
63 mShutdown(shutdownImmediately
),
64 mCSPModule(gGuidAppleCSP
, mCssm
), mCSP(mCSPModule
),
65 mAuthority(authority
),
66 mCodeSignatures(signatures
),
67 mAudit(geteuid(), getpid())
69 // make me eternal (in the object mesh)
72 mAudit
.registerSession();
74 // engage the subsidiary port handler for sleep notifications
80 // Clean up the server object
89 // Locate a connection by reply port and make it the current connection
90 // of this thread. The connection will be marked busy, and can be accessed
91 // by calling Server::connection() [no argument] until it is released by
92 // calling Connection::endWork().
94 Connection
&Server::connection(mach_port_t port
)
96 Server
&server
= active();
97 StLock
<Mutex
> _(server
);
98 Connection
*conn
= server
.mConnections
.get(port
, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE
);
99 active().mCurrentConnection() = conn
;
104 Connection
&Server::connection(bool tolerant
)
106 Connection
*conn
= active().mCurrentConnection();
107 assert(conn
); // have to have one
113 void Server::requestComplete(CSSM_RETURN
&rcode
)
115 // note: there may not be an active connection if connection setup failed
116 if (RefPointer
<Connection
> &conn
= active().mCurrentConnection()) {
117 conn
->endWork(rcode
);
120 IFDUMPING("state", NodeCore::dumpAll());
125 // Shorthand for "current" process and session.
126 // This is the process and session for the current connection.
128 Process
&Server::process()
130 return connection().process();
133 Session
&Server::session()
135 return connection().process().session();
138 RefPointer
<Key
> Server::key(KeyHandle key
)
140 return HandleObject::findRef
<Key
>(key
, CSSMERR_CSP_INVALID_KEY_REFERENCE
);
143 RefPointer
<Database
> Server::database(DbHandle db
)
145 return find
<Database
>(db
, CSSMERR_DL_INVALID_DB_HANDLE
);
148 RefPointer
<KeychainDatabase
> Server::keychain(DbHandle db
)
150 return find
<KeychainDatabase
>(db
, CSSMERR_DL_INVALID_DB_HANDLE
);
153 RefPointer
<Database
> Server::optionalDatabase(DbHandle db
, bool persistent
)
155 if (persistent
&& db
!= noDb
)
158 return &process().localStore();
163 // Locate an ACL bearer (database or key) by handle
165 AclSource
&Server::aclBearer(AclKind kind
, CSSM_HANDLE handle
)
167 AclSource
&bearer
= HandleObject::find
<AclSource
>(handle
, CSSMERR_CSSM_INVALID_ADDIN_HANDLE
);
168 if (kind
!= bearer
.acl().aclKind())
169 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE
);
175 // Run the server. This will not return until the server is forced to exit.
179 MachServer::run(0x10000,
180 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0
) |
181 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT
));
186 // Handle thread overflow. MachServer will call this if it has hit its thread
187 // limit and yet still needs another thread.
189 void Server::threadLimitReached(UInt32 limit
)
191 Syslog::notice("securityd has reached its thread limit (%ld) - service deadlock is possible",
197 // The primary server run-loop function.
198 // Invokes the MIG-generated main dispatch function (ucsp_server), as well
199 // as the self-send dispatch (self_server).
200 // For debug builds, look up request names in a MIG-generated table
201 // for better debug-log messages.
203 boolean_t
ucsp_server(mach_msg_header_t
*, mach_msg_header_t
*);
204 boolean_t
self_server(mach_msg_header_t
*, mach_msg_header_t
*);
209 struct IPCName
{ const char *name
; int ipc
; };
210 static IPCName ucspNames
[] = { subsystem_to_name_map_ucsp
}; // generated by MIG
211 static IPCName selfNames
[] = { subsystem_to_name_map_self
}; // generated by MIG
215 boolean_t
Server::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
218 const int id
= in
->msgh_id
;
219 const int ucspBase
= ucspNames
[0].ipc
;
220 const int selfBase
= selfNames
[0].ipc
;
222 (id
>= ucspBase
&& id
< ucspBase
+ ucsp_MSG_COUNT
) ? ucspNames
[id
- ucspBase
].name
:
223 (id
>= selfBase
&& id
< selfBase
+ self_MSG_COUNT
) ? selfNames
[id
- selfBase
].name
:
225 secdebug("SSreq", "begin %s (%d)", name
, in
->msgh_id
);
228 boolean_t result
= ucsp_server(in
, out
) || self_server(in
, out
);
229 IFDEBUG(secdebug("SSreq", "end %s (%d)", name
, in
->msgh_id
));
236 // Set up a new Connection. This establishes the environment (process et al) as needed
237 // and registers a properly initialized Connection object to run with.
238 // Type indicates how "deep" we need to initialize (new session, process, or connection).
239 // Everything at and below that level is constructed. This is straight-forward except
240 // in the case of session re-initialization (see below).
242 void Server::setupConnection(ConnectLevel type
, Port servicePort
, Port replyPort
, Port taskPort
,
243 const audit_token_t
&auditToken
, const ClientSetupInfo
*info
, const char *identity
)
245 // first, make or find the process based on task port
246 StLock
<Mutex
> _(*this);
247 RefPointer
<Process
> &proc
= mProcesses
[taskPort
];
248 if (type
== connectNewSession
&& proc
) {
249 // The client has talked to us before and now wants to create a new session.
250 proc
->changeSession(servicePort
);
252 if (proc
&& type
== connectNewProcess
) {
253 // the client has amnesia - reset it
254 assert(info
&& identity
);
255 proc
->reset(servicePort
, taskPort
, info
, identity
, AuditToken(auditToken
));
256 proc
->changeSession(servicePort
);
259 if (type
== connectNewThread
) // client error (or attack)
260 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
);
261 assert(info
&& identity
);
262 proc
= new Process(servicePort
, taskPort
, info
, identity
, AuditToken(auditToken
));
263 notifyIfDead(taskPort
);
264 mPids
[proc
->pid()] = proc
;
267 // now, establish a connection and register it in the server
268 Connection
*connection
= new Connection(*proc
, replyPort
);
269 if (mConnections
.contains(replyPort
)) // malicious re-entry attempt?
270 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR
); //@@@ error code? (client error)
271 mConnections
[replyPort
] = connection
;
272 notifyIfDead(replyPort
);
277 // Synchronously end a Connection.
278 // This is due to a request from the client, so no thread races are possible.
279 // In practice, this is optional since the DPN for the client thread reply port
280 // will destroy the connection anyway when the thread dies.
282 void Server::endConnection(Port replyPort
)
284 StLock
<Mutex
> _(*this);
285 PortMap
<Connection
>::iterator it
= mConnections
.find(replyPort
);
286 assert(it
!= mConnections
.end());
287 it
->second
->terminate();
288 mConnections
.erase(it
);
293 // Handling dead-port notifications.
294 // This receives DPNs for all kinds of ports we're interested in.
296 void Server::notifyDeadName(Port port
)
298 StLock
<Mutex
> _(*this);
299 secdebug("SSports", "port %d is dead", port
.port());
301 // is it a connection?
302 PortMap
<Connection
>::iterator conIt
= mConnections
.find(port
);
303 if (conIt
!= mConnections
.end()) {
304 conIt
->second
->abort();
305 mConnections
.erase(conIt
);
310 PortMap
<Process
>::iterator procIt
= mProcesses
.find(port
);
311 if (procIt
!= mProcesses
.end()) {
312 Process
*proc
= procIt
->second
;
314 mPids
.erase(proc
->pid());
315 mProcesses
.erase(procIt
);
319 // well, what IS IT?!
320 secdebug("server", "spurious dead port notification for port %d", port
.port());
325 // Handling no-senders notifications.
326 // This is currently only used for (subsidiary) service ports
328 void Server::notifyNoSenders(Port port
, mach_port_mscount_t
)
330 secdebug("SSports", "port %d no senders", port
.port());
331 Session::destroy(port
);
337 // These are sent as Mach messages from ourselves to escape the limitations of
338 // the signal handler environment.
340 kern_return_t
self_server_handleSignal(mach_port_t sport
,
341 mach_port_t taskPort
, int sig
)
344 if (taskPort
!= mach_task_self()) {
345 Syslog::error("handleSignal: received from someone other than myself");
346 secdebug("SS", "unauthorized handleSignal");
349 secdebug("SS", "dispatching indirect signal %d", sig
);
352 ServerChild::checkChildren();
355 secdebug("SS", "SIGINT received: terminating immediately");
356 Syslog::notice("securityd terminated due to SIGINT");
359 if (Server::active().beginShutdown()) {
360 Syslog::notice("securityd shutting down; lingering for remaining clients");
362 secdebug("SS", "SIGTERM received: terminating immediately");
363 Syslog::notice("securityd terminated due to SIGTERM");
368 secdebug("SS", "SIGPIPE received: ignoring");
369 Syslog::notice("securityd ignoring SIGPIPE received");
372 #if defined(DEBUGDUMP)
381 secdebug("SS", "exception handling a signal (ignored)");
383 mach_port_deallocate(mach_task_self(), taskPort
);
389 // Notifier for system sleep events
391 void Server::SleepWatcher::systemWillSleep()
393 secdebug("SS", "sleep notification received");
394 Session::processSystemSleep();
395 secdebug("server", "distributing sleep event to %ld clients", mPowerClients
.size());
396 for (set
<PowerWatcher
*>::const_iterator it
= mPowerClients
.begin(); it
!= mPowerClients
.end(); it
++)
397 (*it
)->systemWillSleep();
400 void Server::SleepWatcher::systemIsWaking()
402 secdebug("server", "distributing wakeup event to %ld clients", mPowerClients
.size());
403 for (set
<PowerWatcher
*>::const_iterator it
= mPowerClients
.begin(); it
!= mPowerClients
.end(); it
++)
404 (*it
)->systemIsWaking();
407 void Server::SleepWatcher::add(PowerWatcher
*client
)
409 assert(mPowerClients
.find(client
) == mPowerClients
.end());
410 mPowerClients
.insert(client
);
413 void Server::SleepWatcher::remove(PowerWatcher
*client
)
415 assert(mPowerClients
.find(client
) != mPowerClients
.end());
416 mPowerClients
.erase(client
);
421 // Expose the process/pid map to the outside
423 Process
*Server::findPid(pid_t pid
) const
425 PidMap::const_iterator it
= mPids
.find(pid
);
426 return (it
== mPids
.end()) ? NULL
: it
->second
;
431 // Set delayed shutdown mode
433 void Server::waitForClients(bool waiting
)
435 if (mShutdown
== shuttingDown
) // too late to change your mind now
438 mShutdown
= shutdownDelayed
;
440 mShutdown
= shutdownImmediately
;
445 // Shutdown processing
447 bool Server::beginShutdown()
449 if (mShutdown
!= shutdownDelayed
)
452 secdebug("server", "beginning shutdown with %d client(s)", int(mProcesses
.size()));
453 mShutdown
= shuttingDown
;
455 #if defined(SHUTDOWN_SNITCH)
456 struct Snitch
: public MachServer::Timer
{
457 void action() { Server::active().shutdownSnitch(); }
459 setTimer(new Snitch
, Time::Interval(29)); // right before we get SIGKILLed
466 void Server::eventDone()
468 if (mShutdown
== shuttingDown
) {
469 if (mProcesses
.empty()) {
470 secdebug("SS", "out of clients - shutdown complete");
471 Syslog::notice("securityd has finished serving its clients - terminating now");
474 secdebug("SS", "shutdown in progress - %d process(es) left", int(mProcesses
.size()));
475 IFDUMPING("shutdown", NodeCore::dumpAll());
480 #if defined(SHUTDOWN_SNITCH)
482 void Server::shutdownSnitch()
484 Syslog::notice("29 seconds after shutdown began, securityd still has %d clients:", int(mPids
.size()));
485 for (PidMap::const_iterator it
= mPids
.begin(); it
!= mPids
.end(); ++it
)
486 if (SecCodeRef clientCode
= it
->second
->processCode()) {
487 CFRef
<CFURLRef
> path
;
488 SecCodeCopyPath(clientCode
, kSecCSDefaultFlags
, &path
.aref());
490 Syslog::notice(" %s (%d)", cfString(path
).c_str(), it
->first
);
492 Syslog::notice(" pid=%d", it
->first
);
496 #endif //SHUTDOWN_SNITCH
500 // Initialize the CSSM/MDS subsystem.
501 // This was once done lazily on demand. These days, we are setting up the
502 // system MDS here, and CSSM is pretty much always needed, so this is called
503 // early during program startup. Do note that the server may not (yet) be running.
505 void Server::loadCssm()
507 if (!mCssm
->isActive()) {
508 StLock
<Mutex
> _(*this);
509 if (!mCssm
->isActive()) {
510 secdebug("SS", "Installing MDS");
511 IFDEBUG(if (geteuid() == 0))
512 MDSClient::mds().install();
513 secdebug("SS", "CSSM initializing");
516 secdebug("SS", "CSSM ready with CSP %s", mCSP
->guid().toString().c_str());
523 // LongtermActivity/lock combo
525 LongtermStLock::LongtermStLock(Mutex
&lck
)
526 : StLock
<Mutex
>(lck
, false) // don't take the lock yet
528 if (lck
.tryLock()) { // uncontested
529 this->mActive
= true;
530 } else { // contested - need backup thread
531 Server::active().longTermActivity();