diff options
-rw-r--r-- | src/ca/scaexec.cpp | 433 | ||||
-rw-r--r-- | src/ca/scauser.cpp | 33 | ||||
-rw-r--r-- | src/ca/utilca.def | 1 | ||||
-rw-r--r-- | src/wixlib/UtilExtension_Platform.wxi | 2 |
4 files changed, 389 insertions, 80 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 | ||
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 | |||
296 | static HRESULT ModifyUserLocalServiceRight( | 400 | static 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 | ||
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 | |||
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 | ||
616 | LExit: | 871 | LExit: |
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 | * *****************************************************************/ |
657 | extern "C" UINT __stdcall RemoveUser( | 914 | extern "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; | 995 | LExit: |
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 | ||
786 | LExit: | 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 | * *****************************************************************/ | ||
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: | ||
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 | ||
631 | LExit: | 662 | LExit: |
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 |
diff --git a/src/wixlib/UtilExtension_Platform.wxi b/src/wixlib/UtilExtension_Platform.wxi index d88b2a57..913c01b9 100644 --- a/src/wixlib/UtilExtension_Platform.wxi +++ b/src/wixlib/UtilExtension_Platform.wxi | |||
@@ -137,7 +137,7 @@ | |||
137 | 137 | ||
138 | <CustomAction Id="$(var.Prefix)ConfigureUsers$(var.Suffix)" DllEntry="ConfigureUsers" Execute="immediate" Return="check" SuppressModularization="yes" BinaryRef="$(var.Prefix)UtilCA$(var.Suffix)" /> | 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)" /> | 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 --> | 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)" /> | 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 | 143 | ||