[apparmor] [patch] [2/3] Make ProfileStorage a class

Christian Boltz apparmor at cboltz.de
Sun Jul 9 19:32:55 UTC 2017


Hello,

this patch moves ProfileStorage() from aa.py to the new profile_storage.py 
and makes it a class. The variable name in __init__() changes (profile 
-> self.data), but the content stays the same.

The ProfileStorage class acts like a dict(), but has some additional
checks for unknown keys in place.

Also add some tests to make sure unknown keys really raise an exception.


[ 03-ProfileStorage-class.diff ]

=== modified file ./utils/apparmor/aa.py
--- utils/apparmor/aa.py        2017-07-09 17:33:29.044202432 +0200
+++ utils/apparmor/aa.py        2017-07-09 16:33:42.196851342 +0200
@@ -49,19 +49,21 @@
                             RE_PROFILE_UNIX, RE_RULE_HAS_COMMA, RE_HAS_COMMENT_SPLIT,
                             strip_quotes, parse_profile_start_line, re_match_include )
 
+from apparmor.profile_storage import ProfileStorage
+
 import apparmor.rules as aarules
 
-from apparmor.rule.capability import CapabilityRuleset, CapabilityRule
-from apparmor.rule.change_profile import ChangeProfileRuleset, ChangeProfileRule
-from apparmor.rule.dbus       import DbusRuleset,       DbusRule
-from apparmor.rule.file       import FileRuleset,       FileRule
-from apparmor.rule.network    import NetworkRuleset,    NetworkRule
-from apparmor.rule.ptrace     import PtraceRuleset,    PtraceRule
-from apparmor.rule.rlimit     import RlimitRuleset,    RlimitRule
-from apparmor.rule.signal     import SignalRuleset,    SignalRule
+from apparmor.rule.capability       import CapabilityRule
+from apparmor.rule.change_profile   import ChangeProfileRule
+from apparmor.rule.dbus             import DbusRule
+from apparmor.rule.file             import FileRule
+from apparmor.rule.network          import NetworkRule
+from apparmor.rule.ptrace           import PtraceRule
+from apparmor.rule.rlimit           import RlimitRule
+from apparmor.rule.signal           import SignalRule
 from apparmor.rule import quote_if_needed
 
 ruletypes = ['capability', 'change_profile', 'dbus', 'file', 'network', 'ptrace', 'rlimit', 'signal']
 
 # setup module translations
 _ = init_translation()
@@ -428,60 +430,6 @@
         return {local_profile: extras[local_profile]}
     return dict()
 
-def ProfileStorage(profilename, hat, calledby):
-    # keys used in aa[profile][hat]:
-    # a) rules (as dict): alias, include, lvar
-    # b) rules (as hasher): allow, deny
-    # c) one for each rule class
-    # d) other: external, flags, name, profile, attachment, initial_comment, filename, info,
-    #           profile_keyword, header_comment (these two are currently only set by set_profile_flags())
-
-    profile = dict()
-
-    # profile['info'] isn't used anywhere, but can be helpful in debugging.
-    profile['info'] = {'profile': profilename, 'hat': hat, 'calledby': calledby}
-
-    profile['capability']       = CapabilityRuleset()
-    profile['dbus']             = DbusRuleset()
-    profile['file']             = FileRuleset()
-    profile['change_profile']   = ChangeProfileRuleset()
-    profile['network']          = NetworkRuleset()
-    profile['ptrace']           = PtraceRuleset()
-    profile['rlimit']           = RlimitRuleset()
-    profile['signal']           = SignalRuleset()
-
-    profile['alias']            = dict()
-    profile['include']          = dict()
-    profile['localinclude']     = dict()
-    profile['repo']             = dict()
-    profile['lvar']             = dict()
-
-    profile['filename']         = ''
-    profile['name']             = ''
-    profile['attachment']       = ''
-    profile['flags']            = ''
-    profile['external']         = False
-    profile['header_comment']   = ''
-    profile['initial_comment']  = ''
-    profile['profile_keyword']  = False
-    profile['profile']          = False  # profile or hat?
-
-    profile['allow'] = dict()
-    profile['deny'] = dict()
-
-    profile['allow']['link']    = hasher()
-    profile['deny']['link']     = hasher()
-
-    # mount, pivot_root, unix have a .get() fallback to list() - initialize them nevertheless
-    profile['allow']['mount']   = list()
-    profile['deny']['mount']    = list()
-    profile['allow']['pivot_root'] = list()
-    profile['deny']['pivot_root']  = list()
-    profile['allow']['unix']    = list()
-    profile['deny']['unix']     = list()
-
-    return profile
-
 def create_new_profile(localfile, is_stub=False):
     local_profile = hasher()
     local_profile[localfile] = ProfileStorage('NEW', localfile, 'create_new_profile()')
=== modified file ./utils/apparmor/profile_storage.py
--- utils/apparmor/profile_storage.py   2017-07-09 18:27:33.212757482 +0200
+++ utils/apparmor/profile_storage.py   2017-07-09 18:05:30.321558068 +0200
@@ -1 +1,102 @@
+# ----------------------------------------------------------------------
+#    Copyright (C) 2013 Kshitij Gupta <kgupta8592 at gmail.com>
+#    Copyright (C) 2014-2017 Christian Boltz <apparmor at 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
+#    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.
+#
+# ----------------------------------------------------------------------
+
+
+from apparmor.common import AppArmorBug, hasher
+
+from apparmor.rule.capability       import CapabilityRuleset
+from apparmor.rule.change_profile   import ChangeProfileRuleset
+from apparmor.rule.dbus             import DbusRuleset
+from apparmor.rule.file             import FileRuleset
+from apparmor.rule.network          import NetworkRuleset
+from apparmor.rule.ptrace           import PtraceRuleset
+from apparmor.rule.rlimit           import RlimitRuleset
+from apparmor.rule.signal           import SignalRuleset
+
+class ProfileStorage:
+    '''class to store the content (header, rules, comments) of a profilename
+
+       Acts like a dict(), but has some additional checks.
+     '''
+
+    def __init__(self, profilename, hat, calledby):
+        data = dict()
+
+        # self.data['info'] isn't used anywhere, but can be helpful in debugging.
+        data['info'] = {'profile': profilename, 'hat': hat, 'calledby': calledby}
+
+        data['capability']       = CapabilityRuleset()
+        data['dbus']             = DbusRuleset()
+        data['file']             = FileRuleset()
+        data['change_profile']   = ChangeProfileRuleset()
+        data['network']          = NetworkRuleset()
+        data['ptrace']           = PtraceRuleset()
+        data['rlimit']           = RlimitRuleset()
+        data['signal']           = SignalRuleset()
+
+        data['alias']            = dict()
+        data['include']          = dict()
+        data['localinclude']     = dict()
+        data['lvar']             = dict()
+        data['repo']             = dict()
+
+        data['filename']         = ''
+        data['name']             = ''
+        data['attachment']       = ''
+        data['flags']            = ''
+        data['external']         = False
+        data['header_comment']   = ''  # currently only set by set_profile_flags()
+        data['initial_comment']  = ''
+        data['profile_keyword']  = False  # currently only set by set_profile_flags()
+        data['profile']          = False  # profile or hat?
+
+        data['allow'] = dict()
+        data['deny'] = dict()
+
+        data['allow']['link']    = hasher()
+        data['deny']['link']     = hasher()
+
+        # mount, pivot_root, unix have a .get() fallback to list() - initialize them nevertheless
+        data['allow']['mount']   = list()
+        data['deny']['mount']    = list()
+        data['allow']['pivot_root'] = list()
+        data['deny']['pivot_root']  = list()
+        data['allow']['unix']    = list()
+        data['deny']['unix']     = list()
+
+        self.data = data
+
+    def __getitem__(self, key):
+        if key in self.data:
+            return self.data[key]
+        else:
+            raise AppArmorBug('attemp to read unknown key %s' % key)
+
+    def __setitem__(self, key, value):
+        # TODO: Most of the keys (containing *Ruleset, dict(), list() or hasher()) should be read-only.
+        #       Their content needs to be changed, but the container shouldn't
+        #       Note: serialize_profile_from_old_profile.write_prior_segments() and write_prior_segments() expect the container to be writeable!
+        # TODO: check if value has the expected type
+        if key in self.data:
+            self.data[key] = value
+        else:
+            raise AppArmorBug('attemp to set unknown key %s' % key)
+
+    def get(self, key, fallback=None):
+        if key in self.data:
+            return self.data.get(key, fallback)
+        else:
+            raise AppArmorBug('attemp to read unknown key %s' % key)

--- utils/test/test-profile-storage.py  2017-07-09 18:29:04.376428223 +0200
+++ utils/test/test-profile-storage.py  2017-07-09 18:30:59.520012608 +0200
@@ -0,0 +1,41 @@
+#! /usr/bin/python3
+# ------------------------------------------------------------------
+#
+#    Copyright (C) 2017 Christian Boltz <apparmor at 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
+#    License published by the Free Software Foundation.
+#
+# ------------------------------------------------------------------
+
+import unittest
+from common_test import AATest, setup_all_loops
+
+from apparmor.common import AppArmorBug
+from apparmor.profile_storage import ProfileStorage
+
+class TestUnknownKey(AATest):
+    def AASetup(self):
+        self.storage = ProfileStorage('/test/foo', 'hat', 'TEST')
+
+    def test_read(self):
+        with self.assertRaises(AppArmorBug):
+            self.storage['foo']
+
+    def test_get(self):
+        with self.assertRaises(AppArmorBug):
+            self.storage.get('foo')
+
+    def test_get_with_fallback(self):
+        with self.assertRaises(AppArmorBug):
+            self.storage.get('foo', 'bar')
+
+    def test_set(self):
+        with self.assertRaises(AppArmorBug):
+            self.storage['foo'] = 'bar'
+
+
+setup_all_loops(__name__)
+if __name__ == '__main__':
+    unittest.main(verbosity=2)


Regards,

Christian Boltz
-- 
A pair of extra monkeys under Coolo's charge would probably help
more... It's clear to us that Coolo's days have now 36 hours...
[Nelson Marques in opensuse-factory]
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: This is a digitally signed message part.
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20170709/2f97dba4/attachment-0001.pgp>


More information about the AppArmor mailing list