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         # 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