aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-12 07:46:35 -0700
committerRob Mensching <rob@firegiant.com>2021-04-12 15:34:06 -0700
commitfc542c0974840882e5bbba0046c530e2ede34170 (patch)
tree4794ea6c80550b7e7f5057c248e784df92b83a6b /src
parenta20bee47c43861dd9f38adb88e74a6417292732b (diff)
downloadwix-fc542c0974840882e5bbba0046c530e2ede34170.tar.gz
wix-fc542c0974840882e5bbba0046c530e2ede34170.tar.bz2
wix-fc542c0974840882e5bbba0046c530e2ede34170.zip
Add support for configuring SNI SSL certificates
Diffstat (limited to 'src')
-rw-r--r--src/ca/cost.h1
-rw-r--r--src/ca/httpca.vcxproj21
-rw-r--r--src/ca/httpca.vcxproj.filters3
-rw-r--r--src/ca/precomp.h7
-rw-r--r--src/ca/snisslcert.cpp704
-rw-r--r--src/ca/wixhttpca.cpp12
-rw-r--r--src/ca/wixhttpca.def3
-rw-r--r--src/test/WixToolsetTest.Http/HttpExtensionFixture.cs19
-rw-r--r--src/test/WixToolsetTest.Http/TestData/SniSsl/Package.en-us.wxl11
-rw-r--r--src/test/WixToolsetTest.Http/TestData/SniSsl/Package.wxs15
-rw-r--r--src/test/WixToolsetTest.Http/TestData/SniSsl/PackageComponents.wxs12
-rw-r--r--src/test/WixToolsetTest.Http/TestData/SniSsl/example.txt1
-rw-r--r--src/wixext/HttpCompiler.cs117
-rw-r--r--src/wixext/HttpTableDefinitions.cs18
-rw-r--r--src/wixext/Symbols/HttpSymbolDefinitions.cs4
-rw-r--r--src/wixext/Symbols/WixHttpSniSslCertSymbol.cs95
-rw-r--r--src/wixlib/HttpExtension_Platform.wxi27
-rw-r--r--src/wixlib/en-us.wxl8
18 files changed, 1063 insertions, 15 deletions
diff --git a/src/ca/cost.h b/src/ca/cost.h
index 283758b0..9677e7e8 100644
--- a/src/ca/cost.h
+++ b/src/ca/cost.h
@@ -3,3 +3,4 @@
3 3
4 4
5const UINT COST_HTTP_URL_ACL = 2000; 5const UINT COST_HTTP_URL_ACL = 2000;
6const UINT COST_HTTP_SNI_SSL = 2000;
diff --git a/src/ca/httpca.vcxproj b/src/ca/httpca.vcxproj
index 9abe7598..fde00ff4 100644
--- a/src/ca/httpca.vcxproj
+++ b/src/ca/httpca.vcxproj
@@ -3,7 +3,7 @@
3<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 3<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
4 <Import Project="..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props')" /> 4 <Import Project="..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props" Condition="Exists('..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props')" />
5 <Import Project="..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props" Condition="Exists('..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props')" /> 5 <Import Project="..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props" Condition="Exists('..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props')" />
6 6
7 <ItemGroup Label="ProjectConfigurations"> 7 <ItemGroup Label="ProjectConfigurations">
8 <ProjectConfiguration Include="Debug|Win32"> 8 <ProjectConfiguration Include="Debug|Win32">
9 <Configuration>Debug</Configuration> 9 <Configuration>Debug</Configuration>
@@ -13,7 +13,7 @@
13 <Configuration>Release</Configuration> 13 <Configuration>Release</Configuration>
14 <Platform>Win32</Platform> 14 <Platform>Win32</Platform>
15 </ProjectConfiguration> 15 </ProjectConfiguration>
16 <ProjectConfiguration Include="Debug|x64"> 16 <ProjectConfiguration Include="Debug|x64">
17 <Configuration>Debug</Configuration> 17 <Configuration>Debug</Configuration>
18 <Platform>x64</Platform> 18 <Platform>x64</Platform>
19 </ProjectConfiguration> 19 </ProjectConfiguration>
@@ -30,7 +30,7 @@
30 <Platform>ARM64</Platform> 30 <Platform>ARM64</Platform>
31 </ProjectConfiguration> 31 </ProjectConfiguration>
32 </ItemGroup> 32 </ItemGroup>
33 33
34 <PropertyGroup Label="Globals"> 34 <PropertyGroup Label="Globals">
35 <ProjectGuid>{90743805-C043-47C7-B5FF-8F5EE5C8A2DE}</ProjectGuid> 35 <ProjectGuid>{90743805-C043-47C7-B5FF-8F5EE5C8A2DE}</ProjectGuid>
36 <ConfigurationType>DynamicLibrary</ConfigurationType> 36 <ConfigurationType>DynamicLibrary</ConfigurationType>
@@ -40,27 +40,28 @@
40 <ProjectModuleDefinitionFile>wixhttpca.def</ProjectModuleDefinitionFile> 40 <ProjectModuleDefinitionFile>wixhttpca.def</ProjectModuleDefinitionFile>
41 <Description>WiX Toolset Http CustomAction</Description> 41 <Description>WiX Toolset Http CustomAction</Description>
42 </PropertyGroup> 42 </PropertyGroup>
43 43
44 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> 44 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
45 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> 45 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
46 46
47 <PropertyGroup> 47 <PropertyGroup>
48 <ProjectAdditionalLinkLibraries>crypt32.lib;httpapi.lib;msi.lib</ProjectAdditionalLinkLibraries> 48 <ProjectAdditionalLinkLibraries>crypt32.lib;httpapi.lib;msi.lib;rpcrt4.lib;ws2_32.lib</ProjectAdditionalLinkLibraries>
49 </PropertyGroup> 49 </PropertyGroup>
50 50
51 <ItemGroup> 51 <ItemGroup>
52 <ClCompile Include="dllmain.cpp"> 52 <ClCompile Include="dllmain.cpp">
53 <PrecompiledHeader>Create</PrecompiledHeader> 53 <PrecompiledHeader>Create</PrecompiledHeader>
54 </ClCompile> 54 </ClCompile>
55 <ClCompile Include="snisslcert.cpp" />
55 <ClCompile Include="wixhttpca.cpp" /> 56 <ClCompile Include="wixhttpca.cpp" />
56 <ClInclude Include="cost.h" /> 57 <ClInclude Include="cost.h" />
57 <ClInclude Include="precomp.h" /> 58 <ClInclude Include="precomp.h" />
58 <None Include="packages.config" /> 59 <None Include="packages.config" />
59 <None Include="wixhttpca.def" /> 60 <None Include="wixhttpca.def" />
60 </ItemGroup> 61 </ItemGroup>
61 62
62 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> 63 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
63 64
64 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> 65 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
65 <PropertyGroup> 66 <PropertyGroup>
66 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> 67 <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
@@ -68,4 +69,4 @@
68 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props'))" /> 69 <Error Condition="!Exists('..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.DUtil.4.0.56\build\WixToolset.DUtil.props'))" />
69 <Error Condition="!Exists('..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props'))" /> 70 <Error Condition="!Exists('..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\WixToolset.WcaUtil.4.0.17\build\WixToolset.WcaUtil.props'))" />
70 </Target> 71 </Target>
71</Project> \ No newline at end of file 72</Project>
diff --git a/src/ca/httpca.vcxproj.filters b/src/ca/httpca.vcxproj.filters
index 979b9ef3..2ccd604d 100644
--- a/src/ca/httpca.vcxproj.filters
+++ b/src/ca/httpca.vcxproj.filters
@@ -21,6 +21,9 @@
21 <ClCompile Include="dllmain.cpp"> 21 <ClCompile Include="dllmain.cpp">
22 <Filter>Source Files</Filter> 22 <Filter>Source Files</Filter>
23 </ClCompile> 23 </ClCompile>
24 <ClCompile Include="snisslcert.cpp">
25 <Filter>Source Files</Filter>
26 </ClCompile>
24 </ItemGroup> 27 </ItemGroup>
25 <ItemGroup> 28 <ItemGroup>
26 <ClInclude Include="precomp.h"> 29 <ClInclude Include="precomp.h">
diff --git a/src/ca/precomp.h b/src/ca/precomp.h
index d5143dac..c78d78c1 100644
--- a/src/ca/precomp.h
+++ b/src/ca/precomp.h
@@ -16,3 +16,10 @@
16#include "cost.h" 16#include "cost.h"
17 17
18#include "caDecor.h" 18#include "caDecor.h"
19
20enum eHandleExisting
21{
22 heReplace = 0,
23 heIgnore = 1,
24 heFail = 2
25};
diff --git a/src/ca/snisslcert.cpp b/src/ca/snisslcert.cpp
new file mode 100644
index 00000000..466ef0b9
--- /dev/null
+++ b/src/ca/snisslcert.cpp
@@ -0,0 +1,704 @@
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#include "precomp.h"
4
5#if _WIN32_WINNT < 0x0602
6
7typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_KEY
8{
9 SOCKADDR_STORAGE IpPort;
10 PWSTR Host;
11} HTTP_SERVICE_CONFIG_SSL_SNI_KEY, * PHTTP_SERVICE_CONFIG_SSL_SNI_KEY;
12
13typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_SET
14{
15 HTTP_SERVICE_CONFIG_SSL_SNI_KEY KeyDesc;
16 HTTP_SERVICE_CONFIG_SSL_PARAM ParamDesc;
17} HTTP_SERVICE_CONFIG_SSL_SNI_SET, * PHTTP_SERVICE_CONFIG_SSL_SNI_SET;
18
19typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_QUERY
20{
21 HTTP_SERVICE_CONFIG_QUERY_TYPE QueryDesc;
22 HTTP_SERVICE_CONFIG_SSL_SNI_KEY KeyDesc;
23 DWORD dwToken;
24} HTTP_SERVICE_CONFIG_SSL_SNI_QUERY, * PHTTP_SERVICE_CONFIG_SSL_SNI_QUERY;
25
26#define HttpServiceConfigSslSniCertInfo static_cast<HTTP_SERVICE_CONFIG_ID>(HttpServiceConfigCache + 1)
27
28#endif
29
30static UINT SchedHttpSniSslCerts(
31 __in WCA_TODO todoSched
32);
33static HRESULT WriteExistingSniSslCert(
34 __in WCA_TODO action,
35 __in_z LPCWSTR wzId,
36 __in_z LPCWSTR wzHost,
37 __in int iPort,
38 __in int iHandleExisting,
39 __in HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSniSslSet,
40 __inout_z LPWSTR* psczCustomActionData
41);
42static HRESULT WriteSniSslCert(
43 __in WCA_TODO action,
44 __in_z LPCWSTR wzId,
45 __in_z LPCWSTR wzHost,
46 __in int iPort,
47 __in int iHandleExisting,
48 __in_z LPCWSTR wzCertificateThumbprint,
49 __in_z LPCWSTR wzAppId,
50 __in_z_opt LPCWSTR wzCertificateStore,
51 __inout_z LPWSTR* psczCustomActionData
52);
53static HRESULT EnsureAppId(
54 __inout_z LPWSTR* psczAppId,
55 __in_opt HTTP_SERVICE_CONFIG_SSL_SNI_SET* pExistingSniSslSet
56);
57static HRESULT StringFromGuid(
58 __in REFGUID rguid,
59 __inout_z LPWSTR* psczGuid
60);
61static HRESULT AddSniSslCert(
62 __in_z LPCWSTR wzId,
63 __in_z LPWSTR wzHost,
64 __in int iPort,
65 __in BYTE rgbCertificateThumbprint[],
66 __in DWORD cbCertificateThumbprint,
67 __in GUID* pAppId,
68 __in_z LPWSTR wzSslCertStore
69);
70static HRESULT GetSniSslCert(
71 __in_z LPWSTR wzHost,
72 __in int nPort,
73 __out HTTP_SERVICE_CONFIG_SSL_SNI_SET** ppSet
74);
75static HRESULT RemoveSniSslCert(
76 __in_z LPCWSTR wzId,
77 __in_z LPWSTR wzHost,
78 __in int iPort
79);
80static void SetSniSslCertSetKey(
81 __in HTTP_SERVICE_CONFIG_SSL_SNI_KEY* pKey,
82 __in_z LPWSTR wzHost,
83 __in int iPort
84);
85
86
87LPCWSTR vcsWixHttpSniSslCertQuery =
88L"SELECT `WixHttpSniSslCert`.`WixHttpSniSslCert`, `WixHttpSniSslCert`.`Host`, `WixHttpSniSslCert`.`Port`, `WixHttpSniSslCert`.`Thumbprint`, `WixHttpSniSslCert`.`AppId`, `WixHttpSniSslCert`.`Store`, `WixHttpSniSslCert`.`HandleExisting`, `WixHttpSniSslCert`.`Component_` "
89L"FROM `WixHttpSniSslCert`";
90enum eWixHttpSniSslCertQuery { hurqId = 1, hurqHost, hurqPort, hurqCertificateThumbprint, hurqAppId, hurqCertificateStore, hurqHandleExisting, hurqComponent };
91
92/******************************************************************
93 SchedWixHttpSniSslCertsInstall - immediate custom action entry
94 point to prepare adding URL reservations.
95
96********************************************************************/
97extern "C" UINT __stdcall SchedHttpSniSslCertsInstall(
98 __in MSIHANDLE hInstall
99)
100{
101 HRESULT hr = S_OK;
102
103 hr = WcaInitialize(hInstall, "SchedHttpSniSslCertsInstall");
104 ExitOnFailure(hr, "Failed to initialize");
105
106 hr = SchedHttpSniSslCerts(WCA_TODO_INSTALL);
107
108LExit:
109 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS);
110}
111
112/******************************************************************
113 SchedWixHttpSniSslCertsUninstall - immediate custom action entry
114 point to prepare removing URL reservations.
115
116********************************************************************/
117extern "C" UINT __stdcall SchedHttpSniSslCertsUninstall(
118 __in MSIHANDLE hInstall
119)
120{
121 HRESULT hr = S_OK;
122
123 hr = WcaInitialize(hInstall, "SchedHttpSniSslCertsUninstall");
124 ExitOnFailure(hr, "Failed to initialize");
125
126 hr = SchedHttpSniSslCerts(WCA_TODO_UNINSTALL);
127
128LExit:
129 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS);
130}
131
132/******************************************************************
133 ExecHttpSniSslCerts - deferred custom action entry point to
134 register and remove URL reservations.
135
136********************************************************************/
137extern "C" UINT __stdcall ExecHttpSniSslCerts(
138 __in MSIHANDLE hInstall
139)
140{
141 HRESULT hr = S_OK;
142 BOOL fHttpInitialized = FALSE;
143 LPWSTR sczCustomActionData = NULL;
144 LPWSTR wz = NULL;
145 int iTodo = WCA_TODO_UNKNOWN;
146 LPWSTR sczId = NULL;
147 LPWSTR sczHost = NULL;
148 int iPort = 0;
149 eHandleExisting handleExisting = heIgnore;
150 LPWSTR sczCertificateThumbprint = NULL;
151 LPWSTR sczAppId = NULL;
152 LPWSTR sczCertificateStore = NULL;
153
154 BOOL fRollback = ::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
155 BOOL fRemove = FALSE;
156 BOOL fAdd = FALSE;
157 BOOL fFailOnExisting = FALSE;
158
159 GUID guidAppId = { };
160 BYTE* pbCertificateThumbprint = NULL;
161 DWORD cbCertificateThumbprint = 0;
162
163 // Initialize.
164 hr = WcaInitialize(hInstall, "ExecHttpSniSslCerts");
165 ExitOnFailure(hr, "Failed to initialize");
166
167 hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL));
168 ExitOnFailure(hr, "Failed to initialize HTTP Server configuration");
169
170 fHttpInitialized = TRUE;
171
172 hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
173 ExitOnFailure(hr, "Failed to get CustomActionData");
174 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", sczCustomActionData);
175
176 wz = sczCustomActionData;
177 while (wz && *wz)
178 {
179 // Extract the custom action data and if rolling back, swap INSTALL and UNINSTALL.
180 hr = WcaReadIntegerFromCaData(&wz, &iTodo);
181 ExitOnFailure(hr, "Failed to read todo from custom action data");
182
183 hr = WcaReadStringFromCaData(&wz, &sczId);
184 ExitOnFailure(hr, "Failed to read Id from custom action data");
185
186 hr = WcaReadStringFromCaData(&wz, &sczHost);
187 ExitOnFailure(hr, "Failed to read Host from custom action data");
188
189 hr = WcaReadIntegerFromCaData(&wz, &iPort);
190 ExitOnFailure(hr, "Failed to read Port from custom action data");
191
192 hr = WcaReadIntegerFromCaData(&wz, reinterpret_cast<int*>(&handleExisting));
193 ExitOnFailure(hr, "Failed to read HandleExisting from custom action data");
194
195 hr = WcaReadStringFromCaData(&wz, &sczCertificateThumbprint);
196 ExitOnFailure(hr, "Failed to read CertificateThumbprint from custom action data");
197
198 hr = WcaReadStringFromCaData(&wz, &sczAppId);
199 ExitOnFailure(hr, "Failed to read AppId from custom action data");
200
201 hr = WcaReadStringFromCaData(&wz, &sczCertificateStore);
202 ExitOnFailure(hr, "Failed to read CertificateStore from custom action data");
203
204 switch (iTodo)
205 {
206 case WCA_TODO_INSTALL:
207 case WCA_TODO_REINSTALL:
208 fRemove = heReplace == handleExisting || fRollback;
209 fAdd = !fRollback || *sczCertificateThumbprint;
210 fFailOnExisting = heFail == handleExisting && !fRollback;
211 break;
212
213 case WCA_TODO_UNINSTALL:
214 fRemove = !fRollback;
215 fAdd = fRollback && *sczCertificateThumbprint;
216 fFailOnExisting = FALSE;
217 break;
218 }
219
220 if (fRemove)
221 {
222 hr = RemoveSniSslCert(sczId, sczHost, iPort);
223 if (S_OK == hr)
224 {
225 WcaLog(LOGMSG_STANDARD, "Removed SNI SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
226 }
227 else if (FAILED(hr))
228 {
229 if (fRollback)
230 {
231 WcaLogError(hr, "Failed to remove SNI SSL certificate to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
232 }
233 else
234 {
235 ExitOnFailure(hr, "Failed to remove SNI SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
236 }
237 }
238 }
239
240 if (fAdd)
241 {
242 WcaLog(LOGMSG_STANDARD, "Adding SNI SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
243
244 hr = StrAllocHexDecode(sczCertificateThumbprint, &pbCertificateThumbprint, &cbCertificateThumbprint);
245 ExitOnFailure(hr, "Failed to convert thumbprint to bytes for SNI SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
246
247 hr = ::IIDFromString(sczAppId, &guidAppId);
248 ExitOnFailure(hr, "Failed to convert AppId '%ls' back to GUID for SNI SSL certificate '%ls' for hostname: %ls:%d", sczAppId, sczId, sczHost, iPort);
249
250 hr = AddSniSslCert(sczId, sczHost, iPort, pbCertificateThumbprint, cbCertificateThumbprint, &guidAppId, sczCertificateStore && *sczCertificateStore ? sczCertificateStore : L"MY");
251 if (S_FALSE == hr && fFailOnExisting)
252 {
253 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
254 }
255
256 if (S_OK == hr)
257 {
258 WcaLog(LOGMSG_STANDARD, "Added SNI SSL certificate '%ls' for hostname: %ls:%d with thumbprint: %ls", sczId, sczHost, iPort, sczCertificateThumbprint);
259 }
260 else if (FAILED(hr))
261 {
262 if (fRollback)
263 {
264 WcaLogError(hr, "Failed to add SNI SSL certificate to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
265 }
266 else
267 {
268 ExitOnFailure(hr, "Failed to add SNI SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort);
269 }
270 }
271
272 ReleaseNullMem(pbCertificateThumbprint);
273 }
274 }
275
276LExit:
277 ReleaseMem(pbCertificateThumbprint);
278 ReleaseStr(sczCertificateStore);
279 ReleaseStr(sczAppId);
280 ReleaseStr(sczCertificateThumbprint);
281 ReleaseStr(sczHost);
282 ReleaseStr(sczId);
283 ReleaseStr(sczCustomActionData);
284
285 if (fHttpInitialized)
286 {
287 ::HttpTerminate(HTTP_INITIALIZE_CONFIG, NULL);
288 }
289
290 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS);
291}
292
293static UINT SchedHttpSniSslCerts(
294 __in WCA_TODO todoSched
295)
296{
297 HRESULT hr = S_OK;
298 UINT er = ERROR_SUCCESS;
299 BOOL fHttpInitialized = FALSE;
300 DWORD cCertificates = 0;
301
302 PMSIHANDLE hView = NULL;
303 PMSIHANDLE hRec = NULL;
304 PMSIHANDLE hQueryReq = NULL;
305 PMSIHANDLE hAceView = NULL;
306
307 LPWSTR sczCustomActionData = NULL;
308 LPWSTR sczRollbackCustomActionData = NULL;
309
310 LPWSTR sczId = NULL;
311 LPWSTR sczComponent = NULL;
312 WCA_TODO todoComponent = WCA_TODO_UNKNOWN;
313 LPWSTR sczHost = NULL;
314 int iPort = 0;
315 LPWSTR sczCertificateThumbprint = NULL;
316 LPWSTR sczAppId = NULL;
317 LPWSTR sczCertificateStore = NULL;
318 int iHandleExisting = 0;
319
320 HTTP_SERVICE_CONFIG_SSL_SNI_SET* pExistingSniSslSet = NULL;
321
322 // Anything to do?
323 hr = WcaTableExists(L"WixHttpSniSslCert");
324 ExitOnFailure(hr, "Failed to check if the WixHttpSniSslCert table exists");
325 if (S_FALSE == hr)
326 {
327 WcaLog(LOGMSG_STANDARD, "WixHttpSniSslCert table doesn't exist, so there are no URL reservations to configure");
328 ExitFunction();
329 }
330
331 // Query and loop through all the SNI SSL certificates.
332 hr = WcaOpenExecuteView(vcsWixHttpSniSslCertQuery, &hView);
333 ExitOnFailure(hr, "Failed to open view on the WixHttpSniSslCert table");
334
335 hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL));
336 ExitOnFailure(hr, "Failed to initialize HTTP Server configuration");
337
338 fHttpInitialized = TRUE;
339
340 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
341 {
342 hr = WcaGetRecordString(hRec, hurqId, &sczId);
343 ExitOnFailure(hr, "Failed to get WixHttpSniSslCert.WixHttpSniSslCert");
344
345 hr = WcaGetRecordString(hRec, hurqComponent, &sczComponent);
346 ExitOnFailure(hr, "Failed to get WixHttpSniSslCert.Component_");
347
348 // Figure out what we're doing for this reservation, treating reinstall the same as install.
349 todoComponent = WcaGetComponentToDo(sczComponent);
350 if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched)
351 {
352 WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d) for WixHttpSniSslCert '%ls'", sczComponent, todoComponent, todoSched, sczId);
353 continue;
354 }
355
356 hr = WcaGetRecordFormattedString(hRec, hurqHost, &sczHost);
357 ExitOnFailure(hr, "Failed to get WixHttpSniSslCert.Host");
358
359 hr = WcaGetRecordFormattedInteger(hRec, hurqPort, &iPort);
360 ExitOnFailure(hr, "Failed to get WixHttpSniSslCert.Port");
361
362 hr = WcaGetRecordFormattedString(hRec, hurqCertificateThumbprint, &sczCertificateThumbprint);
363 ExitOnFailure(hr, "Failed to get WixHttpSniSslCert.CertificateThumbprint");
364
365 if (!sczHost || !*sczHost)
366 {
367 hr = E_INVALIDARG;
368 ExitOnFailure(hr, "Require a Host value for WixHttpSniSslCert '%ls'", sczId);
369 }
370
371 if (!iPort)
372 {
373 hr = E_INVALIDARG;
374 ExitOnFailure(hr, "Require a Port value for WixHttpSniSslCert '%ls'", sczId);
375 }
376
377 if (!sczCertificateThumbprint || !*sczCertificateThumbprint)
378 {
379 hr = E_INVALIDARG;
380 ExitOnFailure(hr, "Require a CertificateThumbprint value for WixHttpSniSslCert '%ls'", sczId);
381 }
382
383 hr = WcaGetRecordFormattedString(hRec, hurqAppId, &sczAppId);
384 ExitOnFailure(hr, "Failed to get AppId for WixHttpSniSslCert '%ls'", sczId);
385
386 hr = WcaGetRecordFormattedString(hRec, hurqCertificateStore, &sczCertificateStore);
387 ExitOnFailure(hr, "Failed to get CertificateStore for WixHttpSniSslCert '%ls'", sczId);
388
389 hr = WcaGetRecordInteger(hRec, hurqHandleExisting, &iHandleExisting);
390 ExitOnFailure(hr, "Failed to get HandleExisting for WixHttpSniSslCert '%ls'", sczId);
391
392 hr = GetSniSslCert(sczHost, iPort, &pExistingSniSslSet);
393 ExitOnFailure(hr, "Failed to get the existing SNI SSL certificate for WixHttpSniSslCert '%ls'", sczId);
394
395 hr = EnsureAppId(&sczAppId, pExistingSniSslSet);
396 ExitOnFailure(hr, "Failed to ensure AppId for WixHttpSniSslCert '%ls'", sczId);
397
398 hr = WriteExistingSniSslCert(todoComponent, sczId, sczHost, iPort, iHandleExisting, pExistingSniSslSet, &sczRollbackCustomActionData);
399 ExitOnFailure(hr, "Failed to write rollback custom action data for WixHttpSniSslCert '%ls'", sczId);
400
401 hr = WriteSniSslCert(todoComponent, sczId, sczHost, iPort, iHandleExisting, sczCertificateThumbprint, sczAppId, sczCertificateStore, &sczCustomActionData);
402 ExitOnFailure(hr, "Failed to write custom action data for WixHttpSniSslCert '%ls'", sczId);
403 ++cCertificates;
404
405 ReleaseNullMem(pExistingSniSslSet);
406 }
407
408 // Reaching the end of the list is not an error.
409 if (E_NOMOREITEMS == hr)
410 {
411 hr = S_OK;
412 }
413 ExitOnFailure(hr, "Failure occurred while processing WixHttpSniSslCert table");
414
415 // Schedule ExecHttpSniSslCerts if there's anything to do.
416 if (cCertificates)
417 {
418 WcaLog(LOGMSG_STANDARD, "Scheduling SNI SSL certificate (%ls)", sczCustomActionData);
419 WcaLog(LOGMSG_STANDARD, "Scheduling rollback SNI SSL certificate (%ls)", sczRollbackCustomActionData);
420
421 if (WCA_TODO_INSTALL == todoSched)
422 {
423 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixRollbackHttpSniSslCertsInstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SNI_SSL);
424 ExitOnFailure(hr, "Failed to schedule install SNI SSL certificate rollback");
425 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixExecHttpSniSslCertsInstall"), sczCustomActionData, cCertificates * COST_HTTP_SNI_SSL);
426 ExitOnFailure(hr, "Failed to schedule install SNI SSL certificate execution");
427 }
428 else
429 {
430 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixRollbackHttpSniSslCertsUninstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SNI_SSL);
431 ExitOnFailure(hr, "Failed to schedule uninstall SNI SSL certificate rollback");
432 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixExecHttpSniSslCertsUninstall"), sczCustomActionData, cCertificates * COST_HTTP_SNI_SSL);
433 ExitOnFailure(hr, "Failed to schedule uninstall SNI SSL certificate execution");
434 }
435 }
436 else
437 {
438 WcaLog(LOGMSG_STANDARD, "No SNI SSL certificates scheduled");
439 }
440
441LExit:
442 ReleaseMem(pExistingSniSslSet);
443 ReleaseStr(sczCertificateStore);
444 ReleaseStr(sczAppId);
445 ReleaseStr(sczCertificateThumbprint);
446 ReleaseStr(sczHost);
447 ReleaseStr(sczComponent);
448 ReleaseStr(sczId);
449 ReleaseStr(sczRollbackCustomActionData);
450 ReleaseStr(sczCustomActionData);
451
452 if (fHttpInitialized)
453 {
454 ::HttpTerminate(HTTP_INITIALIZE_CONFIG, NULL);
455 }
456
457 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
458}
459
460static HRESULT WriteExistingSniSslCert(
461 __in WCA_TODO action,
462 __in_z LPCWSTR wzId,
463 __in_z LPCWSTR wzHost,
464 __in int iPort,
465 __in int iHandleExisting,
466 __in HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSniSslSet,
467 __inout_z LPWSTR* psczCustomActionData
468)
469{
470 HRESULT hr = S_OK;
471 LPWSTR sczCertificateThumbprint = NULL;
472 LPWSTR sczAppId = NULL;
473 LPCWSTR wzCertificateStore = NULL;
474
475 if (pSniSslSet)
476 {
477 hr = StrAllocHexEncode(reinterpret_cast<BYTE*>(pSniSslSet->ParamDesc.pSslHash), pSniSslSet->ParamDesc.SslHashLength, &sczCertificateThumbprint);
478 ExitOnFailure(hr, "Failed to convert existing certificate thumbprint to hex for WixHttpSniSslCert '%ls'", wzId);
479
480 hr = StringFromGuid(pSniSslSet->ParamDesc.AppId, &sczAppId);
481 ExitOnFailure(hr, "Failed to copy existing AppId for WixHttpSniSslCert '%ls'", wzId);
482
483 wzCertificateStore = pSniSslSet->ParamDesc.pSslCertStoreName;
484 }
485
486 hr = WriteSniSslCert(action, wzId, wzHost, iPort, iHandleExisting, sczCertificateThumbprint ? sczCertificateThumbprint : L"", sczAppId ? sczAppId : L"", wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData);
487 ExitOnFailure(hr, "Failed to write custom action data for WixHttpSniSslCert '%ls'", wzId);
488
489LExit:
490 ReleaseStr(sczAppId);
491 ReleaseStr(sczCertificateThumbprint);
492
493 return hr;
494}
495
496static HRESULT WriteSniSslCert(
497 __in WCA_TODO action,
498 __in_z LPCWSTR wzId,
499 __in_z LPCWSTR wzHost,
500 __in int iPort,
501 __in int iHandleExisting,
502 __in_z LPCWSTR wzCertificateThumbprint,
503 __in_z LPCWSTR wzAppId,
504 __in_z_opt LPCWSTR wzCertificateStore,
505 __inout_z LPWSTR* psczCustomActionData
506)
507{
508 HRESULT hr = S_OK;
509
510 hr = WcaWriteIntegerToCaData(action, psczCustomActionData);
511 ExitOnFailure(hr, "Failed to write action to custom action data");
512
513 hr = WcaWriteStringToCaData(wzId, psczCustomActionData);
514 ExitOnFailure(hr, "Failed to write id to custom action data");
515
516 hr = WcaWriteStringToCaData(wzHost, psczCustomActionData);
517 ExitOnFailure(hr, "Failed to write Host to custom action data");
518
519 hr = WcaWriteIntegerToCaData(iPort, psczCustomActionData);
520 ExitOnFailure(hr, "Failed to write Port to custom action data");
521
522 hr = WcaWriteIntegerToCaData(iHandleExisting, psczCustomActionData);
523 ExitOnFailure(hr, "Failed to write HandleExisting to custom action data");
524
525 hr = WcaWriteStringToCaData(wzCertificateThumbprint, psczCustomActionData);
526 ExitOnFailure(hr, "Failed to write CertificateThumbprint to custom action data");
527
528 hr = WcaWriteStringToCaData(wzAppId, psczCustomActionData);
529 ExitOnFailure(hr, "Failed to write AppId to custom action data");
530
531 hr = WcaWriteStringToCaData(wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData);
532 ExitOnFailure(hr, "Failed to write CertificateStore to custom action data");
533
534LExit:
535 return hr;
536}
537
538static HRESULT EnsureAppId(
539 __inout_z LPWSTR* psczAppId,
540 __in_opt HTTP_SERVICE_CONFIG_SSL_SNI_SET* pExistingSniSslSet
541)
542{
543 HRESULT hr = S_OK;
544 RPC_STATUS rs = RPC_S_OK;
545 GUID guid = { };
546
547 if (!psczAppId || !*psczAppId || !**psczAppId)
548 {
549 if (pExistingSniSslSet)
550 {
551 hr = StringFromGuid(pExistingSniSslSet->ParamDesc.AppId, psczAppId);
552 ExitOnFailure(hr, "Failed to ensure AppId guid");
553 }
554 else
555 {
556 rs = ::UuidCreate(&guid);
557 hr = HRESULT_FROM_RPC(rs);
558 ExitOnRootFailure(hr, "Failed to create guid for AppId");
559
560 hr = StringFromGuid(guid, psczAppId);
561 ExitOnFailure(hr, "Failed to ensure AppId guid");
562 }
563 }
564
565LExit:
566 return hr;
567}
568
569static HRESULT StringFromGuid(
570 __in REFGUID rguid,
571 __inout_z LPWSTR* psczGuid
572)
573{
574 HRESULT hr = S_OK;
575 WCHAR wzGuid[39];
576
577 if (!::StringFromGUID2(rguid, wzGuid, countof(wzGuid)))
578 {
579 hr = E_OUTOFMEMORY;
580 ExitOnRootFailure(hr, "Failed to convert guid into string");
581 }
582
583 hr = StrAllocString(psczGuid, wzGuid, 0);
584 ExitOnFailure(hr, "Failed to copy guid");
585
586LExit:
587 return hr;
588}
589
590static HRESULT AddSniSslCert(
591 __in_z LPCWSTR /*wzId*/,
592 __in_z LPWSTR wzHost,
593 __in int iPort,
594 __in BYTE rgbCertificateThumbprint[],
595 __in DWORD cbCertificateThumbprint,
596 __in GUID* pAppId,
597 __in_z LPWSTR wzSslCertStore
598)
599{
600 HRESULT hr = S_OK;
601 DWORD er = ERROR_SUCCESS;
602 HTTP_SERVICE_CONFIG_SSL_SNI_SET set = { };
603
604 SetSniSslCertSetKey(&set.KeyDesc, wzHost, iPort);
605 set.ParamDesc.SslHashLength = cbCertificateThumbprint;
606 set.ParamDesc.pSslHash = rgbCertificateThumbprint;
607 set.ParamDesc.AppId = *pAppId;
608 set.ParamDesc.pSslCertStoreName = wzSslCertStore;
609
610 er = ::HttpSetServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &set, sizeof(set), NULL);
611 if (ERROR_ALREADY_EXISTS == er)
612 {
613 hr = S_FALSE;
614 }
615 else
616 {
617 hr = HRESULT_FROM_WIN32(er);
618 }
619
620 return hr;
621}
622
623static HRESULT GetSniSslCert(
624 __in_z LPWSTR wzHost,
625 __in int nPort,
626 __out HTTP_SERVICE_CONFIG_SSL_SNI_SET** ppSet
627)
628{
629 HRESULT hr = S_OK;
630 DWORD er = ERROR_SUCCESS;
631 HTTP_SERVICE_CONFIG_SSL_SNI_QUERY query = { };
632 HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSet = NULL;
633 ULONG cbSet = 0;
634
635 *ppSet = NULL;
636
637 query.QueryDesc = HttpServiceConfigQueryExact;
638 SetSniSslCertSetKey(&query.KeyDesc, wzHost, nPort);
639
640 er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL);
641 if (ERROR_INSUFFICIENT_BUFFER == er)
642 {
643 pSet = reinterpret_cast<HTTP_SERVICE_CONFIG_SSL_SNI_SET*>(MemAlloc(cbSet, TRUE));
644 ExitOnNull(pSet, hr, E_OUTOFMEMORY, "Failed to allocate query SN SSL certificate buffer");
645
646 er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL);
647 }
648
649 if (ERROR_SUCCESS == er)
650 {
651 *ppSet = pSet;
652 pSet = NULL;
653 }
654 else if (ERROR_FILE_NOT_FOUND == er)
655 {
656 hr = S_FALSE;
657 }
658 else
659 {
660 hr = HRESULT_FROM_WIN32(er);
661 }
662
663LExit:
664 ReleaseMem(pSet);
665
666 return hr;
667}
668
669static HRESULT RemoveSniSslCert(
670 __in_z LPCWSTR /*wzId*/,
671 __in_z LPWSTR wzHost,
672 __in int iPort
673)
674{
675 HRESULT hr = S_OK;
676 DWORD er = ERROR_SUCCESS;
677 HTTP_SERVICE_CONFIG_SSL_SNI_SET set = { };
678
679 SetSniSslCertSetKey(&set.KeyDesc, wzHost, iPort);
680
681 er = ::HttpDeleteServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &set, sizeof(set), NULL);
682 if (ERROR_FILE_NOT_FOUND == er)
683 {
684 hr = S_FALSE;
685 }
686 else
687 {
688 hr = HRESULT_FROM_WIN32(er);
689 }
690
691 return hr;
692}
693
694static void SetSniSslCertSetKey(
695 __in HTTP_SERVICE_CONFIG_SSL_SNI_KEY* pKey,
696 __in_z LPWSTR wzHost,
697 __in int iPort
698)
699{
700 pKey->Host = wzHost;
701 SOCKADDR_IN* pss = reinterpret_cast<SOCKADDR_IN*>(&pKey->IpPort);
702 pss->sin_family = AF_INET;
703 pss->sin_port = htons(static_cast<USHORT>(iPort));
704}
diff --git a/src/ca/wixhttpca.cpp b/src/ca/wixhttpca.cpp
index 3c091192..0355f718 100644
--- a/src/ca/wixhttpca.cpp
+++ b/src/ca/wixhttpca.cpp
@@ -40,8 +40,6 @@ LPCWSTR vcsHttpUrlAceQuery =
40 L"WHERE `WixHttpUrlAce`.`WixHttpUrlReservation_`=?"; 40 L"WHERE `WixHttpUrlAce`.`WixHttpUrlReservation_`=?";
41enum eHttpUrlAceQuery { huaqSecurityPrincipal = 1, huaqRights }; 41enum eHttpUrlAceQuery { huaqSecurityPrincipal = 1, huaqRights };
42 42
43enum eHandleExisting { heReplace = 0, heIgnore = 1, heFail = 2 };
44
45/****************************************************************** 43/******************************************************************
46 SchedHttpUrlReservations - immediate custom action worker to 44 SchedHttpUrlReservations - immediate custom action worker to
47 prepare configuring URL reservations. 45 prepare configuring URL reservations.
@@ -348,6 +346,11 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
348 ExitOnFailure(hr, "Failed to get CustomActionData."); 346 ExitOnFailure(hr, "Failed to get CustomActionData.");
349 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", sczCustomActionData); 347 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", sczCustomActionData);
350 348
349 if (!sczCustomActionData || !*sczCustomActionData)
350 {
351 WcaLog(LOGMSG_STANDARD, "No URL reservations to be executed.");
352 }
353
351 wz = sczCustomActionData; 354 wz = sczCustomActionData;
352 while (wz && *wz) 355 while (wz && *wz)
353 { 356 {
@@ -388,7 +391,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
388 { 391 {
389 if (fRollback) 392 if (fRollback)
390 { 393 {
391 WcaLogError(hr, "Failed to remove reservation for URL '%ls'", sczUrl); 394 WcaLogError(hr, "Failed to remove reservation for rollback for URL '%ls'", sczUrl);
392 } 395 }
393 else 396 else
394 { 397 {
@@ -396,6 +399,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
396 } 399 }
397 } 400 }
398 } 401 }
402
399 if (fAdd) 403 if (fAdd)
400 { 404 {
401 WcaLog(LOGMSG_STANDARD, "Adding reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL); 405 WcaLog(LOGMSG_STANDARD, "Adding reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
@@ -408,7 +412,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
408 { 412 {
409 if (fRollback) 413 if (fRollback)
410 { 414 {
411 WcaLogError(hr, "Failed to add reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL); 415 WcaLogError(hr, "Failed to add reservation for rollback for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
412 } 416 }
413 else 417 else
414 { 418 {
diff --git a/src/ca/wixhttpca.def b/src/ca/wixhttpca.def
index f7e3e004..281c5631 100644
--- a/src/ca/wixhttpca.def
+++ b/src/ca/wixhttpca.def
@@ -7,3 +7,6 @@ EXPORTS
7 SchedHttpUrlReservationsInstall 7 SchedHttpUrlReservationsInstall
8 SchedHttpUrlReservationsUninstall 8 SchedHttpUrlReservationsUninstall
9 ExecHttpUrlReservations 9 ExecHttpUrlReservations
10 SchedHttpSniSslCertsInstall
11 SchedHttpSniSslCertsUninstall
12 ExecHttpSniSslCerts
diff --git a/src/test/WixToolsetTest.Http/HttpExtensionFixture.cs b/src/test/WixToolsetTest.Http/HttpExtensionFixture.cs
index d1a1f565..1d350bdf 100644
--- a/src/test/WixToolsetTest.Http/HttpExtensionFixture.cs
+++ b/src/test/WixToolsetTest.Http/HttpExtensionFixture.cs
@@ -10,6 +10,25 @@ namespace WixToolsetTest.Http
10 public class HttpExtensionFixture 10 public class HttpExtensionFixture
11 { 11 {
12 [Fact] 12 [Fact]
13 public void CanBuildUsingSniSssl()
14 {
15 var folder = TestData.Get("TestData", "SniSsl");
16 var build = new Builder(folder, typeof(HttpExtensionFactory), new[] { folder });
17
18 var results = build.BuildAndQuery(Build, "CustomAction", "WixHttpSniSslCert");
19 WixAssert.CompareLineByLine(new[]
20 {
21 "CustomAction:Wix4ExecHttpSniSslCertsInstall_X86\t3073\tWix4HttpCA_X86\tExecHttpSniSslCerts\t",
22 "CustomAction:Wix4ExecHttpSniSslCertsUninstall_X86\t3073\tWix4HttpCA_X86\tExecHttpSniSslCerts\t",
23 "CustomAction:Wix4RollbackHttpSniSslCertsInstall_X86\t3329\tWix4HttpCA_X86\tExecHttpSniSslCerts\t",
24 "CustomAction:Wix4RollbackHttpSniSslCertsUninstall_X86\t3329\tWix4HttpCA_X86\tExecHttpSniSslCerts\t",
25 "CustomAction:Wix4SchedHttpSniSslCertsInstall_X86\t1\tWix4HttpCA_X86\tSchedHttpSniSslCertsInstall\t",
26 "CustomAction:Wix4SchedHttpSniSslCertsUninstall_X86\t1\tWix4HttpCA_X86\tSchedHttpSniSslCertsUninstall\t",
27 "WixHttpSniSslCert:sslC9YX6_H7UL_WGBx4DoDGI.Sj.D0\texample.com\t8080\t[SOME_THUMBPRINT]\t\t\t2\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo",
28 }, results);
29 }
30
31 [Fact]
13 public void CanBuildUsingUrlReservation() 32 public void CanBuildUsingUrlReservation()
14 { 33 {
15 var folder = TestData.Get(@"TestData\UsingUrlReservation"); 34 var folder = TestData.Get(@"TestData\UsingUrlReservation");
diff --git a/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.en-us.wxl b/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.en-us.wxl
new file mode 100644
index 00000000..38c12ac1
--- /dev/null
+++ b/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.en-us.wxl
@@ -0,0 +1,11 @@
1<?xml version="1.0" encoding="utf-8"?>
2
3<!--
4This file contains the declaration of all the localizable strings.
5-->
6<WixLocalization xmlns="http://wixtoolset.org/schemas/v4/wxl" Culture="en-US">
7
8 <String Id="DowngradeError">A newer version of [ProductName] is already installed.</String>
9 <String Id="FeatureTitle">MsiPackage</String>
10
11</WixLocalization>
diff --git a/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.wxs b/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.wxs
new file mode 100644
index 00000000..c85e9bba
--- /dev/null
+++ b/src/test/WixToolsetTest.Http/TestData/SniSsl/Package.wxs
@@ -0,0 +1,15 @@
1<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
2 <Package Name="MsiPackage" Version="1.0.0.0" Manufacturer="Example Corporation" UpgradeCode="047730a5-30fe-4a62-a520-da9381b8226a">
3 <MajorUpgrade DowngradeErrorMessage="!(loc.DowngradeError)" />
4
5 <Feature Id="ProductFeature" Title="!(loc.FeatureTitle)">
6 <ComponentGroupRef Id="ProductComponents" />
7 </Feature>
8 </Package>
9
10 <Fragment>
11 <StandardDirectory Id="ProgramFilesFolder">
12 <Directory Id="INSTALLFOLDER" Name="MsiPackage" />
13 </StandardDirectory>
14 </Fragment>
15</Wix>
diff --git a/src/test/WixToolsetTest.Http/TestData/SniSsl/PackageComponents.wxs b/src/test/WixToolsetTest.Http/TestData/SniSsl/PackageComponents.wxs
new file mode 100644
index 00000000..f0aae485
--- /dev/null
+++ b/src/test/WixToolsetTest.Http/TestData/SniSsl/PackageComponents.wxs
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="utf-8"?>
2<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
3 xmlns:http="http://wixtoolset.org/schemas/v4/wxs/http">
4 <Fragment>
5 <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
6 <Component>
7 <File Source="example.txt" />
8 <http:SniSslCertificate Host="example.com" Port="8080" Thumbprint="[SOME_THUMBPRINT]" HandleExisting="fail" />
9 </Component>
10 </ComponentGroup>
11 </Fragment>
12</Wix>
diff --git a/src/test/WixToolsetTest.Http/TestData/SniSsl/example.txt b/src/test/WixToolsetTest.Http/TestData/SniSsl/example.txt
new file mode 100644
index 00000000..1b4ffe8a
--- /dev/null
+++ b/src/test/WixToolsetTest.Http/TestData/SniSsl/example.txt
@@ -0,0 +1 @@
This is example.txt. \ No newline at end of file
diff --git a/src/wixext/HttpCompiler.cs b/src/wixext/HttpCompiler.cs
index cb217147..6c572470 100644
--- a/src/wixext/HttpCompiler.cs
+++ b/src/wixext/HttpCompiler.cs
@@ -48,6 +48,10 @@ namespace WixToolset.Http
48 48
49 switch (element.Name.LocalName) 49 switch (element.Name.LocalName)
50 { 50 {
51 case "SniSslCertificate":
52 this.ParseSniSslCertificateElement(intermediate, section, element, componentId);
53 break;
54
51 case "UrlReservation": 55 case "UrlReservation":
52 this.ParseUrlReservationElement(intermediate, section, element, componentId, null); 56 this.ParseUrlReservationElement(intermediate, section, element, componentId, null);
53 break; 57 break;
@@ -63,6 +67,119 @@ namespace WixToolset.Http
63 } 67 }
64 68
65 /// <summary> 69 /// <summary>
70 /// Parses a SniSsl element.
71 /// </summary>
72 /// <param name="node">The element to parse.</param>
73 /// <param name="componentId">Identifier of the component that owns this SNI SSL Certificate.</param>
74 private void ParseSniSslCertificateElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId)
75 {
76 var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node);
77 Identifier id = null;
78 string host = null;
79 string port = null;
80 string appId = null;
81 string store = null;
82 string thumbprint = null;
83 var handleExisting = HandleExisting.Replace;
84 string handleExistingValue = null;
85
86 foreach (var attrib in node.Attributes())
87 {
88 if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace)
89 {
90 switch (attrib.Name.LocalName)
91 {
92 case "Id":
93 id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib);
94 break;
95 case "AppId":
96 appId = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
97 break;
98 case "HandleExisting":
99 handleExistingValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
100 switch (handleExistingValue)
101 {
102 case "replace":
103 handleExisting = HandleExisting.Replace;
104 break;
105 case "ignore":
106 handleExisting = HandleExisting.Ignore;
107 break;
108 case "fail":
109 handleExisting = HandleExisting.Fail;
110 break;
111 default:
112 this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, node.Name.LocalName, "HandleExisting", handleExistingValue, "replace", "ignore", "fail"));
113 break;
114 }
115 break;
116 case "Host":
117 host = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
118 break;
119 case "Port":
120 port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
121 break;
122 case "Store":
123 store = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
124 break;
125 case "Thumbprint":
126 thumbprint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib);
127 break;
128 default:
129 this.ParseHelper.UnexpectedAttribute(node, attrib);
130 break;
131 }
132 }
133 else
134 {
135 this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, node, attrib);
136 }
137 }
138
139 // Need the element ID for child element processing, so generate now if not authored.
140 if (null == id)
141 {
142 id = this.ParseHelper.CreateIdentifier("ssl", componentId, host, port);
143 }
144
145 // Required attributes.
146 if (null == host)
147 {
148 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Host"));
149 }
150
151 if (null == port)
152 {
153 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Port"));
154 }
155
156 if (null == thumbprint)
157 {
158 this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint"));
159 }
160
161 // Parse unknown children.
162 this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node);
163
164 if (!this.Messaging.EncounteredError)
165 {
166 section.AddSymbol(new WixHttpSniSslCertSymbol(sourceLineNumbers, id)
167 {
168 Host = host,
169 Port = port,
170 Thumbprint = thumbprint,
171 AppId = appId,
172 Store = store,
173 HandleExisting = handleExisting,
174 ComponentRef = componentId,
175 });
176
177 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsInstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
178 this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4SchedHttpSniSslCertsUninstall", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64);
179 }
180 }
181
182 /// <summary>
66 /// Parses a UrlReservation element. 183 /// Parses a UrlReservation element.
67 /// </summary> 184 /// </summary>
68 /// <param name="node">The element to parse.</param> 185 /// <param name="node">The element to parse.</param>
diff --git a/src/wixext/HttpTableDefinitions.cs b/src/wixext/HttpTableDefinitions.cs
index 0665ce8d..83cd565a 100644
--- a/src/wixext/HttpTableDefinitions.cs
+++ b/src/wixext/HttpTableDefinitions.cs
@@ -6,6 +6,23 @@ namespace WixToolset.Http
6 6
7 public static class HttpTableDefinitions 7 public static class HttpTableDefinitions
8 { 8 {
9 public static readonly TableDefinition WixHttpSniSslCert = new TableDefinition(
10 "WixHttpSniSslCert",
11 HttpSymbolDefinitions.WixHttpSniSslCert,
12 new[]
13 {
14 new ColumnDefinition("WixHttpSniSslCert", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "The non-localized primary key for the table.", modularizeType: ColumnModularizeType.Column),
15 new ColumnDefinition("Host", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Host for the SNI SSL certificate.", modularizeType: ColumnModularizeType.Property),
16 new ColumnDefinition("Port", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Port for the SNI SSL certificate.", modularizeType: ColumnModularizeType.Property),
17 new ColumnDefinition("Thumbprint", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "humbprint of the SNI SSL certificate to find.", modularizeType: ColumnModularizeType.Property),
18 new ColumnDefinition("AppId", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Optional application id for the SNI SSL certificate.", modularizeType: ColumnModularizeType.Property),
19 new ColumnDefinition("Store", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Optional application id for the SNI SSL certificate.", modularizeType: ColumnModularizeType.Property),
20 new ColumnDefinition("HandleExisting", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2, description: "The behavior when trying to install a SNI SSL certificate and it already exists."),
21 new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table referencing the component that controls the URL reservation.", modularizeType: ColumnModularizeType.Column),
22 },
23 symbolIdIsPrimaryKey: true
24 );
25
9 public static readonly TableDefinition WixHttpUrlReservation = new TableDefinition( 26 public static readonly TableDefinition WixHttpUrlReservation = new TableDefinition(
10 "WixHttpUrlReservation", 27 "WixHttpUrlReservation",
11 HttpSymbolDefinitions.WixHttpUrlReservation, 28 HttpSymbolDefinitions.WixHttpUrlReservation,
@@ -35,6 +52,7 @@ namespace WixToolset.Http
35 52
36 public static readonly TableDefinition[] All = new[] 53 public static readonly TableDefinition[] All = new[]
37 { 54 {
55 WixHttpSniSslCert,
38 WixHttpUrlReservation, 56 WixHttpUrlReservation,
39 WixHttpUrlAce, 57 WixHttpUrlAce,
40 }; 58 };
diff --git a/src/wixext/Symbols/HttpSymbolDefinitions.cs b/src/wixext/Symbols/HttpSymbolDefinitions.cs
index a6deb307..2aa03468 100644
--- a/src/wixext/Symbols/HttpSymbolDefinitions.cs
+++ b/src/wixext/Symbols/HttpSymbolDefinitions.cs
@@ -7,6 +7,7 @@ namespace WixToolset.Http
7 7
8 public enum HttpSymbolDefinitionType 8 public enum HttpSymbolDefinitionType
9 { 9 {
10 WixHttpSniSslCert,
10 WixHttpUrlAce, 11 WixHttpUrlAce,
11 WixHttpUrlReservation, 12 WixHttpUrlReservation,
12 } 13 }
@@ -29,6 +30,9 @@ namespace WixToolset.Http
29 { 30 {
30 switch (type) 31 switch (type)
31 { 32 {
33 case HttpSymbolDefinitionType.WixHttpSniSslCert:
34 return HttpSymbolDefinitions.WixHttpSniSslCert;
35
32 case HttpSymbolDefinitionType.WixHttpUrlAce: 36 case HttpSymbolDefinitionType.WixHttpUrlAce:
33 return HttpSymbolDefinitions.WixHttpUrlAce; 37 return HttpSymbolDefinitions.WixHttpUrlAce;
34 38
diff --git a/src/wixext/Symbols/WixHttpSniSslCertSymbol.cs b/src/wixext/Symbols/WixHttpSniSslCertSymbol.cs
new file mode 100644
index 00000000..ec67a089
--- /dev/null
+++ b/src/wixext/Symbols/WixHttpSniSslCertSymbol.cs
@@ -0,0 +1,95 @@
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
3namespace WixToolset.Http
4{
5 using WixToolset.Data;
6 using WixToolset.Http.Symbols;
7
8 public static partial class HttpSymbolDefinitions
9 {
10 public static readonly IntermediateSymbolDefinition WixHttpSniSslCert = new IntermediateSymbolDefinition(
11 HttpSymbolDefinitionType.WixHttpSniSslCert.ToString(),
12 new[]
13 {
14 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.Host), IntermediateFieldType.String),
15 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.Port), IntermediateFieldType.String),
16 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.Thumbprint), IntermediateFieldType.String),
17 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.AppId), IntermediateFieldType.String),
18 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.Store), IntermediateFieldType.String),
19 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.HandleExisting), IntermediateFieldType.Number),
20 new IntermediateFieldDefinition(nameof(WixHttpSniSslCertSymbolFields.ComponentRef), IntermediateFieldType.String),
21 },
22 typeof(WixHttpSniSslCertSymbol));
23 }
24}
25
26namespace WixToolset.Http.Symbols
27{
28 using WixToolset.Data;
29
30 public enum WixHttpSniSslCertSymbolFields
31 {
32 Host,
33 Port,
34 Thumbprint,
35 AppId,
36 Store,
37 HandleExisting,
38 ComponentRef,
39 }
40
41 public class WixHttpSniSslCertSymbol : IntermediateSymbol
42 {
43 public WixHttpSniSslCertSymbol() : base(HttpSymbolDefinitions.WixHttpSniSslCert, null, null)
44 {
45 }
46
47 public WixHttpSniSslCertSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(HttpSymbolDefinitions.WixHttpSniSslCert, sourceLineNumber, id)
48 {
49 }
50
51 public IntermediateField this[WixHttpSniSslCertSymbolFields index] => this.Fields[(int)index];
52
53 public string Host
54 {
55 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.Host].AsString();
56 set => this.Set((int)WixHttpSniSslCertSymbolFields.Host, value);
57 }
58
59 public string Port
60 {
61 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.Port].AsString();
62 set => this.Set((int)WixHttpSniSslCertSymbolFields.Port, value);
63 }
64
65 public string Thumbprint
66 {
67 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.Thumbprint].AsString();
68 set => this.Set((int)WixHttpSniSslCertSymbolFields.Thumbprint, value);
69 }
70
71 public string AppId
72 {
73 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.AppId].AsString();
74 set => this.Set((int)WixHttpSniSslCertSymbolFields.AppId, value);
75 }
76
77 public string Store
78 {
79 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.Store].AsString();
80 set => this.Set((int)WixHttpSniSslCertSymbolFields.Store, value);
81 }
82
83 public HandleExisting HandleExisting
84 {
85 get => (HandleExisting)this.Fields[(int)WixHttpSniSslCertSymbolFields.HandleExisting].AsNumber();
86 set => this.Set((int)WixHttpSniSslCertSymbolFields.HandleExisting, (int)value);
87 }
88
89 public string ComponentRef
90 {
91 get => this.Fields[(int)WixHttpSniSslCertSymbolFields.ComponentRef].AsString();
92 set => this.Set((int)WixHttpSniSslCertSymbolFields.ComponentRef, value);
93 }
94 }
95}
diff --git a/src/wixlib/HttpExtension_Platform.wxi b/src/wixlib/HttpExtension_Platform.wxi
index 2d498862..4f4a9e23 100644
--- a/src/wixlib/HttpExtension_Platform.wxi
+++ b/src/wixlib/HttpExtension_Platform.wxi
@@ -32,6 +32,33 @@
32 </Fragment> 32 </Fragment>
33 33
34 <Fragment> 34 <Fragment>
35 <UIRef Id="WixHttpErrors" />
36 <UI>
37 <ProgressText Action="$(var.Prefix)SchedHttpSniSslCertsInstall$(var.Suffix)" Message="!(loc.WixSchedHttpSniSslCertsInstall)" />
38 <ProgressText Action="$(var.Prefix)SchedHttpSniSslCertsUninstall$(var.Suffix)" Message="!(loc.WixSchedHttpSniSslCertsUninstall)" />
39 <ProgressText Action="$(var.Prefix)RollbackHttpSniSslCertsInstall$(var.Suffix)" Message="!(loc.WixRollbackHttpSniSslCertsInstall)" />
40 <ProgressText Action="$(var.Prefix)ExecHttpSniSslCertsInstall$(var.Suffix)" Message="!(loc.WixExecHttpSniSslCertsInstall)" />
41 <ProgressText Action="$(var.Prefix)RollbackHttpSniSslCertsUninstall$(var.Suffix)" Message="!(loc.WixRollbackHttpSniSslCertsUninstall)" />
42 <ProgressText Action="$(var.Prefix)ExecHttpSniSslCertsUninstall$(var.Suffix)" Message="!(loc.WixExecHttpSniSslCertsUninstall)" />
43 </UI>
44
45 <CustomAction Id="$(var.Prefix)SchedHttpSniSslCertsInstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="SchedHttpSniSslCertsInstall" Execute="immediate" Return="check" SuppressModularization="yes" />
46 <CustomAction Id="$(var.Prefix)SchedHttpSniSslCertsUninstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="SchedHttpSniSslCertsUninstall" Execute="immediate" Return="check" SuppressModularization="yes" />
47 <CustomAction Id="$(var.Prefix)RollbackHttpSniSslCertsInstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="ExecHttpSniSslCerts" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" />
48 <CustomAction Id="$(var.Prefix)ExecHttpSniSslCertsInstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="ExecHttpSniSslCerts" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" />
49 <CustomAction Id="$(var.Prefix)RollbackHttpSniSslCertsUninstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="ExecHttpSniSslCerts" Execute="rollback" Impersonate="no" Return="check" SuppressModularization="yes" />
50 <CustomAction Id="$(var.Prefix)ExecHttpSniSslCertsUninstall$(var.Suffix)" BinaryRef="$(var.Prefix)HttpCA$(var.Suffix)" DllEntry="ExecHttpSniSslCerts" Execute="deferred" Impersonate="no" Return="check" SuppressModularization="yes" />
51
52 <!--
53 We need HTTP.SYS on Windows 8 or later for SNI SSL support.
54 -->
55 <InstallExecuteSequence>
56 <Custom Action="$(var.Prefix)SchedHttpSniSslCertsUninstall$(var.Suffix)" Before="RemoveFiles" Overridable="yes" Condition="VersionNT >= 602" />
57 <Custom Action="$(var.Prefix)SchedHttpSniSslCertsInstall$(var.Suffix)" After="InstallFiles" Overridable="yes" Condition="VersionNT >= 602" />
58 </InstallExecuteSequence>
59 </Fragment>
60
61 <Fragment>
35 <Binary Id="$(var.Prefix)HttpCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))httpca.dll" /> 62 <Binary Id="$(var.Prefix)HttpCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))httpca.dll" />
36 </Fragment> 63 </Fragment>
37</Include> 64</Include>
diff --git a/src/wixlib/en-us.wxl b/src/wixlib/en-us.wxl
index b6c180ed..22d74f78 100644
--- a/src/wixlib/en-us.wxl
+++ b/src/wixlib/en-us.wxl
@@ -1,6 +1,5 @@
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. --> 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 2
3
4<WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl"> 3<WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl">
5 <String Id="WixSchedHttpUrlReservationsInstall" Overridable="yes">Preparing to configure Windows HTTP Server</String> 4 <String Id="WixSchedHttpUrlReservationsInstall" Overridable="yes">Preparing to configure Windows HTTP Server</String>
6 <String Id="WixSchedHttpUrlReservationsUninstall" Overridable="yes">Preparing to configure Windows HTTP Server</String> 5 <String Id="WixSchedHttpUrlReservationsUninstall" Overridable="yes">Preparing to configure Windows HTTP Server</String>
@@ -8,4 +7,11 @@
8 <String Id="WixExecHttpUrlReservationsInstall" Overridable="yes">Configuring Windows HTTP Server</String> 7 <String Id="WixExecHttpUrlReservationsInstall" Overridable="yes">Configuring Windows HTTP Server</String>
9 <String Id="WixRollbackHttpUrlReservationsUninstall" Overridable="yes">Rolling back Windows HTTP Server configuration</String> 8 <String Id="WixRollbackHttpUrlReservationsUninstall" Overridable="yes">Rolling back Windows HTTP Server configuration</String>
10 <String Id="WixExecHttpUrlReservationsUninstall" Overridable="yes">Configuring Windows HTTP Server</String> 9 <String Id="WixExecHttpUrlReservationsUninstall" Overridable="yes">Configuring Windows HTTP Server</String>
10
11 <String Id="WixSchedHttpSniSslCertsInstall" Overridable="yes">Preparing to configure Windows HTTP Server SSL</String>
12 <String Id="WixSchedHttpSniSslCertsUninstall" Overridable="yes">Preparing to configure Windows HTTP Server SSL</String>
13 <String Id="WixRollbackHttpSniSslCertsInstall" Overridable="yes">Rolling back Windows HTTP Server SSL configuration</String>
14 <String Id="WixExecHttpSniSslCertsInstall" Overridable="yes">Configuring Windows HTTP Server SSL</String>
15 <String Id="WixRollbackHttpSniSslCertsUninstall" Overridable="yes">Rolling back Windows HTTP Server SSL configuration</String>
16 <String Id="WixExecHttpSniSslCertsUninstall" Overridable="yes">Configuring Windows HTTP Server SSL</String>
11</WixLocalization> 17</WixLocalization>