Fix rollback of user rights
Rob Mensching committed
Apr 11, 2021 at 14:15 UTC
4b3f52f14bce8a032fcc476556cc4d60aa20241b
4 files changed
+389
-80
src/ca/scaexec.cpp
+354
-79
@@ -293,6 +293,110 @@ LExit:
293
}
294
295
296
+static HRESULT GetUserHasRight(
297
+ __in LSA_HANDLE hPolicy,
298
+ __in PSID pUserSid,
299
+ __in LPWSTR wzRight,
300
+ __out BOOL* fHasRight
301
+)
302
+{
303
+ HRESULT hr = S_OK;
304
+ NTSTATUS nt = 0;
305
+ LSA_UNICODE_STRING lucPrivilege = { 0 };
306
+ PLSA_ENUMERATION_INFORMATION rgSids = NULL;
307
+ ULONG cSids = 0;
308
+ *fHasRight = FALSE;
309
+
310
+ lucPrivilege.Buffer = wzRight;
311
+ lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
312
+ lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
313
+
314
+ nt = ::LsaEnumerateAccountsWithUserRight(hPolicy, &lucPrivilege, reinterpret_cast<PVOID*>(&rgSids), &cSids);
315
+ hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
316
+ ExitOnFailure(hr, "Failed to enumerate users for right: %ls", lucPrivilege.Buffer);
317
+
318
+ for (DWORD i = 0; i < cSids; ++i)
319
+ {
320
+ PLSA_ENUMERATION_INFORMATION pInfo = rgSids + i;
321
+ if (::EqualSid(pUserSid, pInfo->Sid))
322
+ {
323
+ *fHasRight = TRUE;
324
+ break;
325
+ }
326
+ }
327
+
328
+LExit:
329
+ if (rgSids)
330
+ {
331
+ ::LsaFreeMemory(rgSids);
332
+ }
333
+
334
+ return hr;
335
+}
336
+
337
+
338
+static HRESULT GetExistingUserRightsAssignments(
339
+ __in_opt LPCWSTR wzDomain,
340
+ __in LPCWSTR wzName,
341
+ __inout int* iAttributes
342
+)
343
+{
344
+ HRESULT hr = S_OK;
345
+ NTSTATUS nt = 0;
346
+ BOOL fHasRight = FALSE;
347
+
348
+ LSA_HANDLE hPolicy = NULL;
349
+ LSA_OBJECT_ATTRIBUTES objectAttributes = { 0 };
350
+
351
+ LPWSTR pwzUser = NULL;
352
+ PSID psid = NULL;
353
+
354
+ if (wzDomain && *wzDomain)
355
+ {
356
+ hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
357
+ ExitOnFailure(hr, "Failed to allocate user with domain string");
358
+ }
359
+ else
360
+ {
361
+ hr = StrAllocString(&pwzUser, wzName, 0);
362
+ ExitOnFailure(hr, "Failed to allocate string from user name.");
363
+ }
364
+
365
+ hr = AclGetAccountSid(NULL, pwzUser, &psid);
366
+ ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
367
+
368
+ nt = ::LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy);
369
+ hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
370
+ ExitOnFailure(hr, "Failed to open LSA policy store");
371
+
372
+ hr = GetUserHasRight(hPolicy, psid, L"SeServiceLogonRight", &fHasRight);
373
+ ExitOnFailure(hr, "Failed to check LogonAsService right");
374
+
375
+ if (fHasRight)
376
+ {
377
+ *iAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
378
+ }
379
+
380
+ hr = GetUserHasRight(hPolicy, psid, L"SeBatchLogonRight", &fHasRight);
381
+ ExitOnFailure(hr, "Failed to check LogonAsBatchJob right");
382
+
383
+ if (fHasRight)
384
+ {
385
+ *iAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
386
+ }
387
+
388
+LExit:
389
+ if (hPolicy)
390
+ {
391
+ ::LsaClose(hPolicy);
392
+ }
393
+
394
+ ReleaseSid(psid);
395
+ ReleaseStr(pwzUser);
396
+ return hr;
397
+}
398
+
399
+
400
static HRESULT ModifyUserLocalServiceRight(
401
__in_opt LPCWSTR wzDomain,
402
__in LPCWSTR wzName,
@@ -466,6 +570,117 @@ static void SetUserPasswordAndAttributes(
570
}
571
572
573
+static HRESULT RemoveUserInternal(
574
+ LPWSTR wzGroupCaData,
575
+ LPWSTR wzDomain,
576
+ LPWSTR wzName,
577
+ int iAttributes
578
+)
579
+{
580
+ HRESULT hr = S_OK;
581
+ UINT er = ERROR_SUCCESS;
582
+
583
+ LPWSTR pwz = NULL;
584
+ LPWSTR pwzGroup = NULL;
585
+ LPWSTR pwzGroupDomain = NULL;
586
+ LPCWSTR wz = NULL;
587
+ PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
588
+
589
+ //
590
+ // Remove the logon as service privilege.
591
+ //
592
+ if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
593
+ {
594
+ hr = ModifyUserLocalServiceRight(wzDomain, wzName, FALSE);
595
+ if (FAILED(hr))
596
+ {
597
+ WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
598
+ hr = S_OK;
599
+ }
600
+ }
601
+
602
+ if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
603
+ {
604
+ hr = ModifyUserLocalBatchRight(wzDomain, wzName, FALSE);
605
+ if (FAILED(hr))
606
+ {
607
+ WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
608
+ hr = S_OK;
609
+ }
610
+ }
611
+
612
+ //
613
+ // Remove the User Account if the user was created by us.
614
+ //
615
+ if (!(SCAU_DONT_CREATE_USER & iAttributes))
616
+ {
617
+ if (wzDomain && *wzDomain)
618
+ {
619
+ er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
620
+ if (RPC_S_SERVER_UNAVAILABLE == er)
621
+ {
622
+ // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
623
+ er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
624
+ }
625
+ if (ERROR_SUCCESS == er)
626
+ {
627
+ wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
628
+ }
629
+ else
630
+ {
631
+ wz = wzDomain;
632
+ }
633
+ }
634
+
635
+ er = ::NetUserDel(wz, wzName);
636
+ if (NERR_UserNotFound == er)
637
+ {
638
+ er = NERR_Success;
639
+ }
640
+ ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", wzName);
641
+ }
642
+ else
643
+ {
644
+ //
645
+ // Remove the user from the groups
646
+ //
647
+ pwz = wzGroupCaData;
648
+ while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
649
+ {
650
+ hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
651
+
652
+ if (FAILED(hr))
653
+ {
654
+ WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
655
+ }
656
+ else
657
+ {
658
+ hr = RemoveUserFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain);
659
+ if (FAILED(hr))
660
+ {
661
+ WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", wzName, pwzGroup);
662
+ }
663
+ }
664
+ }
665
+
666
+ if (E_NOMOREITEMS == hr) // if there are no more items, all is well
667
+ {
668
+ hr = S_OK;
669
+ }
670
+
671
+ ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", wzName);
672
+ }
673
+
674
+LExit:
675
+ if (pDomainControllerInfo)
676
+ {
677
+ ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
678
+ }
679
+
680
+ return hr;
681
+}
682
+
683
+
684
/********************************************************************
685
CreateUser - CUSTOM ACTION ENTRY POINT for creating users
686
@@ -484,6 +699,7 @@ extern "C" UINT __stdcall CreateUser(
699
LPWSTR pwz = NULL;
700
LPWSTR pwzName = NULL;
701
LPWSTR pwzDomain = NULL;
702
+ LPWSTR pwzScriptKey = NULL;
703
LPWSTR pwzPassword = NULL;
704
LPWSTR pwzGroup = NULL;
705
LPWSTR pwzGroupDomain = NULL;
@@ -491,6 +707,10 @@ extern "C" UINT __stdcall CreateUser(
707
int iAttributes = 0;
708
BOOL fInitializedCom = FALSE;
709
710
+ WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
711
+ int iOriginalAttributes = 0;
712
+ int iRollbackAttributes = 0;
713
+
714
USER_INFO_1 userInfo;
715
USER_INFO_1* puserInfo = NULL;
716
DWORD dw;
@@ -521,9 +741,44 @@ extern "C" UINT __stdcall CreateUser(
741
hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
742
ExitOnFailure(hr, "failed to read attributes from custom action data");
743
744
+ hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
745
+ ExitOnFailure(hr, "failed to read encoding key from custom action data");
746
+
747
hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
748
ExitOnFailure(hr, "failed to read password from custom action data");
749
750
+ // There is no rollback scheduled if the key is empty.
751
+ // Best effort to get original configuration and save it in the script so rollback can restore it.
752
+ if (*pwzScriptKey)
753
+ {
754
+ hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
755
+ ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
756
+
757
+ iRollbackAttributes = 0;
758
+ hr = GetExistingUserRightsAssignments(pwzDomain, pwzName, &iOriginalAttributes);
759
+ if (FAILED(hr))
760
+ {
761
+ WcaLogError(hr, "failed to get existing user rights: %ls, continuing anyway.", pwzName);
762
+ }
763
+ else
764
+ {
765
+ if (!(SCAU_ALLOW_LOGON_AS_SERVICE & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes))
766
+ {
767
+ iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
768
+ }
769
+ if (!(SCAU_ALLOW_LOGON_AS_BATCH & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes))
770
+ {
771
+ iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
772
+ }
773
+ }
774
+
775
+ hr = WcaCaScriptWriteNumber(hRollbackScript, iRollbackAttributes);
776
+ ExitOnFailure(hr, "Failed to add data to rollback script.");
777
+
778
+ // Nudge the system to get all our rollback data written to disk.
779
+ WcaCaScriptFlush(hRollbackScript);
780
+ }
781
+
782
if (!(SCAU_DONT_CREATE_USER & iAttributes))
783
{
784
::ZeroMemory(&userInfo, sizeof(USER_INFO_1));
@@ -614,6 +869,8 @@ extern "C" UINT __stdcall CreateUser(
869
ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName);
870
871
LExit:
872
+ WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
873
+
874
if (puserInfo)
875
{
876
::NetApiBufferFree((LPVOID)puserInfo);
@@ -627,6 +884,7 @@ LExit:
884
ReleaseStr(pwzData);
885
ReleaseStr(pwzName);
886
ReleaseStr(pwzDomain);
887
+ ReleaseStr(pwzScriptKey);
888
ReleaseStr(pwzPassword);
889
ReleaseStr(pwzGroup);
890
ReleaseStr(pwzGroupDomain);
@@ -650,15 +908,14 @@ LExit:
908
909
910
/********************************************************************
653
- RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
911
+ CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback
912
655
- Input: deferred CustomActionData - Name\tDomain
913
* *****************************************************************/
657
-extern "C" UINT __stdcall RemoveUser(
914
+extern "C" UINT __stdcall CreateUserRollback(
915
MSIHANDLE hInstall
659
- )
916
+)
917
{
661
- //AssertSz(0, "Debug RemoveAccount");
918
+ //AssertSz(0, "Debug CreateUserRollback");
919
920
HRESULT hr = S_OK;
921
UINT er = ERROR_SUCCESS;
@@ -666,15 +923,16 @@ extern "C" UINT __stdcall RemoveUser(
923
LPWSTR pwzData = NULL;
924
LPWSTR pwz = NULL;
925
LPWSTR pwzName = NULL;
669
- LPWSTR pwzDomain= NULL;
670
- LPWSTR pwzGroup = NULL;
671
- LPWSTR pwzGroupDomain = NULL;
926
+ LPWSTR pwzDomain = NULL;
927
+ LPWSTR pwzScriptKey = NULL;
928
int iAttributes = 0;
673
- LPCWSTR wz = NULL;
674
- PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
929
BOOL fInitializedCom = FALSE;
930
677
- hr = WcaInitialize(hInstall, "RemoveUser");
931
+ WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
932
+ LPWSTR pwzRollbackData = NULL;
933
+ int iOriginalAttributes = 0;
934
+
935
+ hr = WcaInitialize(hInstall, "CreateUserRollback");
936
ExitOnFailure(hr, "failed to initialize");
937
938
hr = ::CoInitialize(NULL);
@@ -690,6 +948,9 @@ extern "C" UINT __stdcall RemoveUser(
948
// Read in the CustomActionData
949
//
950
pwz = pwzData;
951
+ hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
952
+ ExitOnFailure(hr, "failed to read encoding key from custom action data");
953
+
954
hr = WcaReadStringFromCaData(&pwz, &pwzName);
955
ExitOnFailure(hr, "failed to read name from custom action data");
956
@@ -699,96 +960,110 @@ extern "C" UINT __stdcall RemoveUser(
960
hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
961
ExitOnFailure(hr, "failed to read attributes from custom action data");
962
702
- //
703
- // Remove the logon as service privilege.
704
- //
705
- if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
963
+ // Best effort to read original configuration from CreateUser.
964
+ hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
965
+ if (FAILED(hr))
966
{
707
- hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, FALSE);
708
- if (FAILED(hr))
709
- {
710
- WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
711
- hr = S_OK;
712
- }
967
+ WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway.");
968
}
714
-
715
- if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
969
+ else
970
{
717
- hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, FALSE);
971
+ hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData);
972
if (FAILED(hr))
973
{
720
- WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
721
- hr = S_OK;
974
+ WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway.");
975
}
723
- }
724
-
725
- //
726
- // Remove the User Account if the user was created by us.
727
- //
728
- if (!(SCAU_DONT_CREATE_USER & iAttributes))
729
- {
730
- if (pwzDomain && *pwzDomain)
976
+ else
977
{
732
- er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo );
733
- if (RPC_S_SERVER_UNAVAILABLE == er)
734
- {
735
- // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
736
- er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo );
737
- }
738
- if (ERROR_SUCCESS == er)
739
- {
740
- wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
741
- }
742
- else
743
- {
744
- wz = pwzDomain;
745
- }
746
- }
747
-
748
- er = ::NetUserDel(wz, pwzName);
749
- if (NERR_UserNotFound == er)
750
- {
751
- er = NERR_Success;
752
- }
753
- ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", pwzName);
754
- }
755
- else
756
- {
757
- //
758
- // Remove the user from the groups
759
- //
760
- while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
761
- {
762
- hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
978
+ WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData);
979
980
+ pwz = pwzRollbackData;
981
+ hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes);
982
if (FAILED(hr))
983
{
766
- WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
984
+ WcaLogError(hr, "failed to read attributes from rollback data, continuing anyway");
985
}
986
else
987
{
770
- hr = RemoveUserFromGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
771
- if (FAILED(hr))
772
- {
773
- WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", pwzName, pwzGroup);
774
- }
988
+ iAttributes |= iOriginalAttributes;
989
}
990
}
991
+ }
992
778
- if (E_NOMOREITEMS == hr) // if there are no more items, all is well
779
- {
780
- hr = S_OK;
781
- }
993
+ hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
994
+
995
+LExit:
996
+ WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
997
+
998
+ ReleaseStr(pwzData);
999
+ ReleaseStr(pwzName);
1000
+ ReleaseStr(pwzDomain);
1001
+ ReleaseStr(pwzScriptKey);
1002
+ ReleaseStr(pwzRollbackData);
1003
783
- ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", pwzName);
1004
+ if (fInitializedCom)
1005
+ {
1006
+ ::CoUninitialize();
1007
}
1008
786
-LExit:
787
- if (pDomainControllerInfo)
1009
+ if (FAILED(hr))
1010
{
789
- ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
1011
+ er = ERROR_INSTALL_FAILURE;
1012
}
1013
1014
+ return WcaFinalize(er);
1015
+}
1016
+
1017
+
1018
+/********************************************************************
1019
+ RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
1020
+
1021
+ Input: deferred CustomActionData - Name\tDomain
1022
+ * *****************************************************************/
1023
+extern "C" UINT __stdcall RemoveUser(
1024
+ MSIHANDLE hInstall
1025
+)
1026
+{
1027
+ //AssertSz(0, "Debug RemoveUser");
1028
+
1029
+ HRESULT hr = S_OK;
1030
+ UINT er = ERROR_SUCCESS;
1031
+
1032
+ LPWSTR pwzData = NULL;
1033
+ LPWSTR pwz = NULL;
1034
+ LPWSTR pwzName = NULL;
1035
+ LPWSTR pwzDomain = NULL;
1036
+ int iAttributes = 0;
1037
+ BOOL fInitializedCom = FALSE;
1038
+
1039
+ hr = WcaInitialize(hInstall, "RemoveUser");
1040
+ ExitOnFailure(hr, "failed to initialize");
1041
+
1042
+ hr = ::CoInitialize(NULL);
1043
+ ExitOnFailure(hr, "failed to initialize COM");
1044
+ fInitializedCom = TRUE;
1045
+
1046
+ hr = WcaGetProperty(L"CustomActionData", &pwzData);
1047
+ ExitOnFailure(hr, "failed to get CustomActionData");
1048
+
1049
+ WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
1050
+
1051
+ //
1052
+ // Read in the CustomActionData
1053
+ //
1054
+ pwz = pwzData;
1055
+ hr = WcaReadStringFromCaData(&pwz, &pwzName);
1056
+ ExitOnFailure(hr, "failed to read name from custom action data");
1057
+
1058
+ hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
1059
+ ExitOnFailure(hr, "failed to read domain from custom action data");
1060
+
1061
+ hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1062
+ ExitOnFailure(hr, "failed to read attributes from custom action data");
1063
+
1064
+ hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
1065
+
1066
+LExit:
1067
ReleaseStr(pwzData);
1068
ReleaseStr(pwzName);
1069
ReleaseStr(pwzDomain);
src/ca/scauser.cpp
+33
@@ -475,10 +475,19 @@ HRESULT ScaUserExecute(
475
DWORD er = 0;
476
PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477
478
+ LPWSTR pwzBaseScriptKey = NULL;
479
+ DWORD cScriptKey = 0;
480
+
481
USER_INFO_0 *pUserInfo = NULL;
482
+ LPWSTR pwzScriptKey = NULL;
483
LPWSTR pwzActionData = NULL;
484
LPWSTR pwzRollbackData = NULL;
485
486
+ // Get the base script key for this CustomAction.
487
+ hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
488
+ ExitOnFailure(hr, "Failed to get encoding key.");
489
+
490
+ // Loop through all the users to be configured.
491
for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
492
{
493
USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
@@ -555,6 +564,17 @@ HRESULT ScaUserExecute(
564
// Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
565
if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
566
{
567
+ ++cScriptKey;
568
+ hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
569
+ ExitOnFailure(hr, "Failed to create encoding key.");
570
+
571
+ // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
572
+ hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
573
+ ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
574
+
575
+ hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
576
+ ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
577
+
578
INT iRollbackUserAttributes = psu->iAttributes;
579
580
// If the user already exists, ensure this is accounted for in rollback
@@ -567,6 +587,10 @@ HRESULT ScaUserExecute(
587
iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
588
}
589
590
+ // The deferred CA determines when to rollback User Rights Assignments so these should never be set.
591
+ iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_SERVICE;
592
+ iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_BATCH;
593
+
594
hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
595
ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
596
hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
@@ -584,6 +608,12 @@ HRESULT ScaUserExecute(
608
hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
609
ExitOnFailure(hr, "failed to schedule CreateUserRollback");
610
}
611
+ else
612
+ {
613
+ // Write empty script key to CustomActionData since there is no rollback.
614
+ hr = WcaWriteStringToCaData(L"", &pwzActionData);
615
+ ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
616
+ }
617
618
//
619
// Schedule the creation now.
@@ -614,6 +644,7 @@ HRESULT ScaUserExecute(
644
ExitOnFailure(hr, "failed to schedule RemoveUser");
645
}
646
647
+ ReleaseNullStr(pwzScriptKey);
648
ReleaseNullStr(pwzActionData);
649
ReleaseNullStr(pwzRollbackData);
650
if (pUserInfo)
@@ -629,6 +660,8 @@ HRESULT ScaUserExecute(
660
}
661
662
LExit:
663
+ ReleaseStr(pwzBaseScriptKey);
664
+ ReleaseStr(pwzScriptKey);
665
ReleaseStr(pwzActionData);
666
ReleaseStr(pwzRollbackData);
667
if (pUserInfo)
src/ca/utilca.def
+1
@@ -45,6 +45,7 @@ EXPORTS
45
CreateSmb
46
DropSmb
47
CreateUser
48
+ CreateUserRollback
49
RemoveUser
50
;scasched.cpp
51
ConfigurePerfmonInstall
src/wixlib/UtilExtension_Platform.wxi
+1
-1
@@ -137,7 +137,7 @@
137
138
<CustomAction Id="$(var.Prefix)ConfigureUsers$(var.Suffix)" DllEntry="ConfigureUsers" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
139
<CustomAction Id="$(var.Prefix)CreateUser$(var.Suffix)" DllEntry="CreateUser" Impersonate="no" Execute="deferred" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
140
- <CustomAction Id="$(var.Prefix)CreateUserRollback$(var.Suffix)" DllEntry="RemoveUser" Impersonate="no" Execute="rollback" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
140
+ <CustomAction Id="$(var.Prefix)CreateUserRollback$(var.Suffix)" DllEntry="CreateUserRollback" Impersonate="no" Execute="rollback" Return="check" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
141
<!-- RemoveUser is a type commit action because it is not possible to rollback the removal of a user -->
142
<CustomAction Id="$(var.Prefix)RemoveUser$(var.Suffix)" DllEntry="RemoveUser" Impersonate="no" Execute="commit" Return="ignore" HideTarget="yes" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" />
143