]> git.saurik.com Git - apple/securityd.git/blob - src/server.cpp
securityd-36489.tar.gz
[apple/securityd.git] / src / server.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, 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>
39 #include "pcscmonitor.h"
40
41 #include "agentquery.h"
42
43
44 using namespace MachPlusPlus;
45
46 //
47 // Construct an Authority
48 //
49 Authority::Authority(const char *configFile)
50 : Authorization::Engine(configFile)
51 {
52 }
53
54 Authority::~Authority()
55 {
56 }
57
58 //
59 // Construct the server object
60 //
61 Server::Server(Authority &authority, CodeSignatures &signatures, const char *bootstrapName)
62 : MachServer(bootstrapName),
63 mBootstrapName(bootstrapName),
64 mShutdown(shutdownImmediately),
65 mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule),
66 mAuthority(authority),
67 mCodeSignatures(signatures),
68 mAudit(geteuid(), getpid())
69 {
70 // make me eternal (in the object mesh)
71 ref();
72
73 mAudit.registerSession();
74
75 // engage the subsidiary port handler for sleep notifications
76 add(sleepWatcher);
77 }
78
79
80 //
81 // Clean up the server object
82 //
83 Server::~Server()
84 {
85 //@@@ more later
86 }
87
88
89 //
90 // Locate a connection by reply port and make it the current connection
91 // of this thread. The connection will be marked busy, and can be accessed
92 // by calling Server::connection() [no argument] until it is released by
93 // calling Connection::endWork().
94 //
95 Connection &Server::connection(mach_port_t port)
96 {
97 Server &server = active();
98 StLock<Mutex> _(server);
99 Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
100 active().mCurrentConnection() = conn;
101 conn->beginWork();
102 return *conn;
103 }
104
105 Connection &Server::connection(bool tolerant)
106 {
107 Connection *conn = active().mCurrentConnection();
108 assert(conn); // have to have one
109 if (!tolerant)
110 conn->checkWork();
111 return *conn;
112 }
113
114 void Server::requestComplete(CSSM_RETURN &rcode)
115 {
116 // note: there may not be an active connection if connection setup failed
117 if (RefPointer<Connection> &conn = active().mCurrentConnection()) {
118 conn->endWork(rcode);
119 conn = NULL;
120 }
121 IFDUMPING("state", NodeCore::dumpAll());
122 }
123
124
125 //
126 // Shorthand for "current" process and session.
127 // This is the process and session for the current connection.
128 //
129 Process &Server::process()
130 {
131 return connection().process();
132 }
133
134 Session &Server::session()
135 {
136 return connection().process().session();
137 }
138
139 RefPointer<Key> Server::key(KeyHandle key)
140 {
141 return HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
142 }
143
144 RefPointer<Database> Server::database(DbHandle db)
145 {
146 return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
147 }
148
149 RefPointer<KeychainDatabase> Server::keychain(DbHandle db)
150 {
151 return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
152 }
153
154 RefPointer<Database> Server::optionalDatabase(DbHandle db, bool persistent)
155 {
156 if (persistent && db != noDb)
157 return database(db);
158 else
159 return &process().localStore();
160 }
161
162
163 //
164 // Locate an ACL bearer (database or key) by handle
165 //
166 AclSource &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
167 {
168 AclSource &bearer = HandleObject::find<AclSource>(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
169 if (kind != bearer.acl().aclKind())
170 CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE);
171 return bearer;
172 }
173
174
175 //
176 // Run the server. This will not return until the server is forced to exit.
177 //
178 void Server::run()
179 {
180 MachServer::run(0x10000,
181 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
182 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT));
183 }
184
185
186 //
187 // Handle thread overflow. MachServer will call this if it has hit its thread
188 // limit and yet still needs another thread.
189 //
190 void Server::threadLimitReached(UInt32 limit)
191 {
192 Syslog::notice("securityd has reached its thread limit (%ld) - service deadlock is possible",
193 limit);
194 }
195
196
197 //
198 // The primary server run-loop function.
199 // Invokes the MIG-generated main dispatch function (ucsp_server), as well
200 // as the self-send dispatch (self_server).
201 // For debug builds, look up request names in a MIG-generated table
202 // for better debug-log messages.
203 //
204 boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *);
205 boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *);
206
207
208 #if !defined(NDEBUG)
209
210 struct IPCName { const char *name; int ipc; };
211 static IPCName ucspNames[] = { subsystem_to_name_map_ucsp }; // generated by MIG
212 static IPCName selfNames[] = { subsystem_to_name_map_self }; // generated by MIG
213
214 #endif //NDEBUG
215
216 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
217 {
218 #if !defined(NDEBUG)
219 const int id = in->msgh_id;
220 const int ucspBase = ucspNames[0].ipc;
221 const int selfBase = selfNames[0].ipc;
222 const char *name =
223 (id >= ucspBase && id < ucspBase + ucsp_MSG_COUNT) ? ucspNames[id - ucspBase].name :
224 (id >= selfBase && id < selfBase + self_MSG_COUNT) ? selfNames[id - selfBase].name :
225 "OUT OF BOUNDS";
226 secdebug("SSreq", "begin %s (%d)", name, in->msgh_id);
227 #endif //NDEBUG
228
229 boolean_t result = ucsp_server(in, out) || self_server(in, out);
230 IFDEBUG(secdebug("SSreq", "end %s (%d)", name, in->msgh_id));
231
232 return result;
233 }
234
235
236 //
237 // Set up a new Connection. This establishes the environment (process et al) as needed
238 // and registers a properly initialized Connection object to run with.
239 // Type indicates how "deep" we need to initialize (new session, process, or connection).
240 // Everything at and below that level is constructed. This is straight-forward except
241 // in the case of session re-initialization (see below).
242 //
243 void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort,
244 const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity)
245 {
246 // first, make or find the process based on task port
247 StLock<Mutex> _(*this);
248 RefPointer<Process> &proc = mProcesses[taskPort];
249 if (type == connectNewSession && proc) {
250 // The client has talked to us before and now wants to create a new session.
251 proc->changeSession(servicePort);
252 }
253 if (proc && type == connectNewProcess) {
254 // the client has amnesia - reset it
255 assert(info && identity);
256 proc->reset(servicePort, taskPort, info, identity, AuditToken(auditToken));
257 proc->changeSession(servicePort);
258 }
259 if (!proc) {
260 if (type == connectNewThread) // client error (or attack)
261 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
262 assert(info && identity);
263 proc = new Process(servicePort, taskPort, info, identity, AuditToken(auditToken));
264 notifyIfDead(taskPort);
265 mPids[proc->pid()] = proc;
266 }
267
268 // now, establish a connection and register it in the server
269 Connection *connection = new Connection(*proc, replyPort);
270 if (mConnections.contains(replyPort)) // malicious re-entry attempt?
271 CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error)
272 mConnections[replyPort] = connection;
273 notifyIfDead(replyPort);
274 }
275
276
277 //
278 // Synchronously end a Connection.
279 // This is due to a request from the client, so no thread races are possible.
280 // In practice, this is optional since the DPN for the client thread reply port
281 // will destroy the connection anyway when the thread dies.
282 //
283 void Server::endConnection(Port replyPort)
284 {
285 StLock<Mutex> _(*this);
286 PortMap<Connection>::iterator it = mConnections.find(replyPort);
287 assert(it != mConnections.end());
288 it->second->terminate();
289 mConnections.erase(it);
290 }
291
292
293 //
294 // Handling dead-port notifications.
295 // This receives DPNs for all kinds of ports we're interested in.
296 //
297 void Server::notifyDeadName(Port port)
298 {
299 StLock<Mutex> _(*this);
300 secdebug("SSports", "port %d is dead", port.port());
301
302 // is it a connection?
303 PortMap<Connection>::iterator conIt = mConnections.find(port);
304 if (conIt != mConnections.end()) {
305 conIt->second->abort();
306 mConnections.erase(conIt);
307 return;
308 }
309
310 // is it a process?
311 PortMap<Process>::iterator procIt = mProcesses.find(port);
312 if (procIt != mProcesses.end()) {
313 Process *proc = procIt->second;
314 proc->kill();
315 mPids.erase(proc->pid());
316 mProcesses.erase(procIt);
317 return;
318 }
319
320 // well, what IS IT?!
321 secdebug("server", "spurious dead port notification for port %d", port.port());
322 }
323
324
325 //
326 // Handling no-senders notifications.
327 // This is currently only used for (subsidiary) service ports
328 //
329 void Server::notifyNoSenders(Port port, mach_port_mscount_t)
330 {
331 secdebug("SSports", "port %d no senders", port.port());
332 Session::destroy(port);
333 }
334
335
336 //
337 // Handling signals.
338 // These are sent as Mach messages from ourselves to escape the limitations of
339 // the signal handler environment.
340 //
341 kern_return_t self_server_handleSignal(mach_port_t sport,
342 mach_port_t taskPort, int sig)
343 {
344 try {
345 if (taskPort != mach_task_self()) {
346 Syslog::error("handleSignal: received from someone other than myself");
347 secdebug("SS", "unauthorized handleSignal");
348 return KERN_SUCCESS;
349 }
350 secdebug("SS", "dispatching indirect signal %d", sig);
351 switch (sig) {
352 case SIGCHLD:
353 ServerChild::checkChildren();
354 break;
355 case SIGINT:
356 secdebug("SS", "SIGINT received: terminating immediately");
357 Syslog::notice("securityd terminated due to SIGINT");
358 exit(0);
359 case SIGTERM:
360 if (Server::active().beginShutdown()) {
361 Syslog::notice("securityd shutting down; lingering for remaining clients");
362 } else {
363 secdebug("SS", "SIGTERM received: terminating immediately");
364 Syslog::notice("securityd terminated due to SIGTERM");
365 exit(0);
366 }
367 break;
368 case SIGPIPE:
369 secdebug("SS", "SIGPIPE received: ignoring");
370 Syslog::notice("securityd ignoring SIGPIPE received");
371 break;
372
373 #if defined(DEBUGDUMP)
374 case SIGUSR1:
375 NodeCore::dumpAll();
376 break;
377 #endif //DEBUGDUMP
378
379 case SIGUSR2:
380 {
381 extern PCSCMonitor *gPCSC;
382 gPCSC->startSoftTokens();
383 break;
384 }
385
386 default:
387 assert(false);
388 }
389 } catch(...) {
390 secdebug("SS", "exception handling a signal (ignored)");
391 }
392 mach_port_deallocate(mach_task_self(), taskPort);
393 return KERN_SUCCESS;
394 }
395
396
397 //
398 // Notifier for system sleep events
399 //
400 void Server::SleepWatcher::systemWillSleep()
401 {
402 secdebug("SS", "sleep notification received");
403 Session::processSystemSleep();
404 secdebug("server", "distributing sleep event to %ld clients", mPowerClients.size());
405 for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
406 (*it)->systemWillSleep();
407 }
408
409 void Server::SleepWatcher::systemIsWaking()
410 {
411 secdebug("server", "distributing wakeup event to %ld clients", mPowerClients.size());
412 for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++)
413 (*it)->systemIsWaking();
414 }
415
416 void Server::SleepWatcher::add(PowerWatcher *client)
417 {
418 assert(mPowerClients.find(client) == mPowerClients.end());
419 mPowerClients.insert(client);
420 }
421
422 void Server::SleepWatcher::remove(PowerWatcher *client)
423 {
424 assert(mPowerClients.find(client) != mPowerClients.end());
425 mPowerClients.erase(client);
426 }
427
428
429 //
430 // Expose the process/pid map to the outside
431 //
432 Process *Server::findPid(pid_t pid) const
433 {
434 PidMap::const_iterator it = mPids.find(pid);
435 return (it == mPids.end()) ? NULL : it->second;
436 }
437
438
439 //
440 // Set delayed shutdown mode
441 //
442 void Server::waitForClients(bool waiting)
443 {
444 if (mShutdown == shuttingDown) // too late to change your mind now
445 return;
446 if (waiting)
447 mShutdown = shutdownDelayed;
448 else
449 mShutdown = shutdownImmediately;
450 }
451
452
453 //
454 // Shutdown processing
455 //
456 bool Server::beginShutdown()
457 {
458 if (mShutdown != shutdownDelayed)
459 return false;
460
461 secdebug("server", "beginning shutdown with %d client(s)", int(mProcesses.size()));
462 mShutdown = shuttingDown;
463
464 #if defined(SHUTDOWN_SNITCH)
465 struct Snitch : public MachServer::Timer {
466 void action() { Server::active().shutdownSnitch(); }
467 };
468 setTimer(new Snitch, Time::Interval(29)); // right before we get SIGKILLed
469 #endif
470
471 return true;
472 }
473
474
475 void Server::eventDone()
476 {
477 if (mShutdown == shuttingDown) {
478 if (mProcesses.empty()) {
479 secdebug("SS", "out of clients - shutdown complete");
480 Syslog::notice("securityd has finished serving its clients - terminating now");
481 exit(0);
482 } else {
483 secdebug("SS", "shutdown in progress - %d process(es) left", int(mProcesses.size()));
484 IFDUMPING("shutdown", NodeCore::dumpAll());
485 }
486 }
487 }
488
489 #if defined(SHUTDOWN_SNITCH)
490
491 void Server::shutdownSnitch()
492 {
493 Syslog::notice("29 seconds after shutdown began, securityd still has %d clients:", int(mPids.size()));
494 for (PidMap::const_iterator it = mPids.begin(); it != mPids.end(); ++it)
495 if (SecCodeRef clientCode = it->second->processCode()) {
496 CFRef<CFURLRef> path;
497 SecCodeCopyPath(clientCode, kSecCSDefaultFlags, &path.aref());
498 if (path)
499 Syslog::notice(" %s (%d)", cfString(path).c_str(), it->first);
500 else
501 Syslog::notice(" pid=%d", it->first);
502 }
503 }
504
505 #endif //SHUTDOWN_SNITCH
506
507
508 //
509 // Initialize the CSSM/MDS subsystem.
510 // This was once done lazily on demand. These days, we are setting up the
511 // system MDS here, and CSSM is pretty much always needed, so this is called
512 // early during program startup. Do note that the server may not (yet) be running.
513 //
514 void Server::loadCssm()
515 {
516 if (!mCssm->isActive()) {
517 StLock<Mutex> _(*this);
518 if (!mCssm->isActive()) {
519 secdebug("SS", "Installing MDS");
520 IFDEBUG(if (geteuid() == 0))
521 MDSClient::mds().install();
522 secdebug("SS", "CSSM initializing");
523 mCssm->init();
524 mCSP->attach();
525 secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
526 }
527 }
528 }
529
530
531 //
532 // LongtermActivity/lock combo
533 //
534 LongtermStLock::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 }