mirror of
https://gitlab.com/apparmor/apparmor
synced 2025-08-22 10:07:12 +00:00
Add basic support for the priority rules prefix. This patch does not allow the utils to set or suggest priorities. It allows parsing and retaining of the priority prefix if it already exists on rules and checking if it's in the supported range. Signed-off-by: Georgia Garcia <georgia.garcia@canonical.com> Signed-off-by: John Johansen <john.johansen@canonical.com>
179 lines
6.3 KiB
Python
179 lines
6.3 KiB
Python
# ----------------------------------------------------------------------
|
|
# Copyright (C) 2013 Kshitij Gupta <kgupta8592@gmail.com>
|
|
# Copyright (C) 2020 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.
|
|
#
|
|
# ----------------------------------------------------------------------
|
|
|
|
import re
|
|
|
|
from apparmor.common import AppArmorBug, AppArmorException
|
|
from apparmor.regex import RE_PROFILE_VARIABLE, strip_quotes
|
|
from apparmor.rule import BaseRule, BaseRuleset, parse_comment, quote_if_needed
|
|
from apparmor.translations import init_translation
|
|
|
|
_ = init_translation()
|
|
|
|
|
|
class VariableRule(BaseRule):
|
|
"""Class to handle and store a single variable rule"""
|
|
|
|
rule_name = 'variable'
|
|
_match_re = RE_PROFILE_VARIABLE
|
|
|
|
def __init__(self, varname, mode, values, audit=False, deny=False, allow_keyword=False,
|
|
comment='', log_event=None, priority=None):
|
|
|
|
super().__init__(audit=audit, deny=deny, allow_keyword=allow_keyword,
|
|
comment=comment, log_event=log_event, priority=priority)
|
|
|
|
# variables don't support priority, allow keyword, audit or deny
|
|
self.ensure_modifiers_not_supported()
|
|
|
|
if not isinstance(varname, str):
|
|
raise AppArmorBug('Passed unknown type for varname to %s: %s' % (self.__class__.__name__, varname))
|
|
if not varname.startswith('@{'):
|
|
raise AppArmorException("Passed invalid varname to %s (doesn't start with '@{'): %s" % (self.__class__.__name__, varname))
|
|
if not varname.endswith('}'):
|
|
raise AppArmorException("Passed invalid varname to %s (doesn't end with '}'): %s" % (self.__class__.__name__, varname))
|
|
|
|
if not isinstance(mode, str):
|
|
raise AppArmorBug('Passed unknown type for variable assignment mode to %s: %s' % (self.__class__.__name__, mode))
|
|
if mode not in ('=', '+='):
|
|
raise AppArmorBug('Passed unknown variable assignment mode to %s: %s' % (self.__class__.__name__, mode))
|
|
|
|
if not isinstance(values, set):
|
|
raise AppArmorBug('Passed unknown type for values to %s: %s' % (self.__class__.__name__, values))
|
|
if not values:
|
|
raise AppArmorException('Passed empty list of values to %s: %s' % (self.__class__.__name__, values))
|
|
|
|
self.varname = varname
|
|
self.mode = mode
|
|
self.values = values
|
|
|
|
@classmethod
|
|
def _create_instance(cls, raw_rule, matches):
|
|
"""parse raw_rule and return instance of this class"""
|
|
|
|
comment = parse_comment(matches)
|
|
|
|
varname = matches.group('varname')
|
|
mode = matches.group('mode')
|
|
values = separate_vars(matches.group('values'))
|
|
|
|
return cls(varname, mode, values,
|
|
audit=False, deny=False, allow_keyword=False, comment=comment, priority=None)
|
|
|
|
def get_clean(self, depth=0):
|
|
"""return rule (in clean/default formatting)"""
|
|
|
|
space = ' ' * depth
|
|
|
|
data = []
|
|
for value in sorted(self.values):
|
|
if not value:
|
|
value = '""'
|
|
data.append(quote_if_needed(value))
|
|
|
|
return '%s%s %s %s' % (space, self.varname, self.mode, ' '.join(data))
|
|
|
|
def _is_covered_localvars(self, other_rule):
|
|
"""check if other_rule is covered by this rule object"""
|
|
|
|
if self.varname != other_rule.varname:
|
|
return False
|
|
|
|
if self.mode != other_rule.mode:
|
|
return False
|
|
|
|
if not self._is_covered_list(self.values, None, set(other_rule.values), None, 'values'):
|
|
return False
|
|
|
|
# still here? -> then it is covered
|
|
return True
|
|
|
|
def _is_equal_localvars(self, rule_obj, strict):
|
|
"""compare if rule-specific variables are equal"""
|
|
|
|
if self.varname != rule_obj.varname:
|
|
return False
|
|
|
|
if self.mode != rule_obj.mode:
|
|
return False
|
|
|
|
if self.values != rule_obj.values:
|
|
return False
|
|
|
|
return True
|
|
|
|
def _logprof_header_localvars(self):
|
|
return _('Variable'), self.get_clean()
|
|
|
|
|
|
class VariableRuleset(BaseRuleset):
|
|
"""Class to handle and store a collection of variable rules"""
|
|
|
|
def add(self, rule, cleanup=False):
|
|
"""Add variable rule object
|
|
|
|
If the variable name is already known, raise an exception because re-defining a variable isn't allowed.
|
|
"""
|
|
|
|
if rule.mode == '=':
|
|
for knownrule in self.rules:
|
|
if rule.varname == knownrule.varname:
|
|
raise AppArmorException(
|
|
_('Redefining existing variable %(variable)s: %(value)s')
|
|
% {'variable': rule.varname, 'value': rule.values})
|
|
|
|
super().add(rule, cleanup)
|
|
|
|
def get_merged_variables(self):
|
|
"""Get merged variables of this object.
|
|
|
|
Note that no error checking is done because variables can be defined in one file and extended in another.
|
|
"""
|
|
|
|
var_set = {}
|
|
var_add = {}
|
|
|
|
for rule in self.rules:
|
|
if rule.mode == '=':
|
|
var_set[rule.varname] = rule.values # blindly set, add() prevents redefinition of variables
|
|
else:
|
|
if not var_add.get(rule.varname):
|
|
var_add[rule.varname] = rule.values
|
|
else:
|
|
var_add[rule.varname] |= rule.values
|
|
|
|
return {'=': var_set, '+=': var_add}
|
|
|
|
|
|
def separate_vars(vs):
|
|
"""Returns a list of all the values for a variable"""
|
|
data = set()
|
|
vs = vs.strip()
|
|
|
|
re_vars = re.compile(r'^(("[^"]*")|([^"\s]+))\s*(.*)$')
|
|
while re_vars.search(vs):
|
|
matches = re_vars.search(vs).groups()
|
|
|
|
if matches[0].endswith(','):
|
|
raise AppArmorException(_('Variable declarations do not accept trailing commas'))
|
|
|
|
data.add(strip_quotes(matches[0]))
|
|
vs = matches[3].strip()
|
|
|
|
if vs:
|
|
raise AppArmorException('Variable assignments contains invalid parts (unbalanced quotes?): %s' % vs)
|
|
|
|
return data
|