aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/secureobj.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/secureobj.cpp')
-rw-r--r--src/ext/Util/ca/secureobj.cpp915
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
6LPCWSTR 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`";
9enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_ATTRIBUTES, 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
19enum eSECURE_OBJECT_ATTRIBUTE
20{
21 SECURE_OBJECT_ATTRIBUTE_INHERITABLE = 0x1,
22};
23
24static 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
56static 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
87static 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 }
161LExit:
162 ReleaseStr(pwzCustomActionData);
163
164 if (psd)
165 {
166 ::LocalFree(psd);
167 }
168
169 return hr;
170}
171
172static 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
307LExit:
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******************************************************************/
320extern "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
461LExit:
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******************************************************************/
481extern "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
563LExit:
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******************************************************************/
583extern "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
770LExit:
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
798extern "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
899LExit:
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}