issue new verdict on configuration change

This commit is contained in:
Vladimir Stoilov
2022-09-05 17:14:45 +02:00
committed by Daniel
parent 0423dfbbbf
commit fdc8ef5698
18 changed files with 298 additions and 83 deletions

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

View 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
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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
}
}