mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Improve endpoint/rule lists and filtering of DNS requests
This commit is contained in:
parent
1b4a7568f2
commit
f34dccb8f3
8 changed files with 52 additions and 45 deletions
|
@ -54,13 +54,6 @@ var defaultDeciders = []deciderFn{
|
||||||
checkAutoPermitRelated,
|
checkAutoPermitRelated,
|
||||||
}
|
}
|
||||||
|
|
||||||
var dnsFromSystemResolverDeciders = []deciderFn{
|
|
||||||
checkEndpointListsForSystemResolverDNSRequests,
|
|
||||||
checkConnectivityDomain,
|
|
||||||
checkBypassPrevention,
|
|
||||||
checkFilterLists,
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecideOnConnection makes a decision about a connection.
|
// DecideOnConnection makes a decision about a connection.
|
||||||
// When called, the connection and profile is already locked.
|
// When called, the connection and profile is already locked.
|
||||||
func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) {
|
func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) {
|
||||||
|
@ -99,25 +92,18 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe
|
||||||
conn.Entity.EnableCNAMECheck(ctx, layeredProfile.FilterCNAMEs())
|
conn.Entity.EnableCNAMECheck(ctx, layeredProfile.FilterCNAMEs())
|
||||||
conn.Entity.LoadLists(ctx)
|
conn.Entity.LoadLists(ctx)
|
||||||
|
|
||||||
// DNS request from the system resolver require a special decision process,
|
|
||||||
// because the original requesting process is not known. Here, we only check
|
|
||||||
// global-only and the most important per-app aspects. The resulting
|
|
||||||
// connection is then blocked when the original requesting process is known.
|
|
||||||
if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() {
|
|
||||||
// Run all deciders and return if they came to a conclusion.
|
|
||||||
done, _ := runDeciders(ctx, dnsFromSystemResolverDeciders, conn, layeredProfile, pkt)
|
|
||||||
if !done {
|
|
||||||
conn.Accept("allowing system resolver dns request", noReasonOptionKey)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, layeredProfile, pkt)
|
done, defaultAction := runDeciders(ctx, defaultDeciders, conn, layeredProfile, pkt)
|
||||||
if done {
|
if done {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DNS Request are always default allowed, as the endpoint lists could not
|
||||||
|
// be checked fully.
|
||||||
|
if conn.Type == network.DNSRequest {
|
||||||
|
conn.Accept("allowing dns request", noReasonOptionKey)
|
||||||
|
}
|
||||||
|
|
||||||
// Deciders did not conclude, use default action.
|
// Deciders did not conclude, use default action.
|
||||||
switch defaultAction {
|
switch defaultAction {
|
||||||
case profile.DefaultActionPermit:
|
case profile.DefaultActionPermit:
|
||||||
|
@ -197,6 +183,14 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, _ *pr
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
|
// DNS request from the system resolver require a special decision process,
|
||||||
|
// because the original requesting process is not known. Here, we only check
|
||||||
|
// global-only and the most important per-app aspects. The resulting
|
||||||
|
// connection is then blocked when the original requesting process is known.
|
||||||
|
if conn.Type == network.DNSRequest && conn.Process().IsSystemResolver() {
|
||||||
|
return checkEndpointListsForSystemResolverDNSRequests(ctx, conn, p)
|
||||||
|
}
|
||||||
|
|
||||||
var result endpoints.EPResult
|
var result endpoints.EPResult
|
||||||
var reason endpoints.Reason
|
var reason endpoints.Reason
|
||||||
|
|
||||||
|
@ -210,7 +204,7 @@ func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profil
|
||||||
optionKey = profile.CfgOptionEndpointsKey
|
optionKey = profile.CfgOptionEndpointsKey
|
||||||
}
|
}
|
||||||
switch result {
|
switch result {
|
||||||
case endpoints.Denied:
|
case endpoints.Denied, endpoints.MatchError:
|
||||||
conn.DenyWithContext(reason.String(), optionKey, reason.Context())
|
conn.DenyWithContext(reason.String(), optionKey, reason.Context())
|
||||||
return true
|
return true
|
||||||
case endpoints.Permitted:
|
case endpoints.Permitted:
|
||||||
|
@ -225,13 +219,13 @@ func checkEndpointLists(ctx context.Context, conn *network.Connection, p *profil
|
||||||
// checkEndpointLists that is only meant for DNS queries by the system
|
// checkEndpointLists that is only meant for DNS queries by the system
|
||||||
// resolver. It only checks the endpoint filter list of the local profile and
|
// resolver. It only checks the endpoint filter list of the local profile and
|
||||||
// does not include the global profile.
|
// does not include the global profile.
|
||||||
func checkEndpointListsForSystemResolverDNSRequests(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
func checkEndpointListsForSystemResolverDNSRequests(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile) bool {
|
||||||
profileEndpoints := p.LocalProfile().GetEndpoints()
|
profileEndpoints := p.LocalProfile().GetEndpoints()
|
||||||
if profileEndpoints.IsSet() {
|
if profileEndpoints.IsSet() {
|
||||||
result, reason := profileEndpoints.Match(ctx, conn.Entity)
|
result, reason := profileEndpoints.Match(ctx, conn.Entity)
|
||||||
if endpoints.IsDecision(result) {
|
if endpoints.IsDecision(result) {
|
||||||
switch result {
|
switch result {
|
||||||
case endpoints.Denied:
|
case endpoints.Denied, endpoints.MatchError:
|
||||||
conn.DenyWithContext(reason.String(), profile.CfgOptionEndpointsKey, reason.Context())
|
conn.DenyWithContext(reason.String(), profile.CfgOptionEndpointsKey, reason.Context())
|
||||||
return true
|
return true
|
||||||
case endpoints.Permitted:
|
case endpoints.Permitted:
|
||||||
|
@ -396,11 +390,13 @@ func checkResolverScope(_ context.Context, conn *network.Connection, p *profile.
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDomainHeuristics(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
func checkDomainHeuristics(ctx context.Context, conn *network.Connection, p *profile.LayeredProfile, _ packet.Packet) bool {
|
||||||
if !p.DomainHeuristics() {
|
// Don't check if no domain is available.
|
||||||
|
if conn.Entity.Domain == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if conn.Entity.Domain == "" {
|
// Check if domain heuristics are enabled.
|
||||||
|
if !p.DomainHeuristics() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,10 +481,11 @@ func checkAutoPermitRelated(_ context.Context, conn *network.Connection, p *prof
|
||||||
|
|
||||||
// checkRelation tries to find a relation between a process and a communication. This is for better out of the box experience and is _not_ meant to thwart intentional malware.
|
// checkRelation tries to find a relation between a process and a communication. This is for better out of the box experience and is _not_ meant to thwart intentional malware.
|
||||||
func checkRelation(conn *network.Connection) (related bool, reason string) {
|
func checkRelation(conn *network.Connection) (related bool, reason string) {
|
||||||
if conn.Entity.Domain != "" {
|
// Don't check if no domain is available.
|
||||||
|
if conn.Entity.Domain == "" {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
// don't check for unknown processes
|
// Don't check for unknown processes.
|
||||||
if conn.Process().Pid < 0 {
|
if conn.Process().Pid < 0 {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,18 @@ type EndpointASN struct {
|
||||||
|
|
||||||
// Matches checks whether the given entity matches this endpoint definition.
|
// Matches checks whether the given entity matches this endpoint definition.
|
||||||
func (ep *EndpointASN) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) {
|
func (ep *EndpointASN) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) {
|
||||||
|
if entity.IP == nil {
|
||||||
|
return NoMatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entity.IPScope.IsGlobal() {
|
||||||
|
return NoMatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
asn, ok := entity.GetASN(ctx)
|
asn, ok := entity.GetASN(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return Undeterminable, nil
|
asnStr := strconv.Itoa(int(ep.ASN))
|
||||||
|
return MatchError, ep.makeReason(ep, asnStr, "ASN data not available to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
if asn == ep.ASN {
|
if asn == ep.ASN {
|
||||||
|
|
|
@ -21,9 +21,17 @@ type EndpointCountry struct {
|
||||||
|
|
||||||
// Matches checks whether the given entity matches this endpoint definition.
|
// Matches checks whether the given entity matches this endpoint definition.
|
||||||
func (ep *EndpointCountry) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) {
|
func (ep *EndpointCountry) Matches(ctx context.Context, entity *intel.Entity) (EPResult, Reason) {
|
||||||
|
if entity.IP == nil {
|
||||||
|
return NoMatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !entity.IPScope.IsGlobal() {
|
||||||
|
return NoMatch, nil
|
||||||
|
}
|
||||||
|
|
||||||
country, ok := entity.GetCountry(ctx)
|
country, ok := entity.GetCountry(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return Undeterminable, nil
|
return MatchError, ep.makeReason(ep, country, "country data not available to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
if country == ep.Country {
|
if country == ep.Country {
|
||||||
|
|
|
@ -17,7 +17,7 @@ type EndpointIP struct {
|
||||||
// Matches checks whether the given entity matches this endpoint definition.
|
// Matches checks whether the given entity matches this endpoint definition.
|
||||||
func (ep *EndpointIP) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
func (ep *EndpointIP) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
||||||
if entity.IP == nil {
|
if entity.IP == nil {
|
||||||
return Undeterminable, nil
|
return NoMatch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ep.IP.Equal(entity.IP) {
|
if ep.IP.Equal(entity.IP) {
|
||||||
|
|
|
@ -17,8 +17,9 @@ type EndpointIPRange struct {
|
||||||
// Matches checks whether the given entity matches this endpoint definition.
|
// Matches checks whether the given entity matches this endpoint definition.
|
||||||
func (ep *EndpointIPRange) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
func (ep *EndpointIPRange) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
||||||
if entity.IP == nil {
|
if entity.IP == nil {
|
||||||
return Undeterminable, nil
|
return NoMatch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ep.Net.Contains(entity.IP) {
|
if ep.Net.Contains(entity.IP) {
|
||||||
return ep.match(ep, entity, ep.Net.String(), "IP is in")
|
return ep.match(ep, entity, ep.Net.String(), "IP is in")
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ type EndpointScope struct {
|
||||||
// Matches checks whether the given entity matches this endpoint definition.
|
// Matches checks whether the given entity matches this endpoint definition.
|
||||||
func (ep *EndpointScope) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
func (ep *EndpointScope) Matches(_ context.Context, entity *intel.Entity) (EPResult, Reason) {
|
||||||
if entity.IP == nil {
|
if entity.IP == nil {
|
||||||
return Undeterminable, nil
|
return NoMatch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var scope uint8
|
var scope uint8
|
||||||
|
|
|
@ -27,7 +27,7 @@ type EndpointBase struct { //nolint:maligned // TODO
|
||||||
|
|
||||||
func (ep *EndpointBase) match(s fmt.Stringer, entity *intel.Entity, value, desc string, keyval ...interface{}) (EPResult, Reason) {
|
func (ep *EndpointBase) match(s fmt.Stringer, entity *intel.Entity, value, desc string, keyval ...interface{}) (EPResult, Reason) {
|
||||||
result := ep.matchesPPP(entity)
|
result := ep.matchesPPP(entity)
|
||||||
if result == Undeterminable || result == NoMatch {
|
if result == NoMatch {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,10 +57,6 @@ func (ep *EndpointBase) makeReason(s fmt.Stringer, value, desc string, keyval ..
|
||||||
func (ep *EndpointBase) matchesPPP(entity *intel.Entity) (result EPResult) {
|
func (ep *EndpointBase) matchesPPP(entity *intel.Entity) (result EPResult) {
|
||||||
// only check if protocol is defined
|
// only check if protocol is defined
|
||||||
if ep.Protocol > 0 {
|
if ep.Protocol > 0 {
|
||||||
// if protocol is unknown, return Undeterminable
|
|
||||||
if entity.Protocol == 0 {
|
|
||||||
return Undeterminable
|
|
||||||
}
|
|
||||||
// if protocol does not match, return NoMatch
|
// if protocol does not match, return NoMatch
|
||||||
if entity.Protocol != ep.Protocol {
|
if entity.Protocol != ep.Protocol {
|
||||||
return NoMatch
|
return NoMatch
|
||||||
|
@ -69,10 +65,6 @@ func (ep *EndpointBase) matchesPPP(entity *intel.Entity) (result EPResult) {
|
||||||
|
|
||||||
// only check if port is defined
|
// only check if port is defined
|
||||||
if ep.StartPort > 0 {
|
if ep.StartPort > 0 {
|
||||||
// if port is unknown, return Undeterminable
|
|
||||||
if entity.DstPort() == 0 {
|
|
||||||
return Undeterminable
|
|
||||||
}
|
|
||||||
// if port does not match, return NoMatch
|
// if port does not match, return NoMatch
|
||||||
if entity.DstPort() < ep.StartPort || entity.DstPort() > ep.EndPort {
|
if entity.DstPort() < ep.StartPort || entity.DstPort() > ep.EndPort {
|
||||||
return NoMatch
|
return NoMatch
|
||||||
|
|
|
@ -17,7 +17,7 @@ type EPResult uint8
|
||||||
// Endpoint matching return values
|
// Endpoint matching return values
|
||||||
const (
|
const (
|
||||||
NoMatch EPResult = iota
|
NoMatch EPResult = iota
|
||||||
Undeterminable
|
MatchError
|
||||||
Denied
|
Denied
|
||||||
Permitted
|
Permitted
|
||||||
)
|
)
|
||||||
|
@ -25,7 +25,7 @@ const (
|
||||||
// IsDecision returns true if result represents a decision
|
// IsDecision returns true if result represents a decision
|
||||||
// and false if result is NoMatch or Undeterminable.
|
// and false if result is NoMatch or Undeterminable.
|
||||||
func IsDecision(result EPResult) bool {
|
func IsDecision(result EPResult) bool {
|
||||||
return result == Denied || result == Permitted || result == Undeterminable
|
return result == Denied || result == Permitted || result == MatchError
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseEndpoints parses a list of endpoints and returns a list of Endpoints for matching.
|
// ParseEndpoints parses a list of endpoints and returns a list of Endpoints for matching.
|
||||||
|
@ -88,8 +88,8 @@ func (epr EPResult) String() string {
|
||||||
switch epr {
|
switch epr {
|
||||||
case NoMatch:
|
case NoMatch:
|
||||||
return "No Match"
|
return "No Match"
|
||||||
case Undeterminable:
|
case MatchError:
|
||||||
return "Undeterminable"
|
return "Match Error"
|
||||||
case Denied:
|
case Denied:
|
||||||
return "Denied"
|
return "Denied"
|
||||||
case Permitted:
|
case Permitted:
|
||||||
|
|
Loading…
Add table
Reference in a new issue