From c779cc6b9bce15e7f6fea09c1032acbc01009ef6 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Wed, 29 Jun 2016 02:34:41 +0000 Subject: [PATCH] Implementation of filtering and prompting for Socks connections --- main.go | 8 +- socks_server_chain.go | 185 +++++++++++++++++++++++++++++------------- 2 files changed, 131 insertions(+), 62 deletions(-) diff --git a/main.go b/main.go index 5e5e6eb..d1948f8 100644 --- a/main.go +++ b/main.go @@ -13,10 +13,10 @@ import ( "time" "unsafe" - "github.com/op/go-logging" "github.com/subgraph/fw-daemon/nfqueue" "github.com/subgraph/go-procsnitch" // "github.com/subgraph/fw-daemon/Godeps/_workspace/src/github.com/op/go-logging" + "github.com/op/go-logging" ) var log = logging.MustGetLogger("sgfw") @@ -172,10 +172,10 @@ func loadConfiguration(configFilePath string) (*SocksJsonConfig, error) { func getSocksChainConfig(config *SocksJsonConfig) *socksChainConfig { // XXX - fields := strings.Split(config.TorSocks, ":") + fields := strings.Split(config.TorSocks, "|") torSocksNet := fields[0] torSocksAddr := fields[1] - fields = strings.Split(config.SocksListener, ":") + fields = strings.Split(config.SocksListener, "|") socksListenNet := fields[0] socksListenAddr := fields[1] socksConfig := socksChainConfig{ @@ -233,7 +233,7 @@ func main() { */ wg := sync.WaitGroup{} - chain := NewSocksChain(socksConfig, &wg, ds) + chain := NewSocksChain(socksConfig, &wg, fw) chain.start() fw.runFilter() diff --git a/socks_server_chain.go b/socks_server_chain.go index 41969b1..6c6ce8f 100644 --- a/socks_server_chain.go +++ b/socks_server_chain.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "io" "net" "os" @@ -9,6 +8,7 @@ import ( "github.com/subgraph/fw-daemon/socks5" "github.com/subgraph/go-procsnitch" + "strconv" ) type socksChainConfig struct { @@ -20,14 +20,10 @@ type socksChainConfig struct { type socksChain struct { cfg *socksChainConfig - dbus *dbusServer + fw *Firewall listener net.Listener wg *sync.WaitGroup procInfo procsnitch.ProcInfo - - lock sync.Mutex - policyMap map[string]*Policy - policies []*Policy } type socksChainSession struct { @@ -41,13 +37,56 @@ type socksChainSession struct { server *socksChain } -func NewSocksChain(cfg *socksChainConfig, wg *sync.WaitGroup, dbus *dbusServer) *socksChain { +const ( + socksVerdictDrop = 1 + socksVerdictAccept = 2 +) + +type pendingSocksConnection struct { + pol *Policy + hname string + destIP net.IP + destPort uint16 + pinfo *procsnitch.Info + verdict chan int +} + +func (sc *pendingSocksConnection) policy() *Policy { + return sc.pol +} + +func (sc *pendingSocksConnection) procInfo() *procsnitch.Info { + return sc.pinfo +} + +func (sc *pendingSocksConnection) hostname() string { + return sc.hname +} + +func (sc *pendingSocksConnection) dst() net.IP { + return sc.destIP +} +func (sc *pendingSocksConnection) dstPort() uint16 { + return sc.destPort +} + +func (sc *pendingSocksConnection) deliverVerdict(v int) { + sc.verdict <- v + close(sc.verdict) +} + +func (sc *pendingSocksConnection) accept() { sc.deliverVerdict(socksVerdictAccept) } + +func (sc *pendingSocksConnection) drop() { sc.deliverVerdict(socksVerdictDrop) } + +func (sc *pendingSocksConnection) print() string { return "socks connection" } + +func NewSocksChain(cfg *socksChainConfig, wg *sync.WaitGroup, fw *Firewall) *socksChain { chain := socksChain{ - cfg: cfg, - dbus: dbus, - wg: wg, - procInfo: procsnitch.SystemProcInfo{}, - policyMap: make(map[string]*Policy), + cfg: cfg, + fw: fw, + wg: wg, + procInfo: procsnitch.SystemProcInfo{}, } return &chain } @@ -58,7 +97,7 @@ func (s *socksChain) start() { var err error s.listener, err = net.Listen(s.cfg.ListenSocksNet, s.cfg.ListenSocksAddr) if err != nil { - log.Error("ERR/socks: Failed to listen on the socks address: %v", err) + log.Errorf("ERR/socks: Failed to listen on the socks address: %v", err) os.Exit(1) } @@ -74,7 +113,7 @@ func (s *socksChain) socksAcceptLoop() error { conn, err := s.listener.Accept() if err != nil { if e, ok := err.(net.Error); ok && !e.Temporary() { - log.Info("ERR/socks: Failed to Accept(): %v", err) + log.Infof("ERR/socks: Failed to Accept(): %v", err) return err } continue @@ -88,30 +127,15 @@ func (c *socksChainSession) sessionWorker() { defer c.clientConn.Close() clientAddr := c.clientConn.RemoteAddr() - log.Info("INFO/socks: New connection from: %v", clientAddr) + log.Infof("INFO/socks: New connection from: %v", clientAddr) // Do the SOCKS handshake with the client, and read the command. var err error if c.req, err = socks5.Handshake(c.clientConn); err != nil { - log.Info("ERR/socks: Failed SOCKS5 handshake: %v", err) + log.Infof("ERR/socks: Failed SOCKS5 handshake: %v", err) return } - pinfo := procsnitch.FindProcessForConnection(c.clientConn, c.procInfo) - if pinfo == nil { - log.Warning("No proc found for connection from: %s", c.clientConn.RemoteAddr()) - return - } - - // XXX work-in-progress - // Determine policy for the connection - // if destination not specified in existing policy - // then prompt user for policy ALLOW/DENY for that destination - c.server.lock.Lock() - policy := c.policyForPath(pinfo.ExePath) - c.server.lock.Unlock() - fmt.Printf("policyForPath %s is %s\n", pinfo.ExePath, policy) - switch c.req.Cmd { case socks5.CommandTorResolve, socks5.CommandTorResolvePTR: err = c.dispatchTorSOCKS() @@ -121,16 +145,78 @@ func (c *socksChainSession) sessionWorker() { // Successfully even, send the response back with the addresc. c.req.ReplyAddr(socks5.ReplySucceeded, c.bndAddr) } - return case socks5.CommandConnect: + if !c.filterConnect() { + c.req.Reply(socks5.ReplyConnectionRefused) + return + } + c.handleConnect() default: // Should *NEVER* happen, validated as part of handshake. - log.Info("BUG/socks: Unsupported SOCKS command: 0x%02x", c.req.Cmd) + log.Infof("BUG/socks: Unsupported SOCKS command: 0x%02x", c.req.Cmd) c.req.Reply(socks5.ReplyCommandNotSupported) - return } +} + +func (c *socksChainSession) addressDetails() (string, net.IP, uint16) { + addr := c.req.Addr + host, pstr := addr.HostPort() + port, err := strconv.ParseUint(pstr, 10, 16) + if err != nil || port == 0 || port > 0xFFFF { + log.Warningf("Illegal port value in socks address: %v", addr) + return "", nil, 0 + } + if addr.Type() == 3 { + return host, nil, uint16(port) + } + ip := net.ParseIP(host) + if ip == nil { + log.Warningf("Failed to extract address information from socks address: %v", addr) + } + return "", ip, uint16(port) +} - err = c.dispatchTorSOCKS() +func (c *socksChainSession) filterConnect() bool { + pinfo := procsnitch.FindProcessForConnection(c.clientConn, c.procInfo) + if pinfo == nil { + log.Warningf("No proc found for connection from: %s", c.clientConn.RemoteAddr()) + return false + } + + policy := c.server.fw.PolicyForPath(pinfo.ExePath) + + hostname, ip, port := c.addressDetails() + if ip == nil && hostname == "" { + return false + } + result := policy.rules.filter(ip, port, hostname, pinfo) + switch result { + case FILTER_DENY: + return false + case FILTER_ALLOW: + return true + case FILTER_PROMPT: + pending := &pendingSocksConnection{ + pol: policy, + hname: hostname, + destIP: ip, + destPort: port, + pinfo: pinfo, + verdict: make(chan int), + } + policy.processPromptResult(pending) + v := <-pending.verdict + if v == socksVerdictAccept { + return true + } + } + + return false + +} + +func (c *socksChainSession) handleConnect() { + err := c.dispatchTorSOCKS() if err != nil { return } @@ -139,7 +225,7 @@ func (c *socksChainSession) sessionWorker() { if c.optData != nil { if _, err = c.upstreamConn.Write(c.optData); err != nil { - log.Info("ERR/socks: Failed writing OptData: %v", err) + log.Infof("ERR/socks: Failed writing OptData: %v", err) return } c.optData = nil @@ -147,6 +233,11 @@ func (c *socksChainSession) sessionWorker() { // A upstream connection has been established, push data back and forth // till the session is done. + c.forwardTraffic() + log.Infof("INFO/socks: Closed SOCKS connection from: %v", c.clientConn.RemoteAddr()) +} + +func (c *socksChainSession) forwardTraffic() { var wg sync.WaitGroup wg.Add(2) @@ -160,7 +251,6 @@ func (c *socksChainSession) sessionWorker() { go copyLoop(c.clientConn, c.upstreamConn) wg.Wait() - log.Info("INFO/socks: Closed SOCKS connection from: %v", clientAddr) } func (c *socksChainSession) dispatchTorSOCKS() (err error) { @@ -170,24 +260,3 @@ func (c *socksChainSession) dispatchTorSOCKS() (err error) { } return } - -func (s *socksChainSession) policyForPath(path string) *Policy { - s.server.lock.Lock() - defer s.server.lock.Unlock() - - if _, ok := s.server.policyMap[path]; !ok { - p := new(Policy) - // XXX is fw needed? - // p.fw = fw - p.path = path - p.application = path - entry := entryForPath(path) - if entry != nil { - p.application = entry.name - p.icon = entry.icon - } - s.server.policyMap[path] = p - s.server.policies = append(s.server.policies, p) - } - return s.server.policyMap[path] -}