From 57b7689b2dc5e971911d1c6d48eebb5424ebca89 Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Wed, 10 Jan 2024 23:48:40 -0800 Subject: Rename inter-Burn communication related pipe file When BootstrapperApplications move out of proc there will be another set of pipes in Burn that behave differently from the inter-Burn communication pipes that exist now. So renaming the existing pipe related files now to better reflect that it is related to Burn talking to Burn when elevated or embedded. --- src/burn/engine/burnpipe.cpp | 782 +++++++++++++++++++++++++++++++++++ src/burn/engine/burnpipe.h | 104 +++++ src/burn/engine/engine.vcxproj | 4 +- src/burn/engine/pipe.cpp | 782 ----------------------------------- src/burn/engine/pipe.h | 104 ----- src/burn/engine/precomp.h | 2 +- src/burn/test/BurnUnitTest/precomp.h | 2 +- 7 files changed, 890 insertions(+), 890 deletions(-) create mode 100644 src/burn/engine/burnpipe.cpp create mode 100644 src/burn/engine/burnpipe.h delete mode 100644 src/burn/engine/pipe.cpp delete mode 100644 src/burn/engine/pipe.h diff --git a/src/burn/engine/burnpipe.cpp b/src/burn/engine/burnpipe.cpp new file mode 100644 index 00000000..5003622c --- /dev/null +++ b/src/burn/engine/burnpipe.cpp @@ -0,0 +1,782 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +#include "precomp.h" + +static const DWORD PIPE_64KB = 64 * 1024; +static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, +static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. + +static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; +static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; +static const LPCWSTR LOGGING_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Log"; + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* pcbMessage + ); +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ); +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData + ); +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ); +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ); + + + +/******************************************************************* + PipeConnectionInitialize - initialize pipe connection data. + +*******************************************************************/ +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); + pConnection->hPipe = INVALID_HANDLE_VALUE; + pConnection->hCachePipe = INVALID_HANDLE_VALUE; + pConnection->hLoggingPipe = INVALID_HANDLE_VALUE; +} + +/******************************************************************* + PipeConnectionUninitialize - free data in a pipe connection. + +*******************************************************************/ +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + ReleaseFileHandle(pConnection->hLoggingPipe); + ReleaseFileHandle(pConnection->hCachePipe); + ReleaseFileHandle(pConnection->hPipe); + ReleaseHandle(pConnection->hProcess); + ReleaseStr(pConnection->sczSecret); + ReleaseStr(pConnection->sczName); + + PipeConnectionInitialize(pConnection); +} + +/******************************************************************* + PipeSendMessage - + +*******************************************************************/ +extern "C" HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_RESULT result = { }; + + hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); + ExitOnFailure(hr, "Failed to write send message to pipe."); + + hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); + ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); + + *pdwResult = result.dwResult; + +LExit: + return hr; +} + +/******************************************************************* + PipePumpMessages - + +*******************************************************************/ +extern "C" HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ) +{ + HRESULT hr = S_OK; + BURN_PIPE_MESSAGE msg = { }; + SIZE_T iData = 0; + LPSTR sczMessage = NULL; + DWORD dwResult = 0; + + // Pump messages from child process. + while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) + { + switch (msg.dwMessage) + { + case BURN_PIPE_MESSAGE_TYPE_LOG: + iData = 0; + + hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); + ExitOnFailure(hr, "Failed to read log message."); + + hr = LogStringWorkRaw(sczMessage); + ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); + + dwResult = static_cast(hr); + break; + + case BURN_PIPE_MESSAGE_TYPE_COMPLETE: + if (!msg.pvData || sizeof(DWORD) != msg.cbData) + { + hr = E_INVALIDARG; + ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); + } + + pResult->dwResult = *static_cast(msg.pvData); + ExitFunction1(hr = S_OK); // exit loop. + + case BURN_PIPE_MESSAGE_TYPE_TERMINATE: + iData = 0; + + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, &pResult->dwResult); + ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); + + if (sizeof(DWORD) * 2 == msg.cbData) + { + hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); + ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); + } + + ExitFunction1(hr = S_OK); // exit loop. + + default: + if (pfnCallback) + { + hr = pfnCallback(&msg, pvContext, &dwResult); + } + else + { + hr = E_INVALIDARG; + } + ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); + break; + } + + // post result + hr = WritePipeMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); + ExitOnFailure(hr, "Failed to post result to child process."); + + FreePipeMessage(&msg); + } + ExitOnFailure(hr, "Failed to get message over pipe"); + + if (S_FALSE == hr) + { + hr = S_OK; + } + +LExit: + ReleaseStr(sczMessage); + FreePipeMessage(&msg); + + return hr; +} + +/******************************************************************* + PipeCreateNameAndSecret - + +*******************************************************************/ +extern "C" HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ) +{ + HRESULT hr = S_OK; + WCHAR wzGuid[GUID_STRING_LENGTH]; + LPWSTR sczConnectionName = NULL; + LPWSTR sczSecret = NULL; + + // Create the unique pipe name. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe guid."); + + hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); + ExitOnFailure(hr, "Failed to allocate pipe name."); + + // Create the unique client secret. + hr = GuidFixedCreate(wzGuid); + ExitOnRootFailure(hr, "Failed to create pipe secret."); + + hr = StrAllocString(&sczSecret, wzGuid, 0); + ExitOnFailure(hr, "Failed to allocate pipe secret."); + + *psczConnectionName = sczConnectionName; + sczConnectionName = NULL; + *psczSecret = sczSecret; + sczSecret = NULL; + +LExit: + ReleaseStr(sczSecret); + ReleaseStr(sczConnectionName); + + return hr; +} + +/******************************************************************* + PipeCreatePipes - create the pipes and event to signal child process. + +*******************************************************************/ +extern "C" HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCompanion + ) +{ + Assert(pConnection->sczName); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); + + HRESULT hr = S_OK; + PSECURITY_DESCRIPTOR psd = NULL; + SECURITY_ATTRIBUTES sa = { }; + LPWSTR sczFullPipeName = NULL; + HANDLE hPipe = INVALID_HANDLE_VALUE; + HANDLE hCachePipe = INVALID_HANDLE_VALUE; + HANDLE hLoggingPipe = INVALID_HANDLE_VALUE; + + // Only grant special rights when the pipe is being used for "embedded" scenarios. + if (!fCompanion) + { + // Create the security descriptor that grants read/write/sync access to Everyone. + // TODO: consider locking down "WD" to LogonIds (logon session) + LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; + if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) + { + ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); + } + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = psd; + sa.bInheritHandle = FALSE; + } + + // Create the pipe. + hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); + + // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. + hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); + if (INVALID_HANDLE_VALUE == hPipe) + { + ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); + } + + if (fCompanion) + { + // Create the cache pipe. + hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); + + hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); + if (INVALID_HANDLE_VALUE == hCachePipe) + { + ExitWithLastError(hr, "Failed to create cache pipe: %ls", sczFullPipeName); + } + + // Create the logging pipe. + hr = StrAllocFormatted(&sczFullPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate full name of logging pipe: %ls", pConnection->sczName); + + hLoggingPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); + if (INVALID_HANDLE_VALUE == hLoggingPipe) + { + ExitWithLastError(hr, "Failed to create logging pipe: %ls", sczFullPipeName); + } + } + + pConnection->hLoggingPipe = hLoggingPipe; + hLoggingPipe = INVALID_HANDLE_VALUE; + + pConnection->hCachePipe = hCachePipe; + hCachePipe = INVALID_HANDLE_VALUE; + + pConnection->hPipe = hPipe; + hPipe = INVALID_HANDLE_VALUE; + +LExit: + ReleaseFileHandle(hLoggingPipe); + ReleaseFileHandle(hCachePipe); + ReleaseFileHandle(hPipe); + ReleaseStr(sczFullPipeName); + + if (psd) + { + ::LocalFree(psd); + } + + return hr; +} + +/******************************************************************* + PipeWaitForChildConnect - + +*******************************************************************/ +extern "C" HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ) +{ + HRESULT hr = S_OK; + HANDLE hPipes[3] = { pConnection->hPipe, pConnection->hCachePipe, pConnection->hLoggingPipe}; + LPCWSTR wzSecret = pConnection->sczSecret; + DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); + DWORD dwCurrentProcessId = ::GetCurrentProcessId(); + DWORD dwAck = 0; + + for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) + { + HANDLE hPipe = hPipes[i]; + DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; + + // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever + // if the child decides not to show up. + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to set pipe to non-blocking."); + } + + // Loop for a while waiting for a connection from child process. + DWORD cRetry = 0; + do + { + if (!::ConnectNamedPipe(hPipe, NULL)) + { + DWORD er = ::GetLastError(); + if (ERROR_PIPE_CONNECTED == er) + { + hr = S_OK; + break; + } + else if (ERROR_PIPE_LISTENING == er) + { + if (cRetry < PIPE_RETRY_FOR_CONNECTION) + { + hr = HRESULT_FROM_WIN32(er); + } + else + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + break; + } + + ++cRetry; + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else + { + hr = HRESULT_FROM_WIN32(er); + break; + } + } + } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); + ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); + + // Put the pipe back in blocking mode. + dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; + if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) + { + ExitWithLastError(hr, "Failed to reset pipe to blocking."); + } + + // Prove we are the one that created the elevated process by passing the secret. + hr = FileWriteHandle(hPipe, reinterpret_cast(&cbSecret), sizeof(cbSecret)); + ExitOnFailure(hr, "Failed to write secret length to pipe."); + + hr = FileWriteHandle(hPipe, reinterpret_cast(wzSecret), cbSecret); + ExitOnFailure(hr, "Failed to write secret to pipe."); + + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); + ExitOnFailure(hr, "Failed to write our process id to pipe."); + + // Wait until the elevated process responds that it is ready to go. + hr = FileReadHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to read ACK from pipe."); + + // The ACK should match out expected child process id. + //if (pConnection->dwProcessId != dwAck) + //{ + // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); + //} + } + +LExit: + return hr; +} + +/******************************************************************* + PipeTerminateLoggingPipe - + +*******************************************************************/ +extern "C" HRESULT PipeTerminateLoggingPipe( + __in HANDLE hLoggingPipe, + __in DWORD dwParentExitCode + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + + // Prepare the exit message. + hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); + ExitOnFailure(hr, "Failed to write exit code to message buffer."); + + hr = WritePipeMessage(hLoggingPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), pbData, cbData); + ExitOnFailure(hr, "Failed to post complete message to logging pipe."); + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + PipeTerminateChildProcess - + +*******************************************************************/ +extern "C" HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ) +{ + HRESULT hr = S_OK; + BYTE* pbData = NULL; + SIZE_T cbData = 0; + BOOL fTimedOut = FALSE; + + // Prepare the exit message. + hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); + ExitOnFailure(hr, "Failed to write exit code to message buffer."); + + hr = BuffWriteNumber(&pbData, &cbData, fRestart); + ExitOnFailure(hr, "Failed to write restart to message buffer."); + + // Send the messages. + if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) + { + hr = WritePipeMessage(pConnection->hCachePipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); + } + + hr = WritePipeMessage(pConnection->hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); + ExitOnFailure(hr, "Failed to post terminate message to child process."); + + // If we were able to get a handle to the other process, wait for it to exit. + if (pConnection->hProcess) + { + hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION); + ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit."); + + AssertSz(!fTimedOut, "Timed out while waiting for child process to exit."); + } + +#ifdef DEBUG + if (pConnection->hProcess && !fTimedOut) + { + DWORD dwChildExitCode = 0; + HRESULT hrDebug = S_OK; + + hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode); + if (E_ACCESSDENIED != hrDebug && FAILED(hrDebug)) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. + { + TraceError(hrDebug, "Failed to wait for child process completion."); + } + + AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode, + "Child elevated process did not return matching exit code to parent process."); + } +#endif + +LExit: + ReleaseBuffer(pbData); + + return hr; +} + +/******************************************************************* + PipeChildConnect - Called from the child process to connect back + to the pipe provided by the parent process. + +*******************************************************************/ +extern "C" HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCompanion + ) +{ + Assert(pConnection->sczName); + Assert(pConnection->sczSecret); + Assert(!pConnection->hProcess); + Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); + Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); + + HRESULT hr = S_OK; + LPWSTR sczPipeName = NULL; + + // Try to connect to the parent. + hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent pipe."); + + hr = E_UNEXPECTED; + for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) + { + pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hPipe) + { + hr = HRESULT_FROM_WIN32(::GetLastError()); + if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. + { + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + + ::Sleep(PIPE_WAIT_FOR_CONNECTION); + } + else // we have a connection, go with it. + { + hr = S_OK; + } + } + ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); + + if (fCompanion) + { + // Connect to the parent for the cache pipe. + hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); + + pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) + { + ExitWithLastError(hr, "Failed to open parent cache pipe: %ls", sczPipeName) + } + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent cache pipe: %ls", sczPipeName); + + // Connect to the parent for the logging pipe. + hr = StrAllocFormatted(&sczPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); + ExitOnFailure(hr, "Failed to allocate name of parent logging pipe."); + + pConnection->hLoggingPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE == pConnection->hLoggingPipe) + { + ExitWithLastError(hr, "Failed to open parent logging pipe: %ls", sczPipeName) + } + + // Verify the parent and notify it that the child connected. + hr = ChildPipeConnected(pConnection->hLoggingPipe, pConnection->sczSecret, &pConnection->dwProcessId); + ExitOnFailure(hr, "Failed to verify parent logging pipe: %ls", sczPipeName); + } + + pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); + ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); + +LExit: + ReleaseStr(sczPipeName); + + return hr; +} + + +static HRESULT AllocatePipeMessage( + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __out_bcount(cb) LPVOID* ppvMessage, + __out SIZE_T* pcbMessage + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + size_t cb = 0; + DWORD dwcbData = 0; + + // If no data was provided, ensure the count of bytes is zero. + if (!pvData) + { + cbData = 0; + } + else if (MAXDWORD < cbData) + { + ExitWithRootFailure(hr, E_INVALIDDATA, "Pipe message is too large."); + } + + hr = ::SizeTAdd(sizeof(dwMessage) + sizeof(dwcbData), cbData, &cb); + ExitOnRootFailure(hr, "Failed to calculate total pipe message size"); + + dwcbData = (DWORD)cbData; + + // Allocate the message. + pv = MemAlloc(cb, FALSE); + ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); + + memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); + memcpy_s(static_cast(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &dwcbData, sizeof(dwcbData)); + if (dwcbData) + { + memcpy_s(static_cast(pv) + sizeof(dwMessage) + sizeof(dwcbData), cb - sizeof(dwMessage) - sizeof(dwcbData), pvData, dwcbData); + } + + *pcbMessage = cb; + *ppvMessage = pv; + pv = NULL; + +LExit: + ReleaseMem(pv); + return hr; +} + +static void FreePipeMessage( + __in BURN_PIPE_MESSAGE *pMsg + ) +{ + if (pMsg->fAllocatedData) + { + ReleaseNullMem(pMsg->pvData); + pMsg->fAllocatedData = FALSE; + } +} + +static HRESULT WritePipeMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData + ) +{ + HRESULT hr = S_OK; + LPVOID pv = NULL; + SIZE_T cb = 0; + + hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); + ExitOnFailure(hr, "Failed to allocate message to write."); + + // Write the message. + hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); + ExitOnFailure(hr, "Failed to write message type to pipe."); + +LExit: + ReleaseMem(pv); + return hr; +} + +static HRESULT GetPipeMessage( + __in HANDLE hPipe, + __in BURN_PIPE_MESSAGE* pMsg + ) +{ + HRESULT hr = S_OK; + BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(DWORD)] = { }; + + hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount)); + if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) + { + memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount)); + hr = S_FALSE; + } + ExitOnFailure(hr, "Failed to read message from pipe."); + + pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount); + pMsg->cbData = *(DWORD*)(pbMessageAndByteCount + sizeof(DWORD)); + if (pMsg->cbData) + { + pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); + ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); + + hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); + ExitOnFailure(hr, "Failed to read data for message."); + + pMsg->fAllocatedData = TRUE; + } + +LExit: + if (!pMsg->fAllocatedData && pMsg->pvData) + { + MemFree(pMsg->pvData); + } + + return hr; +} + +static HRESULT ChildPipeConnected( + __in HANDLE hPipe, + __in_z LPCWSTR wzSecret, + __inout DWORD* pdwProcessId + ) +{ + HRESULT hr = S_OK; + LPWSTR sczVerificationSecret = NULL; + DWORD cbVerificationSecret = 0; + DWORD dwVerificationProcessId = 0; + DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. + + // Read the verification secret. + hr = FileReadHandle(hPipe, reinterpret_cast(&cbVerificationSecret), sizeof(cbVerificationSecret)); + ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); + + if (255 < cbVerificationSecret / sizeof(WCHAR)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent is too big."); + } + + hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); + ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); + + FileReadHandle(hPipe, reinterpret_cast(sczVerificationSecret), cbVerificationSecret); + ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); + + // Verify the secrets match. + if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification secret from parent does not match."); + } + + // Read the verification process id. + hr = FileReadHandle(hPipe, reinterpret_cast(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); + ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); + + // If a process id was not provided, we'll trust the process id from the parent. + if (*pdwProcessId == 0) + { + *pdwProcessId = dwVerificationProcessId; + } + else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. + { + hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + ExitOnRootFailure(hr, "Verification process id from parent does not match."); + } + + // All is well, tell the parent process. + // TODO: consider sending BURN_PROTOCOL_VERSION as a way to verify compatibility. + hr = FileWriteHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); + ExitOnFailure(hr, "Failed to inform parent process that child is running."); + +LExit: + ReleaseStr(sczVerificationSecret); + return hr; +} diff --git a/src/burn/engine/burnpipe.h b/src/burn/engine/burnpipe.h new file mode 100644 index 00000000..6571c0e2 --- /dev/null +++ b/src/burn/engine/burnpipe.h @@ -0,0 +1,104 @@ +#pragma once +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _BURN_PIPE_CONNECTION +{ + LPWSTR sczName; + LPWSTR sczSecret; + DWORD dwProcessId; + + HANDLE hProcess; + HANDLE hPipe; + HANDLE hCachePipe; + HANDLE hLoggingPipe; +} BURN_PIPE_CONNECTION; + +typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD +{ + BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, + BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, + BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, +} BURN_PIPE_MESSAGE_TYPE; + +typedef struct _BURN_PIPE_MESSAGE +{ + DWORD dwMessage; + DWORD cbData; + + BOOL fAllocatedData; + LPVOID pvData; +} BURN_PIPE_MESSAGE; + +typedef struct _BURN_PIPE_RESULT +{ + DWORD dwResult; + BOOL fRestart; +} BURN_PIPE_RESULT; + + +typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( + __in BURN_PIPE_MESSAGE* pMsg, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); + + +// Common functions. +void PipeConnectionInitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +void PipeConnectionUninitialize( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeSendMessage( + __in HANDLE hPipe, + __in DWORD dwMessage, + __in_bcount_opt(cbData) LPVOID pvData, + __in SIZE_T cbData, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __out DWORD* pdwResult + ); +HRESULT PipePumpMessages( + __in HANDLE hPipe, + __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, + __in_opt LPVOID pvContext, + __in BURN_PIPE_RESULT* pResult + ); + +// Parent functions. +HRESULT PipeCreateNameAndSecret( + __out_z LPWSTR *psczConnectionName, + __out_z LPWSTR *psczSecret + ); +HRESULT PipeCreatePipes( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCompanion + ); +HRESULT PipeWaitForChildConnect( + __in BURN_PIPE_CONNECTION* pConnection + ); +HRESULT PipeTerminateLoggingPipe( + __in HANDLE hLoggingPipe, + __in DWORD dwParentExitCode + ); +HRESULT PipeTerminateChildProcess( + __in BURN_PIPE_CONNECTION* pConnection, + __in DWORD dwParentExitCode, + __in BOOL fRestart + ); + +// Child functions. +HRESULT PipeChildConnect( + __in BURN_PIPE_CONNECTION* pConnection, + __in BOOL fCompanion + ); + +#ifdef __cplusplus +} +#endif diff --git a/src/burn/engine/engine.vcxproj b/src/burn/engine/engine.vcxproj index 768f20ba..152e9cec 100644 --- a/src/burn/engine/engine.vcxproj +++ b/src/burn/engine/engine.vcxproj @@ -77,7 +77,7 @@ - + @@ -128,7 +128,7 @@ - + diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp deleted file mode 100644 index 5003622c..00000000 --- a/src/burn/engine/pipe.cpp +++ /dev/null @@ -1,782 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - -#include "precomp.h" - -static const DWORD PIPE_64KB = 64 * 1024; -static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, -static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. - -static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; -static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; -static const LPCWSTR LOGGING_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Log"; - -static HRESULT AllocatePipeMessage( - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __out_bcount(cb) LPVOID* ppvMessage, - __out SIZE_T* pcbMessage - ); -static void FreePipeMessage( - __in BURN_PIPE_MESSAGE *pMsg - ); -static HRESULT WritePipeMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData - ); -static HRESULT GetPipeMessage( - __in HANDLE hPipe, - __in BURN_PIPE_MESSAGE* pMsg - ); -static HRESULT ChildPipeConnected( - __in HANDLE hPipe, - __in_z LPCWSTR wzSecret, - __inout DWORD* pdwProcessId - ); - - - -/******************************************************************* - PipeConnectionInitialize - initialize pipe connection data. - -*******************************************************************/ -void PipeConnectionInitialize( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); - pConnection->hPipe = INVALID_HANDLE_VALUE; - pConnection->hCachePipe = INVALID_HANDLE_VALUE; - pConnection->hLoggingPipe = INVALID_HANDLE_VALUE; -} - -/******************************************************************* - PipeConnectionUninitialize - free data in a pipe connection. - -*******************************************************************/ -void PipeConnectionUninitialize( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - ReleaseFileHandle(pConnection->hLoggingPipe); - ReleaseFileHandle(pConnection->hCachePipe); - ReleaseFileHandle(pConnection->hPipe); - ReleaseHandle(pConnection->hProcess); - ReleaseStr(pConnection->sczSecret); - ReleaseStr(pConnection->sczName); - - PipeConnectionInitialize(pConnection); -} - -/******************************************************************* - PipeSendMessage - - -*******************************************************************/ -extern "C" HRESULT PipeSendMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ) -{ - HRESULT hr = S_OK; - BURN_PIPE_RESULT result = { }; - - hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData); - ExitOnFailure(hr, "Failed to write send message to pipe."); - - hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result); - ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); - - *pdwResult = result.dwResult; - -LExit: - return hr; -} - -/******************************************************************* - PipePumpMessages - - -*******************************************************************/ -extern "C" HRESULT PipePumpMessages( - __in HANDLE hPipe, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __in BURN_PIPE_RESULT* pResult - ) -{ - HRESULT hr = S_OK; - BURN_PIPE_MESSAGE msg = { }; - SIZE_T iData = 0; - LPSTR sczMessage = NULL; - DWORD dwResult = 0; - - // Pump messages from child process. - while (S_OK == (hr = GetPipeMessage(hPipe, &msg))) - { - switch (msg.dwMessage) - { - case BURN_PIPE_MESSAGE_TYPE_LOG: - iData = 0; - - hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); - ExitOnFailure(hr, "Failed to read log message."); - - hr = LogStringWorkRaw(sczMessage); - ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); - - dwResult = static_cast(hr); - break; - - case BURN_PIPE_MESSAGE_TYPE_COMPLETE: - if (!msg.pvData || sizeof(DWORD) != msg.cbData) - { - hr = E_INVALIDARG; - ExitOnRootFailure(hr, "No status returned to PipePumpMessages()"); - } - - pResult->dwResult = *static_cast(msg.pvData); - ExitFunction1(hr = S_OK); // exit loop. - - case BURN_PIPE_MESSAGE_TYPE_TERMINATE: - iData = 0; - - hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, &pResult->dwResult); - ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()"); - - if (sizeof(DWORD) * 2 == msg.cbData) - { - hr = BuffReadNumber(static_cast(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); - ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()"); - } - - ExitFunction1(hr = S_OK); // exit loop. - - default: - if (pfnCallback) - { - hr = pfnCallback(&msg, pvContext, &dwResult); - } - else - { - hr = E_INVALIDARG; - } - ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage); - break; - } - - // post result - hr = WritePipeMessage(hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); - ExitOnFailure(hr, "Failed to post result to child process."); - - FreePipeMessage(&msg); - } - ExitOnFailure(hr, "Failed to get message over pipe"); - - if (S_FALSE == hr) - { - hr = S_OK; - } - -LExit: - ReleaseStr(sczMessage); - FreePipeMessage(&msg); - - return hr; -} - -/******************************************************************* - PipeCreateNameAndSecret - - -*******************************************************************/ -extern "C" HRESULT PipeCreateNameAndSecret( - __out_z LPWSTR *psczConnectionName, - __out_z LPWSTR *psczSecret - ) -{ - HRESULT hr = S_OK; - WCHAR wzGuid[GUID_STRING_LENGTH]; - LPWSTR sczConnectionName = NULL; - LPWSTR sczSecret = NULL; - - // Create the unique pipe name. - hr = GuidFixedCreate(wzGuid); - ExitOnRootFailure(hr, "Failed to create pipe guid."); - - hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); - ExitOnFailure(hr, "Failed to allocate pipe name."); - - // Create the unique client secret. - hr = GuidFixedCreate(wzGuid); - ExitOnRootFailure(hr, "Failed to create pipe secret."); - - hr = StrAllocString(&sczSecret, wzGuid, 0); - ExitOnFailure(hr, "Failed to allocate pipe secret."); - - *psczConnectionName = sczConnectionName; - sczConnectionName = NULL; - *psczSecret = sczSecret; - sczSecret = NULL; - -LExit: - ReleaseStr(sczSecret); - ReleaseStr(sczConnectionName); - - return hr; -} - -/******************************************************************* - PipeCreatePipes - create the pipes and event to signal child process. - -*******************************************************************/ -extern "C" HRESULT PipeCreatePipes( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCompanion - ) -{ - Assert(pConnection->sczName); - Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); - - HRESULT hr = S_OK; - PSECURITY_DESCRIPTOR psd = NULL; - SECURITY_ATTRIBUTES sa = { }; - LPWSTR sczFullPipeName = NULL; - HANDLE hPipe = INVALID_HANDLE_VALUE; - HANDLE hCachePipe = INVALID_HANDLE_VALUE; - HANDLE hLoggingPipe = INVALID_HANDLE_VALUE; - - // Only grant special rights when the pipe is being used for "embedded" scenarios. - if (!fCompanion) - { - // Create the security descriptor that grants read/write/sync access to Everyone. - // TODO: consider locking down "WD" to LogonIds (logon session) - LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; - if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) - { - ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); - } - - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = psd; - sa.bInheritHandle = FALSE; - } - - // Create the pipe. - hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName); - - // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such. - hPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, psd ? &sa : NULL); - if (INVALID_HANDLE_VALUE == hPipe) - { - ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName); - } - - if (fCompanion) - { - // Create the cache pipe. - hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); - - hCachePipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); - if (INVALID_HANDLE_VALUE == hCachePipe) - { - ExitWithLastError(hr, "Failed to create cache pipe: %ls", sczFullPipeName); - } - - // Create the logging pipe. - hr = StrAllocFormatted(&sczFullPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate full name of logging pipe: %ls", pConnection->sczName); - - hLoggingPipe = ::CreateNamedPipeW(sczFullPipeName, PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, PIPE_64KB, PIPE_64KB, 1, NULL); - if (INVALID_HANDLE_VALUE == hLoggingPipe) - { - ExitWithLastError(hr, "Failed to create logging pipe: %ls", sczFullPipeName); - } - } - - pConnection->hLoggingPipe = hLoggingPipe; - hLoggingPipe = INVALID_HANDLE_VALUE; - - pConnection->hCachePipe = hCachePipe; - hCachePipe = INVALID_HANDLE_VALUE; - - pConnection->hPipe = hPipe; - hPipe = INVALID_HANDLE_VALUE; - -LExit: - ReleaseFileHandle(hLoggingPipe); - ReleaseFileHandle(hCachePipe); - ReleaseFileHandle(hPipe); - ReleaseStr(sczFullPipeName); - - if (psd) - { - ::LocalFree(psd); - } - - return hr; -} - -/******************************************************************* - PipeWaitForChildConnect - - -*******************************************************************/ -extern "C" HRESULT PipeWaitForChildConnect( - __in BURN_PIPE_CONNECTION* pConnection - ) -{ - HRESULT hr = S_OK; - HANDLE hPipes[3] = { pConnection->hPipe, pConnection->hCachePipe, pConnection->hLoggingPipe}; - LPCWSTR wzSecret = pConnection->sczSecret; - DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); - DWORD dwCurrentProcessId = ::GetCurrentProcessId(); - DWORD dwAck = 0; - - for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) - { - HANDLE hPipe = hPipes[i]; - DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT; - - // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever - // if the child decides not to show up. - if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) - { - ExitWithLastError(hr, "Failed to set pipe to non-blocking."); - } - - // Loop for a while waiting for a connection from child process. - DWORD cRetry = 0; - do - { - if (!::ConnectNamedPipe(hPipe, NULL)) - { - DWORD er = ::GetLastError(); - if (ERROR_PIPE_CONNECTED == er) - { - hr = S_OK; - break; - } - else if (ERROR_PIPE_LISTENING == er) - { - if (cRetry < PIPE_RETRY_FOR_CONNECTION) - { - hr = HRESULT_FROM_WIN32(er); - } - else - { - hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); - break; - } - - ++cRetry; - ::Sleep(PIPE_WAIT_FOR_CONNECTION); - } - else - { - hr = HRESULT_FROM_WIN32(er); - break; - } - } - } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr); - ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); - - // Put the pipe back in blocking mode. - dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT; - if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL)) - { - ExitWithLastError(hr, "Failed to reset pipe to blocking."); - } - - // Prove we are the one that created the elevated process by passing the secret. - hr = FileWriteHandle(hPipe, reinterpret_cast(&cbSecret), sizeof(cbSecret)); - ExitOnFailure(hr, "Failed to write secret length to pipe."); - - hr = FileWriteHandle(hPipe, reinterpret_cast(wzSecret), cbSecret); - ExitOnFailure(hr, "Failed to write secret to pipe."); - - hr = FileWriteHandle(hPipe, reinterpret_cast(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); - ExitOnFailure(hr, "Failed to write our process id to pipe."); - - // Wait until the elevated process responds that it is ready to go. - hr = FileReadHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); - ExitOnFailure(hr, "Failed to read ACK from pipe."); - - // The ACK should match out expected child process id. - //if (pConnection->dwProcessId != dwAck) - //{ - // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); - //} - } - -LExit: - return hr; -} - -/******************************************************************* - PipeTerminateLoggingPipe - - -*******************************************************************/ -extern "C" HRESULT PipeTerminateLoggingPipe( - __in HANDLE hLoggingPipe, - __in DWORD dwParentExitCode - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - - // Prepare the exit message. - hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); - ExitOnFailure(hr, "Failed to write exit code to message buffer."); - - hr = WritePipeMessage(hLoggingPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_COMPLETE), pbData, cbData); - ExitOnFailure(hr, "Failed to post complete message to logging pipe."); - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - PipeTerminateChildProcess - - -*******************************************************************/ -extern "C" HRESULT PipeTerminateChildProcess( - __in BURN_PIPE_CONNECTION* pConnection, - __in DWORD dwParentExitCode, - __in BOOL fRestart - ) -{ - HRESULT hr = S_OK; - BYTE* pbData = NULL; - SIZE_T cbData = 0; - BOOL fTimedOut = FALSE; - - // Prepare the exit message. - hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); - ExitOnFailure(hr, "Failed to write exit code to message buffer."); - - hr = BuffWriteNumber(&pbData, &cbData, fRestart); - ExitOnFailure(hr, "Failed to write restart to message buffer."); - - // Send the messages. - if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) - { - hr = WritePipeMessage(pConnection->hCachePipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); - ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); - } - - hr = WritePipeMessage(pConnection->hPipe, static_cast(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); - ExitOnFailure(hr, "Failed to post terminate message to child process."); - - // If we were able to get a handle to the other process, wait for it to exit. - if (pConnection->hProcess) - { - hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION); - ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit."); - - AssertSz(!fTimedOut, "Timed out while waiting for child process to exit."); - } - -#ifdef DEBUG - if (pConnection->hProcess && !fTimedOut) - { - DWORD dwChildExitCode = 0; - HRESULT hrDebug = S_OK; - - hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode); - if (E_ACCESSDENIED != hrDebug && FAILED(hrDebug)) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. - { - TraceError(hrDebug, "Failed to wait for child process completion."); - } - - AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode, - "Child elevated process did not return matching exit code to parent process."); - } -#endif - -LExit: - ReleaseBuffer(pbData); - - return hr; -} - -/******************************************************************* - PipeChildConnect - Called from the child process to connect back - to the pipe provided by the parent process. - -*******************************************************************/ -extern "C" HRESULT PipeChildConnect( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCompanion - ) -{ - Assert(pConnection->sczName); - Assert(pConnection->sczSecret); - Assert(!pConnection->hProcess); - Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); - Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); - - HRESULT hr = S_OK; - LPWSTR sczPipeName = NULL; - - // Try to connect to the parent. - hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate name of parent pipe."); - - hr = E_UNEXPECTED; - for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry) - { - pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE == pConnection->hPipe) - { - hr = HRESULT_FROM_WIN32(::GetLastError()); - if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent. - { - hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); - } - - ::Sleep(PIPE_WAIT_FOR_CONNECTION); - } - else // we have a connection, go with it. - { - hr = S_OK; - } - } - ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) - - // Verify the parent and notify it that the child connected. - hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); - ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); - - if (fCompanion) - { - // Connect to the parent for the cache pipe. - hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); - - pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE == pConnection->hCachePipe) - { - ExitWithLastError(hr, "Failed to open parent cache pipe: %ls", sczPipeName) - } - - // Verify the parent and notify it that the child connected. - hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); - ExitOnFailure(hr, "Failed to verify parent cache pipe: %ls", sczPipeName); - - // Connect to the parent for the logging pipe. - hr = StrAllocFormatted(&sczPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); - ExitOnFailure(hr, "Failed to allocate name of parent logging pipe."); - - pConnection->hLoggingPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - if (INVALID_HANDLE_VALUE == pConnection->hLoggingPipe) - { - ExitWithLastError(hr, "Failed to open parent logging pipe: %ls", sczPipeName) - } - - // Verify the parent and notify it that the child connected. - hr = ChildPipeConnected(pConnection->hLoggingPipe, pConnection->sczSecret, &pConnection->dwProcessId); - ExitOnFailure(hr, "Failed to verify parent logging pipe: %ls", sczPipeName); - } - - pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); - ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); - -LExit: - ReleaseStr(sczPipeName); - - return hr; -} - - -static HRESULT AllocatePipeMessage( - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __out_bcount(cb) LPVOID* ppvMessage, - __out SIZE_T* pcbMessage - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - size_t cb = 0; - DWORD dwcbData = 0; - - // If no data was provided, ensure the count of bytes is zero. - if (!pvData) - { - cbData = 0; - } - else if (MAXDWORD < cbData) - { - ExitWithRootFailure(hr, E_INVALIDDATA, "Pipe message is too large."); - } - - hr = ::SizeTAdd(sizeof(dwMessage) + sizeof(dwcbData), cbData, &cb); - ExitOnRootFailure(hr, "Failed to calculate total pipe message size"); - - dwcbData = (DWORD)cbData; - - // Allocate the message. - pv = MemAlloc(cb, FALSE); - ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message."); - - memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage)); - memcpy_s(static_cast(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &dwcbData, sizeof(dwcbData)); - if (dwcbData) - { - memcpy_s(static_cast(pv) + sizeof(dwMessage) + sizeof(dwcbData), cb - sizeof(dwMessage) - sizeof(dwcbData), pvData, dwcbData); - } - - *pcbMessage = cb; - *ppvMessage = pv; - pv = NULL; - -LExit: - ReleaseMem(pv); - return hr; -} - -static void FreePipeMessage( - __in BURN_PIPE_MESSAGE *pMsg - ) -{ - if (pMsg->fAllocatedData) - { - ReleaseNullMem(pMsg->pvData); - pMsg->fAllocatedData = FALSE; - } -} - -static HRESULT WritePipeMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData - ) -{ - HRESULT hr = S_OK; - LPVOID pv = NULL; - SIZE_T cb = 0; - - hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb); - ExitOnFailure(hr, "Failed to allocate message to write."); - - // Write the message. - hr = FileWriteHandle(hPipe, reinterpret_cast(pv), cb); - ExitOnFailure(hr, "Failed to write message type to pipe."); - -LExit: - ReleaseMem(pv); - return hr; -} - -static HRESULT GetPipeMessage( - __in HANDLE hPipe, - __in BURN_PIPE_MESSAGE* pMsg - ) -{ - HRESULT hr = S_OK; - BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(DWORD)] = { }; - - hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount)); - if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr) - { - memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount)); - hr = S_FALSE; - } - ExitOnFailure(hr, "Failed to read message from pipe."); - - pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount); - pMsg->cbData = *(DWORD*)(pbMessageAndByteCount + sizeof(DWORD)); - if (pMsg->cbData) - { - pMsg->pvData = MemAlloc(pMsg->cbData, FALSE); - ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message."); - - hr = FileReadHandle(hPipe, reinterpret_cast(pMsg->pvData), pMsg->cbData); - ExitOnFailure(hr, "Failed to read data for message."); - - pMsg->fAllocatedData = TRUE; - } - -LExit: - if (!pMsg->fAllocatedData && pMsg->pvData) - { - MemFree(pMsg->pvData); - } - - return hr; -} - -static HRESULT ChildPipeConnected( - __in HANDLE hPipe, - __in_z LPCWSTR wzSecret, - __inout DWORD* pdwProcessId - ) -{ - HRESULT hr = S_OK; - LPWSTR sczVerificationSecret = NULL; - DWORD cbVerificationSecret = 0; - DWORD dwVerificationProcessId = 0; - DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. - - // Read the verification secret. - hr = FileReadHandle(hPipe, reinterpret_cast(&cbVerificationSecret), sizeof(cbVerificationSecret)); - ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); - - if (255 < cbVerificationSecret / sizeof(WCHAR)) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification secret from parent is too big."); - } - - hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); - ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); - - FileReadHandle(hPipe, reinterpret_cast(sczVerificationSecret), cbVerificationSecret); - ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); - - // Verify the secrets match. - if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification secret from parent does not match."); - } - - // Read the verification process id. - hr = FileReadHandle(hPipe, reinterpret_cast(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); - ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); - - // If a process id was not provided, we'll trust the process id from the parent. - if (*pdwProcessId == 0) - { - *pdwProcessId = dwVerificationProcessId; - } - else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. - { - hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); - ExitOnRootFailure(hr, "Verification process id from parent does not match."); - } - - // All is well, tell the parent process. - // TODO: consider sending BURN_PROTOCOL_VERSION as a way to verify compatibility. - hr = FileWriteHandle(hPipe, reinterpret_cast(&dwAck), sizeof(dwAck)); - ExitOnFailure(hr, "Failed to inform parent process that child is running."); - -LExit: - ReleaseStr(sczVerificationSecret); - return hr; -} diff --git a/src/burn/engine/pipe.h b/src/burn/engine/pipe.h deleted file mode 100644 index 6571c0e2..00000000 --- a/src/burn/engine/pipe.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once -// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct _BURN_PIPE_CONNECTION -{ - LPWSTR sczName; - LPWSTR sczSecret; - DWORD dwProcessId; - - HANDLE hProcess; - HANDLE hPipe; - HANDLE hCachePipe; - HANDLE hLoggingPipe; -} BURN_PIPE_CONNECTION; - -typedef enum _BURN_PIPE_MESSAGE_TYPE : DWORD -{ - BURN_PIPE_MESSAGE_TYPE_LOG = 0xF0000001, - BURN_PIPE_MESSAGE_TYPE_COMPLETE = 0xF0000002, - BURN_PIPE_MESSAGE_TYPE_TERMINATE = 0xF0000003, -} BURN_PIPE_MESSAGE_TYPE; - -typedef struct _BURN_PIPE_MESSAGE -{ - DWORD dwMessage; - DWORD cbData; - - BOOL fAllocatedData; - LPVOID pvData; -} BURN_PIPE_MESSAGE; - -typedef struct _BURN_PIPE_RESULT -{ - DWORD dwResult; - BOOL fRestart; -} BURN_PIPE_RESULT; - - -typedef HRESULT (*PFN_PIPE_MESSAGE_CALLBACK)( - __in BURN_PIPE_MESSAGE* pMsg, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); - - -// Common functions. -void PipeConnectionInitialize( - __in BURN_PIPE_CONNECTION* pConnection - ); -void PipeConnectionUninitialize( - __in BURN_PIPE_CONNECTION* pConnection - ); -HRESULT PipeSendMessage( - __in HANDLE hPipe, - __in DWORD dwMessage, - __in_bcount_opt(cbData) LPVOID pvData, - __in SIZE_T cbData, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __out DWORD* pdwResult - ); -HRESULT PipePumpMessages( - __in HANDLE hPipe, - __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, - __in_opt LPVOID pvContext, - __in BURN_PIPE_RESULT* pResult - ); - -// Parent functions. -HRESULT PipeCreateNameAndSecret( - __out_z LPWSTR *psczConnectionName, - __out_z LPWSTR *psczSecret - ); -HRESULT PipeCreatePipes( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCompanion - ); -HRESULT PipeWaitForChildConnect( - __in BURN_PIPE_CONNECTION* pConnection - ); -HRESULT PipeTerminateLoggingPipe( - __in HANDLE hLoggingPipe, - __in DWORD dwParentExitCode - ); -HRESULT PipeTerminateChildProcess( - __in BURN_PIPE_CONNECTION* pConnection, - __in DWORD dwParentExitCode, - __in BOOL fRestart - ); - -// Child functions. -HRESULT PipeChildConnect( - __in BURN_PIPE_CONNECTION* pConnection, - __in BOOL fCompanion - ); - -#ifdef __cplusplus -} -#endif diff --git a/src/burn/engine/precomp.h b/src/burn/engine/precomp.h index a64ce474..d9a98f07 100644 --- a/src/burn/engine/precomp.h +++ b/src/burn/engine/precomp.h @@ -85,7 +85,7 @@ #include "detect.h" #include "plan.h" #include "logging.h" -#include "pipe.h" +#include "burnpipe.h" #include "cache.h" #include "dependency.h" #include "core.h" diff --git a/src/burn/test/BurnUnitTest/precomp.h b/src/burn/test/BurnUnitTest/precomp.h index 2b90cb58..1aa18204 100644 --- a/src/burn/test/BurnUnitTest/precomp.h +++ b/src/burn/test/BurnUnitTest/precomp.h @@ -59,7 +59,7 @@ #include "registration.h" #include "relatedbundle.h" #include "plan.h" -#include "pipe.h" +#include "burnpipe.h" #include "logging.h" #include "cache.h" #include "dependency.h" -- cgit v1.2.3-55-g6feb