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