main
cpp 214 lines 6.04 KB
Raw
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 const DWORD TEST_CHILD_SENT_MESSAGE_ID = 0xFFFE;
6 const DWORD TEST_PARENT_SENT_MESSAGE_ID = 0xFFFF;
7 const HRESULT S_TEST_SUCCEEDED = 0x3133;
8 const char TEST_MESSAGE_DATA[] = "{94949868-7EAE-4ac5-BEAC-AFCA2821DE01}";
9
10
11 static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
12 __inout LPSHELLEXECUTEINFOW lpExecInfo
13 );
14 static DWORD CALLBACK ElevateTest_ThreadProc(
15 __in LPVOID lpThreadParameter
16 );
17 static HRESULT ProcessParentMessages(
18 __in PIPE_MESSAGE* pMsg,
19 __in_opt LPVOID pvContext,
20 __out DWORD* pdwResult
21 );
22 static HRESULT ProcessChildMessages(
23 __in PIPE_MESSAGE* pMsg,
24 __in_opt LPVOID pvContext,
25 __out DWORD* pdwResult
26 );
27
28 namespace Microsoft
29 {
30 namespace Tools
31 {
32 namespace WindowsInstallerXml
33 {
34 namespace Test
35 {
36 namespace Bootstrapper
37 {
38 using namespace System;
39 using namespace System::IO;
40 using namespace System::Threading;
41 using namespace Xunit;
42
43 public ref class ElevationTest : BurnUnitTest
44 {
45 public:
46 ElevationTest(BurnTestFixture^ fixture) : BurnUnitTest(fixture)
47 {
48 }
49
50 [Fact]
51 void ElevateTest()
52 {
53 HRESULT hr = S_OK;
54 BURN_ENGINE_STATE engineState = { };
55 BURN_PIPE_CONNECTION* pConnection = &engineState.companionConnection;
56 DWORD dwResult = S_OK;
57
58 engineState.sczBundleEngineWorkingPath = L"tests\\ignore\\this\\path\\to\\burn.exe";
59
60 try
61 {
62 ShelFunctionOverride(ElevateTest_ShellExecuteExW);
63 CoreFunctionOverride(NULL, ThrdWaitForCompletion);
64
65 BurnPipeConnectionInitialize(pConnection);
66
67 //
68 // per-user side setup
69 //
70 hr = ElevationElevate(&engineState, WM_BURN_ELEVATE, NULL);
71 TestThrowOnFailure(hr, L"Failed to elevate.");
72
73 // post execute message
74 hr = BurnPipeSendMessage(pConnection->hPipe, TEST_PARENT_SENT_MESSAGE_ID, NULL, 0, ProcessParentMessages, NULL, &dwResult);
75 TestThrowOnFailure(hr, "Failed to post execute message to per-machine process.");
76
77 //
78 // initiate termination
79 //
80 hr = BurnPipeTerminateChildProcess(pConnection, 666, FALSE);
81 TestThrowOnFailure(hr, L"Failed to terminate elevated process.");
82
83 // check flags
84 Assert::Equal(S_TEST_SUCCEEDED, (HRESULT)dwResult);
85 }
86 finally
87 {
88 BurnPipeConnectionUninitialize(pConnection);
89 }
90 }
91 };
92 }
93 }
94 }
95 }
96 }
97
98
99 static BOOL STDAPICALLTYPE ElevateTest_ShellExecuteExW(
100 __inout LPSHELLEXECUTEINFOW lpExecInfo
101 )
102 {
103 HRESULT hr = S_OK;
104 LPWSTR scz = NULL;
105
106 hr = StrAllocString(&scz, lpExecInfo->lpParameters, 0);
107 ExitOnFailure(hr, "Failed to copy arguments.");
108
109 // Pretend this thread is the elevated process.
110 lpExecInfo->hProcess = ::CreateThread(NULL, 0, ElevateTest_ThreadProc, scz, 0, NULL);
111 ExitOnNullWithLastError(lpExecInfo->hProcess, hr, "Failed to create thread.");
112 scz = NULL;
113
114 LExit:
115 ReleaseStr(scz);
116
117 return SUCCEEDED(hr);
118 }
119
120 static DWORD CALLBACK ElevateTest_ThreadProc(
121 __in LPVOID lpThreadParameter
122 )
123 {
124 HRESULT hr = S_OK;
125 LPWSTR sczArguments = (LPWSTR)lpThreadParameter;
126 BURN_PIPE_CONNECTION connection = { };
127 BURN_PIPE_RESULT result = { };
128
129 BurnPipeConnectionInitialize(&connection);
130
131 StrAlloc(&connection.sczName, MAX_PATH);
132 StrAlloc(&connection.sczSecret, MAX_PATH);
133
134 // parse command line arguments
135 if (3 != swscanf_s(sczArguments, L"-q -burn.elevated %s %s %u", connection.sczName, MAX_PATH, connection.sczSecret, MAX_PATH, &connection.dwProcessId))
136 {
137 hr = E_INVALIDARG;
138 ExitOnFailure(hr, "Failed to parse argument string.");
139 }
140
141 // set up connection with per-user process
142 hr = BurnPipeChildConnect(&connection, TRUE);
143 ExitOnFailure(hr, "Failed to connect to per-user process.");
144
145 // pump messages
146 hr = BurnPipePumpMessages(connection.hPipe, ProcessChildMessages, static_cast<LPVOID>(connection.hPipe), &result);
147 ExitOnFailure(hr, "Failed while pumping messages in child 'process'.");
148
149 LExit:
150 BurnPipeConnectionUninitialize(&connection);
151 ReleaseStr(sczArguments);
152
153 return FAILED(hr) ? (DWORD)hr : result.dwResult;
154 }
155
156 static HRESULT ProcessParentMessages(
157 __in PIPE_MESSAGE* pMsg,
158 __in_opt LPVOID /*pvContext*/,
159 __out DWORD* pdwResult
160 )
161 {
162 HRESULT hr = S_OK;
163 HRESULT hrResult = E_INVALIDDATA;
164
165 // Process the message.
166 switch (pMsg->dwMessageType)
167 {
168 case TEST_CHILD_SENT_MESSAGE_ID:
169 if (sizeof(TEST_MESSAGE_DATA) == pMsg->cbData && 0 == memcmp(TEST_MESSAGE_DATA, pMsg->pvData, sizeof(TEST_MESSAGE_DATA)))
170 {
171 hrResult = S_TEST_SUCCEEDED;
172 }
173 break;
174
175 default:
176 hr = E_INVALIDARG;
177 ExitOnRootFailure(hr, "Unexpected elevated message sent to parent process, msg: %u", pMsg->dwMessageType);
178 }
179
180 *pdwResult = static_cast<DWORD>(hrResult);
181
182 LExit:
183 return hr;
184 }
185
186 static HRESULT ProcessChildMessages(
187 __in PIPE_MESSAGE* pMsg,
188 __in_opt LPVOID pvContext,
189 __out DWORD* pdwResult
190 )
191 {
192 HRESULT hr = S_OK;
193 HANDLE hPipe = static_cast<HANDLE>(pvContext);
194 DWORD dwResult = 0;
195
196 // Process the message.
197 switch (pMsg->dwMessageType)
198 {
199 case TEST_PARENT_SENT_MESSAGE_ID:
200 // send test message
201 hr = BurnPipeSendMessage(hPipe, TEST_CHILD_SENT_MESSAGE_ID, (LPVOID)TEST_MESSAGE_DATA, sizeof(TEST_MESSAGE_DATA), NULL, NULL, &dwResult);
202 ExitOnFailure(hr, "Failed to send message to per-machine process.");
203 break;
204
205 default:
206 hr = E_INVALIDARG;
207 ExitOnRootFailure(hr, "Unexpected elevated message sent to child process, msg: %u", pMsg->dwMessageType);
208 }
209
210 *pdwResult = dwResult;
211
212 LExit:
213 return hr;
214 }