From 83bc7d3adeb6507747eeebdd87037d50b5955696 Mon Sep 17 00:00:00 2001 From: Alexandre Pujol Date: Thu, 26 Sep 2024 22:15:46 +0100 Subject: [PATCH] feat(aa-log): minor improvment in rule generation & formatting. --- pkg/aa/base.go | 2 +- pkg/aa/file.go | 21 +++++++++++++++++++++ pkg/aa/mount.go | 8 ++++---- pkg/aa/parse.go | 16 +++++++++++----- pkg/aa/profile.go | 38 ++++++++++++++++++++++---------------- pkg/aa/rules.go | 40 +++++++++++++++++++++++++++++++++++----- pkg/aa/template.go | 6 +++--- 7 files changed, 97 insertions(+), 34 deletions(-) diff --git a/pkg/aa/base.go b/pkg/aa/base.go index 96746652..60952511 100644 --- a/pkg/aa/base.go +++ b/pkg/aa/base.go @@ -9,8 +9,8 @@ import ( ) type Base struct { - IsLineRule bool Comment string + IsLineRule bool NoNewPrivs bool FileInherit bool Optional bool diff --git a/pkg/aa/file.go b/pkg/aa/file.go index 50b23bae..549ff66d 100644 --- a/pkg/aa/file.go +++ b/pkg/aa/file.go @@ -118,6 +118,21 @@ func (r *File) String() string { } func (r *File) Validate() error { + if !isAARE(r.Path) { + return fmt.Errorf("'%s' is not a valid AARE", r.Path) + } + for _, v := range r.Access { + if v == "" { + continue + } + if !slices.Contains(requirements[r.Kind()]["access"], v) || + !slices.Contains(requirements[r.Kind()]["transition"], v) { + return fmt.Errorf("invalid mode '%s'", v) + } + } + if r.Target != "" && !isAARE(r.Target) { + return fmt.Errorf("'%s' is not a valid AARE", r.Target) + } return nil } @@ -260,6 +275,12 @@ func (r *Link) String() string { } func (r *Link) Validate() error { + if !isAARE(r.Path) { + return fmt.Errorf("'%s' is not a valid AARE", r.Path) + } + if !isAARE(r.Target) { + return fmt.Errorf("'%s' is not a valid AARE", r.Target) + } return nil } diff --git a/pkg/aa/mount.go b/pkg/aa/mount.go index 914efc2f..ad83801c 100644 --- a/pkg/aa/mount.go +++ b/pkg/aa/mount.go @@ -17,10 +17,10 @@ const ( func init() { requirements[MOUNT] = requirement{ "flags": { - "acl", "async", "atime", "ro", "rw", "bind", "rbind", "dev", - "diratime", "dirsync", "exec", "iversion", "loud", "mand", "move", - "noacl", "noatime", "nodev", "nodiratime", "noexec", "noiversion", - "nomand", "norelatime", "nosuid", "nouser", "private", "relatime", + "ro", "rw", "acl", "async", "atime", "bind", "dev", "diratime", + "dirsync", "exec", "iversion", "loud", "mand", "move", "noacl", + "noatime", "nodev", "nodiratime", "noexec", "noiversion", "nomand", + "norelatime", "nosuid", "nouser", "private", "rbind", "relatime", "remount", "rprivate", "rshared", "rslave", "runbindable", "shared", "silent", "slave", "strictatime", "suid", "sync", "unbindable", "user", "verbose", diff --git a/pkg/aa/parse.go b/pkg/aa/parse.go index 88808a37..b7fb5283 100644 --- a/pkg/aa/parse.go +++ b/pkg/aa/parse.go @@ -495,9 +495,15 @@ func (r rule) String() string { } func isAARE(str string) bool { - return strings.HasPrefix(str, "@") || - strings.HasPrefix(str, "/") || - strings.HasPrefix(str, "\"") + if len(str) < 1 { + return false + } + switch str[0] { + case '@', '/', '"': + return true + default: + return false + } } // Convert a slice of internal rules to a slice of ApparmorRule. @@ -652,8 +658,8 @@ done: } // Parse apparmor profile rules by paragraphs -func ParseRules(input string) ([]Rules, []string, error) { - paragraphRules := []Rules{} +func ParseRules(input string) (ParaRules, []string, error) { + paragraphRules := ParaRules{} paragraphs := []string{} for _, match := range regParagraph.FindAllStringSubmatch(input, -1) { diff --git a/pkg/aa/profile.go b/pkg/aa/profile.go index ec506897..30e8b106 100644 --- a/pkg/aa/profile.go +++ b/pkg/aa/profile.go @@ -139,16 +139,18 @@ func (p *Profile) GetAttachments() string { var ( newLogMap = map[string]func(log map[string]string) Rule{ + // class "rlimits": newRlimitFromLog, - "cap": newCapabilityFromLog, - "io_uring": newIOUringFromLog, - "signal": newSignalFromLog, - "ptrace": newPtraceFromLog, "namespace": newUsernsFromLog, - "unix": newUnixFromLog, - "dbus": newDbusFromLog, + "cap": newCapabilityFromLog, + "net": newNetworkFromLog, "posix_mqueue": newMqueueFromLog, "sysv_mqueue": newMqueueFromLog, + "signal": newSignalFromLog, + "ptrace": newPtraceFromLog, + "unix": newUnixFromLog, + "io_uring": newIOUringFromLog, + "dbus": newDbusFromLog, "mount": func(log map[string]string) Rule { if strings.Contains(log["flags"], "remount") { return newRemountFromLog(log) @@ -156,7 +158,6 @@ var ( newRule := newLogMountMap[log["operation"]] return newRule(log) }, - "net": newNetworkFromLog, "file": func(log map[string]string) Rule { if log["operation"] == "change_onexec" { return newChangeProfileFromLog(log) @@ -164,14 +165,19 @@ var ( return newFileFromLog(log) } }, - "exec": newFileFromLog, - "getattr": newFileFromLog, - "mkdir": newFileFromLog, - "mknod": newFileFromLog, - "open": newFileFromLog, - "rename_src": newFileFromLog, - "truncate": newFileFromLog, - "unlink": newFileFromLog, + // operation + "capable": newCapabilityFromLog, + "chmod": newFileFromLog, + "exec": newFileFromLog, + "getattr": newFileFromLog, + "link": newFileFromLog, + "mkdir": newFileFromLog, + "mknod": newFileFromLog, + "open": newFileFromLog, + "rename_dest": newFileFromLog, + "rename_src": newFileFromLog, + "truncate": newFileFromLog, + "unlink": newFileFromLog, } newLogMountMap = map[string]func(log map[string]string) Rule{ "mount": newMountFromLog, @@ -213,7 +219,7 @@ func (p *Profile) AddRule(log map[string]string) { case strings.Contains(log["operation"], "dbus"): p.Rules = append(p.Rules, newDbusFromLog(log)) default: - fmt.Printf("unknown log type: %s", log["operation"]) + fmt.Printf("unknown log type: %s\n", log["operation"]) } } } diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index 63741227..8e8ed5d9 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -94,7 +94,7 @@ func (r Rules) Delete(i int) Rules { } func (r Rules) DeleteKind(kind Kind) Rules { - res := make(Rules, 0) + res := make(Rules, 0, len(r)) for _, rule := range r { if rule == nil { continue @@ -106,8 +106,8 @@ func (r Rules) DeleteKind(kind Kind) Rules { return res } -func (r Rules) Filter(filter Kind) Rules { - res := make(Rules, 0) +func (r Rules) FilterOut(filter Kind) Rules { + res := make(Rules, 0, len(r)) for _, rule := range r { if rule == nil { continue @@ -119,8 +119,21 @@ func (r Rules) Filter(filter Kind) Rules { return res } +func (r Rules) Filter(filter Kind) Rules { + res := make(Rules, 0, len(r)) + for _, rule := range r { + if rule == nil { + continue + } + if rule.Kind() == filter { + res = append(res, rule) + } + } + return res +} + func (r Rules) GetVariables() []*Variable { - res := make([]*Variable, 0) + res := make([]*Variable, 0, len(r)) for _, rule := range r { switch rule := rule.(type) { case *Variable: @@ -131,7 +144,7 @@ func (r Rules) GetVariables() []*Variable { } func (r Rules) GetIncludes() []*Include { - res := make([]*Include, 0) + res := make([]*Include, 0, len(r)) for _, rule := range r { switch rule := rule.(type) { case *Include: @@ -247,3 +260,20 @@ func (r Rules) Format() Rules { r.setPaddings(paddingsIndex, paddingsMaxLen) return r } + +// ParaRules is a slice of Rules grouped by paragraph +type ParaRules []Rules + +func (r ParaRules) Flatten() Rules { + totalLen := 0 + for i := range r { + totalLen += len(r[i]) + } + + res := make(Rules, 0, totalLen) + for i := range r { + res = append(res, r[i]...) + } + + return res +} diff --git a/pkg/aa/template.go b/pkg/aa/template.go index 18f07bc2..92c10b46 100644 --- a/pkg/aa/template.go +++ b/pkg/aa/template.go @@ -138,7 +138,7 @@ var ( // The order AARE should be sorted stringAlphabet = []byte( - "!\"#$%&'*(){}[]+,-./:;<=>?@\\^_`|~0123456789abcdefghijklmnopqrstuvwxyz", + "!\"#$%&'*(){}[]@+,-./:;<=>?\\^_`|~0123456789abcdefghijklmnopqrstuvwxyz", ) stringWeights = generateWeights(stringAlphabet) @@ -232,11 +232,11 @@ func cjoin(i any) string { } } -func kindOf(i any) string { +func kindOf(i Rule) string { if i == nil { return "" } - return i.(Rule).Kind().String() + return i.Kind().String() } func setindent(i string) string {