On 12/08/2015 11:31 AM, Christian Boltz wrote: > Hello, > > $subject. > > Those classes will be used to parse and handle ptrace rules. > They understand the syntax of ptrace rules. > > Note that get_clean() doesn't output superfluos things, so > ptrace ( trace ), > will become > ptrace trace, > > Acked-by: John Johansen <[email protected]>
> > [ 29-add-PtraceRule.diff ] > > === modified file ./utils/apparmor/rule/ptrace.py > --- utils/apparmor/rule/ptrace.py 2015-12-08 20:14:58.843940439 +0100 > +++ utils/apparmor/rule/ptrace.py 2015-11-29 22:03:44.870796517 +0100 > @@ -0,0 +1,210 @@ > +# ---------------------------------------------------------------------- > +# Copyright (C) 2015 Christian Boltz <[email protected]> > +# > +# 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.aare import AARE > +from apparmor.regex import RE_PROFILE_PTRACE, RE_PROFILE_NAME > +from apparmor.common import AppArmorBug, AppArmorException, type_is_str > +from apparmor.rule import BaseRule, BaseRuleset, check_and_split_list, > parse_modifiers, quote_if_needed > + > +# setup module translations > +from apparmor.translations import init_translation > +_ = init_translation() > + > + > +access_keywords = ['r', 'w', 'rw', 'wr', 'read', 'write', 'readby', > 'trace', 'tracedby'] # XXX 'wr' and 'write' accepted by the parser, but not > documented in apparmor.d.pod > + > +# XXX joint_access_keyword and RE_ACCESS_KEYWORDS exactly as in PtraceRule - > move to function! > +joint_access_keyword = '\s*(' + '|'.join(access_keywords) + ')\s*' > +RE_ACCESS_KEYWORDS = ( joint_access_keyword + # one of the access_keyword or > + '|' + # or > + '\(' + joint_access_keyword + '(' + '(\s|,)+' + > joint_access_keyword + ')*' + '\)' # one or more access_keyword in (...) > + ) > + > + > +RE_PTRACE_DETAILS = re.compile( > + '^' + > + '(\s+(?P<access>' + RE_ACCESS_KEYWORDS + '))?' + # optional access > keyword(s) > + '(\s+(peer=' + RE_PROFILE_NAME % 'peer' + '))?' + # optional peer > + '\s*$') > + > + > +class PtraceRule(BaseRule): > + '''Class to handle and store a single ptrace rule''' > + > + # Nothing external should reference this class, all external users > + # should reference the class field PtraceRule.ALL > + class __PtraceAll(object): > + pass > + > + ALL = __PtraceAll > + > + def __init__(self, access, peer, audit=False, deny=False, > allow_keyword=False, > + comment='', log_event=None): > + > + super(PtraceRule, self).__init__(audit=audit, deny=deny, > + allow_keyword=allow_keyword, > + comment=comment, > + log_event=log_event) > + > + self.access, self.all_access, unknown_items = > check_and_split_list(access, access_keywords, PtraceRule.ALL, 'PtraceRule', > 'access') > + if unknown_items: > + raise AppArmorException(_('Passed unknown access keyword to > PtraceRule: %s') % ' '.join(unknown_items)) > + > + # XXX same as in SignalRule - move to _init_peer() function! > + self.peer = None > + self.all_peers = False > + if peer == PtraceRule.ALL: > + self.all_peers = True > + elif type_is_str(peer): > + if len(peer.strip()) == 0: > + raise AppArmorBug('Passed empty peer to PtraceRule: %s' % > str(peer)) > + self.peer = AARE(peer, False, log_event=log_event) > + else: > + raise AppArmorBug('Passed unknown object to PtraceRule: %s' % > str(peer)) > + > + > + @classmethod > + def _match(cls, raw_rule): > + return RE_PROFILE_PTRACE.search(raw_rule) > + > + @classmethod > + def _parse(cls, raw_rule): > + '''parse raw_rule and return PtraceRule''' > + > + matches = cls._match(raw_rule) > + if not matches: > + raise AppArmorException(_("Invalid ptrace rule '%s'") % raw_rule) > + > + audit, deny, allow_keyword, comment = parse_modifiers(matches) > + > + rule_details = '' > + if matches.group('details'): > + rule_details = matches.group('details') > + > + if rule_details: > + details = RE_PTRACE_DETAILS.search(rule_details) > + if not details: > + raise AppArmorException(_("Invalid or unknown keywords in > 'ptrace %s" % rule_details)) > + > + if details.group('access'): > + # XXX move to function _split_access()? > + access = details.group('access') > + if access.startswith('(') and access.endswith(')'): > + access = access[1:-1] > + access = access.replace(',', ' ').split() # split by ',' or > whitespace > + else: > + access = PtraceRule.ALL > + > + if details.group('peer'): > + peer = details.group('peer') > + else: > + peer = PtraceRule.ALL > + else: > + access = PtraceRule.ALL > + peer = PtraceRule.ALL > + > + return PtraceRule(access, peer, > + audit=audit, deny=deny, > allow_keyword=allow_keyword, comment=comment) > + > + def get_clean(self, depth=0): > + '''return rule (in clean/default formatting)''' > + > + space = ' ' * depth > + > + if self.all_access: > + access = '' > + elif len(self.access) == 1: > + access = ' %s' % ' '.join(self.access) > + elif self.access: > + access = ' (%s)' % ' '.join(sorted(self.access)) > + else: > + raise AppArmorBug('Empty access in ptrace rule') > + > + if self.all_peers: > + peer = '' > + elif self.peer: > + peer = ' peer=%s' % quote_if_needed(self.peer.regex) > + else: > + raise AppArmorBug('Empty peer in ptrace rule') > + > + return('%s%sptrace%s%s,%s' % (space, self.modifiers_str(), access, > peer, self.comment)) > + > + def is_covered_localvars(self, other_rule): > + '''check if other_rule is covered by this rule object''' > + > + if not other_rule.access and not other_rule.all_access: > + raise AppArmorBug('No access specified in other ptrace rule') > + > + if not other_rule.peer and not other_rule.all_peers: > + raise AppArmorBug('No peer specified in other ptrace rule') > + > + if not self.all_access: > + if other_rule.all_access: > + return False > + if other_rule.access != self.access: > + return False > + > + if not self.all_peers: > + if other_rule.all_peers: > + return False > + if not self.peer.match(other_rule.peer.regex): > + return False > + > + # still here? -> then it is covered > + return True > + > + def is_equal_localvars(self, rule_obj): > + '''compare if rule-specific variables are equal''' > + > + if not type(rule_obj) == PtraceRule: > + raise AppArmorBug('Passed non-ptrace rule: %s' % str(rule_obj)) > + > + if (self.access != rule_obj.access > + or self.all_access != rule_obj.all_access): > + return False > + > + if self.all_peers != rule_obj.all_peers: > + return False > + > + if self.peer and not self.peer.is_equal(rule_obj.peer): > + return False > + > + return True > + > + def logprof_header_localvars(self): > + if self.all_access: > + access = _('ALL') > + else: > + access = ' '.join(sorted(self.access)) > + > + if self.all_peers: > + peer = _('ALL') > + else: > + peer = self.peer.regex > + > + return [ > + _('Access mode'), access, > + _('Peer'), peer > + ] > + > + > +class PtraceRuleset(BaseRuleset): > + '''Class to handle and store a collection of ptrace rules''' > + > + def get_glob(self, path_or_rule): > + '''Return the next possible glob. For ptrace rules, that means > removing access or removing/globbing peer''' > + # XXX only remove one part, not all > + return 'ptrace,' > > > Regards, > > Christian Boltz > -- AppArmor mailing list [email protected] Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor
