From: Goldwyn Rodrigues <rgold...@suse.com> A simple utility to return the keywords used in apparmor.d profile files.
This would enable utilities such as yast to create apparmor profiles without the need to cross-checking and verifying the syntax. While there is nothing fancy about the tool, if you think this needs more command-line arguments, I will be happy to put them in. Signed-off-by: Goldwyn Rodrigues <rgold...@suse.com> --- utils/aa-keywords | 31 ++++++++++++++++++++ utils/apparmor/rule/file.py | 65 +++++++++++++++++++++++++++++++++--------- utils/apparmor/rule/network.py | 8 ++++++ utils/apparmor/rule/ptrace.py | 4 +++ utils/apparmor/rule/rlimit.py | 12 ++++++++ utils/apparmor/rule/signal.py | 6 ++++ 6 files changed, 113 insertions(+), 13 deletions(-) create mode 100644 utils/aa-keywords diff --git a/utils/aa-keywords b/utils/aa-keywords new file mode 100644 index 0000000..3e22910 --- /dev/null +++ b/utils/aa-keywords @@ -0,0 +1,31 @@ +#! /usr/bin/python3 +# ---------------------------------------------------------------------- +# Copyright (C) 2017 SUSE Linux Products +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of version 2 of the GNU General Public +# License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# ---------------------------------------------------------------------- + +import sys,json + +from apparmor.rule.file import FileRule +from apparmor.rule.rlimit import RlimitRule +from apparmor.rule.signal import SignalRule +from apparmor.rule.ptrace import PtraceRule +from apparmor.rule.network import NetworkRule + +jsonout = { 'file': FileRule.keywords(), + 'network': NetworkRule.keywords(), + 'rlimit': RlimitRule.keywords(), + 'signal': SignalRule.keywords(), + 'ptrace': PtraceRule.keywords() + } +sys.stdout.write(json.dumps(jsonout, sort_keys=False, separators=(',', ': ')) + '\n') + diff --git a/utils/apparmor/rule/file.py b/utils/apparmor/rule/file.py index 86270fd..8b8aaca 100644 --- a/utils/apparmor/rule/file.py +++ b/utils/apparmor/rule/file.py @@ -21,13 +21,41 @@ from apparmor.rule import BaseRule, BaseRuleset, check_and_split_list, logprof_v from apparmor.translations import init_translation _ = init_translation() - -allow_exec_transitions = ('ix', 'ux', 'Ux', 'px', 'Px', 'cx', 'Cx') # 2 chars - len relevant for split_perms() -allow_exec_fallback_transitions = ('pix', 'Pix', 'cix', 'Cix', 'pux', 'PUx', 'cux', 'CUx') # 3 chars - len relevant for split_perms() -deny_exec_transitions = ('x') -file_permissions = ('m', 'r', 'w', 'a', 'l', 'k') # also defines the write order - - +allow_exec_transitions = { + 'ix': 'inhert execute', + 'ux': 'unconfined execute', + 'Ux': 'unconfined execute - scrub environment', + 'px': 'discrete profile execute', + 'Px': 'discrete profile execute - scrub environment', + 'cx': 'transition to subprofile execute', + 'Cx': 'transition to subprofile execute - scrub environment' +} # 2 chars - len relevant for split_perms() + +allow_exec_fallback_transitions = { + 'pix': 'discrete profile execute with inherit fallback', + 'Pix': 'discrete profile execute with inherit fallback - scrub the environment', + 'cix': 'transition to subprofile on execute with inherit fallback', + 'Cix': 'transition to subprofile on execute with inherit fallback - scrub the environment', + 'pux': 'discrete profile execute with fallback to unconfined', + 'PUx': 'discrete profile execute with fallback to unconfined - scrub the environment', + 'cux': 'transition to subprofile on execute with fallback to unconfirmed', + 'CUx': 'transition to subprofile on execute with fallback to unconfirmed - scrub the environment' +} # 3 chars - len relevant for split_perms() + +deny_exec_transitions = { + 'x': 'Execute' +} + +file_permissions = { + 'm': 'allow PROTO_EXEC with mmap calls', + 'r': 'read mode', + 'w': 'write mode', + 'a': 'append mode', + 'l': 'link', + 'k': 'lock' +} # also defines the write order + +#file_permissions = ('m', 'r', 'w', 'a', 'l', 'k') class FileRule(BaseRule): '''Class to handle and store a single file rule''' @@ -76,7 +104,7 @@ class FileRule(BaseRule): elif perms == None: perms = set() - self.perms, self.all_perms, unknown_items = check_and_split_list(perms, file_permissions, FileRule.ALL, 'FileRule', 'permissions', allow_empty_list=True) + self.perms, self.all_perms, unknown_items = check_and_split_list(perms, tuple(file_permissions.keys()), FileRule.ALL, 'FileRule', 'permissions', allow_empty_list=True) if unknown_items: raise AppArmorBug('Passed unknown perms to FileRule: %s' % str(unknown_items)) if self.perms and 'a' in self.perms and 'w' in self.perms: @@ -95,7 +123,7 @@ class FileRule(BaseRule): else: if exec_perms == 'x': raise AppArmorException(_("Execute flag ('x') in file rule must specify the exec mode (ix, Px, Cx etc.)")) - elif exec_perms not in allow_exec_transitions and exec_perms not in allow_exec_fallback_transitions: + elif exec_perms not in tuple(allow_exec_transitions.keys()) and exec_perms not in tuple(allow_exec_fallback_transitions.keys()): raise AppArmorBug('Unknown execute mode specified in file rule: %s' % exec_perms) self.exec_perms = exec_perms else: @@ -169,6 +197,17 @@ class FileRule(BaseRule): return FileRule(path, perms, exec_perms, target, owner, file_keyword, leading_perms, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + @staticmethod + def keywords(): + ''' return a list of valid keywords ''' + kw = {'Extra': 'deny', + 'Exec Transitions': allow_exec_transitions, + 'Exec Fallback Transitions': allow_exec_fallback_transitions, + 'Deny Exec Transitions': deny_exec_transitions, + 'File Permissions': file_permissions + } + return kw + def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' @@ -224,7 +263,7 @@ class FileRule(BaseRule): def _join_given_perms(self, perms, exec_perms): '''return the permissions as string (using the perms and exec_perms given as parameter)''' perm_string = '' - for perm in file_permissions: + for perm in tuple(file_permissions.keys()): if perm in perms: perm_string = perm_string + perm @@ -511,7 +550,7 @@ def split_perms(perm_string, deny): exec_mode = None while perm_string: - if perm_string[0] in file_permissions: + if perm_string[0] in tuple(file_permissions.keys()): perms.add(perm_string[0]) perm_string = perm_string[1:] elif perm_string[0] == 'x': @@ -519,12 +558,12 @@ def split_perms(perm_string, deny): raise AppArmorException(_("'x' must be preceded by an exec qualifier (i, P, C or U)")) exec_mode = 'x' perm_string = perm_string[1:] - elif perm_string.startswith(allow_exec_transitions): + elif perm_string.startswith(tuple(allow_exec_transitions.keys())): if exec_mode and exec_mode != perm_string[0:2]: raise AppArmorException(_('conflicting execute permissions found: %s and %s' % (exec_mode, perm_string[0:2]))) exec_mode = perm_string[0:2] perm_string = perm_string[2:] - elif perm_string.startswith(allow_exec_fallback_transitions): + elif perm_string.startswith(tuple(allow_exec_fallback_transitions.keys())): if exec_mode and exec_mode != perm_string[0:3]: raise AppArmorException(_('conflicting execute permissions found: %s and %s' % (exec_mode, perm_string[0:3]))) exec_mode = perm_string[0:3] diff --git a/utils/apparmor/rule/network.py b/utils/apparmor/rule/network.py index d1cc69b..90424c1 100644 --- a/utils/apparmor/rule/network.py +++ b/utils/apparmor/rule/network.py @@ -129,6 +129,14 @@ class NetworkRule(BaseRule): return NetworkRule(domain, type_or_protocol, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + @staticmethod + def keywords(): + kw = {'Domain': network_domain_keywords, + 'Type': network_type_keywords, + 'Protocol': network_protocol_keywords + } + return kw + def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' diff --git a/utils/apparmor/rule/ptrace.py b/utils/apparmor/rule/ptrace.py index a82d06a..be93379 100644 --- a/utils/apparmor/rule/ptrace.py +++ b/utils/apparmor/rule/ptrace.py @@ -109,6 +109,10 @@ class PtraceRule(BaseRule): return PtraceRule(access, peer, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + @staticmethod + def keywords(): + return {'words': access_keywords} + def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' diff --git a/utils/apparmor/rule/rlimit.py b/utils/apparmor/rule/rlimit.py index 2a2f5cd..81dc5c0 100644 --- a/utils/apparmor/rule/rlimit.py +++ b/utils/apparmor/rule/rlimit.py @@ -136,6 +136,18 @@ class RlimitRule(BaseRule): return RlimitRule(rlimit, value, comment=comment) + @staticmethod + def keywords(): + ''' return the valid keywords ''' + sz = {'size': rlimit_size, 'suffix': 'K, M, G'} + nice = {'nice': rlimit_size, 'range_min': -20, 'range_max': 19} + numbers = {'numbers': rlimit_number} + time = {'time': rlimit_time, 'suffix': 'ms, us' } + return {'size': sz, + 'number': numbers, + 'nice': nice, + 'time': time} + def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' diff --git a/utils/apparmor/rule/signal.py b/utils/apparmor/rule/signal.py index e37fec8..4781a34 100644 --- a/utils/apparmor/rule/signal.py +++ b/utils/apparmor/rule/signal.py @@ -147,6 +147,12 @@ class SignalRule(BaseRule): return SignalRule(access, signal, peer, audit=audit, deny=deny, allow_keyword=allow_keyword, comment=comment) + @staticmethod + def keywords(): + return {'access': access_keywords, + 'signals': signal_keywords + } + def get_clean(self, depth=0): '''return rule (in clean/default formatting)''' -- 2.10.2 -- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor