2 * Copyright (c) 2000-2009,2011-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // session - authentication session domains
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
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.
40 #include <signal.h> // SIGTERM
41 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
43 #include "connection.h"
46 #include <security_utilities/logging.h>
47 #include <agentquery.h>
49 using namespace CommonCriteria
;
53 // The static session map
55 Session::SessionMap
Session::mSessions
;
56 Mutex
Session::mSessionLock(Mutex::recursive
);
59 const char Session::kUsername
[] = "username";
60 const char Session::kRealname
[] = "realname";
64 // Create a Session object from initial parameters (create)
66 Session::Session(const AuditInfo
&audit
, Server
&server
)
67 : mAudit(audit
), mSecurityAgent(NULL
), mAuthHost(NULL
), mKeybagState(0)
69 // link to Server as the global nexus in the object mesh
73 StLock
<Mutex
> _(mSessionLock
);
74 assert(!mSessions
[audit
.sessionId()]);
75 mSessions
[audit
.sessionId()] = this;
78 SECURITYD_SESSION_CREATE(this, this->sessionId(), &mAudit
, sizeof(mAudit
));
79 Syslog::notice("Session %d created", this->sessionId());
88 SECURITYD_SESSION_DESTROY(this, this->sessionId());
89 Syslog::notice("Session %d destroyed", this->sessionId());
93 Server
&Session::server() const
95 return parent
<Server
>();
100 // Locate a session object by session identifier
102 Session
&Session::find(pid_t id
, bool create
)
104 if (id
== callerSecuritySession
)
105 return Server::session();
106 StLock
<Mutex
> _(mSessionLock
);
107 SessionMap::iterator it
= mSessions
.find(id
);
108 if (it
!= mSessions
.end())
113 CssmError::throwMe(errSessionInvalidId
);
116 assert(info
.sessionId() == id
);
117 RefPointer
<Session
> session
= new DynamicSession(info
);
118 mSessions
.insert(make_pair(id
, session
));
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.
130 void Session::destroy(SessionId id
)
132 // remove session from session map
133 bool unlocked
= false;
134 RefPointer
<Session
> session
= NULL
;
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
);
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;
154 service_context_t context
= session
->get_current_service_context();
155 service_client_kb_lock(&context
);
164 StLock
<Mutex
> _(*this); // do we need to take this so early?
165 SECURITYD_SESSION_KILL(this, this->sessionId());
166 invalidateSessionAuthHosts();
168 // invalidate shared credentials
170 StLock
<Mutex
> _(mCredsLock
);
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
++)
179 // base kill processing
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.
192 void Session::updateAudit() const
194 CommonCriteria::AuditInfo info
;
196 info
.get(mAudit
.sessionId());
203 void Session::verifyKeyStorePassphrase(int32_t retries
)
205 QueryKeybagPassphrase
keybagQuery(*this, retries
);
206 keybagQuery
.inferHints(Server::process());
207 if (keybagQuery
.query() != SecurityAgent::noReason
) {
208 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
212 void Session::changeKeyStorePassphrase()
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());
223 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
227 void Session::resetKeyStorePassphrase(const CssmData
&passphrase
)
229 service_context_t context
= get_current_service_context();
230 service_client_kb_reset(&context
, passphrase
.data(), (int)passphrase
.length());
233 service_context_t
Session::get_current_service_context()
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()
241 void Session::keybagClearState(int state
)
243 mKeybagState
&= ~state
;
246 void Session::keybagSetState(int state
)
248 mKeybagState
|= state
;
251 bool Session::keybagGetState(int state
)
253 return mKeybagState
& state
;
258 // Manage authorization client processes
260 void Session::invalidateSessionAuthHosts()
262 StLock
<Mutex
> _(mAuthHostLock
);
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
;
272 void Session::invalidateAuthHosts()
274 StLock
<Mutex
> _(mSessionLock
);
275 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
276 it
->second
->invalidateSessionAuthHosts();
280 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
282 void Session::processSystemSleep()
284 SecurityAgentXPCQuery::killAllXPCClients();
286 StLock
<Mutex
> _(mSessionLock
);
287 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
288 it
->second
->allReferences(&DbCommon::sleepProcessing
);
293 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
295 void Session::processLockAll()
297 allReferences(&DbCommon::lockProcessing
);
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.
307 RootSession::RootSession(uint64_t attributes
, Server
&server
)
308 : Session(AuditInfo::current(), server
)
311 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
316 // Dynamic sessions use the audit session context of the first-contact client caller.
318 DynamicSession::DynamicSession(const AuditInfo
&audit
)
319 : Session(audit
, Server::active())
325 // Authorization operations
327 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
328 const AuthItemSet
&environment
,
329 AuthorizationFlags flags
,
330 AuthorizationBlob
&newHandle
,
331 const audit_token_t
&auditToken
)
333 // invoke the authorization computation engine
334 CredentialSet resultCreds
;
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
)));
339 SECURITYD_AUTH_CREATE(this, auth
.get());
341 // Make a copy of the mSessionCreds
342 CredentialSet sessionCreds
;
344 StLock
<Mutex
> _(mCredsLock
);
345 sessionCreds
= mSessionCreds
;
348 AuthItemSet outRights
;
349 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
350 &sessionCreds
, &resultCreds
, outRights
, *auth
);
351 newHandle
= auth
->handle();
353 // merge resulting creds into shared pool
354 if ((flags
& kAuthorizationFlagExtendRights
) &&
355 !(flags
& kAuthorizationFlagDestroyRights
))
357 StLock
<Mutex
> _(mCredsLock
);
358 mergeCredentials(resultCreds
);
359 auth
->mergeCredentials(resultCreds
);
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());
369 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
371 AuthorizationToken::Deleter
deleter(authBlob
);
372 AuthorizationToken
&auth
= deleter
;
373 Process
&process
= Server::process();
374 process
.checkAuthorization(&auth
);
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())
383 // now get rid of the authorization itself
384 if (process
.removeAuthorization(&auth
))
388 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
389 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
390 AuthorizationFlags flags
,
391 AuthItemSet
&grantedRights
)
393 AuthorizationToken
&auth
= authorization(authBlob
);
394 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
397 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
398 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
399 AuthorizationFlags flags
,
400 AuthItemSet
&grantedRights
)
402 CredentialSet resultCreds
;
403 CredentialSet effective
;
405 StLock
<Mutex
> _(mCredsLock
);
406 effective
= auth
.effectiveCreds();
408 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
409 &effective
, &resultCreds
, grantedRights
, auth
);
411 // merge resulting creds into shared pool
412 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
414 StLock
<Mutex
> _(mCredsLock
);
415 mergeCredentials(resultCreds
);
416 auth
.mergeCredentials(resultCreds
);
419 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
420 &auth
, int(rights
.size()), int(grantedRights
.size()));
424 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
426 AuthItemSet
&contextInfo
)
428 AuthorizationToken
&auth
= authorization(authBlob
);
429 secdebug("SSauth", "Authorization %p get-info", &auth
);
430 contextInfo
= auth
.infoSet(tag
);
434 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
435 AuthorizationExternalForm
&extForm
)
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
);
448 return errAuthorizationExternalizeNotAllowed
;
451 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
452 AuthorizationBlob
&authBlob
)
454 // interpret the external form
455 const AuthorizationExternalBlob
&extBlob
=
456 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
458 // locate source authorization
459 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
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
);
469 return errAuthorizationInternalizeNotAllowed
;
474 // Accessor method for setting audit session flags.
476 void Session::setAttributes(SessionAttributeBits bits
)
478 StLock
<Mutex
> _(*this);
480 // assert((bits & ~settableAttributes) == 0);
481 mAudit
.ai_flags
= bits
;
486 // The default session setup operation always fails.
487 // Subclasses can override this to support session setup calls.
489 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
491 MacOSError::throwMe(errSessionAuthorizationDenied
);
494 uid_t
Session::originatorUid()
496 if (mAudit
.uid() == AU_DEFAUDITID
) {
497 StLock
<Mutex
> _(*this);
504 // Authorization database I/O
506 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
508 string
rightName(inRightName
);
509 return Server::authority().getRule(rightName
, rightDict
);
513 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
515 CredentialSet resultCreds
;
516 AuthorizationToken
&auth
= authorization(authBlob
);
517 CredentialSet effective
;
520 StLock
<Mutex
> _(mCredsLock
);
521 effective
= auth
.effectiveCreds();
524 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
527 StLock
<Mutex
> _(mCredsLock
);
528 mergeCredentials(resultCreds
);
529 auth
.mergeCredentials(resultCreds
);
532 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
533 &authorization(authBlob
), inRightName
, int32_t(result
));
538 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
540 CredentialSet resultCreds
;
541 AuthorizationToken
&auth
= authorization(authBlob
);
542 CredentialSet effective
;
545 StLock
<Mutex
> _(mCredsLock
);
546 effective
= auth
.effectiveCreds();
549 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
552 StLock
<Mutex
> _(mCredsLock
);
553 mergeCredentials(resultCreds
);
554 auth
.mergeCredentials(resultCreds
);
557 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
558 &authorization(authBlob
), inRightName
, int32_t(result
));
564 // Merge a set of credentials into the shared-session credential pool
566 // must hold mCredsLock
567 void Session::mergeCredentials(CredentialSet
&creds
)
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
);
577 // replace "new" with "old" in input set to retain synchronization
579 updatedCredentials
.erase(*it
);
580 updatedCredentials
.insert(*old
);
583 creds
.swap(updatedCredentials
);
588 // Locate an AuthorizationToken given a blob
590 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
592 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
593 Server::process().checkAuthorization(&auth
);
598 // Run the Authorization engine to check if a given right has been authorized,
599 // independent of an external client request.
601 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
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
;
610 flags
|= kAuthorizationFlagInteractionAllowed
;
611 AuthorizationBlob dummyHandle
;
612 const audit_token_t
*at
= connection
.auditToken();
614 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
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
)
621 bool isAuthorized
= false;
624 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
625 if (errAuthorizationSuccess
== status
)
633 RefPointer
<AuthHostInstance
>
634 Session::authhost(const AuthHostType hostType
, const bool restart
)
636 StLock
<Mutex
> _(mAuthHostLock
);
638 if (hostType
== privilegedAuthHost
)
640 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
643 PerSession::kill(*mAuthHost
);
644 mAuthHost
= new AuthHostInstance(*this, hostType
);
648 else /* if (hostType == securityAgent) */
650 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
653 PerSession::kill(*mSecurityAgent
);
654 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
656 return mSecurityAgent
;
660 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
662 if (Server::process().uid() != 0)
663 MacOSError::throwMe(errSessionAuthorizationDenied
);
664 StLock
<Mutex
> _(*this);
665 mSessionAgentPrefs
= userPrefsDict
;
668 CFDataRef
DynamicSession::copyUserPrefs()
670 StLock
<Mutex
> _(*this);
671 if (mSessionAgentPrefs
)
672 CFRetain(mSessionAgentPrefs
);
673 return mSessionAgentPrefs
;
680 #if defined(DEBUGDUMP)
682 void Session::dumpNode()
684 PerSession::dumpNode();
685 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
686 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);