2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-31 06:16:03 +00:00

ProfileStorage: store parent profile

... and extend the tests to get some coverage.
This commit is contained in:
Christian Boltz
2024-10-06 20:39:01 +02:00
parent 792d1a5568
commit a0e6fbe32a
4 changed files with 50 additions and 20 deletions

View File

@@ -417,6 +417,7 @@ def create_new_profile(localfile, is_stub=False):
full_hat = combine_profname((localfile, hat))
if not local_profile.get(full_hat, False):
local_profile[full_hat] = ProfileStorage('NEW', hat, 'create_new_profile() required_hats')
local_profile[full_hat]['parent'] = localfile
local_profile[full_hat]['is_hat'] = True
local_profile[full_hat]['flags'] = 'complain'
@@ -561,6 +562,8 @@ def change_profile_flags(prof_filename, program, flag, set_flag):
for lineno, line in enumerate(f_in):
if RE_PROFILE_START.search(line):
depth += 1
# TODO: hand over profile and hat (= parent profile)
# (and find out why it breaks test-aa.py with several "a child profile inside another child profile is not allowed" errors when doing so)
(profile, hat, prof_storage) = ProfileStorage.parse(line, prof_filename, lineno, '', '')
old_flags = prof_storage['flags']
newflags = ', '.join(add_or_remove_flag(old_flags, flag, set_flag))
@@ -581,6 +584,7 @@ def change_profile_flags(prof_filename, program, flag, set_flag):
line = '%s\n' % line[0]
elif RE_PROFILE_HAT_DEF.search(line):
depth += 1
# TODO: hand over profile and hat (= parent profile)
(profile, hat, prof_storage) = ProfileStorage.parse(line, prof_filename, lineno, '', '')
old_flags = prof_storage['flags']
newflags = ', '.join(add_or_remove_flag(old_flags, flag, set_flag))
@@ -590,6 +594,7 @@ def change_profile_flags(prof_filename, program, flag, set_flag):
line = '%s\n' % line[0]
elif RE_PROFILE_END.search(line):
depth -= 1
# TODO: restore 'profile' and 'hat' to previous values (not really needed/used for aa-complain etc., but can't hurt)
f_out.write(line)
os.rename(temp_file.name, prof_filename)
@@ -694,6 +699,7 @@ def ask_addhat(hashlog):
if ans == 'CMD_ADDHAT':
aa[profile][hat] = ProfileStorage(profile, hat, 'ask_addhat addhat')
aa[profile][hat]['parent'] = profile
aa[profile][hat]['flags'] = aa[profile][profile]['flags']
hashlog[aamode][full_hat]['final_name'] = '%s//%s' % (profile, hat)
changed[profile] = True
@@ -703,6 +709,7 @@ def ask_addhat(hashlog):
if not aa[profile].get(hat, False):
# create default hat if it doesn't exist yet
aa[profile][hat] = ProfileStorage(profile, hat, 'ask_addhat default hat')
aa[profile][hat]['parent'] = profile
aa[profile][hat]['flags'] = aa[profile][profile]['flags']
changed[profile] = True
elif ans == 'CMD_DENY':
@@ -1059,9 +1066,11 @@ def ask_the_questions(log_dict):
if log_dict[aamode][full_profile]['is_hat']:
aa[profile][hat] = ProfileStorage(profile, hat, 'mergeprof ask_the_questions() - missing hat')
aa[profile][hat]['parent'] = profile
aa[profile][hat]['is_hat'] = True
else:
aa[profile][hat] = ProfileStorage(profile, hat, 'mergeprof ask_the_questions() - missing subprofile')
aa[profile][hat]['parent'] = profile
aa[profile][hat]['is_hat'] = False
# check for and ask about conflicting exec modes
@@ -1862,6 +1871,7 @@ def parse_profile_data(data, file, do_include, in_preamble):
profname = combine_profname((parsed_prof, hat))
if not profile_data.get(profname, False):
profile_data[profname] = ProfileStorage(parsed_prof, hat, 'parse_profile_data() required_hats')
profile_data[profname]['parent'] = parsed_prof
profile_data[profname]['is_hat'] = True
# End of file reached but we're stuck in a profile

View File

@@ -1,6 +1,6 @@
# ----------------------------------------------------------------------
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
# Copyright (C) 2014-2021 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2014-2024 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@@ -77,6 +77,7 @@ class ProfileStorage:
data['filename'] = ''
data['logprof_suggest'] = '' # set in abstractions that should be suggested by aa-logprof
data['parent'] = '' # parent profile, or '' for top-level profiles and external hats
data['name'] = ''
data['attachment'] = ''
data['xattrs'] = ''
@@ -221,11 +222,13 @@ class ProfileStorage:
_('%(profile)s profile in %(file)s contains syntax errors in line %(line)s: a child profile inside another child profile is not allowed.')
% {'profile': profile, 'file': file, 'line': lineno + 1})
parent = profile
hat = matches['profile']
prof_or_hat_name = hat
pps_set_hat_external = False
else: # stand-alone profile
parent = ''
profile = matches['profile']
prof_or_hat_name = profile
if len(profile.split('//')) > 2:
@@ -241,6 +244,7 @@ class ProfileStorage:
prof_storage = cls(profile, hat, cls.__name__ + '.parse()')
prof_storage['parent'] = parent
prof_storage['name'] = prof_or_hat_name
prof_storage['filename'] = file
prof_storage['external'] = pps_set_hat_external

View File

@@ -1,7 +1,7 @@
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2014-2021 Christian Boltz
# Copyright (C) 2014-2024 Christian Boltz
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@@ -515,6 +515,21 @@ class AaTest_parse_profile_data(AATest):
self.assertEqual(prof['/foo']['filename'], 'somefile')
self.assertEqual(prof['/foo']['flags'], None)
def test_parse_parent_and_child(self):
prof = parse_profile_data('profile /foo {\nprofile /bar {\n}\n}\n'.split(), 'somefile', False, False)
self.assertEqual(list(prof.keys()), ['/foo', '/foo///bar'])
self.assertEqual(prof['/foo']['parent'], '')
self.assertEqual(prof['/foo']['name'], '/foo')
self.assertEqual(prof['/foo']['filename'], 'somefile')
self.assertEqual(prof['/foo']['flags'], None)
self.assertEqual(prof['/foo///bar']['parent'], '/foo')
self.assertEqual(prof['/foo///bar']['name'], '/bar')
self.assertEqual(prof['/foo///bar']['filename'], 'somefile')
self.assertEqual(prof['/foo///bar']['flags'], None)
def test_parse_duplicate_profile(self):
with self.assertRaises(AppArmorException):
# file contains two profiles with the same name

View File

@@ -1,7 +1,7 @@
#! /usr/bin/python3
# ------------------------------------------------------------------
#
# Copyright (C) 2017-2021 Christian Boltz <apparmor@cboltz.de>
# Copyright (C) 2017-2024 Christian Boltz <apparmor@cboltz.de>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 2 of the GNU General Public
@@ -141,29 +141,30 @@ class AaTest_repr(AATest):
class AaTest_parse_profile_start(AATest):
tests = (
# profile start line profile hat name profile hat attachment xattrs flags pps_set_hat_external
(('/foo {', None, None), ('/foo', '/foo', '/foo', '', '', None, False)),
(('/foo (complain) {', None, None), ('/foo', '/foo', '/foo', '', '', 'complain', False)),
(('profile foo /foo {', None, None), ('foo', 'foo', 'foo', '/foo', '', None, False)), # named profile
(('profile /foo {', '/bar', None), ('/foo', '/bar', '/foo', '', '', None, False)), # child profile
(('/foo//bar {', None, None), ('/foo//bar', '/foo', 'bar', '', '', None, True)), # external hat
(('profile "/foo" (complain) {', None, None), ('/foo', '/foo', '/foo', '', '', 'complain', False)),
(('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('/foo', '/foo', '/foo', '', 'user.bar=bar', None, False)),
(('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('/foo', '/foo', '/foo', '', 'user.bar=bar user.foo=*', None, False)),
(('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', 'myvalue="foo.bar"', None, False)),
# profile start line profile hat parent name profile hat attachment xattrs flags pps_set_hat_external
(('/foo {', None, None), ('', '/foo', '/foo', '/foo', '', '', None, False)),
(('/foo (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', '', 'complain', False)),
(('profile foo /foo {', None, None), ('', 'foo', 'foo', 'foo', '/foo', '', None, False)), # named profile
(('profile /foo {', '/bar', None), ('/bar', '/foo', '/bar', '/foo', '', '', None, False)), # child profile
(('/foo//bar {', None, None), ('', '/foo//bar', '/foo', 'bar', '', '', None, True)), # external hat
(('profile "/foo" (complain) {', None, None), ('', '/foo', '/foo', '/foo', '', '', 'complain', False)),
(('profile "/foo" xattrs=(user.bar=bar) {', None, None), ('', '/foo', '/foo', '/foo', '', 'user.bar=bar', None, False)),
(('profile "/foo" xattrs=(user.bar=bar user.foo=*) {', None, None), ('', '/foo', '/foo', '/foo', '', 'user.bar=bar user.foo=*', None, False)),
(('/usr/bin/xattrs-test xattrs=(myvalue="foo.bar") {', None, None), ('', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '/usr/bin/xattrs-test', '', 'myvalue="foo.bar"', None, False)),
)
def _run_test(self, params, expected):
(profile, hat, prof_storage) = ProfileStorage.parse(params[0], 'somefile', 1, params[1], params[2])
self.assertEqual(prof_storage['name'], expected[0])
self.assertEqual(profile, expected[1])
self.assertEqual(hat, expected[2])
self.assertEqual(prof_storage['attachment'], expected[3])
self.assertEqual(prof_storage['xattrs'], expected[4])
self.assertEqual(prof_storage['flags'], expected[5])
self.assertEqual(prof_storage['parent'], expected[0])
self.assertEqual(prof_storage['name'], expected[1])
self.assertEqual(profile, expected[2])
self.assertEqual(hat, expected[3])
self.assertEqual(prof_storage['attachment'], expected[4])
self.assertEqual(prof_storage['xattrs'], expected[5])
self.assertEqual(prof_storage['flags'], expected[6])
self.assertEqual(prof_storage['is_hat'], False)
self.assertEqual(prof_storage['external'], expected[6])
self.assertEqual(prof_storage['external'], expected[7])
class AaTest_parse_profile_start_errors(AATest):