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