From 27d0a4809dae68850af130fdf30bd7332da84683 Mon Sep 17 00:00:00 2001 From: shw Date: Mon, 22 May 2017 19:41:26 +0000 Subject: [PATCH] Updated SOCKS5 connection lookup code now correctly identifies originating process. Includes code to read internal proxy state information from (updated) oz-daemon. --- sgfw/ipc.go | 66 ++++++++++++++++++++++++++++++++++ sgfw/policy.go | 60 +++++++++++++++++++++++++++++++ sgfw/socks_server_chain.go | 74 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 197 insertions(+), 3 deletions(-) diff --git a/sgfw/ipc.go b/sgfw/ipc.go index 34d2d0c..f048fcf 100644 --- a/sgfw/ipc.go +++ b/sgfw/ipc.go @@ -7,6 +7,9 @@ import ( "bufio" "strings" "strconv" + "errors" + + "github.com/subgraph/oz/ipc" ) const ReceiverSocketPath = "/tmp/fwoz.sock" @@ -300,3 +303,66 @@ func OzReceiver(fw *Firewall) { } } + + +type ListProxiesMsg struct { + _ string "ListProxies" +} + +type ListProxiesResp struct { + Proxies []string "ListProxiesResp" +} + +func ListProxies() ([]string, error) { + resp, err := clientSend(&ListProxiesMsg{}) + if err != nil { + return nil, err + } + body, ok := resp.Body.(*ListProxiesResp) + if !ok { + return nil, errors.New("ListProxies response was not expected type") + } + return body.Proxies, nil +} + +const OzSocketName = "@oz-control" +var bSockName = OzSocketName + +var messageFactory = ipc.NewMsgFactory( + new(ListProxiesMsg), + new(ListProxiesResp), +) + +func clientConnect() (*ipc.MsgConn, error) { + bSockName = os.Getenv("SOCKET_NAME") + + if bSockName != "" { + fmt.Println("Attempting to connect on custom socket provided through environment: ", bSockName) + + if bSockName[0:1] != "@" { + fmt.Println("Environment variable specified invalid socket name... prepending @") + bSockName = "@" + bSockName + } + + } else { + bSockName = OzSocketName + } + + return ipc.Connect(bSockName, messageFactory, nil) +} + +func clientSend(msg interface{}) (*ipc.Message, error) { + c, err := clientConnect() + if err != nil { + return nil, err + } + defer c.Close() + rr, err := c.ExchangeMsg(msg) + if err != nil { + return nil, err + } + + resp := <-rr.Chan() + rr.Done() + return resp, nil +} diff --git a/sgfw/policy.go b/sgfw/policy.go index 9971353..097faa7 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -549,6 +549,66 @@ func getRealRoot(pathname string, pid int) string { return pathname } +// XXX: This is redundant code.... it should be called by findProcessForPacket() +func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, proto string, strictness, icode int) (*procsnitch.Info, string) { + var res *procsnitch.Info = nil + var optstr string + removePids := make([]int, 0) + + for i := 0; i < len(OzInitPids); i++ { + data := "" + fname := fmt.Sprintf("/proc/%d/net/%s", OzInitPids[i].Pid, proto) +//fmt.Println("XXX: opening: ", fname) + bdata, err := readFileDirect(fname) + + if err != nil { + fmt.Println("Error reading proc data from ", fname, ": ", err) + + if err == syscall.ENOENT { + removePids = append(removePids, OzInitPids[i].Pid) + } + + continue + } else { + data = string(bdata) + lines := strings.Split(data, "\n") + rlines := make([]string, 0) + + for l := 0; l < len(lines); l++ { + lines[l] = strings.TrimSpace(lines[l]) + ssplit := strings.Split(lines[l], ":") + + if len(ssplit) != 6 { + continue + } + + rlines = append(rlines, strings.Join(ssplit, ":")) + } + + if proto == "tcp" { + res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, rlines) + } else if proto == "udp" { + res = procsnitch.LookupUDPSocketProcessAll(srcip, srcp, dstip, dstp, rlines, strictness) + } else if proto == "icmp" { + res = procsnitch.LookupICMPSocketProcessAll(srcip, dstip, icode, rlines) + } + + if res != nil { + optstr = "Sandbox: " + OzInitPids[i].Name + res.ExePath = getRealRoot(res.ExePath, OzInitPids[i].Pid) + break + } + } + + } + + for _, p := range removePids { + removeInitPid(p) + } + + return res, optstr +} + func findProcessForPacket(pkt *nfqueue.NFQPacket, reverse bool, strictness int) (*procsnitch.Info, string) { srcip, dstip := getPacketIPAddrs(pkt) srcp, dstp := getPacketPorts(pkt) diff --git a/sgfw/socks_server_chain.go b/sgfw/socks_server_chain.go index c470554..18d1304 100644 --- a/sgfw/socks_server_chain.go +++ b/sgfw/socks_server_chain.go @@ -53,6 +53,7 @@ type pendingSocksConnection struct { pinfo *procsnitch.Info verdict chan int prompting bool + optstr string } func (sc *pendingSocksConnection) policy() *Policy { @@ -64,7 +65,7 @@ func (sc *pendingSocksConnection) procInfo() *procsnitch.Info { } func (sc *pendingSocksConnection) getOptString() string { - return "" + return sc.optstr } func (sc *pendingSocksConnection) hostname() string { @@ -205,20 +206,86 @@ func (c *socksChainSession) addressDetails() (string, net.IP, uint16) { return "", ip, uint16(port) } +func findProxyEndpoint(pdata []string, conn net.Conn) (*procsnitch.Info, string) { + for _, pstr := range pdata { + toks := strings.Split(pstr, " ") + + if len(toks) != 6 { + continue + } + + s1, d1, s2, d2 := toks[0], toks[2], toks[3], toks[5] + + if strings.HasSuffix(d1, ",") { + d1 = d1[0:len(d1)-1] + } + + if conn.LocalAddr().String() == d2 && conn.RemoteAddr().String() == s2 { + srcips, srcps, err := net.SplitHostPort(s1) + dstips, dstps, err2 := net.SplitHostPort(d1) + + if err != nil && err2 != nil { + continue + } + + srcip := net.ParseIP(srcips) + dstip := net.ParseIP(dstips) + + if srcip == nil || dstip == nil { + continue + } + + srcport, err := strconv.Atoi(srcps) + dstport, err2 := strconv.Atoi(dstps) + + if err != nil || err2 != nil { + continue + } + + res := procsnitch.LookupTCPSocketProcessAll(srcip, uint16(srcport), dstip, uint16(dstport), nil) + res, optstr := LookupSandboxProc(srcip, uint16(srcport), dstip, uint16(dstport), "tcp", procsnitch.MATCH_STRICT, 0) + + if res != nil { + return res, optstr + } + } + + } + + return nil, "" +} + func (c *socksChainSession) filterConnect() bool { - pinfo := procsnitch.FindProcessForConnection(c.clientConn, c.procInfo) + allProxies, err := ListProxies() + var pinfo *procsnitch.Info = nil + var optstr = "" + + if err == nil { + pinfo, optstr = findProxyEndpoint(allProxies, c.clientConn) + } + + if pinfo == nil { + pinfo = procsnitch.FindProcessForConnection(c.clientConn, c.procInfo) + } + if pinfo == nil { log.Warningf("No proc found for [socks5] connection from: %s", c.clientConn.RemoteAddr()) return false } + if optstr == "" { + optstr = "[via SOCKS5/Tor]" + } else { + optstr = "SOCKS5|Tor / " + optstr + } + policy := c.server.fw.PolicyForPath(pinfo.ExePath) hostname, ip, port := c.addressDetails() if ip == nil && hostname == "" { return false } - result := policy.rules.filter(nil, nil, ip, port, hostname, pinfo, "SOCKS") + result := policy.rules.filter(nil, nil, ip, port, hostname, pinfo, optstr) switch result { case FILTER_DENY: return false @@ -261,6 +328,7 @@ func (c *socksChainSession) filterConnect() bool { pinfo: pinfo, verdict: make(chan int), prompting: false, + optstr: optstr, } policy.processPromptResult(pending) v := <-pending.verdict