diff --git a/pkg/aa/capability.go b/pkg/aa/capability.go index 47c60138..7d202af9 100644 --- a/pkg/aa/capability.go +++ b/pkg/aa/capability.go @@ -15,3 +15,16 @@ func CapabilityFromLog(log map[string]string, noNewPrivs, fileInherit bool) Appa Name: log["capname"], } } + +func (r *Capability) Less(other any) bool { + o, _ := other.(*Capability) + if r.Name == o.Name { + return r.Qualifier.Less(o.Qualifier) + } + return r.Name < o.Name +} + +func (r *Capability) Equals(other any) bool { + o, _ := other.(*Capability) + return r.Name == o.Name && r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/change_profile.go b/pkg/aa/change_profile.go index feba6a8a..b0f1f708 100644 --- a/pkg/aa/change_profile.go +++ b/pkg/aa/change_profile.go @@ -17,3 +17,19 @@ func ChangeProfileFromLog(log map[string]string, noNewPrivs, fileInherit bool) A ProfileName: log["name"], } } + +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 + } + return r.Exec < o.Exec + } + return r.ExecMode < o.ExecMode +} + +func (r *ChangeProfile) Equals(other any) bool { + o, _ := other.(*ChangeProfile) + return r.ExecMode == o.ExecMode && r.Exec == o.Exec && r.ProfileName == o.ProfileName +} diff --git a/pkg/aa/dbus.go b/pkg/aa/dbus.go index c1907e4c..2745723e 100644 --- a/pkg/aa/dbus.go +++ b/pkg/aa/dbus.go @@ -27,3 +27,36 @@ func DbusFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRu Label: 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 + } + return r.Access < o.Access + } + return r.Qualifier.Less(o.Qualifier) +} + +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) +} diff --git a/pkg/aa/file.go b/pkg/aa/file.go index eff893bf..2d00a4f7 100644 --- a/pkg/aa/file.go +++ b/pkg/aa/file.go @@ -23,3 +23,24 @@ func FileFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRu Target: log["target"], } } + +func (r *File) Less(other any) bool { + o, _ := other.(*File) + 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) +} + +func (r *File) Equals(other any) bool { + o, _ := other.(*File) + return r.Path == o.Path && r.Access == o.Access && + r.Target == o.Target && r.Qualifier.Equals(o.Qualifier) +} + diff --git a/pkg/aa/include.go b/pkg/aa/include.go index 83c5bede..aa8e4938 100644 --- a/pkg/aa/include.go +++ b/pkg/aa/include.go @@ -10,3 +10,19 @@ type Include struct { 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 +} diff --git a/pkg/aa/io_uring.go b/pkg/aa/io_uring.go index 061ca707..2df84715 100644 --- a/pkg/aa/io_uring.go +++ b/pkg/aa/io_uring.go @@ -10,3 +10,18 @@ type IOUring struct { Label string } +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 + } + return r.Access < o.Access + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *IOUring) Equals(other any) bool { + o, _ := other.(*IOUring) + return r.Access == o.Access && r.Label == o.Label && r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/mount.go b/pkg/aa/mount.go index 105fee43..f0bad6dd 100644 --- a/pkg/aa/mount.go +++ b/pkg/aa/mount.go @@ -13,6 +13,24 @@ type MountConditions struct { Options []string } +func (m MountConditions) Less(other MountConditions) bool { + if m.Fs == other.Fs { + if m.Op == other.Op { + if m.FsType == other.FsType { + return len(m.Options) < len(other.Options) + } + return m.FsType < other.FsType + } + return m.Op < other.Op + } + return m.Fs < other.Fs +} + +func (m MountConditions) Equals(other MountConditions) bool { + return m.Fs == other.Fs && m.Op == other.Op && m.FsType == other.FsType && + slices.Equal(m.Options, other.Options) +} + type Mount struct { Qualifier MountConditions @@ -33,6 +51,28 @@ func MountFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorR MountPoint: log["name"], } } + +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 + } + return r.Source < o.Source + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Mount) Equals(other any) bool { + o, _ := other.(*Mount) + return r.Source == o.Source && r.MountPoint == o.MountPoint && + r.MountConditions.Equals(o.MountConditions) && + r.Qualifier.Equals(o.Qualifier) +} + type Umount struct { Qualifier MountConditions @@ -52,6 +92,24 @@ func UmountFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmor } } +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) + } + return r.MountPoint < o.MountPoint + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Umount) Equals(other any) bool { + o, _ := other.(*Umount) + return r.MountPoint == o.MountPoint && + r.MountConditions.Equals(o.MountConditions) && + r.Qualifier.Equals(o.Qualifier) +} + type Remount struct { Qualifier MountConditions @@ -70,3 +128,21 @@ func RemountFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmo 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) + } + return r.MountPoint < o.MountPoint + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Remount) Equals(other any) bool { + o, _ := other.(*Remount) + return r.MountPoint == o.MountPoint && + r.MountConditions.Equals(o.MountConditions) && + r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/mqueue.go b/pkg/aa/mqueue.go index d00b6bd3..e9f06b4e 100644 --- a/pkg/aa/mqueue.go +++ b/pkg/aa/mqueue.go @@ -19,3 +19,22 @@ func MqueueFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmor Label: log["label"], } } + +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 + } + return r.Access < o.Access + } + 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) +} diff --git a/pkg/aa/network.go b/pkg/aa/network.go index 62a0b44f..00a98378 100644 --- a/pkg/aa/network.go +++ b/pkg/aa/network.go @@ -10,6 +10,20 @@ type AddressExpr struct { Port string } +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 { Qualifier @@ -32,3 +46,24 @@ func NetworkFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmo 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 + } + return r.Domain < o.Domain + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Network) Equals(other any) bool { + o, _ := other.(*Network) + return r.Domain == o.Domain && r.Type == o.Type && + r.Protocol == o.Protocol && r.AddressExpr.Equals(o.AddressExpr) && + r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/pivot_root.go b/pkg/aa/pivot_root.go index 2e336ab1..2c0f5c63 100644 --- a/pkg/aa/pivot_root.go +++ b/pkg/aa/pivot_root.go @@ -19,3 +19,24 @@ func PivotRootFromLog(log map[string]string, noNewPrivs, fileInherit bool) Appar TargetProfile: log["name"], } } + +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 + } + return r.OldRoot < o.OldRoot + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *PivotRoot) Equals(other any) bool { + o, _ := other.(*PivotRoot) + return r.OldRoot == o.OldRoot && r.NewRoot == o.NewRoot && + r.TargetProfile == o.TargetProfile && + r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/ptrace.go b/pkg/aa/ptrace.go index a6bde6e3..05f94320 100644 --- a/pkg/aa/ptrace.go +++ b/pkg/aa/ptrace.go @@ -18,3 +18,19 @@ func PtraceFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmor } } +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 + } + return r.Access < o.Access + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Ptrace) Equals(other any) bool { + o, _ := other.(*Ptrace) + return r.Access == o.Access && r.Peer == o.Peer && + r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/rlimit.go b/pkg/aa/rlimit.go index a4fabec4..1b6fac85 100644 --- a/pkg/aa/rlimit.go +++ b/pkg/aa/rlimit.go @@ -9,3 +9,19 @@ type Rlimit struct { Op string Value string } + +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 + } + return r.Op < o.Op + } + return r.Key < o.Key +} + +func (r *Rlimit) Equals(other any) bool { + o, _ := other.(*Rlimit) + return r.Key == o.Key && r.Op == o.Op && r.Value == o.Value +} diff --git a/pkg/aa/rules.go b/pkg/aa/rules.go index 287bfd97..0f0be834 100644 --- a/pkg/aa/rules.go +++ b/pkg/aa/rules.go @@ -23,97 +23,22 @@ func NewQualifier(owner, noNewPrivs, fileInherit bool) Qualifier { } } -func NewCapability(log map[string]string, noNewPrivs, fileInherit bool) Capability { - return Capability{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - Name: log["capname"], +func (r Qualifier) Less(other Qualifier) bool { + if r.Audit == other.Audit { + if r.AccessType == other.AccessType { + return r.Owner + } + return r.AccessType < other.AccessType } + return r.Audit } -func NewNetwork(log map[string]string, noNewPrivs, fileInherit bool) Network { - return Network{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - AddressExpr: AddressExpr{ - Source: log["laddr"], - Destination: log["faddr"], - Port: log["lport"], - }, - Domain: log["family"], - Type: log["sock_type"], - Protocol: log["protocol"], - } +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 } -func NewFile(log map[string]string, noNewPrivs, fileInherit bool) File { - owner := false - if log["fsuid"] == log["ouid"] && log["OUID"] != "root" { - owner = true - } - return File{ - Qualifier: NewQualifier(owner, noNewPrivs, fileInherit), - Path: log["name"], - Access: maskToAccess[log["requested_mask"]], - Target: log["target"], - } -} - -func NewSignal(log map[string]string, noNewPrivs, fileInherit bool) Signal { - return Signal{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - Access: maskToAccess[log["requested_mask"]], - Set: log["signal"], - Peer: log["peer"], - } -} - -func NewPtrace(log map[string]string, noNewPrivs, fileInherit bool) Ptrace { - return Ptrace{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - Access: maskToAccess[log["requested_mask"]], - Peer: log["peer"], - } -} - -func NewUnix(log map[string]string, noNewPrivs, fileInherit bool) Unix { - return Unix{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - Access: maskToAccess[log["requested_mask"]], - Type: log["sock_type"], - Protocol: log["protocol"], - Address: log["addr"], - Label: log["peer_label"], - Attr: log["attr"], - Opt: log["opt"], - Peer: log["peer"], - PeerAddr: log["peer_addr"], - } -} - -func NewMount(log map[string]string, noNewPrivs, fileInherit bool) Mount { - return Mount{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - MountConditions: MountConditions{ - Fs: "", - Op: "", - FsType: log["fstype"], - Options: []string{}, - }, - Source: log["srcname"], - MountPoint: log["name"], - } -} - -func NewDbus(log map[string]string, noNewPrivs, fileInherit bool) Dbus { - return Dbus{ - Qualifier: NewQualifier(false, noNewPrivs, fileInherit), - Access: log["mask"], - Bus: log["bus"], - Name: log["name"], - Path: log["path"], - Interface: log["interface"], - Member: log["member"], - Label: log["peer_label"], - } // Preamble specific rules type Abi struct { diff --git a/pkg/aa/signal.go b/pkg/aa/signal.go index 09a699f5..0c884188 100644 --- a/pkg/aa/signal.go +++ b/pkg/aa/signal.go @@ -20,3 +20,22 @@ func SignalFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmor } } +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 + } + return r.Access < o.Access + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Signal) Equals(other any) bool { + o, _ := other.(*Signal) + return r.Access == o.Access && r.Set == o.Set && + r.Peer == o.Peer && r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/unix.go b/pkg/aa/unix.go index c1fa6ea1..f7a97089 100644 --- a/pkg/aa/unix.go +++ b/pkg/aa/unix.go @@ -31,3 +31,43 @@ func UnixFromLog(log map[string]string, noNewPrivs, fileInherit bool) ApparmorRu 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 + } + return r.Access < o.Access + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Unix) Equals(other any) bool { + o, _ := other.(*Unix) + 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) +} diff --git a/pkg/aa/userns.go b/pkg/aa/userns.go index 36238df8..a97cfa07 100644 --- a/pkg/aa/userns.go +++ b/pkg/aa/userns.go @@ -16,3 +16,15 @@ func UsernsFromLog(log map[string]string, noNewPrivs, fileInherit bool) Apparmor } } +func (r *Userns) Less(other any) bool { + o, _ := other.(*Userns) + if r.Qualifier.Equals(o.Qualifier) { + return r.Create + } + return r.Qualifier.Less(o.Qualifier) +} + +func (r *Userns) Equals(other any) bool { + o, _ := other.(*Userns) + return r.Create == o.Create && r.Qualifier.Equals(o.Qualifier) +} diff --git a/pkg/aa/variables.go b/pkg/aa/variables.go index 03f7c58f..7936158c 100644 --- a/pkg/aa/variables.go +++ b/pkg/aa/variables.go @@ -12,17 +12,32 @@ import ( "strings" "github.com/arduino/go-paths-helper" + "golang.org/x/exp/slices" ) var ( regVariablesDef = regexp.MustCompile(`@{(.*)}\s*[+=]+\s*(.*)`) regVariablesRef = regexp.MustCompile(`@{([^{}]+)}`) + // Default Apparmor magic directory: /etc/apparmor.d/. + MagicRoot = paths.New("/etc/apparmor.d") +) + 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