mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-02 07:15:18 +00:00
A commit before changing modes style
This commit is contained in:
@@ -3,6 +3,7 @@ import sys
|
|||||||
|
|
||||||
sys.path.append('../')
|
sys.path.append('../')
|
||||||
import apparmor.aa
|
import apparmor.aa
|
||||||
|
import apparmor.logparser
|
||||||
|
|
||||||
#from apparmor.aa import parse_event
|
#from apparmor.aa import parse_event
|
||||||
|
|
||||||
@@ -13,8 +14,9 @@ class Test(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def test_parse_event(self):
|
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'
|
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['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['profile'], '/usr/sbin/httpd2-prefork//vhost_foo', 'Incorrectly parsed/decode profile name')
|
||||||
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
||||||
@@ -26,7 +28,7 @@ class Test(unittest.TestCase):
|
|||||||
#print(parsed_event)
|
#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'
|
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['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['profile'], 'foo bar', 'Incorrectly parsed/decode profile name')
|
||||||
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
self.assertEqual(parsed_event['aamode'], 'PERMITTING')
|
||||||
|
@@ -16,7 +16,7 @@ class Test(unittest.TestCase):
|
|||||||
for regex in regex_tests.sections():
|
for regex in regex_tests.sections():
|
||||||
parsed_regex = re.compile(apparmor.common.convert_regexp(regex))
|
parsed_regex = re.compile(apparmor.common.convert_regexp(regex))
|
||||||
for regex_testcase in regex_tests.options(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):
|
#def test_readkey(self):
|
||||||
# print("Please press the Y button on the keyboard.")
|
# print("Please press the Y button on the keyboard.")
|
||||||
|
@@ -6,13 +6,6 @@ import apparmor.aa
|
|||||||
import os
|
import os
|
||||||
import argparse
|
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 = ''
|
logmark = ''
|
||||||
|
|
||||||
apparmor.aa.loadincludes()
|
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 tempfile
|
||||||
|
|
||||||
import apparmor.config
|
import apparmor.config
|
||||||
|
import apparmor.logparser
|
||||||
import apparmor.severity
|
import apparmor.severity
|
||||||
import LibAppArmor
|
import LibAppArmor
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ user_globs = []
|
|||||||
## Variables used under logprof
|
## Variables used under logprof
|
||||||
### Were our
|
### Were our
|
||||||
t = hasher()#dict()
|
t = hasher()#dict()
|
||||||
transitions = dict()
|
transitions = hasher()
|
||||||
aa = hasher() # Profiles originally in sd, replace by aa
|
aa = hasher() # Profiles originally in sd, replace by aa
|
||||||
original_aa = hasher()
|
original_aa = hasher()
|
||||||
extras = hasher() # Inactive profiles from extras
|
extras = hasher() # Inactive profiles from extras
|
||||||
@@ -70,7 +71,7 @@ log = []
|
|||||||
pid = dict()
|
pid = dict()
|
||||||
|
|
||||||
seen = hasher()#dir()
|
seen = hasher()#dir()
|
||||||
profile_changes = dict()
|
profile_changes = hasher()
|
||||||
prelog = hasher()
|
prelog = hasher()
|
||||||
log_dict = hasher()#dict()
|
log_dict = hasher()#dict()
|
||||||
changed = dict()
|
changed = dict()
|
||||||
@@ -215,7 +216,7 @@ def check_for_apparmor():
|
|||||||
|
|
||||||
def which(file):
|
def which(file):
|
||||||
"""Returns the executable fullpath for the file, None otherwise"""
|
"""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:
|
for env_dir in env_dirs:
|
||||||
env_path = env_dir + '/' + file
|
env_path = env_dir + '/' + file
|
||||||
# Test if the path is executable or not
|
# Test if the path is executable or not
|
||||||
@@ -244,7 +245,7 @@ def get_full_path(original_path):
|
|||||||
return os.path.realpath(path)
|
return os.path.realpath(path)
|
||||||
|
|
||||||
def find_executable(bin_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
|
full_bin = None
|
||||||
if os.path.exists(bin_path):
|
if os.path.exists(bin_path):
|
||||||
full_bin = get_full_path(bin_path)
|
full_bin = get_full_path(bin_path)
|
||||||
@@ -259,7 +260,9 @@ def find_executable(bin_path):
|
|||||||
|
|
||||||
def get_profile_filename(profile):
|
def get_profile_filename(profile):
|
||||||
"""Returns the full profile name"""
|
"""Returns the full profile name"""
|
||||||
if profile.startswith('/'):
|
if existing_profiles.get(profile, False):
|
||||||
|
return existing_profiles[profile]
|
||||||
|
elif profile.startswith('/'):
|
||||||
# Remove leading /
|
# Remove leading /
|
||||||
profile = profile[1:]
|
profile = profile[1:]
|
||||||
else:
|
else:
|
||||||
@@ -370,14 +373,8 @@ def handle_binfmt(profile, path):
|
|||||||
library = glob_common(library)
|
library = glob_common(library)
|
||||||
if not library:
|
if not library:
|
||||||
continue
|
continue
|
||||||
try:
|
profile['allow']['path'][library]['mode'] = profile['allow']['path'][library].get('mode', set()) | str_to_mode('mr')
|
||||||
profile['allow']['path'][library]['mode'] |= str_to_mode('mr')
|
profile['allow']['path'][library]['audit'] |= profile['allow']['path'][library].get('audit', set())
|
||||||
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
|
|
||||||
|
|
||||||
def get_inactive_profile(local_profile):
|
def get_inactive_profile(local_profile):
|
||||||
if extras.get(local_profile, False):
|
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]['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]['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':
|
if interpreter == 'perl':
|
||||||
local_profile[localfile]['include']['abstractions/perl'] = True
|
local_profile[localfile]['include']['abstractions/perl'] = True
|
||||||
@@ -619,6 +616,7 @@ def set_profile_flags(prof_filename, newflags):
|
|||||||
def profile_exists(program):
|
def profile_exists(program):
|
||||||
"""Returns True if profile exists, False otherwise"""
|
"""Returns True if profile exists, False otherwise"""
|
||||||
# Check cache of profiles
|
# Check cache of profiles
|
||||||
|
|
||||||
if existing_profiles.get(program, False):
|
if existing_profiles.get(program, False):
|
||||||
return True
|
return True
|
||||||
# Check the disk for profile
|
# Check the disk for profile
|
||||||
@@ -626,7 +624,7 @@ def profile_exists(program):
|
|||||||
#print(prof_path)
|
#print(prof_path)
|
||||||
if os.path.isfile(prof_path):
|
if os.path.isfile(prof_path):
|
||||||
# Add to cache of profile
|
# Add to cache of profile
|
||||||
existing_profiles[program] = True
|
existing_profiles[program] = prof_path
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -897,7 +895,7 @@ def handle_children(profile, hat, root):
|
|||||||
profile_changes[pid] = profile + '//' + hat
|
profile_changes[pid] = profile + '//' + hat
|
||||||
else:
|
else:
|
||||||
profile_changes[pid] = profile
|
profile_changes[pid] = profile
|
||||||
elif type == 'unknown_hat':
|
elif typ == 'unknown_hat':
|
||||||
pid, p, h, aamode, uhat = entry[:5]
|
pid, p, h, aamode, uhat = entry[:5]
|
||||||
if not regex_nullcomplain.search(p):
|
if not regex_nullcomplain.search(p):
|
||||||
profile = p
|
profile = p
|
||||||
@@ -952,7 +950,7 @@ def handle_children(profile, hat, root):
|
|||||||
elif ans == 'CMD_DENY':
|
elif ans == 'CMD_DENY':
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif type == 'capability':
|
elif typ == 'capability':
|
||||||
pid, p, h, prog, aamode, capability = entry[:6]
|
pid, p, h, prog, aamode, capability = entry[:6]
|
||||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||||
profile = p
|
profile = p
|
||||||
@@ -961,9 +959,8 @@ def handle_children(profile, hat, root):
|
|||||||
continue
|
continue
|
||||||
prelog[aamode][profile][hat]['capability'][capability] = True
|
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]
|
pid, p, h, prog, aamode, mode, detail, to_name = entry[:8]
|
||||||
|
|
||||||
if not mode:
|
if not mode:
|
||||||
mode = 0
|
mode = 0
|
||||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||||
@@ -973,7 +970,7 @@ def handle_children(profile, hat, root):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
domainchange = 'nochange'
|
domainchange = 'nochange'
|
||||||
if type == 'exec':
|
if typ == 'exec':
|
||||||
domainchange = 'change'
|
domainchange = 'change'
|
||||||
|
|
||||||
# Escape special characters
|
# Escape special characters
|
||||||
@@ -1346,7 +1343,7 @@ def handle_children(profile, hat, root):
|
|||||||
if not aa[profile].get(exec_target, False):
|
if not aa[profile].get(exec_target, False):
|
||||||
ynans = 'y'
|
ynans = 'y'
|
||||||
if exec_mode & str_to_mode('i'):
|
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':
|
if ynans == 'y':
|
||||||
hat = exec_target
|
hat = exec_target
|
||||||
aa[profile][hat]['declared'] = False
|
aa[profile][hat]['declared'] = False
|
||||||
@@ -1377,7 +1374,7 @@ def handle_children(profile, hat, root):
|
|||||||
if domainchange == 'change':
|
if domainchange == 'change':
|
||||||
return None
|
return None
|
||||||
|
|
||||||
elif type == 'netdomain':
|
elif typ == 'netdomain':
|
||||||
pid, p, h, prog, aamode, family, sock_type, protocol = entry[:8]
|
pid, p, h, prog, aamode, family, sock_type, protocol = entry[:8]
|
||||||
|
|
||||||
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
if not regex_nullcomplain.search(p) and not regex_nullcomplain.search(h):
|
||||||
@@ -1390,296 +1387,12 @@ def handle_children(profile, hat, root):
|
|||||||
|
|
||||||
return None
|
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')
|
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')
|
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_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_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')
|
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):
|
def hide_log_mode(mode):
|
||||||
mode = mode.replace('::', '')
|
mode = mode.replace('::', '')
|
||||||
return mode
|
return mode
|
||||||
@@ -1747,8 +1460,8 @@ def order_globs(globs, path):
|
|||||||
return sorted(globs)
|
return sorted(globs)
|
||||||
|
|
||||||
def ask_the_questions():
|
def ask_the_questions():
|
||||||
found = None
|
found = 0
|
||||||
print(log_dict)
|
global seen_events
|
||||||
for aamode in sorted(log_dict.keys()):
|
for aamode in sorted(log_dict.keys()):
|
||||||
# Describe the type of changes
|
# Describe the type of changes
|
||||||
if aamode == 'PERMITTING':
|
if aamode == 'PERMITTING':
|
||||||
@@ -1939,14 +1652,15 @@ def ask_the_questions():
|
|||||||
# If already present skip
|
# If already present skip
|
||||||
if aa[profile][hat][incname]:
|
if aa[profile][hat][incname]:
|
||||||
continue
|
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:
|
if not include_valid:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cm, am, m = match_include_to_path(incname, 'allow', path)
|
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):
|
if cm and mode_contains(cm, mode):
|
||||||
dm = match_include_to_path(incname, 'deny', path)
|
dm = match_include_to_path(incname, 'deny', path)
|
||||||
# If the mode is denied
|
# If the mode is denied
|
||||||
@@ -2041,7 +1755,7 @@ def ask_the_questions():
|
|||||||
q['options'] = options
|
q['options'] = options
|
||||||
q['selected'] = default_option - 1
|
q['selected'] = default_option - 1
|
||||||
q['functions'] = ['CMD_ALLOW', 'CMD_DENY', 'CMD_GLOB',
|
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']
|
'CMD_FINISHED', 'CMD_OTHER', 'CMD_IGNORE_ENTRY']
|
||||||
q['default'] = 'CMD_DENY'
|
q['default'] = 'CMD_DENY'
|
||||||
if aamode == 'PERMITTING':
|
if aamode == 'PERMITTING':
|
||||||
@@ -2183,16 +1897,16 @@ def ask_the_questions():
|
|||||||
if match:
|
if match:
|
||||||
# /foo/**.ext and /foo/*.ext => /**.ext
|
# /foo/**.ext and /foo/*.ext => /**.ext
|
||||||
newpath = re.sub('/[^/]+/\*{1,2}\.[^/]+$', '/**'+match.group()[0], newpath)
|
newpath = re.sub('/[^/]+/\*{1,2}\.[^/]+$', '/**'+match.group()[0], newpath)
|
||||||
elif re.search('/[^/]+\*\*[^/]*\.[^/]+$'):
|
elif re.search('/[^/]+\*\*[^/]*\.[^/]+$', newpath):
|
||||||
# /foo**.ext and /foo**bar.ext => /**.ext
|
# /foo**.ext and /foo**bar.ext => /**.ext
|
||||||
match = re.search('/[^/]+\*\*[^/]*(\.[^/]+)$')
|
match = re.search('/[^/]+\*\*[^/]*(\.[^/]+)$', newpath)
|
||||||
newpath = re.sub('/[^/]+\*\*[^/]*\.[^/]+$', '/**'+match.groups()[0], newpath)
|
newpath = re.sub('/[^/]+\*\*[^/]*\.[^/]+$', '/**'+match.groups()[0], newpath)
|
||||||
elif re.search('/\*\*[^/]+\.[^/]+$'):
|
elif re.search('/\*\*[^/]+\.[^/]+$', newpath):
|
||||||
# /**foo.ext => /**.ext
|
# /**foo.ext => /**.ext
|
||||||
match = re.search('/\*\*[^/]+(\.[^/]+)$')
|
match = re.search('/\*\*[^/]+(\.[^/]+)$', newpath)
|
||||||
newpath = re.sub('/\*\*[^/]+\.[^/]+$', '/**'+match.groups()[0], newpath)
|
newpath = re.sub('/\*\*[^/]+\.[^/]+$', '/**'+match.groups()[0], newpath)
|
||||||
else:
|
else:
|
||||||
match = re.search('(\.[^/]+)$')
|
match = re.search('(\.[^/]+)$', newpath)
|
||||||
newpath = re.sub('/[^/]+(\.[^/]+)$', '/*'+match.groups()[0], newpath)
|
newpath = re.sub('/[^/]+(\.[^/]+)$', '/*'+match.groups()[0], newpath)
|
||||||
|
|
||||||
if newpath not in options:
|
if newpath not in options:
|
||||||
@@ -2388,7 +2102,7 @@ def re_match_include(path):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def valid_include(profile, incname):
|
def valid_include(profile, incname):
|
||||||
if profile['include'].get(incname, False):
|
if profile and profile['include'].get(incname, False):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if cfg['settings']['custom_includes']:
|
if cfg['settings']['custom_includes']:
|
||||||
@@ -2410,19 +2124,22 @@ def match_net_includes(profile, family, nettype):
|
|||||||
|
|
||||||
return newincludes
|
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
|
# set up variables for this pass
|
||||||
t = hasher()
|
t = hasher()
|
||||||
transitions = hasher()
|
# transitions = hasher()
|
||||||
seen = hasher()
|
seen = hasher()
|
||||||
aa = hasher()
|
global log
|
||||||
profile_changes = hasher()
|
|
||||||
prelog = hasher()
|
|
||||||
log = []
|
log = []
|
||||||
log_dict = hasher()
|
global sev_db
|
||||||
changed = dict()
|
# aa = hasher()
|
||||||
|
# profile_changes = hasher()
|
||||||
|
# prelog = hasher()
|
||||||
|
# log = []
|
||||||
|
# log_dict = hasher()
|
||||||
|
# changed = dict()
|
||||||
skip = hasher()
|
skip = hasher()
|
||||||
filelist = hasher()
|
# filelist = hasher()
|
||||||
|
|
||||||
UI_Info(_('Reading log entries from %s.') %filename)
|
UI_Info(_('Reading log entries from %s.') %filename)
|
||||||
UI_Info(_('Updating AppArmor profiles in %s.') %profile_dir)
|
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:
|
if not sev_db:
|
||||||
sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown'))
|
sev_db = apparmor.severity.Severity(CONFDIR + '/severity.db', _('unknown'))
|
||||||
|
#print(pid)
|
||||||
|
#print(existing_profiles)
|
||||||
##if not repo_cf and cfg['repostory']['url']:
|
##if not repo_cf and cfg['repostory']['url']:
|
||||||
## repo_cfg = read_config('repository.conf')
|
## repo_cfg = read_config('repository.conf')
|
||||||
## if not repo_cfg['repository'].get('enabled', False) or repo_cfg['repository]['enabled'] not in ['yes', 'no']:
|
## if not repo_cfg['repository'].get('enabled', False) or repo_cfg['repository]['enabled'] not in ['yes', 'no']:
|
||||||
## UI_ask_to_enable_repo()
|
## UI_ask_to_enable_repo()
|
||||||
|
log_reader = apparmor.logparser.ReadLog(pid, filename, existing_profiles, profile_dir, log)
|
||||||
read_log(logmark)
|
log = log_reader.read_log(logmark)
|
||||||
|
#read_log(logmark)
|
||||||
|
|
||||||
for root in log:
|
for root in log:
|
||||||
handle_children('', '', root)
|
handle_children('', '', root)
|
||||||
|
#for root in range(len(log)):
|
||||||
|
#log[root] = handle_children('', '', log[root])
|
||||||
|
#print(log)
|
||||||
for pid in sorted(profile_changes.keys()):
|
for pid in sorted(profile_changes.keys()):
|
||||||
set_process(pid, profile_changes[pid])
|
set_process(pid, profile_changes[pid])
|
||||||
|
|
||||||
@@ -2624,27 +2345,27 @@ def collapse_log():
|
|||||||
combinedmode |= aa[profile][hat]['allow']['path'][path]
|
combinedmode |= aa[profile][hat]['allow']['path'][path]
|
||||||
|
|
||||||
# Match path to regexps in profile
|
# 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
|
# 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 not combinedmode or not mode_contains(combinedmode, mode):
|
||||||
if log[aamode][profile][hat]['path'].get(path, False):
|
if log_dict[aamode][profile][hat]['path'].get(path, False):
|
||||||
mode |= log[aamode][profile][hat]['path'][path]
|
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():
|
for capability in prelog[aamode][profile][hat]['capability'].keys():
|
||||||
# If capability not already in profile
|
# If capability not already in profile
|
||||||
if not aa[profile][hat]['allow']['capability'][capability].get('set', False):
|
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']
|
nd = prelog[aamode][profile][hat]['netdomain']
|
||||||
for family in nd.keys():
|
for family in nd.keys():
|
||||||
for sock_type in nd[family].keys():
|
for sock_type in nd[family].keys():
|
||||||
if not profile_known_network(aa[profile][hat], family, sock_type):
|
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):
|
def profilemode(mode):
|
||||||
pass
|
pass
|
||||||
@@ -2905,6 +2626,7 @@ def read_profiles():
|
|||||||
if is_skippable_file(file):
|
if is_skippable_file(file):
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
#print('read %s' %file)
|
||||||
read_profile(profile_dir + '/' + file, True)
|
read_profile(profile_dir + '/' + file, True)
|
||||||
|
|
||||||
def read_inactive_profiles():
|
def read_inactive_profiles():
|
||||||
@@ -2932,6 +2654,7 @@ def read_profile(file, active_profile):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
profile_data = parse_profile_data(data, file, 0)
|
profile_data = parse_profile_data(data, file, 0)
|
||||||
|
|
||||||
if profile_data and active_profile:
|
if profile_data and active_profile:
|
||||||
attach_profile_data(aa, profile_data)
|
attach_profile_data(aa, profile_data)
|
||||||
attach_profile_data(original_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
|
repo_data = None
|
||||||
parsed_profiles = []
|
parsed_profiles = []
|
||||||
initial_comment = ''
|
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_END = re.compile('^\}\s*(#.*)?$')
|
||||||
RE_PROFILE_CAP = re.compile('^(audit\s+)?(allow\s+|deny\s+)?capability\s+(\S+)\s*,\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*(#.*)?$')
|
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
|
profile_data[profile][hat]['external'] = True
|
||||||
else:
|
else:
|
||||||
hat = profile
|
hat = profile
|
||||||
|
# Profile stored
|
||||||
|
existing_profiles[profile] = file
|
||||||
|
|
||||||
flags = matches[6]
|
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])
|
print('Ignored: New definition for variable for:',list_var,'=', value, 'operation was:',var_operation,'old value=', var[list_var])
|
||||||
pass
|
pass
|
||||||
#raise AppArmorException('An existing variable redefined: %s' %list_var)
|
#raise AppArmorException('An existing variable redefined: %s' %list_var)
|
||||||
else:
|
elif var_operation == '+=':
|
||||||
if var.get(list_var, False):
|
if var.get(list_var, False):
|
||||||
var[list_var] = set(var[list_var] + vlist)
|
var[list_var] = set(var[list_var] + vlist)
|
||||||
else:
|
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):
|
def strip_quotes(data):
|
||||||
@@ -3870,7 +3597,7 @@ def match_include_to_path(incname, allow, path):
|
|||||||
combinedmode = 0
|
combinedmode = 0
|
||||||
combinedaudit = 0
|
combinedaudit = 0
|
||||||
matches = []
|
matches = []
|
||||||
|
incname = profile_dir + '/' + incname
|
||||||
includelist = [incname]
|
includelist = [incname]
|
||||||
while includelist:
|
while includelist:
|
||||||
incfile = includelist.pop(0)
|
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):
|
def convert_regexp(regexp):
|
||||||
|
regex_paren = re.compile('^(.*){([^}]*)}(.*)$')
|
||||||
regexp = regexp.strip()
|
regexp = regexp.strip()
|
||||||
new_reg = re.sub(r'(?<!\\)(\.|\+|\$)',r'\\\1',regexp)
|
new_reg = re.sub(r'(?<!\\)(\.|\+|\$)',r'\\\1',regexp)
|
||||||
# below will fail if { or } or , are part of a path too?
|
# 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('}', '}')
|
||||||
# new_reg = new_reg.replace(',', '|')
|
# new_reg = new_reg.replace(',', '|')
|
||||||
|
|
||||||
while re.search('{[^}]*,[^}]*}', new_reg):
|
while regex_paren.search(new_reg):
|
||||||
match = re.search('(.*){([^}]*)}(.*)', new_reg).groups()
|
match = regex_paren.search(new_reg).groups()
|
||||||
prev = match[0]
|
prev = match[0]
|
||||||
after = match[2]
|
after = match[2]
|
||||||
p1 = match[1].replace(',','|')
|
p1 = match[1].replace(',','|')
|
||||||
@@ -194,10 +195,18 @@ class DebugLogger:
|
|||||||
self.debug_level = logging.DEBUG
|
self.debug_level = logging.DEBUG
|
||||||
if os.getenv('LOGPROF_DEBUG', False):
|
if os.getenv('LOGPROF_DEBUG', False):
|
||||||
self.debugging = os.getenv('LOGPROF_DEBUG')
|
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:
|
if self.debugging == 1:
|
||||||
debug_level = logging.ERROR
|
debug_level = logging.ERROR
|
||||||
elif self.debug_level == 2:
|
elif self.debug_level == 2:
|
||||||
debug_level = logging.INFO
|
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='/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')
|
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"""
|
"""Loads the variables for the given profile"""
|
||||||
regex_include = re.compile('^#?include\s*<(\S*)>')
|
regex_include = re.compile('^#?include\s*<(\S*)>')
|
||||||
if os.path.isfile(prof_path):
|
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:
|
for line in f_in:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
# If any includes, load variables from them first
|
# If any includes, load variables from them first
|
||||||
|
@@ -235,6 +235,15 @@ def UI_PromptUser(q):
|
|||||||
cmd == 'XXXINVALIDXXX'
|
cmd == 'XXXINVALIDXXX'
|
||||||
return (cmd, arg)
|
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):
|
def UI_ShortMessage(title, message):
|
||||||
SendDataToYast({
|
SendDataToYast({
|
||||||
'type': 'short-dialog-message',
|
'type': 'short-dialog-message',
|
||||||
@@ -303,7 +312,7 @@ def Text_PromptUser(question):
|
|||||||
|
|
||||||
default_key = defaulthotkey.groups()[0].lower()
|
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))
|
raise AppArmorException('PromptUser: %s %s' %(_('Invalid default'), default))
|
||||||
|
|
||||||
widest = 0
|
widest = 0
|
||||||
@@ -315,7 +324,7 @@ def Text_PromptUser(question):
|
|||||||
widest = len(header)
|
widest = len(header)
|
||||||
widest += 1
|
widest += 1
|
||||||
|
|
||||||
formatstr = '%-' + widest + 's %s\n'
|
formatstr = '%-' + str(widest) + 's %s\n'
|
||||||
|
|
||||||
function_regexp = '^('
|
function_regexp = '^('
|
||||||
function_regexp += '|'.join(keys.keys())
|
function_regexp += '|'.join(keys.keys())
|
||||||
@@ -348,7 +357,7 @@ def Text_PromptUser(question):
|
|||||||
else:
|
else:
|
||||||
format_option = ' %s - %s '
|
format_option = ' %s - %s '
|
||||||
prompt += format_option %(index+1, option)
|
prompt += format_option %(index+1, option)
|
||||||
prompt += '\n'
|
prompt += '\n'
|
||||||
|
|
||||||
prompt += ' / '.join(menu_items)
|
prompt += ' / '.join(menu_items)
|
||||||
|
|
||||||
@@ -371,7 +380,7 @@ def Text_PromptUser(question):
|
|||||||
# sys.stdout.write('\n%s\n' %helptext)
|
# sys.stdout.write('\n%s\n' %helptext)
|
||||||
# ans = 'XXXINVALIDXXX'
|
# ans = 'XXXINVALIDXXX'
|
||||||
|
|
||||||
elif int(ans) == 10:
|
elif is_number(ans) == 10:
|
||||||
# If they hit return choose default option
|
# If they hit return choose default option
|
||||||
ans = default_key
|
ans = default_key
|
||||||
|
|
||||||
@@ -389,3 +398,10 @@ def Text_PromptUser(question):
|
|||||||
ans = keys[ans]
|
ans = keys[ans]
|
||||||
|
|
||||||
return ans, selected
|
return ans, selected
|
||||||
|
|
||||||
|
def is_number(number):
|
||||||
|
try:
|
||||||
|
return int(number)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
Reference in New Issue
Block a user