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