]> git.saurik.com Git - apple/securityd.git/blob - src/server.cpp
securityd-32661.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
40 #include "agentquery.h"
41
42
43 using namespace MachPlusPlus;
44
45 //
46 // Construct an Authority
47 //
48 Authority::Authority(const char *configFile)
49 : Authorization::Engine(configFile)
50 {
51 }
52
53 Authority::~Authority()
54 {
55 }
56
57 //
58 // Construct the server object
59 //
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())
68 {
69 // make me eternal (in the object mesh)
70 ref();
71
72 mAudit.registerSession();
73
74 // engage the subsidiary port handler for sleep notifications
75 add(sleepWatcher);
76 }
77
78
79 //
80 // Clean up the server object
81 //
82 Server::~Server()
83 {
84 //@@@ more later
85 }
86
87
88 //
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().
93 //
94 Connection &Server::connection(mach_port_t port)
95 {
96 Server &server = active();
97 StLock<Mutex> _(server);
98 Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE);
99 active().mCurrentConnection() = conn;
100 conn->beginWork();
101 return *conn;
102 }
103
104 Connection &Server::connection(bool tolerant)
105 {
106 Connection *conn = active().mCurrentConnection();
107 assert(conn); // have to have one
108 if (!tolerant)
109 conn->checkWork();
110 return *conn;
111 }
112
113 void Server::requestComplete(CSSM_RETURN &rcode)
114 {
115 // note: there may not be an active connection if connection setup failed
116 if (RefPointer<Connection> &conn = active().mCurrentConnection()) {
117 conn->endWork(rcode);
118 conn = NULL;
119 }
120 IFDUMPING("state", NodeCore::dumpAll());
121 }
122
123
124 //
125 // Shorthand for "current" process and session.
126 // This is the process and session for the current connection.
127 //
128 Process &Server::process()
129 {
130 return connection().process();
131 }
132
133 Session &Server::session()
134 {
135 return connection().process().session();
136 }
137
138 RefPointer<Key> Server::key(KeyHandle key)
139 {
140 return HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE);
141 }
142
143 RefPointer<Database> Server::database(DbHandle db)
144 {
145 return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE);
146 }
147
148 RefPointer<KeychainDatabase> Server::keychain(DbHandle db)
149 {
150 return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE);
151 }
152
153 RefPointer<Database> Server::optionalDatabase(DbHandle db, bool persistent)
154 {
155 if (persistent && db != noDb)
156 return database(db);
157 else
158 return &process().localStore();
159 }
160
161
162 //
163 // Locate an ACL bearer (database or key) by handle
164 //
165 AclSource &Server::aclBearer(AclKind kind, CSSM_HANDLE handle)
166 {
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);
170 return bearer;
171 }
172
173
174 //
175 // Run the server. This will not return until the server is forced to exit.
176 //
177 void Server::run()
178 {
179 MachServer::run(0x10000,
180 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
181 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT));
182 }
183
184
185 //
186 // Handle thread overflow. MachServer will call this if it has hit its thread
187 // limit and yet still needs another thread.
188 //
189 void Server::threadLimitReached(UInt32 limit)
190 {
191 Syslog::notice("securityd has reached its thread limit (%ld) - service deadlock is possible",
192 limit);
193 }
194
195
196 //
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.
202 //
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 *);
205
206
207 #if !defined(NDEBUG)
208
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
212
213 #endif //NDEBUG
214
215 boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out)
216 {
217 #if !defined(NDEBUG)
218 const int id = in->msgh_id;
219 const int ucspBase = ucspNames[0].ipc;
220 const int selfBase = selfNames[0].ipc;
221 const char *name =
222 (id >= ucspBase && id < ucspBase + ucsp_MSG_COUNT) ? ucspNames[id - ucspBase].name :
223 (id >= selfBase && id < selfBase + self_MSG_COUNT) ? selfNames[id - selfBase].name :
224 "OUT OF BOUNDS";
225 secdebug("SSreq", "begin %s (%d)", name, in->msgh_id);
226 #endif //NDEBUG
227
228 boolean_t result = ucsp_server(in, out) || self_server(in, out);
229 IFDEBUG(secdebug("SSreq", "end %s (%d)", name, in->msgh_id));
230
231 return result;
232 }
233
234
235 //
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).
241 //
242 void Server::setupConnection(ConnectLevel type, Port servicePort, Port replyPort, Port taskPort,
243 const audit_token_t &auditToken, const ClientSetupInfo *info, const char *identity)
244 {
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);
251 }
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);
257 }
258 if (!proc) {
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;
265 }
266
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);
273 }
274
275
276 //
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.
281 //
282 void Server::endConnection(Port replyPort)
283 {
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);
289 }
290
291
292 //
293 // Handling dead-port notifications.
294 // This receives DPNs for all kinds of ports we're interested in.
295 //
296 void Server::notifyDeadName(Port port)
297 {
298 StLock<Mutex> _(*this);
299 secdebug("SSports", "port %d is dead", port.port());
300
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);
306 return;
307 }
308
309 // is it a process?
310 PortMap<Process>::iterator procIt = mProcesses.find(port);
311 if (procIt != mProcesses.end()) {
312 Process *proc = procIt->second;
313 proc->kill();
314 mPids.erase(proc->pid());
315 mProcesses.erase(procIt);
316 return;
317 }
318
319 // well, what IS IT?!
320 secdebug("server", "spurious dead port notification for port %d", port.port());
321 }
322
323
324 //
325 // Handling no-senders notifications.
326 // This is currently only used for (subsidiary) service ports
327 //
328 void Server::notifyNoSenders(Port port, mach_port_mscount_t)
329 {
330 secdebug("SSports", "port %d no senders", port.port());
331 Session::destroy(port);
332 }
333
334
335 //
336 // Handling signals.
337 // These are sent as Mach messages from ourselves to escape the limitations of
338 // the signal handler environment.
339 //
340 kern_return_t self_server_handleSignal(mach_port_t sport,
341 mach_port_t taskPort, int sig)
342 {
343 try {
344 if (taskPort != mach_task_self()) {
345 Syslog::error("handleSignal: received from someone other than myself");
346 secdebug("SS", "unauthorized handleSignal");
347 return KERN_SUCCESS;
348 }
349 secdebug("SS", "dispatching indirect signal %d", sig);
350 switch (sig) {
351 case SIGCHLD:
352 ServerChild::checkChildren();
353 break;
354 case SIGINT:
355 secdebug("SS", "SIGINT received: terminating immediately");
356 Syslog::notice("securityd terminated due to SIGINT");
357 exit(0);
358 case SIGTERM:
359 if (Server::active().beginShutdown()) {
360 Syslog::notice("securityd shutting down; lingering for remaining clients");
361 } else {
362 secdebug("SS", "SIGTERM received: terminating immediately");
363 Syslog::notice("securityd terminated due to SIGTERM");
364 exit(0);
365 }
366 break;
367 case SIGPIPE:
368 secdebug("SS", "SIGPIPE received: ignoring");
369 Syslog::notice("securityd ignoring SIGPIPE received");
370 break;
371
372 #if defined(DEBUGDUMP)
373 case SIGUSR1:
374 NodeCore::dumpAll();
375 break;
376 #endif //DEBUGDUMP
377 default:
378 assert(false);
379 }
380 } catch(...) {
381 secdebug("SS", "exception handling a signal (ignored)");
382 }
383 mach_port_deallocate(mach_task_self(), taskPort);
384 return KERN_SUCCESS;
385 }
386
387
388 //
389 // Notifier for system sleep events
390 //
391 void Server::SleepWatcher::systemWillSleep()
392 {
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();
398 }
399
400 void Server::SleepWatcher::systemIsWaking()
401 {
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();
405 }
406
407 void Server::SleepWatcher::add(PowerWatcher *client)
408 {
409 assert(mPowerClients.find(client) == mPowerClients.end());
410 mPowerClients.insert(client);
411 }
412
413 void Server::SleepWatcher::remove(PowerWatcher *client)
414 {
415 assert(mPowerClients.find(client) != mPowerClients.end());
416 mPowerClients.erase(client);
417 }
418
419
420 //
421 // Expose the process/pid map to the outside
422 //
423 Process *Server::findPid(pid_t pid) const
424 {
425 PidMap::const_iterator it = mPids.find(pid);
426 return (it == mPids.end()) ? NULL : it->second;
427 }
428
429
430 //
431 // Set delayed shutdown mode
432 //
433 void Server::waitForClients(bool waiting)
434 {
435 if (mShutdown == shuttingDown) // too late to change your mind now
436 return;
437 if (waiting)
438 mShutdown = shutdownDelayed;
439 else
440 mShutdown = shutdownImmediately;
441 }
442
443
444 //
445 // Shutdown processing
446 //
447 bool Server::beginShutdown()
448 {
449 if (mShutdown != shutdownDelayed)
450 return false;
451
452 secdebug("server", "beginning shutdown with %d client(s)", int(mProcesses.size()));
453 mShutdown = shuttingDown;
454
455 #if defined(SHUTDOWN_SNITCH)
456 struct Snitch : public MachServer::Timer {
457 void action() { Server::active().shutdownSnitch(); }
458 };
459 setTimer(new Snitch, Time::Interval(29)); // right before we get SIGKILLed
460 #endif
461
462 return true;
463 }
464
465
466 void Server::eventDone()
467 {
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");
472 exit(0);
473 } else {
474 secdebug("SS", "shutdown in progress - %d process(es) left", int(mProcesses.size()));
475 IFDUMPING("shutdown", NodeCore::dumpAll());
476 }
477 }
478 }
479
480 #if defined(SHUTDOWN_SNITCH)
481
482 void Server::shutdownSnitch()
483 {
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());
489 if (path)
490 Syslog::notice(" %s (%d)", cfString(path).c_str(), it->first);
491 else
492 Syslog::notice(" pid=%d", it->first);
493 }
494 }
495
496 #endif //SHUTDOWN_SNITCH
497
498
499 //
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.
504 //
505 void Server::loadCssm()
506 {
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");
514 mCssm->init();
515 mCSP->attach();
516 secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str());
517 }
518 }
519 }
520
521
522 //
523 // LongtermActivity/lock combo
524 //
525 LongtermStLock::LongtermStLock(Mutex &lck)
526 : StLock<Mutex>(lck, false) // don't take the lock yet
527 {
528 if (lck.tryLock()) { // uncontested
529 this->mActive = true;
530 } else { // contested - need backup thread
531 Server::active().longTermActivity();
532 this->lock();
533 }
534 }