@joebigelow / wix / commits / 4b3f52f1

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