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 # pylint: disable=too-many-public-methods 20 # 21 # Create a Python representation of the policy. 22 # The idea is that this is module provides convenient 23 # abstractions and methods for accessing the policy 24 # structures. 25 import logging 26 from itertools import chain 27 from errno import ENOENT 28 29 try: 30 import selinux 31 except ImportError: 32 pass 33 34 from . import qpol 35 36 # The libqpol SWIG class is not quite natural for 37 # Python the policy is repeatedly referenced in the 38 # function calls, which makes sense for C code 39 # but not for python code, so each object keeps 40 # a reference to the policy for internal use. 41 # This also makes sense since an object would only 42 # be valid for the policy it comes from. 43 44 # Exceptions 45 from . import exception 46 47 # Components 48 from . import boolcond 49 from . import default 50 from . import mls 51 from . import objclass 52 from . import polcap 53 from . import role 54 from . import typeattr 55 from . import user 56 57 # Rules 58 from . import mlsrule 59 from . import rbacrule 60 from . import terule 61 62 # Constraints 63 from . import constraint 64 65 # In-policy Labeling 66 from . import fscontext 67 from . import initsid 68 from . import netcontext 69 70 71 class SELinuxPolicy(object): 72 73 """The complete SELinux policy.""" 74 75 def __init__(self, policyfile=None): 76 """ 77 Parameter: 78 policyfile Path to a policy to open. 79 """ 80 81 self.log = logging.getLogger(self.__class__.__name__) 82 self.policy = None 83 self.filename = None 84 85 if policyfile: 86 self._load_policy(policyfile) 87 else: 88 try: 89 self._load_running_policy() 90 except NameError: 91 raise RuntimeError("Loading the running policy requires libselinux Python bindings") 92 93 def __repr__(self): 94 return "<SELinuxPolicy(\"{0}\")>".format(self.filename) 95 96 def __str__(self): 97 return self.filename 98 99 def __deepcopy__(self, memo): 100 # shallow copy as all of the members are immutable 101 newobj = SELinuxPolicy.__new__(SELinuxPolicy) 102 newobj.policy = self.policy 103 newobj.filename = self.filename 104 memo[id(self)] = newobj 105 return newobj 106 107 # 108 # Policy loading functions 109 # 110 111 def _load_policy(self, filename): 112 """Load the specified policy.""" 113 self.log.info("Opening SELinux policy \"{0}\"".format(filename)) 114 115 try: 116 self.policy = qpol.qpol_policy_factory(str(filename)) 117 except SyntaxError as err: 118 raise exception.InvalidPolicy("Error opening policy file \"{0}\": {1}". 119 format(filename, err)) 120 121 self.log.info("Successfully opened SELinux policy \"{0}\"".format(filename)) 122 self.filename = filename 123 124 @staticmethod 125 def _potential_policies(): 126 """Generate a list of potential policies to use.""" 127 # Start with binary policies in the standard location 128 base_policy_path = selinux.selinux_binary_policy_path() 129 for version in range(qpol.QPOL_POLICY_MAX_VERSION, qpol.QPOL_POLICY_MIN_VERSION-1, -1): 130 yield "{0}.{1}".format(base_policy_path, version) 131 132 # Last chance, try selinuxfs. This is not first, to avoid 133 # holding kernel memory for a long time 134 if selinux.selinuxfs_exists(): 135 yield selinux.selinux_current_policy_path() 136 137 def _load_running_policy(self): 138 """Try to load the current running policy.""" 139 self.log.info("Attempting to locate current running policy.") 140 141 for filename in self._potential_policies(): 142 try: 143 self._load_policy(filename) 144 except OSError as err: 145 if err.errno != ENOENT: 146 raise 147 else: 148 break 149 else: 150 raise RuntimeError("Unable to locate an SELinux policy to load.") 151 152 # 153 # Policy properties 154 # 155 @property 156 def handle_unknown(self): 157 """The handle unknown permissions setting (allow,deny,reject)""" 158 return self.policy.handle_unknown() 159 160 @property 161 def mls(self): 162 """(T/F) The policy has MLS enabled.""" 163 return mls.enabled(self.policy) 164 165 @property 166 def version(self): 167 """The policy database version (e.g. v29)""" 168 return self.policy.version() 169 170 # 171 # Policy statistics 172 # 173 174 @property 175 def allow_count(self): 176 """The number of (type) allow rules.""" 177 return self.policy.avrule_allow_count() 178 179 @property 180 def auditallow_count(self): 181 """The number of auditallow rules.""" 182 return self.policy.avrule_auditallow_count() 183 184 @property 185 def boolean_count(self): 186 """The number of Booleans.""" 187 return self.policy.bool_count() 188 189 @property 190 def category_count(self): 191 """The number of categories.""" 192 return sum(1 for _ in self.categories()) 193 194 @property 195 def class_count(self): 196 """The number of object classes.""" 197 return self.policy.class_count() 198 199 @property 200 def common_count(self): 201 """The number of common permission sets.""" 202 return self.policy.common_count() 203 204 @property 205 def conditional_count(self): 206 """The number of conditionals.""" 207 return self.policy.cond_count() 208 209 @property 210 def constraint_count(self): 211 """The number of standard constraints.""" 212 return sum(1 for c in self.constraints() if c.ruletype == "constrain") 213 214 @property 215 def dontaudit_count(self): 216 """The number of dontaudit rules.""" 217 return self.policy.avrule_dontaudit_count() 218 219 @property 220 def fs_use_count(self): 221 """fs_use_* statements.""" 222 return self.policy.fs_use_count() 223 224 @property 225 def genfscon_count(self): 226 """The number of genfscon statements.""" 227 return self.policy.genfscon_count() 228 229 @property 230 def initialsids_count(self): 231 """The number of initial sid statements.""" 232 return self.policy.isid_count() 233 234 @property 235 def level_count(self): 236 """The number of levels.""" 237 return sum(1 for _ in self.levels()) 238 239 @property 240 def mlsconstraint_count(self): 241 """The number of MLS constraints.""" 242 return sum(1 for c in self.constraints() if c.ruletype == "mlsconstrain") 243 244 @property 245 def mlsvalidatetrans_count(self): 246 """The number of MLS validatetrans.""" 247 return sum(1 for v in self.constraints() if v.ruletype == "mlsvalidatetrans") 248 249 @property 250 def netifcon_count(self): 251 """The number of netifcon statements.""" 252 return self.policy.netifcon_count() 253 254 @property 255 def neverallow_count(self): 256 """The number of neverallow rules.""" 257 return self.policy.avrule_neverallow_count() 258 259 @property 260 def nodecon_count(self): 261 """The number of nodecon statements.""" 262 return self.policy.nodecon_count() 263 264 @property 265 def permission_count(self): 266 """The number of permissions.""" 267 return sum(len(c.perms) for c in chain(self.commons(), self.classes())) 268 269 @property 270 def permissives_count(self): 271 """The number of permissive types.""" 272 return self.policy.permissive_count() 273 274 @property 275 def polcap_count(self): 276 """The number of policy capabilities.""" 277 return self.policy.polcap_count() 278 279 @property 280 def portcon_count(self): 281 """The number of portcon statements.""" 282 return self.policy.portcon_count() 283 284 @property 285 def range_transition_count(self): 286 """The number of range_transition rules.""" 287 return self.policy.range_trans_count() 288 289 @property 290 def role_count(self): 291 """The number of roles.""" 292 return self.policy.role_count() 293 294 @property 295 def role_allow_count(self): 296 """The number of (role) allow rules.""" 297 return self.policy.role_allow_count() 298 299 @property 300 def role_transition_count(self): 301 """The number of role_transition rules.""" 302 return self.policy.role_trans_count() 303 304 @property 305 def type_attribute_count(self): 306 """The number of (type) attributes.""" 307 return sum(1 for _ in self.typeattributes()) 308 309 @property 310 def type_count(self): 311 """The number of types.""" 312 return sum(1 for _ in self.types()) 313 314 @property 315 def type_change_count(self): 316 """The number of type_change rules.""" 317 return self.policy.terule_change_count() 318 319 @property 320 def type_member_count(self): 321 """The number of type_member rules.""" 322 return self.policy.terule_member_count() 323 324 @property 325 def type_transition_count(self): 326 """The number of type_transition rules.""" 327 return self.policy.terule_trans_count() + self.policy.filename_trans_count() 328 329 @property 330 def user_count(self): 331 """The number of users.""" 332 return self.policy.user_count() 333 334 @property 335 def validatetrans_count(self): 336 """The number of validatetrans.""" 337 return sum(1 for v in self.constraints() if v.ruletype == "validatetrans") 338 339 # 340 # Policy components lookup functions 341 # 342 def lookup_boolean(self, name): 343 """Look up a Boolean.""" 344 return boolcond.boolean_factory(self.policy, name) 345 346 def lookup_class(self, name): 347 """Look up an object class.""" 348 return objclass.class_factory(self.policy, name) 349 350 def lookup_common(self, name): 351 """Look up a common permission set.""" 352 return objclass.common_factory(self.policy, name) 353 354 def lookup_initialsid(self, name): 355 """Look up an initial sid.""" 356 return initsid.initialsid_factory(self.policy, name) 357 358 def lookup_level(self, level): 359 """Look up a MLS level.""" 360 return mls.level_factory(self.policy, level) 361 362 def lookup_sensitivity(self, name): 363 """Look up a MLS sensitivity by name.""" 364 return mls.sensitivity_factory(self.policy, name) 365 366 def lookup_range(self, range_): 367 """Look up a MLS range.""" 368 return mls.range_factory(self.policy, range_) 369 370 def lookup_role(self, name): 371 """Look up a role by name.""" 372 return role.role_factory(self.policy, name) 373 374 def lookup_type(self, name): 375 """Look up a type by name.""" 376 return typeattr.type_factory(self.policy, name, deref=True) 377 378 def lookup_type_or_attr(self, name): 379 """Look up a type or type attribute by name.""" 380 return typeattr.type_or_attr_factory(self.policy, name, deref=True) 381 382 def lookup_typeattr(self, name): 383 """Look up a type attribute by name.""" 384 return typeattr.attribute_factory(self.policy, name) 385 386 def lookup_user(self, name): 387 """Look up a user by name.""" 388 return user.user_factory(self.policy, name) 389 390 # 391 # Policy components generators 392 # 393 394 def bools(self): 395 """Generator which yields all Booleans.""" 396 for bool_ in self.policy.bool_iter(): 397 yield boolcond.boolean_factory(self.policy, bool_) 398 399 def categories(self): 400 """Generator which yields all MLS categories.""" 401 for cat in self.policy.cat_iter(): 402 try: 403 yield mls.category_factory(self.policy, cat) 404 except TypeError: 405 # libqpol unfortunately iterates over aliases too 406 pass 407 408 def classes(self): 409 """Generator which yields all object classes.""" 410 for class_ in self.policy.class_iter(): 411 yield objclass.class_factory(self.policy, class_) 412 413 def commons(self): 414 """Generator which yields all commons.""" 415 for common in self.policy.common_iter(): 416 yield objclass.common_factory(self.policy, common) 417 418 def defaults(self): 419 """Generator which yields all default_* statements.""" 420 for default_ in self.policy.default_iter(): 421 try: 422 for default_obj in default.default_factory(self.policy, default_): 423 yield default_obj 424 except exception.NoDefaults: 425 # qpol iterates over all classes. Handle case 426 # where a class has no default_* settings. 427 pass 428 429 def levels(self): 430 """Generator which yields all level declarations.""" 431 for level in self.policy.level_iter(): 432 433 try: 434 yield mls.level_decl_factory(self.policy, level) 435 except TypeError: 436 # libqpol unfortunately iterates over levels and sens aliases 437 pass 438 439 def polcaps(self): 440 """Generator which yields all policy capabilities.""" 441 for cap in self.policy.polcap_iter(): 442 yield polcap.polcap_factory(self.policy, cap) 443 444 def roles(self): 445 """Generator which yields all roles.""" 446 for role_ in self.policy.role_iter(): 447 yield role.role_factory(self.policy, role_) 448 449 def sensitivities(self): 450 """Generator which yields all sensitivities.""" 451 # see mls.py for more info on why level_iter is used here. 452 for sens in self.policy.level_iter(): 453 try: 454 yield mls.sensitivity_factory(self.policy, sens) 455 except TypeError: 456 # libqpol unfortunately iterates over sens and aliases 457 pass 458 459 def types(self): 460 """Generator which yields all types.""" 461 for type_ in self.policy.type_iter(): 462 try: 463 yield typeattr.type_factory(self.policy, type_) 464 except TypeError: 465 # libqpol unfortunately iterates over attributes and aliases 466 pass 467 468 def typeattributes(self): 469 """Generator which yields all (type) attributes.""" 470 for type_ in self.policy.type_iter(): 471 try: 472 yield typeattr.attribute_factory(self.policy, type_) 473 except TypeError: 474 # libqpol unfortunately iterates over attributes and aliases 475 pass 476 477 def users(self): 478 """Generator which yields all users.""" 479 for user_ in self.policy.user_iter(): 480 yield user.user_factory(self.policy, user_) 481 482 # 483 # Policy rules generators 484 # 485 def mlsrules(self): 486 """Generator which yields all MLS rules.""" 487 for rule in self.policy.range_trans_iter(): 488 yield mlsrule.mls_rule_factory(self.policy, rule) 489 490 def rbacrules(self): 491 """Generator which yields all RBAC rules.""" 492 for rule in chain(self.policy.role_allow_iter(), 493 self.policy.role_trans_iter()): 494 yield rbacrule.rbac_rule_factory(self.policy, rule) 495 496 def terules(self): 497 """Generator which yields all type enforcement rules.""" 498 for rule in chain(self.policy.avrule_iter(), 499 self.policy.terule_iter(), 500 self.policy.filename_trans_iter()): 501 yield terule.te_rule_factory(self.policy, rule) 502 503 # 504 # Policy rule type validators 505 # 506 @staticmethod 507 def validate_constraint_ruletype(types): 508 """Validate constraint types.""" 509 constraint.validate_ruletype(types) 510 511 @staticmethod 512 def validate_mls_ruletype(types): 513 """Validate MLS rule types.""" 514 mlsrule.validate_ruletype(types) 515 516 @staticmethod 517 def validate_rbac_ruletype(types): 518 """Validate RBAC rule types.""" 519 rbacrule.validate_ruletype(types) 520 521 @staticmethod 522 def validate_te_ruletype(types): 523 """Validate type enforcement rule types.""" 524 terule.validate_ruletype(types) 525 526 # 527 # Constraints generators 528 # 529 530 def constraints(self): 531 """Generator which yields all constraints (regular and MLS).""" 532 for constraint_ in chain(self.policy.constraint_iter(), 533 self.policy.validatetrans_iter()): 534 535 yield constraint.constraint_factory(self.policy, constraint_) 536 537 # 538 # In-policy Labeling statement generators 539 # 540 def fs_uses(self): 541 """Generator which yields all fs_use_* statements.""" 542 for fs_use in self.policy.fs_use_iter(): 543 yield fscontext.fs_use_factory(self.policy, fs_use) 544 545 def genfscons(self): 546 """Generator which yields all genfscon statements.""" 547 for fscon in self.policy.genfscon_iter(): 548 yield fscontext.genfscon_factory(self.policy, fscon) 549 550 def initialsids(self): 551 """Generator which yields all initial SID statements.""" 552 for sid in self.policy.isid_iter(): 553 yield initsid.initialsid_factory(self.policy, sid) 554 555 def netifcons(self): 556 """Generator which yields all netifcon statements.""" 557 for ifcon in self.policy.netifcon_iter(): 558 yield netcontext.netifcon_factory(self.policy, ifcon) 559 560 def nodecons(self): 561 """Generator which yields all nodecon statements.""" 562 for node in self.policy.nodecon_iter(): 563 yield netcontext.nodecon_factory(self.policy, node) 564 565 def portcons(self): 566 """Generator which yields all portcon statements.""" 567 for port in self.policy.portcon_iter(): 568 yield netcontext.portcon_factory(self.policy, port) 569