using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Net.Sockets;
using System.Security.Principal;
using System.Windows.Forms;
using NetFwTypeLib;
using NETCONLib;
namespace MSR.LST.Net
{
///
/// WARNING: This class does not inform the user that the firewall punchthrough is being added. Applications
/// should always inform the user when adding punchthroughs to the firewall, for security reasons.
///
public abstract class FirewallUtility
{
#region Constants
// Constants for accessing the SP2 firewall through COM
const string INetFwMgrGuid = "{304CE942-6E39-40D8-943A-B913C40C9CD4}";
const string INetFwAuthorizedApplicationGuid = "{EC9846B3-2762-4A6B-A214-6ACB603462D2}";
const string INetFwOpenPortGuid = "{0CA545C6-37AD-4A6C-BF92-9F7610067EF5}";
// Constants from the Win32 API for the Internet Connection Firewall
const byte NAT_PROTOCOL_TCP = 6;
const byte NAT_PROTOCOL_UDP = 17;
#endregion
#region Any-Version Firewall Methods
///
/// Adds an exception(s) to either a 2003 or XP SP2 firewall.
///
/// The name of the exception to be added for both types of firewalls.
/// The ports to be added to Win2003 firewalls.
/// An array of ProtocolTypes for each port.
/// The path of the application being added (for new, SP2 firewalls).
public static void AddFirewallException (string exceptionName, ushort[] ports, ProtocolType[] protocols, string appPath)
{
if (HasVistaFirewall) {
AddAppToVistaFirewall(exceptionName, appPath);
}
else if (HasSp2Firewall)
{
if (Sp2FirewallEnabled)
{
AddAppToSP2Firewall(exceptionName, appPath, "*", true);
}
}
else
{
if (ports.Length != protocols.Length)
throw new ArgumentException(Strings.PortsAndPortTypesSameLength);
for (int cnt = 0; cnt < ports.Length; ++cnt)
{
AddOldFirewallPort(exceptionName, ports[cnt], protocols[cnt]);
}
}
}
///
/// Removes an exception(s) from firewall.
///
/// The name of the exception to be removed for any type of firewall.
/// The ports to be removed to Win2003 firewalls.
/// An array of ProtocolTypes for each port.
/// The path of the application being removed (for new, SP2 firewalls).
public static void RemoveFirewallException (string exceptionName, ushort[] ports, ProtocolType[] protocols, string appPath)
{
if (HasVistaFirewall) {
RemoveAppFromVistaFirewall(exceptionName);
}
else if (HasSp2Firewall)
{
if (Sp2FirewallEnabled)
{
RemoveAppFromSP2Firewall(appPath);
}
}
else
{
if (ports.Length != protocols.Length)
throw new ArgumentException(Strings.PortsAndPortTypesSameLength);
for (int cnt = 0; cnt < ports.Length; ++cnt)
{
RemoveOldFirewallPort(exceptionName, ports[cnt], protocols[cnt]);
}
}
}
#endregion
#region Vista and later Firewall Methods
///
/// Add the exception rule for all profiles and all protocols.
///
///
///
/// false if the firewall type isn't available, true for successful completion.
public static bool AddAppToVistaFirewall(string ruleName, string appPath) {
Type fwPolicy2Type = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
if (fwPolicy2Type == null) {
return false;
}
INetFwPolicy2 fwPolicy2 = (INetFwPolicy2)System.Activator.CreateInstance(fwPolicy2Type);
INetFwRule fwRule = (INetFwRule)System.Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwRule"));
fwRule.Name = ruleName;
fwRule.ApplicationName = appPath;
fwRule.Enabled = true;
fwPolicy2.Rules.Add(fwRule);
return true;
}
///
/// Add a ports/protocol exception rule for all profiles for Vista or later firewall
///
///
///
/// false if the firewall type isn't available, true for successful completion.
public static bool AddPortExceptionToVistaFirewall(string ruleName, string ports, NET_FW_IP_PROTOCOL_ protocol) {
Type fwPolicy2Type = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
if (fwPolicy2Type == null) {
return false;
}
INetFwPolicy2 fwPolicy2 = (INetFwPolicy2)System.Activator.CreateInstance(fwPolicy2Type);
INetFwRule fwRule = (INetFwRule)System.Activator.CreateInstance(Type.GetTypeFromProgID("HNetCfg.FwRule"));
fwRule.Name = ruleName;
fwRule.Protocol = (int)protocol;
fwRule.LocalPorts = ports;
fwRule.Enabled = true;
fwPolicy2.Rules.Add(fwRule);
return true;
}
public static void RemoveAppFromVistaFirewall(string ruleName) {
Type fwPolicy2Type = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
if (fwPolicy2Type == null) {
return;
}
INetFwPolicy2 fwPolicy2 = (INetFwPolicy2)System.Activator.CreateInstance(fwPolicy2Type);
fwPolicy2.Rules.Remove(ruleName);
}
public static bool HasVistaFirewall {
get {
try {
Type fwPolicy2Type = Type.GetTypeFromProgID("HNetCfg.FwPolicy2");
if (fwPolicy2Type == null) {
return false;
}
INetFwPolicy2 fwPolicy2 = (INetFwPolicy2)System.Activator.CreateInstance(fwPolicy2Type);
}
catch {
return false;
}
return true;
}
}
#endregion Vista and later Firewall Methods
#region XP SP2-specific Firewall Methods
///
/// Attempts to determine if the computer has a Windows XP SP2 compatible firewall.
///
public static bool HasSp2Firewall
{
get
{
try
{
INetFwMgr fwMgr = (INetFwMgr) Activator.CreateInstance(
Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
INetFwAuthorizedApplication fwAA = (INetFwAuthorizedApplication) Activator.CreateInstance(
Type.GetTypeFromCLSID(new Guid(INetFwAuthorizedApplicationGuid)), true);
}
catch
{
return false;
}
return true;
}
}
///
/// Returns true if the comptuer's SP2 firewall is enabled.
///
///
/// Throws if this computer does not have an SP2 firewall.
///
public static bool Sp2FirewallEnabled
{
get
{
try
{
INetFwMgr fwMgr = (INetFwMgr) Activator.CreateInstance(
Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
return fwMgr.LocalPolicy.CurrentProfile.FirewallEnabled;
}
catch (System.Runtime.InteropServices.COMException)
{
// This exception occurrs on a machine not running the service for the firewall, but only on
// machines that have the firewall installed. Therefore, the firewall is installed but not
// enabled, so we just return false. This happens by default after you install 2003 SP1 (bug?).
// Expected exception msg: "There are no more endpoints available from the endpoint mapper"...
return false;
}
}
}
///
/// Adds an application with specified parameters to a XP SP2-compatible firewall exception list.
///
/// Title of the rule
/// Full path of the image
/// Space seperated network addresses permitted to access the application
/// (e.g. "LocalSubnet", "*", "192.168.10.0/255.255.255.0")
/// If the exception rule should be enabled
///
/// WARNING: This method does not inform the user that the firewall punchthrough is being added. Applications
/// should always inform the user when adding punchthroughs to the firewall, for security reasons.
///
public static void AddAppToSP2Firewall(String name, String imageName, String strLocalSubnet, bool enabled)
{
// Instantiating the HNetCfg.NetFwMgr object to get "LocalPolicy" and then "CurrentProfile"
INetFwMgr fwMgr = (INetFwMgr) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
INetFwPolicy fwPolicy = fwMgr.LocalPolicy;
INetFwProfile fwProfile = fwPolicy.CurrentProfile;
// Checking got skipped since the entry gets update if exist and inserted if not
// (No check necessary); Check if the entry already exists. "System.IO.FileNotFoundException"
// will be thrown if entry doesn't exist.
// fwAA = fwProfile.AuthorizedApplications.Item(imageName);
// Instantiating the HNetCfg.NetFwAuthorizedApplication object
INetFwAuthorizedApplication fwAA = (INetFwAuthorizedApplication) Activator.CreateInstance(
Type.GetTypeFromCLSID(new Guid(INetFwAuthorizedApplicationGuid)), true);
// Assigning values to the AuthorizedApplication to be added to the firewall permission list.
// Make this entry Enabled/Disabled
fwAA.Enabled = enabled;
// The friendly name for this "Exception" rule
fwAA.Name = name;
// Whether only the local subnet can access this application or not
fwAA.RemoteAddresses = strLocalSubnet;
// The image name full path
fwAA.ProcessImageFileName = imageName;
// Adding AuthorizedApplication to the Exception List
fwProfile.AuthorizedApplications.Add(fwAA);
}
///
/// Removes an application from an XP SP2-compatible firewall exception list.
///
/// Full name of image to be removed from FW exception list
///
/// WARNING: This method does not inform the user that the firewall punchthrough is being added. Applications
/// should always inform the user when adding punchthroughs to the firewall, for security reasons.
///
public static void RemoveAppFromSP2Firewall(String imageName)
{
// Instantiating the HNetCfg.NetFwMgr object to get "LocalPolicy" and then "CurrentProfile"
INetFwMgr fwMgr = (INetFwMgr) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
INetFwPolicy fwPolicy = fwMgr.LocalPolicy;
INetFwProfile fwProfile = fwPolicy.CurrentProfile;
// Remove application from exception rule list
fwProfile.AuthorizedApplications.Remove(imageName);
}
public static void AddPortExceptionToSP2Firewall(string name, int port, NET_FW_IP_PROTOCOL_ protocol) {
// Instantiating the HNetCfg.NetFwMgr object to get "LocalPolicy" and then "CurrentProfile"
INetFwMgr fwMgr = (INetFwMgr)Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
INetFwPolicy fwPolicy = fwMgr.LocalPolicy;
INetFwProfile fwProfile = fwPolicy.CurrentProfile;
INetFwOpenPort fwOpenPort = (INetFwOpenPort)Activator.CreateInstance(
Type.GetTypeFromCLSID(new Guid(INetFwOpenPortGuid)), true);
fwOpenPort.Name = name;
fwOpenPort.Port = port;
fwOpenPort.Protocol = protocol;
fwOpenPort.Enabled = true;
fwProfile.GloballyOpenPorts.Add(fwOpenPort);
}
public static void RemovePortExceptionFromSP2Firewall(int port, NET_FW_IP_PROTOCOL_ protocol) {
// Instantiating the HNetCfg.NetFwMgr object to get "LocalPolicy" and then "CurrentProfile"
INetFwMgr fwMgr = (INetFwMgr) Activator.CreateInstance(Type.GetTypeFromCLSID(new Guid(INetFwMgrGuid)), true);
INetFwPolicy fwPolicy = fwMgr.LocalPolicy;
INetFwProfile fwProfile = fwPolicy.CurrentProfile;
// Remove application from exception rule list
fwProfile.GloballyOpenPorts.Remove(port, protocol);
}
#endregion
#region Pre-SP2 or 2003 Firewall Methods
///
/// Returns true if this computer has a Pre-SP2 firewall enabled on any connection.
///
///
/// This value may or may not be true, even if the computer has an SP2 firewall. If this
/// computer has an SP2-compatible firewall, this value should be ignored.
///
public static bool OldFirewallEnabled
{
get
{
INetSharingManager mgr = new NetSharingManagerClass();
// Iterate through all of the available connections
foreach(INetConnection iCon in mgr.EnumEveryConnection)
{
INetSharingConfiguration iShareConfig = mgr.get_INetSharingConfigurationForINetConnection(iCon);
if( iShareConfig.InternetFirewallEnabled ) // skip this connection if the firewall is disabled
{
return true;
}
}
return false;
}
}
///
/// Checks to find whether the Windows XP/2003 Firewall is enabled on adapters and if so it opens ports.
///
/// Name of the Port Map to look for
/// Port Number
/// true if TCP, false if UDP
///
/// WARNING: This method does not inform the user that the firewall punchthrough is being added. Applications
/// should always inform the user when adding punchthroughs to the firewall, for security reasons.
///
public static void AddOldFirewallPort(string portMapName, ushort port, ProtocolType protocol)
{
ValidateForOldCompatibleFirewall();
ValidateAdministrator();
// Get the protocolAsByte ICF constant
byte protocolAsByte = ConvertAndValidateProtocol(protocol);
INetSharingManager mgr = new NetSharingManagerClass();
// Iterate through all of the available connections
foreach(INetConnection iCon in mgr.EnumEveryConnection)
{
INetSharingConfiguration iShareConfig = mgr.get_INetSharingConfigurationForINetConnection(iCon);
if( iShareConfig.InternetFirewallEnabled ) // skip this connection if the firewall is disabled
{
// Make sure that this firewall doesn't already have a port map for the same port
bool portMapExists = false;
foreach(INetSharingPortMapping portMap in iShareConfig.get_EnumPortMappings(tagSHARINGCONNECTION_ENUM_FLAGS.ICSSC_ENABLED))
{
if ((ushort)(portMap.Properties.ExternalPort) == port && portMap.Properties.IPProtocol == protocolAsByte)
{
portMapExists = true;
break;
}
}
if (!portMapExists)
{
// Finally, add & enable the new port map
INetSharingPortMapping newPortMap = iShareConfig.AddPortMapping(portMapName, protocolAsByte, port, port, 0, SystemInformation.ComputerName, tagICS_TARGETTYPE.ICSTT_NAME);
newPortMap.Enable();
}
}
}
}
///
/// Checks to find whether the Windows XP/2003 Firewall is enabled on adapters and if so it opens ports.
///
/// Name of the Port Map to look for (must be the same as when it was added)
/// Port Number
/// true if TCP, false if UDP
///
/// WARNING: This method does not inform the user that the firewall punchthrough is being added. Applications
/// should always inform the user when adding punchthroughs to the firewall, for security reasons.
///
public static void RemoveOldFirewallPort(string portMapName, ushort port, ProtocolType protocol)
{
ValidateForOldCompatibleFirewall();
ValidateAdministrator();
byte protocolAsByte = ConvertAndValidateProtocol(protocol);
INetSharingManager mgr = new NetSharingManagerClass();
// Iterate through all of the available connections
foreach(INetConnection iCon in mgr.EnumEveryConnection)
{
INetSharingConfiguration iShareConfig = mgr.get_INetSharingConfigurationForINetConnection(iCon);
if( iShareConfig.InternetFirewallEnabled ) // skip this connection if the firewall is disabled
{
foreach(INetSharingPortMapping portMap in iShareConfig.get_EnumPortMappings(tagSHARINGCONNECTION_ENUM_FLAGS.ICSSC_ENABLED))
{
// Remove this port mapping only if the name & port match
if ((ushort)(portMap.Properties.ExternalPort) == port && portMap.Properties.IPProtocol == protocolAsByte)
{
if (String.Compare(portMap.Properties.Name, portMapName) == 0)
{
iShareConfig.RemovePortMapping(portMap);
}
}
}
}
}
}
#endregion
#region Private Helper Methods
private static void ValidateForOldCompatibleFirewall()
{
// Check the OS version and return if not Windows XP or greater
OperatingSystem os = System.Environment.OSVersion;
if (os.Version.Major < 5) // earlier than Win2K
throw new NotSupportedException(Strings.OSMustBeWindowsXPSP2OrGreater);
if (os.Version.Major == 5 && os.Version.Minor < 1) // Win2K but not XP
throw new NotSupportedException(Strings.OSMustBeWindowsXPSP2OrGreater);
if( HasSp2Firewall ) // Skip computers with firewall == SP2's firewall (i.e. NOT XP pre-SP2 & Win2K3)
throw new NotSupportedException(Strings.DoNotCallAddPortToOldFirewall);
}
private static void ValidateAdministrator()
{
// Check to make sure we're in an Administrator role, as Limited accounts don't have access to read or write the Firewall settings
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
if (wp.IsInRole(WindowsBuiltInRole.Administrator) == false)
throw new InvalidOperationException(Strings.YouMustBeAnAdministratorToModify);
}
private static byte ConvertAndValidateProtocol(ProtocolType protocol)
{
switch(protocol)
{
case ProtocolType.Tcp:
return FirewallUtility.NAT_PROTOCOL_TCP;
case ProtocolType.Udp:
return FirewallUtility.NAT_PROTOCOL_UDP;
default:
throw new NotSupportedException(Strings.ProtocolIsNotSupported);
}
}
#endregion
}
}