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 # try libselinux for current policy 128 if selinux.selinuxfs_exists(): 129 yield selinux.selinux_current_policy_path() 130 131 # otherwise look through the supported policy versions 132 base_policy_path = selinux.selinux_binary_policy_path() 133 for version in range(qpol.QPOL_POLICY_MAX_VERSION, qpol.QPOL_POLICY_MIN_VERSION-1, -1): 134 yield "{0}.{1}".format(base_policy_path, version) 135 136 def _load_running_policy(self): 137 """Try to load the current running policy.""" 138 self.log.info("Attempting to locate current running policy.") 139 140 for filename in self._potential_policies(): 141 try: 142 self._load_policy(filename) 143 except OSError as err: 144 if err.errno != ENOENT: 145 raise 146 else: 147 break 148 else: 149 raise RuntimeError("Unable to locate an SELinux policy to load.") 150 151 # 152 # Policy properties 153 # 154 @property 155 def handle_unknown(self): 156 """The handle unknown permissions setting (allow,deny,reject)""" 157 return self.policy.handle_unknown() 158 159 @property 160 def mls(self): 161 """(T/F) The policy has MLS enabled.""" 162 return mls.enabled(self.policy) 163 164 @property 165 def version(self): 166 """The policy database version (e.g. v29)""" 167 return self.policy.version() 168 169 # 170 # Policy statistics 171 # 172 173 @property 174 def allow_count(self): 175 """The number of (type) allow rules.""" 176 return self.policy.avrule_allow_count() 177 178 @property 179 def auditallow_count(self): 180 """The number of auditallow rules.""" 181 return self.policy.avrule_auditallow_count() 182 183 @property 184 def boolean_count(self): 185 """The number of Booleans.""" 186 return self.policy.bool_count() 187 188 @property 189 def category_count(self): 190 """The number of categories.""" 191 return sum(1 for _ in self.categories()) 192 193 @property 194 def class_count(self): 195 """The number of object classes.""" 196 return self.policy.class_count() 197 198 @property 199 def common_count(self): 200 """The number of common permission sets.""" 201 return self.policy.common_count() 202 203 @property 204 def conditional_count(self): 205 """The number of conditionals.""" 206 return self.policy.cond_count() 207 208 @property 209 def constraint_count(self): 210 """The number of standard constraints.""" 211 return sum(1 for c in self.constraints() if c.ruletype == "constrain") 212 213 @property 214 def default_count(self): 215 """The number of default_* rules.""" 216 return sum(1 for d in self.defaults()) 217 218 @property 219 def dontaudit_count(self): 220 """The number of dontaudit rules.""" 221 return self.policy.avrule_dontaudit_count() 222 223 @property 224 def fs_use_count(self): 225 """fs_use_* statements.""" 226 return self.policy.fs_use_count() 227 228 @property 229 def genfscon_count(self): 230 """The number of genfscon statements.""" 231 return self.policy.genfscon_count() 232 233 @property 234 def initialsids_count(self): 235 """The number of initial sid statements.""" 236 return self.policy.isid_count() 237 238 @property 239 def level_count(self): 240 """The number of levels.""" 241 return sum(1 for _ in self.levels()) 242 243 @property 244 def mlsconstraint_count(self): 245 """The number of MLS constraints.""" 246 return sum(1 for c in self.constraints() if c.ruletype == "mlsconstrain") 247 248 @property 249 def mlsvalidatetrans_count(self): 250 """The number of MLS validatetrans.""" 251 return sum(1 for v in self.constraints() if v.ruletype == "mlsvalidatetrans") 252 253 @property 254 def netifcon_count(self): 255 """The number of netifcon statements.""" 256 return self.policy.netifcon_count() 257 258 @property 259 def neverallow_count(self): 260 """The number of neverallow rules.""" 261 return self.policy.avrule_neverallow_count() 262 263 @property 264 def nodecon_count(self): 265 """The number of nodecon statements.""" 266 return self.policy.nodecon_count() 267 268 @property 269 def permission_count(self): 270 """The number of permissions.""" 271 return sum(len(c.perms) for c in chain(self.commons(), self.classes())) 272 273 @property 274 def permissives_count(self): 275 """The number of permissive types.""" 276 return self.policy.permissive_count() 277 278 @property 279 def polcap_count(self): 280 """The number of policy capabilities.""" 281 return self.policy.polcap_count() 282 283 @property 284 def portcon_count(self): 285 """The number of portcon statements.""" 286 return self.policy.portcon_count() 287 288 @property 289 def range_transition_count(self): 290 """The number of range_transition rules.""" 291 return self.policy.range_trans_count() 292 293 @property 294 def role_count(self): 295 """The number of roles.""" 296 return self.policy.role_count() 297 298 @property 299 def role_allow_count(self): 300 """The number of (role) allow rules.""" 301 return self.policy.role_allow_count() 302 303 @property 304 def role_transition_count(self): 305 """The number of role_transition rules.""" 306 return self.policy.role_trans_count() 307 308 @property 309 def type_attribute_count(self): 310 """The number of (type) attributes.""" 311 return sum(1 for _ in self.typeattributes()) 312 313 @property 314 def type_count(self): 315 """The number of types.""" 316 return sum(1 for _ in self.types()) 317 318 @property 319 def type_change_count(self): 320 """The number of type_change rules.""" 321 return self.policy.terule_change_count() 322 323 @property 324 def type_member_count(self): 325 """The number of type_member rules.""" 326 return self.policy.terule_member_count() 327 328 @property 329 def type_transition_count(self): 330 """The number of type_transition rules.""" 331 return self.policy.terule_trans_count() + self.policy.filename_trans_count() 332 333 @property 334 def user_count(self): 335 """The number of users.""" 336 return self.policy.user_count() 337 338 @property 339 def validatetrans_count(self): 340 """The number of validatetrans.""" 341 return sum(1 for v in self.constraints() if v.ruletype == "validatetrans") 342 343 # 344 # Policy components lookup functions 345 # 346 def lookup_boolean(self, name): 347 """Look up a Boolean.""" 348 return boolcond.boolean_factory(self.policy, name) 349 350 def lookup_class(self, name): 351 """Look up an object class.""" 352 return objclass.class_factory(self.policy, name) 353 354 def lookup_common(self, name): 355 """Look up a common permission set.""" 356 return objclass.common_factory(self.policy, name) 357 358 def lookup_initialsid(self, name): 359 """Look up an initial sid.""" 360 return initsid.initialsid_factory(self.policy, name) 361 362 def lookup_level(self, level): 363 """Look up a MLS level.""" 364 return mls.level_factory(self.policy, level) 365 366 def lookup_sensitivity(self, name): 367 """Look up a MLS sensitivity by name.""" 368 return mls.sensitivity_factory(self.policy, name) 369 370 def lookup_range(self, range_): 371 """Look up a MLS range.""" 372 return mls.range_factory(self.policy, range_) 373 374 def lookup_role(self, name): 375 """Look up a role by name.""" 376 return role.role_factory(self.policy, name) 377 378 def lookup_type(self, name): 379 """Look up a type by name.""" 380 return typeattr.type_factory(self.policy, name, deref=True) 381 382 def lookup_type_or_attr(self, name): 383 """Look up a type or type attribute by name.""" 384 return typeattr.type_or_attr_factory(self.policy, name, deref=True) 385 386 def lookup_typeattr(self, name): 387 """Look up a type attribute by name.""" 388 return typeattr.attribute_factory(self.policy, name) 389 390 def lookup_user(self, name): 391 """Look up a user by name.""" 392 return user.user_factory(self.policy, name) 393 394 # 395 # Policy components generators 396 # 397 398 def bools(self): 399 """Generator which yields all Booleans.""" 400 for bool_ in self.policy.bool_iter(): 401 yield boolcond.boolean_factory(self.policy, bool_) 402 403 def categories(self): 404 """Generator which yields all MLS categories.""" 405 for cat in self.policy.cat_iter(): 406 try: 407 yield mls.category_factory(self.policy, cat) 408 except TypeError: 409 # libqpol unfortunately iterates over aliases too 410 pass 411 412 def classes(self): 413 """Generator which yields all object classes.""" 414 for class_ in self.policy.class_iter(): 415 yield objclass.class_factory(self.policy, class_) 416 417 def commons(self): 418 """Generator which yields all commons.""" 419 for common in self.policy.common_iter(): 420 yield objclass.common_factory(self.policy, common) 421 422 def defaults(self): 423 """Generator which yields all default_* statements.""" 424 for default_ in self.policy.default_iter(): 425 try: 426 for default_obj in default.default_factory(self.policy, default_): 427 yield default_obj 428 except exception.NoDefaults: 429 # qpol iterates over all classes. Handle case 430 # where a class has no default_* settings. 431 pass 432 433 def levels(self): 434 """Generator which yields all level declarations.""" 435 for level in self.policy.level_iter(): 436 437 try: 438 yield mls.level_decl_factory(self.policy, level) 439 except TypeError: 440 # libqpol unfortunately iterates over levels and sens aliases 441 pass 442 443 def polcaps(self): 444 """Generator which yields all policy capabilities.""" 445 for cap in self.policy.polcap_iter(): 446 yield polcap.polcap_factory(self.policy, cap) 447 448 def roles(self): 449 """Generator which yields all roles.""" 450 for role_ in self.policy.role_iter(): 451 yield role.role_factory(self.policy, role_) 452 453 def sensitivities(self): 454 """Generator which yields all sensitivities.""" 455 # see mls.py for more info on why level_iter is used here. 456 for sens in self.policy.level_iter(): 457 try: 458 yield mls.sensitivity_factory(self.policy, sens) 459 except TypeError: 460 # libqpol unfortunately iterates over sens and aliases 461 pass 462 463 def types(self): 464 """Generator which yields all types.""" 465 for type_ in self.policy.type_iter(): 466 try: 467 yield typeattr.type_factory(self.policy, type_) 468 except TypeError: 469 # libqpol unfortunately iterates over attributes and aliases 470 pass 471 472 def typeattributes(self): 473 """Generator which yields all (type) attributes.""" 474 for type_ in self.policy.type_iter(): 475 try: 476 yield typeattr.attribute_factory(self.policy, type_) 477 except TypeError: 478 # libqpol unfortunately iterates over attributes and aliases 479 pass 480 481 def users(self): 482 """Generator which yields all users.""" 483 for user_ in self.policy.user_iter(): 484 yield user.user_factory(self.policy, user_) 485 486 # 487 # Policy rules generators 488 # 489 def mlsrules(self): 490 """Generator which yields all MLS rules.""" 491 for rule in self.policy.range_trans_iter(): 492 yield mlsrule.mls_rule_factory(self.policy, rule) 493 494 def rbacrules(self): 495 """Generator which yields all RBAC rules.""" 496 for rule in chain(self.policy.role_allow_iter(), 497 self.policy.role_trans_iter()): 498 yield rbacrule.rbac_rule_factory(self.policy, rule) 499 500 def terules(self): 501 """Generator which yields all type enforcement rules.""" 502 for rule in chain(self.policy.avrule_iter(), 503 self.policy.terule_iter(), 504 self.policy.filename_trans_iter()): 505 yield terule.te_rule_factory(self.policy, rule) 506 507 # 508 # Policy rule type validators 509 # 510 @staticmethod 511 def validate_constraint_ruletype(types): 512 """Validate constraint types.""" 513 return constraint.validate_ruletype(types) 514 515 @staticmethod 516 def validate_default_ruletype(types): 517 """Validate default_* types.""" 518 return default.validate_ruletype(types) 519 520 @staticmethod 521 def validate_default_value(value): 522 """Validate default_* values.""" 523 return default.validate_default_value(value) 524 525 @staticmethod 526 def validate_default_range(value): 527 """Validate default_range range.""" 528 return default.validate_default_range(value) 529 530 @staticmethod 531 def validate_fs_use_ruletype(types): 532 """Validate fs_use_* rule types.""" 533 return fscontext.validate_ruletype(types) 534 535 @staticmethod 536 def validate_mls_ruletype(types): 537 """Validate MLS rule types.""" 538 return mlsrule.validate_ruletype(types) 539 540 @staticmethod 541 def validate_rbac_ruletype(types): 542 """Validate RBAC rule types.""" 543 return rbacrule.validate_ruletype(types) 544 545 @staticmethod 546 def validate_te_ruletype(types): 547 """Validate type enforcement rule types.""" 548 return terule.validate_ruletype(types) 549 550 # 551 # Constraints generators 552 # 553 554 def constraints(self): 555 """Generator which yields all constraints (regular and MLS).""" 556 for constraint_ in chain(self.policy.constraint_iter(), 557 self.policy.validatetrans_iter()): 558 559 yield constraint.constraint_factory(self.policy, constraint_) 560 561 # 562 # In-policy Labeling statement generators 563 # 564 def fs_uses(self): 565 """Generator which yields all fs_use_* statements.""" 566 for fs_use in self.policy.fs_use_iter(): 567 yield fscontext.fs_use_factory(self.policy, fs_use) 568 569 def genfscons(self): 570 """Generator which yields all genfscon statements.""" 571 for fscon in self.policy.genfscon_iter(): 572 yield fscontext.genfscon_factory(self.policy, fscon) 573 574 def initialsids(self): 575 """Generator which yields all initial SID statements.""" 576 for sid in self.policy.isid_iter(): 577 yield initsid.initialsid_factory(self.policy, sid) 578 579 def netifcons(self): 580 """Generator which yields all netifcon statements.""" 581 for ifcon in self.policy.netifcon_iter(): 582 yield netcontext.netifcon_factory(self.policy, ifcon) 583 584 def nodecons(self): 585 """Generator which yields all nodecon statements.""" 586 for node in self.policy.nodecon_iter(): 587 yield netcontext.nodecon_factory(self.policy, node) 588 589 def portcons(self): 590 """Generator which yields all portcon statements.""" 591 for port in self.policy.portcon_iter(): 592 yield netcontext.portcon_factory(self.policy, port) 593