// apparmor.d - Full set of apparmor profiles // Copyright (C) 2021-2024 Alexandre Pujol // SPDX-License-Identifier: GPL-2.0-only package aa import ( "fmt" "strings" ) const ( ABI Kind = "abi" ALIAS Kind = "alias" INCLUDE Kind = "include" VARIABLE Kind = "variable" COMMENT Kind = "comment" tokIFEXISTS = "if exists" ) type Comment struct { Base } func newComment(rule rule) (Rule, error) { base := newBase(rule) base.IsLineRule = true return &Comment{Base: base}, nil } func (r *Comment) Kind() Kind { return COMMENT } func (r *Comment) Constraint() Constraint { return AnyRule } func (r *Comment) String() string { return renderTemplate(r.Kind(), r) } func (r *Comment) Validate() error { return nil } func (r *Comment) Compare(other Rule) int { return 0 // Comments are always equal to each other as they are not compared } func (r *Comment) Merge(other Rule) bool { return false // Never merge comments } func (r *Comment) Lengths() []int { return []int{} // No len for comments } func (r *Comment) setPaddings(max []int) {} // No paddings for comments type Abi struct { Base Path string IsMagic bool } func newAbi(q Qualifier, rule rule) (Rule, error) { var magic bool if len(rule) != 1 { return nil, fmt.Errorf("invalid abi format: %s", rule) } path := rule.Get(0) switch { case path[0] == '"': magic = false case path[0] == '<': magic = true default: return nil, fmt.Errorf("invalid path %s in rule: %s", path, rule) } return &Abi{ Base: newBase(rule), Path: strings.Trim(path, "\"<>"), IsMagic: magic, }, nil } func (r *Abi) Kind() Kind { return ABI } func (r *Abi) Constraint() Constraint { return PreambleRule } func (r *Abi) String() string { return renderTemplate(r.Kind(), r) } func (r *Abi) Validate() error { return nil } func (r *Abi) Compare(other Rule) int { o, _ := other.(*Abi) if res := compare(r.Path, o.Path); res != 0 { return res } return compare(r.IsMagic, o.IsMagic) } func (r *Abi) Merge(other Rule) bool { return false // Never merge abi } func (r *Abi) Lengths() []int { return []int{} // No len for abi } func (r *Abi) setPaddings(max []int) {} // No paddings for abi type Alias struct { Base Path string RewrittenPath string } func newAlias(q Qualifier, rule rule) (Rule, error) { if len(rule) != 3 { return nil, fmt.Errorf("invalid alias format: %s", rule) } if rule.Get(1) != tokARROW { return nil, fmt.Errorf("invalid alias format, missing %s in: %s", tokARROW, rule) } return &Alias{ Base: newBase(rule), Path: rule.Get(0), RewrittenPath: rule.Get(2), }, nil } func (r *Alias) Kind() Kind { return ALIAS } func (r *Alias) Constraint() Constraint { return PreambleRule } func (r *Alias) String() string { return renderTemplate(r.Kind(), r) } func (r *Alias) Validate() error { return nil } func (r *Alias) Compare(other Rule) int { o, _ := other.(*Alias) if res := compare(r.Path, o.Path); res != 0 { return res } return compare(r.RewrittenPath, o.RewrittenPath) } func (r *Alias) Merge(other Rule) bool { return false // Never merge alias } func (r *Alias) Lengths() []int { return []int{} // No len for alias } func (r *Alias) setPaddings(max []int) {} // No paddings for alias type Include struct { Base IfExists bool Path string IsMagic bool } func newInclude(rule rule) (Rule, error) { var magic bool var ifexists bool size := len(rule) if size == 0 { return nil, fmt.Errorf("invalid include format: %v", rule) } r := rule.GetSlice() if size >= 3 && strings.Join(r[:2], " ") == tokIFEXISTS { ifexists = true r = r[2:] } path := r[0] switch { case path[0] == '"': magic = false case path[0] == '<': magic = true default: return nil, fmt.Errorf("invalid path format: %v", path) } return &Include{ Base: newBase(rule), IfExists: ifexists, Path: strings.Trim(path, "\"<>"), IsMagic: magic, }, nil } func (r *Include) Kind() Kind { return INCLUDE } func (r *Include) Constraint() Constraint { return AnyRule } func (r *Include) String() string { return renderTemplate(r.Kind(), r) } func (r *Include) Validate() error { return nil } func (r *Include) Compare(other Rule) int { const base = "abstractions/base" o, _ := other.(*Include) if res := compare(r.Path, o.Path); res != 0 { if r.Path == base { return -1 } if o.Path == base { return 1 } return res } if res := compare(r.IsMagic, o.IsMagic); res != 0 { return res } return compare(r.IfExists, o.IfExists) } func (r *Include) Merge(other Rule) bool { return false // Never merge include } func (r *Include) Lengths() []int { return []int{} // No len for include } func (r *Include) setPaddings(max []int) {} // No paddings for include type Variable struct { Base Name string Values []string Define bool } func newVariable(rule rule) (Rule, error) { var define bool var values []string if len(rule) < 3 { return nil, fmt.Errorf("invalid variable format: %v", rule) } r := rule.GetSlice() name := strings.Trim(rule.Get(0), VARIABLE.Tok()+"}") switch rule.Get(1) { case tokEQUAL: define = true values = r[2:] case tokPLUS + tokEQUAL: define = false values = r[2:] default: return nil, fmt.Errorf("invalid operator in variable: %v", rule) } return &Variable{ Base: newBase(rule), Name: name, Values: values, Define: define, }, nil } func (r *Variable) Kind() Kind { return VARIABLE } func (r *Variable) Constraint() Constraint { return PreambleRule } func (r *Variable) String() string { return renderTemplate(r.Kind(), r) } func (r *Variable) Validate() error { return nil } func (r *Variable) Compare(other Rule) int { o, _ := other.(*Variable) if res := compare(r.Name, o.Name); res != 0 { return res } if res := compare(r.Define, o.Define); res != 0 { return res } return compare(r.Values, o.Values) } func (r *Variable) Merge(other Rule) bool { o, _ := other.(*Variable) if r.Name == o.Name && r.Define == o.Define { r.Values = merge(r.Kind(), "access", r.Values, o.Values) b := &r.Base return b.merge(o.Base) } return false } func (r *Variable) Lengths() []int { return []int{} // No len for variable } func (r *Variable) setPaddings(max []int) {} // No paddings for variable