issue new verdict on configuration change
This commit is contained in:
@@ -182,12 +182,12 @@ func FilterResolvedDNS(
|
||||
return rrCache
|
||||
}
|
||||
|
||||
// Only filter criticial things if request comes from the system resolver.
|
||||
// Only filter critical things if request comes from the system resolver.
|
||||
sysResolver := conn.Process().IsSystemResolver()
|
||||
|
||||
// Filter dns records and return if the query is blocked.
|
||||
rrCache = filterDNSResponse(ctx, conn, layeredProfile, rrCache, sysResolver)
|
||||
if conn.Verdict == network.VerdictBlock {
|
||||
if conn.Verdict.Current == network.VerdictBlock {
|
||||
return rrCache
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
|
||||
}
|
||||
|
||||
// check if the current verdict is already past the inspection criteria.
|
||||
if conn.Verdict > inspectVerdicts[key] {
|
||||
if conn.Verdict.Current > inspectVerdicts[key] {
|
||||
activeInspectors[key] = true
|
||||
continue
|
||||
}
|
||||
@@ -86,11 +86,11 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
|
||||
continueInspection = true
|
||||
case BLOCK_CONN:
|
||||
conn.SetVerdict(network.VerdictBlock, "", "", nil)
|
||||
verdict = conn.Verdict
|
||||
verdict = conn.Verdict.Current
|
||||
activeInspectors[key] = true
|
||||
case DROP_CONN:
|
||||
conn.SetVerdict(network.VerdictDrop, "", "", nil)
|
||||
verdict = conn.Verdict
|
||||
verdict = conn.Verdict.Current
|
||||
activeInspectors[key] = true
|
||||
case STOP_INSPECTING:
|
||||
activeInspectors[key] = true
|
||||
|
||||
@@ -44,18 +44,46 @@ var (
|
||||
ownPID = os.Getpid()
|
||||
)
|
||||
|
||||
const configChangeEvent = "config change"
|
||||
const profileConfigChangeEvent = "profile config change"
|
||||
|
||||
func init() {
|
||||
// TODO: Move interception module to own package (dir).
|
||||
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications")
|
||||
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications", "profiles")
|
||||
|
||||
network.SetDefaultFirewallHandler(defaultHandler)
|
||||
captain.PreConnect = func(ctx context.Context) {
|
||||
interception.CloseAllConnections()
|
||||
}
|
||||
|
||||
// setup event callback when spn has connected
|
||||
captain.ResetConnections = resetAllConnectionsVerdict
|
||||
}
|
||||
|
||||
func interceptionPrep() error {
|
||||
err := interceptionModule.RegisterEventHook(
|
||||
"config",
|
||||
configChangeEvent,
|
||||
"firewall config change event",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnections()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
_ = fmt.Errorf("failed registering event hook: %w", err)
|
||||
}
|
||||
|
||||
err = interceptionModule.RegisterEventHook(
|
||||
"profiles",
|
||||
profileConfigChangeEvent,
|
||||
"firewall config change event",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnections()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
_ = fmt.Errorf("failed registering event hook: %w", err)
|
||||
}
|
||||
|
||||
if err := registerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -63,6 +91,29 @@ func interceptionPrep() error {
|
||||
return prepAPIAuth()
|
||||
}
|
||||
|
||||
func resetAllConnections() {
|
||||
log.Critical("Reseting all connections")
|
||||
log.Info("interception: resetting all connections")
|
||||
err := interception.DeleteAllConnections()
|
||||
if err != nil {
|
||||
log.Criticalf("failed to run ResetAllExternalConnections: %q", err)
|
||||
}
|
||||
for _, id := range network.GetAllIDs() {
|
||||
conn, err := getConnectionByID(id)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !captain.IsExcepted(conn.Entity.IP) {
|
||||
conn.SetFirewallHandler(initialHandler)
|
||||
// Reset entity if it exists.
|
||||
if conn.Entity != nil {
|
||||
conn.Entity.ResetLists()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func interceptionStart() error {
|
||||
getConfig()
|
||||
|
||||
@@ -168,10 +219,32 @@ func getConnection(pkt packet.Packet) (*network.Connection, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func getConnectionByID(id string) (*network.Connection, error) {
|
||||
// Create or get connection in single inflight lock in order to prevent duplicates.
|
||||
connPtr, _, _ := getConnectionSingleInflight.Do(id, func() (interface{}, error) {
|
||||
// First, check for an existing connection.
|
||||
conn, ok := network.GetConnection(id)
|
||||
if ok {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Else return nil
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
if connPtr == nil {
|
||||
return nil, errors.New("connection does not exist")
|
||||
}
|
||||
|
||||
connection := connPtr.(*network.Connection)
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
func resetAllConnectionsVerdict(ctx context.Context) {
|
||||
resetAllConnections()
|
||||
// interception.CloseAllConnections()
|
||||
network.ClearConnections()
|
||||
log.Critical("Clearing connections")
|
||||
// network.ClearConnections()
|
||||
// log.Critical("Clearing connections")
|
||||
// interception.CloseAllConnections()
|
||||
// ids := network.GetAllIDs()
|
||||
// for _, id := range ids {
|
||||
@@ -389,7 +462,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
conn.Entity.IPScope == netutils.LocalMulticast):
|
||||
|
||||
// Reroute rogue dns queries back to Portmaster.
|
||||
conn.Verdict = network.VerdictRerouteToNameserver
|
||||
conn.SetVerdictDirectly(network.VerdictRerouteToNameserver)
|
||||
conn.Reason.Msg = "redirecting rogue dns query"
|
||||
conn.Internal = true
|
||||
// End directly, as no other processing is necessary.
|
||||
@@ -424,6 +497,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// Check if connection should be tunneled.
|
||||
checkTunneling(pkt.Ctx(), conn, pkt)
|
||||
|
||||
updateVerdictBasedOnPreviousState(conn, pkt)
|
||||
|
||||
switch {
|
||||
case conn.Inspecting:
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
|
||||
@@ -462,8 +537,8 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
|
||||
}
|
||||
|
||||
// do not allow to circumvent decision: e.g. to ACCEPT packets from a DROP-ed connection
|
||||
if verdict < conn.Verdict {
|
||||
verdict = conn.Verdict
|
||||
if verdict < conn.Verdict.Current {
|
||||
verdict = conn.Verdict.Current
|
||||
}
|
||||
|
||||
var err error
|
||||
@@ -509,6 +584,18 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V
|
||||
}
|
||||
}
|
||||
|
||||
func updateVerdictBasedOnPreviousState(conn *network.Connection, pkt packet.Packet) {
|
||||
if conn.Verdict.Current == network.VerdictAccept {
|
||||
if conn.Verdict.Previous == network.VerdictRerouteToTunnel && !conn.Tunneled {
|
||||
conn.SetVerdictDirectly(network.VerdictBlock)
|
||||
} else if conn.Verdict.Previous == network.VerdictAccept && conn.Tunneled {
|
||||
conn.SetVerdictDirectly(network.VerdictBlock)
|
||||
} else if conn.Tunneled {
|
||||
conn.SetVerdictDirectly(network.VerdictRerouteToTunnel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// func tunnelHandler(pkt packet.Packet) {
|
||||
// tunnelInfo := GetTunnelInfo(pkt.Info().Dst)
|
||||
// if tunnelInfo == nil {
|
||||
|
||||
95
firewall/interception/connection_manager_linux.go
Normal file
95
firewall/interception/connection_manager_linux.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package interception
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
ct "github.com/florianl/go-conntrack"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/firewall/interception/nfq"
|
||||
)
|
||||
|
||||
// CloseAllConnections closes all active connection on conntrack.
|
||||
func CloseAllConnections() error {
|
||||
nfct, err := ct.Open(&ct.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = nfct.Close() }()
|
||||
|
||||
connections, err := nfct.Dump(ct.Conntrack, ct.IPv4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Criticalf("Number of connections: %d", len(connections))
|
||||
for _, connection := range connections {
|
||||
fmt.Printf("[%2d] %s - %s\n", connection.Origin.Proto.Number, connection.Origin.Src, connection.Origin.Dst)
|
||||
err := nfct.Delete(ct.Conntrack, ct.IPv4, connection)
|
||||
log.Errorf("Error deleting connection %q", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteAllConnections deletes all entries from conntrack table.
|
||||
func DeleteAllConnections() error {
|
||||
nfct, err := ct.Open(&ct.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = nfct.Close() }()
|
||||
|
||||
connections, err := getAllPermanentConnections(nfct)
|
||||
|
||||
for _, connection := range connections {
|
||||
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteConnection deletes a specific connection.
|
||||
func DeleteConnection(sourceIP net.IP, sourcePort uint16, destinationIP net.IP, destinationPort uint16) error {
|
||||
nfct, err := ct.Open(&ct.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = nfct.Close() }()
|
||||
|
||||
filter := &ct.IPTuple{Src: &sourceIP, Dst: &destinationIP, Proto: &ct.ProtoTuple{SrcPort: &sourcePort, DstPort: &destinationPort}}
|
||||
connectionFilter := ct.Con{
|
||||
Origin: filter,
|
||||
}
|
||||
|
||||
connections, _ := nfct.Get(ct.Conntrack, ct.IPv4, connectionFilter)
|
||||
for _, connection := range connections {
|
||||
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
|
||||
}
|
||||
|
||||
connectionFilter.Origin = nil
|
||||
connectionFilter.Reply = filter
|
||||
connections, err = nfct.Get(ct.Conntrack, ct.IPv4, connectionFilter)
|
||||
for _, connection := range connections {
|
||||
_ = nfct.Delete(ct.Conntrack, ct.IPv4, connection)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func getAllPermanentConnections(nfct *ct.Nfct) ([]ct.Con, error) {
|
||||
permanentFlags := []uint32{nfq.MarkAccept, nfq.MarkBlock, nfq.MarkDrop, nfq.MarkAcceptAlways, nfq.MarkBlockAlways, nfq.MarkDropAlways, nfq.MarkRerouteSPN}
|
||||
filter := ct.FilterAttr{}
|
||||
filter.MarkMask = []byte{0xFF, 0xFF, 0xFF, 0xFF}
|
||||
filter.Mark = []byte{0x00, 0x00, 0x00, 0x00} // 4 zeros starting value
|
||||
connections := make([]ct.Con, 0)
|
||||
for _, mark := range permanentFlags {
|
||||
binary.BigEndian.PutUint32(filter.Mark, mark) // Little endian is in reverse not sure why. BigEndian makes it in correct order.
|
||||
currentConnections, err := nfct.Query(ct.Conntrack, ct.IPv4, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
connections = append(connections, currentConnections...)
|
||||
}
|
||||
return connections, nil
|
||||
}
|
||||
@@ -2,9 +2,6 @@ package interception
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
ct "github.com/florianl/go-conntrack"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
@@ -52,24 +49,3 @@ func Stop() error {
|
||||
|
||||
return stop()
|
||||
}
|
||||
|
||||
func CloseAllConnections() error {
|
||||
nfct, err := ct.Open(&ct.Config{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = nfct.Close() }()
|
||||
|
||||
connections, err := nfct.Dump(ct.Conntrack, ct.IPv4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Criticalf("Number of connections: %d", len(connections))
|
||||
for _, connection := range connections {
|
||||
fmt.Printf("[%2d] %s - %s\n", connection.Origin.Proto.Number, connection.Origin.Src, connection.Origin.Dst)
|
||||
err := nfct.Delete(ct.Conntrack, ct.IPv4, connection)
|
||||
log.Errorf("Error deleting connection %q", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||
|
||||
// Reset verdict for connection.
|
||||
log.Tracer(ctx).Infof("filter: re-evaluating verdict on %s", conn)
|
||||
conn.Verdict = network.VerdictUndecided
|
||||
// conn.SetVerdictDirectly(network.VerdictUndecided)
|
||||
|
||||
// Reset entity if it exists.
|
||||
if conn.Entity != nil {
|
||||
|
||||
@@ -28,7 +28,7 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa
|
||||
case conn.Inbound:
|
||||
// Can't tunnel incoming connections.
|
||||
return
|
||||
case conn.Verdict != network.VerdictAccept:
|
||||
case conn.Verdict.Current != network.VerdictAccept:
|
||||
// Connection will be blocked.
|
||||
return
|
||||
case conn.Process().Pid == ownPID:
|
||||
@@ -156,7 +156,7 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa
|
||||
conn.Failed("failed to request tunneling", "")
|
||||
} else {
|
||||
//log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested")
|
||||
conn.Verdict = network.VerdictRerouteToTunnel
|
||||
//conn.SetVerdictDirectly(network.VerdictRerouteToTunnel)
|
||||
conn.Tunneled = true
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user