mirror of
https://github.com/roddhjav/apparmor.d.git
synced 2024-11-14 23:43:56 +01:00
feat(aa): ensure accesses are slice of string.
This commit is contained in:
parent
a2910122d2
commit
c719a0a109
@ -61,77 +61,6 @@ func (f *AppArmorProfileFile) GetDefaultProfile() *Profile {
|
||||
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
|
||||
// Follow: https://apparmor.pujol.io/development/guidelines/#guidelines
|
||||
func (f *AppArmorProfileFile) Sort() {
|
||||
|
@ -62,8 +62,8 @@ func TestAppArmorProfile_String(t *testing.T) {
|
||||
&Include{IsMagic: true, Path: "abstractions/base"},
|
||||
&Include{IsMagic: true, Path: "abstractions/nameservice-strict"},
|
||||
rlimit1,
|
||||
&Capability{Name: "dac_read_search"},
|
||||
&Capability{Name: "dac_override"},
|
||||
&Capability{Names: []string{"dac_read_search"}},
|
||||
&Capability{Names: []string{"dac_override"}},
|
||||
&Network{Domain: "inet", Type: "stream"},
|
||||
&Network{Domain: "inet6", Type: "stream"},
|
||||
&Mount{
|
||||
@ -79,25 +79,25 @@ func TestAppArmorProfile_String(t *testing.T) {
|
||||
MountPoint: "@{run}/user/@{uid}/",
|
||||
},
|
||||
&Signal{
|
||||
Access: "receive",
|
||||
Set: "term",
|
||||
Access: []string{"receive"},
|
||||
Set: []string{"term"},
|
||||
Peer: "at-spi-bus-launcher",
|
||||
},
|
||||
&Ptrace{Access: "read", Peer: "nautilus"},
|
||||
&Ptrace{Access: []string{"read"}, Peer: "nautilus"},
|
||||
&Unix{
|
||||
Access: "send receive",
|
||||
Access: []string{"send", "receive"},
|
||||
Type: "stream",
|
||||
Address: "@/tmp/.ICE-unix/1995",
|
||||
PeerLabel: "gnome-shell",
|
||||
PeerAddr: "none",
|
||||
},
|
||||
&Dbus{
|
||||
Access: "bind",
|
||||
Access: []string{"bind"},
|
||||
Bus: "session",
|
||||
Name: "org.gnome.*",
|
||||
},
|
||||
&Dbus{
|
||||
Access: "receive",
|
||||
Access: []string{"receive"},
|
||||
Bus: "system",
|
||||
Path: "/org/freedesktop/DBus",
|
||||
Interface: "org.freedesktop.DBus",
|
||||
@ -105,9 +105,9 @@ func TestAppArmorProfile_String(t *testing.T) {
|
||||
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"},
|
||||
&File{Path: "@{sys}/devices/@{pci}/class", Access: "r"},
|
||||
&File{Path: "/opt/intel/oneapi/compiler/*/linux/lib/*.so./*", Access: []string{"r", "m"}},
|
||||
&File{Path: "@{PROC}/@{pid}/task/@{tid}/comm", Access: []string{"r", "w"}},
|
||||
&File{Path: "@{sys}/devices/@{pci}/class", Access: []string{"r"}},
|
||||
includeLocal1,
|
||||
},
|
||||
}},
|
||||
@ -306,19 +306,19 @@ func TestAppArmorProfile_Integration(t *testing.T) {
|
||||
},
|
||||
Rules: Rules{
|
||||
&Include{IfExists: true, IsMagic: true, Path: "local/aa-status"},
|
||||
&Capability{Name: "dac_read_search"},
|
||||
&File{Path: "@{exec_path}", Access: "mr"},
|
||||
&File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: "r"},
|
||||
&File{Path: "@{PROC}/", Access: "r"},
|
||||
&File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: "r"},
|
||||
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: "r"},
|
||||
&File{Path: "@{PROC}/@{pids}/attr/current", Access: "r"},
|
||||
&Capability{Names: []string{"dac_read_search"}},
|
||||
&File{Path: "@{exec_path}", Access: []string{"m", "r"}},
|
||||
&File{Path: "@{PROC}/@{pids}/attr/apparmor/current", Access: []string{"r"}},
|
||||
&File{Path: "@{PROC}/", Access: []string{"r"}},
|
||||
&File{Path: "@{sys}/module/apparmor/parameters/enabled", Access: []string{"r"}},
|
||||
&File{Path: "@{sys}/kernel/security/apparmor/profiles", Access: []string{"r"}},
|
||||
&File{Path: "@{PROC}/@{pids}/attr/current", Access: []string{"r"}},
|
||||
&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"},
|
||||
&File{Path: "/dev/tty@{int}", Access: "rw"},
|
||||
&Capability{Name: "sys_ptrace"},
|
||||
&Ptrace{Access: "read"},
|
||||
&File{Path: "/dev/tty@{int}", Access: []string{"r", "w"}},
|
||||
&Capability{Names: []string{"sys_ptrace"}},
|
||||
&Ptrace{Access: []string{"read"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
|
@ -4,29 +4,36 @@
|
||||
|
||||
package aa
|
||||
|
||||
|
||||
type Capability struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Name string
|
||||
Names []string
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func newCapabilityFromLog(log map[string]string) Rule {
|
||||
return &Capability{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Name: log["capname"],
|
||||
Names: []string{log["capname"]},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Capability) Less(other any) bool {
|
||||
o, _ := other.(*Capability)
|
||||
if r.Name != o.Name {
|
||||
return r.Name < o.Name
|
||||
for i := 0; i < len(r.Names) && i < len(o.Names); i++ {
|
||||
if r.Names[i] != o.Names[i] {
|
||||
return r.Names[i] < o.Names[i]
|
||||
}
|
||||
}
|
||||
return r.Qualifier.Less(o.Qualifier)
|
||||
}
|
||||
|
||||
func (r *Capability) Equals(other any) bool {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,8 +26,8 @@ var (
|
||||
"profile": "pkexec",
|
||||
"comm": "pkexec",
|
||||
}
|
||||
capability1 = &Capability{Name: "net_admin"}
|
||||
capability2 = &Capability{Name: "sys_ptrace"}
|
||||
capability1 = &Capability{Names: []string{"net_admin"}}
|
||||
capability2 = &Capability{Names: []string{"sys_ptrace"}}
|
||||
|
||||
// Network
|
||||
network1Log = map[string]string{
|
||||
@ -147,13 +147,13 @@ var (
|
||||
"peer": "firefox//&firejail-default",
|
||||
}
|
||||
signal1 = &Signal{
|
||||
Access: "receive",
|
||||
Set: "kill",
|
||||
Access: []string{"receive"},
|
||||
Set: []string{"kill"},
|
||||
Peer: "firefox//&firejail-default",
|
||||
}
|
||||
signal2 = &Signal{
|
||||
Access: "receive",
|
||||
Set: "up",
|
||||
Access: []string{"receive"},
|
||||
Set: []string{"up"},
|
||||
Peer: "firefox//&firejail-default",
|
||||
}
|
||||
|
||||
@ -177,8 +177,8 @@ var (
|
||||
"denied_mask": "readby",
|
||||
"peer": "systemd-journald",
|
||||
}
|
||||
ptrace1 = &Ptrace{Access: "read", Peer: "nautilus"}
|
||||
ptrace2 = &Ptrace{Access: "readby", Peer: "systemd-journald"}
|
||||
ptrace1 = &Ptrace{Access: []string{"read"}, Peer: "nautilus"}
|
||||
ptrace2 = &Ptrace{Access: []string{"readby"}, Peer: "systemd-journald"}
|
||||
|
||||
// Unix
|
||||
unix1Log = map[string]string{
|
||||
@ -197,7 +197,7 @@ var (
|
||||
"protocol": "0",
|
||||
}
|
||||
unix1 = &Unix{
|
||||
Access: "send receive",
|
||||
Access: []string{"receive", "send"},
|
||||
Type: "stream",
|
||||
Protocol: "0",
|
||||
Address: "none",
|
||||
@ -206,7 +206,7 @@ var (
|
||||
}
|
||||
unix2 = &Unix{
|
||||
RuleBase: RuleBase{FileInherit: true},
|
||||
Access: "receive",
|
||||
Access: []string{"receive"},
|
||||
Type: "stream",
|
||||
}
|
||||
|
||||
@ -234,7 +234,7 @@ var (
|
||||
"label": "evolution-source-registry",
|
||||
}
|
||||
dbus1 = &Dbus{
|
||||
Access: "receive",
|
||||
Access: []string{"receive"},
|
||||
Bus: "session",
|
||||
Path: "/org/gtk/vfs/metadata",
|
||||
Interface: "org.gtk.vfs.Metadata",
|
||||
@ -243,12 +243,12 @@ var (
|
||||
PeerLabel: "tracker-extract",
|
||||
}
|
||||
dbus2 = &Dbus{
|
||||
Access: "bind",
|
||||
Access: []string{"bind"},
|
||||
Bus: "session",
|
||||
Name: "org.gnome.evolution.dataserver.Sources5",
|
||||
}
|
||||
dbus3 = &Dbus{
|
||||
Access: "bind",
|
||||
Access: []string{"bind"},
|
||||
Bus: "session",
|
||||
Name: "org.gnome.evolution.dataserver",
|
||||
}
|
||||
@ -283,11 +283,11 @@ var (
|
||||
"OUID": "user",
|
||||
"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{
|
||||
RuleBase: RuleBase{NoNewPrivs: true},
|
||||
Owner: true,
|
||||
Path: "@{PROC}/4163/cgroup",
|
||||
Access: "r",
|
||||
Access: []string{"r"},
|
||||
}
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ package aa
|
||||
type Dbus struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Access []string
|
||||
Bus string
|
||||
Name string
|
||||
Path string
|
||||
@ -28,7 +28,7 @@ func newDbusFromLog(log map[string]string) Rule {
|
||||
return &Dbus{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: log["mask"],
|
||||
Access: []string{log["mask"]},
|
||||
Bus: log["bus"],
|
||||
Name: name,
|
||||
Path: log["path"],
|
||||
@ -41,8 +41,10 @@ func newDbusFromLog(log map[string]string) Rule {
|
||||
|
||||
func (r *Dbus) Less(other any) bool {
|
||||
o, _ := other.(*Dbus)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
for i := 0; i < len(r.Access) && i < len(o.Access); i++ {
|
||||
if r.Access[i] != o.Access[i] {
|
||||
return r.Access[i] < o.Access[i]
|
||||
}
|
||||
}
|
||||
if 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 {
|
||||
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.Member == o.Member && r.PeerName == o.PeerName &&
|
||||
r.PeerLabel == o.PeerLabel && r.Qualifier.Equals(o.Qualifier)
|
||||
|
@ -9,7 +9,7 @@ type File struct {
|
||||
Qualifier
|
||||
Owner bool
|
||||
Path string
|
||||
Access string
|
||||
Access []string
|
||||
Target string
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ func newFileFromLog(log map[string]string) Rule {
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Owner: owner,
|
||||
Path: log["name"],
|
||||
Access: toAccess(log["requested_mask"]),
|
||||
Access: toAccess("file-log", log["requested_mask"]),
|
||||
Target: log["target"],
|
||||
}
|
||||
}
|
||||
@ -41,8 +41,8 @@ func (r *File) Less(other any) bool {
|
||||
if r.Path != o.Path {
|
||||
return r.Path < o.Path
|
||||
}
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if 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 {
|
||||
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)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ package aa
|
||||
type IOUring struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Access []string
|
||||
Label string
|
||||
}
|
||||
|
||||
@ -15,15 +15,15 @@ func newIOUringFromLog(log map[string]string) Rule {
|
||||
return &IOUring{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: toAccess(log["requested"]),
|
||||
Access: toAccess(tokIOURING, log["requested"]),
|
||||
Label: log["label"],
|
||||
}
|
||||
}
|
||||
|
||||
func (r *IOUring) Less(other any) bool {
|
||||
o, _ := other.(*IOUring)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
type Mqueue struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Access []string
|
||||
Type string
|
||||
Label string
|
||||
Name string
|
||||
@ -27,7 +27,7 @@ func newMqueueFromLog(log map[string]string) Rule {
|
||||
return &Mqueue{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: toAccess(log["requested"]),
|
||||
Access: toAccess(tokMQUEUE, log["requested"]),
|
||||
Type: mqueueType,
|
||||
Label: log["label"],
|
||||
Name: log["name"],
|
||||
@ -36,8 +36,8 @@ func newMqueueFromLog(log map[string]string) Rule {
|
||||
|
||||
func (r *Mqueue) Less(other any) bool {
|
||||
o, _ := other.(*Mqueue)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if 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 {
|
||||
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)
|
||||
}
|
||||
|
@ -39,3 +39,76 @@ func (p *Profile) Equals(other any) bool {
|
||||
maps.Equal(p.Attributes, o.Attributes) &&
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ package aa
|
||||
type Ptrace struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Access []string
|
||||
Peer string
|
||||
}
|
||||
|
||||
@ -15,15 +15,15 @@ func newPtraceFromLog(log map[string]string) Rule {
|
||||
return &Ptrace{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: toAccess(log["requested_mask"]),
|
||||
Access: toAccess(tokPTRACE, log["requested_mask"]),
|
||||
Peer: log["peer"],
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Ptrace) Less(other any) bool {
|
||||
o, _ := other.(*Ptrace)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if 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 {
|
||||
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)
|
||||
}
|
||||
|
@ -251,9 +251,9 @@ func TestRule_Less(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "file/access",
|
||||
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "r"},
|
||||
other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: "w"},
|
||||
want: true,
|
||||
rule: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"r"}},
|
||||
other: &File{Path: "/usr/share/poppler/cMap/Identity-H", Access: []string{"w"}},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "file/close",
|
||||
|
@ -7,8 +7,8 @@ package aa
|
||||
type Signal struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Set string
|
||||
Access []string
|
||||
Set []string
|
||||
Peer string
|
||||
}
|
||||
|
||||
@ -16,19 +16,19 @@ func newSignalFromLog(log map[string]string) Rule {
|
||||
return &Signal{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: toAccess(log["requested_mask"]),
|
||||
Set: log["signal"],
|
||||
Access: toAccess(tokSIGNAL, log["requested_mask"]),
|
||||
Set: toAccess(tokSIGNAL, log["signal"]),
|
||||
Peer: log["peer"],
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Signal) Less(other any) bool {
|
||||
o, _ := other.(*Signal)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if r.Set != o.Set {
|
||||
return r.Set < o.Set
|
||||
if len(r.Set) != len(o.Set) {
|
||||
return len(r.Set) < len(o.Set)
|
||||
}
|
||||
if 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 {
|
||||
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)
|
||||
}
|
||||
|
@ -33,19 +33,10 @@ var (
|
||||
}
|
||||
|
||||
// convert apparmor requested mask to apparmor access mode
|
||||
requestedMaskToAccess = map[string]string{
|
||||
"a": "w",
|
||||
"ac": "w",
|
||||
"c": "w",
|
||||
"d": "w",
|
||||
"m": "rm",
|
||||
"ra": "rw",
|
||||
"wc": "w",
|
||||
"wd": "w",
|
||||
"wr": "rw",
|
||||
"wrc": "rw",
|
||||
"wrd": "rw",
|
||||
"x": "rix",
|
||||
maskToAccess = map[string]string{
|
||||
"a": "w",
|
||||
"c": "w",
|
||||
"d": "w",
|
||||
}
|
||||
|
||||
// The order the apparmor rules should be sorted
|
||||
@ -172,9 +163,38 @@ func getLetterIn(alphabet []string, in string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func toAccess(mask string) string {
|
||||
if requestedMaskToAccess[mask] != "" {
|
||||
return requestedMaskToAccess[mask]
|
||||
// Helper function to convert a access string to slice of access
|
||||
func toAccess(constraint string, input string) []string {
|
||||
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
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ package aa
|
||||
type Unix struct {
|
||||
RuleBase
|
||||
Qualifier
|
||||
Access string
|
||||
Access []string
|
||||
Type string
|
||||
Protocol string
|
||||
Address string
|
||||
@ -22,7 +22,7 @@ func newUnixFromLog(log map[string]string) Rule {
|
||||
return &Unix{
|
||||
RuleBase: newRuleFromLog(log),
|
||||
Qualifier: newQualifierFromLog(log),
|
||||
Access: toAccess(log["requested_mask"]),
|
||||
Access: toAccess(tokUNIX, log["requested_mask"]),
|
||||
Type: log["sock_type"],
|
||||
Protocol: log["protocol"],
|
||||
Address: log["addr"],
|
||||
@ -36,8 +36,8 @@ func newUnixFromLog(log map[string]string) Rule {
|
||||
|
||||
func (r *Unix) Less(other any) bool {
|
||||
o, _ := other.(*Unix)
|
||||
if r.Access != o.Access {
|
||||
return r.Access < o.Access
|
||||
if len(r.Access) != len(o.Access) {
|
||||
return len(r.Access) < len(o.Access)
|
||||
}
|
||||
if 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 {
|
||||
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.Label == o.Label && r.Attr == o.Attr && r.Opt == o.Opt &&
|
||||
r.PeerLabel == o.PeerLabel && r.PeerAddr == o.PeerAddr &&
|
||||
|
@ -197,8 +197,8 @@ func (aaLogs AppArmorLogs) String() string {
|
||||
}
|
||||
|
||||
// ParseToProfiles convert the log data into a new AppArmorProfiles
|
||||
func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles {
|
||||
profiles := make(aa.AppArmorProfileFiles, 0)
|
||||
func (aaLogs AppArmorLogs) ParseToProfiles() map[string]*aa.Profile {
|
||||
profiles := make(map[string]*aa.Profile, 0)
|
||||
for _, log := range aaLogs {
|
||||
name := ""
|
||||
if strings.Contains(log["operation"], "dbus") {
|
||||
@ -208,9 +208,7 @@ func (aaLogs AppArmorLogs) ParseToProfiles() aa.AppArmorProfileFiles {
|
||||
}
|
||||
|
||||
if _, ok := profiles[name]; !ok {
|
||||
profile := &aa.AppArmorProfileFile{
|
||||
Profiles: []*aa.Profile{{Header: aa.Header{Name: name}}},
|
||||
}
|
||||
profile := &aa.Profile{Header: aa.Header{Name: name}}
|
||||
profile.AddRule(log)
|
||||
profiles[name] = profile
|
||||
} else {
|
||||
|
@ -292,46 +292,42 @@ func TestAppArmorLogs_ParseToProfiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
aaLogs AppArmorLogs
|
||||
want aa.AppArmorProfileFiles
|
||||
want map[string]*aa.Profile
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
aaLogs: append(append(refKmod, refPowerProfiles...), refKmod...),
|
||||
want: aa.AppArmorProfileFiles{
|
||||
"kmod": &aa.AppArmorProfileFile{
|
||||
Profiles: []*aa.Profile{{
|
||||
Header: aa.Header{Name: "kmod"},
|
||||
Rules: aa.Rules{
|
||||
&aa.Unix{
|
||||
RuleBase: aa.RuleBase{FileInherit: true},
|
||||
Access: "send receive",
|
||||
Type: "stream",
|
||||
Protocol: "0",
|
||||
},
|
||||
&aa.Unix{
|
||||
RuleBase: aa.RuleBase{FileInherit: true},
|
||||
Access: "send receive",
|
||||
Type: "stream",
|
||||
Protocol: "0",
|
||||
},
|
||||
want: map[string]*aa.Profile{
|
||||
"kmod": {
|
||||
Header: aa.Header{Name: "kmod"},
|
||||
Rules: aa.Rules{
|
||||
&aa.Unix{
|
||||
RuleBase: aa.RuleBase{FileInherit: true},
|
||||
Access: []string{"receive", "send"},
|
||||
Type: "stream",
|
||||
Protocol: "0",
|
||||
},
|
||||
}},
|
||||
&aa.Unix{
|
||||
RuleBase: aa.RuleBase{FileInherit: true},
|
||||
Access: []string{"receive", "send"},
|
||||
Type: "stream",
|
||||
Protocol: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"power-profiles-daemon": &aa.AppArmorProfileFile{
|
||||
Profiles: []*aa.Profile{{
|
||||
Header: aa.Header{Name: "power-profiles-daemon"},
|
||||
Rules: aa.Rules{
|
||||
&aa.Dbus{
|
||||
Access: "send",
|
||||
Bus: "system",
|
||||
Path: "/org/freedesktop/DBus",
|
||||
Interface: "org.freedesktop.DBus",
|
||||
Member: "AddMatch",
|
||||
PeerName: "org.freedesktop.DBus",
|
||||
PeerLabel: "dbus-daemon",
|
||||
},
|
||||
"power-profiles-daemon": {
|
||||
Header: aa.Header{Name: "power-profiles-daemon"},
|
||||
Rules: aa.Rules{
|
||||
&aa.Dbus{
|
||||
Access: []string{"send"},
|
||||
Bus: "system",
|
||||
Path: "/org/freedesktop/DBus",
|
||||
Interface: "org.freedesktop.DBus",
|
||||
Member: "AddMatch",
|
||||
PeerName: "org.freedesktop.DBus",
|
||||
PeerLabel: "dbus-daemon",
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user