Home | History | Annotate | Download | only in setools
      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 import logging
     20 from socket import IPPROTO_TCP, IPPROTO_UDP
     21 
     22 from .mixins import MatchContext
     23 from .query import PolicyQuery
     24 from .policyrep import port_range, PortconProtocol
     25 from .util import match_range
     26 
     27 
     28 class PortconQuery(MatchContext, PolicyQuery):
     29 
     30     """
     31     Port context query.
     32 
     33     Parameter:
     34     policy          The policy to query.
     35 
     36     Keyword Parameters/Class attributes:
     37     protocol        The protocol to match (socket.IPPROTO_TCP for
     38                     TCP or socket.IPPROTO_UDP for UDP)
     39 
     40     ports           A 2-tuple of the port range to match. (Set both to
     41                     the same value for a single port)
     42     ports_subset    If true, the criteria will match if it is a subset
     43                     of the portcon's range.
     44     ports_overlap   If true, the criteria will match if it overlaps
     45                     any of the portcon's range.
     46     ports_superset  If true, the criteria will match if it is a superset
     47                     of the portcon's range.
     48     ports_proper    If true, use proper superset/subset operations.
     49                     No effect if not using set operations.
     50 
     51     user            The criteria to match the context's user.
     52     user_regex      If true, regular expression matching
     53                     will be used on the user.
     54 
     55     role            The criteria to match the context's role.
     56     role_regex      If true, regular expression matching
     57                     will be used on the role.
     58 
     59     type_           The criteria to match the context's type.
     60     type_regex      If true, regular expression matching
     61                     will be used on the type.
     62 
     63     range_          The criteria to match the context's range.
     64     range_subset    If true, the criteria will match if it is a subset
     65                     of the context's range.
     66     range_overlap   If true, the criteria will match if it overlaps
     67                     any of the context's range.
     68     range_superset  If true, the criteria will match if it is a superset
     69                     of the context's range.
     70     range_proper    If true, use proper superset/subset operations.
     71                     No effect if not using set operations.
     72     """
     73 
     74     _protocol = None
     75     _ports = None
     76     ports_subset = False
     77     ports_overlap = False
     78     ports_superset = False
     79     ports_proper = False
     80 
     81     @property
     82     def ports(self):
     83         return self._ports
     84 
     85     @ports.setter
     86     def ports(self, value):
     87         pending_ports = port_range(*value)
     88 
     89         if all(pending_ports):
     90             if pending_ports.low < 1 or pending_ports.high < 1:
     91                 raise ValueError("Port numbers must be positive: {0.low}-{0.high}".
     92                                  format(pending_ports))
     93 
     94             if pending_ports.low > pending_ports.high:
     95                 raise ValueError(
     96                     "The low port must be smaller than the high port: {0.low}-{0.high}".
     97                     format(pending_ports))
     98 
     99             self._ports = pending_ports
    100         else:
    101             self._ports = None
    102 
    103     @property
    104     def protocol(self):
    105         return self._protocol
    106 
    107     @protocol.setter
    108     def protocol(self, value):
    109         if value:
    110             self._protocol = PortconProtocol(value)
    111         else:
    112             self._protocol = None
    113 
    114     def __init__(self, policy, **kwargs):
    115         super(PortconQuery, self).__init__(policy, **kwargs)
    116         self.log = logging.getLogger(__name__)
    117 
    118     def results(self):
    119         """Generator which yields all matching portcons."""
    120         self.log.info("Generating portcon results from {0.policy}".format(self))
    121         self.log.debug("Ports: {0.ports}, overlap: {0.ports_overlap}, "
    122                        "subset: {0.ports_subset}, superset: {0.ports_superset}, "
    123                        "proper: {0.ports_proper}".format(self))
    124         self.log.debug("Protocol: {0.protocol!r}".format(self))
    125         self._match_context_debug(self.log)
    126 
    127         for portcon in self.policy.portcons():
    128 
    129             if self.ports and not match_range(
    130                     portcon.ports,
    131                     self.ports,
    132                     self.ports_subset,
    133                     self.ports_overlap,
    134                     self.ports_superset,
    135                     self.ports_proper):
    136                 continue
    137 
    138             if self.protocol and self.protocol != portcon.protocol:
    139                 continue
    140 
    141             if not self._match_context(portcon.context):
    142                 continue
    143 
    144             yield portcon
    145