Home | History | Annotate | Download | only in policyrep
      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 itertools
     20 
     21 from . import exception
     22 from . import qpol
     23 from . import rule
     24 from . import typeattr
     25 from . import boolcond
     26 
     27 
     28 def te_rule_factory(policy, symbol):
     29     """Factory function for creating TE rule objects."""
     30 
     31     if isinstance(symbol, qpol.qpol_avrule_t):
     32         if symbol.is_extended(policy):
     33             return AVRuleXperm(policy, symbol)
     34         else:
     35             return AVRule(policy, symbol)
     36     elif isinstance(symbol, (qpol.qpol_terule_t, qpol.qpol_filename_trans_t)):
     37         return TERule(policy, symbol)
     38     else:
     39         raise TypeError("TE rules cannot be looked-up.")
     40 
     41 
     42 def expanded_te_rule_factory(original, source, target):
     43     """
     44     Factory function for creating expanded TE rules.
     45 
     46     original    The TE rule the expanded rule originates from.
     47     source      The source type of the expanded rule.
     48     target      The target type of the expanded rule.
     49     """
     50 
     51     if isinstance(original, (ExpandedAVRule, ExpandedAVRuleXperm, ExpandedTERule)):
     52         return original
     53     elif isinstance(original, AVRuleXperm):
     54         rule = ExpandedAVRuleXperm(original.policy, original.qpol_symbol)
     55     elif isinstance(original, AVRule):
     56         rule = ExpandedAVRule(original.policy, original.qpol_symbol)
     57     elif isinstance(original, TERule):
     58         rule = ExpandedTERule(original.policy, original.qpol_symbol)
     59     else:
     60         raise TypeError("The original rule must be a TE rule class.")
     61 
     62     rule.source = source
     63     rule.target = target
     64     rule.origin = original
     65     return rule
     66 
     67 
     68 def validate_ruletype(t):
     69     """Validate TE Rule types."""
     70     if t not in ["allow", "auditallow", "dontaudit", "neverallow",
     71                  "type_transition", "type_member", "type_change",
     72                  "allowxperm", "auditallowxperm", "dontauditxperm", "neverallowxperm"]:
     73         raise exception.InvalidTERuleType("{0} is not a valid TE rule type.".format(t))
     74 
     75     return t
     76 
     77 
     78 class BaseTERule(rule.PolicyRule):
     79 
     80     """A type enforcement rule."""
     81 
     82     @property
     83     def source(self):
     84         """The rule's source type/attribute."""
     85         return typeattr.type_or_attr_factory(self.policy, self.qpol_symbol.source_type(self.policy))
     86 
     87     @property
     88     def target(self):
     89         """The rule's target type/attribute."""
     90         return typeattr.type_or_attr_factory(self.policy, self.qpol_symbol.target_type(self.policy))
     91 
     92     @property
     93     def filename(self):
     94         raise NotImplementedError
     95 
     96     @property
     97     def conditional(self):
     98         """The rule's conditional expression."""
     99         try:
    100             return boolcond.condexpr_factory(self.policy, self.qpol_symbol.cond(self.policy))
    101         except AttributeError:
    102             raise exception.RuleNotConditional
    103 
    104     @property
    105     def conditional_block(self):
    106         """The conditional block of the rule (T/F)"""
    107         try:
    108             return bool(self.qpol_symbol.which_list(self.policy))
    109         except AttributeError:
    110             raise exception.RuleNotConditional
    111 
    112     def expand(self):
    113         """Expand the rule into an equivalent set of rules without attributes."""
    114         for s, t in itertools.product(self.source.expand(), self.target.expand()):
    115             yield expanded_te_rule_factory(self, s, t)
    116 
    117 
    118 class AVRule(BaseTERule):
    119 
    120     """An access vector type enforcement rule."""
    121 
    122     def __str__(self):
    123         try:
    124             return self._rule_string
    125         except AttributeError:
    126             self._rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} ".format(self)
    127 
    128             # allow/dontaudit/auditallow/neverallow rules
    129             perms = self.perms
    130             if len(perms) > 1:
    131                 self._rule_string += "{{ {0} }};".format(' '.join(perms))
    132             else:
    133                 # convert to list since sets cannot be indexed
    134                 self._rule_string += "{0};".format(list(perms)[0])
    135 
    136             try:
    137                 self._rule_string += " [ {0.conditional} ]:{0.conditional_block}".format(self)
    138             except exception.RuleNotConditional:
    139                 pass
    140 
    141         return self._rule_string
    142 
    143     @property
    144     def perms(self):
    145         """The rule's permission set."""
    146         return set(self.qpol_symbol.perm_iter(self.policy))
    147 
    148     @property
    149     def default(self):
    150         """The rule's default type."""
    151         raise exception.RuleUseError("{0} rules do not have a default type.".format(self.ruletype))
    152 
    153     @property
    154     def filename(self):
    155         raise exception.RuleUseError("{0} rules do not have file names".format(self.ruletype))
    156 
    157 
    158 class ioctlSet(set):
    159 
    160     """
    161     A set with overridden string functions which compresses
    162     the output into ioctl ranges instead of individual elements.
    163     """
    164 
    165     def __format__(self, spec):
    166         """
    167         String formating.
    168 
    169         The standard formatting (no specification) will render the
    170         ranges of ioctls, space separated.
    171 
    172         The , option by itself will render the ranges of ioctls,
    173         comma separated
    174 
    175         Any other combination of formatting options will fall back
    176         to set's formatting behavior.
    177         """
    178 
    179         # generate short permission notation
    180         perms = sorted(self)
    181         shortlist = []
    182         for _, i in itertools.groupby(perms, key=lambda k, c=itertools.count(): k - next(c)):
    183             group = list(i)
    184             if len(group) > 1:
    185                 shortlist.append("{0:#06x}-{1:#06x}".format(group[0], group[-1]))
    186             else:
    187                 shortlist.append("{0:#06x}".format(group[0]))
    188 
    189         if not spec:
    190             return " ".join(shortlist)
    191         elif spec == ",":
    192             return ", ".join(shortlist)
    193         else:
    194             return super(ioctlSet, self).__format__(spec)
    195 
    196     def __str__(self):
    197         return "{0}".format(self)
    198 
    199     def __repr__(self):
    200         return "{{ {0:,} }}".format(self)
    201 
    202     def ranges(self):
    203         """
    204         Return the number of ranges in the set.  Main use
    205         is to determine if brackets need to be used in
    206         string output.
    207         """
    208         return sum(1 for (_a, _b) in itertools.groupby(
    209             sorted(self), key=lambda k, c=itertools.count(): k - next(c)))
    210 
    211 
    212 class AVRuleXperm(AVRule):
    213 
    214     """An extended permission access vector type enforcement rule."""
    215 
    216     extended = True
    217 
    218     def __hash__(self):
    219         return hash("{0.ruletype}|{0.source}|{0.target}|{0.tclass}|{0.xperm_type}".format(self))
    220 
    221     def __str__(self):
    222         try:
    223             return self._rule_string
    224         except AttributeError:
    225             self._rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.xperm_type} ". \
    226                                 format(self)
    227 
    228             # generate short permission notation
    229             perms = self.perms
    230             if perms.ranges() > 1:
    231                 self._rule_string += "{{ {0} }};".format(perms)
    232             else:
    233                 self._rule_string += "{0};".format(perms)
    234 
    235         return self._rule_string
    236 
    237     @property
    238     def perms(self):
    239         """The rule's extended permission set."""
    240         return ioctlSet(self.qpol_symbol.xperm_iter(self.policy))
    241 
    242     @property
    243     def xperm_type(self):
    244         """The standard permission extended by these permissions (e.g. ioctl)."""
    245         return self.qpol_symbol.xperm_type(self.policy)
    246 
    247 
    248 class TERule(BaseTERule):
    249 
    250     """A type_* type enforcement rule."""
    251 
    252     def __str__(self):
    253         try:
    254             return self._rule_string
    255         except AttributeError:
    256             self._rule_string = "{0.ruletype} {0.source} {0.target}:{0.tclass} {0.default}".format(
    257                 self)
    258 
    259             try:
    260                 self._rule_string += " \"{0}\";".format(self.filename)
    261             except (exception.TERuleNoFilename, exception.RuleUseError):
    262                 # invalid use for type_change/member
    263                 self._rule_string += ";"
    264 
    265             try:
    266                 self._rule_string += " [ {0.conditional} ]:{0.conditional_block}".format(self)
    267             except exception.RuleNotConditional:
    268                 pass
    269 
    270             return self._rule_string
    271 
    272     def __hash__(self):
    273         try:
    274             cond = self.conditional
    275             cond_block = self.conditional_block
    276         except exception.RuleNotConditional:
    277             cond = None
    278             cond_block = None
    279 
    280         try:
    281             filename = self.filename
    282         except (exception.TERuleNoFilename, exception.RuleUseError):
    283             filename = None
    284 
    285         return hash("{0.ruletype}|{0.source}|{0.target}|{0.tclass}|{1}|{2}|{3}".format(
    286             self, filename, cond, cond_block))
    287 
    288     @property
    289     def perms(self):
    290         """The rule's permission set."""
    291         raise exception.RuleUseError(
    292             "{0} rules do not have a permission set.".format(self.ruletype))
    293 
    294     @property
    295     def default(self):
    296         """The rule's default type."""
    297         return typeattr.type_factory(self.policy, self.qpol_symbol.default_type(self.policy))
    298 
    299     @property
    300     def filename(self):
    301         """The type_transition rule's file name."""
    302         try:
    303             return self.qpol_symbol.filename(self.policy)
    304         except AttributeError:
    305             if self.ruletype == "type_transition":
    306                 raise exception.TERuleNoFilename
    307             else:
    308                 raise exception.RuleUseError("{0} rules do not have file names".
    309                                              format(self.ruletype))
    310 
    311 
    312 class ExpandedAVRule(AVRule):
    313 
    314     """An expanded access vector type enforcement rule."""
    315 
    316     source = None
    317     target = None
    318     origin = None
    319 
    320 
    321 class ExpandedAVRuleXperm(AVRuleXperm):
    322 
    323     """An expanded extended permission access vector type enforcement rule."""
    324 
    325     source = None
    326     target = None
    327     origin = None
    328 
    329 
    330 class ExpandedTERule(TERule):
    331 
    332     """An expanded type_* type enforcement rule."""
    333 
    334     source = None
    335     target = None
    336     origin = None
    337