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>