diff options
Diffstat (limited to 'src/ext/Util/ca/secureobj.cpp')
-rw-r--r-- | src/ext/Util/ca/secureobj.cpp | 915 |
1 files changed, 915 insertions, 0 deletions
diff --git a/src/ext/Util/ca/secureobj.cpp b/src/ext/Util/ca/secureobj.cpp new file mode 100644 index 00000000..72842eb5 --- /dev/null +++ b/src/ext/Util/ca/secureobj.cpp | |||
@@ -0,0 +1,915 @@ | |||
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 | // structs | ||
6 | LPCWSTR wzQUERY_SECUREOBJECTS = L"SELECT `Wix4SecureObject`.`Wix4SecureObject`, `Wix4SecureObject`.`Table`, `Wix4SecureObject`.`Domain`, `Wix4SecureObject`.`User`, `Wix4SecureObject`.`Attributes`, " | ||
7 | L"`Wix4SecureObject`.`Permission`, `Wix4SecureObject`.`Component_`, `Component`.`Attributes` FROM `Wix4SecureObject`,`Component` WHERE " | ||
8 | L"`Wix4SecureObject`.`Component_`=`Component`.`Component`"; | ||
9 | enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_ATTRIBUTES, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES }; | ||
10 | |||
11 | LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?"; | ||
12 | enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY }; | ||
13 | |||
14 | LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?"; | ||
15 | enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 }; | ||
16 | |||
17 | enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY }; | ||
18 | |||
19 | enum eSECURE_OBJECT_ATTRIBUTE | ||
20 | { | ||
21 | SECURE_OBJECT_ATTRIBUTE_INHERITABLE = 0x1, | ||
22 | }; | ||
23 | |||
24 | static eOBJECTTYPE EObjectTypeFromString( | ||
25 | __in LPCWSTR pwzTable | ||
26 | ) | ||
27 | { | ||
28 | if (NULL == pwzTable) | ||
29 | { | ||
30 | return OT_UNKNOWN; | ||
31 | } | ||
32 | |||
33 | eOBJECTTYPE eType = OT_UNKNOWN; | ||
34 | |||
35 | // ensure we're looking at a known table | ||
36 | if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) | ||
37 | { | ||
38 | eType = OT_SERVICE; | ||
39 | } | ||
40 | else if (0 == lstrcmpW(L"CreateFolder", pwzTable)) | ||
41 | { | ||
42 | eType = OT_FOLDER; | ||
43 | } | ||
44 | else if (0 == lstrcmpW(L"File", pwzTable)) | ||
45 | { | ||
46 | eType = OT_FILE; | ||
47 | } | ||
48 | else if (0 == lstrcmpW(L"Registry", pwzTable)) | ||
49 | { | ||
50 | eType = OT_REGISTRY; | ||
51 | } | ||
52 | |||
53 | return eType; | ||
54 | } | ||
55 | |||
56 | static SE_OBJECT_TYPE SEObjectTypeFromString( | ||
57 | __in LPCWSTR pwzTable | ||
58 | ) | ||
59 | { | ||
60 | if (NULL == pwzTable) | ||
61 | { | ||
62 | return SE_UNKNOWN_OBJECT_TYPE; | ||
63 | } | ||
64 | |||
65 | SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; | ||
66 | |||
67 | if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) | ||
68 | { | ||
69 | objectType = SE_SERVICE; | ||
70 | } | ||
71 | else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable)) | ||
72 | { | ||
73 | objectType = SE_FILE_OBJECT; | ||
74 | } | ||
75 | else if (0 == lstrcmpW(L"Registry", pwzTable)) | ||
76 | { | ||
77 | objectType = SE_REGISTRY_KEY; | ||
78 | } | ||
79 | else | ||
80 | { | ||
81 | // Do nothing; we'll return SE_UNKNOWN_OBJECT_TYPE, and the caller should handle the situation | ||
82 | } | ||
83 | |||
84 | return objectType; | ||
85 | } | ||
86 | |||
87 | static HRESULT StoreACLRollbackInfo( | ||
88 | __in LPWSTR pwzObject, | ||
89 | __in LPCWSTR pwzTable | ||
90 | ) | ||
91 | { | ||
92 | HRESULT hr = S_OK; | ||
93 | DWORD er = ERROR_SUCCESS; | ||
94 | PSECURITY_DESCRIPTOR psd = NULL; | ||
95 | SECURITY_DESCRIPTOR_CONTROL sdc = {0}; | ||
96 | DWORD dwRevision = 0; | ||
97 | LPWSTR pwzCustomActionData = NULL; | ||
98 | LPWSTR pwzSecurityInfo = NULL; | ||
99 | |||
100 | Assert(pwzObject && pwzTable); | ||
101 | |||
102 | SE_OBJECT_TYPE objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable)); | ||
103 | |||
104 | if (SE_UNKNOWN_OBJECT_TYPE != objectType) | ||
105 | { | ||
106 | er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd); | ||
107 | if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er || ERROR_SERVICE_DOES_NOT_EXIST == HRESULT_CODE(er)) | ||
108 | { | ||
109 | // If the file, path or service doesn't exist yet, skip rollback without a message | ||
110 | hr = HRESULT_FROM_WIN32(er); | ||
111 | ExitFunction(); | ||
112 | } | ||
113 | |||
114 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Unable to schedule rollback for object: %ls", pwzObject); | ||
115 | |||
116 | //Need to see if DACL is protected so getting Descriptor information | ||
117 | if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) | ||
118 | { | ||
119 | ExitOnLastError(hr, "Unable to schedule rollback for object (failed to get security descriptor control): %ls", pwzObject); | ||
120 | } | ||
121 | |||
122 | // Convert the security information to a string, and write this to the custom action data | ||
123 | if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(psd,SDDL_REVISION_1,DACL_SECURITY_INFORMATION,&pwzSecurityInfo,NULL)) | ||
124 | { | ||
125 | hr = E_UNEXPECTED; | ||
126 | ExitOnFailure(hr, "Unable to schedule rollback for object (failed to convert security descriptor to a valid security descriptor string): %ls", pwzObject); | ||
127 | } | ||
128 | |||
129 | hr = WcaWriteStringToCaData(pwzObject, &pwzCustomActionData); | ||
130 | ExitOnFailure(hr, "failed to add object data to rollback CustomActionData"); | ||
131 | |||
132 | hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData); | ||
133 | ExitOnFailure(hr, "failed to add table name to rollback CustomActionData"); | ||
134 | |||
135 | hr = WcaWriteStringToCaData(pwzSecurityInfo, &pwzCustomActionData); | ||
136 | ExitOnFailure(hr, "failed to add security info data to rollback CustomActionData"); | ||
137 | |||
138 | // Write a 1 if DACL is protected, 0 otherwise | ||
139 | if (sdc & SE_DACL_PROTECTED) | ||
140 | { | ||
141 | hr = WcaWriteIntegerToCaData(1,&pwzCustomActionData); | ||
142 | ExitOnFailure(hr, "failed to add data to rollbackCustomActionData"); | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | hr = WcaWriteIntegerToCaData(0,&pwzCustomActionData); | ||
147 | ExitOnFailure(hr, "failed to add data to rollback CustomActionData"); | ||
148 | } | ||
149 | |||
150 | hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjectsRollback"), pwzCustomActionData, COST_SECUREOBJECT); | ||
151 | ExitOnFailure(hr, "failed to schedule ExecSecureObjectsRollback for item: %ls of type: %ls", pwzObject, pwzTable); | ||
152 | |||
153 | ReleaseStr(pwzCustomActionData); | ||
154 | pwzCustomActionData = NULL; | ||
155 | |||
156 | } | ||
157 | else | ||
158 | { | ||
159 | MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable); | ||
160 | } | ||
161 | LExit: | ||
162 | ReleaseStr(pwzCustomActionData); | ||
163 | |||
164 | if (psd) | ||
165 | { | ||
166 | ::LocalFree(psd); | ||
167 | } | ||
168 | |||
169 | return hr; | ||
170 | } | ||
171 | |||
172 | static HRESULT GetTargetPath( | ||
173 | __in eOBJECTTYPE eType, | ||
174 | __in LPCWSTR pwzSecureObject, | ||
175 | __out LPWSTR* ppwzTargetPath | ||
176 | ) | ||
177 | { | ||
178 | HRESULT hr = S_OK; | ||
179 | |||
180 | PMSIHANDLE hView = NULL; | ||
181 | PMSIHANDLE hRecObject = NULL; | ||
182 | PMSIHANDLE hRec = NULL; | ||
183 | |||
184 | int iRoot = 0; | ||
185 | int iAllUsers = 0; | ||
186 | LPWSTR pwzKey = NULL; | ||
187 | LPWSTR pwzFormattedString = NULL; | ||
188 | |||
189 | if (OT_SERVICE == eType) | ||
190 | { | ||
191 | hr = WcaTableExists(L"ServiceInstall"); | ||
192 | if (S_FALSE == hr) | ||
193 | { | ||
194 | hr = E_UNEXPECTED; | ||
195 | } | ||
196 | ExitOnFailure(hr, "failed to open ServiceInstall table to secure object"); | ||
197 | |||
198 | hr = WcaOpenView(wzQUERY_SERVICEINSTALL, &hView); | ||
199 | ExitOnFailure(hr, "failed to open view on ServiceInstall table"); | ||
200 | |||
201 | // create a record that stores the object to secure | ||
202 | hRec = MsiCreateRecord(1); | ||
203 | MsiRecordSetStringW(hRec, 1, pwzSecureObject); | ||
204 | |||
205 | // execute a view looking for the object's ServiceInstall.ServiceInstall row. | ||
206 | hr = WcaExecuteView(hView, hRec); | ||
207 | ExitOnFailure(hr, "failed to execute view on ServiceInstall table"); | ||
208 | hr = WcaFetchSingleRecord(hView, &hRecObject); | ||
209 | ExitOnFailure(hr, "failed to fetch ServiceInstall row for secure object"); | ||
210 | |||
211 | hr = WcaGetRecordFormattedString(hRecObject, QSSI_NAME, ppwzTargetPath); | ||
212 | ExitOnFailure(hr, "failed to get service name for secure object: %ls", pwzSecureObject); | ||
213 | } | ||
214 | else if (OT_FOLDER == eType) | ||
215 | { | ||
216 | hr = WcaGetTargetPath(pwzSecureObject, ppwzTargetPath); | ||
217 | ExitOnFailure(hr, "failed to get target path for directory id: %ls", pwzSecureObject); | ||
218 | } | ||
219 | else if (OT_FILE == eType) | ||
220 | { | ||
221 | hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzSecureObject); | ||
222 | ExitOnFailure(hr, "failed to create formatted string for securing file object: %ls", pwzSecureObject); | ||
223 | |||
224 | hr = WcaGetFormattedString(pwzFormattedString, ppwzTargetPath); | ||
225 | ExitOnFailure(hr, "failed to get file path from formatted string: %ls for secure object: %ls", pwzFormattedString, pwzSecureObject); | ||
226 | } | ||
227 | else if (OT_REGISTRY == eType) | ||
228 | { | ||
229 | hr = WcaTableExists(L"Registry"); | ||
230 | if (S_FALSE == hr) | ||
231 | { | ||
232 | hr = E_UNEXPECTED; | ||
233 | } | ||
234 | ExitOnFailure(hr, "failed to open Registry table to secure object"); | ||
235 | |||
236 | hr = WcaOpenView(wzQUERY_REGISTRY, &hView); | ||
237 | ExitOnFailure(hr, "failed to open view on Registry table"); | ||
238 | |||
239 | // create a record that stores the object to secure | ||
240 | hRec = MsiCreateRecord(1); | ||
241 | MsiRecordSetStringW(hRec, 1, pwzSecureObject); | ||
242 | |||
243 | // execute a view looking for the object's Registry row | ||
244 | hr = WcaExecuteView(hView, hRec); | ||
245 | ExitOnFailure(hr, "failed to execute view on Registry table"); | ||
246 | hr = WcaFetchSingleRecord(hView, &hRecObject); | ||
247 | ExitOnFailure(hr, "failed to fetch Registry row for secure object"); | ||
248 | |||
249 | hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot); | ||
250 | ExitOnFailure(hr, "Failed to get reg key root for secure object: %ls", pwzSecureObject); | ||
251 | |||
252 | hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey); | ||
253 | ExitOnFailure(hr, "Failed to get reg key for secure object: %ls", pwzSecureObject); | ||
254 | |||
255 | // Decode the root value | ||
256 | if (-1 == iRoot) | ||
257 | { | ||
258 | // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property | ||
259 | hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers); | ||
260 | ExitOnFailure(hr, "failed to get value of ALLUSERS property"); | ||
261 | |||
262 | if (1 == iAllUsers) | ||
263 | { | ||
264 | hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0); | ||
265 | ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); | ||
266 | } | ||
267 | else | ||
268 | { | ||
269 | hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0); | ||
270 | ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); | ||
271 | } | ||
272 | } | ||
273 | else if (msidbRegistryRootClassesRoot == iRoot) | ||
274 | { | ||
275 | hr = StrAllocString(ppwzTargetPath, L"CLASSES_ROOT\\", 0); | ||
276 | ExitOnFailure(hr, "failed to allocate target registry string with HKCR root"); | ||
277 | } | ||
278 | else if (msidbRegistryRootCurrentUser == iRoot) | ||
279 | { | ||
280 | hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0); | ||
281 | ExitOnFailure(hr, "failed to allocate target registry string with HKCU root"); | ||
282 | } | ||
283 | else if (msidbRegistryRootLocalMachine == iRoot) | ||
284 | { | ||
285 | hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0); | ||
286 | ExitOnFailure(hr, "failed to allocate target registry string with HKLM root"); | ||
287 | } | ||
288 | else if (msidbRegistryRootUsers == iRoot) | ||
289 | { | ||
290 | hr = StrAllocString(ppwzTargetPath, L"USERS\\", 0); | ||
291 | ExitOnFailure(hr, "failed to allocate target registry string with HKU root"); | ||
292 | } | ||
293 | else | ||
294 | { | ||
295 | ExitOnFailure(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%ls' root: %d", pwzSecureObject, iRoot); | ||
296 | } | ||
297 | |||
298 | hr = StrAllocConcat(ppwzTargetPath, pwzKey, 0); | ||
299 | ExitOnFailure(hr, "Failed to concat key: %ls for secure object: %ls", pwzKey, pwzSecureObject); | ||
300 | } | ||
301 | else | ||
302 | { | ||
303 | AssertSz(FALSE, "How did you get here?"); | ||
304 | ExitOnFailure(hr = E_UNEXPECTED, "Unknown secure object type: %d", eType); | ||
305 | } | ||
306 | |||
307 | LExit: | ||
308 | ReleaseStr(pwzFormattedString); | ||
309 | ReleaseStr(pwzKey); | ||
310 | |||
311 | return hr; | ||
312 | } | ||
313 | |||
314 | /****************************************************************** | ||
315 | SchedSecureObjects - entry point for SchedSecureObjects Custom Action | ||
316 | |||
317 | called as Type 1 CustomAction (binary DLL) from Windows Installer | ||
318 | in InstallExecuteSequence, to schedule ExecSecureObjects | ||
319 | ******************************************************************/ | ||
320 | extern "C" UINT __stdcall SchedSecureObjects( | ||
321 | __in MSIHANDLE hInstall | ||
322 | ) | ||
323 | { | ||
324 | // AssertSz(FALSE, "debug SchedSecureObjects"); | ||
325 | HRESULT hr = S_OK; | ||
326 | UINT er = ERROR_SUCCESS; | ||
327 | |||
328 | LPWSTR pwzSecureObject = NULL; | ||
329 | LPWSTR pwzData = NULL; | ||
330 | LPWSTR pwzTable = NULL; | ||
331 | LPWSTR pwzTargetPath = NULL; | ||
332 | |||
333 | PMSIHANDLE hView = NULL; | ||
334 | PMSIHANDLE hRec = NULL; | ||
335 | |||
336 | INSTALLSTATE isInstalled; | ||
337 | INSTALLSTATE isAction; | ||
338 | |||
339 | LPWSTR pwzCustomActionData = NULL; | ||
340 | |||
341 | DWORD cObjects = 0; | ||
342 | eOBJECTTYPE eType = OT_UNKNOWN; | ||
343 | DWORD dwAttributes = 0; | ||
344 | |||
345 | // | ||
346 | // initialize | ||
347 | // | ||
348 | hr = WcaInitialize(hInstall, "SchedSecureObjects"); | ||
349 | ExitOnFailure(hr, "failed to initialize"); | ||
350 | |||
351 | // anything to do? | ||
352 | if (S_OK != WcaTableExists(L"Wix4SecureObject")) | ||
353 | { | ||
354 | WcaLog(LOGMSG_STANDARD, "Wix4SecureObject table doesn't exist, so there are no objects to secure."); | ||
355 | ExitFunction(); | ||
356 | } | ||
357 | |||
358 | // | ||
359 | // loop through all the objects to be secured | ||
360 | // | ||
361 | hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView); | ||
362 | ExitOnFailure(hr, "failed to open view on Wix4SecureObject table"); | ||
363 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
364 | { | ||
365 | hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable); | ||
366 | ExitOnFailure(hr, "failed to get object table"); | ||
367 | |||
368 | eType = EObjectTypeFromString(pwzTable); | ||
369 | |||
370 | if (OT_UNKNOWN == eType) | ||
371 | { | ||
372 | ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable); | ||
373 | } | ||
374 | |||
375 | int iCompAttributes = 0; | ||
376 | hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes); | ||
377 | ExitOnFailure(hr, "failed to get Component attributes for secure object"); | ||
378 | |||
379 | BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit; | ||
380 | |||
381 | // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA | ||
382 | #ifdef _WIN64 | ||
383 | if (!fIs64Bit) | ||
384 | { | ||
385 | continue; | ||
386 | } | ||
387 | #else | ||
388 | if (fIs64Bit) | ||
389 | { | ||
390 | continue; | ||
391 | } | ||
392 | #endif | ||
393 | |||
394 | // Get the object to secure | ||
395 | hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject); | ||
396 | ExitOnFailure(hr, "failed to get name of object"); | ||
397 | |||
398 | hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath); | ||
399 | ExitOnFailure(hr, "failed to get target path of object '%ls'", pwzSecureObject); | ||
400 | |||
401 | hr = WcaGetRecordString(hRec, QSO_COMPONENT, &pwzData); | ||
402 | ExitOnFailure(hr, "failed to get Component name for secure object"); | ||
403 | |||
404 | // | ||
405 | // if we are installing this Component | ||
406 | // | ||
407 | er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction); | ||
408 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData); | ||
409 | |||
410 | if (WcaIsInstalling(isInstalled, isAction)) | ||
411 | { | ||
412 | hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData); | ||
413 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
414 | |||
415 | // add the data to the CustomActionData | ||
416 | hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzData); | ||
417 | ExitOnFailure(hr, "failed to get name of object"); | ||
418 | hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData); | ||
419 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
420 | |||
421 | hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData); | ||
422 | ExitOnFailure(hr, "failed to get domain for user to configure object"); | ||
423 | hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); | ||
424 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
425 | |||
426 | hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData); | ||
427 | ExitOnFailure(hr, "failed to get user to configure object"); | ||
428 | hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); | ||
429 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
430 | |||
431 | hr = WcaGetRecordInteger(hRec, QSO_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes)); | ||
432 | ExitOnFailure(hr, "failed to get attributes to configure object"); | ||
433 | hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData); | ||
434 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
435 | |||
436 | hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData); | ||
437 | ExitOnFailure(hr, "failed to get permission to configure object"); | ||
438 | hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData); | ||
439 | ExitOnFailure(hr, "failed to add data to CustomActionData"); | ||
440 | |||
441 | ++cObjects; | ||
442 | } | ||
443 | } | ||
444 | |||
445 | // if we looped through all records all is well | ||
446 | if (E_NOMOREITEMS == hr) | ||
447 | hr = S_OK; | ||
448 | ExitOnFailure(hr, "failed while looping through all objects to secure"); | ||
449 | |||
450 | // | ||
451 | // schedule the custom action and add to progress bar | ||
452 | // | ||
453 | if (pwzCustomActionData && *pwzCustomActionData) | ||
454 | { | ||
455 | Assert(0 < cObjects); | ||
456 | |||
457 | hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjects"), pwzCustomActionData, cObjects * COST_SECUREOBJECT); | ||
458 | ExitOnFailure(hr, "failed to schedule ExecSecureObjects action"); | ||
459 | } | ||
460 | |||
461 | LExit: | ||
462 | ReleaseStr(pwzSecureObject); | ||
463 | ReleaseStr(pwzCustomActionData); | ||
464 | ReleaseStr(pwzData); | ||
465 | ReleaseStr(pwzTable); | ||
466 | ReleaseStr(pwzTargetPath); | ||
467 | |||
468 | if (FAILED(hr)) | ||
469 | { | ||
470 | er = ERROR_INSTALL_FAILURE; | ||
471 | } | ||
472 | return WcaFinalize(er); | ||
473 | } | ||
474 | |||
475 | /****************************************************************** | ||
476 | SchedSecureObjectsRollback - entry point for SchedSecureObjectsRollback Custom Action | ||
477 | |||
478 | called as Type 1 CustomAction (binary DLL) from Windows Installer | ||
479 | in InstallExecuteSequence before SchedSecureObjects | ||
480 | ******************************************************************/ | ||
481 | extern "C" UINT __stdcall SchedSecureObjectsRollback( | ||
482 | __in MSIHANDLE hInstall | ||
483 | ) | ||
484 | { | ||
485 | // AssertSz(FALSE, "debug SchedSecureObjectsRollback"); | ||
486 | HRESULT hr = S_OK; | ||
487 | UINT er = ERROR_SUCCESS; | ||
488 | |||
489 | LPWSTR pwzSecureObject = NULL; | ||
490 | LPWSTR pwzTable = NULL; | ||
491 | LPWSTR pwzTargetPath = NULL; | ||
492 | |||
493 | PMSIHANDLE hView = NULL; | ||
494 | PMSIHANDLE hRec = NULL; | ||
495 | |||
496 | LPWSTR pwzCustomActionData = NULL; | ||
497 | |||
498 | eOBJECTTYPE eType = OT_UNKNOWN; | ||
499 | |||
500 | // | ||
501 | // initialize | ||
502 | // | ||
503 | hr = WcaInitialize(hInstall, "SchedSecureObjectsRollback"); | ||
504 | ExitOnFailure(hr, "failed to initialize"); | ||
505 | |||
506 | // | ||
507 | // loop through all the objects to be secured | ||
508 | // | ||
509 | hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView); | ||
510 | ExitOnFailure(hr, "failed to open view on Wix4SecureObject table"); | ||
511 | while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) | ||
512 | { | ||
513 | hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable); | ||
514 | ExitOnFailure(hr, "failed to get object table"); | ||
515 | |||
516 | eType = EObjectTypeFromString(pwzTable); | ||
517 | |||
518 | if (OT_UNKNOWN == eType) | ||
519 | { | ||
520 | ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable); | ||
521 | } | ||
522 | |||
523 | int iCompAttributes = 0; | ||
524 | hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes); | ||
525 | ExitOnFailure(hr, "failed to get Component attributes for secure object"); | ||
526 | |||
527 | BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit; | ||
528 | |||
529 | // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA | ||
530 | #ifdef _WIN64 | ||
531 | if (!fIs64Bit) | ||
532 | { | ||
533 | continue; | ||
534 | } | ||
535 | #else | ||
536 | if (fIs64Bit) | ||
537 | { | ||
538 | continue; | ||
539 | } | ||
540 | #endif | ||
541 | |||
542 | // get the object being secured that we are planning to schedule rollback for | ||
543 | hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject); | ||
544 | ExitOnFailure(hr, "failed to get name of object"); | ||
545 | |||
546 | hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath); | ||
547 | ExitOnFailure(hr, "failed to get target path of object '%ls' in order to schedule rollback", pwzSecureObject); | ||
548 | |||
549 | hr = StoreACLRollbackInfo(pwzTargetPath, pwzTable); | ||
550 | if (FAILED(hr)) | ||
551 | { | ||
552 | WcaLog(LOGMSG_STANDARD, "Failed to store ACL rollback information with error 0x%x - continuing", hr); | ||
553 | } | ||
554 | } | ||
555 | |||
556 | // if we looped through all records all is well | ||
557 | if (E_NOMOREITEMS == hr) | ||
558 | { | ||
559 | hr = S_OK; | ||
560 | } | ||
561 | ExitOnFailure(hr, "failed while looping through all objects to schedule rollback for"); | ||
562 | |||
563 | LExit: | ||
564 | ReleaseStr(pwzCustomActionData); | ||
565 | ReleaseStr(pwzSecureObject); | ||
566 | ReleaseStr(pwzTable); | ||
567 | ReleaseStr(pwzTargetPath); | ||
568 | |||
569 | if (FAILED(hr)) | ||
570 | { | ||
571 | er = ERROR_INSTALL_FAILURE; | ||
572 | } | ||
573 | return WcaFinalize(er); | ||
574 | } | ||
575 | |||
576 | /****************************************************************** | ||
577 | CaExecSecureObjects - entry point for SecureObjects Custom Action | ||
578 | called as Type 1025 CustomAction (deferred binary DLL) | ||
579 | |||
580 | NOTE: deferred CustomAction since it modifies the machine | ||
581 | NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwAttributes\tdwPermissions\t... | ||
582 | ******************************************************************/ | ||
583 | extern "C" UINT __stdcall ExecSecureObjects( | ||
584 | __in MSIHANDLE hInstall | ||
585 | ) | ||
586 | { | ||
587 | // AssertSz(FALSE, "debug ExecSecureObjects"); | ||
588 | HRESULT hr = S_OK; | ||
589 | DWORD er = ERROR_SUCCESS; | ||
590 | |||
591 | LPWSTR pwz = NULL; | ||
592 | LPWSTR pwzData = NULL; | ||
593 | LPWSTR pwzObject = NULL; | ||
594 | LPWSTR pwzTable = NULL; | ||
595 | LPWSTR pwzDomain = NULL; | ||
596 | DWORD dwRevision = 0; | ||
597 | LPWSTR pwzUser = NULL; | ||
598 | DWORD dwPermissions = 0; | ||
599 | DWORD dwAttributes = 0; | ||
600 | LPWSTR pwzAccount = NULL; | ||
601 | PSID psid = NULL; | ||
602 | |||
603 | EXPLICIT_ACCESSW ea = {0}; | ||
604 | SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; | ||
605 | PSECURITY_DESCRIPTOR psd = NULL; | ||
606 | SECURITY_DESCRIPTOR_CONTROL sdc = {0}; | ||
607 | SECURITY_INFORMATION si = {0}; | ||
608 | PACL pAclExisting = NULL; // doesn't get freed | ||
609 | PACL pAclNew = NULL; | ||
610 | |||
611 | PMSIHANDLE hActionRec = ::MsiCreateRecord(1); | ||
612 | |||
613 | // | ||
614 | // initialize | ||
615 | // | ||
616 | hr = WcaInitialize(hInstall, "ExecSecureObjects"); | ||
617 | ExitOnFailure(hr, "failed to initialize"); | ||
618 | |||
619 | hr = WcaGetProperty(L"CustomActionData", &pwzData); | ||
620 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
621 | |||
622 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
623 | |||
624 | pwz = pwzData; | ||
625 | |||
626 | // | ||
627 | // loop through all the passed in data | ||
628 | // | ||
629 | while (pwz && *pwz) | ||
630 | { | ||
631 | hr = WcaReadStringFromCaData(&pwz, &pwzObject); | ||
632 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
633 | |||
634 | hr = WcaReadStringFromCaData(&pwz, &pwzTable); | ||
635 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
636 | hr = WcaReadStringFromCaData(&pwz, &pwzDomain); | ||
637 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
638 | hr = WcaReadStringFromCaData(&pwz, &pwzUser); | ||
639 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
640 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes)); | ||
641 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
642 | hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions)); | ||
643 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
644 | |||
645 | WcaLog(LOGMSG_VERBOSE, "Securing Object: %ls Type: %ls User: %ls", pwzObject, pwzTable, pwzUser); | ||
646 | |||
647 | // | ||
648 | // create the appropriate SID | ||
649 | // | ||
650 | |||
651 | // figure out the right user to put into the access block | ||
652 | if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone")) | ||
653 | { | ||
654 | hr = AclGetWellKnownSid(WinWorldSid, &psid); | ||
655 | } | ||
656 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators")) | ||
657 | { | ||
658 | hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid); | ||
659 | } | ||
660 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem")) | ||
661 | { | ||
662 | hr = AclGetWellKnownSid(WinLocalSystemSid, &psid); | ||
663 | } | ||
664 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService")) | ||
665 | { | ||
666 | hr = AclGetWellKnownSid(WinLocalServiceSid, &psid); | ||
667 | } | ||
668 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService")) | ||
669 | { | ||
670 | hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid); | ||
671 | } | ||
672 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser")) | ||
673 | { | ||
674 | hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid); | ||
675 | } | ||
676 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests")) | ||
677 | { | ||
678 | hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid); | ||
679 | } | ||
680 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER")) | ||
681 | { | ||
682 | hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid); | ||
683 | } | ||
684 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE")) | ||
685 | { | ||
686 | hr = AclGetWellKnownSid(WinInteractiveSid, &psid); | ||
687 | } | ||
688 | else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users")) | ||
689 | { | ||
690 | hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid); | ||
691 | } | ||
692 | else | ||
693 | { | ||
694 | hr = StrAllocFormatted(&pwzAccount, L"%s%s%s", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser); | ||
695 | ExitOnFailure(hr, "failed to build domain user name"); | ||
696 | |||
697 | hr = AclGetAccountSid(NULL, pwzAccount, &psid); | ||
698 | } | ||
699 | ExitOnFailure(hr, "failed to get sid for account: %ls%ls%ls", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser); | ||
700 | |||
701 | // | ||
702 | // build up the explicit access | ||
703 | // | ||
704 | ea.grfAccessMode = SET_ACCESS; | ||
705 | |||
706 | if (dwAttributes & SECURE_OBJECT_ATTRIBUTE_INHERITABLE) | ||
707 | { | ||
708 | ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; | ||
709 | } | ||
710 | else | ||
711 | { | ||
712 | ea.grfInheritance = NO_INHERITANCE; | ||
713 | } | ||
714 | |||
715 | #pragma prefast(push) | ||
716 | #pragma prefast(disable:25029) | ||
717 | ::BuildTrusteeWithSidW(&ea.Trustee, psid); | ||
718 | #pragma prefast(pop) | ||
719 | |||
720 | objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable)); | ||
721 | |||
722 | // always add these permissions for services | ||
723 | // these are basic permissions that are often forgotten | ||
724 | if (0 == lstrcmpW(L"ServiceInstall", pwzTable)) | ||
725 | { | ||
726 | dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE; | ||
727 | } | ||
728 | |||
729 | ea.grfAccessPermissions = dwPermissions; | ||
730 | |||
731 | if (SE_UNKNOWN_OBJECT_TYPE != objectType) | ||
732 | { | ||
733 | er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd); | ||
734 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %ls", pwzObject); | ||
735 | |||
736 | //Need to see if DACL is protected so getting Descriptor information | ||
737 | if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) | ||
738 | { | ||
739 | ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject); | ||
740 | } | ||
741 | |||
742 | #pragma prefast(push) | ||
743 | #pragma prefast(disable:25029) | ||
744 | er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew); | ||
745 | #pragma prefast(pop) | ||
746 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %ls", pwzObject); | ||
747 | |||
748 | if (sdc & SE_DACL_PROTECTED) | ||
749 | { | ||
750 | si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; | ||
751 | } | ||
752 | else | ||
753 | { | ||
754 | si = DACL_SECURITY_INFORMATION; | ||
755 | } | ||
756 | er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL); | ||
757 | MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %ls", pwzObject); | ||
758 | } | ||
759 | else | ||
760 | { | ||
761 | MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable); | ||
762 | } | ||
763 | |||
764 | hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE); | ||
765 | ExitOnFailure(hr, "failed to send progress message"); | ||
766 | |||
767 | objectType = SE_UNKNOWN_OBJECT_TYPE; | ||
768 | } | ||
769 | |||
770 | LExit: | ||
771 | ReleaseStr(pwzUser); | ||
772 | ReleaseStr(pwzDomain); | ||
773 | ReleaseStr(pwzTable); | ||
774 | ReleaseStr(pwzObject); | ||
775 | ReleaseStr(pwzData); | ||
776 | ReleaseStr(pwzAccount); | ||
777 | |||
778 | if (pAclNew) | ||
779 | { | ||
780 | ::LocalFree(pAclNew); | ||
781 | } | ||
782 | if (psd) | ||
783 | { | ||
784 | ::LocalFree(psd); | ||
785 | } | ||
786 | if (psid) | ||
787 | { | ||
788 | AclFreeSid(psid); | ||
789 | } | ||
790 | |||
791 | if (FAILED(hr)) | ||
792 | { | ||
793 | er = ERROR_INSTALL_FAILURE; | ||
794 | } | ||
795 | return WcaFinalize(er); | ||
796 | } | ||
797 | |||
798 | extern "C" UINT __stdcall ExecSecureObjectsRollback( | ||
799 | __in MSIHANDLE hInstall | ||
800 | ) | ||
801 | { | ||
802 | // AssertSz(FALSE, "debug ExecSecureObjectsRollback"); | ||
803 | HRESULT hr = S_OK; | ||
804 | DWORD er = ERROR_SUCCESS; | ||
805 | |||
806 | LPWSTR pwz = NULL; | ||
807 | LPWSTR pwzData = NULL; | ||
808 | LPWSTR pwzObject = NULL; | ||
809 | LPWSTR pwzTable = NULL; | ||
810 | LPWSTR pwzSecurityInfo = NULL; | ||
811 | |||
812 | SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE; | ||
813 | PSECURITY_DESCRIPTOR psd = NULL; | ||
814 | ULONG psdSize; | ||
815 | SECURITY_DESCRIPTOR_CONTROL sdc = {0}; | ||
816 | SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION; | ||
817 | PACL pDacl = NULL; | ||
818 | BOOL bDaclPresent = false; | ||
819 | BOOL bDaclDefaulted = false; | ||
820 | DWORD dwRevision = 0; | ||
821 | int iProtected; | ||
822 | |||
823 | // initialize | ||
824 | hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback"); | ||
825 | ExitOnFailure(hr, "failed to initialize"); | ||
826 | |||
827 | hr = WcaGetProperty(L"CustomActionData", &pwzData); | ||
828 | ExitOnFailure(hr, "failed to get CustomActionData"); | ||
829 | |||
830 | WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData); | ||
831 | |||
832 | pwz = pwzData; | ||
833 | |||
834 | hr = WcaReadStringFromCaData(&pwz, &pwzObject); | ||
835 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
836 | |||
837 | hr = WcaReadStringFromCaData(&pwz, &pwzTable); | ||
838 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
839 | |||
840 | objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable)); | ||
841 | |||
842 | if (SE_UNKNOWN_OBJECT_TYPE != objectType) | ||
843 | { | ||
844 | hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo); | ||
845 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
846 | |||
847 | hr = WcaReadIntegerFromCaData(&pwz, &iProtected); | ||
848 | ExitOnFailure(hr, "failed to process CustomActionData"); | ||
849 | |||
850 | if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize)) | ||
851 | { | ||
852 | ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor"); | ||
853 | } | ||
854 | |||
855 | if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted)) | ||
856 | { | ||
857 | hr = E_UNEXPECTED; | ||
858 | ExitOnFailure(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError()); | ||
859 | } | ||
860 | |||
861 | // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it. | ||
862 | if (!bDaclPresent) | ||
863 | { | ||
864 | hr = E_UNEXPECTED; | ||
865 | ExitOnFailure(hr, "security descriptor does not contain a DACL"); | ||
866 | } | ||
867 | |||
868 | //Need to see if DACL is protected so getting Descriptor information | ||
869 | if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision)) | ||
870 | { | ||
871 | ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject); | ||
872 | } | ||
873 | |||
874 | // Write a 1 if DACL is protected, 0 otherwise | ||
875 | switch (iProtected) | ||
876 | { | ||
877 | case 0: | ||
878 | // Unnecessary to do anything - leave si to the default flags | ||
879 | break; | ||
880 | |||
881 | case 1: | ||
882 | si = si | PROTECTED_DACL_SECURITY_INFORMATION; | ||
883 | break; | ||
884 | |||
885 | default: | ||
886 | hr = E_UNEXPECTED; | ||
887 | ExitOnFailure(hr, "unrecognized value in CustomActionData"); | ||
888 | break; | ||
889 | } | ||
890 | |||
891 | er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL); | ||
892 | ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError()); | ||
893 | } | ||
894 | else | ||
895 | { | ||
896 | MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable); | ||
897 | } | ||
898 | |||
899 | LExit: | ||
900 | ReleaseStr(pwzData); | ||
901 | ReleaseStr(pwzObject); | ||
902 | ReleaseStr(pwzTable); | ||
903 | ReleaseStr(pwzSecurityInfo); | ||
904 | |||
905 | if (psd) | ||
906 | { | ||
907 | ::LocalFree(psd); | ||
908 | } | ||
909 | |||
910 | if (FAILED(hr)) | ||
911 | { | ||
912 | er = ERROR_INSTALL_FAILURE; | ||
913 | } | ||
914 | return WcaFinalize(er); | ||
915 | } | ||