diff --git a/utils/apparmor/rule/file.py b/utils/apparmor/rule/file.py index fdd0bdda4..a7580979f 100644 --- a/utils/apparmor/rule/file.py +++ b/utils/apparmor/rule/file.py @@ -306,6 +306,20 @@ class FileRule(BaseRule): return True + def severity(self, sev_db): + if self.all_paths: + severity = sev_db.rank_path('/**', 'mrwlkix') + else: + severity = -1 + sev = sev_db.rank_path(self.path.regex, self._joint_perms()) + if isinstance(sev, int): # type check avoids breakage caused by 'unknown' + severity = max(severity, sev) + + if severity == -1: + severity = sev # effectively 'unknown' + + return severity + def logprof_header_localvars(self): if self.owner: owner = _('Yes') diff --git a/utils/apparmor/severity.py b/utils/apparmor/severity.py index 569d5a9dc..8ea8f780f 100644 --- a/utils/apparmor/severity.py +++ b/utils/apparmor/severity.py @@ -88,6 +88,15 @@ class Severity(object): warn("unknown capability: %s" % resource) return self.severity['DEFAULT_RANK'] + def rank_path(self, path, mode=None): + """Returns the rank for the given path""" + if '@' in path: # path contains variable + return self.handle_variable_rank(path, mode) + elif path[0] == '/': # file resource + return self.handle_file(path, mode) + else: + raise AppArmorException("Unexpected path input: %s" % path) + def check_subtree(self, tree, mode, sev, segments): """Returns the max severity from the regex tree""" if len(segments) == 0: @@ -136,9 +145,9 @@ class Severity(object): def rank(self, resource, mode=None): """Returns the rank for the resource file/capability""" if '@' in resource: # path contains variable - return self.handle_variable_rank(resource, mode) + return self.rank_path(resource, mode) elif resource[0] == '/': # file resource - return self.handle_file(resource, mode) + return self.rank_path(resource, mode) elif resource[0:4] == 'CAP_': # capability resource return self.rank_capability(resource[4:]) else: diff --git a/utils/test/test-file.py b/utils/test/test-file.py index 991d2dc21..6c3bc3695 100644 --- a/utils/test/test-file.py +++ b/utils/test/test-file.py @@ -19,6 +19,7 @@ from common_test import AATest, setup_all_loops from apparmor.rule.file import FileRule, FileRuleset from apparmor.rule import BaseRule +import apparmor.severity as severity from apparmor.common import AppArmorException, AppArmorBug from apparmor.logparser import ReadLog from apparmor.translations import init_translation @@ -697,6 +698,29 @@ class FileCoveredTest_ManualOrInvalid(AATest): with self.assertRaises(AppArmorBug): obj.is_equal(testobj) +class FileSeverityTest(AATest): + tests = [ + ('/usr/bin/whatis ix,', 5), + ('/etc ix,', 'unknown'), + ('/dev/doublehit ix,', 0), + ('/dev/doublehit rix,', 4), + ('/dev/doublehit rwix,', 8), + ('/dev/tty10 rwix,', 9), + ('/var/adm/foo/** rix,', 3), + ('/etc/apparmor/** r,', 6), + ('/etc/** r,', 'unknown'), + ('/usr/foo@bar r,', 'unknown'), # filename containing @ + ('/home/foo@bar rw,', 6), # filename containing @ + ('file,', 'unknown'), # bare file rule XXX should return maximum severity + ] + + def _run_test(self, params, expected): + sev_db = severity.Severity('severity.db', 'unknown') + obj = FileRule.parse(params) + rank = obj.severity(sev_db) + self.assertEqual(rank, expected) + + #class FileLogprofHeaderTest(AATest): # tests = [ # ('file,', [ _('Access mode'), _('ALL'), _('Bus'), _('ALL'), _('Path'), _('ALL'), _('Name'), _('ALL'), _('Interface'), _('ALL'), _('Member'), _('ALL'), _('Peer exec_perms'), _('ALL'), _('Peer label'), _('ALL')]),