// -------------------------------------------------------------------------- // 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 #include "Access.h" #include "StatusCode.h" // -------------------------------------------------------------------------- // CAPIConnection::CAPIConnection // // Arguments: // // Returns: // // 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), _pAPIDispatchSync(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 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, 128, // Max connection info length (kernel accepts (MaxMessageLength - 68) maximum (188 here)), // used for client validation PORT_MAXIMUM_MESSAGE_LENGTH, // MaxMessageLength 16 * PORT_MAXIMUM_MESSAGE_LENGTH); // Release the security descriptor memory. ReleaseMemory(pSecurityDescriptor); if (!NT_SUCCESS(_status)) { pServerAPI->Release(); } } // -------------------------------------------------------------------------- // CAPIConnection::~CAPIConnection // // Arguments: // // Returns: // // 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: // // 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: // // Returns: NTSTATUS // // Purpose: Listens for server API connections and requests. // // History: 2000-11-28 vtan created // -------------------------------------------------------------------------- NTSTATUS CAPIConnection::Listen (CAPIDispatchSync* pAPIDispatchSync) { NTSTATUS status; // If a connection port was created then start listening. if (_hPort != NULL) { _pAPIDispatchSync = pAPIDispatchSync; do { (NTSTATUS)ListenToServerConnectionPort(); } while (!_fStopListening); status = STATUS_SUCCESS; } else { status = STATUS_OBJECT_NAME_NOT_FOUND; } return(status); } // -------------------------------------------------------------------------- // CAPIConnection::AddAccess // // Arguments: // // 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: // // 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: // // 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(&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(portMessage.GetData()); ulAPINumber = pAPI->ulAPINumber; if ((ulAPINumber & API_GENERIC_SPECIAL_MASK) != 0) { switch (pAPI->ulAPINumber & API_GENERIC_SPECIAL_MASK) { case API_GENERIC_STOPSERVER: { // Here, our job is to tear down the API port management infrastructure. // First, verify that we received this from ourselves, and not some // other random process. if (HandleToULong(portMessage.GetUniqueProcess()) == GetCurrentProcessId()) { status = STATUS_SUCCESS; // Cause our LPC port listening loop to exit. After this we're // no longer monitoring the port for new requests. _fStopListening = true; } else { status = STATUS_ACCESS_DENIED; } // Blow the message back to our caller. Even though this is // RejectRequest(), it'll cause the calling thread's NtRequestWaitReplyPort // to return. TSTATUS(pAPIDispatcher->RejectRequest(portMessage, status)); // Wait a reasonable about of time for any outstanding requests to // come home and be dequeued. if( CAPIDispatchSync::WaitForZeroDispatches(_pAPIDispatchSync, DISPATCHSYNC_TIMEOUT) != WAIT_TIMEOUT ) { int i, iLimit; // Now iterate all the CAPIDispatchers we know of and close them. // this will reject any further requests and not have clients // block in NtRequestWaitReplyPort. _dispatchers_lock.Acquire(); // protect vs. competing cleanup threads // (eg. Listen() --> HandleServerConnectionClosed() ) iLimit = _dispatchers.GetCount(); for (i = iLimit - 1; i >= 0; --i) { CAPIDispatcher *p; p = static_cast(_dispatchers.Get(i)); if (p != NULL) { p->CloseConnection(); p->Release(); } _dispatchers.Remove(i); } _dispatchers_lock.Release(); // Proceed w/ shutdown sequence CAPIDispatchSync::SignalPortShutdown(_pAPIDispatchSync); } else { _fStopListening = false; status = STATUS_TIMEOUT; } 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, _pAPIDispatchSync); } 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(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(); _dispatchers_lock.Acquire(); // protect vs. competing cleanup threads (eg. API_GENERIC_STOPSERVER). iIndex = FindIndexDispatcher(pAPIDispatcher); if (iIndex >= 0) { _dispatchers.Remove(iIndex); } _dispatchers_lock.Release(); } 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(_dispatchers.Get(i))) { iResult = i; } } return(iResult); }