]>
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 | ||
d8f41ccd A |
244 | // |
245 | // Handling dead-port notifications. | |
246 | // This receives DPNs for all kinds of ports we're interested in. | |
247 | // | |
248 | void 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 | // | |
290 | void 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 | // | |
301 | kern_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 | ||
350 | kern_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 | // | |
372 | void 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 | ||
380 | void 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 | ||
387 | void 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 | ||
395 | void Server::SleepWatcher::add(PowerWatcher *client) | |
396 | { | |
397 | assert(mPowerClients.find(client) == mPowerClients.end()); | |
398 | mPowerClients.insert(client); | |
399 | } | |
400 | ||
401 | void 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 | // | |
411 | Process *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 | // | |
421 | void 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 | // | |
432 | static FILE *reportFile; | |
433 | ||
434 | void 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 | // | |
461 | void 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 |
473 | void 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 | 482 | void 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 | ||
495 | bool 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 | // | |
510 | void 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 | // | |
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 | } |