mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-31 06:16:03 +00:00
add get_all_merged_variables()
This function returns a dict with all variables and their values for a given profile file. It also checks for redefined variables, and adding to non-existing variables, and errors out in both cases. Note that it does not check the include order and is therefore more forgiving than apparmor_parser. Also add some tests for get_all_merged_variables(). Note: the tests are based on reading "real" profiles, therefore we need to initialize apparmor.aa and call some of its functions. The alternative would be to construct ProfileList objects for some profiles and includes manually, but doing that in a way that can replace the tests with "real" profiles would be quite some work, and I'm not bored enough for that ;-)
This commit is contained in:
@@ -185,6 +185,66 @@ class ProfileList:
|
||||
|
||||
return None # nothing found
|
||||
|
||||
def get_all_merged_variables(self, filename, all_incfiles, profile_dir):
|
||||
''' Get merged variables of a file and its includes
|
||||
|
||||
Note that this function is more forgiving than apparmor_parser.
|
||||
It detects variable redefinitions and adding values to non-existing variables.
|
||||
However, it doesn't honor the order - so adding to a variable first and defining
|
||||
it later won't trigger an error.
|
||||
'''
|
||||
|
||||
if not self.files.get(filename):
|
||||
raise AppArmorBug('%s not listed in ProfileList files' % filename)
|
||||
|
||||
merged_variables = {}
|
||||
|
||||
mainfile_variables = self.files[filename]['variable'].get_merged_variables()
|
||||
|
||||
# keep track in which file a variable gets set
|
||||
set_in = {}
|
||||
for var in mainfile_variables['=']:
|
||||
merged_variables[var] = mainfile_variables['='][var]
|
||||
set_in[var] = filename
|
||||
|
||||
# collect variable additions (+=)
|
||||
inc_add = {}
|
||||
if mainfile_variables['+=']:
|
||||
inc_add[filename] = mainfile_variables['+='] # variable additions from main file
|
||||
|
||||
for incname in all_incfiles:
|
||||
# include[] keys can be a) 'abstractions/foo' and b) '/full/path'
|
||||
if incname.startswith(profile_dir):
|
||||
incname = incname.replace('%s/' % profile_dir, '')
|
||||
|
||||
if not self.files.get(incname):
|
||||
continue # tunables/* only end up in self.files if they contain variable or alias definitions
|
||||
|
||||
inc_vars = self.files[incname]['variable'].get_merged_variables()
|
||||
|
||||
for var in inc_vars['=']:
|
||||
if merged_variables.get(var):
|
||||
raise AppArmorException('While parsing %(profile)s: Conflicting variable definitions for variable %(var)s found in %(file1)s and %(file2)s.' % {
|
||||
'var': var, 'profile': filename, 'file1': set_in[var], 'file2': incname})
|
||||
else:
|
||||
merged_variables[var] = inc_vars['='][var]
|
||||
set_in[var] = incname
|
||||
|
||||
# variable additions can happen in other files than the variable definition. First collect them from all files...
|
||||
if inc_vars['+=']:
|
||||
inc_add[incname] = inc_vars['+=']
|
||||
|
||||
for incname in inc_add:
|
||||
# ... and then check if the variables that get extended have an initial definition. If yes, merge them.
|
||||
for var in inc_add[incname]:
|
||||
if merged_variables.get(var):
|
||||
merged_variables[var] |= inc_add[incname][var]
|
||||
else:
|
||||
raise AppArmorException('While parsing %(profile)s: Variable %(var)s was not previously declared, but is being assigned additional value in file %(file)s.' % {
|
||||
'var': var, 'profile': filename, 'file': incname})
|
||||
|
||||
return merged_variables
|
||||
|
||||
def profiles_in_file(self, filename):
|
||||
''' Return list of profiles in the given file '''
|
||||
if not self.files.get(filename):
|
||||
|
@@ -10,7 +10,11 @@
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
import unittest
|
||||
from common_test import AATest, setup_all_loops
|
||||
from common_test import AATest, setup_aa, setup_all_loops, write_file
|
||||
|
||||
import apparmor.aa
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from apparmor.common import AppArmorBug, AppArmorException
|
||||
from apparmor.profile_list import ProfileList
|
||||
@@ -285,7 +289,74 @@ class TestGet(AATest):
|
||||
with self.assertRaises(AppArmorBug):
|
||||
self.pl.get_raw('/etc/apparmor.d/not.found')
|
||||
|
||||
class AaTest_get_all_merged_variables(AATest):
|
||||
tests = []
|
||||
|
||||
def AASetup(self):
|
||||
self.createTmpdir()
|
||||
|
||||
# copy the local profiles to the test directory
|
||||
self.profile_dir = '%s/profiles' % self.tmpdir
|
||||
apparmor.aa.profile_dir = self.profile_dir
|
||||
shutil.copytree('../../profiles/apparmor.d/', self.profile_dir, symlinks=True)
|
||||
|
||||
def _load_profiles(self):
|
||||
apparmor.aa.reset_aa()
|
||||
|
||||
# load the profiles and abstractions
|
||||
apparmor.aa.profiledir = self.profile_dir
|
||||
apparmor.aa.loadincludes()
|
||||
apparmor.aa.read_profiles()
|
||||
|
||||
def test_unchanged(self):
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq')
|
||||
vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'})
|
||||
self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/'})
|
||||
|
||||
def test_extended_home(self):
|
||||
write_file(self.profile_dir, 'tunables/home.d/extend_home', '@{HOME} += /my/castle/')
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq')
|
||||
vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'})
|
||||
self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/'})
|
||||
|
||||
def test_extended_home_2(self):
|
||||
write_file(self.profile_dir, 'tunables/home.d/extend_home', '@{HOME} += /my/castle/')
|
||||
write_file(self.profile_dir, 'tunables/home.d/moving_around', '@{HOME} += /on/the/road/')
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq')
|
||||
vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
self.assertEqual(vars['@{TFTP_DIR}'], {'/var/tftp', '/srv/tftp', '/srv/tftpboot'})
|
||||
self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/', '/on/the/road/'})
|
||||
|
||||
def test_extend_home_in_mainfile(self):
|
||||
write_file(self.profile_dir, 'tunables/home.d/extend_home', '@{HOME} += /my/castle/')
|
||||
write_file(self.profile_dir, 'dummy_profile', 'include <tunables/global>\n@{HOME} += /in/the/profile/')
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'dummy_profile')
|
||||
vars = apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'dummy_profile'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
self.assertEqual(vars.get('@{TFTP_DIR}', None), None)
|
||||
self.assertEqual(vars['@{HOME}'], {'@{HOMEDIRS}/*/', '/root/', '/my/castle/', '/in/the/profile/'})
|
||||
|
||||
def test_redefine_home(self):
|
||||
write_file(self.profile_dir, 'tunables/home.d/overwrite_home', '@{HOME} = /my/castle/') # note: =, not +=
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq')
|
||||
with self.assertRaises(AppArmorException):
|
||||
apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
|
||||
def test_add_to_nonexisting(self):
|
||||
write_file(self.profile_dir, 'tunables/home.d/no_such_var', '@{NO_SUCH_HOME} += /my/castle/') # add to non-existing variable
|
||||
self._load_profiles()
|
||||
prof_filename = os.path.join(self.profile_dir, 'usr.sbin.dnsmasq')
|
||||
with self.assertRaises(AppArmorException):
|
||||
apparmor.aa.active_profiles.get_all_merged_variables(os.path.join(self.profile_dir, 'usr.sbin.dnsmasq'), apparmor.aa.include_list_recursive(apparmor.aa.active_profiles.files[prof_filename]), self.profile_dir)
|
||||
|
||||
|
||||
setup_aa(apparmor.aa)
|
||||
setup_all_loops(__name__)
|
||||
if __name__ == '__main__':
|
||||
unittest.main(verbosity=1)
|
||||
|
Reference in New Issue
Block a user