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