From f3f5414fd41ef7c48e68d73d4cccd9bbb315b1fa Mon Sep 17 00:00:00 2001 From: dma Date: Sun, 10 Sep 2017 00:34:48 +0000 Subject: [PATCH] Support for TLSGuard in prompter --- fw-prompt/dbus.go | 5 +- fw-prompt/fw-prompt.go | 2 +- gnome-shell/firewall@subgraph.com/dialog.js | 84 ++++++++++-- .../firewall@subgraph.com/extension.js | 7 +- sgfw/const.go | 6 + sgfw/ipc.go | 21 ++- sgfw/policy.go | 128 ++++++++++-------- sgfw/prompt.go | 24 +++- sgfw/rules.go | 95 ++++++------- sgfw/sgfw.go | 53 ++++---- sgfw/socks_server_chain.go | 61 ++++++--- 11 files changed, 308 insertions(+), 178 deletions(-) diff --git a/fw-prompt/dbus.go b/fw-prompt/dbus.go index 933cb66..9227f2e 100644 --- a/fw-prompt/dbus.go +++ b/fw-prompt/dbus.go @@ -27,6 +27,7 @@ type promptData struct { Username string Groupname string Pid int + Sandbox string OptString string Expanded bool Expert bool @@ -63,10 +64,10 @@ func newDbusServer() (*dbusServer, error) { return ds, nil } -func (ds *dbusServer) RequestPrompt(application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, +func (ds *dbusServer) RequestPrompt(application, icon, path, address string, port int32, ip, origin, proto string, uid, gid int32, username, groupname string, pid int32, sandbox string, optstring string, expanded, expert bool, action int32) (int32, string, *dbus.Error) { log.Printf("request prompt: app = %s, icon = %s, path = %s, address = %s, action = %v\n", application, icon, path, address, action) - decision := addRequest(nil, path, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, optstring) + decision := addRequest(nil, path, proto, int(pid), ip, address, int(port), int(uid), int(gid), origin, optstring, sandbox) log.Print("Waiting on decision...") decision.Cond.L.Lock() for !decision.Ready { diff --git a/fw-prompt/fw-prompt.go b/fw-prompt/fw-prompt.go index 9915f5f..9a53812 100644 --- a/fw-prompt/fw-prompt.go +++ b/fw-prompt/fw-prompt.go @@ -319,7 +319,7 @@ func createListStore(general bool) *gtk.ListStore { return listStore } -func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin, optstring string) *decisionWaiter { +func addRequest(listStore *gtk.ListStore, path, proto string, pid int, ipaddr, hostname string, port, uid, gid int, origin, optstring string, sandbox string) *decisionWaiter { if listStore == nil { listStore = globalLS } diff --git a/gnome-shell/firewall@subgraph.com/dialog.js b/gnome-shell/firewall@subgraph.com/dialog.js index 427cad7..f1fe7dd 100644 --- a/gnome-shell/firewall@subgraph.com/dialog.js +++ b/gnome-shell/firewall@subgraph.com/dialog.js @@ -19,7 +19,7 @@ const RuleScope = { const DetailSection = new Lang.Class({ Name: 'DetailSection', - _init: function() { + _init: function(sandboxed) { this.actor = new St.BoxLayout({ style_class: 'fw-details-section' }); this._left = new St.BoxLayout({ vertical: true, style_class: 'fw-details-left'}); this._right = new St.BoxLayout({ vertical: true }); @@ -32,6 +32,11 @@ const DetailSection = new Lang.Class({ this.origin = this._addDetails("Origin:"); this.user = this._addDetails("User:"); this.group = this._addDetails("Group:"); + this.sandboxed = sandboxed; + + if (sandboxed) { + this.sandbox = this._addDetails("Sandbox:"); + } this.optstring = this._addDetails(""); }, @@ -43,7 +48,7 @@ const DetailSection = new Lang.Class({ return msg; }, - setDetails: function(ip, path, pid, uid, gid, user, group, origin, proto, optstring) { + setDetails: function(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox) { this.ipAddr.text = ip; this.path.text = path; @@ -73,6 +78,10 @@ const DetailSection = new Lang.Class({ this.group.text = "gid:" + gid.toString(); } + if (sandbox != "") { + this.sandbox.text = sandbox; + } + this.optstring.text = optstring } }); @@ -130,7 +139,7 @@ Signals.addSignalMethods(OptionListItem.prototype); const OptionList = new Lang.Class({ Name: 'OptionList', - _init: function(pid_known) { + _init: function(pid_known, sandboxed) { this.actor = new St.BoxLayout({vertical: true, style_class: 'fw-option-list'}); if (pid_known) { this.buttonGroup = new ButtonGroup("Forever", "Session", "Once", "PID"); @@ -140,6 +149,7 @@ const OptionList = new Lang.Class({ this.actor.add_child(this.buttonGroup.actor); this.items = []; this._selected; + this.tlsGuard = true; }, setOptionText: function(idx, text) { @@ -149,6 +159,29 @@ const OptionList = new Lang.Class({ } this.items[idx].setText(text); }, + + addTLSOption: function(tlsGuardEnabled) { + let tlsg = new OptionListItem("Drop connection if not TLS with valid certificate",0); + tlsg.setSelected(tlsGuardEnabled); + tlsg.connect('selected', Lang.bind(this, function() { + this._toggleTLSGuard(tlsg); + })); + let emptyRow = new OptionListItem("",0); + this.actor.add_child(emptyRow.actor); + this.actor.add_child(tlsg.actor); + }, + + _toggleTLSGuard: function(item) { + if (this.tlsGuard == true) { + item.actor.remove_style_pseudo_class('selected'); + item.setSelected(false); + this.tlsGuard = false; + } else { + this.tlsGuard = true; + item.actor.add_style_pseudo_class('selected'); + item.setSelected(true) + } + }, addOptions: function(options) { for(let i = 0; i < options.length; i++) { @@ -158,7 +191,7 @@ const OptionList = new Lang.Class({ this._optionSelected(this.items[0]) } }, - + _addOption: function(text, idx) { let item = new OptionListItem(text, idx); item.connect('selected', Lang.bind(this, function() { @@ -422,7 +455,7 @@ const PromptDialog = new Lang.Class({ Name: 'PromptDialog', Extends: ModalDialog.ModalDialog, - _init: function(invocation, pid_known) { + _init: function(invocation, pid_known, sandboxed) { this.parent({ styleClass: 'fw-prompt-dialog' }); this._invocation = invocation; this.header = new PromptDialogHeader(); @@ -432,10 +465,10 @@ const PromptDialog = new Lang.Class({ this.contentLayout.add(this.details.actor, {y_fill: false, x_fill: true}); let box = new St.BoxLayout({ vertical: true }); this.details.set_child(box); - this.info = new DetailSection(); + this.info = new DetailSection(sandboxed); box.add_child(this.info.actor); - this.optionList = new OptionList(pid_known); + this.optionList = new OptionList(pid_known, sandboxed); box.add_child(this.optionList.actor); this.optionList.addOptions([ "Only PORT AND ADDRESS", @@ -443,6 +476,13 @@ const PromptDialog = new Lang.Class({ "Only PORT", "Any Connection"]); + if (sandboxed) { + this.optionList.addTLSOption(true); + } + + // let tlsGuard = new OptionListItem("Drop connection if not TLS with valid certificate.",0); + //box.add_child(optionList.actor); + this._initialKeyFocusDestroyId = 1; this.setButtons([ { label: "Allow", action: Lang.bind(this, this.onAllow) }, @@ -466,10 +506,16 @@ const PromptDialog = new Lang.Class({ } let verb = "DENY"; if(allow) { - verb = "ALLOW"; + verb = "ALLOW"; + if (this.optionList.tlsGuard) { + verb = "ALLOW_TLSONLY"; + } else { + verb = "ALLOW"; + } } - let rule = verb + "|" + this.ruleTarget(); - let scope = this.optionList.selectedScope() + let rule = verb + "|" + this.ruleTarget() + "|" + this.ruleSandbox(); + + let scope = this.optionList.selectedScope(); this._invocation.return_value(GLib.Variant.new('(is)', [scope, rule])); this._invocation = null; }, @@ -491,10 +537,20 @@ const PromptDialog = new Lang.Class({ } }, - update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action) { + ruleSandbox: function() { + return this._sandbox; + }, + + ruleTLSGuard: function() { + return this.optionList.tlsGuard; + }, + + update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, sandbox, expanded, expert, action) { this._address = address; this._port = port; this._proto = proto; + this._sandbox = sandbox; + this._tlsGuard; let port_str = (proto+"").toUpperCase() + " Port "+ port; @@ -502,6 +558,10 @@ const PromptDialog = new Lang.Class({ port_str = (proto+"").toUpperCase() + " Code "+ port; } + if (sandbox != "") { + application = application + " (sandboxed)" + } + this.header.setTitle(application); if (proto == "tcp") { @@ -548,6 +608,6 @@ const PromptDialog = new Lang.Class({ } this.optionList.buttonGroup._setChecked(this.optionList.scopeToIdx(action)) - this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, proto, optstring); + this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, proto, optstring, sandbox); }, }); diff --git a/gnome-shell/firewall@subgraph.com/extension.js b/gnome-shell/firewall@subgraph.com/extension.js index 3499a6d..59d63b9 100644 --- a/gnome-shell/firewall@subgraph.com/extension.js +++ b/gnome-shell/firewall@subgraph.com/extension.js @@ -57,6 +57,7 @@ const FirewallPromptInterface = ' \ \ \ \ + \ \ \ \ @@ -92,11 +93,11 @@ const FirewallPromptHandler = new Lang.Class({ }, RequestPromptAsync: function(params, invocation) { - let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, optstring, expanded, expert, action] = params; + let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, sandbox, optstring, expanded, expert, action] = params; // this._closeDialog(); - this._dialog = new Dialog.PromptDialog(invocation, (pid >= 0)); + this._dialog = new Dialog.PromptDialog(invocation, (pid >= 0), (sandbox != "")); this._invocation = invocation; - this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action); + this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, sandbox, expanded, expert, action); this._dialog.open(); }, diff --git a/sgfw/const.go b/sgfw/const.go index 3bb2078..cb0d8f0 100644 --- a/sgfw/const.go +++ b/sgfw/const.go @@ -15,16 +15,19 @@ type RuleAction uint16 const ( RULE_ACTION_DENY RuleAction = iota RULE_ACTION_ALLOW + RULE_ACTION_ALLOW_TLSONLY ) // RuleActionString is used to get a string from an action id var RuleActionString = map[RuleAction]string{ RULE_ACTION_DENY: "DENY", RULE_ACTION_ALLOW: "ALLOW", + RULE_ACTION_ALLOW_TLSONLY: "ALLOW_TLSONLY", } // RuleActionValue is used to get an action id using the action string var RuleActionValue = map[string]RuleAction{ RuleActionString[RULE_ACTION_DENY]: RULE_ACTION_DENY, RuleActionString[RULE_ACTION_ALLOW]: RULE_ACTION_ALLOW, + RuleActionString[RULE_ACTION_ALLOW_TLSONLY]: RULE_ACTION_ALLOW_TLSONLY, } //RuleMode contains the time scope of a rule @@ -94,18 +97,21 @@ const ( FILTER_DENY FilterResult = iota FILTER_ALLOW FILTER_PROMPT + FILTER_ALLOW_TLSONLY ) // FilterResultString converts a filter value ID to its string var FilterResultString = map[FilterResult]string{ FILTER_DENY: "DENY", FILTER_ALLOW: "ALLOW", FILTER_PROMPT: "PROMPT", + FILTER_ALLOW_TLSONLY: "ALLOW_TLSONLY", } // FilterResultValue converts a filter value string to its ID var FilterResultValue = map[string]FilterResult{ FilterResultString[FILTER_DENY]: FILTER_DENY, FilterResultString[FILTER_ALLOW]: FILTER_ALLOW, FilterResultString[FILTER_PROMPT]: FILTER_PROMPT, + FilterResultString[FILTER_ALLOW_TLSONLY]: FILTER_ALLOW_TLSONLY, } // DbusRule struct of the rule passed to the dbus interface diff --git a/sgfw/ipc.go b/sgfw/ipc.go index f048fcf..9286130 100644 --- a/sgfw/ipc.go +++ b/sgfw/ipc.go @@ -18,12 +18,13 @@ const ReceiverSocketPath = "/tmp/fwoz.sock" type OzInitProc struct { Name string Pid int + SandboxID int } var OzInitPids []OzInitProc = []OzInitProc{} -func addInitPid(pid int, name string) { +func addInitPid(pid int, name string, sboxid int) { fmt.Println("::::::::::: init pid added: ", pid, " -> ", name) for i := 0; i < len(OzInitPids); i++ { if OzInitPids[i].Pid == pid { @@ -31,7 +32,7 @@ fmt.Println("::::::::::: init pid added: ", pid, " -> ", name) } } - ozi := OzInitProc{Name: name, Pid: pid} + ozi := OzInitProc{Name: name, Pid: pid, SandboxID: sboxid} OzInitPids = append(OzInitPids, ozi) } @@ -163,6 +164,7 @@ func ReceiverLoop(fw *Firewall, c net.Conn) { if tokens[0] == "register-init" && len(tokens) >= 3 { initp := tokens[1] + initpid, err := strconv.Atoi(initp) if err != nil { @@ -171,8 +173,15 @@ func ReceiverLoop(fw *Firewall, c net.Conn) { return } - ozname := strings.Join(tokens[2:], " ") - addInitPid(initpid, ozname) + sboxid, err := strconv.Atoi(tokens[3]) + if err != nil { + log.Notice("IPC received invalid oz sbox number: ",tokens[3]) + c.Write([]byte("Bad command: sandbox id was invalid")) + return + } + + // ozname := strings.Join(tokens[2:], " ") + addInitPid(initpid, tokens[2], sboxid) c.Write([]byte("OK.\n")) return } else if tokens[0] == "unregister-init" && len(tokens) == 2 { @@ -278,8 +287,8 @@ func OzReceiver(fw *Firewall) { if len(sboxes) > 0 { log.Warning("Adding existing Oz sandbox init pids...") for s := 0; s < len(sboxes); s++ { - profname := fmt.Sprintf("%s (%d)", sboxes[s].Profile, sboxes[s].Id) - addInitPid(sboxes[s].InitPid, profname) + //profname := fmt.Sprintf("%s (%d)", sboxes[s].Profile, sboxes[s].Id) + addInitPid(sboxes[s].InitPid, sboxes[s].Profile, sboxes[s].Id) } } else { log.Warning("It does not appear there were any Oz sandboxed processes already launched.") diff --git a/sgfw/policy.go b/sgfw/policy.go index c0cf57d..c8873dc 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -2,20 +2,20 @@ package sgfw import ( "fmt" - "strings" "strconv" + "strings" "sync" -// "encoding/binary" + // "encoding/binary" -// nfnetlink "github.com/subgraph/go-nfnetlink" - nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" + // nfnetlink "github.com/subgraph/go-nfnetlink" "github.com/google/gopacket/layers" + nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" "github.com/subgraph/go-procsnitch" "net" + "os" "syscall" "unsafe" - "os" ) var _interpreters = []string{ @@ -45,7 +45,9 @@ type pendingConnection interface { srcPort() uint16 dst() net.IP dstPort() uint16 + sandbox() string accept() + acceptTLSOnly() drop() setPrompting(bool) getPrompting() bool @@ -53,10 +55,10 @@ type pendingConnection interface { } type pendingPkt struct { - pol *Policy - name string - pkt *nfqueue.NFQPacket - pinfo *procsnitch.Info + pol *Policy + name string + pkt *nfqueue.NFQPacket + pinfo *procsnitch.Info optstring string prompting bool } @@ -72,6 +74,10 @@ func getEmptyPInfo() *procsnitch.Info { return &pinfo } +func (pp *pendingPkt) sandbox() string { + return pp.pinfo.Sandbox +} + func (pp *pendingPkt) policy() *Policy { return pp.pol } @@ -142,6 +148,13 @@ func (pp *pendingPkt) accept() { pp.pkt.Accept() } +func (pp *pendingPkt) acceptTLSOnly() { + // Not implemented + + pp.pkt.SetMark(1) + pp.pkt.Accept() +} + func (pp *pendingPkt) drop() { pp.pkt.SetMark(1) pp.pkt.Accept() @@ -196,10 +209,10 @@ func (fw *Firewall) policyForPath(path string) *Policy { func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, optstr string) { -/* hbytes, err := pkt.GetHWAddr() - if err != nil { - log.Notice("Failed to get HW address underlying packet: ", err) - } else { log.Notice("got hwaddr: ", hbytes) } */ + /* 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() @@ -209,7 +222,7 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, o if !FirewallConfig.LogRedact { log.Infof("Lookup(%s): %s", dstip.String(), name) } -// fwo := matchAgainstOzRules(srcip, dstip, dstp) + // fwo := matchAgainstOzRules(srcip, dstip, dstp) result := p.rules.filterPacket(pkt, pinfo, srcip, name, optstr) switch result { @@ -227,7 +240,8 @@ func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info, o func (p *Policy) processPromptResult(pc pendingConnection) { p.pendingQueue = append(p.pendingQueue, pc) -fmt.Println("processPromptResult(): p.promptInProgress = ", p.promptInProgress) + fmt.Println("im here now.. processing prompt result..") + fmt.Println("processPromptResult(): p.promptInProgress = ", p.promptInProgress) if DoMultiPrompt || (!DoMultiPrompt && !p.promptInProgress) { p.promptInProgress = true go p.fw.dbus.prompt(p) @@ -248,13 +262,13 @@ func (p *Policy) nextPending() (pendingConnection, bool) { return nil, true } -// for len(p.pendingQueue) != 0 { - for i := 0; i < len(p.pendingQueue); i++ { - if !p.pendingQueue[i].getPrompting() { - return p.pendingQueue[i], false - } + // for len(p.pendingQueue) != 0 { + for i := 0; i < len(p.pendingQueue); i++ { + if !p.pendingQueue[i].getPrompting() { + return p.pendingQueue[i], false } -// } + } + // } return nil, false } @@ -281,7 +295,7 @@ func (p *Policy) processNewRule(r *Rule, scope FilterScope) bool { if scope != APPLY_ONCE { p.rules = append(p.rules, r) } - + log.Noticef("processNewRule: ",r) p.filterPending(r) if len(p.pendingQueue) == 0 { p.promptInProgress = false @@ -291,7 +305,7 @@ func (p *Policy) processNewRule(r *Rule, scope FilterScope) bool { } func (p *Policy) parseRule(s string, add bool) (*Rule, error) { -log.Noticef("XXX: attempt to parse rule: |%s|\n", s) + log.Noticef("XXX: attempt to parse rule: |%s|\n", s) r := new(Rule) r.pid = -1 r.mode = RULE_MODE_PERMANENT @@ -324,15 +338,17 @@ func (p *Policy) removeRule(r *Rule) { func (p *Policy) filterPending(rule *Rule) { remaining := []pendingConnection{} for _, pc := range p.pendingQueue { - if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.proto(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID)) { + if rule.match(pc.src(), pc.dst(), pc.dstPort(), pc.hostname(), pc.proto(), pc.procInfo().UID, pc.procInfo().GID, uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID), pc.procInfo().Sandbox) { log.Infof("Adding rule for: %s", rule.getString(FirewallConfig.LogRedact)) log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print()) if rule.rtype == RULE_ACTION_ALLOW { pc.accept() + } else if rule.rtype == RULE_ACTION_ALLOW_TLSONLY { + pc.acceptTLSOnly() } else { srcs := pc.src().String() + ":" + strconv.Itoa(int(pc.srcPort())) - log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d (user prompt)", - pc.procInfo().ExePath, pc.proto(), srcs, pc.dst(), pc.dstPort) + log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d (user prompt) %v", + pc.procInfo().ExePath, pc.proto(), srcs, pc.dst(), pc.dstPort, rule.rtype) pc.drop() } } else { @@ -390,7 +406,6 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info return fmt.Sprintf("(%s %s:%d -> %s:%d)", proto, SrcIp, SrcPort, name, DstPort) } - return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, SrcIp, SrcPort, name, DstPort) } @@ -412,20 +427,20 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { } _, dstip := getPacketIPAddrs(pkt) -/* _, dstp := getPacketPorts(pkt) - fwo := matchAgainstOzRules(srcip, dstip, dstp) - log.Notice("XXX: Attempting [2] to filter packet on rules -> ", fwo) + /* _, dstp := getPacketPorts(pkt) + fwo := eatchAgainstOzRules(srcip, dstip, dstp) + log.Notice("XXX: Attempting [2] to filter packet on rules -> ", fwo) - if fwo == OZ_FWRULE_WHITELIST { - log.Noticef("Automatically passed through whitelisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp) - pkt.Accept() - return - } else if fwo == OZ_FWRULE_BLACKLIST { - log.Noticef("Automatically blocking blacklisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp) - pkt.SetMark(1) - pkt.Accept() - return - } */ + if fwo == OZ_FWRULE_WHITELIST { + log.Noticef("Automatically passed through whitelisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp) + pkt.Accept() + return + } else if fwo == OZ_FWRULE_BLACKLIST { + log.Noticef("Automatically blocking blacklisted sandbox traffic from %s to %s:%d\n", srcip, dstip, dstp) + pkt.SetMark(1) + pkt.Accept() + return + } */ ppath := "*" strictness := procsnitch.MATCH_STRICT @@ -440,8 +455,8 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { ppath = "[unknown]" optstring = "[Connection could not be mapped]" log.Warningf("No proc found for %s", printPacket(pkt, fw.dns.Lookup(dstip, pinfo.Pid), nil)) -// pkt.Accept() -// return + // pkt.Accept() + // return } else { ppath = pinfo.ExePath cf := strings.Fields(pinfo.CmdLine) @@ -455,13 +470,13 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) { } } log.Debugf("filterPacket [%s] %s", ppath, printPacket(pkt, fw.dns.Lookup(dstip, pinfo.Pid), nil)) -/* if basicAllowPacket(pkt) { - pkt.Accept() - return - } -*/ + /* if basicAllowPacket(pkt) { + pkt.Accept() + return + } + */ policy := fw.PolicyForPath(ppath) -//log.Notice("XXX: flunked basicallowpacket; policy = ", policy) + //log.Notice("XXX: flunked basicallowpacket; policy = ", policy) policy.processPacket(pkt, pinfo, optstring) } @@ -502,7 +517,7 @@ func getAllProcNetDataLocal() ([]string, error) { for i := 0; i < len(OzInitPids); i++ { fname := fmt.Sprintf("/proc/%d/net/tcp", OzInitPids[i]) -//fmt.Println("XXX: opening: ", fname) + //fmt.Println("XXX: opening: ", fname) bdata, err := readFileDirect(fname) if err != nil { @@ -558,7 +573,7 @@ func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, pro for i := 0; i < len(OzInitPids); i++ { data := "" fname := fmt.Sprintf("/proc/%d/net/%s", OzInitPids[i].Pid, proto) -//fmt.Println("XXX: opening: ", fname) + //fmt.Println("XXX: opening: ", fname) bdata, err := readFileDirect(fname) if err != nil { @@ -594,7 +609,8 @@ func LookupSandboxProc(srcip net.IP, srcp uint16, dstip net.IP, dstp uint16, pro } if res != nil { - optstr = "Sandbox: " + OzInitPids[i].Name + // optstr = "Sandbox: " + OzInitPids[i].Name + res.Sandbox = OzInitPids[i].Name res.ExePath = getRealRoot(res.ExePath, OzInitPids[i].Pid) break } @@ -657,7 +673,7 @@ func findProcessForPacket(pkt *nfqueue.NFQPacket, reverse bool, strictness int) for i := 0; i < len(OzInitPids); i++ { data := "" fname := fmt.Sprintf("/proc/%d/net/%s", OzInitPids[i].Pid, proto) -//fmt.Println("XXX: opening: ", fname) + //fmt.Println("XXX: opening: ", fname) bdata, err := readFileDirect(fname) if err != nil { @@ -725,9 +741,9 @@ func basicAllowPacket(pkt *nfqueue.NFQPacket) bool { return dstip.IsLoopback() || dstip.IsLinkLocalMulticast() || (pkt.Packet.Layer(layers.LayerTypeTCP) == nil && - pkt.Packet.Layer(layers.LayerTypeUDP) == nil && - pkt.Packet.Layer(layers.LayerTypeICMPv4) == nil && - pkt.Packet.Layer(layers.LayerTypeICMPv6) == nil) + pkt.Packet.Layer(layers.LayerTypeUDP) == nil && + pkt.Packet.Layer(layers.LayerTypeICMPv4) == nil && + pkt.Packet.Layer(layers.LayerTypeICMPv6) == nil) } func getPacketIPAddrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { @@ -741,7 +757,7 @@ func getPacketIPAddrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { if ipLayer == nil { if ipv4 { - return net.IP{0,0,0,0}, net.IP{0,0,0,0} + return net.IP{0, 0, 0, 0}, net.IP{0, 0, 0, 0} } return net.IP{}, net.IP{} } diff --git a/sgfw/prompt.go b/sgfw/prompt.go index e31f6e9..a6bdb7b 100644 --- a/sgfw/prompt.go +++ b/sgfw/prompt.go @@ -4,7 +4,7 @@ import ( "fmt" "os/user" "strconv" - //"strings" + "strings" "sync" "time" @@ -140,6 +140,8 @@ func (p *prompter) processConnection(pc pendingConnection) { if pc.dst() != nil { dststr = pc.dst().String() + } else { + dststr = addr + " (proxy to resolve)" } call := p.dbusObj.Call("com.subgraph.FirewallPrompt.RequestPrompt", 0, @@ -156,6 +158,7 @@ func (p *prompter) processConnection(pc pendingConnection) { uidToUser(pc.procInfo().UID), gidToGroup(pc.procInfo().GID), int32(pc.procInfo().Pid), + pc.sandbox(), pc.getOptString(), FirewallConfig.PromptExpanded, FirewallConfig.PromptExpert, @@ -177,6 +180,16 @@ func (p *prompter) processConnection(pc pendingConnection) { // sometimes there's a src // this needs to be re-visited + toks := strings.Split(rule, "|") + //verb := toks[0] + //target := toks[1] + sandbox := "" + + if len(toks) > 2 { + sandbox = toks[2] + } + + tempRule := fmt.Sprintf("%s|%s",toks[0],toks[1]) if pc.src() != nil { @@ -184,13 +197,12 @@ func (p *prompter) processConnection(pc pendingConnection) { //rule += "||" //} //ule += "|||" + pc.src().String() - - rule += "||-1:-1||" + pc.src().String() - log.Warningf("Creating rule: %v", rule) + + tempRule += "||-1:-1|"+sandbox+"|" + pc.src().String() } else { - rule += "||-1:-1||" + tempRule += "||-1:-1|"+sandbox+"|" } - r, err := policy.parseRule(rule, false) + r, err := policy.parseRule(tempRule, false) if err != nil { log.Warningf("Error parsing rule string returned from dbus RequestPrompt: %v", err) policy.removePending(pc) diff --git a/sgfw/rules.go b/sgfw/rules.go index 67e44f1..2aa769b 100644 --- a/sgfw/rules.go +++ b/sgfw/rules.go @@ -7,19 +7,20 @@ import ( "net" "os" "path" + "regexp" "strconv" "strings" - "regexp" nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" -// "github.com/subgraph/go-nfnetlink" + // "github.com/subgraph/go-nfnetlink" "github.com/subgraph/go-procsnitch" ) const matchAny = 0 + //const noAddress = uint32(0xffffffff) -var anyAddress net.IP = net.IP{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,} -var noAddress net.IP = net.IP{0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff} +var anyAddress net.IP = net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +var noAddress net.IP = net.IP{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} type Rule struct { id uint @@ -37,7 +38,7 @@ type Rule struct { gid int uname string gname string - sandbox string + sandbox string } func (r *Rule) String() string { @@ -48,6 +49,8 @@ func (r *Rule) getString(redact bool) string { rtype := RuleActionString[RULE_ACTION_DENY] if r.rtype == RULE_ACTION_ALLOW { rtype = RuleActionString[RULE_ACTION_ALLOW] + } else if r.rtype == RULE_ACTION_ALLOW_TLSONLY { + rtype = RuleActionString[RULE_ACTION_ALLOW_TLSONLY] } rmode := "" if r.mode == RULE_MODE_SYSTEM { @@ -67,12 +70,11 @@ func (r *Rule) getString(redact bool) string { sbox := "|" if r.sandbox != "" { - sbox = "|SANDBOX:"+sbox + sbox = "|" + sbox } else { log.Notice("sandbox is ", r.sandbox) } - return fmt.Sprintf("%s|%s%s%s%s%s", rtype, protostr, r.AddrString(redact), rmode, rpriv, sbox) } @@ -84,9 +86,9 @@ func (r *Rule) AddrString(redact bool) string { } else if r.network != nil { addr = r.network.String() } else if !addrMatchesAny(r.addr) && !addrMatchesNone(r.addr) { -// bs := make([]byte, 4) -// binary.BigEndian.PutUint32(bs, r.addr) -// addr = fmt.Sprintf("%d.%d.%d.%d", bs[0], bs[1], bs[2], bs[3]) + // bs := make([]byte, 4) + // binary.BigEndian.PutUint32(bs, r.addr) + // addr = fmt.Sprintf("%d.%d.%d.%d", bs[0], bs[1], bs[2], bs[3]) addr = r.addr.String() } @@ -103,7 +105,7 @@ func (r *Rule) AddrString(redact bool) string { type RuleList []*Rule -func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, proto string, uid, gid int, uname, gname string) bool { +func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, proto string, uid, gid int, uname, gname string, sandbox string) bool { if r.proto != proto { return false } @@ -117,7 +119,7 @@ func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname string, pr return false } -log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", r.addr, " / ", r.hostname, " : ", r.port) + log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", r.addr, " / ", r.hostname, " : ", r.port) if r.port != matchAny && r.port != dstPort { return false } @@ -142,7 +144,7 @@ log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", r.addr, } if proto == "icmp" { fmt.Printf("network = %v, src = %v, r.addr = %x, src to4 = %x\n", r.network, src, r.addr, binary.BigEndian.Uint32(src.To4())) - if (r.network != nil && r.network.Contains(src)) || (r.addr.Equal(src)) { + if (r.network != nil && r.network.Contains(src)) || (r.addr.Equal(src)) { return true } } @@ -160,26 +162,32 @@ func (rl *RuleList) filter(pkt *nfqueue.NFQPacket, src, dst net.IP, dstPort uint return FILTER_PROMPT } result := FILTER_PROMPT - sandboxed := strings.HasPrefix(optstr, "SOCKS5|Tor / Sandbox") + sandboxed := false + if pinfo.Sandbox != "" { + sandboxed = true + } + // sandboxed := strings.HasPrefix(optstr, "SOCKS5|Tor / Sandbox") for _, r := range *rl { -log.Notice("------------ trying match of src ", src, " against: ", r, " | ", r.saddr, " / optstr = ", optstr, "; pid ", pinfo.Pid, " vs rule pid ", r.pid) -log.Notice("r.saddr: ", r.saddr, "src: ", src , "sandboxed ", sandboxed, "optstr: ", optstr) + + log.Notice("fuck ",r) + log.Notice("------------ trying match of src ", src, " against: ", r, " | ", r.saddr, " / optstr = ", optstr, "; pid ", pinfo.Pid, " vs rule pid ", r.pid) + log.Notice("r.saddr: ", r.saddr, "src: ", src, "sandboxed ", sandboxed, "optstr: ", optstr) if r.saddr == nil && src != nil && sandboxed { -log.Notice("! Skipping comparison against incompatible rule types: rule src = ", r.saddr, " / packet src = ", src) + log.Notice("! Skipping comparison against incompatible rule types: rule src = ", r.saddr, " / packet src = ", src) continue } else if r.saddr == nil && src == nil && sandboxed { continue } else if r.saddr != nil && !r.saddr.Equal(src) && r.proto != "icmp" { -log.Notice("! Skipping comparison of mismatching source ips") + log.Notice("! Skipping comparison of mismatching source ips") continue } log.Notice("r.saddr = ", r.saddr, "src = ", src, "\n") if r.pid >= 0 && r.pid != pinfo.Pid { -//log.Notice("! Skipping comparison of mismatching PIDs") + //log.Notice("! Skipping comparison of mismatching PIDs") continue } - if r.match(src, dst, dstPort, hostname, getNFQProto(pkt), pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) { -log.Notice("+ MATCH SUCCEEDED") + if r.match(src, dst, dstPort, hostname, getNFQProto(pkt), pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID), pinfo.Sandbox) { + log.Notice("+ MATCH SUCCEEDED") dstStr := dst.String() if FirewallConfig.LogRedact { dstStr = STR_REDACTED @@ -196,10 +204,10 @@ log.Notice("+ MATCH SUCCEEDED") srcStr, dstStr, dstPort) if r.rtype == RULE_ACTION_DENY { - log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d", - pinfo.ExePath, r.proto, - srcStr, - dstStr, dstPort) + log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d", + pinfo.ExePath, r.proto, + srcStr, + dstStr, dstPort) return FILTER_DENY } else if r.rtype == RULE_ACTION_ALLOW { result = FILTER_ALLOW @@ -208,9 +216,11 @@ log.Notice("+ MATCH SUCCEEDED") return result } } - } else { log.Notice("+ MATCH FAILED") } + } else { + log.Notice("+ MATCH FAILED") + } } -log.Notice("--- RESULT = ", result) + log.Notice("--- RESULT = ", result) return result } @@ -235,7 +245,7 @@ func (r *Rule) parse(s string) bool { return false } - if !r.parsePrivs(parts[3]) { + if !r.parsePrivs(parts[3]) { log.Notice("invalid privs ", parts[3], " in line ", s) return false } @@ -245,9 +255,7 @@ func (r *Rule) parse(s string) bool { return false } - log.Notice("parsed sandbox ", parts[4]) - -//fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname) + fmt.Printf("uid = %v, gid = %v, user = %v, group = %v, hostname = %v, sandbox = %v\n", r.uid, r.gid, r.uname, r.gname, r.hostname, r.sandbox) if len(parts) == 6 && len(strings.TrimSpace(parts[5])) > 0 { r.saddr = net.ParseIP(parts[5]) @@ -262,15 +270,7 @@ func (r *Rule) parse(s string) bool { } func (r *Rule) parseSandbox(p string) bool { - if p == "" { - r.sandbox = "" - return true - } - toks := strings.Split(p, ":") - if len(toks) != 2 { - return false - } - r.sandbox = toks[1] + r.sandbox = p return true } @@ -312,6 +312,9 @@ func (r *Rule) parseVerb(v string) bool { case RuleActionString[RULE_ACTION_ALLOW]: r.rtype = RULE_ACTION_ALLOW return true + case RuleActionString[RULE_ACTION_ALLOW_TLSONLY]: + r.rtype = RULE_ACTION_ALLOW_TLSONLY + return true case RuleActionString[RULE_ACTION_DENY]: r.rtype = RULE_ACTION_DENY return true @@ -325,7 +328,7 @@ func (r *Rule) parseTarget(t string) bool { return false } sind := 0 - lind := len(addrPort)-1 + lind := len(addrPort) - 1 if addrPort[0] == "udp" || addrPort[0] == "icmp" || addrPort[0] == "tcp" { r.proto = addrPort[0] sind++ @@ -335,7 +338,7 @@ func (r *Rule) parseTarget(t string) bool { newAddr := strings.Join(addrPort[sind:lind], ":") return r.parseAddr(newAddr) && r.parsePort(addrPort[lind]) -// return r.parseAddr(addrPort[sind]) && r.parsePort(addrPort[sind+1]) + // return r.parseAddr(addrPort[sind]) && r.parsePort(addrPort[sind+1]) } func (r *Rule) parseAddr(a string) bool { @@ -344,12 +347,12 @@ func (r *Rule) parseAddr(a string) bool { r.addr = anyAddress return true } -// if strings.IndexFunc(a, unicode.IsLetter) != -1 { + // if strings.IndexFunc(a, unicode.IsLetter) != -1 { if net.ParseIP(a) == nil { r.hostname = a return true } -// ip := net.ParseIP(a) + // ip := net.ParseIP(a) ip, ipnet, err := net.ParseCIDR(a) if err != nil || ip == nil { ip = net.ParseIP(a) @@ -500,7 +503,7 @@ func addrMatchesAny(addr net.IP) bool { any := anyAddress if addr.To4() != nil { - any = net.IP{0,0,0,0} + any = net.IP{0, 0, 0, 0} } return any.Equal(addr) @@ -510,7 +513,7 @@ func addrMatchesNone(addr net.IP) bool { none := noAddress if addr.To4() != nil { - none = net.IP{0xff,0xff,0xff,0xff} + none = net.IP{0xff, 0xff, 0xff, 0xff} } return none.Equal(addr) diff --git a/sgfw/sgfw.go b/sgfw/sgfw.go index 95f5db3..52dd846 100644 --- a/sgfw/sgfw.go +++ b/sgfw/sgfw.go @@ -6,19 +6,19 @@ import ( "regexp" "sync" "syscall" -// "time" + // "time" "bufio" "encoding/json" - "strings" "fmt" + "strings" "github.com/op/go-logging" nfqueue "github.com/subgraph/go-nfnetlink/nfqueue" -// "github.com/subgraph/go-nfnetlink" - "github.com/subgraph/go-procsnitch" - "github.com/subgraph/fw-daemon/proc-coroner" + // "github.com/subgraph/go-nfnetlink" "github.com/google/gopacket" "github.com/google/gopacket/layers" + "github.com/subgraph/fw-daemon/proc-coroner" + "github.com/subgraph/go-procsnitch" ) var dbusp *dbusObjectP = nil @@ -96,9 +96,9 @@ func (fw *Firewall) runFilter() { q := nfqueue.NewNFQueue(0) // XXX: need to implement this -// q.DefaultVerdict = nfqueue.DROP + // q.DefaultVerdict = nfqueue.DROP // XXX: need this as well -// q.Timeout = 5 * time.Minute + // q.Timeout = 5 * time.Minute ps, err := q.Open() @@ -145,6 +145,7 @@ func (fw *Firewall) runFilter() { } type SocksJsonConfig struct { + Name string SocksListener string TorSocks string } @@ -174,7 +175,7 @@ func loadSocksConfiguration(configFilePath string) (*SocksJsonConfig, error) { } func getSocksChainConfig(config *SocksJsonConfig) *socksChainConfig { - // XXX + // TODO: fix this to support multiple named proxy forwarders of different types fields := strings.Split(config.TorSocks, "|") torSocksNet := fields[0] torSocksAddr := fields[1] @@ -182,6 +183,7 @@ func getSocksChainConfig(config *SocksJsonConfig) *socksChainConfig { socksListenNet := fields[0] socksListenAddr := fields[1] socksConfig := socksChainConfig{ + Name: config.Name, TargetSocksNet: torSocksNet, TargetSocksAddr: torSocksAddr, ListenSocksNet: socksListenNet, @@ -192,7 +194,6 @@ func getSocksChainConfig(config *SocksJsonConfig) *socksChainConfig { return &socksConfig } - func Main() { readConfig() logBackend, logBackend2 := setupLoggerBackend(FirewallConfig.LoggingLevel) @@ -232,23 +233,23 @@ func Main() { fw.loadRules() - /* - go func() { - http.ListenAndServe("localhost:6060", nil) - }() - */ - - wg := sync.WaitGroup{} - - config, err := loadSocksConfiguration(defaultSocksCfgPath) - if err != nil && !os.IsNotExist(err) { - panic(err) - } - if config != nil { - socksConfig := getSocksChainConfig(config) - chain := NewSocksChain(socksConfig, &wg, fw) - chain.start() - } else { + /* + go func() { + http.ListenAndServe("localhost:6060", nil) + }() + */ + + wg := sync.WaitGroup{} + + config, err := loadSocksConfiguration(defaultSocksCfgPath) + if err != nil && !os.IsNotExist(err) { + panic(err) + } + if config != nil { + socksConfig := getSocksChainConfig(config) + chain := NewSocksChain(socksConfig, &wg, fw) + chain.start() + } else { log.Notice("Did not find SOCKS5 configuration file at", defaultSocksCfgPath, "; ignoring subsystem...") } diff --git a/sgfw/socks_server_chain.go b/sgfw/socks_server_chain.go index 3aff235..f34bb33 100644 --- a/sgfw/socks_server_chain.go +++ b/sgfw/socks_server_chain.go @@ -17,6 +17,7 @@ type socksChainConfig struct { TargetSocksAddr string ListenSocksNet string ListenSocksAddr string + Name string } type socksChain struct { @@ -41,6 +42,7 @@ type socksChainSession struct { const ( socksVerdictDrop = 1 socksVerdictAccept = 2 + socksVerdictAcceptTLSOnly = 3 ) type pendingSocksConnection struct { @@ -56,6 +58,10 @@ type pendingSocksConnection struct { optstr string } +func (sc *pendingSocksConnection) sandbox() string { + return sc.pinfo.Sandbox +} + func (sc *pendingSocksConnection) policy() *Policy { return sc.pol } @@ -96,6 +102,10 @@ func (sc *pendingSocksConnection) deliverVerdict(v int) { func (sc *pendingSocksConnection) accept() { sc.deliverVerdict(socksVerdictAccept) } +// need to generalize special accept + +func (sc *pendingSocksConnection) acceptTLSOnly() {sc.deliverVerdict(socksVerdictAcceptTLSOnly) } + func (sc *pendingSocksConnection) drop() { sc.deliverVerdict(socksVerdictDrop) } func (sc *pendingSocksConnection) getPrompting() bool { return sc.prompting } @@ -176,11 +186,13 @@ func (c *socksChainSession) sessionWorker() { c.req.ReplyAddr(ReplySucceeded, c.bndAddr) } case CommandConnect: - if !c.filterConnect() { + verdict, tls := c.filterConnect() + + if !verdict { c.req.Reply(ReplyConnectionRefused) return } - c.handleConnect() + c.handleConnect(tls) default: // Should *NEVER* happen, validated as part of handshake. log.Infof("BUG/socks: Unsupported SOCKS command: 0x%02x", c.req.Cmd) @@ -255,7 +267,9 @@ func findProxyEndpoint(pdata []string, conn net.Conn) (*procsnitch.Info, string) return nil, "" } -func (c *socksChainSession) filterConnect() bool { +func (c *socksChainSession) filterConnect() (bool, bool) { + // return filter verdict, tlsguard + allProxies, err := ListProxies() var pinfo *procsnitch.Info = nil var optstr = "" @@ -270,27 +284,30 @@ func (c *socksChainSession) filterConnect() bool { if pinfo == nil { log.Warningf("No proc found for [socks5] connection from: %s", c.clientConn.RemoteAddr()) - return false + return false, false } if optstr == "" { - optstr = "[via SOCKS5/Tor]" + optstr = "Via SOCKS5: " + c.cfg.Name } else { - optstr = "SOCKS5|Tor / " + optstr + optstr = "[Via SOCKS5: " + c.cfg.Name + "] " + optstr } policy := c.server.fw.PolicyForPath(pinfo.ExePath) hostname, ip, port := c.addressDetails() if ip == nil && hostname == "" { - return false + return false, false } result := policy.rules.filter(nil, nil, ip, port, hostname, pinfo, optstr) + log.Errorf("result %v",result) switch result { case FILTER_DENY: - return false + return false, false case FILTER_ALLOW: - return true + return true, false + case FILTER_ALLOW_TLSONLY: + return true, true case FILTER_PROMPT: caddr := c.clientConn.RemoteAddr().String() caddrt := strings.Split(caddr, ":") @@ -333,15 +350,17 @@ func (c *socksChainSession) filterConnect() bool { policy.processPromptResult(pending) v := <-pending.verdict if v == socksVerdictAccept { - return true + return true, false + } else if v == socksVerdictAcceptTLSOnly { + return true, true } } - return false + return false, false } -func (c *socksChainSession) handleConnect() { +func (c *socksChainSession) handleConnect(tls bool) { err := c.dispatchTorSOCKS() if err != nil { return @@ -359,18 +378,20 @@ func (c *socksChainSession) handleConnect() { // A upstream connection has been established, push data back and forth // till the session is done. - c.forwardTraffic() + c.forwardTraffic(tls) log.Infof("INFO/socks: Closed SOCKS connection from: %v", c.clientConn.RemoteAddr()) } -func (c *socksChainSession) forwardTraffic() { - err := TLSGuard(c.clientConn, c.upstreamConn, c.req.Addr.addrStr) +func (c *socksChainSession) forwardTraffic(tls bool) { + if tls == true { + err := TLSGuard(c.clientConn, c.upstreamConn, c.req.Addr.addrStr) - if err != nil { - log.Error("Dropping traffic due to TLSGuard violation: ", err) - return - } else { - log.Notice("TLSGuard approved certificate presented for connection to: ", c.req.Addr.addrStr) + if err != nil { + log.Error("Dropping traffic due to TLSGuard violation: ", err) + return + } else { + log.Notice("TLSGuard approved certificate presented for connection to: ", c.req.Addr.addrStr) + } } var wg sync.WaitGroup