aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2024-04-04 15:24:34 -0700
committerRob Mensching <rob@firegiant.com>2024-04-05 09:46:43 -0700
commit681cf4a9eb6be7e4092c6e5b690773fbd8469e63 (patch)
tree1ff50c7856c83bf930a0cbb4f52d5e1b5cbc016f
parent6ed045ee1fe69be037a999df2e57122a25f0dedf (diff)
downloadwix-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.vcxproj2
-rw-r--r--src/dtf/SfxCA/SfxUtil.cpp169
-rw-r--r--src/dtf/dtf.cmd23
-rw-r--r--src/test/msi/TestData/CustomActionTests/ManagedCustomActions/Package.wxs6
-rw-r--r--src/test/msi/TestData/CustomActionTests/TestCA/CustomAction.cs38
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
116bool DirectoryExists(const wchar_t* szDir) 129static 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
166static 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)
143bool ExtractToTempDirectory(__in MSIHANDLE hSession, __in HMODULE hModule, 200bool 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
292LExit:
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
11msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b 18msbuild -Restore SfxCA\sfxca_t.proj -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_sfxca.binlog || exit /b
12 19
13msbuild -Restore -t:Pack dtf.sln -p:Configuration=%_C% -tl -nologo -m -warnaserror -bl:..\..\build\logs\dtf_build.binlog || exit /b 20msbuild -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}