2024-03-21 21:36:41 +01:00
|
|
|
// apparmor.d - Full set of apparmor profiles
|
|
|
|
// Copyright (C) 2021-2024 Alexandre Pujol <alexandre@pujol.io>
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
|
|
|
package directive
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
|
|
|
|
2024-04-28 01:36:16 +02:00
|
|
|
"github.com/roddhjav/apparmor.d/pkg/paths"
|
2024-03-25 23:40:25 +01:00
|
|
|
"github.com/roddhjav/apparmor.d/pkg/prebuild/cfg"
|
2024-03-21 21:36:41 +01:00
|
|
|
)
|
|
|
|
|
2024-03-25 23:40:25 +01:00
|
|
|
var (
|
2024-04-02 18:48:03 +02:00
|
|
|
// Define the directive keyword globally
|
|
|
|
Keyword = "#aa:"
|
|
|
|
|
2024-03-25 23:40:25 +01:00
|
|
|
// Build the profiles with the following directive applied
|
|
|
|
Directives = map[string]Directive{}
|
2024-03-21 21:36:41 +01:00
|
|
|
|
2024-03-25 23:40:25 +01:00
|
|
|
regDirective = regexp.MustCompile(`(?m).*` + Keyword + `([a-z]*) (.*)`)
|
|
|
|
)
|
2024-03-21 21:36:41 +01:00
|
|
|
|
|
|
|
// Main directive interface
|
|
|
|
type Directive interface {
|
2024-03-25 23:40:25 +01:00
|
|
|
cfg.BaseInterface
|
2024-05-25 23:30:20 +02:00
|
|
|
Apply(opt *Option, profile string) (string, error)
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Directive options
|
|
|
|
type Option struct {
|
2024-03-23 18:41:10 +01:00
|
|
|
Name string
|
|
|
|
ArgMap map[string]string
|
|
|
|
ArgList []string
|
|
|
|
File *paths.Path
|
|
|
|
Raw string
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func NewOption(file *paths.Path, match []string) *Option {
|
|
|
|
if len(match) != 3 {
|
|
|
|
panic(fmt.Sprintf("Invalid directive: %v", match))
|
|
|
|
}
|
2024-03-23 18:41:10 +01:00
|
|
|
argList := strings.Fields(match[2])
|
|
|
|
argMap := map[string]string{}
|
|
|
|
for _, t := range argList {
|
2024-03-21 21:36:41 +01:00
|
|
|
tmp := strings.Split(t, "=")
|
|
|
|
if len(tmp) < 2 {
|
2024-03-23 18:41:10 +01:00
|
|
|
argMap[tmp[0]] = ""
|
2024-03-21 21:36:41 +01:00
|
|
|
} else {
|
2024-03-23 18:41:10 +01:00
|
|
|
argMap[tmp[0]] = tmp[1]
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return &Option{
|
2024-03-23 18:41:10 +01:00
|
|
|
Name: match[1],
|
|
|
|
ArgMap: argMap,
|
|
|
|
ArgList: argList,
|
|
|
|
File: file,
|
|
|
|
Raw: match[0],
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-28 15:22:00 +02:00
|
|
|
// Clean the selected directive from profile.
|
|
|
|
// Useful to remove directive text applied on some condition only
|
|
|
|
func (o *Option) Clean(profile string) string {
|
|
|
|
reg := regexp.MustCompile(`\s*` + Keyword + o.Name + ` .*$`)
|
2024-06-14 21:50:17 +02:00
|
|
|
return strings.Replace(profile, o.Raw, reg.ReplaceAllString(o.Raw, ""), 1)
|
2024-04-28 15:22:00 +02:00
|
|
|
}
|
|
|
|
|
2024-03-25 23:40:25 +01:00
|
|
|
func RegisterDirective(d Directive) {
|
|
|
|
Directives[d.Name()] = d
|
|
|
|
}
|
|
|
|
|
2024-05-25 23:30:20 +02:00
|
|
|
func Run(file *paths.Path, profile string) (string, error) {
|
|
|
|
var err error
|
2024-03-21 21:36:41 +01:00
|
|
|
for _, match := range regDirective.FindAllStringSubmatch(profile, -1) {
|
|
|
|
opt := NewOption(file, match)
|
|
|
|
drtv, ok := Directives[opt.Name]
|
|
|
|
if !ok {
|
2024-05-29 22:12:54 +02:00
|
|
|
return "", fmt.Errorf("Unknown directive '%s' in %s", opt.Name, opt.File)
|
2024-05-25 23:30:20 +02:00
|
|
|
}
|
|
|
|
profile, err = drtv.Apply(opt, profile)
|
|
|
|
if err != nil {
|
2024-05-29 22:12:54 +02:00
|
|
|
return "", fmt.Errorf("%s %s: %w", drtv.Name(), opt.File, err)
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|
|
|
|
}
|
2024-05-25 23:30:20 +02:00
|
|
|
return profile, nil
|
2024-03-21 21:36:41 +01:00
|
|
|
}
|