aboutsummaryrefslogtreecommitdiff
path: root/src/engine/pipe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/pipe.cpp')
-rw-r--r--src/engine/pipe.cpp873
1 files changed, 873 insertions, 0 deletions
diff --git a/src/engine/pipe.cpp b/src/engine/pipe.cpp
new file mode 100644
index 00000000..7ecc4859
--- /dev/null
+++ b/src/engine/pipe.cpp
@@ -0,0 +1,873 @@
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 DWORD cbData,
16 __out_bcount(cb) LPVOID* ppvMessage,
17 __out DWORD* 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 DWORD 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 DWORD 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 OS_VERSION osVersion = OS_VERSION_UNKNOWN;
395 DWORD dwServicePack = 0;
396 LPCWSTR wzVerb = NULL;
397 HANDLE hProcess = NULL;
398
399 hr = StrAllocFormatted(&sczParameters, L"-q -%ls %ls %ls %u", BURN_COMMANDLINE_SWITCH_ELEVATED, pConnection->sczName, pConnection->sczSecret, dwCurrentProcessId);
400 ExitOnFailure(hr, "Failed to allocate parameters for elevated process.");
401
402 OsGetVersion(&osVersion, &dwServicePack);
403 wzVerb = (OS_VERSION_VISTA > osVersion) || !fElevate ? L"open" : L"runas";
404
405 // Since ShellExecuteEx doesn't support passing inherited handles, don't bother with CoreAppendFileHandleSelfToCommandLine.
406 // We could fallback to using ::DuplicateHandle to inject the file handle later if necessary.
407 hr = ShelExec(wzExecutablePath, sczParameters, wzVerb, NULL, SW_HIDE, hwndParent, &hProcess);
408 ExitOnFailure(hr, "Failed to launch elevated child process: %ls", wzExecutablePath);
409
410 pConnection->dwProcessId = ::GetProcessId(hProcess);
411 pConnection->hProcess = hProcess;
412 hProcess = NULL;
413
414LExit:
415 ReleaseHandle(hProcess);
416 ReleaseStr(sczParameters);
417
418 return hr;
419}
420
421/*******************************************************************
422 PipeWaitForChildConnect -
423
424*******************************************************************/
425extern "C" HRESULT PipeWaitForChildConnect(
426 __in BURN_PIPE_CONNECTION* pConnection
427 )
428{
429 HRESULT hr = S_OK;
430 HANDLE hPipes[2] = { pConnection->hPipe, pConnection->hCachePipe};
431 LPCWSTR wzSecret = pConnection->sczSecret;
432 DWORD cbSecret = lstrlenW(wzSecret) * sizeof(WCHAR);
433 DWORD dwCurrentProcessId = ::GetCurrentProcessId();
434 DWORD dwAck = 0;
435 DWORD cb = 0;
436
437 for (DWORD i = 0; i < countof(hPipes) && INVALID_HANDLE_VALUE != hPipes[i]; ++i)
438 {
439 HANDLE hPipe = hPipes[i];
440 DWORD dwPipeState = PIPE_READMODE_BYTE | PIPE_NOWAIT;
441
442 // Temporarily make the pipe non-blocking so we will not get stuck in ::ConnectNamedPipe() forever
443 // if the child decides not to show up.
444 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
445 {
446 ExitWithLastError(hr, "Failed to set pipe to non-blocking.");
447 }
448
449 // Loop for a while waiting for a connection from child process.
450 DWORD cRetry = 0;
451 do
452 {
453 if (!::ConnectNamedPipe(hPipe, NULL))
454 {
455 DWORD er = ::GetLastError();
456 if (ERROR_PIPE_CONNECTED == er)
457 {
458 hr = S_OK;
459 break;
460 }
461 else if (ERROR_PIPE_LISTENING == er)
462 {
463 if (cRetry < PIPE_RETRY_FOR_CONNECTION)
464 {
465 hr = HRESULT_FROM_WIN32(er);
466 }
467 else
468 {
469 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
470 break;
471 }
472
473 ++cRetry;
474 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
475 }
476 else
477 {
478 hr = HRESULT_FROM_WIN32(er);
479 break;
480 }
481 }
482 } while (HRESULT_FROM_WIN32(ERROR_PIPE_LISTENING) == hr);
483 ExitOnRootFailure(hr, "Failed to wait for child to connect to pipe.");
484
485 // Put the pipe back in blocking mode.
486 dwPipeState = PIPE_READMODE_BYTE | PIPE_WAIT;
487 if (!::SetNamedPipeHandleState(hPipe, &dwPipeState, NULL, NULL))
488 {
489 ExitWithLastError(hr, "Failed to reset pipe to blocking.");
490 }
491
492 // Prove we are the one that created the elevated process by passing the secret.
493 if (!::WriteFile(hPipe, &cbSecret, sizeof(cbSecret), &cb, NULL))
494 {
495 ExitWithLastError(hr, "Failed to write secret length to pipe.");
496 }
497
498 if (!::WriteFile(hPipe, wzSecret, cbSecret, &cb, NULL))
499 {
500 ExitWithLastError(hr, "Failed to write secret to pipe.");
501 }
502
503 if (!::WriteFile(hPipe, &dwCurrentProcessId, sizeof(dwCurrentProcessId), &cb, NULL))
504 {
505 ExitWithLastError(hr, "Failed to write our process id to pipe.");
506 }
507
508 // Wait until the elevated process responds that it is ready to go.
509 if (!::ReadFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
510 {
511 ExitWithLastError(hr, "Failed to read ACK from pipe.");
512 }
513
514 // The ACK should match out expected child process id.
515 //if (pConnection->dwProcessId != dwAck)
516 //{
517 // hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
518 // ExitOnRootFailure(hr, "Incorrect ACK from elevated pipe: %u", dwAck);
519 //}
520 }
521
522LExit:
523 return hr;
524}
525
526/*******************************************************************
527 PipeTerminateChildProcess -
528
529*******************************************************************/
530extern "C" HRESULT PipeTerminateChildProcess(
531 __in BURN_PIPE_CONNECTION* pConnection,
532 __in DWORD dwParentExitCode,
533 __in BOOL fRestart
534 )
535{
536 HRESULT hr = S_OK;
537 BYTE* pbData = NULL;
538 SIZE_T cbData = 0;
539
540 // Prepare the exit message.
541 hr = BuffWriteNumber(&pbData, &cbData, dwParentExitCode);
542 ExitOnFailure(hr, "Failed to write exit code to message buffer.");
543
544 hr = BuffWriteNumber(&pbData, &cbData, fRestart);
545 ExitOnFailure(hr, "Failed to write restart to message buffer.");
546
547 // Send the messages.
548 if (INVALID_HANDLE_VALUE != pConnection->hCachePipe)
549 {
550 hr = WritePipeMessage(pConnection->hCachePipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
551 ExitOnFailure(hr, "Failed to post terminate message to child process cache thread.");
552 }
553
554 hr = WritePipeMessage(pConnection->hPipe, static_cast<DWORD>(BURN_PIPE_MESSAGE_TYPE_TERMINATE), pbData, cbData);
555 ExitOnFailure(hr, "Failed to post terminate message to child process.");
556
557 // If we were able to get a handle to the other process, wait for it to exit.
558 if (pConnection->hProcess)
559 {
560 if (WAIT_FAILED == ::WaitForSingleObject(pConnection->hProcess, PIPE_WAIT_FOR_CONNECTION * PIPE_RETRY_FOR_CONNECTION))
561 {
562 ExitWithLastError(hr, "Failed to wait for child process exit.");
563 }
564
565#ifdef DEBUG
566 DWORD dwChildExitCode = 0;
567 DWORD dwErrorCode = ERROR_SUCCESS;
568 BOOL fReturnedExitCode = ::GetExitCodeProcess(pConnection->hProcess, &dwChildExitCode);
569 if (!fReturnedExitCode)
570 {
571 dwErrorCode = ::GetLastError(); // if the other process is elevated and we are not, then we'll get ERROR_ACCESS_DENIED.
572
573 // The unit test use a thread instead of a process so try to get the exit code from
574 // the thread because we failed to get it from the process.
575 if (ERROR_INVALID_HANDLE == dwErrorCode)
576 {
577 fReturnedExitCode = ::GetExitCodeThread(pConnection->hProcess, &dwChildExitCode);
578 }
579 }
580 AssertSz((fReturnedExitCode && dwChildExitCode == dwParentExitCode) ||
581 (!fReturnedExitCode && ERROR_ACCESS_DENIED == dwErrorCode),
582 "Child elevated process did not return matching exit code to parent process.");
583#endif
584 }
585
586LExit:
587 return hr;
588}
589
590/*******************************************************************
591 PipeChildConnect - Called from the child process to connect back
592 to the pipe provided by the parent process.
593
594*******************************************************************/
595extern "C" HRESULT PipeChildConnect(
596 __in BURN_PIPE_CONNECTION* pConnection,
597 __in BOOL fConnectCachePipe
598 )
599{
600 Assert(pConnection->sczName);
601 Assert(pConnection->sczSecret);
602 Assert(!pConnection->hProcess);
603 Assert(INVALID_HANDLE_VALUE == pConnection->hPipe);
604 Assert(INVALID_HANDLE_VALUE == pConnection->hCachePipe);
605
606 HRESULT hr = S_OK;
607 LPWSTR sczPipeName = NULL;
608
609 // Try to connect to the parent.
610 hr = StrAllocFormatted(&sczPipeName, PIPE_NAME_FORMAT_STRING, pConnection->sczName);
611 ExitOnFailure(hr, "Failed to allocate name of parent pipe.");
612
613 hr = E_UNEXPECTED;
614 for (DWORD cRetry = 0; FAILED(hr) && cRetry < PIPE_RETRY_FOR_CONNECTION; ++cRetry)
615 {
616 pConnection->hPipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
617 if (INVALID_HANDLE_VALUE == pConnection->hPipe)
618 {
619 hr = HRESULT_FROM_WIN32(::GetLastError());
620 if (E_FILENOTFOUND == hr) // if the pipe isn't created, call it a timeout waiting on the parent.
621 {
622 hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT);
623 }
624
625 ::Sleep(PIPE_WAIT_FOR_CONNECTION);
626 }
627 else // we have a connection, go with it.
628 {
629 hr = S_OK;
630 }
631 }
632 ExitOnRootFailure(hr, "Failed to open parent pipe: %ls", sczPipeName)
633
634 // Verify the parent and notify it that the child connected.
635 hr = ChildPipeConnected(pConnection->hPipe, pConnection->sczSecret, &pConnection->dwProcessId);
636 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
637
638 if (fConnectCachePipe)
639 {
640 // Connect to the parent for the cache pipe.
641 hr = StrAllocFormatted(&sczPipeName, CACHE_PIPE_NAME_FORMAT_STRING, pConnection->sczName);
642 ExitOnFailure(hr, "Failed to allocate name of parent cache pipe.");
643
644 pConnection->hCachePipe = ::CreateFileW(sczPipeName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
645 if (INVALID_HANDLE_VALUE == pConnection->hCachePipe)
646 {
647 ExitWithLastError(hr, "Failed to open parent pipe: %ls", sczPipeName)
648 }
649
650 // Verify the parent and notify it that the child connected.
651 hr = ChildPipeConnected(pConnection->hCachePipe, pConnection->sczSecret, &pConnection->dwProcessId);
652 ExitOnFailure(hr, "Failed to verify parent pipe: %ls", sczPipeName);
653 }
654
655 pConnection->hProcess = ::OpenProcess(SYNCHRONIZE, FALSE, pConnection->dwProcessId);
656 ExitOnNullWithLastError(pConnection->hProcess, hr, "Failed to open companion process with PID: %u", pConnection->dwProcessId);
657
658LExit:
659 ReleaseStr(sczPipeName);
660
661 return hr;
662}
663
664
665static HRESULT AllocatePipeMessage(
666 __in DWORD dwMessage,
667 __in_bcount_opt(cbData) LPVOID pvData,
668 __in DWORD cbData,
669 __out_bcount(cb) LPVOID* ppvMessage,
670 __out DWORD* cbMessage
671 )
672{
673 HRESULT hr = S_OK;
674 LPVOID pv = NULL;
675 DWORD cb = 0;
676
677 // If no data was provided, ensure the count of bytes is zero.
678 if (!pvData)
679 {
680 cbData = 0;
681 }
682
683 // Allocate the message.
684 cb = sizeof(dwMessage) + sizeof(cbData) + cbData;
685 pv = MemAlloc(cb, FALSE);
686 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for message.");
687
688 memcpy_s(pv, cb, &dwMessage, sizeof(dwMessage));
689 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage), cb - sizeof(dwMessage), &cbData, sizeof(cbData));
690 if (cbData)
691 {
692 memcpy_s(static_cast<BYTE*>(pv) + sizeof(dwMessage) + sizeof(cbData), cb - sizeof(dwMessage) - sizeof(cbData), pvData, cbData);
693 }
694
695 *cbMessage = cb;
696 *ppvMessage = pv;
697 pv = NULL;
698
699LExit:
700 ReleaseMem(pv);
701 return hr;
702}
703
704static void FreePipeMessage(
705 __in BURN_PIPE_MESSAGE *pMsg
706 )
707{
708 if (pMsg->fAllocatedData)
709 {
710 ReleaseNullMem(pMsg->pvData);
711 pMsg->fAllocatedData = FALSE;
712 }
713}
714
715static HRESULT WritePipeMessage(
716 __in HANDLE hPipe,
717 __in DWORD dwMessage,
718 __in_bcount_opt(cbData) LPVOID pvData,
719 __in DWORD cbData
720 )
721{
722 HRESULT hr = S_OK;
723 LPVOID pv = NULL;
724 DWORD cb = 0;
725
726 hr = AllocatePipeMessage(dwMessage, pvData, cbData, &pv, &cb);
727 ExitOnFailure(hr, "Failed to allocate message to write.");
728
729 // Write the message.
730 DWORD cbWrote = 0;
731 DWORD cbTotalWritten = 0;
732 while (cbTotalWritten < cb)
733 {
734 if (!::WriteFile(hPipe, pv, cb - cbTotalWritten, &cbWrote, NULL))
735 {
736 ExitWithLastError(hr, "Failed to write message type to pipe.");
737 }
738
739 cbTotalWritten += cbWrote;
740 }
741
742LExit:
743 ReleaseMem(pv);
744 return hr;
745}
746
747static HRESULT GetPipeMessage(
748 __in HANDLE hPipe,
749 __in BURN_PIPE_MESSAGE* pMsg
750 )
751{
752 HRESULT hr = S_OK;
753 DWORD rgdwMessageAndByteCount[2] = { };
754 DWORD cb = 0;
755 DWORD cbRead = 0;
756
757 while (cbRead < sizeof(rgdwMessageAndByteCount))
758 {
759 if (!::ReadFile(hPipe, reinterpret_cast<BYTE*>(rgdwMessageAndByteCount) + cbRead, sizeof(rgdwMessageAndByteCount) - cbRead, &cb, NULL))
760 {
761 DWORD er = ::GetLastError();
762 if (ERROR_MORE_DATA == er)
763 {
764 hr = S_OK;
765 }
766 else if (ERROR_BROKEN_PIPE == er) // parent process shut down, time to exit.
767 {
768 memset(rgdwMessageAndByteCount, 0, sizeof(rgdwMessageAndByteCount));
769 hr = S_FALSE;
770 break;
771 }
772 else
773 {
774 hr = HRESULT_FROM_WIN32(er);
775 }
776 ExitOnRootFailure(hr, "Failed to read message from pipe.");
777 }
778
779 cbRead += cb;
780 }
781
782 pMsg->dwMessage = rgdwMessageAndByteCount[0];
783 pMsg->cbData = rgdwMessageAndByteCount[1];
784 if (pMsg->cbData)
785 {
786 pMsg->pvData = MemAlloc(pMsg->cbData, FALSE);
787 ExitOnNull(pMsg->pvData, hr, E_OUTOFMEMORY, "Failed to allocate data for message.");
788
789 if (!::ReadFile(hPipe, pMsg->pvData, pMsg->cbData, &cb, NULL))
790 {
791 ExitWithLastError(hr, "Failed to read data for message.");
792 }
793
794 pMsg->fAllocatedData = TRUE;
795 }
796
797LExit:
798 if (!pMsg->fAllocatedData && pMsg->pvData)
799 {
800 MemFree(pMsg->pvData);
801 }
802
803 return hr;
804}
805
806static HRESULT ChildPipeConnected(
807 __in HANDLE hPipe,
808 __in_z LPCWSTR wzSecret,
809 __inout DWORD* pdwProcessId
810 )
811{
812 HRESULT hr = S_OK;
813 LPWSTR sczVerificationSecret = NULL;
814 DWORD cbVerificationSecret = 0;
815 DWORD dwVerificationProcessId = 0;
816 DWORD dwRead = 0;
817 DWORD dwAck = ::GetCurrentProcessId(); // send our process id as the ACK.
818 DWORD cb = 0;
819
820 // Read the verification secret.
821 if (!::ReadFile(hPipe, &cbVerificationSecret, sizeof(cbVerificationSecret), &dwRead, NULL))
822 {
823 ExitWithLastError(hr, "Failed to read size of verification secret from parent pipe.");
824 }
825
826 if (255 < cbVerificationSecret / sizeof(WCHAR))
827 {
828 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
829 ExitOnRootFailure(hr, "Verification secret from parent is too big.");
830 }
831
832 hr = StrAlloc(&sczVerificationSecret, cbVerificationSecret / sizeof(WCHAR) + 1);
833 ExitOnFailure(hr, "Failed to allocate buffer for verification secret.");
834
835 if (!::ReadFile(hPipe, sczVerificationSecret, cbVerificationSecret, &dwRead, NULL))
836 {
837 ExitWithLastError(hr, "Failed to read verification secret from parent pipe.");
838 }
839
840 // Verify the secrets match.
841 if (CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, 0, sczVerificationSecret, -1, wzSecret, -1))
842 {
843 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
844 ExitOnRootFailure(hr, "Verification secret from parent does not match.");
845 }
846
847 // Read the verification process id.
848 if (!::ReadFile(hPipe, &dwVerificationProcessId, sizeof(dwVerificationProcessId), &dwRead, NULL))
849 {
850 ExitWithLastError(hr, "Failed to read verification process id from parent pipe.");
851 }
852
853 // If a process id was not provided, we'll trust the process id from the parent.
854 if (*pdwProcessId == 0)
855 {
856 *pdwProcessId = dwVerificationProcessId;
857 }
858 else if (*pdwProcessId != dwVerificationProcessId) // verify the ids match.
859 {
860 hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
861 ExitOnRootFailure(hr, "Verification process id from parent does not match.");
862 }
863
864 // All is well, tell the parent process.
865 if (!::WriteFile(hPipe, &dwAck, sizeof(dwAck), &cb, NULL))
866 {
867 ExitWithLastError(hr, "Failed to inform parent process that child is running.");
868 }
869
870LExit:
871 ReleaseStr(sczVerificationSecret);
872 return hr;
873}