1 # Copyright 2014, 2016, 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 import socket 20 from collections import namedtuple 21 22 from . import qpol 23 from . import symbol 24 from . import context 25 26 port_range = namedtuple("port_range", ["low", "high"]) 27 28 29 def netifcon_factory(policy, name): 30 """Factory function for creating netifcon objects.""" 31 32 if not isinstance(name, qpol.qpol_netifcon_t): 33 raise NotImplementedError 34 35 return Netifcon(policy, name) 36 37 38 def nodecon_factory(policy, name): 39 """Factory function for creating nodecon objects.""" 40 41 if not isinstance(name, qpol.qpol_nodecon_t): 42 raise NotImplementedError 43 44 return Nodecon(policy, name) 45 46 47 def portcon_factory(policy, name): 48 """Factory function for creating portcon objects.""" 49 50 if not isinstance(name, qpol.qpol_portcon_t): 51 raise NotImplementedError 52 53 return Portcon(policy, name) 54 55 56 class NetContext(symbol.PolicySymbol): 57 58 """Base class for in-policy network labeling rules.""" 59 60 def __str__(self): 61 raise NotImplementedError 62 63 @property 64 def context(self): 65 """The context for this statement.""" 66 return context.context_factory(self.policy, self.qpol_symbol.context(self.policy)) 67 68 def statement(self): 69 return str(self) 70 71 72 class Netifcon(NetContext): 73 74 """A netifcon statement.""" 75 76 def __str__(self): 77 return "netifcon {0.netif} {0.context} {0.packet}".format(self) 78 79 def __hash__(self): 80 return hash("netifcon|{0.netif}".format(self)) 81 82 @property 83 def netif(self): 84 """The network interface name.""" 85 return self.qpol_symbol.name(self.policy) 86 87 @property 88 def context(self): 89 """The context for the interface.""" 90 return context.context_factory(self.policy, self.qpol_symbol.if_con(self.policy)) 91 92 @property 93 def packet(self): 94 """The context for the packets.""" 95 return context.context_factory(self.policy, self.qpol_symbol.msg_con(self.policy)) 96 97 98 class Nodecon(NetContext): 99 100 """A nodecon statement.""" 101 102 def __str__(self): 103 return "nodecon {0.address} {0.netmask} {0.context}".format(self) 104 105 def __hash__(self): 106 return hash("nodecon|{0.address}|{0.netmask}".format(self)) 107 108 def __eq__(self, other): 109 # Libqpol allocates new C objects in the 110 # nodecons iterator, so pointer comparison 111 # in the PolicySymbol object doesn't work. 112 try: 113 return (self.address == other.address and 114 self.netmask == other.netmask and 115 self.context == other.context) 116 except AttributeError: 117 return (str(self) == str(other)) 118 119 @property 120 def ip_version(self): 121 """ 122 The IP version for the nodecon (socket.AF_INET or 123 socket.AF_INET6). 124 """ 125 return self.qpol_symbol.protocol(self.policy) 126 127 @property 128 def address(self): 129 """The network address for the nodecon.""" 130 return self.qpol_symbol.addr(self.policy) 131 132 @property 133 def netmask(self): 134 """The network mask for the nodecon.""" 135 return self.qpol_symbol.mask(self.policy) 136 137 138 class PortconProtocol(int): 139 140 """ 141 A portcon protocol type. 142 143 The possible values are equivalent to protocol 144 values in the socket module, e.g. IPPROTO_TCP, but 145 overrides the string representation with the 146 corresponding protocol string (udp, tcp). 147 """ 148 149 _proto_to_text = {socket.IPPROTO_TCP: 'tcp', 150 socket.IPPROTO_UDP: 'udp'} 151 152 def __str__(self): 153 return self._proto_to_text[self] 154 155 156 class Portcon(NetContext): 157 158 """A portcon statement.""" 159 160 def __str__(self): 161 low, high = self.ports 162 163 if low == high: 164 return "portcon {0.protocol} {1} {0.context}".format(self, low) 165 else: 166 return "portcon {0.protocol} {1}-{2} {0.context}".format(self, low, high) 167 168 def __hash__(self): 169 return hash("portcon|{0.protocol}|{1.low}|{1.high}".format(self, self.ports)) 170 171 @property 172 def protocol(self): 173 """ 174 The protocol number for the portcon (socket.IPPROTO_TCP 175 or socket.IPPROTO_UDP). 176 """ 177 return PortconProtocol(self.qpol_symbol.protocol(self.policy)) 178 179 @property 180 def ports(self): 181 """ 182 The port range for this portcon. 183 184 Return: Tuple(low, high) 185 low The low port of the range. 186 high The high port of the range. 187 """ 188 low = self.qpol_symbol.low_port(self.policy) 189 high = self.qpol_symbol.high_port(self.policy) 190 return port_range(low, high) 191