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