]>
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> | |
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 | // | |
60 | // Construct the server object | |
61 | // | |
62 | Server::Server(Authority &authority, CodeSignatures &signatures, const char *bootstrapName) | |
63 | : MachServer(bootstrapName), | |
64 | mBootstrapName(bootstrapName), | |
65 | mCSPModule(gGuidAppleCSP, mCssm), mCSP(mCSPModule), | |
66 | mAuthority(authority), | |
67 | mCodeSignatures(signatures), | |
68 | mVerbosity(0), | |
69 | mWaitForClients(true), mShuttingDown(false) | |
70 | { | |
71 | // make me eternal (in the object mesh) | |
72 | ref(); | |
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, audit_token_t &auditToken) | |
95 | { | |
96 | Server &server = active(); | |
97 | StLock<Mutex> _(server); | |
98 | Connection *conn = server.mConnections.get(port, CSSM_ERRCODE_INVALID_CONTEXT_HANDLE); | |
99 | conn->process().checkSession(auditToken); | |
100 | active().mCurrentConnection() = conn; | |
101 | conn->beginWork(auditToken); | |
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 U32HandleObject::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 | // The handle might be used across IPC, so we clamp it accordingly | |
166 | // | |
167 | AclSource &Server::aclBearer(AclKind kind, U32HandleObject::Handle handle) | |
168 | { | |
169 | AclSource &bearer = U32HandleObject::find<AclSource>(handle, CSSMERR_CSSM_INVALID_ADDIN_HANDLE); | |
170 | if (kind != bearer.acl().aclKind()) | |
171 | CssmError::throwMe(CSSMERR_CSSM_INVALID_HANDLE_USAGE); | |
172 | return bearer; | |
173 | } | |
174 | ||
175 | ||
176 | // | |
177 | // Run the server. This will not return until the server is forced to exit. | |
178 | // | |
179 | void Server::run() | |
180 | { | |
181 | MachServer::run(0x10000, | |
182 | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) | | |
183 | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT)); | |
184 | } | |
185 | ||
186 | ||
187 | // | |
188 | // Handle thread overflow. MachServer will call this if it has hit its thread | |
189 | // limit and yet still needs another thread. | |
190 | // | |
191 | void Server::threadLimitReached(UInt32 limit) | |
192 | { | |
193 | Syslog::notice("securityd has reached its thread limit (%ld) - service deadlock is possible", | |
194 | limit); | |
195 | } | |
196 | ||
197 | ||
198 | // | |
199 | // The primary server run-loop function. | |
200 | // Invokes the MIG-generated main dispatch function (ucsp_server), as well | |
201 | // as the self-send dispatch (self_server). | |
202 | // For debug builds, look up request names in a MIG-generated table | |
203 | // for better debug-log messages. | |
204 | // | |
205 | boolean_t ucsp_server(mach_msg_header_t *, mach_msg_header_t *); | |
206 | boolean_t self_server(mach_msg_header_t *, mach_msg_header_t *); | |
207 | ||
208 | ||
209 | boolean_t Server::handle(mach_msg_header_t *in, mach_msg_header_t *out) | |
210 | { | |
211 | return ucsp_server(in, out) || self_server(in, out); | |
212 | } | |
213 | ||
214 | ||
215 | // | |
216 | // Set up a new Connection. This establishes the environment (process et al) as needed | |
217 | // and registers a properly initialized Connection object to run with. | |
218 | // Type indicates how "deep" we need to initialize (new session, process, or connection). | |
219 | // Everything at and below that level is constructed. This is straight-forward except | |
220 | // in the case of session re-initialization (see below). | |
221 | // | |
222 | void Server::setupConnection(ConnectLevel type, Port replyPort, Port taskPort, | |
223 | const audit_token_t &auditToken, const ClientSetupInfo *info) | |
224 | { | |
225 | AuditToken audit(auditToken); | |
226 | ||
227 | // first, make or find the process based on task port | |
228 | StLock<Mutex> _(*this); | |
229 | RefPointer<Process> &proc = mProcesses[taskPort]; | |
230 | if (proc && proc->session().sessionId() != audit.sessionId()) | |
231 | proc->changeSession(audit.sessionId()); | |
232 | if (proc && type == connectNewProcess) { | |
233 | // the client has amnesia - reset it | |
234 | assert(info); | |
235 | proc->reset(taskPort, info, audit); | |
236 | proc->changeSession(audit.sessionId()); | |
237 | } | |
238 | if (!proc) { | |
239 | if (type == connectNewThread) // client error (or attack) | |
240 | CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); | |
241 | assert(info); | |
242 | proc = new Process(taskPort, info, audit); | |
243 | notifyIfDead(taskPort); | |
244 | mPids[proc->pid()] = proc; | |
245 | } | |
246 | ||
247 | // now, establish a connection and register it in the server | |
248 | Connection *connection = new Connection(*proc, replyPort); | |
249 | if (mConnections.contains(replyPort)) // malicious re-entry attempt? | |
250 | CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR); //@@@ error code? (client error) | |
251 | mConnections[replyPort] = connection; | |
252 | notifyIfDead(replyPort); | |
253 | } | |
254 | ||
255 | ||
256 | // | |
257 | // Synchronously end a Connection. | |
258 | // This is due to a request from the client, so no thread races are possible. | |
259 | // In practice, this is optional since the DPN for the client thread reply port | |
260 | // will destroy the connection anyway when the thread dies. | |
261 | // | |
262 | void Server::endConnection(Port replyPort) | |
263 | { | |
264 | StLock<Mutex> _(*this); | |
265 | PortMap<Connection>::iterator it = mConnections.find(replyPort); | |
266 | assert(it != mConnections.end()); | |
267 | it->second->terminate(); | |
268 | mConnections.erase(it); | |
269 | } | |
270 | ||
271 | ||
272 | // | |
273 | // Handling dead-port notifications. | |
274 | // This receives DPNs for all kinds of ports we're interested in. | |
275 | // | |
276 | void Server::notifyDeadName(Port port) | |
277 | { | |
278 | // We need the lock to get a proper iterator on mConnections or mProcesses, | |
279 | // but must release it before we call abort or kill, as these might take | |
280 | // unbounded time, including calls out to token daemons etc. | |
281 | ||
282 | StLock<Mutex> serverLock(*this); | |
283 | secdebug("SSports", "port %d is dead", port.port()); | |
284 | ||
285 | // is it a connection? | |
286 | PortMap<Connection>::iterator conIt = mConnections.find(port); | |
287 | if (conIt != mConnections.end()) { | |
288 | SECURITYD_PORTS_DEAD_CONNECTION(port); | |
289 | RefPointer<Connection> con = conIt->second; | |
290 | mConnections.erase(conIt); | |
291 | serverLock.unlock(); | |
292 | con->abort(); | |
293 | return; | |
294 | } | |
295 | ||
296 | // is it a process? | |
297 | PortMap<Process>::iterator procIt = mProcesses.find(port); | |
298 | if (procIt != mProcesses.end()) { | |
299 | SECURITYD_PORTS_DEAD_PROCESS(port); | |
300 | RefPointer<Process> proc = procIt->second; | |
301 | mPids.erase(proc->pid()); | |
302 | mProcesses.erase(procIt); | |
303 | serverLock.unlock(); | |
304 | // The kill may take some time; make sure there is a spare thread around | |
305 | // to prevent deadlocks | |
306 | StLock<MachServer, &Server::busy, &Server::idle> _(*this); | |
307 | proc->kill(); | |
308 | return; | |
309 | } | |
310 | ||
311 | // well, what IS IT?! | |
312 | SECURITYD_PORTS_DEAD_ORPHAN(port); | |
313 | secdebug("server", "spurious dead port notification for port %d", port.port()); | |
314 | } | |
315 | ||
316 | ||
317 | // | |
318 | // Handling no-senders notifications. | |
319 | // This is currently only used for (subsidiary) service ports | |
320 | // | |
321 | void Server::notifyNoSenders(Port port, mach_port_mscount_t) | |
322 | { | |
323 | SECURITYD_PORTS_DEAD_SESSION(port); | |
324 | } | |
325 | ||
326 | ||
327 | // | |
328 | // Handling signals. | |
329 | // These are sent as Mach messages from ourselves to escape the limitations of | |
330 | // the signal handler environment. | |
331 | // | |
332 | kern_return_t self_server_handleSignal(mach_port_t sport, | |
333 | mach_port_t taskPort, int sig) | |
334 | { | |
335 | try { | |
336 | SECURITYD_SIGNAL_HANDLED(sig); | |
337 | if (taskPort != mach_task_self()) { | |
338 | Syslog::error("handleSignal: received from someone other than myself"); | |
339 | return KERN_SUCCESS; | |
340 | } | |
341 | switch (sig) { | |
342 | case SIGCHLD: | |
343 | ServerChild::checkChildren(); | |
344 | break; | |
345 | case SIGINT: | |
346 | SECURITYD_SHUTDOWN_NOW(); | |
347 | Syslog::notice("securityd terminated due to SIGINT"); | |
348 | _exit(0); | |
349 | case SIGTERM: | |
350 | Server::active().beginShutdown(); | |
351 | break; | |
352 | case SIGPIPE: | |
353 | fprintf(stderr, "securityd ignoring SIGPIPE received"); | |
354 | break; | |
355 | ||
356 | #if defined(DEBUGDUMP) | |
357 | case SIGUSR1: | |
358 | NodeCore::dumpAll(); | |
359 | break; | |
360 | #endif //DEBUGDUMP | |
361 | ||
362 | case SIGUSR2: | |
363 | { | |
364 | extern PCSCMonitor *gPCSC; | |
365 | gPCSC->startSoftTokens(); | |
366 | break; | |
367 | } | |
368 | ||
369 | default: | |
370 | assert(false); | |
371 | } | |
372 | } catch(...) { | |
373 | secdebug("SS", "exception handling a signal (ignored)"); | |
374 | } | |
375 | mach_port_deallocate(mach_task_self(), taskPort); | |
376 | return KERN_SUCCESS; | |
377 | } | |
378 | ||
379 | ||
380 | kern_return_t self_server_handleSession(mach_port_t sport, | |
381 | mach_port_t taskPort, uint32_t event, uint64_t ident) | |
382 | { | |
383 | try { | |
384 | if (taskPort != mach_task_self()) { | |
385 | Syslog::error("handleSession: received from someone other than myself"); | |
386 | return KERN_SUCCESS; | |
387 | } | |
388 | if (event == AUE_SESSION_CLOSE) | |
389 | Session::destroy(ident); | |
390 | } catch(...) { | |
391 | secdebug("SS", "exception handling a signal (ignored)"); | |
392 | } | |
393 | mach_port_deallocate(mach_task_self(), taskPort); | |
394 | return KERN_SUCCESS; | |
395 | } | |
396 | ||
397 | ||
398 | // | |
399 | // Notifier for system sleep events | |
400 | // | |
401 | void Server::SleepWatcher::systemWillSleep() | |
402 | { | |
403 | SECURITYD_POWER_SLEEP(); | |
404 | Session::processSystemSleep(); | |
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 | SECURITYD_POWER_WAKE(); | |
412 | for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) | |
413 | (*it)->systemIsWaking(); | |
414 | } | |
415 | ||
416 | void Server::SleepWatcher::systemWillPowerOn() | |
417 | { | |
418 | SECURITYD_POWER_ON(); | |
419 | Server::active().longTermActivity(); | |
420 | for (set<PowerWatcher *>::const_iterator it = mPowerClients.begin(); it != mPowerClients.end(); it++) | |
421 | (*it)->systemWillPowerOn(); | |
422 | } | |
423 | ||
424 | void Server::SleepWatcher::add(PowerWatcher *client) | |
425 | { | |
426 | assert(mPowerClients.find(client) == mPowerClients.end()); | |
427 | mPowerClients.insert(client); | |
428 | } | |
429 | ||
430 | void Server::SleepWatcher::remove(PowerWatcher *client) | |
431 | { | |
432 | assert(mPowerClients.find(client) != mPowerClients.end()); | |
433 | mPowerClients.erase(client); | |
434 | } | |
435 | ||
436 | ||
437 | // | |
438 | // Expose the process/pid map to the outside | |
439 | // | |
440 | Process *Server::findPid(pid_t pid) const | |
441 | { | |
442 | PidMap::const_iterator it = mPids.find(pid); | |
443 | return (it == mPids.end()) ? NULL : it->second; | |
444 | } | |
445 | ||
446 | ||
447 | // | |
448 | // Set delayed shutdown mode | |
449 | // | |
450 | void Server::waitForClients(bool waiting) | |
451 | { | |
452 | mWaitForClients = waiting; | |
453 | } | |
454 | ||
455 | ||
456 | // | |
457 | // Begin shutdown processing. | |
458 | // We relinquish our primary state authority. From now on, we'll be | |
459 | // kept alive (only) by our current clients. | |
460 | // | |
461 | static FILE *reportFile; | |
462 | ||
463 | void Server::beginShutdown() | |
464 | { | |
465 | StLock<Mutex> _(*this); | |
466 | if (!mWaitForClients) { | |
467 | SECURITYD_SHUTDOWN_NOW(); | |
468 | _exit(0); | |
469 | } else { | |
470 | if (!mShuttingDown) { | |
471 | mShuttingDown = true; | |
472 | Session::invalidateAuthHosts(); | |
473 | SECURITYD_SHUTDOWN_BEGIN(); | |
474 | if (verbosity() >= 2) { | |
475 | reportFile = fopen("/var/log/securityd-shutdown.log", "w"); | |
476 | shutdownSnitch(); | |
477 | } | |
478 | } | |
479 | } | |
480 | } | |
481 | ||
482 | ||
483 | // | |
484 | // During shutdown, we report residual clients to dtrace, and allow a state dump | |
485 | // for debugging. | |
486 | // We don't bother locking for the shuttingDown() check; it's a latching boolean | |
487 | // and we'll be good enough without a lock. | |
488 | // | |
489 | void Server::eventDone() | |
490 | { | |
491 | if (this->shuttingDown()) { | |
492 | StLock<Mutex> lazy(*this, false); // lazy lock acquisition | |
493 | if (SECURITYD_SHUTDOWN_COUNT_ENABLED()) { | |
494 | lazy.lock(); | |
495 | SECURITYD_SHUTDOWN_COUNT(mProcesses.size(), VProc::Transaction::debugCount()); | |
496 | } | |
497 | if (verbosity() >= 2) { | |
498 | lazy.lock(); | |
499 | shutdownSnitch(); | |
500 | } | |
501 | IFDUMPING("shutdown", NodeCore::dumpAll()); | |
502 | } | |
503 | } | |
504 | ||
505 | ||
506 | void Server::shutdownSnitch() | |
507 | { | |
508 | time_t now; | |
509 | time(&now); | |
510 | fprintf(reportFile, "%.24s %d residual clients:\n", ctime(&now), int(mPids.size())); | |
511 | for (PidMap::const_iterator it = mPids.begin(); it != mPids.end(); ++it) | |
512 | if (SecCodeRef clientCode = it->second->processCode()) { | |
513 | CFRef<CFURLRef> path; | |
514 | OSStatus rc = SecCodeCopyPath(clientCode, kSecCSDefaultFlags, &path.aref()); | |
515 | if (path) | |
516 | fprintf(reportFile, " %s (%d)\n", cfString(path).c_str(), it->first); | |
517 | else | |
518 | fprintf(reportFile, "pid=%d (error %d)\n", it->first, int32_t(rc)); | |
519 | } | |
520 | fprintf(reportFile, "\n"); | |
521 | fflush(reportFile); | |
522 | } | |
523 | ||
524 | bool Server::inDarkWake() | |
525 | { | |
526 | return IOPMIsADarkWake(IOPMConnectionGetSystemCapabilities()); | |
527 | } | |
528 | ||
529 | // | |
530 | // Initialize the CSSM/MDS subsystem. | |
531 | // This was once done lazily on demand. These days, we are setting up the | |
532 | // system MDS here, and CSSM is pretty much always needed, so this is called | |
533 | // early during program startup. Do note that the server may not (yet) be running. | |
534 | // | |
535 | void Server::loadCssm(bool mdsIsInstalled) | |
536 | { | |
537 | if (!mCssm->isActive()) { | |
538 | StLock<Mutex> _(*this); | |
539 | VProc::Transaction xact; | |
540 | if (!mCssm->isActive()) { | |
541 | if (!mdsIsInstalled) { // non-system securityd instance should not reinitialize MDS | |
542 | secdebug("SS", "Installing MDS"); | |
543 | IFDEBUG(if (geteuid() == 0)) | |
544 | MDSClient::mds().install(); | |
545 | } | |
546 | secdebug("SS", "CSSM initializing"); | |
547 | mCssm->init(); | |
548 | mCSP->attach(); | |
549 | secdebug("SS", "CSSM ready with CSP %s", mCSP->guid().toString().c_str()); | |
550 | } | |
551 | } | |
552 | } | |
553 | ||
554 | ||
555 | // | |
556 | // LongtermActivity/lock combo | |
557 | // | |
558 | LongtermStLock::LongtermStLock(Mutex &lck) | |
559 | : StLock<Mutex>(lck, false) // don't take the lock yet | |
560 | { | |
561 | if (lck.tryLock()) { // uncontested | |
562 | this->mActive = true; | |
563 | } else { // contested - need backup thread | |
564 | Server::active().longTermActivity(); | |
565 | this->lock(); | |
566 | } | |
567 | } |