mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
Add option key responsible for the verdict
Also, expose the RevisionCounter
This commit is contained in:
parent
263eb0578a
commit
c09d32cf08
8 changed files with 176 additions and 133 deletions
|
@ -17,13 +17,14 @@ import (
|
|||
"github.com/safing/portmaster/resolver"
|
||||
)
|
||||
|
||||
func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) ([]dns.RR, []string, int) {
|
||||
func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) ([]dns.RR, []string, int, string) {
|
||||
goodEntries := make([]dns.RR, 0, len(entries))
|
||||
filteredRecords := make([]string, 0, len(entries))
|
||||
|
||||
// keeps track of the number of valid and allowed
|
||||
// A and AAAA records.
|
||||
var allowedAddressRecords int
|
||||
var interveningOptionKey string
|
||||
|
||||
for _, rr := range entries {
|
||||
// get IP and classification
|
||||
|
@ -45,10 +46,12 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
|
|||
case classification == netutils.HostLocal:
|
||||
// No DNS should return localhost addresses
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey
|
||||
continue
|
||||
case scope == netutils.Global && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
// No global DNS should return LAN addresses
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
interveningOptionKey = profile.CfgOptionRemoveOutOfScopeDNSKey
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
@ -58,12 +61,15 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
|
|||
switch {
|
||||
case p.BlockScopeInternet() && classification == netutils.Global:
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
interveningOptionKey = profile.CfgOptionBlockScopeInternetKey
|
||||
continue
|
||||
case p.BlockScopeLAN() && (classification == netutils.SiteLocal || classification == netutils.LinkLocal):
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
interveningOptionKey = profile.CfgOptionBlockScopeLANKey
|
||||
continue
|
||||
case p.BlockScopeLocal() && classification == netutils.HostLocal:
|
||||
filteredRecords = append(filteredRecords, rr.String())
|
||||
interveningOptionKey = profile.CfgOptionBlockScopeLocalKey
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -75,7 +81,7 @@ func filterDNSSection(entries []dns.RR, p *profile.LayeredProfile, scope int8) (
|
|||
goodEntries = append(goodEntries, rr)
|
||||
}
|
||||
|
||||
return goodEntries, filteredRecords, allowedAddressRecords
|
||||
return goodEntries, filteredRecords, allowedAddressRecords, interveningOptionKey
|
||||
}
|
||||
|
||||
func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *resolver.RRCache {
|
||||
|
@ -97,18 +103,19 @@ func filterDNSResponse(conn *network.Connection, rrCache *resolver.RRCache) *res
|
|||
|
||||
var filteredRecords []string
|
||||
var validIPs int
|
||||
var interveningOptionKey string
|
||||
|
||||
rrCache.Answer, filteredRecords, validIPs = filterDNSSection(rrCache.Answer, p, rrCache.ServerScope)
|
||||
rrCache.Answer, filteredRecords, validIPs, interveningOptionKey = filterDNSSection(rrCache.Answer, p, rrCache.ServerScope)
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
|
||||
|
||||
// we don't count the valid IPs in the extra section
|
||||
rrCache.Extra, filteredRecords, _ = filterDNSSection(rrCache.Extra, p, rrCache.ServerScope)
|
||||
rrCache.Extra, filteredRecords, _, _ = filterDNSSection(rrCache.Extra, p, rrCache.ServerScope)
|
||||
rrCache.FilteredEntries = append(rrCache.FilteredEntries, filteredRecords...)
|
||||
|
||||
if len(rrCache.FilteredEntries) > 0 {
|
||||
rrCache.Filtered = true
|
||||
if validIPs == 0 {
|
||||
conn.Block("no addresses returned for this domain are permitted")
|
||||
conn.Block("no addresses returned for this domain are permitted", interveningOptionKey)
|
||||
|
||||
// If all entries are filtered, this could mean that these are broken/bogus resource records.
|
||||
if rrCache.Expired() {
|
||||
|
@ -151,12 +158,6 @@ func DecideOnResolvedDNS(
|
|||
rrCache *resolver.RRCache,
|
||||
) *resolver.RRCache {
|
||||
|
||||
// check profile
|
||||
if checkProfileExists(ctx, conn, nil) {
|
||||
// returns true if check triggered
|
||||
return nil
|
||||
}
|
||||
|
||||
// special grant for connectivity domains
|
||||
if checkConnectivityDomain(ctx, conn, nil) {
|
||||
// returns true if check triggered
|
||||
|
@ -186,14 +187,14 @@ func mayBlockCNAMEs(ctx context.Context, conn *network.Connection) bool {
|
|||
|
||||
result, reason := conn.Process().Profile().MatchEndpoint(ctx, conn.Entity)
|
||||
if result == endpoints.Denied {
|
||||
conn.BlockWithContext(reason.String(), reason.Context())
|
||||
conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context())
|
||||
return true
|
||||
}
|
||||
|
||||
if result == endpoints.NoMatch {
|
||||
result, reason = conn.Process().Profile().MatchFilterLists(ctx, conn.Entity)
|
||||
if result == endpoints.Denied {
|
||||
conn.BlockWithContext(reason.String(), reason.Context())
|
||||
conn.BlockWithContext(reason.String(), profile.CfgOptionFilterCNAMEKey, reason.Context())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,11 +85,11 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict
|
|||
verdict = network.VerdictDrop
|
||||
continueInspection = true
|
||||
case BLOCK_CONN:
|
||||
conn.SetVerdict(network.VerdictBlock, "", nil)
|
||||
conn.SetVerdict(network.VerdictBlock, "", "", nil)
|
||||
verdict = conn.Verdict
|
||||
activeInspectors[key] = true
|
||||
case DROP_CONN:
|
||||
conn.SetVerdict(network.VerdictDrop, "", nil)
|
||||
conn.SetVerdict(network.VerdictDrop, "", "", nil)
|
||||
verdict = conn.Verdict
|
||||
activeInspectors[key] = true
|
||||
case STOP_INSPECTING:
|
||||
|
|
|
@ -171,7 +171,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
ps := getPortStatusAndMarkUsed(pkt.Info().LocalPort())
|
||||
if ps.isMe {
|
||||
// approve
|
||||
conn.Accept("internally approved")
|
||||
conn.Accept("connection by Portmaster", noReasonOptionKey)
|
||||
conn.Internal = true
|
||||
// finish
|
||||
conn.StopFirewallHandler()
|
||||
|
@ -191,7 +191,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
|||
// check if filtering is enabled
|
||||
if !filterEnabled() {
|
||||
conn.Inspecting = false
|
||||
conn.SetVerdict(network.VerdictAccept, "privacy filter disabled", nil)
|
||||
conn.Accept("privacy filter disabled", noReasonOptionKey)
|
||||
conn.StopFirewallHandler()
|
||||
issueVerdict(conn, pkt, 0, true)
|
||||
return
|
||||
|
|
|
@ -36,44 +36,81 @@ import (
|
|||
// 3. DecideOnConnection
|
||||
// is called with the first packet of a network connection.
|
||||
|
||||
const noReasonOptionKey = ""
|
||||
|
||||
var deciders = []func(context.Context, *network.Connection, packet.Packet) bool{
|
||||
checkPortmasterConnection,
|
||||
checkSelfCommunication,
|
||||
checkConnectionType,
|
||||
checkConnectivityDomain,
|
||||
checkConnectionScope,
|
||||
checkEndpointLists,
|
||||
checkBypassPrevention,
|
||||
checkFilterLists,
|
||||
dropInbound,
|
||||
checkDomainHeuristics,
|
||||
checkAutoPermitRelated,
|
||||
}
|
||||
|
||||
// DecideOnConnection makes a decision about a connection.
|
||||
// When called, the connection and profile is already locked.
|
||||
func DecideOnConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) {
|
||||
// update profiles and check if communication needs reevaluation
|
||||
if conn.UpdateAndCheck() {
|
||||
// Check if we have a process and profile.
|
||||
layeredProfile := conn.Process().Profile()
|
||||
if layeredProfile == nil {
|
||||
conn.Deny("unknown process or profile", noReasonOptionKey)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the layered profile needs updating.
|
||||
if layeredProfile.NeedsUpdate() {
|
||||
// Update revision counter in connection.
|
||||
conn.ProfileRevisionCounter = layeredProfile.Update()
|
||||
conn.SaveWhenFinished()
|
||||
|
||||
// Reset verdict for connection.
|
||||
log.Tracer(ctx).Infof("filter: re-evaluating verdict on %s", conn)
|
||||
conn.Verdict = network.VerdictUndecided
|
||||
|
||||
// Reset entity if it exists.
|
||||
if conn.Entity != nil {
|
||||
conn.Entity.ResetLists()
|
||||
}
|
||||
}
|
||||
|
||||
var deciders = []func(context.Context, *network.Connection, packet.Packet) bool{
|
||||
checkPortmasterConnection,
|
||||
checkSelfCommunication,
|
||||
checkProfileExists,
|
||||
checkConnectionType,
|
||||
checkConnectivityDomain,
|
||||
checkConnectionScope,
|
||||
checkEndpointLists,
|
||||
checkBypassPrevention,
|
||||
checkFilterLists,
|
||||
checkInbound,
|
||||
checkDomainHeuristics,
|
||||
checkDefaultPermit,
|
||||
checkAutoPermitRelated,
|
||||
checkDefaultAction,
|
||||
// Run all deciders and return if they came to a conclusion.
|
||||
done, defaultAction := runDeciders(ctx, conn, pkt)
|
||||
if done {
|
||||
return
|
||||
}
|
||||
|
||||
// Deciders did not conclude, use default action.
|
||||
switch defaultAction {
|
||||
case profile.DefaultActionPermit:
|
||||
conn.Accept("default permit", profile.CfgOptionDefaultActionKey)
|
||||
case profile.DefaultActionAsk:
|
||||
prompt(ctx, conn, pkt)
|
||||
default:
|
||||
conn.Deny("default block", profile.CfgOptionDefaultActionKey)
|
||||
}
|
||||
}
|
||||
|
||||
func runDeciders(ctx context.Context, conn *network.Connection, pkt packet.Packet) (done bool, defaultAction uint8) {
|
||||
layeredProfile := conn.Process().Profile()
|
||||
|
||||
// Read-lock the all the profiles.
|
||||
layeredProfile.LockForUsage()
|
||||
defer layeredProfile.UnlockForUsage()
|
||||
|
||||
// Go though all deciders, return if one sets an action.
|
||||
for _, decider := range deciders {
|
||||
if decider(ctx, conn, pkt) {
|
||||
return
|
||||
return true, profile.DefaultActionNotSet
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultAction == DefaultActionBlock
|
||||
conn.Deny("endpoint is not allowed (default=block)")
|
||||
// Return the default action.
|
||||
return false, layeredProfile.DefaultAction()
|
||||
}
|
||||
|
||||
// checkPortmasterConnection allows all connection that originate from
|
||||
|
@ -82,7 +119,7 @@ func checkPortmasterConnection(ctx context.Context, conn *network.Connection, pk
|
|||
// grant self
|
||||
if conn.Process().Pid == os.Getpid() {
|
||||
log.Tracer(ctx).Infof("filter: granting own connection %s", conn)
|
||||
conn.Verdict = network.VerdictAccept
|
||||
conn.Accept("connection by Portmaster", noReasonOptionKey)
|
||||
conn.Internal = true
|
||||
return true
|
||||
}
|
||||
|
@ -115,7 +152,7 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt p
|
|||
if err != nil {
|
||||
log.Tracer(ctx).Warningf("filter: failed to find load local peer process with PID %d: %s", otherPid, err)
|
||||
} else if otherProcess.Pid == conn.Process().Pid {
|
||||
conn.Accept("connection to self")
|
||||
conn.Accept("connection to self", noReasonOptionKey)
|
||||
conn.Internal = true
|
||||
return true
|
||||
}
|
||||
|
@ -126,14 +163,6 @@ func checkSelfCommunication(ctx context.Context, conn *network.Connection, pkt p
|
|||
return false
|
||||
}
|
||||
|
||||
func checkProfileExists(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
if conn.Process().Profile() == nil {
|
||||
conn.Block("unknown process or profile")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
var result endpoints.EPResult
|
||||
var reason endpoints.Reason
|
||||
|
@ -142,17 +171,20 @@ func checkEndpointLists(ctx context.Context, conn *network.Connection, _ packet.
|
|||
p := conn.Process().Profile()
|
||||
|
||||
// check endpoints list
|
||||
var optionKey string
|
||||
if conn.Inbound {
|
||||
result, reason = p.MatchServiceEndpoint(ctx, conn.Entity)
|
||||
optionKey = profile.CfgOptionServiceEndpointsKey
|
||||
} else {
|
||||
result, reason = p.MatchEndpoint(ctx, conn.Entity)
|
||||
optionKey = profile.CfgOptionEndpointsKey
|
||||
}
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.DenyWithContext(reason.String(), reason.Context())
|
||||
conn.DenyWithContext(reason.String(), optionKey, reason.Context())
|
||||
return true
|
||||
case endpoints.Permitted:
|
||||
conn.AcceptWithContext(reason.String(), reason.Context())
|
||||
conn.AcceptWithContext(reason.String(), optionKey, reason.Context())
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -167,16 +199,16 @@ func checkConnectionType(ctx context.Context, conn *network.Connection, _ packet
|
|||
case network.IncomingLAN, network.IncomingInternet, network.IncomingInvalid:
|
||||
if p.BlockInbound() {
|
||||
if conn.Scope == network.IncomingHost {
|
||||
conn.Block("inbound connections blocked")
|
||||
conn.Block("inbound connections blocked", profile.CfgOptionBlockInboundKey)
|
||||
} else {
|
||||
conn.Drop("inbound connections blocked")
|
||||
conn.Drop("inbound connections blocked", profile.CfgOptionBlockInboundKey)
|
||||
}
|
||||
return true
|
||||
}
|
||||
case network.PeerInternet:
|
||||
// BlockP2P only applies to connections to the Internet
|
||||
if p.BlockP2P() {
|
||||
conn.Block("direct connections (P2P) blocked")
|
||||
conn.Block("direct connections (P2P) blocked", profile.CfgOptionBlockP2PKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -202,7 +234,7 @@ func checkConnectivityDomain(_ context.Context, conn *network.Connection, _ pack
|
|||
|
||||
case netenv.IsConnectivityDomain(conn.Entity.Domain):
|
||||
// Special grant!
|
||||
conn.Accept("special grant for connectivity domain during network bootstrap")
|
||||
conn.Accept("special grant for connectivity domain during network bootstrap", noReasonOptionKey)
|
||||
return true
|
||||
|
||||
default:
|
||||
|
@ -221,29 +253,29 @@ func checkConnectionScope(_ context.Context, conn *network.Connection, _ packet.
|
|||
switch classification {
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
if p.BlockScopeInternet() {
|
||||
conn.Deny("Internet access blocked") // Block Outbound / Drop Inbound
|
||||
conn.Deny("Internet access blocked", profile.CfgOptionBlockScopeInternetKey) // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
case netutils.SiteLocal, netutils.LinkLocal, netutils.LocalMulticast:
|
||||
if p.BlockScopeLAN() {
|
||||
conn.Block("LAN access blocked") // Block Outbound / Drop Inbound
|
||||
conn.Block("LAN access blocked", profile.CfgOptionBlockScopeLANKey) // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
case netutils.HostLocal:
|
||||
if p.BlockScopeLocal() {
|
||||
conn.Block("Localhost access blocked") // Block Outbound / Drop Inbound
|
||||
conn.Block("Localhost access blocked", profile.CfgOptionBlockScopeLocalKey) // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
default: // netutils.Invalid
|
||||
conn.Deny("invalid IP") // Block Outbound / Drop Inbound
|
||||
conn.Deny("invalid IP", noReasonOptionKey) // Block Outbound / Drop Inbound
|
||||
return true
|
||||
}
|
||||
} else if conn.Entity.Domain != "" {
|
||||
// DNS Query
|
||||
// DNS is expected to resolve to LAN or Internet addresses
|
||||
// TODO: handle domains mapped to localhost
|
||||
// This is a DNS Request.
|
||||
// DNS is expected to resolve to LAN or Internet addresses.
|
||||
// Localhost queries are immediately responded to by the nameserver.
|
||||
if p.BlockScopeInternet() && p.BlockScopeLAN() {
|
||||
conn.Block("Internet and LAN access blocked")
|
||||
conn.Block("Internet and LAN access blocked", profile.CfgOptionBlockScopeInternetKey)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -256,10 +288,10 @@ func checkBypassPrevention(_ context.Context, conn *network.Connection, _ packet
|
|||
result, reason, reasonCtx := PreventBypassing(conn)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.BlockWithContext("bypass prevention: "+reason, reasonCtx)
|
||||
conn.BlockWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
||||
return true
|
||||
case endpoints.Permitted:
|
||||
conn.AcceptWithContext("bypass prevention: "+reason, reasonCtx)
|
||||
conn.AcceptWithContext("bypass prevention: "+reason, profile.CfgOptionPreventBypassingKey, reasonCtx)
|
||||
return true
|
||||
case endpoints.NoMatch:
|
||||
}
|
||||
|
@ -274,7 +306,7 @@ func checkFilterLists(ctx context.Context, conn *network.Connection, pkt packet.
|
|||
result, reason := p.MatchFilterLists(ctx, conn.Entity)
|
||||
switch result {
|
||||
case endpoints.Denied:
|
||||
conn.DenyWithContext(reason.String(), reason.Context())
|
||||
conn.DenyWithContext(reason.String(), profile.CfgOptionFilterListsKey, reason.Context())
|
||||
return true
|
||||
case endpoints.NoMatch:
|
||||
// nothing to do
|
||||
|
@ -315,7 +347,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
|
|||
domainToCheck,
|
||||
score,
|
||||
)
|
||||
conn.Block("possible DGA domain commonly used by malware")
|
||||
conn.Block("possible DGA domain commonly used by malware", profile.CfgOptionDomainHeuristicsKey)
|
||||
return true
|
||||
}
|
||||
log.Tracer(ctx).Tracef("filter: LMS score of eTLD+1 %s is %.2f", etld1, score)
|
||||
|
@ -335,7 +367,7 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
|
|||
domainToCheck,
|
||||
score,
|
||||
)
|
||||
conn.Block("possible data tunnel for covert communication and protection bypassing")
|
||||
conn.Block("possible data tunnel for covert communication and protection bypassing", profile.CfgOptionDomainHeuristicsKey)
|
||||
return true
|
||||
}
|
||||
log.Tracer(ctx).Tracef("filter: LMS score of entire domain is %.2f", score)
|
||||
|
@ -344,20 +376,10 @@ func checkDomainHeuristics(ctx context.Context, conn *network.Connection, _ pack
|
|||
return false
|
||||
}
|
||||
|
||||
func checkInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
func dropInbound(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
// implicit default=block for inbound
|
||||
if conn.Inbound {
|
||||
conn.Drop("endpoint is not allowed (incoming is always default=block)")
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
// check default action
|
||||
p := conn.Process().Profile()
|
||||
if p.DefaultAction() == profile.DefaultActionPermit {
|
||||
conn.Accept("endpoint is not blocked (default=permit)")
|
||||
conn.Drop("incoming connection blocked by default", profile.CfgOptionServiceEndpointsKey)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -365,22 +387,24 @@ func checkDefaultPermit(_ context.Context, conn *network.Connection, _ packet.Pa
|
|||
|
||||
func checkAutoPermitRelated(_ context.Context, conn *network.Connection, _ packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
if !p.DisableAutoPermit() {
|
||||
related, reason := checkRelation(conn)
|
||||
if related {
|
||||
conn.Accept(reason)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func checkDefaultAction(_ context.Context, conn *network.Connection, pkt packet.Packet) bool {
|
||||
p := conn.Process().Profile()
|
||||
if p.DefaultAction() == profile.DefaultActionAsk {
|
||||
prompt(conn, pkt)
|
||||
// Auto permit is disabled for default action permit.
|
||||
if p.DefaultAction() == profile.DefaultActionPermit {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if auto permit is disabled.
|
||||
if p.DisableAutoPermit() {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check for relation to auto permit.
|
||||
related, reason := checkRelation(conn)
|
||||
if related {
|
||||
conn.Accept(reason, profile.CfgOptionDisableAutoPermitKey)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -426,7 +450,7 @@ matchLoop:
|
|||
}
|
||||
|
||||
if related {
|
||||
reason = fmt.Sprintf("domain is related to process: %s is related to %s", domainElement, processElement)
|
||||
reason = fmt.Sprintf("auto permitted: domain is related to process: %s is related to %s", domainElement, processElement)
|
||||
}
|
||||
return related, reason
|
||||
}
|
||||
|
|
|
@ -197,11 +197,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
|
|||
// A reason for this might be that the request is sink-holed to a forced
|
||||
// IP address in which case we "accept" it, but let the firewall handle
|
||||
// the resolving as it wishes.
|
||||
if responder, ok := conn.ReasonContext.(nsutil.Responder); ok {
|
||||
if responder, ok := conn.Reason.Context.(nsutil.Responder); ok {
|
||||
// Save the request as open, as we don't know if there will be a connection or not.
|
||||
network.SaveOpenDNSRequest(conn)
|
||||
|
||||
tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason)
|
||||
tracer.Infof("nameserver: handing over request for %s to special filter responder: %s", q.ID(), conn.Reason.Msg)
|
||||
return reply(responder)
|
||||
}
|
||||
|
||||
|
@ -243,11 +243,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg)
|
|||
rrCache = firewall.DecideOnResolvedDNS(ctx, conn, q, rrCache)
|
||||
if rrCache == nil {
|
||||
// Check again if there is a responder from the firewall.
|
||||
if responder, ok := conn.ReasonContext.(nsutil.Responder); ok {
|
||||
if responder, ok := conn.Reason.Context.(nsutil.Responder); ok {
|
||||
// Save the request as open, as we don't know if there will be a connection or not.
|
||||
network.SaveOpenDNSRequest(conn)
|
||||
|
||||
tracer.Infof("nameserver: handing over request for %s to filter responder: %s", q.ID(), conn.Reason)
|
||||
tracer.Infof("nameserver: handing over request for %s to filter responder: %s", q.ID(), conn.Reason.Msg)
|
||||
return reply(responder)
|
||||
}
|
||||
|
||||
|
|
|
@ -83,12 +83,10 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
|||
// The verdict may change so any access to it must be guarded by the
|
||||
// connection lock.
|
||||
Verdict Verdict
|
||||
// Reason is a human readable description justifying the set verdict.
|
||||
// Reason holds information justifying the verdict, as well as additional
|
||||
// information about the reason.
|
||||
// Access to Reason must be guarded by the connection lock.
|
||||
Reason string
|
||||
// ReasonContext may holds additional reason-specific information and
|
||||
// any access must be guarded by the connection lock.
|
||||
ReasonContext interface{}
|
||||
Reason Reason
|
||||
// Started holds the number of seconds in UNIX epoch time at which
|
||||
// the connection has been initated and first seen by the portmaster.
|
||||
// Staretd is only every set when creating a new connection object
|
||||
|
@ -141,10 +139,26 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
|||
// inspectorData holds additional meta data for the inspectors.
|
||||
// using the inspectors index as a map key.
|
||||
inspectorData map[uint8]interface{}
|
||||
// profileRevisionCounter is used to track changes to the process
|
||||
// ProfileRevisionCounter is used to track changes to the process
|
||||
// profile and required for correct re-evaluation of a connections
|
||||
// verdict.
|
||||
profileRevisionCounter uint64
|
||||
ProfileRevisionCounter uint64
|
||||
}
|
||||
|
||||
// Reason holds information justifying a verdict, as well as additional
|
||||
// information about the reason.
|
||||
type Reason struct {
|
||||
// Msg is a human readable description of the reason.
|
||||
Msg string
|
||||
// OptionKey is the configuration option key of the setting that
|
||||
// was responsible for the verdict.
|
||||
OptionKey string
|
||||
// Profile is the database key of the profile that held the setting
|
||||
// that was responsible for the verdict.
|
||||
Profile string
|
||||
// ReasonContext may hold additional reason-specific information and
|
||||
// any access must be guarded by the connection lock.
|
||||
Context interface{}
|
||||
}
|
||||
|
||||
func getProcessContext(proc *process.Process) ProcessContext {
|
||||
|
@ -290,7 +304,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
|||
Entity: entity,
|
||||
// meta
|
||||
Started: time.Now().Unix(),
|
||||
profileRevisionCounter: proc.Profile().RevisionCnt(),
|
||||
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -300,73 +314,77 @@ func GetConnection(id string) (*Connection, bool) {
|
|||
}
|
||||
|
||||
// AcceptWithContext accepts the connection.
|
||||
func (conn *Connection) AcceptWithContext(reason string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictAccept, reason, ctx) {
|
||||
func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) {
|
||||
log.Warningf("filter: tried to accept %s, but current verdict is %s", conn, conn.Verdict)
|
||||
}
|
||||
}
|
||||
|
||||
// Accept is like AcceptWithContext but only accepts a reason.
|
||||
func (conn *Connection) Accept(reason string) {
|
||||
conn.AcceptWithContext(reason, nil)
|
||||
func (conn *Connection) Accept(reason, reasonOptionKey string) {
|
||||
conn.AcceptWithContext(reason, reasonOptionKey, nil)
|
||||
}
|
||||
|
||||
// BlockWithContext blocks the connection.
|
||||
func (conn *Connection) BlockWithContext(reason string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictBlock, reason, ctx) {
|
||||
func (conn *Connection) BlockWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictBlock, reason, reasonOptionKey, ctx) {
|
||||
log.Warningf("filter: tried to block %s, but current verdict is %s", conn, conn.Verdict)
|
||||
}
|
||||
}
|
||||
|
||||
// Block is like BlockWithContext but does only accepts a reason.
|
||||
func (conn *Connection) Block(reason string) {
|
||||
conn.BlockWithContext(reason, nil)
|
||||
func (conn *Connection) Block(reason, reasonOptionKey string) {
|
||||
conn.BlockWithContext(reason, reasonOptionKey, nil)
|
||||
}
|
||||
|
||||
// DropWithContext drops the connection.
|
||||
func (conn *Connection) DropWithContext(reason string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictDrop, reason, ctx) {
|
||||
func (conn *Connection) DropWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictDrop, reason, reasonOptionKey, ctx) {
|
||||
log.Warningf("filter: tried to drop %s, but current verdict is %s", conn, conn.Verdict)
|
||||
}
|
||||
}
|
||||
|
||||
// Drop is like DropWithContext but does only accepts a reason.
|
||||
func (conn *Connection) Drop(reason string) {
|
||||
conn.DropWithContext(reason, nil)
|
||||
func (conn *Connection) Drop(reason, reasonOptionKey string) {
|
||||
conn.DropWithContext(reason, reasonOptionKey, nil)
|
||||
}
|
||||
|
||||
// DenyWithContext blocks or drops the link depending on the connection direction.
|
||||
func (conn *Connection) DenyWithContext(reason string, ctx interface{}) {
|
||||
func (conn *Connection) DenyWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||
if conn.Inbound {
|
||||
conn.DropWithContext(reason, ctx)
|
||||
conn.DropWithContext(reason, reasonOptionKey, ctx)
|
||||
} else {
|
||||
conn.BlockWithContext(reason, ctx)
|
||||
conn.BlockWithContext(reason, reasonOptionKey, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Deny is like DenyWithContext but only accepts a reason.
|
||||
func (conn *Connection) Deny(reason string) {
|
||||
conn.DenyWithContext(reason, nil)
|
||||
func (conn *Connection) Deny(reason, reasonOptionKey string) {
|
||||
conn.DenyWithContext(reason, reasonOptionKey, nil)
|
||||
}
|
||||
|
||||
// FailedWithContext marks the connection with VerdictFailed and stores the reason.
|
||||
func (conn *Connection) FailedWithContext(reason string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictFailed, reason, ctx) {
|
||||
func (conn *Connection) FailedWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||
if !conn.SetVerdict(VerdictFailed, reason, reasonOptionKey, ctx) {
|
||||
log.Warningf("filter: tried to drop %s due to error but current verdict is %s", conn, conn.Verdict)
|
||||
}
|
||||
}
|
||||
|
||||
// Failed is like FailedWithContext but only accepts a string.
|
||||
func (conn *Connection) Failed(reason string) {
|
||||
conn.FailedWithContext(reason, nil)
|
||||
func (conn *Connection) Failed(reason, reasonOptionKey string) {
|
||||
conn.FailedWithContext(reason, reasonOptionKey, nil)
|
||||
}
|
||||
|
||||
// SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts.
|
||||
func (conn *Connection) SetVerdict(newVerdict Verdict, reason string, reasonCtx interface{}) (ok bool) {
|
||||
func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) {
|
||||
if newVerdict >= conn.Verdict {
|
||||
conn.Verdict = newVerdict
|
||||
conn.Reason = reason
|
||||
conn.ReasonContext = reasonCtx
|
||||
conn.Reason.Msg = reason
|
||||
conn.Reason.Context = reasonCtx
|
||||
if reasonOptionKey != "" && conn.Process() != nil {
|
||||
conn.Reason.OptionKey = reasonOptionKey
|
||||
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
@ -490,7 +508,7 @@ func (conn *Connection) packetHandler() {
|
|||
defaultFirewallHandler(conn, pkt)
|
||||
}
|
||||
// log verdict
|
||||
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason)
|
||||
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason.Msg)
|
||||
|
||||
// save does not touch any changing data
|
||||
// must not be locked, will deadlock with cleaner functions
|
||||
|
|
|
@ -124,15 +124,15 @@ func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns
|
|||
}
|
||||
|
||||
// Create resource record with verdict and reason.
|
||||
rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Verb(), conn.Reason))
|
||||
rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Verb(), conn.Reason.Msg))
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err)
|
||||
return nil
|
||||
}
|
||||
extra := []dns.RR{rr}
|
||||
|
||||
// Add additional records from ReasonContext.
|
||||
if rrProvider, ok := conn.ReasonContext.(nsutil.RRProvider); ok {
|
||||
// Add additional records from Reason.Context.
|
||||
if rrProvider, ok := conn.Reason.Context.(nsutil.RRProvider); ok {
|
||||
rrs := rrProvider.GetExtraRRs(ctx, request)
|
||||
extra = append(extra, rrs...)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ type LayeredProfile struct {
|
|||
|
||||
localProfile *Profile
|
||||
layers []*Profile
|
||||
revisionCounter uint64
|
||||
RevisionCounter uint64
|
||||
|
||||
validityFlag *abool.AtomicBool
|
||||
validityFlagLock sync.Mutex
|
||||
|
|
Loading…
Add table
Reference in a new issue