From ff8be655668c7778d9c2065789bbde750f188521 Mon Sep 17 00:00:00 2001 From: Stephen Watt Date: Thu, 28 Sep 2017 11:01:41 -0400 Subject: [PATCH] Added connection timestamps to firewall prompting. Disabled old synchronous RequestPrompt Dbus method in fw-prompt. fw-prompt GUI now (as originally) remains above other windows when there are pending decisions. Fixed improper traversal of pending connections in fw-prompt GUI. Consolidated redundant code blocks in fw-prompt GUI. --- TODO | 9 +- fw-prompt/dbus.go | 12 +-- fw-prompt/fw-prompt.go | 182 ++++++++++++------------------------- sgfw/dbus.go | 1 + sgfw/policy.go | 21 +++-- sgfw/prompt.go | 1 + sgfw/sgfw.go | 14 +-- sgfw/socks_server_chain.go | 51 ++++++++--- sgfw/tlsguard.go | 60 ++++++++++-- 9 files changed, 173 insertions(+), 178 deletions(-) diff --git a/TODO b/TODO index 8484072..dc58b75 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ fw-daemon: - pc.socks() an getOptString() return overlapping information + pc.socks() and getOptString() return overlapping information remove all stale references to SANDBOX: rules/policyForPathAndSandbox() @@ -7,13 +7,6 @@ fw-daemon: fw-prompt: scope returned by new rules is bad (always set to process) - prompter should have a timestamp field - - Iteration through fw-prompt choices can't brute force by index # - - This function needs to be updated because it no longer works: - func toggleHover() { mainWin.SetKeepAbove(len(decisionWaiters) > 0) } - Each duplicate prompt needs to be expandable into individual items diff --git a/fw-prompt/dbus.go b/fw-prompt/dbus.go index 8a300a8..8e03ade 100644 --- a/fw-prompt/dbus.go +++ b/fw-prompt/dbus.go @@ -52,10 +52,10 @@ func newDbusServer() (*dbusServer, error) { return ds, nil } -func (ds *dbusServer) RequestPrompt(guid, application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string, - is_socks bool, optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) { +/*func (ds *dbusServer) RequestPrompt(guid, application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string, + is_socks bool, timestamp string, optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) { log.Printf("request prompt: app = %s, icon = %s, path = %s, address = %s / ip = %s, is_socks = %v, action = %v\n", application, icon, path, address, ip, is_socks, action) - decision := addRequest(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, is_socks, optstring, sandbox, int(action)) + decision := addRequest(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, timestamp, is_socks, optstring, sandbox, int(action)) log.Print("Waiting on decision...") decision.Cond.L.Lock() for !decision.Ready { @@ -64,12 +64,12 @@ func (ds *dbusServer) RequestPrompt(guid, application, icon, path, address strin log.Print("Decision returned: ", decision.Rule) decision.Cond.L.Unlock() return int32(decision.Scope), decision.Rule, nil -} +}*/ func (ds *dbusServer) RequestPromptAsync(guid, application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string, - is_socks bool, optstring string, expanded, expert bool, action int32) (bool, *dbus.Error) { + is_socks bool, timestamp string, optstring string, expanded, expert bool, action int32) (bool, *dbus.Error) { log.Printf("ASYNC request prompt: guid = %s, app = %s, icon = %s, path = %s, address = %s / ip = %s, is_socks = %v, action = %v\n", guid, application, icon, path, address, ip, is_socks, action) - addRequestAsync(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, is_socks, optstring, sandbox, int(action)) + addRequestAsync(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, timestamp, is_socks, optstring, sandbox, int(action)) return true, nil } diff --git a/fw-prompt/fw-prompt.go b/fw-prompt/fw-prompt.go index 2296947..75eb4af 100644 --- a/fw-prompt/fw-prompt.go +++ b/fw-prompt/fw-prompt.go @@ -34,23 +34,24 @@ type decisionWaiter struct { } type ruleColumns struct { - nrefs int - Path string - GUID string - Icon string - Proto string - Pid int - Target string - Hostname string - Port int - UID int - GID int - Uname string - Gname string - Origin string - IsSocks bool - ForceTLS bool - Scope int + nrefs int + Path string + GUID string + Icon string + Proto string + Pid int + Target string + Hostname string + Port int + UID int + GID int + Uname string + Gname string + Origin string + Timestamp string + IsSocks bool + ForceTLS bool + Scope int } var dbuso *dbusObject @@ -70,6 +71,7 @@ var btnApprove, btnDeny, btnIgnore *gtk.Button var chkTLS, chkUser, chkGroup *gtk.CheckButton func dumpDecisions() { + return fmt.Println("XXX Total of decisions pending: ", len(decisionWaiters)) for i := 0; i < len(decisionWaiters); i++ { fmt.Printf("XXX %d ready = %v, rule = %v\n", i+1, decisionWaiters[i].Ready, decisionWaiters[i].Rule) @@ -77,6 +79,7 @@ func dumpDecisions() { } func addDecision() *decisionWaiter { + return nil decision := decisionWaiter{Lock: &sync.Mutex{}, Ready: false, Scope: int(sgfw.APPLY_ONCE), Rule: ""} decision.Cond = sync.NewCond(decision.Lock) decisionWaiters = append(decisionWaiters, &decision) @@ -315,7 +318,7 @@ func createColumn(title string, id int) *gtk.TreeViewColumn { func createListStore(general bool) *gtk.ListStore { colData := []glib.Type{glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING, - glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_INT} + glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_STRING, glib.TYPE_INT, glib.TYPE_STRING, glib.TYPE_INT} listStore, err := gtk.ListStoreNew(colData...) if err != nil { @@ -331,7 +334,7 @@ func removeRequest(listStore *gtk.ListStore, guid string) { defer globalPromptLock.Unlock() /* XXX: This is horrible. Figure out how to do this properly. */ - for ridx := 0; ridx < 2000; ridx++ { + for ridx := 0; ridx < globalLS.IterNChildren(nil); ridx++ { rule, _, err := getRuleByIdx(ridx) if err != nil { @@ -357,7 +360,7 @@ func addRequestInc(listStore *gtk.ListStore, guid, path, icon, proto string, pid globalPromptLock.Lock() defer globalPromptLock.Unlock() - for ridx := 0; ridx < 2000; ridx++ { + for ridx := 0; ridx < globalLS.IterNChildren(nil); ridx++ { /* XXX: This is horrible. Figure out how to do this properly. */ rule, iter, err := getRuleByIdx(ridx) @@ -385,98 +388,14 @@ func addRequestInc(listStore *gtk.ListStore, guid, path, icon, proto string, pid } func addRequestAsync(listStore *gtk.ListStore, guid, path, icon, proto string, pid int, ipaddr, hostname string, port, uid, gid int, - origin string, is_socks bool, optstring string, sandbox string, action int) bool { - if listStore == nil { - listStore = globalLS - waitTimes := []int{1, 2, 5, 10} - - if listStore == nil { - log.Println("SGFW prompter was not ready to receive firewall request... waiting") - - for _, wtime := range waitTimes { - time.Sleep(time.Duration(wtime) * time.Second) - listStore = globalLS - - if listStore != nil { - break - } - - log.Println("SGFW prompter is still waiting...") - } - - } - - } - - if listStore == nil { - log.Fatal("SGFW prompter GUI failed to load for unknown reasons") - } - - if addRequestInc(listStore, guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, is_socks, optstring, sandbox, action) { - fmt.Println("REQUEST WAS DUPLICATE") - return false - } else { - fmt.Println("NOT DUPLICATE") - } - - globalPromptLock.Lock() - iter := listStore.Append() - - if is_socks { - if (optstring != "") && (strings.Index(optstring, "SOCKS") == -1) { - optstring = "SOCKS5 / " + optstring - } else if optstring == "" { - optstring = "SOCKS5" - } - } - - colVals := make([]interface{}, 15) - colVals[0] = 1 - colVals[1] = guid - colVals[2] = path - colVals[3] = icon - colVals[4] = proto - colVals[5] = pid - - if ipaddr == "" { - colVals[6] = "---" - } else { - colVals[6] = ipaddr - } - - colVals[7] = hostname - colVals[8] = port - colVals[9] = uid - colVals[10] = gid - colVals[11] = origin - colVals[12] = 0 - - if is_socks { - colVals[12] = 1 - } - - colVals[13] = optstring - colVals[14] = action - - colNums := make([]int, len(colVals)) - - for n := 0; n < len(colVals); n++ { - colNums[n] = n - } - - err := listStore.Set(iter, colNums, colVals) - globalPromptLock.Unlock() - - if err != nil { - log.Fatal("Unable to add row:", err) - } - - toggleHover() + origin, timestamp string, is_socks bool, optstring string, sandbox string, action int) bool { + addRequest(listStore, guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, timestamp, is_socks, + optstring, sandbox, action) return true } func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid int, ipaddr, hostname string, port, uid, gid int, - origin string, is_socks bool, optstring string, sandbox string, action int) *decisionWaiter { + origin, timestamp string, is_socks bool, optstring string, sandbox string, action int) *decisionWaiter { if listStore == nil { listStore = globalLS waitTimes := []int{1, 2, 5, 10} @@ -506,7 +425,9 @@ func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid in if addRequestInc(listStore, guid, path, icon, proto, pid, ipaddr, hostname, port, uid, gid, origin, is_socks, optstring, sandbox, action) { fmt.Println("REQUEST WAS DUPLICATE") decision := addDecision() + globalPromptLock.Lock() toggleHover() + globalPromptLock.Unlock() return decision } else { fmt.Println("NOT DUPLICATE") @@ -523,7 +444,7 @@ func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid in } } - colVals := make([]interface{}, 15) + colVals := make([]interface{}, 16) colVals[0] = 1 colVals[1] = guid colVals[2] = path @@ -542,14 +463,15 @@ func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid in colVals[9] = uid colVals[10] = gid colVals[11] = origin - colVals[12] = 0 + colVals[12] = timestamp + colVals[13] = 0 if is_socks { - colVals[12] = 1 + colVals[13] = 1 } - colVals[13] = optstring - colVals[14] = action + colVals[14] = optstring + colVals[15] = action colNums := make([]int, len(colVals)) @@ -558,7 +480,6 @@ func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid in } err := listStore.Set(iter, colNums, colVals) - globalPromptLock.Unlock() if err != nil { log.Fatal("Unable to add row:", err) @@ -567,6 +488,7 @@ func addRequest(listStore *gtk.ListStore, guid, path, icon, proto string, pid in decision := addDecision() dumpDecisions() toggleHover() + globalPromptLock.Unlock() return decision } @@ -682,14 +604,17 @@ func makeDecision(idx int, rule string, scope int) error { return nil } +/* Do we need to hold the lock while this is called? Stay safe... */ func toggleHover() { - mainWin.SetKeepAbove(len(decisionWaiters) > 0) + nitems := globalLS.IterNChildren(nil) + + mainWin.SetKeepAbove(nitems > 0) } func toggleValidRuleState() { ok := true - // Unfortunately, this can cause deadlock since it's a part ofi the item removal cascade + // Unfortunately, this can cause deadlock since it's a part of the item removal cascade // globalPromptLock.Lock() // defer globalPromptLock.Unlock() @@ -912,8 +837,13 @@ func getRuleByIdx(idx int) (ruleColumns, *gtk.TreeIter, error) { return rule, nil, err } + rule.Timestamp, err = lsGetStr(globalLS, iter, 12) + if err != nil { + return rule, nil, err + } + rule.IsSocks = false - is_socks, err := lsGetInt(globalLS, iter, 12) + is_socks, err := lsGetInt(globalLS, iter, 13) if err != nil { return rule, nil, err } @@ -922,7 +852,7 @@ func getRuleByIdx(idx int) (ruleColumns, *gtk.TreeIter, error) { rule.IsSocks = true } - rule.Scope, err = lsGetInt(globalLS, iter, 14) + rule.Scope, err = lsGetInt(globalLS, iter, 15) if err != nil { return rule, nil, err } @@ -965,7 +895,7 @@ func addPendingPrompts(rules []string) { for _, rule := range rules { fields := strings.Split(rule, "|") - if len(fields) != 18 { + if len(fields) != 19 { log.Printf("Got saved prompt message with strange data: \"%s\"", rule) continue } @@ -1011,15 +941,16 @@ func addPendingPrompts(rules []string) { continue } - optstring := fields[16] + timestamp := fields[16] + optstring := fields[17] - action, err := strconv.Atoi(fields[17]) + action, err := strconv.Atoi(fields[18]) if err != nil { log.Println("Error converting action in pending prompt message to integer:", err) continue } - addRequestAsync(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, is_socks, optstring, sandbox, action) + addRequestAsync(nil, guid, path, icon, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, timestamp, is_socks, optstring, sandbox, action) } } @@ -1255,14 +1186,15 @@ func main() { tv.AppendColumn(createColumn("UID", 9)) tv.AppendColumn(createColumn("GID", 10)) tv.AppendColumn(createColumn("Origin", 11)) + tv.AppendColumn(createColumn("Timestamp", 12)) - scol := createColumn("Is SOCKS", 12) + scol := createColumn("Is SOCKS", 13) scol.SetVisible(false) tv.AppendColumn(scol) - tv.AppendColumn(createColumn("Details", 13)) + tv.AppendColumn(createColumn("Details", 14)) - acol := createColumn("Scope", 14) + acol := createColumn("Scope", 15) acol.SetVisible(false) tv.AppendColumn(acol) diff --git a/sgfw/dbus.go b/sgfw/dbus.go index f365160..95d42e2 100644 --- a/sgfw/dbus.go +++ b/sgfw/dbus.go @@ -241,6 +241,7 @@ func (ds *dbusServer) GetPendingRequests(policy string) ([]string, *dbus.Error) pstr += strconv.FormatInt(int64(pc.procInfo().Pid), 10) + "|" pstr += pc.sandbox() + "|" pstr += strconv.FormatBool(pc.socks()) + "|" + pstr += pc.getTimestamp() + "|" pstr += pc.getOptString() + "|" pstr += strconv.FormatUint(uint64(FirewallConfig.DefaultActionID), 10) pending_data = append(pending_data, pstr) diff --git a/sgfw/policy.go b/sgfw/policy.go index 10495ef..936ae73 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -13,6 +13,7 @@ import ( "net" "os" "syscall" + "time" "unsafe" ) @@ -53,6 +54,7 @@ type pendingConnection interface { setPrompter(*prompter) getPrompter() *prompter getGUID() string + getTimestamp() string print() string } @@ -65,6 +67,7 @@ type pendingPkt struct { prompting bool prompter *prompter guid string + timestamp time.Time } /* Not a *REAL* GUID */ @@ -97,6 +100,10 @@ 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) socks() bool { return false } @@ -281,13 +288,9 @@ func (fw *Firewall) policyForPath(path string) *Policy { return fw.policyMap[path] } -func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, optstr string) { - +func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, timestamp time.Time, pinfo *procsnitch.Info, optstr string) { fmt.Println("policy processPacket()") - /* hbytes, err := pkt.GetHWAddr() - if err != nil { - log.Notice("Failed to get HW address underlying packet: ", err) - } else { log.Notice("got hwaddr: ", hbytes) } */ + p.lock.Lock() defer p.lock.Unlock() dstb := pkt.Packet.NetworkLayer().NetworkFlow().Dst().Raw() @@ -320,7 +323,7 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, o case FILTER_ALLOW: pkt.Accept() case FILTER_PROMPT: - p.processPromptResult(&pendingPkt{pol: p, name: name, pkt: pkt, pinfo: pinfo, optstring: optstr, prompter: nil, prompting: false}) + p.processPromptResult(&pendingPkt{pol: p, name: name, pkt: pkt, pinfo: pinfo, optstring: optstr, prompter: nil, timestamp: timestamp, prompting: false}) default: log.Warningf("Unexpected filter result: %d", result) } @@ -499,7 +502,7 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, SrcIp, SrcPort, name, DstPort) } -func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { +func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket, timestamp time.Time) { fmt.Println("firewall: filterPacket()") isudp := pkt.Packet.Layer(layers.LayerTypeUDP) != nil @@ -578,7 +581,7 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { */ policy := fw.PolicyForPathAndSandbox(ppath, pinfo.Sandbox) //log.Notice("XXX: flunked basicallowpacket; policy = ", policy) - policy.processPacket(pkt, pinfo, optstring) + policy.processPacket(pkt, timestamp, pinfo, optstring) } func readFileDirect(filename string) ([]byte, error) { diff --git a/sgfw/prompt.go b/sgfw/prompt.go index d997c8d..bd7ef07 100644 --- a/sgfw/prompt.go +++ b/sgfw/prompt.go @@ -248,6 +248,7 @@ func (p *prompter) processConnection(pc pendingConnection) { int32(pc.procInfo().Pid), pc.sandbox(), pc.socks(), + pc.getTimestamp(), pc.getOptString(), FirewallConfig.PromptExpanded, FirewallConfig.PromptExpert, diff --git a/sgfw/sgfw.go b/sgfw/sgfw.go index 8aa49ac..fe6a428 100644 --- a/sgfw/sgfw.go +++ b/sgfw/sgfw.go @@ -1,16 +1,16 @@ package sgfw import ( + "bufio" + "encoding/json" + "fmt" "os" "os/signal" "regexp" + "strings" "sync" "syscall" - // "time" - "bufio" - "encoding/json" - "fmt" - "strings" + "time" "github.com/op/go-logging" nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" @@ -110,6 +110,8 @@ func (fw *Firewall) runFilter() { go func() { for p := range ps { + timestamp := time.Now() + if fw.isEnabled() { ipLayer := p.Packet.Layer(layers.LayerTypeIPv4) if ipLayer == nil { @@ -127,7 +129,7 @@ func (fw *Firewall) runFilter() { } - fw.filterPacket(p) + fw.filterPacket(p, timestamp) } else { p.Accept() } diff --git a/sgfw/socks_server_chain.go b/sgfw/socks_server_chain.go index 85ee570..ad597dc 100644 --- a/sgfw/socks_server_chain.go +++ b/sgfw/socks_server_chain.go @@ -59,6 +59,7 @@ type pendingSocksConnection struct { prompter *prompter guid string optstr string + timestamp time.Time } func (sc *pendingSocksConnection) sandbox() string { @@ -116,17 +117,31 @@ func (sc *pendingSocksConnection) deliverVerdict(v int) { } } -func (sc *pendingSocksConnection) accept() { sc.deliverVerdict(socksVerdictAccept) } +func (sc *pendingSocksConnection) accept() { + sc.deliverVerdict(socksVerdictAccept) +} // need to generalize special accept -func (sc *pendingSocksConnection) acceptTLSOnly() { sc.deliverVerdict(socksVerdictAcceptTLSOnly) } +func (sc *pendingSocksConnection) acceptTLSOnly() { + sc.deliverVerdict(socksVerdictAcceptTLSOnly) +} -func (sc *pendingSocksConnection) drop() { sc.deliverVerdict(socksVerdictDrop) } +func (sc *pendingSocksConnection) drop() { + sc.deliverVerdict(socksVerdictDrop) +} + +func (sc *pendingSocksConnection) setPrompter(val *prompter) { + sc.prompter = val +} -func (sc *pendingSocksConnection) setPrompter(val *prompter) { sc.prompter = val } +func (sc *pendingSocksConnection) getPrompter() *prompter { + return sc.prompter +} -func (sc *pendingSocksConnection) getPrompter() *prompter { return sc.prompter } +func (sc *pendingSocksConnection) getTimestamp() string { + return sc.timestamp.Format("15:04:05.00") +} func (sc *pendingSocksConnection) getGUID() string { if sc.guid == "" { @@ -136,11 +151,17 @@ func (sc *pendingSocksConnection) getGUID() string { return sc.guid } -func (sc *pendingSocksConnection) getPrompting() bool { return sc.prompting } +func (sc *pendingSocksConnection) getPrompting() bool { + return sc.prompting +} -func (sc *pendingSocksConnection) setPrompting(val bool) { sc.prompting = val } +func (sc *pendingSocksConnection) setPrompting(val bool) { + sc.prompting = val +} -func (sc *pendingSocksConnection) print() string { return "socks connection" } +func (sc *pendingSocksConnection) print() string { + return "socks connection" +} func NewSocksChain(cfg *socksChainConfig, wg *sync.WaitGroup, fw *Firewall) *socksChain { chain := socksChain{ @@ -163,10 +184,11 @@ func (s *socksChain) start() { } s.wg.Add(1) - go s.socksAcceptLoop() + ts := time.Now() + go s.socksAcceptLoop(ts) } -func (s *socksChain) socksAcceptLoop() error { +func (s *socksChain) socksAcceptLoop(timestamp time.Time) error { defer s.wg.Done() defer s.listener.Close() @@ -180,11 +202,11 @@ func (s *socksChain) socksAcceptLoop() error { continue } session := &socksChainSession{cfg: s.cfg, clientConn: conn, procInfo: s.procInfo, server: s} - go session.sessionWorker() + go session.sessionWorker(timestamp) } } -func (c *socksChainSession) sessionWorker() { +func (c *socksChainSession) sessionWorker(timestamp time.Time) { defer c.clientConn.Close() clientAddr := c.clientConn.RemoteAddr() @@ -214,7 +236,7 @@ func (c *socksChainSession) sessionWorker() { c.req.ReplyAddr(ReplySucceeded, c.bndAddr) } case CommandConnect: - verdict, tls := c.filterConnect() + verdict, tls := c.filterConnect(timestamp) if !verdict { c.req.Reply(ReplyConnectionRefused) @@ -295,7 +317,7 @@ func findProxyEndpoint(pdata []string, conn net.Conn) (*procsnitch.Info, string) return nil, "" } -func (c *socksChainSession) filterConnect() (bool, bool) { +func (c *socksChainSession) filterConnect(timestamp time.Time) (bool, bool) { // return filter verdict, tlsguard allProxies, err := ListProxies() @@ -383,6 +405,7 @@ func (c *socksChainSession) filterConnect() (bool, bool) { prompting: false, prompter: nil, optstr: optstr, + timestamp: timestamp, } policy.processPromptResult(pending) v := <-pending.verdict diff --git a/sgfw/tlsguard.go b/sgfw/tlsguard.go index fbcd51a..e98117a 100644 --- a/sgfw/tlsguard.go +++ b/sgfw/tlsguard.go @@ -2,6 +2,7 @@ package sgfw import ( "crypto/x509" + "encoding/binary" "errors" "fmt" "io" @@ -62,6 +63,20 @@ type connReader struct { err error } +var cipherSuiteMap map[uint16]string = map[uint16]string{ + 0x0000: "TLS_NULL_WITH_NULL_NULL", + 0x0030: "TLS_DH_DSS_WITH_AES_128_CBC_SHA", +} + +func getCipherSuiteName(value uint) string { + val, ok := cipherSuiteMap[uint16(value)] + if !ok { + return "UNKNOWN" + } + + return val +} + func connectionReader(conn net.Conn, is_client bool, c chan connReader, done chan bool) { var ret_error error = nil buffered := []byte{} @@ -226,8 +241,8 @@ select_loop: other.Write(cr.data) continue } else if cr.client { - other.Write(cr.data) - continue + // other.Write(cr.data) + // continue } else if cr.rtype != SSL3_RT_HANDSHAKE { return errors.New(fmt.Sprintf("Expected TLS server handshake byte was not received [%#x vs 0x16]", cr.rtype)) } @@ -236,8 +251,8 @@ select_loop: return errors.New(fmt.Sprintf("TLSGuard dropping connection with unknown content type: %#x", cr.rtype)) } - serverMsg := cr.data[5:] - s := uint(serverMsg[0]) + handshakeMsg := cr.data[5:] + s := uint(handshakeMsg[0]) fmt.Printf("s = %#x\n", s) if cr.client && s != uint(client_expected) { @@ -246,6 +261,31 @@ select_loop: return errors.New(fmt.Sprintf("Server sent handshake type %#x but expected %#x", s, server_expected)) } + if cr.client { + if s == SSL3_MT_CLIENT_HELLO { + fmt.Println("CLIENT HELLO") + hello_offset := 4 + // 2 byte protocol version + fmt.Println("CLIENT HELLO VERSION = ", handshakeMsg[hello_offset:hello_offset+2]) + hello_offset += 2 + // 4 byte Random/GMT time + fmt.Println("CLIENT HELLO GMT = ", handshakeMsg[hello_offset:hello_offset+4]) + hello_offset += 4 + // 28 bytes Random/random_bytes + hello_offset += 28 + // 1 byte (32-bit session ID) + fmt.Println("CLIENT HELLO SESSION ID = ", handshakeMsg[hello_offset:hello_offset+1]) + hello_offset++ + // 2 byte cipher suite array + cs := binary.BigEndian.Uint16(handshakeMsg[hello_offset : hello_offset+2]) + fmt.Printf("cs = %v / %#v\n", cs, cs) + fmt.Printf("CLIENT HELLO CIPHERSUITE: %v (%s)\n", handshakeMsg[hello_offset:hello_offset+2], getCipherSuiteName(uint(cs))) + } + + other.Write(cr.data) + continue + } + if !cr.client && s == SSL3_MT_HELLO_REQUEST { fmt.Println("Server sent hello request") continue @@ -256,16 +296,16 @@ select_loop: } // Message len, 3 bytes - serverMessageLen := serverMsg[1:4] - serverMessageLenInt := int(int(serverMessageLen[0])<<16 | int(serverMessageLen[1])<<8 | int(serverMessageLen[2])) + handshakeMessageLen := handshakeMsg[1:4] + handshakeMessageLenInt := int(int(handshakeMessageLen[0])<<16 | int(handshakeMessageLen[1])<<8 | int(handshakeMessageLen[2])) if s == SSL3_MT_CERTIFICATE { fmt.Println("HMM") - // fmt.Printf("chunk len = %v, serverMsgLen = %v, slint = %v\n", len(chunk), len(serverMsg), serverMessageLenInt) - if len(serverMsg) < serverMessageLenInt { - return errors.New(fmt.Sprintf("len(serverMsg) %v < serverMessageLenInt %v!\n", len(serverMsg), serverMessageLenInt)) + // fmt.Printf("chunk len = %v, handshakeMsgLen = %v, slint = %v\n", len(chunk), len(handshakeMsg), handshakeMessageLenInt) + if len(handshakeMsg) < handshakeMessageLenInt { + return errors.New(fmt.Sprintf("len(handshakeMsg) %v < handshakeMessageLenInt %v!\n", len(handshakeMsg), handshakeMessageLenInt)) } - serverHelloBody := serverMsg[4 : 4+serverMessageLenInt] + serverHelloBody := handshakeMsg[4 : 4+handshakeMessageLenInt] certChainLen := int(int(serverHelloBody[0])<<16 | int(serverHelloBody[1])<<8 | int(serverHelloBody[2])) remaining := certChainLen pos := serverHelloBody[3:certChainLen]