mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-09-01 14:55:10 +00:00
[2/3] Make ProfileStorage a class
Move ProfileStorage() from aa.py to the new profile_storage.py and make 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. Acked-by: Seth Arnold <seth.arnold@canonical.com>
This commit is contained in:
@@ -49,16 +49,18 @@ from apparmor.regex import (RE_PROFILE_START, RE_PROFILE_END, RE_PROFILE_LINK,
|
||||
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']
|
||||
@@ -426,60 +428,6 @@ def get_inactive_profile(local_profile):
|
||||
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()')
|
||||
|
101
utils/apparmor/profile_storage.py
Normal file
101
utils/apparmor/profile_storage.py
Normal file
@@ -0,0 +1,101 @@
|
||||
# ----------------------------------------------------------------------
|
||||
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
||||
# Copyright (C) 2014-2017 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
|
||||
# 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('attempt 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('attempt 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('attempt to read unknown key %s' % key)
|
41
utils/test/test-profile-storage.py
Normal file
41
utils/test/test-profile-storage.py
Normal file
@@ -0,0 +1,41 @@
|
||||
#! /usr/bin/python3
|
||||
# ------------------------------------------------------------------
|
||||
#
|
||||
# Copyright (C) 2017 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
|
||||
# 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)
|
Reference in New Issue
Block a user