// apparmor.d - Full set of apparmor profiles // Copyright (C) 2021-2024 Alexandre Pujol // SPDX-License-Identifier: GPL-2.0-only package aa import ( "reflect" "sort" "strings" ) const ( tokALL = "all" tokALLOW = "allow" tokAUDIT = "audit" tokDENY = "deny" ) // Rule generic interface for all AppArmor rules type Rule interface { Less(other any) bool Equals(other any) bool String() string } type Rules []Rule func (r Rules) String() string { return renderTemplate("rules", r) } // Sort the rules in a profile. // Follow: https://apparmor.pujol.io/development/guidelines/#guidelines func (r Rules) Sort() { sort.Slice(r, func(i, j int) bool { typeOfI := reflect.TypeOf(r[i]) typeOfJ := reflect.TypeOf(r[j]) if typeOfI != typeOfJ { valueOfI := typeToValue(typeOfI) valueOfJ := typeToValue(typeOfJ) if typeOfI == reflect.TypeOf((*Include)(nil)) && r[i].(*Include).IfExists { valueOfI = "include_if_exists" } if typeOfJ == reflect.TypeOf((*Include)(nil)) && r[j].(*Include).IfExists { valueOfJ = "include_if_exists" } return ruleWeights[valueOfI] < ruleWeights[valueOfJ] } return r[i].Less(r[j]) }) } type RuleBase struct { IsLineRule bool Comment string NoNewPrivs bool FileInherit bool Prefix string Padding string Optional bool } func newRuleFromLog(log map[string]string) RuleBase { fileInherit := false if log["operation"] == "file_inherit" { fileInherit = true } noNewPrivs := false optional := false msg := "" switch log["error"] { case "-1": if strings.Contains(log["info"], "optional:") { optional = true msg = strings.Replace(log["info"], "optional: ", "", 1) } else { noNewPrivs = true } case "-13": ignoreProfileInfo := []string{"namespace", "disconnected path"} for _, info := range ignoreProfileInfo { if strings.Contains(log["info"], info) { break } } msg = log["info"] default: } return RuleBase{ IsLineRule: false, Comment: msg, NoNewPrivs: noNewPrivs, FileInherit: fileInherit, Optional: optional, } } func (r RuleBase) Less(other any) bool { return false } func (r RuleBase) Equals(other any) bool { return false } func (r RuleBase) String() string { return renderTemplate("comment", r) } type Qualifier struct { Audit bool AccessType string } func newQualifierFromLog(log map[string]string) Qualifier { audit := false if log["apparmor"] == "AUDIT" { audit = true } return Qualifier{Audit: audit} } func (r Qualifier) Less(other Qualifier) bool { if r.Audit != other.Audit { return r.Audit } return r.AccessType < other.AccessType } func (r Qualifier) Equals(other Qualifier) bool { return r.Audit == other.Audit && r.AccessType == other.AccessType } type All struct { RuleBase } func (r *All) Less(other any) bool { return false } func (r *All) Equals(other any) bool { return false } func (r *All) String() string { return renderTemplate(tokALL, r) }