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 // sessions are destroy outside of a process request session->get_current_service_context()
155 service_context_t context
= { session
->sessionId(), session
->originatorUid(), {} };
156 service_client_kb_lock(&context
);
165 StLock
<Mutex
> _(*this); // do we need to take this so early?
166 SECURITYD_SESSION_KILL(this, this->sessionId());
167 invalidateSessionAuthHosts();
169 // invalidate shared credentials
171 StLock
<Mutex
> _(mCredsLock
);
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
++)
180 // base kill processing
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.
193 void Session::updateAudit() const
195 CommonCriteria::AuditInfo info
;
197 info
.get(mAudit
.sessionId());
204 void Session::verifyKeyStorePassphrase(int32_t retries
)
206 QueryKeybagPassphrase
keybagQuery(*this, retries
);
207 keybagQuery
.inferHints(Server::process());
208 if (keybagQuery
.query() != SecurityAgent::noReason
) {
209 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
213 void Session::changeKeyStorePassphrase()
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());
224 CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED
);
228 void Session::resetKeyStorePassphrase(const CssmData
&passphrase
)
230 service_context_t context
= get_current_service_context();
231 service_client_kb_reset(&context
, passphrase
.data(), (int)passphrase
.length());
234 service_context_t
Session::get_current_service_context()
236 service_context_t context
= { sessionId(), originatorUid(), *Server::connection().auditToken() };
240 void Session::keybagClearState(int state
)
242 mKeybagState
&= ~state
;
245 void Session::keybagSetState(int state
)
247 mKeybagState
|= state
;
250 bool Session::keybagGetState(int state
)
252 return mKeybagState
& state
;
257 // Manage authorization client processes
259 void Session::invalidateSessionAuthHosts()
261 StLock
<Mutex
> _(mAuthHostLock
);
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
;
271 void Session::invalidateAuthHosts()
273 StLock
<Mutex
> _(mSessionLock
);
274 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
275 it
->second
->invalidateSessionAuthHosts();
279 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
281 void Session::processSystemSleep()
283 SecurityAgentXPCQuery::killAllXPCClients();
285 StLock
<Mutex
> _(mSessionLock
);
286 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
287 it
->second
->allReferences(&DbCommon::sleepProcessing
);
292 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
294 void Session::processLockAll()
296 allReferences(&DbCommon::lockProcessing
);
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.
306 RootSession::RootSession(uint64_t attributes
, Server
&server
)
307 : Session(AuditInfo::current(), server
)
310 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
315 // Dynamic sessions use the audit session context of the first-contact client caller.
317 DynamicSession::DynamicSession(const AuditInfo
&audit
)
318 : Session(audit
, Server::active())
324 // Authorization operations
326 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
327 const AuthItemSet
&environment
,
328 AuthorizationFlags flags
,
329 AuthorizationBlob
&newHandle
,
330 const audit_token_t
&auditToken
)
332 // invoke the authorization computation engine
333 CredentialSet resultCreds
;
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
)));
338 SECURITYD_AUTH_CREATE(this, auth
.get());
340 // Make a copy of the mSessionCreds
341 CredentialSet sessionCreds
;
343 StLock
<Mutex
> _(mCredsLock
);
344 sessionCreds
= mSessionCreds
;
347 AuthItemSet outRights
;
348 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
349 &sessionCreds
, &resultCreds
, outRights
, *auth
);
350 newHandle
= auth
->handle();
352 // merge resulting creds into shared pool
353 if ((flags
& kAuthorizationFlagExtendRights
) &&
354 !(flags
& kAuthorizationFlagDestroyRights
))
356 StLock
<Mutex
> _(mCredsLock
);
357 mergeCredentials(resultCreds
);
358 auth
->mergeCredentials(resultCreds
);
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());
368 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
370 AuthorizationToken::Deleter
deleter(authBlob
);
371 AuthorizationToken
&auth
= deleter
;
372 Process
&process
= Server::process();
373 process
.checkAuthorization(&auth
);
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())
382 // now get rid of the authorization itself
383 if (process
.removeAuthorization(&auth
))
387 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
388 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
389 AuthorizationFlags flags
,
390 AuthItemSet
&grantedRights
)
392 AuthorizationToken
&auth
= authorization(authBlob
);
393 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
396 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
397 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
398 AuthorizationFlags flags
,
399 AuthItemSet
&grantedRights
)
401 CredentialSet resultCreds
;
402 CredentialSet effective
;
404 StLock
<Mutex
> _(mCredsLock
);
405 effective
= auth
.effectiveCreds();
407 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
408 &effective
, &resultCreds
, grantedRights
, auth
);
410 // merge resulting creds into shared pool
411 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
413 StLock
<Mutex
> _(mCredsLock
);
414 mergeCredentials(resultCreds
);
415 auth
.mergeCredentials(resultCreds
);
418 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
419 &auth
, int(rights
.size()), int(grantedRights
.size()));
423 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
425 AuthItemSet
&contextInfo
)
427 AuthorizationToken
&auth
= authorization(authBlob
);
428 secdebug("SSauth", "Authorization %p get-info", &auth
);
429 contextInfo
= auth
.infoSet(tag
);
433 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
434 AuthorizationExternalForm
&extForm
)
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
);
447 return errAuthorizationExternalizeNotAllowed
;
450 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
451 AuthorizationBlob
&authBlob
)
453 // interpret the external form
454 const AuthorizationExternalBlob
&extBlob
=
455 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
457 // locate source authorization
458 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
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
);
468 return errAuthorizationInternalizeNotAllowed
;
473 // Accessor method for setting audit session flags.
475 void Session::setAttributes(SessionAttributeBits bits
)
477 StLock
<Mutex
> _(*this);
479 // assert((bits & ~settableAttributes) == 0);
480 mAudit
.ai_flags
= bits
;
485 // The default session setup operation always fails.
486 // Subclasses can override this to support session setup calls.
488 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
490 MacOSError::throwMe(errSessionAuthorizationDenied
);
493 uid_t
Session::originatorUid()
495 if (mAudit
.uid() == AU_DEFAUDITID
) {
496 StLock
<Mutex
> _(*this);
503 // Authorization database I/O
505 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
507 string
rightName(inRightName
);
508 return Server::authority().getRule(rightName
, rightDict
);
512 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
514 CredentialSet resultCreds
;
515 AuthorizationToken
&auth
= authorization(authBlob
);
516 CredentialSet effective
;
519 StLock
<Mutex
> _(mCredsLock
);
520 effective
= auth
.effectiveCreds();
523 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
526 StLock
<Mutex
> _(mCredsLock
);
527 mergeCredentials(resultCreds
);
528 auth
.mergeCredentials(resultCreds
);
531 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
532 &authorization(authBlob
), inRightName
, int32_t(result
));
537 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
539 CredentialSet resultCreds
;
540 AuthorizationToken
&auth
= authorization(authBlob
);
541 CredentialSet effective
;
544 StLock
<Mutex
> _(mCredsLock
);
545 effective
= auth
.effectiveCreds();
548 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
551 StLock
<Mutex
> _(mCredsLock
);
552 mergeCredentials(resultCreds
);
553 auth
.mergeCredentials(resultCreds
);
556 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
557 &authorization(authBlob
), inRightName
, int32_t(result
));
563 // Merge a set of credentials into the shared-session credential pool
565 // must hold mCredsLock
566 void Session::mergeCredentials(CredentialSet
&creds
)
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
);
576 // replace "new" with "old" in input set to retain synchronization
578 updatedCredentials
.erase(*it
);
579 updatedCredentials
.insert(*old
);
582 creds
.swap(updatedCredentials
);
587 // Locate an AuthorizationToken given a blob
589 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
591 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
592 Server::process().checkAuthorization(&auth
);
597 // Run the Authorization engine to check if a given right has been authorized,
598 // independent of an external client request.
600 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
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
;
609 flags
|= kAuthorizationFlagInteractionAllowed
;
610 AuthorizationBlob dummyHandle
;
611 const audit_token_t
*at
= connection
.auditToken();
613 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
616 // for places within securityd that don't want to #include
617 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
618 bool Session::isRightAuthorized(string
&rightName
, Connection
&connection
, bool allowUI
)
620 bool isAuthorized
= false;
623 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
624 if (errAuthorizationSuccess
== status
)
632 RefPointer
<AuthHostInstance
>
633 Session::authhost(const AuthHostType hostType
, const bool restart
)
635 StLock
<Mutex
> _(mAuthHostLock
);
637 if (hostType
== privilegedAuthHost
)
639 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
642 PerSession::kill(*mAuthHost
);
643 mAuthHost
= new AuthHostInstance(*this, hostType
);
647 else /* if (hostType == securityAgent) */
649 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
652 PerSession::kill(*mSecurityAgent
);
653 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
655 return mSecurityAgent
;
659 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
661 if (Server::process().uid() != 0)
662 MacOSError::throwMe(errSessionAuthorizationDenied
);
663 StLock
<Mutex
> _(*this);
664 mSessionAgentPrefs
= userPrefsDict
;
667 CFDataRef
DynamicSession::copyUserPrefs()
669 StLock
<Mutex
> _(*this);
670 if (mSessionAgentPrefs
)
671 CFRetain(mSessionAgentPrefs
);
672 return mSessionAgentPrefs
;
679 #if defined(DEBUGDUMP)
681 void Session::dumpNode()
683 PerSession::dumpNode();
684 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
685 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);