From 5c851a848a6eeb86472e8a1cca814dd0cb1b0483 Mon Sep 17 00:00:00 2001 From: adnan shaheen Date: Mon, 5 Feb 2018 14:01:55 +1000 Subject: WIXFEAT:4009 - Add support for outbound firewall rule --- src/ca/firewall.cpp | 46 +++++++++++++++------- .../FirewallExtensionFixture.cs | 17 +++++++- .../UsingOutboundFirewall/Package.en-us.wxl | 11 ++++++ .../TestData/UsingOutboundFirewall/Package.wxs | 22 +++++++++++ .../UsingOutboundFirewall/PackageComponents.wxs | 14 +++++++ .../TestData/UsingOutboundFirewall/example.txt | 1 + .../WixToolsetTest.Firewall.csproj | 4 ++ src/wixext/FirewallCompiler.cs | 7 ++++ src/wixext/FirewallConstants.cs | 2 + src/wixext/FirewallDecompiler.cs | 13 ++++++ src/wixext/FirewallTableDefinitions.cs | 1 + src/wixext/Tuples/WixFirewallExceptionTuple.cs | 8 ++++ src/wixext/firewall.xsd | 10 ++++- src/wixlib/firewall.wixproj | 4 +- src/wixlib/packages.config | 2 +- 15 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.en-us.wxl create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.wxs create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/PackageComponents.wxs create mode 100644 src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/example.txt diff --git a/src/ca/firewall.cpp b/src/ca/firewall.cpp index 62a5b454..bf40ce77 100644 --- a/src/ca/firewall.cpp +++ b/src/ca/firewall.cpp @@ -3,7 +3,7 @@ #include "precomp.h" LPCWSTR vcsFirewallExceptionQuery = - L"SELECT `Name`, `RemoteAddresses`, `Port`, `Protocol`, `Program`, `Attributes`, `Profile`, `Component_`, `Description` FROM `WixFirewallException`"; + L"SELECT `Name`, `RemoteAddresses`, `Port`, `Protocol`, `Program`, `Attributes`, `Profile`, `Component_`, `Description`, `Direction` FROM `WixFirewallException`"; enum eFirewallExceptionQuery { feqName = 1, feqRemoteAddresses, feqPort, feqProtocol, feqProgram, feqAttributes, feqProfile, feqComponent, feqDescription }; enum eFirewallExceptionTarget { fetPort = 1, fetApplication, fetUnknown }; enum eFirewallExceptionAttributes { feaIgnoreFailures = 1 }; @@ -36,6 +36,7 @@ static UINT SchedFirewallExceptions( LPWSTR pwzComponent = NULL; LPWSTR pwzFormattedFile = NULL; LPWSTR pwzDescription = NULL; + int iDirection = 0; // initialize hr = WcaInitialize(hInstall, "SchedFirewallExceptions"); @@ -130,6 +131,9 @@ static UINT SchedFirewallExceptions( hr = WcaWriteStringToCaData(pwzDescription, &pwzCustomActionData); ExitOnFailure(hr, "failed to write firewall rule description to custom action data"); + + hr = WcaWriteIntegerToCaData(iDirection, &pwzCustomActionData); + ExitOnFailure(hr, "failed to write firewall rule direction to custom action data"); } // reaching the end of the list is actually a good thing, not an error @@ -270,6 +274,7 @@ static HRESULT CreateFwRuleObject( __in LPCWSTR wzPort, __in int iProtocol, __in LPCWSTR wzDescription, + __in int iDirection, __out INetFwRule** ppNetFwRule ) { @@ -321,6 +326,12 @@ static HRESULT CreateFwRuleObject( ExitOnFailure(hr, "failed to set exception description '%ls'", bstrDescription); } + if (MSI_NULL_INTEGER != iDirection) + { + hr = pNetFwRule->put_Direction(static_cast (iDirection)); + ExitOnFailure(hr, "failed to set exception direction"); + } + *ppNetFwRule = pNetFwRule; pNetFwRule = NULL; @@ -429,7 +440,8 @@ static HRESULT AddApplicationException( __in BOOL fIgnoreFailures, __in LPCWSTR wzPort, __in int iProtocol, - __in LPCWSTR wzDescription + __in LPCWSTR wzDescription, + __in int iDirection ) { HRESULT hr = S_OK; @@ -456,7 +468,7 @@ static HRESULT AddApplicationException( hr = pNetFwRules->Item(bstrName, &pNetFwRule); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { - hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, &pNetFwRule); + hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, iDirection, &pNetFwRule); ExitOnFailure(hr, "failed to create FwRule object"); // set edge traversal to true @@ -590,8 +602,9 @@ static HRESULT AddPortException( __in BOOL fIgnoreFailures, __in LPCWSTR wzPort, __in int iProtocol, - __in LPCWSTR wzDescription - ) + __in LPCWSTR wzDescription, + __in int iDirection +) { HRESULT hr = S_OK; BSTR bstrName = NULL; @@ -614,7 +627,7 @@ static HRESULT AddPortException( hr = pNetFwRules->Item(bstrName, &pNetFwRule); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { - hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, &pNetFwRule); + hr = CreateFwRuleObject(bstrName, iProfile, wzRemoteAddresses, wzPort, iProtocol, wzDescription, iDirection, &pNetFwRule); ExitOnFailure(hr, "failed to create FwRule object"); // enable it @@ -825,14 +838,15 @@ static HRESULT AddApplicationException( __in BOOL fIgnoreFailures, __in LPCWSTR wzPort, __in int iProtocol, - __in LPCWSTR wzDescription - ) + __in LPCWSTR wzDescription, + __in int iDirection +) { HRESULT hr = S_OK; if (fSupportProfiles) { - hr = AddApplicationException(wzFile, wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription); + hr = AddApplicationException(wzFile, wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription, iDirection); } else { @@ -860,14 +874,15 @@ static HRESULT AddPortException( __in BOOL fIgnoreFailures, __in LPCWSTR wzPort, __in int iProtocol, - __in LPCWSTR wzDescription - ) + __in LPCWSTR wzDescription, + __in int iDirection +) { HRESULT hr = S_OK; if (fSupportProfiles) { - hr = AddPortException(wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription); + hr = AddPortException(wzName, iProfile, wzRemoteAddresses, fIgnoreFailures, wzPort, iProtocol, wzDescription, iDirection); } else { @@ -951,6 +966,7 @@ extern "C" UINT __stdcall ExecFirewallExceptions( LPWSTR pwzDescription = NULL; int iProtocol = 0; int iProfile = 0; + int iDirection = 0; // initialize hr = WcaInitialize(hInstall, "ExecFirewallExceptions"); @@ -1013,6 +1029,8 @@ extern "C" UINT __stdcall ExecFirewallExceptions( ExitOnFailure(hr, "failed to read protocol from custom action data"); hr = WcaReadStringFromCaData(&pwz, &pwzDescription); ExitOnFailure(hr, "failed to read protocol from custom action data"); + hr = WcaReadIntegerFromCaData(&pwz, &iDirection); + ExitOnFailure(hr, "failed to read direction from custom action data"); switch (iTarget) { @@ -1022,7 +1040,7 @@ extern "C" UINT __stdcall ExecFirewallExceptions( case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls on port %ls, protocol %d", pwzName, pwzPort, iProtocol); - hr = AddPortException(fSupportProfiles, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); + hr = AddPortException(fSupportProfiles, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription, iDirection); ExitOnFailure(hr, "failed to add/update port exception for name '%ls' on port %ls, protocol %d", pwzName, pwzPort, iProtocol); break; @@ -1040,7 +1058,7 @@ extern "C" UINT __stdcall ExecFirewallExceptions( case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: WcaLog(LOGMSG_STANDARD, "Installing firewall exception2 %ls (%ls)", pwzName, pwzFile); - hr = AddApplicationException(fSupportProfiles, pwzFile, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription); + hr = AddApplicationException(fSupportProfiles, pwzFile, pwzName, iProfile, pwzRemoteAddresses, fIgnoreFailures, pwzPort, iProtocol, pwzDescription, iDirection); ExitOnFailure(hr, "failed to add/update application exception for name '%ls', file '%ls'", pwzName, pwzFile); break; diff --git a/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs b/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs index 8f8ba44a..ceac4f26 100644 --- a/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs +++ b/src/test/WixToolsetTest.Firewall/FirewallExtensionFixture.cs @@ -25,7 +25,7 @@ namespace WixToolsetTest.Firewall "CustomAction:Wix4RollbackFirewallExceptionsUninstall_X86\t3329\tWix4FWCA_X86\tExecFirewallExceptions\t", "CustomAction:Wix4SchedFirewallExceptionsInstall_X86\t1\tWix4FWCA_X86\tSchedFirewallExceptionsInstall\t", "CustomAction:Wix4SchedFirewallExceptionsUninstall_X86\t1\tWix4FWCA_X86\tSchedFirewallExceptionsUninstall\t", - "WixFirewallException:ExampleFirewall\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example firewall", + "WixFirewallException:ExampleFirewall\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example firewall\t1", }, results); } @@ -44,7 +44,20 @@ namespace WixToolsetTest.Firewall "CustomAction:Wix4RollbackFirewallExceptionsUninstall_A64\t3329\tWix4FWCA_A64\tExecFirewallExceptions\t", "CustomAction:Wix4SchedFirewallExceptionsInstall_A64\t1\tWix4FWCA_A64\tSchedFirewallExceptionsInstall\t", "CustomAction:Wix4SchedFirewallExceptionsUninstall_A64\t1\tWix4FWCA_A64\tSchedFirewallExceptionsUninstall\t", - "WixFirewallException:ExampleFirewall\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example firewall", + "WixFirewallException:ExampleFirewall\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example firewall\t1", + }, results); + } + + [Fact] + public void CanBuildUsingOutboundFirewall() + { + var folder = TestData.Get(@"TestData\UsingOutboundFirewall"); + var build = new Builder(folder, typeof(FirewallExtensionFactory), new[] { folder }); + + var results = build.BuildAndQuery(Build, "WixFirewallException"); + Assert.Equal(new[] + { + "WixFirewallException:fexPv9RR1kBvP6gMgWyXMVQkVbopOA\texample\t*\t42\t6\t\t0\t2147483647\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tAn example outbound firewall\t2", }, results); } diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.en-us.wxl b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.en-us.wxl new file mode 100644 index 00000000..38c12ac1 --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.en-us.wxl @@ -0,0 +1,11 @@ + + + + + + A newer version of [ProductName] is already installed. + MsiPackage + + diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.wxs b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.wxs new file mode 100644 index 00000000..68ff98fd --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/Package.wxs @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/PackageComponents.wxs b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/PackageComponents.wxs new file mode 100644 index 00000000..f8c1e781 --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/PackageComponents.wxs @@ -0,0 +1,14 @@ + + + + + + + + * + + + + + diff --git a/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/example.txt b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/example.txt new file mode 100644 index 00000000..1b4ffe8a --- /dev/null +++ b/src/test/WixToolsetTest.Firewall/TestData/UsingOutboundFirewall/example.txt @@ -0,0 +1 @@ +This is example.txt. \ No newline at end of file diff --git a/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj b/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj index 7727a69b..d04368c1 100644 --- a/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj +++ b/src/test/WixToolsetTest.Firewall/WixToolsetTest.Firewall.csproj @@ -16,6 +16,10 @@ + + + + diff --git a/src/wixext/FirewallCompiler.cs b/src/wixext/FirewallCompiler.cs index 1fa80f48..900af7aa 100644 --- a/src/wixext/FirewallCompiler.cs +++ b/src/wixext/FirewallCompiler.cs @@ -81,6 +81,7 @@ namespace WixToolset.Firewall string scope = null; string remoteAddresses = null; string description = null; + int? direction = null; foreach (var attrib in element.Attributes()) { @@ -177,6 +178,11 @@ namespace WixToolset.Firewall case "Description": description = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; + case "Outbound": + direction = this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib) == YesNoType.Yes + ? FirewallConstants.NET_FW_RULE_DIR_OUT + : FirewallConstants.NET_FW_RULE_DIR_IN; + break; default: this.ParseHelper.UnexpectedAttribute(element, attrib); break; @@ -260,6 +266,7 @@ namespace WixToolset.Firewall Profile = profile ?? FirewallConstants.NET_FW_PROFILE2_ALL, ComponentRef = componentId, Description = description, + Direction = direction ?? FirewallConstants.NET_FW_RULE_DIR_IN, }); if (!String.IsNullOrEmpty(port)) diff --git a/src/wixext/FirewallConstants.cs b/src/wixext/FirewallConstants.cs index 16caa5b4..7bb12ba4 100644 --- a/src/wixext/FirewallConstants.cs +++ b/src/wixext/FirewallConstants.cs @@ -9,6 +9,8 @@ namespace WixToolset.Firewall static class FirewallConstants { // from icftypes.h + public const int NET_FW_RULE_DIR_IN = 1; + public const int NET_FW_RULE_DIR_OUT = 2; public const int NET_FW_IP_PROTOCOL_TCP = 6; public const int NET_FW_IP_PROTOCOL_UDP = 17; diff --git a/src/wixext/FirewallDecompiler.cs b/src/wixext/FirewallDecompiler.cs index b060f8e2..c9478de1 100644 --- a/src/wixext/FirewallDecompiler.cs +++ b/src/wixext/FirewallDecompiler.cs @@ -146,6 +146,19 @@ namespace WixToolset.Firewall fire.Description = (string)row[9]; } + if (!row.IsColumnEmpty(10)) + { + switch (Convert.ToInt32(row[10])) + { + case FirewallConstants.NET_FW_RULE_DIR_IN: + fire.Direction = Firewall.FirewallException.DirectionType.@in; + break; + case FirewallConstants.NET_FW_RULE_DIR_OUT: + fire.Direction = Firewall.FirewallException.DirectionType.@out; + break; + } + } + Wix.Component component = (Wix.Component)this.Core.GetIndexedElement("Component", (string)row[8]); if (null != component) { diff --git a/src/wixext/FirewallTableDefinitions.cs b/src/wixext/FirewallTableDefinitions.cs index 4bae1ef0..068fe696 100644 --- a/src/wixext/FirewallTableDefinitions.cs +++ b/src/wixext/FirewallTableDefinitions.cs @@ -21,6 +21,7 @@ namespace WixToolset.Firewall new ColumnDefinition("Profile", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Integer, minValue: 1, maxValue: 2147483647, description: "Profile (1=domain; 2=private; 4=public; 2147483647=all)."), new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, keyTable: "Component", keyColumn: 1, description: "Foreign key into the Component table referencing component that controls the firewall configuration.", modularizeType: ColumnModularizeType.Column), new ColumnDefinition("Description", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Description displayed in Windows Firewall manager for this firewall rule."), + new ColumnDefinition("Direction", ColumnType.Number, 1, primaryKey: false, nullable: true, ColumnCategory.Integer, minValue: 1, maxValue: 2, description: "Direction (1=in; 2=out)"), }, tupleIdIsPrimaryKey: true ); diff --git a/src/wixext/Tuples/WixFirewallExceptionTuple.cs b/src/wixext/Tuples/WixFirewallExceptionTuple.cs index d08b9e45..d34b8207 100644 --- a/src/wixext/Tuples/WixFirewallExceptionTuple.cs +++ b/src/wixext/Tuples/WixFirewallExceptionTuple.cs @@ -20,6 +20,7 @@ namespace WixToolset.Firewall new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Profile), IntermediateFieldType.Number), new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.ComponentRef), IntermediateFieldType.String), new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Description), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixFirewallExceptionTupleFields.Direction), IntermediateFieldType.Number), }, typeof(WixFirewallExceptionTuple)); } @@ -40,6 +41,7 @@ namespace WixToolset.Firewall.Tuples Profile, ComponentRef, Description, + Direction, } public class WixFirewallExceptionTuple : IntermediateTuple @@ -107,5 +109,11 @@ namespace WixToolset.Firewall.Tuples get => this.Fields[(int)WixFirewallExceptionTupleFields.Description].AsString(); set => this.Set((int)WixFirewallExceptionTupleFields.Description, value); } + + public int Direction + { + get => this.Fields[(int)WixFirewallExceptionTupleFields.Direction].AsNumber(); + set => this.Set((int)WixFirewallExceptionTupleFields.Direction, value); + } } } \ No newline at end of file diff --git a/src/wixext/firewall.xsd b/src/wixext/firewall.xsd index d64aafef..fec7e37a 100644 --- a/src/wixext/firewall.xsd +++ b/src/wixext/firewall.xsd @@ -141,7 +141,7 @@ - If "yes," failures to register this firewall exception will be silently + If "yes", failures to register this firewall exception will be silently ignored. If "no" (the default), failures will cause rollback. @@ -170,6 +170,14 @@ + + + + + If "yes", registers an outbound firewall rule. + + + diff --git a/src/wixlib/firewall.wixproj b/src/wixlib/firewall.wixproj index cb273a38..c1daa05a 100644 --- a/src/wixlib/firewall.wixproj +++ b/src/wixlib/firewall.wixproj @@ -1,7 +1,7 @@ - + {1acffefd-505a-41a5-acbf-a02b7b473aa2} @@ -73,7 +73,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + \ No newline at end of file diff --git a/src/wixlib/packages.config b/src/wixlib/packages.config index 20afb58c..8db41667 100644 --- a/src/wixlib/packages.config +++ b/src/wixlib/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file -- cgit v1.2.3-55-g6feb