mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Improve decision tree, move resolver scope checking behind endpoint list rules
This commit is contained in:
parent
d626cea102
commit
6f9d17bba2
1 changed files with 82 additions and 82 deletions
|
@ -37,7 +37,7 @@ import (
|
||||||
|
|
||||||
const noReasonOptionKey = ""
|
const noReasonOptionKey = ""
|
||||||
|
|
||||||
type deciderFn func(context.Context, *network.Connection, packet.Packet) bool
|
type deciderFn func(context.Context, *network.Connection, *profile.LayeredProfile, packet.Packet) bool
|
||||||
|
|
||||||
var defaultDeciders = []deciderFn{
|
var defaultDeciders = []deciderFn{
|
||||||
checkPortmasterConnection,
|
checkPortmasterConnection,
|
||||||
|
@ -45,6 +45,7 @@ var defaultDeciders = []deciderFn{
|
||||||
checkConnectionType,
|
checkConnectionType,
|
||||||
checkConnectionScope,
|
checkConnectionScope,
|
||||||
checkEndpointLists,
|
checkEndpointLists,
|
||||||
|
checkResolverScope,
|
||||||
checkConnectivityDomain,
|
checkConnectivityDomain,
|
||||||
checkBypassPrevention,
|
checkBypassPrevention,
|
||||||
checkFilterLists,
|
checkFilterLists,
|
||||||
|
@ -97,7 +98,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||||
// connection is then blocked when the original requesting process is known.
|
// connection is then blocked when the original requesting process is known.
|
||||||
if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() {
|
if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() {
|
||||||
// Run all deciders and return if they came to a conclusion.
|
// Run all deciders and return if they came to a conclusion.
|
||||||
done, _ := runDeciders(ctx, dnsFromSystemResolverDeciders, conn, pkt)
|
done, _ := runDeciders(ctx, dnsFromSystemResolverDeciders, conn, layeredProfile, pkt)
|
||||||
if !done {
|
if !done {
|
||||||
conn.Accept("allowing system resolver dns request", noReasonOptionKey)
|
conn.Accept("allowing system resolver dns request", noReasonOptionKey)
|
||||||
}
|
}
|
||||||
|
@ -105,7 +106,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run all deciders and return if they came to a conclusion.
|
// Run all deciders and return if they came to a conclusion.
|
||||||
done, defaultAction := runDeciders(ctx, defaultDeciders, conn, pkt)
|
done, defaultAction := runDeciders(ctx, defaultDeciders, conn, layeredProfile, pkt)
|
||||||
if done {
|
if done {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -121,16 +122,14 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runDeciders(ctx context.Context, selectedDeciders []deciderFn, conn *network.Connection, pkt packet.Packet) (done bool, defaultAction uint8) {
|
func runDeciders(ctx context.Context, selectedDeciders []deciderFn, conn *network.Connection, layeredProfile *profile.LayeredProfile, pkt packet.Packet) (done bool, defaultAction uint8) {
|
||||||
layeredProfile := conn.Process().Profile()
|
// Read-lock all the profiles.
|
||||||
|
|
||||||
// Read-lock the all the profiles.
|
|
||||||
layeredProfile.LockForUsage()
|
layeredProfile.LockForUsage()
|
||||||
defer layeredProfile.UnlockForUsage()
|
defer layeredProfile.UnlockForUsage()
|
||||||
|
|
||||||
// Go though all deciders, return if one sets an action.
|
// Go though all deciders, return if one sets an action.
|
||||||
for _, decider := range selectedDeciders {
|
for _, decider := range selectedDeciders {
|
||||||
if decider(ctx, conn, pkt) {
|
if decider(ctx, conn, layeredProfile, pkt) {
|
||||||
return true, profile.DefaultActionNotSet
|
return true, profile.DefaultActionNotSet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,7 +140,7 @@ func runDeciders(ctx context.Context, selectedDeciders []deciderFn, conn *networ
|
||||||
|
|
||||||
// checkPortmasterConnection allows all connection that originate from
|
// checkPortmasterConnection allows all connection that originate from
|
||||||
// portmaster itself.
|
// portmaster itself.
|
||||||
func checkPortmasterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) bool {
|
func checkPortmasterConnection(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, pkt packet.Packet) bool {
|
||||||
// Grant own outgoing connections.
|
// Grant own outgoing connections.
|
||||||
if conn.Process().Pid == ownPID &&
|
if conn.Process().Pid == ownPID &&
|
||||||
(pkt == nil || pkt.IsOutbound()) {
|
(pkt == nil || pkt.IsOutbound()) {
|
||||||
|
@ -155,7 +154,7 @@ func checkPortmasterConnection(ctx context.Context, conn *network.Connection, pk
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkSelfCommunication checks if the process is communicating with itself.
|
// checkSelfCommunication checks if the process is communicating with itself.
|
||||||
func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt packet.Packet) bool {
|
func checkSelfCommunication(ctx context.Context, conn *network.Connection, _ *profile.LayeredProfile, pkt packet.Packet) bool {
|
||||||
// check if process is communicating with itself
|
// check if process is communicating with itself
|
||||||
if pkt != nil {
|
if pkt != nil {
|
||||||
// TODO: evaluate the case where different IPs in the 127/8 net are used.
|
// TODO: evaluate the case where different IPs in the 127/8 net are used.
|
||||||
|
@ -190,13 +189,10 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt p
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
var result endpoints.EPResult
|
var result endpoints.EPResult
|
||||||
var reason endpoints.Reason
|
var reason endpoints.Reason
|
||||||
|
|
||||||
// there must always be a profile.
|
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
// check endpoints list
|
// check endpoints list
|
||||||
var optionKey string
|
var optionKey string
|
||||||
if conn.Inbound {
|
if conn.Inbound {
|
||||||
|
@ -218,35 +214,41 @@ func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConnectionType(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkConnectionType(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
p := conn.Process().Profile()
|
switch {
|
||||||
|
case conn.Type != network.IPConnection:
|
||||||
|
|
||||||
// check conn type
|
// Decider only applies to IP connections.
|
||||||
switch conn.Scope {
|
return false
|
||||||
case network.IncomingLAN, network.IncomingInternet, network.IncomingInvalid:
|
|
||||||
if p.BlockInbound() {
|
case conn.Inbound &&
|
||||||
if conn.Scope == network.IncomingHost {
|
!conn.Entity.IPScope.IsLocalhost() &&
|
||||||
conn.Block("inbound connections blocked", profile.CfgOptionBlockInboundKey)
|
p.BlockInbound():
|
||||||
} else {
|
|
||||||
|
// BlockInbound does not apply to the Localhost scope.
|
||||||
conn.Drop("inbound connections blocked", profile.CfgOptionBlockInboundKey)
|
conn.Drop("inbound connections blocked", profile.CfgOptionBlockInboundKey)
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
|
||||||
case network.PeerInternet:
|
case conn.Entity.IPScope.IsGlobal() &&
|
||||||
// BlockP2P only applies to connections to the Internet
|
conn.Entity.Domain == "" &&
|
||||||
if p.BlockP2P() {
|
p.BlockP2P():
|
||||||
|
|
||||||
|
// BlockP2P only applies to the Global scope.
|
||||||
conn.Block("direct connections (P2P) blocked", profile.CfgOptionBlockP2PKey)
|
conn.Block("direct connections (P2P) blocked", profile.CfgOptionBlockP2PKey)
|
||||||
return true
|
return true
|
||||||
}
|
|
||||||
}
|
default:
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConnectivityDomain(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkConnectivityDomain(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
case conn.Entity.Domain == "":
|
||||||
|
// Only applies if a domain is available.
|
||||||
|
return false
|
||||||
|
|
||||||
case netenv.GetOnlineStatus() > netenv.StatusPortal:
|
case netenv.GetOnlineStatus() > netenv.StatusPortal:
|
||||||
// Special grant only applies if network status is Portal (or even more limited).
|
// Special grant only applies if network status is Portal (or even more limited).
|
||||||
return false
|
return false
|
||||||
|
@ -270,9 +272,7 @@ func checkConnectivityDomain(_ context.Context, conn *network.Connection, _ pack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkConnectionScope(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkConnectionScope(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
// If we are handling a DNS request, check if we can immediately block it.
|
// If we are handling a DNS request, check if we can immediately block it.
|
||||||
if conn.Type == network.DNSRequest {
|
if conn.Type == network.DNSRequest {
|
||||||
// DNS is expected to resolve to LAN or Internet addresses.
|
// DNS is expected to resolve to LAN or Internet addresses.
|
||||||
|
@ -307,8 +307,46 @@ func checkConnectionScope(_ context.Context, conn *network.Connection, _ packet.
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkBypassPrevention(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
|
if p.PreventBypassing() {
|
||||||
|
// check for bypass protection
|
||||||
|
result, reason, reasonCtx := PreventBypassing(conn)
|
||||||
|
switch result {
|
||||||
|
case endpoints.Denied:
|
||||||
|
conn.BlockWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
||||||
|
return true
|
||||||
|
case endpoints.Permitted:
|
||||||
|
conn.AcceptWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
||||||
|
return true
|
||||||
|
case endpoints.NoMatch:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkFilterLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, pkt packet.Packet) bool {
|
||||||
|
// apply privacy filter lists
|
||||||
|
result, reason := p.MatchFilterLists(ctx, conn.Entity)
|
||||||
|
switch result {
|
||||||
|
case endpoints.Denied:
|
||||||
|
conn.DenyWithContext(reason.String(), profile.CfgOptionFilterListsKey, reason.Context())
|
||||||
|
return true
|
||||||
|
case endpoints.NoMatch:
|
||||||
|
// nothing to do
|
||||||
|
default:
|
||||||
|
log.Tracer(ctx).Debugf("filter: filter lists returned unsupported verdict: %s", result)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkResolverScope(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
// If the IP address was resolved, check the scope of the resolver.
|
// If the IP address was resolved, check the scope of the resolver.
|
||||||
switch {
|
switch {
|
||||||
|
case conn.Type != network.IPConnection:
|
||||||
|
// Only applies to IP connections.
|
||||||
case !p.RemoveOutOfScopeDNS():
|
case !p.RemoveOutOfScopeDNS():
|
||||||
// Out of scope checking is not active.
|
// Out of scope checking is not active.
|
||||||
case conn.Resolver == nil:
|
case conn.Resolver == nil:
|
||||||
|
@ -328,43 +366,7 @@ func checkConnectionScope(_ context.Context, conn *network.Connection, _ packet.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkBypassPrevention(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkDomainHeuristics(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
if conn.Process().Profile().PreventBypassing() {
|
|
||||||
// check for bypass protection
|
|
||||||
result, reason, reasonCtx := PreventBypassing(conn)
|
|
||||||
switch result {
|
|
||||||
case endpoints.Denied:
|
|
||||||
conn.BlockWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
|
||||||
return true
|
|
||||||
case endpoints.Permitted:
|
|
||||||
conn.AcceptWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
|
||||||
return true
|
|
||||||
case endpoints.NoMatch:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkFilterLists(ctx context.Context, conn *network.Connection, pkt packet.Packet) bool {
|
|
||||||
// apply privacy filter lists
|
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
result, reason := p.MatchFilterLists(ctx, conn.Entity)
|
|
||||||
switch result {
|
|
||||||
case endpoints.Denied:
|
|
||||||
conn.DenyWithContext(reason.String(), profile.CfgOptionFilterListsKey, reason.Context())
|
|
||||||
return true
|
|
||||||
case endpoints.NoMatch:
|
|
||||||
// nothing to do
|
|
||||||
default:
|
|
||||||
log.Tracer(ctx).Debugf("filter: filter lists returned unsupported verdict: %s", result)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
|
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
if !p.DomainHeuristics() {
|
if !p.DomainHeuristics() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -422,7 +424,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func dropInbound(_ context.Context, conn *network.Connection, _ *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
// implicit default=block for inbound
|
// implicit default=block for inbound
|
||||||
if conn.Inbound {
|
if conn.Inbound {
|
||||||
conn.Drop("incoming connection blocked by default", profile.CfgOptionServiceEndpointsKey)
|
conn.Drop("incoming connection blocked by default", profile.CfgOptionServiceEndpointsKey)
|
||||||
|
@ -431,9 +433,7 @@ func dropInbound(_ context.Context, conn *network.Connection, _ packet.Packet) b
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAutoPermitRelated(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
func checkAutoPermitRelated(_ context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
p := conn.Process().Profile()
|
|
||||||
|
|
||||||
// Auto permit is disabled for default action permit.
|
// Auto permit is disabled for default action permit.
|
||||||
if p.DefaultAction() == profile.DefaultActionPermit {
|
if p.DefaultAction() == profile.DefaultActionPermit {
|
||||||
return false
|
return false
|
||||||
|
|
Loading…
Add table
Reference in a new issue