feat(aa): improve apparmor struct.

This commit is contained in:
Alexandre Pujol 2024-04-14 23:58:34 +01:00
parent ea1736083a
commit ab4feda5ba
No known key found for this signature in database
GPG Key ID: C5469996F0DF68EC
28 changed files with 638 additions and 496 deletions

View File

@ -5,23 +5,25 @@
package aa
type Capability struct {
Rule
Qualifier
Name string
}
func CapabilityFromLog(log map[string]string) ApparmorRule {
func newCapabilityFromLog(log map[string]string) *Capability {
return &Capability{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Name: log["capname"],
}
}
func (r *Capability) Less(other any) bool {
o, _ := other.(*Capability)
if r.Name == o.Name {
return r.Qualifier.Less(o.Qualifier)
if r.Name != o.Name {
return r.Name < o.Name
}
return r.Name < o.Name
return r.Qualifier.Less(o.Qualifier)
}
func (r *Capability) Equals(other any) bool {

View File

@ -5,15 +5,17 @@
package aa
type ChangeProfile struct {
Rule
Qualifier
ExecMode string
Exec string
ProfileName string
}
func ChangeProfileFromLog(log map[string]string) ApparmorRule {
func newChangeProfileFromLog(log map[string]string) *ChangeProfile {
return &ChangeProfile{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
ExecMode: log["mode"],
Exec: log["exec"],
ProfileName: log["target"],
@ -22,16 +24,20 @@ func ChangeProfileFromLog(log map[string]string) ApparmorRule {
func (r *ChangeProfile) Less(other any) bool {
o, _ := other.(*ChangeProfile)
if r.ExecMode == o.ExecMode {
if r.Exec == o.Exec {
return r.ProfileName < o.ProfileName
}
if r.ExecMode != o.ExecMode {
return r.ExecMode < o.ExecMode
}
if r.Exec != o.Exec {
return r.Exec < o.Exec
}
return r.ExecMode < o.ExecMode
if r.ProfileName != o.ProfileName {
return r.ProfileName < o.ProfileName
}
return r.Qualifier.Less(o.Qualifier)
}
func (r *ChangeProfile) Equals(other any) bool {
o, _ := other.(*ChangeProfile)
return r.ExecMode == o.ExecMode && r.Exec == o.Exec && r.ProfileName == o.ProfileName
return r.ExecMode == o.ExecMode && r.Exec == o.Exec &&
r.ProfileName == o.ProfileName && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -71,13 +71,13 @@ var (
"flags": "rw, rbind",
}
mount1 = &Mount{
Qualifier: Qualifier{Comment: "failed perms check"},
Rule: Rule{Comment: "failed perms check"},
MountConditions: MountConditions{FsType: "overlay"},
Source: "overlay",
MountPoint: "/var/lib/docker/overlay2/opaque-bug-check1209538631/merged/",
}
mount2 = &Mount{
Qualifier: Qualifier{Comment: "failed perms check"},
Rule: Rule{Comment: "failed perms check"},
MountConditions: MountConditions{Options: []string{"rw", "rbind"}},
Source: "/oldroot/dev/tty",
MountPoint: "/newroot/dev/tty",
@ -197,17 +197,17 @@ var (
"protocol": "0",
}
unix1 = &Unix{
Access: "send receive",
Type: "stream",
Protocol: "0",
Address: "none",
Peer: "dbus-daemon",
PeerAddr: "@/tmp/dbus-AaKMpxzC4k",
Access: "send receive",
Type: "stream",
Protocol: "0",
Address: "none",
PeerAddr: "@/tmp/dbus-AaKMpxzC4k",
PeerLabel: "dbus-daemon",
}
unix2 = &Unix{
Qualifier: Qualifier{FileInherit: true},
Access: "receive",
Type: "stream",
Rule: Rule{FileInherit: true},
Access: "receive",
Type: "stream",
}
// Dbus
@ -236,11 +236,11 @@ var (
dbus1 = &Dbus{
Access: "receive",
Bus: "session",
Name: ":1.15",
Path: "/org/gtk/vfs/metadata",
Interface: "org.gtk.vfs.Metadata",
Member: "Remove",
Label: "tracker-extract",
PeerName: ":1.15",
PeerLabel: "tracker-extract",
}
dbus2 = &Dbus{
Access: "bind",
@ -285,8 +285,9 @@ var (
}
file1 = &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"}
file2 = &File{
Qualifier: Qualifier{Owner: true, NoNewPrivs: true},
Path: "@{PROC}/4163/cgroup",
Access: "r",
Rule: Rule{NoNewPrivs: true},
Owner: true,
Path: "@{PROC}/4163/cgroup",
Access: "r",
}
)

View File

@ -5,6 +5,7 @@
package aa
type Dbus struct {
Rule
Qualifier
Access string
Bus string
@ -12,45 +13,58 @@ type Dbus struct {
Path string
Interface string
Member string
Label string
PeerName string
PeerLabel string
}
func DbusFromLog(log map[string]string) ApparmorRule {
func newDbusFromLog(log map[string]string) *Dbus {
name := ""
peerName := ""
if log["mask"] == "bind" {
name = log["name"]
} else {
peerName = log["name"]
}
return &Dbus{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: log["mask"],
Bus: log["bus"],
Name: log["name"],
Name: name,
Path: log["path"],
Interface: log["interface"],
Member: log["member"],
Label: log["peer_label"],
PeerName: peerName,
PeerLabel: log["peer_label"],
}
}
func (r *Dbus) Less(other any) bool {
o, _ := other.(*Dbus)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
if r.Bus == o.Bus {
if r.Name == o.Name {
if r.Path == o.Path {
if r.Interface == o.Interface {
if r.Member == o.Member {
return r.Label < o.Label
}
return r.Member < o.Member
}
return r.Interface < o.Interface
}
return r.Path < o.Path
}
return r.Name < o.Name
}
return r.Bus < o.Bus
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Bus != o.Bus {
return r.Bus < o.Bus
}
if r.Name != o.Name {
return r.Name < o.Name
}
if r.Path != o.Path {
return r.Path < o.Path
}
if r.Interface != o.Interface {
return r.Interface < o.Interface
}
if r.Member != o.Member {
return r.Member < o.Member
}
if r.PeerName != o.PeerName {
return r.PeerName < o.PeerName
}
if r.PeerLabel != o.PeerLabel {
return r.PeerLabel < o.PeerLabel
}
return r.Qualifier.Less(o.Qualifier)
}
@ -58,5 +72,6 @@ func (r *Dbus) Equals(other any) bool {
o, _ := other.(*Dbus)
return r.Access == o.Access && r.Bus == o.Bus && r.Name == o.Name &&
r.Path == o.Path && r.Interface == o.Interface &&
r.Member == o.Member && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier)
r.Member == o.Member && r.PeerName == o.PeerName &&
r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -5,15 +5,26 @@
package aa
type File struct {
Rule
Qualifier
Owner bool
Path string
Access string
Target string
}
func FileFromLog(log map[string]string) ApparmorRule {
func newFileFromLog(log map[string]string) *File {
owner := false
fsuid, hasFsUID := log["fsuid"]
ouid, hasOuUID := log["ouid"]
isDbus := strings.Contains(log["operation"], "dbus")
if hasFsUID && hasOuUID && fsuid == ouid && ouid != "0" && !isDbus {
owner = true
}
return &File{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Owner: owner,
Path: log["name"],
Access: toAccess(log["requested_mask"]),
Target: log["target"],
@ -24,23 +35,26 @@ func (r *File) Less(other any) bool {
o, _ := other.(*File)
letterR := getLetterIn(fileAlphabet, r.Path)
letterO := getLetterIn(fileAlphabet, o.Path)
if fileWeights[letterR] == fileWeights[letterO] || letterR == "" || letterO == "" {
if r.Qualifier.Equals(o.Qualifier) {
if r.Path == o.Path {
if r.Access == o.Access {
return r.Target < o.Target
}
return r.Access < o.Access
}
return r.Path < o.Path
}
return r.Qualifier.Less(o.Qualifier)
if fileWeights[letterR] != fileWeights[letterO] && letterR != "" && letterO != "" {
return fileWeights[letterR] < fileWeights[letterO]
}
return fileWeights[letterR] < fileWeights[letterO]
if r.Path != o.Path {
return r.Path < o.Path
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Target != o.Target {
return r.Target < o.Target
}
if o.Owner != r.Owner {
return r.Owner
}
return r.Qualifier.Less(o.Qualifier)
}
func (r *File) Equals(other any) bool {
o, _ := other.(*File)
return r.Path == o.Path && r.Access == o.Access &&
return r.Path == o.Path && r.Access == o.Access && r.Owner == o.Owner &&
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -1,28 +0,0 @@
// 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
type Include struct {
IfExists bool
Path string
IsMagic bool
}
func (r *Include) Less(other any) bool {
o, _ := other.(*Include)
if r.Path == o.Path {
if r.IsMagic == o.IsMagic {
return r.IfExists
}
return r.IsMagic
}
return r.Path < o.Path
}
func (r *Include) Equals(other any) bool {
o, _ := other.(*Include)
return r.Path == o.Path && r.IsMagic == o.IsMagic &&
r.IfExists == o.IfExists
}

View File

@ -5,19 +5,29 @@
package aa
type IOUring struct {
Rule
Qualifier
Access string
Label string
}
func newIOUringFromLog(log map[string]string) *IOUring {
return &IOUring{
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested"]),
Label: log["label"],
}
}
func (r *IOUring) Less(other any) bool {
o, _ := other.(*IOUring)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
return r.Label < o.Label
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Label != o.Label {
return r.Label < o.Label
}
return r.Qualifier.Less(o.Qualifier)
}

View File

@ -15,7 +15,7 @@ type MountConditions struct {
Options []string
}
func MountConditionsFromLog(log map[string]string) MountConditions {
func newMountConditionsFromLog(log map[string]string) MountConditions {
if _, present := log["flags"]; present {
return MountConditions{
FsType: log["fstype"],
@ -26,10 +26,10 @@ func MountConditionsFromLog(log map[string]string) MountConditions {
}
func (m MountConditions) Less(other MountConditions) bool {
if m.FsType == other.FsType {
return len(m.Options) < len(other.Options)
if m.FsType != other.FsType {
return m.FsType < other.FsType
}
return m.FsType < other.FsType
return len(m.Options) < len(other.Options)
}
func (m MountConditions) Equals(other MountConditions) bool {
@ -37,16 +37,18 @@ func (m MountConditions) Equals(other MountConditions) bool {
}
type Mount struct {
Rule
Qualifier
MountConditions
Source string
MountPoint string
}
func MountFromLog(log map[string]string) ApparmorRule {
func newMountFromLog(log map[string]string) *Mount {
return &Mount{
Qualifier: NewQualifierFromLog(log),
MountConditions: MountConditionsFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
MountConditions: newMountConditionsFromLog(log),
Source: log["srcname"],
MountPoint: log["name"],
}
@ -54,15 +56,15 @@ func MountFromLog(log map[string]string) ApparmorRule {
func (r *Mount) Less(other any) bool {
o, _ := other.(*Mount)
if r.Qualifier.Equals(o.Qualifier) {
if r.Source == o.Source {
if r.MountPoint == o.MountPoint {
return r.MountConditions.Less(o.MountConditions)
}
return r.MountPoint < o.MountPoint
}
if r.Source != o.Source {
return r.Source < o.Source
}
if r.MountPoint != o.MountPoint {
return r.MountPoint < o.MountPoint
}
if r.MountConditions.Equals(o.MountConditions) {
return r.MountConditions.Less(o.MountConditions)
}
return r.Qualifier.Less(o.Qualifier)
}
@ -74,27 +76,29 @@ func (r *Mount) Equals(other any) bool {
}
type Umount struct {
Rule
Qualifier
MountConditions
MountPoint string
}
func UmountFromLog(log map[string]string) ApparmorRule {
func newUmountFromLog(log map[string]string) *Umount {
return &Umount{
Qualifier: NewQualifierFromLog(log),
MountConditions: MountConditionsFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
MountConditions: newMountConditionsFromLog(log),
MountPoint: log["name"],
}
}
func (r *Umount) Less(other any) bool {
o, _ := other.(*Umount)
if r.Qualifier.Equals(o.Qualifier) {
if r.MountPoint == o.MountPoint {
return r.MountConditions.Less(o.MountConditions)
}
if r.MountPoint != o.MountPoint {
return r.MountPoint < o.MountPoint
}
if r.MountConditions.Equals(o.MountConditions) {
return r.MountConditions.Less(o.MountConditions)
}
return r.Qualifier.Less(o.Qualifier)
}
@ -106,27 +110,29 @@ func (r *Umount) Equals(other any) bool {
}
type Remount struct {
Rule
Qualifier
MountConditions
MountPoint string
}
func RemountFromLog(log map[string]string) ApparmorRule {
func newRemountFromLog(log map[string]string) *Remount {
return &Remount{
Qualifier: NewQualifierFromLog(log),
MountConditions: MountConditionsFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
MountConditions: newMountConditionsFromLog(log),
MountPoint: log["name"],
}
}
func (r *Remount) Less(other any) bool {
o, _ := other.(*Remount)
if r.Qualifier.Equals(o.Qualifier) {
if r.MountPoint == o.MountPoint {
return r.MountConditions.Less(o.MountConditions)
}
if r.MountPoint != o.MountPoint {
return r.MountPoint < o.MountPoint
}
if r.MountConditions.Equals(o.MountConditions) {
return r.MountConditions.Less(o.MountConditions)
}
return r.Qualifier.Less(o.Qualifier)
}

View File

@ -4,9 +4,12 @@
package aa
import "strings"
import (
"strings"
)
type Mqueue struct {
Rule
Qualifier
Access string
Type string
@ -14,7 +17,7 @@ type Mqueue struct {
Name string
}
func MqueueFromLog(log map[string]string) ApparmorRule {
func newMqueueFromLog(log map[string]string) *Mqueue {
mqueueType := "posix"
if strings.Contains(log["class"], "posix") {
mqueueType = "posix"
@ -22,7 +25,8 @@ func MqueueFromLog(log map[string]string) ApparmorRule {
mqueueType = "sysv"
}
return &Mqueue{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested"]),
Type: mqueueType,
Label: log["label"],
@ -32,19 +36,20 @@ func MqueueFromLog(log map[string]string) ApparmorRule {
func (r *Mqueue) Less(other any) bool {
o, _ := other.(*Mqueue)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
if r.Type == o.Type {
return r.Label < o.Label
}
return r.Type < o.Type
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Type != o.Type {
return r.Type < o.Type
}
if r.Label != o.Label {
return r.Label < o.Label
}
return r.Qualifier.Less(o.Qualifier)
}
func (r *Mqueue) Equals(other any) bool {
o, _ := other.(*Mqueue)
return r.Access == o.Access && r.Type == o.Type && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier)
return r.Access == o.Access && r.Type == o.Type && r.Label == o.Label &&
r.Name == o.Name && r.Qualifier.Equals(o.Qualifier)
}

View File

@ -10,54 +10,63 @@ type AddressExpr struct {
Port string
}
func newAddressExprFromLog(log map[string]string) AddressExpr {
return AddressExpr{
Source: log["laddr"],
Destination: log["faddr"],
Port: log["lport"],
}
}
func (r AddressExpr) Less(other AddressExpr) bool {
if r.Source != other.Source {
return r.Source < other.Source
}
if r.Destination != other.Destination {
return r.Destination < other.Destination
}
return r.Port < other.Port
}
func (r AddressExpr) Equals(other AddressExpr) bool {
return r.Source == other.Source && r.Destination == other.Destination &&
r.Port == other.Port
}
func (r AddressExpr) Less(other AddressExpr) bool {
if r.Source == other.Source {
if r.Destination == other.Destination {
return r.Port < other.Port
}
return r.Destination < other.Destination
}
return r.Source < other.Source
}
type Network struct {
Rule
Qualifier
AddressExpr
Domain string
Type string
Protocol string
AddressExpr
}
func NetworkFromLog(log map[string]string) ApparmorRule {
func newNetworkFromLog(log map[string]string) *Network {
return &Network{
Qualifier: NewQualifierFromLog(log),
AddressExpr: AddressExpr{
Source: log["laddr"],
Destination: log["faddr"],
Port: log["lport"],
},
Domain: log["family"],
Type: log["sock_type"],
Protocol: log["protocol"],
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
AddressExpr: newAddressExprFromLog(log),
Domain: log["family"],
Type: log["sock_type"],
Protocol: log["protocol"],
}
}
func (r *Network) Less(other any) bool {
o, _ := other.(*Network)
if r.Qualifier.Equals(o.Qualifier) {
if r.Domain == o.Domain {
if r.Type == o.Type {
return r.Protocol < o.Protocol
}
return r.Type < o.Type
}
if r.Domain != o.Domain {
return r.Domain < o.Domain
}
if r.Type != o.Type {
return r.Type < o.Type
}
if r.Protocol != o.Protocol {
return r.Protocol < o.Protocol
}
if r.AddressExpr.Less(o.AddressExpr) {
return r.AddressExpr.Less(o.AddressExpr)
}
return r.Qualifier.Less(o.Qualifier)
}

View File

@ -5,15 +5,17 @@
package aa
type PivotRoot struct {
Rule
Qualifier
OldRoot string
NewRoot string
TargetProfile string
}
func PivotRootFromLog(log map[string]string) ApparmorRule {
func newPivotRootFromLog(log map[string]string) *PivotRoot {
return &PivotRoot{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
OldRoot: log["srcname"],
NewRoot: log["name"],
TargetProfile: "",
@ -22,15 +24,15 @@ func PivotRootFromLog(log map[string]string) ApparmorRule {
func (r *PivotRoot) Less(other any) bool {
o, _ := other.(*PivotRoot)
if r.Qualifier.Equals(o.Qualifier) {
if r.OldRoot == o.OldRoot {
if r.NewRoot == o.NewRoot {
return r.TargetProfile < o.TargetProfile
}
return r.NewRoot < o.NewRoot
}
if r.OldRoot != o.OldRoot {
return r.OldRoot < o.OldRoot
}
if r.NewRoot != o.NewRoot {
return r.NewRoot < o.NewRoot
}
if r.TargetProfile != o.TargetProfile {
return r.TargetProfile < o.TargetProfile
}
return r.Qualifier.Less(o.Qualifier)
}

87
pkg/aa/preamble.go Normal file
View File

@ -0,0 +1,87 @@
// 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 (
"slices"
)
type Abi struct {
Rule
Path string
IsMagic bool
}
func (r *Abi) Less(other any) bool {
o, _ := other.(*Abi)
if r.Path != o.Path {
return r.Path < o.Path
}
return r.IsMagic == o.IsMagic
}
func (r *Abi) Equals(other any) bool {
o, _ := other.(*Abi)
return r.Path == o.Path && r.IsMagic == o.IsMagic
}
type Alias struct {
Rule
Path string
RewrittenPath string
}
func (r Alias) Less(other any) bool {
o, _ := other.(*Alias)
if r.Path != o.Path {
return r.Path < o.Path
}
return r.RewrittenPath < o.RewrittenPath
}
func (r Alias) Equals(other any) bool {
o, _ := other.(*Alias)
return r.Path == o.Path && r.RewrittenPath == o.RewrittenPath
}
type Include struct {
Rule
IfExists bool
Path string
IsMagic bool
}
func (r *Include) Less(other any) bool {
o, _ := other.(*Include)
if r.Path == o.Path {
return r.Path < o.Path
}
if r.IsMagic != o.IsMagic {
return r.IsMagic
}
return r.IfExists
}
func (r *Include) Equals(other any) bool {
o, _ := other.(*Include)
return r.Path == o.Path && r.IsMagic == o.IsMagic && r.IfExists == o.IfExists
}
type Variable struct {
Rule
Name string
Values []string
}
func (r *Variable) Less(other Variable) bool {
if r.Name != other.Name {
return r.Name < other.Name
}
return len(r.Values) < len(other.Values)
}
func (r *Variable) Equals(other Variable) bool {
return r.Name == other.Name && slices.Equal(r.Values, other.Values)
}

View File

@ -31,10 +31,10 @@ type AppArmorProfile struct {
// Preamble section of a profile
type Preamble struct {
Abi []Abi
Includes []Include
Aliases []Alias
Variables []Variable
Abi []*Abi
Includes []*Include
Aliases []*Alias
Variables []*Variable
}
// Profile section of a profile
@ -78,7 +78,7 @@ func (p *AppArmorProfile) AddRule(log map[string]string) {
}
case "-13":
if strings.Contains(log["info"], "namespace creation restricted") {
p.Rules = append(p.Rules, UsernsFromLog(log))
p.Rules = append(p.Rules, newUsernsFromLog(log))
} else if strings.Contains(log["info"], "disconnected path") && !slices.Contains(p.Flags, "attach_disconnected") {
p.Flags = append(p.Flags, "attach_disconnected")
}
@ -87,49 +87,51 @@ func (p *AppArmorProfile) AddRule(log map[string]string) {
switch log["class"] {
case "cap":
p.Rules = append(p.Rules, CapabilityFromLog(log))
p.Rules = append(p.Rules, newCapabilityFromLog(log))
case "net":
if log["family"] == "unix" {
p.Rules = append(p.Rules, UnixFromLog(log))
p.Rules = append(p.Rules, newUnixFromLog(log))
} else {
p.Rules = append(p.Rules, NetworkFromLog(log))
p.Rules = append(p.Rules, newNetworkFromLog(log))
}
case "mount":
if strings.Contains(log["flags"], "remount") {
p.Rules = append(p.Rules, RemountFromLog(log))
p.Rules = append(p.Rules, newRemountFromLog(log))
} else {
switch log["operation"] {
case "mount":
p.Rules = append(p.Rules, MountFromLog(log))
p.Rules = append(p.Rules, newMountFromLog(log))
case "umount":
p.Rules = append(p.Rules, UmountFromLog(log))
p.Rules = append(p.Rules, newUmountFromLog(log))
case "remount":
p.Rules = append(p.Rules, RemountFromLog(log))
p.Rules = append(p.Rules, newRemountFromLog(log))
case "pivotroot":
p.Rules = append(p.Rules, PivotRootFromLog(log))
p.Rules = append(p.Rules, newPivotRootFromLog(log))
}
}
case "posix_mqueue", "sysv_mqueue":
p.Rules = append(p.Rules, MqueueFromLog(log))
p.Rules = append(p.Rules, newMqueueFromLog(log))
case "signal":
p.Rules = append(p.Rules, SignalFromLog(log))
p.Rules = append(p.Rules, newSignalFromLog(log))
case "ptrace":
p.Rules = append(p.Rules, PtraceFromLog(log))
p.Rules = append(p.Rules, newPtraceFromLog(log))
case "namespace":
p.Rules = append(p.Rules, UsernsFromLog(log))
p.Rules = append(p.Rules, newUsernsFromLog(log))
case "unix":
p.Rules = append(p.Rules, UnixFromLog(log))
p.Rules = append(p.Rules, newUnixFromLog(log))
case "dbus":
p.Rules = append(p.Rules, newDbusFromLog(log))
case "file":
if log["operation"] == "change_onexec" {
p.Rules = append(p.Rules, ChangeProfileFromLog(log))
p.Rules = append(p.Rules, newChangeProfileFromLog(log))
} else {
p.Rules = append(p.Rules, FileFromLog(log))
p.Rules = append(p.Rules, newFileFromLog(log))
}
default:
if strings.Contains(log["operation"], "dbus") {
p.Rules = append(p.Rules, DbusFromLog(log))
p.Rules = append(p.Rules, newDbusFromLog(log))
} else if log["family"] == "unix" {
p.Rules = append(p.Rules, UnixFromLog(log))
p.Rules = append(p.Rules, newUnixFromLog(log))
}
}
}
@ -155,7 +157,7 @@ func (p *AppArmorProfile) Sort() {
})
}
// MergeRules merge similar rules together
// MergeRules merge similar rules together.
// Steps:
// - Remove identical rules
// - Merge rule access. Eg: for same path, 'r' and 'w' becomes 'rw'
@ -179,7 +181,7 @@ func (p *AppArmorProfile) MergeRules() {
}
}
// Format the profile for better readability before printing it
// Format the profile for better readability before printing it.
// Follow: https://apparmor.pujol.io/development/guidelines/#the-file-block
func (p *AppArmorProfile) Format() {
const prefixOwner = " "

View File

@ -43,10 +43,10 @@ func TestAppArmorProfile_String(t *testing.T) {
name: "foo",
p: &AppArmorProfile{
Preamble: Preamble{
Abi: []Abi{{IsMagic: true, Path: "abi/4.0"}},
Includes: []Include{{IsMagic: true, Path: "tunables/global"}},
Aliases: []Alias{{Path: "/mnt/usr", RewrittenPath: "/usr"}},
Variables: []Variable{{
Abi: []*Abi{{IsMagic: true, Path: "abi/4.0"}},
Includes: []*Include{{IsMagic: true, Path: "tunables/global"}},
Aliases: []*Alias{{Path: "/mnt/usr", RewrittenPath: "/usr"}},
Variables: []*Variable{{
Name: "exec_path",
Values: []string{"@{bin}/foo", "@{lib}/foo"},
}},
@ -83,11 +83,11 @@ func TestAppArmorProfile_String(t *testing.T) {
},
&Ptrace{Access: "read", Peer: "nautilus"},
&Unix{
Access: "send receive",
Type: "stream",
Address: "@/tmp/.ICE-unix/1995",
Peer: "gnome-shell",
PeerAddr: "none",
Access: "send receive",
Type: "stream",
Address: "@/tmp/.ICE-unix/1995",
PeerLabel: "gnome-shell",
PeerAddr: "none",
},
&Dbus{
Access: "bind",
@ -97,11 +97,11 @@ func TestAppArmorProfile_String(t *testing.T) {
&Dbus{
Access: "receive",
Bus: "system",
Name: ":1.3",
Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus",
Member: "AddMatch",
Label: "power-profiles-daemon",
PeerName: ":1.3",
PeerLabel: "power-profiles-daemon",
},
&File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: "rm"},
&File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: "rw"},
@ -290,9 +290,9 @@ func TestAppArmorProfile_Integration(t *testing.T) {
name: "aa-status",
p: &AppArmorProfile{
Preamble: Preamble{
Abi: []Abi{{IsMagic: true, Path: "abi/3.0"}},
Includes: []Include{{IsMagic: true, Path: "tunables/global"}},
Variables: []Variable{{
Abi: []*Abi{{IsMagic: true, Path: "abi/3.0"}},
Includes: []*Include{{IsMagic: true, Path: "tunables/global"}},
Variables: []*Variable{{
Name: "exec_path",
Values: []string{"@{bin}/aa-status", "@{bin}/apparmor_status"},
}},
@ -310,7 +310,7 @@ func TestAppArmorProfile_Integration(t *testing.T) {
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: "r"},
&File{Path: "@{PROC}/@{pids}/attr/current", Access: "r"},
&Include{IsMagic: true, Path: "abstractions/consoles"},
&File{Qualifier: Qualifier{Owner: true}, Path: "@{PROC}/@{pid}/mounts", Access: "r"},
&File{Owner: true, Path: "@{PROC}/@{pid}/mounts", Access: "r"},
&Include{IsMagic: true, Path: "abstractions/base"},
&File{Path: "/dev/tty@{int}", Access: "rw"},
&Capability{Name: "sys_ptrace"},

View File

@ -5,14 +5,16 @@
package aa
type Ptrace struct {
Rule
Qualifier
Access string
Peer string
}
func PtraceFromLog(log map[string]string) ApparmorRule {
func newPtraceFromLog(log map[string]string) *Ptrace {
return &Ptrace{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Peer: log["peer"],
}
@ -20,12 +22,12 @@ func PtraceFromLog(log map[string]string) ApparmorRule {
func (r *Ptrace) Less(other any) bool {
o, _ := other.(*Ptrace)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
return r.Peer == o.Peer
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Peer != o.Peer {
return r.Peer == o.Peer
}
return r.Qualifier.Less(o.Qualifier)
}

View File

@ -10,15 +10,24 @@ type Rlimit struct {
Value string
}
func newRlimitFromLog(log map[string]string) *Rlimit {
return &Rlimit{
Rule: newRuleFromLog(log),
Key: log["key"],
Op: log["op"],
Value: log["value"],
}
}
func (r *Rlimit) Less(other any) bool {
o, _ := other.(*Rlimit)
if r.Key == o.Key {
if r.Op == o.Op {
return r.Value < o.Value
}
if r.Key != o.Key {
return r.Key < o.Key
}
if r.Op != o.Op {
return r.Op < o.Op
}
return r.Key < o.Key
return r.Value < o.Value
}
func (r *Rlimit) Equals(other any) bool {

View File

@ -5,6 +5,7 @@
package aa
import (
"fmt"
"strings"
)
@ -12,43 +13,12 @@ type Rule struct {
Comment string
NoNewPrivs bool
FileInherit bool
}
func (r *Rule) Less(other any) bool {
return false
}
func (r *Rule) Equals(other any) bool {
return false
}
// Qualifier to apply extra settings to a rule
type Qualifier struct {
Audit bool
AccessType string
Owner bool
NoNewPrivs bool
FileInherit bool
Optional bool
Comment string
Prefix string
Padding string
Optional bool
}
func NewQualifierFromLog(log map[string]string) Qualifier {
owner := false
fsuid, hasFsUID := log["fsuid"]
ouid, hasOuUID := log["ouid"]
isDbus := strings.Contains(log["operation"], "dbus")
if hasFsUID && hasOuUID && fsuid == ouid && ouid != "0" && !isDbus {
owner = true
}
audit := false
if log["apparmor"] == "AUDIT" {
audit = true
}
func newRuleFromLog(log map[string]string) Rule {
fileInherit := false
if log["operation"] == "file_inherit" {
fileInherit = true
@ -76,62 +46,54 @@ func NewQualifierFromLog(log map[string]string) Qualifier {
default:
}
return Qualifier{
Audit: audit,
Owner: owner,
return Rule{
Comment: msg,
NoNewPrivs: noNewPrivs,
FileInherit: fileInherit,
Optional: optional,
Comment: msg,
}
}
func (r Rule) Less(other any) bool {
return false
}
func (r Rule) Equals(other any) bool {
return false
}
type Qualifier struct {
Audit bool
AccessType string
}
func newQualifierFromLog(log map[string]string) Qualifier {
audit := false
if log["apparmor"] == "AUDIT" {
audit = true
}
return Qualifier{Audit: audit}
}
func (r Qualifier) Less(other Qualifier) bool {
if r.Owner == other.Owner {
if r.Audit == other.Audit {
return r.AccessType < other.AccessType
}
if r.Audit != other.Audit {
return r.Audit
}
return other.Owner
return r.AccessType < other.AccessType
}
func (r Qualifier) Equals(other Qualifier) bool {
return r.Audit == other.Audit && r.AccessType == other.AccessType &&
r.Owner == other.Owner && r.NoNewPrivs == other.NoNewPrivs &&
r.FileInherit == other.FileInherit
return r.Audit == other.Audit && r.AccessType == other.AccessType
}
// Preamble specific rules
type Abi struct {
Path string
IsMagic bool
type All struct {
Rule
}
func (r Abi) Less(other Abi) bool {
if r.Path == other.Path {
return r.IsMagic == other.IsMagic
}
return r.Path < other.Path
func (r *All) Less(other any) bool {
return false
}
func (r Abi) Equals(other Abi) bool {
return r.Path == other.Path && r.IsMagic == other.IsMagic
}
type Alias struct {
Path string
RewrittenPath string
}
func (r Alias) Less(other Alias) bool {
if r.Path == other.Path {
return r.RewrittenPath < other.RewrittenPath
}
return r.Path < other.Path
}
func (r Alias) Equals(other Alias) bool {
return r.Path == other.Path && r.RewrittenPath == other.RewrittenPath
func (r *All) Equals(other any) bool {
return false
}

View File

@ -17,76 +17,100 @@ func TestRule_FromLog(t *testing.T) {
want ApparmorRule
}{
{
name: "capbability",
fromLog: CapabilityFromLog,
log: capability1Log,
want: capability1,
name: "capbability",
fromLog: func(m map[string]string) ApparmorRule {
return newCapabilityFromLog(m)
},
log: capability1Log,
want: capability1,
},
{
name: "network",
fromLog: NetworkFromLog,
log: network1Log,
want: network1,
name: "network",
fromLog: func(m map[string]string) ApparmorRule {
return newNetworkFromLog(m)
},
log: network1Log,
want: network1,
},
{
name: "mount",
fromLog: MountFromLog,
log: mount1Log,
want: mount1,
name: "mount",
fromLog: func(m map[string]string) ApparmorRule {
return newMountFromLog(m)
},
log: mount1Log,
want: mount1,
},
{
name: "umount",
fromLog: UmountFromLog,
log: umount1Log,
want: umount1,
name: "umount",
fromLog: func(m map[string]string) ApparmorRule {
return newUmountFromLog(m)
},
log: umount1Log,
want: umount1,
},
{
name: "pivotroot",
fromLog: PivotRootFromLog,
log: pivotroot1Log,
want: pivotroot1,
name: "pivotroot",
fromLog: func(m map[string]string) ApparmorRule {
return newPivotRootFromLog(m)
},
log: pivotroot1Log,
want: pivotroot1,
},
{
name: "changeprofile",
fromLog: ChangeProfileFromLog,
log: changeprofile1Log,
want: changeprofile1,
name: "changeprofile",
fromLog: func(m map[string]string) ApparmorRule {
return newChangeProfileFromLog(m)
},
log: changeprofile1Log,
want: changeprofile1,
},
{
name: "signal",
fromLog: SignalFromLog,
log: signal1Log,
want: signal1,
name: "signal",
fromLog: func(m map[string]string) ApparmorRule {
return newSignalFromLog(m)
},
log: signal1Log,
want: signal1,
},
{
name: "ptrace/xdg-document-portal",
fromLog: PtraceFromLog,
log: ptrace1Log,
want: ptrace1,
name: "ptrace/xdg-document-portal",
fromLog: func(m map[string]string) ApparmorRule {
return newPtraceFromLog(m)
},
log: ptrace1Log,
want: ptrace1,
},
{
name: "ptrace/snap-update-ns.firefox",
fromLog: PtraceFromLog,
log: ptrace2Log,
want: ptrace2,
name: "ptrace/snap-update-ns.firefox",
fromLog: func(m map[string]string) ApparmorRule {
return newPtraceFromLog(m)
},
log: ptrace2Log,
want: ptrace2,
},
{
name: "unix",
fromLog: UnixFromLog,
log: unix1Log,
want: unix1,
name: "unix",
fromLog: func(m map[string]string) ApparmorRule {
return newUnixFromLog(m)
},
log: unix1Log,
want: unix1,
},
{
name: "dbus",
fromLog: DbusFromLog,
log: dbus1Log,
want: dbus1,
name: "dbus",
fromLog: func(m map[string]string) ApparmorRule {
return newDbusFromLog(m)
},
log: dbus1Log,
want: dbus1,
},
{
name: "file",
fromLog: FileFromLog,
log: file1Log,
want: file1,
name: "file",
fromLog: func(m map[string]string) ApparmorRule {
return newFileFromLog(m)
},
log: file1Log,
want: file1,
},
}
for _, tt := range tests {
@ -109,13 +133,13 @@ func TestRule_Less(t *testing.T) {
name: "include1",
rule: include1,
other: includeLocal1,
want: true,
want: false,
},
{
name: "include2",
rule: include1,
other: include2,
want: true,
want: false,
},
{
name: "include3",
@ -245,9 +269,9 @@ func TestRule_Less(t *testing.T) {
},
{
name: "file/owner",
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Qualifier: Qualifier{Owner: true}},
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Owner: true},
other: &File{Path: "/usr/share/poppler/cMap/Identity-H"},
want: false,
want: true,
},
{
name: "file/access",

View File

@ -5,15 +5,17 @@
package aa
type Signal struct {
Rule
Qualifier
Access string
Set string
Peer string
}
func SignalFromLog(log map[string]string) ApparmorRule {
func newSignalFromLog(log map[string]string) *Signal {
return &Signal{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Set: log["signal"],
Peer: log["peer"],
@ -22,15 +24,15 @@ func SignalFromLog(log map[string]string) ApparmorRule {
func (r *Signal) Less(other any) bool {
o, _ := other.(*Signal)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
if r.Set == o.Set {
return r.Peer < o.Peer
}
return r.Set < o.Set
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Set != o.Set {
return r.Set < o.Set
}
if r.Peer != o.Peer {
return r.Peer < o.Peer
}
return r.Qualifier.Less(o.Qualifier)
}

View File

@ -8,4 +8,5 @@
{{- else -}}
{{ " \"" }}{{ .Path }}{{ "\"" }}
{{- end -}}
{{- template "comment" . -}}
{{- end -}}

View File

@ -56,7 +56,7 @@
{{- end -}}
{{- if eq $type "Rlimit" -}}
{{ "set rlimit " }}{{ .Key }} {{ .Op }} {{ .Value }}{{ "," }}
{{ "set rlimit " }}{{ .Key }} {{ .Op }} {{ .Value }}{{ "," }}{{ template "comment" . }}
{{- end -}}
{{- if eq $type "Capability" -}}
@ -191,15 +191,24 @@
{{- with .Type -}}
{{ " type=" }}{{ . }}
{{- end -}}
{{- with .Protocol -}}
{{ " protocol=" }}{{ . }}
{{- end -}}
{{- with .Address -}}
{{ " addr=" }}{{ . }}
{{- end -}}
{{- if .Peer -}}
{{ " peer=(label=" }}{{ .Peer }}
{{- with .PeerAddr -}}
{{ ", addr="}}{{ . }}
{{- with .Label -}}
{{ " label=" }}{{ . }}
{{- end -}}
{{- if and .PeerLabel .PeerAddr -}}
{{ " peer=(label=" }}{{ .PeerLabel }}{{ ", addr="}}{{ .PeerAddr }}{{ ")" }}
{{- else -}}
{{- with .PeerLabel -}}
{{ overindent "peer=(label=" }}{{ . }}{{ ")" }}
{{- end -}}
{{- with .PeerAddr -}}
{{ overindent "peer=(addr=" }}{{ . }}{{ ")" }}
{{- end -}}
{{- ")" -}}
{{- end -}}
{{- "," -}}
{{- template "comment" . -}}
@ -256,13 +265,13 @@
{{- with .Member -}}
{{ overindent "member=" }}{{ . }}{{ "\n" }}
{{- end -}}
{{- if and .Name .Label -}}
{{ overindent "peer=(name=" }}{{ .Name }}{{ ", label="}}{{ .Label }}{{ ")" }}
{{- if and .PeerName .PeerLabel -}}
{{ overindent "peer=(name=" }}{{ .PeerName }}{{ ", label="}}{{ .PeerLabel }}{{ ")" }}
{{- else -}}
{{- with .Name -}}
{{- with .PeerName -}}
{{ overindent "peer=(name=" }}{{ . }}{{ ")" }}
{{- end -}}
{{- with .Label -}}
{{- with .PeerLabel -}}
{{ overindent "peer=(label=" }}{{ . }}{{ ")" }}
{{- end -}}
{{- end -}}
@ -273,6 +282,9 @@
{{- if eq $type "File" -}}
{{- template "qualifier" . -}}
{{- if .Owner -}}
{{- "owner " -}}
{{- end -}}
{{- .Path -}}
{{- " " -}}
{{- with .Padding -}}

View File

@ -2,9 +2,6 @@
{{- with .Prefix -}}
{{ . }}
{{- end -}}
{{- if .Owner -}}
{{- "owner " -}}
{{- end -}}
{{- if .Audit -}}
{{- "audit " -}}
{{- end -}}

View File

@ -5,62 +5,64 @@
package aa
type Unix struct {
Rule
Qualifier
Access string
Type string
Protocol string
Address string
Label string
Attr string
Opt string
Peer string
PeerAddr string
Access string
Type string
Protocol string
Address string
Label string
Attr string
Opt string
PeerLabel string
PeerAddr string
}
func UnixFromLog(log map[string]string) ApparmorRule {
func newUnixFromLog(log map[string]string) *Unix {
return &Unix{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]),
Type: log["sock_type"],
Protocol: log["protocol"],
Address: log["addr"],
Label: log["peer_label"],
Label: log["label"],
Attr: log["attr"],
Opt: log["opt"],
Peer: log["peer"],
PeerLabel: log["peer"],
PeerAddr: log["peer_addr"],
}
}
func (r *Unix) Less(other any) bool {
o, _ := other.(*Unix)
if r.Qualifier.Equals(o.Qualifier) {
if r.Access == o.Access {
if r.Type == o.Type {
if r.Protocol == o.Protocol {
if r.Address == o.Address {
if r.Label == o.Label {
if r.Attr == o.Attr {
if r.Opt == o.Opt {
if r.Peer == o.Peer {
return r.PeerAddr < o.PeerAddr
}
return r.Peer < o.Peer
}
return r.Opt < o.Opt
}
return r.Attr < o.Attr
}
return r.Label < o.Label
}
return r.Address < o.Address
}
return r.Protocol < o.Protocol
}
return r.Type < o.Type
}
if r.Access != o.Access {
return r.Access < o.Access
}
if r.Type != o.Type {
return r.Type < o.Type
}
if r.Protocol != o.Protocol {
return r.Protocol < o.Protocol
}
if r.Address != o.Address {
return r.Address < o.Address
}
if r.Label != o.Label {
return r.Label < o.Label
}
if r.Attr != o.Attr {
return r.Attr < o.Attr
}
if r.Opt != o.Opt {
return r.Opt < o.Opt
}
if r.PeerLabel != o.PeerLabel {
return r.PeerLabel < o.PeerLabel
}
if r.PeerAddr != o.PeerAddr {
return r.PeerAddr < o.PeerAddr
}
return r.Qualifier.Less(o.Qualifier)
}
@ -69,5 +71,6 @@ func (r *Unix) Equals(other any) bool {
return r.Access == o.Access && r.Type == o.Type &&
r.Protocol == o.Protocol && r.Address == o.Address &&
r.Label == o.Label && r.Attr == o.Attr && r.Opt == o.Opt &&
r.Peer == o.Peer && r.PeerAddr == o.PeerAddr && r.Qualifier.Equals(o.Qualifier)
r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr &&
r.Qualifier.Equals(o.Qualifier)
}

View File

@ -5,20 +5,22 @@
package aa
type Userns struct {
Rule
Qualifier
Create bool
}
func UsernsFromLog(log map[string]string) ApparmorRule {
func newUsernsFromLog(log map[string]string) *Userns {
return &Userns{
Qualifier: NewQualifierFromLog(log),
Rule: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log),
Create: true,
}
}
func (r *Userns) Less(other any) bool {
o, _ := other.(*Userns)
if r.Qualifier.Equals(o.Qualifier) {
if r.Create != o.Create {
return r.Create
}
return r.Qualifier.Less(o.Qualifier)

View File

@ -9,7 +9,6 @@ package aa
import (
"regexp"
"slices"
"strings"
)
@ -18,35 +17,19 @@ var (
regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`)
)
type Variable struct {
Name string
Values []string
}
func (r Variable) Less(other Variable) bool {
if r.Name == other.Name {
return len(r.Values) < len(other.Values)
}
return r.Name < other.Name
}
func (r Variable) Equals(other Variable) bool {
return r.Name == other.Name && slices.Equal(r.Values, other.Values)
}
// DefaultTunables return a minimal working profile to build the profile
// It should not be used when loading file from /etc/apparmor.d
func DefaultTunables() *AppArmorProfile {
return &AppArmorProfile{
Preamble: Preamble{
Variables: []Variable{
{"bin", []string{"/{,usr/}{,s}bin"}},
{"lib", []string{"/{,usr/}lib{,exec,32,64}"}},
{"multiarch", []string{"*-linux-gnu*"}},
{"HOME", []string{"/home/*"}},
{"user_share_dirs", []string{"/home/*/.local/share"}},
{"etc_ro", []string{"/{,usr/}etc/"}},
{"int", []string{"[0-9]{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}"}},
Variables: []*Variable{
{Name: "bin", Values: []string{"/{,usr/}{,s}bin"}},
{Name: "lib", Values: []string{"/{,usr/}lib{,exec,32,64}"}},
{Name: "multiarch", Values: []string{"*-linux-gnu*"}},
{Name: "HOME", Values: []string{"/home/*"}},
{Name: "user_share_dirs", Values: []string{"/home/*/.local/share"}},
{Name: "etc_ro", Values: []string{"/{,usr/}etc/"}},
{Name: "int", Values: []string{"[0-9]{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}"}},
},
},
}
@ -68,7 +51,7 @@ func (p *AppArmorProfile) ParseVariables(content string) {
}
}
if !found {
variable := Variable{Name: key, Values: values}
variable := &Variable{Name: key, Values: values}
p.Variables = append(p.Variables, variable)
}
}

View File

@ -9,6 +9,10 @@ import (
"testing"
)
// TODO: space in variable need to be tested.
// @{name} = "Mullvad VPN"
// profile mullvad-gui /{opt/"Mullvad/mullvad-gui,opt/VPN"/mullvad-gui,mullvad-gui} flags=(attach_disconnected,complain) {
func TestDefaultTunables(t *testing.T) {
tests := []struct {
name string
@ -18,14 +22,14 @@ func TestDefaultTunables(t *testing.T) {
name: "aa",
want: &AppArmorProfile{
Preamble: Preamble{
Variables: []Variable{
{"bin", []string{"/{,usr/}{,s}bin"}},
{"lib", []string{"/{,usr/}lib{,exec,32,64}"}},
{"multiarch", []string{"*-linux-gnu*"}},
{"HOME", []string{"/home/*"}},
{"user_share_dirs", []string{"/home/*/.local/share"}},
{"etc_ro", []string{"/{,usr/}etc/"}},
{"int", []string{"[0-9]{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}"}},
Variables: []*Variable{
{Name: "bin", Values: []string{"/{,usr/}{,s}bin"}},
{Name: "lib", Values: []string{"/{,usr/}lib{,exec,32,64}"}},
{Name: "multiarch", Values: []string{"*-linux-gnu*"}},
{Name: "HOME", Values: []string{"/home/*"}},
{Name: "user_share_dirs", Values: []string{"/home/*/.local/share"}},
{Name: "etc_ro", Values: []string{"/{,usr/}etc/"}},
{Name: "int", Values: []string{"[0-9]{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}{[0-9],}"}},
},
},
},
@ -44,7 +48,7 @@ func TestAppArmorProfile_ParseVariables(t *testing.T) {
tests := []struct {
name string
content string
want []Variable
want []*Variable
}{
{
name: "firefox",
@ -54,12 +58,12 @@ func TestAppArmorProfile_ParseVariables(t *testing.T) {
@{firefox_cache_dirs} = @{user_cache_dirs}/mozilla/
@{exec_path} = /{usr/,}bin/@{firefox_name} @{firefox_lib_dirs}/@{firefox_name}
`,
want: []Variable{
{"firefox_name", []string{"firefox{,-esr,-bin}"}},
{"firefox_lib_dirs", []string{"/{usr/,}lib{,32,64}/@{firefox_name}", "/opt/@{firefox_name}"}},
{"firefox_config_dirs", []string{"@{HOME}/.mozilla/"}},
{"firefox_cache_dirs", []string{"@{user_cache_dirs}/mozilla/"}},
{"exec_path", []string{"/{usr/,}bin/@{firefox_name}", "@{firefox_lib_dirs}/@{firefox_name}"}},
want: []*Variable{
{Name: "firefox_name", Values: []string{"firefox{,-esr,-bin}"}},
{Name: "firefox_lib_dirs", Values: []string{"/{usr/,}lib{,32,64}/@{firefox_name}", "/opt/@{firefox_name}"}},
{Name: "firefox_config_dirs", Values: []string{"@{HOME}/.mozilla/"}},
{Name: "firefox_cache_dirs", Values: []string{"@{user_cache_dirs}/mozilla/"}},
{Name: "exec_path", Values: []string{"/{usr/,}bin/@{firefox_name}", "@{firefox_lib_dirs}/@{firefox_name}"}},
},
},
{
@ -68,8 +72,8 @@ func TestAppArmorProfile_ParseVariables(t *testing.T) {
@{exec_path} += /{usr/,}bin/Xorg{,.bin}
@{exec_path} += /{usr/,}lib/Xorg{,.wrap}
@{exec_path} += /{usr/,}lib/xorg/Xorg{,.wrap}`,
want: []Variable{
{"exec_path", []string{
want: []*Variable{
{Name: "exec_path", Values: []string{
"/{usr/,}bin/X",
"/{usr/,}bin/Xorg{,.bin}",
"/{usr/,}lib/Xorg{,.wrap}",
@ -81,9 +85,9 @@ func TestAppArmorProfile_ParseVariables(t *testing.T) {
name: "snapd",
content: `@{lib_dirs} = @{lib}/ /snap/snapd/@{int}@{lib}
@{exec_path} = @{lib_dirs}/snapd/snapd`,
want: []Variable{
{"lib_dirs", []string{"@{lib}/", "/snap/snapd/@{int}@{lib}"}},
{"exec_path", []string{"@{lib_dirs}/snapd/snapd"}},
want: []*Variable{
{Name: "lib_dirs", Values: []string{"@{lib}/", "/snap/snapd/@{int}@{lib}"}},
{Name: "exec_path", Values: []string{"@{lib_dirs}/snapd/snapd"}},
},
},
}
@ -104,11 +108,21 @@ func TestAppArmorProfile_resolve(t *testing.T) {
input string
want []string
}{
{
name: "default",
input: "@{etc_ro}",
want: []string{"/{,usr/}etc/"},
},
{
name: "empty",
input: "@{}",
want: []string{"@{}"},
},
{
name: "nil",
input: "@{foo}",
want: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -123,15 +137,15 @@ func TestAppArmorProfile_resolve(t *testing.T) {
func TestAppArmorProfile_ResolveAttachments(t *testing.T) {
tests := []struct {
name string
variables []Variable
variables []*Variable
want []string
}{
{
name: "firefox",
variables: []Variable{
{"firefox_name", []string{"firefox{,-esr,-bin}"}},
{"firefox_lib_dirs", []string{"/{usr/,}/lib{,32,64}/@{firefox_name}", "/opt/@{firefox_name}"}},
{"exec_path", []string{"/{usr/,}bin/@{firefox_name}", "@{firefox_lib_dirs}/@{firefox_name}"}},
variables: []*Variable{
{Name: "firefox_name", Values: []string{"firefox{,-esr,-bin}"}},
{Name: "firefox_lib_dirs", Values: []string{"/{usr/,}/lib{,32,64}/@{firefox_name}", "/opt/@{firefox_name}"}},
{Name: "exec_path", Values: []string{"/{usr/,}bin/@{firefox_name}", "@{firefox_lib_dirs}/@{firefox_name}"}},
},
want: []string{
"/{usr/,}bin/firefox{,-esr,-bin}",
@ -141,10 +155,10 @@ func TestAppArmorProfile_ResolveAttachments(t *testing.T) {
},
{
name: "chromium",
variables: []Variable{
{"name", []string{"chromium"}},
{"lib_dirs", []string{"/{usr/,}lib/@{name}"}},
{"exec_path", []string{"@{lib_dirs}/@{name}"}},
variables: []*Variable{
{Name: "name", Values: []string{"chromium"}},
{Name: "lib_dirs", Values: []string{"/{usr/,}lib/@{name}"}},
{Name: "exec_path", Values: []string{"@{lib_dirs}/@{name}"}},
},
want: []string{
"/{usr/,}lib/chromium/chromium",
@ -152,9 +166,9 @@ func TestAppArmorProfile_ResolveAttachments(t *testing.T) {
},
{
name: "geoclue",
variables: []Variable{
{"libexec", []string{"/{usr/,}libexec"}},
{"exec_path", []string{"@{libexec}/geoclue", "@{libexec}/geoclue-2.0/demos/agent"}},
variables: []*Variable{
{Name: "libexec", Values: []string{"/{usr/,}libexec"}},
{Name: "exec_path", Values: []string{"@{libexec}/geoclue", "@{libexec}/geoclue-2.0/demos/agent"}},
},
want: []string{
"/{usr/,}libexec/geoclue",
@ -163,11 +177,11 @@ func TestAppArmorProfile_ResolveAttachments(t *testing.T) {
},
{
name: "opera",
variables: []Variable{
{"multiarch", []string{"*-linux-gnu*"}},
{"name", []string{"opera{,-beta,-developer}"}},
{"lib_dirs", []string{"/{usr/,}lib/@{multiarch}/@{name}"}},
{"exec_path", []string{"@{lib_dirs}/@{name}"}},
variables: []*Variable{
{Name: "multiarch", Values: []string{"*-linux-gnu*"}},
{Name: "name", Values: []string{"opera{,-beta,-developer}"}},
{Name: "lib_dirs", Values: []string{"/{usr/,}lib/@{multiarch}/@{name}"}},
{Name: "exec_path", Values: []string{"@{lib_dirs}/@{name}"}},
},
want: []string{
"/{usr/,}lib/*-linux-gnu*/opera{,-beta,-developer}/opera{,-beta,-developer}",

View File

@ -303,16 +303,16 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) {
Name: "kmod",
Rules: aa.Rules{
&aa.Unix{
Qualifier: aa.Qualifier{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
Rule: aa.Rule{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
},
&aa.Unix{
Qualifier: aa.Qualifier{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
Rule: aa.Rule{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
},
},
},
@ -324,11 +324,11 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) {
&aa.Dbus{
Access: "send",
Bus: "system",
Name: "org.freedesktop.DBus",
Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus",
Member: "AddMatch",
Label: "dbus-daemon",
PeerName: "org.freedesktop.DBus",
PeerLabel: "dbus-daemon",
},
},
},

View File

@ -107,7 +107,7 @@ func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile {
Bus: rules["bus"],
Path: rules["path"],
Interface: iface,
Name: `":1.@{int}"`,
PeerName: `":1.@{int}"`,
})
}
for _, iface := range interfaces {
@ -116,7 +116,7 @@ func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile {
Bus: rules["bus"],
Path: rules["path"],
Interface: iface,
Name: `"{:1.@{int},org.freedesktop.DBus}"`,
PeerName: `"{:1.@{int},org.freedesktop.DBus}"`,
})
}
p.Rules = append(p.Rules, &aa.Dbus{
@ -125,7 +125,7 @@ func (d Dbus) own(rules map[string]string) *aa.AppArmorProfile {
Path: rules["path"],
Interface: "org.freedesktop.DBus.Introspectable",
Member: "Introspect",
Name: `":1.@{int}"`,
PeerName: `":1.@{int}"`,
})
return p
}
@ -139,8 +139,8 @@ func (d Dbus) talk(rules map[string]string) *aa.AppArmorProfile {
Bus: rules["bus"],
Path: rules["path"],
Interface: iface,
Name: `"{:1.@{int},` + rules["name"] + `}"`,
Label: rules["label"],
PeerName: `"{:1.@{int},` + rules["name"] + `}"`,
PeerLabel: rules["label"],
})
}
for _, iface := range interfaces {
@ -149,8 +149,8 @@ func (d Dbus) talk(rules map[string]string) *aa.AppArmorProfile {
Bus: rules["bus"],
Path: rules["path"],
Interface: iface,
Name: `"{:1.@{int},` + rules["name"] + `}"`,
Label: rules["label"],
PeerName: `"{:1.@{int},` + rules["name"] + `}"`,
PeerLabel: rules["label"],
})
}
return p