feat(aa-log): add a new apparmor profile struct

Also rewrite variables resolution to this new struct.
This commit is contained in:
Alexandre Pujol 2023-08-17 23:00:52 +01:00
parent b2d093e125
commit a8470dfa38
No known key found for this signature in database
GPG key ID: C5469996F0DF68EC
5 changed files with 379 additions and 156 deletions

View file

@ -2,104 +2,19 @@
// Copyright (C) 2023 Alexandre Pujol <alexandre@pujol.io>
// SPDX-License-Identifier: GPL-2.0-only
// Warning: this is purposely not using a Yacc parser. Its only aim is to
// extract variables and attachments for apparmor.d profile
package aa
// AppArmorProfiles represents a full set of apparmor profiles
type AppArmorProfiles map[string]*AppArmorProfile
import (
"regexp"
"strings"
"golang.org/x/exp/maps"
)
var (
regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`)
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
// Tunables
Tunables = map[string][]string{
"bin": {"/{usr/,}{s,}bin"},
"lib": {"/{usr/,}lib{,exec,32,64}"},
"multiarch": {"*-linux-gnu*"},
"user_share_dirs": {"/home/*/.local/share"},
"etc_ro": {"/{usr/,}etc/"},
}
)
// 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...)
// - The structure is simplified as it only aims at writting profile, not parsing it.
type AppArmorProfile struct {
Variables map[string][]string
Attachments []string
Preamble
Profile
}
func NewAppArmorProfile() *AppArmorProfile {
variables := make(map[string][]string)
maps.Copy(variables, Tunables)
return &AppArmorProfile{
Variables: variables,
Attachments: []string{},
}
}
// ParseVariables extract all variables from the profile
func (p *AppArmorProfile) ParseVariables(content string) {
matches := regVariablesDef.FindAllStringSubmatch(content, -1)
for _, match := range matches {
if len(match) > 2 {
key := match[1]
values := match[2]
if _, ok := p.Variables[key]; ok {
p.Variables[key] = append(p.Variables[key], strings.Split(values, " ")...)
} else {
p.Variables[key] = strings.Split(values, " ")
}
}
}
}
// resolve recursively resolves all variables references
func (p *AppArmorProfile) resolve(str string) []string {
if strings.Contains(str, "@{") {
vars := []string{}
match := regVariablesRef.FindStringSubmatch(str)
if len(match) > 1 {
variable := match[0]
varname := match[1]
for _, value := range p.Variables[varname] {
newVar := strings.ReplaceAll(str, variable, value)
vars = append(vars, p.resolve(newVar)...)
}
} else {
vars = append(vars, str)
}
return vars
}
return []string{str}
}
// ResolveAttachments resolve profile attachments defined in exec_path
func (p *AppArmorProfile) ResolveAttachments() {
for _, exec := range p.Variables["exec_path"] {
p.Attachments = append(p.Attachments, p.resolve(exec)...)
}
}
// NestAttachments return a nested attachment string
func (p *AppArmorProfile) NestAttachments() string {
if len(p.Attachments) == 0 {
return ""
} else if len(p.Attachments) == 1 {
return p.Attachments[0]
} else {
res := []string{}
for _, attachment := range p.Attachments {
if strings.HasPrefix(attachment, "/") {
res = append(res, attachment[1:])
} else {
res = append(res, attachment)
}
}
return "/{" + strings.Join(res, ",") + "}"
}
return &AppArmorProfile{}
}