From 99e386705fd040581c4d157c52d5500db0bfaad8 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 21 Mar 2024 22:09:16 +0000 Subject: [PATCH] feat(build): rewrite the dbus directive fot the new format. --- pkg/prebuild/directive/core_test.go | 29 ++++++ pkg/prebuild/directive/dbus.go | 154 ++++++++++++++++++++++++++++ pkg/prebuild/directive/dbus_test.go | 142 +++++++++++++++++++++++++ 3 files changed, 325 insertions(+) create mode 100644 pkg/prebuild/directive/dbus.go create mode 100644 pkg/prebuild/directive/dbus_test.go diff --git a/pkg/prebuild/directive/core_test.go b/pkg/prebuild/directive/core_test.go index f07e3e33..fe05dc17 100644 --- a/pkg/prebuild/directive/core_test.go +++ b/pkg/prebuild/directive/core_test.go @@ -61,3 +61,32 @@ func TestNewOption(t *testing.T) { }) } } + +func TestRun(t *testing.T) { + tests := []struct { + name string + file *paths.Path + profile string + want string + }{ + { + name: "none", + file: nil, + profile: ` `, + want: ` `, + }, + { + name: "present", + file: nil, + profile: ` #aa:dbus own bus=system name=org.freedesktop.systemd1`, + want: dbusOwnSystemd1, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Run(tt.file, tt.profile); got != tt.want { + t.Errorf("Run() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/prebuild/directive/dbus.go b/pkg/prebuild/directive/dbus.go new file mode 100644 index 00000000..3e9c289d --- /dev/null +++ b/pkg/prebuild/directive/dbus.go @@ -0,0 +1,154 @@ +// 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" +) + +var defaultInterfaces = []string{ + "org.freedesktop.DBus.Properties", + "org.freedesktop.DBus.ObjectManager", +} + +type Dbus struct { + DirectiveBase +} + +func init() { + Directives["dbus"] = &Dbus{ + DirectiveBase: DirectiveBase{ + message: "Dbus directive applied", + usage: `#aa:dbus own bus=(system | session) name= + #aa:dbus talk bus=(system | session) name= label=`, + }, + } +} + +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 { + var p *aa.AppArmorProfile + var action string + if _, ok := opt.Args["own"]; ok { + action = "own" + } else if _, ok := opt.Args["talk"]; ok { + action = "talk" + } else { + panic(fmt.Sprintf("Unknown dbus action: %s in %s", opt.Name, opt.File)) + } + + d.sanityCheck(action, opt) + switch action { + case "own": + p = d.own(opt.Args) + case "talk": + p = d.talk(opt.Args) + } + + generatedDbus := p.String() + lenDbus := len(generatedDbus) + generatedDbus = generatedDbus[:lenDbus-1] + profile = strings.Replace(profile, opt.Raw, generatedDbus, -1) + return profile +} + +func (d Dbus) sanityCheck(action string, opt *Option) { + if _, present := opt.Args["name"]; !present { + panic(fmt.Sprintf("Missing name for 'dbus: %s' in %s", action, opt.File)) + } + if _, present := opt.Args["bus"]; !present { + panic(fmt.Sprintf("Missing bus for '%s' in %s", opt.Args["name"], opt.File)) + } + if _, present := opt.Args["label"]; !present && action == "talk" { + panic(fmt.Sprintf("Missing label for '%s' in %s", opt.Args["name"], opt.File)) + } + + // Set default values + if _, present := opt.Args["path"]; !present { + opt.Args["path"] = "/" + strings.Replace(opt.Args["name"], ".", "/", -1) + "{,/**}" + } + opt.Args["name"] += "{,.*}" +} + +func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile { + interfaces := setInterfaces(rules) + p := &aa.AppArmorProfile{} + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "bind", Bus: rules["bus"], Name: rules["name"], + }) + for _, iface := range interfaces { + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "receive", + Bus: rules["bus"], + Path: rules["path"], + Interface: iface, + Name: `":1.@{int}"`, + }) + } + for _, iface := range interfaces { + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "send", + Bus: rules["bus"], + Path: rules["path"], + Interface: iface, + Name: `"{:1.@{int},org.freedesktop.DBus}"`, + }) + } + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "receive", + Bus: rules["bus"], + Path: rules["path"], + Interface: "org.freedesktop.DBus.Introspectable", + Member: "Introspect", + Name: `":1.@{int}"`, + }) + return p +} + +func (d Dbus) talk(rules map[string]string) *aa.AppArmorProfile { + interfaces := setInterfaces(rules) + p := &aa.AppArmorProfile{} + for _, iface := range interfaces { + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "send", + Bus: rules["bus"], + Path: rules["path"], + Interface: iface, + Name: `"{:1.@{int},` + rules["name"] + `}"`, + Label: rules["label"], + }) + } + for _, iface := range interfaces { + p.Rules = append(p.Rules, &aa.Dbus{ + Access: "receive", + Bus: rules["bus"], + Path: rules["path"], + Interface: iface, + Name: `"{:1.@{int},` + rules["name"] + `}"`, + Label: rules["label"], + }) + } + return p +} diff --git a/pkg/prebuild/directive/dbus_test.go b/pkg/prebuild/directive/dbus_test.go new file mode 100644 index 00000000..9d04ecb3 --- /dev/null +++ b/pkg/prebuild/directive/dbus_test.go @@ -0,0 +1,142 @@ +// apparmor.d - Full set of apparmor profiles +// Copyright (C) 2021-2024 Alexandre Pujol +// SPDX-License-Identifier: GPL-2.0-only + +package directive + +import ( + "testing" +) + +const dbusOwnSystemd1 = ` dbus bind bus=system name=org.freedesktop.systemd1{,.*}, + dbus receive bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.systemd1{,.*} + peer=(name=":1.@{int}"), + dbus receive bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name=":1.@{int}"), + dbus receive bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name=":1.@{int}"), + dbus send bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.systemd1{,.*} + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus send bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus send bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus receive bus=system path=/org/freedesktop/systemd1{,/**} + interface=org.freedesktop.DBus.Introspectable + member=Introspect + peer=(name=":1.@{int}"),` + +func TestDbus_Apply(t *testing.T) { + tests := []struct { + name string + opt *Option + profile string + want string + }{ + { + name: "own", + opt: &Option{ + Name: "dbus", + Args: map[string]string{ + "bus": "system", + "name": "org.freedesktop.systemd1", + "own": "", + }, + File: nil, + Raw: " #aa:dbus own bus=system name=org.freedesktop.systemd1", + }, + profile: " #aa:dbus own bus=system name=org.freedesktop.systemd1", + want: dbusOwnSystemd1, + }, + { + name: "own-interface", + opt: &Option{ + Name: "dbus", + Args: map[string]string{ + "bus": "session", + "name": "com.rastersoft.dingextension", + "interface": "org.gtk.Actions", + "own": "", + }, + File: nil, + Raw: " #aa:dbus own bus=session name=com.rastersoft.dingextension interface=org.gtk.Actions", + }, + profile: " #aa:dbus own bus=session name=com.rastersoft.dingextension interface=org.gtk.Actions", + want: ` dbus bind bus=session name=com.rastersoft.dingextension{,.*}, + dbus receive bus=session path=/com/rastersoft/dingextension{,/**} + interface=com.rastersoft.dingextension{,.*} + peer=(name=":1.@{int}"), + dbus receive bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.gtk.Actions + peer=(name=":1.@{int}"), + dbus receive bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name=":1.@{int}"), + dbus receive bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name=":1.@{int}"), + dbus send bus=session path=/com/rastersoft/dingextension{,/**} + interface=com.rastersoft.dingextension{,.*} + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus send bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.gtk.Actions + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus send bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus send bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name="{:1.@{int},org.freedesktop.DBus}"), + dbus receive bus=session path=/com/rastersoft/dingextension{,/**} + interface=org.freedesktop.DBus.Introspectable + member=Introspect + peer=(name=":1.@{int}"),`, + }, + { + name: "talk", + opt: &Option{ + Name: "dbus", + Args: map[string]string{ + "bus": "system", + "name": "org.freedesktop.Accounts", + "label": "accounts-daemon", + "talk": "", + }, + File: nil, + Raw: " #aa:dbus talk bus=system name=org.freedesktop.Accounts label=accounts-daemon", + }, + profile: " #aa:dbus talk bus=system name=org.freedesktop.Accounts label=accounts-daemon", + want: ` dbus send bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.Accounts{,.*} + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon), + dbus send bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon), + dbus send bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon), + dbus receive bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.Accounts{,.*} + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon), + dbus receive bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.DBus.Properties + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon), + dbus receive bus=system path=/org/freedesktop/Accounts{,/**} + interface=org.freedesktop.DBus.ObjectManager + peer=(name="{:1.@{int},org.freedesktop.Accounts{,.*}}", label=accounts-daemon),`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Directives["dbus"].Apply(tt.opt, tt.profile); got != tt.want { + t.Errorf("Dbus.Apply() = %v, want %v", got, tt.want) + } + }) + } +}