summaryrefslogtreecommitdiff
path: root/src/burn/engine/pipe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/burn/engine/pipe.cpp')
-rw-r--r--src/burn/engine/pipe.cpp821
1 files changed, 821 insertions, 0 deletions
diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp
new file mode 100644
index 00000000..a9fd24e8
--- /dev/null
+++ b/src/burn/engine/pipe.cpp
@@ -0,0 +1,821 @@
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
5static const DWORD PIPE_64KB = 64 * 1024;
6static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second,
7static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes.
8
9static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls";
10static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache";
11
12static HRESULT AllocatePipeMessage(
13 __in DWORD dwMessage,
14 __in_bcount_opt(cbData) LPVOID pvData,
15 __in SIZE_T cbData,
16 __out_bcount(cb) LPVOID* ppvMessage,
17 __out SIZE_T* cbMessage
18 );
19static void FreePipeMessage(
20 __in BURN_PIPE_MESSAGE *pMsg
21 );
22static HRESULT WritePipeMessage(
23 __in HANDLE hPipe,
24 __in DWORD dwMessage,
25 __in_bcount_opt(cbData) LPVOID pvData,
26 __in SIZE_T cbData
27 );
28static HRESULT GetPipeMessage(
29 __in HANDLE hPipe,
30 __in BURN_PIPE_MESSAGE* pMsg
31 );
32static HRESULT ChildPipeConnected(
33 __in HANDLE hPipe,
34 __in_z LPCWSTR wzSecret,
35 __inout DWORD* pdwProcessId
36 );
37
38
39
40/*******************************************************************
41 PipeConnectionInitialize - initialize pipe connection data.
42
43*******************************************************************/
44void PipeConnectionInitialize(
45 __in BURN_PIPE_CONNECTION* pConnection
46 )
47{
48 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
49 pConnection->hPipe = INVALID_HANDLE_VALUE;
50 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
51}
52
53/*******************************************************************
54 PipeConnectionUninitialize - free data in a pipe connection.
55
56*******************************************************************/
57void PipeConnectionUninitialize(
58 __in BURN_PIPE_CONNECTION* pConnection
59 )
60{
61 ReleaseFileHandle(pConnection->hCachePipe);
62 ReleaseFileHandle(pConnection->hPipe);
63 ReleaseHandle(pConnection->hProcess);
64 ReleaseStr(pConnection->sczSecret);
65 ReleaseStr(pConnection->sczName);
66
67 memset(pConnection, 0, sizeof(BURN_PIPE_CONNECTION));
68 pConnection->hPipe = INVALID_HANDLE_VALUE;
69 pConnection->hCachePipe = INVALID_HANDLE_VALUE;
70}
71
72/*******************************************************************
73 PipeSendMessage -
74
75*******************************************************************/
76extern "C" HRESULT PipeSendMessage(
77 __in HANDLE hPipe,
78 __in DWORD dwMessage,
79 __in_bcount_opt(cbData) LPVOID pvData,
80 __in SIZE_T cbData,
81 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
82 __in_opt LPVOID pvContext,
83 __out DWORD* pdwResult
84 )
85{
86 HRESULT hr = S_OK;
87 BURN_PIPE_RESULT result = { };
88
89 hr = WritePipeMessage(hPipe, dwMessage, pvData, cbData);
90 ExitOnFailure(hr, "Failed to write send message to pipe.");
91
92 hr = PipePumpMessages(hPipe, pfnCallback, pvContext, &result);
93 ExitOnFailure(hr, "Failed to pump messages during send message to pipe.");
94
95 *pdwResult = result.dwResult;
96
97LExit:
98 return hr;
99}
100
101/*******************************************************************
102 PipePumpMessages -
103
104*******************************************************************/
105extern "C" HRESULT PipePumpMessages(
106 __in HANDLE hPipe,
107 __in_opt PFN_PIPE_MESSAGE_CALLBACK pfnCallback,
108 __in_opt LPVOID pvContext,
109 __in BURN_PIPE_RESULT* pResult
110 )
111{
112 HRESULT hr = S_OK;
113 BURN_PIPE_MESSAGE msg = { };
114 SIZE_T iData = 0;
115 LPSTR sczMessage = NULL;
116 DWORD dwResult = 0;
117
118 // Pump messages from child process.
119 while (S_OK == (hr = GetPipeMessage(hPipe, &msg)))
120 {
121 switch (msg.dwMessage)
122 {
123 case BURN_PIPE_MESSAGE_TYPE_LOG:
124 iData = 0;
125
126 hr = BuffReadStringAnsi((BYTE*)msg.pvData, msg.cbData, &iData, &sczMessage);
127 ExitOnFailure(hr, "Failed to read log message.");
128
129 hr = LogStringWorkRaw(sczMessage);
130 ExitOnFailure(hr, "Failed to write log message:'%hs'.", sczMessage);
131
132 dwResult = static_cast<DWORD>(hr);
133 break;
134
135 case BURN_PIPE_MESSAGE_TYPE_COMPLETE:
136 if (!msg.pvData || sizeof(DWORD) != msg.cbData)
137 {
138 hr = E_INVALIDARG;
139 ExitOnRootFailure(hr, "No status returned to PipePumpMessages()");
140 }
141
142 pResult->dwResult = *static_cast<DWORD*>(msg.pvData);
143 ExitFunction1(hr = S_OK); // exit loop.
144
145 case BURN_PIPE_MESSAGE_TYPE_TERMINATE:
146 iData = 0;
147
148 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, &pResult->dwResult);
149 ExitOnFailure(hr, "Failed to read returned result to PipePumpMessages()");
150
151 if (sizeof(DWORD) * 2 == msg.cbData)
152 {
153 hr = BuffReadNumber(static_cast<BYTE*>(msg.pvData), msg.cbData, &iData, (DWORD*)&pResult->fRestart);
154 ExitOnFailure(hr, "Failed to read returned restart to PipePumpMessages()");
155 }
156
157 ExitFunction1(hr = S_OK); // exit loop.
158
159 default:
160 if (pfnCallback)
161 {
162 hr = pfnCallback(&msg, pvContext, &dwResult);
163 }
164 else
165 {
166 hr = E_INVALIDARG;
167 }
168 ExitOnFailure(hr, "Failed to process message: %u", msg.dwMessage);
169 break;
170 }
171
172 // post result
173 hr = WritePipeMessage(hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_COMPLETE), &dwResult, sizeof(dwResult));
174 ExitOnFailure(hr, "Failed to post result to child process.");
175
176 FreePipeMessage(&msg);
177 }
178 ExitOnFailure(hr, "Failed to get message over pipe");
179
180 if (S_FALSE == hr)
181 {
182 hr = S_OK;
183 }
184
185LExit:
186 ReleaseStr(sczMessage);
187 FreePipeMessage(&msg);
188
189 return hr;
190}
191
192/*******************************************************************
193 PipeCreateNameAndSecret -
194
195*******************************************************************/
196extern "C" HRESULT PipeCreateNameAndSecret(
197 __out_z LPWSTR *psczConnectionName,
198 __out_z LPWSTR *psczSecret
199 )
200{
201 HRESULT hr = S_OK;
202 WCHAR wzGuid[GUID_STRING_LENGTH];
203 LPWSTR sczConnectionName = NULL;
204 LPWSTR sczSecret = NULL;
205
206 // Create the unique pipe name.
207 hr = GuidFixedCreate(wzGuid);
208 ExitOnRootFailure(hr, "Failed to create pipe guid.");
209
210 hr = StrAllocFormatted(&sczConnectionName, L"BurnPipe.%s", wzGuid);
211 ExitOnFailure(hr, "Failed to allocate pipe name.");
212
213 // Create the unique client secret.
214 hr = GuidFixedCreate(wzGuid);
215 ExitOnRootFailure(hr, "Failed to create pipe secret.");
216
217 hr = StrAllocString(&sczSecret, wzGuid, 0);
218 ExitOnFailure(hr, "Failed to allocate pipe secret.");
219
220 *psczConnectionName = sczConnectionName;
221 sczConnectionName = NULL;
222 *psczSecret = sczSecret;
223 sczSecret = NULL;
224
225LExit:
226 ReleaseStr(sczSecret);
227 ReleaseStr(sczConnectionName);
228
229 return hr;
230}
231
232/*******************************************************************
233 PipeCreatePipes - create the pipes and event to signal child process.
234
235*******************************************************************/
236extern "C" HRESULT PipeCreatePipes(
237 __in BURN_PIPE_CONNECTION* pConnection,
238 __in BOOL fCreateCachePipe,
239 __out HANDLE* phEvent
240 )
241{
242 Assert(pConnection->sczName);
243 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
244 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
245
246 HRESULT hr = S_OK;
247 PSECURITY_DESCRIPTOR psd = NULL;
248 SECURITY_ATTRIBUTES sa = { };
249 LPWSTR sczFullPipeName = NULL;
250 HANDLE hPipe = INVALID_HANDLE_VALUE;
251 HANDLE hCachePipe = INVALID_HANDLE_VALUE;
252
253 // Only the grant special rights when the pipe is being used for "embedded"
254 // scenarios (aka: there is no cache pipe).
255 if (!fCreateCachePipe)
256 {
257 // Create the security descriptor that grants read/write/sync access to Everyone.
258 // TODO: consider locking down "WD" to LogonIds (logon session)
259 LPCWSTR wzSddl = L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GRGW0x00100000;;;WD)";
260 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSddl, SDDL_REVISION_1, &psd, NULL))
261 {
262 ExitWithLastError(hr, "Failed to create the security descriptor for the connection event and pipe.");
263 }
264
265 sa.nLength = sizeof(sa);
266 sa.lpSecurityDescriptor = psd;
267 sa.bInheritHandle = FALSE;
268 }
269
270 // Create the pipe.
271 hr = StrAllocFormatted(&sczFullPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
272 ExitOnFailure(hr, "Failed to allocate full name of pipe: %ls", pConnection->sczName);
273
274 // TODO: consider using overlapped IO to do waits on the pipe and still be able to cancel and such.
275 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);
276 if (INVALID_HANDLE_VALUE == hPipe)
277 {
278 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
279 }
280
281 if (fCreateCachePipe)
282 {
283 // Create the cache pipe.
284 hr = StrAllocFormatted(&sczFullPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
285 ExitOnFailure(hr, "Failed to allocate full name of cache pipe: %ls", pConnection->sczName);
286
287 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);
288 if (INVALID_HANDLE_VALUE == hCachePipe)
289 {
290 ExitWithLastError(hr, "Failed to create pipe: %ls", sczFullPipeName);
291 }
292 }
293
294 pConnection->hCachePipe = hCachePipe;
295 hCachePipe = INVALID_HANDLE_VALUE;
296
297 pConnection->hPipe = hPipe;
298 hPipe = INVALID_HANDLE_VALUE;
299
300 // TODO: remove the following
301 *phEvent = NULL;
302
303LExit:
304 ReleaseFileHandle(hCachePipe);
305 ReleaseFileHandle(hPipe);
306 ReleaseStr(sczFullPipeName);
307
308 if (psd)
309 {
310 ::LocalFree(psd);
311 }
312
313 return hr;
314}
315
316/*******************************************************************
317 PipeLaunchParentProcess - Called from the per-machine process to create
318 a per-user process and set up the
319 communication pipe.
320
321*******************************************************************/
322const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated";
323HRESULT PipeLaunchParentProcess(
324 __in_z LPCWSTR wzCommandLine,
325 __in int nCmdShow,
326 __in_z LPWSTR sczConnectionName,
327 __in_z LPWSTR sczSecret,
328 __in BOOL /*fDisableUnelevate*/
329 )
330{
331 HRESULT hr = S_OK;
332 DWORD dwProcessId = 0;
333 LPWSTR sczBurnPath = NULL;
334 LPWSTR sczParameters = NULL;
335 HANDLE hProcess = NULL;
336
337 dwProcessId = ::GetCurrentProcessId();
338
339 hr = PathForCurrentProcess(&sczBurnPath, NULL);
340 ExitOnFailure(hr, "Failed to get current process path.");
341
342 hr = StrAllocFormatted(&sczParameters, L"-%ls %ls %ls %u %ls", BURN_COMMANDLINE_SWITCH_UNELEVATED, sczConnectionName, sczSecret, dwProcessId, wzCommandLine);
343 ExitOnFailure(hr, "Failed to allocate parameters for unelevated process.");
344
345#ifdef ENABLE_UNELEVATE
346 if (fDisableUnelevate)
347 {
348 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
349 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
350 }
351 else
352 {
353 // Try to launch unelevated and if that fails for any reason, try launch our process normally (even though that may make it elevated).
354 hr = ProcExecuteAsInteractiveUser(sczBurnPath, sczParameters, &hProcess);
355 if (FAILED(hr))
356 {
357 hr = ShelExecUnelevated(sczBurnPath, sczParameters, L"open", NULL, nCmdShow);
358 if (FAILED(hr))
359 {
360 hr = ShelExec(sczBurnPath, sczParameters, L"open", NULL, nCmdShow, NULL, NULL);
361 ExitOnFailure(hr, "Failed to launch parent process: %ls", sczBurnPath);
362 }
363 }
364 }
365#else
366 hr = ProcExec(sczBurnPath, sczParameters, nCmdShow, &hProcess);
367 ExitOnFailure(hr, "Failed to launch parent process with unelevate disabled: %ls", sczBurnPath);
368#endif
369
370LExit:
371 ReleaseHandle(hProcess);
372 ReleaseStr(sczParameters);
373 ReleaseStr(sczBurnPath);
374
375 return hr;
376}
377
378/*******************************************************************
379 PipeLaunchChildProcess - Called from the per-user process to create
380 the per-machine process and set up the
381 communication pipe.
382
383*******************************************************************/
384extern "C" HRESULT PipeLaunchChildProcess(
385 __in_z LPCWSTR wzExecutablePath,
386 __in BURN_PIPE_CONNECTION* pConnection,
387 __in BOOL fElevate,
388 __in_opt HWND hwndParent
389 )
390{
391 HRESULT hr = S_OK;
392 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
393 LPWSTR sczParameters = NULL;
394 LPCWSTR wzVerb = NULL;
395 HANDLE hProcess = NULL;
396
397 hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId);
398 ExitOnFailure(hr, "Failed to allocate parameters for elevated process.");
399
400 wzVerb = !fElevate ? L"open" : L"runas";
401
402 // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine.
403 // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary.
404 hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_SHOWNA, hwndParent, &hProcess);
405 ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath);
406
407 pConnection->dwProcessId = ::GetProcessId(hProcess);
408 pConnection->hProcess = hProcess;
409 hProcess = NULL;
410
411LExit:
412 ReleaseHandle(hProcess);
413 ReleaseStr(sczParameters);
414
415 return hr;
416}
417
418/*******************************************************************
419 PipeWaitForChildConnect -
420
421*******************************************************************/
422extern "C" HRESULT PipeWaitForChildConnect(
423 __in BURN_PIPE_CONNECTION* pConnection
424 )
425{
426 HRESULT hr = S_OK;
427 HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe};
428 LPCWSTR wzSecret = pConnection->sczSecret;
429 DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
430 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
431 DWORD dwAck = 0;
432
433 for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
434 {
435 HANDLE hPipe = hPipes[i];
436 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
437
438 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
439 // if the child decides not to show up.
440 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
441 {
442 ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
443 }
444
445 // Loop for a while waiting for a connection from child process.
446 DWORD cRetry = 0;
447 do
448 {
449 if (!::ConnectNamedPipe(hPipe, NULL))
450 {
451 DWORD er = ::GetLastError();
452 if (ERROR_PIPE_CONNECTED == er)
453 {
454 hr = S_OK;
455 break;
456 }
457 else if (ERROR_PIPE_LISTENING == er)
458 {
459 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
460 {
461 hr = HRESULT_FROM_WIN32(er);
462 }
463 else
464 {
465 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
466 break;
467 }
468
469 ++cRetry;
470 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
471 }
472 else
473 {
474 hr = HRESULT_FROM_WIN32(er);
475 break;
476 }
477 }
478 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
479 ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
480
481 // Put the pipe back in blocking mode.
482 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
483 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
484 {
485 ExitWithLastError(hr, "Failed to reset pipe to blocking.");
486 }
487
488 // Prove we are the one that created the elevated process by passing the secret.
489 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&cbSecret), sizeof(cbSecret));
490 ExitOnFailure(hr, "Failed to write secret length to pipe.");
491
492 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(wzSecret), cbSecret);
493 ExitOnFailure(hr, "Failed to write secret to pipe.");
494
495 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwCurrentProcessId), sizeof(dwCurrentProcessId));
496 ExitOnFailure(hr, "Failed to write our process id to pipe.");
497
498 // Wait until the elevated process responds that it is ready to go.
499 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwAck), sizeof(dwAck));
500 ExitOnFailure(hr, "Failed to read ACK from pipe.");
501
502 // The ACK should match out expected child process id.
503 //if (pConnection->dwProcessId != dwAck)
504 //{
505 // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
506 // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
507 //}
508 }
509
510LExit:
511 return hr;
512}
513
514/*******************************************************************
515 PipeTerminateChildProcess -
516
517*******************************************************************/
518extern "C" HRESULT PipeTerminateChildProcess(
519 __in BURN_PIPE_CONNECTION* pConnection,
520 __in DWORD dwParentExitCode,
521 __in BOOL fRestart
522 )
523{
524 HRESULT hr = S_OK;
525 BYTE* pbData = NULL;
526 SIZE_T cbData = 0;
527
528 // Prepare the exit message.
529 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
530 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
531
532 hr = BuffWriteNumber(&pbData, &cbData, fRestart);
533 ExitOnFailure(hr, "Failed to write restart to message buffer.");
534
535 // Send the messages.
536 if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
537 {
538 hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
539 ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
540 }
541
542 hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
543 ExitOnFailure(hr, "Failed to post terminate message to child process.");
544
545 // If we were able to get a handle to the other process, wait for it to exit.
546 if (pConnection->hProcess)
547 {
548 if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
549 {
550 ExitWithLastError(hr, "Failed to wait for child process exit.");
551 }
552
553#ifdef DEBUG
554 DWORD dwChildExitCode = 0;
555 DWORD dwErrorCode = ERROR_SUCCESS;
556 BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
557 if (!fReturnedExitCode)
558 {
559 dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
560
561 // The unit test use a thread instead of a process so try to get the exit code from
562 // the thread because we failed to get it from the process.
563 if (ERROR_INVALID_HANDLE == dwErrorCode)
564 {
565 fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
566 }
567 }
568 AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
569 (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
570 "Child elevated process did not return matching exit code to parent process.");
571#endif
572 }
573
574LExit:
575 return hr;
576}
577
578/*******************************************************************
579 PipeChildConnect - Called from the child process to connect back
580 to the pipe provided by the parent process.
581
582*******************************************************************/
583extern "C" HRESULT PipeChildConnect(
584 __in BURN_PIPE_CONNECTION* pConnection,
585 __in BOOL fConnectCachePipe
586 )
587{
588 Assert(pConnection->sczName);
589 Assert(pConnection->sczSecret);
590 Assert(!pConnection->hProcess);
591 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
592 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
593
594 HRESULT hr = S_OK;
595 LPWSTR sczPipeName = NULL;
596
597 // Try to connect to the parent.
598 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
599 ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
600
601 hr = E_UNEXPECTED;
602 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
603 {
604 pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
605 if (INVALID_HANDLE_VALUE == pConnection->hPipe)
606 {
607 hr = HRESULT_FROM_WIN32(::GetLastError());
608 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
609 {
610 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
611 }
612
613 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
614 }
615 else // we have a connection, go with it.
616 {
617 hr = S_OK;
618 }
619 }
620 ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
621
622 // Verify the parent and notify it that the child connected.
623 hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
624 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
625
626 if (fConnectCachePipe)
627 {
628 // Connect to the parent for the cache pipe.
629 hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
630 ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
631
632 pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
633 if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
634 {
635 ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName)
636 }
637
638 // Verify the parent and notify it that the child connected.
639 hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
640 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
641 }
642
643 pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
644 ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
645
646LExit:
647 ReleaseStr(sczPipeName);
648
649 return hr;
650}
651
652
653static HRESULT AllocatePipeMessage(
654 __in DWORD dwMessage,
655 __in_bcount_opt(cbData) LPVOID pvData,
656 __in SIZE_T cbData,
657 __out_bcount(cb) LPVOID* ppvMessage,
658 __out SIZE_T* cbMessage
659 )
660{
661 HRESULT hr = S_OK;
662 LPVOID pv = NULL;
663 SIZE_T cb = 0;
664
665 // If no data was provided, ensure the count of bytes is zero.
666 if (!pvData)
667 {
668 cbData = 0;
669 }
670
671 // Allocate the message.
672 cb = sizeof(dwMessage) + sizeof(cbData) + cbData;
673 pv = MemAlloc(cb, FALSE);
674 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
675
676 memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
677 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData));
678 if (cbData)
679 {
680 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData);
681 }
682
683 *cbMessage = cb;
684 *ppvMessage = pv;
685 pv = NULL;
686
687LExit:
688 ReleaseMem(pv);
689 return hr;
690}
691
692static void FreePipeMessage(
693 __in BURN_PIPE_MESSAGE *pMsg
694 )
695{
696 if (pMsg->fAllocatedData)
697 {
698 ReleaseNullMem(pMsg->pvData);
699 pMsg->fAllocatedData = FALSE;
700 }
701}
702
703static HRESULT WritePipeMessage(
704 __in HANDLE hPipe,
705 __in DWORD dwMessage,
706 __in_bcount_opt(cbData) LPVOID pvData,
707 __in SIZE_T cbData
708 )
709{
710 HRESULT hr = S_OK;
711 LPVOID pv = NULL;
712 SIZE_T cb = 0;
713
714 hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
715 ExitOnFailure(hr, "Failed to allocate message to write.");
716
717 // Write the message.
718 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(pv), cb);
719 ExitOnFailure(hr, "Failed to write message type to pipe.");
720
721LExit:
722 ReleaseMem(pv);
723 return hr;
724}
725
726static HRESULT GetPipeMessage(
727 __in HANDLE hPipe,
728 __in BURN_PIPE_MESSAGE* pMsg
729 )
730{
731 HRESULT hr = S_OK;
732 BYTE pbMessageAndByteCount[sizeof(DWORD) + sizeof(SIZE_T)] = { };
733
734 hr = FileReadHandle(hPipe, pbMessageAndByteCount, sizeof(pbMessageAndByteCount));
735 if (HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE) == hr)
736 {
737 memset(pbMessageAndByteCount, 0, sizeof(pbMessageAndByteCount));
738 hr = S_FALSE;
739 }
740 ExitOnFailure(hr, "Failed to read message from pipe.");
741
742 pMsg->dwMessage = *(DWORD*)(pbMessageAndByteCount);
743 pMsg->cbData = *(SIZE_T*)(pbMessageAndByteCount + sizeof(DWORD));
744 if (pMsg->cbData)
745 {
746 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
747 ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
748
749 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(pMsg->pvData), pMsg->cbData);
750 ExitOnFailure(hr, "Failed to read data for message.");
751
752 pMsg->fAllocatedData = TRUE;
753 }
754
755LExit:
756 if (!pMsg->fAllocatedData && pMsg->pvData)
757 {
758 MemFree(pMsg->pvData);
759 }
760
761 return hr;
762}
763
764static HRESULT ChildPipeConnected(
765 __in HANDLE hPipe,
766 __in_z LPCWSTR wzSecret,
767 __inout DWORD* pdwProcessId
768 )
769{
770 HRESULT hr = S_OK;
771 LPWSTR sczVerificationSecret = NULL;
772 DWORD cbVerificationSecret = 0;
773 DWORD dwVerificationProcessId = 0;
774 DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
775
776 // Read the verification secret.
777 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&cbVerificationSecret), sizeof(cbVerificationSecret));
778 ExitOnFailure(hr, "Failed to read size of verification secret from parent pipe.");
779
780 if (255 < cbVerificationSecret / sizeof(WCHAR))
781 {
782 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
783 ExitOnRootFailure(hr, "Verification secret from parent is too big.");
784 }
785
786 hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
787 ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
788
789 FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(sczVerificationSecret), cbVerificationSecret);
790 ExitOnFailure(hr, "Failed to read verification secret from parent pipe.");
791
792 // Verify the secrets match.
793 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
794 {
795 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
796 ExitOnRootFailure(hr, "Verification secret from parent does not match.");
797 }
798
799 // Read the verification process id.
800 hr = FileReadHandle(hPipe, reinterpret_cast<LPBYTE>(&dwVerificationProcessId), sizeof(dwVerificationProcessId));
801 ExitOnFailure(hr, "Failed to read verification process id from parent pipe.");
802
803 // If a process id was not provided, we'll trust the process id from the parent.
804 if (*pdwProcessId == 0)
805 {
806 *pdwProcessId = dwVerificationProcessId;
807 }
808 else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
809 {
810 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
811 ExitOnRootFailure(hr, "Verification process id from parent does not match.");
812 }
813
814 // All is well, tell the parent process.
815 hr = FileWriteHandle(hPipe, reinterpret_cast<LPCBYTE>(&dwAck), sizeof(dwAck));
816 ExitOnFailure(hr, "Failed to inform parent process that child is running.");
817
818LExit:
819 ReleaseStr(sczVerificationSecret);
820 return hr;
821}