// apparmor.d - Full set of apparmor profiles // Copyright (C) 2021-2024 Alexandre Pujol // SPDX-License-Identifier: GPL-2.0-only // Dbus directive // // Example of supported directive: // #aa:dbus own bus=session name=org.freedesktop.FileManager1 // #aa:dbus talk name=org.freedesktop.login1 label=systemd-logind // // See https://apparmor.pujol.io/development/dbus/#dbus-directive // package directive import ( "fmt" "strings" "github.com/roddhjav/apparmor.d/pkg/aa" "github.com/roddhjav/apparmor.d/pkg/prebuild/cfg" ) var defaultInterfaces = []string{ "org.freedesktop.DBus.Properties", "org.freedesktop.DBus.ObjectManager", } type Dbus struct { cfg.Base } func init() { RegisterDirective(&Dbus{ Base: cfg.Base{ Keyword: "dbus", Msg: "Dbus directive applied", Help: []string{ "own bus= name= [interface=AARE] [path=AARE]", "talk bus= name= label= [interface=AARE] [path=AARE]", }, }}, ) } func setInterfaces(rules map[string]string) []string { interfaces := []string{rules["name"]} if _, present := rules["interface"]; present { interfaces = append(interfaces, rules["interface"]) } interfaces = append(interfaces, defaultInterfaces...) return interfaces } func (d Dbus) Apply(opt *Option, profile string) (string, error) { var r aa.Rules action, err := d.sanityCheck(opt) if err != nil { return "", err } switch action { case "own": r = d.own(opt.ArgMap) case "talk": r = d.talk(opt.ArgMap) } aa.IndentationLevel = strings.Count( strings.SplitN(opt.Raw, Keyword, 1)[0], aa.Indentation, ) generatedDbus := r.String() lenDbus := len(generatedDbus) generatedDbus = generatedDbus[:lenDbus-1] profile = strings.Replace(profile, opt.Raw, generatedDbus, -1) return profile, nil } func (d Dbus) sanityCheck(opt *Option) (string, error) { if len(opt.ArgList) < 1 { return "", fmt.Errorf("Unknown dbus action: %s in %s", opt.Name, opt.File) } action := opt.ArgList[0] if action != "own" && action != "talk" { return "", fmt.Errorf("Unknown dbus action: %s in %s", opt.Name, opt.File) } if _, present := opt.ArgMap["name"]; !present { return "", fmt.Errorf("Missing name for 'dbus: %s' in %s", action, opt.File) } if _, present := opt.ArgMap["bus"]; !present { return "", fmt.Errorf("Missing bus for '%s' in %s", opt.ArgMap["name"], opt.File) } if _, present := opt.ArgMap["label"]; !present && action == "talk" { return "", fmt.Errorf("Missing label for '%s' in %s", opt.ArgMap["name"], opt.File) } // Set default values if _, present := opt.ArgMap["path"]; !present { opt.ArgMap["path"] = "/" + strings.Replace(opt.ArgMap["name"], ".", "/", -1) + "{,/**}" } opt.ArgMap["name"] += "{,.*}" return action, nil } func (d Dbus) own(rules map[string]string) aa.Rules { interfaces := setInterfaces(rules) res := aa.Rules{} res = append(res, &aa.Dbus{ Access: []string{"bind"}, Bus: rules["bus"], Name: rules["name"], }) for _, iface := range interfaces { res = append(res, &aa.Dbus{ Access: []string{"receive"}, Bus: rules["bus"], Path: rules["path"], Interface: iface, PeerName: `":1.@{int}"`, }) } for _, iface := range interfaces { res = append(res, &aa.Dbus{ Access: []string{"send"}, Bus: rules["bus"], Path: rules["path"], Interface: iface, PeerName: `"{:1.@{int},org.freedesktop.DBus}"`, }) } res = append(res, &aa.Dbus{ Access: []string{"receive"}, Bus: rules["bus"], Path: rules["path"], Interface: "org.freedesktop.DBus.Introspectable", Member: "Introspect", PeerName: `":1.@{int}"`, }) return res } func (d Dbus) talk(rules map[string]string) aa.Rules { interfaces := setInterfaces(rules) res := aa.Rules{} for _, iface := range interfaces { res = append(res, &aa.Dbus{ Access: []string{"send"}, Bus: rules["bus"], Path: rules["path"], Interface: iface, PeerName: `"{:1.@{int},` + rules["name"] + `}"`, PeerLabel: rules["label"], }) } for _, iface := range interfaces { res = append(res, &aa.Dbus{ Access: []string{"receive"}, Bus: rules["bus"], Path: rules["path"], Interface: iface, PeerName: `"{:1.@{int},` + rules["name"] + `}"`, PeerLabel: rules["label"], }) } return res }