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

1412 lines
34 KiB
C++

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1991 - 1992
//
// File: KLPC.C
//
// Contents: LPC Support for the KSEC device driver
//
// Functions: CreateLpcPort
// AcceptConnection
// LPCServerThread
// HandleLPCError
// ShutdownServerThread
// StartLPCThread
// StopLPCThread
//
// History: 20 May 92 RichardW Created
//
//------------------------------------------------------------------------
#include <lsapch.hxx>
extern "C" {
#include "klpcstub.h"
}
// Convenient defines
#define MESSAGE_SIZE sizeof( SPM_LPC_MESSAGE )
// Module variables:
WCHAR szPortName[] = SPM_PORTNAME;
HANDLE hListenThread;
HANDLE hLpcPort; // This port ID is used for everything.
SECURITY_STATUS LsapTrapStatusCode ;
DSA_THSave * GetDsaThreadState ;
DSA_THRestore * RestoreDsaThreadState ;
PLSAP_API_LOG LpcApiLog ;
#define DBG_CONNECT ((ULONG) 0xFFFFFFFF)
#define DBG_DISCONNECT ((ULONG) 0xFFFFFFFE)
//
// Local Prototypes:
//
NTSTATUS CreateLpcPort(HANDLE *, PWSTR, DWORD, DWORD, DWORD);
DWORD LpcHandler(PVOID pMsg);
DWORD RundownConnection(PVOID pMessage);
DWORD
RundownConnectionNoFree(PVOID pMsg);
PLSAP_API_LOG
ApiLogCreate(
ULONG Entries
)
{
PLSAP_API_LOG ApiLog ;
ULONG Size ;
if ( Entries == 0 )
{
Entries = DEFAULT_LOG_SIZE;
}
DsysAssert( ((Entries & (Entries - 1) ) == 0 ) );
if ( Entries & (Entries - 1 ))
{
return NULL ;
}
Size = sizeof( LSAP_API_LOG ) + ( sizeof( LSAP_API_LOG_ENTRY ) * (Entries - 1) ) ;
ApiLog = (PLSAP_API_LOG) LsapAllocatePrivateHeap( Size );
if ( ApiLog )
{
ApiLog->TotalSize = Entries ;
ApiLog->ModSize = Entries - 1;
LsaIAddTouchAddress( ApiLog, Size );
}
return ApiLog ;
}
PLSAP_API_LOG_ENTRY
ApiLogAlloc(
PLSAP_API_LOG Log
)
{
ULONG WatchDog ;
PLSAP_API_LOG_ENTRY Entry = NULL ;
if ( !Log )
{
return NULL ;
}
WatchDog = Log->TotalSize * 2 ;
while ( ( Log->Entries[ Log->Current ].ThreadId != 0 ) &&
( Log->Entries[ Log->Current ].ThreadId != 0xFFFFFFFF ) &&
( WatchDog ) )
{
Log->Current++ ;
Log->Current &= Log->ModSize ;
WatchDog-- ;
}
if ( WatchDog )
{
Entry = & Log->Entries[ Log->Current ] ;
Entry->ThreadId = 0 ;
Log->Current ++ ;
Log->Current &= Log->ModSize;
}
return Entry ;
}
PLSAP_API_LOG_ENTRY
ApiLogLocate(
PLSAP_API_LOG Log,
ULONG MessageId
)
{
ULONG i ;
PLSAP_API_LOG_ENTRY Entry = NULL ;
for ( i = 0 ; i < Log->TotalSize ; i++ )
{
if ( Log->Entries[ i ].MessageId == MessageId )
{
Entry = &Log->Entries[ i ];
break;
}
}
return Entry ;
}
//+-------------------------------------------------------------------------
//
// Function: SetKsecEvent
//
// Synopsis: Triggers the event releasing the KSecDD
//
// Effects: Better be ready for LPC by when this call is executed
//
//--------------------------------------------------------------------------
NTSTATUS
SetKsecEvent(void)
{
HANDLE hEvent;
hEvent = SpmOpenEvent(EVENT_ALL_ACCESS,FALSE, SPM_EVENTNAME);
if (!hEvent)
{
DebugLog((DEB_WARN, "Could not open %ws, %d\n", SPM_EVENTNAME, GetLastError()));
return(STATUS_INVALID_HANDLE);
}
if (!SetEvent(hEvent))
{
DebugLog((DEB_ERROR, "Failed to set ksec event, %d\n", GetLastError()));
(void) CloseHandle(hEvent);
return(STATUS_INVALID_HANDLE);
}
(void) CloseHandle(hEvent);
return(STATUS_SUCCESS);
}
//+-------------------------------------------------------------------------
//
// Function: LsapBuildSD
//
// Synopsis: Shared code to build the SD for either the KsecEvent or
// the LPC port. For the KsecEvent, give everybody
// GENERIC_EXECUTE access. For the LPC port, give everybody
// access to call in on it.
//
// Effects: For KsecEvent, sets the security on the event.
// For LPC port, returns the SD as an OUT parameter
//
//--------------------------------------------------------------------------
NTSTATUS
LsapBuildSD(
IN ULONG dwType,
OUT PSECURITY_DESCRIPTOR *ppSD OPTIONAL
)
{
HANDLE hEvent = NULL;
NTSTATUS Status;
ULONG SDLength;
PACL pEventDacl = NULL;
PSECURITY_DESCRIPTOR pEventSD = NULL;
ULONG ulWorldAccess = 0;
ULONG ulAdminAccess = 0;
if (dwType == BUILD_KSEC_SD)
{
hEvent = SpmOpenEvent(EVENT_ALL_ACCESS,FALSE, SPM_EVENTNAME);
if (!hEvent)
{
DebugLog((DEB_WARN, "Could not open %ws, %d\n", SPM_EVENTNAME, GetLastError()));
return(STATUS_INVALID_HANDLE);
}
//
// The default DACL is the same as SePublicDefaultDacl in ntos\se
//
// World gets GENERIC_EXECUTE
// Admin gets GENERIC_READ, GENERIC_EXECUTE, READ_CONTROL
// System gets GENERIC_ALL
//
ulWorldAccess = GENERIC_EXECUTE | GENERIC_READ;
ulAdminAccess = GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
}
else
{
//
// ppSD is an OUT parameter for BUILD_LPC_SD
//
ASSERT(ppSD != NULL);
ulWorldAccess = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE;
ulAdminAccess = GENERIC_ALL;
}
SDLength = sizeof(SECURITY_DESCRIPTOR) +
(ULONG) sizeof(ACL) +
(3 * ((ULONG) sizeof(ACCESS_ALLOWED_ACE) - sizeof(ULONG))) +
RtlLengthSid( LsapLocalSystemSid ) +
RtlLengthSid( LsapAliasAdminsSid ) +
RtlLengthSid( LsapWorldSid );
pEventSD = (PSECURITY_DESCRIPTOR) LsapAllocateLsaHeap(SDLength);
if (pEventSD == NULL)
{
if (dwType == BUILD_KSEC_SD)
{
CloseHandle(hEvent);
}
return(STATUS_INSUFFICIENT_RESOURCES);
}
pEventDacl = (PACL) ((PBYTE) pEventSD + sizeof(SECURITY_DESCRIPTOR));
Status = RtlCreateAcl( pEventDacl,
SDLength - sizeof(SECURITY_DESCRIPTOR),
ACL_REVISION);
ASSERT( NT_SUCCESS(Status) );
//
// WORLD access
//
Status = RtlAddAccessAllowedAce (
pEventDacl,
ACL_REVISION,
ulWorldAccess,
LsapWorldSid
);
ASSERT( NT_SUCCESS(Status) );
//
// SYSTEM access
//
Status = RtlAddAccessAllowedAce (
pEventDacl,
ACL_REVISION,
GENERIC_ALL,
LsapLocalSystemSid
);
ASSERT( NT_SUCCESS(Status) );
//
// ADMINISTRATORS access
//
Status = RtlAddAccessAllowedAce (
pEventDacl,
ACL_REVISION,
ulAdminAccess,
LsapAliasAdminsSid
);
ASSERT( NT_SUCCESS(Status) );
//
// Now initialize security descriptors
// that export this protection
//
Status = RtlCreateSecurityDescriptor(
pEventSD,
SECURITY_DESCRIPTOR_REVISION1
);
ASSERT( NT_SUCCESS(Status) );
Status = RtlSetDaclSecurityDescriptor(
pEventSD,
TRUE, // DaclPresent
pEventDacl,
FALSE // DaclDefaulted
);
ASSERT( NT_SUCCESS(Status) );
if (dwType == BUILD_KSEC_SD)
{
Status = NtSetSecurityObject(
hEvent,
DACL_SECURITY_INFORMATION,
pEventSD
);
CloseHandle(hEvent);
LsapFreeLsaHeap(pEventSD);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to set event SD: 0x%x\n",Status));
}
}
else
{
ASSERT(hEvent == NULL);
if (NT_SUCCESS(Status))
{
*ppSD = pEventSD;
}
else
{
DebugLog((DEB_ERROR, "Failed to create LPC SD: 0x%x\n", Status));
LsapFreeLsaHeap(pEventSD);
}
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: CreateLpcPort
//
// Synopsis: Creates an LPC port and returns a handle to it.
//
// Effects:
//
// Arguments: phPort - receives port handle
// pszPortName - Unicode name of port
// cbConnect - Size of the connect message data
// cbMessage - Size of the messages
// cMessages - Max number of messages queued
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
CreateLpcPort( HANDLE * phPort,
PWSTR pszPortName,
DWORD cbConnect,
DWORD cbMessage,
DWORD cMessages)
{
NTSTATUS nsReturn;
OBJECT_ATTRIBUTES PortObjAttr;
UNICODE_STRING ucsPortName;
PSECURITY_DESCRIPTOR psdPort;
//
// Create a security descriptor for the port we are about to create
//
nsReturn = LsapBuildSD(BUILD_LPC_SD, &psdPort);
if (!NT_SUCCESS(nsReturn))
{
return nsReturn;
}
//
// Create the name
//
RtlInitUnicodeString(&ucsPortName, pszPortName);
InitializeObjectAttributes(&PortObjAttr, &ucsPortName, 0, NULL, psdPort);
//
// Create the port
nsReturn = NtCreatePort(phPort, // returned handle
&PortObjAttr, // name, etc.
cbConnect, // size of a connect msg
cbMessage, // size of a normal msg
cMessages * cbMessage // number of msgs to buffer
); // communication
LsapFreeLsaHeap(psdPort);
return nsReturn;
}
//+---------------------------------------------------------------------------
//
// Function: AcceptConnection
//
// Synopsis: Accepts a connection from a client.
//
// Effects:
//
// Arguments: [ConnectReq] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 7-22-93 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
AcceptConnection(
PVOID pvConnect
)
{
PSession pSession = NULL ;
HANDLE hCommPort;
PHANDLE phPort;
NTSTATUS scRet;
BOOLEAN bAccept = TRUE;
NTSTATUS Status;
PSPM_LPC_MESSAGE ConnectReq = (PSPM_LPC_MESSAGE) pvConnect;
PLSAP_AU_REGISTER_CONNECT_RESP Response;
PLSAP_TASK_QUEUE pQueue;
WCHAR LogonProcessName[LSAP_MAX_PACKAGE_NAME_LENGTH+1];
CHAR NarrowLogonName[ LSAP_MAX_PACKAGE_NAME_LENGTH + 1 ];
SECPKG_CLIENT_INFO ClientInfo;
LUID LogonId;
ULONG Flags = 0;
LSA_CALL_INFO CallInfo ;
DBG_DISPATCH_PROLOGUE( LpcApiLog, pvConnect, CallInfo );
scRet = LsapValidLogonProcess(
&ConnectReq->ConnectionRequest,
ConnectReq->pmMessage.u1.s1.DataLength,
&ConnectReq->pmMessage.ClientId,
&LogonId,
&Flags
);
if (NT_SUCCESS(scRet))
{
//
// Create a session to represent the client.
//
strncpy(
NarrowLogonName,
ConnectReq->ConnectionRequest.LogonProcessName,
LSAP_MAX_PACKAGE_NAME_LENGTH
);
NarrowLogonName[ LSAP_MAX_PACKAGE_NAME_LENGTH ] = '\0';
mbstowcs(
LogonProcessName,
NarrowLogonName,
LSAP_MAX_PACKAGE_NAME_LENGTH+1
);
scRet = CreateSession( &ConnectReq->pmMessage.ClientId,
TRUE,
LogonProcessName,
Flags,
&pSession);
}
if (!NT_SUCCESS(scRet))
{
bAccept = FALSE;
phPort = &hCommPort;
if ( pSession )
{
SpmpDereferenceSession( pSession );
pSession = NULL ;
}
}
else
{
phPort = &pSession->hPort;
//
// Fill in the complete connection info:
//
Response = (PLSAP_AU_REGISTER_CONNECT_RESP) &ConnectReq->ConnectionRequest;
if ( pSession->dwProcessID == pDefaultSession->dwProcessID )
{
//
// We're connecting to us. Set a flag:
//
Response->SecurityMode |= LSA_MODE_SAME_PROCESS ;
}
Response->CompletionStatus = STATUS_SUCCESS;
Response->PackageCount = SpmpCurrentPackageCount();
}
//
// Accept the connection
//
DebugLog((DEB_TRACE, "LpcListen: %sing connection from %x.%x\n",
(bAccept ? "Accept" : "Reject"),
ConnectReq->pmMessage.ClientId.UniqueProcess,
ConnectReq->pmMessage.ClientId.UniqueThread ));
Status = NtAcceptConnectPort(phPort, // Save the port handle
pSession, // Associate the session
(PPORT_MESSAGE) ConnectReq,
// Connection request to accept
bAccept, // Accept the connection
NULL, // Server view (none)
NULL // Client view (none)
);
if ( !NT_SUCCESS( Status ) )
{
//
// Failed to respond appropriately. If we had
// set things up for this session, tear them down
//
if ( NT_SUCCESS( scRet ) )
{
SpmpDereferenceSession( pSession );
pSession = NULL ;
goto Cleanup ;
}
}
if ((!NT_SUCCESS(scRet)) || (!bAccept))
{
if ( scRet == STATUS_INVALID_CID )
{
PPORT_MESSAGE Message = (PPORT_MESSAGE) ConnectReq ;
DbgPrint( "LSA: Failed to %s client [%x.%x, Message %x] because of invalid clientid\n",
( bAccept ? "accept" : "reject" ),
Message->ClientId.UniqueProcess,
Message->ClientId.UniqueThread,
Message->MessageId );
}
DebugLog((DEB_ERROR, "Failed to accept 0x%08x\n", scRet));
//
// Delete the session we just created:
//
if ( pSession )
{
SpmpDereferenceSession( pSession );
}
goto Cleanup;
}
//
// Must complete the session record *BEFORE* calling CompleteConnectPort,
// since as soon as that happens, the other guy could send another message
// and we might hit an assert.
//
if (bAccept)
{
Status = NtCompleteConnectPort(pSession->hPort);
}
Cleanup:
DBG_DISPATCH_POSTLOGUE( (NT_SUCCESS(Status) ? ULongToPtr(scRet) : ULongToPtr(Status)),
LongToPtr(DBG_CONNECT) );
LsapFreePrivateHeap( ConnectReq );
return( 0 );
}
//+---------------------------------------------------------------------------
//
// Function: LpcServerThread
//
// Synopsis: Handles all requests from clients
//
// Arguments: [pvIgnored] --
//
// History: 7-23-93 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
ULONG
LpcServerThread(PVOID pvIgnored)
{
PSession pSession;
PSession pMySession = GetCurrentSession();
NTSTATUS scRet;
PSPM_LPC_MESSAGE pMessage;
CSHORT sMessageType;
NTSTATUS Status;
UCHAR PanicBuffer[sizeof(SPM_LPC_MESSAGE)];
BOOLEAN OutOfMemory;
HANDLE hDummy;
PVOID TaskPointer ;
LPTHREAD_START_ROUTINE TaskFunction ;
BOOL ScheduleUrgent ;
BOOL ExecNow ;
ULONG ErrorCount ;
#if DBG_TRACK_API
PLSAP_API_LOG_ENTRY Entry ;
#endif
//
// First, create the port:
//
DebugLog((DEB_TRACE_INIT, "LpcServerThread starting up, creating port\n"));
scRet = CreateLpcPort( &hLpcPort, // Handle that stores the port
szPortName, // Name of the port.
sizeof(SPMConnect), // Size of a connect message
MESSAGE_SIZE, // Size of a request message
16); // Number of messages to queue
if (FAILED(scRet))
{
DebugLog((DEB_ERROR, "CreateLpcPort returned 0x%08x\n", scRet));
return((ULONG) scRet);
}
DebugLog((DEB_TRACE, "LPCServerThread started on port %ws\n", szPortName));
DebugLog((DEB_TRACE_INIT, "LpcServerThread starting up: setting event\n"));
//
// Trigger the KSec event that will cause the device driver to allow
// connections
//
scRet = SetKsecEvent();
#if DBG
if (FAILED(scRet))
{
DebugLog((DEB_ERROR, "Error setting event, %x\n", scRet));
}
#endif
#if DBG_TRACK_API
LpcApiLog = ApiLogCreate( 0 );
if ( !LpcApiLog )
{
NtClose( hLpcPort );
return STATUS_NO_MEMORY ;
}
#endif
//
// All we do is wait here:
//
for (; ; )
{
//
// Allocate memory for the message
//
pMessage = (PSPM_LPC_MESSAGE) LsapAllocatePrivateHeap(
sizeof( SPM_LPC_MESSAGE ) );
if (pMessage)
{
OutOfMemory = FALSE;
}
else
{
OutOfMemory = TRUE;
pMessage = (PSPM_LPC_MESSAGE) PanicBuffer;
}
//
// Wait for a message from one of the critters
//
pSession = NULL;
ExecNow = FALSE ;
Status = NtReplyWaitReceivePort(hLpcPort, // Port
(void **)&pSession, // Get session
NULL, // No reply
(PPORT_MESSAGE) pMessage); // Recvd msg
if ( !NT_SUCCESS( Status ) )
{
DebugLog(( DEB_ERROR, "LpcServer: ReplyWaitReceive returned %x\n",
Status ));
if ( !OutOfMemory )
{
LsapFreePrivateHeap( pMessage );
}
continue;
}
DebugLog((DEB_TRACE_WAPI, "LpcServer: Received msg from %x.%x\n",
pMessage->pmMessage.ClientId.UniqueProcess,
pMessage->pmMessage.ClientId.UniqueThread));
if (pSession)
{
DsysAssert(pSession->hPort);
}
else
{
DsysAssert(pMessage->pmMessage.u2.s2.Type == LPC_CONNECTION_REQUEST);
}
if (OutOfMemory)
{
//
// Generate a fail
//
DebugLog((DEB_ERROR, "KLPC: out of memory, failing request %x\n",
pMessage->pmMessage.MessageId));
if (pMessage->pmMessage.u2.s2.Type == LPC_CONNECTION_REQUEST)
{
Status = NtAcceptConnectPort(
&hDummy,
NULL,
(PPORT_MESSAGE) pMessage,
FALSE,
NULL,
NULL);
}
else if (pMessage->pmMessage.u2.s2.Type == LPC_REQUEST)
{
pMessage->ApiMessage.Args.SpmArguments.fAPI |= SPMAPI_FLAG_ERROR_RET;
pMessage->ApiMessage.scRet = STATUS_INSUFFICIENT_RESOURCES;
Status = NtReplyPort(pSession->hPort,
(PPORT_MESSAGE) pMessage);
}
else if (pMessage->pmMessage.u2.s2.Type == LPC_PORT_CLOSED)
{
SetCurrentSession( pSession );
RundownConnectionNoFree( pMessage );
SetCurrentSession( pMySession );
}
else
{
DebugLog((DEB_ERROR, "Unknown Message received, punting\n"));
}
continue;
}
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "Discarding message, %x\n", scRet));
LsapFreePrivateHeap( pMessage );
continue;
}
//
// Check message for LPC errors
//
#if DBG_TRACK_API
Entry = ApiLogAlloc( LpcApiLog );
if ( Entry )
{
Entry->MessageId = pMessage->pmMessage.MessageId ;
Entry->pvMessage = pMessage ;
GetSystemTimeAsFileTime( (LPFILETIME) &Entry->QueueTime );
}
#endif
sMessageType = pMessage->pmMessage.u2.s2.Type;
switch(sMessageType & ~LPC_KERNELMODE_MESSAGE)
{
case LPC_REQUEST:
case LPC_REPLY:
case LPC_DATAGRAM:
//
// "Normal" API requests. Route to the standard
// handler, non urgent:
//
TaskFunction = LpcHandler ;
ScheduleUrgent = FALSE ;
if ((pMessage->ApiMessage.dwAPI > LsapAuMaxApiNumber) &&
(pMessage->ApiMessage.dwAPI < SPMAPI_MaxApiNumber) &&
(pMessage->ApiMessage.Args.SpmArguments.fAPI & SPMAPI_FLAG_EXEC_NOW) )
{
ExecNow = TRUE ;
}
break;
case LPC_CONNECTION_REQUEST:
//
// New connection request. Handle with some priority
//
TaskFunction = AcceptConnection ;
ScheduleUrgent = TRUE ;
pSession = pMySession ;
break;
case LPC_PORT_CLOSED:
//
// Client has gone away. Make sure we clean up
//
TaskFunction = RundownConnection ;
ScheduleUrgent = FALSE ;
DebugLog((DEB_TRACE, "Client %d.%d died, running down session\n",
pMessage->pmMessage.ClientId.UniqueProcess,
pMessage->pmMessage.ClientId.UniqueThread));
break ;
case LPC_LOST_REPLY:
case LPC_CLIENT_DIED:
case LPC_EXCEPTION:
case LPC_DEBUG_EVENT:
case LPC_ERROR_EVENT:
default:
//
// These are debugger messages, so we should never see them.
//
DebugLog((DEB_WARN,"Discarding message type %d\n",sMessageType));
LsapFreePrivateHeap( pMessage );
continue;
}
//
// If the message has the EXEC_NOW flag on, that means that the caller
// deemed this urgent, and not to be spawned to another thread.
//
if ( ExecNow )
{
TlsSetValue(dwSession, pSession);
LpcHandler(pMessage);
TlsSetValue(dwSession, pMySession);
continue;
}
//
// Assign a thread to handle the request, and
// then loop back and wait again.
//
TaskPointer = LsapAssignThread(
TaskFunction,
pMessage,
pSession,
ScheduleUrgent != 0);
if ( !TaskPointer )
{
//
// Generate a fail
//
DebugLog((DEB_ERROR, "KLPC: out of memory, failing request %x\n",
pMessage->pmMessage.MessageId));
pMessage->ApiMessage.Args.SpmArguments.fAPI |= SPMAPI_FLAG_ERROR_RET;
pMessage->ApiMessage.scRet = STATUS_INSUFFICIENT_RESOURCES;
Status = NtReplyPort(pSession->hPort,
(PPORT_MESSAGE) pMessage);
DBG_DISPATCH_POSTLOGUE( ULongToPtr( STATUS_INSUFFICIENT_RESOURCES ), 0 );
LsapFreePrivateHeap( pMessage );
}
#if DBG_TRACK_API
if ( Entry )
{
Entry->WorkItem = TaskPointer ;
}
#endif
}
return((ULONG) scRet);
}
//+---------------------------------------------------------------------------
//
// Function: LpcHandler
//
// Synopsis: Generic threadpool function called to handle an LPC request
//
// Arguments: [pMsg] -- Message to process
//
// History: 7-23-93 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
LpcHandler(
PVOID pMsg
)
{
PSPM_LPC_MESSAGE pApi = (PSPM_LPC_MESSAGE) pMsg ;
PSession pSession = (PSession) TlsGetValue(dwSession);
NTSTATUS Status = STATUS_SUCCESS;
DWORD i;
LSA_CALL_INFO CallInfo ;
PULONG_PTR Where ;
BOOL BreakOnCall = FALSE;
ZeroMemory( &CallInfo, sizeof(CallInfo) );
DBG_DISPATCH_PROLOGUE( LpcApiLog, pApi, CallInfo );
DsysAssert( pSession != pDefaultSession );
//
// Verify that if the caller claimed to be from Kernel mode
// that they still are. If the session is still indefinite,
// fix that up now:
//
if ( ( pSession->fSession & SESFLAG_MAYBEKERNEL ) != 0 )
{
if ( ( pApi->pmMessage.u2.s2.Type & LPC_KERNELMODE_MESSAGE ) != 0 )
{
pSession->fSession &= ~(SESFLAG_MAYBEKERNEL | SESFLAG_WOW_PROCESS) ;
pSession->fSession |= SESFLAG_KERNEL ;
if ( pEfsSession )
{
if ( (pEfsSession->fSession & SESFLAG_EFS) == 0 )
{
LsapUpdateEfsSession( pSession );
}
}
}
else
{
//
// This was a very bad caller. They set the flag that it
// was going to be a kernel mode session, but then they turned
// out not to be in kernel mode. Kill this session
//
LockSession( pSession );
if ( pSession->hPort )
{
NtClose( pSession->hPort );
pSession->hPort = NULL ;
}
UnlockSession( pSession );
goto Cleanup;
}
}
if ((pApi->ApiMessage.dwAPI > LsapAuMaxApiNumber) &&
((pSession->fSession & SESFLAG_KERNEL) != 0))
{
if ((pApi->pmMessage.u2.s2.Type & LPC_KERNELMODE_MESSAGE) == 0)
{
DebugLog((DEB_ERROR,"Caller claimed to be from kernelmode but sent non-kernelmode message\n"));
pApi->ApiMessage.scRet = STATUS_ACCESS_DENIED ;
Status = STATUS_ACCESS_DENIED;
}
}
CallInfo.Message = pApi ;
CallInfo.CallInfo.ProcessId = HandleToUlong(pApi->pmMessage.ClientId.UniqueProcess);
CallInfo.CallInfo.ThreadId = HandleToUlong(pApi->pmMessage.ClientId.UniqueThread);
CallInfo.CallInfo.Attributes = 0 ;
CallInfo.InProcCall = FALSE ;
CallInfo.Session = pSession ;
if (((pSession->fSession & SESFLAG_TCB_PRIV) != 0) ||
((pSession->fSession & SESFLAG_KERNEL) != 0))
{
CallInfo.CallInfo.Attributes |= SECPKG_CALL_IS_TCB ;
}
if ( pApi->ApiMessage.Args.SpmArguments.fAPI & SPMAPI_FLAG_ANSI_CALL)
{
CallInfo.CallInfo.Attributes |= SECPKG_CALL_ANSI ;
}
if ( pApi->pmMessage.u2.s2.Type & LPC_KERNELMODE_MESSAGE )
{
CallInfo.CallInfo.Attributes |= SECPKG_CALL_KERNEL_MODE ;
if ( pApi->ApiMessage.Args.SpmArguments.fAPI & SPMAPI_FLAG_KMAP_MEM )
{
CallInfo.Flags |= CALL_FLAG_KERNEL_POOL ;
CallInfo.KMap = (PKSEC_LSA_MEMORY_HEADER) pApi->ApiMessage.Args.SpmArguments.ContextPointer;
}
}
//
// If the kernel driver has set the error-ret flag, then we have
// been asked to break in by the driver. If we're allowed to take
// breakpoints (checked later), we'll break in. For now, set the flag
// that we should check:
//
if ( pApi->ApiMessage.Args.SpmArguments.fAPI & SPMAPI_FLAG_ERROR_RET )
{
if ( CallInfo.CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE )
{
BreakOnCall = TRUE ;
}
}
if ( pSession->fSession & SESFLAG_WOW_PROCESS )
{
CallInfo.CallInfo.Attributes |= SECPKG_CALL_WOWCLIENT ;
}
CallInfo.Allocs = 0 ;
if (NT_SUCCESS(Status))
{
DebugLog((DEB_TRACE_WAPI, "[%x.%x] Dispatching API (Message %x)\n",
pApi->pmMessage.ClientId.UniqueProcess,
pApi->pmMessage.ClientId.UniqueThread,
pApi->pmMessage.MessageId));
LsapSetCurrentCall( &CallInfo );
//
// Call the dispatcher, and have the request routed to the security package
//
#ifdef PERF
PerfApiCount[pApi->ApiMessage.dwAPI]++;
#endif
DsysAssert( pSession->hPort );
//
// If we need a breakpoint, this will do it. Note that this
// will return immediately if we weren't started under a debugger.
//
if ( BreakOnCall )
{
LsapInternalBreak();
}
Status = DispatchAPI( pApi );
LsapSetCurrentCall( NULL );
#if DBG
if ( ( LsapTrapStatusCode != 0 ) )
{
DsysAssert( LsapTrapStatusCode != pApi->ApiMessage.scRet );
}
#endif
}
//
// Done. Send the message back to the caller, and return to the
// thread pool.
//
if ( ( pApi->ApiMessage.dwAPI > LsapAuMaxApiNumber ) &&
( pApi->ApiMessage.Args.SpmArguments.fAPI & SPMAPI_FLAG_ALLOCS ) )
{
if ( CallInfo.Allocs )
{
Where = (PULONG_PTR) pApi->ApiMessage.bData ;
*Where++ = CallInfo.Allocs ;
for ( i = 0 ; i < CallInfo.Allocs ; i++ )
{
*Where++ = (ULONG_PTR) CallInfo.Buffers[ i ];
}
}
else
{
pApi->ApiMessage.Args.SpmArguments.fAPI &= ~(SPMAPI_FLAG_ALLOCS) ;
}
}
DsysAssert(pSession->hPort);
do
{
Status = NtReplyPort( pSession->hPort,
(PPORT_MESSAGE) pApi);
if ( ! NT_SUCCESS( Status ) )
{
if (Status == STATUS_NO_MEMORY)
{
Sleep(125); // Sleep for an eighth of a second, and retry
continue;
}
if (Status == STATUS_INVALID_CID)
{
//
// Already received the CLIENT_DIED and has been run down,
// and the session has been deref'd, so when we go, it will
// be closed completely.
//
break ;
}
//
// All other errors, until we have something more sensible to
// do,
//
break;
}
} while ( !NT_SUCCESS(Status) );
Cleanup:
DBG_DISPATCH_POSTLOGUE(
(NT_SUCCESS( Status ) ? ULongToPtr(pApi->ApiMessage.scRet) : ULongToPtr(Status)),
LongToPtr(pApi->ApiMessage.dwAPI) );
LsapFreePrivateHeap( pApi );
//
// We're out of here.
//
return(0);
}
//+---------------------------------------------------------------------------
//
// Function: RundownConnection
//
// Synopsis: Handles running down a closed connection
//
// Arguments: [pMsg] -- Message
//
// History: 4-01-94 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
RundownConnectionNoFree(PVOID pMsg)
{
NTSTATUS scRet;
PSession pSession;
LSA_CALL_INFO CallInfo ;
DBG_DISPATCH_PROLOGUE( LpcApiLog, pMsg, CallInfo );
pSession = GetCurrentSession();
DebugLog((DEB_TRACE, "[%x] Process Detach\n", pSession->dwProcessID));
//
// Call the session manager to do preliminary cleanup:
//
LsapSessionDisconnect( pSession );
//
// Deref the session. Note that a client may have died while we were
// processing one or more requests in other threads. So, this is a
// safe (possibly deferred) dereference operation.
//
SpmpDereferenceSession(pSession);
//
// Use the default, spmgr session.
//
TlsSetValue(dwSession, pDefaultSession);
//
// Clean up and we're out of here...
//
DBG_DISPATCH_POSTLOGUE( ULongToPtr(STATUS_SUCCESS), LongToPtr(DBG_DISCONNECT) );
return(0);
}
DWORD
RundownConnection(
PVOID pMessage
)
{
RundownConnectionNoFree( pMessage );
LsapFreePrivateHeap( pMessage );
return 0 ;
}
//+---------------------------------------------------------------------------
//
// Function: CatchLpcDeath
//
// Synopsis: This function is invoked when the LPC thread dies
//
// Arguments: [PVOID] --
//
// History: 9-13-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD
CatchLpcDeath(
PVOID pvIgnored)
{
DsysAssertMsg(FALSE, "LPC Thread died");
return(0);
}
//+---------------------------------------------------------------------------
//
// Function: StartLpcThread
//
// Synopsis: Initializes the LPC server.
//
// Arguments: (none)
//
// History: 7-23-93 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
NTSTATUS
StartLpcThread(void)
{
DWORD tid;
hListenThread = LsapCreateThread(
NULL,
0,
LpcServerThread,
0,
0,
&tid
);
if (!hListenThread)
{
return(STATUS_UNSUCCESSFUL);
}
LsaIRegisterNotification(
CatchLpcDeath,
NULL,
NOTIFIER_TYPE_HANDLE_WAIT,
0,
NOTIFIER_FLAG_ONE_SHOT,
0,
hListenThread
);
return(STATUS_SUCCESS);
}