]> git.saurik.com Git - apple/securityd.git/blobdiff - src/session.cpp
securityd-55199.3.tar.gz
[apple/securityd.git] / src / session.cpp
index 98c2762c3ec14c40a93967ba0318830be35a8091..b79f683ef247aaa461eb6ea1404197e95c15ecf1 100644 (file)
@@ -1,10 +1,8 @@
 /*
- * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2004,2008-2009 Apple Inc. All Rights Reserved.
  * 
  * @APPLE_LICENSE_HEADER_START@
  * 
- * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
- * 
  * This file contains Original Code and/or Modifications of Original Code
  * as defined in and that are subject to the Apple Public Source License
  * Version 2.0 (the 'License'). You may not use this file except in
 //
 // session - authentication session domains
 //
-// A Session is defined by a mach_init bootstrap dictionary. These dictionaries are
-// hierarchical and inherited, so they work well for characterization of processes
-// that "belong" together. (Of course, if your mach_init is broken, you're in bad shape.)
+// Security sessions are now by definition congruent to audit subsystem sessions.
+// We represent these sessions within securityd as subclasses of class Session,
+// but we reach for the kernel's data whenever we're not sure if our data is
+// up to date.
 //
-// Sessions are multi-threaded objects.
+// Modifications to session state are made from client space using system calls.
+// We discover them when we see changes in audit records as they come in with
+// new requests. We cannot use system notifications for such changes because
+// securityd is fully symmetrically multi-threaded, and thus may process new
+// requests from clients before it gets those notifications.
 //
+#include <pwd.h>
+#include <signal.h>                     // SIGTERM
+#include <Security/AuthorizationPriv.h> // kAuthorizationFlagLeastPrivileged
 #include "session.h"
 #include "connection.h"
 #include "database.h"
 #include "server.h"
+#include <security_utilities/logging.h>
+#include <agentquery.h>
+
+using namespace CommonCriteria;
 
 
 //
 // The static session map
 //
-PortMap<Session> Session::mSessions;
+Session::SessionMap Session::mSessions;
+Mutex Session::mSessionLock(Mutex::recursive);
+
+
+const char Session::kUsername[] = "username";
+const char Session::kRealname[] = "realname";
 
 
 //
 // Create a Session object from initial parameters (create)
 //
-Session::Session(Bootstrap bootstrap, Port servicePort, SessionAttributeBits attrs) 
-    : mBootstrap(bootstrap), mServicePort(servicePort),
-         mAttributes(attrs), mDying(false)
+Session::Session(const AuditInfo &audit, Server &server)
+       : mAudit(audit), mSecurityAgent(NULL), mAuthHost(NULL), mKeybagState(0)
+{
+       // link to Server as the global nexus in the object mesh
+       parent(server);
+       
+       // self-register
+       StLock<Mutex> _(mSessionLock);
+       assert(!mSessions[audit.sessionId()]);
+       mSessions[audit.sessionId()] = this;
+       
+       // log it
+       SECURITYD_SESSION_CREATE(this, this->sessionId(), &mAudit, sizeof(mAudit));
+       Syslog::notice("Session %d created", this->sessionId());
+}
+
+
+//
+// Destroy a Session
+//
+Session::~Session()
 {
-    secdebug("SSsession", "%p CREATED: handle=0x%lx bootstrap=%d service=%d attrs=0x%lx",
-        this, handle(), mBootstrap.port(), mServicePort.port(), mAttributes);
+       SECURITYD_SESSION_DESTROY(this, this->sessionId());
+       Syslog::notice("Session %d destroyed", this->sessionId());
 }
 
 
-void Session::release()
+Server &Session::server() const
 {
-       // nothing by default
+       return parent<Server>();
 }
 
 
 //
-// The root session inherits the startup bootstrap and service port
+// Locate a session object by session identifier
 //
-RootSession::RootSession(Port servicePort, SessionAttributeBits attrs)
-    : Session(Bootstrap(), servicePort, sessionIsRoot | sessionWasInitialized | attrs)
+Session &Session::find(pid_t id, bool create)
 {
-       ref();  // eternalize
-
-    // self-install (no thread safety issues here)
-       mSessions[mServicePort] = this;
+       if (id == callerSecuritySession)
+               return Server::session();
+       StLock<Mutex> _(mSessionLock);
+       SessionMap::iterator it = mSessions.find(id);
+       if (it != mSessions.end())
+               return *it->second;
+
+       // new session
+       if (!create)
+               CssmError::throwMe(errSessionInvalidId);
+       AuditInfo info;
+       info.get(id);
+       assert(info.sessionId() == id);
+       RefPointer<Session> session = new DynamicSession(info);
+       mSessions.insert(make_pair(id, session));
+       return *session;
 }
 
 
 //
-// Dynamic sessions use the given bootstrap and re-register in it
+// Act on a death notification for a session's underlying audit session object.
+// We may not destroy the Session outright here (due to processes that use it),
+// but we do clear out its accumulated wealth.
+// Note that we may get spurious death notifications for audit sessions that we
+// never learned about. Ignore those.
 //
-DynamicSession::DynamicSession(const Bootstrap &bootstrap)
-       : ReceivePort(Server::active().bootstrapName(), bootstrap),
-         Session(bootstrap, *this)
+void Session::destroy(SessionId id)
 {
-       // tell the server to listen to our port
-       Server::active().add(*this);
+    // remove session from session map
+    bool unlocked = false;
+    RefPointer<Session> session = NULL;
+    {
+        StLock<Mutex> _(mSessionLock);
+        SessionMap::iterator it = mSessions.find(id);
+        if (it != mSessions.end()) {
+            session = it->second;
+            assert(session->sessionId() == id);
+            mSessions.erase(it);
+
+            for (SessionMap::iterator kb_it = mSessions.begin(); kb_it != mSessions.end(); kb_it++) {
+                RefPointer<Session> kb_session = kb_it->second;
+                if (kb_session->originatorUid() == session->originatorUid()) {
+                    if (kb_session->keybagGetState(session_keybag_unlocked)) unlocked = true;
+                }
+            }
+        }
+    }
+
+    if (session.get()) {
+        if (!unlocked) {
+            service_context_t context = session->get_current_service_context();
+            service_client_kb_lock(&context);
+        }
+        session->kill();
+    }
+}
+
+
+void Session::kill()
+{
+    StLock<Mutex> _(*this);     // do we need to take this so early?
+       SECURITYD_SESSION_KILL(this, this->sessionId());
+    invalidateSessionAuthHosts();
        
-       // register for port notifications
-    Server::active().notifyIfDead(bootstrapPort());    //@@@??? still needed?
-       Server::active().notifyIfUnused(*this);
+    // invalidate shared credentials
+    {
+        StLock<Mutex> _(mCredsLock);
+        
+        IFDEBUG(if (!mSessionCreds.empty()) 
+            secdebug("SSauth", "session %p clearing %d shared credentials", 
+                this, int(mSessionCreds.size())));
+        for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
+            (*it)->invalidate();
+    }
+       
+       // base kill processing
+       PerSession::kill();
+}
 
-       // self-register
-       StLock<Mutex> _(mSessions);
-       assert(!mSessions[*this]);  // can't be registered already (we just made it)
-       mSessions[*this] = this;
+
+//
+// Refetch audit session data for the current audit session (to catch outside updates
+// to the audit record). This is the price we're paying for not requiring an IPC to
+// securityd when audit session data changes (this is desirable for delayering the
+// software layer cake).
+// If we ever disallow changes to (parts of the) audit session record in the kernel,
+// we can loosen up on this continual re-fetching.
+//
+void Session::updateAudit() const
+{
+    CommonCriteria::AuditInfo info;
+    try {
+        info.get(mAudit.sessionId());
+    } catch (...) {
+        return;
+    }
+    mAudit = info;
 }
 
-DynamicSession::~DynamicSession()
+void Session::verifyKeyStorePassphrase(int32_t retries)
 {
-       // remove our service port from the server
-       Server::active().remove(*this);
+    QueryKeybagPassphrase keybagQuery(*this, retries);
+    keybagQuery.inferHints(Server::process());
+    if (keybagQuery.query() != SecurityAgent::noReason) {
+        CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
+    }
 }
 
+void Session::changeKeyStorePassphrase()
+{
+    service_context_t context = get_current_service_context();
+    QueryKeybagNewPassphrase keybagQuery(*this);
+    keybagQuery.inferHints(Server::process());
+    CssmAutoData pass(Allocator::standard(Allocator::sensitive));
+    CssmAutoData oldPass(Allocator::standard(Allocator::sensitive));
+    SecurityAgent::Reason queryReason = keybagQuery.query(oldPass, pass);
+    if (queryReason == SecurityAgent::noReason) {
+        service_client_kb_change_secret(&context, oldPass.data(), (int)oldPass.length(), pass.data(), (int)pass.length());
+    } else {
+        CssmError::throwMe(CSSM_ERRCODE_OPERATION_AUTH_DENIED);
+    }
+}
 
-void DynamicSession::release()
+void Session::resetKeyStorePassphrase(const CssmData &passphrase)
 {
-       mBootstrap.destroy();
+    service_context_t context = get_current_service_context();
+    service_client_kb_reset(&context, passphrase.data(), (int)passphrase.length());
 }
 
+service_context_t Session::get_current_service_context()
+{
+    // if this gets called from a timer there is no connection() object.
+    // need to check for valid connection object and pass the audit token along
+    service_context_t context = { sessionId(), originatorUid(), {} }; //*Server::connection().auditToken()
+    return context;
+}
 
-//
-// Destroy a Session
-//
-Session::~Session()
+void Session::keybagClearState(int state)
 {
-    secdebug("SSsession", "%p DESTROYED: handle=0x%lx bootstrap=%d",
-        this, handle(), mBootstrap.port());
+    mKeybagState &= ~state;
+}
+
+void Session::keybagSetState(int state)
+{
+    mKeybagState |= state;
+}
+
+bool Session::keybagGetState(int state)
+{
+    return mKeybagState & state;
 }
 
 
 //
-// Locate a session object by service port or (Session API) identifier
+// Manage authorization client processes
 //
-Session &Session::find(Port servicePort)
+void Session::invalidateSessionAuthHosts()
 {
-    StLock<Mutex> _(mSessions);
-       PortMap<Session>::const_iterator it = mSessions.find(servicePort);
-       assert(it != mSessions.end());
-       return *it->second;
+    StLock<Mutex> _(mAuthHostLock);
+    
+    // if you got here, we don't care about pending operations: the auth hosts die
+    Syslog::warning("Killing auth hosts");
+    if (mSecurityAgent) mSecurityAgent->UnixPlusPlus::Child::kill(SIGTERM);
+    if (mAuthHost) mAuthHost->UnixPlusPlus::Child::kill(SIGTERM);
+    mSecurityAgent = NULL;
+    mAuthHost = NULL;
 }
 
-Session &Session::find(SecuritySessionId id)
+void Session::invalidateAuthHosts()
 {
-    switch (id) {
-    case callerSecuritySession:
-        return Server::session();
-    default:
-        return HandleObject::find<Session>(id, CSSMERR_CSSM_INVALID_ADDIN_HANDLE);
-    }
+       StLock<Mutex> _(mSessionLock);
+       for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
+        it->second->invalidateSessionAuthHosts();
 }
 
-
 //
-// Act on a death notification for a session's (sub)bootstrap port.
-// We may not destroy the Session outright here (due to processes that use it),
-// but we do clear out its accumulated wealth.
+// On system sleep, call sleepProcessing on all DbCommons of all Sessions
 //
-void Session::destroy(Port servPort)
+void Session::processSystemSleep()
 {
-    // remove session from session map
-    StLock<Mutex> _(mSessions);
-    PortMap<Session>::iterator it = mSessions.find(servPort);
-    assert(it != mSessions.end());
-    Session *session = it->second;
-       session->kill();
-    mSessions.erase(it);
+    SecurityAgentXPCQuery::killAllXPCClients();
+
+       StLock<Mutex> _(mSessionLock);
+       for (SessionMap::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
+               it->second->allReferences(&DbCommon::sleepProcessing);
 }
 
-void Session::kill()
+
+//
+// On "lockAll", call sleepProcessing on all DbCommons of this session (only)
+//
+void Session::processLockAll()
 {
-       release();
+       allReferences(&DbCommon::lockProcessing);
+}
 
-    StLock<Mutex> _(mLock);
-    
-    // this session is now officially dying
-    mDying = true;
-    
-    // invalidate shared credentials
-    {
-        StLock<Mutex> _(mCredsLock);
-        
-        IFDEBUG(if (!mSessionCreds.empty()) 
-            secdebug("SSauth", "session %p clearing %d shared credentials", 
-                this, int(mSessionCreds.size())));
-        for (CredentialSet::iterator it = mSessionCreds.begin(); it != mSessionCreds.end(); it++)
-            (*it)->invalidate();
-    }
-       
-       // base kill processing
-       PerSession::kill();
+
+//
+// The root session corresponds to the audit session that security is running in.
+// This is usually the initial system session; but in debug scenarios it may be
+// an "ordinary" graphic login session. In such a debug case, we may add attribute
+// flags to the session to make our (debugging) life easier.
+//
+RootSession::RootSession(uint64_t attributes, Server &server)
+       : Session(AuditInfo::current(), server)
+{
+       ref();                          // eternalize
+       mAudit.ai_flags |= attributes;          // merge imposed attributes
 }
 
 
 //
-// Relay lockAllDatabases to all known sessions
+// Dynamic sessions use the audit session context of the first-contact client caller.
 //
-void Session::processSystemSleep()
+DynamicSession::DynamicSession(const AuditInfo &audit)
+       : Session(audit, Server::active())
 {
-       StLock<Mutex> _(mSessions);
-       for (PortMap<Session>::const_iterator it = mSessions.begin(); it != mSessions.end(); it++)
-               it->second->allReferences<DbCommon>(&DbCommon::sleepProcessing);
 }
 
 
@@ -200,21 +328,23 @@ OSStatus Session::authCreate(const AuthItemSet &rights,
        const AuthItemSet &environment,
        AuthorizationFlags flags,
        AuthorizationBlob &newHandle,
-       const security_token_t &securityToken)
+       const audit_token_t &auditToken)
 {
        // invoke the authorization computation engine
        CredentialSet resultCreds;
        
-       // this will acquire mLock, so we delay acquiring it
-       auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, securityToken));
+       // this will acquire the object lock, so we delay acquiring it (@@@ no longer needed)
+       auto_ptr<AuthorizationToken> auth(new AuthorizationToken(*this, resultCreds, auditToken, (flags&kAuthorizationFlagLeastPrivileged)));
 
+       SECURITYD_AUTH_CREATE(this, auth.get());
+    
     // Make a copy of the mSessionCreds
     CredentialSet sessionCreds;
     {
         StLock<Mutex> _(mCredsLock);
         sessionCreds = mSessionCreds;
     }
-        
+       
        AuthItemSet outRights;
        OSStatus result = Server::authority().authorize(rights, environment, flags,
         &sessionCreds, &resultCreds, outRights, *auth);
@@ -259,9 +389,17 @@ OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
        const AuthItemSet &rights, const AuthItemSet &environment,
        AuthorizationFlags flags,
        AuthItemSet &grantedRights)
+{
+       AuthorizationToken &auth = authorization(authBlob);
+       return auth.session().authGetRights(auth, rights, environment, flags, grantedRights);
+}
+
+OSStatus Session::authGetRights(AuthorizationToken &auth,
+       const AuthItemSet &rights, const AuthItemSet &environment,
+       AuthorizationFlags flags,
+       AuthItemSet &grantedRights)
 {
     CredentialSet resultCreds;
-    AuthorizationToken &auth = authorization(authBlob);
     CredentialSet effective;
     {
         StLock<Mutex> _(mCredsLock);
@@ -279,7 +417,7 @@ OSStatus Session::authGetRights(const AuthorizationBlob &authBlob,
        }
 
        secdebug("SSauth", "Authorization %p copyRights asked for %d got %d",
-               &authorization(authBlob), int(rights.size()), int(grantedRights.size()));
+               &auth, int(rights.size()), int(grantedRights.size()));
        return result;
 }
 
@@ -297,13 +435,13 @@ OSStatus Session::authExternalize(const AuthorizationBlob &authBlob,
        AuthorizationExternalForm &extForm)
 {
        const AuthorizationToken &auth = authorization(authBlob);
-       StLock<Mutex> _(mLock);
+       StLock<Mutex> _(*this);
        if (auth.mayExternalize(Server::process())) {
                memset(&extForm, 0, sizeof(extForm));
         AuthorizationExternalBlob &extBlob =
             reinterpret_cast<AuthorizationExternalBlob &>(extForm);
         extBlob.blob = auth.handle();
-        extBlob.session = bootstrapPort();
+        extBlob.session = this->sessionId();
                secdebug("SSauth", "Authorization %p externalized", &auth);
                return noErr;
        } else
@@ -322,7 +460,7 @@ OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
     
        // check for permission and do it
        if (sourceAuth.mayInternalize(Server::process(), true)) {
-               StLock<Mutex> _(mLock);
+               StLock<Mutex> _(*this);
                authBlob = extBlob.blob;
         Server::process().addAuthorization(&sourceAuth);
         secdebug("SSauth", "Authorization %p internalized", &sourceAuth);
@@ -332,30 +470,39 @@ OSStatus Session::authInternalize(const AuthorizationExternalForm &extForm,
 }
 
 
+// 
+// Accessor method for setting audit session flags.
+// 
+void Session::setAttributes(SessionAttributeBits bits)
+{
+       StLock<Mutex> _(*this);
+       updateAudit();
+//     assert((bits & ~settableAttributes) == 0);
+       mAudit.ai_flags = bits;
+       mAudit.set();
+}
+
 //
-// Set up a (new-ish) Session.
-// This call must be made from a process within the session, and it must be the first
-// such process to make the call.
+// The default session setup operation always fails.
+// Subclasses can override this to support session setup calls.
 //
-void Session::setup(SessionCreationFlags flags, SessionAttributeBits attrs)
+void Session::setupAttributes(SessionCreationFlags flags, SessionAttributeBits attrs)
 {
-    // check current process object - it may have been cached before the client's bootstrap switch
-    Process *process = &Server::process();
-    process->session().setupAttributes(attrs);
+       MacOSError::throwMe(errSessionAuthorizationDenied);
 }
 
-
-void Session::setupAttributes(SessionAttributeBits attrs)
+uid_t Session::originatorUid()
 {
-    secdebug("SSsession", "%p setup attrs=0x%lx", this, attrs);
-    if (attrs & ~settableAttributes)
-        MacOSError::throwMe(errSessionInvalidAttributes);
-    if (attribute(sessionWasInitialized))
-        MacOSError::throwMe(errSessionAuthorizationDenied);
-    setAttributes(attrs | sessionWasInitialized);
+    if (mAudit.uid() == AU_DEFAUDITID) {
+        StLock<Mutex> _(*this);
+        updateAudit();
+    }
+    return mAudit.uid();
 }
 
-
+//
+// Authorization database I/O
+//
 OSStatus Session::authorizationdbGet(AuthorizationString inRightName, CFDictionaryRef *rightDict)
 {
        string rightName(inRightName);
@@ -382,8 +529,8 @@ OSStatus Session::authorizationdbSet(const AuthorizationBlob &authBlob, Authoriz
         auth.mergeCredentials(resultCreds);
        }
 
-       secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%ld)",
-               &authorization(authBlob), inRightName, result);
+       secdebug("SSauth", "Authorization %p authorizationdbSet %s (result=%d)",
+               &authorization(authBlob), inRightName, int32_t(result));
        return result;
 }
 
@@ -407,8 +554,8 @@ OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, Autho
         auth.mergeCredentials(resultCreds);
        }
 
-       secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%ld)",
-               &authorization(authBlob), inRightName, result);
+       secdebug("SSauth", "Authorization %p authorizationdbRemove %s (result=%d)",
+               &authorization(authBlob), inRightName, int32_t(result));
        return result;
 }
 
@@ -420,18 +567,20 @@ OSStatus Session::authorizationdbRemove(const AuthorizationBlob &authBlob, Autho
 void Session::mergeCredentials(CredentialSet &creds)
 {
     secdebug("SSsession", "%p merge creds @%p", this, &creds);
+       CredentialSet updatedCredentials = creds;
        for (CredentialSet::const_iterator it = creds.begin(); it != creds.end(); it++)
-               if (((*it)->isShared() && (*it)->isValid())) {
+               if ((*it)->isShared() && (*it)->isValid()) {
                        CredentialSet::iterator old = mSessionCreds.find(*it);
                        if (old == mSessionCreds.end()) {
                                mSessionCreds.insert(*it);
             } else {
                 // replace "new" with "old" in input set to retain synchronization
                                (*old)->merge(**it);
-                creds.erase(it);
-                creds.insert(*old);
+                updatedCredentials.erase(*it);
+                updatedCredentials.insert(*old);
             }
                }
+       creds.swap(updatedCredentials);
 }
 
 
@@ -445,6 +594,85 @@ AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
        return auth;
 }
 
+//
+// Run the Authorization engine to check if a given right has been authorized,
+// independent of an external client request.  
+//
+OSStatus Session::authCheckRight(string &rightName, Connection &connection, bool allowUI)
+{
+    // dummy up the arguments for authCreate()
+    AuthorizationItem rightItem = { rightName.c_str(), 0, NULL, 0 };
+    AuthorizationItemSet rightItemSet = { 1, &rightItem };
+    AuthItemSet rightAuthItemSet(&rightItemSet);
+    AuthItemSet envAuthItemSet(kAuthorizationEmptyEnvironment);
+    AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagExtendRights;
+    if (true == allowUI)
+        flags |= kAuthorizationFlagInteractionAllowed;
+    AuthorizationBlob dummyHandle;
+    const audit_token_t *at = connection.auditToken();
+    
+    return authCreate(rightAuthItemSet, envAuthItemSet, flags, dummyHandle, *at);
+}
+
+// for places within securityd that don't want to #include
+// <libsecurity_authorization/Authorization.h> or to fuss about exceptions
+bool Session::isRightAuthorized(string &rightName, Connection &connection, bool allowUI)
+{
+    bool isAuthorized = false;
+    
+    try {
+        OSStatus status = authCheckRight(rightName, connection, allowUI);
+        if (errAuthorizationSuccess == status)
+            isAuthorized = true;
+    }
+    catch (...) { 
+    }
+    return isAuthorized;
+}
+
+RefPointer<AuthHostInstance> 
+Session::authhost(const AuthHostType hostType, const bool restart)
+{
+       StLock<Mutex> _(mAuthHostLock);
+
+       if (hostType == privilegedAuthHost)
+       {
+               if (restart || !mAuthHost || (mAuthHost->state() != Security::UnixPlusPlus::Child::alive))
+               {
+                       if (mAuthHost)
+                               PerSession::kill(*mAuthHost);
+                       mAuthHost = new AuthHostInstance(*this, hostType);      
+               }
+               return mAuthHost;
+       }
+       else /* if (hostType == securityAgent) */
+       {
+               if (restart || !mSecurityAgent || (mSecurityAgent->state() != Security::UnixPlusPlus::Child::alive))
+               {
+                       if (mSecurityAgent)
+                               PerSession::kill(*mSecurityAgent);
+                       mSecurityAgent = new AuthHostInstance(*this, hostType);
+               }
+               return mSecurityAgent;
+       }
+}
+
+void DynamicSession::setUserPrefs(CFDataRef userPrefsDict)
+{
+       if (Server::process().uid() != 0)
+               MacOSError::throwMe(errSessionAuthorizationDenied);
+       StLock<Mutex> _(*this);
+       mSessionAgentPrefs = userPrefsDict;
+}
+
+CFDataRef DynamicSession::copyUserPrefs()
+{
+       StLock<Mutex> _(*this);
+       if (mSessionAgentPrefs)
+               CFRetain(mSessionAgentPrefs);
+       return mSessionAgentPrefs;
+}
+
 
 //
 // Debug dumping
@@ -454,10 +682,8 @@ AuthorizationToken &Session::authorization(const AuthorizationBlob &blob)
 void Session::dumpNode()
 {
        PerSession::dumpNode();
-       if (mDying)
-               Debug::dump(" DYING");
-       Debug::dump(" boot=%d service=%d attrs=0x%lx",
-               mBootstrap.port(), mServicePort.port(), mAttributes);
+       Debug::dump(" auid=%d attrs=%#x authhost=%p securityagent=%p",
+               this->sessionId(), uint32_t(this->attributes()), mAuthHost, mSecurityAgent);
 }
 
 #endif //DEBUGDUMP