2023-04-19 18:40:40 +02:00
|
|
|
// apparmor.d - Full set of apparmor profiles
|
|
|
|
// Copyright (C) 2023 Alexandre Pujol <alexandre@pujol.io>
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
|
|
|
package aa
|
2023-08-18 00:12:46 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"golang.org/x/exp/slices"
|
|
|
|
)
|
|
|
|
|
2023-08-18 00:00:52 +02:00
|
|
|
// AppArmorProfiles represents a full set of apparmor profiles
|
|
|
|
type AppArmorProfiles map[string]*AppArmorProfile
|
2023-04-19 18:40:40 +02:00
|
|
|
|
2023-08-18 00:00:52 +02:00
|
|
|
// ApparmorProfile represents a full apparmor profile.
|
|
|
|
// Warning: close to the BNF grammar of apparmor profile but not exactly the same (yet):
|
|
|
|
// - Some rules are not supported yet (subprofile, hat...)
|
2023-09-01 20:26:52 +02:00
|
|
|
// - The structure is simplified as it only aims at writing profile, not parsing it.
|
2023-04-19 18:40:40 +02:00
|
|
|
type AppArmorProfile struct {
|
2023-08-18 00:00:52 +02:00
|
|
|
Preamble
|
|
|
|
Profile
|
2023-04-19 18:40:40 +02:00
|
|
|
}
|
|
|
|
|
2023-09-25 01:06:07 +02:00
|
|
|
// Preamble section of a profile
|
|
|
|
type Preamble struct {
|
|
|
|
Abi []Abi
|
|
|
|
Includes []Include
|
|
|
|
Aliases []Alias
|
|
|
|
Variables []Variable
|
|
|
|
}
|
|
|
|
|
|
|
|
// Profile section of a profile
|
|
|
|
type Profile struct {
|
|
|
|
Name string
|
|
|
|
Attachments []string
|
|
|
|
Attributes map[string]string
|
|
|
|
Flags []string
|
|
|
|
Rules Rules
|
|
|
|
}
|
|
|
|
|
|
|
|
// ApparmorRule generic interface
|
|
|
|
type ApparmorRule interface {
|
|
|
|
Less(other any) bool
|
|
|
|
Equals(other any) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
type Rules []ApparmorRule
|
|
|
|
|
2023-07-25 23:01:07 +02:00
|
|
|
func NewAppArmorProfile() *AppArmorProfile {
|
2023-08-18 00:00:52 +02:00
|
|
|
return &AppArmorProfile{}
|
2023-04-19 18:40:40 +02:00
|
|
|
}
|
2023-08-18 00:11:11 +02:00
|
|
|
|
|
|
|
// String returns the formatted representation of a profile as a string
|
|
|
|
func (p *AppArmorProfile) String() string {
|
|
|
|
var res bytes.Buffer
|
|
|
|
err := tmplAppArmorProfile.Execute(&res, p)
|
|
|
|
if err != nil {
|
|
|
|
return err.Error()
|
|
|
|
}
|
|
|
|
return res.String()
|
|
|
|
}
|
|
|
|
|
2023-08-18 00:12:46 +02:00
|
|
|
// AddRule adds a new rule to the profile from a log map
|
|
|
|
func (p *AppArmorProfile) AddRule(log map[string]string) {
|
|
|
|
noNewPrivs := false
|
|
|
|
fileInherit := false
|
|
|
|
if log["operation"] == "file_inherit" {
|
|
|
|
fileInherit = true
|
|
|
|
}
|
|
|
|
|
|
|
|
switch log["error"] {
|
|
|
|
case "-1":
|
|
|
|
noNewPrivs = true
|
|
|
|
case "-2":
|
|
|
|
if !slices.Contains(p.Flags, "mediate_deleted") {
|
|
|
|
p.Flags = append(p.Flags, "mediate_deleted")
|
|
|
|
}
|
|
|
|
case "-13":
|
2023-09-25 01:10:12 +02:00
|
|
|
// FIXME: -13 can be a lot of things, not only attach_disconnected
|
|
|
|
// Eg: info="User namespace creation restricted"
|
2023-08-18 00:12:46 +02:00
|
|
|
if !slices.Contains(p.Flags, "attach_disconnected") {
|
|
|
|
p.Flags = append(p.Flags, "attach_disconnected")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
switch log["class"] {
|
|
|
|
case "cap":
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, CapabilityFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
case "net":
|
|
|
|
if log["family"] == "unix" {
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, UnixFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
} else {
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, NetworkFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
}
|
2023-09-25 01:10:12 +02:00
|
|
|
case "mount":
|
|
|
|
p.Rules = append(p.Rules, MountFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "remount":
|
|
|
|
p.Rules = append(p.Rules, RemountFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "umount":
|
|
|
|
p.Rules = append(p.Rules, UmountFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "pivot_root":
|
|
|
|
p.Rules = append(p.Rules, PivotRootFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "change_profile":
|
|
|
|
p.Rules = append(p.Rules, RemountFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "mqueue":
|
|
|
|
p.Rules = append(p.Rules, MqueueFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
case "signal":
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, SignalFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
case "ptrace":
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, PtraceFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "namespace":
|
|
|
|
p.Rules = append(p.Rules, UsernsFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
case "unix":
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, UnixFromLog(log, noNewPrivs, fileInherit))
|
|
|
|
case "file":
|
|
|
|
p.Rules = append(p.Rules, FileFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
default:
|
|
|
|
if strings.Contains(log["operation"], "dbus") {
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, DbusFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
} else if log["family"] == "unix" {
|
2023-09-25 01:10:12 +02:00
|
|
|
p.Rules = append(p.Rules, UnixFromLog(log, noNewPrivs, fileInherit))
|
2023-08-18 00:12:46 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|