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
);