diff --git a/dbus.go b/dbus.go
index ea169f4..55b5829 100644
--- a/dbus.go
+++ b/dbus.go
@@ -7,6 +7,8 @@ import (
"github.com/subgraph/fw-daemon/Godeps/_workspace/src/github.com/godbus/dbus"
"github.com/subgraph/fw-daemon/Godeps/_workspace/src/github.com/godbus/dbus/introspect"
+ "github.com/subgraph/fw-daemon/Godeps/_workspace/src/github.com/op/go-logging"
+ "path"
)
const introspectXml = `
@@ -15,9 +17,31 @@ const introspectXml = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
` +
introspect.IntrospectDataString +
``
@@ -27,10 +51,19 @@ const objectPath = "/com/subgraph/Firewall"
const interfaceName = "com.subgraph.Firewall"
type dbusServer struct {
+ fw *Firewall
conn *dbus.Conn
prompter *prompter
}
+type DbusRule struct {
+ Id uint32
+ App string
+ Path string
+ Verb uint32
+ Target string
+}
+
func newDbusServer() (*dbusServer, error) {
conn, err := dbus.SystemBus()
if err != nil {
@@ -88,13 +121,96 @@ func (ds *dbusServer) Introspect(msg dbus.Message) (string, *dbus.Error) {
}
func (ds *dbusServer) SetEnabled(flag bool) *dbus.Error {
- log.Info("SetEnabled(%v) called", flag)
+ log.Debug("SetEnabled(%v) called", flag)
+ ds.fw.setEnabled(flag)
return nil
}
func (ds *dbusServer) IsEnabled() (bool, *dbus.Error) {
- log.Info("IsEnabled() called")
- return true, nil
+ log.Debug("IsEnabled() called")
+ return ds.fw.isEnabled(), nil
+}
+
+func createDbusRule(r *Rule) DbusRule {
+ return DbusRule{
+ Id: uint32(r.id),
+ App: path.Base(r.policy.path),
+ Path: r.policy.path,
+ Verb: uint32(r.rtype),
+ Target: r.AddrString(),
+ }
+}
+
+func (ds *dbusServer) ListRules() ([]DbusRule, *dbus.Error) {
+ ds.fw.lock.Lock()
+ defer ds.fw.lock.Unlock()
+ var result []DbusRule
+ for _, p := range ds.fw.policies {
+ for _, r := range p.rules {
+ result = append(result, createDbusRule(r))
+ }
+ }
+ return result, nil
+}
+
+func (ds *dbusServer) DeleteRule(id uint32) *dbus.Error {
+ ds.fw.lock.Lock()
+ r := ds.fw.rulesById[uint(id)]
+ ds.fw.lock.Unlock()
+ if r != nil {
+ r.policy.removeRule(r)
+ }
+ if !r.sessionOnly {
+ ds.fw.saveRules()
+ }
+ return nil
+}
+
+func (ds *dbusServer) UpdateRule(rule DbusRule) *dbus.Error {
+ log.Debug("UpdateRule %v", rule)
+ ds.fw.lock.Lock()
+ r := ds.fw.rulesById[uint(rule.Id)]
+ ds.fw.lock.Unlock()
+ if r != nil {
+ tmp := new(Rule)
+ tmp.addr = noAddress
+ if !tmp.parseTarget(rule.Target) {
+ log.Warning("Unable to parse target: %s", rule.Target)
+ return nil
+ }
+ r.policy.lock.Lock()
+ if rule.Verb == RULE_ALLOW || rule.Verb == RULE_DENY {
+ r.rtype = int(rule.Verb)
+ }
+ r.hostname = tmp.hostname
+ r.addr = tmp.addr
+ r.port = tmp.port
+ r.policy.lock.Unlock()
+ if !r.sessionOnly {
+ ds.fw.saveRules()
+ }
+ }
+ return nil
+}
+
+func (ds *dbusServer) GetConfig() (map[string]dbus.Variant, *dbus.Error) {
+ conf := make(map[string]dbus.Variant)
+ conf["loglevel"] = dbus.MakeVariant(int32(ds.fw.logBackend.GetLevel("sgfw")))
+ conf["logredact"] = dbus.MakeVariant(ds.fw.logRedact)
+ return conf, nil
+}
+
+func (ds *dbusServer) SetConfig(key string, val dbus.Variant) *dbus.Error {
+ switch key {
+ case "loglevel":
+ l := val.Value().(int32)
+ lvl := logging.Level(l)
+ ds.fw.logBackend.SetLevel(lvl, "sgfw")
+ case "logredact":
+ flag := val.Value().(bool)
+ ds.fw.logRedact = flag
+ }
+ return nil
}
func (ds *dbusServer) prompt(p *Policy) {
diff --git a/iptables.go b/iptables.go
index cdf943d..2a4e2d0 100644
--- a/iptables.go
+++ b/iptables.go
@@ -17,7 +17,7 @@ func setupIPTables() {
func addIPTRules(rules ...string) {
for _, r := range rules {
- if(iptables('C', r)) {
+ if iptables('C', r) {
log.Info("IPTables rule already present: %s", r)
} else {
log.Info("Installing IPTables rule: %s", r)
diff --git a/main.go b/main.go
index dd6f3d6..71fd102 100644
--- a/main.go
+++ b/main.go
@@ -8,10 +8,10 @@ import (
"github.com/subgraph/fw-daemon/Godeps/_workspace/src/github.com/op/go-logging"
"github.com/subgraph/fw-daemon/nfqueue"
+ "github.com/subgraph/fw-daemon/proc"
"sync"
"syscall"
"unsafe"
- "github.com/subgraph/fw-daemon/proc"
)
var log = logging.MustGetLogger("sgfw")
@@ -22,6 +22,7 @@ var logFormat = logging.MustStringFormatter(
var ttyFormat = logging.MustStringFormatter(
"%{color}%{time:15:04:05} ▶ %{level:.4s} %{id:03x}%{color:reset} %{message}",
)
+
const ioctlReadTermios = 0x5401
func isTerminal(fd int) bool {
@@ -30,7 +31,7 @@ func isTerminal(fd int) bool {
return err == 0
}
-func init() {
+func setupLoggerBackend() logging.LeveledBackend {
format := logFormat
if isTerminal(int(os.Stderr.Fd())) {
format = ttyFormat
@@ -38,16 +39,66 @@ func init() {
backend := logging.NewLogBackend(os.Stderr, "", 0)
formatter := logging.NewBackendFormatter(backend, format)
leveler := logging.AddModuleLevel(formatter)
- log.SetBackend(leveler)
+ return leveler
}
type Firewall struct {
dbus *dbusServer
dns *dnsCache
+ enabled bool
+
+ logRedact bool
+ logBackend logging.LeveledBackend
+
lock sync.Mutex
policyMap map[string]*Policy
policies []*Policy
+
+ ruleLock sync.Mutex
+ rulesById map[uint]*Rule
+ nextRuleId uint
+}
+
+func (fw *Firewall) setEnabled(flag bool) {
+ fw.lock.Lock()
+ defer fw.lock.Unlock()
+ fw.enabled = flag
+}
+
+func (fw *Firewall) isEnabled() bool {
+ fw.lock.Lock()
+ defer fw.lock.Unlock()
+ return fw.enabled
+}
+
+func (fw *Firewall) clearRules() {
+ fw.ruleLock.Lock()
+ defer fw.ruleLock.Unlock()
+ fw.rulesById = nil
+ fw.nextRuleId = 0
+}
+
+func (fw *Firewall) addRule(r *Rule) {
+ fw.ruleLock.Lock()
+ defer fw.ruleLock.Unlock()
+
+ r.id = fw.nextRuleId
+ fw.nextRuleId += 1
+ if fw.rulesById == nil {
+ fw.rulesById = make(map[uint]*Rule)
+ }
+ fw.rulesById[r.id] = r
+}
+
+func (fw *Firewall) getRuleById(id uint) *Rule {
+ fw.ruleLock.Lock()
+ defer fw.ruleLock.Unlock()
+
+ if fw.rulesById == nil {
+ return nil
+ }
+ return fw.rulesById[id]
}
func (fw *Firewall) runFilter() {
@@ -64,7 +115,11 @@ func (fw *Firewall) runFilter() {
for {
select {
case pkt := <-packets:
- fw.filterPacket(pkt)
+ if fw.isEnabled() {
+ fw.filterPacket(pkt)
+ } else {
+ pkt.Accept()
+ }
case <-sigs:
return
}
@@ -72,6 +127,8 @@ func (fw *Firewall) runFilter() {
}
func main() {
+ logBackend := setupLoggerBackend()
+ log.SetBackend(logBackend)
proc.SetLogger(log)
if os.Geteuid() != 0 {
@@ -88,17 +145,21 @@ func main() {
}
fw := &Firewall{
- dbus: ds,
- dns: NewDnsCache(),
- policyMap: make(map[string]*Policy),
+ dbus: ds,
+ dns: NewDnsCache(),
+ enabled: true,
+ logRedact: false,
+ logBackend: logBackend,
+ policyMap: make(map[string]*Policy),
}
+ ds.fw = fw
fw.loadRules()
/*
- go func() {
- http.ListenAndServe("localhost:6060", nil)
- }()
+ go func() {
+ http.ListenAndServe("localhost:6060", nil)
+ }()
*/
fw.runFilter()
diff --git a/policy.go b/policy.go
index d1a298b..424b67b 100644
--- a/policy.go
+++ b/policy.go
@@ -12,7 +12,7 @@ type pendingPkt struct {
policy *Policy
hostname string
pkt *nfqueue.Packet
- pinfo *proc.ProcInfo
+ pinfo *proc.ProcInfo
}
type Policy struct {
@@ -110,6 +110,34 @@ func (p *Policy) processNewRule(r *Rule, scope int32) bool {
return p.promptInProgress
}
+func (p *Policy) parseRule(s string, add bool) (*Rule, error) {
+ r := new(Rule)
+ r.policy = p
+ if !r.parse(s) {
+ return nil, parseError(s)
+ }
+ if add {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+ p.rules = append(p.rules, r)
+ }
+ p.fw.addRule(r)
+ return r, nil
+}
+
+func (p *Policy) removeRule(r *Rule) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ var newRules RuleList
+ for _, rr := range p.rules {
+ if rr.id != r.id {
+ newRules = append(newRules, rr)
+ }
+ }
+ p.rules = newRules
+}
+
func (p *Policy) filterPending(rule *Rule) {
remaining := []*pendingPkt{}
for _, pp := range p.pendingQueue {
diff --git a/prompt.go b/prompt.go
index 04edd86..c3f6b18 100644
--- a/prompt.go
+++ b/prompt.go
@@ -100,18 +100,16 @@ func (p *prompter) processPacket(pp *pendingPkt) {
pp.policy.removePending(pp)
pp.pkt.Mark = 1
pp.pkt.Accept()
- //pp.pkt.Drop()
return
}
log.Debug("Received prompt response: %s [%s]", printScope(scope), rule)
- r, err := parseRule(rule)
+ r, err := pp.policy.parseRule(rule, false)
if err != nil {
log.Warning("Error parsing rule string returned from dbus RequestPrompt: %v", err)
pp.policy.removePending(pp)
pp.pkt.Mark = 1
pp.pkt.Accept()
- //pp.pkt.Drop()
return
}
if scope == APPLY_SESSION {
diff --git a/rules.go b/rules.go
index 21dd45a..85ce1da 100644
--- a/rules.go
+++ b/rules.go
@@ -8,11 +8,11 @@ import (
"unicode"
"github.com/subgraph/fw-daemon/nfqueue"
+ "github.com/subgraph/fw-daemon/proc"
"io/ioutil"
"os"
- "strconv"
"path"
- "github.com/subgraph/fw-daemon/proc"
+ "strconv"
)
const (
@@ -24,6 +24,8 @@ const matchAny = 0
const noAddress = uint32(0xffffffff)
type Rule struct {
+ id uint
+ policy *Policy
sessionOnly bool
rtype int
hostname string
@@ -32,10 +34,17 @@ type Rule struct {
}
func (r *Rule) String() string {
- addr := "*"
- port := "*"
rtype := "DENY"
+ if r.rtype == RULE_ALLOW {
+ rtype = "ALLOW"
+ }
+ return fmt.Sprintf("%s|%s", rtype, r.AddrString())
+}
+
+func (r *Rule) AddrString() string {
+ addr := "*"
+ port := "*"
if r.hostname != "" {
addr = r.hostname
} else if r.addr != matchAny && r.addr != noAddress {
@@ -48,11 +57,7 @@ func (r *Rule) String() string {
port = fmt.Sprintf("%d", r.port)
}
- if r.rtype == RULE_ALLOW {
- rtype = "ALLOW"
- }
-
- return fmt.Sprintf("%s|%s:%s", rtype, addr, port)
+ return fmt.Sprintf("%s:%s", addr, port)
}
type RuleList []*Rule
@@ -154,25 +159,17 @@ func (r *Rule) parsePort(p string) bool {
}
var err error
port, err := strconv.ParseUint(p, 10, 16)
- if err != nil {
+ if err != nil || port == 0 || port > 0xFFFF {
return false
}
r.port = uint16(port)
return true
}
-func parseRule(s string) (*Rule, error) {
- r := new(Rule)
- if !r.parse(s) {
- return nil, parseError(s)
- }
- return r, nil
-}
-
const ruleFile = "/var/lib/sgfw/sgfw_rules"
func maybeCreateDir(dir string) error {
- _,err := os.Stat(dir)
+ _, err := os.Stat(dir)
if os.IsNotExist(err) {
return os.MkdirAll(dir, 0755)
}
@@ -190,7 +187,7 @@ func (fw *Firewall) saveRules() {
fw.lock.Lock()
defer fw.lock.Unlock()
- p,err := rulesPath()
+ p, err := rulesPath()
if err != nil {
log.Warning("Failed to open %s for writing: %v", p, err)
return
@@ -239,7 +236,9 @@ func (fw *Firewall) loadRules() {
fw.lock.Lock()
defer fw.lock.Unlock()
- p,err := rulesPath()
+ fw.clearRules()
+
+ p, err := rulesPath()
if err != nil {
log.Warning("Failed to open %s for reading: %v", p, err)
return
@@ -275,12 +274,9 @@ func processRuleLine(policy *Policy, line string) {
log.Warning("Cannot process rule line without first seeing path line: %s", line)
return
}
- rule, err := parseRule(line)
+ _, err := policy.parseRule(line, true)
if err != nil {
log.Warning("Error parsing rule (%s): %v", line, err)
return
}
- policy.lock.Lock()
- defer policy.lock.Unlock()
- policy.rules = append(policy.rules, rule)
}