| 1 | // 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. |
| 2 | |
| 3 | #include "precomp.h" |
| 4 | |
| 5 | static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"%ls.Cache"; |
| 6 | static const LPCWSTR LOGGING_PIPE_NAME_FORMAT_STRING = L"%ls.Log"; |
| 7 | |
| 8 | static HRESULT ChildPipeConnected( |
| 9 | __in HANDLE hPipe, |
| 10 | __in_z LPCWSTR wzSecret, |
| 11 | __inout DWORD* pdwProcessId |
| 12 | ); |
| 13 | |
| 14 | |
| 15 | /******************************************************************* |
| 16 | BurnPipeConnectionInitialize - initialize pipe connection data. |
| 17 | |
| 18 | *******************************************************************/ |
| 19 | void BurnPipeConnectionInitialize( |
| 20 | __in BURN_PIPE_CONNECTION* pConnection |
| 21 | ) |
| 22 | { |
| 23 | memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION)); |
| 24 | pConnection->hPipe = INVALID_HANDLE_VALUE; |
| 25 | pConnection->hCachePipe = INVALID_HANDLE_VALUE; |
| 26 | pConnection->hLoggingPipe = INVALID_HANDLE_VALUE; |
| 27 | } |
| 28 | |
| 29 | /******************************************************************* |
| 30 | BurnPipeConnectionUninitialize - free data in a pipe connection. |
| 31 | |
| 32 | *******************************************************************/ |
| 33 | void BurnPipeConnectionUninitialize( |
| 34 | __in BURN_PIPE_CONNECTION* pConnection |
| 35 | ) |
| 36 | { |
| 37 | ReleasePipeHandle(pConnection->hLoggingPipe); |
| 38 | ReleasePipeHandle(pConnection->hCachePipe); |
| 39 | ReleasePipeHandle(pConnection->hPipe); |
| 40 | ReleaseHandle(pConnection->hProcess); |
| 41 | ReleaseStr(pConnection->sczSecret); |
| 42 | ReleaseStr(pConnection->sczName); |
| 43 | |
| 44 | BurnPipeConnectionInitialize(pConnection); |
| 45 | } |
| 46 | |
| 47 | /******************************************************************* |
| 48 | BurnPipeSendMessage - |
| 49 | |
| 50 | *******************************************************************/ |
| 51 | extern "C" HRESULT BurnPipeSendMessage( |
| 52 | __in HANDLE hPipe, |
| 53 | __in DWORD dwMessage, |
| 54 | __in_bcount_opt(cbData) LPVOID pvData, |
| 55 | __in SIZE_T cbData, |
| 56 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, |
| 57 | __in_opt LPVOID pvContext, |
| 58 | __out DWORD* pdwResult |
| 59 | ) |
| 60 | { |
| 61 | HRESULT hr = S_OK; |
| 62 | BURN_PIPE_RESULT result = { }; |
| 63 | |
| 64 | hr = PipeWriteMessage(hPipe, dwMessage, pvData, cbData); |
| 65 | ExitOnFailure(hr, "Failed to write send message to pipe."); |
| 66 | |
| 67 | hr = BurnPipePumpMessages(hPipe, pfnCallback, pvContext, &result); |
| 68 | ExitOnFailure(hr, "Failed to pump messages during send message to pipe."); |
| 69 | |
| 70 | *pdwResult = result.dwResult; |
| 71 | |
| 72 | LExit: |
| 73 | return hr; |
| 74 | } |
| 75 | |
| 76 | /******************************************************************* |
| 77 | BurnPipePumpMessages - |
| 78 | |
| 79 | *******************************************************************/ |
| 80 | extern "C" HRESULT BurnPipePumpMessages( |
| 81 | __in HANDLE hPipe, |
| 82 | __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback, |
| 83 | __in_opt LPVOID pvContext, |
| 84 | __in BURN_PIPE_RESULT* pResult |
| 85 | ) |
| 86 | { |
| 87 | HRESULT hr = S_OK; |
| 88 | PIPE_MESSAGE msg = { }; |
| 89 | SIZE_T iData = 0; |
| 90 | LPSTR sczMessage = NULL; |
| 91 | DWORD dwResult = 0; |
| 92 | |
| 93 | // Pump messages from child process. |
| 94 | while (S_OK == (hr = PipeReadMessage(hPipe, &msg))) |
| 95 | { |
| 96 | switch (msg.dwMessageType) |
| 97 | { |
| 98 | case BURN_PIPE_MESSAGE_TYPE_LOG: |
| 99 | iData = 0; |
| 100 | |
| 101 | hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage); |
| 102 | ExitOnFailure(hr, "Failed to read log message."); |
| 103 | |
| 104 | hr = LogStringWorkRaw(sczMessage); |
| 105 | ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage); |
| 106 | |
| 107 | dwResult = static_cast<DWORD>(hr); |
| 108 | break; |
| 109 | |
| 110 | case BURN_PIPE_MESSAGE_TYPE_COMPLETE: |
| 111 | if (!msg.pvData || sizeof(DWORD) != msg.cbData) |
| 112 | { |
| 113 | hr = E_INVALIDARG; |
| 114 | ExitOnRootFailure(hr, "No status returned to BurnPipePumpMessages()"); |
| 115 | } |
| 116 | |
| 117 | pResult->dwResult = *static_cast<DWORD*>(msg.pvData); |
| 118 | ExitFunction1(hr = S_OK); // exit loop. |
| 119 | |
| 120 | case BURN_PIPE_MESSAGE_TYPE_TERMINATE: |
| 121 | iData = 0; |
| 122 | |
| 123 | hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult); |
| 124 | ExitOnFailure(hr, "Failed to read returned result to BurnPipePumpMessages()"); |
| 125 | |
| 126 | if (sizeof(DWORD) * 2 == msg.cbData) |
| 127 | { |
| 128 | hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart); |
| 129 | ExitOnFailure(hr, "Failed to read returned restart to BurnPipePumpMessages()"); |
| 130 | } |
| 131 | |
| 132 | ExitFunction1(hr = S_OK); // exit loop. |
| 133 | |
| 134 | default: |
| 135 | if (pfnCallback) |
| 136 | { |
| 137 | hr = pfnCallback(&msg, pvContext, &dwResult); |
| 138 | } |
| 139 | else |
| 140 | { |
| 141 | hr = E_INVALIDARG; |
| 142 | } |
| 143 | ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessageType); |
| 144 | break; |
| 145 | } |
| 146 | |
| 147 | // post result |
| 148 | hr = PipeWriteMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult)); |
| 149 | ExitOnFailure(hr, "Failed to post result to child process."); |
| 150 | |
| 151 | ReleasePipeMessage(&msg); |
| 152 | } |
| 153 | ExitOnFailure(hr, "Failed to get message over pipe"); |
| 154 | |
| 155 | if (S_FALSE == hr) |
| 156 | { |
| 157 | hr = S_OK; |
| 158 | } |
| 159 | |
| 160 | LExit: |
| 161 | ReleaseStr(sczMessage); |
| 162 | ReleasePipeMessage(&msg); |
| 163 | |
| 164 | return hr; |
| 165 | } |
| 166 | |
| 167 | /******************************************************************* |
| 168 | BurnPipeCreateNameAndSecret - |
| 169 | |
| 170 | *******************************************************************/ |
| 171 | extern "C" HRESULT BurnPipeCreateNameAndSecret( |
| 172 | __out_z LPWSTR *psczConnectionName, |
| 173 | __out_z LPWSTR *psczSecret |
| 174 | ) |
| 175 | { |
| 176 | HRESULT hr = S_OK; |
| 177 | WCHAR wzGuid[GUID_STRING_LENGTH]; |
| 178 | LPWSTR sczConnectionName = NULL; |
| 179 | LPWSTR sczSecret = NULL; |
| 180 | |
| 181 | // Create the unique pipe name. |
| 182 | hr = GuidFixedCreate(wzGuid); |
| 183 | ExitOnRootFailure(hr, "Failed to create pipe guid."); |
| 184 | |
| 185 | hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid); |
| 186 | ExitOnFailure(hr, "Failed to allocate pipe name."); |
| 187 | |
| 188 | // Create the unique client secret. |
| 189 | hr = GuidFixedCreate(wzGuid); |
| 190 | ExitOnRootFailure(hr, "Failed to create pipe secret."); |
| 191 | |
| 192 | hr = StrAllocString(&sczSecret, wzGuid, 0); |
| 193 | ExitOnFailure(hr, "Failed to allocate pipe secret."); |
| 194 | |
| 195 | *psczConnectionName = sczConnectionName; |
| 196 | sczConnectionName = NULL; |
| 197 | *psczSecret = sczSecret; |
| 198 | sczSecret = NULL; |
| 199 | |
| 200 | LExit: |
| 201 | ReleaseStr(sczSecret); |
| 202 | ReleaseStr(sczConnectionName); |
| 203 | |
| 204 | return hr; |
| 205 | } |
| 206 | |
| 207 | /******************************************************************* |
| 208 | BurnPipeCreatePipes - create the pipes and event to signal child process. |
| 209 | |
| 210 | *******************************************************************/ |
| 211 | extern "C" HRESULT BurnPipeCreatePipes( |
| 212 | __in BURN_PIPE_CONNECTION* pConnection, |
| 213 | __in BOOL fCompanion |
| 214 | ) |
| 215 | { |
| 216 | Assert(pConnection->sczName); |
| 217 | Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); |
| 218 | Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); |
| 219 | Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); |
| 220 | |
| 221 | HRESULT hr = S_OK; |
| 222 | PSECURITY_DESCRIPTOR psd = NULL; |
| 223 | SECURITY_ATTRIBUTES sa = { }; |
| 224 | LPWSTR sczPipeName = NULL; |
| 225 | HANDLE hPipe = INVALID_HANDLE_VALUE; |
| 226 | HANDLE hCachePipe = INVALID_HANDLE_VALUE; |
| 227 | HANDLE hLoggingPipe = INVALID_HANDLE_VALUE; |
| 228 | |
| 229 | // Only grant special rights when the pipe is being used for "embedded" scenarios. |
| 230 | if (!fCompanion) |
| 231 | { |
| 232 | // Create the security descriptor that grants read/write/sync access to Everyone. |
| 233 | // TODO: consider locking down "WD" to LogonIds (logon session) |
| 234 | LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)"; |
| 235 | if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL)) |
| 236 | { |
| 237 | ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe."); |
| 238 | } |
| 239 | |
| 240 | sa.nLength = sizeof(sa); |
| 241 | sa.lpSecurityDescriptor = psd; |
| 242 | sa.bInheritHandle = FALSE; |
| 243 | } |
| 244 | |
| 245 | // Create the pipe. |
| 246 | hr = PipeCreate(pConnection->sczName, psd ? &sa : NULL, &hPipe); |
| 247 | ExitOnFailure(hr, "Failed to create pipe: %ls", pConnection->sczName); |
| 248 | |
| 249 | if (fCompanion) |
| 250 | { |
| 251 | // Create the cache pipe. |
| 252 | hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); |
| 253 | ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName); |
| 254 | |
| 255 | hr = PipeCreate(sczPipeName, NULL, &hCachePipe); |
| 256 | ExitOnFailure(hr, "Failed to create cache pipe: %ls", sczPipeName); |
| 257 | |
| 258 | // Create the logging pipe. |
| 259 | hr = StrAllocFormatted(&sczPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); |
| 260 | ExitOnFailure(hr, "Failed to allocate full name of logging pipe: %ls", pConnection->sczName); |
| 261 | |
| 262 | hr = PipeCreate(sczPipeName, NULL, &hLoggingPipe); |
| 263 | ExitOnFailure(hr, "Failed to create logging pipe: %ls", sczPipeName); |
| 264 | } |
| 265 | |
| 266 | pConnection->hLoggingPipe = hLoggingPipe; |
| 267 | hLoggingPipe = INVALID_HANDLE_VALUE; |
| 268 | |
| 269 | pConnection->hCachePipe = hCachePipe; |
| 270 | hCachePipe = INVALID_HANDLE_VALUE; |
| 271 | |
| 272 | pConnection->hPipe = hPipe; |
| 273 | hPipe = INVALID_HANDLE_VALUE; |
| 274 | |
| 275 | LExit: |
| 276 | ReleasePipeHandle(hLoggingPipe); |
| 277 | ReleasePipeHandle(hCachePipe); |
| 278 | ReleasePipeHandle(hPipe); |
| 279 | ReleaseStr(sczPipeName); |
| 280 | |
| 281 | if (psd) |
| 282 | { |
| 283 | ::LocalFree(psd); |
| 284 | } |
| 285 | |
| 286 | return hr; |
| 287 | } |
| 288 | |
| 289 | /******************************************************************* |
| 290 | BurnPipeWaitForChildConnect - |
| 291 | |
| 292 | *******************************************************************/ |
| 293 | extern "C" HRESULT BurnPipeWaitForChildConnect( |
| 294 | __in BURN_PIPE_CONNECTION* pConnection |
| 295 | ) |
| 296 | { |
| 297 | HRESULT hr = S_OK; |
| 298 | HANDLE hPipes[3] = { pConnection->hPipe, pConnection->hCachePipe, pConnection->hLoggingPipe}; |
| 299 | LPCWSTR wzSecret = pConnection->sczSecret; |
| 300 | DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR); |
| 301 | DWORD dwCurrentProcessId = ::GetCurrentProcessId(); |
| 302 | DWORD dwAck = 0; |
| 303 | |
| 304 | for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i) |
| 305 | { |
| 306 | HANDLE hPipe = hPipes[i]; |
| 307 | |
| 308 | hr = PipeServerWaitForClientConnect(pConnection->hProcess, hPipe); |
| 309 | ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe."); |
| 310 | |
| 311 | // Prove we are the one that created the elevated process by passing the secret. |
| 312 | hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbSecret), sizeof(cbSecret)); |
| 313 | ExitOnFailure(hr, "Failed to write secret length to pipe."); |
| 314 | |
| 315 | hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(wzSecret), cbSecret); |
| 316 | ExitOnFailure(hr, "Failed to write secret to pipe."); |
| 317 | |
| 318 | hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwCurrentProcessId), sizeof(dwCurrentProcessId)); |
| 319 | ExitOnFailure(hr, "Failed to write our process id to pipe."); |
| 320 | |
| 321 | // Wait until the elevated process responds that it is ready to go. |
| 322 | hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwAck), sizeof(dwAck)); |
| 323 | ExitOnFailure(hr, "Failed to read ACK from pipe."); |
| 324 | |
| 325 | // The ACK should match out expected child process id. |
| 326 | //if (pConnection->dwProcessId != dwAck) |
| 327 | //{ |
| 328 | // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); |
| 329 | // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck); |
| 330 | //} |
| 331 | } |
| 332 | |
| 333 | LExit: |
| 334 | return hr; |
| 335 | } |
| 336 | |
| 337 | /******************************************************************* |
| 338 | BurnPipeTerminateLoggingPipe - |
| 339 | |
| 340 | *******************************************************************/ |
| 341 | extern "C" HRESULT BurnPipeTerminateLoggingPipe( |
| 342 | __in HANDLE hLoggingPipe, |
| 343 | __in DWORD dwParentExitCode |
| 344 | ) |
| 345 | { |
| 346 | HRESULT hr = S_OK; |
| 347 | BYTE* pbData = NULL; |
| 348 | SIZE_T cbData = 0; |
| 349 | |
| 350 | // Prepare the exit message. |
| 351 | hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); |
| 352 | ExitOnFailure(hr, "Failed to write exit code to message buffer."); |
| 353 | |
| 354 | hr = PipeWriteMessage(hLoggingPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), pbData, cbData); |
| 355 | ExitOnFailure(hr, "Failed to post complete message to logging pipe."); |
| 356 | |
| 357 | LExit: |
| 358 | ReleaseMem(pbData); |
| 359 | |
| 360 | return hr; |
| 361 | } |
| 362 | |
| 363 | /******************************************************************* |
| 364 | BurnPipeTerminateChildProcess - |
| 365 | |
| 366 | *******************************************************************/ |
| 367 | extern "C" HRESULT BurnPipeTerminateChildProcess( |
| 368 | __in BURN_PIPE_CONNECTION* pConnection, |
| 369 | __in DWORD dwParentExitCode, |
| 370 | __in BOOL fRestart |
| 371 | ) |
| 372 | { |
| 373 | HRESULT hr = S_OK; |
| 374 | BYTE* pbData = NULL; |
| 375 | SIZE_T cbData = 0; |
| 376 | BOOL fTimedOut = FALSE; |
| 377 | |
| 378 | // Prepare the exit message. |
| 379 | hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode); |
| 380 | ExitOnFailure(hr, "Failed to write exit code to message buffer."); |
| 381 | |
| 382 | hr = BuffWriteNumber(&pbData, &cbData, fRestart); |
| 383 | ExitOnFailure(hr, "Failed to write restart to message buffer."); |
| 384 | |
| 385 | // Send the messages. |
| 386 | if (INVALID_HANDLE_VALUE != pConnection->hCachePipe) |
| 387 | { |
| 388 | hr = PipeWriteMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); |
| 389 | ExitOnFailure(hr, "Failed to post terminate message to child process cache thread."); |
| 390 | } |
| 391 | |
| 392 | hr = PipeWriteMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData); |
| 393 | ExitOnFailure(hr, "Failed to post terminate message to child process."); |
| 394 | |
| 395 | // If we were able to get a handle to the other process, wait for it to exit. |
| 396 | if (pConnection->hProcess) |
| 397 | { |
| 398 | hr = AppWaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION); |
| 399 | ExitOnWaitObjectFailure(hr, fTimedOut, "Failed to wait for child process exit."); |
| 400 | |
| 401 | AssertSz(!fTimedOut, "Timed out while waiting for child process to exit."); |
| 402 | } |
| 403 | |
| 404 | #ifdef DEBUG |
| 405 | if (pConnection->hProcess && !fTimedOut) |
| 406 | { |
| 407 | DWORD dwChildExitCode = 0; |
| 408 | HRESULT hrDebug = S_OK; |
| 409 | |
| 410 | hrDebug = CoreWaitForProcCompletion(pConnection->hProcess, 0, &dwChildExitCode); |
| 411 | if (E_ACCESSDENIED != hrDebug && FAILED(hrDebug)) // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED. |
| 412 | { |
| 413 | TraceError(hrDebug, "Failed to wait for child process completion."); |
| 414 | } |
| 415 | |
| 416 | AssertSz(E_ACCESSDENIED == hrDebug || dwChildExitCode == dwParentExitCode, |
| 417 | "Child elevated process did not return matching exit code to parent process."); |
| 418 | } |
| 419 | #endif |
| 420 | |
| 421 | LExit: |
| 422 | ReleaseMem(pbData); |
| 423 | |
| 424 | return hr; |
| 425 | } |
| 426 | |
| 427 | /******************************************************************* |
| 428 | BurnPipeChildConnect - Called from the child process to connect back |
| 429 | to the pipe provided by the parent process. |
| 430 | |
| 431 | *******************************************************************/ |
| 432 | extern "C" HRESULT BurnPipeChildConnect( |
| 433 | __in BURN_PIPE_CONNECTION* pConnection, |
| 434 | __in BOOL fCompanion |
| 435 | ) |
| 436 | { |
| 437 | Assert(pConnection->sczName); |
| 438 | Assert(pConnection->sczSecret); |
| 439 | Assert(!pConnection->hProcess); |
| 440 | Assert(INVALID_HANDLE_VALUE == pConnection->hPipe); |
| 441 | Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe); |
| 442 | Assert(INVALID_HANDLE_VALUE == pConnection->hLoggingPipe); |
| 443 | |
| 444 | HRESULT hr = S_OK; |
| 445 | LPWSTR sczPipeName = NULL; |
| 446 | |
| 447 | // Try to connect to the parent. |
| 448 | hr = PipeClientConnect(pConnection->sczName, &pConnection->hPipe); |
| 449 | ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName) |
| 450 | |
| 451 | // Verify the parent and notify it that the child connected. |
| 452 | hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId); |
| 453 | ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName); |
| 454 | |
| 455 | if (fCompanion) |
| 456 | { |
| 457 | // Connect to the parent for the cache pipe. |
| 458 | hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName); |
| 459 | ExitOnFailure(hr, "Failed to allocate name of parent cache pipe."); |
| 460 | |
| 461 | hr = PipeClientConnect(sczPipeName, &pConnection->hCachePipe); |
| 462 | ExitOnFailure(hr, "Failed to open parent cache pipe: %ls", sczPipeName) |
| 463 | |
| 464 | // Verify the parent and notify it that the child connected. |
| 465 | hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId); |
| 466 | ExitOnFailure(hr, "Failed to verify parent cache pipe: %ls", sczPipeName); |
| 467 | |
| 468 | // Connect to the parent for the logging pipe. |
| 469 | hr = StrAllocFormatted(&sczPipeName, LOGGING_PIPE_NAME_FORMAT_STRING, pConnection->sczName); |
| 470 | ExitOnFailure(hr, "Failed to allocate name of parent logging pipe."); |
| 471 | |
| 472 | hr = PipeClientConnect(sczPipeName, &pConnection->hLoggingPipe); |
| 473 | ExitOnFailure(hr, "Failed to open parent cache pipe: %ls", sczPipeName) |
| 474 | |
| 475 | // Verify the parent and notify it that the child connected. |
| 476 | hr = ChildPipeConnected(pConnection->hLoggingPipe, pConnection->sczSecret, &pConnection->dwProcessId); |
| 477 | ExitOnFailure(hr, "Failed to verify parent logging pipe: %ls", sczPipeName); |
| 478 | } |
| 479 | |
| 480 | pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId); |
| 481 | ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId); |
| 482 | |
| 483 | LExit: |
| 484 | ReleaseStr(sczPipeName); |
| 485 | |
| 486 | return hr; |
| 487 | } |
| 488 | |
| 489 | static HRESULT ChildPipeConnected( |
| 490 | __in HANDLE hPipe, |
| 491 | __in_z LPCWSTR wzSecret, |
| 492 | __inout DWORD* pdwProcessId |
| 493 | ) |
| 494 | { |
| 495 | HRESULT hr = S_OK; |
| 496 | LPWSTR sczVerificationSecret = NULL; |
| 497 | DWORD cbVerificationSecret = 0; |
| 498 | DWORD dwVerificationProcessId = 0; |
| 499 | DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK. |
| 500 | |
| 501 | // Read the verification secret. |
| 502 | hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&cbVerificationSecret), sizeof(cbVerificationSecret)); |
| 503 | ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe."); |
| 504 | |
| 505 | if (255 < cbVerificationSecret / sizeof(WCHAR)) |
| 506 | { |
| 507 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); |
| 508 | ExitOnRootFailure(hr, "Verification secret from parent is too big."); |
| 509 | } |
| 510 | |
| 511 | hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1); |
| 512 | ExitOnFailure(hr, "Failed to allocate buffer for verification secret."); |
| 513 | |
| 514 | FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(sczVerificationSecret), cbVerificationSecret); |
| 515 | ExitOnFailure(hr, "Failed to read verification secret from parent pipe."); |
| 516 | |
| 517 | // Verify the secrets match. |
| 518 | if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1)) |
| 519 | { |
| 520 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); |
| 521 | ExitOnRootFailure(hr, "Verification secret from parent does not match."); |
| 522 | } |
| 523 | |
| 524 | // Read the verification process id. |
| 525 | hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwVerificationProcessId), sizeof(dwVerificationProcessId)); |
| 526 | ExitOnFailure(hr, "Failed to read verification process id from parent pipe."); |
| 527 | |
| 528 | // If a process id was not provided, we'll trust the process id from the parent. |
| 529 | if (*pdwProcessId == 0) |
| 530 | { |
| 531 | *pdwProcessId = dwVerificationProcessId; |
| 532 | } |
| 533 | else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match. |
| 534 | { |
| 535 | hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA); |
| 536 | ExitOnRootFailure(hr, "Verification process id from parent does not match."); |
| 537 | } |
| 538 | |
| 539 | // All is well, tell the parent process. |
| 540 | // TODO: consider sending BURN_PROTOCOL_VERSION as a way to verify compatibility. |
| 541 | hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwAck), sizeof(dwAck)); |
| 542 | ExitOnFailure(hr, "Failed to inform parent process that child is running."); |
| 543 | |
| 544 | LExit: |
| 545 | ReleaseStr(sczVerificationSecret); |
| 546 | return hr; |
| 547 | } |