]> git.saurik.com Git - apple/security.git/blame - securityd/src/server.cpp
Security-58286.70.7.tar.gz
[apple/security.git] / securityd / src / server.cpp
CommitLineData
d8f41ccd
A
1/*
2 * Copyright (c) 2000-2010,2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// server - securityd main server object
27//
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>
32#include "server.h"
33#include "session.h"
34#include "acls.h"
35#include "notifications.h"
36#include "child.h"
37#include <mach/mach_error.h>
38#include <security_utilities/ccaudit.h>
fa7225c8 39#include <security_utilities/casts.h>
d8f41ccd
A
40#include "pcscmonitor.h"
41
42#include "agentquery.h"
43
44
45using namespace MachPlusPlus;
46
d8f41ccd
A
47//
48// Construct the server object
49//
fa7225c8 50Server::Server(CodeSignatures &signatures, const char *bootstrapName)
d8f41ccd
A
51 : MachServer(bootstrapName),
52 mBootstrapName(bootstrapName),
53 mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
fa7225c8 54 mCodeSignatures(signatures),
d8f41ccd
A
55 mVerbosity(0),
56 mWaitForClients(true), mShuttingDown(false)
57{
58 // make me eternal (in the object mesh)
59 ref();
60
61 // engage the subsidiary port handler for sleep notifications
62 add(sleepWatcher);
63}
64
65
66//
67// Clean up the server object
68//
69Server::~Server()
70{
71 //@@@ more later
72}
73
74
75//
76// Locate a connection by reply port and make it the current connection
77// of this thread. The connection will be marked busy, and can be accessed
78// by calling Server::connection() [no argument] until it is released by
79// calling Connection::endWork().
80//
81Connection &Server::connection(mach_port_t port, audit_token_t &auditToken)
82{
83 Server &server = active();
84 StLock<Mutex> _(server);
85 Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
86 conn->process().checkSession(auditToken);
87 active().mCurrentConnection() = conn;
88 conn->beginWork(auditToken);
89 return *conn;
90}
91
92Connection &Server::connection(bool tolerant)
93{
94 Connection *conn = active().mCurrentConnection();
95 assert(conn); // have to have one
96 if (!tolerant)
97 conn->checkWork();
98 return *conn;
99}
100
101void Server::requestComplete(CSSM_RETURN &rcode)
102{
103 // note: there may not be an active connection if connection setup failed
104 if (RefPointer<Connection> &conn = active().mCurrentConnection()) {
105 conn->endWork(rcode);
106 conn = NULL;
107 }
108 IFDUMPING("state", NodeCore::dumpAll());
109}
110
111
112//
113// Shorthand for "current" process and session.
114// This is the process and session for the current connection.
115//
116Process &Server::process()
117{
118 return connection().process();
119}
120
121Session &Server::session()
122{
123 return connection().process().session();
124}
125
126RefPointer<Key> Server::key(KeyHandle key)
127{
128 return U32HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
129}
130
131RefPointer<Database> Server::database(DbHandle db)
132{
133 return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
134}
135
136RefPointer<KeychainDatabase> Server::keychain(DbHandle db)
137{
138 return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
139}
140
141RefPointer<Database> Server::optionalDatabase(DbHandle db, bool persistent)
142{
143 if (persistent && db != noDb)
144 return database(db);
145 else
146 return &process().localStore();
147}
148
149
150//
151// Locate an ACL bearer (database or key) by handle
152// The handle might be used across IPC, so we clamp it accordingly
153//
154AclSource &Server::aclBearer(AclKind kind, U32HandleObject::Handle handle)
155{
156 AclSource &bearer = U32HandleObject::find<AclSource>(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
157 if (kind != bearer.acl().aclKind())
158 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE);
159 return bearer;
160}
161
162
163//
164// Run the server. This will not return until the server is forced to exit.
165//
166void Server::run()
167{
168 MachServer::run(0x10000,
169 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
170 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT));
171}
172
173
174//
175// Handle thread overflow. MachServer will call this if it has hit its thread
176// limit and yet still needs another thread.
177//
178void Server::threadLimitReached(UInt32 limit)
179{
fa7225c8
A
180 Syslog::notice("securityd has reached its thread limit (%d) - service deadlock is possible",
181 (uint32_t) limit);
d8f41ccd
A
182}
183
184
185//
186// The primary server run-loop function.
187// Invokes the MIG-generated main dispatch function (ucsp_server), as well
188// as the self-send dispatch (self_server).
189// For debug builds, look up request names in a MIG-generated table
190// for better debug-log messages.
191//
192boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *);
193boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *);
194
195
196boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
197{
198 return ucsp_server(in, out) || self_server(in, out);
199}
200
201
202//
203// Set up a new Connection. This establishes the environment (process et al) as needed
204// and registers a properly initialized Connection object to run with.
205// Type indicates how "deep" we need to initialize (new session, process, or connection).
206// Everything at and below that level is constructed. This is straight-forward except
207// in the case of session re-initialization (see below).
208//
209void Server::setupConnection(ConnectLevel type, Port replyPort, Port taskPort,
210 const audit_token_t &auditToken, const ClientSetupInfo *info)
211{
fa7225c8 212 Security::CommonCriteria::AuditToken audit(auditToken);
d8f41ccd
A
213
214 // first, make or find the process based on task port
215 StLock<Mutex> _(*this);
216 RefPointer<Process> &proc = mProcesses[taskPort];
217 if (proc && proc->session().sessionId() != audit.sessionId())
218 proc->changeSession(audit.sessionId());
219 if (proc && type == connectNewProcess) {
220 // the client has amnesia - reset it
221 assert(info);
222 proc->reset(taskPort, info, audit);
223 proc->changeSession(audit.sessionId());
224 }
225 if (!proc) {
226 if (type == connectNewThread) // client error (or attack)
227 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
228 assert(info);
229 proc = new Process(taskPort, info, audit);
230 notifyIfDead(taskPort);
231 mPids[proc->pid()] = proc;
232 }
233
234 // now, establish a connection and register it in the server
235 Connection *connection = new Connection(*proc, replyPort);
236 if (mConnections.contains(replyPort)) // malicious re-entry attempt?
237 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error)
238 mConnections[replyPort] = connection;
239 notifyIfDead(replyPort);
240}
241
242
243//
244// Synchronously end a Connection.
245// This is due to a request from the client, so no thread races are possible.
246// In practice, this is optional since the DPN for the client thread reply port
247// will destroy the connection anyway when the thread dies.
248//
249void Server::endConnection(Port replyPort)
250{
251 StLock<Mutex> _(*this);
252 PortMap<Connection>::iterator it = mConnections.find(replyPort);
253 assert(it != mConnections.end());
254 it->second->terminate();
255 mConnections.erase(it);
256}
257
258
259//
260// Handling dead-port notifications.
261// This receives DPNs for all kinds of ports we're interested in.
262//
263void Server::notifyDeadName(Port port)
264{
265 // We need the lock to get a proper iterator on mConnections or mProcesses,
266 // but must release it before we call abort or kill, as these might take
267 // unbounded time, including calls out to token daemons etc.
268
269 StLock<Mutex> serverLock(*this);
b04fe171 270
d8f41ccd
A
271 // is it a connection?
272 PortMap<Connection>::iterator conIt = mConnections.find(port);
273 if (conIt != mConnections.end()) {
b04fe171 274 secinfo("SS", "%p dead connection %d", this, port.port());
d8f41ccd
A
275 RefPointer<Connection> con = conIt->second;
276 mConnections.erase(conIt);
277 serverLock.unlock();
278 con->abort();
279 return;
280 }
281
282 // is it a process?
283 PortMap<Process>::iterator procIt = mProcesses.find(port);
284 if (procIt != mProcesses.end()) {
b04fe171 285 secinfo("SS", "%p dead process %d", this, port.port());
d8f41ccd
A
286 RefPointer<Process> proc = procIt->second;
287 mPids.erase(proc->pid());
288 mProcesses.erase(procIt);
289 serverLock.unlock();
290 // The kill may take some time; make sure there is a spare thread around
291 // to prevent deadlocks
292 StLock<MachServer, &Server::busy, &Server::idle> _(*this);
293 proc->kill();
294 return;
295 }
296
297 // well, what IS IT?!
fa7225c8 298 secnotice("server", "spurious dead port notification for port %d", port.port());
d8f41ccd
A
299}
300
301
302//
303// Handling no-senders notifications.
304// This is currently only used for (subsidiary) service ports
305//
306void Server::notifyNoSenders(Port port, mach_port_mscount_t)
307{
b04fe171 308 secinfo("SS", "%p dead session %d", this, port.port());
d8f41ccd
A
309}
310
311
312//
313// Handling signals.
314// These are sent as Mach messages from ourselves to escape the limitations of
315// the signal handler environment.
316//
317kern_return_t self_server_handleSignal(mach_port_t sport,
318 mach_port_t taskPort, int sig)
319{
320 try {
fa7225c8 321 secnotice("SS", "signal handled %d", sig);
d8f41ccd
A
322 if (taskPort != mach_task_self()) {
323 Syslog::error("handleSignal: received from someone other than myself");
324 return KERN_SUCCESS;
325 }
326 switch (sig) {
327 case SIGCHLD:
328 ServerChild::checkChildren();
329 break;
330 case SIGINT:
fa7225c8 331 secnotice("SS", "shutdown due to SIGINT");
d8f41ccd
A
332 Syslog::notice("securityd terminated due to SIGINT");
333 _exit(0);
334 case SIGTERM:
335 Server::active().beginShutdown();
336 break;
337 case SIGPIPE:
338 fprintf(stderr, "securityd ignoring SIGPIPE received");
339 break;
340
341#if defined(DEBUGDUMP)
342 case SIGUSR1:
343 NodeCore::dumpAll();
344 break;
345#endif //DEBUGDUMP
346
347 case SIGUSR2:
348 {
349 extern PCSCMonitor *gPCSC;
350 gPCSC->startSoftTokens();
351 break;
352 }
353
354 default:
355 assert(false);
356 }
357 } catch(...) {
fa7225c8 358 secnotice("SS", "exception handling a signal (ignored)");
d8f41ccd
A
359 }
360 mach_port_deallocate(mach_task_self(), taskPort);
361 return KERN_SUCCESS;
362}
363
364
365kern_return_t self_server_handleSession(mach_port_t sport,
366 mach_port_t taskPort, uint32_t event, uint64_t ident)
367{
368 try {
369 if (taskPort != mach_task_self()) {
370 Syslog::error("handleSession: received from someone other than myself");
371 return KERN_SUCCESS;
372 }
fa7225c8
A
373 if (event == AUE_SESSION_END)
374 Session::destroy(int_cast<uint64_t, Session::SessionId>(ident));
d8f41ccd 375 } catch(...) {
fa7225c8 376 secnotice("SS", "exception handling a signal (ignored)");
d8f41ccd
A
377 }
378 mach_port_deallocate(mach_task_self(), taskPort);
379 return KERN_SUCCESS;
380}
381
382
383//
384// Notifier for system sleep events
385//
386void Server::SleepWatcher::systemWillSleep()
387{
fa7225c8 388 secnotice("SS", "%p will sleep", this);
d8f41ccd
A
389 Session::processSystemSleep();
390 for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
391 (*it)->systemWillSleep();
392}
393
394void Server::SleepWatcher::systemIsWaking()
395{
fa7225c8 396 secnotice("SS", "%p is waking", this);
d8f41ccd
A
397 for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
398 (*it)->systemIsWaking();
399}
400
401void Server::SleepWatcher::systemWillPowerOn()
402{
fa7225c8 403 secnotice("SS", "%p will power on", this);
d8f41ccd
A
404 Server::active().longTermActivity();
405 for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
406 (*it)->systemWillPowerOn();
407}
408
409void Server::SleepWatcher::add(PowerWatcher *client)
410{
411 assert(mPowerClients.find(client) == mPowerClients.end());
412 mPowerClients.insert(client);
413}
414
415void Server::SleepWatcher::remove(PowerWatcher *client)
416{
417 assert(mPowerClients.find(client) != mPowerClients.end());
418 mPowerClients.erase(client);
419}
420
421
422//
423// Expose the process/pid map to the outside
424//
425Process *Server::findPid(pid_t pid) const
426{
427 PidMap::const_iterator it = mPids.find(pid);
428 return (it == mPids.end()) ? NULL : it->second;
429}
430
431
432//
433// Set delayed shutdown mode
434//
435void Server::waitForClients(bool waiting)
436{
437 mWaitForClients = waiting;
438}
439
440
441//
442// Begin shutdown processing.
443// We relinquish our primary state authority. From now on, we'll be
444// kept alive (only) by our current clients.
445//
446static FILE *reportFile;
447
448void Server::beginShutdown()
449{
450 StLock<Mutex> _(*this);
451 if (!mWaitForClients) {
fa7225c8 452 secnotice("SS", "%p shutting down now", this);
d8f41ccd
A
453 _exit(0);
454 } else {
455 if (!mShuttingDown) {
456 mShuttingDown = true;
457 Session::invalidateAuthHosts();
fa7225c8 458 secnotice("SS", "%p beginning shutdown", this);
d8f41ccd
A
459 if (verbosity() >= 2) {
460 reportFile = fopen("/var/log/securityd-shutdown.log", "w");
461 shutdownSnitch();
462 }
463 }
464 }
465}
466
467
468//
469// During shutdown, we report residual clients to dtrace, and allow a state dump
470// for debugging.
471// We don't bother locking for the shuttingDown() check; it's a latching boolean
472// and we'll be good enough without a lock.
473//
474void Server::eventDone()
475{
476 if (this->shuttingDown()) {
477 StLock<Mutex> lazy(*this, false); // lazy lock acquisition
d8f41ccd
A
478 if (verbosity() >= 2) {
479 lazy.lock();
fa7225c8 480 secnotice("SS", "shutting down with %ld processes and %ld transactions", mProcesses.size(), VProc::Transaction::debugCount());
d8f41ccd
A
481 shutdownSnitch();
482 }
483 IFDUMPING("shutdown", NodeCore::dumpAll());
484 }
485}
486
487
488void Server::shutdownSnitch()
489{
490 time_t now;
491 time(&now);
492 fprintf(reportFile, "%.24s %d residual clients:\n", ctime(&now), int(mPids.size()));
fa7225c8
A
493 for (PidMap::const_iterator it = mPids.begin(); it != mPids.end(); ++it) {
494 string path = it->second->getPath();
495 fprintf(reportFile, " %s (%d)\n", path.c_str(), it->first);
496 }
d8f41ccd
A
497 fprintf(reportFile, "\n");
498 fflush(reportFile);
499}
500
501bool Server::inDarkWake()
502{
503 return IOPMIsADarkWake(IOPMConnectionGetSystemCapabilities());
504}
505
506//
507// Initialize the CSSM/MDS subsystem.
508// This was once done lazily on demand. These days, we are setting up the
509// system MDS here, and CSSM is pretty much always needed, so this is called
510// early during program startup. Do note that the server may not (yet) be running.
511//
512void Server::loadCssm(bool mdsIsInstalled)
513{
514 if (!mCssm->isActive()) {
515 StLock<Mutex> _(*this);
516 VProc::Transaction xact;
517 if (!mCssm->isActive()) {
518 if (!mdsIsInstalled) { // non-system securityd instance should not reinitialize MDS
fa7225c8 519 secnotice("SS", "Installing MDS");
d8f41ccd
A
520 IFDEBUG(if (geteuid() == 0))
521 MDSClient::mds().install();
522 }
fa7225c8 523 secnotice("SS", "CSSM initializing");
d8f41ccd
A
524 mCssm->init();
525 mCSP->attach();
fa7225c8 526 secnotice("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
d8f41ccd
A
527 }
528 }
529}
530
531
532//
533// LongtermActivity/lock combo
534//
535LongtermStLock::LongtermStLock(Mutex &lck)
536 : StLock<Mutex>(lck, false) // don't take the lock yet
537{
538 if (lck.tryLock()) { // uncontested
539 this->mActive = true;
540 } else { // contested - need backup thread
541 Server::active().longTermActivity();
542 this->lock();
543 }
544}