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