Revamp/cleanup firewall prompting
This commit is contained in:
194
firewall/prompt.go
Normal file
194
firewall/prompt.go
Normal file
@@ -0,0 +1,194 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/notifications"
|
||||
"github.com/safing/portmaster/network"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/portmaster/profile"
|
||||
)
|
||||
|
||||
const (
|
||||
// notification action IDs
|
||||
permitDomainAll = "permit-domain-all"
|
||||
permitDomainDistinct = "permit-domain-distinct"
|
||||
denyDomainAll = "deny-domain-all"
|
||||
denyDomainDistinct = "deny-domain-distinct"
|
||||
|
||||
permitIP = "permit-ip"
|
||||
denyIP = "deny-ip"
|
||||
permitServingIP = "permit-serving-ip"
|
||||
denyServingIP = "deny-serving-ip"
|
||||
)
|
||||
|
||||
func prompt(comm *network.Communication, link *network.Link, pkt packet.Packet, fqdn string) {
|
||||
nTTL := time.Duration(promptTimeout()) * time.Second
|
||||
|
||||
// first check if there is an existing notification for this.
|
||||
// build notification ID
|
||||
var nID string
|
||||
switch {
|
||||
case comm.Direction, fqdn == "": // connection to/from IP
|
||||
if pkt == nil {
|
||||
log.Error("firewall: could not prompt for incoming/direct connection: missing pkt")
|
||||
if link != nil {
|
||||
link.Deny("internal error")
|
||||
} else {
|
||||
comm.Deny("internal error")
|
||||
}
|
||||
return
|
||||
}
|
||||
nID = fmt.Sprintf("firewall-prompt-%d-%s-%s", comm.Process().Pid, comm.Domain, pkt.Info().RemoteIP)
|
||||
default: // connection to domain
|
||||
nID = fmt.Sprintf("firewall-prompt-%d-%s", comm.Process().Pid, comm.Domain)
|
||||
}
|
||||
n := notifications.Get(nID)
|
||||
saveResponse := true
|
||||
|
||||
if n != nil {
|
||||
// update with new expiry
|
||||
n.Update(time.Now().Add(nTTL).Unix())
|
||||
// do not save response to profile
|
||||
saveResponse = false
|
||||
} else {
|
||||
// create new notification
|
||||
n = (¬ifications.Notification{
|
||||
ID: nID,
|
||||
Type: notifications.Prompt,
|
||||
Expires: time.Now().Add(nTTL).Unix(),
|
||||
})
|
||||
|
||||
// add message and actions
|
||||
switch {
|
||||
case comm.Direction: // incoming
|
||||
n.Message = fmt.Sprintf("Application %s wants to accept connections from %s (on %d/%d)", comm.Process(), pkt.Info().RemoteIP(), pkt.Info().Protocol, pkt.Info().LocalPort())
|
||||
n.AvailableActions = []*notifications.Action{
|
||||
¬ifications.Action{
|
||||
ID: permitServingIP,
|
||||
Text: "Permit",
|
||||
},
|
||||
¬ifications.Action{
|
||||
ID: denyServingIP,
|
||||
Text: "Deny",
|
||||
},
|
||||
}
|
||||
case fqdn == "": // direct connection
|
||||
n.Message = fmt.Sprintf("Application %s wants to connect to %s (on %d/%d)", comm.Process(), pkt.Info().RemoteIP(), pkt.Info().Protocol, pkt.Info().RemotePort())
|
||||
n.AvailableActions = []*notifications.Action{
|
||||
¬ifications.Action{
|
||||
ID: permitIP,
|
||||
Text: "Permit",
|
||||
},
|
||||
¬ifications.Action{
|
||||
ID: denyIP,
|
||||
Text: "Deny",
|
||||
},
|
||||
}
|
||||
default: // connection to domain
|
||||
if pkt != nil {
|
||||
n.Message = fmt.Sprintf("Application %s wants to connect to %s (%s %d/%d)", comm.Process(), comm.Domain, pkt.Info().RemoteIP(), pkt.Info().Protocol, pkt.Info().RemotePort())
|
||||
} else {
|
||||
n.Message = fmt.Sprintf("Application %s wants to connect to %s", comm.Process(), comm.Domain)
|
||||
}
|
||||
n.AvailableActions = []*notifications.Action{
|
||||
¬ifications.Action{
|
||||
ID: permitDomainAll,
|
||||
Text: "Permit all",
|
||||
},
|
||||
¬ifications.Action{
|
||||
ID: permitDomainDistinct,
|
||||
Text: "Permit",
|
||||
},
|
||||
¬ifications.Action{
|
||||
ID: denyDomainDistinct,
|
||||
Text: "Deny",
|
||||
},
|
||||
}
|
||||
}
|
||||
// save new notification
|
||||
n.Save()
|
||||
}
|
||||
|
||||
// wait for response/timeout
|
||||
select {
|
||||
case promptResponse := <-n.Response():
|
||||
switch promptResponse {
|
||||
case permitDomainAll, permitDomainDistinct, permitIP, permitServingIP:
|
||||
if link != nil {
|
||||
link.Accept("permitted by user")
|
||||
} else {
|
||||
comm.Accept("permitted by user")
|
||||
}
|
||||
default: // deny
|
||||
if link != nil {
|
||||
link.Accept("denied by user")
|
||||
} else {
|
||||
comm.Accept("denied by user")
|
||||
}
|
||||
}
|
||||
|
||||
// end here if we won't save the response to the profile
|
||||
if !saveResponse {
|
||||
return
|
||||
}
|
||||
|
||||
new := &profile.EndpointPermission{
|
||||
Type: profile.EptDomain,
|
||||
Value: comm.Domain,
|
||||
Permit: false,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// permission type
|
||||
switch promptResponse {
|
||||
case permitDomainAll, denyDomainAll:
|
||||
new.Value = "." + new.Value
|
||||
case permitIP, permitServingIP, denyIP, denyServingIP:
|
||||
if pkt == nil {
|
||||
log.Warningf("firewall: received invalid prompt response: %s for %s", promptResponse, comm.Domain)
|
||||
return
|
||||
}
|
||||
if pkt.Info().Version == packet.IPv4 {
|
||||
new.Type = profile.EptIPv4
|
||||
} else {
|
||||
new.Type = profile.EptIPv6
|
||||
}
|
||||
new.Value = pkt.Info().RemoteIP().String()
|
||||
}
|
||||
|
||||
// permission verdict
|
||||
switch promptResponse {
|
||||
case permitDomainAll, permitDomainDistinct, permitIP, permitServingIP:
|
||||
new.Permit = false
|
||||
}
|
||||
|
||||
// get user profile
|
||||
profileSet := comm.Process().ProfileSet()
|
||||
profileSet.Lock()
|
||||
defer profileSet.Unlock()
|
||||
userProfile := profileSet.UserProfile()
|
||||
userProfile.Lock()
|
||||
defer userProfile.Unlock()
|
||||
|
||||
// add to correct list
|
||||
switch promptResponse {
|
||||
case permitServingIP, denyServingIP:
|
||||
userProfile.ServiceEndpoints = append(userProfile.ServiceEndpoints, new)
|
||||
default:
|
||||
userProfile.Endpoints = append(userProfile.Endpoints, new)
|
||||
}
|
||||
|
||||
// save!
|
||||
go userProfile.Save("")
|
||||
|
||||
case <-n.Expired():
|
||||
if link != nil {
|
||||
link.Accept("no response to prompt")
|
||||
} else {
|
||||
comm.Accept("no response to prompt")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user