Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

expand protocol range of firewall extension, add tests #449

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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