Hello,
On Wed, Apr 15, 2015 at 3:07 AM, Christian Boltz <appar...@cboltz.de> wrote: > Hello, > > this patch adds utils/apparmor/rule/network.py with the NetworkRule and > NetworkRuleset classes. These classes are meant to handle network rules. > > In comparison to the existing code in aa.py, relevant news are: > - the keywords are checked against a list of allowed domains, types and > protocols (these lists are based on what the utils/vim/Makefile > generates - on the long term an autogenerated file with the keywords > for all rule types would be nice ;-) > In theory you can have a script generate a python module that solely contains these keywords, which can be imported in here. Sounds pretty trivial ;-) > - there are variables for domain and type_or_protocol instead of > first_param and second_param. (If someone is bored enough to map the > protocol "shortcuts" to their expanded meaning, that shouldn't be too > hard.) > - (obviously) more readable code because we have everything at one place > now > yay! > - some bugs are fixed along the way (for example, "network foo" will now > be kept, not "network foo bar" - see my last mail about > write_net_rules() for details) > > > > [ 44-add-NetworkRule-and-NetworkRuleset-classes.diff ] > > === added file 'utils/apparmor/rule/network.py' > --- utils/apparmor/rule/network.py 1970-01-01 00:00:00 +0000 > +++ utils/apparmor/rule/network.py 2015-04-14 20:47:40 +0000 > @@ -0,0 +1,210 @@ > +#!/usr/bin/env python > +# ---------------------------------------------------------------------- > +# Copyright (C) 2013 Kshitij Gupta <kgupta8...@gmail.com> > +# Copyright (C) 2015 Christian Boltz <appar...@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.regex import RE_PROFILE_NETWORK > +from apparmor.common import AppArmorBug, AppArmorException > +from apparmor.rule import BaseRule, BaseRuleset, parse_modifiers > + > +# setup module translations > +from apparmor.translations import init_translation > +_ = init_translation() > + > + > +network_domain_keywords = [ 'unix', 'inet', 'ax25', 'ipx', 'appletalk', > 'netrom', 'bridge', 'atmpvc', 'x25', 'inet6', > + 'rose', 'netbeui', 'security', 'key', > 'netlink', 'packet', 'ash', 'econet', 'atmsvc', 'rds', 'sna', > + 'irda', 'pppox', 'wanpipe', 'llc', 'can', > 'tipc', 'bluetooth', 'iucv', 'rxrpc', 'isdn', 'phonet', > + 'ieee802154', 'caif', 'alg', 'nfc', 'vsock' > ] > + # missing in manpage: 'unix', 'rds', 'llc', 'can', 'tipc', > 'iucv', 'rxrpc', 'isdn', 'phonet', 'ieee802154', 'caif', 'alg', 'nfc', > 'vsock' > + > comments from others on the missing types? +network_type_keywords = ['stream', 'dgram', 'seqpacket', 'rdm', 'raw', > 'packet'] > +network_protocol_keywords = ['tcp', 'udp', 'icmp'] > + > + > +RE_NETWORK_DOMAIN = '(' + '|'.join(network_domain_keywords) + ')' > +RE_NETWORK_TYPE = '(' + '|'.join(network_type_keywords) + ')' > +RE_NETWORK_PROTOCOL = '(' + '|'.join(network_protocol_keywords) + ')' > neat! > + > +RE_NETWORK_DETAILS = re.compile( > + '^\s*(' + > + '(?P<domain>' + RE_NETWORK_DOMAIN + ')(\s+(?P<type_or_protocol>' > + RE_NETWORK_TYPE + '|' + RE_NETWORK_PROTOCOL + '))?' + # domain, optional > type or protocol > exercising new/larger monitor privileges? ;-) can be split into two lines I think. + '|' + # or > + '(?P<protocol>' + RE_NETWORK_PROTOCOL + ')' + # protocol only > + ')\s*$') > The leading and trailing \s* become superfluous as __parse has: rule_details = matches.group('details').strip() + + > + > + > + > + > + > what is the standard for whitespace separation? 7 seems a bit much. +class NetworkRule(BaseRule): > + '''Class to handle and store a single network rule''' > + > + # Nothing external should reference this class, all external users > + # should reference the class field NetworkRule.ALL > + class __NetworkAll(object): > + pass > + > + ALL = __NetworkAll > + > + def __init__(self, domain, type_or_protocol, audit=False, deny=False, > allow_keyword=False, > + comment='', log_event=None): > + > + ''' > + NETWORK RULE = 'network' [ [ DOMAIN [ TYPE | PROTOCOL ] ] | [ > PROTOCOL ] ] ',' > + ''' > + > + super(NetworkRule, self).__init__(audit=audit, deny=deny, > + allow_keyword=allow_keyword, > + comment=comment, > + log_event=log_event) > + > + self.domain = None > + self.all_domains = False > + if domain == NetworkRule.ALL: > + self.all_domains = True > + elif type(domain) == str: > + if domain in network_domain_keywords: > + self.domain = domain > + else: > + raise AppArmorBug('Passed unknown domain to NetworkRule: > %s' % str(domain)) > str(domain) is unnecessary, as type of domain is known to be str. > + else: > + raise AppArmorBug('Passed unknown object to NetworkRule: %s' > % str(domain)) > + > + self.type_or_protocol = None > + self.all_type_or_protocols = False > + if type_or_protocol == NetworkRule.ALL: > + self.all_type_or_protocols = True > + elif type(type_or_protocol) == str: > + if type_or_protocol in network_protocol_keywords: > + self.type_or_protocol = type_or_protocol > + elif type_or_protocol in network_type_keywords: > + if self.all_domains: > + raise AppArmorException('Passing type %s to > NetworkRule without specifying a domain keyword is not allowed' % > str(type_or_protocol)) > str(type_or_protocol) not required. + self.type_or_protocol = type_or_protocol > + else: > + raise AppArmorBug('Passed unknown type_or_protocol to > NetworkRule: %s' % str(type_or_protocol)) > str(type_or_protocol) not required. #copy paste reviews + else: > + raise AppArmorBug('Passed unknown object to NetworkRule: %s' > % str(type_or_protocol)) > + > + @classmethod > + def _parse(cls, raw_rule): > + '''parse raw_rule and return NetworkRule''' > + > + matches = RE_PROFILE_NETWORK.search(raw_rule) > + if not matches: > + raise AppArmorException(_("Invalid network rule '%s'") % > raw_rule) > + > + audit, deny, allow_keyword, comment = parse_modifiers(matches) > + > + rule_details = '' > + if matches.group('details'): > + rule_details = matches.group('details').strip() > + > + if rule_details: > + details = RE_NETWORK_DETAILS.search(rule_details) > + if not details: > + raise AppArmorException(_("Invalid or unknown keywords in > 'network %s" % rule_details)) > + > + if details.group('domain'): > + domain = details.group('domain') > + else: > + domain = NetworkRule.ALL > + > + if details.group('type_or_protocol'): > + type_or_protocol = details.group('type_or_protocol') > + elif details.group('protocol'): > + type_or_protocol = details.group('protocol') > + else: > + type_or_protocol = NetworkRule.ALL > + else: > + domain = NetworkRule.ALL > + type_or_protocol = NetworkRule.ALL > + > + return NetworkRule(domain, type_or_protocol, > + 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_domains: > + domain = '' > + elif self.domain: > + domain = ' %s' % self.domain > + else: > + raise AppArmorBug('Empty domain in network rule') > + > + if self.all_type_or_protocols: > + type_or_protocol = '' > + elif self.type_or_protocol: > + type_or_protocol = ' %s' % self.type_or_protocol > + else: > + raise AppArmorBug('Empty type or protocol in network rule') > + > + return('%s%snetwork%s%s,%s' % (space, self.modifiers_str(), > domain, type_or_protocol, self.comment)) > + > + def is_covered_localvars(self, other_rule): > + '''check if other_rule is covered by this rule object''' > + > + if not other_rule.domain and not other_rule.all_domains: > + raise AppArmorBug('No domain specified in other network rule') > + > + if not other_rule.type_or_protocol and not > other_rule.all_type_or_protocols: > + raise AppArmorBug('No type or protocol specified in other > network rule') > + > + if not self.all_domains: > + if other_rule.all_domains: > + return False > + if other_rule.domain != self.domain: > + return False > + > + if not self.all_type_or_protocols: > + if other_rule.all_type_or_protocols: > + return False > + if other_rule.type_or_protocol != self.type_or_protocol: > + 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) == NetworkRule: > + raise AppArmorBug('Passes non-network rule: %s' % > str(rule_obj)) > Passes or Passed? + > + if (self.domain != rule_obj.domain > + or self.all_domains != rule_obj.all_domains): > + return False > + > + if (self.type_or_protocol != rule_obj.type_or_protocol > + or self.all_type_or_protocols != > rule_obj.all_type_or_protocols): > + return False > + > + return True > + > + > +class NetworkRuleset(BaseRuleset): > + '''Class to handle and store a collection of network rules''' > + > + def get_glob(self, path_or_rule): > + '''Return the next possible glob. For network rules, that's > "network DOMAIN," or "network," (all network)''' > + # XXX return 'network DOMAIN,' > + return 'network,' > > > > Thanks. Regards, Kshitij Gupta > Regards, > > Christian Boltz > -- > We break the translation consistently (wow, consistent break, I like > that wording) [from https://bugzilla.novell.com/show_bug.cgi?id=165509] > > > -- > AppArmor mailing list > AppArmor@lists.ubuntu.com > Modify settings or unsubscribe at: > https://lists.ubuntu.com/mailman/listinfo/apparmor >
-- AppArmor mailing list AppArmor@lists.ubuntu.com Modify settings or unsubscribe at: https://lists.ubuntu.com/mailman/listinfo/apparmor