]>
Commit | Line | Data |
---|---|---|
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 | ||
45 | using namespace MachPlusPlus; | |
46 | ||
d8f41ccd A |
47 | // |
48 | // Construct the server object | |
49 | // | |
fa7225c8 | 50 | Server::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 | // | |
69 | Server::~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 | // | |
81 | Connection &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 | ||
92 | Connection &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 | ||
101 | void 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 | // | |
118 | Process &Server::process() | |
119 | { | |
120 | return connection().process(); | |
121 | } | |
122 | ||
123 | Session &Server::session() | |
124 | { | |
125 | return connection().process().session(); | |
126 | } | |
127 | ||
128 | RefPointer<Key> Server::key(KeyHandle key) | |
129 | { | |
130 | return U32HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE); | |
131 | } | |
132 | ||
133 | RefPointer<Database> Server::database(DbHandle db) | |
134 | { | |
135 | return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE); | |
136 | } | |
137 | ||
138 | RefPointer<KeychainDatabase> Server::keychain(DbHandle db) | |
139 | { | |
140 | return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE); | |
141 | } | |
142 | ||
143 | RefPointer<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 | // | |
156 | AclSource &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 | // | |
168 | void 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 | // | |
180 | void 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 | // | |
194 | boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *); | |
195 | boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *); | |
196 | ||
197 | ||
198 | boolean_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 | // | |
211 | void 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 | ||
244 | ||
245 | // | |
246 | // Synchronously end a Connection. | |
247 | // This is due to a request from the client, so no thread races are possible. | |
248 | // In practice, this is optional since the DPN for the client thread reply port | |
249 | // will destroy the connection anyway when the thread dies. | |
250 | // | |
251 | void Server::endConnection(Port replyPort) | |
252 | { | |
253 | StLock<Mutex> _(*this); | |
254 | PortMap<Connection>::iterator it = mConnections.find(replyPort); | |
255 | assert(it != mConnections.end()); | |
256 | it->second->terminate(); | |
257 | mConnections.erase(it); | |
258 | } | |
259 | ||
260 | ||
261 | // | |
262 | // Handling dead-port notifications. | |
263 | // This receives DPNs for all kinds of ports we're interested in. | |
264 | // | |
265 | void Server::notifyDeadName(Port port) | |
266 | { | |
267 | // We need the lock to get a proper iterator on mConnections or mProcesses, | |
268 | // but must release it before we call abort or kill, as these might take | |
269 | // unbounded time, including calls out to token daemons etc. | |
270 | ||
271 | StLock<Mutex> serverLock(*this); | |
b04fe171 | 272 | |
d8f41ccd A |
273 | // is it a connection? |
274 | PortMap<Connection>::iterator conIt = mConnections.find(port); | |
275 | if (conIt != mConnections.end()) { | |
79b9da22 | 276 | secinfo("SecServer", "%p dead connection %d", this, port.port()); |
d8f41ccd A |
277 | RefPointer<Connection> con = conIt->second; |
278 | mConnections.erase(conIt); | |
279 | serverLock.unlock(); | |
280 | con->abort(); | |
281 | return; | |
282 | } | |
283 | ||
284 | // is it a process? | |
285 | PortMap<Process>::iterator procIt = mProcesses.find(port); | |
286 | if (procIt != mProcesses.end()) { | |
79b9da22 | 287 | secinfo("SecServer", "%p dead process %d", this, port.port()); |
d8f41ccd A |
288 | RefPointer<Process> proc = procIt->second; |
289 | mPids.erase(proc->pid()); | |
290 | mProcesses.erase(procIt); | |
291 | serverLock.unlock(); | |
292 | // The kill may take some time; make sure there is a spare thread around | |
293 | // to prevent deadlocks | |
294 | StLock<MachServer, &Server::busy, &Server::idle> _(*this); | |
295 | proc->kill(); | |
296 | return; | |
297 | } | |
298 | ||
299 | // well, what IS IT?! | |
fa7225c8 | 300 | secnotice("server", "spurious dead port notification for port %d", port.port()); |
d8f41ccd A |
301 | } |
302 | ||
303 | ||
304 | // | |
305 | // Handling no-senders notifications. | |
306 | // This is currently only used for (subsidiary) service ports | |
307 | // | |
308 | void Server::notifyNoSenders(Port port, mach_port_mscount_t) | |
309 | { | |
79b9da22 | 310 | secinfo("SecServer", "%p dead session %d", this, port.port()); |
d8f41ccd A |
311 | } |
312 | ||
313 | ||
314 | // | |
315 | // Handling signals. | |
316 | // These are sent as Mach messages from ourselves to escape the limitations of | |
317 | // the signal handler environment. | |
318 | // | |
319 | kern_return_t self_server_handleSignal(mach_port_t sport, | |
320 | mach_port_t taskPort, int sig) | |
321 | { | |
322 | try { | |
79b9da22 | 323 | secnotice("SecServer", "signal handled %d", sig); |
d8f41ccd A |
324 | if (taskPort != mach_task_self()) { |
325 | Syslog::error("handleSignal: received from someone other than myself"); | |
326 | return KERN_SUCCESS; | |
327 | } | |
328 | switch (sig) { | |
329 | case SIGCHLD: | |
330 | ServerChild::checkChildren(); | |
331 | break; | |
332 | case SIGINT: | |
79b9da22 | 333 | secnotice("SecServer", "shutdown due to SIGINT"); |
d8f41ccd A |
334 | Syslog::notice("securityd terminated due to SIGINT"); |
335 | _exit(0); | |
336 | case SIGTERM: | |
337 | Server::active().beginShutdown(); | |
338 | break; | |
339 | case SIGPIPE: | |
340 | fprintf(stderr, "securityd ignoring SIGPIPE received"); | |
341 | break; | |
342 | ||
343 | #if defined(DEBUGDUMP) | |
344 | case SIGUSR1: | |
345 | NodeCore::dumpAll(); | |
346 | break; | |
347 | #endif //DEBUGDUMP | |
348 | ||
349 | case SIGUSR2: | |
350 | { | |
351 | extern PCSCMonitor *gPCSC; | |
352 | gPCSC->startSoftTokens(); | |
353 | break; | |
354 | } | |
355 | ||
356 | default: | |
357 | assert(false); | |
358 | } | |
359 | } catch(...) { | |
79b9da22 | 360 | secnotice("SecServer", "exception handling a signal (ignored)"); |
d8f41ccd A |
361 | } |
362 | mach_port_deallocate(mach_task_self(), taskPort); | |
363 | return KERN_SUCCESS; | |
364 | } | |
365 | ||
366 | ||
367 | kern_return_t self_server_handleSession(mach_port_t sport, | |
368 | mach_port_t taskPort, uint32_t event, uint64_t ident) | |
369 | { | |
370 | try { | |
371 | if (taskPort != mach_task_self()) { | |
372 | Syslog::error("handleSession: received from someone other than myself"); | |
79b9da22 | 373 | mach_port_deallocate(mach_task_self(), taskPort); |
d8f41ccd A |
374 | return KERN_SUCCESS; |
375 | } | |
fa7225c8 A |
376 | if (event == AUE_SESSION_END) |
377 | Session::destroy(int_cast<uint64_t, Session::SessionId>(ident)); | |
d8f41ccd | 378 | } catch(...) { |
79b9da22 | 379 | secnotice("SecServer", "exception handling a signal (ignored)"); |
d8f41ccd A |
380 | } |
381 | mach_port_deallocate(mach_task_self(), taskPort); | |
382 | return KERN_SUCCESS; | |
383 | } | |
384 | ||
385 | ||
386 | // | |
387 | // Notifier for system sleep events | |
388 | // | |
389 | void Server::SleepWatcher::systemWillSleep() | |
390 | { | |
79b9da22 | 391 | secnotice("SecServer", "%p will sleep", this); |
d8f41ccd A |
392 | Session::processSystemSleep(); |
393 | for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) | |
394 | (*it)->systemWillSleep(); | |
395 | } | |
396 | ||
397 | void Server::SleepWatcher::systemIsWaking() | |
398 | { | |
79b9da22 | 399 | secnotice("SecServer", "%p is waking", this); |
d8f41ccd A |
400 | for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) |
401 | (*it)->systemIsWaking(); | |
402 | } | |
403 | ||
404 | void Server::SleepWatcher::systemWillPowerOn() | |
405 | { | |
79b9da22 | 406 | secnotice("SecServer", "%p will power on", this); |
d8f41ccd A |
407 | Server::active().longTermActivity(); |
408 | for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) | |
409 | (*it)->systemWillPowerOn(); | |
410 | } | |
411 | ||
412 | void Server::SleepWatcher::add(PowerWatcher *client) | |
413 | { | |
414 | assert(mPowerClients.find(client) == mPowerClients.end()); | |
415 | mPowerClients.insert(client); | |
416 | } | |
417 | ||
418 | void Server::SleepWatcher::remove(PowerWatcher *client) | |
419 | { | |
420 | assert(mPowerClients.find(client) != mPowerClients.end()); | |
421 | mPowerClients.erase(client); | |
422 | } | |
423 | ||
424 | ||
425 | // | |
426 | // Expose the process/pid map to the outside | |
427 | // | |
428 | Process *Server::findPid(pid_t pid) const | |
429 | { | |
430 | PidMap::const_iterator it = mPids.find(pid); | |
431 | return (it == mPids.end()) ? NULL : it->second; | |
432 | } | |
433 | ||
434 | ||
435 | // | |
436 | // Set delayed shutdown mode | |
437 | // | |
438 | void Server::waitForClients(bool waiting) | |
439 | { | |
440 | mWaitForClients = waiting; | |
441 | } | |
442 | ||
443 | ||
444 | // | |
445 | // Begin shutdown processing. | |
446 | // We relinquish our primary state authority. From now on, we'll be | |
447 | // kept alive (only) by our current clients. | |
448 | // | |
449 | static FILE *reportFile; | |
450 | ||
451 | void Server::beginShutdown() | |
452 | { | |
453 | StLock<Mutex> _(*this); | |
454 | if (!mWaitForClients) { | |
79b9da22 | 455 | secnotice("SecServer", "%p shutting down now", this); |
d8f41ccd A |
456 | _exit(0); |
457 | } else { | |
458 | if (!mShuttingDown) { | |
459 | mShuttingDown = true; | |
460 | Session::invalidateAuthHosts(); | |
79b9da22 | 461 | secnotice("SecServer", "%p beginning shutdown", this); |
d8f41ccd A |
462 | if (verbosity() >= 2) { |
463 | reportFile = fopen("/var/log/securityd-shutdown.log", "w"); | |
464 | shutdownSnitch(); | |
465 | } | |
466 | } | |
467 | } | |
468 | } | |
469 | ||
470 | ||
471 | // | |
472 | // During shutdown, we report residual clients to dtrace, and allow a state dump | |
473 | // for debugging. | |
474 | // We don't bother locking for the shuttingDown() check; it's a latching boolean | |
475 | // and we'll be good enough without a lock. | |
476 | // | |
477 | void Server::eventDone() | |
478 | { | |
79b9da22 | 479 | StLock<Mutex> lock(*this); |
d8f41ccd | 480 | if (this->shuttingDown()) { |
d8f41ccd | 481 | if (verbosity() >= 2) { |
79b9da22 | 482 | secnotice("SecServer", "shutting down with %ld processes and %ld transactions", mProcesses.size(), VProc::Transaction::debugCount()); |
d8f41ccd A |
483 | shutdownSnitch(); |
484 | } | |
485 | IFDUMPING("shutdown", NodeCore::dumpAll()); | |
486 | } | |
487 | } | |
488 | ||
489 | ||
490 | void Server::shutdownSnitch() | |
491 | { | |
492 | time_t now; | |
493 | time(&now); | |
494 | fprintf(reportFile, "%.24s %d residual clients:\n", ctime(&now), int(mPids.size())); | |
fa7225c8 A |
495 | for (PidMap::const_iterator it = mPids.begin(); it != mPids.end(); ++it) { |
496 | string path = it->second->getPath(); | |
497 | fprintf(reportFile, " %s (%d)\n", path.c_str(), it->first); | |
498 | } | |
d8f41ccd A |
499 | fprintf(reportFile, "\n"); |
500 | fflush(reportFile); | |
501 | } | |
502 | ||
503 | bool Server::inDarkWake() | |
504 | { | |
505 | return IOPMIsADarkWake(IOPMConnectionGetSystemCapabilities()); | |
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(bool mdsIsInstalled) | |
515 | { | |
516 | if (!mCssm->isActive()) { | |
517 | StLock<Mutex> _(*this); | |
518 | VProc::Transaction xact; | |
519 | if (!mCssm->isActive()) { | |
520 | if (!mdsIsInstalled) { // non-system securityd instance should not reinitialize MDS | |
79b9da22 | 521 | secnotice("SecServer", "Installing MDS"); |
d8f41ccd A |
522 | IFDEBUG(if (geteuid() == 0)) |
523 | MDSClient::mds().install(); | |
524 | } | |
79b9da22 | 525 | secnotice("SecServer", "CSSM initializing"); |
d8f41ccd A |
526 | mCssm->init(); |
527 | mCSP->attach(); | |
79b9da22 | 528 | secnotice("SecServer", "CSSM ready with CSP %s", mCSP->guid().toString().c_str()); |
d8f41ccd A |
529 | } |
530 | } | |
531 | } | |
532 | ||
533 | ||
534 | // | |
535 | // LongtermActivity/lock combo | |
536 | // | |
537 | LongtermStLock::LongtermStLock(Mutex &lck) | |
538 | : StLock<Mutex>(lck, false) // don't take the lock yet | |
539 | { | |
540 | if (lck.tryLock()) { // uncontested | |
541 | this->mActive = true; | |
542 | } else { // contested - need backup thread | |
543 | Server::active().longTermActivity(); | |
544 | this->lock(); | |
545 | } | |
546 | } |