apparmor.d/pkg/aa/resolve.go
Alexandre Pujol 89abbae6bd
Merge branch 'feat/aa'
Improve go apparmor lib.

* aa: (62 commits)
  feat(aa): handle appending value to defined variables.
  chore(aa): cosmetic.
  fix: userspace prebuild test.
  chore: cleanup unit test.
  feat(aa): improve log conversion.
  feat(aa): move conversion function to its own file & add unit tests.
  fix: go linter issue & not defined variables.
  tests(aa): improve aa unit tests.
  tests(aa): improve rules unit tests.
  feat(aa): ensure the prebuild jobs are working.
  feat(aa): add more unit tests.
  chore(aa): cleanup.
  feat(aa): Move sort, merge and format methods to the rules interface.
  feat(aa): add the hat template.
  feat(aa): add the Kind struct to manage aa rules.
  feat(aa): cleanup rules methods.
  feat(aa): add function to resolve include preamble.
  feat(aa): updaqte mount flags order.
  feat(aa): update default tunable selection.
  feat(aa): parse apparmor preamble files.
  ...
2024-05-30 19:29:34 +01:00

181 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
}