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 SecurityAgent::Clients::killAllClients();
215 StLock
<Mutex
> _(mSessionLock
);
216 for (SessionMap::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
217 it
->second
->allReferences(&DbCommon::sleepProcessing
);
222 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
224 void Session::processLockAll()
226 allReferences(&DbCommon::lockProcessing
);
231 // The root session corresponds to the audit session that security is running in.
232 // This is usually the initial system session; but in debug scenarios it may be
233 // an "ordinary" graphic login session. In such a debug case, we may add attribute
234 // flags to the session to make our (debugging) life easier.
236 RootSession::RootSession(uint64_t attributes
, Server
&server
)
237 : Session(AuditInfo::current(), server
)
240 mAudit
.ai_flags
|= attributes
; // merge imposed attributes
245 // Dynamic sessions use the audit session context of the first-contact client caller.
247 DynamicSession::DynamicSession(const AuditInfo
&audit
)
248 : Session(audit
, Server::active())
254 // Authorization operations
256 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
257 const AuthItemSet
&environment
,
258 AuthorizationFlags flags
,
259 AuthorizationBlob
&newHandle
,
260 const audit_token_t
&auditToken
)
262 // invoke the authorization computation engine
263 CredentialSet resultCreds
;
265 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
266 auto_ptr
<AuthorizationToken
> auth(new AuthorizationToken(*this, resultCreds
, auditToken
, (flags
&kAuthorizationFlagLeastPrivileged
)));
268 SECURITYD_AUTH_CREATE(this, auth
.get());
270 // Make a copy of the mSessionCreds
271 CredentialSet sessionCreds
;
273 StLock
<Mutex
> _(mCredsLock
);
274 sessionCreds
= mSessionCreds
;
277 AuthItemSet outRights
;
278 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
279 &sessionCreds
, &resultCreds
, outRights
, *auth
);
280 newHandle
= auth
->handle();
282 // merge resulting creds into shared pool
283 if ((flags
& kAuthorizationFlagExtendRights
) &&
284 !(flags
& kAuthorizationFlagDestroyRights
))
286 StLock
<Mutex
> _(mCredsLock
);
287 mergeCredentials(resultCreds
);
288 auth
->mergeCredentials(resultCreds
);
291 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
292 // not be destroyed anymore since it's destructor asserts it has no processes
293 Server::process().addAuthorization(auth
.get());
298 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
300 AuthorizationToken::Deleter
deleter(authBlob
);
301 AuthorizationToken
&auth
= deleter
;
302 Process
&process
= Server::process();
303 process
.checkAuthorization(&auth
);
305 if (flags
& kAuthorizationFlagDestroyRights
) {
306 // explicitly invalidate all shared credentials and remove them from the session
307 for (CredentialSet::const_iterator it
= auth
.begin(); it
!= auth
.end(); it
++)
308 if ((*it
)->isShared())
312 // now get rid of the authorization itself
313 if (process
.removeAuthorization(&auth
))
317 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
318 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
319 AuthorizationFlags flags
,
320 AuthItemSet
&grantedRights
)
322 AuthorizationToken
&auth
= authorization(authBlob
);
323 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
326 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
327 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
328 AuthorizationFlags flags
,
329 AuthItemSet
&grantedRights
)
331 CredentialSet resultCreds
;
332 CredentialSet effective
;
334 StLock
<Mutex
> _(mCredsLock
);
335 effective
= auth
.effectiveCreds();
337 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
338 &effective
, &resultCreds
, grantedRights
, auth
);
340 // merge resulting creds into shared pool
341 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
343 StLock
<Mutex
> _(mCredsLock
);
344 mergeCredentials(resultCreds
);
345 auth
.mergeCredentials(resultCreds
);
348 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
349 &auth
, int(rights
.size()), int(grantedRights
.size()));
353 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
355 AuthItemSet
&contextInfo
)
357 AuthorizationToken
&auth
= authorization(authBlob
);
358 secdebug("SSauth", "Authorization %p get-info", &auth
);
359 contextInfo
= auth
.infoSet(tag
);
363 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
364 AuthorizationExternalForm
&extForm
)
366 const AuthorizationToken
&auth
= authorization(authBlob
);
367 StLock
<Mutex
> _(*this);
368 if (auth
.mayExternalize(Server::process())) {
369 memset(&extForm
, 0, sizeof(extForm
));
370 AuthorizationExternalBlob
&extBlob
=
371 reinterpret_cast<AuthorizationExternalBlob
&>(extForm
);
372 extBlob
.blob
= auth
.handle();
373 extBlob
.session
= this->sessionId();
374 secdebug("SSauth", "Authorization %p externalized", &auth
);
377 return errAuthorizationExternalizeNotAllowed
;
380 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
381 AuthorizationBlob
&authBlob
)
383 // interpret the external form
384 const AuthorizationExternalBlob
&extBlob
=
385 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
387 // locate source authorization
388 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
390 // check for permission and do it
391 if (sourceAuth
.mayInternalize(Server::process(), true)) {
392 StLock
<Mutex
> _(*this);
393 authBlob
= extBlob
.blob
;
394 Server::process().addAuthorization(&sourceAuth
);
395 secdebug("SSauth", "Authorization %p internalized", &sourceAuth
);
398 return errAuthorizationInternalizeNotAllowed
;
403 // Accessor method for setting audit session flags.
405 void Session::setAttributes(SessionAttributeBits bits
)
407 StLock
<Mutex
> _(*this);
409 // assert((bits & ~settableAttributes) == 0);
410 mAudit
.ai_flags
= bits
;
415 // The default session setup operation always fails.
416 // Subclasses can override this to support session setup calls.
418 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
420 MacOSError::throwMe(errSessionAuthorizationDenied
);
425 // Authorization database I/O
427 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
429 string
rightName(inRightName
);
430 return Server::authority().getRule(rightName
, rightDict
);
434 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
436 CredentialSet resultCreds
;
437 AuthorizationToken
&auth
= authorization(authBlob
);
438 CredentialSet effective
;
441 StLock
<Mutex
> _(mCredsLock
);
442 effective
= auth
.effectiveCreds();
445 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
448 StLock
<Mutex
> _(mCredsLock
);
449 mergeCredentials(resultCreds
);
450 auth
.mergeCredentials(resultCreds
);
453 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
454 &authorization(authBlob
), inRightName
, int32_t(result
));
459 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
461 CredentialSet resultCreds
;
462 AuthorizationToken
&auth
= authorization(authBlob
);
463 CredentialSet effective
;
466 StLock
<Mutex
> _(mCredsLock
);
467 effective
= auth
.effectiveCreds();
470 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
473 StLock
<Mutex
> _(mCredsLock
);
474 mergeCredentials(resultCreds
);
475 auth
.mergeCredentials(resultCreds
);
478 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
479 &authorization(authBlob
), inRightName
, int32_t(result
));
485 // Merge a set of credentials into the shared-session credential pool
487 // must hold mCredsLock
488 void Session::mergeCredentials(CredentialSet
&creds
)
490 secdebug("SSsession", "%p merge creds @%p", this, &creds
);
491 CredentialSet updatedCredentials
= creds
;
492 for (CredentialSet::const_iterator it
= creds
.begin(); it
!= creds
.end(); it
++)
493 if ((*it
)->isShared() && (*it
)->isValid()) {
494 CredentialSet::iterator old
= mSessionCreds
.find(*it
);
495 if (old
== mSessionCreds
.end()) {
496 mSessionCreds
.insert(*it
);
498 // replace "new" with "old" in input set to retain synchronization
500 updatedCredentials
.erase(*it
);
501 updatedCredentials
.insert(*old
);
504 creds
.swap(updatedCredentials
);
509 // Locate an AuthorizationToken given a blob
511 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
513 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
514 Server::process().checkAuthorization(&auth
);
519 // Run the Authorization engine to check if a given right has been authorized,
520 // independent of an external client request.
522 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
524 // dummy up the arguments for authCreate()
525 AuthorizationItem rightItem
= { rightName
.c_str(), 0, NULL
, 0 };
526 AuthorizationItemSet rightItemSet
= { 1, &rightItem
};
527 AuthItemSet
rightAuthItemSet(&rightItemSet
);
528 AuthItemSet
envAuthItemSet(kAuthorizationEmptyEnvironment
);
529 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagExtendRights
;
531 flags
|= kAuthorizationFlagInteractionAllowed
;
532 AuthorizationBlob dummyHandle
;
533 const audit_token_t
*at
= connection
.auditToken();
535 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
538 // for places within securityd that don't want to #include
539 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
540 bool Session::isRightAuthorized(string
&rightName
, Connection
&connection
, bool allowUI
)
542 bool isAuthorized
= false;
545 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
546 if (errAuthorizationSuccess
== status
)
554 RefPointer
<AuthHostInstance
>
555 Session::authhost(const AuthHostType hostType
, const bool restart
)
557 StLock
<Mutex
> _(mAuthHostLock
);
559 if (hostType
== privilegedAuthHost
)
561 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
564 PerSession::kill(*mAuthHost
);
565 mAuthHost
= new AuthHostInstance(*this, hostType
);
569 else /* if (hostType == securityAgent) */
571 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
574 PerSession::kill(*mSecurityAgent
);
575 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
577 return mSecurityAgent
;
581 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
583 if (Server::process().uid() != 0)
584 MacOSError::throwMe(errSessionAuthorizationDenied
);
585 StLock
<Mutex
> _(*this);
586 mSessionAgentPrefs
= userPrefsDict
;
589 CFDataRef
DynamicSession::copyUserPrefs()
591 StLock
<Mutex
> _(*this);
592 if (mSessionAgentPrefs
)
593 CFRetain(mSessionAgentPrefs
);
594 return mSessionAgentPrefs
;
601 #if defined(DEBUGDUMP)
603 void Session::dumpNode()
605 PerSession::dumpNode();
606 Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
607 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);