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