2023-12-15 23:11:25 +01:00
|
|
|
// apparmor.d - Full set of apparmor profiles
|
2024-02-07 00:16:21 +01:00
|
|
|
// Copyright (C) 2023-2024 Alexandre Pujol <alexandre@pujol.io>
|
2023-12-15 23:11:25 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
|
|
|
package prebuild
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/arduino/go-paths-helper"
|
|
|
|
"github.com/roddhjav/apparmor.d/pkg/aa"
|
2024-03-10 15:46:34 +01:00
|
|
|
"github.com/roddhjav/apparmor.d/pkg/util"
|
2023-12-15 23:11:25 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// Build the profiles with the following directive applied
|
|
|
|
var (
|
|
|
|
Directives = []DirectiveFunc{
|
|
|
|
DirectiveDbus,
|
2024-03-10 15:46:34 +01:00
|
|
|
DirectiveStack,
|
2023-12-15 23:11:25 +01:00
|
|
|
}
|
|
|
|
DirectiveMsg = map[string]string{
|
2024-03-10 15:46:34 +01:00
|
|
|
"DirectiveDbus": "DBus directive applied",
|
|
|
|
"DirectiveStack": "Stack directive applied",
|
2023-12-15 23:11:25 +01:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-03-10 15:46:34 +01:00
|
|
|
regDbus = regexp.MustCompile(`(?m) # dbus: (.*)$`)
|
|
|
|
regStack = regexp.MustCompile(`(?m) # stack: (.*)$`)
|
|
|
|
regRules = regexp.MustCompile(`(?m)^profile.*{$((.|\n)*)}`)
|
|
|
|
regEndOfRules = regexp.MustCompile(`(?m)([\t ]*include if exists <.*>\n)+}`)
|
|
|
|
regCleanStakedRules = util.ToRegexRepl([]string{
|
|
|
|
`(?m)^.*include <abstractions/base>.*$`, ``,
|
|
|
|
`(?m)^.*@{exec_path}.*$`, ``,
|
|
|
|
`(?m)^.*(|P|p)(|U|u)(|i)x,.*$`, ``,
|
|
|
|
`(?m)^(?:[\t ]*(?:\r?\n))+`, ``,
|
|
|
|
})
|
2023-12-15 23:11:25 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
type DirectiveFunc func(*paths.Path, string) string
|
|
|
|
|
|
|
|
// Apply dbus directive
|
|
|
|
//
|
|
|
|
// Example of supported dbus directive:
|
|
|
|
// # dbus: own bus=session name=org.freedesktop.FileManager1
|
|
|
|
// # dbus: talk name=org.freedesktop.login1 label=systemd-logind
|
|
|
|
func DirectiveDbus(file *paths.Path, profile string) string {
|
|
|
|
var p *aa.AppArmorProfile
|
|
|
|
for _, match := range regDbus.FindAllStringSubmatch(profile, -1) {
|
|
|
|
origin := match[0]
|
|
|
|
rules := map[string]string{}
|
|
|
|
|
|
|
|
fields := strings.Fields(match[1])
|
|
|
|
action, fields := fields[0], fields[1:]
|
|
|
|
for _, t := range fields {
|
|
|
|
tmp := strings.Split(t, "=")
|
|
|
|
rules[tmp[0]] = tmp[1]
|
|
|
|
}
|
|
|
|
|
|
|
|
rules = sanitizeDbusRule(file, action, rules)
|
|
|
|
switch action {
|
|
|
|
case "own":
|
|
|
|
p = dbusOwn(rules)
|
|
|
|
case "talk":
|
|
|
|
p = dbusTalk(rules)
|
|
|
|
default:
|
|
|
|
panic(fmt.Sprintf("Unknown dbus directive: %s in %s", action, file))
|
|
|
|
}
|
|
|
|
|
|
|
|
generatedDbus := p.String()
|
|
|
|
lenDbus := len(generatedDbus)
|
2024-02-24 18:02:43 +01:00
|
|
|
generatedDbus = generatedDbus[:lenDbus-1]
|
2023-12-15 23:11:25 +01:00
|
|
|
profile = strings.Replace(profile, origin, generatedDbus, -1)
|
|
|
|
}
|
|
|
|
return profile
|
|
|
|
}
|
|
|
|
|
|
|
|
func sanitizeDbusRule(file *paths.Path, action string, rules map[string]string) map[string]string {
|
|
|
|
// Sanity check
|
|
|
|
if _, present := rules["name"]; !present {
|
2024-02-15 00:22:01 +01:00
|
|
|
panic(fmt.Sprintf("Missing name for 'dbus: %s' in %s", action, file))
|
2023-12-15 23:11:25 +01:00
|
|
|
}
|
|
|
|
if _, present := rules["bus"]; !present {
|
|
|
|
panic(fmt.Sprintf("Missing bus for '%s' in %s", rules["name"], file))
|
|
|
|
}
|
|
|
|
if _, present := rules["label"]; !present && action == "talk" {
|
|
|
|
panic(fmt.Sprintf("Missing label for '%s' in %s", rules["name"], file))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set default values
|
|
|
|
if _, present := rules["path"]; !present {
|
|
|
|
rules["path"] = "/" + strings.Replace(rules["name"], ".", "/", -1) + "{,/**}"
|
|
|
|
}
|
|
|
|
if _, present := rules["interface"]; !present {
|
|
|
|
rules["interface"] = "org.freedesktop.DBus.{Properties,ObjectManager}"
|
|
|
|
}
|
|
|
|
rules["name"] += "{,.*}"
|
|
|
|
return rules
|
|
|
|
}
|
|
|
|
|
|
|
|
func dbusOwn(rules map[string]string) *aa.AppArmorProfile {
|
|
|
|
interfaces := []string{rules["name"], rules["interface"]}
|
|
|
|
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}"`,
|
|
|
|
})
|
|
|
|
}
|
2024-02-15 00:22:01 +01:00
|
|
|
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}"`,
|
|
|
|
})
|
2023-12-15 23:11:25 +01:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
|
|
|
func dbusTalk(rules map[string]string) *aa.AppArmorProfile {
|
|
|
|
interfaces := []string{rules["name"], rules["interface"]}
|
|
|
|
p := &aa.AppArmorProfile{}
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
p.Rules = append(p.Rules, &aa.Dbus{
|
|
|
|
Access: "send",
|
|
|
|
Bus: rules["bus"],
|
|
|
|
Path: rules["path"],
|
|
|
|
Interface: iface,
|
2024-02-15 00:22:01 +01:00
|
|
|
Name: `"{:1.@{int},` + rules["name"] + `}"`,
|
2023-12-15 23:11:25 +01:00
|
|
|
Label: rules["label"],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
for _, iface := range interfaces {
|
|
|
|
p.Rules = append(p.Rules, &aa.Dbus{
|
|
|
|
Access: "receive",
|
|
|
|
Bus: rules["bus"],
|
|
|
|
Path: rules["path"],
|
|
|
|
Interface: iface,
|
2024-02-15 00:22:01 +01:00
|
|
|
Name: `"{:1.@{int},` + rules["name"] + `}"`,
|
2023-12-15 23:11:25 +01:00
|
|
|
Label: rules["label"],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return p
|
|
|
|
}
|
2024-03-10 15:46:34 +01:00
|
|
|
|
|
|
|
// Apply stack directive
|
|
|
|
//
|
|
|
|
// The stacked profiles are appended at the end of the current profile.
|
|
|
|
// Arguments: list of profiles to stack on the current profile
|
|
|
|
// Example of supported dbus directive:
|
|
|
|
// # stack: pipewire pipewire-media-session
|
|
|
|
func DirectiveStack(file *paths.Path, profile string) string {
|
|
|
|
for _, match := range regStack.FindAllStringSubmatch(profile, -1) {
|
|
|
|
res := ""
|
|
|
|
origin := match[0]
|
|
|
|
profilesToStack := strings.Fields(match[1])
|
|
|
|
for _, name := range profilesToStack {
|
|
|
|
tmp, err := RootApparmord.Join(name).ReadFile()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
stackedProfile := string(tmp)
|
|
|
|
|
|
|
|
m := regRules.FindStringSubmatch(stackedProfile)
|
|
|
|
if len(m) < 2 {
|
|
|
|
panic(fmt.Sprintf("No profile found in %s", name))
|
|
|
|
}
|
|
|
|
stackedRules := m[1]
|
2024-03-10 16:53:25 +01:00
|
|
|
stackedRules = regCleanStakedRules.Replace(stackedRules)
|
2024-03-10 15:46:34 +01:00
|
|
|
res += " # Stacked profile: " + name + "\n" + stackedRules + "\n"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert the stacked profile at the end of the current profile, remove the stack directive
|
|
|
|
m := regEndOfRules.FindStringSubmatch(profile)
|
|
|
|
if len(m) <= 1 {
|
|
|
|
panic(fmt.Sprintf("No end of rules found in %s", file))
|
|
|
|
}
|
|
|
|
profile = strings.Replace(profile, m[0], res+m[0], -1)
|
|
|
|
profile = strings.Replace(profile, origin, "", -1)
|
|
|
|
}
|
|
|
|
return profile
|
|
|
|
}
|