feat(aa): add the Kind struct to manage aa rules.

This commit is contained in:
Alexandre Pujol 2024-05-28 18:15:22 +01:00
parent 1333ec2025
commit 3b0944c615
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
23 changed files with 239 additions and 223 deletions

View File

@ -24,7 +24,7 @@ func newRule(rule []string) RuleBase {
idx := 0
for idx < len(rule) {
if rule[idx] == tokCOMMENT {
if rule[idx] == COMMENT.Tok() {
comment = " " + strings.Join(rule[idx+1:], " ")
break
}
@ -85,15 +85,15 @@ func (r RuleBase) Equals(other any) bool {
}
func (r RuleBase) String() string {
return renderTemplate("comment", r)
return renderTemplate(r.Kind(), r)
}
func (r RuleBase) Constraint() constraint {
return anyKind
}
func (r RuleBase) Kind() string {
return "base"
func (r RuleBase) Kind() Kind {
return COMMENT
}
type Qualifier struct {

View File

@ -5,8 +5,7 @@
package aa
const (
tokHAT = "hat"
tokCARET = "^"
HAT Kind = "hat"
)
// Hat represents a single AppArmor hat.
@ -26,7 +25,7 @@ func (p *Hat) Less(other any) bool {
}
func (p *Hat) Equals(other any) bool {
o, _ := other.(*Profile)
o, _ := other.(*Hat)
return p.Name == o.Name
}
@ -38,6 +37,6 @@ func (p *Hat) Constraint() constraint {
return blockKind
}
func (p *Hat) Kind() string {
return tokHAT
func (p *Hat) Kind() Kind {
return HAT
}

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokCAPABILITY = "capability"
const CAPABILITY Kind = "capability"
func init() {
requirements[tokCAPABILITY] = requirement{
requirements[CAPABILITY] = requirement{
"name": {
"audit_control", "audit_read", "audit_write", "block_suspend", "bpf",
"checkpoint_restore", "chown", "dac_override", "dac_read_search",
@ -36,7 +36,7 @@ func newCapabilityFromLog(log map[string]string) Rule {
return &Capability{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Names: Must(toValues(tokCAPABILITY, "name", log["capname"])),
Names: Must(toValues(CAPABILITY, "name", log["capname"])),
}
}
@ -70,6 +70,6 @@ func (r *Capability) Constraint() constraint {
return blockKind
}
func (r *Capability) Kind() string {
return tokCAPABILITY
func (r *Capability) Kind() Kind {
return CAPABILITY
}

View File

@ -6,10 +6,10 @@ package aa
import "fmt"
const tokCHANGEPROFILE = "change_profile"
const CHANGEPROFILE Kind = "change_profile"
func init() {
requirements[tokCHANGEPROFILE] = requirement{
requirements[CHANGEPROFILE] = requirement{
"mode": []string{"safe", "unsafe"},
}
}
@ -67,6 +67,6 @@ func (r *ChangeProfile) Constraint() constraint {
return blockKind
}
func (r *ChangeProfile) Kind() string {
return tokCHANGEPROFILE
func (r *ChangeProfile) Kind() Kind {
return CHANGEPROFILE
}

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokDBUS = "dbus"
const DBUS Kind = "dbus"
func init() {
requirements[tokDBUS] = requirement{
requirements[DBUS] = requirement{
"access": []string{
"send", "receive", "bind", "eavesdrop", "r", "read",
"w", "write", "rw",
@ -110,6 +110,6 @@ func (r *Dbus) Constraint() constraint {
return blockKind
}
func (r *Dbus) Kind() string {
return tokDBUS
func (r *Dbus) Kind() Kind {
return DBUS
}

View File

@ -11,14 +11,14 @@ import (
)
const (
tokLINK = "link"
tokFILE = "file"
tokOWNER = "owner"
tokSUBSET = "subset"
LINK Kind = "link"
FILE Kind = "file"
tokOWNER = "owner"
tokSUBSET = "subset"
)
func init() {
requirements[tokFILE] = requirement{
requirements[FILE] = requirement{
"access": {"m", "r", "w", "l", "k"},
"transition": {
"ix", "ux", "Ux", "px", "Px", "cx", "Cx", "pix", "Pix", "cix",
@ -40,15 +40,15 @@ func isOwner(log map[string]string) bool {
// cmpFileAccess compares two access strings for file rules.
// It is aimed to be used in slices.SortFunc.
func cmpFileAccess(i, j string) int {
if slices.Contains(requirements[tokFILE]["access"], i) &&
slices.Contains(requirements[tokFILE]["access"], j) {
return requirementsWeights[tokFILE]["access"][i] - requirementsWeights[tokFILE]["access"][j]
if slices.Contains(requirements[FILE]["access"], i) &&
slices.Contains(requirements[FILE]["access"], j) {
return requirementsWeights[FILE]["access"][i] - requirementsWeights[FILE]["access"][j]
}
if slices.Contains(requirements[tokFILE]["transition"], i) &&
slices.Contains(requirements[tokFILE]["transition"], j) {
return requirementsWeights[tokFILE]["transition"][i] - requirementsWeights[tokFILE]["transition"][j]
if slices.Contains(requirements[FILE]["transition"], i) &&
slices.Contains(requirements[FILE]["transition"], j) {
return requirementsWeights[FILE]["transition"][i] - requirementsWeights[FILE]["transition"][j]
}
if slices.Contains(requirements[tokFILE]["access"], i) {
if slices.Contains(requirements[FILE]["access"], i) {
return -1
}
return 1
@ -121,8 +121,8 @@ func (r *File) Constraint() constraint {
return blockKind
}
func (r *File) Kind() string {
return tokFILE
func (r *File) Kind() Kind {
return FILE
}
type Link struct {
@ -179,6 +179,6 @@ func (r *Link) Constraint() constraint {
return blockKind
}
func (r *Link) Kind() string {
return tokLINK
func (r *Link) Kind() Kind {
return LINK
}

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokIOURING = "io_uring"
const IOURING Kind = "io_uring"
func init() {
requirements[tokIOURING] = requirement{
requirements[IOURING] = requirement{
"access": []string{"sqpoll", "override_creds"},
}
}
@ -28,7 +28,7 @@ func newIOUringFromLog(log map[string]string) Rule {
return &IOUring{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: Must(toAccess(tokIOURING, log["requested"])),
Access: Must(toAccess(IOURING, log["requested"])),
Label: log["label"],
}
}
@ -64,6 +64,6 @@ func (r *IOUring) Constraint() constraint {
return blockKind
}
func (r *IOUring) Kind() string {
return tokIOURING
func (r *IOUring) Kind() Kind {
return IOURING
}

View File

@ -10,13 +10,13 @@ import (
)
const (
tokMOUNT = "mount"
tokREMOUNT = "remount"
tokUMOUNT = "umount"
MOUNT Kind = "mount"
REMOUNT Kind = "remount"
UMOUNT Kind = "umount"
)
func init() {
requirements[tokMOUNT] = requirement{
requirements[MOUNT] = requirement{
"flags": {
"acl", "async", "atime", "ro", "rw", "bind", "rbind", "dev",
"diratime", "dirsync", "exec", "iversion", "loud", "mand", "move",
@ -38,14 +38,14 @@ func newMountConditionsFromLog(log map[string]string) MountConditions {
if _, present := log["flags"]; present {
return MountConditions{
FsType: log["fstype"],
Options: Must(toValues(tokMOUNT, "flags", log["flags"])),
Options: Must(toValues(MOUNT, "flags", log["flags"])),
}
}
return MountConditions{FsType: log["fstype"]}
}
func (m MountConditions) Validate() error {
return validateValues(tokMOUNT, "flags", m.Options)
return validateValues(MOUNT, "flags", m.Options)
}
func (m MountConditions) Less(other MountConditions) bool {
@ -113,8 +113,8 @@ func (r *Mount) Constraint() constraint {
return blockKind
}
func (r *Mount) Kind() string {
return tokMOUNT
func (r *Mount) Kind() Kind {
return MOUNT
}
type Umount struct {
@ -166,8 +166,8 @@ func (r *Umount) Constraint() constraint {
return blockKind
}
func (r *Umount) Kind() string {
return tokUMOUNT
func (r *Umount) Kind() Kind {
return UMOUNT
}
type Remount struct {
@ -219,6 +219,6 @@ func (r *Remount) Constraint() constraint {
return blockKind
}
func (r *Remount) Kind() string {
return tokREMOUNT
func (r *Remount) Kind() Kind {
return REMOUNT
}

View File

@ -10,10 +10,10 @@ import (
"strings"
)
const tokMQUEUE = "mqueue"
const MQUEUE Kind = "mqueue"
func init() {
requirements[tokMQUEUE] = requirement{
requirements[MQUEUE] = requirement{
"access": []string{
"r", "w", "rw", "read", "write", "create", "open",
"delete", "getattr", "setattr",
@ -41,7 +41,7 @@ func newMqueueFromLog(log map[string]string) Rule {
return &Mqueue{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: Must(toAccess(tokMQUEUE, log["requested"])),
Access: Must(toAccess(MQUEUE, log["requested"])),
Type: mqueueType,
Label: log["label"],
Name: log["name"],
@ -86,6 +86,6 @@ func (r *Mqueue) Constraint() constraint {
return blockKind
}
func (r *Mqueue) Kind() string {
return tokMQUEUE
func (r *Mqueue) Kind() Kind {
return MQUEUE
}

View File

@ -8,10 +8,10 @@ import (
"fmt"
)
const tokNETWORK = "network"
const NETWORK Kind = "network"
func init() {
requirements[tokNETWORK] = requirement{
requirements[NETWORK] = requirement{
"access": []string{
"create", "bind", "listen", "accept", "connect", "shutdown",
"getattr", "setattr", "getopt", "setopt", "send", "receive",
@ -126,6 +126,6 @@ func (r *Network) Constraint() constraint {
return blockKind
}
func (r *Network) Kind() string {
return tokNETWORK
func (r *Network) Kind() Kind {
return NETWORK
}

View File

@ -26,12 +26,16 @@ const (
var (
newRuleMap = map[string]func([]string) (Rule, error){
tokCOMMENT: newComment,
tokABI: newAbi,
tokALIAS: newAlias,
tokINCLUDE: newInclude,
COMMENT.Tok(): newComment,
ABI.Tok(): newAbi,
ALIAS.Tok(): newAlias,
INCLUDE.Tok(): newInclude,
}
tok = map[Kind]string{
COMMENT: "#",
VARIABLE: "@{",
}
openBlocks = []rune{tokOPENPAREN, tokOPENBRACE, tokOPENBRACKET}
closeBlocks = []rune{tokCLOSEPAREN, tokCLOSEBRACE, tokCLOSEBRACKET}
)
@ -53,7 +57,7 @@ func tokenize(str string) []string {
blockStack := []rune{}
tokens := make([]string, 0, len(str)/2)
if len(str) > 2 && str[0:2] == tokVARIABLE {
if len(str) > 2 && str[0:2] == VARIABLE.Tok() {
isVariable = true
}
for _, r := range str {
@ -122,7 +126,7 @@ func tokenToSlice(token string) []string {
func tokensStripComment(tokens []string) []string {
res := []string{}
for _, v := range tokens {
if v == tokCOMMENT {
if v == COMMENT.Tok() {
break
}
res = append(res, v)
@ -147,7 +151,7 @@ func newRules(rules [][]string) (Rules, error) {
return nil, err
}
res = append(res, r)
} else if strings.HasPrefix(rule[0], tokVARIABLE) {
} else if strings.HasPrefix(rule[0], VARIABLE.Tok()) {
r, err = newVariable(rule)
if err != nil {
return nil, err
@ -167,7 +171,7 @@ func (f *AppArmorProfileFile) parsePreamble(input []string) error {
tokenizedRules := [][]string{}
for _, line := range input {
if strings.HasPrefix(line, tokCOMMENT) {
if strings.HasPrefix(line, COMMENT.Tok()) {
r, err = newComment(strings.Split(line, " "))
if err != nil {
return err
@ -215,7 +219,7 @@ done:
switch {
case tmp == "":
continue
case strings.HasPrefix(tmp, tokPROFILE):
case strings.HasPrefix(tmp, PROFILE.Tok()):
rawHeader = tmp
break done
default:

View File

@ -4,7 +4,7 @@
package aa
const tokPIVOTROOT = "pivot_root"
const PIVOTROOT = "pivot_root"
type PivotRoot struct {
RuleBase
@ -57,6 +57,6 @@ func (r *PivotRoot) Constraint() constraint {
return blockKind
}
func (r *PivotRoot) Kind() string {
return tokPIVOTROOT
func (r *PivotRoot) Kind() Kind {
return PIVOTROOT
}

View File

@ -11,12 +11,13 @@ import (
)
const (
tokABI = "abi"
tokALIAS = "alias"
tokINCLUDE = "include"
ABI Kind = "abi"
ALIAS Kind = "alias"
INCLUDE Kind = "include"
VARIABLE Kind = "variable"
COMMENT Kind = "comment"
tokIFEXISTS = "if exists"
tokVARIABLE = "@{"
tokCOMMENT = "#"
)
type Comment struct {
@ -42,7 +43,7 @@ func (r *Comment) Equals(other any) bool {
}
func (r *Comment) String() string {
return renderTemplate("comment", r)
return renderTemplate(r.Kind(), r)
}
func (r *Comment) IsPreamble() bool {
@ -53,8 +54,8 @@ func (r *Comment) Constraint() constraint {
return anyKind
}
func (r *Comment) Kind() string {
return tokCOMMENT
func (r *Comment) Kind() Kind {
return COMMENT
}
type Abi struct {
@ -65,7 +66,7 @@ type Abi struct {
func newAbi(rule []string) (Rule, error) {
var magic bool
if len(rule) > 0 && rule[0] == tokABI {
if len(rule) > 0 && rule[0] == ABI.Tok() {
rule = rule[1:]
}
if len(rule) != 1 {
@ -113,8 +114,8 @@ func (r *Abi) Constraint() constraint {
return preambleKind
}
func (r *Abi) Kind() string {
return tokABI
func (r *Abi) Kind() Kind {
return ABI
}
type Alias struct {
@ -124,7 +125,7 @@ type Alias struct {
}
func newAlias(rule []string) (Rule, error) {
if len(rule) > 0 && rule[0] == tokALIAS {
if len(rule) > 0 && rule[0] == ALIAS.Tok() {
rule = rule[1:]
}
if len(rule) != 3 {
@ -165,8 +166,8 @@ func (r *Alias) Constraint() constraint {
return preambleKind
}
func (r *Alias) Kind() string {
return tokALIAS
func (r *Alias) Kind() Kind {
return ALIAS
}
type Include struct {
@ -180,7 +181,7 @@ func newInclude(rule []string) (Rule, error) {
var magic bool
var ifexists bool
if len(rule) > 0 && rule[0] == tokINCLUDE {
if len(rule) > 0 && rule[0] == INCLUDE.Tok() {
rule = rule[1:]
}
@ -239,8 +240,8 @@ func (r *Include) Constraint() constraint {
return anyKind
}
func (r *Include) Kind() string {
return tokINCLUDE
func (r *Include) Kind() Kind {
return INCLUDE
}
type Variable struct {
@ -257,7 +258,7 @@ func newVariable(rule []string) (Rule, error) {
return nil, fmt.Errorf("invalid variable format: %v", rule)
}
name := strings.Trim(rule[0], tokVARIABLE+"}")
name := strings.Trim(rule[0], VARIABLE.Tok()+"}")
switch rule[1] {
case tokEQUAL:
define = true
@ -297,13 +298,13 @@ func (r *Variable) Equals(other any) bool {
}
func (r *Variable) String() string {
return renderTemplate("variable", r)
return renderTemplate(r.Kind(), r)
}
func (r *Variable) Constraint() constraint {
return preambleKind
}
func (r *Variable) Kind() string {
return tokVARIABLE
func (r *Variable) Kind() Kind {
return VARIABLE
}

View File

@ -14,13 +14,14 @@ import (
)
const (
PROFILE Kind = "profile"
tokATTRIBUTES = "xattrs"
tokFLAGS = "flags"
tokPROFILE = "profile"
)
func init() {
requirements[tokPROFILE] = requirement{
requirements[PROFILE] = requirement{
tokFLAGS: {
"enforce", "complain", "kill", "default_allow", "unconfined",
"prompt", "audit", "mediate_deleted", "attach_disconnected",
@ -52,7 +53,7 @@ func newHeader(rule []string) (Header, error) {
if rule[len(rule)-1] == "{" {
rule = rule[:len(rule)-1]
}
if rule[0] == tokPROFILE {
if rule[0] == PROFILE.Tok() {
rule = rule[1:]
}
@ -120,8 +121,8 @@ func (p *Profile) Constraint() constraint {
return blockKind
}
func (p *Profile) Kind() string {
return tokPROFILE
func (p *Profile) Kind() Kind {
return PROFILE
}
// Merge merge similar rules together.

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokPTRACE = "ptrace"
const PTRACE Kind = "ptrace"
func init() {
requirements[tokPTRACE] = requirement{
requirements[PTRACE] = requirement{
"access": []string{
"r", "w", "rw", "read", "readby", "trace", "tracedby",
},
@ -30,7 +30,7 @@ func newPtraceFromLog(log map[string]string) Rule {
return &Ptrace{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: Must(toAccess(tokPTRACE, log["requested_mask"])),
Access: Must(toAccess(PTRACE, log["requested_mask"])),
Peer: log["peer"],
}
}
@ -67,6 +67,6 @@ func (r *Ptrace) Constraint() constraint {
return blockKind
}
func (r *Ptrace) Kind() string {
return tokPTRACE
func (r *Ptrace) Kind() Kind {
return PTRACE
}

View File

@ -59,7 +59,7 @@ func (f *AppArmorProfileFile) Resolve() error {
}
func (f *AppArmorProfileFile) resolveValues(input string) ([]string, error) {
if !strings.Contains(input, tokVARIABLE) {
if !strings.Contains(input, VARIABLE.Tok()) {
return []string{input}, nil
}
@ -76,7 +76,7 @@ func (f *AppArmorProfileFile) resolveValues(input string) ([]string, error) {
if vrbl.Name == varname {
found = true
for _, v := range vrbl.Values {
if strings.Contains(v, tokVARIABLE+varname+"}") {
if strings.Contains(v, VARIABLE.Tok()+varname+"}") {
return nil, fmt.Errorf("recursive variable found in: %s", varname)
}
newValues := strings.ReplaceAll(input, variable, v)
@ -152,7 +152,7 @@ func (f *AppArmorProfileFile) resolveInclude(include *Include) error {
}
// Remove all includes in iFile
iFile.Preamble = iFile.Preamble.DeleteKind(tokINCLUDE)
iFile.Preamble = iFile.Preamble.DeleteKind(INCLUDE)
// Cache the included file
includeCache[include] = iFile

View File

@ -7,12 +7,11 @@ package aa
import "fmt"
const (
tokRLIMIT = "rlimit"
tokSET = "set"
RLIMIT Kind = "rlimit"
)
func init() {
requirements[tokRLIMIT] = requirement{
requirements[RLIMIT] = requirement{
"keys": {
"cpu", "fsize", "data", "stack", "core", "rss", "nofile", "ofile",
"as", "nproc", "memlock", "locks", "sigpending", "msgqueue", "nice",
@ -68,6 +67,6 @@ func (r *Rlimit) Constraint() constraint {
return blockKind
}
func (r *Rlimit) Kind() string {
return tokRLIMIT
func (r *Rlimit) Kind() Kind {
return RLIMIT
}

View File

@ -26,6 +26,20 @@ const (
blockKind // The rule can only be found in a profile
)
// Kind represents an AppArmor rule kind.
type Kind string
func (k Kind) String() string {
return string(k)
}
func (k Kind) Tok() string {
if t, ok := tok[k]; ok {
return t
}
return string(k)
}
// Rule generic interface for all AppArmor rules
type Rule interface {
Validate() error
@ -33,7 +47,7 @@ type Rule interface {
Equals(other any) bool
String() string
Constraint() constraint
Kind() string
Kind() Kind
}
type Rules []Rule
@ -77,7 +91,7 @@ func (r Rules) Delete(i int) Rules {
return append(r[:i], r[i+1:]...)
}
func (r Rules) DeleteKind(kind string) Rules {
func (r Rules) DeleteKind(kind Kind) Rules {
res := make(Rules, 0)
for _, rule := range r {
if rule.Kind() != kind {
@ -87,7 +101,7 @@ func (r Rules) DeleteKind(kind string) Rules {
return res
}
func (r Rules) Filter(filter string) Rules {
func (r Rules) Filter(filter Kind) Rules {
res := make(Rules, 0)
for _, rule := range r {
if rule.Kind() != filter {
@ -128,12 +142,12 @@ func Must[T any](v T, err error) T {
return v
}
func validateValues(rule string, key string, values []string) error {
func validateValues(kind Kind, key string, values []string) error {
for _, v := range values {
if v == "" {
continue
}
if !slices.Contains(requirements[rule][key], v) {
if !slices.Contains(requirements[kind][key], v) {
return fmt.Errorf("invalid mode '%s'", v)
}
}
@ -142,10 +156,10 @@ func validateValues(rule string, key string, values []string) error {
// Helper function to convert a string to a slice of rule values according to
// the rule requirements as defined in the requirements map.
func toValues(rule string, key string, input string) ([]string, error) {
req, ok := requirements[rule][key]
func toValues(kind Kind, key string, input string) ([]string, error) {
req, ok := requirements[kind][key]
if !ok {
return nil, fmt.Errorf("unrecognized requirement '%s' for rule %s", key, rule)
return nil, fmt.Errorf("unrecognized requirement '%s' for rule %s", key, kind)
}
res := tokenToSlice(input)
@ -156,22 +170,22 @@ func toValues(rule string, key string, input string) ([]string, error) {
}
}
slices.SortFunc(res, func(i, j string) int {
return requirementsWeights[rule][key][i] - requirementsWeights[rule][key][j]
return requirementsWeights[kind][key][i] - requirementsWeights[kind][key][j]
})
return slices.Compact(res), nil
}
// Helper function to convert an access string to a slice of access according to
// the rule requirements as defined in the requirements map.
func toAccess(rule string, input string) ([]string, error) {
func toAccess(kind Kind, input string) ([]string, error) {
var res []string
switch rule {
case tokFILE:
switch kind {
case FILE:
raw := strings.Split(input, "")
trans := []string{}
for _, access := range raw {
if slices.Contains(requirements[tokFILE]["access"], access) {
if slices.Contains(requirements[FILE]["access"], access) {
res = append(res, access)
} else {
trans = append(trans, access)
@ -180,17 +194,17 @@ func toAccess(rule string, input string) ([]string, error) {
transition := strings.Join(trans, "")
if len(transition) > 0 {
if slices.Contains(requirements[tokFILE]["transition"], transition) {
if slices.Contains(requirements[FILE]["transition"], transition) {
res = append(res, transition)
} else {
return nil, fmt.Errorf("unrecognized transition: %s", transition)
}
}
case tokFILE + "-log":
case FILE + "-log":
raw := strings.Split(input, "")
for _, access := range raw {
if slices.Contains(requirements[tokFILE]["access"], access) {
if slices.Contains(requirements[FILE]["access"], access) {
res = append(res, access)
} else if maskToAccess[access] != "" {
res = append(res, maskToAccess[access])
@ -200,7 +214,7 @@ func toAccess(rule string, input string) ([]string, error) {
}
default:
return toValues(rule, "access", input)
return toValues(kind, "access", input)
}
slices.SortFunc(res, cmpFileAccess)

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokSIGNAL = "signal"
const SIGNAL Kind = "signal"
func init() {
requirements[tokSIGNAL] = requirement{
requirements[SIGNAL] = requirement{
"access": {
"r", "w", "rw", "read", "write", "send", "receive",
},
@ -44,7 +44,7 @@ func newSignalFromLog(log map[string]string) Rule {
return &Signal{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: Must(toAccess(tokSIGNAL, log["requested_mask"])),
Access: Must(toAccess(SIGNAL, log["requested_mask"])),
Set: []string{log["signal"]},
Peer: log["peer"],
}
@ -88,6 +88,6 @@ func (r *Signal) Constraint() constraint {
return blockKind
}
func (r *Signal) Kind() string {
return tokSIGNAL
func (r *Signal) Kind() Kind {
return SIGNAL
}

View File

@ -25,7 +25,7 @@ var (
// The functions available in the template
tmplFunctionMap = template.FuncMap{
"typeof": typeOf,
"kindof": kindOf,
"join": join,
"cjoin": cjoin,
"indent": indent,
@ -34,24 +34,25 @@ var (
}
// The apparmor templates
tmpl = generateTemplates([]string{
tmpl = generateTemplates([]Kind{
// Global templates
"apparmor",
tokPROFILE,
PROFILE,
HAT,
"rules",
// Preamble templates
tokABI,
tokALIAS,
tokINCLUDE,
"variable",
"comment",
ABI,
ALIAS,
INCLUDE,
VARIABLE,
COMMENT,
// Rules templates
tokALL, tokRLIMIT, tokUSERNS, tokCAPABILITY, tokNETWORK,
tokMOUNT, tokREMOUNT, tokUMOUNT, tokPIVOTROOT, tokCHANGEPROFILE,
tokMQUEUE, tokIOURING, tokUNIX, tokPTRACE, tokSIGNAL, tokDBUS,
tokFILE, tokLINK,
ALL, RLIMIT, USERNS, CAPABILITY, NETWORK,
MOUNT, REMOUNT, UMOUNT, PIVOTROOT, CHANGEPROFILE,
MQUEUE, IOURING, UNIX, PTRACE, SIGNAL, DBUS,
FILE, LINK,
})
// convert apparmor requested mask to apparmor access mode
@ -64,27 +65,28 @@ var (
}
// The order the apparmor rules should be sorted
ruleAlphabet = []string{
"include",
"all",
"rlimit",
"userns",
"capability",
"network",
"mount",
"remount",
"umount",
"pivotroot",
"changeprofile",
"mqueue",
"iouring",
"signal",
"ptrace",
"unix",
"dbus",
"file",
"link",
"profile",
ruleAlphabet = []Kind{
INCLUDE,
ALL,
RLIMIT,
USERNS,
CAPABILITY,
NETWORK,
MOUNT,
REMOUNT,
UMOUNT,
PIVOTROOT,
CHANGEPROFILE,
MQUEUE,
IOURING,
SIGNAL,
PTRACE,
UNIX,
DBUS,
FILE,
LINK,
PROFILE,
HAT,
"include_if_exists",
}
ruleWeights = generateWeights(ruleAlphabet)
@ -117,16 +119,16 @@ var (
fileWeights = generateWeights(fileAlphabet)
// The order the rule values (access, type, domains, etc) should be sorted
requirements = map[string]requirement{}
requirementsWeights map[string]map[string]map[string]int
requirements = map[Kind]requirement{}
requirementsWeights map[Kind]map[string]map[string]int
)
func init() {
requirementsWeights = generateRequirementsWeights(requirements)
}
func generateTemplates(names []string) map[string]*template.Template {
res := make(map[string]*template.Template, len(names))
func generateTemplates(names []Kind) map[Kind]*template.Template {
res := make(map[Kind]*template.Template, len(names))
base := template.New("").Funcs(tmplFunctionMap)
base = template.Must(base.ParseFS(tmplFiles,
"templates/*.j2", "templates/rule/*.j2",
@ -141,11 +143,11 @@ func generateTemplates(names []string) map[string]*template.Template {
return res
}
func renderTemplate(name string, data any) string {
func renderTemplate(name Kind, data any) string {
var res strings.Builder
template, ok := tmpl[name]
if !ok {
panic("template '" + name + "' not found")
panic("template '" + name.String() + "' not found")
}
err := template.Execute(&res, data)
if err != nil {
@ -154,16 +156,16 @@ func renderTemplate(name string, data any) string {
return res.String()
}
func generateWeights(alphabet []string) map[string]int {
res := make(map[string]int, len(alphabet))
func generateWeights[T Kind | string](alphabet []T) map[T]int {
res := make(map[T]int, len(alphabet))
for i, r := range alphabet {
res[r] = i
}
return res
}
func generateRequirementsWeights(requirements map[string]requirement) map[string]map[string]map[string]int {
res := make(map[string]map[string]map[string]int, len(requirements))
func generateRequirementsWeights(requirements map[Kind]requirement) map[Kind]map[string]map[string]int {
res := make(map[Kind]map[string]map[string]int, len(requirements))
for rule, req := range requirements {
res[rule] = make(map[string]map[string]int, len(req))
for key, values := range req {
@ -207,15 +209,11 @@ func cjoin(i any) string {
}
}
func typeOf(i any) string {
func kindOf(i any) string {
if i == nil {
return ""
}
return strings.TrimPrefix(reflect.TypeOf(i).String(), "*aa.")
}
func typeToValue(i reflect.Type) string {
return strings.ToLower(strings.TrimPrefix(i.String(), "*aa."))
return i.(Rule).Kind().String()
}
func setindent(i string) string {

View File

@ -4,118 +4,118 @@
{{- define "rules" -}}
{{- $oldtype := "" -}}
{{- $oldkind := "" -}}
{{- range . -}}
{{- $type := typeof . -}}
{{- if eq $type "" -}}
{{- $kind := kindof . -}}
{{- if eq $kind "" -}}
{{- "\n" -}}
{{- continue -}}
{{- end -}}
{{- if eq $type "Comment" -}}
{{- if eq $kind "comment" -}}
{{- template "comment" . -}}
{{- "\n" -}}
{{- continue -}}
{{- end -}}
{{- if and (ne $type $oldtype) (ne $oldtype "") -}}
{{- if and (ne $kind $oldkind) (ne $oldkind "") -}}
{{- "\n" -}}
{{- end -}}
{{- indent "" -}}
{{- if eq $type "Abi" -}}
{{- if eq $kind "abi" -}}
{{- template "abi" . -}}
{{- end -}}
{{- if eq $type "Alias" -}}
{{- if eq $kind "alias" -}}
{{- template "alias" . -}}
{{- end -}}
{{- if eq $type "Include" -}}
{{- if eq $kind "include" -}}
{{- template "include" . -}}
{{- end -}}
{{- if eq $type "Variable" -}}
{{- if eq $kind "variable" -}}
{{- template "variable" . -}}
{{- end -}}
{{- if eq $type "All" -}}
{{- if eq $kind "all" -}}
{{- template "all" . -}}
{{- end -}}
{{- if eq $type "Rlimit" -}}
{{- if eq $kind "rlimit" -}}
{{- template "rlimit" . -}}
{{- end -}}
{{- if eq $type "Userns" -}}
{{- if eq $kind "userns" -}}
{{- template "userns" . -}}
{{- end -}}
{{- if eq $type "Capability" -}}
{{- if eq $kind "capability" -}}
{{- template "capability" . -}}
{{- end -}}
{{- if eq $type "Network" -}}
{{- if eq $kind "network" -}}
{{- template "network" . -}}
{{- end -}}
{{- if eq $type "Mount" -}}
{{- if eq $kind "mount" -}}
{{- template "mount" . -}}
{{- end -}}
{{- if eq $type "Remount" -}}
{{- if eq $kind "remount" -}}
{{- template "remount" . -}}
{{- end -}}
{{- if eq $type "Umount" -}}
{{- if eq $kind "umount" -}}
{{- template "umount" . -}}
{{- end -}}
{{- if eq $type "PivotRoot" -}}
{{- if eq $kind "pivot_root" -}}
{{- template "pivot_root" . -}}
{{- end -}}
{{- if eq $type "ChangeProfile" -}}
{{- if eq $kind "change_profile" -}}
{{- template "change_profile" . -}}
{{- end -}}
{{- if eq $type "Mqueue" -}}
{{- if eq $kind "mqueue" -}}
{{- template "mqueue" . -}}
{{- end -}}
{{- if eq $type "IOUring" -}}
{{- if eq $kind "io_uring" -}}
{{- template "io_uring" . -}}
{{- end -}}
{{- if eq $type "Unix" -}}
{{- if eq $kind "unix" -}}
{{- template "unix" . -}}
{{- end -}}
{{- if eq $type "Ptrace" -}}
{{- if eq $kind "ptrace" -}}
{{- template "ptrace" . -}}
{{- end -}}
{{- if eq $type "Signal" -}}
{{- if eq $kind "signal" -}}
{{- template "signal" . -}}
{{- end -}}
{{- if eq $type "Dbus" -}}
{{- if eq $kind "dbus" -}}
{{- template "dbus" . -}}
{{- end -}}
{{- if eq $type "File" -}}
{{- if eq $kind "file" -}}
{{- template "file" . -}}
{{- end -}}
{{- if eq $type "Link" -}}
{{- if eq $kind "link" -}}
{{- template "link" . -}}
{{- end -}}
{{- if eq $type "Profile" -}}
{{- if eq $kind "profile" -}}
{{- template "profile" . -}}
{{- end -}}
{{- "\n" -}}
{{- $oldtype = $type -}}
{{- $oldkind = $kind -}}
{{- end -}}
{{- end -}}

View File

@ -9,10 +9,10 @@ import (
"slices"
)
const tokUNIX = "unix"
const UNIX Kind = "unix"
func init() {
requirements[tokUNIX] = requirement{
requirements[UNIX] = requirement{
"access": []string{
"create", "bind", "listen", "accept", "connect", "shutdown",
"getattr", "setattr", "getopt", "setopt", "send", "receive",
@ -39,7 +39,7 @@ func newUnixFromLog(log map[string]string) Rule {
return &Unix{
RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: Must(toAccess(tokUNIX, log["requested_mask"])),
Access: Must(toAccess(UNIX, log["requested_mask"])),
Type: log["sock_type"],
Protocol: log["protocol"],
Address: log["addr"],
@ -107,6 +107,6 @@ func (r *Unix) Constraint() constraint {
return blockKind
}
func (r *Unix) Kind() string {
return tokUNIX
func (r *Unix) Kind() Kind {
return UNIX
}

View File

@ -4,7 +4,7 @@
package aa
const tokUSERNS = "userns"
const USERNS Kind = "userns"
type Userns struct {
RuleBase
@ -45,6 +45,6 @@ func (r *Userns) Constraint() constraint {
return blockKind
}
func (r *Userns) Kind() string {
return tokUSERNS
func (r *Userns) Kind() Kind {
return USERNS
}