[apparmor] [PATCH] aa-keywords: Expose parser keywords
Goldwyn Rodrigues
rgoldwyn at suse.de
Tue Feb 28 02:39:40 UTC 2017
From: Goldwyn Rodrigues <rgoldwyn at 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 <rgoldwyn at 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
More information about the AppArmor
mailing list