Support for TLSGuard in prompter

shw_dev
dma 7 years ago
parent 9ff74569f3
commit f3f5414fd4

@ -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 {

@ -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
}

@ -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) {
@ -150,6 +160,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++) {
this._addOption(options[i], i)
@ -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);
},
});

@ -57,6 +57,7 @@ const FirewallPromptInterface = '<node> \
<arg type="s" direction="in" name="user" /> \
<arg type="s" direction="in" name="group" /> \
<arg type="i" direction="in" name="pid" /> \
<arg type="s" direction="in" name="sandbox" /> \
<arg type="s" direction="in" name="optstring" /> \
<arg type="b" direction="in" name="expanded" /> \
<arg type="b" direction="in" name="expert" /> \
@ -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();
},

@ -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

@ -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.")

@ -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{}
}

@ -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 {
@ -185,12 +198,11 @@ func (p *prompter) processConnection(pc pendingConnection) {
//}
//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)

@ -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)

@ -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...")
}

@ -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

Loading…
Cancel
Save