2
0
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:
Christian Boltz
2017-07-11 13:32:33 +02:00
parent cf86c9252d
commit 325ff61910
3 changed files with 152 additions and 62 deletions

View File

@@ -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, 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
import apparmor.rules as aarules import apparmor.rules as aarules
from apparmor.rule.capability import CapabilityRuleset, CapabilityRule from apparmor.rule.capability import CapabilityRule
from apparmor.rule.change_profile import ChangeProfileRuleset, ChangeProfileRule from apparmor.rule.change_profile import ChangeProfileRule
from apparmor.rule.dbus import DbusRuleset, DbusRule from apparmor.rule.dbus import DbusRule
from apparmor.rule.file import FileRuleset, FileRule from apparmor.rule.file import FileRule
from apparmor.rule.network import NetworkRuleset, NetworkRule from apparmor.rule.network import NetworkRule
from apparmor.rule.ptrace import PtraceRuleset, PtraceRule from apparmor.rule.ptrace import PtraceRule
from apparmor.rule.rlimit import RlimitRuleset, RlimitRule from apparmor.rule.rlimit import RlimitRule
from apparmor.rule.signal import SignalRuleset, SignalRule from apparmor.rule.signal import SignalRule
from apparmor.rule import quote_if_needed from apparmor.rule import quote_if_needed
ruletypes = ['capability', 'change_profile', 'dbus', 'file', 'network', 'ptrace', 'rlimit', 'signal'] 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 {local_profile: extras[local_profile]}
return dict() 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): def create_new_profile(localfile, is_stub=False):
local_profile = hasher() local_profile = hasher()
local_profile[localfile] = ProfileStorage('NEW', localfile, 'create_new_profile()') local_profile[localfile] = ProfileStorage('NEW', localfile, 'create_new_profile()')

View 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)

View 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)