summaryrefslogtreecommitdiff
path: root/src/ext/Http/ca/wixhttpca.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Http/ca/wixhttpca.cpp')
-rw-r--r--src/ext/Http/ca/wixhttpca.cpp530
1 files changed, 530 insertions, 0 deletions
diff --git a/src/ext/Http/ca/wixhttpca.cpp b/src/ext/Http/ca/wixhttpca.cpp
new file mode 100644
index 00000000..8c846ffc
--- /dev/null
+++ b/src/ext/Http/ca/wixhttpca.cpp
@@ -0,0 +1,530 @@
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
5static HRESULT AppendUrlAce(
6 __in LPWSTR wzSecurityPrincipal,
7 __in int iRights,
8 __in LPWSTR* psczSDDL
9 );
10static HRESULT WriteHttpUrlReservation(
11 __in WCA_TODO action,
12 __in LPWSTR wzUrl,
13 __in LPWSTR wzSDDL,
14 __in int iHandleExisting,
15 __in LPWSTR* psczCustomActionData
16 );
17static HRESULT AddUrlReservation(
18 __in LPWSTR wzUrl,
19 __in LPWSTR wzSddl
20 );
21static HRESULT GetUrlReservation(
22 __in LPWSTR wzUrl,
23 __deref_out_z LPWSTR* psczSddl
24 );
25static HRESULT RemoveUrlReservation(
26 __in LPWSTR wzUrl
27 );
28
29HTTPAPI_VERSION vcHttpVersion = HTTPAPI_VERSION_1;
30ULONG vcHttpFlags = HTTP_INITIALIZE_CONFIG;
31
32LPCWSTR vcsHttpUrlReservationQuery =
33 L"SELECT `Wix4HttpUrlReservation`.`Wix4HttpUrlReservation`, `Wix4HttpUrlReservation`.`HandleExisting`, `Wix4HttpUrlReservation`.`Sddl`, `Wix4HttpUrlReservation`.`Url`, `Wix4HttpUrlReservation`.`Component_` "
34 L"FROM `Wix4HttpUrlReservation`";
35enum eHttpUrlReservationQuery { hurqId = 1, hurqHandleExisting, hurqSDDL, hurqUrl, hurqComponent };
36
37LPCWSTR vcsHttpUrlAceQuery =
38 L"SELECT `Wix4HttpUrlAce`.`SecurityPrincipal`, `Wix4HttpUrlAce`.`Rights` "
39 L"FROM `Wix4HttpUrlAce` "
40 L"WHERE `Wix4HttpUrlAce`.`Wix4HttpUrlReservation_`=?";
41enum eHttpUrlAceQuery { huaqSecurityPrincipal = 1, huaqRights };
42
43/******************************************************************
44 SchedHttpUrlReservations - immediate custom action worker to
45 prepare configuring URL reservations.
46
47********************************************************************/
48static UINT SchedHttpUrlReservations(
49 __in MSIHANDLE hInstall,
50 __in WCA_TODO todoSched
51 )
52{
53 HRESULT hr = S_OK;
54 UINT er = ERROR_SUCCESS;
55 BOOL fAceTableExists = FALSE;
56 BOOL fHttpInitialized = FALSE;
57 DWORD cUrlReservations = 0;
58
59 PMSIHANDLE hView = NULL;
60 PMSIHANDLE hRec = NULL;
61 PMSIHANDLE hQueryReq = NULL;
62 PMSIHANDLE hAceView = NULL;
63
64 LPWSTR sczCustomActionData = NULL;
65 LPWSTR sczRollbackCustomActionData = NULL;
66
67 LPWSTR sczId = NULL;
68 LPWSTR sczComponent = NULL;
69 WCA_TODO todoComponent = WCA_TODO_UNKNOWN;
70 LPWSTR sczUrl = NULL;
71 LPWSTR sczSecurityPrincipal = NULL;
72 int iRights = 0;
73 int iHandleExisting = 0;
74
75 LPWSTR sczExistingSDDL = NULL;
76 LPWSTR sczSDDL = NULL;
77
78 // Initialize.
79 hr = WcaInitialize(hInstall, "SchedHttpUrlReservations");
80 ExitOnFailure(hr, "Failed to initialize.");
81
82 // Anything to do?
83 hr = WcaTableExists(L"Wix4HttpUrlReservation");
84 ExitOnFailure(hr, "Failed to check if the Wix4HttpUrlReservation table exists.");
85 if (S_FALSE == hr)
86 {
87 WcaLog(LOGMSG_STANDARD, "Wix4HttpUrlReservation table doesn't exist, so there are no URL reservations to configure.");
88 ExitFunction();
89 }
90
91 hr = WcaTableExists(L"Wix4HttpUrlAce");
92 ExitOnFailure(hr, "Failed to check if the Wix4HttpUrlAce table exists.");
93 fAceTableExists = S_OK == hr;
94
95 // Query and loop through all the URL reservations.
96 hr = WcaOpenExecuteView(vcsHttpUrlReservationQuery, &hView);
97 ExitOnFailure(hr, "Failed to open view on the Wix4HttpUrlReservation table.");
98
99 hr = HRESULT_FROM_WIN32(::HttpInitialize(vcHttpVersion, vcHttpFlags, NULL));
100 ExitOnFailure(hr, "Failed to initialize HTTP Server configuration.");
101
102 fHttpInitialized = TRUE;
103
104 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
105 {
106 hr = WcaGetRecordString(hRec, hurqId, &sczId);
107 ExitOnFailure(hr, "Failed to get Wix4HttpUrlReservation.Wix4HttpUrlReservation");
108
109 hr = WcaGetRecordString(hRec, hurqComponent, &sczComponent);
110 ExitOnFailure(hr, "Failed to get Wix4HttpUrlReservation.Component_");
111
112 // Figure out what we're doing for this reservation, treating reinstall the same as install.
113 todoComponent = WcaGetComponentToDo(sczComponent);
114 if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched)
115 {
116 WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d) for UrlReservation '%ls'.", sczComponent, todoComponent, todoSched, sczId);
117 continue;
118 }
119
120 hr = WcaGetRecordFormattedString(hRec, hurqUrl, &sczUrl);
121 ExitOnFailure(hr, "Failed to get Wix4HttpUrlReservation.Url");
122
123 hr = WcaGetRecordInteger(hRec, hurqHandleExisting, &iHandleExisting);
124 ExitOnFailure(hr, "Failed to get Wix4HttpUrlReservation.HandleExisting");
125
126 if (::MsiRecordIsNull(hRec, hurqSDDL))
127 {
128 hr = StrAllocString(&sczSDDL, L"D:", 2);
129 ExitOnFailure(hr, "Failed to allocate SDDL string.");
130
131 // Skip creating the SDDL on uninstall, since it's never used and the lookup(s) could fail.
132 if (fAceTableExists && WCA_TODO_UNINSTALL != todoComponent)
133 {
134 hQueryReq = ::MsiCreateRecord(1);
135 hr = WcaSetRecordString(hQueryReq, 1, sczId);
136 ExitOnFailure(hr, "Failed to create record for querying Wix4HttpUrlAce table for reservation %ls", sczId);
137
138 hr = WcaOpenView(vcsHttpUrlAceQuery, &hAceView);
139 ExitOnFailure(hr, "Failed to open view on Wix4HttpUrlAce table for reservation %ls", sczId);
140 hr = WcaExecuteView(hAceView, hQueryReq);
141 ExitOnFailure(hr, "Failed to execute view on Wix4HttpUrlAce table for reservation %ls", sczId);
142
143 while (S_OK == (hr = WcaFetchRecord(hAceView, &hRec)))
144 {
145 hr = WcaGetRecordFormattedString(hRec, huaqSecurityPrincipal, &sczSecurityPrincipal);
146 ExitOnFailure(hr, "Failed to get Wix4HttpUrlAce.SecurityPrincipal");
147
148 hr = WcaGetRecordInteger(hRec, huaqRights, &iRights);
149 ExitOnFailure(hr, "Failed to get Wix4HttpUrlAce.Rights");
150
151 hr = AppendUrlAce(sczSecurityPrincipal, iRights, &sczSDDL);
152 ExitOnFailure(hr, "Failed to append URL ACE.");
153 }
154
155 if (E_NOMOREITEMS == hr)
156 {
157 hr = S_OK;
158 }
159 ExitOnFailure(hr, "Failed to enumerate selected rows from Wix4HttpUrlAce table.");
160 }
161 }
162 else
163 {
164 hr = WcaGetRecordFormattedString(hRec, hurqSDDL, &sczSDDL);
165 ExitOnFailure(hr, "Failed to get Wix4HttpUrlReservation.SDDL");
166 }
167
168 hr = GetUrlReservation(sczUrl, &sczExistingSDDL);
169 ExitOnFailure(hr, "Failed to get the existing SDDL for %ls", sczUrl);
170
171 hr = WriteHttpUrlReservation(todoComponent, sczUrl, sczExistingSDDL ? sczExistingSDDL : L"", iHandleExisting, &sczRollbackCustomActionData);
172 ExitOnFailure(hr, "Failed to write URL Reservation to rollback custom action data.");
173
174 hr = WriteHttpUrlReservation(todoComponent, sczUrl, sczSDDL, iHandleExisting, &sczCustomActionData);
175 ExitOnFailure(hr, "Failed to write URL reservation to custom action data.");
176 ++cUrlReservations;
177 }
178
179 // Reaching the end of the list is not an error.
180 if (E_NOMOREITEMS == hr)
181 {
182 hr = S_OK;
183 }
184 ExitOnFailure(hr, "Failure occurred while processing Wix4HttpUrlReservation table.");
185
186 // Schedule ExecHttpUrlReservations if there's anything to do.
187 if (cUrlReservations)
188 {
189 WcaLog(LOGMSG_STANDARD, "Scheduling URL reservations (%ls)", sczCustomActionData);
190 WcaLog(LOGMSG_STANDARD, "Scheduling rollback URL reservations (%ls)", sczRollbackCustomActionData);
191
192 if (WCA_TODO_INSTALL == todoSched)
193 {
194 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixRollbackHttpUrlReservationsInstall"), sczRollbackCustomActionData, cUrlReservations * COST_HTTP_URL_ACL);
195 ExitOnFailure(hr, "Failed to schedule install URL reservations rollback.");
196 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixExecHttpUrlReservationsInstall"), sczCustomActionData, cUrlReservations * COST_HTTP_URL_ACL);
197 ExitOnFailure(hr, "Failed to schedule install URL reservations execution.");
198 }
199 else
200 {
201 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixRollbackHttpUrlReservationsUninstall"), sczRollbackCustomActionData, cUrlReservations * COST_HTTP_URL_ACL);
202 ExitOnFailure(hr, "Failed to schedule uninstall URL reservations rollback.");
203 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"WixExecHttpUrlReservationsUninstall"), sczCustomActionData, cUrlReservations * COST_HTTP_URL_ACL);
204 ExitOnFailure(hr, "Failed to schedule uninstall URL reservations execution.");
205 }
206 }
207 else
208 {
209 WcaLog(LOGMSG_STANDARD, "No URL reservations scheduled.");
210 }
211
212LExit:
213 ReleaseStr(sczSDDL);
214 ReleaseStr(sczExistingSDDL);
215 ReleaseStr(sczSecurityPrincipal);
216 ReleaseStr(sczUrl)
217 ReleaseStr(sczComponent);
218 ReleaseStr(sczId);
219 ReleaseStr(sczRollbackCustomActionData);
220 ReleaseStr(sczCustomActionData);
221
222 if (fHttpInitialized)
223 {
224 ::HttpTerminate(vcHttpFlags, NULL);
225 }
226
227 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
228}
229
230static HRESULT AppendUrlAce(
231 __in LPWSTR wzSecurityPrincipal,
232 __in int iRights,
233 __in LPWSTR* psczSDDL
234 )
235{
236 HRESULT hr = S_OK;
237 LPCWSTR wzSid = NULL;
238 LPWSTR sczSid = NULL;
239
240 Assert(wzSecurityPrincipal && *wzSecurityPrincipal);
241 Assert(psczSDDL && *psczSDDL);
242
243 // As documented in the xsd, if the first char is '*', then the rest of the string is a SID string, e.g. *S-1-5-18.
244 if (L'*' == wzSecurityPrincipal[0])
245 {
246 wzSid = &wzSecurityPrincipal[1];
247 }
248 else
249 {
250 hr = AclGetAccountSidStringEx(NULL, wzSecurityPrincipal, &sczSid);
251 ExitOnFailure(hr, "Failed to lookup the SID for account %ls", wzSecurityPrincipal);
252
253 wzSid = sczSid;
254 }
255
256 hr = StrAllocFormatted(psczSDDL, L"%ls(A;;%#x;;;%ls)", *psczSDDL, iRights, wzSid);
257
258LExit:
259 ReleaseStr(sczSid);
260
261 return hr;
262}
263
264static HRESULT WriteHttpUrlReservation(
265 __in WCA_TODO action,
266 __in LPWSTR wzUrl,
267 __in LPWSTR wzSDDL,
268 __in int iHandleExisting,
269 __in LPWSTR* psczCustomActionData
270 )
271{
272 HRESULT hr = S_OK;
273
274 hr = WcaWriteIntegerToCaData(action, psczCustomActionData);
275 ExitOnFailure(hr, "Failed to write action to custom action data.");
276
277 hr = WcaWriteStringToCaData(wzUrl, psczCustomActionData);
278 ExitOnFailure(hr, "Failed to write URL to custom action data.");
279
280 hr = WcaWriteStringToCaData(wzSDDL, psczCustomActionData);
281 ExitOnFailure(hr, "Failed to write SDDL to custom action data.");
282
283 hr = WcaWriteIntegerToCaData(iHandleExisting, psczCustomActionData);
284 ExitOnFailure(hr, "Failed to write HandleExisting to custom action data.")
285
286LExit:
287 return hr;
288}
289
290/******************************************************************
291 SchedHttpUrlReservationsInstall - immediate custom action entry
292 point to prepare adding URL reservations.
293
294********************************************************************/
295extern "C" UINT __stdcall SchedHttpUrlReservationsInstall(
296 __in MSIHANDLE hInstall
297 )
298{
299 return SchedHttpUrlReservations(hInstall, WCA_TODO_INSTALL);
300}
301
302/******************************************************************
303 SchedHttpUrlReservationsUninstall - immediate custom action entry
304 point to prepare removing URL reservations.
305
306********************************************************************/
307extern "C" UINT __stdcall SchedHttpUrlReservationsUninstall(
308 __in MSIHANDLE hInstall
309 )
310{
311 return SchedHttpUrlReservations(hInstall, WCA_TODO_UNINSTALL);
312}
313
314/******************************************************************
315 ExecHttpUrlReservations - deferred custom action entry point to
316 register and remove URL reservations.
317
318********************************************************************/
319extern "C" UINT __stdcall ExecHttpUrlReservations(
320 __in MSIHANDLE hInstall
321 )
322{
323 HRESULT hr = S_OK;
324 BOOL fHttpInitialized = FALSE;
325 LPWSTR sczCustomActionData = NULL;
326 LPWSTR wz = NULL;
327 int iTodo = WCA_TODO_UNKNOWN;
328 LPWSTR sczUrl = NULL;
329 LPWSTR sczSDDL = NULL;
330 eHandleExisting handleExisting = heIgnore;
331 BOOL fRollback = ::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
332 BOOL fRemove = FALSE;
333 BOOL fAdd = FALSE;
334 BOOL fFailOnExisting = FALSE;
335
336 // Initialize.
337 hr = WcaInitialize(hInstall, "ExecHttpUrlReservations");
338 ExitOnFailure(hr, "Failed to initialize.");
339
340 hr = HRESULT_FROM_WIN32(::HttpInitialize(vcHttpVersion, vcHttpFlags, NULL));
341 ExitOnFailure(hr, "Failed to initialize HTTP Server configuration.");
342
343 fHttpInitialized = TRUE;
344
345 hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
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 {
357 // Extract the custom action data and if rolling back, swap INSTALL and UNINSTALL.
358 hr = WcaReadIntegerFromCaData(&wz, &iTodo);
359 ExitOnFailure(hr, "Failed to read todo from custom action data.");
360
361 hr = WcaReadStringFromCaData(&wz, &sczUrl);
362 ExitOnFailure(hr, "Failed to read Url from custom action data.");
363
364 hr = WcaReadStringFromCaData(&wz, &sczSDDL);
365 ExitOnFailure(hr, "Failed to read SDDL from custom action data.");
366
367 hr = WcaReadIntegerFromCaData(&wz, reinterpret_cast<int*>(&handleExisting));
368 ExitOnFailure(hr, "Failed to read HandleExisting from custom action data.");
369
370 switch (iTodo)
371 {
372 case WCA_TODO_INSTALL:
373 case WCA_TODO_REINSTALL:
374 fRemove = heReplace == handleExisting || fRollback;
375 fAdd = !fRollback || *sczSDDL;
376 fFailOnExisting = heFail == handleExisting && !fRollback;
377 break;
378
379 case WCA_TODO_UNINSTALL:
380 fRemove = !fRollback;
381 fAdd = fRollback && *sczSDDL;
382 fFailOnExisting = FALSE;
383 break;
384 }
385
386 if (fRemove)
387 {
388 WcaLog(LOGMSG_STANDARD, "Removing reservation for URL '%ls'", sczUrl);
389 hr = RemoveUrlReservation(sczUrl);
390 if (FAILED(hr))
391 {
392 if (fRollback)
393 {
394 WcaLogError(hr, "Failed to remove reservation for rollback for URL '%ls'", sczUrl);
395 }
396 else
397 {
398 ExitOnFailure(hr, "Failed to remove reservation for URL '%ls'", sczUrl);
399 }
400 }
401 }
402
403 if (fAdd)
404 {
405 WcaLog(LOGMSG_STANDARD, "Adding reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
406 hr = AddUrlReservation(sczUrl, sczSDDL);
407 if (S_FALSE == hr && fFailOnExisting)
408 {
409 hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
410 }
411 if (FAILED(hr))
412 {
413 if (fRollback)
414 {
415 WcaLogError(hr, "Failed to add reservation for rollback for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
416 }
417 else
418 {
419 ExitOnFailure(hr, "Failed to add reservation for URL '%ls' with SDDL '%ls'", sczUrl, sczSDDL);
420 }
421 }
422 }
423 }
424
425LExit:
426 ReleaseStr(sczSDDL);
427 ReleaseStr(sczUrl);
428 ReleaseStr(sczCustomActionData);
429
430 if (fHttpInitialized)
431 {
432 ::HttpTerminate(vcHttpFlags, NULL);
433 }
434
435 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS);
436}
437
438static HRESULT AddUrlReservation(
439 __in LPWSTR wzUrl,
440 __in LPWSTR wzSddl
441 )
442{
443 HRESULT hr = S_OK;
444 DWORD er = ERROR_SUCCESS;
445 HTTP_SERVICE_CONFIG_URLACL_SET set = { };
446
447 set.KeyDesc.pUrlPrefix = wzUrl;
448 set.ParamDesc.pStringSecurityDescriptor = wzSddl;
449
450 er = ::HttpSetServiceConfiguration(NULL, HttpServiceConfigUrlAclInfo, &set, sizeof(set), NULL);
451 if (ERROR_ALREADY_EXISTS == er)
452 {
453 hr = S_FALSE;
454 }
455 else
456 {
457 hr = HRESULT_FROM_WIN32(er);
458 }
459 ExitOnFailure(hr, "Failed to add URL reservation: %ls, ACL: %ls", wzUrl, wzSddl);
460
461LExit:
462 return hr;
463}
464
465static HRESULT GetUrlReservation(
466 __in LPWSTR wzUrl,
467 __deref_out_z LPWSTR* psczSddl
468 )
469{
470 HRESULT hr = S_OK;
471 DWORD er = ERROR_SUCCESS;
472 HTTP_SERVICE_CONFIG_URLACL_QUERY query = { };
473 HTTP_SERVICE_CONFIG_URLACL_SET* pSet = NULL;
474 ULONG cbSet = 0;
475
476 query.QueryDesc = HttpServiceConfigQueryExact;
477 query.KeyDesc.pUrlPrefix = wzUrl;
478
479 er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigUrlAclInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL);
480 if (ERROR_INSUFFICIENT_BUFFER == er)
481 {
482 pSet = reinterpret_cast<HTTP_SERVICE_CONFIG_URLACL_SET*>(MemAlloc(cbSet, TRUE));
483 ExitOnNull(pSet, hr, E_OUTOFMEMORY, "Failed to allocate query URLACL buffer.");
484
485 er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigUrlAclInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL);
486 }
487
488 if (ERROR_SUCCESS == er)
489 {
490 hr = StrAllocString(psczSddl, pSet->ParamDesc.pStringSecurityDescriptor, 0);
491 }
492 else if (ERROR_FILE_NOT_FOUND == er)
493 {
494 hr = S_FALSE;
495 }
496 else
497 {
498 hr = HRESULT_FROM_WIN32(er);
499 }
500
501LExit:
502 ReleaseMem(pSet);
503
504 return hr;
505}
506
507static HRESULT RemoveUrlReservation(
508 __in LPWSTR wzUrl
509 )
510{
511 HRESULT hr = S_OK;
512 DWORD er = ERROR_SUCCESS;
513 HTTP_SERVICE_CONFIG_URLACL_SET set = { };
514
515 set.KeyDesc.pUrlPrefix = wzUrl;
516
517 er = ::HttpDeleteServiceConfiguration(NULL, HttpServiceConfigUrlAclInfo, &set, sizeof(set), NULL);
518 if (ERROR_FILE_NOT_FOUND == er)
519 {
520 hr = S_FALSE;
521 }
522 else
523 {
524 hr = HRESULT_FROM_WIN32(er);
525 }
526 ExitOnFailure(hr, "Failed to remove URL reservation: %ls", wzUrl);
527
528LExit:
529 return hr;
530}