diff --git a/firewall/interception.go b/firewall/interception.go index e5be02dc..706111b4 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -177,7 +177,9 @@ func interceptionStart() error { interceptionModule.StartWorker("stat logger", statLogger) interceptionModule.StartWorker("packet handler", packetHandler) - return interception.Start() + err := interception.Start() + + return err } func interceptionStop() error { diff --git a/firewall/interception/interception_windows.go b/firewall/interception/interception_windows.go index 0441d3a6..d1931fb8 100644 --- a/firewall/interception/interception_windows.go +++ b/firewall/interception/interception_windows.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/safing/portmaster/firewall/interception/windowskext" + "github.com/safing/portmaster/network" "github.com/safing/portmaster/network/packet" "github.com/safing/portmaster/updates" ) @@ -39,3 +40,16 @@ func stop() error { func ResetVerdictOfAllConnections() error { return windowskext.ClearCache() } + +func UpdateVerdictOfConnection(conn *network.Connection) error { + return windowskext.UpdateVerdict(conn) +} + +func GetVersion() (string, error) { + version, err := windowskext.GetVersion() + if err != nil { + return "", err + } + + return version.String(), nil +} diff --git a/firewall/interception/windowskext/handler.go b/firewall/interception/windowskext/handler.go index a19cbfdc..2b0e6e5c 100644 --- a/firewall/interception/windowskext/handler.go +++ b/firewall/interception/windowskext/handler.go @@ -6,6 +6,7 @@ package windowskext import ( "encoding/binary" "errors" + "fmt" "net" "github.com/tevino/abool" @@ -50,6 +51,23 @@ type VerdictInfo struct { verdict network.Verdict // verdict for the connection } +type VerdictUpdateInfo struct { + ipV6 uint8 //True: IPv6, False: IPv4 + protocol uint8 //Protocol (UDP, TCP, ...) + localIP [4]uint32 //Source Address, only srcIP[0] if IPv4 + remoteIP [4]uint32 //Destination Address + localPort uint16 //Source Port + remotePort uint16 //Destination port + verdict uint8 //New verdict +} + +type VersionInfo struct { + major uint8 + minor uint8 + revision uint8 + build uint8 +} + // Handler transforms received packets to the Packet interface. func Handler(packets chan packet.Packet) { if !ready.IsSet() { @@ -152,3 +170,7 @@ func convertIPv6(input [4]uint32) net.IP { } return net.IP(addressBuf) } + +func (v *VersionInfo) String() string { + return fmt.Sprintf("%d.%d.%d.%d", v.major, v.minor, v.revision, v.build) +} diff --git a/firewall/interception/windowskext/kext.go b/firewall/interception/windowskext/kext.go index ca8315e6..5f7642df 100644 --- a/firewall/interception/windowskext/kext.go +++ b/firewall/interception/windowskext/kext.go @@ -226,6 +226,61 @@ func ClearCache() error { return err } +func UpdateVerdict(conn *network.Connection) error { + kextLock.RLock() + defer kextLock.RUnlock() + + // Check if driver is initialized + if !ready.IsSet() { + log.Error("kext: failed to clear the cache: kext not ready") + return ErrKextNotReady + } + + // initialize variables + info := &VerdictUpdateInfo{ + ipV6: uint8(conn.IPVersion), + protocol: uint8(conn.IPProtocol), + localPort: conn.LocalPort, + remotePort: conn.Entity.Port, + verdict: uint8(conn.Verdict.Active), + } + + // copy ip addresses + copy(asByteArray(&info.localIP[0]), conn.LocalIP) + copy(asByteArray(&info.remoteIP[0]), conn.Entity.IP) + + // Make driver request + data := asByteArray(&info) + err := deviceIoControlDirect(kextHandle, IOCTL_UPDATE_VERDICT, data) + return err +} + +func GetVersion() (*VersionInfo, error) { + kextLock.RLock() + defer kextLock.RUnlock() + + // Check if driver is initialized + if !ready.IsSet() { + log.Error("kext: failed to clear the cache: kext not ready") + return nil, ErrKextNotReady + } + + data := make([]uint8, 4) + err := deviceIoControlDirect(kextHandle, IOCTL_VERSION, data) + + if err != nil { + return nil, err + } + + version := &VersionInfo{ + major: data[0], + minor: data[1], + revision: data[2], + build: data[3], + } + return version, nil +} + func asByteArray[T any](obj *T) []byte { return unsafe.Slice((*byte)(unsafe.Pointer(obj)), unsafe.Sizeof(*obj)) } diff --git a/firewall/interception/windowskext/syscall.go b/firewall/interception/windowskext/syscall.go index 6ec11348..7a06cd41 100644 --- a/firewall/interception/windowskext/syscall.go +++ b/firewall/interception/windowskext/syscall.go @@ -15,12 +15,13 @@ const ( ) var ( - IOCTL_HELLO = ctlCode(SIOCTL_TYPE, 0x800, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) - IOCTL_RECV_VERDICT_REQ_POLL = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) + IOCTL_VERSION = ctlCode(SIOCTL_TYPE, 0x800, METHOD_NEITHER, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) + IOCTL_RECV_VERDICT_REQ_POLL = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) // Not used IOCTL_RECV_VERDICT_REQ = ctlCode(SIOCTL_TYPE, 0x802, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_SET_VERDICT = ctlCode(SIOCTL_TYPE, 0x803, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_GET_PAYLOAD = ctlCode(SIOCTL_TYPE, 0x804, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) IOCTL_CLEAR_CACHE = ctlCode(SIOCTL_TYPE, 0x805, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) + IOCTL_UPDATE_VERDICT = ctlCode(SIOCTL_TYPE, 0x806, METHOD_NEITHER, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA) ) func ctlCode(device_type, function, method, access uint32) uint32 { @@ -89,3 +90,21 @@ func deviceIoControlReadWrite(handle windows.Handle, code uint32, inData []byte, return bytesReturned, err } + +// Use for METHOD_NEITHER IOCTL, the data buffer is passed directly to the kernel +func deviceIoControlDirect(handle windows.Handle, code uint32, data []byte) error { + var dataPtr *byte = nil + var dataSize uint32 = 0 + if data != nil { + dataPtr = &data[0] + dataSize = uint32(len(data)) + } + + err := windows.DeviceIoControl(handle, + code, + dataPtr, dataSize, + nil, 0, + nil, nil) + + return err +}