package main
import (
"errors"
"fmt"
"strings"
"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 = `
` +
introspect.IntrospectDataString +
``
const busName = "com.subgraph.Firewall"
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 {
return nil, err
}
reply, err := conn.RequestName(busName, dbus.NameFlagDoNotQueue)
if err != nil {
return nil, err
}
if reply != dbus.RequestNameReplyPrimaryOwner {
return nil, errors.New("Bus name is already owned")
}
ds := &dbusServer{}
if err := conn.Export(ds, objectPath, interfaceName); err != nil {
return nil, err
}
ps := strings.Split(objectPath, "/")
path := "/"
for _, p := range ps {
if len(path) > 1 {
path += "/"
}
path += p
if err := conn.Export(ds, dbus.ObjectPath(path), "org.freedesktop.DBus.Introspectable"); err != nil {
return nil, err
}
}
ds.conn = conn
ds.prompter = newPrompter(conn)
return ds, nil
}
func (ds *dbusServer) Introspect(msg dbus.Message) (string, *dbus.Error) {
path := string(msg.Headers[dbus.FieldPath].Value().(dbus.ObjectPath))
if path == objectPath {
return introspectXml, nil
}
parts := strings.Split(objectPath, "/")
current := "/"
for i := 0; i < len(parts)-1; i++ {
if len(current) > 1 {
current += "/"
}
current += parts[i]
if path == current {
next := parts[i+1]
return fmt.Sprintf("", next), nil
}
}
return "", nil
}
func (ds *dbusServer) SetEnabled(flag bool) *dbus.Error {
log.Debug("SetEnabled(%v) called", flag)
ds.fw.setEnabled(flag)
return nil
}
func (ds *dbusServer) IsEnabled() (bool, *dbus.Error) {
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(false),
}
}
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(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)
logRedact = flag
}
return nil
}
func (ds *dbusServer) prompt(p *Policy) {
log.Info("prompting...")
ds.prompter.prompt(p)
}