1 # Copyright 2014-2015, Tresys Technology, LLC 2 # 3 # This file is part of SETools. 4 # 5 # SETools is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Lesser General Public License as 7 # published by the Free Software Foundation, either version 2.1 of 8 # the License, or (at your option) any later version. 9 # 10 # SETools is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Lesser General Public License for more details. 14 # 15 # You should have received a copy of the GNU Lesser General Public 16 # License along with SETools. If not, see 17 # <http://www.gnu.org/licenses/>. 18 # 19 try: 20 import ipaddress 21 except ImportError: # pragma: no cover 22 pass 23 24 import logging 25 from socket import AF_INET, AF_INET6 26 27 from . import contextquery 28 29 30 class NodeconQuery(contextquery.ContextQuery): 31 32 """ 33 Query nodecon statements. 34 35 Parameter: 36 policy The policy to query. 37 38 Keyword Parameters/Class attributes: 39 network The IPv4/IPv6 address or IPv4/IPv6 network address 40 with netmask, e.g. 192.168.1.0/255.255.255.0 or 41 "192.168.1.0/24". 42 network_overlap If true, the net will match if it overlaps with 43 the nodecon's network instead of equality. 44 ip_version The IP version of the nodecon to match. (socket.AF_INET 45 for IPv4 or socket.AF_INET6 for IPv6) 46 user The criteria to match the context's user. 47 user_regex If true, regular expression matching 48 will be used on the user. 49 role The criteria to match the context's role. 50 role_regex If true, regular expression matching 51 will be used on the role. 52 type_ The criteria to match the context's type. 53 type_regex If true, regular expression matching 54 will be used on the type. 55 range_ The criteria to match the context's range. 56 range_subset If true, the criteria will match if it is a subset 57 of the context's range. 58 range_overlap If true, the criteria will match if it overlaps 59 any of the context's range. 60 range_superset If true, the criteria will match if it is a superset 61 of the context's range. 62 range_proper If true, use proper superset/subset operations. 63 No effect if not using set operations. 64 """ 65 66 _network = None 67 network_overlap = False 68 _ip_version = None 69 70 @property 71 def ip_version(self): 72 return self._ip_version 73 74 @ip_version.setter 75 def ip_version(self, value): 76 if value: 77 if not (value == AF_INET or value == AF_INET6): 78 raise ValueError( 79 "The address family must be {0} for IPv4 or {1} for IPv6.". 80 format(AF_INET, AF_INET6)) 81 82 self._ip_version = value 83 else: 84 self._ip_version = None 85 86 @property 87 def network(self): 88 return self._network 89 90 @network.setter 91 def network(self, value): 92 if value: 93 try: 94 self._network = ipaddress.ip_network(value) 95 except NameError: # pragma: no cover 96 raise RuntimeError("Nodecon IP address/network functions require Python 3.3+.") 97 else: 98 self._network = None 99 100 def results(self): 101 """Generator which yields all matching nodecons.""" 102 self.log.info("Generating results from {0.policy}".format(self)) 103 self.log.debug("Network: {0.network!r}, overlap: {0.network_overlap}".format(self)) 104 self.log.debug("IP Version: {0.ip_version}".format(self)) 105 self.log.debug("User: {0.user!r}, regex: {0.user_regex}".format(self)) 106 self.log.debug("Role: {0.role!r}, regex: {0.role_regex}".format(self)) 107 self.log.debug("Type: {0.type_!r}, regex: {0.type_regex}".format(self)) 108 self.log.debug("Range: {0.range_!r}, subset: {0.range_subset}, overlap: {0.range_overlap}, " 109 "superset: {0.range_superset}, proper: {0.range_proper}".format(self)) 110 111 for nodecon in self.policy.nodecons(): 112 113 if self.network: 114 try: 115 netmask = ipaddress.ip_address(nodecon.netmask) 116 except NameError: # pragma: no cover 117 # Should never actually hit this since the self.network 118 # setter raises the same exception. 119 raise RuntimeError("Nodecon IP address/network functions require Python 3.3+.") 120 121 # Python 3.3's IPv6Network constructor does not support 122 # expanded netmasks, only CIDR numbers. Convert netmask 123 # into CIDR. 124 # This is Brian Kernighan's method for counting set bits. 125 # If the netmask happens to be invalid, this will 126 # not detect it. 127 CIDR = 0 128 int_netmask = int(netmask) 129 while int_netmask: 130 int_netmask &= int_netmask - 1 131 CIDR += 1 132 133 net = ipaddress.ip_network('{0}/{1}'.format(nodecon.address, CIDR)) 134 135 if self.network_overlap: 136 if not self.network.overlaps(net): 137 continue 138 else: 139 if not net == self.network: 140 continue 141 142 if self.ip_version and self.ip_version != nodecon.ip_version: 143 continue 144 145 if not self._match_context(nodecon.context): 146 continue 147 148 yield nodecon 149