Vanara/System/Network/WindowsFirewall.cs

978 lines
46 KiB
C#

#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Security.AccessControl;
using System.Security.Principal;
using System.ServiceProcess;
using Vanara.Extensions;
using Vanara.InteropServices;
using Vanara.PInvoke;
using static Vanara.PInvoke.FirewallApi;
namespace Vanara.Network;
/// <summary>Specifies the conditions under which edge traversal traffic is allowed.</summary>
/// <remarks>
/// In order for Windows Firewall to dynamically allow edge traversal traffic, the application must use the IPV6_PROTECTION_LEVEL socket
/// option on the listening socket and set it to <c>PROTECTION_LEVEL_UNRESTRICTED</c> only in the cases where edge traversal traffic should
/// be allowed. The Windows Firewall rule added for the application must then set its edge traversal option to
/// <c>DeferToApp</c> or <c>DeferToUser</c>.
/// </remarks>
public enum EdgeTraversalType
{
/// <summary>Edge traversal traffic is always blocked.This is the same as setting the EdgeTraversal property using INetFwRule to VARIANT_FALSE.</summary>
Deny = NET_FW_EDGE_TRAVERSAL_TYPE.NET_FW_EDGE_TRAVERSAL_TYPE_DENY,
/// <summary>Edge traversal traffic is always allowed.This is the same as setting the EdgeTraversal property using INetFwRule to VARIANT_TRUE.</summary>
Allow = NET_FW_EDGE_TRAVERSAL_TYPE.NET_FW_EDGE_TRAVERSAL_TYPE_ALLOW,
/// <summary>
/// Edge traversal traffic is allowed when the application sets the IPV6_PROTECTION_LEVEL socket option to PROTECTION_LEVEL_UNRESTRICTED.
/// Otherwise, it is blocked.
/// </summary>
DeferToApp = NET_FW_EDGE_TRAVERSAL_TYPE.NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_APP,
/// <summary>
/// The user is prompted whether to allow edge traversal traffic when the application sets the IPV6_PROTECTION_LEVEL socket option to
/// PROTECTION_LEVEL_UNRESTRICTED. If the user chooses to allow edge traversal traffic, the rule is modified to defer to the
/// application's settings.If the application has not set the IPV6_PROTECTION_LEVEL socket option to PROTECTION_LEVEL_UNRESTRICTED, edge
/// traversal traffic is blocked.In order to use this option, the firewall rule must have both the application path and protocol scopes
/// specified. This option cannot be used if port(s) are defined.
/// </summary>
DeferToUser = NET_FW_EDGE_TRAVERSAL_TYPE.NET_FW_EDGE_TRAVERSAL_TYPE_DEFER_TO_USER,
}
/// <summary>Specifies the action for a rule or default setting.</summary>
public enum FirewallAction
{
/// <summary>Block traffic.</summary>
Block = NET_FW_ACTION.NET_FW_ACTION_BLOCK,
/// <summary>Allow traffic.</summary>
Allow = NET_FW_ACTION.NET_FW_ACTION_ALLOW,
/// <summary>Maximum traffic.</summary>
Max = NET_FW_ACTION.NET_FW_ACTION_MAX,
}
/// <summary>Specifies the type of authentication which must occur in order for traffic to be allowed.</summary>
public enum FirewallAuthenticateType
{
/// <summary>No security check is performed.</summary>
None = NET_FW_AUTHENTICATE_TYPE.NET_FW_AUTHENTICATE_NONE,
/// <summary>
/// The traffic is allowed if it is IPsec-protected with authentication and no encapsulation protection. This means that the peer is
/// authenticated, but there is no integrity protection on the data.
/// </summary>
NoEncapsulation = NET_FW_AUTHENTICATE_TYPE.NET_FW_AUTHENTICATE_NO_ENCAPSULATION,
/// <summary>The traffic is allowed if it is IPsec-protected with authentication and integrity protection.</summary>
WithIntegrity = NET_FW_AUTHENTICATE_TYPE.NET_FW_AUTHENTICATE_WITH_INTEGRITY,
/// <summary>
/// The traffic is allowed if its is IPsec-protected with authentication and integrity protection. In addition, negotiation of encryption
/// protections on subsequent packets is requested.
/// </summary>
NegotiateEncryption = NET_FW_AUTHENTICATE_TYPE.NET_FW_AUTHENTICATE_AND_NEGOTIATE_ENCRYPTION,
/// <summary>
/// The traffic is allowed if it is IPsec-protected with authentication, integrity and encryption protection since the very first packet.
/// </summary>
Encrypt = NET_FW_AUTHENTICATE_TYPE.NET_FW_AUTHENTICATE_AND_ENCRYPT,
}
/// <summary>Specifies the effect of modifications to the current policy.</summary>
public enum FirewallPolicyModifyState
{
/// <summary>Changing or adding a firewall rule or firewall group to the current profile will take effect.</summary>
Ok = NET_FW_MODIFY_STATE.NET_FW_MODIFY_STATE_OK,
/// <summary>
/// Changing or adding a firewall rule or firewall group to the current profile will not take effect because the profile is
/// controlled by the group policy.
/// </summary>
OverrideGroupPolicy = NET_FW_MODIFY_STATE.NET_FW_MODIFY_STATE_GP_OVERRIDE,
/// <summary>
/// Changing or adding a firewall rule or firewall group to the current profile will not take effect because unsolicited inbound
/// traffic is not allowed.
/// </summary>
InboundBlocked = NET_FW_MODIFY_STATE.NET_FW_MODIFY_STATE_INBOUND_BLOCKED,
}
/// <summary>Specifies the type of profile.</summary>
[Flags]
public enum FirewallProfileType
{
/// <summary/>
All = NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_ALL,
/// <summary>Profile type is domain.</summary>
Domain = NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_DOMAIN,
/// <summary>Profile type is private. This profile type is used for home and other private network types.</summary>
Private = NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_PRIVATE,
/// <summary>Profile type is public. This profile type is used for public Internet access points.</summary>
Public = NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_PUBLIC,
}
/// <summary>Specifies the direction of traffic to which a rule applies.</summary>
public enum RuleDirection
{
/// <summary>The rule applies to inbound traffic.</summary>
In = NET_FW_RULE_DIRECTION.NET_FW_RULE_DIR_IN,
/// <summary>The rule applies to outbound traffic.</summary>
Out = NET_FW_RULE_DIRECTION.NET_FW_RULE_DIR_OUT,
}
/// <summary>Specifies the interface types to which a rule applies.</summary>
[Flags]
public enum RuleInterfaceType
{
/// <summary>All interfaces</summary>
All = 0,
/// <summary>Local area network interfaces.</summary>
Lan = 1,
/// <summary>Remote access interfaces.</summary>
RemoteAccess = 2,
/// <summary>Wireless interfaces.</summary>
Wireless = 4,
}
/// <summary>Wrapper for Windows Firewall interface methods (i.e. <see cref="INetFwPolicy2"/>).</summary>
public static class WindowsFirewall
{
private static readonly ComReleaser<INetFwPolicy2> pPol = ComReleaserFactory.Create(new INetFwPolicy2());
private static FirewallProfile? dom, priv, pub;
private static FirewallRules? rules, svcRules;
static WindowsFirewall()
{
if (!PInvokeClient.WindowsVista.IsPlatformSupported())
throw new PlatformNotSupportedException("Windows Firewall requires Windows Vista or later.");
}
/// <summary>Gets the firewall profile for the active domain.</summary>
/// <value>The domain profile.</value>
public static FirewallProfile DomainProfile => dom ??= new FirewallProfile(pPol.Item, NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_DOMAIN);
/// <summary>
/// <para>
/// The LocalPolicyModifyState attribute determines if adding or setting a rule or group of rules will take effect in the current
/// firewall profile.
/// </para>
/// <para>This property is read-only.</para>
/// </summary>
public static FirewallPolicyModifyState LocalPolicyModifyState => (FirewallPolicyModifyState)pPol.Item.LocalPolicyModifyState;
/// <summary>Gets the firewall profile for the private network. This profile type is used for home and other private network types.</summary>
/// <value>The private profile.</value>
public static FirewallProfile PrivateProfile => priv ??= new FirewallProfile(pPol.Item, NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_PRIVATE);
/// <summary>Gets the firewall profile for the public network. This profile type is used for public Internet access points.</summary>
/// <value>The public profile.</value>
public static FirewallProfile PublicProfile => pub ??= new FirewallProfile(pPol.Item, NET_FW_PROFILE_TYPE2.NET_FW_PROFILE2_PUBLIC);
/// <summary>
/// <para>Retrieves the collection of firewall rules.</para>
/// <para>This property is read-only.</para>
/// </summary>
public static FirewallRules Rules => rules ??= new FirewallRules(pPol.Item.Rules);
/// <summary>Retrieves the collection of Windows Service Hardening networking rules.</summary>
public static FirewallRules ServiceHardeningRules => svcRules ??= new FirewallRules(pPol.Item.ServiceRestriction.Rules);
/// <summary>
/// <para>Retrieves the interface used to access the Windows Service Hardening store.</para>
/// <para>This property is read-only.</para>
/// </summary>
public static INetFwServiceRestriction ServiceRestriction => pPol.Item.ServiceRestriction;
/// <summary>
/// The <c>get_IsRuleGroupCurrentlyEnabled</c> method determines whether a specified group of firewall rules are enabled or disabled for
/// the current profile.
/// </summary>
/// <param name="group">
/// A string that was used to group rules together. It can be the group name or an indirect string to the group name in the form of
/// "@C:\Program Files\Contoso Storefront\StorefrontRes.dll,-1234". Rules belonging to this group would be queried.
/// </param>
/// <returns>
/// <para>
/// This call returns a boolean enable status which indicates whether the group of rules identified by the group parameter are enabled or
/// disabled. If this value is set to true ( <c>VARIANT_TRUE</c>), the group of rules is enabled; otherwise, the group is disabled.
/// </para>
/// </returns>
/// <remarks>
/// When indirect strings in the form of "@C:\Program Files\Contoso Storefront\StorefrontRes.dll,-1234" are passed as parameters to the
/// Windows Firewall with Advanced Security APIs, they should be specified by a full path. The file should have a secure access that
/// permits the Local Service account read access to allow the Windows Firewall Service to read the strings. To avoid non-privileged
/// security principals from modifying the strings, the DLLs should only allow write access to the Administrator account.
/// </remarks>
public static bool IsRuleGroupEnabled(string group) => pPol.Item.IsRuleGroupCurrentlyEnabled[group];
/// <summary>Indicates whether service restriction rules are enabled to limit traffic to the resources specified by the firewall rules.</summary>
/// <param name="serviceName">Name of the service being queried concerning service restriction state.</param>
/// <param name="appName">Name of the application being queried concerning service restriction state.</param>
/// <returns>
/// Indicates whether service restriction rules are in place to restrict the specified service. If <see langword="true"/>, service is
/// restricted. Otherwise, service is not restricted to the resources specified by firewall rules.
/// </returns>
public static bool IsServiceRestricted(string serviceName, string appName) => pPol.Item.ServiceRestriction.ServiceRestricted(serviceName, appName);
/// <summary>The <c>RestoreLocalFirewallDefaults</c> method restores the local firewall configuration to its default state.</summary>
public static void RestoreLocalFirewallDefaults() => pPol.Item.RestoreLocalFirewallDefaults();
/// <summary>The <c>RestrictService</c> method turns service restriction on or off for a given service.</summary>
/// <param name="serviceName">Name of the service for which service restriction is being turned on or off.</param>
/// <param name="appName">Name of the application for which service restriction is being turned on or off.</param>
/// <param name="restrictService">
/// Indicates whether service restriction is being turned on or off. If this value is <see langword="true"/>, the service will be
/// restricted when sending or receiving network traffic. The Windows Service Hardening rules collection can contain rules which can
/// allow this service specific inbound or outbound network access per specific requirements. If <see langword="false"/>, the service is
/// not restricted by Windows Service Hardening.
/// </param>
/// <param name="serviceSidRestricted">
/// Indicates the type of service SID for the specified service. If this value is <see langword="true"/>, the service SID will be
/// restricted. Otherwise, it will be unrestricted.
/// </param>
/// <remarks>When adding rules, note that there may be a small time lag before the newly-added rule is applied.</remarks>
public static void RestrictService(string serviceName, string appName, bool restrictService, bool serviceSidRestricted) =>
pPol.Item.ServiceRestriction.RestrictService(serviceName, appName, restrictService, serviceSidRestricted);
}
/// <summary>Represents a profile of the Windows Firewall.</summary>
public class FirewallProfile
{
private readonly INetFwPolicy2 iPol;
private readonly NET_FW_PROFILE_TYPE2 type;
/// <summary>Initializes a new instance of the <see cref="FirewallProfile"/> class with the specified type.</summary>
/// <param name="type">The profile type.</param>
public FirewallProfile(FirewallProfileType type) : this(new(), (NET_FW_PROFILE_TYPE2)type) { }
internal FirewallProfile(INetFwPolicy2 item, NET_FW_PROFILE_TYPE2 type)
{
iPol = item;
this.type = type;
}
private FirewallProfile() => throw new NotSupportedException();
/// <summary>
/// <para>Indicates whether the firewall should not allow inbound traffic.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>
/// All interfaces are firewall-enabled. This means that all the exceptions (such as GloballyOpenPorts, Applications, or
/// Services) which are specified in the profile are ignored and only locally-initiated traffic is allowed.
/// </para>
/// </remarks>
public bool BlockAllInboundTraffic { get => iPol.BlockAllInboundTraffic[type]; set => iPol.BlockAllInboundTraffic[type] = value; }
/// <summary>
/// <para>Specifies the default action for inbound traffic. These settings are Block by default.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <value>The default inbound action.</value>
/// <remarks>
/// All interfaces are firewall-enabled. This means that all the exceptions (such as GloballyOpenPorts, Applications, or
/// Services) which are specified in the profile, are ignored And only locally-initiated traffic is allowed.
/// </remarks>
public FirewallAction DefaultInboundAction { get => (FirewallAction)iPol.DefaultInboundAction[type]; set => iPol.DefaultInboundAction[type] = (NET_FW_ACTION)value; }
/// <summary>
/// <para>Specifies the default action for outbound traffic. These settings are Allow by default.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <value>The default outbound action.</value>
/// <remarks>
/// All interfaces are firewall-enabled. This means that all the exceptions (such as GloballyOpenPorts, Applications, or
/// Services) which are specified in the profile are ignored And only locally-initiated traffic is allowed.
/// </remarks>
public FirewallAction DefaultOutboundAction { get => (FirewallAction)iPol.DefaultOutboundAction[type]; set => iPol.DefaultOutboundAction[type] = (NET_FW_ACTION)value; }
/// <summary>
/// <para>Indicates whether a firewall is enabled locally (the effective result may differ due to group policy settings).</para>
/// <para>This property is read/write.</para>
/// </summary>
public bool Enabled { get => iPol.FirewallEnabled[type]; set => iPol.FirewallEnabled[type] = value; }
/// <summary>
/// <para>Specifies a list of interfaces on which firewall settings are excluded.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <value>The excluded interfaces.</value>
/// <remarks>
/// <para>
/// An excluded interface is an interface to which the firewall is not applicable. The firewall is not applicable to any traffic received
/// from or sent to an excluded interface. An empty list indicates that there are no excluded interfaces.
/// </para>
/// </remarks>
public string[]? ExcludedInterfaces
{
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
get => iPol.ExcludedInterfaces[type] is null ? null : (string[])Array.ConvertAll((object[])iPol.ExcludedInterfaces[type], o => o.ToString());
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.
set => iPol.ExcludedInterfaces[type] = value is null || value.Length == 0 ? null : Array.ConvertAll(value, s => (object)s);
}
/// <summary>
/// Indicates whether interactive firewall notifications are disabled.
/// <para>This property is read/write.</para>
/// </summary>
public bool NotificationsDisabled { get => iPol.NotificationsDisabled[type]; set => iPol.NotificationsDisabled[type] = value; }
/// <summary>
/// <para>Indicates whether the firewall should not allow unicast responses to multicast And broadcast traffic.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>If a computer sends a broadcast packet, a unicast response is allowed for three seconds. Use this property to change this behavior.</para>
/// </remarks>
public bool UnicastResponsesToMulticastBroadcastDisabled { get => iPol.UnicastResponsesToMulticastBroadcastDisabled[type]; set => iPol.UnicastResponsesToMulticastBroadcastDisabled[type] = value; }
/// <summary>The <c>IsRuleGroupEnabled</c> method determines whether a specified group of firewall rules are enabled or disabled.</summary>
/// <param name="group">
/// A string that was used to group rules together. It can be the group name or an indirect string to the group name in the form of
/// "@yourresourcedll.dll,-23255". Rules belonging to this group would be queried.
/// </param>
/// <returns>
/// This call returns a boolean enable status which indicates whether the group of rules identified by the group parameter are enabled or
/// disabled. If this value is set to true (VARIANT_TRUE), the group of rules is enabled; otherwise, the group is disabled.
/// </returns>
/// <remarks>
/// When indirect strings in the form of "@yourresourcedll.dll,-23255" are passed as parameters to the Windows Firewall with Advanced
/// Security APIs, they should either be placed under the System32 Windows directory or specified by a full path. Further the file should
/// have a secure access that permits the Local Service account read access to allow the Windows Firewall Service to read the strings. To
/// avoid non-privileged security principals from modifying the strings, the DLLs should only allow write access to the Administrator account.
/// </remarks>
public bool IsRuleGroupEnabled(string group) => iPol.IsRuleGroupEnabled(type, group);
}
/// <summary>Provides access to the properties of a firewall rule.</summary>
/// <remarks>
/// <para>
/// Each time you change a property of a rule, Windows Firewall commits the rule and verifies it for correctness. As a result, when you edit
/// a rule, you must perform the steps in a specific order. For example, if you add an ICMP rule, you must first set the protocol to ICMP,
/// then add the rule. If these steps are taken in the opposite order, an error occurs and the change is lost.
/// </para>
/// <para>
/// If you are editing a TCP port rule and converting it into an ICMP rule, first delete the port, change protocol from TCP to ICMP, and then
/// add the rule.
/// </para>
/// <para>
/// In order to retrieve and modify existing rules, instances of this interface must be retrieved through INetFwRules. All configuration
/// changes take place immediately.
/// </para>
/// <para>When accessing the properties of a rule, keep in mind that there may be a small time lag before a newly-added rule is applied.</para>
/// <para>Properties are used to create firewall rules. Many of the properties can be used in order to create very specific firewall rules.</para>
/// <list type="table">
/// <listheader>
/// <term>Property</term>
/// <term>Type and format</term>
/// <term>Constraints</term>
/// </listheader>
/// <rule>
/// <term>Name</term>
/// <term>Clear text string.</term>
/// <term>Required. The string must not contain a "|" and it must not be "all".</term>
/// </rule><rule>
/// <term>Description</term>
/// <term>Clear text string.</term>
/// <term>Optional. The string must not contain a "|".</term>
/// </rule><rule>
/// <term>Grouping</term>
/// <term>String in the format "@&lt;dll name&gt;, &lt;resource string identifier&gt;".</term>
/// <term>Required.</term>
/// </rule><rule>
/// <term>Enabled</term>
/// <term>Boolean (VARIANT_BOOLEAN).</term>
/// <term>Optional. Defaults to false (VARIANT_FALSE) if nothing is specified.</term>
/// </rule><rule>
/// <term>ApplicationName</term>
/// <term>Clear text string.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>ServiceName</term>
/// <term>Clear text string.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>LocalPorts</term>
/// <term>Clear text string containing a list of port numbers. "RPC" is an acceptable value.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>RemotePorts</term>
/// <term>Clear text string containing a list of port numbers.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>LocalAddresses</term>
/// <term>Clear text string containing a list of IPv4 and IPv6 addresses separated by commas. Range values and"*"are acceptable in this list.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>RemoteAddresses</term>
/// <term>Clear text string containing a list of IPv4 and IPv6 addresses separated by commas. Range values and"*"are acceptable in this list.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>Protocol</term>
/// <term>Number.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>put_Profiles</term>
/// <term>
/// String value in the format "type, code". Multiple types and codes can be included in the string by separating each pair with a ";".
/// </term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>Interfaces</term>
/// <term>Array of strings containing the friendly names of interfaces.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>InterfaceTypes</term>
/// <term>
/// String value. Multiple interface types can be included in the string by separating each value with a ",". Acceptable values are
/// "RemoteAccess", "Wireless", "Lan", and "All".
/// </term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>Direction</term>
/// <term>Enumeration.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>Action</term>
/// <term>Enumeration.</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>EdgeTraversal</term>
/// <term>Boolean (VARIANT_BOOLEAN).</term>
/// <term>Optional.</term>
/// </rule><rule>
/// <term>Profiles</term>
/// <term>Enumeration.</term>
/// <term>Optional.</term>
/// </rule>
/// </list>
/// <para>For additional information on each property, please see the corresponding topic.</para>
/// </remarks>
/// <seealso cref="INamedEntity"/>
public class FirewallRule : INamedEntity, IEquatable<FirewallRule>
{
internal readonly INetFwRule iRule;
/// <summary>Initializes a new instance of the <see cref="FirewallRule"/> class.</summary>
/// <param name="name">The friendly name for the rule.</param>
/// <param name="group">The group name for the rule.</param>
/// <param name="allow">If set to <see langword="true"/>, this is an allow rule; otherwise a blocking rule.</param>
/// <param name="in">If set to <see langword="true"/>, the direction is in; otherwise it is out.</param>
/// <param name="description">The optional description.</param>
public FirewallRule(string name, string? group, bool allow = true, bool @in = true, string? description = null)
{
iRule = new INetFwRule();
Name = name;
Grouping = group;
Action = (FirewallAction)(allow ? NET_FW_ACTION.NET_FW_ACTION_ALLOW : NET_FW_ACTION.NET_FW_ACTION_BLOCK);
Direction = (RuleDirection)(@in ? NET_FW_RULE_DIRECTION.NET_FW_RULE_DIR_IN : NET_FW_RULE_DIRECTION.NET_FW_RULE_DIR_OUT);
Description = description;
}
internal FirewallRule(INetFwRule netFwRule) => iRule = netFwRule;
/// <summary>
/// <para>Specifies the action for a rule or default setting.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public FirewallAction Action { get => (FirewallAction)iRule.Action; set => iRule.Action = (NET_FW_ACTION)value; }
/// <summary>
/// <para>Specifies the friendly name of the application to which this rule applies.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(null)]
public string? ApplicationName { get => iRule.ApplicationName; set => iRule.ApplicationName = value; }
/// <summary>
/// <para>Specifies the description of this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional. The string must not contain the "|" character.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(null)]
public string? Description { get => iRule.Description; set => iRule.Description = value; }
/// <summary>
/// <para>Specifies the direction of traffic for which the rule applies.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional. If this property is not specified, the default value is <c>in</c>.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(NET_FW_RULE_DIRECTION.NET_FW_RULE_DIR_IN)]
public RuleDirection Direction { get => (RuleDirection)iRule.Direction; set => iRule.Direction = (NET_FW_RULE_DIRECTION)value; }
/// <summary>
/// <para>Indicates whether edge traversal is enabled or disabled for this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>
/// The EdgeTraversal property indicates that specific inbound traffic is allowed to tunnel through NATs and other edge devices using the
/// Teredo tunneling technology. In order for this setting to work correctly, the application or service with the inbound firewall rule
/// needs to support IPv6. The primary application of this setting allows listeners on the host to be globally addressable through a
/// Teredo IPv6 address.
/// </para>
/// <para>New rules have the EdgeTraversal property disabled by default.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(false)]
public bool EdgeTraversal { get => iRule.EdgeTraversal; set => iRule.EdgeTraversal = value; }
/// <summary>
/// <para>This property can be used to access the edge properties of a firewall rule defined by NET_FW_EDGE_TRAVERSAL_TYPE.</para>
/// <para>This property is read/write.</para>
/// </summary>
public EdgeTraversalType EdgeTraversalOptions { get => (EdgeTraversalType)iRule2.EdgeTraversalOptions; set => iRule2.EdgeTraversalOptions = (NET_FW_EDGE_TRAVERSAL_TYPE)value; }
/// <summary>
/// <para>Enables or disables a rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional. A new rule is disabled by default.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public bool Enabled { get => iRule.Enabled; set => iRule.Enabled = value; }
/// <summary>
/// <para>Specifies the group to which an individual rule belongs.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>
/// Using the Grouping property is highly recommended, as it groups multiple rules into a single line in the Windows Firewall control
/// panel. This allows the user to enable or disable multiple rules with a single click. The Grouping property can also be specified
/// using indirect strings. In this case, a group description can also be specified that will appear in the rule group properties in the
/// Windows Firewall control panel. For example, if the group string is specified by an indirect string at index 1005
/// ("@yourresources.dll,-1005"), the group description can be specified at a resource string higher by 10000 "@youresources.dll,-11005."
/// </para>
/// <para>
/// When indirect strings in the form of "h" are passed as parameters to the Windows Firewall with Advanced Security APIs, they should
/// either be placed under the System32 Windows directory or specified by a full path. Further, the file should have a secure access that
/// permits the Local Service account read access to allow the Windows Firewall Service to read the strings. To avoid non-privileged
/// security principals from modifying the strings, the DLLs should only allow write access to the Administrator account.
/// </para>
/// </remarks>
public Windows.Shell.IndirectString? Grouping { get => iRule.Grouping is null ? null : new(iRule.Grouping); set => iRule.Grouping = value?.RawValue; }
// <summary> <para>Specifies the list of ICMP types and codes for this rule.</para> <para>This property is read/write.</para> </summary>
// <remarks> <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
// <para> The icmpTypesAndCodes parameter is a list of ICMP types and codes separated by semicolon. "*" indicates all ICMP types and
// codes. </para> </remarks>
public (byte icmpType, ushort icmpCode)[]? IcmpTypesAndCodes
{
get
{
if (iRule.IcmpTypesAndCodes == "*")
{
return null;
}
string[]? s = iRule.IcmpTypesAndCodes.FromDelim(';');
return s is null ? null : Array.ConvertAll(s, StrToPair);
static (byte icmpType, ushort icmpCode) StrToPair(string input)
{
System.Text.RegularExpressions.Match m = System.Text.RegularExpressions.Regex.Match(input, @"(\d{1,3})\:(\d{1,3}|\*)");
return !m.Success
? throw new InvalidCastException($"Unable to parse ICMP type and code: {input}")
: ((byte icmpType, ushort icmpCode))(byte.Parse(m.Groups[1].Value), m.Groups[2].Value == "*" ? (ushort)0x100 : ushort.Parse(m.Groups[2].Value));
}
}
set => iRule.IcmpTypesAndCodes = value is null ? null : Array.ConvertAll(value, p => $"{p.icmpType}:{p.icmpCode}").ToDelim(';');
}
/// <summary>
/// <para>Specifies the list of interfaces for which the rule applies.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional. The interfaces in the list are represented by their friendly name.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public string[]? InterfaceNames
{
#pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type.
get => iRule.Interfaces is null ? null : (string[])Array.ConvertAll((object[])iRule.Interfaces, o => o.ToString());
#pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type.
set => iRule.Interfaces = value is null || value.Length == 0 ? null : Array.ConvertAll(value, s => (object)s);
}
/// <summary>
/// <para>Specifies the list of interfaces for which the rule applies.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional. The interfaces in the list are represented by their friendly name.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public System.Net.NetworkInformation.NetworkInterface[]? Interfaces
{
get
{
if (iRule.Interfaces is null)
{
return null;
}
string[] ifList = iRule.GetInterfaces();
return System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces().Where(i => ifList.Contains(i.Name)).ToArray();
}
set => iRule.Interfaces = value is null || value.Length == 0 ? null : Array.ConvertAll(value, i => (object)i.Name);
}
/// <summary>
/// <para>Specifies the list of interface types for which the rule applies.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(0)]
public RuleInterfaceType InterfaceTypes
{
get => (RuleInterfaceType)Enum.Parse(typeof(RuleInterfaceType), iRule.InterfaceTypes ?? "All");
set => iRule.InterfaceTypes = value.ToString().Replace(" ", "");
}
/// <summary>
/// <para>Specifies the list of local addresses for this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>
/// The localAddrs parameter consists of one or more comma-delimited tokens specifying the local addresses from which the application can
/// listen for traffic. "*" is the default value. Valid tokens include:
/// </para>
/// <list type="bullet"><rule>
/// <term>"*" indicates any local address. If present, this must be the only token included.</term>
/// </rule><rule>
/// <term>"Defaultgateway"</term>
/// </rule><rule>
/// <term>"DHCP"</term>
/// </rule><rule>
/// <term>"WINS"</term>
/// </rule><rule>
/// <term>"LocalSubnet" indicates any local address on the local subnet. This token is not case-sensitive.</term>
/// </rule><rule>
/// <term>
/// A subnet can be specified using either the subnet mask or network prefix notation. If neither a subnet mask not a network prefix is
/// specified, the subnet mask defaults to 255.255.255.255.
/// </term>
/// </rule><rule>
/// <term>A valid IPv6 address.</term>
/// </rule><rule>
/// <term>An IPv4 address range in the format of "start address - end address" with no spaces included.</term>
/// </rule><rule>
/// <term>An IPv6 address range in the format of "start address - end address" with no spaces included.</term>
/// </rule>
/// </list>
/// </remarks>
public string[]? LocalAddresses { get => iRule.LocalAddresses.FromDelim(','); set => iRule.LocalAddresses = value.ToDelim(','); }
/// <summary>
/// <para>
/// Specifies the package identifier or the app container identifier of a process, whether from a Windows Store app or a desktop app.
/// </para>
/// <para>This property is read/write.</para>
/// </summary>
public string LocalAppPackageId { get => iRule3.LocalAppPackageId; set => iRule3.LocalAppPackageId = value; }
/// <summary>
/// <para>Specifies the list of local ports for this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>The Protocol property must be set before the <c>LocalPorts</c> property or an error will be returned.</para>
/// </remarks>
public string[]? LocalPorts { get => iRule.LocalPorts.FromDelim(','); set => iRule.LocalPorts = value.ToDelim(','); }
/// <summary>
/// <para>Specifies a list of authorized local users for an app container.</para>
/// <para>This property is read/write.</para>
/// </summary>
public RawSecurityDescriptor? LocalUserAuthorizedList
{
get => iRule3.LocalUserAuthorizedList.ToSD();
set => iRule3.LocalUserAuthorizedList = value.FromSD();
}
/// <summary>
/// <para>Specifies the user security identifier (SID) of the user who is the owner of the rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// If this rule does not specify <c>localUserConditions</c>, all the traffic that this rule matches must be destined to or originated
/// from this user.
/// </remarks>
public SecurityIdentifier? LocalUserOwner
{
get => iRule3.LocalUserOwner is null ? null : new(iRule3.LocalUserOwner);
set => iRule3.LocalUserOwner = value?.Value;
}
/// <summary>
/// <para>Specifies the friendly name of this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is required. The string must not contain the "|" character.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public string Name { get => iRule.Name; set => iRule.Name = value; }
/// <summary>
/// <para>Specifies the profiles to which the rule belongs.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
[DefaultValue(FirewallProfileType.All)]
public FirewallProfileType Profiles { get => (FirewallProfileType)iRule.Profiles; set => iRule.Profiles = (NET_FW_PROFILE_TYPE2)value; }
/// <summary>
/// <para>Specifies the IP protocol of this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>The <c>Protocol</c> property must be set before the LocalPorts or RemotePorts properties or an error will be returned.</para>
/// <para>A list of protocol numbers is available at the IANA website.</para>
/// </remarks>
[DefaultValue(256)]
public int Protocol { get => iRule.Protocol; set => iRule.Protocol = value; }
/// <summary>
/// <para>Specifies the list of remote addresses for this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>
/// The remoteAddrs parameter consists of one or more comma-delimited tokens specifying the remote addresses from which the application
/// can listen for traffic. The default value is "*". Valid tokens include:
/// </para>
/// <list type="bullet"><rule>
/// <term>"*" indicates any remote address. If present, this must be the only token included.</term>
/// </rule><rule>
/// <term>"Defaultgateway"</term>
/// </rule><rule>
/// <term>"DHCP"</term>
/// </rule><rule>
/// <term>"DNS"</term>
/// </rule><rule>
/// <term>"WINS"</term>
/// </rule><rule>
/// <term>"LocalSubnet" indicates any local address on the local subnet. This token is not case-sensitive.</term>
/// </rule><rule>
/// <term>
/// A subnet can be specified using either the subnet mask or network prefix notation. If neither a subnet mask not a network prefix is
/// specified, the subnet mask defaults to 255.255.255.255.
/// </term>
/// </rule><rule>
/// <term>A valid IPv6 address.</term>
/// </rule><rule>
/// <term>An IPv4 address range in the format of "start address - end address" with no spaces included.</term>
/// </rule><rule>
/// <term>An IPv6 address range in the format of "start address - end address" with no spaces included.</term>
/// </rule>
/// </list>
/// </remarks>
public string[]? RemoteAddresses { get => iRule.RemoteAddresses.FromDelim(','); set => iRule.RemoteAddresses = value.ToDelim(','); }
/// <summary>
/// <para>Specifies a list of remote computers which are authorized to access an app container.</para>
/// <para>This property is read/write.</para>
/// </summary>
public RawSecurityDescriptor? RemoteMachineAuthorizedList { get => iRule3.RemoteMachineAuthorizedList.ToSD(); set => iRule3.RemoteMachineAuthorizedList = value.FromSD(); }
/// <summary>
/// <para>Specifies the list of remote ports for this rule.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>This property is optional.</para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// <para>The Protocol property must be set before the <c>RemotePorts</c> property or an error will be returned.</para>
/// </remarks>
public string[]? RemotePorts { get => iRule.RemotePorts.FromDelim(','); set => iRule.RemotePorts = value.ToDelim(','); }
/// <summary>
/// <para>Specifies a list of remote users who are authorized to access an app container.</para>
/// <para>This property is read/write.</para>
/// </summary>
public RawSecurityDescriptor? RemoteUserAuthorizedList { get => iRule3.RemoteUserAuthorizedList.ToSD(); set => iRule3.RemoteUserAuthorizedList = value.FromSD(); }
/// <summary>
/// <para>
/// Specifies which firewall verifications of security levels provided by IPsec must be guaranteed to allow the collection. The allowed
/// values must correspond to those of the NET_FW_AUTHENTICATE_TYPE enumeration.
/// </para>
/// <para>This property is read/write.</para>
/// </summary>
public FirewallAuthenticateType SecureFlags { get => (FirewallAuthenticateType)iRule3.SecureFlags; set => iRule3.SecureFlags = (NET_FW_AUTHENTICATE_TYPE)value; }
/// <summary>
/// <para>Specifies the service name property of the application.</para>
/// <para>This property is read/write.</para>
/// </summary>
/// <remarks>
/// <para>
/// This property is optional. A serviceName value of "*" indicates that a service, not an application, must be sending or receiving traffic.
/// </para>
/// <para>Also see the restrictions on changing properties described in the Remarks section of the <see cref="FirewallRule"/> class.</para>
/// </remarks>
public string ServiceName { get => iRule.serviceName; set => iRule.serviceName = value; }
private INetFwRule2 iRule2 => iRule as INetFwRule2 ?? throw new NotSupportedException();
private INetFwRule3 iRule3 => iRule as INetFwRule3 ?? throw new NotSupportedException();
/// <inheritdoc/>
public bool Equals(FirewallRule? other) => Equals(other?.Name, Name) &&
Equals(other?.Profiles, Profiles) &&
Equals(other?.Direction, Direction) &&
Equals(other?.Enabled, Enabled) &&
Equals(other?.Action, Action) &&
Equals(other?.ApplicationName, ApplicationName);
}
/// <summary>Represents the rules for the Windows Firewall.</summary>
/// <seealso cref="IReadOnlyList{T}"/>
/// <seealso cref="ICollection{T}"/>
public class FirewallRules : IReadOnlyList<FirewallRule>, ICollection<FirewallRule>
{
private readonly INetFwRules iRules;
internal FirewallRules(INetFwRules rules) => iRules = rules;
private FirewallRules() => throw new NotSupportedException();
/// <inheritdoc/>
public int Count => iRules.Count;
/// <inheritdoc/>
bool ICollection<FirewallRule>.IsReadOnly => false;
/// <summary>Gets the <see cref="FirewallRule"/> with the specified name.</summary>
/// <value>The <see cref="FirewallRule"/>.</value>
/// <param name="name">The name of the rule to retrieve.</param>
/// <returns>The element with the specified name.</returns>
public FirewallRule this[string name] => new(iRules.Item(name));
/// <inheritdoc/>
public FirewallRule this[int index] => this.ToArray()[index];
/// <summary>The <c>Add</c> method adds a new rule to the collection.</summary>
/// <param name="rule">Rule to be added to the collection.</param>
/// <remarks>
/// <para>If a rule with the same rule identifier as the one being submitted already exists, the existing rule is overwritten.</para>
/// <para>Adding a firewall rule with a LocalAppPackageId specified can lead to unexpected behavior and is not supported.</para>
/// </remarks>
public void Add(FirewallRule rule) => iRules.Add(rule.iRule);
/// <inheritdoc/>
public void Clear()
{
IEnumerator enumerator = iRules.GetEnumerator();
while (enumerator.MoveNext())
{
if (enumerator.Current is INetFwRule rule)
iRules.Remove(rule.Name);
}
}
/// <inheritdoc/>
public bool Contains(FirewallRule item) => ((IEnumerable<FirewallRule>)this).Contains(item);
/// <inheritdoc/>
public void CopyTo(FirewallRule[] array, int arrayIndex) => Array.Copy(this.ToArray(), 0, array, arrayIndex, Count);
/// <inheritdoc/>
public IEnumerator<FirewallRule> GetEnumerator()
{
IEnumerator enumerator = iRules.GetEnumerator();
while (enumerator.MoveNext())
{
if (enumerator.Current is INetFwRule rule)
yield return new FirewallRule(rule);
}
}
/// <inheritdoc/>
public int IndexOf(FirewallRule item) => this.Select((value, index) => new { value, index })
.Where(pair => Equals(pair.value, item))
.Select(pair => pair.index + 1)
.FirstOrDefault() - 1;
/// <inheritdoc/>
public bool Remove(FirewallRule item) => Remove(item.Name);
/// <summary>The <c>Remove</c> method removes a rule from the collection.</summary>
/// <param name="name">Name of the rule to remove from the collection.</param>
/// <remarks>If a rule specified by the name parameter does not exist in the collection, the <c>Remove</c> method has no effect.</remarks>
public bool Remove(string name)
{
try { iRules.Remove(name); return true; } catch { return false; }
}
/// <inheritdoc/>
public void RemoveAt(int index) => Remove(this[index].Name);
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
internal static class FwExt
{
public static string[]? FromDelim(this string? list, char sep) => string.IsNullOrEmpty(list) ? null : list?.Split(new char[] { sep }, StringSplitOptions.RemoveEmptyEntries);
public static string? FromSD(this RawSecurityDescriptor? sd) => sd?.GetSddlForm(AccessControlSections.Owner | AccessControlSections.Access);
public static string? ToDelim(this string[]? list, char sep) => list is null || list.Length == 0 ? null : string.Join(sep.ToString(), list);
public static RawSecurityDescriptor? ToSD(this string? sddl) => sddl is null ? null : new(sddl);
}