diff --git a/README.md b/README.md index 9b3655af..24541fe1 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ Think of a pi-hole for your computer. Or an ad-blocker that blocks ads on your w **Features/Settings:** - Select and activate block-lists -- Manually black/whitelist domains - - You can whitelist domains in case something breaks +- Manually block/allow domains + - You can allow domains in case something breaks - CNAME Blocking (block these new nasty "unblockable" ads/trackers) - Block all subdomains of a domain in the block-lists diff --git a/firewall/master.go b/firewall/master.go index 82e1fc0a..7f0eb18b 100644 --- a/firewall/master.go +++ b/firewall/master.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" + "github.com/safing/portmaster/detection/dga" "github.com/safing/portmaster/netenv" "github.com/safing/portbase/log" @@ -58,6 +59,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe checkBypassPrevention, checkFilterLists, checkInbound, + checkLMSScore, checkDefaultPermit, checkAutoPermitRelated, checkDefaultAction, @@ -70,7 +72,7 @@ func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packe } // DefaultAction == DefaultActionBlock - conn.Deny("endpoint is not whitelisted (default=block)") + conn.Deny("endpoint is not allowed (default=block)") } // checkPortmasterConnection allows all connection that originate from @@ -281,10 +283,26 @@ func checkFilterLists(ctx context.Context, conn *network.Connection, pkt packet. return false } +func checkLMSScore(ctx context.Context, conn *network.Connection, _ packet.Packet) bool { + if conn.Entity.Domain == "" { + return false + } + + // check for possible DNS tunneling / data transmission + lms := dga.LmsScoreOfDomain(conn.Entity.Domain) + if lms < 10 { + log.Tracer(ctx).Warningf("nameserver: possible data tunnel by %s: %s has lms score of %f, returning nxdomain", conn.Process(), conn.Entity.Domain, lms) + conn.BlockWithContext("Possible data tunnel", conn.ReasonContext) + + return true + } + return false +} + func checkInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool { // implicit default=block for inbound if conn.Inbound { - conn.Drop("endpoint is not whitelisted (incoming is always default=block)") + conn.Drop("endpoint is not allowed (incoming is always default=block)") return true } return false @@ -294,7 +312,7 @@ func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Pa // check default action p := conn.Process().Profile() if p.DefaultAction() == profile.DefaultActionPermit { - conn.Accept("endpoint is not blacklisted (default=permit)") + conn.Accept("endpoint is not blocked (default=permit)") return true } return false diff --git a/intel/filterlists/lookup.go b/intel/filterlists/lookup.go index 8304efb3..827aeab9 100644 --- a/intel/filterlists/lookup.go +++ b/intel/filterlists/lookup.go @@ -47,14 +47,14 @@ func lookupBlockLists(entity, value string) ([]string, error) { } // LookupCountry returns a list of sources that mark the country -// as blacklisted. If country is not stored in the cache database +// as blocked. If country is not stored in the cache database // a nil slice is returned. func LookupCountry(country string) ([]string, error) { return lookupBlockLists("country", country) } // LookupDomain returns a list of sources that mark the domain -// as blacklisted. If domain is not stored in the cache database +// as blocked. If domain is not stored in the cache database // a nil slice is returned. func LookupDomain(domain string) ([]string, error) { // make sure we only fully qualified domains @@ -67,13 +67,13 @@ func LookupDomain(domain string) ([]string, error) { } // LookupASNString returns a list of sources that mark the ASN -// as blacklisted. If ASN is not stored in the cache database +// as blocked. If ASN is not stored in the cache database // a nil slice is returned. func LookupASNString(asn string) ([]string, error) { return lookupBlockLists("asn", asn) } -// LookupIP returns a list of blacklist sources that contain +// LookupIP returns a list of block sources that contain // a reference to ip. LookupIP automatically checks the IPv4 or // IPv6 lists respectively. func LookupIP(ip net.IP) ([]string, error) { @@ -95,7 +95,7 @@ func LookupIPString(ipStr string) ([]string, error) { return LookupIP(ip) } -// LookupIPv4String returns a list of blacklist sources that +// LookupIPv4String returns a list of block sources that // contain a reference to ip. If the IP is not stored in the // cache database a nil slice is returned. func LookupIPv4String(ipv4 string) ([]string, error) { @@ -113,7 +113,7 @@ func LookupIPv4(ipv4 net.IP) ([]string, error) { return LookupIPv4String(ip.String()) } -// LookupIPv6String returns a list of blacklist sources that +// LookupIPv6String returns a list of block sources that // contain a reference to ip. If the IP is not stored in the // cache database a nil slice is returned. func LookupIPv6String(ipv6 string) ([]string, error) { diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index f7a3b736..ad457131 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -12,7 +12,6 @@ import ( "github.com/safing/portbase/log" "github.com/safing/portbase/modules" - "github.com/safing/portmaster/detection/dga" "github.com/safing/portmaster/firewall" "github.com/safing/portmaster/nameserver/nsutil" "github.com/safing/portmaster/netenv" @@ -211,17 +210,6 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, query *dns.Msg) er // save security level to query q.SecurityLevel = conn.Process().Profile().SecurityLevel() - // check for possible DNS tunneling / data transmission - // TODO: improve this - lms := dga.LmsScoreOfDomain(q.FQDN) - // log.Tracef("nameserver: domain %s has lms score of %f", fqdn, lms) - if lms < 10 { - tracer.Warningf("nameserver: possible data tunnel by %s: %s has lms score of %f, returning nxdomain", conn.Process(), q.FQDN, lms) - conn.Block("Possible data tunnel") - sendResponse(w, query, conn.Verdict, conn.Reason, conn.ReasonContext) - return nil - } - // check profile before we even get intel and rr firewall.DecideOnConnection(ctx, conn, nil) diff --git a/network/connection.go b/network/connection.go index d05ef682..4811f911 100644 --- a/network/connection.go +++ b/network/connection.go @@ -87,10 +87,10 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri timestamp := time.Now().Unix() dnsConn := &Connection{ Scope: fqdn, - Entity: (&intel.Entity{ + Entity: &intel.Entity{ Domain: fqdn, CNAME: cnames, - }), + }, process: proc, Started: timestamp, Ended: timestamp, @@ -123,20 +123,20 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection { default: // netutils.Invalid scope = IncomingInvalid } - entity = (&intel.Entity{ + entity = &intel.Entity{ IP: pkt.Info().Src, Protocol: uint8(pkt.Info().Protocol), Port: pkt.Info().SrcPort, - }) + } } else { // outbound connection - entity = (&intel.Entity{ + entity = &intel.Entity{ IP: pkt.Info().Dst, Protocol: uint8(pkt.Info().Protocol), Port: pkt.Info().DstPort, - }) + } // check if we can find a domain for that IP ipinfo, err := resolver.GetIPInfo(pkt.Info().Dst.String()) diff --git a/network/dns.go b/network/dns.go index b33fa99f..d5f71faa 100644 --- a/network/dns.go +++ b/network/dns.go @@ -23,15 +23,22 @@ var ( unidentifiedProcessScopePrefix = strconv.Itoa(process.UnidentifiedProcessID) + "/" ) +func getDNSRequestCacheKey(pid int, fqdn string) string { + return strconv.Itoa(pid) + "/" + fqdn +} + func removeOpenDNSRequest(pid int, fqdn string) { openDNSRequestsLock.Lock() defer openDNSRequestsLock.Unlock() - key := strconv.Itoa(pid) + "/" + fqdn + key := getDNSRequestCacheKey(pid, fqdn) _, ok := openDNSRequests[key] if ok { delete(openDNSRequests, key) - } else if pid != process.UnidentifiedProcessID { + return + } + + if pid != process.UnidentifiedProcessID { // check if there is an open dns request from an unidentified process delete(openDNSRequests, unidentifiedProcessScopePrefix+fqdn) } @@ -42,26 +49,24 @@ func SaveOpenDNSRequest(conn *Connection) { openDNSRequestsLock.Lock() defer openDNSRequestsLock.Unlock() - key := strconv.Itoa(conn.process.Pid) + "/" + conn.Scope - - existingConn, ok := openDNSRequests[key] - if ok { + key := getDNSRequestCacheKey(conn.process.Pid, conn.Scope) + if existingConn, ok := openDNSRequests[key]; ok { existingConn.Lock() defer existingConn.Unlock() - existingConn.Ended = conn.Started - } else { - openDNSRequests[key] = conn + return } + + openDNSRequests[key] = conn } func openDNSRequestWriter(ctx context.Context) error { ticker := time.NewTicker(writeOpenDNSRequestsTickDuration) + defer ticker.Stop() for { select { case <-ctx.Done(): - ticker.Stop() return nil case <-ticker.C: writeOpenDNSRequestsToDB() diff --git a/profile/config.go b/profile/config.go index 13b4f81f..b9516ce5 100644 --- a/profile/config.go +++ b/profile/config.go @@ -85,9 +85,9 @@ var ( func registerConfiguration() error { // Default Filter Action - // permit - blacklist mode: everything is permitted unless blocked + // permit - blocklist mode: everything is permitted unless blocked // ask - ask mode: if not verdict is found, the user is consulted - // block - whitelist mode: everything is blocked unless permitted + // block - allowlist mode: everything is blocked unless permitted err := config.Register(&config.Option{ Name: "Default Filter Action", Key: CfgOptionDefaultActionKey, diff --git a/profile/endpoints/reason.go b/profile/endpoints/reason.go index d137c3b3..5bb86f71 100644 --- a/profile/endpoints/reason.go +++ b/profile/endpoints/reason.go @@ -23,7 +23,7 @@ type reason struct { func (r *reason) String() string { prefix := "endpoint in blocklist: " if r.Permitted { - prefix = "endpoint in whitelist: " + prefix = "endpoint in allowlist: " } return prefix + r.description + " " + r.Value