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