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 // A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
29 // hierarchical and inherited, so they work well for characterization of processes
30 // that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
32 // Sessions are multi-threaded objects.
35 #include <signal.h> // SIGTERM
36 #include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
38 #include "connection.h"
41 #include <security_utilities/logging.h>
44 // The static session map
46 PortMap
<Session
> Session::mSessions
;
48 std::string
Session::kUsername
= "username";
49 std::string
Session::kRealname
= "realname";
52 // Create a Session object from initial parameters (create)
54 Session::Session(Bootstrap bootstrap
, Port servicePort
, SessionAttributeBits attrs
)
55 : mBootstrap(bootstrap
), mServicePort(servicePort
),
56 mAttributes(attrs
), mSecurityAgent(NULL
), mAuthHost(NULL
)
58 secdebug("SSsession", "%p CREATED: handle=%#x bootstrap=%d service=%d attrs=%#x",
59 this, handle(), mBootstrap
.port(), mServicePort
.port(), uint32_t(mAttributes
));
60 SECURITYD_SESSION_CREATE(this, attrs
, servicePort
);
61 Syslog::notice("Session 0x%lx created", this->handle());
70 secdebug("SSsession", "%p DESTROYED: handle=%#x bootstrap=%d",
71 this, handle(), mBootstrap
.port());
72 Syslog::notice("Session 0x%lx destroyed", this->handle());
77 // Locate a session object by service port or (Session API) identifier
79 Session
&Session::find(Port servicePort
)
81 StLock
<Mutex
> _(mSessions
);
82 PortMap
<Session
>::const_iterator it
= mSessions
.find(servicePort
);
83 assert(it
!= mSessions
.end());
87 Session
&Session::find(SecuritySessionId id
)
90 case callerSecuritySession
:
91 return Server::session();
94 return U32HandleObject::find
<Session
>(id
, CSSMERR_CSSM_INVALID_ADDIN_HANDLE
);
95 } catch (const CommonError
&err
) {
96 Syslog::warning("Session::find(%#x) failed rcode=%d", id
, err
.osStatus());
97 for (PortMap
<Session
>::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); ++it
)
98 Syslog::notice(" Valid sessions include %#x attrs=%#x",
99 it
->second
->handle(), it
->second
->attributes());
107 // Act on a death notification for a session's (sub)bootstrap port.
108 // We may not destroy the Session outright here (due to processes that use it),
109 // but we do clear out its accumulated wealth.
111 void Session::destroy(Port servPort
)
113 // remove session from session map
114 StLock
<Mutex
> _(mSessions
);
115 PortMap
<Session
>::iterator it
= mSessions
.find(servPort
);
116 assert(it
!= mSessions
.end());
117 RefPointer
<Session
> session
= it
->second
;
118 SECURITYD_SESSION_DESTROY(session
);
119 Syslog::notice("Session 0x%lx dead", session
->handle());
126 StLock
<Mutex
> _(*this); // do we need to take this so early?
128 invalidateSessionAuthHosts();
130 // invalidate shared credentials
132 StLock
<Mutex
> _(mCredsLock
);
134 IFDEBUG(if (!mSessionCreds
.empty())
135 secdebug("SSauth", "session %p clearing %d shared credentials",
136 this, int(mSessionCreds
.size())));
137 for (CredentialSet::iterator it
= mSessionCreds
.begin(); it
!= mSessionCreds
.end(); it
++)
141 // base kill processing
145 void Session::invalidateSessionAuthHosts()
147 StLock
<Mutex
> _(mAuthHostLock
);
149 // if you got here, we don't care about pending operations: the auth hosts die
150 Syslog::warning("Killing auth hosts");
151 if (mSecurityAgent
) mSecurityAgent
->UnixPlusPlus::Child::kill(SIGTERM
);
152 if (mAuthHost
) mAuthHost
->UnixPlusPlus::Child::kill(SIGTERM
);
153 mSecurityAgent
= NULL
;
157 void Session::invalidateAuthHosts()
159 StLock
<Mutex
> _(mSessions
);
160 for (PortMap
<Session
>::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
161 it
->second
->invalidateSessionAuthHosts();
165 // On system sleep, call sleepProcessing on all DbCommons of all Sessions
167 void Session::processSystemSleep()
169 StLock
<Mutex
> _(mSessions
);
170 for (PortMap
<Session
>::const_iterator it
= mSessions
.begin(); it
!= mSessions
.end(); it
++)
171 it
->second
->allReferences(&DbCommon::sleepProcessing
);
176 // On "lockAll", call sleepProcessing on all DbCommons of this session (only)
178 void Session::processLockAll()
180 allReferences(&DbCommon::lockProcessing
);
184 // The root session inherits the startup bootstrap and service port
186 RootSession::RootSession(Server
&server
, SessionAttributeBits attrs
)
187 : Session(Bootstrap(), server
.primaryServicePort(),
188 sessionIsRoot
| sessionWasInitialized
| attrs
)
190 parent(server
); // the Server is our parent
193 // self-install (no thread safety issues here)
194 mSessions
[mServicePort
] = this;
198 // Dynamic sessions use the given bootstrap and re-register in it
200 DynamicSession::DynamicSession(TaskPort taskPort
)
201 : ReceivePort(Server::active().bootstrapName(), taskPort
.bootstrap(), false),
202 Session(taskPort
.bootstrap(), *this),
203 mOriginatorTask(taskPort
), mHaveOriginatorUid(false)
205 // link to Server as the global nexus in the object mesh
206 parent(Server::active());
208 // tell the server to listen to our port
209 Server::active().add(*this);
211 // register for port notifications
212 Server::active().notifyIfDead(bootstrapPort()); //@@@??? still needed?
213 Server::active().notifyIfUnused(*this);
216 StLock
<Mutex
> _(mSessions
);
217 assert(!mSessions
[*this]); // can't be registered already (we just made it)
218 mSessions
[*this] = this;
220 secdebug("SSsession", "%p dynamic session originator=%d (pid=%d)",
221 this, mOriginatorTask
.port(), taskPort
.pid());
224 DynamicSession::~DynamicSession()
226 // remove our service port from the server
227 Server::active().remove(*this);
231 void DynamicSession::kill()
233 StLock
<Mutex
> _(*this);
234 mBootstrap
.destroy(); // release our bootstrap port
235 Session::kill(); // continue with parent kill
240 // Set up a DynamicSession.
241 // This call must be made from a process within the session, and it must be the first
242 // such process to make the call.
244 void DynamicSession::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
246 StLock
<Mutex
> _(*this);
247 SECURITYD_SESSION_SETATTR(this, attrs
);
248 Syslog::notice("Session 0x%lx attributes 0x%x", this->handle(), attrs
);
249 secdebug("SSsession", "%p setup flags=%#x attrs=%#x", this, uint32_t(flags
), uint32_t(attrs
));
250 if (attrs
& ~settableAttributes
)
251 MacOSError::throwMe(errSessionInvalidAttributes
);
253 if (attribute(sessionWasInitialized
))
254 MacOSError::throwMe(errSessionAuthorizationDenied
);
255 setAttributes(attrs
| sessionWasInitialized
);
260 // Check whether the calling process is the session originator.
261 // If it's not, throw.
263 void DynamicSession::checkOriginator()
265 if (mOriginatorTask
!= Server::process().taskPort())
266 MacOSError::throwMe(errSessionAuthorizationDenied
);
271 // The "originator uid" is a uid value that can be provided by the session originator
272 // and retrieved by anyone. Securityd places no semantic meaning on this value.
274 uid_t
DynamicSession::originatorUid() const
276 if (mHaveOriginatorUid
)
277 return mOriginatorUid
;
279 MacOSError::throwMe(errSessionValueNotSet
);
283 void DynamicSession::originatorUid(uid_t uid
)
286 if (mHaveOriginatorUid
) // must not re-set this
287 MacOSError::throwMe(errSessionAuthorizationDenied
);
288 mHaveOriginatorUid
= true;
289 mOriginatorUid
= uid
;
291 Server::active().longTermActivity();
292 struct passwd
*pw
= getpwuid(uid
);
296 mOriginatorCredential
= Credential(uid
, pw
->pw_name
? pw
->pw_name
: "", pw
->pw_gecos
? pw
->pw_gecos
: "", "", true/*shared*/);
300 secdebug("SSsession", "%p session uid set to %d", this, uid
);
304 // Authorization operations
306 OSStatus
Session::authCreate(const AuthItemSet
&rights
,
307 const AuthItemSet
&environment
,
308 AuthorizationFlags flags
,
309 AuthorizationBlob
&newHandle
,
310 const audit_token_t
&auditToken
)
312 // invoke the authorization computation engine
313 CredentialSet resultCreds
;
315 // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
316 auto_ptr
<AuthorizationToken
> auth(new AuthorizationToken(*this, resultCreds
, auditToken
, (flags
&kAuthorizationFlagLeastPrivileged
)));
318 SECURITYD_AUTH_CREATE(this, auth
.get());
320 // Make a copy of the mSessionCreds
321 CredentialSet sessionCreds
;
323 StLock
<Mutex
> _(mCredsLock
);
324 sessionCreds
= mSessionCreds
;
327 AuthItemSet outRights
;
328 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
329 &sessionCreds
, &resultCreds
, outRights
, *auth
);
330 newHandle
= auth
->handle();
332 // merge resulting creds into shared pool
333 if ((flags
& kAuthorizationFlagExtendRights
) &&
334 !(flags
& kAuthorizationFlagDestroyRights
))
336 StLock
<Mutex
> _(mCredsLock
);
337 mergeCredentials(resultCreds
);
338 auth
->mergeCredentials(resultCreds
);
341 // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to
342 // not be destroyed anymore since it's destructor asserts it has no processes
343 Server::process().addAuthorization(auth
.get());
348 void Session::authFree(const AuthorizationBlob
&authBlob
, AuthorizationFlags flags
)
350 AuthorizationToken::Deleter
deleter(authBlob
);
351 AuthorizationToken
&auth
= deleter
;
352 Process
&process
= Server::process();
353 process
.checkAuthorization(&auth
);
355 if (flags
& kAuthorizationFlagDestroyRights
) {
356 // explicitly invalidate all shared credentials and remove them from the session
357 for (CredentialSet::const_iterator it
= auth
.begin(); it
!= auth
.end(); it
++)
358 if ((*it
)->isShared())
362 // now get rid of the authorization itself
363 if (process
.removeAuthorization(&auth
))
367 OSStatus
Session::authGetRights(const AuthorizationBlob
&authBlob
,
368 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
369 AuthorizationFlags flags
,
370 AuthItemSet
&grantedRights
)
372 AuthorizationToken
&auth
= authorization(authBlob
);
373 return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
);
376 OSStatus
Session::authGetRights(AuthorizationToken
&auth
,
377 const AuthItemSet
&rights
, const AuthItemSet
&environment
,
378 AuthorizationFlags flags
,
379 AuthItemSet
&grantedRights
)
381 CredentialSet resultCreds
;
382 CredentialSet effective
;
384 StLock
<Mutex
> _(mCredsLock
);
385 effective
= auth
.effectiveCreds();
387 OSStatus result
= Server::authority().authorize(rights
, environment
, flags
,
388 &effective
, &resultCreds
, grantedRights
, auth
);
390 // merge resulting creds into shared pool
391 if ((flags
& kAuthorizationFlagExtendRights
) && !(flags
& kAuthorizationFlagDestroyRights
))
393 StLock
<Mutex
> _(mCredsLock
);
394 mergeCredentials(resultCreds
);
395 auth
.mergeCredentials(resultCreds
);
398 secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
399 &auth
, int(rights
.size()), int(grantedRights
.size()));
403 OSStatus
Session::authGetInfo(const AuthorizationBlob
&authBlob
,
405 AuthItemSet
&contextInfo
)
407 AuthorizationToken
&auth
= authorization(authBlob
);
408 secdebug("SSauth", "Authorization %p get-info", &auth
);
409 contextInfo
= auth
.infoSet(tag
);
413 OSStatus
Session::authExternalize(const AuthorizationBlob
&authBlob
,
414 AuthorizationExternalForm
&extForm
)
416 const AuthorizationToken
&auth
= authorization(authBlob
);
417 StLock
<Mutex
> _(*this);
418 if (auth
.mayExternalize(Server::process())) {
419 memset(&extForm
, 0, sizeof(extForm
));
420 AuthorizationExternalBlob
&extBlob
=
421 reinterpret_cast<AuthorizationExternalBlob
&>(extForm
);
422 extBlob
.blob
= auth
.handle();
423 extBlob
.session
= bootstrapPort();
424 secdebug("SSauth", "Authorization %p externalized", &auth
);
427 return errAuthorizationExternalizeNotAllowed
;
430 OSStatus
Session::authInternalize(const AuthorizationExternalForm
&extForm
,
431 AuthorizationBlob
&authBlob
)
433 // interpret the external form
434 const AuthorizationExternalBlob
&extBlob
=
435 reinterpret_cast<const AuthorizationExternalBlob
&>(extForm
);
437 // locate source authorization
438 AuthorizationToken
&sourceAuth
= AuthorizationToken::find(extBlob
.blob
);
440 // check for permission and do it
441 if (sourceAuth
.mayInternalize(Server::process(), true)) {
442 StLock
<Mutex
> _(*this);
443 authBlob
= extBlob
.blob
;
444 Server::process().addAuthorization(&sourceAuth
);
445 secdebug("SSauth", "Authorization %p internalized", &sourceAuth
);
448 return errAuthorizationInternalizeNotAllowed
;
453 // The default session setup operation always fails.
454 // Subclasses can override this to support session setup calls.
456 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
)
458 MacOSError::throwMe(errSessionAuthorizationDenied
);
463 // Authorization database I/O
465 OSStatus
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef
*rightDict
)
467 string
rightName(inRightName
);
468 return Server::authority().getRule(rightName
, rightDict
);
472 OSStatus
Session::authorizationdbSet(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
)
474 CredentialSet resultCreds
;
475 AuthorizationToken
&auth
= authorization(authBlob
);
476 CredentialSet effective
;
479 StLock
<Mutex
> _(mCredsLock
);
480 effective
= auth
.effectiveCreds();
483 OSStatus result
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
);
486 StLock
<Mutex
> _(mCredsLock
);
487 mergeCredentials(resultCreds
);
488 auth
.mergeCredentials(resultCreds
);
491 secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
492 &authorization(authBlob
), inRightName
, int32_t(result
));
497 OSStatus
Session::authorizationdbRemove(const AuthorizationBlob
&authBlob
, AuthorizationString inRightName
)
499 CredentialSet resultCreds
;
500 AuthorizationToken
&auth
= authorization(authBlob
);
501 CredentialSet effective
;
504 StLock
<Mutex
> _(mCredsLock
);
505 effective
= auth
.effectiveCreds();
508 OSStatus result
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
);
511 StLock
<Mutex
> _(mCredsLock
);
512 mergeCredentials(resultCreds
);
513 auth
.mergeCredentials(resultCreds
);
516 secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
517 &authorization(authBlob
), inRightName
, int32_t(result
));
523 // Merge a set of credentials into the shared-session credential pool
525 // must hold mCredsLock
526 void Session::mergeCredentials(CredentialSet
&creds
)
528 secdebug("SSsession", "%p merge creds @%p", this, &creds
);
529 CredentialSet updatedCredentials
= creds
;
530 for (CredentialSet::const_iterator it
= creds
.begin(); it
!= creds
.end(); it
++)
531 if ((*it
)->isShared() && (*it
)->isValid()) {
532 CredentialSet::iterator old
= mSessionCreds
.find(*it
);
533 if (old
== mSessionCreds
.end()) {
534 mSessionCreds
.insert(*it
);
536 // replace "new" with "old" in input set to retain synchronization
538 updatedCredentials
.erase(*it
);
539 updatedCredentials
.insert(*old
);
542 creds
.swap(updatedCredentials
);
547 // Locate an AuthorizationToken given a blob
549 AuthorizationToken
&Session::authorization(const AuthorizationBlob
&blob
)
551 AuthorizationToken
&auth
= AuthorizationToken::find(blob
);
552 Server::process().checkAuthorization(&auth
);
557 // Run the Authorization engine to check if a given right has been authorized,
558 // independent of an external client request.
560 OSStatus
Session::authCheckRight(string
&rightName
, Connection
&connection
, bool allowUI
)
562 // dummy up the arguments for authCreate()
563 AuthorizationItem rightItem
= { rightName
.c_str(), 0, NULL
, 0 };
564 AuthorizationItemSet rightItemSet
= { 1, &rightItem
};
565 AuthItemSet
rightAuthItemSet(&rightItemSet
);
566 AuthItemSet
envAuthItemSet(kAuthorizationEmptyEnvironment
);
567 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagExtendRights
;
569 flags
|= kAuthorizationFlagInteractionAllowed
;
570 AuthorizationBlob dummyHandle
;
571 const audit_token_t
*at
= connection
.auditToken();
573 return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
);
576 // for places within securityd that don't want to #include
577 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions
578 bool Session::isRightAuthorized(string
&rightName
, Connection
&connection
, bool allowUI
)
580 bool isAuthorized
= false;
583 OSStatus status
= authCheckRight(rightName
, connection
, allowUI
);
584 if (errAuthorizationSuccess
== status
)
592 RefPointer
<AuthHostInstance
>
593 Session::authhost(const AuthHostType hostType
, const bool restart
)
595 StLock
<Mutex
> _(mAuthHostLock
);
597 if (hostType
== privilegedAuthHost
)
599 if (restart
|| !mAuthHost
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
))
602 PerSession::kill(*mAuthHost
);
603 mAuthHost
= new AuthHostInstance(*this, hostType
);
607 else /* if (hostType == securityAgent) */
609 if (restart
|| !mSecurityAgent
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
))
612 PerSession::kill(*mSecurityAgent
);
613 mSecurityAgent
= new AuthHostInstance(*this, hostType
);
615 return mSecurityAgent
;
619 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
)
622 if (Server::process().uid() != 0)
623 MacOSError::throwMe(errSessionAuthorizationDenied
);
624 StLock
<Mutex
> _(*this);
625 mSessionAgentPrefs
= userPrefsDict
;
628 CFDataRef
DynamicSession::copyUserPrefs()
630 StLock
<Mutex
> _(*this);
631 if (mSessionAgentPrefs
)
632 CFRetain(mSessionAgentPrefs
);
633 return mSessionAgentPrefs
;
640 #if defined(DEBUGDUMP)
642 void Session::dumpNode()
644 PerSession::dumpNode();
645 Debug::dump(" boot=%d service=%d attrs=%#x authhost=%p securityagent=%p",
646 mBootstrap
.port(), mServicePort
.port(), uint32_t(mAttributes
), mAuthHost
, mSecurityAgent
);