Home | History | Annotate | Download | only in policyrep
      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