|
|
// --------------------------------------------------------------------------
// Module Name: APIConnectionThread.cpp
//
// Copyright (c) 1999-2000, Microsoft Corporation
//
// A class that listens to an LPC connection port waiting for requests from
// a client to connect to the port or a request which references a previously
// established connection.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
#include "StandardHeader.h"
#include "APIConnection.h"
#include <lpcgeneric.h>
#include "Access.h"
#include "StatusCode.h"
// --------------------------------------------------------------------------
// CAPIConnection::CAPIConnection
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Constructor for CAPIConnectionThread. Store the CServerAPI
// function table. This describes how to react to LPC messages.
// This function also creates the server connection port.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// 2000-09-01 vtan use explicit security descriptor
// --------------------------------------------------------------------------
CAPIConnection::CAPIConnection (CServerAPI* pServerAPI) : _status(STATUS_NO_MEMORY), _fStopListening(false), _pServerAPI(pServerAPI), _hPort(NULL)
{ OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING portName; PSECURITY_DESCRIPTOR pSecurityDescriptor;
// Increment the reference on the interface.
pServerAPI->AddRef();
// Get the name from the interface.
RtlInitUnicodeString(&portName, pServerAPI->GetPortName());
// Build a security descriptor for the port that allows:
// S-1-5-18 NT AUTHORITY\SYSTEM PORT_ALL_ACCESS
// S-1-5-32-544 <local administrators> READ_CONTROL | PORT_CONNECT
static SID_IDENTIFIER_AUTHORITY s_SecurityNTAuthority = SECURITY_NT_AUTHORITY;
static const CSecurityDescriptor::ACCESS_CONTROL s_AccessControl[] = { { &s_SecurityNTAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, PORT_ALL_ACCESS }, { &s_SecurityNTAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, READ_CONTROL | PORT_CONNECT } };
// Build a security descriptor that allows the described access above.
pSecurityDescriptor = CSecurityDescriptor::Create(ARRAYSIZE(s_AccessControl), s_AccessControl);
// Initialize the object attributes.
InitializeObjectAttributes(&objectAttributes, &portName, 0, NULL, pSecurityDescriptor);
// Create the port.
_status = NtCreatePort(&_hPort, &objectAttributes, 0, PORT_MAXIMUM_MESSAGE_LENGTH, 16 * PORT_MAXIMUM_MESSAGE_LENGTH);
// Release the security descriptor memory.
ReleaseMemory(pSecurityDescriptor);
if (!NT_SUCCESS(_status)) { pServerAPI->Release(); } }
// --------------------------------------------------------------------------
// CAPIConnection::~CAPIConnection
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Destructor for CAPIConnectionThread. Close the port. Release
// the interface referrence.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
CAPIConnection::~CAPIConnection (void)
{ ReleaseHandle(_hPort); _pServerAPI->Release(); _pServerAPI = NULL; }
// --------------------------------------------------------------------------
// CAPIConnection::ConstructorStatusCode
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Returns the constructor status code back to the caller.
//
// History: 2000-10-18 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::ConstructorStatusCode (void) const
{ return(_status); }
// --------------------------------------------------------------------------
// CAPIConnection::Listen
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Listens for server API connections and requests.
//
// History: 2000-11-28 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::Listen (void)
{ NTSTATUS status;
// If a connection port was created then start listening.
if (_hPort != NULL) { do { (NTSTATUS)ListenToServerConnectionPort(); } while (!_fStopListening); status = STATUS_SUCCESS; } else { status = STATUS_OBJECT_NAME_NOT_FOUND; } return(status); }
// --------------------------------------------------------------------------
// CAPIConnection::AddAccess
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Adds access allowed to the ACL of the port.
//
// History: 2000-10-10 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::AddAccess (PSID pSID, DWORD dwMask)
{ CSecuredObject object(_hPort, SE_KERNEL_OBJECT);
return(object.Allow(pSID, dwMask, 0)); }
// --------------------------------------------------------------------------
// CAPIConnection::RemoveAccess
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Removes access allowed from the ACL of the port.
//
// History: 2000-10-10 vtan created
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::RemoveAccess (PSID pSID)
{ CSecuredObject object(_hPort, SE_KERNEL_OBJECT);
return(object.Remove(pSID)); }
// --------------------------------------------------------------------------
// CAPIConnection::ListenToServerConnectionPort
//
// Arguments: <none>
//
// Returns: NTSTATUS
//
// Purpose: Calls ntdll!NtReplyWaitReceivePort to listen to the LPC port
// for a message. Respond to the message. Messages understood are
// LPC_REQUEST / LPC_CONNECTION_REQUEST / LPC_PORT_CLOSED.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::ListenToServerConnectionPort (void)
{ NTSTATUS status; CAPIDispatcher *pAPIDispatcher; CPortMessage portMessage;
status = NtReplyWaitReceivePort(_hPort, reinterpret_cast<void**>(&pAPIDispatcher), NULL, portMessage.GetPortMessage()); if (NT_SUCCESS(status)) { switch (portMessage.GetType()) { case LPC_REQUEST: status = HandleServerRequest(portMessage, pAPIDispatcher); break; case LPC_CONNECTION_REQUEST: (NTSTATUS)HandleServerConnectionRequest(portMessage); break; case LPC_PORT_CLOSED: status = HandleServerConnectionClosed(portMessage, pAPIDispatcher); break; default: break; } TSTATUS(status); } return(status); }
// --------------------------------------------------------------------------
// CAPIConnection::HandleServerRequest
//
// Arguments: portMessage = CPortMessage containing the message.
// pAPIDispatcher = CAPIDispatcher to handle request.
//
// Returns: NTSTATUS
//
// Purpose: Queue the PORT_MESSAGE request to the handling dispatcher and
// wait for the next message. The queue operation will queue the
// request and either queue a work item if no work item is
// currently executing or just add it to the currently executing
// work item.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::HandleServerRequest (const CPortMessage& portMessage, CAPIDispatcher *pAPIDispatcher)
{ NTSTATUS status; unsigned long ulAPINumber; const API_GENERIC *pAPI;
pAPI = reinterpret_cast<const API_GENERIC*>(portMessage.GetData()); ulAPINumber = pAPI->ulAPINumber; if ((ulAPINumber & API_GENERIC_SPECIAL_MASK) != 0) { switch (pAPI->ulAPINumber & API_GENERIC_SPECIAL_MASK) { case API_GENERIC_STOPSERVER: { int i, iLimit;
if (HandleToULong(portMessage.GetUniqueProcess()) == GetCurrentProcessId()) { status = STATUS_SUCCESS; _fStopListening = true; } else { status = STATUS_ACCESS_DENIED; }
// Send the message back to the client. Even though this is
// RejectRequest it will send back the message and unblock
// the caller.
TSTATUS(pAPIDispatcher->RejectRequest(portMessage, status));
// Now iterate all the CAPIDispatchers we know of and close them
// this will reject any further requests and not have clients
// block in NtRequestWaitReplyPort.
iLimit = _dispatchers.GetCount(); for (i = iLimit - 1; i >= 0; --i) { CAPIDispatcher *pAPIDispatcher;
pAPIDispatcher = static_cast<CAPIDispatcher*>(_dispatchers.Get(i)); if (pAPIDispatcher != NULL) { pAPIDispatcher->CloseConnection(); pAPIDispatcher->Release(); } _dispatchers.Remove(i); }
break; } default: status = STATUS_NOT_IMPLEMENTED; DISPLAYMSG("Invalid API number special code passed to CAPIConnection::HandleServerRequest"); break; } } else if ((pAPI->ulAPINumber & API_GENERIC_OPTIONS_MASK) != 0) { switch (pAPI->ulAPINumber & API_GENERIC_OPTIONS_MASK) { case API_GENERIC_EXECUTE_IMMEDIATELY: status = pAPIDispatcher->ExecuteRequest(portMessage); break; default: status = STATUS_NOT_IMPLEMENTED; DISPLAYMSG("Invalid API number option passed to CAPIConnection::HandleServerRequest"); break; } } else { status = pAPIDispatcher->QueueRequest(portMessage); } return(status); }
// --------------------------------------------------------------------------
// CAPIConnection::HandleServerConnectionRequest
//
// Arguments: portMessage = CPortMessage containing the message.
//
// Returns: NTSTATUS
//
// Purpose: Ask the interface whether this connection should be accepted.
// If the connection is accepted then create the dispatcher that
// handles requests from this particular client. Either way
// inform the kernel that the request is either rejected or
// accepted. If the connection is accepted then complete the
// connection and give the dispatcher that will handle the
// requests the port to reply to.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::HandleServerConnectionRequest (const CPortMessage& portMessage)
{ NTSTATUS status; bool fConnectionAccepted; HANDLE hPort; CAPIDispatcher *pAPIDispatcher;
// Should the connection be accepted?
fConnectionAccepted = _pServerAPI->ConnectionAccepted(portMessage); if (fConnectionAccepted) {
// If so then create the dispatcher to handle this client.
pAPIDispatcher = _pServerAPI->CreateDispatcher(portMessage); if (pAPIDispatcher != NULL) {
// First try to add the CAPIDispatcher object to the static array.
// If this fails then reject the connection and release the memory.
if (!NT_SUCCESS(_dispatchers.Add(pAPIDispatcher))) { pAPIDispatcher->Release(); pAPIDispatcher = NULL; } } } else { pAPIDispatcher = NULL; }
// Without a CAPIDispatcher object reject the connection.
if (pAPIDispatcher == NULL) { fConnectionAccepted = false; }
// Tell the kernel what the result is.
status = NtAcceptConnectPort(&hPort, pAPIDispatcher, const_cast<PORT_MESSAGE*>(portMessage.GetPortMessage()), fConnectionAccepted, NULL, NULL); if (fConnectionAccepted) {
// If we tried to accept the connection but NtAcceptConnectPort
// couldn't allocate the port objects or something then we need
// to clean up the _dispatchers array CAPIDispatcher entry added.
if (NT_SUCCESS(status)) { pAPIDispatcher->SetPort(hPort);
// If the connection is accepted then complete the connection and set
// the reply port to the CAPIDispatcher that will process requests.
TSTATUS(NtCompleteConnectPort(hPort)); } else { int iIndex;
// Otherwise find the CAPIDispatcher that was added and remove it
// from the array. There's no need to wake the client up because
// NtAcceptConnectPort wakes it up in cases of failure.
iIndex = FindIndexDispatcher(pAPIDispatcher); if (iIndex >= 0) { TSTATUS(_dispatchers.Remove(iIndex)); } TSTATUS(pAPIDispatcher->CloseConnection()); pAPIDispatcher->Release(); } } return(status); }
// --------------------------------------------------------------------------
// CAPIConnection::HandleServerConnectionClosed
//
// Arguments: portMessage = CPortMessage containing the message.
// pAPIDispatcher = CAPIDispatcher to handle request.
//
// Returns: NTSTATUS
//
// Purpose: The port associated with the CAPIDispatcher client was
// closed. This is probably because the client process went away.
// Let the dispatcher clean itself up.
//
// History: 1999-11-07 vtan created
// 2000-08-25 vtan moved from Neptune to Whistler
// --------------------------------------------------------------------------
NTSTATUS CAPIConnection::HandleServerConnectionClosed (const CPortMessage& portMessage, CAPIDispatcher *pAPIDispatcher)
{ UNREFERENCED_PARAMETER(portMessage);
NTSTATUS status;
if (pAPIDispatcher != NULL) { int iIndex;
status = pAPIDispatcher->CloseConnection(); pAPIDispatcher->Release(); iIndex = FindIndexDispatcher(pAPIDispatcher); if (iIndex >= 0) { _dispatchers.Remove(iIndex); } } else { status = STATUS_SUCCESS; } return(status); }
// --------------------------------------------------------------------------
// CAPIConnection::FindIndexDispatcher
//
// Arguments: pAPIDispatcher = CAPIDispatcher to find.
//
// Returns: int
//
// Purpose: Finds the index in the dynamic counted object array of the
// dispatcher.
//
// History: 2000-12-02 vtan created
// --------------------------------------------------------------------------
int CAPIConnection::FindIndexDispatcher (CAPIDispatcher *pAPIDispatcher)
{ int i, iLimit, iResult;
iResult = -1; iLimit = _dispatchers.GetCount(); for (i = 0; (iResult < 0) && (i < iLimit); ++i) { if (pAPIDispatcher == static_cast<CAPIDispatcher*>(_dispatchers.Get(i))) { iResult = i; } } return(iResult); }
|