diff options
author | Rob Mensching <rob@firegiant.com> | 2021-04-22 17:06:54 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2021-04-29 16:36:06 -0700 |
commit | af10c45d7b3a44af0b461a557847fe03263dcc10 (patch) | |
tree | 6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/pipe.cpp | |
parent | 9c2aed97299fb96aeee3f1471ce40225437aaecf (diff) | |
download | wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2 wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip |
Move burn into burn
Diffstat (limited to 'src/burn/engine/pipe.cpp')
-rw-r--r-- | src/burn/engine/pipe.cpp | 821 |
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 | |||
5 | static const DWORD PIPE_64KB = 64 * 1024; | ||
6 | static const DWORD PIPE_WAIT_FOR_CONNECTION = 100; // wait a 10th of a second, | ||
7 | static const DWORD PIPE_RETRY_FOR_CONNECTION = 1800; // for up to 3 minutes. | ||
8 | |||
9 | static const LPCWSTR PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls"; | ||
10 | static const LPCWSTR CACHE_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Cache"; | ||
11 | |||
12 | static 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 | ); | ||
19 | static void FreePipeMessage( | ||
20 | __in BURN_PIPE_MESSAGE *pMsg | ||
21 | ); | ||
22 | static HRESULT WritePipeMessage( | ||
23 | __in HANDLE hPipe, | ||
24 | __in DWORD dwMessage, | ||
25 | __in_bcount_opt(cbData) LPVOID pvData, | ||
26 | __in SIZE_T cbData | ||
27 | ); | ||
28 | static HRESULT GetPipeMessage( | ||
29 | __in HANDLE hPipe, | ||
30 | __in BURN_PIPE_MESSAGE* pMsg | ||
31 | ); | ||
32 | static 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 | *******************************************************************/ | ||
44 | void 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 | *******************************************************************/ | ||
57 | void 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 | *******************************************************************/ | ||
76 | extern "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 | |||
97 | LExit: | ||
98 | return hr; | ||
99 | } | ||
100 | |||
101 | /******************************************************************* | ||
102 | PipePumpMessages - | ||
103 | |||
104 | *******************************************************************/ | ||
105 | extern "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 | |||
185 | LExit: | ||
186 | ReleaseStr(sczMessage); | ||
187 | FreePipeMessage(&msg); | ||
188 | |||
189 | return hr; | ||
190 | } | ||
191 | |||
192 | /******************************************************************* | ||
193 | PipeCreateNameAndSecret - | ||
194 | |||
195 | *******************************************************************/ | ||
196 | extern "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 | |||
225 | LExit: | ||
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 | *******************************************************************/ | ||
236 | extern "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 | |||
303 | LExit: | ||
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 | *******************************************************************/ | ||
322 | const LPCWSTR BURN_COMMANDLINE_SWITCH_UNELEVATED = L"burn.unelevated"; | ||
323 | HRESULT 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 | |||
370 | LExit: | ||
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 | *******************************************************************/ | ||
384 | extern "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 | |||
411 | LExit: | ||
412 | ReleaseHandle(hProcess); | ||
413 | ReleaseStr(sczParameters); | ||
414 | |||
415 | return hr; | ||
416 | } | ||
417 | |||
418 | /******************************************************************* | ||
419 | PipeWaitForChildConnect - | ||
420 | |||
421 | *******************************************************************/ | ||
422 | extern "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 | |||
510 | LExit: | ||
511 | return hr; | ||
512 | } | ||
513 | |||
514 | /******************************************************************* | ||
515 | PipeTerminateChildProcess - | ||
516 | |||
517 | *******************************************************************/ | ||
518 | extern "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 | |||
574 | LExit: | ||
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 | *******************************************************************/ | ||
583 | extern "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 | |||
646 | LExit: | ||
647 | ReleaseStr(sczPipeName); | ||
648 | |||
649 | return hr; | ||
650 | } | ||
651 | |||
652 | |||
653 | static 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 | |||
687 | LExit: | ||
688 | ReleaseMem(pv); | ||
689 | return hr; | ||
690 | } | ||
691 | |||
692 | static 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 | |||
703 | static 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 | |||
721 | LExit: | ||
722 | ReleaseMem(pv); | ||
723 | return hr; | ||
724 | } | ||
725 | |||
726 | static 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 | |||
755 | LExit: | ||
756 | if (!pMsg->fAllocatedData && pMsg->pvData) | ||
757 | { | ||
758 | MemFree(pMsg->pvData); | ||
759 | } | ||
760 | |||
761 | return hr; | ||
762 | } | ||
763 | |||
764 | static 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 | |||
818 | LExit: | ||
819 | ReleaseStr(sczVerificationSecret); | ||
820 | return hr; | ||
821 | } | ||