mirror of
https://github.com/roddhjav/apparmor.d.git
synced 2024-12-26 15:06:45 +01:00
180 lines
4.3 KiB
Go
180 lines
4.3 KiB
Go
// apparmor.d - Full set of apparmor profiles
|
|
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
package aa
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/roddhjav/apparmor.d/pkg/paths"
|
|
"github.com/roddhjav/apparmor.d/pkg/util"
|
|
)
|
|
|
|
var (
|
|
includeCache map[*Include]*AppArmorProfileFile = make(map[*Include]*AppArmorProfileFile)
|
|
|
|
regVariableReference = regexp.MustCompile(`@{([^{}]+)}`)
|
|
)
|
|
|
|
// Resolve resolves variables and includes definied in the profile preamble
|
|
func (f *AppArmorProfileFile) Resolve() error {
|
|
// Resolve preamble includes
|
|
// for _, include := range f.Preamble.GetIncludes() {
|
|
// err := f.resolveInclude(include)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// }
|
|
|
|
// Append value to variable
|
|
seen := map[string]*Variable{}
|
|
for idx, variable := range f.Preamble.GetVariables() {
|
|
if _, ok := seen[variable.Name]; ok {
|
|
if variable.Define {
|
|
return fmt.Errorf("variable %s already defined", variable.Name)
|
|
}
|
|
seen[variable.Name].Values = append(seen[variable.Name].Values, variable.Values...)
|
|
f.Preamble = f.Preamble.Delete(idx)
|
|
}
|
|
if variable.Define {
|
|
seen[variable.Name] = variable
|
|
}
|
|
}
|
|
|
|
// Resolve variables
|
|
for _, variable := range f.Preamble.GetVariables() {
|
|
newValues := []string{}
|
|
for _, value := range variable.Values {
|
|
vars, err := f.resolveValues(value)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newValues = append(newValues, vars...)
|
|
}
|
|
variable.Values = newValues
|
|
}
|
|
|
|
// Resolve variables in attachements
|
|
for _, profile := range f.Profiles {
|
|
attachments := []string{}
|
|
for _, att := range profile.Attachments {
|
|
vars, err := f.resolveValues(att)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
attachments = append(attachments, vars...)
|
|
}
|
|
profile.Attachments = attachments
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (f *AppArmorProfileFile) resolveValues(input string) ([]string, error) {
|
|
if !strings.Contains(input, VARIABLE.Tok()) {
|
|
return []string{input}, nil
|
|
}
|
|
|
|
values := []string{}
|
|
match := regVariableReference.FindStringSubmatch(input)
|
|
if len(match) == 0 {
|
|
return nil, fmt.Errorf("Invalid variable reference: %s", input)
|
|
}
|
|
|
|
variable := match[0]
|
|
varname := match[1]
|
|
found := false
|
|
for _, vrbl := range f.Preamble.GetVariables() {
|
|
if vrbl.Name == varname {
|
|
found = true
|
|
for _, v := range vrbl.Values {
|
|
if strings.Contains(v, VARIABLE.Tok()+varname+"}") {
|
|
return nil, fmt.Errorf("recursive variable found in: %s", varname)
|
|
}
|
|
newValues := strings.ReplaceAll(input, variable, v)
|
|
newValues = strings.ReplaceAll(newValues, "//", "/")
|
|
res, err := f.resolveValues(newValues)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
values = append(values, res...)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !found {
|
|
return nil, fmt.Errorf("Variable %s not defined", varname)
|
|
}
|
|
return values, nil
|
|
}
|
|
|
|
// resolveInclude resolves all includes defined in the profile preamble
|
|
func (f *AppArmorProfileFile) resolveInclude(include *Include) error {
|
|
if include == nil || include.Path == "" {
|
|
return fmt.Errorf("Invalid include: %v", include)
|
|
}
|
|
|
|
_, isCached := includeCache[include]
|
|
if !isCached {
|
|
var files paths.PathList
|
|
var err error
|
|
|
|
path := MagicRoot.Join(include.Path)
|
|
if !include.IsMagic {
|
|
path = paths.New(include.Path)
|
|
}
|
|
|
|
if path.IsDir() {
|
|
files, err = path.ReadDir(paths.FilterOutDirectories())
|
|
if err != nil {
|
|
if include.IfExists {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("File %s not found: %v", path, err)
|
|
}
|
|
|
|
} else if path.Exist() {
|
|
files = append(files, path)
|
|
|
|
} else {
|
|
if include.IfExists {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("File %s not found", path)
|
|
|
|
}
|
|
|
|
iFile := &AppArmorProfileFile{}
|
|
for _, file := range files {
|
|
raw, err := util.ReadFile(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if _, err := iFile.Parse(raw); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := iFile.Validate(); err != nil {
|
|
return err
|
|
}
|
|
for _, inc := range iFile.Preamble.GetIncludes() {
|
|
if err := iFile.resolveInclude(inc); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Remove all includes in iFile
|
|
iFile.Preamble = iFile.Preamble.DeleteKind(INCLUDE)
|
|
|
|
// Cache the included file
|
|
includeCache[include] = iFile
|
|
}
|
|
|
|
// Insert iFile in the place of include in the current file
|
|
index := f.Preamble.Index(include)
|
|
f.Preamble = f.Preamble.Replace(index, includeCache[include].Preamble...)
|
|
return nil
|
|
}
|