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