AddRuleAsync DBus calls now include reference guid for proper application of "once" rules.

Deletes of fw-prompt entries at root of tree now fold up the first child entry into the deleted parent entry.
Fixed fw-prompt bug resulting in return of improper scope.
Application name is no longer editable in fw-prompt.
Icon mapping properly supported for symlinked application paths.
Removed spurious warnings for valid fw-prompt removal requests.
Awful hacked up fix to make icons for non-existent applications properly blank and remove GTK warnings.
Removed old/deprecated Oz firewall rules routines.
shw_dev
Stephen Watt 7 years ago
parent b9c2e03afd
commit 62713d74f0

@ -10,9 +10,6 @@ fw-prompt:
more nesting for similar prompts (by application, pid, target host, etc)
gnome-shell:
Start using new async DBus methods
new go-procsnitch vendor package changes should be pushed into main project

@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/gotk3/gotk3/gdk"
"github.com/gotk3/gotk3/glib"
"github.com/gotk3/gotk3/gtk"
"io/ioutil"
@ -74,6 +75,7 @@ var Notebook *gtk.Notebook
var globalTS *gtk.TreeStore = nil
var globalTV *gtk.TreeView
var globalPromptLock = &sync.Mutex{}
var recentLock = &sync.Mutex{}
var globalIcon *gtk.Image
var editApp, editTarget, editPort, editUser, editGroup *gtk.Entry
@ -81,6 +83,28 @@ var comboProto *gtk.ComboBoxText
var radioOnce, radioProcess, radioParent, radioSession, radioPermanent *gtk.RadioButton
var btnApprove, btnDeny, btnIgnore *gtk.Button
var chkTLS, chkUser, chkGroup *gtk.CheckButton
var recentlyRemoved = []string{}
func wasRecentlyRemoved(guid string) bool {
recentLock.Lock()
defer recentLock.Unlock()
for gind, g := range recentlyRemoved {
if g == guid {
recentlyRemoved = append(recentlyRemoved[:gind], recentlyRemoved[gind+1:]...)
return true
}
}
return false
}
func addRecentlyRemoved(guid string) {
recentLock.Lock()
defer recentLock.Unlock()
fmt.Println("RECENTLY REMOVED: ", guid)
recentlyRemoved = append(recentlyRemoved, guid)
}
func promptInfo(msg string) {
dialog := gtk.MessageDialogNew(mainWin, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Displaying full log info:")
@ -337,6 +361,11 @@ func createTreeStore(general bool) *gtk.TreeStore {
}
func removeRequest(treeStore *gtk.TreeStore, guid string) {
if wasRecentlyRemoved(guid) {
fmt.Printf("Entry for %s was recently removed; deleting from cache\n", guid)
return
}
removed := false
if globalTS == nil {
@ -366,7 +395,7 @@ remove_outer:
if err != nil {
break remove_outer
} else if rule.GUID == guid {
removeSelectedRule(ridx)
removeSelectedRule(ridx, sidx)
removed = true
break
}
@ -427,11 +456,36 @@ func storeNewEntry(ts *gtk.TreeStore, iter *gtk.TreeIter, guid, path, icon, prot
log.Fatal("Could not load default icon theme:", err)
}
pb, err := itheme.LoadIcon(icon, 24, gtk.ICON_LOOKUP_GENERIC_FALLBACK)
if err != nil {
log.Println("Could not load icon:", err)
make_blank := false
if icon != "" {
pb, err := itheme.LoadIcon(icon, 24, gtk.ICON_LOOKUP_GENERIC_FALLBACK)
if err != nil {
log.Println("Could not load icon:", err)
make_blank = true
} else {
colVals[COL_NO_ICON_PIXBUF] = pb
}
} else {
colVals[COL_NO_ICON_PIXBUF] = pb
make_blank = true
}
if make_blank {
pb, err := gdk.PixbufNew(gdk.COLORSPACE_RGB, true, 8, 24, 24)
if err != nil {
log.Println("Error creating blank icon:", err)
} else {
colVals[COL_NO_ICON_PIXBUF] = pb
img, err := gtk.ImageNewFromPixbuf(pb)
if err != nil {
log.Println("Error creating image from pixbuf:", err)
} else {
img.Clear()
pb = img.GetPixbuf()
colVals[COL_NO_ICON_PIXBUF] = pb
}
}
}
for n := 0; n < len(colVals); n++ {
@ -466,9 +520,7 @@ func addRequestInc(treeStore *gtk.TreeStore, guid, path, icon, proto string, pid
break
}
fmt.Println("YES REALLY DUPLICATE: ", rule.nrefs)
duplicated = true
subiter := globalTS.Append(iter)
storeNewEntry(globalTS, subiter, guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, optstring, sandbox, action)
break
@ -515,13 +567,11 @@ func addRequest(treeStore *gtk.TreeStore, guid, path, icon, proto string, pid in
}
if addRequestInc(treeStore, guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, optstring, sandbox, action) {
fmt.Println("REQUEST WAS DUPLICATE")
fmt.Println("Request was duplicate: ", guid)
globalPromptLock.Lock()
toggleHover()
globalPromptLock.Unlock()
return true
} else {
fmt.Println("NOT DUPLICATE")
}
globalPromptLock.Lock()
@ -624,9 +674,9 @@ func lsGetInt(ls *gtk.TreeStore, iter *gtk.TreeIter, idx int) (int, error) {
return ival.(int), nil
}
func makeDecision(idx int, rule string, scope int) error {
func makeDecision(rule string, scope int, guid string) error {
var dres bool
call := dbuso.Call("AddRuleAsync", 0, uint32(scope), rule, "*")
call := dbuso.Call("AddRuleAsync", 0, uint32(scope), rule, "*", guid)
err := call.Store(&dres)
if err != nil {
@ -764,20 +814,86 @@ func clearEditor() {
chkTLS.SetActive(false)
}
func removeSelectedRule(idx int) error {
fmt.Println("XXX: attempting to remove idx = ", idx)
func removeSelectedRule(idx, subidx int) error {
fmt.Printf("XXX: attempting to remove idx = %v, %v\n", idx, subidx)
ppathstr := fmt.Sprintf("%d", idx)
pathstr := ppathstr
path, err := gtk.TreePathNewFromString(fmt.Sprintf("%d", idx))
if err != nil {
return err
if subidx > -1 {
pathstr = fmt.Sprintf("%d:%d", idx, subidx)
}
iter, err := globalTS.GetIter(path)
iter, err := globalTS.GetIterFromString(pathstr)
if err != nil {
return err
}
nchildren := globalTS.IterNChildren(iter)
if nchildren >= 1 {
firstpath := fmt.Sprintf("%d:0", idx)
citer, err := globalTS.GetIterFromString(firstpath)
if err != nil {
return err
}
gnrefs, err := globalTS.GetValue(iter, COL_NO_NREFS)
if err != nil {
return err
}
vnrefs, err := gnrefs.GoValue()
if err != nil {
return err
}
nrefs := vnrefs.(int) - 1
for n := 0; n < COL_NO_LAST; n++ {
val, err := globalTS.GetValue(citer, n)
if err != nil {
return err
}
if n == COL_NO_NREFS {
err = globalTS.SetValue(iter, n, nrefs)
} else {
err = globalTS.SetValue(iter, n, val)
}
if err != nil {
return err
}
}
globalTS.Remove(citer)
return nil
}
globalTS.Remove(iter)
if subidx > -1 {
ppath, err := gtk.TreePathNewFromString(ppathstr)
if err != nil {
return err
}
piter, err := globalTS.GetIter(ppath)
if err != nil {
return err
}
nrefs, err := lsGetInt(globalTS, piter, COL_NO_NREFS)
if err != nil {
return err
}
err = globalTS.SetValue(piter, COL_NO_NREFS, nrefs-1)
if err != nil {
return err
}
}
toggleHover()
return nil
}
@ -896,18 +1012,18 @@ func getRuleByIdx(idx, subidx int) (ruleColumns, *gtk.TreeIter, error) {
}
// Needs to be locked by the caller
func getSelectedRule() (ruleColumns, int, error) {
func getSelectedRule() (ruleColumns, int, int, error) {
rule := ruleColumns{}
sel, err := globalTV.GetSelection()
if err != nil {
return rule, -1, err
return rule, -1, -1, err
}
rows := sel.GetSelectedRows(globalTS)
if rows.Length() <= 0 {
return rule, -1, errors.New("no selection was made")
return rule, -1, -1, errors.New("no selection was made")
}
rdata := rows.NthData(0)
@ -917,45 +1033,57 @@ func getSelectedRule() (ruleColumns, int, error) {
ptoks := strings.Split(tpath, ":")
if len(ptoks) > 2 {
return rule, -1, errors.New("internal error parsing selected item tree path")
return rule, -1, -1, errors.New("internal error parsing selected item tree path")
} else if len(ptoks) == 2 {
subidx, err = strconv.Atoi(ptoks[1])
if err != nil {
return rule, -1, err
return rule, -1, -1, err
}
tpath = ptoks[0]
}
lIndex, err := strconv.Atoi(tpath)
if err != nil {
return rule, -1, err
return rule, -1, -1, err
}
fmt.Printf("lindex = %d : %d\n", lIndex, subidx)
// fmt.Printf("lindex = %d : %d\n", lIndex, subidx)
rule, _, err = getRuleByIdx(lIndex, subidx)
if err != nil {
return rule, -1, err
return rule, -1, -1, err
}
return rule, lIndex, nil
return rule, lIndex, subidx, nil
}
func buttonAction(action string) {
globalPromptLock.Lock()
rule, idx, err := getSelectedRule()
rule, idx, subidx, err := getSelectedRule()
if err != nil {
globalPromptLock.Unlock()
promptError("Error occurred processing request: " + err.Error())
return
}
rule, err = createCurrentRule()
urule, err := createCurrentRule()
if err != nil {
globalPromptLock.Unlock()
promptError("Error occurred constructing new rule: " + err.Error())
return
}
// Overlay the rules
rule.Scope = urule.Scope
//rule.Path = urule.Path
rule.Port = urule.Port
rule.Target = urule.Target
rule.Proto = urule.Proto
// rule.UID = urule.UID
// rule.GID = urule.GID
// rule.Uname = urule.Uname
// rule.Gname = urule.Gname
rule.ForceTLS = urule.ForceTLS
fmt.Println("rule = ", rule)
rulestr := action
@ -966,8 +1094,9 @@ func buttonAction(action string) {
rulestr += "|" + rule.Proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port)
rulestr += "|" + sgfw.RuleModeString[sgfw.RuleMode(rule.Scope)]
fmt.Println("RULESTR = ", rulestr)
makeDecision(idx, rulestr, int(rule.Scope))
err = removeSelectedRule(idx)
makeDecision(rulestr, int(rule.Scope), rule.GUID)
err = removeSelectedRule(idx, subidx)
addRecentlyRemoved(rule.GUID)
globalPromptLock.Unlock()
if err == nil {
clearEditor()
@ -1087,6 +1216,7 @@ func main() {
// globalIcon.SetFromIconName("firefox", gtk.ICON_SIZE_DND)
editApp = get_entry("")
editApp.SetEditable(false)
editApp.Connect("changed", toggleValidRuleState)
hbox.PackStart(lbl, false, false, 10)
hbox.PackStart(editApp, true, true, 10)
@ -1198,7 +1328,7 @@ func main() {
// tv.SetActivateOnSingleClick(true)
tv.Connect("row-activated", func() {
globalPromptLock.Lock()
seldata, _, err := getSelectedRule()
seldata, _, _, err := getSelectedRule()
globalPromptLock.Unlock()
if err != nil {
promptError("Unexpected error reading selected rule: " + err.Error())

@ -147,9 +147,3 @@ type DbusRule struct {
Mode uint16
Sandbox string
}
/*const (
OZ_FWRULE_WHITELIST = iota
OZ_FWRULE_BLACKLIST
OZ_FWRULE_NONE
) */

@ -261,12 +261,12 @@ func (ds *dbusServer) GetPendingRequests(policy string) (bool, *dbus.Error) {
return succeeded, nil
}
func (ds *dbusServer) AddRuleAsync(scope uint32, rule string, policy string) (bool, *dbus.Error) {
log.Warningf("AddRuleAsync %v, %v / %v\n", scope, rule, policy)
func (ds *dbusServer) AddRuleAsync(scope uint32, rule, policy, guid string) (bool, *dbus.Error) {
log.Warningf("AddRuleAsync %v, %v / %v / %v\n", scope, rule, policy, guid)
ds.fw.lock.Lock()
defer ds.fw.lock.Unlock()
prule := PendingRule{rule: rule, scope: int(scope), policy: policy}
prule := PendingRule{rule: rule, scope: int(scope), policy: policy, guid: guid}
for pname := range ds.fw.policyMap {
log.Debug("+++ Adding prule to policy")

@ -85,5 +85,14 @@ func loadDesktopFile(path string) {
icon: icon,
name: name,
}
lname := exec
for i := 0; i < 5; i++ {
lname, err = os.Readlink(lname)
if err == nil {
entryMap[lname] = entryMap[exec]
}
}
}
}

@ -139,19 +139,6 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
c.Write([]byte(ruledesc))
}
/* for i := 0; i < len(sandboxRules); i++ {
rulestr := ""
if sandboxRules[i].Whitelist {
rulestr += "whitelist"
} else {
rulestr += "blacklist"
}
rulestr += " " + sandboxRules[i].SrcIf.String() + " -> " + sandboxRules[i].DstIP.String() + " : " + strconv.Itoa(int(sandboxRules[i].DstPort)) + "\n"
c.Write([]byte(rulestr))
} */
return
} else {
tokens := strings.Split(data, " ")

@ -23,17 +23,6 @@ var _interpreters = []string{
"bash",
}
/*type sandboxRule struct {
SrcIf net.IP
DstIP net.IP
DstPort uint16
Whitelist bool
}
var sandboxRules = []sandboxRule {
// { net.IP{172,16,1,42}, net.IP{140,211,166,134}, 21, false },
} */
type pendingConnection interface {
policy() *Policy
procInfo() *procsnitch.Info
@ -222,6 +211,7 @@ type PendingRule struct {
rule string
scope int
policy string
guid string
}
type Policy struct {
@ -313,7 +303,6 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, timestamp time.Time, pinf
if !FirewallConfig.LogRedact {
log.Infof("Lookup(%s): %s", dstip.String(), name)
}
// fwo := matchAgainstOzRules(srcip, dstip, dstp)
result := p.rules.filterPacket(pkt, pinfo, srcip, name, optstr)
switch result {
@ -331,12 +320,8 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, timestamp time.Time, pinf
func (p *Policy) processPromptResult(pc pendingConnection) {
p.pendingQueue = append(p.pendingQueue, pc)
//fmt.Println("processPromptResult(): p.promptInProgress = ", p.promptInProgress)
//if DoMultiPrompt || (!DoMultiPrompt && !p.promptInProgress) {
// if !p.promptInProgress {
p.promptInProgress = true
go p.fw.dbus.prompter.prompt(p)
// }
}
func (p *Policy) nextPending() (pendingConnection, bool) {
@ -372,6 +357,15 @@ func (p *Policy) removePending(pc pendingConnection) {
}
}
func (p *Policy) processNewRuleOnce(r *Rule, guid string) bool {
p.lock.Lock()
defer p.lock.Unlock()
fmt.Println("----------------------- processNewRule() ONCE")
p.filterPendingOne(r, guid)
return true
}
func (p *Policy) processNewRule(r *Rule, scope FilterScope) bool {
p.lock.Lock()
defer p.lock.Unlock()
@ -419,6 +413,47 @@ func (p *Policy) removeRule(r *Rule) {
p.rules = newRules
}
func (p *Policy) filterPendingOne(rule *Rule, guid string) {
remaining := []pendingConnection{}
for _, pc := range p.pendingQueue {
if guid != "" && guid != pc.getGUID() {
continue
}
if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.proto(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID), pc.procInfo().Sandbox) {
prompter := pc.getPrompter()
if prompter == nil {
fmt.Println("-------- prompter = NULL")
} else {
call := prompter.dbusObj.Call("com.subgraph.FirewallPrompt.RemovePrompt", 0, pc.getGUID())
fmt.Println("CAAAAAAAAAAAAAAALL = ", call)
}
log.Infof("Adding rule for: %s", rule.getString(FirewallConfig.LogRedact))
// log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print())
if rule.rtype == RULE_ACTION_ALLOW {
pc.accept()
} else if rule.rtype == RULE_ACTION_ALLOW_TLSONLY {
pc.acceptTLSOnly()
} else {
srcs := pc.src().String() + ":" + strconv.Itoa(int(pc.srcPort()))
log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d (user prompt) %v",
pc.procInfo().ExePath, pc.proto(), srcs, pc.dst(), pc.dstPort, rule.rtype)
pc.drop()
}
// XXX: If matching a GUID, we can break out immediately
} else {
remaining = append(remaining, pc)
}
}
if len(remaining) != len(p.pendingQueue) {
p.pendingQueue = remaining
}
}
func (p *Policy) filterPending(rule *Rule) {
remaining := []pendingConnection{}
for _, pc := range p.pendingQueue {
@ -532,18 +567,6 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket, timestamp time.Time) {
*/
_, dstip := getPacketIPAddrs(pkt)
/* _, dstp := getPacketPorts(pkt)
fwo := eatchAgainstOzRules(srcip, dstip, dstp)
log.Notice("XXX: Attempting [2] to filter packet on rules -> ", fwo)
if fwo == OZ_FWRULE_WHITELIST {
log.Noticef("Automatically passed through whitelisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp)
pkt.Accept()
return
} else if fwo == OZ_FWRULE_BLACKLIST {
log.Noticef("Automatically blocking blacklisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp)
pkt.SetMark(1)
pkt.Accept()
return
} */
ppath := "*"
@ -942,21 +965,3 @@ func getPacketPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) {
return s, d
}
/*func matchAgainstOzRules(srci, dsti net.IP, dstp uint16) int {
for i := 0; i < len(sandboxRules); i++ {
log.Notice("XXX: Attempting to match: ", srci, " / ", dsti, " / ", dstp, " | ", sandboxRules[i])
if sandboxRules[i].SrcIf.Equal(srci) && sandboxRules[i].DstIP.Equal(dsti) && sandboxRules[i].DstPort == dstp {
if sandboxRules[i].Whitelist {
return OZ_FWRULE_WHITELIST
}
return OZ_FWRULE_BLACKLIST
}
}
return OZ_FWRULE_NONE
} */

@ -61,6 +61,9 @@ func (p *prompter) processNextPacket() bool {
p.lock.Lock()
pc, empty = p.nextConnection()
p.lock.Unlock()
if pc != nil {
fmt.Println("GOT NON NIL")
}
//fmt.Println("XXX: processNextPacket() loop; empty = ", empty, " / pc = ", pc)
if pc == nil && empty {
return false
@ -268,38 +271,6 @@ func (p *prompter) processConnection(pc pendingConnection) {
return
/* p.dbusObj.Go("com.subgraph.FirewallPrompt.RequestPrompt", 0, callChan,
pc.getGUID(),
policy.application,
policy.icon,
policy.path,
addr,
int32(pc.dstPort()),
dststr,
pc.src().String(),
pc.proto(),
int32(pc.procInfo().UID),
int32(pc.procInfo().GID),
uidToUser(pc.procInfo().UID),
gidToGroup(pc.procInfo().GID),
int32(pc.procInfo().Pid),
pc.sandbox(),
pc.socks(),
pc.getOptString(),
FirewallConfig.PromptExpanded,
FirewallConfig.PromptExpert,
int32(FirewallConfig.DefaultActionID))
saveChannel(callChan, false, true)
/* err := call.Store(&scope, &rule)
if err != nil {
log.Warningf("Error sending dbus RequestPrompt message: %v", err)
policy.removePending(pc)
pc.drop()
return
} */
// the prompt sends:
// ALLOW|dest or DENY|dest
//
@ -370,6 +341,7 @@ func (p *prompter) nextConnection() (pendingConnection, bool) {
fmt.Println("policy queue len = ", len(p.policyQueue))
for pind < len(p.policyQueue) {
fmt.Printf("policy loop %d of %d\n", pind, len(p.policyQueue))
//fmt.Printf("XXX: pind = %v of %v\n", pind, len(p.policyQueue))
policy := p.policyQueue[pind]
pc, qempty := policy.nextPending()
@ -379,21 +351,54 @@ func (p *prompter) nextConnection() (pendingConnection, bool) {
continue
} else {
pind++
// if pc == nil && !qempty {
if len(policy.rulesPending) > 0 {
fmt.Println("policy rules pending = ", len(policy.rulesPending))
pendingOnce := make([]PendingRule, 0)
pendingOther := make([]PendingRule, 0)
for _, r := range policy.rulesPending {
if r.scope == int(APPLY_ONCE) {
pendingOnce = append(pendingOnce, r)
} else {
pendingOther = append(pendingOther, r)
}
}
fmt.Printf("# pending once = %d, other = %d, pc = %p / policy = %p\n", len(pendingOnce), len(pendingOther), pc, policy)
policy.rulesPending = pendingOther
// One time filters are all applied right here, at once.
for _, pr := range pendingOnce {
toks := strings.Split(pr.rule, "|")
sandbox := ""
if len(toks) > 2 {
sandbox = toks[2]
}
tempRule := fmt.Sprintf("%s|%s", toks[0], toks[1])
tempRule += "||-1:-1|" + sandbox + "|"
r, err := policy.parseRule(tempRule, false)
if err != nil {
log.Warningf("Error parsing rule string returned from dbus RequestPrompt: %v", err)
continue
}
r.mode = RuleMode(pr.scope)
fmt.Println("+++++++ processing one time rule: ", pr.rule)
policy.processNewRuleOnce(r, pr.guid)
}
// if pc == nil && !qempty {
if len(policy.rulesPending) > 0 {
fmt.Println("non/once policy rules pending = ", len(policy.rulesPending))
prule := policy.rulesPending[0]
policy.rulesPending = append(policy.rulesPending[:0], policy.rulesPending[1:]...)
toks := strings.Split(prule.rule, "|")
sandbox := ""
if len(toks) > 2 {
sandbox = toks[2]
}
sandbox += ""
tempRule := fmt.Sprintf("%s|%s", toks[0], toks[1])
tempRule += "||-1:-1|" + sandbox + "|"

@ -592,7 +592,6 @@ select_loop:
if !cr.client && s == SSL3_MT_HELLO_REQUEST {
fmt.Println("Server sent hello request")
continue
}
if s > SSL3_MT_CERTIFICATE_STATUS {

Loading…
Cancel
Save