Use api to create windows firewall rules
This commit is contained in:
parent
91df97aee2
commit
b02f252916
6 changed files with 321 additions and 67 deletions
274
internal/winfw/winfw.go
Normal file
274
internal/winfw/winfw.go
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
// Copyright (c) 2018 Samuel Melrose
|
||||
// SPDX-License-Identifier: MIT
|
||||
// https://github.com/iamacarpet/go-win64api/blob/ef6dbdd6db97301ae08a55eedea773476985a602/firewall.go
|
||||
|
||||
//go:build windows
|
||||
|
||||
package winfw
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
"github.com/scjalliance/comshim"
|
||||
)
|
||||
|
||||
// Firewall related API constants.
|
||||
const (
|
||||
NET_FW_IP_PROTOCOL_TCP = 6
|
||||
NET_FW_IP_PROTOCOL_UDP = 17
|
||||
NET_FW_IP_PROTOCOL_ICMPv4 = 1
|
||||
NET_FW_IP_PROTOCOL_ICMPv6 = 58
|
||||
NET_FW_IP_PROTOCOL_ANY = 256
|
||||
|
||||
NET_FW_RULE_DIR_IN = 1
|
||||
NET_FW_RULE_DIR_OUT = 2
|
||||
|
||||
NET_FW_ACTION_BLOCK = 0
|
||||
NET_FW_ACTION_ALLOW = 1
|
||||
|
||||
// NET_FW_PROFILE2_CURRENT is not real API constant, just helper used in FW functions.
|
||||
// It can mean one profile or multiple (even all) profiles. It depends on which profiles
|
||||
// are currently in use. Every active interface can have it's own profile. F.e.: Public for Wifi,
|
||||
// Domain for VPN, and Private for LAN. All at the same time.
|
||||
NET_FW_PROFILE2_CURRENT = 0
|
||||
NET_FW_PROFILE2_DOMAIN = 1
|
||||
NET_FW_PROFILE2_PRIVATE = 2
|
||||
NET_FW_PROFILE2_PUBLIC = 4
|
||||
NET_FW_PROFILE2_ALL = 2147483647
|
||||
)
|
||||
|
||||
// Firewall Rule Groups
|
||||
// Use this magical strings instead of group names. It will work on all language Windows versions.
|
||||
// You can find more string locations here:
|
||||
// https://windows10dll.nirsoft.net/firewallapi_dll.html
|
||||
const (
|
||||
NET_FW_FILE_AND_PRINTER_SHARING = "@FirewallAPI.dll,-28502"
|
||||
NET_FW_REMOTE_DESKTOP = "@FirewallAPI.dll,-28752"
|
||||
)
|
||||
|
||||
// FWRule represents Firewall Rule.
|
||||
type FWRule struct {
|
||||
Name, Description, ApplicationName, ServiceName string
|
||||
LocalPorts, RemotePorts string
|
||||
// LocalAddresses, RemoteAddresses are always returned with netmask, f.e.:
|
||||
// `10.10.1.1/255.255.255.0`
|
||||
LocalAddresses, RemoteAddresses string
|
||||
// ICMPTypesAndCodes is string. You can find define multiple codes separated by ":" (colon).
|
||||
// Types are listed here:
|
||||
// https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml
|
||||
// So to allow ping set it to:
|
||||
// "0"
|
||||
ICMPTypesAndCodes string
|
||||
Grouping string
|
||||
// InterfaceTypes can be:
|
||||
// "LAN", "Wireless", "RemoteAccess", "All"
|
||||
// You can add multiple deviding with comma:
|
||||
// "LAN, Wireless"
|
||||
InterfaceTypes string
|
||||
Protocol, Direction, Action, Profiles int32
|
||||
Enabled, EdgeTraversal bool
|
||||
}
|
||||
|
||||
// FirewallRuleAddAdvanced allows to modify almost all available FW Rule parameters.
|
||||
// You probably do not want to use this, as function allows to create any rule, even opening all ports
|
||||
// in given profile. So use with caution.
|
||||
func FirewallRuleAddAdvanced(rule FWRule) (bool, error) {
|
||||
return firewallRuleAdd(rule.Name, rule.Description, rule.Grouping, rule.ApplicationName, rule.ServiceName,
|
||||
rule.LocalPorts, rule.RemotePorts, rule.LocalAddresses, rule.RemoteAddresses, rule.ICMPTypesAndCodes,
|
||||
rule.Protocol, rule.Direction, rule.Action, rule.Profiles, rule.Enabled, rule.EdgeTraversal)
|
||||
}
|
||||
|
||||
// firewallRuleAdd is universal function to add all kinds of rules.
|
||||
func firewallRuleAdd(name, description, group, appPath, serviceName, ports, remotePorts, localAddresses, remoteAddresses, icmpTypes string, protocol, direction, action, profile int32, enabled, edgeTraversal bool) (bool, error) {
|
||||
if name == "" {
|
||||
return false, fmt.Errorf("empty FW Rule name, name is mandatory")
|
||||
}
|
||||
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
u, fwPolicy, err := firewallAPIInit()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer firewallAPIRelease(u, fwPolicy)
|
||||
|
||||
if profile == NET_FW_PROFILE2_CURRENT {
|
||||
currentProfiles, err := oleutil.GetProperty(fwPolicy, "CurrentProfileTypes")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get CurrentProfiles: %s", err)
|
||||
}
|
||||
profile = currentProfiles.Value().(int32)
|
||||
}
|
||||
unknownRules, err := oleutil.GetProperty(fwPolicy, "Rules")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get Rules: %s", err)
|
||||
}
|
||||
rules := unknownRules.ToIDispatch()
|
||||
|
||||
if ok, err := FirewallRuleExistsByName(rules, name); err != nil {
|
||||
return false, fmt.Errorf("Error while checking rules for duplicate: %s", err)
|
||||
} else if ok {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
unknown2, err := oleutil.CreateObject("HNetCfg.FWRule")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error creating Rule object: %s", err)
|
||||
}
|
||||
defer unknown2.Release()
|
||||
|
||||
fwRule, err := unknown2.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Error creating Rule object (2): %s", err)
|
||||
}
|
||||
defer fwRule.Release()
|
||||
|
||||
if _, err := oleutil.PutProperty(fwRule, "Name", name); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Name) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Description", description); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Description) of Rule: %s", err)
|
||||
}
|
||||
if appPath != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Applicationname", appPath); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Applicationname) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if serviceName != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "ServiceName", serviceName); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (ServiceName) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if protocol != 0 {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Protocol", protocol); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Protocol) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if icmpTypes != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "IcmpTypesAndCodes", icmpTypes); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (IcmpTypesAndCodes) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if ports != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "LocalPorts", ports); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (LocalPorts) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if remotePorts != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "RemotePorts", remotePorts); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (RemotePorts) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if localAddresses != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "LocalAddresses", localAddresses); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (LocalAddresses) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if remoteAddresses != "" {
|
||||
if _, err := oleutil.PutProperty(fwRule, "RemoteAddresses", remoteAddresses); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (RemoteAddresses) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if direction != 0 {
|
||||
if _, err := oleutil.PutProperty(fwRule, "Direction", direction); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Direction) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Enabled", enabled); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Enabled) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Grouping", group); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Grouping) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Profiles", profile); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Profiles) of Rule: %s", err)
|
||||
}
|
||||
if _, err := oleutil.PutProperty(fwRule, "Action", action); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (Action) of Rule: %s", err)
|
||||
}
|
||||
if edgeTraversal {
|
||||
if _, err := oleutil.PutProperty(fwRule, "EdgeTraversal", edgeTraversal); err != nil {
|
||||
return false, fmt.Errorf("Error setting property (EdgeTraversal) of Rule: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := oleutil.CallMethod(rules, "Add", fwRule); err != nil {
|
||||
return false, fmt.Errorf("Error adding Rule: %s", err)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func FirewallRuleExistsByName(rules *ole.IDispatch, name string) (bool, error) {
|
||||
enumProperty, err := rules.GetProperty("_NewEnum")
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to get enumeration property on Rules: %s", err)
|
||||
}
|
||||
defer enumProperty.Clear()
|
||||
|
||||
enum, err := enumProperty.ToIUnknown().IEnumVARIANT(ole.IID_IEnumVariant)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to cast enum to correct type: %s", err)
|
||||
}
|
||||
if enum == nil {
|
||||
return false, fmt.Errorf("can't get IEnumVARIANT, enum is nil")
|
||||
}
|
||||
|
||||
for itemRaw, length, err := enum.Next(1); length > 0; itemRaw, length, err = enum.Next(1) {
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("Failed to seek next Rule item: %s", err)
|
||||
}
|
||||
|
||||
t, err := func() (bool, error) {
|
||||
item := itemRaw.ToIDispatch()
|
||||
defer item.Release()
|
||||
|
||||
if item, err := oleutil.GetProperty(item, "Name"); err != nil {
|
||||
return false, fmt.Errorf("Failed to get Property (Name) of Rule")
|
||||
} else if item.ToString() == name {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if t {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// firewallAPIInit initialize common fw api.
|
||||
// then:
|
||||
// dispatch firewallAPIRelease(u, fwp)
|
||||
func firewallAPIInit() (*ole.IUnknown, *ole.IDispatch, error) {
|
||||
comshim.Add(1)
|
||||
|
||||
unknown, err := oleutil.CreateObject("HNetCfg.FwPolicy2")
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Failed to create FwPolicy Object: %s", err)
|
||||
}
|
||||
|
||||
fwPolicy, err := unknown.QueryInterface(ole.IID_IDispatch)
|
||||
if err != nil {
|
||||
unknown.Release()
|
||||
return nil, nil, fmt.Errorf("Failed to create FwPolicy Object (2): %s", err)
|
||||
}
|
||||
|
||||
return unknown, fwPolicy, nil
|
||||
}
|
||||
|
||||
// firewallAPIRelease cleans memory.
|
||||
func firewallAPIRelease(u *ole.IUnknown, fwp *ole.IDispatch) {
|
||||
fwp.Release()
|
||||
u.Release()
|
||||
comshim.Done()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue