[23/38] Add get_file_perms() to aa.py

get_file_perms() collects the existing permissions for a file from
various rules (exact matches, wildcards) in the main profile and the
included abstractions.

It will be used to get displaying the current permissions back, and
also to propose rules with merged permissions (next patch).

Also add some tests to make sure it does what it promises ;-)



Acked-by: Steve Beattie <steve@nxnw.org>
This commit is contained in:
Christian Boltz 2016-10-01 20:03:44 +02:00
parent 435281f018
commit f2a60f67e3
2 changed files with 99 additions and 1 deletions

View file

@ -3758,6 +3758,40 @@ def is_known_rule(profile, rule_type, rule_obj):
return False
def get_file_perms(profile, path, audit, deny):
'''get the current permissions for the given path'''
perms = profile['file'].get_perms_for_path(path, audit, deny)
includelist = list(profile['include'].keys())
checked = []
while includelist:
incname = includelist.pop(0)
if incname in checked:
continue
checked.append(incname)
if os.path.isdir(profile_dir + '/' + incname):
includelist += include_dir_filelist(profile_dir, incname)
else:
incperms = include[incname][incname]['file'].get_perms_for_path(path, audit, deny)
for allow_or_deny in ['allow', 'deny']:
for owner_or_all in ['all', 'owner']:
for perm in incperms[allow_or_deny][owner_or_all]:
perms[allow_or_deny][owner_or_all].add(perm)
for incpath in incperms['paths']:
perms['paths'].add(incpath)
for childinc in include[incname][incname]['include'].keys():
if childinc not in checked:
includelist += [childinc]
return perms
def reload_base(bin_path):
if not check_for_apparmor():
return None

View file

@ -21,8 +21,9 @@ import apparmor.aa # needed to set global vars in some tests
from apparmor.aa import (check_for_apparmor, get_output, get_reqs, get_interpreter_and_abstraction, create_new_profile,
get_profile_flags, set_profile_flags, is_skippable_file, is_skippable_dir,
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header,
var_transform, serialize_parse_profile_start)
var_transform, serialize_parse_profile_start, get_file_perms)
from apparmor.common import AppArmorException, AppArmorBug
from apparmor.rule.file import FileRule
class AaTestWithTempdir(AATest):
def AASetup(self):
@ -735,6 +736,69 @@ class AaTestInvalid_serialize_parse_profile_start(AATest):
# 'correct' is always True in the code that uses serialize_parse_profile_start() (set some lines above the function call)
serialize_parse_profile_start(line, 'somefile', 1, profile, hat, prof_data_profile, prof_data_external, True)
class AaTest_get_file_perms_1(AATest):
tests = [
('/usr/share/common-licenses/foo/bar', {'allow': {'all': set(), 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'} }),
('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/dev/null'} }),
('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/foo/bar'} }), # exec perms not included
('/no/thing', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }),
('/usr/lib/ispell/', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }),
('/usr/lib/aspell/*.so', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }),
]
def _run_test(self, params, expected):
self.createTmpdir()
#copy the local profiles to the test directory
self.profile_dir = '%s/profiles' % self.tmpdir
shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True)
profile = apparmor.aa.profile_storage('/test', '/test', 'test-aa.py')
# simple profile without any includes
profile['file'].add(FileRule.parse('owner /usr/share/common-licenses/** w,'))
profile['file'].add(FileRule.parse('/dev/null rwk,'))
profile['file'].add(FileRule.parse('/foo/bar rwix,'))
perms = get_file_perms(profile, params, False, False) # only testing with audit and deny = False
self.assertEqual(perms, expected)
class AaTest_get_file_perms_2(AATest):
tests = [
('/usr/share/common-licenses/foo/bar', {'allow': {'all': {'r'}, 'owner': {'w'} }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/share/common-licenses/**'} }),
('/dev/null', {'allow': {'all': {'r', 'w', 'k'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/dev/null'} }),
('/foo/bar', {'allow': {'all': {'r', 'w'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/foo/bar'} }), # exec perms not included
('/no/thing', {'allow': {'all': set(), 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': set() }),
('/usr/lib/ispell/', {'allow': {'all': {'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/ispell/', '/usr/lib{,32,64}/**'} }), # from abstractions/enchant
('/usr/lib/aspell/*.so', {'allow': {'all': {'m', 'r'}, 'owner': set() }, 'deny': {'all':set(), 'owner': set()}, 'paths': {'/usr/lib/aspell/*', '/usr/lib/aspell/*.so', '/usr/lib{,32,64}/**'} }), # from abstractions/aspell via abstractions/enchant
]
def _run_test(self, params, expected):
self.createTmpdir()
#copy the local profiles to the test directory
self.profile_dir = '%s/profiles' % self.tmpdir
shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True)
# load the abstractions we need in the test
apparmor.aa.profiledir = self.profile_dir
apparmor.aa.load_include('abstractions/base')
apparmor.aa.load_include('abstractions/bash')
apparmor.aa.load_include('abstractions/enchant')
apparmor.aa.load_include('abstractions/aspell')
profile = apparmor.aa.profile_storage('/test', '/test', 'test-aa.py')
profile['include']['abstractions/base'] = True
profile['include']['abstractions/bash'] = True
profile['include']['abstractions/enchant'] = True # includes abstractions/aspell
profile['file'].add(FileRule.parse('owner /usr/share/common-licenses/** w,'))
profile['file'].add(FileRule.parse('/dev/null rwk,'))
profile['file'].add(FileRule.parse('/foo/bar rwix,'))
perms = get_file_perms(profile, params, False, False) # only testing with audit and deny = False
self.assertEqual(perms, expected)
setup_all_loops(__name__)
if __name__ == '__main__':