mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 06:16:03 +00:00
A commit before changing modes style
This commit is contained in:
@@ -3,6 +3,7 @@ import sys
|
||||
|
||||
sys.path.append('../')
|
||||
import apparmor.aa
|
||||
import apparmor.logparser
|
||||
|
||||
#from apparmor.aa import parse_event
|
||||
|
||||
@@ -13,8 +14,9 @@ class Test(unittest.TestCase):
|
||||
|
||||
|
||||
def test_parse_event(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 = apparmor.aa.parse_event(event)
|
||||
parsed_event = apparmor.logparser.ReadLog.parse_event(apparmor.logparser.ReadLog, event)
|
||||
self.assertEqual(parsed_event['name'], '/home/www/foo.bar.in/httpdocs/apparmor/images/test/image 1.jpg', 'Incorrectly parsed/decoded name')
|
||||
self.assertEqual(parsed_event['profile'], '/usr/sbin/httpd2-prefork//vhost_foo', 'Incorrectly parsed/decode profile name')
|
||||
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
||||
@@ -26,7 +28,7 @@ class Test(unittest.TestCase):
|
||||
#print(parsed_event)
|
||||
|
||||
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="w" denied_mask="w" fsuid=0 ouid=1000'
|
||||
parsed_event = apparmor.aa.parse_event(event)
|
||||
parsed_event = apparmor.logparser.ReadLog.parse_event(apparmor.logparser.ReadLog, event)
|
||||
self.assertEqual(parsed_event['name'], '/home/foo/.bash_history', 'Incorrectly parsed/decoded name')
|
||||
self.assertEqual(parsed_event['profile'], 'foo bar', 'Incorrectly parsed/decode profile name')
|
||||
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
||||
|
@@ -16,7 +16,7 @@ class Test(unittest.TestCase):
|
||||
for regex in regex_tests.sections():
|
||||
parsed_regex = re.compile(apparmor.common.convert_regexp(regex))
|
||||
for regex_testcase in regex_tests.options(regex):
|
||||
self.assertEqual(bool(parsed_regex.search(regex_testcase)), eval(regex_tests[regex][regex_testcase]), 'Incorrectly Parsed regex')
|
||||
self.assertEqual(bool(parsed_regex.search(regex_testcase)), eval(regex_tests[regex][regex_testcase]), 'Incorrectly Parsed regex: %s' %regex)
|
||||
|
||||
#def test_readkey(self):
|
||||
# print("Please press the Y button on the keyboard.")
|
||||
|
@@ -6,13 +6,6 @@ import apparmor.aa
|
||||
import os
|
||||
import argparse
|
||||
|
||||
if sys.version_info < (3,0):
|
||||
os.environ['AAPATH'] = '/bin/:/sbin/:/usr/bin/:/usr/sbin'
|
||||
else:
|
||||
os.environb.putenv('AAPATH', '/bin/:/sbin/:/usr/bin/:/usr/sbin')
|
||||
|
||||
|
||||
|
||||
logmark = ''
|
||||
|
||||
apparmor.aa.loadincludes()
|
||||
|
651
Tools/out.out
Normal file
651
Tools/out.out
Normal file
@@ -0,0 +1,651 @@
|
||||
Reading log entries from /var/log/messages.
|
||||
Updating AppArmor profiles in /etc/apparmor.d.
|
||||
ARRAY(0x1ac8bb8) ARRAY(0x1af2b80) ARRAY(0xbbe5f8) ARRAY(0x1b06848) ARRAY(0x1af2bb0) ARRAY(0x1af0a20) ARRAY(0x1b0ab68) ARRAY(0x1ab3918) ARRAY(0x1a865b0) ARRAY(0x1b753e8) ARRAY(0x1afc510) ARRAY(0x1abf788)
|
||||
be dumper@event = (
|
||||
[
|
||||
'path',
|
||||
660,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
);
|
||||
$VAR2 = [
|
||||
[
|
||||
'path',
|
||||
694,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR3 = [
|
||||
[
|
||||
'path',
|
||||
659,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR4 = [
|
||||
[
|
||||
'path',
|
||||
642,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR5 = [
|
||||
[
|
||||
'path',
|
||||
676,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR6 = [
|
||||
[
|
||||
'path',
|
||||
671,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR7 = [
|
||||
[
|
||||
'path',
|
||||
667,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR8 = [
|
||||
[
|
||||
'path',
|
||||
661,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR9 = [
|
||||
[
|
||||
'path',
|
||||
684,
|
||||
'/usr/sbin/nscd',
|
||||
'/usr/sbin/nscd',
|
||||
'HINT',
|
||||
'REJECTING',
|
||||
65540,
|
||||
'/proc/sys/vm/overcommit_memory',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR10 = [
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/empathy/libempathy-gtk-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
1114180,
|
||||
'/usr/lib64/empathy/libempathy-gtk-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/empathy/libempathy-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
1114180,
|
||||
'/usr/lib64/empathy/libempathy-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/etc/ld.so.cache',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/libdbus-glib-1.so.2.2.2',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
32770,
|
||||
'/home/kshitij/.config/Empathy/geometry.ini.7YRV1W',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/home/kshitij/.config/Empathy/geometry.ini.7YRV1W',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/home/kshitij/.config/Empathy/geometry.ini.7YRV1W',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
32770,
|
||||
'/home/kshitij/.config/Empathy/geometry.ini',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
98310,
|
||||
'/run/user/1000/dconf/user',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7664,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/home/kshitij/.config/dconf/user',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR11 = [
|
||||
[
|
||||
'path',
|
||||
7671,
|
||||
'null-complain-profile',
|
||||
'null-complain-profile',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/share/icons/gnome/scalable/actions/list-add-symbolic.svg',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7671,
|
||||
'null-complain-profile',
|
||||
'null-complain-profile',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/share/icons/gnome/scalable/actions/list-add-symbolic.svg',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7671,
|
||||
'null-complain-profile',
|
||||
'null-complain-profile',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/share/icons/gnome/scalable/actions/list-remove-symbolic.svg',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7671,
|
||||
'null-complain-profile',
|
||||
'null-complain-profile',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/share/icons/gnome/scalable/actions/list-remove-symbolic.svg',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7671,
|
||||
'null-complain-profile',
|
||||
'null-complain-profile',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/share/icons/gnome/scalable/actions/list-add-symbolic.svg',
|
||||
''
|
||||
]
|
||||
];
|
||||
$VAR12 = [
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/empathy/libempathy-gtk-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
1114180,
|
||||
'/usr/lib64/empathy/libempathy-gtk-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/empathy/libempathy-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
1114180,
|
||||
'/usr/lib64/empathy/libempathy-3.6.3.so',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/etc/ld.so.cache',
|
||||
''
|
||||
],
|
||||
[
|
||||
'path',
|
||||
7753,
|
||||
'/usr/bin/empathy',
|
||||
'/usr/bin/empathy',
|
||||
'HINT',
|
||||
'PERMITTING',
|
||||
65540,
|
||||
'/usr/lib64/libdbus-glib-1.so.2.2.2',
|
||||
''
|
||||
]
|
||||
];
|
||||
\n end dumper ARRAY(0x1b65788)
|
||||
Entry: ARRAY(0x1b65788)
|
||||
ARRAY(0x1ac9188)
|
||||
Entry: ARRAY(0x1ac9188)
|
||||
ARRAY(0x1a98d98)
|
||||
Entry: ARRAY(0x1a98d98)
|
||||
ARRAY(0x1aae1c0)
|
||||
Entry: ARRAY(0x1aae1c0)
|
||||
ARRAY(0x1af09d8)
|
||||
Entry: ARRAY(0x1af09d8)
|
||||
ARRAY(0x1b6fa20)
|
||||
Entry: ARRAY(0x1b6fa20)
|
||||
ARRAY(0x1a9d920)
|
||||
Entry: ARRAY(0x1a9d920)
|
||||
ARRAY(0x1ae70b8)
|
||||
Entry: ARRAY(0x1ae70b8)
|
||||
ARRAY(0x1b0fac0)
|
||||
Entry: ARRAY(0x1b0fac0)
|
||||
ARRAY(0xbbe340) ARRAY(0x1b54130) ARRAY(0x1b18578) ARRAY(0x1b040e0) ARRAY(0x1b73600) ARRAY(0x1a9e298) ARRAY(0x1b0a370) ARRAY(0x1adfb70) ARRAY(0x1ac9008) ARRAY(0x1ac1aa0) ARRAY(0x1ab8428) ARRAY(0x1b12e90) ARRAY(0x1a89ab0) ARRAY(0x1aaa6d8) ARRAY(0x1b4d400) ARRAY(0x1b4d5c8) ARRAY(0x1adb230) ARRAY(0x1aadb60) ARRAY(0x1ab3d68) ARRAY(0x1b75fb8) ARRAY(0x1aaa588) ARRAY(0x1b4dcd0) ARRAY(0x1a96c48) ARRAY(0x1adf468) ARRAY(0x1b5ca10) ARRAY(0x1aa76e8) ARRAY(0x1ab8cf8) ARRAY(0x1b72df0) ARRAY(0x1ae9630) ARRAY(0x1a86a90)
|
||||
Entry: ARRAY(0xbbe340)
|
||||
Entry: ARRAY(0x1b54130)
|
||||
Entry: ARRAY(0x1b18578)
|
||||
Entry: ARRAY(0x1b040e0)
|
||||
Entry: ARRAY(0x1b73600)
|
||||
Entry: ARRAY(0x1a9e298)
|
||||
Entry: ARRAY(0x1b0a370)
|
||||
Entry: ARRAY(0x1adfb70)
|
||||
Entry: ARRAY(0x1ac9008)
|
||||
Entry: ARRAY(0x1ac1aa0)
|
||||
Entry: ARRAY(0x1ab8428)
|
||||
Entry: ARRAY(0x1b12e90)
|
||||
Entry: ARRAY(0x1a89ab0)
|
||||
Entry: ARRAY(0x1aaa6d8)
|
||||
Entry: ARRAY(0x1b4d400)
|
||||
Entry: ARRAY(0x1b4d5c8)
|
||||
Entry: ARRAY(0x1adb230)
|
||||
Entry: ARRAY(0x1aadb60)
|
||||
Entry: ARRAY(0x1ab3d68)
|
||||
Entry: ARRAY(0x1b75fb8)
|
||||
Entry: ARRAY(0x1aaa588)
|
||||
Entry: ARRAY(0x1b4dcd0)
|
||||
Entry: ARRAY(0x1a96c48)
|
||||
Entry: ARRAY(0x1adf468)
|
||||
Entry: ARRAY(0x1b5ca10)
|
||||
Entry: ARRAY(0x1aa76e8)
|
||||
Entry: ARRAY(0x1ab8cf8)
|
||||
Entry: ARRAY(0x1b72df0)
|
||||
Entry: ARRAY(0x1ae9630)
|
||||
Entry: ARRAY(0x1a86a90)
|
||||
ARRAY(0x1afc540) ARRAY(0x1b15f38) ARRAY(0x1ab8b60) ARRAY(0x1abf7b8) ARRAY(0x1af6378)
|
||||
Entry: ARRAY(0x1afc540)
|
||||
Entry: ARRAY(0x1b15f38)
|
||||
Entry: ARRAY(0x1ab8b60)
|
||||
Entry: ARRAY(0x1abf7b8)
|
||||
Entry: ARRAY(0x1af6378)
|
||||
ARRAY(0x1b61898) ARRAY(0x1b12ec0) ARRAY(0x1af8bf0) ARRAY(0x1b0feb0) ARRAY(0x1b05ab0) ARRAY(0x1ab8470)
|
||||
Entry: ARRAY(0x1b61898)
|
||||
Entry: ARRAY(0x1b12ec0)
|
||||
Entry: ARRAY(0x1af8bf0)
|
||||
Entry: ARRAY(0x1b0feb0)
|
||||
Entry: ARRAY(0x1b05ab0)
|
||||
Entry: ARRAY(0x1ab8470)
|
||||
Complain-mode changes:
|
||||
|
||||
Profile: /usr/bin/empathy
|
||||
Path: /etc/ld.so.cache
|
||||
Mode: r
|
||||
Severity: 1
|
||||
|
||||
1 - #include <abstractions/base>
|
||||
2 - #include <abstractions/gnome>
|
||||
3 - #include <abstractions/kde>
|
||||
4 - #include <abstractions/ubuntu-gnome-terminal>
|
||||
5 -
|
411
apparmor/aa.py
411
apparmor/aa.py
@@ -17,6 +17,7 @@ import atexit
|
||||
import tempfile
|
||||
|
||||
import apparmor.config
|
||||
import apparmor.logparser
|
||||
import apparmor.severity
|
||||
import LibAppArmor
|
||||
|
||||
@@ -61,7 +62,7 @@ user_globs = []
|
||||
## Variables used under logprof
|
||||
### Were our
|
||||
t = hasher()#dict()
|
||||
transitions = dict()
|
||||
transitions = hasher()
|
||||
aa = hasher() # Profiles originally in sd, replace by aa
|
||||
original_aa = hasher()
|
||||
extras = hasher() # Inactive profiles from extras
|
||||
@@ -70,7 +71,7 @@ log = []
|
||||
pid = dict()
|
||||
|
||||
seen = hasher()#dir()
|
||||
profile_changes = dict()
|
||||
profile_changes = hasher()
|
||||
prelog = hasher()
|
||||
log_dict = hasher()#dict()
|
||||
changed = dict()
|
||||
@@ -215,7 +216,7 @@ def check_for_apparmor():
|
||||
|
||||
def which(file):
|
||||
"""Returns the executable fullpath for the file, None otherwise"""
|
||||
env_dirs = os.getenv('AAPATH').split(':')
|
||||
env_dirs = os.getenv('PATH').split(':')
|
||||
for env_dir in env_dirs:
|
||||
env_path = env_dir + '/' + file
|
||||
# Test if the path is executable or not
|
||||
@@ -244,7 +245,7 @@ def get_full_path(original_path):
|
||||
return os.path.realpath(path)
|
||||
|
||||
def find_executable(bin_path):
|
||||
"""Returns the full executable path for the binary given, None otherwise"""
|
||||
"""Returns the full executable path for the given, None otherwise"""
|
||||
full_bin = None
|
||||
if os.path.exists(bin_path):
|
||||
full_bin = get_full_path(bin_path)
|
||||
@@ -259,7 +260,9 @@ def find_executable(bin_path):
|
||||
|
||||
def get_profile_filename(profile):
|
||||
"""Returns the full profile name"""
|
||||
if profile.startswith('/'):
|
||||
if existing_profiles.get(profile, False):
|
||||
return existing_profiles[profile]
|
||||
elif profile.startswith('/'):
|
||||
# Remove leading /
|
||||
profile = profile[1:]
|
||||
else:
|
||||
@@ -370,14 +373,8 @@ def handle_binfmt(profile, path):
|
||||
library = glob_common(library)
|
||||
if not library:
|
||||
continue
|
||||
try:
|
||||
profile['allow']['path'][library]['mode'] |= str_to_mode('mr')
|
||||
except TypeError:
|
||||
profile['allow']['path'][library]['mode'] = str_to_mode('mr')
|
||||
try:
|
||||
profile['allow']['path'][library]['audit'] |= 0
|
||||
except TypeError:
|
||||
profile['allow']['path'][library]['audit'] = 0
|
||||
profile['allow']['path'][library]['mode'] = profile['allow']['path'][library].get('mode', set()) | str_to_mode('mr')
|
||||
profile['allow']['path'][library]['audit'] |= profile['allow']['path'][library].get('audit', set())
|
||||
|
||||
def get_inactive_profile(local_profile):
|
||||
if extras.get(local_profile, False):
|
||||
@@ -404,11 +401,11 @@ def create_new_profile(localfile):
|
||||
|
||||
local_profile[localfile]['allow']['path'][localfile]['mode'] = local_profile[localfile]['allow']['path'][localfile].get('mode', str_to_mode('r')) | str_to_mode('r')
|
||||
|
||||
local_profile[localfile]['allow']['path'][localfile]['audit'] = local_profile[localfile]['allow']['path'][localfile].get('audit', 0)
|
||||
local_profile[localfile]['allow']['path'][localfile]['audit'] = local_profile[localfile]['allow']['path'][localfile].get('audit', set())
|
||||
|
||||
local_profile[localfile]['allow']['path'][interpreter_path]['mode'] = local_profile[localfile]['allow']['path'][interpreter_path].get('mode', str_to_mode('ix')) | str_to_mode('ix')
|
||||
|
||||
local_profile[localfile]['allow']['path'][interpreter_path]['audit'] = local_profile[localfile]['allow']['path'][interpreter_path].get('audit', 0)
|
||||
local_profile[localfile]['allow']['path'][interpreter_path]['audit'] = local_profile[localfile]['allow']['path'][interpreter_path].get('audit', set())
|
||||
|
||||
if interpreter == 'perl':
|
||||
local_profile[localfile]['include']['abstractions/perl'] = True
|
||||
@@ -619,6 +616,7 @@ def set_profile_flags(prof_filename, newflags):
|
||||
def profile_exists(program):
|
||||
"""Returns True if profile exists, False otherwise"""
|
||||
# Check cache of profiles
|
||||
|
||||
if existing_profiles.get(program, False):
|
||||
return True
|
||||
# Check the disk for profile
|
||||
@@ -626,7 +624,7 @@ def profile_exists(program):
|
||||
#print(prof_path)
|
||||
if os.path.isfile(prof_path):
|
||||
# Add to cache of profile
|
||||
existing_profiles[program] = True
|
||||
existing_profiles[program] = prof_path
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -897,7 +895,7 @@ def handle_children(profile, hat, root):
|
||||
profile_changes[pid] = profile + '//' + hat
|
||||
else:
|
||||
profile_changes[pid] = profile
|
||||
elif type == 'unknown_hat':
|
||||
elif typ == 'unknown_hat':
|
||||
pid, p, h, aamode, uhat = entry[:5]
|
||||
if not regex_nullcomplain.search(p):
|
||||
profile = p
|
||||
@@ -952,7 +950,7 @@ def handle_children(profile, hat, root):
|
||||
elif ans == 'CMD_DENY':
|
||||
return None
|
||||
|
||||
elif type == 'capability':
|
||||
elif typ == 'capability':
|
||||
pid, p, h, prog, aamode, capability = entry[:6]
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
profile = p
|
||||
@@ -961,9 +959,8 @@ def handle_children(profile, hat, root):
|
||||
continue
|
||||
prelog[aamode][profile][hat]['capability'][capability] = True
|
||||
|
||||
elif type == 'path' or type == 'exec':
|
||||
elif typ == 'path' or typ == 'exec':
|
||||
pid, p, h, prog, aamode, mode, detail, to_name = entry[:8]
|
||||
|
||||
if not mode:
|
||||
mode = 0
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
@@ -973,7 +970,7 @@ def handle_children(profile, hat, root):
|
||||
continue
|
||||
|
||||
domainchange = 'nochange'
|
||||
if type == 'exec':
|
||||
if typ == 'exec':
|
||||
domainchange = 'change'
|
||||
|
||||
# Escape special characters
|
||||
@@ -1346,7 +1343,7 @@ def handle_children(profile, hat, root):
|
||||
if not aa[profile].get(exec_target, False):
|
||||
ynans = 'y'
|
||||
if exec_mode & str_to_mode('i'):
|
||||
ynans = UI_YesNo(_('A local profile for %s does not exit. Create one') % exec_target, 'n')
|
||||
ynans = UI_YesNo(_('A local profile for %s does not exit. Create one?') % exec_target, 'n')
|
||||
if ynans == 'y':
|
||||
hat = exec_target
|
||||
aa[profile][hat]['declared'] = False
|
||||
@@ -1377,7 +1374,7 @@ def handle_children(profile, hat, root):
|
||||
if domainchange == 'change':
|
||||
return None
|
||||
|
||||
elif type == 'netdomain':
|
||||
elif typ == 'netdomain':
|
||||
pid, p, h, prog, aamode, family, sock_type, protocol = entry[:8]
|
||||
|
||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||
@@ -1390,296 +1387,12 @@ def handle_children(profile, hat, root):
|
||||
|
||||
return None
|
||||
|
||||
def add_to_tree(loc_pid, parent, type, event):
|
||||
debug_logger.info('add_to_tree: pid [%s] type [%s] event [%s]' % (loc_pid, type, event))
|
||||
if not pid.get(loc_pid, False):
|
||||
profile, hat = event[:2]
|
||||
if parent and pid.get(parent, False):
|
||||
if not hat:
|
||||
hat = 'null-complain-profile'
|
||||
array_ref = ['fork', loc_pid, profile, hat]
|
||||
pid[parent].append(array_ref)
|
||||
pid[loc_pid] = array_ref
|
||||
#else:
|
||||
# array_ref = []
|
||||
# log.append(array_ref)
|
||||
# pid[pid] = array_ref
|
||||
pid[loc_pid] = pid.get(loc_pid, []) + [type, loc_pid, event]
|
||||
|
||||
# Variables used by logparsing routines
|
||||
LOG = None
|
||||
next_log_entry = None
|
||||
logmark = None
|
||||
seenmark = None
|
||||
RE_LOG_v2_6_syslog = re.compile('kernel:\s+(\[[\d\.\s]+\]\s+)?type=\d+\s+audit\([\d\.\:]+\):\s+apparmor=')
|
||||
RE_LOG_v2_6_audit = re.compile('type=AVC\s+(msg=)?audit\([\d\.\:]+\):\s+apparmor=')
|
||||
|
||||
MODE_MAP_RE = re.compile('r|w|l|m|k|a|x|i|u|p|c|n|I|U|P|C|N')
|
||||
LOG_MODE_RE = re.compile('r|w|l|m|k|a|x|ix|ux|px|cx|nx|pix|cix|Ix|Ux|Px|PUx|Cx|Nx|Pix|Cix')
|
||||
PROFILE_MODE_RE = re.compile('r|w|l|m|k|a|ix|ux|px|cx|pix|cix|Ux|Px|PUx|Cx|Pix|Cix')
|
||||
PROFILE_MODE_NT_RE = re.compile('r|w|l|m|k|a|x|ix|ux|px|cx|pix|cix|Ux|Px|PUx|Cx|Pix|Cix')
|
||||
PROFILE_MODE_DENY_RE = re.compile('r|w|l|m|k|a|x')
|
||||
|
||||
def prefetch_next_log_entry():
|
||||
if next_log_entry:
|
||||
sys.stderr.out('A log entry already present: %s' % next_log_entry)
|
||||
next_log_entry = LOG.readline()
|
||||
while RE_LOG_v2_6_syslog.search(next_log_entry) or RE_LOG_v2_6_audit.search(next_log_entry) or re.search(logmark, next_log_entry):
|
||||
next_log_entry = LOG.readline()
|
||||
if not next_log_entry:
|
||||
break
|
||||
|
||||
def get_next_log_entry():
|
||||
# If no next log entry fetch it
|
||||
if not next_log_entry:
|
||||
prefetch_next_log_entry()
|
||||
log_entry = next_log_entry
|
||||
next_log_entry = None
|
||||
return log_entry
|
||||
|
||||
def peek_at_next_log_entry():
|
||||
# Take a peek at the next log entry
|
||||
if not next_log_entry:
|
||||
prefetch_next_log_entry()
|
||||
return next_log_entry
|
||||
|
||||
def throw_away_next_log_entry():
|
||||
next_log_entry = None
|
||||
|
||||
def parse_log_record(record):
|
||||
debug_logger.debug('parse_log_record: %s' % record)
|
||||
|
||||
record_event = parse_event(record)
|
||||
return record_event
|
||||
|
||||
def add_event_to_tree(e):
|
||||
aamode = e.get('aamode', 'UNKNOWN')
|
||||
if e.get('type', False):
|
||||
if re.search('(UNKNOWN\[1501\]|APPARMOR_AUDIT|1501)', e['type']):
|
||||
aamode = 'AUDIT'
|
||||
elif re.search('(UNKNOWN\[1502\]|APPARMOR_ALLOWED|1502)', e['type']):
|
||||
aamode = 'PERMITTING'
|
||||
elif re.search('(UNKNOWN\[1503\]|APPARMOR_DENIED|1503)', e['type']):
|
||||
aamode = 'REJECTING'
|
||||
elif re.search('(UNKNOWN\[1504\]|APPARMOR_HINT|1504)', e['type']):
|
||||
aamode = 'HINT'
|
||||
elif re.search('(UNKNOWN\[1505\]|APPARMOR_STATUS|1505)', e['type']):
|
||||
aamode = 'STATUS'
|
||||
elif re.search('(UNKNOWN\[1506\]|APPARMOR_ERROR|1506)', e['type']):
|
||||
aamode = 'ERROR'
|
||||
else:
|
||||
aamode = 'UNKNOWN'
|
||||
|
||||
if aamode in ['UNKNOWN', 'AUDIT', 'STATUS', 'ERROR']:
|
||||
return None
|
||||
|
||||
if 'profile_set' in e['operation']:
|
||||
return None
|
||||
|
||||
# Skip if AUDIT event was issued due to a change_hat in unconfined mode
|
||||
if not e.get('profile', False):
|
||||
return None
|
||||
|
||||
# Convert new null profiles to old single level null profile
|
||||
if '//null-' in e['profile']:
|
||||
e['profile'] = 'null-complain-profile'
|
||||
|
||||
profile = e['profile']
|
||||
hat = None
|
||||
|
||||
if '\\' in e['profile']:
|
||||
profile, hat = e['profile'].split('\\')
|
||||
|
||||
# Filter out change_hat events that aren't from learning
|
||||
if e['operation'] == 'change_hat':
|
||||
if aamode != 'HINT' and aamode != 'PERMITTING':
|
||||
return None
|
||||
profile = e['name']
|
||||
if '\\' in e['name']:
|
||||
profile, hat = e['name'].split('\\')
|
||||
|
||||
# prog is no longer passed around consistently
|
||||
prog = 'HINT'
|
||||
|
||||
if profile != 'null-complain-profile' and not profile_exists(profile):
|
||||
return None
|
||||
|
||||
if e['operation'] == 'exec':
|
||||
if e.get('info', False) and e['info'] == 'mandatory profile missing':
|
||||
add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, aamode, 'PERMITTING', e['denied_mask'], e['name'], e['name2']])
|
||||
elif e.get('name2', False) and '\\null-/' in e['name2']:
|
||||
add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e.get('name', False):
|
||||
add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
else:
|
||||
debug_logger.debug('add_event_to_tree: dropped exec event in %s' % e['profile'])
|
||||
|
||||
elif 'file_' in e['operation']:
|
||||
add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e['operation'] in ['open', 'truncate', 'mkdir', 'mknod', 'rename_src',
|
||||
'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link']:
|
||||
add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e['operation'] == 'capable':
|
||||
add_to_tree(e['pid'], e['parent'], 'capability',
|
||||
[profile, hat, prog, aamode, e['name'], ''])
|
||||
elif e['operation'] == 'setattr' or 'xattr' in e['operation']:
|
||||
add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif 'inode_' in e['operation']:
|
||||
is_domain_change = False
|
||||
if e['operation'] == 'inode_permission' and (e['denied_mask'] & AA_MAY_EXEC) and aamode == 'PERMITTING':
|
||||
following = peek_at_next_log_entry()
|
||||
if following:
|
||||
entry = parse_log_record(following)
|
||||
if entry and entry.get('info', False) == 'set profile':
|
||||
is_domain_change = True
|
||||
throw_away_next_log_entry()
|
||||
|
||||
if is_domain_change:
|
||||
add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], e['name2']])
|
||||
else:
|
||||
add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
|
||||
elif e['operation'] == 'sysctl':
|
||||
add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
|
||||
elif e['operation'] == 'clone':
|
||||
parent , child = e['pid'], e['task']
|
||||
if not parent:
|
||||
parent = 'null-complain-profile'
|
||||
if not hat:
|
||||
hat = 'null-complain-profile'
|
||||
arrayref = ['fork', child, profile, hat]
|
||||
if pid.get(parent, False):
|
||||
pid[parent] += [arrayref]
|
||||
else:
|
||||
log += [arrayref]
|
||||
pid[child] = arrayref
|
||||
|
||||
elif op_type(e['operation']) == 'net':
|
||||
add_to_tree(e['pid'], e['parent'], 'netdomain',
|
||||
[profile, hat, prog, aamode, e['family'], e['sock_type'], e['protocol']])
|
||||
elif e['operation'] == 'change_hat':
|
||||
add_to_tree(e['pid'], e['parent'], 'unknown_hat',
|
||||
[profile, hat, aamode, hat])
|
||||
else:
|
||||
debug_logger.debug('UNHANDLED: %s' % e)
|
||||
|
||||
def read_log(logmark):
|
||||
seenmark = True
|
||||
if logmark:
|
||||
seenmark = False
|
||||
#last = None
|
||||
#event_type = None
|
||||
try:
|
||||
#print(filename)
|
||||
log_open = open_file_read(filename)
|
||||
except IOError:
|
||||
raise AppArmorException('Can not read AppArmor logfile: ' + filename)
|
||||
|
||||
with log_open as f_in:
|
||||
for line in f_in:
|
||||
line = line.strip()
|
||||
debug_logger.debug('read_log: %s' % line)
|
||||
if logmark in line:
|
||||
seenmark = True
|
||||
if not seenmark:
|
||||
debug_logger.debug('read_log: seenmark = %s' % seenmark)
|
||||
|
||||
event = parse_log_record(line)
|
||||
if event:
|
||||
add_event_to_tree(event)
|
||||
logmark = ''
|
||||
|
||||
def parse_event(msg):
|
||||
"""Parse the event from log into key value pairs"""
|
||||
msg = msg.strip()
|
||||
debug_logger.info('parse_event: %s' % msg)
|
||||
#print(repr(msg))
|
||||
if sys.version_info < (3,0):
|
||||
# parse_record fails with u'foo' style strings hence typecasting to string
|
||||
msg = str(msg)
|
||||
event = LibAppArmor.parse_record(msg)
|
||||
ev = dict()
|
||||
ev['resource'] = event.info
|
||||
ev['active_hat'] = event.active_hat
|
||||
ev['aamode'] = event.event
|
||||
ev['time'] = event.epoch
|
||||
ev['operation'] = event.operation
|
||||
ev['profile'] = event.profile
|
||||
ev['name'] = event.name
|
||||
ev['name2'] = event.name2
|
||||
ev['attr'] = event.attribute
|
||||
ev['parent'] = event.parent
|
||||
ev['pid'] = event.pid
|
||||
ev['task'] = event.task
|
||||
ev['info'] = event.info
|
||||
dmask = event.denied_mask
|
||||
rmask = event.requested_mask
|
||||
ev['magic_token'] = event.magic_token
|
||||
if ev['operation'] and op_type(ev['operation']) == 'net':
|
||||
ev['family'] = event.net_family
|
||||
ev['protocol'] = event.net_protocol
|
||||
ev['sock_type'] = event.net_sock_type
|
||||
LibAppArmor.free_record(event)
|
||||
# Map c (create) to a and d (delete) to w, logprof doesn't support c and d
|
||||
if rmask:
|
||||
rmask = rmask.replace('c', 'a')
|
||||
rmask = rmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(rmask)):
|
||||
fatal_error(_('Log contains unknown mode %s') % rmask)
|
||||
if dmask:
|
||||
dmask = dmask.replace('c', 'a')
|
||||
dmask = dmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(dmask)):
|
||||
fatal_error(_('Log contains unknown mode %s') % dmask)
|
||||
#print('parse_event:', ev['profile'], dmask, ev['name2'])
|
||||
mask, name = log_str_to_mode(ev['profile'], dmask, ev['name2'])
|
||||
|
||||
ev['denied_mask'] = mask
|
||||
ev['name2'] = name
|
||||
|
||||
mask, name = log_str_to_mode(ev['profile'], rmask, ev['name2'])
|
||||
ev['request_mask'] = mask
|
||||
ev['name2'] = name
|
||||
|
||||
if not ev['time']:
|
||||
ev['time'] = int(time.time())
|
||||
# Remove None keys
|
||||
#for key in ev.keys():
|
||||
# if not ev[key] or not re.search('[\w]+', ev[key]):
|
||||
# ev.pop(key)
|
||||
|
||||
if ev['aamode']:
|
||||
# Convert aamode values to their counter-parts
|
||||
mode_convertor = {
|
||||
0: 'UNKNOWN',
|
||||
1: 'ERROR',
|
||||
2: 'AUDITING',
|
||||
3: 'PERMITTING',
|
||||
4: 'REJECTING',
|
||||
5: 'HINT',
|
||||
6: 'STATUS'
|
||||
}
|
||||
try:
|
||||
ev['aamode'] = mode_convertor[ev['aamode']]
|
||||
except KeyError:
|
||||
ev['aamode'] = None
|
||||
|
||||
if ev['aamode']:
|
||||
#debug_logger.debug(ev)
|
||||
return ev
|
||||
else:
|
||||
return None
|
||||
|
||||
def hide_log_mode(mode):
|
||||
mode = mode.replace('::', '')
|
||||
return mode
|
||||
@@ -1747,8 +1460,8 @@ def order_globs(globs, path):
|
||||
return sorted(globs)
|
||||
|
||||
def ask_the_questions():
|
||||
found = None
|
||||
print(log_dict)
|
||||
found = 0
|
||||
global seen_events
|
||||
for aamode in sorted(log_dict.keys()):
|
||||
# Describe the type of changes
|
||||
if aamode == 'PERMITTING':
|
||||
@@ -1939,14 +1652,15 @@ def ask_the_questions():
|
||||
# If already present skip
|
||||
if aa[profile][hat][incname]:
|
||||
continue
|
||||
if incname.startswith(profile_dir):
|
||||
incname = incname.replace(profile_dir+'/', '', 1)
|
||||
|
||||
include_valid = valid_include(profile, incname)
|
||||
include_valid = valid_include('', incname)
|
||||
|
||||
if not include_valid:
|
||||
continue
|
||||
|
||||
cm, am, m = match_include_to_path(incname, 'allow', path)
|
||||
|
||||
if 'base' in incname: print(cm,am,m,mode,mode_contains(cm, mode))
|
||||
if cm and mode_contains(cm, mode):
|
||||
dm = match_include_to_path(incname, 'deny', path)
|
||||
# If the mode is denied
|
||||
@@ -2041,7 +1755,7 @@ def ask_the_questions():
|
||||
q['options'] = options
|
||||
q['selected'] = default_option - 1
|
||||
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_GLOB',
|
||||
'CMD_GLOBTEXT', 'CMD_NEW', 'CMD_ABORT',
|
||||
'CMD_GLOBEXT', 'CMD_NEW', 'CMD_ABORT',
|
||||
'CMD_FINISHED', 'CMD_OTHER', 'CMD_IGNORE_ENTRY']
|
||||
q['default'] = 'CMD_DENY'
|
||||
if aamode == 'PERMITTING':
|
||||
@@ -2183,16 +1897,16 @@ def ask_the_questions():
|
||||
if match:
|
||||
# /foo/**.ext and /foo/*.ext => /**.ext
|
||||
newpath = re.sub('/[^/]+/\*{1,2}\.[^/]+$', '/**'+match.group()[0], newpath)
|
||||
elif re.search('/[^/]+\*\*[^/]*\.[^/]+$'):
|
||||
elif re.search('/[^/]+\*\*[^/]*\.[^/]+$', newpath):
|
||||
# /foo**.ext and /foo**bar.ext => /**.ext
|
||||
match = re.search('/[^/]+\*\*[^/]*(\.[^/]+)$')
|
||||
match = re.search('/[^/]+\*\*[^/]*(\.[^/]+)$', newpath)
|
||||
newpath = re.sub('/[^/]+\*\*[^/]*\.[^/]+$', '/**'+match.groups()[0], newpath)
|
||||
elif re.search('/\*\*[^/]+\.[^/]+$'):
|
||||
elif re.search('/\*\*[^/]+\.[^/]+$', newpath):
|
||||
# /**foo.ext => /**.ext
|
||||
match = re.search('/\*\*[^/]+(\.[^/]+)$')
|
||||
match = re.search('/\*\*[^/]+(\.[^/]+)$', newpath)
|
||||
newpath = re.sub('/\*\*[^/]+\.[^/]+$', '/**'+match.groups()[0], newpath)
|
||||
else:
|
||||
match = re.search('(\.[^/]+)$')
|
||||
match = re.search('(\.[^/]+)$', newpath)
|
||||
newpath = re.sub('/[^/]+(\.[^/]+)$', '/*'+match.groups()[0], newpath)
|
||||
|
||||
if newpath not in options:
|
||||
@@ -2388,7 +2102,7 @@ def re_match_include(path):
|
||||
return None
|
||||
|
||||
def valid_include(profile, incname):
|
||||
if profile['include'].get(incname, False):
|
||||
if profile and profile['include'].get(incname, False):
|
||||
return False
|
||||
|
||||
if cfg['settings']['custom_includes']:
|
||||
@@ -2410,19 +2124,22 @@ def match_net_includes(profile, family, nettype):
|
||||
|
||||
return newincludes
|
||||
|
||||
def do_logprof_pass(logmark='', sev_db=sev_db):
|
||||
def do_logprof_pass(logmark='', pid=pid, existing_profiles=existing_profiles):
|
||||
# set up variables for this pass
|
||||
t = hasher()
|
||||
transitions = hasher()
|
||||
# transitions = hasher()
|
||||
seen = hasher()
|
||||
aa = hasher()
|
||||
profile_changes = hasher()
|
||||
prelog = hasher()
|
||||
global log
|
||||
log = []
|
||||
log_dict = hasher()
|
||||
changed = dict()
|
||||
global sev_db
|
||||
# aa = hasher()
|
||||
# profile_changes = hasher()
|
||||
# prelog = hasher()
|
||||
# log = []
|
||||
# log_dict = hasher()
|
||||
# changed = dict()
|
||||
skip = hasher()
|
||||
filelist = hasher()
|
||||
# filelist = hasher()
|
||||
|
||||
UI_Info(_('Reading log entries from %s.') %filename)
|
||||
UI_Info(_('Updating AppArmor profiles in %s.') %profile_dir)
|
||||
@@ -2431,17 +2148,21 @@ def do_logprof_pass(logmark='', sev_db=sev_db):
|
||||
|
||||
if not sev_db:
|
||||
sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown'))
|
||||
|
||||
#print(pid)
|
||||
#print(existing_profiles)
|
||||
##if not repo_cf and cfg['repostory']['url']:
|
||||
## repo_cfg = read_config('repository.conf')
|
||||
## if not repo_cfg['repository'].get('enabled', False) or repo_cfg['repository]['enabled'] not in ['yes', 'no']:
|
||||
## UI_ask_to_enable_repo()
|
||||
|
||||
read_log(logmark)
|
||||
log_reader = apparmor.logparser.ReadLog(pid, filename, existing_profiles, profile_dir, log)
|
||||
log = log_reader.read_log(logmark)
|
||||
#read_log(logmark)
|
||||
|
||||
for root in log:
|
||||
handle_children('', '', root)
|
||||
|
||||
#for root in range(len(log)):
|
||||
#log[root] = handle_children('', '', log[root])
|
||||
#print(log)
|
||||
for pid in sorted(profile_changes.keys()):
|
||||
set_process(pid, profile_changes[pid])
|
||||
|
||||
@@ -2624,27 +2345,27 @@ def collapse_log():
|
||||
combinedmode |= aa[profile][hat]['allow']['path'][path]
|
||||
|
||||
# Match path to regexps in profile
|
||||
combinedmode |= rematchfrag(aa[profile][hat], 'allow', path)
|
||||
combinedmode |= rematchfrag(aa[profile][hat], 'allow', path)[0]
|
||||
|
||||
# Match path from includes
|
||||
combinedmode |= match_prof_incs_to_path(aa[profile][hat], 'allow', path)
|
||||
combinedmode |= match_prof_incs_to_path(aa[profile][hat], 'allow', path)[0]
|
||||
|
||||
if not combinedmode or not mode_contains(combinedmode, mode):
|
||||
if log[aamode][profile][hat]['path'].get(path, False):
|
||||
mode |= log[aamode][profile][hat]['path'][path]
|
||||
if log_dict[aamode][profile][hat]['path'].get(path, False):
|
||||
mode |= log_dict[aamode][profile][hat]['path'][path]
|
||||
|
||||
log[aamode][profile][hat]['path'][path] = mode
|
||||
log_dict[aamode][profile][hat]['path'][path] = mode
|
||||
|
||||
for capability in prelog[aamode][profile][hat]['capability'].keys():
|
||||
# If capability not already in profile
|
||||
if not aa[profile][hat]['allow']['capability'][capability].get('set', False):
|
||||
log[aamode][profile][hat]['capability'][capability] = True
|
||||
log_dict[aamode][profile][hat]['capability'][capability] = True
|
||||
|
||||
nd = prelog[aamode][profile][hat]['netdomain']
|
||||
for family in nd.keys():
|
||||
for sock_type in nd[family].keys():
|
||||
if not profile_known_network(aa[profile][hat], family, sock_type):
|
||||
log[aamode][profile][hat]['netdomain'][family][sock_type] = True
|
||||
log_dict[aamode][profile][hat]['netdomain'][family][sock_type] = True
|
||||
|
||||
def profilemode(mode):
|
||||
pass
|
||||
@@ -2905,6 +2626,7 @@ def read_profiles():
|
||||
if is_skippable_file(file):
|
||||
continue
|
||||
else:
|
||||
#print('read %s' %file)
|
||||
read_profile(profile_dir + '/' + file, True)
|
||||
|
||||
def read_inactive_profiles():
|
||||
@@ -2932,6 +2654,7 @@ def read_profile(file, active_profile):
|
||||
return None
|
||||
|
||||
profile_data = parse_profile_data(data, file, 0)
|
||||
|
||||
if profile_data and active_profile:
|
||||
attach_profile_data(aa, profile_data)
|
||||
attach_profile_data(original_aa, profile_data)
|
||||
@@ -2953,7 +2676,7 @@ def parse_profile_data(data, file, do_include):
|
||||
repo_data = None
|
||||
parsed_profiles = []
|
||||
initial_comment = ''
|
||||
RE_PROFILE_START = re.compile('^(("??\/.+?"??)|(profile\s+("??.+?"??)))\s+((flags=)?\((.+)\)\s+)?\{\s*(#.*)?$')
|
||||
RE_PROFILE_START = re.compile('^(("??/.+?"??)|(profile\s+("??.+?"??)))\s+((flags=)?\((.+)\)\s+)?\{\s*(#.*)?$')
|
||||
RE_PROFILE_END = re.compile('^\}\s*(#.*)?$')
|
||||
RE_PROFILE_CAP = re.compile('^(audit\s+)?(allow\s+|deny\s+)?capability\s+(\S+)\s*,\s*(#.*)?$')
|
||||
RE_PROFILE_LINK = re.compile('^(audit\s+)?(allow\s+|deny\s+)?link\s+(((subset)|(<=))\s+)?([\"\@\/].*?"??)\s+->\s*([\"\@\/].*?"??)\s*,\s*(#.*)?$')
|
||||
@@ -3006,6 +2729,8 @@ def parse_profile_data(data, file, do_include):
|
||||
profile_data[profile][hat]['external'] = True
|
||||
else:
|
||||
hat = profile
|
||||
# Profile stored
|
||||
existing_profiles[profile] = file
|
||||
|
||||
flags = matches[6]
|
||||
|
||||
@@ -3362,11 +3087,13 @@ def store_list_var(var, list_var, value, var_operation):
|
||||
print('Ignored: New definition for variable for:',list_var,'=', value, 'operation was:',var_operation,'old value=', var[list_var])
|
||||
pass
|
||||
#raise AppArmorException('An existing variable redefined: %s' %list_var)
|
||||
else:
|
||||
elif var_operation == '+=':
|
||||
if var.get(list_var, False):
|
||||
var[list_var] = set(var[list_var] + vlist)
|
||||
else:
|
||||
raise AppArmorException('An existing variable redefined: %s' %list_var)
|
||||
raise AppArmorException('Values added to a non-existing variable: %s' %list_var)
|
||||
else:
|
||||
raise AppArmorException('Unknown variable operation: %s' %var_operation)
|
||||
|
||||
|
||||
def strip_quotes(data):
|
||||
@@ -3870,7 +3597,7 @@ def match_include_to_path(incname, allow, path):
|
||||
combinedmode = 0
|
||||
combinedaudit = 0
|
||||
matches = []
|
||||
|
||||
incname = profile_dir + '/' + incname
|
||||
includelist = [incname]
|
||||
while includelist:
|
||||
incfile = includelist.pop(0)
|
||||
|
258
apparmor/aamode.py
Normal file
258
apparmor/aamode.py
Normal file
@@ -0,0 +1,258 @@
|
||||
import re
|
||||
|
||||
AA_MAY_EXEC = set('x')
|
||||
AA_MAY_WRITE = set('w')
|
||||
AA_MAY_READ = set('r')
|
||||
AA_MAY_APPEND = set('a')
|
||||
AA_MAY_LINK = set('l')
|
||||
AA_MAY_LOCK = set('k')
|
||||
AA_EXEC_MMAP = set('m')
|
||||
AA_EXEC_UNSAFE = set('unsafe')
|
||||
AA_EXEC_INHERIT = set('i')
|
||||
AA_EXEC_UNCONFINED = set('U')
|
||||
AA_EXEC_PROFILE = set('P')
|
||||
AA_EXEC_CHILD = set('C')
|
||||
AA_EXEC_NT = set('N')
|
||||
AA_LINK_SUBSET = set('ls')
|
||||
AA_OTHER_SHIFT = 14
|
||||
AA_USER_MASK = 16384 - 1
|
||||
|
||||
AA_EXEC_TYPE = (AA_MAY_EXEC | AA_EXEC_UNSAFE | AA_EXEC_INHERIT |
|
||||
AA_EXEC_UNCONFINED | AA_EXEC_PROFILE | AA_EXEC_CHILD | AA_EXEC_NT)
|
||||
|
||||
MODE_HASH = {'x': AA_MAY_EXEC, 'X': AA_MAY_EXEC,
|
||||
'w': AA_MAY_WRITE, 'W': AA_MAY_WRITE,
|
||||
'r': AA_MAY_READ, 'R': AA_MAY_READ,
|
||||
'a': AA_MAY_APPEND, 'A': AA_MAY_APPEND,
|
||||
'l': AA_MAY_LINK, 'L': AA_MAY_LINK,
|
||||
'k': AA_MAY_LOCK, 'K': AA_MAY_LOCK,
|
||||
'm': AA_EXEC_MMAP, 'M': AA_EXEC_MMAP,
|
||||
'i': AA_EXEC_INHERIT, 'I': AA_EXEC_INHERIT,
|
||||
'u': AA_EXEC_UNCONFINED + AA_EXEC_UNSAFE, # Unconfined + Unsafe
|
||||
'U': AA_EXEC_UNCONFINED,
|
||||
'p': AA_EXEC_PROFILE + AA_EXEC_UNSAFE, # Profile + unsafe
|
||||
'P': AA_EXEC_PROFILE,
|
||||
'c': AA_EXEC_CHILD + AA_EXEC_UNSAFE, # Child + Unsafe
|
||||
'C': AA_EXEC_CHILD,
|
||||
'n': AA_EXEC_NT + AA_EXEC_UNSAFE,
|
||||
'N': AA_EXEC_NT
|
||||
}
|
||||
|
||||
LOG_MODE_RE = re.compile('r|w|l|m|k|a|x|ix|ux|px|cx|nx|pix|cix|Ix|Ux|Px|PUx|Cx|Nx|Pix|Cix')
|
||||
MODE_MAP_RE = re.compile('r|w|l|m|k|a|x|i|u|p|c|n|I|U|P|C|N')
|
||||
|
||||
def str_to_mode(string):
|
||||
if not string:
|
||||
return set()
|
||||
user, other = split_log_mode(string)
|
||||
|
||||
if not user:
|
||||
user = other
|
||||
|
||||
mode = sub_str_to_mode(user)
|
||||
#print(string, mode)
|
||||
#print(string, 'other', sub_str_to_mode(other))
|
||||
mode |= (sub_str_to_mode(other) << AA_OTHER_SHIFT)
|
||||
#print (string, mode)
|
||||
#print('str_to_mode:', mode)
|
||||
return mode
|
||||
|
||||
def sub_str_to_mode(string):
|
||||
mode = set()
|
||||
if not string:
|
||||
return mode
|
||||
while string:
|
||||
pattern = '(%s)' % MODE_MAP_RE.pattern
|
||||
tmp = re.search(pattern, string)
|
||||
if tmp:
|
||||
tmp = tmp.groups()[0]
|
||||
string = re.sub(pattern, '', string)
|
||||
if tmp and MODE_HASH.get(tmp, False):
|
||||
mode |= MODE_HASH[tmp]
|
||||
else:
|
||||
pass
|
||||
|
||||
return mode
|
||||
|
||||
def split_log_mode(mode):
|
||||
user = ''
|
||||
other = ''
|
||||
match = re.search('(.*?)::(.*)', mode)
|
||||
if match:
|
||||
user, other = match.groups()
|
||||
else:
|
||||
user = mode
|
||||
other = mode
|
||||
#print ('split_logmode:', user, mode)
|
||||
return user, other
|
||||
|
||||
def mode_contains(mode, subset):
|
||||
# w implies a
|
||||
if mode & AA_MAY_WRITE:
|
||||
mode |= AA_MAY_APPEND
|
||||
if mode & (AA_MAY_WRITE << AA_OTHER_SHIFT):
|
||||
mode |= (AA_MAY_APPEND << AA_OTHER_SHIFT)
|
||||
|
||||
return (mode & subset) == subset
|
||||
|
||||
def contains(mode, string):
|
||||
return mode_contains(mode, str_to_mode(string))
|
||||
|
||||
def validate_log_mode(mode):
|
||||
pattern = '^(%s)+$' % LOG_MODE_RE.pattern
|
||||
if re.search(pattern, mode):
|
||||
#if LOG_MODE_RE.search(mode):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def hide_log_mode(mode):
|
||||
mode = mode.replace('::', '')
|
||||
return mode
|
||||
|
||||
AA_MAY_EXEC = 1
|
||||
AA_MAY_WRITE = 2
|
||||
AA_MAY_READ = 4
|
||||
AA_MAY_APPEND = 8
|
||||
AA_MAY_LINK = 16
|
||||
AA_MAY_LOCK = 32
|
||||
AA_EXEC_MMAP = 64
|
||||
AA_EXEC_UNSAFE = 128
|
||||
AA_EXEC_INHERIT = 256
|
||||
AA_EXEC_UNCONFINED = 512
|
||||
AA_EXEC_PROFILE = 1024
|
||||
AA_EXEC_CHILD = 2048
|
||||
AA_EXEC_NT = 4096
|
||||
AA_LINK_SUBSET = 8192
|
||||
AA_OTHER_SHIFT = 14
|
||||
AA_USER_MASK = 16384 - 1
|
||||
|
||||
AA_EXEC_TYPE = (AA_MAY_EXEC | AA_EXEC_UNSAFE | AA_EXEC_INHERIT |
|
||||
AA_EXEC_UNCONFINED | AA_EXEC_PROFILE | AA_EXEC_CHILD | AA_EXEC_NT)
|
||||
|
||||
ALL_AA_EXEC_TYPE = AA_EXEC_TYPE # The same value
|
||||
|
||||
# Modes and their values
|
||||
MODE_HASH = {'x': AA_MAY_EXEC, 'X': AA_MAY_EXEC,
|
||||
'w': AA_MAY_WRITE, 'W': AA_MAY_WRITE,
|
||||
'r': AA_MAY_READ, 'R': AA_MAY_READ,
|
||||
'a': AA_MAY_APPEND, 'A': AA_MAY_APPEND,
|
||||
'l': AA_MAY_LINK, 'L': AA_MAY_LINK,
|
||||
'k': AA_MAY_LOCK, 'K': AA_MAY_LOCK,
|
||||
'm': AA_EXEC_MMAP, 'M': AA_EXEC_MMAP,
|
||||
'i': AA_EXEC_INHERIT, 'I': AA_EXEC_INHERIT,
|
||||
'u': AA_EXEC_UNCONFINED + AA_EXEC_UNSAFE, # Unconfined + Unsafe
|
||||
'U': AA_EXEC_UNCONFINED,
|
||||
'p': AA_EXEC_PROFILE + AA_EXEC_UNSAFE, # Profile + unsafe
|
||||
'P': AA_EXEC_PROFILE,
|
||||
'c': AA_EXEC_CHILD + AA_EXEC_UNSAFE, # Child + Unsafe
|
||||
'C': AA_EXEC_CHILD,
|
||||
'n': AA_EXEC_NT + AA_EXEC_UNSAFE,
|
||||
'N': AA_EXEC_NT
|
||||
}
|
||||
|
||||
def log_str_to_mode(profile, string, nt_name):
|
||||
mode = str_to_mode(string)
|
||||
# If contains nx and nix
|
||||
#print (profile, string, nt_name)
|
||||
if contains(mode, 'Nx'):
|
||||
# Transform to px, cx
|
||||
match = re.search('(.+?)//(.+?)', nt_name)
|
||||
if match:
|
||||
lprofile, lhat = match.groups()
|
||||
tmode = 0
|
||||
|
||||
if lprofile == profile:
|
||||
if mode & AA_MAY_EXEC:
|
||||
tmode = str_to_mode('Cx::')
|
||||
if mode & (AA_MAY_EXEC << AA_OTHER_SHIFT):
|
||||
tmode |= str_to_mode('Cx')
|
||||
nt_name = lhat
|
||||
else:
|
||||
if mode & AA_MAY_EXEC:
|
||||
tmode = str_to_mode('Px::')
|
||||
if mode & (AA_MAY_EXEC << AA_OTHER_SHIFT):
|
||||
tmode |= str_to_mode('Px')
|
||||
nt_name = lhat
|
||||
|
||||
mode = mode & ~str_to_mode('Nx')
|
||||
mode |= tmode
|
||||
|
||||
return mode, nt_name
|
||||
|
||||
def hide_log_mode(mode):
|
||||
mode = mode.replace('::', '')
|
||||
return mode
|
||||
|
||||
def validate_log_mode(mode):
|
||||
pattern = '^(%s)+$' % LOG_MODE_RE.pattern
|
||||
if re.search(pattern, mode):
|
||||
#if LOG_MODE_RE.search(mode):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def str_to_mode(string):
|
||||
if not string:
|
||||
return 0
|
||||
user, other = split_log_mode(string)
|
||||
|
||||
if not user:
|
||||
user = other
|
||||
|
||||
mode = sub_str_to_mode(user)
|
||||
#print(string, mode)
|
||||
#print(string, 'other', sub_str_to_mode(other))
|
||||
mode |= (sub_str_to_mode(other) << AA_OTHER_SHIFT)
|
||||
#print (string, mode)
|
||||
#print('str_to_mode:', mode)
|
||||
return mode
|
||||
|
||||
def mode_contains(mode, subset):
|
||||
# w implies a
|
||||
if mode & AA_MAY_WRITE:
|
||||
mode |= AA_MAY_APPEND
|
||||
if mode & (AA_MAY_WRITE << AA_OTHER_SHIFT):
|
||||
mode |= (AA_MAY_APPEND << AA_OTHER_SHIFT)
|
||||
|
||||
# ix does not imply m
|
||||
|
||||
### ix implies m
|
||||
##if mode & AA_EXEC_INHERIT:
|
||||
## mode |= AA_EXEC_MMAP
|
||||
##if mode & (AA_EXEC_INHERIT << AA_OTHER_SHIFT):
|
||||
## mode |= (AA_EXEC_MMAP << AA_OTHER_SHIFT)
|
||||
|
||||
return (mode & subset) == subset
|
||||
|
||||
def contains(mode, string):
|
||||
return mode_contains(mode, str_to_mode(string))
|
||||
|
||||
def sub_str_to_mode(string):
|
||||
mode = 0
|
||||
if not string:
|
||||
return mode
|
||||
while string:
|
||||
pattern = '(%s)' % MODE_MAP_RE.pattern
|
||||
tmp = re.search(pattern, string)
|
||||
if tmp:
|
||||
tmp = tmp.groups()[0]
|
||||
string = re.sub(pattern, '', string)
|
||||
if tmp and MODE_HASH.get(tmp, False):
|
||||
mode |= MODE_HASH[tmp]
|
||||
else:
|
||||
pass
|
||||
|
||||
return mode
|
||||
|
||||
def split_log_mode(mode):
|
||||
user = ''
|
||||
other = ''
|
||||
match = re.search('(.*?)::(.*)', mode)
|
||||
if match:
|
||||
user, other = match.groups()
|
||||
else:
|
||||
user = mode
|
||||
other = mode
|
||||
#print ('split_logmode:', user, mode)
|
||||
return user, other
|
@@ -157,6 +157,7 @@ def hasher():
|
||||
|
||||
|
||||
def convert_regexp(regexp):
|
||||
regex_paren = re.compile('^(.*){([^}]*)}(.*)$')
|
||||
regexp = regexp.strip()
|
||||
new_reg = re.sub(r'(?<!\\)(\.|\+|\$)',r'\\\1',regexp)
|
||||
# below will fail if { or } or , are part of a path too?
|
||||
@@ -165,8 +166,8 @@ def convert_regexp(regexp):
|
||||
# new_reg = new_reg.replace('}', '}')
|
||||
# new_reg = new_reg.replace(',', '|')
|
||||
|
||||
while re.search('{[^}]*,[^}]*}', new_reg):
|
||||
match = re.search('(.*){([^}]*)}(.*)', new_reg).groups()
|
||||
while regex_paren.search(new_reg):
|
||||
match = regex_paren.search(new_reg).groups()
|
||||
prev = match[0]
|
||||
after = match[2]
|
||||
p1 = match[1].replace(',','|')
|
||||
@@ -194,10 +195,18 @@ class DebugLogger:
|
||||
self.debug_level = logging.DEBUG
|
||||
if os.getenv('LOGPROF_DEBUG', False):
|
||||
self.debugging = os.getenv('LOGPROF_DEBUG')
|
||||
try:
|
||||
self.debugging = int(self.debugging)
|
||||
except:
|
||||
self.debugging = False
|
||||
if self.debugging not in range(1,4):
|
||||
sys.stderr.out('Environment Variable: LOGPROF_DEBUG contains invalid value: %s' %os.getenv('LOGPROF_DEBUG'))
|
||||
if self.debugging == 1:
|
||||
debug_level = logging.ERROR
|
||||
elif self.debug_level == 2:
|
||||
debug_level = logging.INFO
|
||||
elif debug_level == 3:
|
||||
debug_level = logging.DEBUG
|
||||
|
||||
#logging.basicConfig(filename='/var/log/apparmor/logprof.log', level=self.debug_level, format='%(asctime)s - %(name)s - %(message)s\n')
|
||||
logging.basicConfig(filename='/home/kshitij/logprof.log', level=self.debug_level, format='%(asctime)s - %(name)s - %(message)s\n')
|
||||
|
379
apparmor/logparser.py
Normal file
379
apparmor/logparser.py
Normal file
@@ -0,0 +1,379 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import LibAppArmor
|
||||
from apparmor.common import (AppArmorException, error, debug, msg,
|
||||
open_file_read, valid_path,
|
||||
hasher, open_file_write, convert_regexp, DebugLogger)
|
||||
|
||||
from apparmor.aamode import *
|
||||
|
||||
class ReadLog:
|
||||
RE_LOG_v2_6_syslog = re.compile('kernel:\s+(\[[\d\.\s]+\]\s+)?type=\d+\s+audit\([\d\.\:]+\):\s+apparmor=')
|
||||
RE_LOG_v2_6_audit = re.compile('type=AVC\s+(msg=)?audit\([\d\.\:]+\):\s+apparmor=')
|
||||
MODE_MAP_RE = re.compile('r|w|l|m|k|a|x|i|u|p|c|n|I|U|P|C|N')
|
||||
LOG_MODE_RE = re.compile('r|w|l|m|k|a|x|ix|ux|px|cx|nx|pix|cix|Ix|Ux|Px|PUx|Cx|Nx|Pix|Cix')
|
||||
PROFILE_MODE_RE = re.compile('r|w|l|m|k|a|ix|ux|px|cx|pix|cix|Ux|Px|PUx|Cx|Pix|Cix')
|
||||
PROFILE_MODE_NT_RE = re.compile('r|w|l|m|k|a|x|ix|ux|px|cx|pix|cix|Ux|Px|PUx|Cx|Pix|Cix')
|
||||
PROFILE_MODE_DENY_RE = re.compile('r|w|l|m|k|a|x')
|
||||
# Used by netdomain to identify the operation types
|
||||
OPERATION_TYPES = {
|
||||
# New socket names
|
||||
'create': 'net',
|
||||
'post_create': 'net',
|
||||
'bind': 'net',
|
||||
'connect': 'net',
|
||||
'listen': 'net',
|
||||
'accept': 'net',
|
||||
'sendmsg': 'net',
|
||||
'recvmsg': 'net',
|
||||
'getsockname': 'net',
|
||||
'getpeername': 'net',
|
||||
'getsockopt': 'net',
|
||||
'setsockopt': 'net',
|
||||
'sock_shutdown': 'net'
|
||||
}
|
||||
def __init__(self, pid, filename, existing_profiles, profile_dir, log):
|
||||
self.filename = filename
|
||||
self.profile_dir = profile_dir
|
||||
self.pid = pid
|
||||
self.existing_profiles = existing_profiles
|
||||
self.log = log
|
||||
self.debug_logger = DebugLogger('ReadLog')
|
||||
self.LOG = None
|
||||
self.logmark = ''
|
||||
self.seenmark = None
|
||||
self.next_log_entry = None
|
||||
|
||||
def prefetch_next_log_entry(self):
|
||||
if self.next_log_entry:
|
||||
sys.stderr.out('A log entry already present: %s' % self.next_log_entry)
|
||||
self.next_log_entry = self.LOG.readline()
|
||||
while not (self.RE_LOG_v2_6_syslog.search(self.next_log_entry) or self.RE_LOG_v2_6_audit.search(self.next_log_entry)): #or re.search(self.logmark, self.next_log_entry)):
|
||||
self.next_log_entry = self.LOG.readline()
|
||||
if not self.next_log_entry:
|
||||
break
|
||||
|
||||
def get_next_log_entry(self):
|
||||
# If no next log entry fetch it
|
||||
if not self.next_log_entry:
|
||||
self.prefetch_next_log_entry()
|
||||
log_entry = self.next_log_entry
|
||||
self.next_log_entry = None
|
||||
return log_entry
|
||||
|
||||
def peek_at_next_log_entry(self):
|
||||
# Take a peek at the next log entry
|
||||
if not self.next_log_entry:
|
||||
self.prefetch_next_log_entry()
|
||||
return self.next_log_entry
|
||||
|
||||
def throw_away_next_log_entry(self):
|
||||
self.next_log_entry = None
|
||||
|
||||
def parse_log_record(self, record):
|
||||
self.debug_logger.debug('parse_log_record: %s' % record)
|
||||
|
||||
record_event = self.parse_event(record)
|
||||
return record_event
|
||||
|
||||
def parse_event(self, msg):
|
||||
"""Parse the event from log into key value pairs"""
|
||||
msg = msg.strip()
|
||||
self.debug_logger.info('parse_event: %s' % msg)
|
||||
#print(repr(msg))
|
||||
if sys.version_info < (3,0):
|
||||
# parse_record fails with u'foo' style strings hence typecasting to string
|
||||
msg = str(msg)
|
||||
event = LibAppArmor.parse_record(msg)
|
||||
ev = dict()
|
||||
ev['resource'] = event.info
|
||||
ev['active_hat'] = event.active_hat
|
||||
ev['aamode'] = event.event
|
||||
ev['time'] = event.epoch
|
||||
ev['operation'] = event.operation
|
||||
ev['profile'] = event.profile
|
||||
ev['name'] = event.name
|
||||
ev['name2'] = event.name2
|
||||
ev['attr'] = event.attribute
|
||||
ev['parent'] = event.parent
|
||||
ev['pid'] = event.pid
|
||||
ev['task'] = event.task
|
||||
ev['info'] = event.info
|
||||
dmask = event.denied_mask
|
||||
rmask = event.requested_mask
|
||||
ev['magic_token'] = event.magic_token
|
||||
if ev['operation'] and self.op_type(ev['operation']) == 'net':
|
||||
ev['family'] = event.net_family
|
||||
ev['protocol'] = event.net_protocol
|
||||
ev['sock_type'] = event.net_sock_type
|
||||
LibAppArmor.free_record(event)
|
||||
# Map c (create) to a and d (delete) to w, logprof doesn't support c and d
|
||||
if rmask:
|
||||
rmask = rmask.replace('c', 'a')
|
||||
rmask = rmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(rmask)):
|
||||
pass#fatal_error(_('Log contains unknown mode %s') % rmask)
|
||||
if dmask:
|
||||
dmask = dmask.replace('c', 'a')
|
||||
dmask = dmask.replace('d', 'w')
|
||||
if not validate_log_mode(hide_log_mode(dmask)):
|
||||
pass #fatal_error(_('Log contains unknown mode %s') % dmask)
|
||||
#print('parse_event:', ev['profile'], dmask, ev['name2'])
|
||||
mask, name = log_str_to_mode(ev['profile'], dmask, ev['name2'])
|
||||
|
||||
ev['denied_mask'] = mask
|
||||
ev['name2'] = name
|
||||
|
||||
mask, name = log_str_to_mode(ev['profile'], rmask, ev['name2'])
|
||||
ev['request_mask'] = mask
|
||||
ev['name2'] = name
|
||||
|
||||
if not ev['time']:
|
||||
ev['time'] = int(time.time())
|
||||
# Remove None keys
|
||||
#for key in ev.keys():
|
||||
# if not ev[key] or not re.search('[\w]+', ev[key]):
|
||||
# ev.pop(key)
|
||||
|
||||
if ev['aamode']:
|
||||
# Convert aamode values to their counter-parts
|
||||
mode_convertor = {
|
||||
0: 'UNKNOWN',
|
||||
1: 'ERROR',
|
||||
2: 'AUDITING',
|
||||
3: 'PERMITTING',
|
||||
4: 'REJECTING',
|
||||
5: 'HINT',
|
||||
6: 'STATUS'
|
||||
}
|
||||
try:
|
||||
ev['aamode'] = mode_convertor[ev['aamode']]
|
||||
except KeyError:
|
||||
ev['aamode'] = None
|
||||
|
||||
if ev['aamode']:
|
||||
#debug_logger.debug(ev)
|
||||
return ev
|
||||
else:
|
||||
return None
|
||||
|
||||
def add_to_tree(self, loc_pid, parent, type, event):
|
||||
self.debug_logger.info('add_to_tree: pid [%s] type [%s] event [%s]' % (loc_pid, type, event))
|
||||
if not self.pid.get(loc_pid, False):
|
||||
profile, hat = event[:2]
|
||||
if parent and self.pid.get(parent, False):
|
||||
if not hat:
|
||||
hat = 'null-complain-profile'
|
||||
arrayref = []
|
||||
self.pid[parent].append(arrayref)
|
||||
self.pid[loc_pid] = arrayref
|
||||
for ia in ['fork', loc_pid, profile, hat]:
|
||||
arrayref.append(ia)
|
||||
# self.pid[parent].append(array_ref)
|
||||
# self.pid[loc_pid] = array_ref
|
||||
else:
|
||||
arrayref = []
|
||||
self.log.append(arrayref)
|
||||
self.pid[loc_pid] = arrayref
|
||||
# self.log.append(array_ref)
|
||||
# self.pid[loc_pid] = array_ref
|
||||
self.pid[loc_pid].append([type, loc_pid] + event)
|
||||
#print("\n\npid",self.pid)
|
||||
#print("log",self.log)
|
||||
|
||||
def add_event_to_tree(self, e):
|
||||
aamode = e.get('aamode', 'UNKNOWN')
|
||||
if e.get('type', False):
|
||||
if re.search('(UNKNOWN\[1501\]|APPARMOR_AUDIT|1501)', e['type']):
|
||||
aamode = 'AUDIT'
|
||||
elif re.search('(UNKNOWN\[1502\]|APPARMOR_ALLOWED|1502)', e['type']):
|
||||
aamode = 'PERMITTING'
|
||||
elif re.search('(UNKNOWN\[1503\]|APPARMOR_DENIED|1503)', e['type']):
|
||||
aamode = 'REJECTING'
|
||||
elif re.search('(UNKNOWN\[1504\]|APPARMOR_HINT|1504)', e['type']):
|
||||
aamode = 'HINT'
|
||||
elif re.search('(UNKNOWN\[1505\]|APPARMOR_STATUS|1505)', e['type']):
|
||||
aamode = 'STATUS'
|
||||
elif re.search('(UNKNOWN\[1506\]|APPARMOR_ERROR|1506)', e['type']):
|
||||
aamode = 'ERROR'
|
||||
else:
|
||||
aamode = 'UNKNOWN'
|
||||
|
||||
if aamode in ['UNKNOWN', 'AUDIT', 'STATUS', 'ERROR']:
|
||||
return None
|
||||
|
||||
if 'profile_set' in e['operation']:
|
||||
return None
|
||||
|
||||
# Skip if AUDIT event was issued due to a change_hat in unconfined mode
|
||||
if not e.get('profile', False):
|
||||
return None
|
||||
|
||||
# Convert new null profiles to old single level null profile
|
||||
if '//null-' in e['profile']:
|
||||
e['profile'] = 'null-complain-profile'
|
||||
|
||||
profile = e['profile']
|
||||
hat = None
|
||||
|
||||
if '//' in e['profile']:
|
||||
profile, hat = e['profile'].split('//')[:2]
|
||||
|
||||
# Filter out change_hat events that aren't from learning
|
||||
if e['operation'] == 'change_hat':
|
||||
if aamode != 'HINT' or aamode != 'PERMITTING':
|
||||
return None
|
||||
profile = e['name']
|
||||
#hat = None
|
||||
if '//' in e['name']:
|
||||
profile, hat = e['name'].split('//')
|
||||
|
||||
if not hat:
|
||||
hat = profile
|
||||
|
||||
# prog is no longer passed around consistently
|
||||
prog = 'HINT'
|
||||
|
||||
if profile != 'null-complain-profile' and not self.profile_exists(profile):
|
||||
return None
|
||||
if e['operation'] == 'exec':
|
||||
if e.get('info', False) and e['info'] == 'mandatory profile missing':
|
||||
self.add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, aamode, 'PERMITTING', e['denied_mask'], e['name'], e['name2']])
|
||||
elif e.get('name2', False) and '\\null-/' in e['name2']:
|
||||
self.add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e.get('name', False):
|
||||
self.add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
else:
|
||||
self.debug_logger.debug('add_event_to_tree: dropped exec event in %s' % e['profile'])
|
||||
|
||||
elif 'file_' in e['operation']:
|
||||
self.add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e['operation'] in ['open', 'truncate', 'mkdir', 'mknod', 'rename_src',
|
||||
'rename_dest', 'unlink', 'rmdir', 'symlink_create', 'link']:
|
||||
#print(e['operation'], e['name'])
|
||||
self.add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif e['operation'] == 'capable':
|
||||
self.add_to_tree(e['pid'], e['parent'], 'capability',
|
||||
[profile, hat, prog, aamode, e['name'], ''])
|
||||
elif e['operation'] == 'setattr' or 'xattr' in e['operation']:
|
||||
self.add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
elif 'inode_' in e['operation']:
|
||||
is_domain_change = False
|
||||
if e['operation'] == 'inode_permission' and (e['denied_mask'] & AA_MAY_EXEC) and aamode == 'PERMITTING':
|
||||
following = self.peek_at_next_log_entry()
|
||||
if following:
|
||||
entry = self.parse_log_record(following)
|
||||
if entry and entry.get('info', False) == 'set profile':
|
||||
is_domain_change = True
|
||||
self.throw_away_next_log_entry()
|
||||
|
||||
if is_domain_change:
|
||||
self.add_to_tree(e['pid'], e['parent'], 'exec',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], e['name2']])
|
||||
else:
|
||||
self.add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
|
||||
elif e['operation'] == 'sysctl':
|
||||
self.add_to_tree(e['pid'], e['parent'], 'path',
|
||||
[profile, hat, prog, aamode, e['denied_mask'], e['name'], ''])
|
||||
|
||||
elif e['operation'] == 'clone':
|
||||
parent , child = e['pid'], e['task']
|
||||
if not parent:
|
||||
parent = 'null-complain-profile'
|
||||
if not hat:
|
||||
hat = 'null-complain-profile'
|
||||
arrayref = []
|
||||
if self.pid.get(parent, False):
|
||||
self.pid[parent].append(arrayref)
|
||||
else:
|
||||
self.log.append(arrayref)
|
||||
self.pid[child].append(arrayref)
|
||||
for ia in ['fork', child, profile, hat]:
|
||||
arrayref.append(ia)
|
||||
# if self.pid.get(parent, False):
|
||||
# self.pid[parent] += [arrayref]
|
||||
# else:
|
||||
# self.log += [arrayref]
|
||||
# self.pid[child] = arrayref
|
||||
|
||||
elif self.op_type(e['operation']) == 'net':
|
||||
self.add_to_tree(e['pid'], e['parent'], 'netdomain',
|
||||
[profile, hat, prog, aamode, e['family'], e['sock_type'], e['protocol']])
|
||||
elif e['operation'] == 'change_hat':
|
||||
self.add_to_tree(e['pid'], e['parent'], 'unknown_hat',
|
||||
[profile, hat, aamode, hat])
|
||||
else:
|
||||
self.debug_logger.debug('UNHANDLED: %s' % e)
|
||||
|
||||
def read_log(self, logmark):
|
||||
seenmark = True
|
||||
if logmark:
|
||||
seenmark = False
|
||||
#last = None
|
||||
#event_type = None
|
||||
try:
|
||||
#print(self.filename)
|
||||
self.LOG = open_file_read(self.filename)
|
||||
except IOError:
|
||||
raise AppArmorException('Can not read AppArmor logfile: ' + self.filename)
|
||||
#LOG = open_file_read(log_open)
|
||||
line = self.get_next_log_entry()
|
||||
while line:
|
||||
line = line.strip()
|
||||
self.debug_logger.debug('read_log: %s' % line)
|
||||
if logmark in line:
|
||||
seenmark = True
|
||||
|
||||
self.debug_logger.debug('read_log: seenmark = %s' %seenmark)
|
||||
if not seenmark:
|
||||
line = self.get_next_log_entry()
|
||||
continue
|
||||
event = self.parse_log_record(line)
|
||||
#print(event)
|
||||
if event:
|
||||
self.add_event_to_tree(event)
|
||||
line = self.get_next_log_entry()
|
||||
self.LOG.close()
|
||||
logmark = ''
|
||||
return self.log
|
||||
|
||||
def op_type(self, operation):
|
||||
"""Returns the operation type if known, unkown otherwise"""
|
||||
operation_type = self.OPERATION_TYPES.get(operation, 'unknown')
|
||||
return operation_type
|
||||
|
||||
def profile_exists(self, program):
|
||||
"""Returns True if profile exists, False otherwise"""
|
||||
# Check cache of profiles
|
||||
if self.existing_profiles.get(program, False):
|
||||
return True
|
||||
# Check the disk for profile
|
||||
prof_path = self.get_profile_filename(program)
|
||||
#print(prof_path)
|
||||
if os.path.isfile(prof_path):
|
||||
# Add to cache of profile
|
||||
self.existing_profiles[program] = True
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_profile_filename(self, profile):
|
||||
"""Returns the full profile name"""
|
||||
if profile.startswith('/'):
|
||||
# Remove leading /
|
||||
profile = profile[1:]
|
||||
else:
|
||||
profile = "profile_" + profile
|
||||
profile = profile.replace('/', '.')
|
||||
full_profilename = self.profile_dir + '/' + profile
|
||||
return full_profilename
|
@@ -167,7 +167,7 @@ class Severity:
|
||||
"""Loads the variables for the given profile"""
|
||||
regex_include = re.compile('^#?include\s*<(\S*)>')
|
||||
if os.path.isfile(prof_path):
|
||||
with open_file_read(prof_path) as f_in:
|
||||
with open(prof_path, 'r') as f_in:
|
||||
for line in f_in:
|
||||
line = line.strip()
|
||||
# If any includes, load variables from them first
|
||||
|
@@ -235,6 +235,15 @@ def UI_PromptUser(q):
|
||||
cmd == 'XXXINVALIDXXX'
|
||||
return (cmd, arg)
|
||||
|
||||
def confirm_and_abort():
|
||||
ans = UI_YesNo(_('Are you sure you want to abandon this set of profile changes and exit?'), 'n')
|
||||
if ans == 'y':
|
||||
UI_Info(_('Abandoning all changes.'))
|
||||
#shutdown_yast()
|
||||
#for prof in created:
|
||||
# delete_profile(prof)
|
||||
sys.exit(0)
|
||||
|
||||
def UI_ShortMessage(title, message):
|
||||
SendDataToYast({
|
||||
'type': 'short-dialog-message',
|
||||
@@ -303,7 +312,7 @@ def Text_PromptUser(question):
|
||||
|
||||
default_key = defaulthotkey.groups()[0].lower()
|
||||
|
||||
if keys.get(default_key, False):
|
||||
if not keys.get(default_key, False):
|
||||
raise AppArmorException('PromptUser: %s %s' %(_('Invalid default'), default))
|
||||
|
||||
widest = 0
|
||||
@@ -315,7 +324,7 @@ def Text_PromptUser(question):
|
||||
widest = len(header)
|
||||
widest += 1
|
||||
|
||||
formatstr = '%-' + widest + 's %s\n'
|
||||
formatstr = '%-' + str(widest) + 's %s\n'
|
||||
|
||||
function_regexp = '^('
|
||||
function_regexp += '|'.join(keys.keys())
|
||||
@@ -348,7 +357,7 @@ def Text_PromptUser(question):
|
||||
else:
|
||||
format_option = ' %s - %s '
|
||||
prompt += format_option %(index+1, option)
|
||||
prompt += '\n'
|
||||
prompt += '\n'
|
||||
|
||||
prompt += ' / '.join(menu_items)
|
||||
|
||||
@@ -371,7 +380,7 @@ def Text_PromptUser(question):
|
||||
# sys.stdout.write('\n%s\n' %helptext)
|
||||
# ans = 'XXXINVALIDXXX'
|
||||
|
||||
elif int(ans) == 10:
|
||||
elif is_number(ans) == 10:
|
||||
# If they hit return choose default option
|
||||
ans = default_key
|
||||
|
||||
@@ -389,3 +398,10 @@ def Text_PromptUser(question):
|
||||
ans = keys[ans]
|
||||
|
||||
return ans, selected
|
||||
|
||||
def is_number(number):
|
||||
try:
|
||||
return int(number)
|
||||
except:
|
||||
return False
|
||||
|
Reference in New Issue
Block a user