diff options
author | Rob Mensching <rob@firegiant.com> | 2024-04-04 15:24:34 -0700 |
---|---|---|
committer | Rob Mensching <rob@firegiant.com> | 2024-04-05 09:46:43 -0700 |
commit | 681cf4a9eb6be7e4092c6e5b690773fbd8469e63 (patch) | |
tree | 1ff50c7856c83bf930a0cbb4f52d5e1b5cbc016f | |
parent | 6ed045ee1fe69be037a999df2e57122a25f0dedf (diff) | |
download | wix-681cf4a9eb6be7e4092c6e5b690773fbd8469e63.tar.gz wix-681cf4a9eb6be7e4092c6e5b690773fbd8469e63.tar.bz2 wix-681cf4a9eb6be7e4092c6e5b690773fbd8469e63.zip |
Ensure elevated SFXCA uses Windows Installer cache and unelevated uses Temp folder
Fixes 8078
-rw-r--r-- | src/dtf/SfxCA/SfxCA.vcxproj | 2 | ||||
-rw-r--r-- | src/dtf/SfxCA/SfxUtil.cpp | 169 | ||||
-rw-r--r-- | src/dtf/dtf.cmd | 23 | ||||
-rw-r--r-- | src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs | 6 | ||||
-rw-r--r-- | src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs | 38 |
5 files changed, 203 insertions, 35 deletions
diff --git a/src/dtf/SfxCA/SfxCA.vcxproj b/src/dtf/SfxCA/SfxCA.vcxproj index 5c4f674f..ee591619 100644 --- a/src/dtf/SfxCA/SfxCA.vcxproj +++ b/src/dtf/SfxCA/SfxCA.vcxproj | |||
@@ -41,7 +41,7 @@ | |||
41 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | 41 | <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> |
42 | 42 | ||
43 | <PropertyGroup> | 43 | <PropertyGroup> |
44 | <ProjectAdditionalLinkLibraries>msi.lib;cabinet.lib;shlwapi.lib</ProjectAdditionalLinkLibraries> | 44 | <ProjectAdditionalLinkLibraries>msi.lib;cabinet.lib;rpcrt4.lib;shlwapi.lib</ProjectAdditionalLinkLibraries> |
45 | </PropertyGroup> | 45 | </PropertyGroup> |
46 | 46 | ||
47 | <ItemGroup> | 47 | <ItemGroup> |
diff --git a/src/dtf/SfxCA/SfxUtil.cpp b/src/dtf/SfxCA/SfxUtil.cpp index 32dc6e04..079f1617 100644 --- a/src/dtf/SfxCA/SfxUtil.cpp +++ b/src/dtf/SfxCA/SfxUtil.cpp | |||
@@ -3,6 +3,8 @@ | |||
3 | #include "precomp.h" | 3 | #include "precomp.h" |
4 | #include "SfxUtil.h" | 4 | #include "SfxUtil.h" |
5 | 5 | ||
6 | #define GUID_STRING_LENGTH 39 | ||
7 | |||
6 | /// <summary> | 8 | /// <summary> |
7 | /// Writes a formatted message to the MSI log. | 9 | /// Writes a formatted message to the MSI log. |
8 | /// Does out-of-proc MSI calls if necessary. | 10 | /// Does out-of-proc MSI calls if necessary. |
@@ -110,20 +112,75 @@ bool DeleteDirectory(const wchar_t* szDir) | |||
110 | hSearch = INVALID_HANDLE_VALUE; | 112 | hSearch = INVALID_HANDLE_VALUE; |
111 | } | 113 | } |
112 | } | 114 | } |
113 | return RemoveDirectory(szDir) != 0; | 115 | |
116 | for (int i = 0; i < 3; i++) | ||
117 | { | ||
118 | if (::RemoveDirectory(szDir)) | ||
119 | { | ||
120 | return true; | ||
121 | } | ||
122 | |||
123 | ::Sleep(100); | ||
124 | } | ||
125 | |||
126 | return false; | ||
114 | } | 127 | } |
115 | 128 | ||
116 | bool DirectoryExists(const wchar_t* szDir) | 129 | static HRESULT CreateGuid( |
130 | _Out_z_cap_c_(GUID_STRING_LENGTH) wchar_t* wzGuid) | ||
117 | { | 131 | { |
118 | if (szDir != NULL) | 132 | HRESULT hr = S_OK; |
133 | RPC_STATUS rs = RPC_S_OK; | ||
134 | UUID guid = {}; | ||
135 | |||
136 | rs = ::UuidCreate(&guid); | ||
137 | if (rs != RPC_S_OK) | ||
119 | { | 138 | { |
120 | DWORD dwAttrs = GetFileAttributes(szDir); | 139 | hr = (HRESULT)(rs | FACILITY_RPC); |
121 | if (dwAttrs != -1 && (dwAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0) | ||
122 | { | ||
123 | return true; | ||
124 | } | ||
125 | } | 140 | } |
126 | return false; | 141 | else if (!::StringFromGUID2(guid, wzGuid, GUID_STRING_LENGTH)) |
142 | { | ||
143 | hr = E_OUTOFMEMORY; | ||
144 | } | ||
145 | else // make the temp directory more recognizable for easy deletion. | ||
146 | { | ||
147 | // Copy the first four hex chars of the GUID over the dashes in the GUID and trim the, so | ||
148 | // '{1234ABCD-ABCD-ABCD-ABCD-ABCDABCDABCD}' turns into '{1234ABCD1ABCD2ABCD3ABCD4ABCDABCDABCD}' | ||
149 | wzGuid[9] = wzGuid[1]; | ||
150 | wzGuid[14] = wzGuid[2]; | ||
151 | wzGuid[19] = wzGuid[3]; | ||
152 | wzGuid[24] = wzGuid[4]; | ||
153 | |||
154 | // Now '{1234ABCD1ABCD2ABCD3ABCD4ABCDABCDABCD}' turns into 'SFXCAABCD1ABCD2ABCD3ABCD4ABCDABCDABCD' | ||
155 | wzGuid[0] = L'S'; | ||
156 | wzGuid[1] = L'F'; | ||
157 | wzGuid[2] = L'X'; | ||
158 | wzGuid[3] = L'C'; | ||
159 | wzGuid[4] = L'A'; | ||
160 | wzGuid[GUID_STRING_LENGTH - 2] = L'\0'; | ||
161 | } | ||
162 | |||
163 | return hr; | ||
164 | } | ||
165 | |||
166 | static HRESULT ProcessElevated() | ||
167 | { | ||
168 | HRESULT hr = S_OK; | ||
169 | HANDLE hToken = NULL; | ||
170 | TOKEN_ELEVATION tokenElevated = {}; | ||
171 | DWORD cbToken = 0; | ||
172 | |||
173 | if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &hToken) && | ||
174 | ::GetTokenInformation(hToken, TokenElevation, &tokenElevated, sizeof(TOKEN_ELEVATION), &cbToken)) | ||
175 | { | ||
176 | hr = (0 != tokenElevated.TokenIsElevated) ? S_OK : S_FALSE; | ||
177 | } | ||
178 | else | ||
179 | { | ||
180 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
181 | } | ||
182 | |||
183 | return hr; | ||
127 | } | 184 | } |
128 | 185 | ||
129 | /// <summary> | 186 | /// <summary> |
@@ -143,49 +200,95 @@ __success(return != false) | |||
143 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, | 200 | bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, |
144 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf) | 201 | __out_ecount_z(cchTempDirBuf) wchar_t* szTempDir, DWORD cchTempDirBuf) |
145 | { | 202 | { |
146 | wchar_t szModule[MAX_PATH]; | 203 | HRESULT hr = S_OK; |
147 | DWORD cchCopied = GetModuleFileName(hModule, szModule, MAX_PATH - 1); | 204 | wchar_t szModule[MAX_PATH] = {}; |
148 | if (cchCopied == 0) | 205 | wchar_t szGuid[GUID_STRING_LENGTH] = {}; |
206 | |||
207 | DWORD cchCopied = ::GetModuleFileName(hModule, szModule, MAX_PATH - 1); | ||
208 | if (cchCopied == 0 || cchCopied == MAX_PATH - 1) | ||
149 | { | 209 | { |
150 | Log(hSession, L"Failed to get module path. Error code %d.", GetLastError()); | 210 | hr = HRESULT_FROM_WIN32(::GetLastError()); |
151 | return false; | 211 | if (SUCCEEDED(hr)) |
212 | { | ||
213 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
214 | } | ||
215 | |||
216 | Log(hSession, L"Failed to get module path. Error code 0x%x.", hr); | ||
217 | goto LExit; | ||
152 | } | 218 | } |
153 | else if (cchCopied == MAX_PATH - 1) | 219 | |
220 | hr = CreateGuid(szGuid); | ||
221 | if (FAILED(hr)) | ||
154 | { | 222 | { |
155 | Log(hSession, L"Failed to get module path -- path is too long."); | 223 | Log(hSession, L"Failed to create a GUID. Error code 0x%x", hr); |
156 | return false; | 224 | goto LExit; |
157 | } | 225 | } |
158 | 226 | ||
159 | if (szTempDir == NULL || cchTempDirBuf < wcslen(szModule) + 1) | 227 | // Unelevated we use the user's temp directory. |
228 | hr = ProcessElevated(); | ||
229 | if (S_FALSE == hr) | ||
230 | { | ||
231 | // Temp path is documented to be returned with a trailing backslash. | ||
232 | cchCopied = ::GetTempPath(cchTempDirBuf, szTempDir); | ||
233 | if (cchCopied == 0 || cchCopied >= cchTempDirBuf) | ||
234 | { | ||
235 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
236 | if (SUCCEEDED(hr)) | ||
237 | { | ||
238 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
239 | } | ||
240 | |||
241 | Log(hSession, L"Failed to get user temp directory. Error code 0x%x", hr); | ||
242 | goto LExit; | ||
243 | } | ||
244 | } | ||
245 | else // elevated or we couldn't check (in the latter case, assume we're elevated since it's safer to use) | ||
160 | { | 246 | { |
161 | Log(hSession, L"Temp directory buffer is NULL or too small."); | 247 | // Windows directory will not contain a trailing backslash, so we add it next. |
162 | return false; | 248 | cchCopied = ::GetWindowsDirectoryW(szTempDir, cchTempDirBuf); |
249 | if (cchCopied == 0 || cchCopied >= cchTempDirBuf) | ||
250 | { | ||
251 | hr = HRESULT_FROM_WIN32(::GetLastError()); | ||
252 | if (SUCCEEDED(hr)) | ||
253 | { | ||
254 | hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); | ||
255 | } | ||
256 | |||
257 | Log(hSession, L"Failed to get Windows directory. Error code 0x%x", hr); | ||
258 | goto LExit; | ||
259 | } | ||
260 | |||
261 | hr = ::StringCchCat(szTempDir, cchTempDirBuf, L"\\Installer\\"); | ||
262 | if (FAILED(hr)) | ||
263 | { | ||
264 | Log(hSession, L"Failed append 'Installer' to Windows directory. Error code 0x%x", hr); | ||
265 | goto LExit; | ||
266 | } | ||
163 | } | 267 | } |
164 | StringCchCopy(szTempDir, cchTempDirBuf, szModule); | ||
165 | StringCchCat(szTempDir, cchTempDirBuf, L"-"); | ||
166 | 268 | ||
167 | BOOL fCreatedDirectory = FALSE; | 269 | hr = ::StringCchCat(szTempDir, cchTempDirBuf, szGuid); |
168 | DWORD cchTempDir = (DWORD) wcslen(szTempDir); | 270 | if (FAILED(hr)) |
169 | for (int i = 0; i < 10000 && !fCreatedDirectory; i++) | ||
170 | { | 271 | { |
171 | swprintf_s(szTempDir + cchTempDir, cchTempDirBuf - cchTempDir, L"%d", i); | 272 | Log(hSession, L"Failed append GUID to temp path. Error code 0x%x", hr); |
172 | fCreatedDirectory = ::CreateDirectory(szTempDir, NULL); | 273 | goto LExit; |
173 | } | 274 | } |
174 | 275 | ||
175 | if (!fCreatedDirectory) | 276 | if (!::CreateDirectory(szTempDir, NULL)) |
176 | { | 277 | { |
177 | Log(hSession, L"Failed to create temp directory. Error code %d", ::GetLastError()); | 278 | hr = HRESULT_FROM_WIN32(::GetLastError()); |
178 | return false; | 279 | Log(hSession, L"Failed to create temp directory. Error code 0x%x", hr); |
280 | goto LExit; | ||
179 | } | 281 | } |
180 | 282 | ||
181 | Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir); | 283 | Log(hSession, L"Extracting custom action to temporary directory: %s\\", szTempDir); |
182 | int err = ExtractCabinet(szModule, szTempDir); | 284 | int err = ExtractCabinet(szModule, szTempDir); |
183 | if (err != 0) | 285 | if (err != 0) |
184 | { | 286 | { |
287 | hr = E_FAIL; | ||
185 | Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); | 288 | Log(hSession, L"Failed to extract to temporary directory. Cabinet error code %d.", err); |
186 | DeleteDirectory(szTempDir); | 289 | DeleteDirectory(szTempDir); |
187 | return false; | ||
188 | } | 290 | } |
189 | return true; | ||
190 | } | ||
191 | 291 | ||
292 | LExit: | ||
293 | return SUCCEEDED(hr); | ||
294 | } | ||
diff --git a/src/dtf/dtf.cmd b/src/dtf/dtf.cmd index d72c803b..530ee432 100644 --- a/src/dtf/dtf.cmd +++ b/src/dtf/dtf.cmd | |||
@@ -4,13 +4,36 @@ | |||
4 | @set _C=Debug | 4 | @set _C=Debug |
5 | :parse_args | 5 | :parse_args |
6 | @if /i "%1"=="release" set _C=Release | 6 | @if /i "%1"=="release" set _C=Release |
7 | @if /i "%1"=="inc" set _INC=1 | ||
8 | @if /i "%1"=="clean" set _CLEAN=1 | ||
7 | @if not "%1"=="" shift & goto parse_args | 9 | @if not "%1"=="" shift & goto parse_args |
8 | 10 | ||
11 | :: Clean | ||
12 | |||
13 | @if "%_INC%"=="" call :clean | ||
14 | @if NOT "%_CLEAN%"=="" goto :end | ||
15 | |||
9 | @echo Building dtf %_C% | 16 | @echo Building dtf %_C% |
10 | 17 | ||
11 | msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b | 18 | msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b |
12 | 19 | ||
13 | msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b | 20 | msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b |
14 | 21 | ||
22 | @goto :end | ||
23 | |||
24 | :clean | ||
25 | @rd /s/q "..\..\build\dtf" 2> nul | ||
26 | @del "..\..\build\artifacts\WixToolset.Dtf.*.nupkg" 2> nul | ||
27 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.compression" 2> nul | ||
28 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.compression.cab" 2> nul | ||
29 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.compression.zip" 2> nul | ||
30 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.customaction" 2> nul | ||
31 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.resources" 2> nul | ||
32 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.windowsinstaller" 2> nul | ||
33 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.windowsinstaller.linq" 2> nul | ||
34 | @rd /s/q "%USERPROFILE%\.nuget\packages\wixtoolset.dtf.windowsinstaller.package" 2> nul | ||
35 | @exit /b | ||
36 | |||
37 | :end | ||
15 | @popd | 38 | @popd |
16 | @endlocal | 39 | @endlocal |
diff --git a/src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs b/src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs index 9b1d224a..4159cd3e 100644 --- a/src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs +++ b/src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs | |||
@@ -13,13 +13,17 @@ | |||
13 | <Binary Id="ManagedCA" SourceFile="TestCA.CA.dll" /> | 13 | <Binary Id="ManagedCA" SourceFile="TestCA.CA.dll" /> |
14 | 14 | ||
15 | <CustomAction Id="ImmediateCA" BinaryRef="ManagedCA" DllEntry="ImmediateCA" Execute="immediate" Return="check" /> | 15 | <CustomAction Id="ImmediateCA" BinaryRef="ManagedCA" DllEntry="ImmediateCA" Execute="immediate" Return="check" /> |
16 | <CustomAction Id="DeferredCA" BinaryRef="ManagedCA" DllEntry="DeferredCA" Execute="deferred" Return="check" /> | 16 | <CustomAction Id="ExecuteImmediateCA" BinaryRef="ManagedCA" DllEntry="ExecuteImmediateCA" Execute="immediate" Return="check" /> |
17 | <CustomAction Id="ImpersonatedDeferredCA" BinaryRef="ManagedCA" DllEntry="ImpersonatedDeferredCA" Execute="deferred" Impersonate="yes" Return="check" /> | ||
18 | <CustomAction Id="DeferredCA" BinaryRef="ManagedCA" DllEntry="DeferredCA" Execute="deferred" Impersonate="no" Return="check" /> | ||
17 | 19 | ||
18 | <InstallUISequence> | 20 | <InstallUISequence> |
19 | <Custom Action="ImmediateCA" Before="CostFinalize" /> | 21 | <Custom Action="ImmediateCA" Before="CostFinalize" /> |
20 | </InstallUISequence> | 22 | </InstallUISequence> |
21 | 23 | ||
22 | <InstallExecuteSequence> | 24 | <InstallExecuteSequence> |
25 | <Custom Action="ExecuteImmediateCA" Before="CostFinalize" /> | ||
26 | <Custom Action="ImpersonatedDeferredCA" Before="InstallFiles" /> | ||
23 | <Custom Action="DeferredCA" After="InstallFiles" /> | 27 | <Custom Action="DeferredCA" After="InstallFiles" /> |
24 | </InstallExecuteSequence> | 28 | </InstallExecuteSequence> |
25 | </Package> | 29 | </Package> |
diff --git a/src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs b/src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs index 6891f8da..54afc2cf 100644 --- a/src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs +++ b/src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs | |||
@@ -4,7 +4,9 @@ namespace WixToolsetTest.TestCA | |||
4 | { | 4 | { |
5 | using System; | 5 | using System; |
6 | using System.Collections.Generic; | 6 | using System.Collections.Generic; |
7 | using System.IO; | ||
7 | using System.Linq; | 8 | using System.Linq; |
9 | using System.Text; | ||
8 | using WixToolset.Dtf.WindowsInstaller; | 10 | using WixToolset.Dtf.WindowsInstaller; |
9 | 11 | ||
10 | public class CustomActions | 12 | public class CustomActions |
@@ -14,6 +16,31 @@ namespace WixToolsetTest.TestCA | |||
14 | { | 16 | { |
15 | session.Log("Begin ImmediateCA"); | 17 | session.Log("Begin ImmediateCA"); |
16 | 18 | ||
19 | var path = Path.Combine(Path.GetTempPath(), "ImmediateCA.txt"); | ||
20 | WriteNow(path); | ||
21 | |||
22 | return ActionResult.Success; | ||
23 | } | ||
24 | |||
25 | [CustomAction] | ||
26 | public static ActionResult ExecuteImmediateCA(Session session) | ||
27 | { | ||
28 | session.Log("Begin ExecuteImmediateCA"); | ||
29 | |||
30 | var path = Path.Combine(Path.GetTempPath(), "ExecuteImmediateCA.txt"); | ||
31 | WriteNow(path); | ||
32 | |||
33 | return ActionResult.Success; | ||
34 | } | ||
35 | |||
36 | [CustomAction] | ||
37 | public static ActionResult ImpersonatedDeferredCA(Session session) | ||
38 | { | ||
39 | session.Log("Begin ImpersonatedDeferredCA"); | ||
40 | |||
41 | var path = Path.Combine(Path.GetTempPath(), "ImpersonatedDeferredCA.txt"); | ||
42 | WriteNow(path); | ||
43 | |||
17 | return ActionResult.Success; | 44 | return ActionResult.Success; |
18 | } | 45 | } |
19 | 46 | ||
@@ -22,7 +49,18 @@ namespace WixToolsetTest.TestCA | |||
22 | { | 49 | { |
23 | session.Log("Begin DeferredCA"); | 50 | session.Log("Begin DeferredCA"); |
24 | 51 | ||
52 | WriteNow(@"C:\Windows\Installer\DeferredCA.txt"); | ||
53 | |||
25 | return ActionResult.Success; | 54 | return ActionResult.Success; |
26 | } | 55 | } |
56 | |||
57 | private static void WriteNow(string path) | ||
58 | { | ||
59 | using (var file = File.Create(path)) | ||
60 | { | ||
61 | var now = Encoding.UTF8.GetBytes(DateTime.Now.ToString("o")); | ||
62 | file.Write(now, 0, now.Length); | ||
63 | } | ||
64 | } | ||
27 | } | 65 | } |
28 | } | 66 | } |