diff --git a/fw-settings/definitions/RuleItem.ui b/fw-settings/definitions/RuleItem.ui
index 1287114..afe2ff6 100644
--- a/fw-settings/definitions/RuleItem.ui
+++ b/fw-settings/definitions/RuleItem.ui
@@ -31,6 +31,31 @@
0
+
+
+
+ 2
+ 0
+
+
+
+
+
+ 3
+ 0
+
+
+
- 2
+ 4
0
@@ -60,7 +85,7 @@
- 3
+ 5
0
@@ -81,7 +106,7 @@
- 5
+ 7
0
@@ -103,7 +128,7 @@
- 4
+ 6
0
diff --git a/fw-settings/definitions/rule_item.go b/fw-settings/definitions/rule_item.go
index 4758df8..648294b 100644
--- a/fw-settings/definitions/rule_item.go
+++ b/fw-settings/definitions/rule_item.go
@@ -41,6 +41,30 @@ func (*defRuleItem) String() string {
0
+
+
+
+ 2
+ 0
+
+
+
+
+
+ 3
+ 0
+
+
- 2
+ 4
0
@@ -70,7 +94,7 @@ func (*defRuleItem) String() string {
- 3
+ 5
0
@@ -91,7 +115,7 @@ func (*defRuleItem) String() string {
- 5
+ 7
0
@@ -113,7 +137,7 @@ func (*defRuleItem) String() string {
- 4
+ 6
0
diff --git a/fw-settings/rules.go b/fw-settings/rules.go
index 8b7387b..dfca6c2 100644
--- a/fw-settings/rules.go
+++ b/fw-settings/rules.go
@@ -17,6 +17,8 @@ type ruleList struct {
col1 *gtk.SizeGroup
col2 *gtk.SizeGroup
col3 *gtk.SizeGroup
+ col4 *gtk.SizeGroup
+ col5 *gtk.SizeGroup
}
type ruleRow struct {
@@ -25,6 +27,8 @@ type ruleRow struct {
widget *gtk.ListBoxRow
gtkLabelApp *gtk.Label
gtkLabelVerb *gtk.Label
+ gtkLabelOrigin *gtk.Label
+ gtkLabelPrivs *gtk.Label
gtkLabelTarget *gtk.Label
gtkButtonEdit *gtk.Button
gtkButtonSave *gtk.Button
@@ -37,6 +41,8 @@ func newRuleList(dbus *dbusObject, win *gtk.Window, list *gtk.ListBox) *ruleList
rl.col1, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col2, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
rl.col3, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
+ rl.col4, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
+ rl.col5, _ = gtk.SizeGroupNew(gtk.SIZE_GROUP_HORIZONTAL)
return rl
}
@@ -59,7 +65,9 @@ func (rl *ruleList) addRules(rules []sgfw.DbusRule, mode sgfw.RuleMode) {
row.rl = rl
rl.col1.AddWidget(row.gtkLabelApp)
rl.col2.AddWidget(row.gtkLabelVerb)
- rl.col3.AddWidget(row.gtkLabelTarget)
+ rl.col3.AddWidget(row.gtkLabelOrigin)
+ rl.col4.AddWidget(row.gtkLabelPrivs)
+ rl.col5.AddWidget(row.gtkLabelTarget)
rl.list.Add(row.widget)
}
}
@@ -73,6 +81,8 @@ func createWidget(rule *sgfw.DbusRule) *ruleRow {
"grid", &grid,
"app_label", &row.gtkLabelApp,
"verb_label", &row.gtkLabelVerb,
+ "origin_label", &row.gtkLabelOrigin,
+ "privs_label", &row.gtkLabelPrivs,
"target_label", &row.gtkLabelTarget,
"edit_button", &row.gtkButtonEdit,
"save_button", &row.gtkButtonSave,
@@ -106,6 +116,8 @@ func (rr *ruleRow) update() {
rr.gtkLabelApp.SetText(rr.rule.App)
rr.gtkLabelApp.SetTooltipText(rr.rule.Path)
rr.gtkLabelVerb.SetText(getVerbText(rr.rule))
+ rr.gtkLabelOrigin.SetText(rr.rule.Origin)
+ rr.gtkLabelPrivs.SetText(rr.rule.Privs)
rr.gtkLabelTarget.SetText(getTargetText(rr.rule))
}
@@ -167,7 +179,9 @@ func (rr *ruleRow) onDelete() {
func (rl *ruleList) remove(rr *ruleRow) {
rl.col1.RemoveWidget(rr.gtkLabelApp)
rl.col2.RemoveWidget(rr.gtkLabelVerb)
- rl.col3.RemoveWidget(rr.gtkLabelTarget)
+ rl.col3.RemoveWidget(rr.gtkLabelOrigin)
+ rl.col4.RemoveWidget(rr.gtkLabelPrivs)
+ rl.col5.RemoveWidget(rr.gtkLabelTarget)
rl.list.Remove(rr.widget)
}
diff --git a/gnome-shell/firewall@subgraph.com/dialog.js b/gnome-shell/firewall@subgraph.com/dialog.js
index 1ba44d4..196392c 100644
--- a/gnome-shell/firewall@subgraph.com/dialog.js
+++ b/gnome-shell/firewall@subgraph.com/dialog.js
@@ -30,6 +30,7 @@ const DetailSection = new Lang.Class({
this.pid = this._addDetails("Process ID:");
this.origin = this._addDetails("Origin:");
this.user = this._addDetails("User:");
+ this.group = this._addDetails("Group:");
this.optstring = this._addDetails("");
},
@@ -41,7 +42,7 @@ const DetailSection = new Lang.Class({
return msg;
},
- setDetails: function(ip, path, pid, user, origin, optstring) {
+ setDetails: function(ip, path, pid, uid, gid, user, group, origin, optstring) {
this.ipAddr.text = ip;
this.path.text = path;
@@ -52,7 +53,19 @@ const DetailSection = new Lang.Class({
}
this.origin.text = origin;
- this.user.text = user;
+
+ if (user != "") {
+ this.user.text = user + " (" + uid.toString() + ")";
+ } else {
+ this.user.text = "uid:" + uid.toString();
+ }
+
+ if (group != "") {
+ this.group.text = group + " (" + gid.toString() + ")";
+ } else {
+ this.group.text = "gid:" + gid.toString();
+ }
+
this.optstring.text = optstring
}
});
@@ -459,7 +472,7 @@ const PromptDialog = new Lang.Class({
}
},
- update: function(application, icon, path, address, port, ip, origin, user, pid, proto, optstring, expanded, expert, action) {
+ update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action) {
this._address = address;
this._port = port;
@@ -488,6 +501,6 @@ const PromptDialog = new Lang.Class({
}
this.optionList.buttonGroup._setChecked(this.optionList.scopeToIdx(action))
- this.info.setDetails(ip, path, pid, user, origin, optstring);
+ this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, optstring);
},
});
diff --git a/gnome-shell/firewall@subgraph.com/extension.js b/gnome-shell/firewall@subgraph.com/extension.js
index 981bc3f..99c60ca 100644
--- a/gnome-shell/firewall@subgraph.com/extension.js
+++ b/gnome-shell/firewall@subgraph.com/extension.js
@@ -51,7 +51,10 @@ const FirewallPromptInterface = ' \
\
\
\
+ \
+ \
\
+ \
\
\
\
@@ -88,11 +91,11 @@ const FirewallPromptHandler = new Lang.Class({
},
RequestPromptAsync: function(params, invocation) {
- let [app, icon, path, address, port, ip, origin, user, pid, optstring, expanded, expert, action] = params;
+ let [app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, optstring, expanded, expert, action] = params;
this._closeDialog();
this._dialog = new Dialog.PromptDialog(invocation);
this._invocation = invocation;
- this._dialog.update(app, icon, path, address, port, ip, origin, user, pid, "TCP", optstring, expanded, expert, action);
+ this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, "TCP", optstring, expanded, expert, action);
this._dialog.open();
},
diff --git a/sgfw/const.go b/sgfw/const.go
index b6ed474..f82c280 100644
--- a/sgfw/const.go
+++ b/sgfw/const.go
@@ -105,7 +105,9 @@ var FilterResultValue = map[string]FilterResult{
// DbusRule struct of the rule passed to the dbus interface
type DbusRule struct {
ID uint32
-// Net string
+ Net string
+ Origin string
+ Privs string
App string
Path string
Verb uint16
diff --git a/sgfw/dbus.go b/sgfw/dbus.go
index 3797a23..7052ada 100644
--- a/sgfw/dbus.go
+++ b/sgfw/dbus.go
@@ -3,6 +3,7 @@ package sgfw
import (
"errors"
"path"
+ "strconv"
"github.com/godbus/dbus"
"github.com/godbus/dbus/introspect"
@@ -93,14 +94,31 @@ func (ds *dbusServer) IsEnabled() (bool, *dbus.Error) {
}
func createDbusRule(r *Rule) DbusRule {
-// XXX: Uncommenting will require fw-settings upgrade.
-/* netstr := ""
+ netstr := ""
if r.network != nil {
netstr = r.network.String()
- } */
+ }
+ ostr := ""
+ if r.saddr != nil {
+ ostr = r.saddr.String()
+ }
+ pstr := ""
+
+ if r.uname != "" {
+ pstr = r.uname
+ } else if r.uid >= 0 {
+ pstr = strconv.Itoa(r.uid)
+ }
+ if r.gname != "" {
+ pstr += ":" + r.gname
+ } else if r.gid >= 0 {
+ pstr += ":" + strconv.Itoa(r.gid)
+ }
return DbusRule{
ID: uint32(r.id),
-// Net: netstr,
+ Net: netstr,
+ Origin: ostr,
+ Privs: pstr,
App: path.Base(r.policy.path),
Path: r.policy.path,
Verb: uint16(r.rtype),
diff --git a/sgfw/policy.go b/sgfw/policy.go
index 7fa3f5a..347486c 100644
--- a/sgfw/policy.go
+++ b/sgfw/policy.go
@@ -60,7 +60,7 @@ type pendingPkt struct {
func getEmptyPInfo() *procsnitch.Info {
pinfo := procsnitch.Info{}
- pinfo.UID, pinfo.Pid, pinfo.ParentPid = -1, -1, -1
+ pinfo.UID, pinfo.GID, pinfo.Pid, pinfo.ParentPid = -1, -1, -1, -1
pinfo.ExePath = "[unknown-exe]"
pinfo.CmdLine = "[unknown-cmdline]"
pinfo.FirstArg = "[unknown-arg]"
@@ -294,7 +294,7 @@ func (p *Policy) removeRule(r *Rule) {
func (p *Policy) filterPending(rule *Rule) {
remaining := []pendingConnection{}
for _, pc := range p.pendingQueue {
- if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname()) {
+ if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID)) {
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 {
diff --git a/sgfw/prompt.go b/sgfw/prompt.go
index 8b83167..e1ee8ae 100644
--- a/sgfw/prompt.go
+++ b/sgfw/prompt.go
@@ -4,6 +4,7 @@ import (
"fmt"
"os/user"
"strconv"
+ "strings"
"sync"
"github.com/godbus/dbus"
@@ -76,7 +77,10 @@ func (p *prompter) processConnection(pc pendingConnection) {
int32(pc.dstPort()),
pc.dst().String(),
pc.src().String(),
+ int32(pc.procInfo().UID),
+ int32(pc.procInfo().GID),
uidToUser(pc.procInfo().UID),
+ gidToGroup(pc.procInfo().GID),
int32(pc.procInfo().Pid),
pc.getOptString(),
FirewallConfig.PromptExpanded,
@@ -91,7 +95,10 @@ func (p *prompter) processConnection(pc pendingConnection) {
}
if pc.src() != nil {
- rule += "|" + pc.src().String()
+ if !strings.HasSuffix(rule, "SYSTEM") && !strings.HasSuffix(rule, "||") {
+ rule += "|"
+ }
+ rule += "||" + pc.src().String()
}
r, err := policy.parseRule(rule, false)
@@ -142,6 +149,7 @@ func (p *prompter) removePolicy(policy *Policy) {
}
var userMap = make(map[int]string)
+var groupMap = make(map[int]string)
func lookupUser(uid int) string {
if uid == -1 {
@@ -151,7 +159,18 @@ func lookupUser(uid int) string {
if err != nil {
return fmt.Sprintf("%d", uid)
}
- return u.Name
+ return u.Username
+}
+
+func lookupGroup(gid int) string {
+ if gid == -1 {
+ return "[unknown]"
+ }
+ g, err := user.LookupGroupId(strconv.Itoa(gid))
+ if err != nil {
+ return fmt.Sprintf("%d", gid)
+ }
+ return g.Name
}
func uidToUser(uid int) string {
@@ -163,3 +182,13 @@ func uidToUser(uid int) string {
userMap[uid] = uname
return uname
}
+
+func gidToGroup(gid int) string {
+ gname, ok := groupMap[gid]
+ if ok {
+ return gname
+ }
+ gname = lookupGroup(gid)
+ groupMap[gid] = gname
+ return gname
+}
diff --git a/sgfw/rules.go b/sgfw/rules.go
index c41fd27..46db5f3 100644
--- a/sgfw/rules.go
+++ b/sgfw/rules.go
@@ -30,6 +30,10 @@ type Rule struct {
addr uint32
saddr net.IP
port uint16
+ uid int
+ gid int
+ uname string
+ gname string
}
func (r *Rule) String() string {
@@ -75,7 +79,16 @@ func (r *Rule) AddrString(redact bool) string {
type RuleList []*Rule
-func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string) bool {
+func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, uid, gid int, uname, gname string) bool {
+ if r.uid != -1 && r.uid != uid {
+ return false
+ } else if r.gid != -1 && r.gid != gid {
+ return false
+ } else if r.uname != "" && r.uname != uname {
+ return false
+ } else if r.gname != "" && r.gname != gname {
+ return false
+ }
xip := make(net.IP, 4)
binary.BigEndian.PutUint32(xip, r.addr)
@@ -126,7 +139,7 @@ log.Notice("! Skipping comparison against incompatible rule types: rule src = ",
log.Notice("! Skipping comparison of mismatching source ips")
continue
}
- if r.match(src, dst, dstPort, hostname) {
+ if r.match(src, dst, dstPort, hostname, pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) {
log.Notice("+ MATCH SUCCEEDED")
dstStr := dst.String()
if FirewallConfig.LogRedact {
@@ -170,25 +183,66 @@ func (r *Rule) parse(s string) bool {
r.addr = noAddress
r.saddr = nil
parts := strings.Split(s, "|")
- if len(parts) < 2 {
+ if len(parts) < 4 || len(parts) > 5 {
return false
}
- if len(parts) >= 3 && parts[2] == "SYSTEM" {
+ if parts[2] == "SYSTEM" {
r.mode = RULE_MODE_SYSTEM
+ } else if parts[2] != "" {
+ return false
+ }
+
+ if !r.parsePrivs(parts[3]) {
+ return false
+ }
+
+//fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname)
+
+ if len(parts) == 5 && len(strings.TrimSpace(parts[4])) > 0 {
+ r.saddr = net.ParseIP(parts[4])
- if len(parts) > 4 {
- r.saddr = net.ParseIP(parts[3])
+ if r.saddr == nil {
+ return false
}
- } else if len(parts) > 3 {
- r.saddr = net.ParseIP(parts[3])
- } else if len(parts) > 2 {
- r.saddr = net.ParseIP(parts[2])
}
return r.parseVerb(parts[0]) && r.parseTarget(parts[1])
}
+func (r *Rule) parsePrivs(p string) bool {
+ toks := strings.Split(p, ":")
+ if len(toks) > 2 {
+ return false
+ }
+ r.uid, r.gid = -1, -1
+ r.uname, r.gname = "", ""
+ ustr := toks[0]
+
+ uid, err := strconv.Atoi(ustr)
+
+ if err != nil {
+ r.uname = ustr
+ } else {
+ r.uid = uid
+ }
+
+ if len(toks) > 1 {
+ gstr := toks[1]
+
+ gid, err := strconv.Atoi(gstr)
+
+ if err != nil {
+ r.gname = gstr
+ } else {
+ r.gid = gid
+ }
+
+ }
+
+ return true
+}
+
func (r *Rule) parseVerb(v string) bool {
switch v {
case RuleActionString[RULE_ACTION_ALLOW]:
@@ -223,9 +277,14 @@ func (r *Rule) parseAddr(a string) bool {
// ip := net.ParseIP(a)
ip, ipnet, err := net.ParseCIDR(a)
if err != nil || ip == nil {
- return false
+ ip = net.ParseIP(a)
+
+ if ip == nil {
+ return false
+ }
+ } else {
+ r.network = ipnet
}
- r.network = ipnet
r.addr = binary.BigEndian.Uint32(ip.To4())
return true
}
@@ -332,8 +391,11 @@ func (fw *Firewall) loadRules() {
for _, line := range strings.Split(string(bs), "\n") {
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
policy = fw.processPathLine(line)
- } else if len(strings.TrimSpace(line)) > 0 {
- processRuleLine(policy, line)
+ } else {
+ trimmed := strings.TrimSpace(line)
+ if len(trimmed) > 0 && trimmed[:1] != "#" {
+ processRuleLine(policy, trimmed)
+ }
}
}
}
diff --git a/vendor/github.com/subgraph/go-procsnitch/proc_pid.go b/vendor/github.com/subgraph/go-procsnitch/proc_pid.go
index 6f2eab9..8338fd2 100644
--- a/vendor/github.com/subgraph/go-procsnitch/proc_pid.go
+++ b/vendor/github.com/subgraph/go-procsnitch/proc_pid.go
@@ -14,6 +14,7 @@ import (
// Info is a struct containing the result of a socket proc query
type Info struct {
UID int
+ GID int
Pid int
ParentPid int
loaded bool
@@ -175,6 +176,7 @@ func (pi *Info) loadProcessInfo() bool {
}
sys := finfo.Sys().(*syscall.Stat_t)
pi.UID = int(sys.Uid)
+ pi.GID = int(sys.Gid)
pi.ParentPid = ppid
pi.ParentCmdLine = string(pbs)
pi.ParentExePath = string(pexePath)