diff options
author | Rob Mensching <rob@firegiant.com> | 2024-01-10 23:48:40 -0800 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2024-01-11 18:24:06 -0800 |
commit | 57b7689b2dc5e971911d1c6d48eebb5424ebca89 (patch) | |
tree | 5a40c4ec7cec3548a119c6ef1a2a274c2258f627 /src/burn/engine/pipe.cpp | |
parent | f676ceeaedbba4125d3e0c691959afcba182d008 (diff) | |
download | wix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.tar.gz wix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.tar.bz2 wix-57b7689b2dc5e971911d1c6d48eebb5424ebca89.zip |
Rename inter-Burn communication related pipe file
When BootstrapperApplications move out of proc there will be another set of
pipes in Burn that behave differently from the inter-Burn communication pipes
that exist now. So renaming the existing pipe related files now to better
reflect that it is related to Burn talking to Burn when elevated or embedded.
Diffstat (limited to 'src/burn/engine/pipe.cpp')
-rw-r--r-- | src/burn/engine/pipe.cpp | 782 |
1 files changed, 0 insertions, 782 deletions
diff --git a/src/burn/engine/pipe.cpp b/src/burn/engine/pipe.cpp deleted file mode 100644 index 5003622c..00000000 --- a/src/burn/engine/pipe.cpp +++ /dev/null | |||
@@ -1,782 +0,0 @@ | |||
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 | static const LPCWSTR LOGGING_PIPE_NAME_FORMAT_STRING = L"\\\\.\\pipe\\%ls.Log"; | ||
12 | |||
13 | static 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 | ); | ||
20 | static void FreePipeMessage( | ||
21 | __in BURN_PIPE_MESSAGE *pMsg | ||
22 | ); | ||
23 | static HRESULT WritePipeMessage( | ||
24 | __in HANDLE hPipe, | ||
25 | __in DWORD dwMessage, | ||
26 | __in_bcount_opt(cbData) LPVOID pvData, | ||
27 | __in SIZE_T cbData | ||
28 | ); | ||
29 | static HRESULT GetPipeMessage( | ||
30 | __in HANDLE hPipe, | ||
31 | __in BURN_PIPE_MESSAGE* pMsg | ||
32 | ); | ||
33 | static 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 | *******************************************************************/ | ||
45 | void 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 | *******************************************************************/ | ||
59 | void 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 | *******************************************************************/ | ||
77 | extern "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 | |||
98 | LExit: | ||
99 | return hr; | ||
100 | } | ||
101 | |||
102 | /******************************************************************* | ||
103 | PipePumpMessages - | ||
104 | |||
105 | *******************************************************************/ | ||
106 | extern "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 | |||
186 | LExit: | ||
187 | ReleaseStr(sczMessage); | ||
188 | FreePipeMessage(&msg); | ||
189 | |||
190 | return hr; | ||
191 | } | ||
192 | |||
193 | /******************************************************************* | ||
194 | PipeCreateNameAndSecret - | ||
195 | |||
196 | *******************************************************************/ | ||
197 | extern "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 | |||
226 | LExit: | ||
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 | *******************************************************************/ | ||
237 | extern "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 | |||
314 | LExit: | ||
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 | *******************************************************************/ | ||
332 | extern "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 | |||
420 | LExit: | ||
421 | return hr; | ||
422 | } | ||
423 | |||
424 | /******************************************************************* | ||
425 | PipeTerminateLoggingPipe - | ||
426 | |||
427 | *******************************************************************/ | ||
428 | extern "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 | |||
444 | LExit: | ||
445 | ReleaseBuffer(pbData); | ||
446 | |||
447 | return hr; | ||
448 | } | ||
449 | |||
450 | /******************************************************************* | ||
451 | PipeTerminateChildProcess - | ||
452 | |||
453 | *******************************************************************/ | ||
454 | extern "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 | |||
508 | LExit: | ||
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 | *******************************************************************/ | ||
519 | extern "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 | |||
597 | LExit: | ||
598 | ReleaseStr(sczPipeName); | ||
599 | |||
600 | return hr; | ||
601 | } | ||
602 | |||
603 | |||
604 | static 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 | |||
647 | LExit: | ||
648 | ReleaseMem(pv); | ||
649 | return hr; | ||
650 | } | ||
651 | |||
652 | static 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 | |||
663 | static 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 | |||
681 | LExit: | ||
682 | ReleaseMem(pv); | ||
683 | return hr; | ||
684 | } | ||
685 | |||
686 | static 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 | |||
715 | LExit: | ||
716 | if (!pMsg->fAllocatedData && pMsg->pvData) | ||
717 | { | ||
718 | MemFree(pMsg->pvData); | ||
719 | } | ||
720 | |||
721 | return hr; | ||
722 | } | ||
723 | |||
724 | static 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 | |||
779 | LExit: | ||
780 | ReleaseStr(sczVerificationSecret); | ||
781 | return hr; | ||
782 | } | ||