Added ephemeral oz sandbox/fw-daemon rules that can be updated via IPC connection.

fw-daemon prompter is now updated with source address of originating packet.
Fixed bug in decoding DNS data.
Packets are dropped properly (by marking and then calling Accept()).
shw_dev
shw 7 years ago
parent 942b0a0c01
commit cadb859dce

@ -0,0 +1,90 @@
package main
import (
"fmt"
"flag"
"strconv"
"io"
"log"
"net"
)
const ReceiverSocketPath = "/tmp/fwoz.sock"
func reader(r io.Reader) {
buf := make([]byte, 1024)
for {
n, err := r.Read(buf[:])
if err != nil {
return
}
fmt.Println(string(buf[0:n]))
}
}
func main() {
dump := flag.Bool("d", false, "dump current oz-fw rules")
whitelist := flag.Bool("w", false, "submit whitelist rule")
blacklist := flag.Bool("b", false, "submit blacklist rule")
src := flag.String("src", "", "source IP address")
dst := flag.String("dst", "", "destination IP address")
port := flag.Int("port", 0, "destination port number")
rm := flag.Bool("rm", false, "remove entry from rules (default is add)")
flag.Parse()
if !*dump {
if *src == "" {
log.Fatal("Error: must specify source address with -src")
} else if *dst == "" {
log.Fatal("Error: must specify destination address with -dst")
} else if *port == 0 {
log.Fatal("Error: must specify destination port with -port")
} else if *port <= 0 || *port > 65535 {
log.Fatal("Error: invalid port was specified")
} else if !*whitelist && !*blacklist {
log.Fatal("Error: -w or -b must be specified to whitelist or blacklist entry")
} else if *whitelist && *blacklist {
log.Fatal("Error: -w and -b cannot be specified together")
}
} else {
fmt.Println("Attempting to dump active rule list.")
}
c, err := net.Dial("unix", ReceiverSocketPath)
if err != nil {
log.Fatal("Could not establish connection to listener:", err)
}
defer c.Close()
if *dump {
c.Write([]byte("dump\n"))
reader(c)
fmt.Println("Done.")
} else {
reqstr := ""
if *rm {
reqstr += "remove "
} else {
reqstr += "add "
}
if *whitelist {
reqstr += "whitelist"
} else {
reqstr += "blacklist"
}
reqstr += " " + *src + " " + *dst + " " + strconv.Itoa(*port) + "\n"
c.Write([]byte(reqstr))
reader(c)
fmt.Println("Done.")
}
}

@ -111,3 +111,9 @@ type DbusRule struct {
Target string
Mode uint16
}
const (
OZ_FWRULE_WHITELIST = iota
OZ_FWRULE_BLACKLIST
OZ_FWRULE_NONE
)

@ -7,6 +7,7 @@ import (
// "github.com/subgraph/go-nfnetlink"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
type dnsCache struct {
@ -24,7 +25,7 @@ func newDNSCache() *dnsCache {
func (dc *dnsCache) processDNS(pkt gopacket.Packet) {
dns := &dnsMsg{}
if !dns.Unpack(pkt.ApplicationLayer().Payload()) {
if !dns.Unpack(pkt.Layer(layers.LayerTypeDNS).LayerContents()) {
log.Warning("Failed to Unpack DNS message")
return
}

@ -0,0 +1,187 @@
package sgfw
import (
"fmt"
"net"
"os"
"bufio"
"strings"
"strconv"
)
const ReceiverSocketPath = "/tmp/fwoz.sock"
func canAddRule(rule sandboxRule) bool {
for i := 0; i < len(sandboxRules); i++ {
if rule.SrcIf.Equal(sandboxRules[i].SrcIf) && rule.Whitelist != sandboxRules[i].Whitelist {
return false
}
}
return true
}
func ruleExists(rule sandboxRule) int {
for i := 0; i < len(sandboxRules); i++ {
if rule.Whitelist == sandboxRules[i].Whitelist && rule.SrcIf.Equal(sandboxRules[i].SrcIf) && rule.DstIP.Equal(sandboxRules[i].DstIP) && rule.DstPort == sandboxRules[i].DstPort {
return i
}
}
return -1
}
func ReceiverLoop(c net.Conn) {
defer c.Close()
bio := bufio.NewReader(c)
for {
buf, err := bio.ReadBytes('\n')
if err != nil {
log.Notice("Error reading data from IPC client: ", err)
return
}
data := string(buf)
if data[len(data)-1] == '\n' {
data = data[0:len(data)-1]
}
if data == "dump" {
log.Notice("Dumping oz-firewall rule set to client...")
banner := fmt.Sprintf("There are a total of %d rule(s).\n", len(sandboxRules))
c.Write([]byte(banner))
for i := 0; i < len(sandboxRules); i++ {
rulestr := ""
if sandboxRules[i].Whitelist {
rulestr += "whitelist"
} else {
rulestr += "blacklist"
}
rulestr += " " + sandboxRules[i].SrcIf.String() + " -> " + sandboxRules[i].DstIP.String() + " : " + strconv.Itoa(int(sandboxRules[i].DstPort)) + "\n"
c.Write([]byte(rulestr))
}
return
} else {
tokens := strings.Split(data, " ")
if len(tokens) != 5 {
log.Notice("IPC received invalid command: " + data)
c.Write([]byte("Received bad number of parameters.\n"))
return
} else if tokens[0] != "add" && tokens[0] != "remove" {
log.Notice("IPC received invalid command: " + data)
c.Write([]byte("Unrecognized command.\n"))
return
} else if tokens[1] != "whitelist" && tokens[1] != "blacklist" {
log.Notice("IPC received invalid command: " + data)
c.Write([]byte("Bad command: must specify either whitelist or blacklist.\n"))
return
}
add := true
if tokens[0] == "remove" {
add = false
}
w := true
if tokens[1] == "blacklist" {
w = false
}
srcip := net.ParseIP(tokens[2])
if srcip == nil {
log.Notice("IPC received invalid source host: ", tokens[2])
c.Write([]byte("Bad command: source host address was invalid"))
return
}
dstip := net.ParseIP(tokens[3])
if dstip == nil {
log.Notice("IPC received invalid destination host: ", tokens[3])
c.Write([]byte("Bad command: dst host address was invalid"))
return
}
dstport, err := strconv.Atoi(tokens[4])
if err != nil || dstport <= 0 || dstport > 65535 {
log.Notice("IPC received invalid destination port: ", tokens[4])
c.Write([]byte("Bad command: dst port was invalid"))
return
}
rule := sandboxRule { srcip, dstip, uint16(dstport), w }
if add && !canAddRule(rule) {
log.Notice("Could not add rule of mismatching type: ", rule)
c.Write([]byte("Error: cannot add rule that would result in mixed whitelist and blacklist"))
return
}
exists := ruleExists(rule)
if add && exists != -1 {
log.Notice("IP received request to add existing rule: ", rule)
c.Write([]byte("Error: cannot add already existing rule"))
return
} else if !add && exists == -1 {
log.Notice("IP received request to remove non-existent rule: ", rule)
c.Write([]byte("Error: could not remove non-existent rule"))
return
}
if add {
log.Notice("Adding new rule to oz sandbox/fw: ", rule)
sandboxRules = append(sandboxRules, rule)
} else {
log.Notice("Removing new rule from oz sandbox/fw: ", rule)
sandboxRules = append(sandboxRules[:exists], sandboxRules[exists+1:]...)
}
log.Notice("IPC received command: " + data)
c.Write([]byte("OK.\n"))
return
}
}
}
func OzReceiver() {
log.Notice("XXX: dispatching oz receiver...")
os.Remove(ReceiverSocketPath)
lfd, err := net.Listen("unix", ReceiverSocketPath)
if err != nil {
log.Fatal("Could not open oz receiver socket:", err)
}
for {
fd, err := lfd.Accept()
if err != nil {
log.Fatal("Could not accept receiver client:", err)
}
go ReceiverLoop(fd)
}
}

@ -21,10 +21,22 @@ var _interpreters = []string{
"bash",
}
type sandboxRule struct {
SrcIf net.IP
DstIP net.IP
DstPort uint16
Whitelist bool
}
var sandboxRules = []sandboxRule {
{ net.IP{172,16,1,42}, net.IP{140,211,166,134}, 21, false },
}
type pendingConnection interface {
policy() *Policy
procInfo() *procsnitch.Info
hostname() string
src() net.IP
dst() net.IP
dstPort() uint16
accept()
@ -50,14 +62,22 @@ func (pp *pendingPkt) procInfo() *procsnitch.Info {
func (pp *pendingPkt) hostname() string {
return pp.name
}
func (pp *pendingPkt) src() net.IP {
src, _ := getPacketIP4Addrs(pp.pkt)
return src
}
func (pp *pendingPkt) dst() net.IP {
dst := pp.pkt.Packet.NetworkLayer().NetworkFlow().Dst()
_, dst := getPacketIP4Addrs(pp.pkt)
return dst
/* dst := pp.pkt.Packet.NetworkLayer().NetworkFlow().Dst()
if dst.EndpointType() != layers.EndpointIPv4 {
return nil
}
return dst.Raw()
return dst.Raw() */
// pp.pkt.NetworkLayer().Layer
}
@ -79,8 +99,7 @@ func (pp *pendingPkt) accept() {
}
func (pp *pendingPkt) drop() {
// XXX: This needs to be fixed
// pp.pkt.Mark = 1
pp.pkt.SetMark(1)
pp.pkt.Accept()
}
@ -124,20 +143,27 @@ func (fw *Firewall) policyForPath(path string) *Policy {
}
func (p *Policy) processPacket(pkt *nfqueue.NFQPacket, pinfo *procsnitch.Info) {
/* 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()
dstip := net.IP(dstb)
srcip := net.IP(pkt.Packet.NetworkLayer().NetworkFlow().Src().Raw())
_, dstp := getPacketPorts(pkt)
name := p.fw.dns.Lookup(dstip)
// name := p.fw.dns.Lookup(pkt.Dst)
if !FirewallConfig.LogRedact {
log.Infof("Lookup(%s): %s", dstip.String(), name)
}
fwo := matchAgainstOzRules(srcip, dstip, dstp)
log.Notice("XXX: Attempting to filter packet on rules -> ", fwo)
result := p.rules.filterPacket(pkt, pinfo, name)
switch result {
case FILTER_DENY:
// XXX: this needs to be fixed
// pkt.Mark = 1
pkt.SetMark(1)
pkt.Accept()
case FILTER_ALLOW:
pkt.Accept()
@ -299,7 +325,24 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) {
}
}
_, dstip := getPacketIP4Addrs(pkt)
srcip, dstip := getPacketIP4Addrs(pkt)
_, dstp := getPacketPorts(pkt)
fwo := matchAgainstOzRules(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
}
pinfo := findProcessForPacket(pkt)
if pinfo == nil {
log.Warningf("No proc found for %s", printPacket(pkt, fw.dns.Lookup(dstip), nil))
@ -390,3 +433,21 @@ func getPacketPorts(pkt *nfqueue.NFQPacket) (uint16, uint16) {
return s, d
}
func matchAgainstOzRules(srci, dsti net.IP, dstp uint16) int {
for i := 0; i < len(sandboxRules); i++ {
log.Notice("XXX: Attempting to match: ", srci, " / ", dsti, " / ", dstp, " | ", sandboxRules[i])
if sandboxRules[i].SrcIf.Equal(srci) && sandboxRules[i].DstIP.Equal(dsti) && sandboxRules[i].DstPort == dstp {
if sandboxRules[i].Whitelist {
return OZ_FWRULE_WHITELIST
}
return OZ_FWRULE_BLACKLIST
}
}
return OZ_FWRULE_NONE
}

@ -75,7 +75,7 @@ func (p *prompter) processConnection(pc pendingConnection) {
addr,
int32(pc.dstPort()),
pc.dst().String(),
"---",
pc.src().String(),
uidToUser(pc.procInfo().UID),
int32(pc.procInfo().Pid),
FirewallConfig.PromptExpanded,

@ -99,6 +99,7 @@ func (fw *Firewall) runFilter() {
if err != nil {
log.Fatal("Error opening NFQueue:", err)
}
q.EnableHWTrace()
defer q.Close()
go func() {
@ -222,6 +223,8 @@ func Main() {
log.Notice("Did not find SOCKS5 configuration file at", defaultSocksCfgPath, "; ignoring subsystem...")
}
go OzReceiver()
fw.runFilter()
// observe process signals and either

@ -44,6 +44,7 @@ const (
type pendingSocksConnection struct {
pol *Policy
hname string
srcIP net.IP
destIP net.IP
destPort uint16
pinfo *procsnitch.Info
@ -69,6 +70,10 @@ func (sc *pendingSocksConnection) dstPort() uint16 {
return sc.destPort
}
func (sc *pendingSocksConnection) src() net.IP {
return sc.srcIP
}
func (sc *pendingSocksConnection) deliverVerdict(v int) {
sc.verdict <- v
close(sc.verdict)
@ -178,7 +183,7 @@ func (c *socksChainSession) addressDetails() (string, net.IP, uint16) {
func (c *socksChainSession) filterConnect() bool {
pinfo := procsnitch.FindProcessForConnection(c.clientConn, c.procInfo)
if pinfo == nil {
log.Warningf("No proc found for connection from: %s", c.clientConn.RemoteAddr())
log.Warningf("No proc found for [socks5] connection from: %s", c.clientConn.RemoteAddr())
return false
}
@ -199,6 +204,7 @@ func (c *socksChainSession) filterConnect() bool {
pol: policy,
hname: hostname,
destIP: ip,
srcIP: net.IP{0,0,0,0},
destPort: port,
pinfo: pinfo,
verdict: make(chan int),

Loading…
Cancel
Save