feat(aa): ensure accesses are slice of string.

This commit is contained in:
Alexandre Pujol 2024-04-23 21:17:25 +01:00
parent a2910122d2
commit c719a0a109
No known key found for this signature in database
GPG key ID: C5469996F0DF68EC
16 changed files with 240 additions and 210 deletions

View file

@ -61,77 +61,6 @@ func (f *AppArmorProfileFile) GetDefaultProfile() *Profile {
return f.Profiles[0] return f.Profiles[0]
} }
// AddRule adds a new rule to the profile from a log map
// See utils/apparmor/logparser.py for the format of the log map
func (f *AppArmorProfileFile) AddRule(log map[string]string) {
p := f.GetDefaultProfile()
// Generate profile flags and extra rules
switch log["error"] {
case "-2":
if !slices.Contains(p.Flags, "mediate_deleted") {
p.Flags = append(p.Flags, "mediate_deleted")
}
case "-13":
if strings.Contains(log["info"], "namespace creation restricted") {
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")
}
default:
}
switch log["class"] {
case "cap":
p.Rules = append(p.Rules, newCapabilityFromLog(log))
case "net":
if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
} else {
p.Rules = append(p.Rules, newNetworkFromLog(log))
}
case "mount":
if strings.Contains(log["flags"], "remount") {
p.Rules = append(p.Rules, newRemountFromLog(log))
} else {
switch log["operation"] {
case "mount":
p.Rules = append(p.Rules, newMountFromLog(log))
case "umount":
p.Rules = append(p.Rules, newUmountFromLog(log))
case "remount":
p.Rules = append(p.Rules, newRemountFromLog(log))
case "pivotroot":
p.Rules = append(p.Rules, newPivotRootFromLog(log))
}
}
case "posix_mqueue", "sysv_mqueue":
p.Rules = append(p.Rules, newMqueueFromLog(log))
case "signal":
p.Rules = append(p.Rules, newSignalFromLog(log))
case "ptrace":
p.Rules = append(p.Rules, newPtraceFromLog(log))
case "namespace":
p.Rules = append(p.Rules, newUsernsFromLog(log))
case "unix":
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, newChangeProfileFromLog(log))
} else {
p.Rules = append(p.Rules, newFileFromLog(log))
}
default:
if strings.Contains(log["operation"], "dbus") {
p.Rules = append(p.Rules, newDbusFromLog(log))
} else if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
}
}
}
// Sort the rules in the profile // Sort the rules in the profile
// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines // Follow: https://apparmor.pujol.io/development/guidelines/#guidelines
func (f *AppArmorProfileFile) Sort() { func (f *AppArmorProfileFile) Sort() {

View file

@ -62,8 +62,8 @@ func TestAppArmorProfile_String(t *testing.T) {
&Include{IsMagic: true, Path: "abstractions/base"}, &Include{IsMagic: true, Path: "abstractions/base"},
&Include{IsMagic: true, Path: "abstractions/nameservice-strict"}, &Include{IsMagic: true, Path: "abstractions/nameservice-strict"},
rlimit1, rlimit1,
&Capability{Name: "dac_read_search"}, &Capability{Names: []string{"dac_read_search"}},
&Capability{Name: "dac_override"}, &Capability{Names: []string{"dac_override"}},
&Network{Domain: "inet", Type: "stream"}, &Network{Domain: "inet", Type: "stream"},
&Network{Domain: "inet6", Type: "stream"}, &Network{Domain: "inet6", Type: "stream"},
&Mount{ &Mount{
@ -79,25 +79,25 @@ func TestAppArmorProfile_String(t *testing.T) {
MountPoint: "@{run}/user/@{uid}/", MountPoint: "@{run}/user/@{uid}/",
}, },
&Signal{ &Signal{
Access: "receive", Access: []string{"receive"},
Set: "term", Set: []string{"term"},
Peer: "at-spi-bus-launcher", Peer: "at-spi-bus-launcher",
}, },
&Ptrace{Access: "read", Peer: "nautilus"}, &Ptrace{Access: []string{"read"}, Peer: "nautilus"},
&Unix{ &Unix{
Access: "send receive", Access: []string{"send", "receive"},
Type: "stream", Type: "stream",
Address: "@/tmp/.ICE-unix/1995", Address: "@/tmp/.ICE-unix/1995",
PeerLabel: "gnome-shell", PeerLabel: "gnome-shell",
PeerAddr: "none", PeerAddr: "none",
}, },
&Dbus{ &Dbus{
Access: "bind", Access: []string{"bind"},
Bus: "session", Bus: "session",
Name: "org.gnome.*", Name: "org.gnome.*",
}, },
&Dbus{ &Dbus{
Access: "receive", Access: []string{"receive"},
Bus: "system", Bus: "system",
Path: "/org/freedesktop/DBus", Path: "/org/freedesktop/DBus",
Interface: "org.freedesktop.DBus", Interface: "org.freedesktop.DBus",
@ -105,9 +105,9 @@ func TestAppArmorProfile_String(t *testing.T) {
PeerName: ":1.3", PeerName: ":1.3",
PeerLabel: "power-profiles-daemon", PeerLabel: "power-profiles-daemon",
}, },
&File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: "rm"}, &File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: []string{"r", "m"}},
&File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: "rw"}, &File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: []string{"r", "w"}},
&File{Path: "@{sys}/devices/@{pci}/class", Access: "r"}, &File{Path: "@{sys}/devices/@{pci}/class", Access: []string{"r"}},
includeLocal1, includeLocal1,
}, },
}}, }},
@ -306,19 +306,19 @@ func TestAppArmorProfile_Integration(t *testing.T) {
}, },
Rules: Rules{ Rules: Rules{
&Include{IfExists: true, IsMagic: true, Path: "local/aa-status"}, &Include{IfExists: true, IsMagic: true, Path: "local/aa-status"},
&Capability{Name: "dac_read_search"}, &Capability{Names: []string{"dac_read_search"}},
&File{Path: "@{exec_path}", Access: "mr"}, &File{Path: "@{exec_path}", Access: []string{"m", "r"}},
&File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: "r"}, &File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: []string{"r"}},
&File{Path: "@{PROC}/", Access: "r"}, &File{Path: "@{PROC}/", Access: []string{"r"}},
&File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: "r"}, &File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: []string{"r"}},
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: "r"}, &File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: []string{"r"}},
&File{Path: "@{PROC}/@{pids}/attr/current", Access: "r"}, &File{Path: "@{PROC}/@{pids}/attr/current", Access: []string{"r"}},
&Include{IsMagic: true, Path: "abstractions/consoles"}, &Include{IsMagic: true, Path: "abstractions/consoles"},
&File{Owner: true, Path: "@{PROC}/@{pid}/mounts", Access: "r"}, &File{Owner: true, Path: "@{PROC}/@{pid}/mounts", Access: []string{"r"}},
&Include{IsMagic: true, Path: "abstractions/base"}, &Include{IsMagic: true, Path: "abstractions/base"},
&File{Path: "/dev/tty@{int}", Access: "rw"}, &File{Path: "/dev/tty@{int}", Access: []string{"r", "w"}},
&Capability{Name: "sys_ptrace"}, &Capability{Names: []string{"sys_ptrace"}},
&Ptrace{Access: "read"}, &Ptrace{Access: []string{"read"}},
}, },
}}, }},
}, },

View file

@ -4,29 +4,36 @@
package aa package aa
type Capability struct { type Capability struct {
RuleBase RuleBase
Qualifier Qualifier
Name string Names []string
}
} }
func newCapabilityFromLog(log map[string]string) Rule { func newCapabilityFromLog(log map[string]string) Rule {
return &Capability{ return &Capability{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Name: log["capname"], Names: []string{log["capname"]},
} }
} }
func (r *Capability) Less(other any) bool { func (r *Capability) Less(other any) bool {
o, _ := other.(*Capability) o, _ := other.(*Capability)
if r.Name != o.Name { for i := 0; i < len(r.Names) && i < len(o.Names); i++ {
return r.Name < o.Name if r.Names[i] != o.Names[i] {
return r.Names[i] < o.Names[i]
}
} }
return r.Qualifier.Less(o.Qualifier) return r.Qualifier.Less(o.Qualifier)
} }
func (r *Capability) Equals(other any) bool { func (r *Capability) Equals(other any) bool {
o, _ := other.(*Capability) o, _ := other.(*Capability)
return r.Name == o.Name && r.Qualifier.Equals(o.Qualifier) return slices.Equal(r.Names, o.Names) && r.Qualifier.Equals(o.Qualifier)
}
} }

View file

@ -26,8 +26,8 @@ var (
"profile": "pkexec", "profile": "pkexec",
"comm": "pkexec", "comm": "pkexec",
} }
capability1 = &Capability{Name: "net_admin"} capability1 = &Capability{Names: []string{"net_admin"}}
capability2 = &Capability{Name: "sys_ptrace"} capability2 = &Capability{Names: []string{"sys_ptrace"}}
// Network // Network
network1Log = map[string]string{ network1Log = map[string]string{
@ -147,13 +147,13 @@ var (
"peer": "firefox//&firejail-default", "peer": "firefox//&firejail-default",
} }
signal1 = &Signal{ signal1 = &Signal{
Access: "receive", Access: []string{"receive"},
Set: "kill", Set: []string{"kill"},
Peer: "firefox//&firejail-default", Peer: "firefox//&firejail-default",
} }
signal2 = &Signal{ signal2 = &Signal{
Access: "receive", Access: []string{"receive"},
Set: "up", Set: []string{"up"},
Peer: "firefox//&firejail-default", Peer: "firefox//&firejail-default",
} }
@ -177,8 +177,8 @@ var (
"denied_mask": "readby", "denied_mask": "readby",
"peer": "systemd-journald", "peer": "systemd-journald",
} }
ptrace1 = &Ptrace{Access: "read", Peer: "nautilus"} ptrace1 = &Ptrace{Access: []string{"read"}, Peer: "nautilus"}
ptrace2 = &Ptrace{Access: "readby", Peer: "systemd-journald"} ptrace2 = &Ptrace{Access: []string{"readby"}, Peer: "systemd-journald"}
// Unix // Unix
unix1Log = map[string]string{ unix1Log = map[string]string{
@ -197,7 +197,7 @@ var (
"protocol": "0", "protocol": "0",
} }
unix1 = &Unix{ unix1 = &Unix{
Access: "send receive", Access: []string{"receive", "send"},
Type: "stream", Type: "stream",
Protocol: "0", Protocol: "0",
Address: "none", Address: "none",
@ -206,7 +206,7 @@ var (
} }
unix2 = &Unix{ unix2 = &Unix{
RuleBase: RuleBase{FileInherit: true}, RuleBase: RuleBase{FileInherit: true},
Access: "receive", Access: []string{"receive"},
Type: "stream", Type: "stream",
} }
@ -234,7 +234,7 @@ var (
"label": "evolution-source-registry", "label": "evolution-source-registry",
} }
dbus1 = &Dbus{ dbus1 = &Dbus{
Access: "receive", Access: []string{"receive"},
Bus: "session", Bus: "session",
Path: "/org/gtk/vfs/metadata", Path: "/org/gtk/vfs/metadata",
Interface: "org.gtk.vfs.Metadata", Interface: "org.gtk.vfs.Metadata",
@ -243,12 +243,12 @@ var (
PeerLabel: "tracker-extract", PeerLabel: "tracker-extract",
} }
dbus2 = &Dbus{ dbus2 = &Dbus{
Access: "bind", Access: []string{"bind"},
Bus: "session", Bus: "session",
Name: "org.gnome.evolution.dataserver.Sources5", Name: "org.gnome.evolution.dataserver.Sources5",
} }
dbus3 = &Dbus{ dbus3 = &Dbus{
Access: "bind", Access: []string{"bind"},
Bus: "session", Bus: "session",
Name: "org.gnome.evolution.dataserver", Name: "org.gnome.evolution.dataserver",
} }
@ -283,11 +283,11 @@ var (
"OUID": "user", "OUID": "user",
"error": "-1", "error": "-1",
} }
file1 = &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"} file1 = &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}}
file2 = &File{ file2 = &File{
RuleBase: RuleBase{NoNewPrivs: true}, RuleBase: RuleBase{NoNewPrivs: true},
Owner: true, Owner: true,
Path: "@{PROC}/4163/cgroup", Path: "@{PROC}/4163/cgroup",
Access: "r", Access: []string{"r"},
} }
) )

View file

@ -7,7 +7,7 @@ package aa
type Dbus struct { type Dbus struct {
RuleBase RuleBase
Qualifier Qualifier
Access string Access []string
Bus string Bus string
Name string Name string
Path string Path string
@ -28,7 +28,7 @@ func newDbusFromLog(log map[string]string) Rule {
return &Dbus{ return &Dbus{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Access: log["mask"], Access: []string{log["mask"]},
Bus: log["bus"], Bus: log["bus"],
Name: name, Name: name,
Path: log["path"], Path: log["path"],
@ -41,8 +41,10 @@ func newDbusFromLog(log map[string]string) Rule {
func (r *Dbus) Less(other any) bool { func (r *Dbus) Less(other any) bool {
o, _ := other.(*Dbus) o, _ := other.(*Dbus)
if r.Access != o.Access { for i := 0; i < len(r.Access) && i < len(o.Access); i++ {
return r.Access < o.Access if r.Access[i] != o.Access[i] {
return r.Access[i] < o.Access[i]
}
} }
if r.Bus != o.Bus { if r.Bus != o.Bus {
return r.Bus < o.Bus return r.Bus < o.Bus
@ -70,7 +72,7 @@ func (r *Dbus) Less(other any) bool {
func (r *Dbus) Equals(other any) bool { func (r *Dbus) Equals(other any) bool {
o, _ := other.(*Dbus) o, _ := other.(*Dbus)
return r.Access == o.Access && r.Bus == o.Bus && r.Name == o.Name && return slices.Equal(r.Access, o.Access) && r.Bus == o.Bus && r.Name == o.Name &&
r.Path == o.Path && r.Interface == o.Interface && r.Path == o.Path && r.Interface == o.Interface &&
r.Member == o.Member && r.PeerName == o.PeerName && r.Member == o.Member && r.PeerName == o.PeerName &&
r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier) r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier)

View file

@ -9,7 +9,7 @@ type File struct {
Qualifier Qualifier
Owner bool Owner bool
Path string Path string
Access string Access []string
Target string Target string
} }
@ -26,7 +26,7 @@ func newFileFromLog(log map[string]string) Rule {
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Owner: owner, Owner: owner,
Path: log["name"], Path: log["name"],
Access: toAccess(log["requested_mask"]), Access: toAccess("file-log", log["requested_mask"]),
Target: log["target"], Target: log["target"],
} }
} }
@ -41,8 +41,8 @@ func (r *File) Less(other any) bool {
if r.Path != o.Path { if r.Path != o.Path {
return r.Path < o.Path return r.Path < o.Path
} }
if r.Access != o.Access { if len(r.Access) != len(o.Access) {
return r.Access < o.Access return len(r.Access) < len(o.Access)
} }
if r.Target != o.Target { if r.Target != o.Target {
return r.Target < o.Target return r.Target < o.Target
@ -55,6 +55,9 @@ func (r *File) Less(other any) bool {
func (r *File) Equals(other any) bool { func (r *File) Equals(other any) bool {
o, _ := other.(*File) o, _ := other.(*File)
return r.Path == o.Path && r.Access == o.Access && r.Owner == o.Owner && return r.Path == o.Path && slices.Equal(r.Access, o.Access) && r.Owner == o.Owner &&
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
}
r.Target == o.Target && r.Qualifier.Equals(o.Qualifier) r.Target == o.Target && r.Qualifier.Equals(o.Qualifier)
} }

View file

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

View file

@ -11,7 +11,7 @@ import (
type Mqueue struct { type Mqueue struct {
RuleBase RuleBase
Qualifier Qualifier
Access string Access []string
Type string Type string
Label string Label string
Name string Name string
@ -27,7 +27,7 @@ func newMqueueFromLog(log map[string]string) Rule {
return &Mqueue{ return &Mqueue{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested"]), Access: toAccess(tokMQUEUE, log["requested"]),
Type: mqueueType, Type: mqueueType,
Label: log["label"], Label: log["label"],
Name: log["name"], Name: log["name"],
@ -36,8 +36,8 @@ func newMqueueFromLog(log map[string]string) Rule {
func (r *Mqueue) Less(other any) bool { func (r *Mqueue) Less(other any) bool {
o, _ := other.(*Mqueue) o, _ := other.(*Mqueue)
if r.Access != o.Access { if len(r.Access) != len(o.Access) {
return r.Access < o.Access return len(r.Access) < len(o.Access)
} }
if r.Type != o.Type { if r.Type != o.Type {
return r.Type < o.Type return r.Type < o.Type
@ -50,6 +50,6 @@ func (r *Mqueue) Less(other any) bool {
func (r *Mqueue) Equals(other any) bool { func (r *Mqueue) Equals(other any) bool {
o, _ := other.(*Mqueue) o, _ := other.(*Mqueue)
return r.Access == o.Access && r.Type == o.Type && r.Label == o.Label && return slices.Equal(r.Access, o.Access) && r.Type == o.Type && r.Label == o.Label &&
r.Name == o.Name && r.Qualifier.Equals(o.Qualifier) r.Name == o.Name && r.Qualifier.Equals(o.Qualifier)
} }

View file

@ -39,3 +39,76 @@ func (p *Profile) Equals(other any) bool {
maps.Equal(p.Attributes, o.Attributes) && maps.Equal(p.Attributes, o.Attributes) &&
slices.Equal(p.Flags, o.Flags) slices.Equal(p.Flags, o.Flags)
} }
// AddRule adds a new rule to the profile from a log map.
func (p *Profile) AddRule(log map[string]string) {
// Generate profile flags and extra rules
switch log["error"] {
case "-2":
if !slices.Contains(p.Flags, "mediate_deleted") {
p.Flags = append(p.Flags, "mediate_deleted")
}
case "-13":
if strings.Contains(log["info"], "namespace creation restricted") {
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")
}
default:
}
switch log["class"] {
case "rlimits":
p.Rules = append(p.Rules, newRlimitFromLog(log))
case "cap":
p.Rules = append(p.Rules, newCapabilityFromLog(log))
case "net":
if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
} else {
p.Rules = append(p.Rules, newNetworkFromLog(log))
}
case "io_uring":
p.Rules = append(p.Rules, newIOUringFromLog(log))
case "mount":
if strings.Contains(log["flags"], "remount") {
p.Rules = append(p.Rules, newRemountFromLog(log))
} else {
switch log["operation"] {
case "mount":
p.Rules = append(p.Rules, newMountFromLog(log))
case "umount":
p.Rules = append(p.Rules, newUmountFromLog(log))
case "remount":
p.Rules = append(p.Rules, newRemountFromLog(log))
case "pivotroot":
p.Rules = append(p.Rules, newPivotRootFromLog(log))
}
}
case "posix_mqueue", "sysv_mqueue":
p.Rules = append(p.Rules, newMqueueFromLog(log))
case "signal":
p.Rules = append(p.Rules, newSignalFromLog(log))
case "ptrace":
p.Rules = append(p.Rules, newPtraceFromLog(log))
case "namespace":
p.Rules = append(p.Rules, newUsernsFromLog(log))
case "unix":
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, newChangeProfileFromLog(log))
} else {
p.Rules = append(p.Rules, newFileFromLog(log))
}
default:
if strings.Contains(log["operation"], "dbus") {
p.Rules = append(p.Rules, newDbusFromLog(log))
} else if log["family"] == "unix" {
p.Rules = append(p.Rules, newUnixFromLog(log))
}
}
}

View file

@ -7,7 +7,7 @@ package aa
type Ptrace struct { type Ptrace struct {
RuleBase RuleBase
Qualifier Qualifier
Access string Access []string
Peer string Peer string
} }
@ -15,15 +15,15 @@ func newPtraceFromLog(log map[string]string) Rule {
return &Ptrace{ return &Ptrace{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]), Access: toAccess(tokPTRACE, log["requested_mask"]),
Peer: log["peer"], Peer: log["peer"],
} }
} }
func (r *Ptrace) Less(other any) bool { func (r *Ptrace) Less(other any) bool {
o, _ := other.(*Ptrace) o, _ := other.(*Ptrace)
if r.Access != o.Access { if len(r.Access) != len(o.Access) {
return r.Access < o.Access return len(r.Access) < len(o.Access)
} }
if r.Peer != o.Peer { if r.Peer != o.Peer {
return r.Peer == o.Peer return r.Peer == o.Peer
@ -33,6 +33,6 @@ func (r *Ptrace) Less(other any) bool {
func (r *Ptrace) Equals(other any) bool { func (r *Ptrace) Equals(other any) bool {
o, _ := other.(*Ptrace) o, _ := other.(*Ptrace)
return r.Access == o.Access && r.Peer == o.Peer && return slices.Equal(r.Access, o.Access) && r.Peer == o.Peer &&
r.Qualifier.Equals(o.Qualifier) r.Qualifier.Equals(o.Qualifier)
} }

View file

@ -251,9 +251,9 @@ func TestRule_Less(t *testing.T) {
}, },
{ {
name: "file/access", name: "file/access",
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"}, rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}},
other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "w"}, other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"w"}},
want: true, want: false,
}, },
{ {
name: "file/close", name: "file/close",

View file

@ -7,8 +7,8 @@ package aa
type Signal struct { type Signal struct {
RuleBase RuleBase
Qualifier Qualifier
Access string Access []string
Set string Set []string
Peer string Peer string
} }
@ -16,19 +16,19 @@ func newSignalFromLog(log map[string]string) Rule {
return &Signal{ return &Signal{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]), Access: toAccess(tokSIGNAL, log["requested_mask"]),
Set: log["signal"], Set: toAccess(tokSIGNAL, log["signal"]),
Peer: log["peer"], Peer: log["peer"],
} }
} }
func (r *Signal) Less(other any) bool { func (r *Signal) Less(other any) bool {
o, _ := other.(*Signal) o, _ := other.(*Signal)
if r.Access != o.Access { if len(r.Access) != len(o.Access) {
return r.Access < o.Access return len(r.Access) < len(o.Access)
} }
if r.Set != o.Set { if len(r.Set) != len(o.Set) {
return r.Set < o.Set return len(r.Set) < len(o.Set)
} }
if r.Peer != o.Peer { if r.Peer != o.Peer {
return r.Peer < o.Peer return r.Peer < o.Peer
@ -38,6 +38,6 @@ func (r *Signal) Less(other any) bool {
func (r *Signal) Equals(other any) bool { func (r *Signal) Equals(other any) bool {
o, _ := other.(*Signal) o, _ := other.(*Signal)
return r.Access == o.Access && r.Set == o.Set && return slices.Equal(r.Access, o.Access) && slices.Equal(r.Set, o.Set) &&
r.Peer == o.Peer && r.Qualifier.Equals(o.Qualifier) r.Peer == o.Peer && r.Qualifier.Equals(o.Qualifier)
} }

View file

@ -33,19 +33,10 @@ var (
} }
// convert apparmor requested mask to apparmor access mode // convert apparmor requested mask to apparmor access mode
requestedMaskToAccess = map[string]string{ maskToAccess = map[string]string{
"a": "w", "a": "w",
"ac": "w", "c": "w",
"c": "w", "d": "w",
"d": "w",
"m": "rm",
"ra": "rw",
"wc": "w",
"wd": "w",
"wr": "rw",
"wrc": "rw",
"wrd": "rw",
"x": "rix",
} }
// The order the apparmor rules should be sorted // The order the apparmor rules should be sorted
@ -172,9 +163,38 @@ func getLetterIn(alphabet []string, in string) string {
return "" return ""
} }
func toAccess(mask string) string { // Helper function to convert a access string to slice of access
if requestedMaskToAccess[mask] != "" { func toAccess(constraint string, input string) []string {
return requestedMaskToAccess[mask] var res []string
switch constraint {
case "file", "file-log":
raw := strings.Split(input, "")
trans := []string{}
for _, access := range raw {
if slices.Contains(fileAccess, access) {
res = append(res, access)
} else if maskToAccess[access] != "" {
res = append(res, maskToAccess[access])
trans = append(trans, access)
}
}
if constraint != "file-log" {
transition := strings.Join(trans, "")
if len(transition) > 0 {
if slices.Contains(fileExecTransition, transition) {
res = append(res, transition)
} else {
panic("unrecognized pattern: " + transition)
}
}
}
return res
default:
res = strings.Fields(input)
slices.Sort(res)
return slices.Compact(res)
} }
return mask
} }

View file

@ -7,7 +7,7 @@ package aa
type Unix struct { type Unix struct {
RuleBase RuleBase
Qualifier Qualifier
Access string Access []string
Type string Type string
Protocol string Protocol string
Address string Address string
@ -22,7 +22,7 @@ func newUnixFromLog(log map[string]string) Rule {
return &Unix{ return &Unix{
RuleBase: newRuleFromLog(log), RuleBase: newRuleFromLog(log),
Qualifier: newQualifierFromLog(log), Qualifier: newQualifierFromLog(log),
Access: toAccess(log["requested_mask"]), Access: toAccess(tokUNIX, log["requested_mask"]),
Type: log["sock_type"], Type: log["sock_type"],
Protocol: log["protocol"], Protocol: log["protocol"],
Address: log["addr"], Address: log["addr"],
@ -36,8 +36,8 @@ func newUnixFromLog(log map[string]string) Rule {
func (r *Unix) Less(other any) bool { func (r *Unix) Less(other any) bool {
o, _ := other.(*Unix) o, _ := other.(*Unix)
if r.Access != o.Access { if len(r.Access) != len(o.Access) {
return r.Access < o.Access return len(r.Access) < len(o.Access)
} }
if r.Type != o.Type { if r.Type != o.Type {
return r.Type < o.Type return r.Type < o.Type
@ -68,7 +68,7 @@ func (r *Unix) Less(other any) bool {
func (r *Unix) Equals(other any) bool { func (r *Unix) Equals(other any) bool {
o, _ := other.(*Unix) o, _ := other.(*Unix)
return r.Access == o.Access && r.Type == o.Type && return slices.Equal(r.Access, o.Access) && r.Type == o.Type &&
r.Protocol == o.Protocol && r.Address == o.Address && r.Protocol == o.Protocol && r.Address == o.Address &&
r.Label == o.Label && r.Attr == o.Attr && r.Opt == o.Opt && r.Label == o.Label && r.Attr == o.Attr && r.Opt == o.Opt &&
r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr && r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr &&

View file

@ -197,8 +197,8 @@ func (aaLogs AppArmorLogs) String() string {
} }
// ParseToProfiles convert the log data into a new AppArmorProfiles // ParseToProfiles convert the log data into a new AppArmorProfiles
func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles { func (aaLogs AppArmorLogs) ParseToProfiles() map[string]*aa.Profile {
profiles := make(aa.AppArmorProfileFiles, 0) profiles := make(map[string]*aa.Profile, 0)
for _, log := range aaLogs { for _, log := range aaLogs {
name := "" name := ""
if strings.Contains(log["operation"], "dbus") { if strings.Contains(log["operation"], "dbus") {
@ -208,9 +208,7 @@ func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles {
} }
if _, ok := profiles[name]; !ok { if _, ok := profiles[name]; !ok {
profile := &aa.AppArmorProfileFile{ profile := &aa.Profile{Header: aa.Header{Name: name}}
Profiles: []*aa.Profile{{Header: aa.Header{Name: name}}},
}
profile.AddRule(log) profile.AddRule(log)
profiles[name] = profile profiles[name] = profile
} else { } else {

View file

@ -292,46 +292,42 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
aaLogs AppArmorLogs aaLogs AppArmorLogs
want aa.AppArmorProfileFiles want map[string]*aa.Profile
}{ }{
{ {
name: "", name: "",
aaLogs: append(append(refKmod, refPowerProfiles...), refKmod...), aaLogs: append(append(refKmod, refPowerProfiles...), refKmod...),
want: aa.AppArmorProfileFiles{ want: map[string]*aa.Profile{
"kmod": &aa.AppArmorProfileFile{ "kmod": {
Profiles: []*aa.Profile{{ Header: aa.Header{Name: "kmod"},
Header: aa.Header{Name: "kmod"}, Rules: aa.Rules{
Rules: aa.Rules{ &aa.Unix{
&aa.Unix{ RuleBase: aa.RuleBase{FileInherit: true},
RuleBase: aa.RuleBase{FileInherit: true}, Access: []string{"receive", "send"},
Access: "send receive", Type: "stream",
Type: "stream", Protocol: "0",
Protocol: "0",
},
&aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: "send receive",
Type: "stream",
Protocol: "0",
},
}, },
}}, &aa.Unix{
RuleBase: aa.RuleBase{FileInherit: true},
Access: []string{"receive", "send"},
Type: "stream",
Protocol: "0",
},
},
}, },
"power-profiles-daemon": &aa.AppArmorProfileFile{ "power-profiles-daemon": {
Profiles: []*aa.Profile{{ Header: aa.Header{Name: "power-profiles-daemon"},
Header: aa.Header{Name: "power-profiles-daemon"}, Rules: aa.Rules{
Rules: aa.Rules{ &aa.Dbus{
&aa.Dbus{ Access: []string{"send"},
Access: "send", Bus: "system",
Bus: "system", Path: "/org/freedesktop/DBus",
Path: "/org/freedesktop/DBus", Interface: "org.freedesktop.DBus",
Interface: "org.freedesktop.DBus", Member: "AddMatch",
Member: "AddMatch", PeerName: "org.freedesktop.DBus",
PeerName: "org.freedesktop.DBus", PeerLabel: "dbus-daemon",
PeerLabel: "dbus-daemon",
},
}, },
}}, },
}, },
}, },
}, },