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-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":
|
|
|
|
if !slices.Contains(p.Flags, "attach_disconnected") {
|
|
|
|
p.Flags = append(p.Flags, "attach_disconnected")
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
|
|
|
|
switch log["class"] {
|
|
|
|
case "cap":
|
|
|
|
p.Capability = append(p.Capability, NewCapability(log, noNewPrivs, fileInherit))
|
|
|
|
case "file":
|
|
|
|
p.File = append(p.File, NewFile(log, noNewPrivs, fileInherit))
|
|
|
|
case "net":
|
|
|
|
if log["family"] == "unix" {
|
|
|
|
p.Unix = append(p.Unix, NewUnix(log, noNewPrivs, fileInherit))
|
|
|
|
} else {
|
|
|
|
p.Network = append(p.Network, NewNetwork(log, noNewPrivs, fileInherit))
|
|
|
|
}
|
|
|
|
case "signal":
|
|
|
|
p.Signal = append(p.Signal, NewSignal(log, noNewPrivs, fileInherit))
|
|
|
|
case "ptrace":
|
|
|
|
p.Ptrace = append(p.Ptrace, NewPtrace(log, noNewPrivs, fileInherit))
|
|
|
|
case "unix":
|
|
|
|
p.Unix = append(p.Unix, NewUnix(log, noNewPrivs, fileInherit))
|
|
|
|
case "mount":
|
|
|
|
p.Mount = append(p.Mount, NewMount(log, noNewPrivs, fileInherit))
|
|
|
|
default:
|
|
|
|
if strings.Contains(log["operation"], "dbus") {
|
|
|
|
p.Dbus = append(p.Dbus, NewDbus(log, noNewPrivs, fileInherit))
|
|
|
|
} else if log["family"] == "unix" {
|
|
|
|
p.Unix = append(p.Unix, NewUnix(log, noNewPrivs, fileInherit))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|