Added support for using fw-daemon on all processes system-wide.

Added changes for fw-daemon to check sandboxed processes' oz-init /proc/[pid]/net/tcp with procsnitch.
fw-daemon IPC accepts "register-init" command to register new oz-init process instance alerts.
fw-daemon also checks for existing oz-init processes on startup.
Updated vendor-bundled go-procsnitch to latest dev version that includes findTCPSocketAll().
shw_dev
shw 7 years ago
parent 30482bf15b
commit 7a1851419c

@ -13,6 +13,29 @@ import (
const ReceiverSocketPath = "/tmp/fwoz.sock"
var OzInitPids []int = []int{}
func addInitPid(pid int) {
fmt.Println("::::::::::: init pid added: ", pid)
for i := 0; i < len(OzInitPids); i++ {
if OzInitPids[i] == pid {
return
}
}
OzInitPids = append(OzInitPids, pid)
}
func removeInitPid(pid int) {
for i := 0; i < len(OzInitPids); i++ {
if OzInitPids[i] == pid {
OzInitPids = append(OzInitPids[:i], OzInitPids[i+1:])
return
}
}
}
func addFWRule(fw *Firewall, whitelist bool, srchost, dsthost, dstport string) error {
policy := fw.PolicyForPath("*")
rulestr := ""
@ -131,7 +154,22 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
return
}
if len(tokens) != 5 {
if tokens[0] == "register-init" && len(tokens) == 2 {
initp := tokens[1]
initpid, err := strconv.Atoi(initp)
if err != nil {
log.Notice("IPC received invalid oz-init pid: ", initp)
c.Write([]byte("Bad command: init pid was invalid"))
return
}
addInitPid(initpid)
c.Write([]byte("OK.\n"))
return
}
if len(tokens) != 6 {
log.Notice("IPC received invalid command: " + data)
c.Write([]byte("Received bad number of parameters.\n"))
return
@ -175,8 +213,18 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
return
}
/* initp := tokens[5]
initpid, err := strconv.Atoi(initp)
if err != nil {
log.Notice("IPC received invalid oz-init pid: ", initp)
c.Write([]byte("Bad command: init pid was invalid"))
return
} */
if add {
log.Noticef("Adding new rule to oz sandbox/fw: %v / %v -> %v : %v", w, srchost, dsthost, dstport)
// addInitPid(initpid)
err := addFWRule(fw, w, srchost, dsthost, dstport)
if err != nil {
log.Error("Error adding dynamic OZ firewall rule to fw-daemon: ", err)
@ -200,6 +248,24 @@ func ReceiverLoop(fw *Firewall, c net.Conn) {
func OzReceiver(fw *Firewall) {
log.Notice("XXX: dispatching oz receiver...")
sboxes, err := getSandboxes()
if err != nil {
log.Warning("Error retrieving list of running Oz sandbox init processes: ", err)
} else {
if len(sboxes) > 0 {
log.Warning("Adding existing Oz sandbox init pids...")
for s := 0; s < len(sboxes); s++ {
addInitPid(sboxes[s].InitPid)
}
} else {
log.Warning("It does not appear there were any Oz sandboxed processes already launched.")
}
}
os.Remove(ReceiverSocketPath)
lfd, err := net.Listen("unix", ReceiverSocketPath)
if err != nil {

@ -13,6 +13,8 @@ import (
"github.com/google/gopacket/layers"
"github.com/subgraph/go-procsnitch"
"net"
"syscall"
"unsafe"
)
var _interpreters = []string{
@ -397,12 +399,93 @@ func (fw *Firewall) filterPacket(pkt *nfqueue.NFQPacket) {
policy.processPacket(pkt, pinfo)
}
func readFileDirect(filename string) ([]byte, error) {
bfilename, err := syscall.BytePtrFromString(filename)
if err != nil {
return nil, err
}
res, _, err := syscall.Syscall(syscall.SYS_OPEN, uintptr(unsafe.Pointer(bfilename)), syscall.O_RDONLY, 0)
fdlong := int64(res)
if fdlong < 0 {
return nil, err
}
fd := int(res)
data := make([]byte, 65535)
val, err := syscall.Read(fd, data)
if err != nil {
return nil, err
}
syscall.Close(fd)
if val < 65535 {
data = data[0:val]
}
return data, nil
}
func getAllProcNetDataLocal() ([]string, error) {
data := ""
for i := 0; i < len(OzInitPids); i++ {
fname := fmt.Sprintf("/proc/%d/net/tcp", OzInitPids[i])
fmt.Println("XXX: opening: ", fname)
bdata, err := readFileDirect(fname)
if err != nil {
fmt.Println("Error reading proc data from ", fname, ": ", err)
} else {
data += string(bdata)
}
}
lines := strings.Split(data, "\n")
rlines := make([]string, 0)
ctr := 1
for l := 0; l < len(lines); l++ {
lines[l] = strings.TrimSpace(lines[l])
ssplit := strings.Split(lines[l], ":")
if len(ssplit) != 6 {
continue
}
ssplit[0] = fmt.Sprintf("%d", ctr)
ctr++
rlines = append(rlines, strings.Join(ssplit, ":"))
}
return rlines, nil
}
func findProcessForPacket(pkt *nfqueue.NFQPacket) *procsnitch.Info {
_, dstip := getPacketIP4Addrs(pkt)
srcip, dstip := getPacketIP4Addrs(pkt)
srcp, dstp := getPacketPorts(pkt)
if pkt.Packet.Layer(layers.LayerTypeTCP) != nil {
return procsnitch.LookupTCPSocketProcess(srcp, dstip, dstp)
// Try normal way first, before the more resource intensive/invasive way.
res := procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, nil)
if res == nil {
extdata, err := getAllProcNetDataLocal()
if err != nil {
log.Warningf("Error looking up sandboxed /proc/net data: %v", err)
} else {
res = procsnitch.LookupTCPSocketProcessAll(srcip, srcp, dstip, dstp, extdata)
}
}
return res
} else if pkt.Packet.Layer(layers.LayerTypeUDP) != nil {
return procsnitch.LookupUDPSocketProcess(srcp)
}

@ -0,0 +1,47 @@
package sgfw
import (
"github.com/subgraph/ozipc"
)
type ListSandboxesMsg struct {
_ string "ListSandboxes"
}
type SandboxInfo struct {
Id int
Address string
Profile string
Mounts []string
InitPid int
}
type ListSandboxesResp struct {
Sandboxes []SandboxInfo "ListSandboxesResp"
}
const socketPath = "@oz-control"
var ozCtrlFactory = ipc.NewMsgFactory(
new(ListSandboxesMsg),
new(ListSandboxesResp),
)
func getSandboxes() ([]SandboxInfo, error) {
c, err := ipc.Connect(socketPath, ozCtrlFactory, nil)
if err != nil {
log.Fatal("Error connecting to oz control socket: ", err)
return nil, err
}
defer c.Close()
rr, err := c.ExchangeMsg(&ListSandboxesMsg{})
if err != nil {
return nil, err
}
resp := <-rr.Chan()
rr.Done()
sboxes := resp.Body.(*ListSandboxesResp)
return sboxes.Sandboxes, nil
}

@ -75,6 +75,15 @@ func LookupUDPSocketProcess(srcPort uint16) *Info {
return pcache.lookup(ss.inode)
}
// LookupTCPSocketProcessAll searches for a TCP socket a given source port, destination IP, and destination port - AND source destination
func LookupTCPSocketProcessAll(srcAddr net.IP, srcPort uint16, dstAddr net.IP, dstPort uint16, custdata []string) *Info {
ss := findTCPSocketAll(srcAddr, srcPort, dstAddr, dstPort, custdata)
if ss == nil {
return nil
}
return pcache.lookup(ss.inode)
}
// LookupTCPSocketProcess searches for a TCP socket with a given source port, destination IP, and destination port
func LookupTCPSocketProcess(srcPort uint16, dstAddr net.IP, dstPort uint16) *Info {
ss := findTCPSocket(srcPort, dstAddr, dstPort)

@ -90,6 +90,20 @@ 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
}
return findSocketCustom("tcp", custdata, func(ss socketStatus) bool {
return ss.remote.port == dstPort && ss.remote.ip.Equal(dstAddr) && ss.local.port == srcPort && ss.local.ip.Equal(srcAddr)
})
}
func findUNIXSocket(socketFile string) *socketStatus {
proto := "unix"
@ -127,6 +141,24 @@ func findUNIXSocket(socketFile string) *socketStatus {
return nil
}
func findSocketCustom(proto string, sockdata []string, matcher func(socketStatus) bool) *socketStatus {
var ss socketStatus
for _, line := range sockdata {
if len(line) == 0 {
continue
}
if err := ss.parseLine(line); err != nil {
log.Warningf("Unable to parse line from custom data source/%s [%s]: %v", proto, line, err)
continue
}
if matcher(ss) {
ss.line = line
return &ss
}
}
return nil
}
func findSocket(proto string, matcher func(socketStatus) bool) *socketStatus {
var ss socketStatus
for _, line := range getSocketLines(proto) {

Loading…
Cancel
Save