feat(aa): add rule constructors from internal parser.

This commit is contained in:
Alexandre Pujol 2024-06-19 23:22:49 +01:00
parent ac9d6d859f
commit 163c5be61c
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
19 changed files with 456 additions and 75 deletions

View File

@ -12,6 +12,10 @@ type All struct {
RuleBase
}
func newAll(q Qualifier, rule rule) (Rule, error) {
return &All{RuleBase: newBase(rule)}, nil
}
func (r *All) Validate() error {
return nil
}

View File

@ -18,17 +18,19 @@ type RuleBase struct {
Optional bool
}
func newRule(rule []string) RuleBase {
func newBase(rule rule) RuleBase {
comment := ""
fileInherit, noNewPrivs, optional := false, false, false
idx := 0
for idx < len(rule) {
if rule[idx] == COMMENT.Tok() {
comment = " " + strings.Join(rule[idx+1:], " ")
break
if len(rule) > 0 {
if len(rule.Get(0)) > 0 && rule.Get(0)[0] == '#' {
// Line rule is a comment
rule = rule[1:]
comment = rule.GetString()
} else {
// Comma rule, with comment at the end
comment = rule[len(rule)-1].comment
}
idx++
}
switch {
case strings.Contains(comment, "file_inherit"):

View File

@ -31,6 +31,18 @@ type Capability struct {
Names []string
}
func newCapability(q Qualifier, rule rule) (Rule, error) {
names, err := toValues(CAPABILITY, "name", rule.GetString())
if err != nil {
return nil, err
}
return &Capability{
RuleBase: newBase(rule),
Qualifier: q,
Names: names,
}, nil
}
func newCapabilityFromLog(log map[string]string) Rule {
return &Capability{
RuleBase: newBaseFromLog(log),

View File

@ -4,7 +4,10 @@
package aa
import "fmt"
import (
"fmt"
"slices"
)
const CHANGEPROFILE Kind = "change_profile"
@ -22,6 +25,38 @@ type ChangeProfile struct {
ProfileName string
}
func newChangeProfile(q Qualifier, rule rule) (Rule, error) {
mode, exec, target := "", "", ""
if len(rule) > 0 {
if slices.Contains(requirements[CHANGEPROFILE]["mode"], rule.Get(0)) {
mode = rule.Get(0)
rule = rule[1:]
}
if len(rule) > 0 {
if rule.Get(0) != tokARROW {
exec = rule.Get(0)
if len(rule) > 2 {
if rule.Get(1) != tokARROW {
return nil, fmt.Errorf("missing '%s' in rule: %s", tokARROW, rule)
}
target = rule.Get(2)
}
} else {
if len(rule) > 1 {
target = rule.Get(1)
}
}
}
}
return &ChangeProfile{
RuleBase: newBase(rule),
Qualifier: q,
ExecMode: mode,
Exec: exec,
ProfileName: target,
}, nil
}
func newChangeProfileFromLog(log map[string]string) Rule {
return &ChangeProfile{
RuleBase: newBaseFromLog(log),

View File

@ -33,6 +33,25 @@ type Dbus struct {
PeerLabel string
}
func newDbus(q Qualifier, rule rule) (Rule, error) {
accesses, err := toAccess(DBUS, rule.GetString())
if err != nil {
return nil, err
}
return &Dbus{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Bus: rule.GetValuesAsString("bus"),
Name: rule.GetValuesAsString("name"),
Path: rule.GetValuesAsString("path"),
Interface: rule.GetValuesAsString("interface"),
Member: rule.GetValuesAsString("member"),
PeerName: rule.GetValues("peer").GetValuesAsString("name"),
PeerLabel: rule.GetValues("peer").GetValuesAsString("label"),
}, nil
}
func newDbusFromLog(log map[string]string) Rule {
name := ""
peerName := ""

View File

@ -46,6 +46,45 @@ type File struct {
Target string
}
func newFile(q Qualifier, rule rule) (Rule, error) {
path, access, target, owner := "", "", "", false
if len(rule) > 0 {
if rule.Get(0) == tokOWNER {
owner = true
rule = rule[1:]
}
if rule.Get(0) == FILE.Tok() {
rule = rule[1:]
}
r := rule.GetSlice()
size := len(r)
if size < 2 {
return nil, fmt.Errorf("missing file or access in rule: %s", rule)
}
path, access = r[0], r[1]
if size > 2 {
if r[2] != tokARROW {
return nil, fmt.Errorf("missing '%s' in rule: %s", tokARROW, rule)
}
target = r[3]
}
}
accesses, err := toAccess(FILE, access)
if err != nil {
return nil, err
}
return &File{
RuleBase: newBase(rule),
Qualifier: q,
Owner: owner,
Path: path,
Access: accesses,
Target: target,
}, nil
}
func newFileFromLog(log map[string]string) Rule {
accesses, err := toAccess("file-log", log["requested_mask"])
if err != nil {
@ -112,6 +151,40 @@ type Link struct {
Target string
}
func newLink(q Qualifier, rule rule) (Rule, error) {
owner, subset, path, target := false, false, "", ""
if len(rule) > 0 {
if rule.Get(0) == tokOWNER {
owner = true
rule = rule[1:]
}
if len(rule) > 0 && rule.Get(0) == tokSUBSET {
subset = true
rule = rule[1:]
}
r := rule.GetSlice()
size := len(r)
if size > 0 {
path = r[0]
}
if size > 2 {
if r[1] != tokARROW {
return nil, fmt.Errorf("missing '%s' in rule: %s", tokARROW, rule)
}
target = r[2]
}
}
return &Link{
RuleBase: newBase(rule),
Qualifier: q,
Owner: owner,
Subset: subset,
Path: path,
Target: target,
}, nil
}
func newLinkFromLog(log map[string]string) Rule {
return &Link{
RuleBase: newBaseFromLog(log),

View File

@ -23,6 +23,19 @@ type IOUring struct {
Label string
}
func newIOUring(q Qualifier, rule rule) (Rule, error) {
accesses, err := toAccess(IOURING, rule.GetString())
if err != nil {
return nil, err
}
return &IOUring{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Label: rule.GetValuesAsString("label"),
}, nil
}
func newIOUringFromLog(log map[string]string) Rule {
return &IOUring{
RuleBase: newBaseFromLog(log),

View File

@ -33,6 +33,17 @@ type MountConditions struct {
Options []string
}
func newMountConditions(rule rule) (MountConditions, error) {
options, err := toValues(MOUNT, "flags", rule.GetValuesAsString("options"))
if err != nil {
return MountConditions{}, err
}
return MountConditions{
FsType: rule.GetValuesAsString("fstype"),
Options: options,
}, nil
}
func newMountConditionsFromLog(log map[string]string) MountConditions {
if _, present := log["flags"]; present {
return MountConditions{
@ -62,6 +73,35 @@ type Mount struct {
MountPoint string
}
func newMount(q Qualifier, rule rule) (Rule, error) {
mount, src := "", ""
r := rule.GetSlice()
if len(r) > 0 {
if r[0] != tokARROW {
src = r[0]
r = r[1:]
}
if len(r) == 2 {
if r[0] != tokARROW {
return nil, fmt.Errorf("missing '%s' in rule: %s", tokARROW, rule)
}
mount = r[1]
}
}
conditions, err := newMountConditions(rule)
if err != nil {
return nil, err
}
return &Mount{
RuleBase: newBase(rule),
Qualifier: q,
MountConditions: conditions,
Source: src,
MountPoint: mount,
}, nil
}
func newMountFromLog(log map[string]string) Rule {
return &Mount{
RuleBase: newBaseFromLog(log),
@ -112,6 +152,24 @@ type Umount struct {
MountPoint string
}
func newUmount(q Qualifier, rule rule) (Rule, error) {
mount := ""
r := rule.GetSlice()
if len(r) > 0 {
mount = r[0]
}
conditions, err := newMountConditions(rule)
if err != nil {
return nil, err
}
return &Umount{
RuleBase: newBase(rule),
Qualifier: q,
MountConditions: conditions,
MountPoint: mount,
}, nil
}
func newUmountFromLog(log map[string]string) Rule {
return &Umount{
RuleBase: newBaseFromLog(log),
@ -158,6 +216,25 @@ type Remount struct {
MountPoint string
}
func newRemount(q Qualifier, rule rule) (Rule, error) {
mount := ""
r := rule.GetSlice()
if len(r) > 0 {
mount = r[0]
}
conditions, err := newMountConditions(rule)
if err != nil {
return nil, err
}
return &Remount{
RuleBase: newBase(rule),
Qualifier: q,
MountConditions: conditions,
MountPoint: mount,
}, nil
}
func newRemountFromLog(log map[string]string) Rule {
return &Remount{
RuleBase: newBaseFromLog(log),

View File

@ -31,6 +31,31 @@ type Mqueue struct {
Name string
}
func newMqueue(q Qualifier, rule rule) (Rule, error) {
access, name := "", ""
r := rule.GetSlice()
size := len(r)
if size > 0 {
access = strings.Join(r[:size-1], " ")
name = r[size-1]
if slices.Contains(requirements[MQUEUE]["access"], name) {
access += " " + name
}
}
accesses, err := toAccess(MQUEUE, access)
if err != nil {
return nil, err
}
return &Mqueue{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Type: rule.GetValuesAsString("type"),
Label: rule.GetValuesAsString("label"),
Name: name,
}, nil
}
func newMqueueFromLog(log map[string]string) Rule {
mqueueType := "posix"
if strings.Contains(log["class"], "posix") {

View File

@ -6,6 +6,7 @@ package aa
import (
"fmt"
"slices"
)
const NETWORK Kind = "network"
@ -70,6 +71,28 @@ type Network struct {
Protocol string
}
func newNetwork(q Qualifier, rule rule) (Rule, error) {
nType, protocol, domain := "", "", ""
r := rule.GetSlice()
if len(r) > 0 {
domain = r[0]
}
if len(r) >= 2 {
if slices.Contains(requirements[NETWORK]["type"], r[1]) {
nType = r[1]
} else if slices.Contains(requirements[NETWORK]["protocol"], r[1]) {
protocol = r[1]
}
}
return &Network{
RuleBase: newBase(rule),
Qualifier: q,
Domain: domain,
Type: nType,
Protocol: protocol,
}, nil
}
func newNetworkFromLog(log map[string]string) Rule {
return &Network{
RuleBase: newBaseFromLog(log),

View File

@ -30,6 +30,26 @@ const (
var (
newRuleMap = map[string]func(q Qualifier, r rule) (Rule, error){
ABI.Tok(): newAbi,
ALIAS.Tok(): newAlias,
ALL.Tok(): newAll,
"set": newRlimit,
USERNS.Tok(): newUserns,
CAPABILITY.Tok(): newCapability,
NETWORK.Tok(): newNetwork,
MOUNT.Tok(): newMount,
UMOUNT.Tok(): newUmount,
REMOUNT.Tok(): newRemount,
MQUEUE.Tok(): newMqueue,
IOURING.Tok(): newIOUring,
PIVOTROOT.Tok(): newPivotRoot,
CHANGEPROFILE.Tok(): newChangeProfile,
SIGNAL.Tok(): newSignal,
PTRACE.Tok(): newPtrace,
UNIX.Tok(): newUnix,
DBUS.Tok(): newDbus,
FILE.Tok(): newFile,
LINK.Tok(): newLink,
}
tok = map[Kind]string{

View File

@ -4,6 +4,8 @@
package aa
import "fmt"
const PIVOTROOT Kind = "pivot_root"
type PivotRoot struct {
@ -14,6 +16,30 @@ type PivotRoot struct {
TargetProfile string
}
func newPivotRoot(q Qualifier, rule rule) (Rule, error) {
newroot, target := "", ""
r := rule.GetSlice()
if len(r) > 0 {
if r[0] != tokARROW {
newroot = r[0]
r = r[1:]
}
if len(r) == 2 {
if r[0] != tokARROW {
return nil, fmt.Errorf("missing '%s' in rule: %s", tokARROW, rule)
}
target = r[1]
}
}
return &PivotRoot{
RuleBase: newBase(rule),
Qualifier: q,
OldRoot: rule.GetValuesAsString("oldroot"),
NewRoot: newroot,
TargetProfile: target,
}, nil
}
func newPivotRootFromLog(log map[string]string) Rule {
return &PivotRoot{
RuleBase: newBaseFromLog(log),

View File

@ -23,8 +23,8 @@ type Comment struct {
RuleBase
}
func newComment(rule []string) (Rule, error) {
base := newRule(rule)
func newComment(rule rule) (Rule, error) {
base := newBase(rule)
base.IsLineRule = true
return &Comment{RuleBase: base}, nil
}
@ -41,10 +41,6 @@ func (r *Comment) String() string {
return renderTemplate(r.Kind(), r)
}
func (r *Comment) IsPreamble() bool {
return true
}
func (r *Comment) Constraint() constraint {
return anyKind
}
@ -59,16 +55,13 @@ type Abi struct {
IsMagic bool
}
func newAbi(rule []string) (Rule, error) {
func newAbi(q Qualifier, rule rule) (Rule, error) {
var magic bool
if len(rule) > 0 && rule[0] == ABI.Tok() {
rule = rule[1:]
}
if len(rule) != 1 {
return nil, fmt.Errorf("invalid abi format: %s", rule)
}
path := rule[0]
path := rule.Get(0)
switch {
case path[0] == '"':
magic = false
@ -78,7 +71,7 @@ func newAbi(rule []string) (Rule, error) {
return nil, fmt.Errorf("invalid path %s in rule: %s", path, rule)
}
return &Abi{
RuleBase: newRule(rule),
RuleBase: newBase(rule),
Path: strings.Trim(path, "\"<>"),
IsMagic: magic,
}, nil
@ -114,20 +107,17 @@ type Alias struct {
RewrittenPath string
}
func newAlias(rule []string) (Rule, error) {
if len(rule) > 0 && rule[0] == ALIAS.Tok() {
rule = rule[1:]
}
func newAlias(q Qualifier, rule rule) (Rule, error) {
if len(rule) != 3 {
return nil, fmt.Errorf("invalid alias format: %s", rule)
}
if rule[1] != tokARROW {
if rule.Get(1) != tokARROW {
return nil, fmt.Errorf("invalid alias format, missing %s in: %s", tokARROW, rule)
}
return &Alias{
RuleBase: newRule(rule),
Path: rule[0],
RewrittenPath: rule[2],
RuleBase: newBase(rule),
Path: rule.Get(0),
RewrittenPath: rule.Get(2),
}, nil
}
@ -162,25 +152,22 @@ type Include struct {
IsMagic bool
}
func newInclude(rule []string) (Rule, error) {
func newInclude(rule rule) (Rule, error) {
var magic bool
var ifexists bool
if len(rule) > 0 && rule[0] == INCLUDE.Tok() {
rule = rule[1:]
}
size := len(rule)
if size == 0 {
return nil, fmt.Errorf("invalid include format: %v", rule)
}
if size >= 3 && strings.Join(rule[:2], " ") == tokIFEXISTS {
r := rule.GetSlice()
if size >= 3 && strings.Join(r[:2], " ") == tokIFEXISTS {
ifexists = true
rule = rule[2:]
r = r[2:]
}
path := rule[0]
path := r[0]
switch {
case path[0] == '"':
magic = false
@ -190,7 +177,7 @@ func newInclude(rule []string) (Rule, error) {
return nil, fmt.Errorf("invalid path format: %v", path)
}
return &Include{
RuleBase: newRule(rule),
RuleBase: newBase(rule),
IfExists: ifexists,
Path: strings.Trim(path, "\"<>"),
IsMagic: magic,
@ -238,29 +225,27 @@ type Variable struct {
Define bool
}
func newVariable(rule []string) (Rule, error) {
func newVariableFromRule(rule rule) (Rule, error) {
var define bool
var values []string
if len(rule) < 3 {
return nil, fmt.Errorf("invalid variable format: %v", rule)
}
name := strings.Trim(rule[0], VARIABLE.Tok()+"}")
switch rule[1] {
r := rule.GetSlice()
name := strings.Trim(rule.Get(0), VARIABLE.Tok()+"}")
switch rule.Get(1) {
case tokEQUAL:
define = true
values = tokensStripComment(rule[2:])
case tokPLUS:
if rule[2] != tokEQUAL {
return nil, fmt.Errorf("invalid operator in variable: %v", rule)
}
values = r[2:]
case tokPLUS + tokEQUAL:
define = false
values = tokensStripComment(rule[3:])
values = r[2:]
default:
return nil, fmt.Errorf("invalid operator in variable: %v", rule)
}
return &Variable{
RuleBase: newRule(rule),
RuleBase: newBase(rule),
Name: name,
Values: values,
Define: define,

View File

@ -43,48 +43,29 @@ type Header struct {
Flags []string
}
func newHeader(rule []string) (Header, error) {
func newHeader(rule rule) (Header, error) {
if len(rule) == 0 {
return Header{}, nil
}
if rule[len(rule)-1] == "{" {
rule = rule[:len(rule)-1]
}
if rule[0] == PROFILE.Tok() {
if rule.Get(0) == PROFILE.Tok() {
rule = rule[1:]
}
delete := []int{}
flags := []string{}
attributes := make(map[string]string)
for idx, token := range rule {
if item, ok := strings.CutPrefix(token, tokFLAGS+"="); ok {
flags = tokenToSlice(item)
delete = append(delete, idx)
} else if item, ok := strings.CutPrefix(token, tokATTRIBUTES+"="); ok {
for _, m := range tokenToSlice(item) {
kv := strings.SplitN(m, "=", 2)
attributes[kv[0]] = kv[1]
}
delete = append(delete, idx)
}
}
for i := len(delete) - 1; i >= 0; i-- {
rule = slices.Delete(rule, delete[i], delete[i]+1)
}
name, attachments := "", []string{}
if len(rule) >= 1 {
name = rule[0]
name = rule.Get(0)
if len(rule) > 1 {
attachments = rule[1:]
attachments = rule[1:].GetSlice()
}
}
attributes := make(map[string]string)
for k, v := range rule.GetValues(tokATTRIBUTES).GetAsMap() {
attributes[k] = strings.Join(v, "")
}
return Header{
Name: name,
Attachments: attachments,
Attributes: attributes,
Flags: flags,
Flags: rule.GetValuesAsSlice(tokFLAGS),
}, nil
}

View File

@ -25,6 +25,19 @@ type Ptrace struct {
Peer string
}
func newPtrace(q Qualifier, rule rule) (Rule, error) {
accesses, err := toAccess(PTRACE, rule.GetString())
if err != nil {
return nil, err
}
return &Ptrace{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Peer: rule.GetValuesAsString("peer"),
}, nil
}
func newPtraceFromLog(log map[string]string) Rule {
return &Ptrace{
RuleBase: newBaseFromLog(log),

View File

@ -27,6 +27,21 @@ type Rlimit struct {
Value string
}
func newRlimit(q Qualifier, rule rule) (Rule, error) {
if len(rule) != 4 {
return nil, fmt.Errorf("invalid set format: %s", rule)
}
if rule.Get(0) != RLIMIT.Tok() {
return nil, fmt.Errorf("invalid rlimit format: %s", rule)
}
return &Rlimit{
RuleBase: newBase(rule),
Key: rule.Get(1),
Op: rule.Get(2),
Value: rule.Get(3),
}, nil
}
func newRlimitFromLog(log map[string]string) Rule {
return &Rlimit{
RuleBase: newBaseFromLog(log),

View File

@ -39,6 +39,24 @@ type Signal struct {
Peer string
}
func newSignal(q Qualifier, rule rule) (Rule, error) {
accesses, err := toAccess(SIGNAL, rule.GetString())
if err != nil {
return nil, err
}
set, err := toValues(SIGNAL, "set", rule.GetValuesAsString("set"))
if err != nil {
return nil, err
}
return &Signal{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Set: set,
Peer: rule.GetValuesAsString("peer"),
}, nil
}
func newSignalFromLog(log map[string]string) Rule {
return &Signal{
RuleBase: newBaseFromLog(log),

View File

@ -34,6 +34,26 @@ type Unix struct {
PeerAddr string
}
func newUnix(q Qualifier, rule rule) (Rule, error) {
accesses, err := toAccess(UNIX, rule.GetString())
if err != nil {
return nil, err
}
return &Unix{
RuleBase: newBase(rule),
Qualifier: q,
Access: accesses,
Type: rule.GetValuesAsString("type"),
Protocol: rule.GetValuesAsString("protocol"),
Address: rule.GetValuesAsString("addr"),
Label: rule.GetValuesAsString("label"),
Attr: rule.GetValuesAsString("attr"),
Opt: rule.GetValuesAsString("opt"),
PeerLabel: rule.GetValues("peer").GetValuesAsString("label"),
PeerAddr: rule.GetValues("peer").GetValuesAsString("addr"),
}, nil
}
func newUnixFromLog(log map[string]string) Rule {
return &Unix{
RuleBase: newBaseFromLog(log),

View File

@ -14,6 +14,26 @@ type Userns struct {
Create bool
}
func newUserns(q Qualifier, rule rule) (Rule, error) {
var create bool
switch len(rule) {
case 0:
create = true
case 1:
if rule.Get(0) != "create" {
return nil, fmt.Errorf("invalid userns format: %s", rule)
}
create = true
default:
return nil, fmt.Errorf("invalid userns format: %s", rule)
}
return &Userns{
RuleBase: newBase(rule),
Qualifier: q,
Create: create,
}, nil
}
func newUsernsFromLog(log map[string]string) Rule {
return &Userns{
RuleBase: newBaseFromLog(log),