//+----------------------------------------------------------------------- // // 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 extern "C" { #include "klpcstub.h" } // // 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; } 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; WCHAR LogonProcessName[LSAP_MAX_PACKAGE_NAME_LENGTH+1]; CHAR NarrowLogonName[ LSAP_MAX_PACKAGE_NAME_LENGTH + 1 ]; 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 { PLSAP_AU_REGISTER_CONNECT_RESP Response; 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 ; DebugLog((DEB_ERROR, "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 ; #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(SPM_LPC_MESSAGE), // Size of a connect message sizeof(SPM_LPC_MESSAGE), // 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; while (TRUE) { Status = NtReplyPort(pSession->hPort, (PPORT_MESSAGE) pMessage); if (Status == STATUS_INSUFFICIENT_RESOURCES || Status == STATUS_NO_MEMORY) { Sleep(125); } else { break; } } } 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)); // // For connection requests, there is no reply. In fact, using Reply // will cause lots of problems since the client will be stalled waiting // for the reply. // if ( sMessageType == LPC_CONNECTION_REQUEST ) { Status = NtAcceptConnectPort( &hDummy, NULL, (PPORT_MESSAGE) pMessage, FALSE, NULL, NULL); DBG_DISPATCH_POSTLOGUE( UlongToPtr( STATUS_INSUFFICIENT_RESOURCES ), LongToPtr(DBG_CONNECT) ); } else { pMessage->ApiMessage.Args.SpmArguments.fAPI |= SPMAPI_FLAG_ERROR_RET; pMessage->ApiMessage.scRet = STATUS_INSUFFICIENT_RESOURCES; while (TRUE) { Status = NtReplyPort(pSession->hPort, (PPORT_MESSAGE) pMessage); if (Status == STATUS_INSUFFICIENT_RESOURCES || Status == STATUS_NO_MEMORY) { Sleep(125); } else { break; } } DBG_DISPATCH_POSTLOGUE( ULongToPtr( STATUS_INSUFFICIENT_RESOURCES ), pMessage->ApiMessage.dwAPI ); } 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; BOOL IsCallInfoSet = 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 ; // // Only copy out the IP address from "new" clients (vs. clients compiled using a version // of lsadll.lib from a previous OS release that didn't have this field). // if ( pApi->ApiMessage.dwAPI == LsapAuLogonUserApi && pApi->pmMessage.u1.s1.DataLength == LSAP_AU_DATA_LENGTH(sizeof(LSAP_LOGON_USER_ARGS))) { RtlCopyMemory(CallInfo.IpAddress, pApi->ApiMessage.Args.LsaArguments.LogonUser.IpAddress, LSAP_ADDRESS_LENGTH); } else if ( pApi->ApiMessage.dwAPI == SPMAPI_AcceptContext ) { RtlCopyMemory(CallInfo.IpAddress, pApi->ApiMessage.Args.SpmArguments.API.AcceptContext.IpAddress, LSAP_ADDRESS_LENGTH); } 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 ); IsCallInfoSet = TRUE; // // Call the dispatcher, and have the request routed to the security package // 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 ); #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 ) { DsysAssert( CallInfo.Allocs <= MAX_BUFFERS_IN_CALL ); 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, // LsapLpcContextCleanup(pApi); break; } } while ( !NT_SUCCESS(Status) ); Cleanup: if (IsCallInfoSet) { LsapSetCurrentCall( NULL ); } 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); }