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