apparmor.d/pkg/aa/profile.go

107 lines
2.7 KiB
Go
Raw Normal View History

// apparmor.d - Full set of apparmor profiles
// 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
import (
"regexp"
"strings"
"golang.org/x/exp/maps"
)
var (
regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`)
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
// Tunables
Tunables = map[string][]string{
"libexec": {},
"multiarch": {"*-linux-gnu*"},
"user_share_dirs": {"/home/*/.local/share"},
"etc_ro": {"/{usr/,}etc/"},
}
)
type AppArmorProfile struct {
Content string
Variables map[string][]string
Attachments []string
}
func NewAppArmorProfile(content string) *AppArmorProfile {
variables := make(map[string][]string)
maps.Copy(variables, Tunables)
return &AppArmorProfile{
Content: content,
Variables: variables,
Attachments: []string{},
}
}
// ParseVariables extract all variables from the profile
func (p *AppArmorProfile) ParseVariables() {
matches := regVariablesDef.FindAllStringSubmatch(p.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]
2023-04-24 13:51:16 +02:00
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, ",") + "}"
}
}