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         mAudit
.get(mAudit
.sessionId()); 
 180 // Manage authorization client processes 
 182 void Session::invalidateSessionAuthHosts() 
 184     StLock
<Mutex
> _(mAuthHostLock
); 
 186     // if you got here, we don't care about pending operations: the auth hosts die 
 187     Syslog::warning("Killing auth hosts"); 
 188     if (mSecurityAgent
) mSecurityAgent
->UnixPlusPlus::Child::kill(SIGTERM
); 
 189     if (mAuthHost
) mAuthHost
->UnixPlusPlus::Child::kill(SIGTERM
); 
 190     mSecurityAgent 
= NULL
; 
 194 void Session::invalidateAuthHosts() 
 196         StLock
<Mutex
> _(mSessionLock
); 
 197         for (SessionMap::const_iterator it 
= mSessions
.begin(); it 
!= mSessions
.end(); it
++) 
 198         it
->second
->invalidateSessionAuthHosts(); 
 202 // On system sleep, call sleepProcessing on all DbCommons of all Sessions 
 204 void Session::processSystemSleep() 
 206         StLock
<Mutex
> _(mSessionLock
); 
 207         for (SessionMap::const_iterator it 
= mSessions
.begin(); it 
!= mSessions
.end(); it
++) 
 208                 it
->second
->allReferences(&DbCommon::sleepProcessing
); 
 213 // On "lockAll", call sleepProcessing on all DbCommons of this session (only) 
 215 void Session::processLockAll() 
 217         allReferences(&DbCommon::lockProcessing
); 
 222 // The root session corresponds to the audit session that security is running in. 
 223 // This is usually the initial system session; but in debug scenarios it may be 
 224 // an "ordinary" graphic login session. In such a debug case, we may add attribute 
 225 // flags to the session to make our (debugging) life easier. 
 227 RootSession::RootSession(uint64_t attributes
, Server 
&server
) 
 228         : Session(AuditInfo::current(), server
) 
 231         mAudit
.ai_flags 
|= attributes
;          // merge imposed attributes 
 236 // Dynamic sessions use the audit session context of the first-contact client caller. 
 238 DynamicSession::DynamicSession(const AuditInfo 
&audit
) 
 239         : Session(audit
, Server::active()) 
 245 // Authorization operations 
 247 OSStatus 
Session::authCreate(const AuthItemSet 
&rights
, 
 248         const AuthItemSet 
&environment
, 
 249         AuthorizationFlags flags
, 
 250         AuthorizationBlob 
&newHandle
, 
 251         const audit_token_t 
&auditToken
) 
 253         // invoke the authorization computation engine 
 254         CredentialSet resultCreds
; 
 256         // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed) 
 257         auto_ptr
<AuthorizationToken
> auth(new AuthorizationToken(*this, resultCreds
, auditToken
, (flags
&kAuthorizationFlagLeastPrivileged
))); 
 259         SECURITYD_AUTH_CREATE(this, auth
.get()); 
 261     // Make a copy of the mSessionCreds 
 262     CredentialSet sessionCreds
; 
 264         StLock
<Mutex
> _(mCredsLock
); 
 265         sessionCreds 
= mSessionCreds
; 
 268         AuthItemSet outRights
; 
 269         OSStatus result 
= Server::authority().authorize(rights
, environment
, flags
, 
 270         &sessionCreds
, &resultCreds
, outRights
, *auth
); 
 271         newHandle 
= auth
->handle(); 
 273     // merge resulting creds into shared pool 
 274     if ((flags 
& kAuthorizationFlagExtendRights
) &&  
 275         !(flags 
& kAuthorizationFlagDestroyRights
)) 
 277         StLock
<Mutex
> _(mCredsLock
); 
 278         mergeCredentials(resultCreds
); 
 279         auth
->mergeCredentials(resultCreds
); 
 282         // Make sure that this isn't done until the auth(AuthorizationToken) is guaranteed to  
 283         // not be destroyed anymore since it's destructor asserts it has no processes 
 284         Server::process().addAuthorization(auth
.get()); 
 289 void Session::authFree(const AuthorizationBlob 
&authBlob
, AuthorizationFlags flags
) 
 291     AuthorizationToken::Deleter 
deleter(authBlob
); 
 292     AuthorizationToken 
&auth 
= deleter
; 
 293         Process 
&process 
= Server::process(); 
 294         process
.checkAuthorization(&auth
); 
 296         if (flags 
& kAuthorizationFlagDestroyRights
) { 
 297                 // explicitly invalidate all shared credentials and remove them from the session 
 298                 for (CredentialSet::const_iterator it 
= auth
.begin(); it 
!= auth
.end(); it
++) 
 299                         if ((*it
)->isShared()) 
 303         // now get rid of the authorization itself 
 304         if (process
.removeAuthorization(&auth
)) 
 308 OSStatus 
Session::authGetRights(const AuthorizationBlob 
&authBlob
, 
 309         const AuthItemSet 
&rights
, const AuthItemSet 
&environment
, 
 310         AuthorizationFlags flags
, 
 311         AuthItemSet 
&grantedRights
) 
 313         AuthorizationToken 
&auth 
= authorization(authBlob
); 
 314         return auth
.session().authGetRights(auth
, rights
, environment
, flags
, grantedRights
); 
 317 OSStatus 
Session::authGetRights(AuthorizationToken 
&auth
, 
 318         const AuthItemSet 
&rights
, const AuthItemSet 
&environment
, 
 319         AuthorizationFlags flags
, 
 320         AuthItemSet 
&grantedRights
) 
 322     CredentialSet resultCreds
; 
 323     CredentialSet effective
; 
 325         StLock
<Mutex
> _(mCredsLock
); 
 326         effective        
= auth
.effectiveCreds(); 
 328         OSStatus result 
= Server::authority().authorize(rights
, environment
, flags
,  
 329         &effective
, &resultCreds
, grantedRights
, auth
); 
 331         // merge resulting creds into shared pool 
 332         if ((flags 
& kAuthorizationFlagExtendRights
) && !(flags 
& kAuthorizationFlagDestroyRights
)) 
 334         StLock
<Mutex
> _(mCredsLock
); 
 335         mergeCredentials(resultCreds
); 
 336         auth
.mergeCredentials(resultCreds
); 
 339         secdebug("SSauth", "Authorization %p copyRights asked for %d got %d", 
 340                 &auth
, int(rights
.size()), int(grantedRights
.size())); 
 344 OSStatus 
Session::authGetInfo(const AuthorizationBlob 
&authBlob
, 
 346         AuthItemSet 
&contextInfo
) 
 348         AuthorizationToken 
&auth 
= authorization(authBlob
); 
 349         secdebug("SSauth", "Authorization %p get-info", &auth
); 
 350         contextInfo 
= auth
.infoSet(tag
); 
 354 OSStatus 
Session::authExternalize(const AuthorizationBlob 
&authBlob
,  
 355         AuthorizationExternalForm 
&extForm
) 
 357         const AuthorizationToken 
&auth 
= authorization(authBlob
); 
 358         StLock
<Mutex
> _(*this); 
 359         if (auth
.mayExternalize(Server::process())) { 
 360                 memset(&extForm
, 0, sizeof(extForm
)); 
 361         AuthorizationExternalBlob 
&extBlob 
= 
 362             reinterpret_cast<AuthorizationExternalBlob 
&>(extForm
); 
 363         extBlob
.blob 
= auth
.handle(); 
 364         extBlob
.session 
= this->sessionId(); 
 365                 secdebug("SSauth", "Authorization %p externalized", &auth
); 
 368                 return errAuthorizationExternalizeNotAllowed
; 
 371 OSStatus 
Session::authInternalize(const AuthorizationExternalForm 
&extForm
,  
 372         AuthorizationBlob 
&authBlob
) 
 374         // interpret the external form 
 375     const AuthorizationExternalBlob 
&extBlob 
=  
 376         reinterpret_cast<const AuthorizationExternalBlob 
&>(extForm
); 
 378     // locate source authorization 
 379     AuthorizationToken 
&sourceAuth 
= AuthorizationToken::find(extBlob
.blob
); 
 381         // check for permission and do it 
 382         if (sourceAuth
.mayInternalize(Server::process(), true)) { 
 383                 StLock
<Mutex
> _(*this); 
 384                 authBlob 
= extBlob
.blob
; 
 385         Server::process().addAuthorization(&sourceAuth
); 
 386         secdebug("SSauth", "Authorization %p internalized", &sourceAuth
); 
 389                 return errAuthorizationInternalizeNotAllowed
; 
 394 // Accessor method for setting audit session flags. 
 396 void Session::setAttributes(SessionAttributeBits bits
) 
 398         StLock
<Mutex
> _(*this); 
 400         assert((bits 
& ~settableAttributes
) == 0); 
 401         mAudit
.ai_flags 
= bits
; 
 406 // The default session setup operation always fails. 
 407 // Subclasses can override this to support session setup calls. 
 409 void Session::setupAttributes(SessionCreationFlags flags
, SessionAttributeBits attrs
) 
 411         MacOSError::throwMe(errSessionAuthorizationDenied
); 
 416 // Authorization database I/O 
 418 OSStatus 
Session::authorizationdbGet(AuthorizationString inRightName
, CFDictionaryRef 
*rightDict
) 
 420         string 
rightName(inRightName
); 
 421         return Server::authority().getRule(rightName
, rightDict
); 
 425 OSStatus 
Session::authorizationdbSet(const AuthorizationBlob 
&authBlob
, AuthorizationString inRightName
, CFDictionaryRef rightDict
) 
 427         CredentialSet resultCreds
; 
 428     AuthorizationToken 
&auth 
= authorization(authBlob
); 
 429     CredentialSet effective
; 
 432         StLock
<Mutex
> _(mCredsLock
); 
 433         effective        
= auth
.effectiveCreds(); 
 436         OSStatus result 
= Server::authority().setRule(inRightName
, rightDict
, &effective
, &resultCreds
, auth
); 
 439         StLock
<Mutex
> _(mCredsLock
); 
 440         mergeCredentials(resultCreds
); 
 441         auth
.mergeCredentials(resultCreds
); 
 444         secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)", 
 445                 &authorization(authBlob
), inRightName
, int32_t(result
)); 
 450 OSStatus 
Session::authorizationdbRemove(const AuthorizationBlob 
&authBlob
, AuthorizationString inRightName
) 
 452         CredentialSet resultCreds
; 
 453     AuthorizationToken 
&auth 
= authorization(authBlob
); 
 454     CredentialSet effective
; 
 457         StLock
<Mutex
> _(mCredsLock
); 
 458         effective        
= auth
.effectiveCreds(); 
 461         OSStatus result 
= Server::authority().removeRule(inRightName
, &effective
, &resultCreds
, auth
); 
 464         StLock
<Mutex
> _(mCredsLock
); 
 465         mergeCredentials(resultCreds
); 
 466         auth
.mergeCredentials(resultCreds
); 
 469         secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)", 
 470                 &authorization(authBlob
), inRightName
, int32_t(result
)); 
 476 // Merge a set of credentials into the shared-session credential pool 
 478 // must hold mCredsLock 
 479 void Session::mergeCredentials(CredentialSet 
&creds
) 
 481     secdebug("SSsession", "%p merge creds @%p", this, &creds
); 
 482         CredentialSet updatedCredentials 
= creds
; 
 483         for (CredentialSet::const_iterator it 
= creds
.begin(); it 
!= creds
.end(); it
++) 
 484                 if ((*it
)->isShared() && (*it
)->isValid()) { 
 485                         CredentialSet::iterator old 
= mSessionCreds
.find(*it
); 
 486                         if (old 
== mSessionCreds
.end()) { 
 487                                 mSessionCreds
.insert(*it
); 
 489                 // replace "new" with "old" in input set to retain synchronization 
 491                 updatedCredentials
.erase(*it
); 
 492                 updatedCredentials
.insert(*old
); 
 495         creds
.swap(updatedCredentials
); 
 500 // Locate an AuthorizationToken given a blob 
 502 AuthorizationToken 
&Session::authorization(const AuthorizationBlob 
&blob
) 
 504     AuthorizationToken 
&auth 
= AuthorizationToken::find(blob
); 
 505         Server::process().checkAuthorization(&auth
); 
 510 // Run the Authorization engine to check if a given right has been authorized, 
 511 // independent of an external client request.   
 513 OSStatus 
Session::authCheckRight(string 
&rightName
, Connection 
&connection
, bool allowUI
) 
 515     // dummy up the arguments for authCreate() 
 516     AuthorizationItem rightItem 
= { rightName
.c_str(), 0, NULL
, 0 }; 
 517     AuthorizationItemSet rightItemSet 
= { 1, &rightItem 
}; 
 518     AuthItemSet 
rightAuthItemSet(&rightItemSet
); 
 519     AuthItemSet 
envAuthItemSet(kAuthorizationEmptyEnvironment
); 
 520     AuthorizationFlags flags 
= kAuthorizationFlagDefaults 
| kAuthorizationFlagExtendRights
; 
 522         flags 
|= kAuthorizationFlagInteractionAllowed
; 
 523     AuthorizationBlob dummyHandle
; 
 524     const audit_token_t 
*at 
= connection
.auditToken(); 
 526     return authCreate(rightAuthItemSet
, envAuthItemSet
, flags
, dummyHandle
, *at
); 
 529 // for places within securityd that don't want to #include 
 530 // <libsecurity_authorization/Authorization.h> or to fuss about exceptions 
 531 bool Session::isRightAuthorized(string 
&rightName
, Connection 
&connection
, bool allowUI
) 
 533     bool isAuthorized 
= false; 
 536         OSStatus status 
= authCheckRight(rightName
, connection
, allowUI
); 
 537         if (errAuthorizationSuccess 
== status
) 
 545 RefPointer
<AuthHostInstance
>  
 546 Session::authhost(const AuthHostType hostType
, const bool restart
) 
 548         StLock
<Mutex
> _(mAuthHostLock
); 
 550         if (hostType 
== privilegedAuthHost
) 
 552                 if (restart 
|| !mAuthHost 
|| (mAuthHost
->state() != Security::UnixPlusPlus::Child::alive
)) 
 555                                 PerSession::kill(*mAuthHost
); 
 556                         mAuthHost 
= new AuthHostInstance(*this, hostType
);       
 560         else /* if (hostType == securityAgent) */ 
 562                 if (restart 
|| !mSecurityAgent 
|| (mSecurityAgent
->state() != Security::UnixPlusPlus::Child::alive
)) 
 565                                 PerSession::kill(*mSecurityAgent
); 
 566                         mSecurityAgent 
= new AuthHostInstance(*this, hostType
); 
 568                 return mSecurityAgent
; 
 572 void DynamicSession::setUserPrefs(CFDataRef userPrefsDict
) 
 574         if (Server::process().uid() != 0) 
 575                 MacOSError::throwMe(errSessionAuthorizationDenied
); 
 576         StLock
<Mutex
> _(*this); 
 577         mSessionAgentPrefs 
= userPrefsDict
; 
 580 CFDataRef 
DynamicSession::copyUserPrefs() 
 582         StLock
<Mutex
> _(*this); 
 583         if (mSessionAgentPrefs
) 
 584                 CFRetain(mSessionAgentPrefs
); 
 585         return mSessionAgentPrefs
; 
 592 #if defined(DEBUGDUMP) 
 594 void Session::dumpNode() 
 596         PerSession::dumpNode(); 
 597         Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p", 
 598                 this->sessionId(), uint32_t(this->attributes()), mAuthHost
, mSecurityAgent
);