Full support for multiple protocol types (UDP, ICMP).

Cleared up awkward fw-settings/fwprompt GUI language caused by introduction of UDP/ICMP ("connection"-less) rules.
fw-daemon automatically passes through all ICMP traffic sent to same address.
Added (temporary) rule for passing through all UDP-based DNS server traffic.
Updated developers' README documentation.
shw_dev
shw 8 years ago
parent ea31a28d3a
commit 51c181a881

@ -8,3 +8,30 @@ root@subgraph:/# cat /etc/fw-daemon-socks.json
"TorSocks": "tcp|127.0.0.1:9050" "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||

@ -116,7 +116,11 @@ func (rr *ruleRow) update() {
rr.gtkLabelApp.SetText(rr.rule.App) rr.gtkLabelApp.SetText(rr.rule.App)
rr.gtkLabelApp.SetTooltipText(rr.rule.Path) rr.gtkLabelApp.SetTooltipText(rr.rule.Path)
rr.gtkLabelVerb.SetText(getVerbText(rr.rule)) rr.gtkLabelVerb.SetText(getVerbText(rr.rule))
if (rr.rule.Proto == "tcp") {
rr.gtkLabelOrigin.SetText(rr.rule.Origin) rr.gtkLabelOrigin.SetText(rr.rule.Origin)
} else {
rr.gtkLabelOrigin.SetText(rr.rule.Origin + " (" + rr.rule.Proto + ")")
}
rr.gtkLabelPrivs.SetText(rr.rule.Privs) rr.gtkLabelPrivs.SetText(rr.rule.Privs)
rr.gtkLabelTarget.SetText(getTargetText(rr.rule)) rr.gtkLabelTarget.SetText(getTargetText(rr.rule))
} }
@ -139,13 +143,26 @@ func getTargetText(rule *sgfw.DbusRule) string {
} }
if items[0] == "*" { 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] == "*" { if items[1] == "*" {
if rule.Proto == "tcp" {
return fmt.Sprintf("All connections to host %s", items[0]) return fmt.Sprintf("All connections to host %s", items[0])
} }
return fmt.Sprintf("All data to host %s", items[0])
}
if rule.Proto == "tcp" {
return fmt.Sprintf("Connections to %s on port %s", items[0], items[1]) 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() { func (rr *ruleRow) onSaveAsNew() {

@ -42,7 +42,7 @@ const DetailSection = new Lang.Class({
return msg; 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.ipAddr.text = ip;
this.path.text = path; this.path.text = path;
@ -55,13 +55,19 @@ const DetailSection = new Lang.Class({
this.origin.text = origin; this.origin.text = origin;
if (user != "") { if (user != "") {
this.user.text = user + " (" + uid.toString() + ")"; this.user.text = user;
if (uid != -1) {
this.user.text += " (" + uid.toString() + ")";
}
} else { } else {
this.user.text = "uid:" + uid.toString(); this.user.text = "uid:" + uid.toString();
} }
if (group != "") { if (group != "") {
this.group.text = group + " (" + gid.toString() + ")"; this.group.text = group;
if (gid != -1) {
this.group.text += " (" + gid.toString() + ")";
}
} else { } else {
this.group.text = "gid:" + gid.toString(); this.group.text = "gid:" + gid.toString();
} }
@ -460,26 +466,42 @@ const PromptDialog = new Lang.Class({
}, },
ruleTarget: function() { ruleTarget: function() {
let base = "";
if(this._proto != "tcp") {
base = this._proto + ":";
}
switch(this.optionList.selectedIdx()) { switch(this.optionList.selectedIdx()) {
case 0: case 0:
return this._address + ":" + this._port; return base + this._address + ":" + this._port;
case 1: case 1:
return this._address + ":*"; return base + this._address + ":*";
case 2: case 2:
return "*:" + this._port; return base + "*:" + this._port;
case 3: case 3:
return "*:*"; return base + "*:*";
} }
}, },
update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action) { update: function(application, icon, path, address, port, ip, origin, uid, gid, user, group, pid, proto, optstring, expanded, expert, action) {
this._address = address; this._address = address;
this._port = port; this._port = port;
this._proto = proto;
let port_str = (proto+"").toUpperCase() + " Port "+ port; let port_str = (proto+"").toUpperCase() + " Port "+ port;
if (proto == "icmp") {
port_str = (proto+"").toUpperCase() + " Code "+ port;
}
this.header.setTitle(application); this.header.setTitle(application);
if (proto == "tcp") {
this.header.setMessage("Wants to connect to "+ address + " on " + port_str); 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) { if (expanded) {
this.details.isOpen = false; this.details.isOpen = false;
@ -491,16 +513,32 @@ const PromptDialog = new Lang.Class({
this.header.setIconDefault(); this.header.setIconDefault();
} }
if (proto == "icmp") {
this.optionList.setOptionText(0, "Only "+ address + " with "+ port_str);
} else {
this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str); this.optionList.setOptionText(0, "Only "+ address + " on "+ port_str);
}
if (expert) { if (expert) {
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(1, "Only "+ address + " on any port");
}
this.optionList.setOptionText(2, "Only "+ port_str); this.optionList.setOptionText(2, "Only "+ port_str);
} else { } else {
this.optionList.setOptionText(1, false); this.optionList.setOptionText(1, false);
this.optionList.setOptionText(2, 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.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);
}, },
}); });

@ -51,6 +51,7 @@ const FirewallPromptInterface = '<node> \
<arg type="i" direction="in" name="port" /> \ <arg type="i" direction="in" name="port" /> \
<arg type="s" direction="in" name="ip" /> \ <arg type="s" direction="in" name="ip" /> \
<arg type="s" direction="in" name="origin" /> \ <arg type="s" direction="in" name="origin" /> \
<arg type="s" direction="in" name="proto" /> \
<arg type="i" direction="in" name="uid" /> \ <arg type="i" direction="in" name="uid" /> \
<arg type="i" direction="in" name="gid" /> \ <arg type="i" direction="in" name="gid" /> \
<arg type="s" direction="in" name="user" /> \ <arg type="s" direction="in" name="user" /> \
@ -91,11 +92,11 @@ const FirewallPromptHandler = new Lang.Class({
}, },
RequestPromptAsync: function(params, invocation) { 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._closeDialog();
this._dialog = new Dialog.PromptDialog(invocation); this._dialog = new Dialog.PromptDialog(invocation);
this._invocation = 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(); this._dialog.open();
}, },

@ -107,6 +107,7 @@ type DbusRule struct {
ID uint32 ID uint32
Net string Net string
Origin string Origin string
Proto string
Privs string Privs string
App string App string
Path string Path string

@ -131,6 +131,7 @@ func createDbusRule(r *Rule) DbusRule {
ID: uint32(r.id), ID: uint32(r.id),
Net: netstr, Net: netstr,
Origin: ostr, Origin: ostr,
Proto: r.proto,
Privs: pstr, Privs: pstr,
App: path.Base(r.policy.path), App: path.Base(r.policy.path),
Path: r.policy.path, Path: r.policy.path,

@ -41,6 +41,7 @@ type pendingConnection interface {
procInfo() *procsnitch.Info procInfo() *procsnitch.Info
hostname() string hostname() string
getOptString() string getOptString() string
proto() string
src() net.IP src() net.IP
srcPort() uint16 srcPort() uint16
dst() net.IP dst() net.IP
@ -107,22 +108,40 @@ func (pp *pendingPkt) dst() net.IP {
// pp.pkt.NetworkLayer().Layer // 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 { func (pp *pendingPkt) srcPort() uint16 {
srcp, _ := getPacketTCPPorts(pp.pkt) srcp, _ := getPacketTCPPorts(pp.pkt)
return srcp return srcp
} }
func (pp *pendingPkt) dstPort() uint16 { func (pp *pendingPkt) dstPort() uint16 {
/* dst := pp.pkt.Packet.TransportLayer().TransportFlow().Dst() if pp.proto() == "tcp" {
if dst.EndpointType() != layers.EndpointTCPPort {
return 0
}
return binary.BigEndian.Uint16(dst.Raw()) */
_, dstp := getPacketTCPPorts(pp.pkt) _, dstp := getPacketTCPPorts(pp.pkt)
return dstp return dstp
// return pp.pkt.DstPort } else if pp.proto() == "udp" {
_, dstp := getPacketUDPPorts(pp.pkt)
return dstp
} else if pp.proto() == "icmp" {
code, _ := getpacketICMPCode(pp.pkt)
return uint16(code)
}
return 0
} }
func (pp *pendingPkt) accept() { func (pp *pendingPkt) accept() {
@ -294,7 +313,7 @@ func (p *Policy) removeRule(r *Rule) {
func (p *Policy) filterPending(rule *Rule) { func (p *Policy) filterPending(rule *Rule) {
remaining := []pendingConnection{} remaining := []pendingConnection{}
for _, pc := range p.pendingQueue { 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.Infof("Adding rule for: %s", rule.getString(FirewallConfig.LogRedact))
log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print()) log.Noticef("%s > %s", rule.getString(FirewallConfig.LogRedact), pc.print())
if rule.rtype == RULE_ACTION_ALLOW { if rule.rtype == RULE_ACTION_ALLOW {
@ -302,7 +321,7 @@ func (p *Policy) filterPending(rule *Rule) {
} else { } else {
srcs := pc.src().String() + ":" + strconv.Itoa(int(pc.srcPort())) srcs := pc.src().String() + ":" + strconv.Itoa(int(pc.srcPort()))
log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d (user prompt)", 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() pc.drop()
} }
} else { } else {
@ -327,20 +346,23 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info
proto := "???" proto := "???"
SrcPort, DstPort := uint16(0), uint16(0) SrcPort, DstPort := uint16(0), uint16(0)
SrcIp, DstIp := getPacketIP4Addrs(pkt) SrcIp, DstIp := getPacketIP4Addrs(pkt)
code := 0
codestr := ""
// switch pkt.Packet.TransportLayer().TransportFlow().EndpointType() {
if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { if pkt.Packet.Layer(layers.LayerTypeTCP) != nil {
// case 4:
proto = "TCP" proto = "TCP"
} else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil { } else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil {
// case 5:
proto = "UDP" proto = "UDP"
} else if pkt.Packet.Layer(layers.LayerTypeICMPv4) != nil {
proto = "ICMP"
} }
if proto == "TCP" { if proto == "TCP" {
SrcPort, DstPort = getPacketTCPPorts(pkt) SrcPort, DstPort = getPacketTCPPorts(pkt)
} else if proto == "UDP" { } else if proto == "UDP" {
SrcPort, DstPort = getPacketUDPPorts(pkt) SrcPort, DstPort = getPacketUDPPorts(pkt)
} else if proto == "ICMP" {
code, codestr = getpacketICMPCode(pkt)
} }
if FirewallConfig.LogRedact { if FirewallConfig.LogRedact {
@ -351,9 +373,13 @@ func printPacket(pkt *nfqueue.NFQPacket, hostname string, pinfo *procsnitch.Info
name = DstIp.String() name = DstIp.String()
} }
if pinfo == nil { 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:%d -> %s:%d)", proto, SrcIp, SrcPort, name, DstPort)
} }
return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, SrcIp, SrcPort, name, DstPort) return fmt.Sprintf("%s %s %s:%d -> %s:%d", pinfo.ExePath, proto, SrcIp, SrcPort, name, DstPort)
} }
@ -505,18 +531,41 @@ func getRealRoot(pathname string, pid int) string {
func findProcessForPacket(pkt *nfqueue.NFQPacket) (*procsnitch.Info, string) { func findProcessForPacket(pkt *nfqueue.NFQPacket) (*procsnitch.Info, string) {
srcip, dstip := getPacketIP4Addrs(pkt) srcip, dstip := getPacketIP4Addrs(pkt)
srcp, dstp := getPacketPorts(pkt) srcp, dstp := getPacketPorts(pkt)
proto := ""
optstr := "" optstr := ""
icode := -1
if pkt.Packet.Layer(layers.LayerTypeTCP) != nil { if pkt.Packet.Layer(layers.LayerTypeTCP) != 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 proto == "" {
log.Warningf("Packet has unknown protocol: %d", pkt.Packet.NetworkLayer().LayerType())
return nil, optstr
}
var res *procsnitch.Info = nil
// Try normal way first, before the more resource intensive/invasive way. // Try normal way first, before the more resource intensive/invasive way.
res := procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, nil) 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 res == nil { if res == nil {
removePids := make([]int, 0) removePids := make([]int, 0)
for i := 0; i < len(OzInitPids); i++ { for i := 0; i < len(OzInitPids); i++ {
data := "" data := ""
fname := fmt.Sprintf("/proc/%d/net/tcp", OzInitPids[i].Pid) 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) bdata, err := readFileDirect(fname)
@ -544,7 +593,13 @@ fmt.Println("XXX: opening: ", fname)
rlines = append(rlines, strings.Join(ssplit, ":")) rlines = append(rlines, strings.Join(ssplit, ":"))
} }
if proto == "tcp" {
res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, rlines) 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)
}
if res != nil { if res != nil {
optstr = "Sandbox: " + OzInitPids[i].Name optstr = "Sandbox: " + OzInitPids[i].Name
@ -562,21 +617,25 @@ fmt.Println("XXX: opening: ", fname)
} }
return res, optstr return res, optstr
} else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil {
return procsnitch.LookupUDPSocketProcess(srcp), optstr
}
log.Warningf("Packet has unknown protocol: %d", pkt.Packet.NetworkLayer().LayerType())
//log.Warningf("Packet has unknown protocol: %d", pkt.Protocol)
return nil, optstr
} }
func basicAllowPacket(pkt *nfqueue.NFQPacket) bool { 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() || return dstip.IsLoopback() ||
dstip.IsLinkLocalMulticast() || dstip.IsLinkLocalMulticast() ||
pkt.Packet.Layer(layers.LayerTypeTCP) == nil (pkt.Packet.Layer(layers.LayerTypeTCP) == nil &&
// pkt.Protocol != nfqueue.TCP pkt.Packet.Layer(layers.LayerTypeUDP) == nil &&
pkt.Packet.Layer(layers.LayerTypeICMPv4) == nil)
} }
func getPacketIP4Addrs(pkt *nfqueue.NFQPacket) (net.IP, net.IP) { 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 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) { func getPacketTCPPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) {
tcpLayer := pkt.Packet.Layer(layers.LayerTypeTCP) tcpLayer := pkt.Packet.Layer(layers.LayerTypeTCP)

@ -77,6 +77,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
int32(pc.dstPort()), int32(pc.dstPort()),
pc.dst().String(), pc.dst().String(),
pc.src().String(), pc.src().String(),
pc.proto(),
int32(pc.procInfo().UID), int32(pc.procInfo().UID),
int32(pc.procInfo().GID), int32(pc.procInfo().GID),
uidToUser(pc.procInfo().UID), uidToUser(pc.procInfo().UID),

@ -25,6 +25,7 @@ type Rule struct {
policy *Policy policy *Policy
mode RuleMode mode RuleMode
rtype RuleAction rtype RuleAction
proto string
hostname string hostname string
network *net.IPNet network *net.IPNet
addr uint32 addr uint32
@ -50,7 +51,12 @@ func (r *Rule) getString(redact bool) string {
rmode = "|" + RuleModeString[RULE_MODE_SYSTEM] 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 { 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]) 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) port = fmt.Sprintf("%d", r.port)
} }
@ -79,7 +85,10 @@ func (r *Rule) AddrString(redact bool) string {
type RuleList []*Rule 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 { if r.uid != -1 && r.uid != uid {
return false return false
} else if r.gid != -1 && r.gid != gid { } 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) { if r.network != nil && r.network.Contains(dst) {
return true 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()) 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 { 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 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") log.Notice("! Skipping comparison of mismatching source ips")
continue 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") log.Notice("+ MATCH SUCCEEDED")
dstStr := dst.String() dstStr := dst.String()
if FirewallConfig.LogRedact { if FirewallConfig.LogRedact {
@ -153,12 +168,12 @@ log.Notice("+ MATCH SUCCEEDED")
} }
log.Noticef("%s > %s %s %s -> %s:%d", log.Noticef("%s > %s %s %s -> %s:%d",
r.getString(FirewallConfig.LogRedact), r.getString(FirewallConfig.LogRedact),
pinfo.ExePath, "TCP", pinfo.ExePath, r.proto,
srcStr, srcStr,
dstStr, dstPort) dstStr, dstPort)
if r.rtype == RULE_ACTION_DENY { if r.rtype == RULE_ACTION_DENY {
log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d", log.Warningf("DENIED outgoing connection attempt by %s from %s %s -> %s:%d",
pinfo.ExePath, "TCP", pinfo.ExePath, r.proto,
srcStr, srcStr,
dstStr, dstPort) dstStr, dstPort)
return FILTER_DENY return FILTER_DENY
@ -257,11 +272,21 @@ func (r *Rule) parseVerb(v string) bool {
func (r *Rule) parseTarget(t string) bool { func (r *Rule) parseTarget(t string) bool {
addrPort := strings.Split(t, ":") 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 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 { func (r *Rule) parseAddr(a string) bool {
@ -296,7 +321,7 @@ func (r *Rule) parsePort(p string) bool {
} }
var err error var err error
port, err := strconv.ParseUint(p, 10, 16) 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 return false
} }
r.port = uint16(port) r.port = uint16(port)

@ -72,6 +72,9 @@ func (sc *pendingSocksConnection) hostname() string {
func (sc *pendingSocksConnection) dst() net.IP { func (sc *pendingSocksConnection) dst() net.IP {
return sc.destIP return sc.destIP
} }
func (sc *pendingSocksConnection) proto() string {
return "tcp"
}
func (sc *pendingSocksConnection) srcPort() uint16 { func (sc *pendingSocksConnection) srcPort() uint16 {
return sc.sourcePort return sc.sourcePort
} }

@ -66,6 +66,24 @@ func FindProcessForConnection(conn net.Conn, procInfo ProcInfo) *Info {
return 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 // LookupUDPSocketProcess searches for a UDP socket with a source port
func LookupUDPSocketProcess(srcPort uint16) *Info { func LookupUDPSocketProcess(srcPort uint16) *Info {
ss := findUDPSocket(srcPort) ss := findUDPSocket(srcPort)

@ -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) 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 { func findUDPSocket(srcPort uint16) *socketStatus {
return findSocket("udp", func(ss socketStatus) bool { return findSocket("udp", func(ss socketStatus) bool {
return ss.local.port == srcPort 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 { func findTCPSocketAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string) *socketStatus {
found := findSocket("tcp", func(ss socketStatus) bool { 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 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
} }
return findSocketCustom("tcp", custdata, func(ss socketStatus) bool { return findSocketCustom("tcp", custdata, func(ss socketStatus) bool {

Loading…
Cancel
Save