diff --git a/fw-prompt/README.txt b/fw-prompt/README.txt index 8a4378a..deece26 100644 --- a/fw-prompt/README.txt +++ b/fw-prompt/README.txt @@ -1,3 +1,33 @@ To get this to work, first edit /usr/share/gnome-shell/modes/subgraph.json Remove the entry for "firewall@subgraph.com", hit CTRL+F2 and then issue the reload "r" command. Reset these changes to revert back to the old gnome-shell prompt. + + + + +Changes for getting this working in ubuntu: +(this is the crux of, but not all of the steps necessary to get this running) + +apt-get install libnetfilter-queue-dev +#apt-get install all dependencies: gtk3/cairo/pango/glib etc. + + +mkdir /etc/sgfw +cat >> /etc/sgfw/sgfw.conf << EOF +log_level="NOTICE" +log_redact=false +prompt_expanded=true +prompt_expert=true +default_action="SESSION" +EOF + + +cp ./src/github.com/subgraph/fw-daemon/sources/etc/dbus-1/system.d/com.subgraph.Firewall.conf /etc/dbus-1/system.d/com.subgraph.Firewall.conf +iptables -t mangle -I PREROUTING 1 -m conntrack --ctstate NEW --proto tcp -j NFQUEUE --queue-num 0 --queue-bypass; iptables -I FORWARD 1 -m mark --mark 0x1 -j REJECT --reject-with icmp-host-prohibited + +go install github.com/subgraph/fw-daemon +go install github.com/subgraph/fw-daemon/fw-settings +go install github.com/subgraph/fw-daemon/fw-prompt + +GODEBUG=cgocheck=0 ./fw-daemon +Then launch ./fw-prompt diff --git a/fw-prompt/fw-prompt.go b/fw-prompt/fw-prompt.go index 7d484f2..e50057a 100644 --- a/fw-prompt/fw-prompt.go +++ b/fw-prompt/fw-prompt.go @@ -6,6 +6,7 @@ import ( "github.com/gotk3/gotk3/glib" "log" "fmt" + "strings" "strconv" "os" "io/ioutil" @@ -42,7 +43,10 @@ type ruleColumns struct { Port int UID int GID int + Uname string + Gname string Origin string + Scope int } @@ -53,14 +57,17 @@ var globalLS *gtk.ListStore var globalTV *gtk.TreeView var decisionWaiters []*decisionWaiter -var editApp, editTarget, editPort *gtk.Entry +var editApp, editTarget, editPort, editUser, editGroup *gtk.Entry var comboProto *gtk.ComboBoxText +var radioOnce, radioProcess, radioParent, radioSession, radioPermanent *gtk.RadioButton +var btnApprove, btnDeny, btnIgnore *gtk.Button +var chkUser, chkGroup *gtk.CheckButton func dumpDecisions() { - fmt.Println("Total of decisions pending: ", len(decisionWaiters)) + fmt.Println("XXX Total of decisions pending: ", len(decisionWaiters)) for i := 0; i < len(decisionWaiters); i++ { - fmt.Printf("%d ready = %v, rule = %v\n", i+1, decisionWaiters[i].Ready, decisionWaiters[i].Rule) + fmt.Printf("XXX %d ready = %v, rule = %v\n", i+1, decisionWaiters[i].Ready, decisionWaiters[i].Rule) } } @@ -174,7 +181,6 @@ func loadPreferences() bool { } prefPath := usr.HomeDir + "/.fwprompt.json" - fmt.Println("xxxxxxxxxxxxxxxxxxxxxx preferences path = ", prefPath) jfile, err := ioutil.ReadFile(prefPath) @@ -214,6 +220,17 @@ func get_vbox() *gtk.Box { return vbox } +func get_checkbox(text string, activated bool) *gtk.CheckButton { + cb, err := gtk.CheckButtonNewWithLabel(text) + + if err != nil { + log.Fatal("Unable to create new checkbox:", err) + } + + cb.SetActive(activated) + return cb +} + func get_combobox() *gtk.ComboBoxText { combo, err := gtk.ComboBoxTextNew() @@ -336,6 +353,7 @@ func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, h decision := addDecision() dumpDecisions() + toggleHover() return decision } @@ -359,7 +377,7 @@ func setup_settings() { } scrollbox.Add(box) - scrollbox.SetSizeRequest(600, 800) + scrollbox.SetSizeRequest(600, 400) tv, err := gtk.TreeViewNew() @@ -438,10 +456,74 @@ func makeDecision(idx int, rule string, scope int) { decisionWaiters[idx].Cond.L.Unlock() } +func toggleHover() { + mainWin.SetKeepAbove(len(decisionWaiters) > 0) +} + +func toggleValidRuleState() { + ok := true + + if numSelections() <= 0 { + ok = false + } + + str, err := editApp.GetText() + if err != nil || strings.Trim(str, "\t ") == "" { + ok = false + } + + str, err = editTarget.GetText() + if err != nil || strings.Trim(str, "\t ") == "" { + ok = false + } + + str, err = editPort.GetText() + if err != nil || strings.Trim(str, "\t ") == "" { + ok = false + } else { + pval, err := strconv.Atoi(str) + + if err != nil || pval < 0 || pval > 65535 { + ok = false + } + } + + if chkUser.GetActive() { + str, err = editUser.GetText() + if err != nil || strings.Trim(str, "\t ") == "" { + ok = false + } + } + + if chkGroup.GetActive() { + str, err = editGroup.GetText() + if err != nil || strings.Trim(str, "\t ") == "" { + ok = false + } + } + + + btnApprove.SetSensitive(ok) + btnDeny.SetSensitive(ok) + btnIgnore.SetSensitive(ok) +} + func createCurrentRule() (ruleColumns, error) { - rule := ruleColumns{} + rule := ruleColumns{Scope: int(sgfw.APPLY_ONCE)} var err error = nil + if radioProcess.GetActive() { + return rule, errors.New("Process scope is unsupported at the moment") + } else if radioParent.GetActive() { + return rule, errors.New("Parent process scope is unsupported at the moment") + } else if radioSession.GetActive() { + rule.Scope = int(sgfw.APPLY_SESSION) + } else if radioPermanent.GetActive() { + rule.Scope = int(sgfw.APPLY_FOREVER) + } else { + rule.Scope = int(sgfw.APPLY_ONCE) + } + rule.Path, err = editApp.GetText() if err != nil { return rule, err @@ -465,6 +547,7 @@ func createCurrentRule() (ruleColumns, error) { rule.Proto = comboProto.GetActiveID() rule.UID, rule.GID = 0, 0 + rule.Uname, rule.Gname = "", "" /* Pid int Origin string */ @@ -475,12 +558,16 @@ func clearEditor() { editApp.SetText("") editTarget.SetText("") editPort.SetText("") + editUser.SetText("") + editGroup.SetText("") comboProto.SetActive(0) -/* ronce.SetActive(true) - rprocess.SetActive(false) - rparent.SetActive(false) - rsession.SetActive(false) - rpermanent.SetActive(false) */ + radioOnce.SetActive(true) + radioProcess.SetActive(false) + radioParent.SetActive(false) + radioSession.SetActive(false) + radioPermanent.SetActive(false) + chkUser.SetActive(false) + chkGroup.SetActive(false) } func removeSelectedRule(idx int) error { @@ -503,9 +590,20 @@ func removeSelectedRule(idx int) error { } decisionWaiters = append(decisionWaiters[:idx], decisionWaiters[idx+1:]...) + toggleHover() return nil } +func numSelections() int { + sel, err := globalTV.GetSelection() + if err != nil { + return -1 + } + + rows := sel.GetSelectedRows(globalLS) + return int(rows.Length()) +} + func getSelectedRule() (ruleColumns, int, error) { rule := ruleColumns{} @@ -568,7 +666,15 @@ func getSelectedRule() (ruleColumns, int, error) { return rule, -1, err } - rule.UID, rule.GID = 0, 0 + rule.UID, err = lsGetInt(globalLS, iter, 7) + if err != nil { + return rule, -1, err + } + + rule.GID, err = lsGetInt(globalLS, iter, 8) + if err != nil { + return rule, -1, err + } rule.Origin, err = lsGetStr(globalLS, iter, 9) if err != nil { @@ -619,7 +725,7 @@ func main() { log.Fatal("Unable to create new notebook:", err) } - loglevel := "Pending Approval" + loglevel := "Firewall Traffic Pending Approval" nbLabel, err := gtk.LabelNew(loglevel) @@ -652,41 +758,48 @@ func main() { tv.SetSizeRequest(300, 300) tv.SetHeadersClickable(true) - ab, err := gtk.ButtonNewWithLabel("Approve") + btnApprove, err = gtk.ButtonNewWithLabel("Approve") if err != nil { log.Fatal("Unable to create button:", err) } - db, err := gtk.ButtonNewWithLabel("Deny") + btnDeny, err = gtk.ButtonNewWithLabel("Deny") if err != nil { log.Fatal("Unable to create button:", err) } - ib, err := gtk.ButtonNewWithLabel("Ignore") + btnIgnore, err = gtk.ButtonNewWithLabel("Ignore") if err != nil { log.Fatal("Unable to create button:", err) } + btnApprove.SetSensitive(false) + btnDeny.SetSensitive(false) + btnIgnore.SetSensitive(false) + bb := get_hbox() - bb.PackStart(ab, false, false, 5) - bb.PackStart(db, false, false, 5) - bb.PackStart(ib, false, false, 5) + bb.PackStart(btnApprove, false, false, 5) + bb.PackStart(btnDeny, false, false, 5) + bb.PackStart(btnIgnore, false, false, 5) editbox := get_vbox() hbox := get_hbox() lbl := get_label("Application path:") editApp = get_entry("") + editApp.Connect("changed", toggleValidRuleState) hbox.PackStart(lbl, false, false, 10) - hbox.PackStart(editApp, false, false, 5) + hbox.PackStart(editApp, true, true, 50) editbox.PackStart(hbox, false, false, 5) hbox = get_hbox() lbl = get_label("Target host/IP:") editTarget = get_entry("") + editTarget.Connect("changed", toggleValidRuleState) hbox.PackStart(lbl, false, false, 10) hbox.PackStart(editTarget, false, false, 5) lbl = get_label("Port:") editPort = get_entry("") + editPort.Connect("changed", toggleValidRuleState) hbox.PackStart(lbl, false, false, 5) hbox.PackStart(editPort, false, false, 5) lbl = get_label("Protocol:") @@ -697,17 +810,34 @@ func main() { hbox = get_hbox() lbl = get_label("Apply rule:") - ronce := get_radiobutton(nil, "Once", true) - rprocess := get_radiobutton(ronce, "This Process", false) - rparent := get_radiobutton(ronce, "Parent Process", false) - rsession := get_radiobutton(ronce, "Session", false) - rpermanent := get_radiobutton(ronce, "Permanent", false) + radioOnce = get_radiobutton(nil, "Once", true) + radioProcess = get_radiobutton(radioOnce, "This Process", false) + radioParent = get_radiobutton(radioOnce, "Parent Process", false) + radioSession = get_radiobutton(radioOnce, "Session", false) + radioPermanent = get_radiobutton(radioOnce, "Permanent", false) + radioProcess.SetSensitive(false) + radioParent.SetSensitive(false) hbox.PackStart(lbl, false, false, 10) - hbox.PackStart(ronce, false, false, 5) - hbox.PackStart(rprocess, false, false, 5) - hbox.PackStart(rparent, false, false, 5) - hbox.PackStart(rsession, false, false, 5) - hbox.PackStart(rpermanent, false, false, 5) + hbox.PackStart(radioOnce, false, false, 5) + hbox.PackStart(radioProcess, false, false, 5) + hbox.PackStart(radioParent, false, false, 5) + hbox.PackStart(radioSession, false, false, 5) + hbox.PackStart(radioPermanent, false, false, 5) + editbox.PackStart(hbox, false, false, 5) + + hbox = get_hbox() + chkUser = get_checkbox("Apply to UID/username", false) + chkUser.Connect("toggled", toggleValidRuleState) + editUser = get_entry("") + editUser.Connect("changed", toggleValidRuleState) + hbox.PackStart(chkUser, false, false, 10) + hbox.PackStart(editUser, false, false, 10) + chkGroup = get_checkbox("Apply to GID/group:", false) + chkGroup.Connect("toggled", toggleValidRuleState) + editGroup = get_entry("") + editGroup.Connect("changed", toggleValidRuleState) + hbox.PackStart(chkGroup, false, false, 10) + hbox.PackStart(editGroup, false, false, 10) editbox.PackStart(hbox, false, false, 5) box.PackStart(bb, false, false, 5) @@ -731,7 +861,7 @@ func main() { tv.SetModel(listStore) - ab.Connect("clicked", func() { + btnApprove.Connect("clicked", func() { rule, idx, err := getSelectedRule() if err != nil { promptError("Error occurred processing request: "+err.Error()) @@ -747,7 +877,7 @@ func main() { fmt.Println("rule = ", rule) rulestr := "ALLOW|" + rule.Proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port) fmt.Println("RULESTR = ", rulestr) - makeDecision(idx, rulestr, int(sgfw.APPLY_ONCE)) + makeDecision(idx, rulestr, int(rule.Scope)) fmt.Println("Decision made.") err = removeSelectedRule(idx) if err == nil { @@ -757,7 +887,7 @@ func main() { } }) - db.Connect("clicked", func() { + btnDeny.Connect("clicked", func() { rule, idx, err := getSelectedRule() if err != nil { promptError("Error occurred processing request: "+err.Error()) @@ -773,7 +903,7 @@ func main() { fmt.Println("rule = ", rule) rulestr := "DENY|" + rule.Proto + ":" + rule.Target + ":" + strconv.Itoa(rule.Port) fmt.Println("RULESTR = ", rulestr) - makeDecision(idx, rulestr, int(sgfw.APPLY_ONCE)) + makeDecision(idx, rulestr, int(rule.Scope)) fmt.Println("Decision made.") err = removeSelectedRule(idx) if err == nil { @@ -783,21 +913,24 @@ func main() { } }) - ib.Connect("clicked", func() { - promptError("Ignoring firewall request.") - return -/* promptError("Ignoring firewall request.") - fmt.Println("LOCKING") - decisionWaiters[0].Cond.L.Lock() - decisionWaiters[0].Ready = true - decisionWaiters[0].Rule = "bloop done" - fmt.Println("SIGNALING") - decisionWaiters[0].Cond.Signal() - fmt.Println("SIGNALED") - fmt.Println("UNLOCKING") - decisionWaiters[0].Cond.L.Unlock() */ + btnIgnore.Connect("clicked", func() { + _, idx, err := getSelectedRule() + if err != nil { + promptError("Error occurred processing request: "+err.Error()) + return + } + + makeDecision(idx, "", 0) + fmt.Println("Decision made.") + err = removeSelectedRule(idx) + if err == nil { + clearEditor() + } else { + promptError("Error setting new rule: "+err.Error()) + } }) +// tv.SetActivateOnSingleClick(true) tv.Connect("row-activated", func() { seldata, _, err := getSelectedRule() if err != nil { @@ -814,28 +947,45 @@ func main() { } editPort.SetText(strconv.Itoa(seldata.Port)) - ronce.SetActive(true) - rprocess.SetActive(false) - rparent.SetActive(false) - rsession.SetActive(false) - rpermanent.SetActive(false) + radioOnce.SetActive(true) + radioProcess.SetActive(false) + radioParent.SetActive(false) + radioSession.SetActive(false) + radioPermanent.SetActive(false) comboProto.SetActiveID(seldata.Proto) + if seldata.Uname != "" { + editUser.SetText(seldata.Uname) + } else if seldata.UID != -1 { + editUser.SetText(strconv.Itoa(seldata.UID)) + } else { + editUser.SetText("") + } + + if seldata.Gname != "" { + editGroup.SetText(seldata.Gname) + } else if seldata.GID != -1 { + editGroup.SetText(strconv.Itoa(seldata.GID)) + } else { + editGroup.SetText("") + } + + chkUser.SetActive(false) + chkGroup.SetActive(false) return -// promptInfo(sval) }) - scrollbox.SetSizeRequest(600, 800) + scrollbox.SetSizeRequest(600, 400) Notebook.AppendPage(scrollbox, nbLabel) - setup_settings() +// setup_settings() mainWin.Add(Notebook) if userPrefs.Winheight > 0 && userPrefs.Winwidth > 0 { - fmt.Printf("height was %d, width was %d\n", userPrefs.Winheight, userPrefs.Winwidth) +// fmt.Printf("height was %d, width was %d\n", userPrefs.Winheight, userPrefs.Winwidth) mainWin.Resize(int(userPrefs.Winwidth), int(userPrefs.Winheight)) } else { - mainWin.SetDefaultSize(800, 600) + mainWin.SetDefaultSize(850, 450) } if userPrefs.Wintop > 0 && userPrefs.Winleft > 0 { @@ -843,6 +993,6 @@ func main() { } mainWin.ShowAll() - mainWin.SetKeepAbove(true) +// mainWin.SetKeepAbove(true) gtk.Main() }