aboutsummaryrefslogtreecommitdiff
path: root/src/ca
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-11 14:15:32 -0700
committerRob Mensching <rob@firegiant.com>2021-04-12 19:28:07 -0700
commit4b3f52f14bce8a032fcc476556cc4d60aa20241b (patch)
tree227e8bfd5263e7d7acb8d37715155e61ad1051ba /src/ca
parentae7e9817bb10d635e031e51496f2e529595a9cfe (diff)
downloadwix-4b3f52f14bce8a032fcc476556cc4d60aa20241b.tar.gz
wix-4b3f52f14bce8a032fcc476556cc4d60aa20241b.tar.bz2
wix-4b3f52f14bce8a032fcc476556cc4d60aa20241b.zip
Fix rollback of user rights
Diffstat (limited to 'src/ca')
-rw-r--r--src/ca/scaexec.cpp433
-rw-r--r--src/ca/scauser.cpp33
-rw-r--r--src/ca/utilca.def1
3 files changed, 388 insertions, 79 deletions
diff --git a/src/ca/scaexec.cpp b/src/ca/scaexec.cpp
index ab9e6599..5845c1b4 100644
--- a/src/ca/scaexec.cpp
+++ b/src/ca/scaexec.cpp
@@ -293,6 +293,110 @@ LExit:
293} 293}
294 294
295 295
296static 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
328LExit:
329 if (rgSids)
330 {
331 ::LsaFreeMemory(rgSids);
332 }
333
334 return hr;
335}
336
337
338static 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
388LExit:
389 if (hPolicy)
390 {
391 ::LsaClose(hPolicy);
392 }
393
394 ReleaseSid(psid);
395 ReleaseStr(pwzUser);
396 return hr;
397}
398
399
296static HRESULT ModifyUserLocalServiceRight( 400static HRESULT ModifyUserLocalServiceRight(
297 __in_opt LPCWSTR wzDomain, 401 __in_opt LPCWSTR wzDomain,
298 __in LPCWSTR wzName, 402 __in LPCWSTR wzName,
@@ -466,6 +570,117 @@ static void SetUserPasswordAndAttributes(
466} 570}
467 571
468 572
573static 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
674LExit:
675 if (pDomainControllerInfo)
676 {
677 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
678 }
679
680 return hr;
681}
682
683
469/******************************************************************** 684/********************************************************************
470 CreateUser - CUSTOM ACTION ENTRY POINT for creating users 685 CreateUser - CUSTOM ACTION ENTRY POINT for creating users
471 686
@@ -484,6 +699,7 @@ extern "C" UINT __stdcall CreateUser(
484 LPWSTR pwz = NULL; 699 LPWSTR pwz = NULL;
485 LPWSTR pwzName = NULL; 700 LPWSTR pwzName = NULL;
486 LPWSTR pwzDomain = NULL; 701 LPWSTR pwzDomain = NULL;
702 LPWSTR pwzScriptKey = NULL;
487 LPWSTR pwzPassword = NULL; 703 LPWSTR pwzPassword = NULL;
488 LPWSTR pwzGroup = NULL; 704 LPWSTR pwzGroup = NULL;
489 LPWSTR pwzGroupDomain = NULL; 705 LPWSTR pwzGroupDomain = NULL;
@@ -491,6 +707,10 @@ extern "C" UINT __stdcall CreateUser(
491 int iAttributes = 0; 707 int iAttributes = 0;
492 BOOL fInitializedCom = FALSE; 708 BOOL fInitializedCom = FALSE;
493 709
710 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
711 int iOriginalAttributes = 0;
712 int iRollbackAttributes = 0;
713
494 USER_INFO_1 userInfo; 714 USER_INFO_1 userInfo;
495 USER_INFO_1* puserInfo = NULL; 715 USER_INFO_1* puserInfo = NULL;
496 DWORD dw; 716 DWORD dw;
@@ -521,9 +741,44 @@ extern "C" UINT __stdcall CreateUser(
521 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); 741 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
522 ExitOnFailure(hr, "failed to read attributes from custom action data"); 742 ExitOnFailure(hr, "failed to read attributes from custom action data");
523 743
744 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
745 ExitOnFailure(hr, "failed to read encoding key from custom action data");
746
524 hr = WcaReadStringFromCaData(&pwz, &pwzPassword); 747 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
525 ExitOnFailure(hr, "failed to read password from custom action data"); 748 ExitOnFailure(hr, "failed to read password from custom action data");
526 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
527 if (!(SCAU_DONT_CREATE_USER & iAttributes)) 782 if (!(SCAU_DONT_CREATE_USER & iAttributes))
528 { 783 {
529 ::ZeroMemory(&userInfo, sizeof(USER_INFO_1)); 784 ::ZeroMemory(&userInfo, sizeof(USER_INFO_1));
@@ -614,6 +869,8 @@ extern "C" UINT __stdcall CreateUser(
614 ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName); 869 ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName);
615 870
616LExit: 871LExit:
872 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
873
617 if (puserInfo) 874 if (puserInfo)
618 { 875 {
619 ::NetApiBufferFree((LPVOID)puserInfo); 876 ::NetApiBufferFree((LPVOID)puserInfo);
@@ -627,6 +884,7 @@ LExit:
627 ReleaseStr(pwzData); 884 ReleaseStr(pwzData);
628 ReleaseStr(pwzName); 885 ReleaseStr(pwzName);
629 ReleaseStr(pwzDomain); 886 ReleaseStr(pwzDomain);
887 ReleaseStr(pwzScriptKey);
630 ReleaseStr(pwzPassword); 888 ReleaseStr(pwzPassword);
631 ReleaseStr(pwzGroup); 889 ReleaseStr(pwzGroup);
632 ReleaseStr(pwzGroupDomain); 890 ReleaseStr(pwzGroupDomain);
@@ -650,15 +908,14 @@ LExit:
650 908
651 909
652/******************************************************************** 910/********************************************************************
653 RemoveUser - CUSTOM ACTION ENTRY POINT for removing users 911 CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback
654 912
655 Input: deferred CustomActionData - Name\tDomain
656 * *****************************************************************/ 913 * *****************************************************************/
657extern "C" UINT __stdcall RemoveUser( 914extern "C" UINT __stdcall CreateUserRollback(
658 MSIHANDLE hInstall 915 MSIHANDLE hInstall
659 ) 916)
660{ 917{
661 //AssertSz(0, "Debug RemoveAccount"); 918 //AssertSz(0, "Debug CreateUserRollback");
662 919
663 HRESULT hr = S_OK; 920 HRESULT hr = S_OK;
664 UINT er = ERROR_SUCCESS; 921 UINT er = ERROR_SUCCESS;
@@ -666,15 +923,16 @@ extern "C" UINT __stdcall RemoveUser(
666 LPWSTR pwzData = NULL; 923 LPWSTR pwzData = NULL;
667 LPWSTR pwz = NULL; 924 LPWSTR pwz = NULL;
668 LPWSTR pwzName = NULL; 925 LPWSTR pwzName = NULL;
669 LPWSTR pwzDomain= NULL; 926 LPWSTR pwzDomain = NULL;
670 LPWSTR pwzGroup = NULL; 927 LPWSTR pwzScriptKey = NULL;
671 LPWSTR pwzGroupDomain = NULL;
672 int iAttributes = 0; 928 int iAttributes = 0;
673 LPCWSTR wz = NULL;
674 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
675 BOOL fInitializedCom = FALSE; 929 BOOL fInitializedCom = FALSE;
676 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");
678 ExitOnFailure(hr, "failed to initialize"); 936 ExitOnFailure(hr, "failed to initialize");
679 937
680 hr = ::CoInitialize(NULL); 938 hr = ::CoInitialize(NULL);
@@ -690,6 +948,9 @@ extern "C" UINT __stdcall RemoveUser(
690 // Read in the CustomActionData 948 // Read in the CustomActionData
691 // 949 //
692 pwz = pwzData; 950 pwz = pwzData;
951 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
952 ExitOnFailure(hr, "failed to read encoding key from custom action data");
953
693 hr = WcaReadStringFromCaData(&pwz, &pwzName); 954 hr = WcaReadStringFromCaData(&pwz, &pwzName);
694 ExitOnFailure(hr, "failed to read name from custom action data"); 955 ExitOnFailure(hr, "failed to read name from custom action data");
695 956
@@ -699,96 +960,110 @@ extern "C" UINT __stdcall RemoveUser(
699 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes); 960 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
700 ExitOnFailure(hr, "failed to read attributes from custom action data"); 961 ExitOnFailure(hr, "failed to read attributes from custom action data");
701 962
702 // 963 // Best effort to read original configuration from CreateUser.
703 // Remove the logon as service privilege. 964 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
704 // 965 if (FAILED(hr))
705 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
706 { 966 {
707 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, FALSE); 967 WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway.");
708 if (FAILED(hr))
709 {
710 WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
711 hr = S_OK;
712 }
713 } 968 }
714 969 else
715 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
716 { 970 {
717 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, FALSE); 971 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData);
718 if (FAILED(hr)) 972 if (FAILED(hr))
719 { 973 {
720 WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing..."); 974 WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway.");
721 hr = S_OK;
722 } 975 }
723 } 976 else
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)
731 { 977 {
732 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo ); 978 WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData);
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);
763 979
980 pwz = pwzRollbackData;
981 hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes);
764 if (FAILED(hr)) 982 if (FAILED(hr))
765 { 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");
767 } 985 }
768 else 986 else
769 { 987 {
770 hr = RemoveUserFromGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain); 988 iAttributes |= iOriginalAttributes;
771 if (FAILED(hr))
772 {
773 WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", pwzName, pwzGroup);
774 }
775 } 989 }
776 } 990 }
991 }
777 992
778 if (E_NOMOREITEMS == hr) // if there are no more items, all is well 993 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
779 { 994
780 hr = S_OK; 995LExit:
781 } 996 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
997
998 ReleaseStr(pwzData);
999 ReleaseStr(pwzName);
1000 ReleaseStr(pwzDomain);
1001 ReleaseStr(pwzScriptKey);
1002 ReleaseStr(pwzRollbackData);
782 1003
783 ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", pwzName); 1004 if (fInitializedCom)
1005 {
1006 ::CoUninitialize();
784 } 1007 }
785 1008
786LExit: 1009 if (FAILED(hr))
787 if (pDomainControllerInfo)
788 { 1010 {
789 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo)); 1011 er = ERROR_INSTALL_FAILURE;
790 } 1012 }
791 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 * *****************************************************************/
1023extern "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
1066LExit:
792 ReleaseStr(pwzData); 1067 ReleaseStr(pwzData);
793 ReleaseStr(pwzName); 1068 ReleaseStr(pwzName);
794 ReleaseStr(pwzDomain); 1069 ReleaseStr(pwzDomain);
diff --git a/src/ca/scauser.cpp b/src/ca/scauser.cpp
index 0d87301f..b25e9daf 100644
--- a/src/ca/scauser.cpp
+++ b/src/ca/scauser.cpp
@@ -475,10 +475,19 @@ HRESULT ScaUserExecute(
475 DWORD er = 0; 475 DWORD er = 0;
476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL; 476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477 477
478 LPWSTR pwzBaseScriptKey = NULL;
479 DWORD cScriptKey = 0;
480
478 USER_INFO_0 *pUserInfo = NULL; 481 USER_INFO_0 *pUserInfo = NULL;
482 LPWSTR pwzScriptKey = NULL;
479 LPWSTR pwzActionData = NULL; 483 LPWSTR pwzActionData = NULL;
480 LPWSTR pwzRollbackData = NULL; 484 LPWSTR pwzRollbackData = NULL;
481 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.
482 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext) 491 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
483 { 492 {
484 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE; 493 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
@@ -555,6 +564,17 @@ HRESULT ScaUserExecute(
555 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user 564 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
556 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER)) 565 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
557 { 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
558 INT iRollbackUserAttributes = psu->iAttributes; 578 INT iRollbackUserAttributes = psu->iAttributes;
559 579
560 // If the user already exists, ensure this is accounted for in rollback 580 // If the user already exists, ensure this is accounted for in rollback
@@ -567,6 +587,10 @@ HRESULT ScaUserExecute(
567 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER; 587 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
568 } 588 }
569 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
570 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData); 594 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
571 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName); 595 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
572 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData); 596 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
@@ -584,6 +608,12 @@ HRESULT ScaUserExecute(
584 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE); 608 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
585 ExitOnFailure(hr, "failed to schedule CreateUserRollback"); 609 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
586 } 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 }
587 617
588 // 618 //
589 // Schedule the creation now. 619 // Schedule the creation now.
@@ -614,6 +644,7 @@ HRESULT ScaUserExecute(
614 ExitOnFailure(hr, "failed to schedule RemoveUser"); 644 ExitOnFailure(hr, "failed to schedule RemoveUser");
615 } 645 }
616 646
647 ReleaseNullStr(pwzScriptKey);
617 ReleaseNullStr(pwzActionData); 648 ReleaseNullStr(pwzActionData);
618 ReleaseNullStr(pwzRollbackData); 649 ReleaseNullStr(pwzRollbackData);
619 if (pUserInfo) 650 if (pUserInfo)
@@ -629,6 +660,8 @@ HRESULT ScaUserExecute(
629 } 660 }
630 661
631LExit: 662LExit:
663 ReleaseStr(pwzBaseScriptKey);
664 ReleaseStr(pwzScriptKey);
632 ReleaseStr(pwzActionData); 665 ReleaseStr(pwzActionData);
633 ReleaseStr(pwzRollbackData); 666 ReleaseStr(pwzRollbackData);
634 if (pUserInfo) 667 if (pUserInfo)
diff --git a/src/ca/utilca.def b/src/ca/utilca.def
index 337c3a68..412d86a3 100644
--- a/src/ca/utilca.def
+++ b/src/ca/utilca.def
@@ -45,6 +45,7 @@ EXPORTS
45 CreateSmb 45 CreateSmb
46 DropSmb 46 DropSmb
47 CreateUser 47 CreateUser
48 CreateUserRollback
48 RemoveUser 49 RemoveUser
49;scasched.cpp 50;scasched.cpp
50 ConfigurePerfmonInstall 51 ConfigurePerfmonInstall