2 * Copyright (c) 2000-2004,2008-2009 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>
48 using namespace CommonCriteria
;
52 // The static session map
54 Session::SessionMap
Session::mSessions
;
55 Mutex
Session::mSessionLock(Mutex::recursive
);
58 const char Session::kUsername
[] = "username";
59 const char Session::kRealname
[] = "realname";
63 // Create a Session object from initial parameters (create)
65 Session::Session(const AuditInfo
&audit
, Server
&server
)
66 : mAudit(audit
), mSecurityAgent(NULL
), mAuthHost(NULL
)
68 // link to Server as the global nexus in the object mesh
72 StLock
<Mutex
> _(mSessionLock
);
73 assert(!mSessions
[audit
.sessionId()]);
74 mSessions
[audit
.sessionId()] = this;
77 SECURITYD_SESSION_CREATE(this, this->sessionId(), &mAudit
, sizeof(mAudit
));
78 Syslog::notice("Session %d created", this->sessionId());
87 SECURITYD_SESSION_DESTROY(this, this->sessionId());
88 Syslog::notice("Session %d destroyed", this->sessionId());
92 Server
&Session::server() const
94 return parent
<Server
>();
99 // Locate a session object by session identifier
101 Session
&Session::find(pid_t id
, bool create
)
103 if (id
== callerSecuritySession
)
104 return Server::session();
105 StLock
<Mutex
> _(mSessionLock
);
106 SessionMap::iterator it
= mSessions
.find(id
);
107 if (it
!= mSessions
.end())
112 CssmError::throwMe(errSessionInvalidId
);
115 assert(info
.sessionId() == id
);
116 RefPointer
<Session
> session
= new DynamicSession(info
);
117 mSessions
.insert(make_pair(id
, session
));
123 // Act on a death notification for a session's underlying audit session object.
124 // We may not destroy the Session outright here (due to processes that use it),
125 // but we do clear out its accumulated wealth.
126 // Note that we may get spurious death notifications for audit sessions that we
127 // never learned about. Ignore those.
129 void Session::destroy(SessionId id
)
131 // remove session from session map
132 StLock
<Mutex
> _(mSessionLock
);
133 SessionMap::iterator it
= mSessions
.find(id
);
134 if (it
!= mSessions
.end()) {
135 RefPointer
<Session
> session
= it
->second
;
136 assert(session
->sessionId() == id
);
145 StLock
<Mutex
> _(*this); // do we need to take this so early?
146 SECURITYD_SESSION_KILL(this, this->sessionId());
147 invalidateSessionAuthHosts();
149 // invalidate shared credentials
151 StLock
<Mutex
> _(mCredsLock
);
153 IFDEBUG(if (!mSessionCreds
.empty())
154 secdebug("SSauth", "session %p clearing %d shared credentials",
155 this, int(mSessionCreds
.size())));
156 for (CredentialSet::iterator it
= mSessionCreds
.begin(); it
!= mSessionCreds
.end(); it
++)
160 // base kill processing
166 // Refetch audit session data for the current audit session (to catch outside updates
167 // to the audit record). This is the price we're paying for not requiring an IPC to
168 // securityd when audit session data changes (this is desirable for delayering the
169 // software layer cake).
170 // If we ever disallow changes to (parts of the) audit session record in the kernel,
171 // we can loosen up on this continual re-fetching.
173 void Session::updateAudit() const
175 CommonCriteria::AuditInfo info
;
176 StLock
<Mutex
> _(mSessionLock
);
178 info
.get(mAudit
.sessionId());
187 // Manage authorization client processes
189 void Session::invalidateSessionAuthHosts()
191 StLock
<Mutex
> _(mAuthHostLock
);
193 // if you got here, we don't care about pending operations: the auth hosts die
194 Syslog::warning("Killing auth hosts");
195 if (mSecurityAgent
) mSecurityAgent
->UnixPlusPlus::Child::kill(SIGTERM
);
196 if (mAuthHost
) mAuthHost
->UnixPlusPlus::Child::kill(SIGTERM
);
197 mSecurityAgent
= NULL
;
201 void Session::invalidateAuthHosts()
203 StLock
<Mutex
> _(mSessionLock
);
204 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
205 it
->second
->invalidateSessionAuthHosts();
209 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
211 void Session::processSystemSleep()
213 StLock
<Mutex
> _(mSessionLock
);
214 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
215 it
->second
->allReferences(&DbCommon::sleepProcessing
);
220 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
222 void Session::processLockAll()
224 allReferences(&DbCommon::lockProcessing
);
229 // The root session corresponds to the audit session that security is running in.
230 // This is usually the initial system session; but in debug scenarios it may be
231 // an "ordinary" graphic login session. In such a debug case, we may add attribute
232 // flags to the session to make our (debugging) life easier.
234 RootSession::RootSession(uint64_t attributes
, Server
&server
)
235 : Session(AuditInfo::current(), server
)
238 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
243 // Dynamic sessions use the audit session context of the first-contact client caller.
245 DynamicSession::DynamicSession(const AuditInfo
&audit
)
246 : Session(audit
, Server::active())
252 // Authorization operations
254 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
255 const AuthItemSet
&environment
,
256 AuthorizationFlags flags
,
257 AuthorizationBlob
&newHandle
,
258 const audit_token_t
&auditToken
)
260 // invoke the authorization computation engine
261 CredentialSet resultCreds
;
263 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
264 auto_ptr
<AuthorizationToken
> auth(new AuthorizationToken(*this, resultCreds
, auditToken
, (flags
&kAuthorizationFlagLeastPrivileged
)));
266 SECURITYD_AUTH_CREATE(this, auth
.get());
268 // Make a copy of the mSessionCreds
269 CredentialSet sessionCreds
;
271 StLock
<Mutex
> _(mCredsLock
);
272 sessionCreds
= mSessionCreds
;
275 AuthItemSet outRights
;
276 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
277 &sessionCreds
, &resultCreds
, outRights
, *auth
);
278 newHandle
= auth
->handle();
280 // merge resulting creds into shared pool
281 if ((flags
& kAuthorizationFlagExtendRights
) &&
282 !(flags
& kAuthorizationFlagDestroyRights
))
284 StLock
<Mutex
> _(mCredsLock
);
285 mergeCredentials(resultCreds
);
286 auth
->mergeCredentials(resultCreds
);
289 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
290 // not be destroyed anymore since it's destructor asserts it has no processes
291 Server::process().addAuthorization(auth
.get());
296 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
298 AuthorizationToken::Deleter
deleter(authBlob
);
299 AuthorizationToken
&auth
= deleter
;
300 Process
&process
= Server::process();
301 process
.checkAuthorization(&auth
);
303 if (flags
& kAuthorizationFlagDestroyRights
) {
304 // explicitly invalidate all shared credentials and remove them from the session
305 for (CredentialSet::const_iterator it
= auth
.begin(); it
!= auth
.end(); it
++)
306 if ((*it
)->isShared())
310 // now get rid of the authorization itself
311 if (process
.removeAuthorization(&auth
))
315 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
316 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
317 AuthorizationFlags flags
,
318 AuthItemSet
&grantedRights
)
320 AuthorizationToken
&auth
= authorization(authBlob
);
321 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
324 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
325 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
326 AuthorizationFlags flags
,
327 AuthItemSet
&grantedRights
)
329 CredentialSet resultCreds
;
330 CredentialSet effective
;
332 StLock
<Mutex
> _(mCredsLock
);
333 effective
= auth
.effectiveCreds();
335 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
336 &effective
, &resultCreds
, grantedRights
, auth
);
338 // merge resulting creds into shared pool
339 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
341 StLock
<Mutex
> _(mCredsLock
);
342 mergeCredentials(resultCreds
);
343 auth
.mergeCredentials(resultCreds
);
346 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
347 &auth
, int(rights
.size()), int(grantedRights
.size()));
351 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
353 AuthItemSet
&contextInfo
)
355 AuthorizationToken
&auth
= authorization(authBlob
);
356 secdebug("SSauth", "Authorization %p get-info", &auth
);
357 contextInfo
= auth
.infoSet(tag
);
361 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
362 AuthorizationExternalForm
&extForm
)
364 const AuthorizationToken
&auth
= authorization(authBlob
);
365 StLock
<Mutex
> _(*this);
366 if (auth
.mayExternalize(Server::process())) {
367 memset(&extForm
, 0, sizeof(extForm
));
368 AuthorizationExternalBlob
&extBlob
=
369 reinterpret_cast<AuthorizationExternalBlob
&>(extForm
);
370 extBlob
.blob
= auth
.handle();
371 extBlob
.session
= this->sessionId();
372 secdebug("SSauth", "Authorization %p externalized", &auth
);
375 return errAuthorizationExternalizeNotAllowed
;
378 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
379 AuthorizationBlob
&authBlob
)
381 // interpret the external form
382 const AuthorizationExternalBlob
&extBlob
=
383 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
385 // locate source authorization
386 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
388 // check for permission and do it
389 if (sourceAuth
.mayInternalize(Server::process(), true)) {
390 StLock
<Mutex
> _(*this);
391 authBlob
= extBlob
.blob
;
392 Server::process().addAuthorization(&sourceAuth
);
393 secdebug("SSauth", "Authorization %p internalized", &sourceAuth
);
396 return errAuthorizationInternalizeNotAllowed
;
401 // Accessor method for setting audit session flags.
403 void Session::setAttributes(SessionAttributeBits bits
)
405 StLock
<Mutex
> _(*this);
407 assert((bits
& ~settableAttributes
) == 0);
408 mAudit
.ai_flags
= bits
;
413 // The default session setup operation always fails.
414 // Subclasses can override this to support session setup calls.
416 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
418 MacOSError::throwMe(errSessionAuthorizationDenied
);
423 // Authorization database I/O
425 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
427 string
rightName(inRightName
);
428 return Server::authority().getRule(rightName
, rightDict
);
432 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
434 CredentialSet resultCreds
;
435 AuthorizationToken
&auth
= authorization(authBlob
);
436 CredentialSet effective
;
439 StLock
<Mutex
> _(mCredsLock
);
440 effective
= auth
.effectiveCreds();
443 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
446 StLock
<Mutex
> _(mCredsLock
);
447 mergeCredentials(resultCreds
);
448 auth
.mergeCredentials(resultCreds
);
451 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
452 &authorization(authBlob
), inRightName
, int32_t(result
));
457 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
459 CredentialSet resultCreds
;
460 AuthorizationToken
&auth
= authorization(authBlob
);
461 CredentialSet effective
;
464 StLock
<Mutex
> _(mCredsLock
);
465 effective
= auth
.effectiveCreds();
468 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
471 StLock
<Mutex
> _(mCredsLock
);
472 mergeCredentials(resultCreds
);
473 auth
.mergeCredentials(resultCreds
);
476 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
477 &authorization(authBlob
), inRightName
, int32_t(result
));
483 // Merge a set of credentials into the shared-session credential pool
485 // must hold mCredsLock
486 void Session::mergeCredentials(CredentialSet
&creds
)
488 secdebug("SSsession", "%p merge creds @%p", this, &creds
);
489 CredentialSet updatedCredentials
= creds
;
490 for (CredentialSet::const_iterator it
= creds
.begin(); it
!= creds
.end(); it
++)
491 if ((*it
)->isShared() && (*it
)->isValid()) {
492 CredentialSet::iterator old
= mSessionCreds
.find(*it
);
493 if (old
== mSessionCreds
.end()) {
494 mSessionCreds
.insert(*it
);
496 // replace "new" with "old" in input set to retain synchronization
498 updatedCredentials
.erase(*it
);
499 updatedCredentials
.insert(*old
);
502 creds
.swap(updatedCredentials
);
507 // Locate an AuthorizationToken given a blob
509 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
511 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
512 Server::process().checkAuthorization(&auth
);
517 // Run the Authorization engine to check if a given right has been authorized,
518 // independent of an external client request.
520 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
522 // dummy up the arguments for authCreate()
523 AuthorizationItem rightItem
= { rightName
.c_str(), 0, NULL
, 0 };
524 AuthorizationItemSet rightItemSet
= { 1, &rightItem
};
525 AuthItemSet
rightAuthItemSet(&rightItemSet
);
526 AuthItemSet
envAuthItemSet(kAuthorizationEmptyEnvironment
);
527 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagExtendRights
;
529 flags
|= kAuthorizationFlagInteractionAllowed
;
530 AuthorizationBlob dummyHandle
;
531 const audit_token_t
*at
= connection
.auditToken();
533 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
536 // for places within securityd that don't want to #include
537 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
538 bool Session::isRightAuthorized(string
&rightName
, Connection
&connection
, bool allowUI
)
540 bool isAuthorized
= false;
543 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
544 if (errAuthorizationSuccess
== status
)
552 RefPointer
<AuthHostInstance
>
553 Session::authhost(const AuthHostType hostType
, const bool restart
)
555 StLock
<Mutex
> _(mAuthHostLock
);
557 if (hostType
== privilegedAuthHost
)
559 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
562 PerSession::kill(*mAuthHost
);
563 mAuthHost
= new AuthHostInstance(*this, hostType
);
567 else /* if (hostType == securityAgent) */
569 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
572 PerSession::kill(*mSecurityAgent
);
573 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
575 return mSecurityAgent
;
579 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
581 if (Server::process().uid() != 0)
582 MacOSError::throwMe(errSessionAuthorizationDenied
);
583 StLock
<Mutex
> _(*this);
584 mSessionAgentPrefs
= userPrefsDict
;
587 CFDataRef
DynamicSession::copyUserPrefs()
589 StLock
<Mutex
> _(*this);
590 if (mSessionAgentPrefs
)
591 CFRetain(mSessionAgentPrefs
);
592 return mSessionAgentPrefs
;
599 #if defined(DEBUGDUMP)
601 void Session::dumpNode()
603 PerSession::dumpNode();
604 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
605 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);