1412 lines
34 KiB
C++
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);
|
|
}
|
|
|
|
|
|
|
|
|