2023-08-18 00:00:52 +02:00
|
|
|
// apparmor.d - Full set of apparmor profiles
|
2024-02-07 00:16:21 +01:00
|
|
|
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
|
2023-08-18 00:00:52 +02:00
|
|
|
// 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"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`)
|
|
|
|
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
|
2023-09-25 01:09:11 +02:00
|
|
|
)
|
|
|
|
|
2023-08-18 00:00:52 +02:00
|
|
|
// DefaultTunables return a minimal working profile to build the profile
|
|
|
|
// It should not be used when loading file from /etc/apparmor.d
|
2024-04-16 22:51:56 +02:00
|
|
|
func DefaultTunables() *AppArmorProfileFile {
|
|
|
|
return &AppArmorProfileFile{
|
2023-08-18 00:00:52 +02:00
|
|
|
Preamble: Preamble{
|
2024-04-15 00:58:34 +02:00
|
|
|
Variables: []*Variable{
|
|
|
|
{Name: "bin", Values: []string{"/{,usr/}{,s}bin"}},
|
|
|
|
{Name: "lib", Values: []string{"/{,usr/}lib{,exec,32,64}"}},
|
|
|
|
{Name: "multiarch", Values: []string{"*-linux-gnu*"}},
|
|
|
|
{Name: "HOME", Values: []string{"/home/*"}},
|
|
|
|
{Name: "user_share_dirs", Values: []string{"/home/*/.local/share"}},
|
|
|
|
{Name: "etc_ro", Values: []string{"/{,usr/}etc/"}},
|
|
|
|
{Name: "int", Values: []string{"[0-9]{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}"}},
|
2023-08-18 00:00:52 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseVariables extract all variables from the profile
|
2024-04-16 22:51:56 +02:00
|
|
|
func (f *AppArmorProfileFile) ParseVariables(content string) {
|
2023-08-18 00:00:52 +02:00
|
|
|
matches := regVariablesDef.FindAllStringSubmatch(content, -1)
|
|
|
|
for _, match := range matches {
|
|
|
|
if len(match) > 2 {
|
|
|
|
key := match[1]
|
|
|
|
values := strings.Split(match[2], " ")
|
|
|
|
found := false
|
2024-04-16 22:51:56 +02:00
|
|
|
for idx, variable := range f.Variables {
|
2023-08-18 00:00:52 +02:00
|
|
|
if variable.Name == key {
|
2024-04-16 22:51:56 +02:00
|
|
|
f.Variables[idx].Values = append(f.Variables[idx].Values, values...)
|
2023-08-18 00:00:52 +02:00
|
|
|
found = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found {
|
2024-04-15 00:58:34 +02:00
|
|
|
variable := &Variable{Name: key, Values: values}
|
2024-04-16 22:51:56 +02:00
|
|
|
f.Variables = append(f.Variables, variable)
|
2023-08-18 00:00:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// resolve recursively resolves all variables references
|
2024-04-16 22:51:56 +02:00
|
|
|
func (f *AppArmorProfileFile) resolve(str string) []string {
|
2023-08-18 00:00:52 +02:00
|
|
|
if strings.Contains(str, "@{") {
|
|
|
|
vars := []string{}
|
|
|
|
match := regVariablesRef.FindStringSubmatch(str)
|
|
|
|
if len(match) > 1 {
|
|
|
|
variable := match[0]
|
|
|
|
varname := match[1]
|
2024-04-16 22:51:56 +02:00
|
|
|
for _, vrbl := range f.Variables {
|
2023-08-18 00:00:52 +02:00
|
|
|
if vrbl.Name == varname {
|
|
|
|
for _, value := range vrbl.Values {
|
|
|
|
newVar := strings.ReplaceAll(str, variable, value)
|
2024-04-16 22:51:56 +02:00
|
|
|
vars = append(vars, f.resolve(newVar)...)
|
2023-08-18 00:00:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vars = append(vars, str)
|
|
|
|
}
|
|
|
|
return vars
|
|
|
|
}
|
|
|
|
return []string{str}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResolveAttachments resolve profile attachments defined in exec_path
|
2024-04-16 22:51:56 +02:00
|
|
|
func (f *AppArmorProfileFile) ResolveAttachments() {
|
|
|
|
p := f.GetDefaultProfile()
|
2024-04-15 15:09:04 +02:00
|
|
|
|
|
|
|
for _, variable := range profile.Variables {
|
2023-08-18 00:00:52 +02:00
|
|
|
if variable.Name == "exec_path" {
|
|
|
|
for _, value := range variable.Values {
|
2024-04-15 15:09:04 +02:00
|
|
|
attachments := profile.resolve(value)
|
2024-04-11 01:15:08 +02:00
|
|
|
if len(attachments) == 0 {
|
|
|
|
panic("Variable not defined in: " + value)
|
|
|
|
}
|
|
|
|
p.Attachments = append(p.Attachments, attachments...)
|
2023-08-18 00:00:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NestAttachments return a nested attachment string
|
2024-04-16 22:51:56 +02:00
|
|
|
func (f *AppArmorProfileFile) NestAttachments() string {
|
|
|
|
p := f.GetDefaultProfile()
|
2023-08-18 00:00:52 +02:00
|
|
|
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, ",") + "}"
|
|
|
|
}
|
|
|
|
}
|