From 0666e9c3c76bb1ac91e9b193d4895897ae90b0db Mon Sep 17 00:00:00 2001 From: Stephen Watt Date: Mon, 2 Oct 2017 21:29:59 -0400 Subject: [PATCH] Added firewall testing framework. Fixed simple bug in fw-prompt that accidentally disabled process scope option. --- fw-prompt/README.txt | 2 +- fw-prompt/fw-prompt.go | 2 +- sgfw/dbus.go | 34 ++++ sgfw/policy.go | 6 +- sgfw/virtual.go | 180 ++++++++++++++++++ .../system.d/com.subgraph.Firewall.conf | 10 + testfw/dbus.go | 77 ++++++++ testfw/testfw.go | 36 ++++ 8 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 sgfw/virtual.go create mode 100644 testfw/dbus.go create mode 100644 testfw/testfw.go diff --git a/fw-prompt/README.txt b/fw-prompt/README.txt index deece26..8a7d344 100644 --- a/fw-prompt/README.txt +++ b/fw-prompt/README.txt @@ -1,5 +1,5 @@ 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. +Remove the entry for "firewall@subgraph.com", hit ALT+F2 and then issue the reload "r" command. Reset these changes to revert back to the old gnome-shell prompt. diff --git a/fw-prompt/fw-prompt.go b/fw-prompt/fw-prompt.go index 38f73d0..8db4389 100644 --- a/fw-prompt/fw-prompt.go +++ b/fw-prompt/fw-prompt.go @@ -1351,7 +1351,7 @@ func main() { editPort.SetText(strconv.Itoa(seldata.Port)) radioOnce.SetActive(seldata.Scope == int(sgfw.APPLY_ONCE)) - radioProcess.SetSensitive(seldata.Scope == int(sgfw.APPLY_PROCESS)) + radioProcess.SetActive(seldata.Scope == int(sgfw.APPLY_PROCESS)) radioParent.SetActive(false) radioSession.SetActive(seldata.Scope == int(sgfw.APPLY_SESSION)) radioPermanent.SetActive(seldata.Scope == int(sgfw.APPLY_FOREVER)) diff --git a/sgfw/dbus.go b/sgfw/dbus.go index fed6100..1e40396 100644 --- a/sgfw/dbus.go +++ b/sgfw/dbus.go @@ -4,6 +4,8 @@ import ( "errors" "path" "strconv" + "net" + "time" "github.com/godbus/dbus" "github.com/godbus/dbus/introspect" @@ -276,6 +278,38 @@ func (ds *dbusServer) AddRuleAsync(scope uint32, rule, policy, guid string) (boo return true, nil } +func (ds *dbusServer) AddTestVPC(proto string, srcip string, sport uint16, dstip string, dport uint16, hostname string) (bool, *dbus.Error) { + log.Warningf("AddTestVPC(proto=%s, srcip=%s, sport=%v, dstip=%s, dport=%v, hostname=%s)\n", + proto, srcip, sport, dstip, dport, hostname) + + sip := net.ParseIP(srcip) + if sip == nil { + log.Error("Test virtual rule supplied bad source IP: ", srcip) + return false, nil + } + + dip := net.ParseIP(srcip) + if dip == nil { + log.Error("Test virtual rule supplied bad dst IP: ", dstip) + return false, nil + } + + now := time.Now() + optstring := "[virtual connection (TEST)]" + pinfo := getEmptyPInfo() + + exepath := "/bin/bla" + pid := 666 + sandbox := "" + + policy := ds.fw.PolicyForPathAndSandbox(GetRealRoot(exepath, pid), sandbox) + vpc := &virtualPkt{_proto: proto, srcip: sip, sport: sport, dstip: dip, dport: dport, name: hostname, timestamp: now, optstring: optstring, pol: policy, pinfo: pinfo} + policy.processPromptResult(vpc) + + log.Warning("NEW VPC: ", vpc) + return true, nil +} + func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error { log.Debugf("UpdateRule %v", rule) ds.fw.lock.Lock() diff --git a/sgfw/policy.go b/sgfw/policy.go index 4f144c4..2fbb486 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -89,8 +89,8 @@ func (pp *pendingPkt) sandbox() string { return pp.pinfo.Sandbox } -func (pc *pendingPkt) getTimestamp() string { - return pc.timestamp.Format("15:04:05.00") +func (pp *pendingPkt) getTimestamp() string { + return pp.timestamp.Format("15:04:05.00") } func (pp *pendingPkt) socks() bool { @@ -701,7 +701,7 @@ func GetRealRoot(pathname string, pid int) string { lnk, err := os.Readlink(pfname) if err != nil { - fmt.Printf("Error reading link at %s: %v", pfname, err) + fmt.Printf("Error reading link at %s: %v\n", pfname, err) return pathname } diff --git a/sgfw/virtual.go b/sgfw/virtual.go new file mode 100644 index 0000000..7754fed --- /dev/null +++ b/sgfw/virtual.go @@ -0,0 +1,180 @@ +package sgfw + +import ( + "fmt" + "net" + "time" + "sync" + + "github.com/subgraph/go-procsnitch" + "github.com/godbus/dbus" +) + +type virtualPkt struct { + pol *Policy + name string + pinfo *procsnitch.Info + optstring string + prompting bool + prompter *prompter + guid string + timestamp time.Time + is_socks bool + _proto string + srcip net.IP + sport uint16 + dstip net.IP + dport uint16 +} + + +var tdb *dbusObjectP +var tdbMutex = &sync.Mutex{} +var tdbInit = false + + +func init() { + fmt.Println("Initializing virtual packet test subsystem...") + + conn, err := dbus.SystemBus() + if err != nil { + fmt.Println("Error setting up server on test DBus path:", err) + tdb = &dbusObjectP{nil} + } + + tdb = &dbusObjectP{conn.Object("com.subgraph.FirewallTest", "/com/subgraph/FirewallTest")} + tdbInit = true +} + +func sendSGFWTestAlert(accepted int, guid string, other string) bool { + var dres bool + + if !tdbInit { + fmt.Println("Skipping over invocation of SGFWTestAlert(); DBus method was not properly bound") + return false + } + + tdbMutex.Lock() + defer tdbMutex.Unlock() + + call := tdb.Call("com.subgraph.FirewallTest.SGFWTestAlert", 0, int32(accepted), guid, other) + err := call.Store(&dres) + if err != nil { + fmt.Println("Error sending DBus SGFWTestAlert() notification:", err) + return false + } + + return true +} + +func (vp *virtualPkt) sandbox() string { + return vp.pinfo.Sandbox +} + +func (vp *virtualPkt) getTimestamp() string { + return vp.timestamp.Format("15:04:05.00") +} + +func (vp *virtualPkt) socks() bool { + return vp.is_socks +} + +func (vp *virtualPkt) policy() *Policy { + return vp.pol +} + +func (vp *virtualPkt) procInfo() *procsnitch.Info { + if vp.pinfo == nil { + return getEmptyPInfo() + } + + return vp.pinfo +} + +func (vp *virtualPkt) getOptString() string { + return vp.optstring +} + +func (vp *virtualPkt) hostname() string { + return vp.name +} + +func (vp *virtualPkt) src() net.IP { + return vp.srcip +} + +func (vp *virtualPkt) dst() net.IP { + return vp.dstip +} + +func (vp *virtualPkt) proto() string { + return vp._proto +} + +func (vp *virtualPkt) srcPort() uint16 { + return vp.sport +} + +func (vp *virtualPkt) dstPort() uint16 { + return vp.dport +} + +func (vp *virtualPkt) accept() { + fmt.Println("VIRTUAL PACKET ACCEPTED") + sendSGFWTestAlert(1, vp.getGUID(), "") +} + +func (vp *virtualPkt) acceptTLSOnly() { + fmt.Println("VIRTUAL PACKET ACCEPTED (TLSONLY)") + sendSGFWTestAlert(1, vp.getGUID(), "tls") +} + +func (vp *virtualPkt) drop() { + fmt.Println("VIRTUAL PACKET DROPPED") + sendSGFWTestAlert(0, vp.getGUID(), "") +} + +func (vp *virtualPkt) setPrompter(val *prompter) { + vp.prompter = val +} + +func (vp *virtualPkt) getPrompter() *prompter { + return vp.prompter +} + +func (vp *virtualPkt) getGUID() string { + if vp.guid == "" { + vp.guid = genGUID() + } + + return vp.guid +} + +func (vp *virtualPkt) getPrompting() bool { + return vp.prompting +} + +func (vp *virtualPkt) setPrompting(val bool) { + vp.prompting = val +} + +func (vp *virtualPkt) print() string { + desc := fmt.Sprintf("virtualPkt { src %s:%u, dst %s:%u (%s) proto %s", + vp.srcip, vp.sport, vp.dstip, vp.dport, vp.hostname, vp._proto) + + // pinfo excluded + desc += fmt.Sprintf(" socks=%v [policy=%s]", vp.is_socks, vp.pol.application) + desc += fmt.Sprintf(" prompting=%v ts=%s", vp.prompting, vp.getTimestamp()) + desc += fmt.Sprintf(" guid=%s [optstring=%s] }", vp.getGUID(), vp.optstring) + return desc +} + +func (vp *virtualPkt) SetPacket(proto string, srcip net.IP, sport uint16, dstip net.IP, dport uint16, hostname string) bool { + vp._proto = proto + vp.srcip = srcip + vp.dstip = dstip + vp.sport = sport + vp.dport = dport + vp.name = hostname + return true +} diff --git a/sources/etc/dbus-1/system.d/com.subgraph.Firewall.conf b/sources/etc/dbus-1/system.d/com.subgraph.Firewall.conf index fc5e02d..e71afd8 100644 --- a/sources/etc/dbus-1/system.d/com.subgraph.Firewall.conf +++ b/sources/etc/dbus-1/system.d/com.subgraph.Firewall.conf @@ -25,4 +25,14 @@ + + + + + + + + + + diff --git a/testfw/dbus.go b/testfw/dbus.go new file mode 100644 index 0000000..9f2fba2 --- /dev/null +++ b/testfw/dbus.go @@ -0,0 +1,77 @@ +package main + +import ( + "errors" + "fmt" + + "github.com/godbus/dbus" +) + + +const busName = "com.subgraph.FirewallTest" +const objectPath = "/com/subgraph/FirewallTest" +const interfaceName = "com.subgraph.FirewallTest" + +type dbusObjectP struct { + dbus.BusObject +} + +func newDbusObjectAdd() (*dbusObjectP, error) { + conn, err := dbus.SystemBus() + if err != nil { + return nil, err + } + + return &dbusObjectP{conn.Object("com.subgraph.Firewall", "/com/subgraph/Firewall")}, nil +} + +type dbusServer struct { + conn *dbus.Conn +} + +func newDbusServer() (*dbusServer, error) { + conn, err := dbus.SystemBus() + if err != nil { + return nil, err + } + + reply, err := conn.RequestName(busName, dbus.NameFlagDoNotQueue) + if err != nil { + return nil, err + } + if reply != dbus.RequestNameReplyPrimaryOwner { + return nil, errors.New("Bus name is already owned") + } + ds := &dbusServer{} + + if err := conn.Export(ds, objectPath, interfaceName); err != nil { + return nil, err + } + + ds.conn = conn + return ds, nil +} + +func (ds *dbusServer) SGFWTestAlert(accepted int32, guid string, other string) (bool, *dbus.Error) { + fmt.Printf("<- SGFWTestAlert(accepted = %v, guid = %s, other=[%s])\n", accepted, guid, other) + + return true, nil +} + +func CallAddTestVPC(d *dbusObjectP, proto string, srcip string, sport uint16, dstip string, dport uint16, hostname string) bool { + var dres bool + + fmt.Printf("CallAddTestVPC(proto=%s, srcip=%s, sport=%u, dstip=%s, dport=%u, hostname=%s)\n", + proto, srcip, sport, dstip, dport, hostname) + + call := d.Call("AddTestVPC", 0, + proto, srcip, sport, dstip, dport, hostname) + + err := call.Store(&dres) + if err != nil { + fmt.Println("Error sending DBus AddTestVPC() request:", err) + return false + } + + return true +} diff --git a/testfw/testfw.go b/testfw/testfw.go new file mode 100644 index 0000000..d972f38 --- /dev/null +++ b/testfw/testfw.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "log" + "time" +) + +var dbuso *dbusObjectP + + +func main() { + fmt.Println("Starting up test units...") + + _, err := newDbusServer() + if err != nil { + log.Fatal("Error:", err) + return + } + + dbuso, err := newDbusObjectAdd() + if err != nil { + log.Fatal("Failed to connect to dbus system bus: %v", err) + } + + res := CallAddTestVPC(dbuso, "udp", "10.0.0.1", 61921, "8.8.8.8", 53, "dnsthing.google.com") + fmt.Println("res =", res) + + + fmt.Println("Waiting until interrupted...") + + for true { + time.Sleep(1 * time.Second) + } + +}