]>
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 | { | |
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 | // | |
116 | Process &Server::process() | |
117 | { | |
118 | return connection().process(); | |
119 | } | |
120 | ||
121 | Session &Server::session() | |
122 | { | |
123 | return connection().process().session(); | |
124 | } | |
125 | ||
126 | RefPointer<Key> Server::key(KeyHandle key) | |
127 | { | |
128 | return U32HandleObject::findRef<Key>(key, CSSMERR_CSP_INVALID_KEY_REFERENCE); | |
129 | } | |
130 | ||
131 | RefPointer<Database> Server::database(DbHandle db) | |
132 | { | |
133 | return find<Database>(db, CSSMERR_DL_INVALID_DB_HANDLE); | |
134 | } | |
135 | ||
136 | RefPointer<KeychainDatabase> Server::keychain(DbHandle db) | |
137 | { | |
138 | return find<KeychainDatabase>(db, CSSMERR_DL_INVALID_DB_HANDLE); | |
139 | } | |
140 | ||
141 | RefPointer<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 | // | |
154 | AclSource &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 | // | |
166 | void 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 | // | |
178 | void 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 | // | |
192 | boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *); | |
193 | boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *); | |
194 | ||
195 | ||
196 | boolean_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 | // | |
209 | void 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 | // | |
249 | void 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 | // | |
263 | void 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 | // | |
306 | void 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 | // | |
317 | kern_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 | ||
365 | kern_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 | // | |
386 | void 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 | ||
394 | void 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 | ||
401 | void 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 | ||
409 | void Server::SleepWatcher::add(PowerWatcher *client) | |
410 | { | |
411 | assert(mPowerClients.find(client) == mPowerClients.end()); | |
412 | mPowerClients.insert(client); | |
413 | } | |
414 | ||
415 | void 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 | // | |
425 | Process *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 | // | |
435 | void 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 | // | |
446 | static FILE *reportFile; | |
447 | ||
448 | void 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 | // | |
474 | void 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 | ||
488 | void 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 | ||
501 | bool 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 | // | |
512 | void 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 | // | |
535 | LongtermStLock::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 | } |