@joebigelow / wix / commits / 237bdc6a

Add WixCanRestart and make wixstdba elevate for restart when required.

Fixes 5499

Sean Hall committed Sep 2, 2022 at 16:12 UTC 237bdc6a97c0dd455da99c16e6b3b7cac4c79e86
20 files changed +336 -76
src/burn/engine/EngineForApplication.h
-16
@@ -6,22 +6,6 @@
6 extern "C" {
7 #endif
8
9 -// constants
10 -
11 -enum WM_BURN
12 -{
13 - WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first.
14 -
15 - WM_BURN_DETECT,
16 - WM_BURN_PLAN,
17 - WM_BURN_ELEVATE,
18 - WM_BURN_APPLY,
19 - WM_BURN_LAUNCH_APPROVED_EXE,
20 - WM_BURN_QUIT,
21 -
22 - WM_BURN_LAST, // this enum value must always be last.
23 -};
24 -
9 // structs
10
11 typedef struct _BOOTSTRAPPER_ENGINE_CONTEXT
src/burn/engine/core.cpp
+4 -3
@@ -593,6 +593,7 @@ LExit:
593
594 extern "C" HRESULT CoreElevate(
595 __in BURN_ENGINE_STATE* pEngineState,
596 + __in WM_BURN reason,
597 __in_opt HWND hwndParent
598 )
599 {
@@ -608,7 +609,7 @@ extern "C" HRESULT CoreElevate(
609 ExitOnFailure(hr, "Failed to cache engine to working directory.");
610 }
611
611 - hr = ElevationElevate(pEngineState, hwndParent);
612 + hr = ElevationElevate(pEngineState, reason, hwndParent);
613 if (E_SUSPECTED_AV_INTERFERENCE == hr && 1 > cAVRetryAttempts)
614 {
615 ++cAVRetryAttempts;
@@ -720,7 +721,7 @@ extern "C" HRESULT CoreApply(
721 // Elevate.
722 if (pEngineState->plan.fPerMachine)
723 {
723 - hr = CoreElevate(pEngineState, pEngineState->userExperience.hwndApply);
724 + hr = CoreElevate(pEngineState, WM_BURN_APPLY, pEngineState->userExperience.hwndApply);
725 ExitOnFailure(hr, "Failed to elevate.");
726
727 hr = ElevationApplyInitialize(pEngineState->companionConnection.hPipe, &pEngineState->userExperience, &pEngineState->variables, &pEngineState->plan);
@@ -872,7 +873,7 @@ extern "C" HRESULT CoreLaunchApprovedExe(
873 ExitOnRootFailure(hr, "BA aborted LaunchApprovedExe begin.");
874
875 // Elevate.
875 - hr = CoreElevate(pEngineState, pLaunchApprovedExe->hwndParent);
876 + hr = CoreElevate(pEngineState, WM_BURN_LAUNCH_APPROVED_EXE, pLaunchApprovedExe->hwndParent);
877 ExitOnFailure(hr, "Failed to elevate.");
878
879 // Launch.
src/burn/engine/core.h
+1
@@ -251,6 +251,7 @@ HRESULT CorePlan(
251 );
252 HRESULT CoreElevate(
253 __in BURN_ENGINE_STATE* pEngineState,
254 + __in WM_BURN reason,
255 __in_opt HWND hwndParent
256 );
257 HRESULT CoreApply(
src/burn/engine/elevation.cpp
+6 -1
@@ -402,6 +402,7 @@ static HRESULT ElevatedOnExecuteActionComplete(
402
403 extern "C" HRESULT ElevationElevate(
404 __in BURN_ENGINE_STATE* pEngineState,
405 + __in WM_BURN reason,
406 __in_opt HWND hwndParent
407 )
408 {
@@ -450,7 +451,11 @@ extern "C" HRESULT ElevationElevate(
451 else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
452 {
453 // The user clicked "Cancel" on the elevation prompt or the elevation prompt timed out, provide the notification with the option to retry.
453 - hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
454 + if (WM_BURN_APPLY == reason)
455 + {
456 + hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
457 + }
458 +
459 nResult = UserExperienceSendError(&pEngineState->userExperience, BOOTSTRAPPER_ERROR_TYPE_ELEVATE, NULL, hr, NULL, MB_ICONERROR | MB_RETRYCANCEL, IDNOACTION);
460 }
461 } while (IDRETRY == nResult);
src/burn/engine/elevation.h
+1
@@ -10,6 +10,7 @@ extern "C" {
10 // Parent (per-user process) side functions.
11 HRESULT ElevationElevate(
12 __in BURN_ENGINE_STATE* pEngineState,
13 + __in WM_BURN reason,
14 __in_opt HWND hwndParent
15 );
16 HRESULT ElevationApplyInitialize(
src/burn/engine/engine.cpp
+3 -20
@@ -893,7 +893,7 @@ static HRESULT ProcessMessage(
893 break;
894
895 case WM_BURN_ELEVATE:
896 - hr = CoreElevate(pEngineState, reinterpret_cast<HWND>(pmsg->lParam));
896 + hr = CoreElevate(pEngineState, WM_BURN_ELEVATE, reinterpret_cast<HWND>(pmsg->lParam));
897 break;
898
899 case WM_BURN_APPLY:
@@ -1069,26 +1069,10 @@ static HRESULT Restart(
1069 )
1070 {
1071 HRESULT hr = S_OK;
1072 - HANDLE hProcessToken = NULL;
1073 - TOKEN_PRIVILEGES priv = { };
1072 DWORD dwRetries = 0;
1073
1076 - if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
1077 - {
1078 - ExitWithLastError(hr, "Failed to get process token.");
1079 - }
1080 -
1081 - priv.PrivilegeCount = 1;
1082 - priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1083 - if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
1084 - {
1085 - ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
1086 - }
1087 -
1088 - if (!::AdjustTokenPrivileges(hProcessToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0))
1089 - {
1090 - ExitWithLastError(hr, "Failed to adjust token to add shutdown privileges.");
1091 - }
1074 + hr = ProcEnablePrivilege(::GetCurrentProcess(), SE_SHUTDOWN_NAME);
1075 + ExitOnFailure(hr, "Failed to enable shutdown privilege in process token.");
1076
1077 pEngineState->fRestarting = TRUE;
1078 CoreUpdateRestartState(pEngineState, BURN_RESTART_STATE_REQUESTING);
@@ -1147,7 +1131,6 @@ static HRESULT Restart(
1131 }
1132
1133 LExit:
1150 - ReleaseHandle(hProcessToken);
1134 return hr;
1135 }
1136
src/burn/engine/platform.h
+16
@@ -8,6 +8,22 @@
8 extern "C" {
9 #endif
10
11 +// constants
12 +
13 +enum WM_BURN
14 +{
15 + WM_BURN_FIRST = WM_APP + 0xFFF, // this enum value must always be first.
16 +
17 + WM_BURN_DETECT,
18 + WM_BURN_PLAN,
19 + WM_BURN_ELEVATE,
20 + WM_BURN_APPLY,
21 + WM_BURN_LAUNCH_APPROVED_EXE,
22 + WM_BURN_QUIT,
23 +
24 + WM_BURN_LAST, // this enum value must always be last.
25 +};
26 +
27 // forward declare
28
29 enum BURN_MODE;
src/burn/engine/variable.cpp
+25
@@ -153,6 +153,10 @@ static HRESULT InitializeVariablePrivileged(
153 __in DWORD_PTR dwpData,
154 __inout BURN_VARIANT* pValue
155 );
156 +static HRESULT InitializeVariableProcessTokenPrivilege(
157 + __in DWORD_PTR dwpData,
158 + __inout BURN_VARIANT* pValue
159 + );
160 static HRESULT InitializeSystemLanguageID(
161 __in DWORD_PTR dwpData,
162 __inout BURN_VARIANT* pValue
@@ -284,6 +288,7 @@ extern "C" HRESULT VariableInitialize(
288 {L"WindowsFolder", InitializeVariableCsidlFolder, CSIDL_WINDOWS},
289 {L"WindowsVolume", InitializeVariableWindowsVolumeFolder, 0},
290 {BURN_BUNDLE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE},
291 + {L"WixCanRestart", InitializeVariableProcessTokenPrivilege, (DWORD_PTR)SE_SHUTDOWN_NAME},
292 {BURN_BUNDLE_COMMAND_LINE_ACTION, InitializeVariableNumeric, 0, FALSE, TRUE},
293 {BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, InitializeVariableString, NULL, FALSE, TRUE},
294 {BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, InitializeVariableString, NULL, FALSE, TRUE},
@@ -2165,6 +2170,26 @@ LExit:
2170 return hr;
2171 }
2172
2173 +static HRESULT InitializeVariableProcessTokenPrivilege(
2174 + __in DWORD_PTR dwpData,
2175 + __inout BURN_VARIANT* pValue
2176 + )
2177 +{
2178 + HRESULT hr = S_OK;
2179 + BOOL fHasPrivilege = FALSE;
2180 + LPCWSTR wzPrivilegeName = (LPCWSTR)dwpData;
2181 +
2182 + hr = ProcHasPrivilege(::GetCurrentProcess(), wzPrivilegeName, &fHasPrivilege);
2183 + ExitOnFailure(hr, "Failed to check if process token has privilege: %ls.", wzPrivilegeName);
2184 +
2185 + // set value
2186 + hr = BVariantSetNumeric(pValue, fHasPrivilege);
2187 + ExitOnFailure(hr, "Failed to set variant value.");
2188 +
2189 +LExit:
2190 + return hr;
2191 +}
2192 +
2193 static HRESULT InitializeSystemLanguageID(
2194 __in DWORD_PTR dwpData,
2195 __inout BURN_VARIANT* pValue
src/burn/test/BurnUnitTest/ElevationTest.cpp
+1 -1
@@ -68,7 +68,7 @@ namespace Bootstrapper
68 //
69 // per-user side setup
70 //
71 - hr = ElevationElevate(&engineState, NULL);
71 + hr = ElevationElevate(&engineState, WM_BURN_ELEVATE, NULL);
72 TestThrowOnFailure(hr, L"Failed to elevate.");
73
74 // post execute message
src/burn/test/BurnUnitTest/ExitCodeTest.cpp
+1 -1
@@ -118,7 +118,7 @@ static void LoadEngineState(
118 //
119 LoadEngineState(&engineState);
120
121 - hr = ElevationElevate(&engineState, NULL);
121 + hr = ElevationElevate(&engineState, WM_BURN_ELEVATE, NULL);
122 TestThrowOnFailure(hr, L"Failed to elevate.");
123
124 for (DWORD i = 0; i < countof(rgExitCodeItems); ++i)
src/burn/test/BurnUnitTest/VariableTest.cpp
+1
@@ -564,6 +564,7 @@ namespace Bootstrapper
564 VariableGetNumericHelper(&variables, L"TerminalServer");
565 VariableGetNumericHelper(&variables, L"UserUILanguageID");
566 VariableGetNumericHelper(&variables, L"UserLanguageID");
567 + VariableGetNumericHelper(&variables, L"WixCanRestart");
568
569 // known folders
570 Assert::Equal<String^>(Environment::GetFolderPath(Environment::SpecialFolder::ApplicationData) + "\\", VariableGetStringHelper(&variables, L"AppDataFolder"));
src/ext/Bal/wixstdba/WixStandardBootstrapperApplication.cpp
+56 -7
@@ -4,6 +4,7 @@
4 #include "BalBaseBootstrapperApplicationProc.h"
5 #include "BalBaseBootstrapperApplication.h"
6
7 +static const LPCWSTR WIXBUNDLE_VARIABLE_CANRESTART = L"WixCanRestart";
8 static const LPCWSTR WIXBUNDLE_VARIABLE_ELEVATED = L"WixBundleElevated";
9
10 static const LPCWSTR WIXSTDBA_WINDOW_CLASS = L"WixStdBA";
@@ -1289,6 +1290,28 @@ public: // IBootstrapperApplication
1290 return S_OK;
1291 }
1292
1293 +
1294 + virtual STDMETHODIMP OnElevateComplete(
1295 + __in HRESULT hrStatus
1296 + )
1297 + {
1298 + if (m_fElevatingForRestart)
1299 + {
1300 + m_fElevatingForRestart = FALSE;
1301 +
1302 + if (SUCCEEDED(hrStatus))
1303 + {
1304 + m_fAllowRestart = TRUE;
1305 +
1306 + ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0);
1307 + }
1308 + // else if failed then OnError showed the user an error message box
1309 + }
1310 +
1311 + return __super::OnElevateComplete(hrStatus);
1312 + }
1313 +
1314 +
1315 virtual STDMETHODIMP_(void) BAProcFallback(
1316 __in BOOTSTRAPPER_APPLICATION_MESSAGE message,
1317 __in const LPVOID pvArgs,
@@ -3734,14 +3757,16 @@ private:
3757
3758 if (dwOldPageId != dwNewPageId)
3759 {
3760 + LONGLONG llCanRestart = 0;
3761 + LONGLONG llElevated = 0;
3762 +
3763 + BalGetNumericVariable(WIXBUNDLE_VARIABLE_CANRESTART, &llCanRestart);
3764 + BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated);
3765 + m_fRestartRequiresElevation = !llCanRestart && !llElevated;
3766 +
3767 // Enable disable controls per-page.
3768 if (m_rgdwPageIds[WIXSTDBA_PAGE_INSTALL] == dwNewPageId) // on the "Install" page, ensure the install button is enabled/disabled correctly.
3769 {
3740 - LONGLONG llElevated = 0;
3741 - if (m_Bundle.fPerMachine)
3742 - {
3743 - BalGetNumericVariable(WIXBUNDLE_VARIABLE_ELEVATED, &llElevated);
3744 - }
3770 ThemeControlElevates(m_pControlInstallButton, (m_Bundle.fPerMachine && !llElevated));
3771
3772 // If the EULA control exists, show it only if a license URL is provided as well.
@@ -3757,12 +3782,18 @@ private:
3782 }
3783 else if (m_rgdwPageIds[WIXSTDBA_PAGE_MODIFY] == dwNewPageId)
3784 {
3785 + ThemeControlElevates(m_pControlRepairButton, (m_Bundle.fPerMachine && !llElevated));
3786 + ThemeControlElevates(m_pControlUninstallButton, (m_Bundle.fPerMachine && !llElevated));
3787 +
3788 ThemeControlEnable(m_pControlRepairButton, !m_fSuppressRepair);
3789 }
3790 else if (m_rgdwPageIds[WIXSTDBA_PAGE_SUCCESS] == dwNewPageId) // on the "Success" page, check if the restart or launch button should be enabled.
3791 {
3792 BOOL fEnableRestartButton = FALSE;
3793 BOOL fLaunchTargetExists = FALSE;
3794 +
3795 + ThemeControlElevates(m_pControlSuccessRestartButton, m_fRestartRequiresElevation);
3796 +
3797 if (m_fShouldRestart)
3798 {
3799 if (BAL_INFO_RESTART_PROMPT == m_BalInfoCommand.restart)
@@ -3784,6 +3815,8 @@ private:
3815 BOOL fShowErrorMessage = FALSE;
3816 BOOL fEnableRestartButton = FALSE;
3817
3818 + ThemeControlElevates(m_pControlFailureRestartButton, m_fRestartRequiresElevation);
3819 +
3820 if (FAILED(m_hrFinal))
3821 {
3822 // If we know the failure message, use that.
@@ -4156,8 +4189,20 @@ private:
4189 {
4190 AssertSz(m_fRestartRequired, "Restart must be requested to be able to click on the restart button.");
4191
4159 - m_fAllowRestart = TRUE;
4160 - ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0);
4192 + if (m_fRestartRequiresElevation)
4193 + {
4194 + m_fElevatingForRestart = TRUE;
4195 + ThemeControlEnable(m_pControlFailureRestartButton, FALSE);
4196 + ThemeControlEnable(m_pControlSuccessRestartButton, FALSE);
4197 +
4198 + m_pEngine->Elevate(m_hWnd);
4199 + }
4200 + else
4201 + {
4202 + m_fAllowRestart = TRUE;
4203 +
4204 + ::SendMessageW(m_hWnd, WM_CLOSE, 0, 0);
4205 + }
4206 }
4207
4208
@@ -4649,6 +4694,8 @@ public:
4694 m_fRestartRequired = FALSE;
4695 m_fShouldRestart = FALSE;
4696 m_fAllowRestart = FALSE;
4697 + m_fRestartRequiresElevation = FALSE;
4698 + m_fElevatingForRestart = FALSE;
4699
4700 m_sczLicenseFile = NULL;
4701 m_sczLicenseUrl = NULL;
@@ -4956,6 +5003,8 @@ private:
5003 BOOL m_fRestartRequired;
5004 BOOL m_fShouldRestart;
5005 BOOL m_fAllowRestart;
5006 + BOOL m_fRestartRequiresElevation;
5007 + BOOL m_fElevatingForRestart;
5008
5009 LPWSTR m_sczLicenseFile;
5010 LPWSTR m_sczLicenseUrl;
src/libs/dutil/WixToolset.DUtil/inc/procutil.h
+14 -2
@@ -23,9 +23,21 @@ HRESULT DAPI ProcSystem(
23 __out BOOL* pfSystem
24 );
25
26 -HRESULT DAPI ProcTokenUser(
26 +HRESULT DAPI ProcGetTokenInformation(
27 __in HANDLE hProcess,
28 - __out TOKEN_USER** ppTokenUser
28 + __in TOKEN_INFORMATION_CLASS tokenInformationClass,
29 + __out LPVOID* ppvTokenInformation
30 + );
31 +
32 +HRESULT DAPI ProcHasPrivilege(
33 + __in HANDLE hProcess,
34 + __in LPCWSTR wzPrivilegeName,
35 + __out BOOL* pfHasPrivilege
36 + );
37 +
38 +HRESULT DAPI ProcEnablePrivilege(
39 + __in HANDLE hProcess,
40 + __in LPCWSTR wzPrivilegeName
41 );
42
43 HRESULT DAPI ProcWow64(
src/libs/dutil/WixToolset.DUtil/procutil.cpp
+89 -17
@@ -85,7 +85,7 @@ extern "C" HRESULT DAPI ProcSystem(
85 HRESULT hr = S_OK;
86 TOKEN_USER* pTokenUser = NULL;
87
88 - hr = ProcTokenUser(hProcess, &pTokenUser);
88 + hr = ProcGetTokenInformation(hProcess, TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser));
89 ProcExitOnFailure(hr, "Failed to get TokenUser from process token.");
90
91 *pfSystem = ::IsWellKnownSid(pTokenUser->User.Sid, WinLocalSystemSid);
@@ -96,15 +96,16 @@ LExit:
96 return hr;
97 }
98
99 -extern "C" HRESULT DAPI ProcTokenUser(
99 +extern "C" HRESULT DAPI ProcGetTokenInformation(
100 __in HANDLE hProcess,
101 - __out TOKEN_USER** ppTokenUser
101 + __in TOKEN_INFORMATION_CLASS tokenInformationClass,
102 + __out LPVOID* ppvTokenInformation
103 )
104 {
105 HRESULT hr = S_OK;
106 DWORD er = ERROR_SUCCESS;
107 HANDLE hToken = NULL;
107 - TOKEN_USER* pTokenUser = NULL;
108 + LPVOID pvTokenInformation = NULL;
109 DWORD cbToken = 0;
110
111 if (!::OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
@@ -112,33 +113,104 @@ extern "C" HRESULT DAPI ProcTokenUser(
113 ProcExitWithLastError(hr, "Failed to open process token.");
114 }
115
115 - if (::GetTokenInformation(hToken, TokenUser, pTokenUser, 0, &cbToken))
116 - {
117 - er = ERROR_SUCCESS;
118 - }
119 - else
116 + if (!::GetTokenInformation(hToken, tokenInformationClass, pvTokenInformation, 0, &cbToken))
117 {
118 er = ::GetLastError();
119 }
120
121 if (er != ERROR_INSUFFICIENT_BUFFER)
122 {
126 - ProcExitOnWin32Error(er, hr, "Failed to get user from process token size.");
123 + ProcExitOnWin32Error(er, hr, "Failed to get information from process token size.");
124 }
125
129 - pTokenUser = reinterpret_cast<TOKEN_USER*>(MemAlloc(cbToken, TRUE));
130 - ProcExitOnNull(pTokenUser, hr, E_OUTOFMEMORY, "Failed to allocate token information.");
126 + pvTokenInformation = MemAlloc(cbToken, TRUE);
127 + ProcExitOnNull(pvTokenInformation, hr, E_OUTOFMEMORY, "Failed to allocate token information.");
128
132 - if (!::GetTokenInformation(hToken, TokenUser, pTokenUser, cbToken, &cbToken))
129 + if (!::GetTokenInformation(hToken, tokenInformationClass, pvTokenInformation, cbToken, &cbToken))
130 {
134 - ProcExitWithLastError(hr, "Failed to get user from process token.");
131 + ProcExitWithLastError(hr, "Failed to get information from process token.");
132 }
133
137 - *ppTokenUser = pTokenUser;
138 - pTokenUser = NULL;
134 + *ppvTokenInformation = pvTokenInformation;
135 + pvTokenInformation = NULL;
136 +
137 +LExit:
138 + ReleaseMem(pvTokenInformation);
139 + ReleaseHandle(hToken);
140 +
141 + return hr;
142 +}
143 +
144 +extern "C" HRESULT DAPI ProcHasPrivilege(
145 + __in HANDLE hProcess,
146 + __in LPCWSTR wzPrivilegeName,
147 + __out BOOL* pfHasPrivilege
148 + )
149 +{
150 + HRESULT hr = S_OK;
151 + TOKEN_PRIVILEGES* pTokenPrivileges = NULL;
152 + LUID luidPrivilege = { };
153 +
154 + *pfHasPrivilege = FALSE;
155 +
156 + if (!::LookupPrivilegeValueW(NULL, wzPrivilegeName, &luidPrivilege))
157 + {
158 + ProcExitWithLastError(hr, "Failed to get privilege LUID: %ls", wzPrivilegeName);
159 + }
160 +
161 + hr = ProcGetTokenInformation(hProcess, TokenPrivileges, reinterpret_cast<LPVOID*>(&pTokenPrivileges));
162 + ProcExitOnFailure(hr, "Failed to get token privilege information.");
163 +
164 + for (DWORD i = 0; i < pTokenPrivileges->PrivilegeCount; ++i)
165 + {
166 + LUID* pTokenLuid = &pTokenPrivileges->Privileges[i].Luid;
167 +
168 + if (luidPrivilege.LowPart == pTokenLuid->LowPart && luidPrivilege.HighPart == pTokenLuid->HighPart)
169 + {
170 + *pfHasPrivilege = TRUE;
171 + break;
172 + }
173 + }
174 +
175 +LExit:
176 + ReleaseMem(pTokenPrivileges);
177 +
178 + return hr;
179 +}
180 +
181 +extern "C" HRESULT DAPI ProcEnablePrivilege(
182 + __in HANDLE hProcess,
183 + __in LPCWSTR wzPrivilegeName
184 + )
185 +{
186 + HRESULT hr = S_OK;
187 + HANDLE hToken = NULL;
188 + TOKEN_PRIVILEGES priv = { };
189 +
190 + priv.PrivilegeCount = 1;
191 + priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
192 +
193 + if (!::LookupPrivilegeValueW(NULL, wzPrivilegeName, &priv.Privileges[0].Luid))
194 + {
195 + ProcExitWithLastError(hr, "Failed to get privilege LUID: %ls", wzPrivilegeName);
196 + }
197 +
198 + if (!::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken))
199 + {
200 + ProcExitWithLastError(hr, "Failed to get process token to adjust privileges.");
201 + }
202 +
203 + if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, 0))
204 + {
205 + ProcExitWithLastError(hr, "Failed to adjust token to add privilege: %ls", wzPrivilegeName);
206 + }
207 +
208 + if (ERROR_NOT_ALL_ASSIGNED == ::GetLastError())
209 + {
210 + hr = S_FALSE;
211 + }
212
213 LExit:
141 - ReleaseMem(pTokenUser);
214 ReleaseHandle(hToken);
215
216 return hr;
src/libs/dutil/test/DUtilUnitTest/ProcUtilTest.cpp
+26 -2
@@ -6,6 +6,7 @@ using namespace System;
6 using namespace System::Security::Principal;
7 using namespace Xunit;
8 using namespace WixBuildTools::TestSupport;
9 +using namespace WixBuildTools::TestSupport::XunitExtensions;
10
11 namespace DutilTests
12 {
@@ -13,7 +14,7 @@ namespace DutilTests
14 {
15 public:
16 [Fact]
16 - void ProcTokenUserTest()
17 + void ProcGetTokenInformationTest()
18 {
19 HRESULT hr = S_OK;
20 TOKEN_USER* pTokenUser = NULL;
@@ -21,7 +22,7 @@ namespace DutilTests
22
23 try
24 {
24 - hr = ProcTokenUser(::GetCurrentProcess(), &pTokenUser);
25 + hr = ProcGetTokenInformation(::GetCurrentProcess(), TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser));
26 NativeAssert::Succeeded(hr, "Failed to get TokenUser for current process.");
27
28 if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid))
@@ -38,5 +39,28 @@ namespace DutilTests
39 ReleaseStr(sczSid);
40 }
41 }
42 +
43 + [SkippableFact]
44 + void ProcHasPrivilegeTest()
45 + {
46 + HRESULT hr = S_OK;
47 + BOOL fHasPrivilege = FALSE;
48 +
49 + hr = ProcHasPrivilege(::GetCurrentProcess(), SE_CREATE_TOKEN_NAME, &fHasPrivilege);
50 + NativeAssert::Succeeded(hr, "Failed to check privilege for current process.");
51 +
52 + if (fHasPrivilege)
53 + {
54 + WixAssert::Skip("Didn't expect process to have SE_CREATE_TOKEN_NAME privilege");
55 + }
56 +
57 + hr = ProcHasPrivilege(::GetCurrentProcess(), SE_INC_WORKING_SET_NAME, &fHasPrivilege);
58 + NativeAssert::Succeeded(hr, "Failed to check privilege for current process.");
59 +
60 + if (!fHasPrivilege)
61 + {
62 + WixAssert::Skip("Expected process to have SE_INC_WORKING_SET_NAME privilege");
63 + }
64 + }
65 };
66 }
src/test/burn/TestData/Manual/BafThmutilTesting/BafThmUtilTesting.cpp
+1 -1
@@ -477,7 +477,7 @@ static HRESULT LogUserSid()
477 TOKEN_USER* pTokenUser = NULL;
478 LPWSTR sczSid = NULL;
479
480 - hr = ProcTokenUser(::GetCurrentProcess(), &pTokenUser);
480 + hr = ProcGetTokenInformation(::GetCurrentProcess(), TokenUser, reinterpret_cast<LPVOID*>(&pTokenUser));
481 BalExitOnFailure(hr, "Failed to get user from process token.");
482
483 if (!::ConvertSidToStringSidW(pTokenUser->User.Sid, &sczSid))
src/test/burn/TestData/Manual/BundleA/ManualTests.txt
+49 -5
@@ -94,11 +94,12 @@ CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege
94 2. Click Install.
95 3. Verify that the UAC prompt came up and accept elevation.
96 4. Click OK on the OnExecuteBegin message box.
97 -5. Click Restart.
98 -6. The machine should restart.
99 -7. Login to the machine.
100 -8. Verify that the bundle did not automatically start running.
101 -9. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like (the process id for w005 must match the elevated i400 and i401):
97 +5. Verify the Restart button doesn't have the UAC shield and then click it.
98 +6. Verify that there was no UAC prompt.
99 +7. The machine should restart.
100 +8. Login to the machine.
101 +9. Verify that the bundle did not automatically start running.
102 +10. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like (the process id for w005 must match the elevated i400 and i401):
103
104 [0DDC:0448]w005: Restarting computer...
105 [1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No
@@ -107,4 +108,47 @@ CanRestartFromUnelevatedPerMachineBundleWithoutShutdownPrivilege
108 [0DDC:0954]i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: No, logoff: No, close app: No
109 =======================================
110
111 +(11. Uninstall the bundle)
112 +
113 +CanRestartFromUnelevatedPerUserBundleWithoutShutdownPrivilege
114 +
115 + Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Only Administrators should have the shutdown privilege. Users should not have it.
116 +
117 +1. Run BundleC.exe unelevated with the command line EXEEXITCODE=3010.
118 +2. Click Install.
119 +3. Verify the Restart button has the UAC shield and then click it.
120 +4. Verify that the UAC prompt came up and accept elevation.
121 +5. The machine should restart.
122 +6. Login to the machine.
123 +7. Verify that the bundle did not automatically start running.
124 +8. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like (the process id for w005 must match the elevated i400 and i401):
125 +
126 +[0DDC:0448]w005: Restarting computer...
127 +[1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No
128 +[1228:18CC]i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: No, logoff: No, close app: No
129 +[0DDC:0954]i400: Received system request to shut down the process: allowed: Yes, elevated: Yes, critical: No, logoff: No, close app: No
130 +[0DDC:0954]i401: Received result of system request to shut down the process: closing: Yes, elevated: Yes, critical: No, logoff: No, close app: No
131 +=======================================
132 +
133 +(9. Uninstall the bundle)
134 +
135 +CanRestartFromUnelevatedPerUserBundleWithShutdownPrivilege
136 +
137 + Note: Requires different User Rights Assignment settings from CanLogRestartFailure - Administrators and Users should have the shutdown privilege.
138 +
139 +1. Run BundleC.exe unelevated with the command line EXEEXITCODE=3010.
140 +2. Click Install.
141 +3. Verify that there was no UAC prompt.
142 +4. Verify the Restart button doesn't have the UAC shield and then click it.
143 +5. Verify that there was no UAC prompt.
144 +6. The machine should restart.
145 +7. Login to the machine.
146 +8. Verify that the bundle did not automatically start running.
147 +9. Look in the bundle log and verify that the restart request didn't cause any errors, and that it logged messages that look like (there should be no elevated i400 and i401 and the process id for w005 must match):
148 +
149 +[1228:0448]w005: Restarting computer...
150 +[1228:18CC]i400: Received system request to shut down the process: allowed: Yes, elevated: No, critical: No, logoff: No, close app: No
151 +[1228:18CC]i401: Received result of system request to shut down the process: closing: Yes, elevated: No, critical: No, logoff: No, close app: No
152 +=======================================
153 +
154 (10. Uninstall the bundle)
src/test/burn/TestData/Manual/BundleC/BundleC.wixproj new
+18
@@ -0,0 +1,18 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <OutputType>Bundle</OutputType>
5 + <BA>hyperlinkLicense</BA>
6 + <UpgradeCode>{5CAE82BB-A2F3-4994-9BA8-4ACEAFAE7738}</UpgradeCode>
7 + </PropertyGroup>
8 + <ItemGroup>
9 + <Compile Include="..\..\Templates\Bundle.wxs" Link="Bundle.wxs" />
10 + </ItemGroup>
11 + <ItemGroup>
12 + <ProjectReference Include="..\PackagePerUser\PackagePerUser.wixproj" />
13 + <ProjectReference Include="..\..\TestBA\TestBAWixlib\testbawixlib.wixproj" />
14 + </ItemGroup>
15 + <ItemGroup>
16 + <PackageReference Include="WixToolset.Bal.wixext" />
17 + </ItemGroup>
18 +</Project>
\ No newline at end of file
src/test/burn/TestData/Manual/BundleC/BundleC.wxs new
+15
@@ -0,0 +1,15 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +
3 +
4 +<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:bal="http://wixtoolset.org/schemas/v4/wxs/bal">
5 + <Fragment>
6 + <PackageGroup Id="BundlePackages">
7 + <MsiPackage Id="PackagePerUser" SourceFile="$(var.PackagePerUser.TargetPath)" />
8 + <ExePackage Id="ExeA" Cache="remove" PerMachine="no"
9 + DetectCondition="" Permanent="yes" InstallArguments="/ec [EXEEXITCODE]">
10 + <PayloadGroupRef Id="TestExePayloads" />
11 + </ExePackage>
12 + </PackageGroup>
13 + <Variable Name="EXEEXITCODE" bal:Overridable="yes" Value="0" />
14 + </Fragment>
15 +</Wix>
src/test/burn/TestData/Manual/PackagePerUser/PackagePerUser.wixproj new
+9
@@ -0,0 +1,9 @@
1 +<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
2 +<Project Sdk="WixToolset.Sdk">
3 + <PropertyGroup>
4 + <UpgradeCode>{F7FBAC90-07A6-4DEB-A9C5-E267EDDA28EF}</UpgradeCode>
5 + </PropertyGroup>
6 + <ItemGroup>
7 + <Compile Include="..\..\Templates\PackagePerUser.wxs" Link="PackagePerUser.wxs" />
8 + </ItemGroup>
9 +</Project>
\ No newline at end of file