1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
|
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2015 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
# License as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# ----------------------------------------------------------------------
import unittest
from apparmor.common import AppArmorException
from apparmor.logparser import ReadLog
from common_test import AATest, setup_all_loops # , setup_aa
class TestParseEvent(AATest):
tests = ()
def setUp(self):
self.parser = ReadLog('', '', '')
def test_parse_event_audit_1(self):
event = 'type=AVC msg=audit(1345027352.096:499): apparmor="ALLOWED" operation="rename_dest" parent=6974 profile="/usr/sbin/httpd2-prefork//vhost_foo" name=2F686F6D652F7777772F666F6F2E6261722E696E2F68747470646F63732F61707061726D6F722F696D616765732F746573742F696D61676520312E6A7067 pid=20143 comm="httpd2-prefork" requested_mask="wc" denied_mask="wc" fsuid=30 ouid=30'
parsed_event = self.parser.parse_event(event)
self.assertEqual(parsed_event['name'], '/home/www/foo.bar.in/httpdocs/apparmor/images/test/image 1.jpg')
self.assertEqual(parsed_event['profile'], '/usr/sbin/httpd2-prefork//vhost_foo')
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
self.assertEqual(parsed_event['request_mask'], 'wc')
self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event))
def test_parse_event_audit_2(self):
event = 'type=AVC msg=audit(1322614918.292:4376): apparmor="ALLOWED" operation="file_perm" parent=16001 profile=666F6F20626172 name="/home/foo/.bash_history" pid=17011 comm="bash" requested_mask="rw" denied_mask="rw" fsuid=0 ouid=1000'
parsed_event = self.parser.parse_event(event)
self.assertEqual(parsed_event['name'], '/home/foo/.bash_history')
self.assertEqual(parsed_event['profile'], 'foo bar')
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
self.assertEqual(parsed_event['request_mask'], 'rw')
self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event))
def test_parse_event_syslog_1(self):
# from https://bugs.launchpad.net/apparmor/+bug/1399027
event = '2014-06-09T20:37:28.975070+02:00 geeko kernel: [21028.143765] type=1400 audit(1402339048.973:1421): apparmor="ALLOWED" operation="open" profile="/home/cb/linuxtag/apparmor/scripts/hello" name="/dev/tty" pid=14335 comm="hello" requested_mask="rw" denied_mask="rw" fsuid=1000 ouid=0'
parsed_event = self.parser.parse_event(event)
self.assertEqual(parsed_event['name'], '/dev/tty')
self.assertEqual(parsed_event['profile'], '/home/cb/linuxtag/apparmor/scripts/hello')
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
self.assertEqual(parsed_event['request_mask'], 'rw')
self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event))
def test_parse_event_syslog_2(self):
# from https://bugs.launchpad.net/apparmor/+bug/1399027
event = 'Dec 7 13:18:59 rosa kernel: audit: type=1400 audit(1417954745.397:82): apparmor="ALLOWED" operation="open" profile="/home/simi/bin/aa-test" name="/usr/bin/" pid=3231 comm="ls" requested_mask="r" denied_mask="r" fsuid=1000 ouid=0'
parsed_event = self.parser.parse_event(event)
self.assertEqual(parsed_event['name'], '/usr/bin/')
self.assertEqual(parsed_event['profile'], '/home/simi/bin/aa-test')
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
self.assertEqual(parsed_event['request_mask'], 'r')
self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event))
def test_parse_disconnected_path(self):
# from https://bugzilla.opensuse.org/show_bug.cgi?id=918787
event = 'type=AVC msg=audit(1424425690.883:716630): apparmor="ALLOWED" operation="file_mmap" info="Failed name lookup - disconnected path" error=-13 profile="/sbin/klogd" name="var/run/nscd/passwd" pid=25333 comm="id" requested_mask="r" denied_mask="r" fsuid=1002 ouid=0'
parsed_event = self.parser.parse_event(event)
self.assertEqual(parsed_event, {
'aamode': 'ERROR', # aamode for disconnected paths overridden aamode in parse_event()
'active_hat': None,
'attr': None,
'denied_mask': 'r',
'error_code': 13,
'fsuid': 1002,
'info': 'Failed name lookup - disconnected path',
'magic_token': 0,
'name': 'var/run/nscd/passwd',
'name2': None,
'operation': 'file_mmap',
'ouid': 0,
'parent': 0,
'pid': 25333,
'profile': '/sbin/klogd',
'request_mask': 'r',
'resource': 'Failed name lookup - disconnected path',
'task': 0,
'time': 1424425690,
'family': None,
'protocol': None,
'sock_type': None,
'class': None,
})
self.assertIsNotNone(ReadLog.RE_LOG_ALL.search(event))
def test_get_rule_type(self):
rules = [
('mount fstype=bpf options=(rw) random_label -> /sys/fs/bpf/,', 'mount'),
('unix send addr=@foo{a,b} peer=(label=splat),', 'unix'),
('userns create, # cmt', 'userns'),
('allow /tmp/foo ra,', 'file'),
('file rwix /foo,', 'file'),
('signal set=quit peer=unconfined,', 'signal')
]
for r, exp in rules:
self.assertEqual(self.parser.get_rule_type(r)[0], exp)
self.assertEqual(self.parser.get_rule_type('invalid rule,'), None)
def test_create_rule_from_ev(self):
events = [
('Sep 9 12:51:50 ubuntu-desktop kernel: [ 1612.746129] type=1400 audit(1284061910.975:672): apparmor="DENIED" operation="capable" parent=2663 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority" pid=7292 comm="syscall_setprio" capability=23 capname="sys_nice"', 'capability', 'capability sys_nice,'),
('[ 1612.746129] audit: type=1400 audit(1284061910.975:672): apparmor="DENIED" operation="capable" parent=2663 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/syscall_setpriority" pid=7292 comm="syscall_setprio" capability=23 capname="sys_nice"', 'capability', 'capability sys_nice,'),
('Sep 9 12:51:36 ubuntu-desktop kernel: [ 97.492562] audit: type=1400 audit(1431116353.523:77): apparmor="DENIED" operation="change_profile" profile="/tests/regression/apparmor/changeprofile" pid=3459 comm="changeprofile" target="/tests/regression/apparmor/rename"', 'change_profile', 'change_profile -> /tests/regression/apparmor/rename,'),
('Jul 31 17:10:35 dbusdev-saucy-amd64 dbus[1692]: apparmor="DENIED" operation="dbus_method_call" bus="session" name="org.freedesktop.DBus" path="/org/freedesktop/DBus" interface="org.freedesktop.DBus" member="Hello" mask="send" pid=2922 profile="/tmp/apparmor-2.8.0/tests/regression/apparmor/dbus_service" peer_profile="unconfined"', 'dbus', 'dbus send bus=session path=/org/freedesktop/DBus interface=org.freedesktop.DBus member=Hello peer=(label=unconfined),'),
('Jul 31 17:11:16 dbusdev-saucy-amd64 dbus[1692]: apparmor="DENIED" operation="dbus_bind" bus="session" name="com.apparmor.Test" mask="bind" pid=2940 profile="/tmp/apparmor-2.8.0/tests/regression/apparmor/dbus_service"', 'dbus', 'dbus bind bus=session name=com.apparmor.Test,'),
('type=AVC msg=audit(1345027352.096:499): apparmor="ALLOWED" operation="rename_dest" parent=6974 profile="/usr/sbin/httpd2-prefork//vhost_foo" name=2F686F6D652F7777772F666F6F2E6261722E696E2F68747470646F63732F61707061726D6F722F696D616765732F746573742F696D61676520312E6A7067 pid=20143 comm="httpd2-prefork" requested_mask="wc" denied_mask="wc" fsuid=30 ouid=30',
'file', 'owner "/home/www/foo.bar.in/httpdocs/apparmor/images/test/image 1.jpg" w,'), # noqa: E127
('2014-06-09T20:37:28.975070+02:00 geeko kernel: [21028.143765] type=1400 audit(1402339048.973:1421): apparmor="ALLOWED" operation="open" profile="/home/cb/linuxtag/apparmor/scripts/hello" name="/dev/tty" pid=14335 comm="hello" requested_mask="rw" denied_mask="rw" fsuid=1000 ouid=0', 'file', '/dev/tty rw,'),
('[ 4584.703379] audit: type=1400 audit(1680266735.359:69): apparmor="DENIED" operation="uring_sqpoll" class="io_uring" profile="/root/apparmor/tests/regression/apparmor/io_uring" pid=1320 comm="io_uring" requested="sqpoll" denied="sqpoll"', 'io_uring', 'io_uring sqpoll,'),
('[ 4584.491076] audit: type=1400 audit(1680266735.147:63): apparmor="DENIED" operation="uring_override" class="io_uring" profile="/root/apparmor/tests/regression/apparmor/io_uring" pid=1193 comm="io_uring" requested="override_creds" denied="override_creds" tcontext="/root/apparmor/tests/regression/apparmor/io_uring"', 'io_uring', 'io_uring override_creds label=/root/apparmor/tests/regression/apparmor/io_uring,'),
('type=AVC msg=audit(1409700640.016:547457): apparmor="DENIED" operation="mount" info="failed mntpnt match" error=-13 profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/mount" name="/tmp/sdtest.19033-29001-MPfz98/mountpoint/" pid=19085 comm="mount" fstype="ext2" srcname="/dev/loop0/" flags="rw, mand"', 'mount', 'mount fstype=(ext2) options=(mand, rw) /dev/loop0/ -> /tmp/sdtest.19033-29001-MPfz98/mountpoint/,'),
('type=AVC msg=audit(1709108389.303:12383): apparmor="DENIED" operation="mount" class="mount" info="failed mntpnt match" error=-13 profile="/home/user/test/testmount" name="/tmp/foo/" pid=14155 comm="testmount" flags="ro, remount"', 'mount', 'mount options=(remount, ro) -> /tmp/foo/,'),
('type=AVC msg=audit(1709025786.045:43147): apparmor="DENIED" operation="umount" class="mount" profile="/home/user/test/testmount" name="/mnt/a/" pid=26697 comm="testmount"', 'mount', 'umount /mnt/a/,'),
('Apr 05 19:36:19 ubuntu kernel: audit: type=1400 audit(1649187379.660:255): apparmor="DENIED" operation="create" profile="/root/apparmor/tests/regression/apparmor/posix_mq_rcv" name="/queuename" pid=791 comm="posix_mq_rcv" requested="create" denied="create" class="posix_mqueue" fsuid=0 ouid=0', 'mqueue', 'mqueue create type=posix /queuename,'),
('Apr 05 19:36:29 ubuntu kernel: audit: type=1400 audit(1649187389.828:262): apparmor="DENIED" operation="open" profile="/root/apparmor/tests/regression/apparmor/posix_mq_rcv" name="/queuename" pid=848 comm="posix_mq_rcv" requested="read create" denied="read" class="posix_mqueue" fsuid=0 ouid=0', 'mqueue', 'mqueue read type=posix /queuename,'),
('Apr 5 19:30:56 precise-amd64 kernel: [153073.826757] type=1400 audit(1308766940.698:3704): apparmor="DENIED" operation="sendmsg" parent=24737 profile="/usr/bin/evince-thumbnailer" pid=24743 comm="evince-thumbnai" laddr=192.168.66.150 lport=765 faddr=192.168.66.200 fport=2049 family="inet" sock_type="stream" protocol=6', 'network', 'network inet stream ip=192.168.66.150 port=765 peer=(ip=192.168.66.200 port=2049),'),
('Apr 5 19:31:04 precise-amd64 kernel: [153073.826757] type=1400 audit(1308766940.698:3704): apparmor="DENIED" operation="sendmsg" parent=24737 profile="/usr/bin/evince-thumbnailer" pid=24743 comm="evince-thumbnai" lport=765 fport=2049 family="inet" sock_type="stream" protocol=6', 'network', 'network inet stream port=765 peer=(port=2049),'),
('type=AVC msg=audit(1409700678.384:547594): apparmor="DENIED" operation="pivotroot" profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/pivot_root" name="/tmp/sdtest.21082-7446-EeefO6/new_root/" pid=21162 comm="pivot_root" srcname="/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/"', 'pivot_root', 'pivot_root oldroot=/tmp/sdtest.21082-7446-EeefO6/new_root/put_old/ /tmp/sdtest.21082-7446-EeefO6/new_root/,'),
('type=AVC msg=audit(1409700683.304:547661): apparmor="DENIED" operation="ptrace" profile="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace" pid=22465 comm="ptrace" requested_mask="tracedby" denied_mask="tracedby" peer="/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace"', 'ptrace', 'ptrace tracedby peer=/home/ubuntu/bzr/apparmor/tests/regression/apparmor/ptrace,'),
('type=AVC msg=audit(1409438250.564:201): apparmor="DENIED" operation="signal" profile="/usr/bin/pulseaudio" pid=2531 comm="pulseaudio" requested_mask="send" denied_mask="send" signal=term peer="/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper"', 'signal', 'signal send set=term peer=/usr/bin/pulseaudio///usr/lib/pulseaudio/pulse/gconf-helper,'),
('type=AVC msg=audit(1711454639.955:322): apparmor="DENIED" operation="connect" class="net" profile="/home/user/test/client.py" pid=80819 comm="client.py" family="unix" sock_type="stream" protocol=0 requested="send receive connect" denied="send receive connect" addr=none peer_addr="@test_abstract_socket" peer="/home/user/test/server.py"', 'unix', 'unix (connect, receive, send) type=stream peer=(addr=@test_abstract_socket),'),
('type=AVC msg=audit(1711214183.107:298): apparmor="DENIED" operation="connect" class="net" profile="/home/user/test/client.py" pid=65262 comm="server.py" family="unix" sock_type="stream" protocol=0 requested="send receive accept" denied="send accept" addr="@test_abstract_socket" peer_addr=none peer="unconfined"', 'unix', 'unix (accept, send) type=stream addr=@test_abstract_socket,'),
('[ 176.385388] audit: type=1400 audit(1666891380.570:78): apparmor="DENIED" operation="userns_create" class="namespace" profile="/usr/bin/userns_child_exec" pid=1785 comm="userns_child_ex" requested="userns_create" denied="userns_create"', 'userns', 'userns create,'),
('[ 429.272003] audit: type=1400 audit(1720613712.153:168): apparmor="AUDIT" operation="userns_create" class="namespace" info="Userns create - transitioning profile" profile="unconfined" pid=5630 comm="unshare" requested="userns_create" target="unprivileged_userns" execpath="/usr/bin/unshare"', 'userns', 'userns create,'),
]
for ev, expected_type, expected_rule in events:
parsed_event = self.parser.parse_event(ev)
r = self.parser.create_rule_from_ev(parsed_event)
self.assertIsNotNone(r)
clean_rule = r.get_clean()
self.assertEqual(self.parser.get_rule_type(clean_rule)[0], expected_type)
self.assertEqual(expected_rule, clean_rule)
class TestParseEventForTreeInvalid(AATest):
tests = (
('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="foo" fsuid=1000 ouid=0', AppArmorException), # invalid file permissions "foo"
('type=AVC msg=audit(1556742870.707:3614): apparmor="ALLOWED" operation="open" profile="/bin/hello" name="/dev/tty" pid=12856 comm="hello" requested_mask="wr" denied_mask="wr::w" fsuid=1000 ouid=0', AppArmorException), # "wr::w" mixes owner and other
)
def setUp(self):
self.parser = ReadLog('', '', '')
def _fake_profile_exists(self, program):
return True
def test_invalid_create_rule_from_ev(self):
events = [
'Jul 31 17:11:16 dbusdev-saucy-amd64 dbus[1692]: apparmor="DENIED" operation="invalid" bus="session" name="com.apparmor.Test" mask="bind" pid=2940 profile="/tmp/apparmor-2.8.0/tests/regression/apparmor/dbus_service"', # operation is invalid
'type=AVC msg=audit(1709108389.303:12383): apparmor="DENIED" operation="mount" class="mount" info="failed mntpnt match" error=-13 profile="/home/user/test/testmount" name="/tmp/foo/" pid=14155 comm="testmount" flags="invalid"', # invalid flag
'[ 176.385388] audit: type=1400 audit(1666891380.570:78): apparmor="DENIED" operation="userns_create" class="namespace" profile="/usr/bin/userns_child_exec" pid=1785 comm="userns_child_ex" requested="userns_create" denied="invalid"' # invalid denied
]
for ev in events:
parsed_event = self.parser.parse_event(ev)
r = self.parser.create_rule_from_ev(parsed_event)
self.assertEqual(r, None)
def _run_test(self, params, expected):
self.parser.profile_exists = self._fake_profile_exists # inject fake function that always returns True - much easier than handing over a ProfileList object to __init__
parsed_event = self.parser.parse_event(params)
with self.assertRaises(expected):
self.parser.parse_event_for_tree(parsed_event)
setup_all_loops(__name__)
if __name__ == "__main__":
unittest.main(verbosity=1)
|