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());
93 // Locate a session object by session identifier
95 Session
&Session::find(pid_t id
, bool create
)
97 if (id
== callerSecuritySession
)
98 return Server::session();
99 StLock
<Mutex
> _(mSessionLock
);
100 SessionMap::iterator it
= mSessions
.find(id
);
101 if (it
!= mSessions
.end())
106 CssmError::throwMe(errSessionInvalidId
);
109 assert(info
.sessionId() == id
);
110 RefPointer
<Session
> session
= new DynamicSession(info
);
111 mSessions
.insert(make_pair(id
, session
));
117 // Act on a death notification for a session's underlying audit session object.
118 // We may not destroy the Session outright here (due to processes that use it),
119 // but we do clear out its accumulated wealth.
120 // Note that we may get spurious death notifications for audit sessions that we
121 // never learned about. Ignore those.
123 void Session::destroy(SessionId id
)
125 // remove session from session map
126 StLock
<Mutex
> _(mSessionLock
);
127 SessionMap::iterator it
= mSessions
.find(id
);
128 if (it
!= mSessions
.end()) {
129 RefPointer
<Session
> session
= it
->second
;
130 assert(session
->sessionId() == id
);
139 StLock
<Mutex
> _(*this); // do we need to take this so early?
140 SECURITYD_SESSION_KILL(this, this->sessionId());
141 invalidateSessionAuthHosts();
143 // invalidate shared credentials
145 StLock
<Mutex
> _(mCredsLock
);
147 IFDEBUG(if (!mSessionCreds
.empty())
148 secdebug("SSauth", "session %p clearing %d shared credentials",
149 this, int(mSessionCreds
.size())));
150 for (CredentialSet::iterator it
= mSessionCreds
.begin(); it
!= mSessionCreds
.end(); it
++)
154 // base kill processing
160 // Refetch audit session data for the current audit session (to catch outside updates
161 // to the audit record). This is the price we're paying for not requiring an IPC to
162 // securityd when audit session data changes (this is desirable for delayering the
163 // software layer cake).
164 // If we ever disallow changes to (parts of the) audit session record in the kernel,
165 // we can loosen up on this continual re-fetching.
167 void Session::updateAudit() const
169 mAudit
.get(mAudit
.sessionId());
174 // Manage authorization client processes
176 void Session::invalidateSessionAuthHosts()
178 StLock
<Mutex
> _(mAuthHostLock
);
180 // if you got here, we don't care about pending operations: the auth hosts die
181 Syslog::warning("Killing auth hosts");
182 if (mSecurityAgent
) mSecurityAgent
->UnixPlusPlus::Child::kill(SIGTERM
);
183 if (mAuthHost
) mAuthHost
->UnixPlusPlus::Child::kill(SIGTERM
);
184 mSecurityAgent
= NULL
;
188 void Session::invalidateAuthHosts()
190 StLock
<Mutex
> _(mSessionLock
);
191 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
192 it
->second
->invalidateSessionAuthHosts();
196 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
198 void Session::processSystemSleep()
200 StLock
<Mutex
> _(mSessionLock
);
201 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
202 it
->second
->allReferences(&DbCommon::sleepProcessing
);
207 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
209 void Session::processLockAll()
211 allReferences(&DbCommon::lockProcessing
);
216 // The root session corresponds to the audit session that security is running in.
217 // This is usually the initial system session; but in debug scenarios it may be
218 // an "ordinary" graphic login session. In such a debug case, we may add attribute
219 // flags to the session to make our (debugging) life easier.
221 RootSession::RootSession(uint64_t attributes
, Server
&server
)
222 : Session(AuditInfo::current(), server
)
225 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
230 // Dynamic sessions use the audit session context of the first-contact client caller.
232 DynamicSession::DynamicSession(const AuditInfo
&audit
)
233 : Session(audit
, Server::active())
239 // Authorization operations
241 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
242 const AuthItemSet
&environment
,
243 AuthorizationFlags flags
,
244 AuthorizationBlob
&newHandle
,
245 const audit_token_t
&auditToken
)
247 // invoke the authorization computation engine
248 CredentialSet resultCreds
;
250 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
251 auto_ptr
<AuthorizationToken
> auth(new AuthorizationToken(*this, resultCreds
, auditToken
, (flags
&kAuthorizationFlagLeastPrivileged
)));
253 SECURITYD_AUTH_CREATE(this, auth
.get());
255 // Make a copy of the mSessionCreds
256 CredentialSet sessionCreds
;
258 StLock
<Mutex
> _(mCredsLock
);
259 sessionCreds
= mSessionCreds
;
262 AuthItemSet outRights
;
263 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
264 &sessionCreds
, &resultCreds
, outRights
, *auth
);
265 newHandle
= auth
->handle();
267 // merge resulting creds into shared pool
268 if ((flags
& kAuthorizationFlagExtendRights
) &&
269 !(flags
& kAuthorizationFlagDestroyRights
))
271 StLock
<Mutex
> _(mCredsLock
);
272 mergeCredentials(resultCreds
);
273 auth
->mergeCredentials(resultCreds
);
276 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
277 // not be destroyed anymore since it's destructor asserts it has no processes
278 Server::process().addAuthorization(auth
.get());
283 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
285 AuthorizationToken::Deleter
deleter(authBlob
);
286 AuthorizationToken
&auth
= deleter
;
287 Process
&process
= Server::process();
288 process
.checkAuthorization(&auth
);
290 if (flags
& kAuthorizationFlagDestroyRights
) {
291 // explicitly invalidate all shared credentials and remove them from the session
292 for (CredentialSet::const_iterator it
= auth
.begin(); it
!= auth
.end(); it
++)
293 if ((*it
)->isShared())
297 // now get rid of the authorization itself
298 if (process
.removeAuthorization(&auth
))
302 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
303 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
304 AuthorizationFlags flags
,
305 AuthItemSet
&grantedRights
)
307 AuthorizationToken
&auth
= authorization(authBlob
);
308 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
311 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
312 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
313 AuthorizationFlags flags
,
314 AuthItemSet
&grantedRights
)
316 CredentialSet resultCreds
;
317 CredentialSet effective
;
319 StLock
<Mutex
> _(mCredsLock
);
320 effective
= auth
.effectiveCreds();
322 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
323 &effective
, &resultCreds
, grantedRights
, auth
);
325 // merge resulting creds into shared pool
326 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
328 StLock
<Mutex
> _(mCredsLock
);
329 mergeCredentials(resultCreds
);
330 auth
.mergeCredentials(resultCreds
);
333 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
334 &auth
, int(rights
.size()), int(grantedRights
.size()));
338 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
340 AuthItemSet
&contextInfo
)
342 AuthorizationToken
&auth
= authorization(authBlob
);
343 secdebug("SSauth", "Authorization %p get-info", &auth
);
344 contextInfo
= auth
.infoSet(tag
);
348 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
349 AuthorizationExternalForm
&extForm
)
351 const AuthorizationToken
&auth
= authorization(authBlob
);
352 StLock
<Mutex
> _(*this);
353 if (auth
.mayExternalize(Server::process())) {
354 memset(&extForm
, 0, sizeof(extForm
));
355 AuthorizationExternalBlob
&extBlob
=
356 reinterpret_cast<AuthorizationExternalBlob
&>(extForm
);
357 extBlob
.blob
= auth
.handle();
358 extBlob
.session
= this->sessionId();
359 secdebug("SSauth", "Authorization %p externalized", &auth
);
362 return errAuthorizationExternalizeNotAllowed
;
365 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
366 AuthorizationBlob
&authBlob
)
368 // interpret the external form
369 const AuthorizationExternalBlob
&extBlob
=
370 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
372 // locate source authorization
373 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
375 // check for permission and do it
376 if (sourceAuth
.mayInternalize(Server::process(), true)) {
377 StLock
<Mutex
> _(*this);
378 authBlob
= extBlob
.blob
;
379 Server::process().addAuthorization(&sourceAuth
);
380 secdebug("SSauth", "Authorization %p internalized", &sourceAuth
);
383 return errAuthorizationInternalizeNotAllowed
;
388 // Accessor method for setting audit session flags.
390 void Session::setAttributes(SessionAttributeBits bits
)
392 StLock
<Mutex
> _(*this);
394 assert((bits
& ~settableAttributes
) == 0);
395 mAudit
.ai_flags
= bits
;
400 // The default session setup operation always fails.
401 // Subclasses can override this to support session setup calls.
403 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
405 MacOSError::throwMe(errSessionAuthorizationDenied
);
410 // Authorization database I/O
412 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
414 string
rightName(inRightName
);
415 return Server::authority().getRule(rightName
, rightDict
);
419 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
421 CredentialSet resultCreds
;
422 AuthorizationToken
&auth
= authorization(authBlob
);
423 CredentialSet effective
;
426 StLock
<Mutex
> _(mCredsLock
);
427 effective
= auth
.effectiveCreds();
430 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
433 StLock
<Mutex
> _(mCredsLock
);
434 mergeCredentials(resultCreds
);
435 auth
.mergeCredentials(resultCreds
);
438 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
439 &authorization(authBlob
), inRightName
, int32_t(result
));
444 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
446 CredentialSet resultCreds
;
447 AuthorizationToken
&auth
= authorization(authBlob
);
448 CredentialSet effective
;
451 StLock
<Mutex
> _(mCredsLock
);
452 effective
= auth
.effectiveCreds();
455 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
458 StLock
<Mutex
> _(mCredsLock
);
459 mergeCredentials(resultCreds
);
460 auth
.mergeCredentials(resultCreds
);
463 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
464 &authorization(authBlob
), inRightName
, int32_t(result
));
470 // Merge a set of credentials into the shared-session credential pool
472 // must hold mCredsLock
473 void Session::mergeCredentials(CredentialSet
&creds
)
475 secdebug("SSsession", "%p merge creds @%p", this, &creds
);
476 CredentialSet updatedCredentials
= creds
;
477 for (CredentialSet::const_iterator it
= creds
.begin(); it
!= creds
.end(); it
++)
478 if ((*it
)->isShared() && (*it
)->isValid()) {
479 CredentialSet::iterator old
= mSessionCreds
.find(*it
);
480 if (old
== mSessionCreds
.end()) {
481 mSessionCreds
.insert(*it
);
483 // replace "new" with "old" in input set to retain synchronization
485 updatedCredentials
.erase(*it
);
486 updatedCredentials
.insert(*old
);
489 creds
.swap(updatedCredentials
);
494 // Locate an AuthorizationToken given a blob
496 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
498 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
499 Server::process().checkAuthorization(&auth
);
504 // Run the Authorization engine to check if a given right has been authorized,
505 // independent of an external client request.
507 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
509 // dummy up the arguments for authCreate()
510 AuthorizationItem rightItem
= { rightName
.c_str(), 0, NULL
, 0 };
511 AuthorizationItemSet rightItemSet
= { 1, &rightItem
};
512 AuthItemSet
rightAuthItemSet(&rightItemSet
);
513 AuthItemSet
envAuthItemSet(kAuthorizationEmptyEnvironment
);
514 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagExtendRights
;
516 flags
|= kAuthorizationFlagInteractionAllowed
;
517 AuthorizationBlob dummyHandle
;
518 const audit_token_t
*at
= connection
.auditToken();
520 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
523 // for places within securityd that don't want to #include
524 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
525 bool Session::isRightAuthorized(string
&rightName
, Connection
&connection
, bool allowUI
)
527 bool isAuthorized
= false;
530 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
531 if (errAuthorizationSuccess
== status
)
539 RefPointer
<AuthHostInstance
>
540 Session::authhost(const AuthHostType hostType
, const bool restart
)
542 StLock
<Mutex
> _(mAuthHostLock
);
544 if (hostType
== privilegedAuthHost
)
546 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
549 PerSession::kill(*mAuthHost
);
550 mAuthHost
= new AuthHostInstance(*this, hostType
);
554 else /* if (hostType == securityAgent) */
556 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
559 PerSession::kill(*mSecurityAgent
);
560 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
562 return mSecurityAgent
;
566 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
568 if (Server::process().uid() != 0)
569 MacOSError::throwMe(errSessionAuthorizationDenied
);
570 StLock
<Mutex
> _(*this);
571 mSessionAgentPrefs
= userPrefsDict
;
574 CFDataRef
DynamicSession::copyUserPrefs()
576 StLock
<Mutex
> _(*this);
577 if (mSessionAgentPrefs
)
578 CFRetain(mSessionAgentPrefs
);
579 return mSessionAgentPrefs
;
586 #if defined(DEBUGDUMP)
588 void Session::dumpNode()
590 PerSession::dumpNode();
591 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
592 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);