mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 23:05:11 +00:00
move several write_* functions to apparmor.profile_storage
ProfileStorage() stores the content of a profile, so it makes sense to also have the functions to write those rules (including helper functions used by these functions) in the same file. Note that I only moved the functions for rule types that are not handled by *Ruleset classes. The functions for writing rules stored in a *Ruleset class will hopefully be superfluous sooner or later (probably later because serialize_parse_profile_start() depends on them, and rewriting it won't be easy) Also move the test for var_transform() to test-profile-storage.py.
This commit is contained in:
@@ -49,7 +49,9 @@ from apparmor.regex import (RE_PROFILE_START, RE_PROFILE_END, RE_PROFILE_LINK,
|
|||||||
RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT,
|
RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT,
|
||||||
strip_quotes, parse_profile_start_line, re_match_include )
|
strip_quotes, parse_profile_start_line, re_match_include )
|
||||||
|
|
||||||
from apparmor.profile_storage import ProfileStorage, ruletypes
|
from apparmor.profile_storage import (ProfileStorage, ruletypes, write_alias,
|
||||||
|
write_includes, write_links, write_list_vars, write_mount,
|
||||||
|
write_pivot_root, write_unix)
|
||||||
|
|
||||||
import apparmor.rules as aarules
|
import apparmor.rules as aarules
|
||||||
|
|
||||||
@@ -2610,89 +2612,18 @@ def write_header(prof_data, depth, name, embedded_hat, write_flags):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_allow_str(allow):
|
|
||||||
if allow == 'deny':
|
|
||||||
return 'deny '
|
|
||||||
elif allow == 'allow':
|
|
||||||
return ''
|
|
||||||
elif allow == '':
|
|
||||||
return ''
|
|
||||||
else:
|
|
||||||
raise AppArmorException(_("Invalid allow string: %(allow)s"))
|
|
||||||
|
|
||||||
def set_ref_allow(prof_data, allow):
|
|
||||||
if allow:
|
|
||||||
return prof_data[allow], set_allow_str(allow)
|
|
||||||
else:
|
|
||||||
return prof_data, ''
|
|
||||||
|
|
||||||
|
|
||||||
def write_pair(prof_data, depth, allow, name, prefix, sep, tail, fn):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
ref, allow = set_ref_allow(prof_data, allow)
|
|
||||||
|
|
||||||
if ref.get(name, False):
|
|
||||||
for key in sorted(ref[name].keys()):
|
|
||||||
value = fn(ref[name][key]) # eval('%s(%s)' % (fn, ref[name][key]))
|
|
||||||
data.append('%s%s%s%s%s%s%s' % (pre, allow, prefix, key, sep, value, tail))
|
|
||||||
if ref[name].keys():
|
|
||||||
data.append('')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_includes(prof_data, depth):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
|
|
||||||
for key in sorted(prof_data['include'].keys()):
|
|
||||||
if key.startswith('/'):
|
|
||||||
qkey = '"%s"' % key
|
|
||||||
else:
|
|
||||||
qkey = '<%s>' % quote_if_needed(key)
|
|
||||||
|
|
||||||
data.append('%s#include %s' % (pre, qkey))
|
|
||||||
|
|
||||||
if data:
|
|
||||||
data.append('')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_change_profile(prof_data, depth):
|
def write_change_profile(prof_data, depth):
|
||||||
data = []
|
data = []
|
||||||
if prof_data.get('change_profile', False):
|
if prof_data.get('change_profile', False):
|
||||||
data = prof_data['change_profile'].get_clean(depth)
|
data = prof_data['change_profile'].get_clean(depth)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def write_alias(prof_data, depth):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
|
|
||||||
if prof_data['alias']:
|
|
||||||
for key in sorted(prof_data['alias'].keys()):
|
|
||||||
data.append('%salias %s -> %s,' % (pre, quote_if_needed(key), quote_if_needed(prof_data['alias'][key])))
|
|
||||||
|
|
||||||
data.append('')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_rlimits(prof_data, depth):
|
def write_rlimits(prof_data, depth):
|
||||||
data = []
|
data = []
|
||||||
if prof_data.get('rlimit', False):
|
if prof_data.get('rlimit', False):
|
||||||
data = prof_data['rlimit'].get_clean(depth)
|
data = prof_data['rlimit'].get_clean(depth)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def var_transform(ref):
|
|
||||||
data = []
|
|
||||||
for value in ref:
|
|
||||||
if not value:
|
|
||||||
value = '""'
|
|
||||||
data.append(quote_if_needed(value))
|
|
||||||
return ' '.join(data)
|
|
||||||
|
|
||||||
def write_list_vars(prof_data, depth):
|
|
||||||
return write_pair(prof_data, depth, '', 'lvar', '', ' = ', '', var_transform)
|
|
||||||
|
|
||||||
def write_capabilities(prof_data, depth):
|
def write_capabilities(prof_data, depth):
|
||||||
data = []
|
data = []
|
||||||
if prof_data.get('capability', False):
|
if prof_data.get('capability', False):
|
||||||
@@ -2711,24 +2642,6 @@ def write_dbus(prof_data, depth):
|
|||||||
data = prof_data['dbus'].get_clean(depth)
|
data = prof_data['dbus'].get_clean(depth)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def write_mount_rules(prof_data, depth, allow):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
|
|
||||||
# no mount rules, so return
|
|
||||||
if not prof_data[allow].get('mount', False):
|
|
||||||
return data
|
|
||||||
|
|
||||||
for mount_rule in prof_data[allow]['mount']:
|
|
||||||
data.append('%s%s' % (pre, mount_rule.serialize()))
|
|
||||||
data.append('')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_mount(prof_data, depth):
|
|
||||||
data = write_mount_rules(prof_data, depth, 'deny')
|
|
||||||
data += write_mount_rules(prof_data, depth, 'allow')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_signal(prof_data, depth):
|
def write_signal(prof_data, depth):
|
||||||
data = []
|
data = []
|
||||||
if prof_data.get('signal', False):
|
if prof_data.get('signal', False):
|
||||||
@@ -2741,69 +2654,6 @@ def write_ptrace(prof_data, depth):
|
|||||||
data = prof_data['ptrace'].get_clean(depth)
|
data = prof_data['ptrace'].get_clean(depth)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def write_pivot_root_rules(prof_data, depth, allow):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
|
|
||||||
# no pivot_root rules, so return
|
|
||||||
if not prof_data[allow].get('pivot_root', False):
|
|
||||||
return data
|
|
||||||
|
|
||||||
for pivot_root_rule in prof_data[allow]['pivot_root']:
|
|
||||||
data.append('%s%s' % (pre, pivot_root_rule.serialize()))
|
|
||||||
data.append('')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_pivot_root(prof_data, depth):
|
|
||||||
data = write_pivot_root_rules(prof_data, depth, 'deny')
|
|
||||||
data += write_pivot_root_rules(prof_data, depth, 'allow')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_unix_rules(prof_data, depth, allow):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
|
|
||||||
# no unix rules, so return
|
|
||||||
if not prof_data[allow].get('unix', False):
|
|
||||||
return data
|
|
||||||
|
|
||||||
for unix_rule in prof_data[allow]['unix']:
|
|
||||||
data.append('%s%s' % (pre, unix_rule.serialize()))
|
|
||||||
data.append('')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_unix(prof_data, depth):
|
|
||||||
data = write_unix_rules(prof_data, depth, 'deny')
|
|
||||||
data += write_unix_rules(prof_data, depth, 'allow')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_link_rules(prof_data, depth, allow):
|
|
||||||
pre = ' ' * depth
|
|
||||||
data = []
|
|
||||||
allowstr = set_allow_str(allow)
|
|
||||||
|
|
||||||
if prof_data[allow].get('link', False):
|
|
||||||
for path in sorted(prof_data[allow]['link'].keys()):
|
|
||||||
to_name = prof_data[allow]['link'][path]['to']
|
|
||||||
subset = ''
|
|
||||||
if prof_data[allow]['link'][path]['mode'] & apparmor.aamode.AA_LINK_SUBSET:
|
|
||||||
subset = 'subset '
|
|
||||||
audit = ''
|
|
||||||
if prof_data[allow]['link'][path].get('audit', False):
|
|
||||||
audit = 'audit '
|
|
||||||
path = quote_if_needed(path)
|
|
||||||
to_name = quote_if_needed(to_name)
|
|
||||||
data.append('%s%s%slink %s%s -> %s,' % (pre, audit, allowstr, subset, path, to_name))
|
|
||||||
data.append('')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_links(prof_data, depth):
|
|
||||||
data = write_link_rules(prof_data, depth, 'deny')
|
|
||||||
data += write_link_rules(prof_data, depth, 'allow')
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def write_file(prof_data, depth):
|
def write_file(prof_data, depth):
|
||||||
data = []
|
data = []
|
||||||
if prof_data.get('file', False):
|
if prof_data.get('file', False):
|
||||||
|
@@ -14,7 +14,9 @@
|
|||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
from apparmor.common import AppArmorBug, hasher
|
from apparmor.aamode import AA_LINK_SUBSET
|
||||||
|
|
||||||
|
from apparmor.common import AppArmorBug, AppArmorException, hasher
|
||||||
|
|
||||||
from apparmor.rule.capability import CapabilityRuleset
|
from apparmor.rule.capability import CapabilityRuleset
|
||||||
from apparmor.rule.change_profile import ChangeProfileRuleset
|
from apparmor.rule.change_profile import ChangeProfileRuleset
|
||||||
@@ -25,6 +27,12 @@ from apparmor.rule.ptrace import PtraceRuleset
|
|||||||
from apparmor.rule.rlimit import RlimitRuleset
|
from apparmor.rule.rlimit import RlimitRuleset
|
||||||
from apparmor.rule.signal import SignalRuleset
|
from apparmor.rule.signal import SignalRuleset
|
||||||
|
|
||||||
|
from apparmor.rule import quote_if_needed
|
||||||
|
|
||||||
|
# setup module translations
|
||||||
|
from apparmor.translations import init_translation
|
||||||
|
_ = init_translation()
|
||||||
|
|
||||||
ruletypes = {
|
ruletypes = {
|
||||||
'capability': {'ruleset': CapabilityRuleset},
|
'capability': {'ruleset': CapabilityRuleset},
|
||||||
'change_profile': {'ruleset': ChangeProfileRuleset},
|
'change_profile': {'ruleset': ChangeProfileRuleset},
|
||||||
@@ -104,3 +112,155 @@ class ProfileStorage:
|
|||||||
return self.data.get(key, fallback)
|
return self.data.get(key, fallback)
|
||||||
else:
|
else:
|
||||||
raise AppArmorBug('attempt to read unknown key %s' % key)
|
raise AppArmorBug('attempt to read unknown key %s' % key)
|
||||||
|
|
||||||
|
|
||||||
|
def set_allow_str(allow):
|
||||||
|
if allow == 'deny':
|
||||||
|
return 'deny '
|
||||||
|
elif allow == 'allow':
|
||||||
|
return ''
|
||||||
|
elif allow == '':
|
||||||
|
return ''
|
||||||
|
else:
|
||||||
|
raise AppArmorException(_("Invalid allow string: %(allow)s"))
|
||||||
|
|
||||||
|
def set_ref_allow(prof_data, allow):
|
||||||
|
if allow:
|
||||||
|
return prof_data[allow], set_allow_str(allow)
|
||||||
|
else:
|
||||||
|
return prof_data, ''
|
||||||
|
|
||||||
|
def write_pair(prof_data, depth, allow, name, prefix, sep, tail, fn):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
ref, allow = set_ref_allow(prof_data, allow)
|
||||||
|
|
||||||
|
if ref.get(name, False):
|
||||||
|
for key in sorted(ref[name].keys()):
|
||||||
|
value = fn(ref[name][key]) # eval('%s(%s)' % (fn, ref[name][key]))
|
||||||
|
data.append('%s%s%s%s%s%s%s' % (pre, allow, prefix, key, sep, value, tail))
|
||||||
|
if ref[name].keys():
|
||||||
|
data.append('')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_alias(prof_data, depth):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
|
||||||
|
if prof_data['alias']:
|
||||||
|
for key in sorted(prof_data['alias'].keys()):
|
||||||
|
data.append('%salias %s -> %s,' % (pre, quote_if_needed(key), quote_if_needed(prof_data['alias'][key])))
|
||||||
|
|
||||||
|
data.append('')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_includes(prof_data, depth):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
|
||||||
|
for key in sorted(prof_data['include'].keys()):
|
||||||
|
if key.startswith('/'):
|
||||||
|
qkey = '"%s"' % key
|
||||||
|
else:
|
||||||
|
qkey = '<%s>' % quote_if_needed(key)
|
||||||
|
|
||||||
|
data.append('%s#include %s' % (pre, qkey))
|
||||||
|
|
||||||
|
if data:
|
||||||
|
data.append('')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def var_transform(ref):
|
||||||
|
data = []
|
||||||
|
for value in ref:
|
||||||
|
if not value:
|
||||||
|
value = '""'
|
||||||
|
data.append(quote_if_needed(value))
|
||||||
|
return ' '.join(data)
|
||||||
|
|
||||||
|
def write_list_vars(prof_data, depth):
|
||||||
|
return write_pair(prof_data, depth, '', 'lvar', '', ' = ', '', var_transform)
|
||||||
|
|
||||||
|
def write_link_rules(prof_data, depth, allow):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
allowstr = set_allow_str(allow)
|
||||||
|
|
||||||
|
if prof_data[allow].get('link', False):
|
||||||
|
for path in sorted(prof_data[allow]['link'].keys()):
|
||||||
|
to_name = prof_data[allow]['link'][path]['to']
|
||||||
|
subset = ''
|
||||||
|
if prof_data[allow]['link'][path]['mode'] & AA_LINK_SUBSET:
|
||||||
|
subset = 'subset '
|
||||||
|
audit = ''
|
||||||
|
if prof_data[allow]['link'][path].get('audit', False):
|
||||||
|
audit = 'audit '
|
||||||
|
path = quote_if_needed(path)
|
||||||
|
to_name = quote_if_needed(to_name)
|
||||||
|
data.append('%s%s%slink %s%s -> %s,' % (pre, audit, allowstr, subset, path, to_name))
|
||||||
|
data.append('')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_links(prof_data, depth):
|
||||||
|
data = write_link_rules(prof_data, depth, 'deny')
|
||||||
|
data += write_link_rules(prof_data, depth, 'allow')
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_mount_rules(prof_data, depth, allow):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# no mount rules, so return
|
||||||
|
if not prof_data[allow].get('mount', False):
|
||||||
|
return data
|
||||||
|
|
||||||
|
for mount_rule in prof_data[allow]['mount']:
|
||||||
|
data.append('%s%s' % (pre, mount_rule.serialize()))
|
||||||
|
data.append('')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_mount(prof_data, depth):
|
||||||
|
data = write_mount_rules(prof_data, depth, 'deny')
|
||||||
|
data += write_mount_rules(prof_data, depth, 'allow')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_pivot_root_rules(prof_data, depth, allow):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# no pivot_root rules, so return
|
||||||
|
if not prof_data[allow].get('pivot_root', False):
|
||||||
|
return data
|
||||||
|
|
||||||
|
for pivot_root_rule in prof_data[allow]['pivot_root']:
|
||||||
|
data.append('%s%s' % (pre, pivot_root_rule.serialize()))
|
||||||
|
data.append('')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_pivot_root(prof_data, depth):
|
||||||
|
data = write_pivot_root_rules(prof_data, depth, 'deny')
|
||||||
|
data += write_pivot_root_rules(prof_data, depth, 'allow')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_unix(prof_data, depth):
|
||||||
|
data = write_unix_rules(prof_data, depth, 'deny')
|
||||||
|
data += write_unix_rules(prof_data, depth, 'allow')
|
||||||
|
return data
|
||||||
|
|
||||||
|
def write_unix_rules(prof_data, depth, allow):
|
||||||
|
pre = ' ' * depth
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# no unix rules, so return
|
||||||
|
if not prof_data[allow].get('unix', False):
|
||||||
|
return data
|
||||||
|
|
||||||
|
for unix_rule in prof_data[allow]['unix']:
|
||||||
|
data.append('%s%s' % (pre, unix_rule.serialize()))
|
||||||
|
data.append('')
|
||||||
|
return data
|
||||||
|
@@ -21,7 +21,7 @@ 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,
|
from apparmor.aa import (check_for_apparmor, get_output, get_reqs, get_interpreter_and_abstraction, create_new_profile,
|
||||||
get_profile_flags, set_profile_flags, set_options_audit_mode, set_options_owner_mode, is_skippable_file, is_skippable_dir,
|
get_profile_flags, set_profile_flags, set_options_audit_mode, set_options_owner_mode, is_skippable_file, is_skippable_dir,
|
||||||
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header,
|
parse_profile_start, parse_profile_data, separate_vars, store_list_var, write_header,
|
||||||
var_transform, serialize_parse_profile_start, get_file_perms, propose_file_rules)
|
serialize_parse_profile_start, get_file_perms, propose_file_rules)
|
||||||
from apparmor.aare import AARE
|
from apparmor.aare import AARE
|
||||||
from apparmor.common import AppArmorException, AppArmorBug
|
from apparmor.common import AppArmorException, AppArmorBug
|
||||||
from apparmor.rule.file import FileRule
|
from apparmor.rule.file import FileRule
|
||||||
@@ -677,17 +677,6 @@ class AaTest_write_header(AATest):
|
|||||||
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
|
result = write_header(prof_data, depth, name, embedded_hat, write_flags)
|
||||||
self.assertEqual(result, [expected])
|
self.assertEqual(result, [expected])
|
||||||
|
|
||||||
class AaTest_var_transform(AATest):
|
|
||||||
tests = [
|
|
||||||
(['foo', ''], 'foo ""' ),
|
|
||||||
(['foo', 'bar'], 'foo bar' ),
|
|
||||||
([''], '""' ),
|
|
||||||
(['bar baz', 'foo'], '"bar baz" foo' ),
|
|
||||||
]
|
|
||||||
|
|
||||||
def _run_test(self, params, expected):
|
|
||||||
self.assertEqual(var_transform(params), expected)
|
|
||||||
|
|
||||||
class AaTest_serialize_parse_profile_start(AATest):
|
class AaTest_serialize_parse_profile_start(AATest):
|
||||||
def _parse(self, line, profile, hat, prof_data_profile, prof_data_external):
|
def _parse(self, line, profile, hat, prof_data_profile, prof_data_external):
|
||||||
# 'correct' is always True in the code that uses serialize_parse_profile_start() (set some lines above the function call)
|
# 'correct' is always True in the code that uses serialize_parse_profile_start() (set some lines above the function call)
|
||||||
|
@@ -13,7 +13,7 @@ import unittest
|
|||||||
from common_test import AATest, setup_all_loops
|
from common_test import AATest, setup_all_loops
|
||||||
|
|
||||||
from apparmor.common import AppArmorBug
|
from apparmor.common import AppArmorBug
|
||||||
from apparmor.profile_storage import ProfileStorage
|
from apparmor.profile_storage import ProfileStorage, var_transform
|
||||||
|
|
||||||
class TestUnknownKey(AATest):
|
class TestUnknownKey(AATest):
|
||||||
def AASetup(self):
|
def AASetup(self):
|
||||||
@@ -35,6 +35,17 @@ class TestUnknownKey(AATest):
|
|||||||
with self.assertRaises(AppArmorBug):
|
with self.assertRaises(AppArmorBug):
|
||||||
self.storage['foo'] = 'bar'
|
self.storage['foo'] = 'bar'
|
||||||
|
|
||||||
|
class AaTest_var_transform(AATest):
|
||||||
|
tests = [
|
||||||
|
(['foo', ''], 'foo ""' ),
|
||||||
|
(['foo', 'bar'], 'foo bar' ),
|
||||||
|
([''], '""' ),
|
||||||
|
(['bar baz', 'foo'], '"bar baz" foo' ),
|
||||||
|
]
|
||||||
|
|
||||||
|
def _run_test(self, params, expected):
|
||||||
|
self.assertEqual(var_transform(params), expected)
|
||||||
|
|
||||||
|
|
||||||
setup_all_loops(__name__)
|
setup_all_loops(__name__)
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Reference in New Issue
Block a user