aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Firewall/ca/firewall.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Firewall/ca/firewall.cpp')
-rw-r--r--src/ext/Firewall/ca/firewall.cpp1085
1 files changed, 1085 insertions, 0 deletions
diff --git a/src/ext/Firewall/ca/firewall.cpp b/src/ext/Firewall/ca/firewall.cpp
new file mode 100644
index 00000000..caae21a1
--- /dev/null
+++ b/src/ext/Firewall/ca/firewall.cpp
@@ -0,0 +1,1085 @@
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
5LPCWSTR vcsFirewallExceptionQuery =
6 L"SELECT `Name`, `RemoteAddresses`, `Port`, `Protocol`, `Program`, `Attributes`, `Profile`, `Component_`, `Description`, `Direction` FROM `Wix4FirewallException`";
7enum eFirewallExceptionQuery { feqName = 1, feqRemoteAddresses, feqPort, feqProtocol, feqProgram, feqAttributes, feqProfile, feqComponent, feqDescription };
8enum eFirewallExceptionTarget { fetPort = 1, fetApplication, fetUnknown };
9enum eFirewallExceptionAttributes { feaIgnoreFailures = 1 };
10
11/******************************************************************
12 SchedFirewallExceptions - immediate custom action worker to
13 register and remove firewall exceptions.
14
15********************************************************************/
16static UINT SchedFirewallExceptions(
17 __in MSIHANDLE hInstall,
18 WCA_TODO todoSched
19 )
20{
21 HRESULT hr = S_OK;
22 UINT er = ERROR_SUCCESS;
23 int cFirewallExceptions = 0;
24
25 PMSIHANDLE hView = NULL;
26 PMSIHANDLE hRec = NULL;
27
28 LPWSTR pwzCustomActionData = NULL;
29 LPWSTR pwzName = NULL;
30 LPWSTR pwzRemoteAddresses = NULL;
31 LPWSTR pwzPort = NULL;
32 int iProtocol = 0;
33 int iAttributes = 0;
34 int iProfile = 0;
35 LPWSTR pwzProgram = NULL;
36 LPWSTR pwzComponent = NULL;
37 LPWSTR pwzFormattedFile = NULL;
38 LPWSTR pwzDescription = NULL;
39 int iDirection = 0;
40
41 // initialize
42 hr = WcaInitialize(hInstall, "SchedFirewallExceptions");
43 ExitOnFailure(hr, "failed to initialize");
44
45 // anything to do?
46 if (S_OK != WcaTableExists(L"Wix4FirewallException"))
47 {
48 WcaLog(LOGMSG_STANDARD, "Wix4FirewallException table doesn't exist, so there are no firewall exceptions to configure.");
49 ExitFunction();
50 }
51
52 // query and loop through all the firewall exceptions
53 hr = WcaOpenExecuteView(vcsFirewallExceptionQuery, &hView);
54 ExitOnFailure(hr, "failed to open view on Wix4FirewallException table");
55
56 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
57 {
58 hr = WcaGetRecordFormattedString(hRec, feqName, &pwzName);
59 ExitOnFailure(hr, "failed to get firewall exception name");
60
61 hr = WcaGetRecordFormattedString(hRec, feqRemoteAddresses, &pwzRemoteAddresses);
62 ExitOnFailure(hr, "failed to get firewall exception remote addresses");
63
64 hr = WcaGetRecordFormattedString(hRec, feqPort, &pwzPort);
65 ExitOnFailure(hr, "failed to get firewall exception port");
66
67 hr = WcaGetRecordInteger(hRec, feqProtocol, &iProtocol);
68 ExitOnFailure(hr, "failed to get firewall exception protocol");
69
70 hr = WcaGetRecordFormattedString(hRec, feqProgram, &pwzProgram);
71 ExitOnFailure(hr, "failed to get firewall exception program");
72
73 hr = WcaGetRecordInteger(hRec, feqAttributes, &iAttributes);
74 ExitOnFailure(hr, "failed to get firewall exception attributes");
75
76 hr = WcaGetRecordInteger(hRec, feqProfile, &iProfile);
77 ExitOnFailure(hr, "failed to get firewall exception profile");
78
79 hr = WcaGetRecordString(hRec, feqComponent, &pwzComponent);
80 ExitOnFailure(hr, "failed to get firewall exception component");
81
82 hr = WcaGetRecordString(hRec, feqDescription, &pwzDescription);
83 ExitOnFailure(hr, "failed to get firewall description");
84
85 // figure out what we're doing for this exception, treating reinstall the same as install
86 WCA_TODO todoComponent = WcaGetComponentToDo(pwzComponent);
87 if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched)
88 {
89 WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d)", pwzComponent, todoComponent, todoSched);
90 continue;
91 }
92
93 // action :: name :: profile :: remoteaddresses :: attributes :: target :: {port::protocol | path}
94 ++cFirewallExceptions;
95 hr = WcaWriteIntegerToCaData(todoComponent, &pwzCustomActionData);
96 ExitOnFailure(hr, "failed to write exception action to custom action data");
97
98 hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData);
99 ExitOnFailure(hr, "failed to write exception name to custom action data");
100
101 hr = WcaWriteIntegerToCaData(iProfile, &pwzCustomActionData);
102 ExitOnFailure(hr, "failed to write exception profile to custom action data");
103
104 hr = WcaWriteStringToCaData(pwzRemoteAddresses, &pwzCustomActionData);
105 ExitOnFailure(hr, "failed to write exception remote addresses to custom action data");
106
107 hr = WcaWriteIntegerToCaData(iAttributes, &pwzCustomActionData);
108 ExitOnFailure(hr, "failed to write exception attributes to custom action data");
109
110 if (*pwzProgram)
111 {
112 // If program is defined, we have an application exception.
113 hr = WcaWriteIntegerToCaData(fetApplication, &pwzCustomActionData);
114 ExitOnFailure(hr, "failed to write exception target (application) to custom action data");
115
116 hr = WcaWriteStringToCaData(pwzProgram, &pwzCustomActionData);
117 ExitOnFailure(hr, "failed to write application path to custom action data");
118 }
119 else
120 {
121 // we have a port-only exception
122 hr = WcaWriteIntegerToCaData(fetPort, &pwzCustomActionData);
123 ExitOnFailure(hr, "failed to write exception target (port) to custom action data");
124 }
125
126 hr = WcaWriteStringToCaData(pwzPort, &pwzCustomActionData);
127 ExitOnFailure(hr, "failed to write application path to custom action data");
128
129 hr = WcaWriteIntegerToCaData(iProtocol, &pwzCustomActionData);
130 ExitOnFailure(hr, "failed to write exception protocol to custom action data");
131
132 hr = WcaWriteStringToCaData(pwzDescription, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to write firewall rule description to custom action data");
134
135 hr = WcaWriteIntegerToCaData(iDirection, &pwzCustomActionData);
136 ExitOnFailure(hr, "failed to write firewall rule direction to custom action data");
137 }
138
139 // reaching the end of the list is actually a good thing, not an error
140 if (E_NOMOREITEMS == hr)
141 {
142 hr = S_OK;
143 }
144 ExitOnFailure(hr, "failure occured while processing Wix4FirewallException table");
145
146 // schedule ExecFirewallExceptions if there's anything to do
147 if (pwzCustomActionData && *pwzCustomActionData)
148 {
149 WcaLog(LOGMSG_STANDARD, "Scheduling firewall exception (%ls)", pwzCustomActionData);
150
151 if (WCA_TODO_INSTALL == todoSched)
152 {
153 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFirewallExceptionsInstall"), pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION);
154 ExitOnFailure(hr, "failed to schedule firewall install exceptions rollback");
155 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFirewallExceptionsInstall"), pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION);
156 ExitOnFailure(hr, "failed to schedule firewall install exceptions execution");
157 }
158 else
159 {
160 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFirewallExceptionsUninstall"), pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION);
161 ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions rollback");
162 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFirewallExceptionsUninstall"), pwzCustomActionData, cFirewallExceptions * COST_FIREWALL_EXCEPTION);
163 ExitOnFailure(hr, "failed to schedule firewall uninstall exceptions execution");
164 }
165 }
166 else
167 {
168 WcaLog(LOGMSG_STANDARD, "No firewall exceptions scheduled");
169 }
170
171LExit:
172 ReleaseStr(pwzCustomActionData);
173 ReleaseStr(pwzName);
174 ReleaseStr(pwzRemoteAddresses);
175 ReleaseStr(pwzPort);
176 ReleaseStr(pwzProgram);
177 ReleaseStr(pwzComponent);
178 ReleaseStr(pwzDescription);
179 ReleaseStr(pwzFormattedFile);
180
181 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
182}
183
184/******************************************************************
185 SchedFirewallExceptionsInstall - immediate custom action entry
186 point to register firewall exceptions.
187
188********************************************************************/
189extern "C" UINT __stdcall SchedFirewallExceptionsInstall(
190 __in MSIHANDLE hInstall
191 )
192{
193 return SchedFirewallExceptions(hInstall, WCA_TODO_INSTALL);
194}
195
196/******************************************************************
197 SchedFirewallExceptionsUninstall - immediate custom action entry
198 point to remove firewall exceptions.
199
200********************************************************************/
201extern "C" UINT __stdcall SchedFirewallExceptionsUninstall(
202 __in MSIHANDLE hInstall
203 )
204{
205 return SchedFirewallExceptions(hInstall, WCA_TODO_UNINSTALL);
206}
207
208/******************************************************************
209 GetFirewallRules - Get the collection of firewall rules.
210
211********************************************************************/
212static HRESULT GetFirewallRules(
213 __in BOOL fIgnoreFailures,
214 __out INetFwRules** ppNetFwRules
215 )
216{
217 HRESULT hr = S_OK;
218 INetFwPolicy2* pNetFwPolicy2 = NULL;
219 INetFwRules* pNetFwRules = NULL;
220 *ppNetFwRules = NULL;
221
222 do
223 {
224 ReleaseNullObject(pNetFwPolicy2);
225 ReleaseNullObject(pNetFwRules);
226
227 if (SUCCEEDED(hr = ::CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_ALL, __uuidof(INetFwPolicy2), (void**)&pNetFwPolicy2)) &&
228 SUCCEEDED(hr = pNetFwPolicy2->get_Rules(&pNetFwRules)))
229 {
230 break;
231 }
232 else if (fIgnoreFailures)
233 {
234 ExitFunction1(hr = S_FALSE);
235 }
236 else
237 {
238 WcaLog(LOGMSG_STANDARD, "Failed to connect to Windows Firewall");
239 UINT er = WcaErrorMessage(msierrFirewallCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
240 switch (er)
241 {
242 case IDABORT: // exit with the current HRESULT
243 ExitFunction();
244 case IDRETRY: // clean up and retry the loop
245 hr = S_FALSE;
246 break;
247 case IDIGNORE: // pass S_FALSE back to the caller, who knows how to ignore the failure
248 ExitFunction1(hr = S_FALSE);
249 default: // No UI, so default is to fail.
250 ExitFunction();
251 }
252 }
253 } while (S_FALSE == hr);
254
255 *ppNetFwRules = pNetFwRules;
256 pNetFwRules = NULL;
257
258LExit:
259 ReleaseObject(pNetFwPolicy2);
260 ReleaseObject(pNetFwRules);
261
262 return hr;
263}
264
265/******************************************************************
266 CreateFwRuleObject - CoCreate a firewall rule, and set the common set of properties which are shared
267 between port and application firewall rules
268
269********************************************************************/
270static HRESULT CreateFwRuleObject(
271 __in BSTR bstrName,
272 __in int iProfile,
273 __in_opt LPCWSTR wzRemoteAddresses,
274 __in LPCWSTR wzPort,
275 __in int iProtocol,
276 __in LPCWSTR wzDescription,
277 __in int iDirection,
278 __out INetFwRule** ppNetFwRule
279 )
280{
281 HRESULT hr = S_OK;
282 BSTR bstrRemoteAddresses = NULL;
283 BSTR bstrPort = NULL;
284 BSTR bstrDescription = NULL;
285 INetFwRule* pNetFwRule = NULL;
286 *ppNetFwRule = NULL;
287
288 // convert to BSTRs to make COM happy
289 bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses);
290 ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses");
291 bstrPort = ::SysAllocString(wzPort);
292 ExitOnNull(bstrPort, hr, E_OUTOFMEMORY, "failed SysAllocString for port");
293 bstrDescription = ::SysAllocString(wzDescription);
294 ExitOnNull(bstrDescription, hr, E_OUTOFMEMORY, "failed SysAllocString for description");
295
296 hr = ::CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_ALL, __uuidof(INetFwRule), (void**)&pNetFwRule);
297 ExitOnFailure(hr, "failed to create NetFwRule object");
298
299 hr = pNetFwRule->put_Name(bstrName);
300 ExitOnFailure(hr, "failed to set exception name");
301
302 hr = pNetFwRule->put_Profiles(static_cast<NET_FW_PROFILE_TYPE2>(iProfile));
303 ExitOnFailure(hr, "failed to set exception profile");
304
305 if (MSI_NULL_INTEGER != iProtocol)
306 {
307 hr = pNetFwRule->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol));
308 ExitOnFailure(hr, "failed to set exception protocol");
309 }
310
311 if (bstrPort && *bstrPort)
312 {
313 hr = pNetFwRule->put_LocalPorts(bstrPort);
314 ExitOnFailure(hr, "failed to set exception port");
315 }
316
317 if (bstrRemoteAddresses && *bstrRemoteAddresses)
318 {
319 hr = pNetFwRule->put_RemoteAddresses(bstrRemoteAddresses);
320 ExitOnFailure(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses);
321 }
322
323 if (bstrDescription && *bstrDescription)
324 {
325 hr = pNetFwRule->put_Description(bstrDescription);
326 ExitOnFailure(hr, "failed to set exception description '%ls'", bstrDescription);
327 }
328
329 if (MSI_NULL_INTEGER != iDirection)
330 {
331 hr = pNetFwRule->put_Direction(static_cast<NET_FW_RULE_DIRECTION> (iDirection));
332 ExitOnFailure(hr, "failed to set exception direction");
333 }
334
335 *ppNetFwRule = pNetFwRule;
336 pNetFwRule = NULL;
337
338LExit:
339 ReleaseBSTR(bstrRemoteAddresses);
340 ReleaseBSTR(bstrPort);
341 ReleaseBSTR(bstrDescription);
342 ReleaseObject(pNetFwRule);
343
344 return hr;
345}
346
347/******************************************************************
348 FSupportProfiles - Returns true if we support profiles on this machine.
349 (Only on Vista or later)
350
351********************************************************************/
352static BOOL FSupportProfiles()
353{
354 BOOL fSupportProfiles = FALSE;
355 INetFwRules* pNetFwRules = NULL;
356
357 // We only support profiles if we can co-create an instance of NetFwPolicy2.
358 // This will not work on pre-vista machines.
359 if (SUCCEEDED(GetFirewallRules(TRUE, &pNetFwRules)) && pNetFwRules != NULL)
360 {
361 fSupportProfiles = TRUE;
362 ReleaseObject(pNetFwRules);
363 }
364
365 return fSupportProfiles;
366}
367
368/******************************************************************
369 GetCurrentFirewallProfile - get the active firewall profile as an
370 INetFwProfile, which owns the lists of exceptions we're
371 updating.
372
373********************************************************************/
374static HRESULT GetCurrentFirewallProfile(
375 __in BOOL fIgnoreFailures,
376 __out INetFwProfile** ppfwProfile
377 )
378{
379 HRESULT hr = S_OK;
380 INetFwMgr* pfwMgr = NULL;
381 INetFwPolicy* pfwPolicy = NULL;
382 INetFwProfile* pfwProfile = NULL;
383 *ppfwProfile = NULL;
384
385 do
386 {
387 ReleaseNullObject(pfwPolicy);
388 ReleaseNullObject(pfwMgr);
389 ReleaseNullObject(pfwProfile);
390
391 if (SUCCEEDED(hr = ::CoCreateInstance(__uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&pfwMgr)) &&
392 SUCCEEDED(hr = pfwMgr->get_LocalPolicy(&pfwPolicy)) &&
393 SUCCEEDED(hr = pfwPolicy->get_CurrentProfile(&pfwProfile)))
394 {
395 break;
396 }
397 else if (fIgnoreFailures)
398 {
399 ExitFunction1(hr = S_FALSE);
400 }
401 else
402 {
403 WcaLog(LOGMSG_STANDARD, "Failed to connect to Windows Firewall");
404 UINT er = WcaErrorMessage(msierrFirewallCannotConnect, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 0);
405 switch (er)
406 {
407 case IDABORT: // exit with the current HRESULT
408 ExitFunction();
409 case IDRETRY: // clean up and retry the loop
410 hr = S_FALSE;
411 break;
412 case IDIGNORE: // pass S_FALSE back to the caller, who knows how to ignore the failure
413 ExitFunction1(hr = S_FALSE);
414 default: // No UI, so default is to fail.
415 ExitFunction();
416 }
417 }
418 } while (S_FALSE == hr);
419
420 *ppfwProfile = pfwProfile;
421 pfwProfile = NULL;
422
423LExit:
424 ReleaseObject(pfwPolicy);
425 ReleaseObject(pfwMgr);
426 ReleaseObject(pfwProfile);
427
428 return hr;
429}
430
431/******************************************************************
432 AddApplicationException
433
434********************************************************************/
435static HRESULT AddApplicationException(
436 __in LPCWSTR wzFile,
437 __in LPCWSTR wzName,
438 __in int iProfile,
439 __in_opt LPCWSTR wzRemoteAddresses,
440 __in BOOL fIgnoreFailures,
441 __in LPCWSTR wzPort,
442 __in int iProtocol,
443 __in LPCWSTR wzDescription,
444 __in int iDirection
445 )
446{
447 HRESULT hr = S_OK;
448 BSTR bstrFile = NULL;
449 BSTR bstrName = NULL;
450 INetFwRules* pNetFwRules = NULL;
451 INetFwRule* pNetFwRule = NULL;
452
453 // convert to BSTRs to make COM happy
454 bstrFile = ::SysAllocString(wzFile);
455 ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path");
456 bstrName = ::SysAllocString(wzName);
457 ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name");
458
459 // get the collection of firewall rules
460 hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules);
461 ExitOnFailure(hr, "failed to get firewall rules object");
462 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
463 {
464 ExitFunction();
465 }
466
467 // try to find it (i.e., support reinstall)
468 hr = pNetFwRules->Item(bstrName, &pNetFwRule);
469 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
470 {
471 hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, iDirection, &pNetFwRule);
472 ExitOnFailure(hr, "failed to create FwRule object");
473
474 // set edge traversal to true
475 hr = pNetFwRule->put_EdgeTraversal(VARIANT_TRUE);
476 ExitOnFailure(hr, "failed to set application exception edgetraversal property");
477
478 // set path
479 hr = pNetFwRule->put_ApplicationName(bstrFile);
480 ExitOnFailure(hr, "failed to set application name");
481
482 // enable it
483 hr = pNetFwRule->put_Enabled(VARIANT_TRUE);
484 ExitOnFailure(hr, "failed to to enable application exception");
485
486 // add it to the list of authorized apps
487 hr = pNetFwRules->Add(pNetFwRule);
488 ExitOnFailure(hr, "failed to add app to the authorized apps list");
489 }
490 else
491 {
492 // we found an existing app exception (if we succeeded, that is)
493 ExitOnFailure(hr, "failed trying to find existing app");
494
495 // enable it (just in case it was disabled)
496 pNetFwRule->put_Enabled(VARIANT_TRUE);
497 }
498
499LExit:
500 ReleaseBSTR(bstrName);
501 ReleaseBSTR(bstrFile);
502 ReleaseObject(pNetFwRules);
503 ReleaseObject(pNetFwRule);
504
505 return fIgnoreFailures ? S_OK : hr;
506}
507
508/******************************************************************
509 AddApplicationExceptionOnCurrentProfile
510
511********************************************************************/
512static HRESULT AddApplicationExceptionOnCurrentProfile(
513 __in LPCWSTR wzFile,
514 __in LPCWSTR wzName,
515 __in_opt LPCWSTR wzRemoteAddresses,
516 __in BOOL fIgnoreFailures
517 )
518{
519 HRESULT hr = S_OK;
520 BSTR bstrFile = NULL;
521 BSTR bstrName = NULL;
522 BSTR bstrRemoteAddresses = NULL;
523 INetFwProfile* pfwProfile = NULL;
524 INetFwAuthorizedApplications* pfwApps = NULL;
525 INetFwAuthorizedApplication* pfwApp = NULL;
526
527 // convert to BSTRs to make COM happy
528 bstrFile = ::SysAllocString(wzFile);
529 ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path");
530 bstrName = ::SysAllocString(wzName);
531 ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name");
532 bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses);
533 ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses");
534
535 // get the firewall profile, which is our entry point for adding exceptions
536 hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile);
537 ExitOnFailure(hr, "failed to get firewall profile");
538 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
539 {
540 ExitFunction();
541 }
542
543 // first, let's see if the app is already on the exception list
544 hr = pfwProfile->get_AuthorizedApplications(&pfwApps);
545 ExitOnFailure(hr, "failed to get list of authorized apps");
546
547 // try to find it (i.e., support reinstall)
548 hr = pfwApps->Item(bstrFile, &pfwApp);
549 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
550 {
551 // not found, so we get to add it
552 hr = ::CoCreateInstance(__uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), reinterpret_cast<void**>(&pfwApp));
553 ExitOnFailure(hr, "failed to create authorized app");
554
555 // set the display name
556 hr = pfwApp->put_Name(bstrName);
557 ExitOnFailure(hr, "failed to set authorized app name");
558
559 // set path
560 hr = pfwApp->put_ProcessImageFileName(bstrFile);
561 ExitOnFailure(hr, "failed to set authorized app path");
562
563 // set the allowed remote addresses
564 if (bstrRemoteAddresses && *bstrRemoteAddresses)
565 {
566 hr = pfwApp->put_RemoteAddresses(bstrRemoteAddresses);
567 ExitOnFailure(hr, "failed to set authorized app remote addresses");
568 }
569
570 // add it to the list of authorized apps
571 hr = pfwApps->Add(pfwApp);
572 ExitOnFailure(hr, "failed to add app to the authorized apps list");
573 }
574 else
575 {
576 // we found an existing app exception (if we succeeded, that is)
577 ExitOnFailure(hr, "failed trying to find existing app");
578
579 // enable it (just in case it was disabled)
580 pfwApp->put_Enabled(VARIANT_TRUE);
581 }
582
583LExit:
584 ReleaseBSTR(bstrRemoteAddresses);
585 ReleaseBSTR(bstrName);
586 ReleaseBSTR(bstrFile);
587 ReleaseObject(pfwApp);
588 ReleaseObject(pfwApps);
589 ReleaseObject(pfwProfile);
590
591 return fIgnoreFailures ? S_OK : hr;
592}
593
594/******************************************************************
595 AddPortException
596
597********************************************************************/
598static HRESULT AddPortException(
599 __in LPCWSTR wzName,
600 __in int iProfile,
601 __in_opt LPCWSTR wzRemoteAddresses,
602 __in BOOL fIgnoreFailures,
603 __in LPCWSTR wzPort,
604 __in int iProtocol,
605 __in LPCWSTR wzDescription,
606 __in int iDirection
607)
608{
609 HRESULT hr = S_OK;
610 BSTR bstrName = NULL;
611 INetFwRules* pNetFwRules = NULL;
612 INetFwRule* pNetFwRule = NULL;
613
614 // convert to BSTRs to make COM happy
615 bstrName = ::SysAllocString(wzName);
616 ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name");
617
618 // get the collection of firewall rules
619 hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules);
620 ExitOnFailure(hr, "failed to get firewall rules object");
621 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
622 {
623 ExitFunction();
624 }
625
626 // try to find it (i.e., support reinstall)
627 hr = pNetFwRules->Item(bstrName, &pNetFwRule);
628 if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
629 {
630 hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, iDirection, &pNetFwRule);
631 ExitOnFailure(hr, "failed to create FwRule object");
632
633 // enable it
634 hr = pNetFwRule->put_Enabled(VARIANT_TRUE);
635 ExitOnFailure(hr, "failed to to enable port exception");
636
637 // add it to the list of authorized ports
638 hr = pNetFwRules->Add(pNetFwRule);
639 ExitOnFailure(hr, "failed to add app to the authorized ports list");
640 }
641 else
642 {
643 // we found an existing port exception (if we succeeded, that is)
644 ExitOnFailure(hr, "failed trying to find existing port rule");
645
646 // enable it (just in case it was disabled)
647 pNetFwRule->put_Enabled(VARIANT_TRUE);
648 }
649
650LExit:
651 ReleaseBSTR(bstrName);
652 ReleaseObject(pNetFwRules);
653 ReleaseObject(pNetFwRule);
654
655 return fIgnoreFailures ? S_OK : hr;
656}
657
658/******************************************************************
659 AddPortExceptionOnCurrentProfile
660
661********************************************************************/
662static HRESULT AddPortExceptionOnCurrentProfile(
663 __in LPCWSTR wzName,
664 __in_opt LPCWSTR wzRemoteAddresses,
665 __in BOOL fIgnoreFailures,
666 __in int iPort,
667 __in int iProtocol
668 )
669{
670 HRESULT hr = S_OK;
671 BSTR bstrName = NULL;
672 BSTR bstrRemoteAddresses = NULL;
673 INetFwProfile* pfwProfile = NULL;
674 INetFwOpenPorts* pfwPorts = NULL;
675 INetFwOpenPort* pfwPort = NULL;
676
677 // convert to BSTRs to make COM happy
678 bstrName = ::SysAllocString(wzName);
679 ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for name");
680 bstrRemoteAddresses = ::SysAllocString(wzRemoteAddresses);
681 ExitOnNull(bstrRemoteAddresses, hr, E_OUTOFMEMORY, "failed SysAllocString for remote addresses");
682
683 // create and initialize a new open port object
684 hr = ::CoCreateInstance(__uuidof(NetFwOpenPort), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwOpenPort), reinterpret_cast<void**>(&pfwPort));
685 ExitOnFailure(hr, "failed to create new open port");
686
687 hr = pfwPort->put_Port(iPort);
688 ExitOnFailure(hr, "failed to set exception port");
689
690 hr = pfwPort->put_Protocol(static_cast<NET_FW_IP_PROTOCOL>(iProtocol));
691 ExitOnFailure(hr, "failed to set exception protocol");
692
693 if (bstrRemoteAddresses && *bstrRemoteAddresses)
694 {
695 hr = pfwPort->put_RemoteAddresses(bstrRemoteAddresses);
696 ExitOnFailure(hr, "failed to set exception remote addresses '%ls'", bstrRemoteAddresses);
697 }
698
699 hr = pfwPort->put_Name(bstrName);
700 ExitOnFailure(hr, "failed to set exception name");
701
702 // get the firewall profile, its current list of open ports, and add ours
703 hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile);
704 ExitOnFailure(hr, "failed to get firewall profile");
705 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
706 {
707 ExitFunction();
708 }
709
710 hr = pfwProfile->get_GloballyOpenPorts(&pfwPorts);
711 ExitOnFailure(hr, "failed to get open ports");
712
713 hr = pfwPorts->Add(pfwPort);
714 ExitOnFailure(hr, "failed to add exception to global list");
715
716LExit:
717 ReleaseBSTR(bstrRemoteAddresses);
718 ReleaseBSTR(bstrName);
719 ReleaseObject(pfwProfile);
720 ReleaseObject(pfwPorts);
721 ReleaseObject(pfwPort);
722
723 return fIgnoreFailures ? S_OK : hr;
724}
725
726/******************************************************************
727 RemoveException - Removes the exception rule with the given name.
728
729********************************************************************/
730static HRESULT RemoveException(
731 __in LPCWSTR wzName,
732 __in BOOL fIgnoreFailures
733 )
734{
735 HRESULT hr = S_OK;;
736 INetFwRules* pNetFwRules = NULL;
737
738 // convert to BSTRs to make COM happy
739 BSTR bstrName = ::SysAllocString(wzName);
740 ExitOnNull(bstrName, hr, E_OUTOFMEMORY, "failed SysAllocString for path");
741
742 // get the collection of firewall rules
743 hr = GetFirewallRules(fIgnoreFailures, &pNetFwRules);
744 ExitOnFailure(hr, "failed to get firewall rules object");
745 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
746 {
747 ExitFunction();
748 }
749
750 hr = pNetFwRules->Remove(bstrName);
751 ExitOnFailure(hr, "failed to remove authorized app");
752
753LExit:
754 ReleaseBSTR(bstrName);
755 ReleaseObject(pNetFwRules);
756
757 return fIgnoreFailures ? S_OK : hr;
758}
759
760/******************************************************************
761 RemoveApplicationExceptionFromCurrentProfile
762
763********************************************************************/
764static HRESULT RemoveApplicationExceptionFromCurrentProfile(
765 __in LPCWSTR wzFile,
766 __in BOOL fIgnoreFailures
767 )
768{
769 HRESULT hr = S_OK;
770 INetFwProfile* pfwProfile = NULL;
771 INetFwAuthorizedApplications* pfwApps = NULL;
772
773 // convert to BSTRs to make COM happy
774 BSTR bstrFile = ::SysAllocString(wzFile);
775 ExitOnNull(bstrFile, hr, E_OUTOFMEMORY, "failed SysAllocString for path");
776
777 // get the firewall profile, which is our entry point for removing exceptions
778 hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile);
779 ExitOnFailure(hr, "failed to get firewall profile");
780 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
781 {
782 ExitFunction();
783 }
784
785 // now get the list of app exceptions and remove the one
786 hr = pfwProfile->get_AuthorizedApplications(&pfwApps);
787 ExitOnFailure(hr, "failed to get list of authorized apps");
788
789 hr = pfwApps->Remove(bstrFile);
790 ExitOnFailure(hr, "failed to remove authorized app");
791
792LExit:
793 ReleaseBSTR(bstrFile);
794 ReleaseObject(pfwApps);
795 ReleaseObject(pfwProfile);
796
797 return fIgnoreFailures ? S_OK : hr;
798}
799
800/******************************************************************
801 RemovePortExceptionFromCurrentProfile
802
803********************************************************************/
804static HRESULT RemovePortExceptionFromCurrentProfile(
805 __in int iPort,
806 __in int iProtocol,
807 __in BOOL fIgnoreFailures
808 )
809{
810 HRESULT hr = S_OK;
811 INetFwProfile* pfwProfile = NULL;
812 INetFwOpenPorts* pfwPorts = NULL;
813
814 // get the firewall profile, which is our entry point for adding exceptions
815 hr = GetCurrentFirewallProfile(fIgnoreFailures, &pfwProfile);
816 ExitOnFailure(hr, "failed to get firewall profile");
817 if (S_FALSE == hr) // user or package author chose to ignore missing firewall
818 {
819 ExitFunction();
820 }
821
822 hr = pfwProfile->get_GloballyOpenPorts(&pfwPorts);
823 ExitOnFailure(hr, "failed to get open ports");
824
825 hr = pfwPorts->Remove(iPort, static_cast<NET_FW_IP_PROTOCOL>(iProtocol));
826 ExitOnFailure(hr, "failed to remove open port %d, protocol %d", iPort, iProtocol);
827
828LExit:
829 return fIgnoreFailures ? S_OK : hr;
830}
831
832static HRESULT AddApplicationException(
833 __in BOOL fSupportProfiles,
834 __in LPCWSTR wzFile,
835 __in LPCWSTR wzName,
836 __in int iProfile,
837 __in_opt LPCWSTR wzRemoteAddresses,
838 __in BOOL fIgnoreFailures,
839 __in LPCWSTR wzPort,
840 __in int iProtocol,
841 __in LPCWSTR wzDescription,
842 __in int iDirection
843)
844{
845 HRESULT hr = S_OK;
846
847 if (fSupportProfiles)
848 {
849 hr = AddApplicationException(wzFile, wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription, iDirection);
850 }
851 else
852 {
853 if (0 != *wzPort || MSI_NULL_INTEGER != iProtocol)
854 {
855 // NOTE: This is treated as an error rather than either creating a rule based on just the application (no port), or
856 // just the port because it is unclear what is the proper fall back. For example, suppose that you have code that
857 // runs in dllhost.exe. Clearly falling back to opening all of dllhost is wrong. Because the firewall is a security
858 // feature, it seems better to require the MSI author to indicate the behavior that they want.
859 WcaLog(LOGMSG_STANDARD, "FirewallExtension: Cannot add firewall rule '%ls', which defines both an application and a port or protocol. Such a rule requires Microsoft Windows Vista or later.", wzName);
860 return fIgnoreFailures ? S_OK : E_NOTIMPL;
861 }
862
863 hr = AddApplicationExceptionOnCurrentProfile(wzFile, wzName, wzRemoteAddresses, fIgnoreFailures);
864 }
865
866 return hr;
867}
868
869static HRESULT AddPortException(
870 __in BOOL fSupportProfiles,
871 __in LPCWSTR wzName,
872 __in int iProfile,
873 __in_opt LPCWSTR wzRemoteAddresses,
874 __in BOOL fIgnoreFailures,
875 __in LPCWSTR wzPort,
876 __in int iProtocol,
877 __in LPCWSTR wzDescription,
878 __in int iDirection
879)
880{
881 HRESULT hr = S_OK;
882
883 if (fSupportProfiles)
884 {
885 hr = AddPortException(wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription, iDirection);
886 }
887 else
888 {
889 hr = AddPortExceptionOnCurrentProfile(wzName, wzRemoteAddresses, fIgnoreFailures, wcstol(wzPort, NULL, 10), iProtocol);
890 }
891
892 return hr;
893}
894
895static HRESULT RemoveApplicationException(
896 __in BOOL fSupportProfiles,
897 __in LPCWSTR wzName,
898 __in LPCWSTR wzFile,
899 __in BOOL fIgnoreFailures,
900 __in LPCWSTR wzPort,
901 __in int iProtocol
902 )
903{
904 HRESULT hr = S_OK;
905
906 if (fSupportProfiles)
907 {
908 hr = RemoveException(wzName, fIgnoreFailures);
909 }
910 else
911 {
912 if (0 != *wzPort || MSI_NULL_INTEGER != iProtocol)
913 {
914 WcaLog(LOGMSG_STANDARD, "FirewallExtension: Cannot remove firewall rule '%ls', which defines both an application and a port or protocol. Such a rule requires Microsoft Windows Vista or later.", wzName);
915 return S_OK;
916 }
917
918 hr = RemoveApplicationExceptionFromCurrentProfile(wzFile, fIgnoreFailures);
919 }
920
921 return hr;
922}
923
924static HRESULT RemovePortException(
925 __in BOOL fSupportProfiles,
926 __in LPCWSTR wzName,
927 __in LPCWSTR wzPort,
928 __in int iProtocol,
929 __in BOOL fIgnoreFailures
930 )
931{
932 HRESULT hr = S_OK;
933
934 if (fSupportProfiles)
935 {
936 hr = RemoveException(wzName, fIgnoreFailures);
937 }
938 else
939 {
940 hr = RemovePortExceptionFromCurrentProfile(wcstol(wzPort, NULL, 10), iProtocol, fIgnoreFailures);
941 }
942
943 return hr;
944}
945
946/******************************************************************
947 ExecFirewallExceptions - deferred custom action entry point to
948 register and remove firewall exceptions.
949
950********************************************************************/
951extern "C" UINT __stdcall ExecFirewallExceptions(
952 __in MSIHANDLE hInstall
953 )
954{
955 HRESULT hr = S_OK;
956 BOOL fSupportProfiles = FALSE;
957 LPWSTR pwz = NULL;
958 LPWSTR pwzCustomActionData = NULL;
959 int iTodo = WCA_TODO_UNKNOWN;
960 LPWSTR pwzName = NULL;
961 LPWSTR pwzRemoteAddresses = NULL;
962 int iAttributes = 0;
963 int iTarget = fetUnknown;
964 LPWSTR pwzFile = NULL;
965 LPWSTR pwzPort = NULL;
966 LPWSTR pwzDescription = NULL;
967 int iProtocol = 0;
968 int iProfile = 0;
969 int iDirection = 0;
970
971 // initialize
972 hr = WcaInitialize(hInstall, "ExecFirewallExceptions");
973 ExitOnFailure(hr, "failed to initialize");
974
975 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
976 ExitOnFailure(hr, "failed to get CustomActionData");
977 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
978
979 hr = ::CoInitialize(NULL);
980 ExitOnFailure(hr, "failed to initialize COM");
981
982 // Find out if we support profiles (only on Vista or later)
983 fSupportProfiles = FSupportProfiles();
984
985 // loop through all the passed in data
986 pwz = pwzCustomActionData;
987 while (pwz && *pwz)
988 {
989 // extract the custom action data and if rolling back, swap INSTALL and UNINSTALL
990 hr = WcaReadIntegerFromCaData(&pwz, &iTodo);
991 ExitOnFailure(hr, "failed to read todo from custom action data");
992 if (::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK))
993 {
994 if (WCA_TODO_INSTALL == iTodo)
995 {
996 iTodo = WCA_TODO_UNINSTALL;
997 }
998 else if (WCA_TODO_UNINSTALL == iTodo)
999 {
1000 iTodo = WCA_TODO_INSTALL;
1001 }
1002 }
1003
1004 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1005 ExitOnFailure(hr, "failed to read name from custom action data");
1006
1007 hr = WcaReadIntegerFromCaData(&pwz, &iProfile);
1008 ExitOnFailure(hr, "failed to read profile from custom action data");
1009
1010 hr = WcaReadStringFromCaData(&pwz, &pwzRemoteAddresses);
1011 ExitOnFailure(hr, "failed to read remote addresses from custom action data");
1012
1013 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1014 ExitOnFailure(hr, "failed to read attributes from custom action data");
1015 BOOL fIgnoreFailures = feaIgnoreFailures == (iAttributes & feaIgnoreFailures);
1016
1017 hr = WcaReadIntegerFromCaData(&pwz, &iTarget);
1018 ExitOnFailure(hr, "failed to read target from custom action data");
1019
1020 if (iTarget == fetApplication)
1021 {
1022 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
1023 ExitOnFailure(hr, "failed to read file path from custom action data");
1024 }
1025
1026 hr = WcaReadStringFromCaData(&pwz, &pwzPort);
1027 ExitOnFailure(hr, "failed to read port from custom action data");
1028 hr = WcaReadIntegerFromCaData(&pwz, &iProtocol);
1029 ExitOnFailure(hr, "failed to read protocol from custom action data");
1030 hr = WcaReadStringFromCaData(&pwz, &pwzDescription);
1031 ExitOnFailure(hr, "failed to read protocol from custom action data");
1032 hr = WcaReadIntegerFromCaData(&pwz, &iDirection);
1033 ExitOnFailure(hr, "failed to read direction from custom action data");
1034
1035 switch (iTarget)
1036 {
1037 case fetPort:
1038 switch (iTodo)
1039 {
1040 case WCA_TODO_INSTALL:
1041 case WCA_TODO_REINSTALL:
1042 WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol);
1043 hr = AddPortException(fSupportProfiles, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription, iDirection);
1044 ExitOnFailure(hr, "failed to add/update port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol);
1045 break;
1046
1047 case WCA_TODO_UNINSTALL:
1048 WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol);
1049 hr = RemovePortException(fSupportProfiles, pwzName, pwzPort, iProtocol, fIgnoreFailures);
1050 ExitOnFailure(hr, "failed to remove port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol);
1051 break;
1052 }
1053 break;
1054
1055 case fetApplication:
1056 switch (iTodo)
1057 {
1058 case WCA_TODO_INSTALL:
1059 case WCA_TODO_REINSTALL:
1060 WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls (%ls)", pwzName, pwzFile);
1061 hr = AddApplicationException(fSupportProfiles, pwzFile, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription, iDirection);
1062 ExitOnFailure(hr, "failed to add/update application exception for name '%ls', file '%ls'", pwzName, pwzFile);
1063 break;
1064
1065 case WCA_TODO_UNINSTALL:
1066 WcaLog(LOGMSG_STANDARD, "Uninstalling firewall exception2 %ls (%ls)", pwzName, pwzFile);
1067 hr = RemoveApplicationException(fSupportProfiles, pwzName, pwzFile, fIgnoreFailures, pwzPort, iProtocol);
1068 ExitOnFailure(hr, "failed to remove application exception for name '%ls', file '%ls'", pwzName, pwzFile);
1069 break;
1070 }
1071 break;
1072 }
1073 }
1074
1075LExit:
1076 ReleaseStr(pwzCustomActionData);
1077 ReleaseStr(pwzName);
1078 ReleaseStr(pwzRemoteAddresses);
1079 ReleaseStr(pwzFile);
1080 ReleaseStr(pwzPort);
1081 ReleaseStr(pwzDescription);
1082 ::CoUninitialize();
1083
1084 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS);
1085}