windows-nt/Source/XPSP1/NT/ds/security/base/lsa/server/sesmgr.cxx
2020-09-26 16:20:57 +08:00

2036 lines
46 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1991 - 1992
//
// File: SesMgr.c
//
// Contents: "Session" manager implementation
//
// Functions: InitSessionManager -- Sets up the session manager
// CreateSession -- Create a session
// DeleteSession -- Delete a session
// LocateSession -- Locates a session based on CallerContext
// GetAndSetSession -- Sticks session in TLS
// FreeSession -- Frees a session from a thread
// AddCredHandle -- Adds a cred handle to session
// DelCredHandle -- Deletes a cred handle from a session
//
// History:
//
//------------------------------------------------------------------------
#include <lsapch.hxx>
extern "C"
{
#include <adtp.h>
}
NTSTATUS
LsapCleanUpHandles(
PSession pSession,
PVOID Ignored
);
NTSTATUS
I_DeleteSession(PSession pSession);
RTL_CRITICAL_SECTION csSessionMgr;
#if DBG
ULONG SessionLock;
#define LockSessions(x) RtlEnterCriticalSection(&csSessionMgr); SessionLock = x
#define UnlockSessions() SessionLock = 0; RtlLeaveCriticalSection(&csSessionMgr)
#else
#define LockSessions(x) RtlEnterCriticalSection(&csSessionMgr)
#define UnlockSessions() RtlLeaveCriticalSection(&csSessionMgr)
#endif
#define SM_ICREATE 1
#define SM_DELETE 2
#define SM_ADDRUNDOWN 3
#define SM_DELRUNDOWN 4
#define SM_ADDHANDLE 5
#define SM_DELHANDLE 6
#define SM_VALIDHANDLE 7
#define SM_CLONE 9
#define SM_CLEANUP 10
#define SM_FINDEFS 11
#define SM_UPDATEEFS 12
#define SM_ADDCONNECT 13
PSession pDefaultSession;
PSession pEfsSession ;
LIST_ENTRY SessionList ;
LIST_ENTRY SessionConnectList ;
VOID
LsapContextRundown(
PSecHandle phContext,
PVOID Context,
ULONG RefCount
);
VOID
LsapCredentialRundown(
PSecHandle phCreds,
PVOID Context,
ULONG RefCount
);
//+-------------------------------------------------------------------------
//
// Function: InitSessionManager()
//
// Synopsis: Initializes whatever is needed to track sessions
//
// Effects: csSessionMgr, psSessionList;
//
// Arguments: void
//
// Requires:
//
// Returns: TRUE if success, FALSE otherwise.
//
// Notes:
//
//--------------------------------------------------------------------------
BOOL
InitSessionManager(void)
{
NTSTATUS scRet = STATUS_SUCCESS;
PSession pSession;
Session sSess;
//
// Fake a session during init:
//
SetCurrentPackageId( SPMGR_ID );
SetCurrentSession( &sSess );
scRet = RtlInitializeCriticalSection(&csSessionMgr);
if (FAILED(scRet))
{
return(FALSE);
}
SmallHandlePackage.Initialize();
LargeHandlePackage.Initialize();
InitializeListHead( &SessionList );
InitializeListHead( &SessionConnectList );
scRet = CreateSession(
&NtCurrentTeb()->ClientId,
FALSE,
LSA_PROCESS_NAME,
0, // no flags
&pSession
);
if (FAILED(scRet))
{
return(FALSE);
}
pSession->fSession |= SESFLAG_DEFAULT | SESFLAG_TCB_PRIV ;
pDefaultSession = pSession;
SetCurrentSession( pSession );
return(TRUE);
}
VOID
LsapFindEfsSession(
VOID
)
{
PLIST_ENTRY Scan ;
PSession pSession ;
LockSessions( SM_FINDEFS );
Scan = SessionList.Flink ;
while ( Scan != &SessionList )
{
pSession = CONTAINING_RECORD( Scan, Session, List );
if ( (pSession->dwProcessID == pDefaultSession->dwProcessID) &&
((pSession->fSession & SESFLAG_KERNEL) != 0 ) )
{
pEfsSession = pSession ;
break;
}
Scan = Scan->Flink ;
}
UnlockSessions();
if ( !pEfsSession )
{
DebugLog(( DEB_ERROR, "EFS Session not found\n" ));
}
}
VOID
LsapUpdateEfsSession(
PSession pSession
)
{
if ( !pEfsSession )
{
return ;
}
LockSessions( SM_UPDATEEFS );
LockSession( pSession );
LockSession( pEfsSession );
pEfsSession->SharedData = pSession->SharedData ;
pSession->SharedData->cRefs++ ;
pEfsSession->fSession |= SESFLAG_EFS ;
UnlockSession( pEfsSession );
UnlockSession( pSession );
UnlockSessions();
}
//+-------------------------------------------------------------------------
//
// Function: CreateSession()
//
// Synopsis: Creates a new session.
//
// Effects: Session list.
//
// Arguments: fOpenImmediate - TRUE indicates the process should be opened
//
// ppSession - receives a pointer to the session.
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
CreateSession( CLIENT_ID * pClientId,
BOOL fOpenImmediate,
PWCHAR ClientProcessName,
ULONG Flags,
PSession * ppSession)
{
NTSTATUS scRet = STATUS_SUCCESS;
PSession pSession;
LPWSTR ClientName;
PLIST_ENTRY Scan ;
PLSAP_SESSION_CONNECT Connect ;
ULONG ConnectType = 0 ;
DebugLog(( DEB_TRACE, "Creating session for [%x.%x]\n",
pClientId->UniqueProcess, pClientId->UniqueThread ));
*ppSession = NULL;
if ( *ClientProcessName )
{
ConnectType |= SESSION_CONNECT_TRUSTED ;
ClientName = (LPWSTR) LsapAllocatePrivateHeap(
(wcslen(ClientProcessName)+1) * sizeof(WCHAR));
if (ClientName == NULL)
{
return(STATUS_INSUFFICIENT_RESOURCES);
}
wcscpy(ClientName,ClientProcessName);
}
else
{
ConnectType |= SESSION_CONNECT_UNTRUSTED ;
ClientName = NULL;
}
pSession = (PSession) LsapAllocatePrivateHeap( sizeof( Session ) );
if (!pSession)
{
*ppSession = NULL;
if( ClientName != NULL )
{
LsapFreePrivateHeap( ClientName );
}
return(STATUS_INSUFFICIENT_RESOURCES);
}
RtlZeroMemory(pSession, sizeof(Session));
//
// Initialize stuff:
//
pSession->fSession = Flags;
//
// we check for this much later, after some same initializing
// steps, so we can use the common cleanup
//
scRet = RtlInitializeCriticalSection( &pSession->SessionLock );
InitializeListHead( &pSession->SectionList );
InitializeListHead( &pSession->RundownList );
pSession->ClientProcessName = ClientName;
pSession->dwProcessID = HandleToUlong(pClientId->UniqueProcess);
//
// Store the handle cleanup as a rundown function
//
if ( NT_SUCCESS( scRet ) )
{
if ( !AddRundown( pSession,
LsapCleanUpHandles,
NULL ) )
{
scRet = STATUS_NO_MEMORY ;
}
}
//
// Lock the sessions now so we know that no new kernel sessions
// will be created
//
LockSessions(SM_ICREATE);
//
// Add the session to the list
//
InsertTailList( &SessionList, &pSession->List );
//
// *Now* check if we're doing ok:
//
if ( !NT_SUCCESS( scRet ))
{
UnlockSessions();
goto Cleanup;
}
//
// If the caller is from kernel mode, look for an existing kernel
// session to use the handle list from.
//
if ((Flags & SESFLAG_MAYBEKERNEL) != 0)
{
PSession pTrace = NULL;
//
// Find a kernel-mode session and use its handle list. Make sure
// it's not the default LSA session, although it is ok to use the
// session tagged as the EFS one. This will keep the EFS session
// and the (primary) rdr session in sync.
//
Scan = SessionList.Flink ;
while ( Scan != &SessionList )
{
pTrace = CONTAINING_RECORD( Scan, Session, List );
if ( pTrace != pSession )
{
if ( ( (pTrace->fSession & SESFLAG_KERNEL) != 0 ) &&
( ( (pTrace->fSession & SESFLAG_EFS) != 0 ) ||
( pTrace->dwProcessID != pDefaultSession->dwProcessID ) ) )
{
break;
}
}
pTrace = NULL ;
Scan = Scan->Flink ;
}
//
// If we found a session, use its handle list and queue
//
if (pTrace != NULL)
{
pTrace->SharedData->cRefs++;
pSession->SharedData = pTrace->SharedData;
DebugLog(( DEB_TRACE, "Linking session %p to %p\n",
pSession, pTrace ));
}
//
// ConnectType is not definite, it is a hint to others
// watching for new sessions
//
ConnectType |= SESSION_CONNECT_KERNEL ;
}
//
// If we don't have a handle list yet, create one and set it to NULL
//
if (pSession->SharedData == NULL)
{
if ( pSession->fSession & SESFLAG_MAYBEKERNEL )
{
pSession->SharedData = (PLSAP_SHARED_SESSION_DATA) LsapAllocatePrivateHeap(
sizeof( LSAP_SHARED_SESSION_DATA ) );
}
else
{
pSession->SharedData = &pSession->DefaultData ;
}
if ( pSession->SharedData )
{
RtlZeroMemory(
pSession->SharedData,
sizeof( LSAP_SHARED_SESSION_DATA ) );
//
// Create Handle Tables:
//
pSession->SharedData->CredHandlePackage = &SmallHandlePackage ;
pSession->SharedData->ContextHandlePackage = &LargeHandlePackage ;
pSession->SharedData->CredTable = pSession->SharedData->CredHandlePackage->Create(
HANDLE_PACKAGE_CALLBACK_ON_DELETE,
NULL,
LsapCredentialRundown );
pSession->SharedData->ContextTable = pSession->SharedData->ContextHandlePackage->Create(
HANDLE_PACKAGE_CALLBACK_ON_DELETE,
NULL,
LsapContextRundown );
if ((pSession->SharedData->CredTable == NULL) || (pSession->SharedData->ContextTable == NULL))
{
scRet = STATUS_INSUFFICIENT_RESOURCES;
}
pSession->SharedData->cRefs = 1;
}
else
{
scRet = STATUS_INSUFFICIENT_RESOURCES ;
}
}
UnlockSessions();
//
// Check for callbacks when a client connects. Note, this does
// not need to be under the session list lock, because it is
// sufficient that no client is using the session until the
// callbacks are complete. Since the connection attempt is
// stalled until this returns, that requirement is met.
//
Scan = SessionConnectList.Flink ;
while ( Scan != &SessionConnectList )
{
Connect = CONTAINING_RECORD( Scan, LSAP_SESSION_CONNECT, List );
if ( Connect->ConnectFilter & ConnectType )
{
Connect->Callback( pSession, Connect->Parameter );
}
Scan = Scan->Flink ;
}
*ppSession = pSession ;
if (fOpenImmediate & (NT_SUCCESS(scRet)))
{
scRet = LsapOpenCaller(pSession);
}
//
// If we failed somewhere, cleanup now.
//
Cleanup:
if (!NT_SUCCESS(scRet))
{
I_DeleteSession(pSession);
*ppSession = NULL;
}
return(scRet);
}
//+---------------------------------------------------------------------------
//
// Function: CloneSession
//
// Synopsis: Temporary copy of a session for scavenger threads
//
// Arguments: [pOriginalSession] --
// [ppSession] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
CloneSession(
PSession pOriginalSession,
PSession * ppSession,
ULONG Flags )
{
PSession pSession;
NTSTATUS Status ;
BOOL LockInitialized = FALSE ;
pSession = (PSession) LsapAllocatePrivateHeap( sizeof( Session ) );
if ( !pSession )
{
*ppSession = NULL ;
return STATUS_NO_MEMORY ;
}
//
// Make sure there is no one else mucking with general session stuff
//
LockSessions(SM_CLONE);
Status = STATUS_SUCCESS ;
//
// Copy all the interesting bits
//
CopyMemory(pSession, pOriginalSession, sizeof(Session));
//
// Make sure it has its own critical section
//
Status = RtlInitializeCriticalSection( &pSession->SessionLock );
if ( NT_SUCCESS( Status ) )
{
LockInitialized = TRUE ;
}
//
// note that it is a clone
//
pSession->fSession |= ( SESFLAG_CLONE | Flags );
//
// Initialize our own rundown list.
//
InitializeListHead( &pSession->RundownList );
//
// Use our own rundown
//
if ( AddRundown( pSession, LsapCleanUpHandles, NULL ) )
{
pOriginalSession->SharedData->cRefs++;
}
else
{
Status = STATUS_NO_MEMORY ;
}
UnlockSessions();
if ( !NT_SUCCESS( Status ) )
{
if ( LockInitialized )
{
RtlDeleteCriticalSection( &pSession->SessionLock );
}
LsapFreePrivateHeap( pSession );
*ppSession = NULL ;
return Status ;
}
//
// Set the reference count of the clone to -1, so that the a single
// ref/deref will kill it.
//
pSession->RefCount = -1;
//
// Clones do *NOT* get added to the session list.
//
*ppSession = pSession;
return(STATUS_SUCCESS);
}
NTSTATUS
CreateShadowSession(
DWORD ProcessId,
PSession * NewSession
)
{
NTSTATUS Status ;
CLIENT_ID ClientId;
PSession Session ;
ClientId.UniqueProcess = (HANDLE) LongToHandle(ProcessId) ;
ClientId.UniqueThread = NULL ;
Status = CreateSession(
&ClientId,
FALSE,
L"",
SESFLAG_SHADOW,
&Session );
*NewSession = Session ;
return Status ;
}
VOID
WINAPI
LsapDeleteContextWrap(
PSecHandle Handle,
PVOID Context,
ULONG RefCount
)
{
PLSA_CALL_INFO CallInfo ;
CallInfo = LsapGetCurrentCall();
CallInfo->CallInfo.CallCount = RefCount ;
WLsaDeleteContext( Handle );
CallInfo->CallInfo.CallCount = 0 ;
}
VOID
WINAPI
LsapFreeCredWrap(
PSecHandle Handle,
PVOID Context,
ULONG RefCount
)
{
PLSA_CALL_INFO CallInfo ;
CallInfo = LsapGetCurrentCall();
CallInfo->CallInfo.CallCount = RefCount ;
WLsaFreeCredHandle( Handle );
CallInfo->CallInfo.CallCount = 0;
}
//+---------------------------------------------------------------------------
//
// Function: LsapCleanUpHandles
//
// Synopsis: Closes all context and credential handles for a session
//
// Arguments: [pSession] --
// [Ignored] --
//
// History: 5-15-97 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
LsapCleanUpHandles(
PSession pSession,
PVOID Ignored
)
{
NTSTATUS scRet = STATUS_SUCCESS;
PSession pSave;
ULONG HandleRefs;
PLIST_ENTRY ListEntry;
LSA_CALL_INFO CallInfo ;
HANDLE hImp ;
//
// If this is the last user of the handle lists, cleanup
//
if ( pSession->SharedData == NULL )
{
return STATUS_SUCCESS ;
}
LockSessions(SM_CLEANUP);
pSession->SharedData->cRefs--;
HandleRefs = pSession->SharedData->cRefs;
UnlockSessions();
if (HandleRefs == 0)
{
LsapInitializeCallInfo( &CallInfo,
FALSE );
CallInfo.CallInfo.Attributes |= SECPKG_CALL_CLEANUP ;
LsapSetCurrentCall( &CallInfo );
pSave = GetCurrentSession();
SetCurrentSession( pSession );
if (pSession->SharedData->ContextTable != NULL)
{
pSession->SharedData->ContextHandlePackage->Delete( pSession->SharedData->ContextTable,
LsapDeleteContextWrap );
}
if (pSession->SharedData->CredTable != NULL)
{
pSession->SharedData->CredHandlePackage->Delete( pSession->SharedData->CredTable,
LsapFreeCredWrap );
}
if ( pSession->fSession & SESFLAG_KERNEL )
{
LsapFreePrivateHeap( pSession->SharedData );
}
SetCurrentSession( pSave );
LsapSetCurrentCall( NULL );
}
pSession->SharedData = NULL;
return(scRet);
}
//+-------------------------------------------------------------------------
//
// Function: I_DeleteSession()
//
// Synopsis: Deletes the session indicated
//
// Effects: Frees memory
//
// Arguments: pSession Session to delete
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
I_DeleteSession(PSession pSession)
{
PSession pTrace;
PLIST_ENTRY List ;
PLSAP_SESSION_RUNDOWN Rundown ;
int i;
DebugLog(( DEB_TRACE, "DeleteSession( %x )\n", pSession ));
DsysAssertMsg(pSession, "DeleteSession passed null pointer");
pSession->fSession |= SESFLAG_CLEANUP;
//
// Clones aren't in the list, so don't try to unlink it
//
if ( ( pSession->fSession & SESFLAG_CLONE ) == 0 )
{
LockSessions(SM_DELETE);
RemoveEntryList( &pSession->List );
UnlockSessions();
}
//
// Execute the rundown functions
//
LockSession( pSession );
while ( !IsListEmpty( &pSession->RundownList ) )
{
List = RemoveHeadList( &pSession->RundownList );
Rundown = CONTAINING_RECORD( List, LSAP_SESSION_RUNDOWN, List );
Rundown->Rundown( pSession, Rundown->Parameter );
LsapFreePrivateHeap( Rundown );
}
UnlockSession( pSession );
RtlDeleteCriticalSection( &pSession->SessionLock );
if (pSession->fSession & SESFLAG_CLONE)
{
//
// Clones are not part of the global list, and are
// around just for bookkeepping for scavenger threads
//
pSession->dwProcessID = 0xFFFFFFFF;
LsapFreePrivateHeap( pSession );
return(STATUS_SUCCESS);
}
//
// Close our handles
//
if (pSession->hProcess)
NtClose(pSession->hProcess);
if (pSession->hPort)
{
NtClose(pSession->hPort);
}
pSession->dwProcessID = 0xFFFFFFFF;
if( pSession->ClientProcessName )
{
LsapFreePrivateHeap( pSession->ClientProcessName );
}
LsapFreePrivateHeap( pSession );
return(STATUS_SUCCESS);
}
HRESULT
LsapDeleteWorkQueue(
PSession pSession,
PVOID Parameter
)
{
if ( pSession->SharedData->pQueue )
{
DeleteSubordinateQueue( pSession->SharedData->pQueue, 0 );
pSession->SharedData->pQueue = NULL ;
}
return STATUS_SUCCESS ;
}
//+---------------------------------------------------------------------------
//
// Function: SpmpReferenceSession
//
// Synopsis: Ups the ref count on a session
//
// Arguments: [pSession] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
SpmpReferenceSession(
PSession pSession)
{
InterlockedIncrement(&pSession->RefCount);
}
//+---------------------------------------------------------------------------
//
// Function: SpmpDereferenceSession
//
// Synopsis: Derefs a session. If the session goes negative, it gets
// deleted.
//
// Arguments: [pSession] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
VOID
SpmpDereferenceSession(
PSession pSession)
{
LONG RefCount = InterlockedDecrement(&pSession->RefCount);
if( RefCount < 0 )
{
if ( pSession == pDefaultSession )
{
pSession->RefCount = 1 ;
}
else
{
DsysAssert( (pSession->RefCount == -1) && (RefCount == -1) );
I_DeleteSession(pSession);
}
}
}
VOID
LsapSessionDisconnect(
PSession pSession
)
{
if ( pSession->SharedData->cRefs == 1 )
{
LsapDeleteWorkQueue( pSession, NULL );
}
}
//+-------------------------------------------------------------------------
//
// Function: FreeSession
//
// Synopsis: Frees a session
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
void
FreeSession(PSession pSession)
{
if (pSession == NULL)
{
pSession = GetCurrentSession();
}
if (!pSession)
{
DebugLog((DEB_ERROR, "FreeSession: No Session?\n"));
return;
}
TlsSetValue(dwSession, pDefaultSession);
}
//
// Rundown Semantics:
//
// Rundowns allow other functions to be called when a session is closed. This
// is useful if you need to keep something around as long as a client is
// connected, but need to clean up afterwards.
//
// Rules: You cannot call back into or reference the client process. This is
// because the process has already terminated.
//
//+---------------------------------------------------------------------------
//
// Function: AddRundown
//
// Synopsis: Adds a function to be called when the session is terminated
//
// Arguments: [pSession] --
// [RundownFn] --
// [pvParameter] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
AddRundown(
PSession pSession,
PLSAP_SESSION_RUNDOWN_FN RundownFn,
PVOID pvParameter)
{
PLSAP_SESSION_RUNDOWN Rundown ;
Rundown = (PLSAP_SESSION_RUNDOWN) LsapAllocatePrivateHeap(
sizeof( LSAP_SESSION_RUNDOWN ) );
if ( Rundown )
{
Rundown->Rundown = RundownFn ;
Rundown->Parameter = pvParameter ;
LockSession( pSession );
InsertTailList( &pSession->RundownList, &Rundown->List );
UnlockSession( pSession );
return TRUE ;
}
return FALSE ;
}
//+---------------------------------------------------------------------------
//
// Function: DelRundown
//
// Synopsis: Removes a rundown function from a session
//
// Arguments: [pSession] --
// [RundownFn] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL
DelRundown(
PSession pSession,
PLSAP_SESSION_RUNDOWN_FN RundownFn
)
{
PLIST_ENTRY Scan;
PLSAP_SESSION_RUNDOWN Rundown ;
LockSession( pSession );
Scan = pSession->RundownList.Flink ;
Rundown = NULL ;
while ( Scan != &pSession->RundownList )
{
Rundown = (PLSAP_SESSION_RUNDOWN) Scan ;
if ( Rundown->Rundown == RundownFn )
{
RemoveEntryList( &Rundown->List );
break;
}
Rundown = NULL ;
Scan = Scan->Flink ;
}
UnlockSession( pSession );
if ( Rundown )
{
LsapFreePrivateHeap( Rundown );
return TRUE ;
}
return FALSE ;
}
BOOL
AddConnectionHook(
PLSAP_SESSION_CONNECT_FN ConnectFn,
PVOID Parameter,
ULONG Filter
)
{
PLSAP_SESSION_CONNECT Connect ;
Connect = (PLSAP_SESSION_CONNECT) LsapAllocatePrivateHeap( sizeof( LSAP_SESSION_CONNECT ) );
if ( Connect )
{
Connect->Callback = ConnectFn ;
Connect->ConnectFilter = Filter ;
Connect->Parameter = Parameter ;
LockSessions( SM_ADDCONNECT );
InsertTailList( &SessionConnectList, &Connect->List );
UnlockSessions();
}
return (Connect != NULL) ;
}
VOID
LsapCredentialRundown(
PSecHandle phCreds,
PVOID Context,
ULONG RefCount
)
{
PSession pSession = (PSession) Context ;
NTSTATUS scRet;
PLSAP_SECURITY_PACKAGE pPackage;
PSession Previous ;
Previous = GetCurrentSession();
if ( ( ( pSession->fSession & SESFLAG_KERNEL ) != 0 ) &&
( ( Previous->fSession & SESFLAG_KERNEL ) != 0 ) )
{
NOTHING ;
}
else
{
SetCurrentSession( pSession );
}
DebugLog((DEB_TRACE, "[%x] CredentialRundown (%p:%p) RefCount=%lu\n",
pSession->dwProcessID, phCreds->dwUpper, phCreds->dwLower, RefCount));
pPackage = SpmpValidRequest(phCreds->dwLower,
SP_ORDINAL_FREECREDHANDLE );
if (pPackage)
{
PLSA_CALL_INFO CallInfo;
ULONG OldRefCount;
SetCurrentPackageId(phCreds->dwLower);
StartCallToPackage( pPackage );
ASSERT( RefCount );
CallInfo = LsapGetCurrentCall();
OldRefCount = CallInfo->CallInfo.CallCount;
CallInfo->CallInfo.CallCount = RefCount;
__try
{
scRet = pPackage->FunctionTable.FreeCredentialsHandle(
phCreds->dwUpper);
}
__except (SP_EXCEPTION)
{
scRet = GetExceptionCode();
scRet = SPException(scRet, phCreds->dwLower);
}
CallInfo->CallInfo.CallCount = OldRefCount;
EndCallToPackage( pPackage );
LsapDelPackageHandle( pPackage, FALSE );
}
SetCurrentSession( Previous );
}
VOID
LsapContextRundown(
PSecHandle phContext,
PVOID Context,
ULONG RefCount
)
{
PSession Session = (PSession) Context;
PSession Previous ;
PLSAP_SECURITY_PACKAGE pspPackage;
PLSA_CALL_INFO CallInfo;
ULONG OldRefCount;
NTSTATUS scRet ;
Previous = GetCurrentSession();
if ( ( ( Session->fSession & SESFLAG_KERNEL ) != 0 ) &&
( ( Previous->fSession & SESFLAG_KERNEL ) != 0 ) )
{
NOTHING ;
}
else
{
SetCurrentSession( Session );
}
pspPackage = SpmpValidRequest( phContext->dwLower,
SP_ORDINAL_DELETECTXT);
if (! pspPackage )
{
DebugLog((DEB_ERROR,"[%x] Invalid request for DeleteContext, package: %d\n",
Session->dwProcessID, phContext->dwLower));
return ;
}
DebugLog(( DEB_TRACE, "[%x] ContextRundown(%p:%p)\n",
Session->dwProcessID, phContext->dwUpper,
phContext->dwLower ));
SetCurrentPackageId(phContext->dwLower);
StartCallToPackage( pspPackage );
//
// RefCount should ALWAYS be 1 currently, as, the context handle code
// assumes context handle issue count never exceeds 1.
//
ASSERT( RefCount == 1 );
CallInfo = LsapGetCurrentCall();
OldRefCount = CallInfo->CallInfo.CallCount;
CallInfo->CallInfo.CallCount = RefCount;
__try
{
scRet = pspPackage->FunctionTable.DeleteContext( phContext->dwUpper );
}
__except (SP_EXCEPTION)
{
scRet = GetExceptionCode();
scRet = SPException( scRet, pspPackage->dwPackageID);
}
CallInfo->CallInfo.CallCount = OldRefCount;
EndCallToPackage( pspPackage );
#if DBG
if ( !NT_SUCCESS( scRet ) )
{
DebugLog((DEB_ERROR, "[%x] package %ws failed DeleteContext with %x\n",
Session->dwProcessID,
pspPackage->Name.Buffer,
scRet ));
}
#endif
DebugLog(( DEB_TRACE_WAPI, "[%x] return code %x\n", Session->dwProcessID,
scRet ));
SetCurrentPackageId( SPMGR_ID );
SetCurrentSession( Previous );
LsapDelPackageHandle( pspPackage, TRUE );
}
//+-------------------------------------------------------------------------
//
// Function: AddCredHandle
//
// Synopsis: Adds an obtained cred handle to the list for this session
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
BOOLEAN
AddCredHandle( PSession pSession,
PCredHandle phCred,
ULONG Flags)
{
if (pSession->fSession & SESFLAG_CLONE)
{
DebugLog((DEB_ERROR, "Attempt to add a credhandle to a clone session\n"));
return FALSE;
}
if ( pSession->fSession & SESFLAG_KERNEL )
{
pSession = pEfsSession ;
}
DebugLog(( DEB_TRACE_HANDLES, "Adding Cred %p : %p to %p\n",
phCred->dwUpper, phCred->dwLower, pSession ));
if(pSession->SharedData->CredHandlePackage->AddHandle(
pSession->SharedData->CredTable,
phCred,
pSession,
Flags))
{
LsapAddPackageHandle( phCred->dwLower, FALSE );
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: AddContextHandle
//
// Synopsis: Adds a context handle to this session
//
// Arguments: [phContext] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOLEAN
AddContextHandle( PSession pSession,
PCtxtHandle phContext,
ULONG Flags)
{
if (pSession->fSession & SESFLAG_CLONE)
{
DebugLog((DEB_ERROR, "Attempt to add a ctxthandle to a clone session\n"));
return FALSE;
}
if ( pSession->fSession & SESFLAG_KERNEL )
{
pSession = pEfsSession ;
}
DebugLog(( DEB_TRACE_HANDLES, "Adding Context %p : %p to %p\n",
phContext->dwUpper, phContext->dwLower, pSession ));
//
// Find out where this 0:0 handle is coming from:
//
DsysAssertMsg( (phContext->dwLower | phContext->dwUpper), "Null context handle added\n");
if(pSession->SharedData->ContextHandlePackage->AddHandle(
pSession->SharedData->ContextTable,
phContext,
pSession,
Flags
))
{
LsapAddPackageHandle( phContext->dwLower, TRUE );
return TRUE;
}
return FALSE;
}
//+---------------------------------------------------------------------------
//
// Function: ValidateContextHandle
//
// Synopsis: Validate the context handle against the session list
//
// Arguments: [pSession] --
// [phContext] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
ValidateContextHandle(
PSession pSession,
PCtxtHandle phContext,
PVOID * pKey
)
{
*pKey = pSession->SharedData->ContextHandlePackage->RefHandle(
pSession->SharedData->ContextTable,
phContext );
DebugLog(( DEB_TRACE_HANDLES, "Validate context (%p : %p) for %p returned %p\n",
phContext->dwUpper, phContext->dwLower,
pSession, *pKey ));
if ( *pKey )
{
return SEC_E_OK ;
}
else
{
return SEC_E_INVALID_HANDLE ;
}
}
VOID
DerefContextHandle(
PSession pSession,
PCtxtHandle phContext,
PVOID Key OPTIONAL
)
{
if ( Key )
{
#if DBG
PSEC_HANDLE_ENTRY Entry = (PSEC_HANDLE_ENTRY) Key ;
DebugLog(( DEB_TRACE_HANDLES, "Deref context handle by key ( %p : %p ) for %p \n",
Entry->Handle.dwUpper, Entry->Handle.dwLower,
pSession ));
#endif
pSession->SharedData->ContextHandlePackage->DerefHandleKey(
pSession->SharedData->ContextTable,
Key );
}
else
{
pSession->SharedData->ContextHandlePackage->DeleteHandle(
pSession->SharedData->ContextTable,
phContext,
0 );
DebugLog(( DEB_TRACE_HANDLES, "Deref context handle by handle (%p : %p) for %p\n",
phContext->dwUpper, phContext->dwLower,
pSession ));
}
}
NTSTATUS
ValidateAndDerefContextHandle(
PSession pSession,
PCtxtHandle phContext
)
{
DebugLog(( DEB_TRACE_HANDLES, "ValidateAndDeref Context (%p : %p)\n",
phContext->dwUpper, phContext->dwLower ));
if ( pSession->SharedData->ContextHandlePackage->ValidateHandle(
pSession->SharedData->ContextTable,
phContext,
TRUE ) )
{
return SEC_E_OK ;
}
return SEC_E_INVALID_HANDLE ;
}
//+---------------------------------------------------------------------------
//
// Function: ValidateCredHandle
//
// Synopsis: Validate the context handle against the session list
//
// Arguments: [pSession] --
// [phCred] --
//
// History: 5-18-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
ValidateCredHandle(
PSession pSession,
PCtxtHandle phCred,
PVOID * pKey
)
{
*pKey = pSession->SharedData->CredHandlePackage->RefHandle(
pSession->SharedData->CredTable,
phCred );
DebugLog(( DEB_TRACE_HANDLES, "Validate cred (%p : %p) for %p returned %p\n",
phCred->dwUpper, phCred->dwLower,
pSession, *pKey ));
if ( *pKey )
{
return SEC_E_OK ;
}
else
{
return SEC_E_INVALID_HANDLE ;
}
}
VOID
DerefCredHandle(
PSession pSession,
PCtxtHandle phCred,
PVOID Key OPTIONAL
)
{
if ( Key )
{
#if DBG
PSEC_HANDLE_ENTRY Entry = (PSEC_HANDLE_ENTRY) Key ;
DebugLog(( DEB_TRACE_HANDLES, "Deref cred ( %p : %p ) for %p \n",
Entry->Handle.dwUpper, Entry->Handle.dwLower,
pSession ));
#endif
pSession->SharedData->CredHandlePackage->DerefHandleKey(
pSession->SharedData->CredTable,
Key );
}
else
{
pSession->SharedData->CredHandlePackage->DeleteHandle(
pSession->SharedData->CredTable,
phCred,
0 );
DebugLog(( DEB_TRACE_HANDLES, "Deref cred (%p : %p) for %p\n",
phCred->dwUpper, phCred->dwLower,
pSession ));
}
}
NTSTATUS
ValidateAndDerefCredHandle(
PSession pSession,
PCtxtHandle phCred
)
{
DebugLog(( DEB_TRACE_HANDLES, "ValidateAndDeref Cred (%p : %p)\n",
phCred->dwUpper, phCred->dwLower ));
if ( pSession->SharedData->CredHandlePackage->ValidateHandle(
pSession->SharedData->CredTable,
phCred,
TRUE ) )
{
return SEC_E_OK ;
}
return SEC_E_INVALID_HANDLE ;
}
BOOL
LsapMoveContextHandle(
PSecHandle Handle,
PSession OriginatingSession,
PSession DestinationSession
)
{
BOOL Ret ;
if ( OriginatingSession->SharedData->ContextHandlePackage->DeleteHandle(
OriginatingSession->SharedData->ContextTable,
Handle,
DELHANDLE_FORCE | DELHANDLE_NO_CALLBACK ) )
{
Ret = DestinationSession->SharedData->ContextHandlePackage->AddHandle(
DestinationSession->SharedData->ContextTable,
Handle,
DestinationSession,
0 );
} else {
Ret = FALSE ;
}
return Ret ;
}
BOOL
LsapMoveCredHandle(
PSecHandle Handle,
PSession OriginatingSession,
PSession DestinationSession
)
{
BOOL Ret ;
if ( OriginatingSession->SharedData->CredHandlePackage->DeleteHandle(
OriginatingSession->SharedData->CredTable,
Handle,
DELHANDLE_FORCE | DELHANDLE_NO_CALLBACK ) )
{
Ret = DestinationSession->SharedData->CredHandlePackage->AddHandle(
DestinationSession->SharedData->CredTable,
Handle,
DestinationSession,
0 );
} else {
Ret = FALSE ;
}
return Ret ;
}
NTSTATUS
LsapValidLogonProcess(
IN PVOID Request,
IN ULONG RequestSize,
IN PCLIENT_ID ClientId,
OUT PLUID LogonId,
OUT PULONG Flags
)
/*++
Routine Description:
This function checks to see if a calling process qualifies as a logon
process. If so, a logon process context is created for the caller and
returned.
A logon process must hold the SeTcbPrivilege privilege. Since there
is no way to impersonate a connection requestor (that would be way
too easy), we have to open the client thread and then open that thread's
token.
Arguments:
ClientId - Pointer to the client Id of the sender of the logon
message. This is used to locate and open the calling thread or
process.
Return Value:
STATUS_SUCCESS - Indicates the caller is a legitimate logon process
and a logon process context block is being returned.
any other value - Indicates the caller is NOT a legitimate logon
process and a logon process context block is NOT being returned.
The value returned indicates the reason why the client is not
acceptable.
--*/
{
NTSTATUS Status, TempStatus;
BOOLEAN PrivilegeHeld;
HANDLE ClientThread, ClientProcess, ClientToken;
PRIVILEGE_SET Privilege;
OBJECT_ATTRIBUTES NullAttributes;
UNICODE_STRING Unicode;
STRING Ansi;
BOOLEAN Untrusted = FALSE;
TOKEN_STATISTICS TokenStats;
PLSAP_AU_REGISTER_CONNECT_INFO_EX ConnectionRequest = (PLSAP_AU_REGISTER_CONNECT_INFO_EX) Request;
ULONG TokenStatsSize = sizeof(TokenStats);
LSAP_AU_REGISTER_CONNECT_INFO NullConnectInfo;
*Flags = 0;
RtlZeroMemory(
&NullConnectInfo,
sizeof(NullConnectInfo)
);
//
// If the connect message is all zeros, setup an untrusted connection.
//
if (RtlEqualMemory(
&NullConnectInfo,
ConnectionRequest,
sizeof(NullConnectInfo))) {
Untrusted = TRUE;
*Flags |= SESFLAG_UNTRUSTED;
}
InitializeObjectAttributes( &NullAttributes, NULL, 0, NULL, NULL );
//
// Open the client thread and that thread's token
//
Status = NtOpenThread(
&ClientThread,
THREAD_QUERY_INFORMATION,
&NullAttributes,
ClientId
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
Status = NtOpenThreadToken(
ClientThread,
TOKEN_QUERY,
TRUE,
&ClientToken
);
TempStatus = NtClose( ClientThread );
DsysAssert( NT_SUCCESS(TempStatus) );
//
// Make sure we succeeded in opening the token
//
if ( !NT_SUCCESS(Status) ) {
if ( Status != STATUS_NO_TOKEN ) {
return Status;
} else {
//
// Open the client process. This is needed to:
//
// 1) Access the client's virtual memory (to copy arguments),
// 2) Duplicate token handles into the process,
// 3) Open the process's token to see if it qualifies as
// a logon process.
//
Status = NtOpenProcess(
&ClientProcess,
PROCESS_QUERY_INFORMATION | // To open primary token
PROCESS_VM_OPERATION | // To allocate memory
PROCESS_VM_READ | // To read memory
PROCESS_VM_WRITE | // To write memory
PROCESS_DUP_HANDLE, // To duplicate a handle into
&NullAttributes,
ClientId
);
if ( !NT_SUCCESS(Status) ) {
return Status;
}
//
// The thread isn't impersonating...open the process's token.
//
Status = NtOpenProcessToken(
ClientProcess,
TOKEN_QUERY,
&ClientToken
);
TempStatus = NtClose( ClientProcess );
DsysAssert( NT_SUCCESS(TempStatus) );
//
// Make sure we succeeded in opening the token
//
if ( !NT_SUCCESS(Status) ) {
return Status;
}
}
} else {
*Flags |= SESFLAG_IMPERSONATE;
}
//
// If the caller has the kernel flag set in the LPC message, this is
// probably a kernel session, but we can't be sure until the first
// request is made and the LPC_KERNELMODE_MESSAGE flag is set. The
// handlers will check that, but until then, set the maybe-flag.
//
if (!Untrusted &&
RequestSize == sizeof( LSAP_AU_REGISTER_CONNECT_INFO_EX) &&
((ConnectionRequest->ClientMode & LSAP_AU_KERNEL_CLIENT) != 0) )
{
*Flags |= SESFLAG_MAYBEKERNEL;
}
//
// OK, we have a token open
//
//
// Get the logon id.
//
Status = NtQueryInformationToken(
ClientToken,
TokenStatistics,
(PVOID) &TokenStats,
TokenStatsSize,
&TokenStatsSize
);
if (!NT_SUCCESS(Status)) {
TempStatus = NtClose( ClientToken );
DsysAssert(NT_SUCCESS(TempStatus));
return(Status);
}
*LogonId = TokenStats.AuthenticationId;
Status = STATUS_SUCCESS ;
if (((*Flags) & SESFLAG_MAYBEKERNEL) == 0) {
//
// Check for the privilege to execute this service.
//
Privilege.PrivilegeCount = 1;
Privilege.Control = PRIVILEGE_SET_ALL_NECESSARY;
Privilege.Privilege[0].Luid = LsapTcbPrivilege;
Privilege.Privilege[0].Attributes = 0;
Status = NtPrivilegeCheck(
ClientToken,
&Privilege,
&PrivilegeHeld
);
DsysAssert( NT_SUCCESS(Status) );
//
// For untrusted clients who didn't really need TCB anyway don't
// bother with an audit. If they do have the privilege, by all
// means audit it.
//
if (!Untrusted || PrivilegeHeld) {
//
// Generate any necessary audits
//
TempStatus = NtPrivilegedServiceAuditAlarm (
&LsapLsaAuName,
&LsapRegisterLogonServiceName,
ClientToken,
&Privilege,
PrivilegeHeld
);
// DsysAssert( NT_SUCCESS(TempStatus) );
if ( !PrivilegeHeld ) {
TempStatus = NtClose( ClientToken );
DsysAssert( NT_SUCCESS(TempStatus) );
return STATUS_PRIVILEGE_NOT_HELD;
}
}
if (PrivilegeHeld)
{
*Flags |= SESFLAG_TCB_PRIV;
}
}
TempStatus = NtClose( ClientToken );
DsysAssert( NT_SUCCESS(TempStatus) );
if (!Untrusted && ((*Flags & SESFLAG_KERNEL) == 0))
{
LsapAdtAuditLogonProcessRegistration( ConnectionRequest );
}
return(STATUS_SUCCESS);
}
//+---------------------------------------------------------------------------
//
// Function: LsapSetSessionOptions
//
// Synopsis: Allows clients to adjust session options
//
// Arguments: [Request] --
// [Response] --
//
// History: 8-05-96 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
LsapSetSessionOptions(
ULONG Request,
ULONG_PTR Argument,
PULONG_PTR Response
)
{
PSession pSession;
PLSAP_TASK_QUEUE pQueue;
NTSTATUS Status ;
pSession = GetCurrentSession();
DebugLog(( DEB_TRACE, "[%d] SetSession( %d )\n",
pSession->dwProcessID, Request ));
Status = STATUS_SUCCESS ;
LockSession( pSession );
switch ( Request )
{
case SETSESSION_GET_STATUS:
*Response = 0;
break;
case SETSESSION_ADD_WORKQUEUE:
if ( pSession->SharedData->pQueue == NULL )
{
if ( CreateSubordinateQueue( pSession, &GlobalQueue ) )
{
//
// If they're going to be that busy, convert the cred list
// to be a large table
//
AddRundown( pSession, LsapDeleteWorkQueue, NULL );
pSession->SharedData->CredTable = LhtConvertSmallToLarge(
pSession->SharedData->CredTable );
pSession->SharedData->CredHandlePackage = &LargeHandlePackage ;
}
else
{
Status = STATUS_UNSUCCESSFUL ;
}
}
break;
case SETSESSION_REMOVE_WORKQUEUE:
break;
case SETSESSION_GET_DISPATCH:
if ( pSession->dwProcessID == GetCurrentProcessId() )
{
Status = InitializeDirectDispatcher();
if ( NT_SUCCESS( Status ) )
{
DllCallbackHandler = (PLSA_DISPATCH_FN) Argument ;
*Response = (ULONG_PTR) DispatchAPIDirect;
}
}
else
{
Status = STATUS_ACCESS_DENIED ;
}
break;
}
UnlockSession( pSession );
return( Status );
}