@joebigelow / wix / commits / fc542c09

Add support for configuring SNI SSL certificates

Rob Mensching committed Apr 12, 2021 at 07:46 UTC fc542c0974840882e5bbba0046c530e2ede34170
18 files changed +1063 -15
src/ca/cost.h
+1
@@ -3,3 +3,4 @@
3
4
5 const UINT COST_HTTP_URL_ACL = 2000;
6 +const UINT COST_HTTP_SNI_SSL = 2000;
src/ca/httpca.vcxproj
+11 -10
@@ -3,7 +3,7 @@
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')" />
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">
8 <ProjectConfiguration Include="Debug|Win32">
9 <Configuration>Debug</Configuration>
@@ -13,7 +13,7 @@
13 <Configuration>Release</Configuration>
14 <Platform>Win32</Platform>
15 </ProjectConfiguration>
16 - <ProjectConfiguration Include="Debug|x64">
16 + <ProjectConfiguration Include="Debug|x64">
17 <Configuration>Debug</Configuration>
18 <Platform>x64</Platform>
19 </ProjectConfiguration>
@@ -30,7 +30,7 @@
30 <Platform>ARM64</Platform>
31 </ProjectConfiguration>
32 </ItemGroup>
33 -
33 +
34 <PropertyGroup Label="Globals">
35 <ProjectGuid>{90743805-C043-47C7-B5FF-8F5EE5C8A2DE}</ProjectGuid>
36 <ConfigurationType>DynamicLibrary</ConfigurationType>
@@ -40,27 +40,28 @@
40 <ProjectModuleDefinitionFile>wixhttpca.def</ProjectModuleDefinitionFile>
41 <Description>WiX Toolset Http CustomAction</Description>
42 </PropertyGroup>
43 -
43 +
44 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
45 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
46 -
46 +
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>
50 -
50 +
51 <ItemGroup>
52 <ClCompile Include="dllmain.cpp">
53 <PrecompiledHeader>Create</PrecompiledHeader>
54 </ClCompile>
55 + <ClCompile Include="snisslcert.cpp" />
56 <ClCompile Include="wixhttpca.cpp" />
57 <ClInclude Include="cost.h" />
58 <ClInclude Include="precomp.h" />
59 <None Include="packages.config" />
60 <None Include="wixhttpca.def" />
61 </ItemGroup>
61 -
62 +
63 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
63 -
64 +
65 <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
66 <PropertyGroup>
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 @@
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'))" />
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'))" />
71 </Target>
71 -</Project>
\ No newline at end of file
72 +</Project>
src/ca/httpca.vcxproj.filters
+3
@@ -21,6 +21,9 @@
21 <ClCompile Include="dllmain.cpp">
22 <Filter>Source Files</Filter>
23 </ClCompile>
24 + <ClCompile Include="snisslcert.cpp">
25 + <Filter>Source Files</Filter>
26 + </ClCompile>
27 </ItemGroup>
28 <ItemGroup>
29 <ClInclude Include="precomp.h">
src/ca/precomp.h
+7
@@ -16,3 +16,10 @@
16 #include "cost.h"
17
18 #include "caDecor.h"
19 +
20 +enum eHandleExisting
21 +{
22 + heReplace = 0,
23 + heIgnore = 1,
24 + heFail = 2
25 +};
src/ca/snisslcert.cpp new
+704
@@ -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 +
7 +typedef 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 +
13 +typedef 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 +
19 +typedef 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 +
30 +static UINT SchedHttpSniSslCerts(
31 + __in WCA_TODO todoSched
32 +);
33 +static 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 +);
42 +static 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 +);
53 +static HRESULT EnsureAppId(
54 + __inout_z LPWSTR* psczAppId,
55 + __in_opt HTTP_SERVICE_CONFIG_SSL_SNI_SET* pExistingSniSslSet
56 +);
57 +static HRESULT StringFromGuid(
58 + __in REFGUID rguid,
59 + __inout_z LPWSTR* psczGuid
60 +);
61 +static 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 +);
70 +static HRESULT GetSniSslCert(
71 + __in_z LPWSTR wzHost,
72 + __in int nPort,
73 + __out HTTP_SERVICE_CONFIG_SSL_SNI_SET** ppSet
74 +);
75 +static HRESULT RemoveSniSslCert(
76 + __in_z LPCWSTR wzId,
77 + __in_z LPWSTR wzHost,
78 + __in int iPort
79 +);
80 +static void SetSniSslCertSetKey(
81 + __in HTTP_SERVICE_CONFIG_SSL_SNI_KEY* pKey,
82 + __in_z LPWSTR wzHost,
83 + __in int iPort
84 +);
85 +
86 +
87 +LPCWSTR vcsWixHttpSniSslCertQuery =
88 +L"SELECT `WixHttpSniSslCert`.`WixHttpSniSslCert`, `WixHttpSniSslCert`.`Host`, `WixHttpSniSslCert`.`Port`, `WixHttpSniSslCert`.`Thumbprint`, `WixHttpSniSslCert`.`AppId`, `WixHttpSniSslCert`.`Store`, `WixHttpSniSslCert`.`HandleExisting`, `WixHttpSniSslCert`.`Component_` "
89 +L"FROM `WixHttpSniSslCert`";
90 +enum 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 +********************************************************************/
97 +extern "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 +
108 +LExit:
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 +********************************************************************/
117 +extern "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 +
128 +LExit:
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 +********************************************************************/
137 +extern "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 +
276 +LExit:
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 +
293 +static 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 +
441 +LExit:
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 +
460 +static 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 +
489 +LExit:
490 + ReleaseStr(sczAppId);
491 + ReleaseStr(sczCertificateThumbprint);
492 +
493 + return hr;
494 +}
495 +
496 +static 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 +
534 +LExit:
535 + return hr;
536 +}
537 +
538 +static 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 +
565 +LExit:
566 + return hr;
567 +}
568 +
569 +static 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 +
586 +LExit:
587 + return hr;
588 +}
589 +
590 +static 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 +
623 +static 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 +
663 +LExit:
664 + ReleaseMem(pSet);
665 +
666 + return hr;
667 +}
668 +
669 +static 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 +
694 +static 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 +}
src/ca/wixhttpca.cpp
+8 -4
@@ -40,8 +40,6 @@ LPCWSTR vcsHttpUrlAceQuery =
40 L"WHERE `WixHttpUrlAce`.`WixHttpUrlReservation_`=?";
41 enum eHttpUrlAceQuery { huaqSecurityPrincipal = 1, huaqRights };
42
43 -enum eHandleExisting { heReplace = 0, heIgnore = 1, heFail = 2 };
44 -
43 /******************************************************************
44 SchedHttpUrlReservations - immediate custom action worker to
45 prepare configuring URL reservations.
@@ -348,6 +346,11 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
346 ExitOnFailure(hr, "Failed to get CustomActionData.");
347 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", sczCustomActionData);
348
349 + if (!sczCustomActionData || !*sczCustomActionData)
350 + {
351 + WcaLog(LOGMSG_STANDARD, "No URL reservations to be executed.");
352 + }
353 +
354 wz = sczCustomActionData;
355 while (wz && *wz)
356 {
@@ -388,7 +391,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
391 {
392 if (fRollback)
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);
395 }
396 else
397 {
@@ -396,6 +399,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
399 }
400 }
401 }
402 +
403 if (fAdd)
404 {
405 WcaLog(LOGMSG_STANDARD, "Adding reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
@@ -408,7 +412,7 @@ extern "C" UINT __stdcall ExecHttpUrlReservations(
412 {
413 if (fRollback)
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);
416 }
417 else
418 {
src/ca/wixhttpca.def
+3
@@ -7,3 +7,6 @@ EXPORTS
7 SchedHttpUrlReservationsInstall
8 SchedHttpUrlReservationsUninstall
9 ExecHttpUrlReservations
10 + SchedHttpSniSslCertsInstall
11 + SchedHttpSniSslCertsUninstall
12 + ExecHttpSniSslCerts
src/test/WixToolsetTest.Http/HttpExtensionFixture.cs
+19
@@ -9,6 +9,25 @@ namespace WixToolsetTest.Http
9
10 public class HttpExtensionFixture
11 {
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]
32 public void CanBuildUsingUrlReservation()
33 {
src/test/WixToolsetTest.Http/TestData/SniSsl/Package.en-us.wxl new
+11
@@ -0,0 +1,11 @@
1 +<?xml version="1.0" encoding="utf-8"?>
2 +
3 +<!--
4 +This 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>
src/test/WixToolsetTest.Http/TestData/SniSsl/Package.wxs new
+15
@@ -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>
src/test/WixToolsetTest.Http/TestData/SniSsl/PackageComponents.wxs new
+12
@@ -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>
src/test/WixToolsetTest.Http/TestData/SniSsl/example.txt new
+1
@@ -0,0 +1 @@
1 +This is example.txt.
\ No newline at end of file
src/wixext/HttpCompiler.cs
+117
@@ -48,6 +48,10 @@ namespace WixToolset.Http
48
49 switch (element.Name.LocalName)
50 {
51 + case "SniSslCertificate":
52 + this.ParseSniSslCertificateElement(intermediate, section, element, componentId);
53 + break;
54 +
55 case "UrlReservation":
56 this.ParseUrlReservationElement(intermediate, section, element, componentId, null);
57 break;
@@ -62,6 +66,119 @@ namespace WixToolset.Http
66 }
67 }
68
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>
183 /// Parses a UrlReservation element.
184 /// </summary>
src/wixext/HttpTableDefinitions.cs
+18
@@ -6,6 +6,23 @@ namespace WixToolset.Http
6
7 public static class HttpTableDefinitions
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 +
26 public static readonly TableDefinition WixHttpUrlReservation = new TableDefinition(
27 "WixHttpUrlReservation",
28 HttpSymbolDefinitions.WixHttpUrlReservation,
@@ -35,6 +52,7 @@ namespace WixToolset.Http
52
53 public static readonly TableDefinition[] All = new[]
54 {
55 + WixHttpSniSslCert,
56 WixHttpUrlReservation,
57 WixHttpUrlAce,
58 };
src/wixext/Symbols/HttpSymbolDefinitions.cs
+4
@@ -7,6 +7,7 @@ namespace WixToolset.Http
7
8 public enum HttpSymbolDefinitionType
9 {
10 + WixHttpSniSslCert,
11 WixHttpUrlAce,
12 WixHttpUrlReservation,
13 }
@@ -29,6 +30,9 @@ namespace WixToolset.Http
30 {
31 switch (type)
32 {
33 + case HttpSymbolDefinitionType.WixHttpSniSslCert:
34 + return HttpSymbolDefinitions.WixHttpSniSslCert;
35 +
36 case HttpSymbolDefinitionType.WixHttpUrlAce:
37 return HttpSymbolDefinitions.WixHttpUrlAce;
38
src/wixext/Symbols/WixHttpSniSslCertSymbol.cs new
+95
@@ -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 +
3 +namespace 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 +
26 +namespace 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 +}
src/wixlib/HttpExtension_Platform.wxi
+27
@@ -31,6 +31,33 @@
31 </InstallExecuteSequence>
32 </Fragment>
33
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>
62 <Binary Id="$(var.Prefix)HttpCA$(var.Suffix)" SourceFile="!(bindpath.$(var.platform))httpca.dll" />
63 </Fragment>
src/wixlib/en-us.wxl
+7 -1
@@ -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. -->
2
3 -
3 <WixLocalization Culture="en-us" xmlns="http://wixtoolset.org/schemas/v4/wxl">
4 <String Id="WixSchedHttpUrlReservationsInstall" 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 @@
7 <String Id="WixExecHttpUrlReservationsInstall" Overridable="yes">Configuring Windows HTTP Server</String>
8 <String Id="WixRollbackHttpUrlReservationsUninstall" Overridable="yes">Rolling back Windows HTTP Server configuration</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>
17 </WixLocalization>