main
cpp 283 lines 8.6 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
6 // function definitions
7
8 extern "C" HRESULT ApprovedExesParseFromXml(
9 __in BURN_APPROVED_EXES* pApprovedExes,
10 __in IXMLDOMNode* pixnBundle
11 )
12 {
13 HRESULT hr = S_OK;
14 IXMLDOMNodeList* pixnNodes = NULL;
15 IXMLDOMNode* pixnNode = NULL;
16 DWORD cNodes = 0;
17 LPWSTR scz = NULL;
18
19 // select approved exe nodes
20 hr = XmlSelectNodes(pixnBundle, L"ApprovedExeForElevation", &pixnNodes);
21 ExitOnFailure(hr, "Failed to select approved exe nodes.");
22
23 // get approved exe node count
24 hr = pixnNodes->get_length((long*)&cNodes);
25 ExitOnFailure(hr, "Failed to get approved exe node count.");
26
27 if (!cNodes)
28 {
29 ExitFunction();
30 }
31
32 // allocate memory for approved exes
33 pApprovedExes->rgApprovedExes = (BURN_APPROVED_EXE*)MemAlloc(sizeof(BURN_APPROVED_EXE) * cNodes, TRUE);
34 ExitOnNull(pApprovedExes->rgApprovedExes, hr, E_OUTOFMEMORY, "Failed to allocate memory for approved exe structs.");
35
36 pApprovedExes->cApprovedExes = cNodes;
37
38 // parse approved exe elements
39 for (DWORD i = 0; i < cNodes; ++i)
40 {
41 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
42
43 hr = XmlNextElement(pixnNodes, &pixnNode, NULL);
44 ExitOnFailure(hr, "Failed to get next node.");
45
46 // @Id
47 hr = XmlGetAttributeEx(pixnNode, L"Id", &pApprovedExe->sczId);
48 ExitOnFailure(hr, "Failed to get @Id.");
49
50 // @Key
51 hr = XmlGetAttributeEx(pixnNode, L"Key", &pApprovedExe->sczKey);
52 ExitOnFailure(hr, "Failed to get @Key.");
53
54 // @ValueName
55 hr = XmlGetAttributeEx(pixnNode, L"ValueName", &pApprovedExe->sczValueName);
56 if (E_NOTFOUND != hr)
57 {
58 ExitOnFailure(hr, "Failed to get @ValueName.");
59 }
60
61 // @Win64
62 hr = XmlGetYesNoAttribute(pixnNode, L"Win64", &pApprovedExe->fWin64);
63 if (E_NOTFOUND != hr)
64 {
65 ExitOnFailure(hr, "Failed to get @Win64.");
66 }
67
68 // prepare next iteration
69 ReleaseNullObject(pixnNode);
70 ReleaseNullStr(scz);
71 }
72
73 hr = S_OK;
74
75 LExit:
76 ReleaseObject(pixnNodes);
77 ReleaseObject(pixnNode);
78 ReleaseStr(scz);
79 return hr;
80 }
81
82 extern "C" void ApprovedExesUninitialize(
83 __in BURN_APPROVED_EXES* pApprovedExes
84 )
85 {
86 if (pApprovedExes->rgApprovedExes)
87 {
88 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
89 {
90 BURN_APPROVED_EXE* pApprovedExe = &pApprovedExes->rgApprovedExes[i];
91
92 ReleaseStr(pApprovedExe->sczId);
93 ReleaseStr(pApprovedExe->sczKey);
94 ReleaseStr(pApprovedExe->sczValueName);
95 }
96 MemFree(pApprovedExes->rgApprovedExes);
97 }
98 }
99
100 extern "C" void ApprovedExesUninitializeLaunch(
101 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe
102 )
103 {
104 if (pLaunchApprovedExe)
105 {
106 ReleaseStr(pLaunchApprovedExe->sczArguments);
107 ReleaseStr(pLaunchApprovedExe->sczExecutablePath);
108 ReleaseStr(pLaunchApprovedExe->sczId);
109 }
110 }
111
112 extern "C" HRESULT ApprovedExesFindById(
113 __in BURN_APPROVED_EXES* pApprovedExes,
114 __in_z LPCWSTR wzId,
115 __out BURN_APPROVED_EXE** ppApprovedExe
116 )
117 {
118 HRESULT hr = S_OK;
119 BURN_APPROVED_EXE* pApprovedExe = NULL;
120
121 for (DWORD i = 0; i < pApprovedExes->cApprovedExes; ++i)
122 {
123 pApprovedExe = &pApprovedExes->rgApprovedExes[i];
124
125 if (CSTR_EQUAL == ::CompareStringW(LOCALE_INVARIANT, 0, pApprovedExe->sczId, -1, wzId, -1))
126 {
127 *ppApprovedExe = pApprovedExe;
128 ExitFunction1(hr = S_OK);
129 }
130 }
131
132 hr = E_NOTFOUND;
133
134 LExit:
135 return hr;
136 }
137
138 extern "C" HRESULT ApprovedExesLaunch(
139 __in BURN_VARIABLES* pVariables,
140 __in BURN_LAUNCH_APPROVED_EXE* pLaunchApprovedExe,
141 __out DWORD* pdwProcessId
142 )
143 {
144 HRESULT hr = S_OK;
145 LPWSTR sczArgumentsFormatted = NULL;
146 LPWSTR sczArgumentsObfuscated = NULL;
147 LPWSTR sczCommand = NULL;
148 LPWSTR sczCommandObfuscated = NULL;
149 LPWSTR sczExecutableDirectory = NULL;
150 size_t cchExecutableDirectory = 0;
151 STARTUPINFOW si = { };
152 PROCESS_INFORMATION pi = { };
153
154 // build command
155 if (pLaunchApprovedExe->sczArguments && *pLaunchApprovedExe->sczArguments)
156 {
157 hr = VariableFormatString(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsFormatted, NULL);
158 ExitOnFailure(hr, "Failed to format argument string.");
159
160 hr = StrAllocFormattedSecure(&sczCommand, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsFormatted);
161 ExitOnFailure(hr, "Failed to create executable command.");
162
163 hr = VariableFormatStringObfuscated(pVariables, pLaunchApprovedExe->sczArguments, &sczArgumentsObfuscated, NULL);
164 ExitOnFailure(hr, "Failed to format obfuscated argument string.");
165
166 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\" %s", pLaunchApprovedExe->sczExecutablePath, sczArgumentsObfuscated);
167 }
168 else
169 {
170 hr = StrAllocFormatted(&sczCommand, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
171 ExitOnFailure(hr, "Failed to create executable command.");
172
173 hr = StrAllocFormatted(&sczCommandObfuscated, L"\"%ls\"", pLaunchApprovedExe->sczExecutablePath);
174 }
175 ExitOnFailure(hr, "Failed to create obfuscated executable command.");
176
177 // Try to get the directory of the executable so we can set the current directory of the process to help those executables
178 // that expect stuff to be relative to them. Best effort only.
179 hr = PathGetDirectory(pLaunchApprovedExe->sczExecutablePath, &sczExecutableDirectory);
180 if (SUCCEEDED(hr))
181 {
182 // CreateProcessW has undocumented MAX_PATH restriction for lpCurrentDirectory even when long path support is enabled.
183 hr = ::StringCchLengthW(sczExecutableDirectory, MAX_PATH - 1, &cchExecutableDirectory);
184 }
185
186 if (FAILED(hr))
187 {
188 ReleaseNullStr(sczExecutableDirectory);
189
190 hr = S_OK;
191 }
192
193 LogId(REPORT_STANDARD, MSG_LAUNCHING_APPROVED_EXE, pLaunchApprovedExe->sczExecutablePath, sczCommandObfuscated);
194
195 si.cb = sizeof(si);
196 if (!::CreateProcessW(pLaunchApprovedExe->sczExecutablePath, sczCommand, NULL, NULL, FALSE, CREATE_NEW_PROCESS_GROUP, NULL, sczExecutableDirectory, &si, &pi))
197 {
198 ExitWithLastError(hr, "Failed to CreateProcess on path: %ls", pLaunchApprovedExe->sczExecutablePath);
199 }
200
201 *pdwProcessId = pi.dwProcessId;
202
203 if (pLaunchApprovedExe->dwWaitForInputIdleTimeout)
204 {
205 ::WaitForInputIdle(pi.hProcess, pLaunchApprovedExe->dwWaitForInputIdleTimeout);
206 }
207
208 LExit:
209 StrSecureZeroFreeString(sczArgumentsFormatted);
210 ReleaseStr(sczArgumentsObfuscated);
211 StrSecureZeroFreeString(sczCommand);
212 ReleaseStr(sczCommandObfuscated);
213 ReleaseStr(sczExecutableDirectory);
214
215 ReleaseHandle(pi.hThread);
216 ReleaseHandle(pi.hProcess);
217
218 return hr;
219 }
220
221 extern "C" HRESULT ApprovedExesVerifySecureLocation(
222 __in BURN_CACHE* pCache,
223 __in BURN_VARIABLES* pVariables,
224 __in LPCWSTR wzExecutablePath
225 )
226 {
227 HRESULT hr = S_OK;
228 LPWSTR scz = NULL;
229 LPWSTR sczSecondary = NULL;
230
231 const LPCWSTR vrgSecureFolderVariables[] = {
232 L"ProgramFiles64Folder",
233 L"ProgramFilesFolder",
234 };
235
236 for (DWORD i = 0; i < countof(vrgSecureFolderVariables); ++i)
237 {
238 LPCWSTR wzSecureFolderVariable = vrgSecureFolderVariables[i];
239
240 hr = VariableGetString(pVariables, wzSecureFolderVariable, &scz);
241 if (SUCCEEDED(hr))
242 {
243 hr = PathDirectoryContainsPath(scz, wzExecutablePath);
244 if (S_OK == hr)
245 {
246 ExitFunction();
247 }
248 }
249 else if (E_NOTFOUND != hr)
250 {
251 ExitOnFailure(hr, "Failed to get the variable: %ls", wzSecureFolderVariable);
252 }
253 }
254
255 // The problem with using a Variable for the root package cache folder is that it might not have been secured yet.
256 // Getting it through CacheGetPerMachineRootCompletedPath makes sure it has been secured.
257 hr = CacheGetPerMachineRootCompletedPath(pCache, &scz, &sczSecondary);
258 ExitOnFailure(hr, "Failed to get the root package cache folder.");
259
260 // If the package cache is redirected, hr is S_FALSE.
261 if (S_FALSE == hr)
262 {
263 hr = PathDirectoryContainsPath(sczSecondary, wzExecutablePath);
264 if (S_OK == hr)
265 {
266 ExitFunction();
267 }
268 }
269
270 hr = PathDirectoryContainsPath(scz, wzExecutablePath);
271 if (S_OK == hr)
272 {
273 ExitFunction();
274 }
275
276 hr = S_FALSE;
277
278 LExit:
279 ReleaseStr(scz);
280 ReleaseStr(sczSecondary);
281
282 return hr;
283 }