package main import ( "errors" "fmt" "path" "strings" "github.com/godbus/dbus" "github.com/godbus/dbus/introspect" "github.com/op/go-logging" ) 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) }