aboutsummaryrefslogtreecommitdiff
path: root/src/ca/secureobj.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-15 21:46:30 -0600
committerGitHub <noreply@github.com>2018-12-15 21:46:30 -0600
commitf7020c0d16baf2b960e7123e233e20c519f6a340 (patch)
treed2cd464ee15b2b3f304ff780c531b39bb292d331 /src/ca/secureobj.cpp
parent6ed8d107e6edf16956c778bda3573f8d7a7690fc (diff)
downloadwix-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.cpp902
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
6LPCWSTR 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`";
9enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES };
10
11LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?";
12enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY };
13
14LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?";
15enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 };
16
17enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY };
18
19static 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
51static 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
82static 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 }
156LExit:
157 ReleaseStr(pwzCustomActionData);
158
159 if (psd)
160 {
161 ::LocalFree(psd);
162 }
163
164 return hr;
165}
166
167static 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
302LExit:
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******************************************************************/
315extern "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
451LExit:
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******************************************************************/
471extern "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
553LExit:
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******************************************************************/
573extern "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
757LExit:
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
785extern "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
886LExit:
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}