diff --git a/README-DEV.txt b/README-DEV.txt index 68fff21..a68758e 100644 --- a/README-DEV.txt +++ b/README-DEV.txt @@ -8,3 +8,30 @@ root@subgraph:/# cat /etc/fw-daemon-socks.json "TorSocks": "tcp|127.0.0.1:9050" } + +Remember that fw-settings will need to be compiled separately with go install .../fw-daemon/fw-settings +And the gnome-shell interface must be refreshed with ALT+F2, r +*** All changes require on the interoperation between the latest versions of fw-daemon, fw-settings, and the gnome-shell Javascript frontend. + + + +These rules will need to be sent to ensure that all passed through/sandboxed(clearnet) traffic will be picked up by the firewall: +iptables -t mangle -I PREROUTING 1 -m conntrack --ctstate NEW --proto tcp -j NFQUEUE --queue-num 0 --queue-bypass +iptables -I FORWARD 1 -m mark --mark 0x1 -j REJECT --reject-with icmp-host-prohibited + +The following rules are likewise necessary for fw-daemon to catch udp and icmp data: +iptables -t mangle -I PREROUTING 1 --proto udp -j NFQUEUE --queue-num 0 --queue-bypass +iptables -t mangle -I PREROUTING 1 --proto icmp -j NFQUEUE --queue-num 0 --queue-bypass + + + +Here are some examples of the newly formatted rules in /var/lib/sgfw/sgfw_rules: + +#[[unknown]] is used to match an unknown process; this is necessary because even though we can sometimes figure out who's sending an ICMP packet, it's functionally impossible for us to tell who the recipient of an ICMP packet is. +[[unknown]] +ALLOW|icmp:4.2.2.4:0|SYSTEM|| + +#Note the use of wildcards. These rules are of course redundant, but get the same basic job done. +[/usr/sbin/ntpd] +ALLOW|udp:*.ntp.org:123|SYSTEM|| +ALLOW|udp:*:123|SYSTEM|| diff --git a/fw-settings/rules.go b/fw-settings/rules.go index dfca6c2..c0b308e 100644 --- a/fw-settings/rules.go +++ b/fw-settings/rules.go @@ -116,7 +116,11 @@ func (rr *ruleRow) update() { rr.gtkLabelApp.SetText(rr.rule.App) rr.gtkLabelApp.SetTooltipText(rr.rule.Path) rr.gtkLabelVerb.SetText(getVerbText(rr.rule)) - rr.gtkLabelOrigin.SetText(rr.rule.Origin) + if (rr.rule.Proto == "tcp") { + rr.gtkLabelOrigin.SetText(rr.rule.Origin) + } else { + rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")") + } rr.gtkLabelPrivs.SetText(rr.rule.Privs) rr.gtkLabelTarget.SetText(getTargetText(rr.rule)) } @@ -139,13 +143,26 @@ func getTargetText(rule *sgfw.DbusRule) string { } if items[0] == "*" { - return fmt.Sprintf("Connections to All hosts on port %s", items[1]) + if rule.Proto == "tcp" { + return fmt.Sprintf("Connections to ALL hosts on port %s", items[1]) + } else if rule.Proto == "icmp" { + return fmt.Sprintf("Data to ALL hosts with ICMP code %s", items[1]) + } + return fmt.Sprintf("Data to ALL hosts on port %s", items[1]) } if items[1] == "*" { - return fmt.Sprintf("All connections to host %s", items[0]) + if rule.Proto == "tcp" { + return fmt.Sprintf("All connections to host %s", items[0]) + } + return fmt.Sprintf("All data to host %s", items[0]) } - return fmt.Sprintf("Connections to %s on port %s", items[0], items[1]) + if rule.Proto == "tcp" { + return fmt.Sprintf("Connections to %s on port %s", items[0], items[1]) + } else if rule.Proto == "icmp" { + return fmt.Sprintf("Data to %s with ICMP code %s", items[0], items[1]) + } + return fmt.Sprintf("Data to %s on port %s", items[0], items[1]) } func (rr *ruleRow) onSaveAsNew() { diff --git a/gnome-shell/firewall@subgraph.com/dialog.js b/gnome-shell/firewall@subgraph.com/dialog.js index 196392c..26612e8 100644 --- a/gnome-shell/firewall@subgraph.com/dialog.js +++ b/gnome-shell/firewall@subgraph.com/dialog.js @@ -42,7 +42,7 @@ const DetailSection = new Lang.Class({ return msg; }, - setDetails: function(ip, path, pid, uid, gid, user, group, origin, optstring) { + setDetails: function(ip, path, pid, uid, gid, user, group, origin, proto, optstring) { this.ipAddr.text = ip; this.path.text = path; @@ -55,13 +55,19 @@ const DetailSection = new Lang.Class({ this.origin.text = origin; if (user != "") { - this.user.text = user + " (" + uid.toString() + ")"; + this.user.text = user; + if (uid != -1) { + this.user.text += " (" + uid.toString() + ")"; + } } else { this.user.text = "uid:" + uid.toString(); } if (group != "") { - this.group.text = group + " (" + gid.toString() + ")"; + this.group.text = group; + if (gid != -1) { + this.group.text += " (" + gid.toString() + ")"; + } } else { this.group.text = "gid:" + gid.toString(); } @@ -460,26 +466,42 @@ const PromptDialog = new Lang.Class({ }, ruleTarget: function() { + let base = ""; + if(this._proto != "tcp") { + base = this._proto + ":"; + } switch(this.optionList.selectedIdx()) { case 0: - return this._address + ":" + this._port; + return base + this._address + ":" + this._port; case 1: - return this._address + ":*"; + return base + this._address + ":*"; case 2: - return "*:" + this._port; + return base + "*:" + this._port; case 3: - return "*:*"; + return base + "*:*"; } }, update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action) { this._address = address; this._port = port; + this._proto = proto; let port_str = (proto+"").toUpperCase() + " Port "+ port; + if (proto == "icmp") { + port_str = (proto+"").toUpperCase() + " Code "+ port; + } + this.header.setTitle(application); - this.header.setMessage("Wants to connect to "+ address + " on " + port_str); + + if (proto == "tcp") { + this.header.setMessage("Wants to connect to "+ address + " on " + port_str); + } else if (proto == "udp") { + this.header.setMessage("Wants to send data to "+ address + " on " + port_str); + } else if (proto == "icmp") { + this.header.setMessage("Wants to send data to "+ address + " with " + port_str); + } if (expanded) { this.details.isOpen = false; @@ -491,16 +513,32 @@ const PromptDialog = new Lang.Class({ this.header.setIconDefault(); } - this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str); + if (proto == "icmp") { + this.optionList.setOptionText(0, "Only "+ address + " with "+ port_str); + } else { + this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str); + } if (expert) { - this.optionList.setOptionText(1, "Only "+ address + " on any port"); + + if (proto == "icmp") { + this.optionList.setOptionText(1, "Only "+ address + " with any ICMP code"); + } else if (proto == "udp") { + this.optionList.setOptionText(1, "Only "+ address + " on any UDP port"); + } else { + this.optionList.setOptionText(1, "Only "+ address + " on any port"); + } + this.optionList.setOptionText(2, "Only "+ port_str); } else { this.optionList.setOptionText(1, false); this.optionList.setOptionText(2, false); } + if (proto != "tcp") { + this.optionList.setOptionText(3, "Any " + proto.toUpperCase() + " data"); + } + this.optionList.buttonGroup._setChecked(this.optionList.scopeToIdx(action)) - this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, optstring); + this.info.setDetails(ip, path, pid, uid, gid, user, group, origin, proto, optstring); }, }); diff --git a/gnome-shell/firewall@subgraph.com/extension.js b/gnome-shell/firewall@subgraph.com/extension.js index 99c60ca..73c743f 100644 --- a/gnome-shell/firewall@subgraph.com/extension.js +++ b/gnome-shell/firewall@subgraph.com/extension.js @@ -51,6 +51,7 @@ const FirewallPromptInterface = ' \ \ \ \ + \ \ \ \ @@ -91,11 +92,11 @@ const FirewallPromptHandler = new Lang.Class({ }, RequestPromptAsync: function(params, invocation) { - let [app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, optstring, expanded, expert, action] = params; + let [app, icon, path, address, port, ip, origin, proto, uid, gid, user, group, pid, optstring, expanded, expert, action] = params; this._closeDialog(); this._dialog = new Dialog.PromptDialog(invocation); this._invocation = invocation; - this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, "TCP", optstring, expanded, expert, action); + this._dialog.update(app, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action); this._dialog.open(); }, diff --git a/sgfw/const.go b/sgfw/const.go index f82c280..2e25532 100644 --- a/sgfw/const.go +++ b/sgfw/const.go @@ -107,6 +107,7 @@ type DbusRule struct { ID uint32 Net string Origin string + Proto string Privs string App string Path string diff --git a/sgfw/dbus.go b/sgfw/dbus.go index e4f7b17..87e316a 100644 --- a/sgfw/dbus.go +++ b/sgfw/dbus.go @@ -131,6 +131,7 @@ func createDbusRule(r *Rule) DbusRule { ID: uint32(r.id), Net: netstr, Origin: ostr, + Proto: r.proto, Privs: pstr, App: path.Base(r.policy.path), Path: r.policy.path, diff --git a/sgfw/policy.go b/sgfw/policy.go index 347486c..b54a310 100644 --- a/sgfw/policy.go +++ b/sgfw/policy.go @@ -41,6 +41,7 @@ type pendingConnection interface { procInfo() *procsnitch.Info hostname() string getOptString() string + proto() string src() net.IP srcPort() uint16 dst() net.IP @@ -107,22 +108,40 @@ func (pp *pendingPkt) dst() net.IP { // pp.pkt.NetworkLayer().Layer } +func getNFQProto(pkt *nfqueue.NFQPacket) string { + if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { + return "tcp" + } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { + return "udp" + } else if pkt.Packet.Layer(layers.LayerTypeICMPv4) != nil { + return "icmp" + } + + return "[unknown]" +} + +func (pp *pendingPkt) proto() string { + return getNFQProto(pp.pkt) +} + func (pp *pendingPkt) srcPort() uint16 { srcp, _ := getPacketTCPPorts(pp.pkt) return srcp } func (pp *pendingPkt) dstPort() uint16 { -/* dst := pp.pkt.Packet.TransportLayer().TransportFlow().Dst() - - if dst.EndpointType() != layers.EndpointTCPPort { - return 0 + if pp.proto() == "tcp" { + _, dstp := getPacketTCPPorts(pp.pkt) + return dstp + } else if pp.proto() == "udp" { + _, dstp := getPacketUDPPorts(pp.pkt) + return dstp + } else if pp.proto() == "icmp" { + code, _ := getpacketICMPCode(pp.pkt) + return uint16(code) } - return binary.BigEndian.Uint16(dst.Raw()) */ - _, dstp := getPacketTCPPorts(pp.pkt) - return dstp -// return pp.pkt.DstPort + return 0 } func (pp *pendingPkt) accept() { @@ -294,7 +313,7 @@ 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.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)) { 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 { @@ -302,7 +321,7 @@ func (p *Policy) filterPending(rule *Rule) { } 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, "TCP", srcs, pc.dst(), pc.dstPort) + pc.procInfo().ExePath, pc.proto(), srcs, pc.dst(), pc.dstPort) pc.drop() } } else { @@ -327,20 +346,23 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info proto := "???" SrcPort, DstPort := uint16(0), uint16(0) SrcIp, DstIp := getPacketIP4Addrs(pkt) + code := 0 + codestr := "" -// switch pkt.Packet.TransportLayer().TransportFlow().EndpointType() { if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { -// case 4: proto = "TCP" } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { -// case 5: proto = "UDP" + } else if pkt.Packet.Layer(layers.LayerTypeICMPv4) != nil { + proto = "ICMP" } if proto == "TCP" { SrcPort, DstPort = getPacketTCPPorts(pkt) } else if proto == "UDP" { SrcPort, DstPort = getPacketUDPPorts(pkt) + } else if proto == "ICMP" { + code, codestr = getpacketICMPCode(pkt) } if FirewallConfig.LogRedact { @@ -351,9 +373,13 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info name = DstIp.String() } if pinfo == nil { + if proto == "ICMP" { + return fmt.Sprintf("(%s %s -> %s: %s [%d])", proto, SrcIp, name, codestr, code) + } 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) } @@ -505,78 +531,111 @@ func getRealRoot(pathname string, pid int) string { func findProcessForPacket(pkt *nfqueue.NFQPacket) (*procsnitch.Info, string) { srcip, dstip := getPacketIP4Addrs(pkt) srcp, dstp := getPacketPorts(pkt) + proto := "" optstr := "" + icode := -1 if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { - // Try normal way first, before the more resource intensive/invasive way. - res := procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, nil) + proto = "tcp" + } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { + proto = "udp" + } else if pkt.Packet.Layer(layers.LayerTypeICMPv4) != nil { + proto = "icmp" + icode, _ = getpacketICMPCode(pkt) + } - if res == nil { - removePids := make([]int, 0) + if proto == "" { + log.Warningf("Packet has unknown protocol: %d", pkt.Packet.NetworkLayer().LayerType()) + return nil, optstr + } - for i := 0; i < len(OzInitPids); i++ { - data := "" - fname := fmt.Sprintf("/proc/%d/net/tcp", OzInitPids[i].Pid) -fmt.Println("XXX: opening: ", fname) - bdata, err := readFileDirect(fname) + var res *procsnitch.Info = nil - if err != nil { - fmt.Println("Error reading proc data from ", fname, ": ", err) + // Try normal way first, before the more resource intensive/invasive way. + if proto == "tcp" { + res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, nil) + } else if proto == "udp" { + res = procsnitch.LookupUDPSocketProcessAll(srcip, srcp, dstip, dstp, nil, true) + } else if proto == "icmp" { + res = procsnitch.LookupICMPSocketProcessAll(srcip, dstip, icode, nil) + } - if err == syscall.ENOENT { - removePids = append(removePids, OzInitPids[i].Pid) - } + if res == nil { + removePids := make([]int, 0) - continue - } else { - data = string(bdata) - lines := strings.Split(data, "\n") - rlines := make([]string, 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) - for l := 0; l < len(lines); l++ { - lines[l] = strings.TrimSpace(lines[l]) - ssplit := strings.Split(lines[l], ":") + if err != nil { + fmt.Println("Error reading proc data from ", fname, ": ", err) - if len(ssplit) != 6 { - continue - } + if err == syscall.ENOENT { + removePids = append(removePids, OzInitPids[i].Pid) + } - rlines = append(rlines, strings.Join(ssplit, ":")) - } + continue + } else { + data = string(bdata) + lines := strings.Split(data, "\n") + rlines := make([]string, 0) - res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, rlines) + for l := 0; l < len(lines); l++ { + lines[l] = strings.TrimSpace(lines[l]) + ssplit := strings.Split(lines[l], ":") - if res != nil { - optstr = "Sandbox: " + OzInitPids[i].Name - res.ExePath = getRealRoot(res.ExePath, OzInitPids[i].Pid) - break + 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, true) + } else if proto == "icmp" { + res = procsnitch.LookupICMPSocketProcessAll(srcip, dstip, icode, rlines) + } - for _, p := range removePids { - removeInitPid(p) + if res != nil { + optstr = "Sandbox: " + OzInitPids[i].Name + res.ExePath = getRealRoot(res.ExePath, OzInitPids[i].Pid) + break + } } } - return res, optstr - } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { - return procsnitch.LookupUDPSocketProcess(srcp), optstr + for _, p := range removePids { + removeInitPid(p) + } + } - log.Warningf("Packet has unknown protocol: %d", pkt.Packet.NetworkLayer().LayerType()) - //log.Warningf("Packet has unknown protocol: %d", pkt.Protocol) - return nil, optstr + return res, optstr } func basicAllowPacket(pkt *nfqueue.NFQPacket) bool { - _, dstip := getPacketIP4Addrs(pkt) + srcip, dstip := getPacketIP4Addrs(pkt) + if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { + _, dport := getPacketUDPPorts(pkt) + if dport == 53 { + return true + } + } + if pkt.Packet.Layer(layers.LayerTypeICMPv4) != nil && srcip.Equal(dstip) { + // An ICMP dest unreach packet sent to ourselves probably isn't a big security risk. + return true + } return dstip.IsLoopback() || dstip.IsLinkLocalMulticast() || - pkt.Packet.Layer(layers.LayerTypeTCP) == nil -// pkt.Protocol != nfqueue.TCP + (pkt.Packet.Layer(layers.LayerTypeTCP) == nil && + pkt.Packet.Layer(layers.LayerTypeUDP) == nil && + pkt.Packet.Layer(layers.LayerTypeICMPv4) == nil) } func getPacketIP4Addrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { @@ -590,6 +649,17 @@ func getPacketIP4Addrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { return ip.SrcIP, ip.DstIP } +func getpacketICMPCode(pkt *nfqueue.NFQPacket) (int, string) { + icmpLayer := pkt.Packet.Layer(layers.LayerTypeICMPv4) + + if icmpLayer == nil { + return -1, "" + } + + icmp, _ := icmpLayer.(*layers.ICMPv4) + return int(icmp.TypeCode.Code()), icmp.TypeCode.String() +} + func getPacketTCPPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) { tcpLayer := pkt.Packet.Layer(layers.LayerTypeTCP) diff --git a/sgfw/prompt.go b/sgfw/prompt.go index d53129e..3acec27 100644 --- a/sgfw/prompt.go +++ b/sgfw/prompt.go @@ -77,6 +77,7 @@ func (p *prompter) processConnection(pc pendingConnection) { int32(pc.dstPort()), pc.dst().String(), pc.src().String(), + pc.proto(), int32(pc.procInfo().UID), int32(pc.procInfo().GID), uidToUser(pc.procInfo().UID), diff --git a/sgfw/rules.go b/sgfw/rules.go index 46db5f3..c636c55 100644 --- a/sgfw/rules.go +++ b/sgfw/rules.go @@ -25,6 +25,7 @@ type Rule struct { policy *Policy mode RuleMode rtype RuleAction + proto string hostname string network *net.IPNet addr uint32 @@ -50,7 +51,12 @@ func (r *Rule) getString(redact bool) string { rmode = "|" + RuleModeString[RULE_MODE_SYSTEM] } - return fmt.Sprintf("%s|%s%s", rtype, r.AddrString(redact), rmode) + protostr := "" + + if r.proto != "tcp" { + protostr = r.proto + ":" + } + return fmt.Sprintf("%s|%s%s%s", rtype, protostr, r.AddrString(redact), rmode) } func (r *Rule) AddrString(redact bool) string { @@ -66,7 +72,7 @@ func (r *Rule) AddrString(redact bool) string { addr = fmt.Sprintf("%d.%d.%d.%d", bs[0], bs[1], bs[2], bs[3]) } - if r.port != matchAny { + if r.port != matchAny || r.proto == "icmp" { port = fmt.Sprintf("%d", r.port) } @@ -79,7 +85,10 @@ func (r *Rule) AddrString(redact bool) string { type RuleList []*Rule -func (r *Rule) match(src net.IP, dst net.IP, dstPort uint16, hostname 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) bool { + if r.proto != proto { + return false + } if r.uid != -1 && r.uid != uid { return false } else if r.gid != -1 && r.gid != gid { @@ -115,6 +124,12 @@ log.Notice("comparison: ", hostname, " / ", dst, " : ", dstPort, " -> ", xip, " if r.network != nil && r.network.Contains(dst) { return true } + 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 == binary.BigEndian.Uint32(src.To4())) { + return true + } + } return r.addr == binary.BigEndian.Uint32(dst.To4()) } @@ -135,11 +150,11 @@ log.Notice("------------ trying match of src ", src, " against: ", r, " | ", r.s if r.saddr == nil && src != nil && sandboxed { log.Notice("! Skipping comparison against incompatible rule types: rule src = ", r.saddr, " / packet src = ", src) continue - } else if r.saddr != nil && !r.saddr.Equal(src) { + } else if r.saddr != nil && !r.saddr.Equal(src) && r.proto != "icmp" { log.Notice("! Skipping comparison of mismatching source ips") continue } - if r.match(src, dst, dstPort, hostname, pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) { + if r.match(src, dst, dstPort, hostname, getNFQProto(pkt), pinfo.UID, pinfo.GID, uidToUser(pinfo.UID), gidToGroup(pinfo.GID)) { log.Notice("+ MATCH SUCCEEDED") dstStr := dst.String() if FirewallConfig.LogRedact { @@ -153,12 +168,12 @@ log.Notice("+ MATCH SUCCEEDED") } log.Noticef("%s > %s %s %s -> %s:%d", r.getString(FirewallConfig.LogRedact), - pinfo.ExePath, "TCP", + pinfo.ExePath, r.proto, srcStr, dstStr, dstPort) if r.rtype == RULE_ACTION_DENY { log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d", - pinfo.ExePath, "TCP", + pinfo.ExePath, r.proto, srcStr, dstStr, dstPort) return FILTER_DENY @@ -257,11 +272,21 @@ func (r *Rule) parseVerb(v string) bool { func (r *Rule) parseTarget(t string) bool { addrPort := strings.Split(t, ":") - if len(addrPort) != 2 { + if len(addrPort) != 2 && len(addrPort) != 3 { return false } + sind := 0 + if len(addrPort) == 3 { + if addrPort[0] != "udp" && addrPort[0] != "icmp" && addrPort[0] != "tcp" { + return false + } + r.proto = addrPort[0] + sind++ + } else { + r.proto = "tcp" + } - return r.parseAddr(addrPort[0]) && r.parsePort(addrPort[1]) + return r.parseAddr(addrPort[sind]) && r.parsePort(addrPort[sind+1]) } func (r *Rule) parseAddr(a string) bool { @@ -296,7 +321,7 @@ func (r *Rule) parsePort(p string) bool { } var err error port, err := strconv.ParseUint(p, 10, 16) - if err != nil || port == 0 || port > 0xFFFF { + if err != nil || (port == 0 && r.proto != "icmp") || port > 0xFFFF { return false } r.port = uint16(port) diff --git a/sgfw/socks_server_chain.go b/sgfw/socks_server_chain.go index f10257c..4ddc01e 100644 --- a/sgfw/socks_server_chain.go +++ b/sgfw/socks_server_chain.go @@ -72,6 +72,9 @@ func (sc *pendingSocksConnection) hostname() string { func (sc *pendingSocksConnection) dst() net.IP { return sc.destIP } +func (sc *pendingSocksConnection) proto() string { + return "tcp" +} func (sc *pendingSocksConnection) srcPort() uint16 { return sc.sourcePort } diff --git a/vendor/github.com/subgraph/go-procsnitch/proc.go b/vendor/github.com/subgraph/go-procsnitch/proc.go index 4979c0b..e30bab7 100644 --- a/vendor/github.com/subgraph/go-procsnitch/proc.go +++ b/vendor/github.com/subgraph/go-procsnitch/proc.go @@ -66,6 +66,24 @@ func FindProcessForConnection(conn net.Conn, procInfo ProcInfo) *Info { return info } +// LookupICMPSocketProcessAll searches for a ICMP socket a given source host, destination IP, and type +func LookupICMPSocketProcessAll(srcAddr net.IP, dstAddr net.IP, code int, custdata []string) *Info { + ss := findICMPSocketAll(srcAddr, dstAddr, code, custdata) + if ss == nil { + return nil + } + return pcache.lookup(ss.inode) +} + +// LookupUDPSocketProcessAll searches for a UDP socket a given source port, destination IP, and destination port - AND source destination +func LookupUDPSocketProcessAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string, loose bool) *Info { + ss := findUDPSocketAll(srcAddr, srcPort, dstAddr, dstPort, custdata, loose) + if ss == nil { + return nil + } + return pcache.lookup(ss.inode) +} + // LookupUDPSocketProcess searches for a UDP socket with a source port func LookupUDPSocketProcess(srcPort uint16) *Info { ss := findUDPSocket(srcPort) diff --git a/vendor/github.com/subgraph/go-procsnitch/socket.go b/vendor/github.com/subgraph/go-procsnitch/socket.go index 3dbe56d..851c140 100644 --- a/vendor/github.com/subgraph/go-procsnitch/socket.go +++ b/vendor/github.com/subgraph/go-procsnitch/socket.go @@ -78,6 +78,45 @@ func (ss *socketStatus) String() string { return fmt.Sprintf("%s -> %s uid=%d inode=%d", ss.local, ss.remote, ss.uid, ss.inode) } +func findICMPSocketAll(srcAddr net.IP, dstAddr net.IP, code int, custdata []string) *socketStatus { + if custdata == nil { + return findSocket("icmp", func(ss socketStatus) bool { + return ss.remote.ip.Equal(dstAddr) && ss.local.ip.Equal(srcAddr) + }) + } + + return findSocketCustom("icmp", custdata, func(ss socketStatus) bool { + return ss.remote.ip.Equal(dstAddr) && ss.local.ip.Equal(srcAddr) + }) +} + +func findUDPSocketAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string, loose bool) *socketStatus { + wildcard := net.IP{0,0,0,0} + + if custdata == nil { + if !loose { + return findSocket("udp", func(ss socketStatus) bool { +// return ss.remote.port == dstPort && ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + return ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) + } + + return findSocket("udp", func(ss socketStatus) bool { + return (ss.remote.ip.Equal(dstAddr) || ss.remote.ip.Equal(wildcard)) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) + } + + if !loose { + return findSocketCustom("udp", custdata, func(ss socketStatus) bool { + return ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) + } + + return findSocketCustom("udp", custdata, func(ss socketStatus) bool { + return (ss.remote.ip.Equal(dstAddr) || ss.remote.ip.Equal(wildcard)) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr) + }) +} + func findUDPSocket(srcPort uint16) *socketStatus { return findSocket("udp", func(ss socketStatus) bool { return ss.local.port == srcPort @@ -91,12 +130,10 @@ 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 + if custdata == nil { + return 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) + }) } return findSocketCustom("tcp", custdata, func(ss socketStatus) bool {