From 7a1851419cd1e1a306099a07b943b82fbebd627c Mon Sep 17 00:00:00 2001 From: shw Date: Tue, 9 May 2017 15:53:48 +0000 Subject: [PATCH] Added support for using fw-daemon on all processes system-wide. Added changes for fw-daemon to check sandboxed processes' oz-init /proc/[pid]/net/tcp with procsnitch. fw-daemon IPC accepts "register-init" command to register new oz-init process instance alerts. fw-daemon also checks for existing oz-init processes on startup. Updated vendor-bundled go-procsnitch to latest dev version that includes findTCPSocketAll(). --- sgfw/ipc.go | 68 ++++++++++++++- sgfw/policy.go | 87 ++++++++++++++++++- sgfw/snitch-ext.go | 47 ++++++++++ .../github.com/subgraph/go-procsnitch/proc.go | 9 ++ .../subgraph/go-procsnitch/socket.go | 32 +++++++ 5 files changed, 240 insertions(+), 3 deletions(-) create mode 100644 sgfw/snitch-ext.go diff --git a/sgfw/ipc.go b/sgfw/ipc.go index 5d88d9a..5776cbf 100644 --- a/sgfw/ipc.go +++ b/sgfw/ipc.go @@ -13,6 +13,29 @@ import ( const ReceiverSocketPath = "/tmp/fwoz.sock" +var OzInitPids []int = []int{} + + +func addInitPid(pid int) { + fmt.Println("::::::::::: init pid added: ", pid) + for i := 0; i < len(OzInitPids); i++ { + if OzInitPids[i] == pid { + return + } + } + + OzInitPids = append(OzInitPids, pid) +} + +func removeInitPid(pid int) { + for i := 0; i < len(OzInitPids); i++ { + if OzInitPids[i] == pid { + OzInitPids = append(OzInitPids[:i], OzInitPids[i+1:]) + return + } + } +} + func addFWRule(fw *Firewall, whitelist bool, srchost, dsthost, dstport string) error { policy := fw.PolicyForPath("*") rulestr := "" @@ -131,7 +154,22 @@ func ReceiverLoop(fw *Firewall, c net.Conn) { return } - if len(tokens) != 5 { + if tokens[0] == "register-init" && len(tokens) == 2 { + initp := tokens[1] + initpid, err := strconv.Atoi(initp) + + if err != nil { + log.Notice("IPC received invalid oz-init pid: ", initp) + c.Write([]byte("Bad command: init pid was invalid")) + return + } + + addInitPid(initpid) + c.Write([]byte("OK.\n")) + return + } + + if len(tokens) != 6 { log.Notice("IPC received invalid command: " + data) c.Write([]byte("Received bad number of parameters.\n")) return @@ -175,8 +213,18 @@ func ReceiverLoop(fw *Firewall, c net.Conn) { return } +/* initp := tokens[5] + initpid, err := strconv.Atoi(initp) + + if err != nil { + log.Notice("IPC received invalid oz-init pid: ", initp) + c.Write([]byte("Bad command: init pid was invalid")) + return + } */ + if add { log.Noticef("Adding new rule to oz sandbox/fw: %v / %v -> %v : %v", w, srchost, dsthost, dstport) +// addInitPid(initpid) err := addFWRule(fw, w, srchost, dsthost, dstport) if err != nil { log.Error("Error adding dynamic OZ firewall rule to fw-daemon: ", err) @@ -200,6 +248,24 @@ func ReceiverLoop(fw *Firewall, c net.Conn) { func OzReceiver(fw *Firewall) { log.Notice("XXX: dispatching oz receiver...") + + sboxes, err := getSandboxes() + + if err != nil { + log.Warning("Error retrieving list of running Oz sandbox init processes: ", err) + } else { + + if len(sboxes) > 0 { + log.Warning("Adding existing Oz sandbox init pids...") + for s := 0; s < len(sboxes); s++ { + addInitPid(sboxes[s].InitPid) + } + } else { + log.Warning("It does not appear there were any Oz sandboxed processes already launched.") + } + + } + os.Remove(ReceiverSocketPath) lfd, err := net.Listen("unix", ReceiverSocketPath) if err != nil { diff --git a/sgfw/policy.go b/sgfw/policy.go index 8ad5ecb..7b30e54 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -13,6 +13,8 @@ import ( "github.com/google/gopacket/layers" "github.com/subgraph/go-procsnitch" "net" + "syscall" + "unsafe" ) var _interpreters = []string{ @@ -397,12 +399,93 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { policy.processPacket(pkt, pinfo) } +func readFileDirect(filename string) ([]byte, error) { + bfilename, err := syscall.BytePtrFromString(filename) + + if err != nil { + return nil, err + } + + res, _, err := syscall.Syscall(syscall.SYS_OPEN, uintptr(unsafe.Pointer(bfilename)), syscall.O_RDONLY, 0) + fdlong := int64(res) + + if fdlong < 0 { + return nil, err + } + + fd := int(res) + data := make([]byte, 65535) + + val, err := syscall.Read(fd, data) + + if err != nil { + return nil, err + } + + syscall.Close(fd) + + if val < 65535 { + data = data[0:val] + } + + return data, nil +} + +func getAllProcNetDataLocal() ([]string, error) { + data := "" + + for i := 0; i < len(OzInitPids); i++ { + fname := fmt.Sprintf("/proc/%d/net/tcp", OzInitPids[i]) +fmt.Println("XXX: opening: ", fname) + bdata, err := readFileDirect(fname) + + if err != nil { + fmt.Println("Error reading proc data from ", fname, ": ", err) + } else { + data += string(bdata) + } + + } + + lines := strings.Split(data, "\n") + rlines := make([]string, 0) + ctr := 1 + + for l := 0; l < len(lines); l++ { + lines[l] = strings.TrimSpace(lines[l]) + ssplit := strings.Split(lines[l], ":") + + if len(ssplit) != 6 { + continue + } + + ssplit[0] = fmt.Sprintf("%d", ctr) + ctr++ + rlines = append(rlines, strings.Join(ssplit, ":")) + } + + return rlines, nil +} + func findProcessForPacket(pkt *nfqueue.NFQPacket) *procsnitch.Info { - _, dstip := getPacketIP4Addrs(pkt) + srcip, dstip := getPacketIP4Addrs(pkt) srcp, dstp := getPacketPorts(pkt) if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { - return procsnitch.LookupTCPSocketProcess(srcp, dstip, dstp) + // Try normal way first, before the more resource intensive/invasive way. + res := procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, nil) + + if res == nil { + extdata, err := getAllProcNetDataLocal() + + if err != nil { + log.Warningf("Error looking up sandboxed /proc/net data: %v", err) + } else { + res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, extdata) + } + } + + return res } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { return procsnitch.LookupUDPSocketProcess(srcp) } diff --git a/sgfw/snitch-ext.go b/sgfw/snitch-ext.go new file mode 100644 index 0000000..3fe4972 --- /dev/null +++ b/sgfw/snitch-ext.go @@ -0,0 +1,47 @@ +package sgfw + +import ( + "github.com/subgraph/ozipc" +) + +type ListSandboxesMsg struct { + _ string "ListSandboxes" +} + +type SandboxInfo struct { + Id int + Address string + Profile string + Mounts []string + InitPid int +} + +type ListSandboxesResp struct { + Sandboxes []SandboxInfo "ListSandboxesResp" +} + +const socketPath = "@oz-control" + +var ozCtrlFactory = ipc.NewMsgFactory( + new(ListSandboxesMsg), + new(ListSandboxesResp), +) + +func getSandboxes() ([]SandboxInfo, error) { + c, err := ipc.Connect(socketPath, ozCtrlFactory, nil) + if err != nil { + log.Fatal("Error connecting to oz control socket: ", err) + return nil, err + } + + defer c.Close() + rr, err := c.ExchangeMsg(&ListSandboxesMsg{}) + if err != nil { + return nil, err + } + + resp := <-rr.Chan() + rr.Done() + sboxes := resp.Body.(*ListSandboxesResp) + return sboxes.Sandboxes, nil +} diff --git a/vendor/github.com/subgraph/go-procsnitch/proc.go b/vendor/github.com/subgraph/go-procsnitch/proc.go index 00da23c..4979c0b 100644 --- a/vendor/github.com/subgraph/go-procsnitch/proc.go +++ b/vendor/github.com/subgraph/go-procsnitch/proc.go @@ -75,6 +75,15 @@ func LookupUDPSocketProcess(srcPort uint16) *Info { return pcache.lookup(ss.inode) } +// LookupTCPSocketProcessAll searches for a TCP socket a given source port, destination IP, and destination port - AND source destination +func LookupTCPSocketProcessAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string) *Info { + ss := findTCPSocketAll(srcAddr, srcPort, dstAddr, dstPort, custdata) + if ss == nil { + return nil + } + return pcache.lookup(ss.inode) +} + // LookupTCPSocketProcess searches for a TCP socket with a given source port, destination IP, and destination port func LookupTCPSocketProcess(srcPort uint16, dstAddr net.IP, dstPort uint16) *Info { ss := findTCPSocket(srcPort, dstAddr, dstPort) diff --git a/vendor/github.com/subgraph/go-procsnitch/socket.go b/vendor/github.com/subgraph/go-procsnitch/socket.go index 27020da..3dbe56d 100644 --- a/vendor/github.com/subgraph/go-procsnitch/socket.go +++ b/vendor/github.com/subgraph/go-procsnitch/socket.go @@ -90,6 +90,20 @@ func findTCPSocket(srcPort uint16, dstAddr net.IP, dstPort uint16) *socketStatus }) } +func findTCPSocketAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string) *socketStatus { + found := findSocket("tcp", func(ss socketStatus) bool { + return ss.remote.port == dstPort && ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) + + if found != nil || custdata == nil { + return found + } + + return findSocketCustom("tcp", custdata, func(ss socketStatus) bool { + return ss.remote.port == dstPort && ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) +} + func findUNIXSocket(socketFile string) *socketStatus { proto := "unix" @@ -127,6 +141,24 @@ func findUNIXSocket(socketFile string) *socketStatus { return nil } +func findSocketCustom(proto string, sockdata []string, matcher func(socketStatus) bool) *socketStatus { + var ss socketStatus + for _, line := range sockdata { + if len(line) == 0 { + continue + } + if err := ss.parseLine(line); err != nil { + log.Warningf("Unable to parse line from custom data source/%s [%s]: %v", proto, line, err) + continue + } + if matcher(ss) { + ss.line = line + return &ss + } + } + return nil +} + func findSocket(proto string, matcher func(socketStatus) bool) *socketStatus { var ss socketStatus for _, line := range getSocketLines(proto) {