Skip to content

Commit

Permalink
expand protocol range of firewall extension, add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbednarski committed Sep 13, 2023
1 parent 409d3b6 commit 2a24415
Show file tree
Hide file tree
Showing 10 changed files with 485 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ public void CanBuildUsingFirewall()
"CustomAction:Wix4SchedFirewallExceptionsInstall_X86\t1\tWix4FWCA_X86\tSchedFirewallExceptionsInstall\t",
"CustomAction:Wix4SchedFirewallExceptionsUninstall_X86\t1\tWix4FWCA_X86\tSchedFirewallExceptionsUninstall\t",
"Wix4FirewallException:ExampleFirewall\tExampleApp\t*\t42\t6\t[#filNdJBJmq3UCUIwmXS8x21aAsvqzk]\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tAn app-based firewall exception\t1",
"Wix4FirewallException:fex_ZpDsnKyHlYiA24JHzvFxm3uLZ8\tExampleDefaultGatewayScope\tDefaultGateway\t4432\t6\t\t0\t2\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tdefaultGateway scope firewall exception\t1",
"Wix4FirewallException:fex6bkfWwpiRGI.wVFx0T7W4LXIHxU\tExampleDHCPScope\tdhcp\t\t211\ttest.exe\t0\t4\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tDHCP scope firewall exception\t1",
"Wix4FirewallException:fex70IVsYNnbwiHQrEepmdTPKH8XYs\tExamplePort\tLocalSubnet\t42\t6\t\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tA port-based firewall exception\t2",
"Wix4FirewallException:fexXxaXCXXFh.UxO_BjmZxi1B1du_Q\tExampleWINSScope\twins\t6573\t6\t\t0\t1\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tWINS scope firewall exception\t1",
"Wix4FirewallException:fexxY71H2ZBkPalv7uid1Yy4qaA_lA\tExampleDNSScope\tdns\t356\t17\t\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tDNS scope firewall exception\t1",
}, results);
}

Expand All @@ -48,7 +52,11 @@ public void CanBuildUsingFirewallARM64()
"CustomAction:Wix4SchedFirewallExceptionsInstall_A64\t1\tWix4FWCA_A64\tSchedFirewallExceptionsInstall\t",
"CustomAction:Wix4SchedFirewallExceptionsUninstall_A64\t1\tWix4FWCA_A64\tSchedFirewallExceptionsUninstall\t",
"Wix4FirewallException:ExampleFirewall\tExampleApp\t*\t42\t6\t[#filNdJBJmq3UCUIwmXS8x21aAsvqzk]\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tAn app-based firewall exception\t1",
"Wix4FirewallException:fex_ZpDsnKyHlYiA24JHzvFxm3uLZ8\tExampleDefaultGatewayScope\tDefaultGateway\t4432\t6\t\t0\t2\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tdefaultGateway scope firewall exception\t1",
"Wix4FirewallException:fex6bkfWwpiRGI.wVFx0T7W4LXIHxU\tExampleDHCPScope\tdhcp\t\t211\ttest.exe\t0\t4\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tDHCP scope firewall exception\t1",
"Wix4FirewallException:fex70IVsYNnbwiHQrEepmdTPKH8XYs\tExamplePort\tLocalSubnet\t42\t6\t\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tA port-based firewall exception\t2",
"Wix4FirewallException:fexXxaXCXXFh.UxO_BjmZxi1B1du_Q\tExampleWINSScope\twins\t6573\t6\t\t0\t1\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tWINS scope firewall exception\t1",
"Wix4FirewallException:fexxY71H2ZBkPalv7uid1Yy4qaA_lA\tExampleDNSScope\tdns\t356\t17\t\t0\t2147483647\tfilNdJBJmq3UCUIwmXS8x21aAsvqzk\tDNS scope firewall exception\t1",
}, results);
}

Expand All @@ -71,8 +79,17 @@ public void CanRoundtripFirewallExceptions()
{
"FirewallException",
"FirewallException",
"FirewallException",
"FirewallException",
"FirewallException",
"FirewallException",
}, actual.Select(a => a.Name).ToArray());
}

[Fact]
public void RoundtripAttributesAreCorrectForApp()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExampleApp");
WixAssert.CompareLineByLine(new[]
{
"Id=ExampleFirewall",
Expand All @@ -85,8 +102,13 @@ public void CanRoundtripFirewallExceptions()
"Description=An app-based firewall exception",
"Outbound=no",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
}, actual[0].Attributes);
}, actual.Attributes);
}

[Fact]
public void RoundtripAttributesAreCorrectForPort()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExamplePort");
WixAssert.CompareLineByLine(new[]
{
"Id=fex70IVsYNnbwiHQrEepmdTPKH8XYs",
Expand All @@ -98,7 +120,79 @@ public void CanRoundtripFirewallExceptions()
"Description=A port-based firewall exception",
"Outbound=yes",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
}, actual[1].Attributes);
}, actual.Attributes);
}

[Fact]
public void RoundtripAttributesAreCorrectForDNSScope()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExampleDNSScope");
WixAssert.CompareLineByLine(new[]
{
"Id=fexxY71H2ZBkPalv7uid1Yy4qaA_lA",
"Name=ExampleDNSScope",
"Scope=DNS",
"Port=356",
"Protocol=udp",
"Profile=all",
"Description=DNS scope firewall exception",
"Outbound=no",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
}, actual.Attributes);
}

[Fact]
public void RoundtripAttributesAreCorrectForDHCPScope()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExampleDHCPScope");
WixAssert.CompareLineByLine(new[]
{
"Id=fex6bkfWwpiRGI.wVFx0T7W4LXIHxU",
"Name=ExampleDHCPScope",
"Scope=DHCP",
"Protocol=211",
"Program=test.exe",
"Profile=public",
"Description=DHCP scope firewall exception",
"Outbound=no",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall"
}, actual.Attributes);
}

[Fact]
public void RoundtripAttributesAreCorrectForWINSScope()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExampleWINSScope");
WixAssert.CompareLineByLine(new[]
{
"Id=fexXxaXCXXFh.UxO_BjmZxi1B1du_Q",
"Name=ExampleWINSScope",
"Scope=WINS",
"Port=6573",
"Protocol=tcp",
"Profile=domain",
"Description=WINS scope firewall exception",
"Outbound=no",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
}, actual.Attributes);
}

[Fact]
public void RoundtripAttributesAreCorrectForDefaultGatewayScope()
{
var actual = BuildAndDecompileAndBuild("http://wixtoolset.org/schemas/v4/wxs/firewall", "ExampleDefaultGatewayScope");
WixAssert.CompareLineByLine(new[]
{
"Id=fex_ZpDsnKyHlYiA24JHzvFxm3uLZ8",
"Name=ExampleDefaultGatewayScope",
"Scope=defaultGateway",
"Port=4432",
"Protocol=tcp",
"Profile=private",
"Description=defaultGateway scope firewall exception",
"Outbound=no",
"xmlns=http://wixtoolset.org/schemas/v4/wxs/firewall",
}, actual.Attributes);
}

private static void Build(string[] args)
Expand All @@ -122,5 +216,31 @@ private static void Decompile(string[] args)
var result = WixRunner.Execute(args);
result.AssertSuccess();
}
class AttributeVerifier
{
public string Name { get; set; }
public string[] Attributes { get; set; }
}

private static AttributeVerifier BuildAndDecompileAndBuild(string nameSpace, string ruleName)
{
var folder = TestData.Get(@"TestData", "UsingFirewall");
var build = new Builder(folder, typeof(FirewallExtensionFactory), new[] { folder });
var output = Path.Combine(folder, $"Firewall{ruleName}.xml");

build.BuildAndDecompileAndBuild(Build, Decompile, output);

var doc = XDocument.Load(output);
var actual = doc.Descendants()
.Where(e => e.Name.Namespace == nameSpace)
.Select(fe => new AttributeVerifier
{
Name = fe.Attributes().Single(a => a.Name.LocalName == "Name").Value,
Attributes = fe.Attributes().Select(a => $"{a.Name.LocalName}={a.Value}").ToArray()
})
.Single(av => av.Name == ruleName);

return actual;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
</File>

<fw:FirewallException Description="A port-based firewall exception" Name="ExamplePort" Port="42" Outbound="yes" Scope="localSubnet" />
<fw:FirewallException Description="DNS scope firewall exception" Name="ExampleDNSScope" Port="356" Protocol="udp" Scope="DNS" />
<fw:FirewallException Description="DHCP scope firewall exception" Name="ExampleDHCPScope" Program="test.exe" Protocol="211" Scope="DHCP" Profile="public" />
<fw:FirewallException Description="WINS scope firewall exception" Name="ExampleWINSScope" Port="6573" Scope="WINS" Profile="domain"/>
<fw:FirewallException Description="defaultGateway scope firewall exception" Name="ExampleDefaultGatewayScope" Port="4432" Scope="defaultGateway" Profile="private" />
</Component>
</ComponentGroup>
</Fragment>
Expand Down
36 changes: 34 additions & 2 deletions src/ext/Firewall/wixext/FirewallCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,12 @@ private void ParseFirewallExceptionElement(Intermediate intermediate, Intermedia
protocol = FirewallConstants.NET_FW_IP_PROTOCOL_UDP;
break;
default:
this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Protocol", protocolValue, "tcp", "udp"));
int parsedProtocol;
if (!Int32.TryParse(protocolValue, out parsedProtocol) || parsedProtocol > 255 || parsedProtocol < 0)
{
this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Protocol", protocolValue, "tcp", "udp", "0-255"));
}
protocol = parsedProtocol;
break;
}
break;
Expand All @@ -149,8 +154,20 @@ private void ParseFirewallExceptionElement(Intermediate intermediate, Intermedia
case "localSubnet":
remoteAddresses = "LocalSubnet";
break;
case "DNS":
remoteAddresses = "dns";
break;
case "DHCP":
remoteAddresses = "dhcp";
break;
case "WINS":
remoteAddresses = "wins";
break;
case "defaultGateway":
remoteAddresses = "DefaultGateway";
break;
default:
this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Scope", scope, "any", "localSubnet"));
this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "Scope", scope, "any", "localSubnet", "DNS", "DHCP", "WINS", "defaultGateway"));
break;
}
break;
Expand Down Expand Up @@ -251,6 +268,21 @@ private void ParseFirewallExceptionElement(Intermediate intermediate, Intermedia
this.Messaging.Write(FirewallErrors.NoExceptionSpecified(sourceLineNumbers));
}

// Ports can only be specified if the protocol is TCP or UDP.
if (!String.IsNullOrEmpty(port) && protocol.HasValue)
{
switch(protocol.Value)
{
case FirewallConstants.NET_FW_IP_PROTOCOL_TCP:
case FirewallConstants.NET_FW_IP_PROTOCOL_UDP:
break;

default:
this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "Port", "Protocol", protocol.Value.ToString()));
break;
}
}

if (!this.Messaging.EncounteredError)
{
// at this point, File attribute and File parent element are treated the same
Expand Down
36 changes: 25 additions & 11 deletions src/ext/Firewall/wixext/FirewallDecompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,29 @@ private void DecompileWixFirewallExceptionTable(Table table)
string[] addresses = ((string)row[2]).Split(',');
if (addresses.Length == 1)
{
// special-case the Scope attribute values
if (addresses[0] == "*")
switch(addresses[0])
{
firewallException.Add(new XAttribute("Scope", "any"));
}
else if (addresses[0] == "LocalSubnet")
{
firewallException.Add(new XAttribute("Scope", "localSubnet"));
}
else
{
FirewallDecompiler.AddRemoteAddress(firewallException, addresses[0]);
case "*":
firewallException.Add(new XAttribute("Scope", "any"));
break;
case "LocalSubnet":
firewallException.Add(new XAttribute("Scope", "localSubnet"));
break;
case "dns":
firewallException.Add(new XAttribute("Scope", "DNS"));
break;
case "dhcp":
firewallException.Add(new XAttribute("Scope", "DHCP"));
break;
case "wins":
firewallException.Add(new XAttribute("Scope", "WINS"));
break;
case "DefaultGateway":
firewallException.Add(new XAttribute("Scope", "defaultGateway"));
break;
default:
FirewallDecompiler.AddRemoteAddress(firewallException, addresses[0]);
break;
}
}
else
Expand All @@ -107,6 +118,9 @@ private void DecompileWixFirewallExceptionTable(Table table)
case FirewallConstants.NET_FW_IP_PROTOCOL_UDP:
firewallException.Add(new XAttribute("Protocol", "udp"));
break;
default:
firewallException.Add(new XAttribute("Protocol", row[4]));
break;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/ext/Firewall/wixext/FirewallTableDefinitions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class FirewallTableDefinitions
new ColumnDefinition("Name", ColumnType.Localized, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Localizable display name.", modularizeType: ColumnModularizeType.Property),
new ColumnDefinition("RemoteAddresses", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Remote address to accept incoming connections from.", modularizeType: ColumnModularizeType.Property),
new ColumnDefinition("Port", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, minValue: 1, description: "Port number.", modularizeType: ColumnModularizeType.Property),
new ColumnDefinition("Protocol", ColumnType.Number, 1, primaryKey: false, nullable: true, ColumnCategory.Integer, minValue: 6, maxValue: 17, description: "Protocol (6=TCP; 17=UDP)."),
new ColumnDefinition("Protocol", ColumnType.Number, 1, primaryKey: false, nullable: true, ColumnCategory.Integer, minValue: 0, maxValue: 255, description: "Protocol (6=TCP; 17=UDP). https://www.iana.org/assignments/protocol-numbers"),
new ColumnDefinition("Program", ColumnType.String, 255, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Exception for a program (formatted path name).", modularizeType: ColumnModularizeType.Property),
new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "Vital=1"),
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)."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- 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. -->
<Project Sdk="WixToolset.Sdk">
<PropertyGroup>
<UpgradeCode>{4D188568-1CCF-4EEE-BC27-17C3DCC83E58}</UpgradeCode>
<ProductComponentsRef>true</ProductComponentsRef>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Templates\Product.wxs" Link="Product.wxs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="WixToolset.Firewall.wixext" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!-- 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. -->

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:fw="http://wixtoolset.org/schemas/v4/wxs/firewall">
<Fragment>
<ComponentGroup Id="ProductComponents">
<ComponentRef Id="FirewallComponent1"/>
</ComponentGroup>
</Fragment>

<Fragment>
<Component Id="FirewallComponent1" Guid="E465C8FE-5B81-4553-9CFC-E0CD96B9A36C" Directory="INSTALLFOLDER">
<fw:FirewallException Id="FirewallException09"
Description="WiX Toolset firewall exception rule integration test - protocol TCP"
Name="WiXToolset401 Test - 0009" Protocol="tcp" Port="900" Scope="any" />
<fw:FirewallException Id="FirewallException10"
Description="WiX Toolset firewall exception rule integration test - protocol UDP"
Name="WiXToolset401 Test - 0010" Protocol="udp" Port="1000" Scope="any" />
<fw:FirewallException Id="FirewallException11"
Description="WiX Toolset firewall exception rule integration test - ports can only be specified if protocol is TCP or UDP"
Name="WiXToolset401 Test - 0011" Protocol="134" Program="test.exe" Scope="any" />
</Component>
</Fragment>
</Wix>
Loading

0 comments on commit 2a24415

Please sign in to comment.