]> git.saurik.com Git - apple/security.git/blob - securityd/src/session.cpp
Security-57031.40.6.tar.gz
[apple/security.git] / securityd / src / session.cpp
1 /*
2 * Copyright (c) 2000-2009,2011-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 // session - authentication session domains
27 //
28 // Security sessions are now by definition congruent to audit subsystem sessions.
29 // We represent these sessions within securityd as subclasses of class Session,
30 // but we reach for the kernel's data whenever we're not sure if our data is
31 // up to date.
32 //
33 // Modifications to session state are made from client space using system calls.
34 // We discover them when we see changes in audit records as they come in with
35 // new requests. We cannot use system notifications for such changes because
36 // securityd is fully symmetrically multi-threaded, and thus may process new
37 // requests from clients before it gets those notifications.
38 //
39 #include <pwd.h>
40 #include <signal.h> // SIGTERM
41 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
42 #include "session.h"
43 #include "connection.h"
44 #include "database.h"
45 #include "server.h"
46 #include <security_utilities/logging.h>
47 #include <agentquery.h>
48
49 using namespace CommonCriteria;
50
51
52 //
53 // The static session map
54 //
55 Session::SessionMap Session::mSessions;
56 Mutex Session::mSessionLock(Mutex::recursive);
57
58
59 const char Session::kUsername[] = "username";
60 const char Session::kRealname[] = "realname";
61
62
63 //
64 // Create a Session object from initial parameters (create)
65 //
66 Session::Session(const AuditInfo &audit, Server &server)
67 : mAudit(audit), mSecurityAgent(NULL), mAuthHost(NULL), mKeybagState(0)
68 {
69 // link to Server as the global nexus in the object mesh
70 parent(server);
71
72 // self-register
73 StLock<Mutex> _(mSessionLock);
74 assert(!mSessions[audit.sessionId()]);
75 mSessions[audit.sessionId()] = this;
76
77 // log it
78 SECURITYD_SESSION_CREATE(this, this->sessionId(), &mAudit, sizeof(mAudit));
79 Syslog::notice("Session %d created", this->sessionId());
80 }
81
82
83 //
84 // Destroy a Session
85 //
86 Session::~Session()
87 {
88 SECURITYD_SESSION_DESTROY(this, this->sessionId());
89 Syslog::notice("Session %d destroyed", this->sessionId());
90 }
91
92
93 Server &Session::server() const
94 {
95 return parent<Server>();
96 }
97
98
99 //
100 // Locate a session object by session identifier
101 //
102 Session &Session::find(pid_t id, bool create)
103 {
104 if (id == callerSecuritySession)
105 return Server::session();
106 StLock<Mutex> _(mSessionLock);
107 SessionMap::iterator it = mSessions.find(id);
108 if (it != mSessions.end())
109 return *it->second;
110
111 // new session
112 if (!create)
113 CssmError::throwMe(errSessionInvalidId);
114 AuditInfo info;
115 info.get(id);
116 assert(info.sessionId() == id);
117 RefPointer<Session> session = new DynamicSession(info);
118 mSessions.insert(make_pair(id, session));
119 return *session;
120 }
121
122
123 //
124 // Act on a death notification for a session's underlying audit session object.
125 // We may not destroy the Session outright here (due to processes that use it),
126 // but we do clear out its accumulated wealth.
127 // Note that we may get spurious death notifications for audit sessions that we
128 // never learned about. Ignore those.
129 //
130 void Session::destroy(SessionId id)
131 {
132 // remove session from session map
133 bool unlocked = false;
134 RefPointer<Session> session = NULL;
135 {
136 StLock<Mutex> _(mSessionLock);
137 SessionMap::iterator it = mSessions.find(id);
138 if (it != mSessions.end()) {
139 session = it->second;
140 assert(session->sessionId() == id);
141 mSessions.erase(it);
142
143 for (SessionMap::iterator kb_it = mSessions.begin(); kb_it != mSessions.end(); kb_it++) {
144 RefPointer<Session> kb_session = kb_it->second;
145 if (kb_session->originatorUid() == session->originatorUid()) {
146 if (kb_session->keybagGetState(session_keybag_unlocked)) unlocked = true;
147 }
148 }
149 }
150 }
151
152 if (session.get()) {
153 if (!unlocked) {
154 service_context_t context = session->get_current_service_context();
155 service_client_kb_lock(&context);
156 }
157 session->kill();
158 }
159 }
160
161
162 void Session::kill()
163 {
164 StLock<Mutex> _(*this); // do we need to take this so early?
165 SECURITYD_SESSION_KILL(this, this->sessionId());
166 invalidateSessionAuthHosts();
167
168 // invalidate shared credentials
169 {
170 StLock<Mutex> _(mCredsLock);
171
172 IFDEBUG(if (!mSessionCreds.empty())
173 secdebug("SSauth", "session %p clearing %d shared credentials",
174 this, int(mSessionCreds.size())));
175 for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
176 (*it)->invalidate();
177 }
178
179 // base kill processing
180 PerSession::kill();
181 }
182
183
184 //
185 // Refetch audit session data for the current audit session (to catch outside updates
186 // to the audit record). This is the price we're paying for not requiring an IPC to
187 // securityd when audit session data changes (this is desirable for delayering the
188 // software layer cake).
189 // If we ever disallow changes to (parts of the) audit session record in the kernel,
190 // we can loosen up on this continual re-fetching.
191 //
192 void Session::updateAudit() const
193 {
194 CommonCriteria::AuditInfo info;
195 try {
196 info.get(mAudit.sessionId());
197 } catch (...) {
198 return;
199 }
200 mAudit = info;
201 }
202
203 void Session::verifyKeyStorePassphrase(int32_t retries)
204 {
205 QueryKeybagPassphrase keybagQuery(*this, retries);
206 keybagQuery.inferHints(Server::process());
207 if (keybagQuery.query() != SecurityAgent::noReason) {
208 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
209 }
210 }
211
212 void Session::changeKeyStorePassphrase()
213 {
214 service_context_t context = get_current_service_context();
215 QueryKeybagNewPassphrase keybagQuery(*this);
216 keybagQuery.inferHints(Server::process());
217 CssmAutoData pass(Allocator::standard(Allocator::sensitive));
218 CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
219 SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
220 if (queryReason == SecurityAgent::noReason) {
221 service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
222 } else {
223 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
224 }
225 }
226
227 void Session::resetKeyStorePassphrase(const CssmData &passphrase)
228 {
229 service_context_t context = get_current_service_context();
230 service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length());
231 }
232
233 service_context_t Session::get_current_service_context()
234 {
235 // if this gets called from a timer there is no connection() object.
236 // need to check for valid connection object and pass the audit token along
237 service_context_t context = { sessionId(), originatorUid(), {} }; //*Server::connection().auditToken()
238 return context;
239 }
240
241 void Session::keybagClearState(int state)
242 {
243 mKeybagState &= ~state;
244 }
245
246 void Session::keybagSetState(int state)
247 {
248 mKeybagState |= state;
249 }
250
251 bool Session::keybagGetState(int state)
252 {
253 return mKeybagState & state;
254 }
255
256
257 //
258 // Manage authorization client processes
259 //
260 void Session::invalidateSessionAuthHosts()
261 {
262 StLock<Mutex> _(mAuthHostLock);
263
264 // if you got here, we don't care about pending operations: the auth hosts die
265 Syslog::warning("Killing auth hosts");
266 if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
267 if (mAuthHost) mAuthHost->UnixPlusPlus::Child::kill(SIGTERM);
268 mSecurityAgent = NULL;
269 mAuthHost = NULL;
270 }
271
272 void Session::invalidateAuthHosts()
273 {
274 StLock<Mutex> _(mSessionLock);
275 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
276 it->second->invalidateSessionAuthHosts();
277 }
278
279 //
280 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
281 //
282 void Session::processSystemSleep()
283 {
284 SecurityAgentXPCQuery::killAllXPCClients();
285
286 StLock<Mutex> _(mSessionLock);
287 for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
288 it->second->allReferences(&DbCommon::sleepProcessing);
289 }
290
291
292 //
293 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
294 //
295 void Session::processLockAll()
296 {
297 allReferences(&DbCommon::lockProcessing);
298 }
299
300
301 //
302 // The root session corresponds to the audit session that security is running in.
303 // This is usually the initial system session; but in debug scenarios it may be
304 // an "ordinary" graphic login session. In such a debug case, we may add attribute
305 // flags to the session to make our (debugging) life easier.
306 //
307 RootSession::RootSession(uint64_t attributes, Server &server)
308 : Session(AuditInfo::current(), server)
309 {
310 ref(); // eternalize
311 mAudit.ai_flags |= attributes; // merge imposed attributes
312 }
313
314
315 //
316 // Dynamic sessions use the audit session context of the first-contact client caller.
317 //
318 DynamicSession::DynamicSession(const AuditInfo &audit)
319 : Session(audit, Server::active())
320 {
321 }
322
323
324 //
325 // Authorization operations
326 //
327 OSStatus Session::authCreate(const AuthItemSet &rights,
328 const AuthItemSet &environment,
329 AuthorizationFlags flags,
330 AuthorizationBlob &newHandle,
331 const audit_token_t &auditToken)
332 {
333 // invoke the authorization computation engine
334 CredentialSet resultCreds;
335
336 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
337 auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
338
339 SECURITYD_AUTH_CREATE(this, auth.get());
340
341 // Make a copy of the mSessionCreds
342 CredentialSet sessionCreds;
343 {
344 StLock<Mutex> _(mCredsLock);
345 sessionCreds = mSessionCreds;
346 }
347
348 AuthItemSet outRights;
349 OSStatus result = Server::authority().authorize(rights, environment, flags,
350 &sessionCreds, &resultCreds, outRights, *auth);
351 newHandle = auth->handle();
352
353 // merge resulting creds into shared pool
354 if ((flags & kAuthorizationFlagExtendRights) &&
355 !(flags & kAuthorizationFlagDestroyRights))
356 {
357 StLock<Mutex> _(mCredsLock);
358 mergeCredentials(resultCreds);
359 auth->mergeCredentials(resultCreds);
360 }
361
362 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
363 // not be destroyed anymore since it's destructor asserts it has no processes
364 Server::process().addAuthorization(auth.get());
365 auth.release();
366 return result;
367 }
368
369 void Session::authFree(const AuthorizationBlob &authBlob, AuthorizationFlags flags)
370 {
371 AuthorizationToken::Deleter deleter(authBlob);
372 AuthorizationToken &auth = deleter;
373 Process &process = Server::process();
374 process.checkAuthorization(&auth);
375
376 if (flags & kAuthorizationFlagDestroyRights) {
377 // explicitly invalidate all shared credentials and remove them from the session
378 for (CredentialSet::const_iterator it = auth.begin(); it != auth.end(); it++)
379 if ((*it)->isShared())
380 (*it)->invalidate();
381 }
382
383 // now get rid of the authorization itself
384 if (process.removeAuthorization(&auth))
385 deleter.remove();
386 }
387
388 OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
389 const AuthItemSet &rights, const AuthItemSet &environment,
390 AuthorizationFlags flags,
391 AuthItemSet &grantedRights)
392 {
393 AuthorizationToken &auth = authorization(authBlob);
394 return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
395 }
396
397 OSStatus Session::authGetRights(AuthorizationToken &auth,
398 const AuthItemSet &rights, const AuthItemSet &environment,
399 AuthorizationFlags flags,
400 AuthItemSet &grantedRights)
401 {
402 CredentialSet resultCreds;
403 CredentialSet effective;
404 {
405 StLock<Mutex> _(mCredsLock);
406 effective = auth.effectiveCreds();
407 }
408 OSStatus result = Server::authority().authorize(rights, environment, flags,
409 &effective, &resultCreds, grantedRights, auth);
410
411 // merge resulting creds into shared pool
412 if ((flags & kAuthorizationFlagExtendRights) && !(flags & kAuthorizationFlagDestroyRights))
413 {
414 StLock<Mutex> _(mCredsLock);
415 mergeCredentials(resultCreds);
416 auth.mergeCredentials(resultCreds);
417 }
418
419 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
420 &auth, int(rights.size()), int(grantedRights.size()));
421 return result;
422 }
423
424 OSStatus Session::authGetInfo(const AuthorizationBlob &authBlob,
425 const char *tag,
426 AuthItemSet &contextInfo)
427 {
428 AuthorizationToken &auth = authorization(authBlob);
429 secdebug("SSauth", "Authorization %p get-info", &auth);
430 contextInfo = auth.infoSet(tag);
431 return noErr;
432 }
433
434 OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
435 AuthorizationExternalForm &extForm)
436 {
437 const AuthorizationToken &auth = authorization(authBlob);
438 StLock<Mutex> _(*this);
439 if (auth.mayExternalize(Server::process())) {
440 memset(&extForm, 0, sizeof(extForm));
441 AuthorizationExternalBlob &extBlob =
442 reinterpret_cast<AuthorizationExternalBlob &>(extForm);
443 extBlob.blob = auth.handle();
444 extBlob.session = this->sessionId();
445 secdebug("SSauth", "Authorization %p externalized", &auth);
446 return noErr;
447 } else
448 return errAuthorizationExternalizeNotAllowed;
449 }
450
451 OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
452 AuthorizationBlob &authBlob)
453 {
454 // interpret the external form
455 const AuthorizationExternalBlob &extBlob =
456 reinterpret_cast<const AuthorizationExternalBlob &>(extForm);
457
458 // locate source authorization
459 AuthorizationToken &sourceAuth = AuthorizationToken::find(extBlob.blob);
460
461 // check for permission and do it
462 if (sourceAuth.mayInternalize(Server::process(), true)) {
463 StLock<Mutex> _(*this);
464 authBlob = extBlob.blob;
465 Server::process().addAuthorization(&sourceAuth);
466 secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
467 return noErr;
468 } else
469 return errAuthorizationInternalizeNotAllowed;
470 }
471
472
473 //
474 // Accessor method for setting audit session flags.
475 //
476 void Session::setAttributes(SessionAttributeBits bits)
477 {
478 StLock<Mutex> _(*this);
479 updateAudit();
480 // assert((bits & ~settableAttributes) == 0);
481 mAudit.ai_flags = bits;
482 mAudit.set();
483 }
484
485 //
486 // The default session setup operation always fails.
487 // Subclasses can override this to support session setup calls.
488 //
489 void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
490 {
491 MacOSError::throwMe(errSessionAuthorizationDenied);
492 }
493
494 uid_t Session::originatorUid()
495 {
496 if (mAudit.uid() == AU_DEFAUDITID) {
497 StLock<Mutex> _(*this);
498 updateAudit();
499 }
500 return mAudit.uid();
501 }
502
503 //
504 // Authorization database I/O
505 //
506 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
507 {
508 string rightName(inRightName);
509 return Server::authority().getRule(rightName, rightDict);
510 }
511
512
513 OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, AuthorizationString inRightName, CFDictionaryRef rightDict)
514 {
515 CredentialSet resultCreds;
516 AuthorizationToken &auth = authorization(authBlob);
517 CredentialSet effective;
518
519 {
520 StLock<Mutex> _(mCredsLock);
521 effective = auth.effectiveCreds();
522 }
523
524 OSStatus result = Server::authority().setRule(inRightName, rightDict, &effective, &resultCreds, auth);
525
526 {
527 StLock<Mutex> _(mCredsLock);
528 mergeCredentials(resultCreds);
529 auth.mergeCredentials(resultCreds);
530 }
531
532 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
533 &authorization(authBlob), inRightName, int32_t(result));
534 return result;
535 }
536
537
538 OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, AuthorizationString inRightName)
539 {
540 CredentialSet resultCreds;
541 AuthorizationToken &auth = authorization(authBlob);
542 CredentialSet effective;
543
544 {
545 StLock<Mutex> _(mCredsLock);
546 effective = auth.effectiveCreds();
547 }
548
549 OSStatus result = Server::authority().removeRule(inRightName, &effective, &resultCreds, auth);
550
551 {
552 StLock<Mutex> _(mCredsLock);
553 mergeCredentials(resultCreds);
554 auth.mergeCredentials(resultCreds);
555 }
556
557 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
558 &authorization(authBlob), inRightName, int32_t(result));
559 return result;
560 }
561
562
563 //
564 // Merge a set of credentials into the shared-session credential pool
565 //
566 // must hold mCredsLock
567 void Session::mergeCredentials(CredentialSet &creds)
568 {
569 secdebug("SSsession", "%p merge creds @%p", this, &creds);
570 CredentialSet updatedCredentials = creds;
571 for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
572 if ((*it)->isShared() && (*it)->isValid()) {
573 CredentialSet::iterator old = mSessionCreds.find(*it);
574 if (old == mSessionCreds.end()) {
575 mSessionCreds.insert(*it);
576 } else {
577 // replace "new" with "old" in input set to retain synchronization
578 (*old)->merge(**it);
579 updatedCredentials.erase(*it);
580 updatedCredentials.insert(*old);
581 }
582 }
583 creds.swap(updatedCredentials);
584 }
585
586
587 //
588 // Locate an AuthorizationToken given a blob
589 //
590 AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
591 {
592 AuthorizationToken &auth = AuthorizationToken::find(blob);
593 Server::process().checkAuthorization(&auth);
594 return auth;
595 }
596
597 //
598 // Run the Authorization engine to check if a given right has been authorized,
599 // independent of an external client request.
600 //
601 OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
602 {
603 // dummy up the arguments for authCreate()
604 AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
605 AuthorizationItemSet rightItemSet = { 1, &rightItem };
606 AuthItemSet rightAuthItemSet(&rightItemSet);
607 AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
608 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
609 if (true == allowUI)
610 flags |= kAuthorizationFlagInteractionAllowed;
611 AuthorizationBlob dummyHandle;
612 const audit_token_t *at = connection.auditToken();
613
614 return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
615 }
616
617 // for places within securityd that don't want to #include
618 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
619 bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
620 {
621 bool isAuthorized = false;
622
623 try {
624 OSStatus status = authCheckRight(rightName, connection, allowUI);
625 if (errAuthorizationSuccess == status)
626 isAuthorized = true;
627 }
628 catch (...) {
629 }
630 return isAuthorized;
631 }
632
633 RefPointer<AuthHostInstance>
634 Session::authhost(const AuthHostType hostType, const bool restart)
635 {
636 StLock<Mutex> _(mAuthHostLock);
637
638 if (hostType == privilegedAuthHost)
639 {
640 if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
641 {
642 if (mAuthHost)
643 PerSession::kill(*mAuthHost);
644 mAuthHost = new AuthHostInstance(*this, hostType);
645 }
646 return mAuthHost;
647 }
648 else /* if (hostType == securityAgent) */
649 {
650 if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
651 {
652 if (mSecurityAgent)
653 PerSession::kill(*mSecurityAgent);
654 mSecurityAgent = new AuthHostInstance(*this, hostType);
655 }
656 return mSecurityAgent;
657 }
658 }
659
660 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
661 {
662 if (Server::process().uid() != 0)
663 MacOSError::throwMe(errSessionAuthorizationDenied);
664 StLock<Mutex> _(*this);
665 mSessionAgentPrefs = userPrefsDict;
666 }
667
668 CFDataRef DynamicSession::copyUserPrefs()
669 {
670 StLock<Mutex> _(*this);
671 if (mSessionAgentPrefs)
672 CFRetain(mSessionAgentPrefs);
673 return mSessionAgentPrefs;
674 }
675
676
677 //
678 // Debug dumping
679 //
680 #if defined(DEBUGDUMP)
681
682 void Session::dumpNode()
683 {
684 PerSession::dumpNode();
685 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
686 this->sessionId(), uint32_t(this->attributes()), mAuthHost, mSecurityAgent);
687 }
688
689 #endif //DEBUGDUMP