mirror of
https://github.com/SELinuxProject/setools
synced 2025-04-01 22:58:12 +00:00
593 lines
18 KiB
Python
593 lines
18 KiB
Python
# Copyright 2014-2015, Tresys Technology, LLC
|
|
#
|
|
# This file is part of SETools.
|
|
#
|
|
# SETools is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License as
|
|
# published by the Free Software Foundation, either version 2.1 of
|
|
# the License, or (at your option) any later version.
|
|
#
|
|
# SETools is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with SETools. If not, see
|
|
# <http://www.gnu.org/licenses/>.
|
|
#
|
|
# pylint: disable=too-many-public-methods
|
|
#
|
|
# Create a Python representation of the policy.
|
|
# The idea is that this is module provides convenient
|
|
# abstractions and methods for accessing the policy
|
|
# structures.
|
|
import logging
|
|
from itertools import chain
|
|
from errno import ENOENT
|
|
|
|
try:
|
|
import selinux
|
|
except ImportError:
|
|
pass
|
|
|
|
from . import qpol
|
|
|
|
# The libqpol SWIG class is not quite natural for
|
|
# Python the policy is repeatedly referenced in the
|
|
# function calls, which makes sense for C code
|
|
# but not for python code, so each object keeps
|
|
# a reference to the policy for internal use.
|
|
# This also makes sense since an object would only
|
|
# be valid for the policy it comes from.
|
|
|
|
# Exceptions
|
|
from . import exception
|
|
|
|
# Components
|
|
from . import boolcond
|
|
from . import default
|
|
from . import mls
|
|
from . import objclass
|
|
from . import polcap
|
|
from . import role
|
|
from . import typeattr
|
|
from . import user
|
|
|
|
# Rules
|
|
from . import mlsrule
|
|
from . import rbacrule
|
|
from . import terule
|
|
|
|
# Constraints
|
|
from . import constraint
|
|
|
|
# In-policy Labeling
|
|
from . import fscontext
|
|
from . import initsid
|
|
from . import netcontext
|
|
|
|
|
|
class SELinuxPolicy(object):
|
|
|
|
"""The complete SELinux policy."""
|
|
|
|
def __init__(self, policyfile=None):
|
|
"""
|
|
Parameter:
|
|
policyfile Path to a policy to open.
|
|
"""
|
|
|
|
self.log = logging.getLogger(self.__class__.__name__)
|
|
self.policy = None
|
|
self.filename = None
|
|
|
|
if policyfile:
|
|
self._load_policy(policyfile)
|
|
else:
|
|
try:
|
|
self._load_running_policy()
|
|
except NameError:
|
|
raise RuntimeError("Loading the running policy requires libselinux Python bindings")
|
|
|
|
def __repr__(self):
|
|
return "<SELinuxPolicy(\"{0}\")>".format(self.filename)
|
|
|
|
def __str__(self):
|
|
return self.filename
|
|
|
|
def __deepcopy__(self, memo):
|
|
# shallow copy as all of the members are immutable
|
|
newobj = SELinuxPolicy.__new__(SELinuxPolicy)
|
|
newobj.policy = self.policy
|
|
newobj.filename = self.filename
|
|
memo[id(self)] = newobj
|
|
return newobj
|
|
|
|
#
|
|
# Policy loading functions
|
|
#
|
|
|
|
def _load_policy(self, filename):
|
|
"""Load the specified policy."""
|
|
self.log.info("Opening SELinux policy \"{0}\"".format(filename))
|
|
|
|
try:
|
|
self.policy = qpol.qpol_policy_factory(str(filename))
|
|
except SyntaxError as err:
|
|
raise exception.InvalidPolicy("Error opening policy file \"{0}\": {1}".
|
|
format(filename, err))
|
|
|
|
self.log.info("Successfully opened SELinux policy \"{0}\"".format(filename))
|
|
self.filename = filename
|
|
|
|
@staticmethod
|
|
def _potential_policies():
|
|
"""Generate a list of potential policies to use."""
|
|
# try libselinux for current policy
|
|
if selinux.selinuxfs_exists():
|
|
yield selinux.selinux_current_policy_path()
|
|
|
|
# otherwise look through the supported policy versions
|
|
base_policy_path = selinux.selinux_binary_policy_path()
|
|
for version in range(qpol.QPOL_POLICY_MAX_VERSION, qpol.QPOL_POLICY_MIN_VERSION-1, -1):
|
|
yield "{0}.{1}".format(base_policy_path, version)
|
|
|
|
def _load_running_policy(self):
|
|
"""Try to load the current running policy."""
|
|
self.log.info("Attempting to locate current running policy.")
|
|
|
|
for filename in self._potential_policies():
|
|
try:
|
|
self._load_policy(filename)
|
|
except OSError as err:
|
|
if err.errno != ENOENT:
|
|
raise
|
|
else:
|
|
break
|
|
else:
|
|
raise RuntimeError("Unable to locate an SELinux policy to load.")
|
|
|
|
#
|
|
# Policy properties
|
|
#
|
|
@property
|
|
def handle_unknown(self):
|
|
"""The handle unknown permissions setting (allow,deny,reject)"""
|
|
return self.policy.handle_unknown()
|
|
|
|
@property
|
|
def mls(self):
|
|
"""(T/F) The policy has MLS enabled."""
|
|
return mls.enabled(self.policy)
|
|
|
|
@property
|
|
def version(self):
|
|
"""The policy database version (e.g. v29)"""
|
|
return self.policy.version()
|
|
|
|
#
|
|
# Policy statistics
|
|
#
|
|
|
|
@property
|
|
def allow_count(self):
|
|
"""The number of (type) allow rules."""
|
|
return self.policy.avrule_allow_count()
|
|
|
|
@property
|
|
def auditallow_count(self):
|
|
"""The number of auditallow rules."""
|
|
return self.policy.avrule_auditallow_count()
|
|
|
|
@property
|
|
def boolean_count(self):
|
|
"""The number of Booleans."""
|
|
return self.policy.bool_count()
|
|
|
|
@property
|
|
def category_count(self):
|
|
"""The number of categories."""
|
|
return sum(1 for _ in self.categories())
|
|
|
|
@property
|
|
def class_count(self):
|
|
"""The number of object classes."""
|
|
return self.policy.class_count()
|
|
|
|
@property
|
|
def common_count(self):
|
|
"""The number of common permission sets."""
|
|
return self.policy.common_count()
|
|
|
|
@property
|
|
def conditional_count(self):
|
|
"""The number of conditionals."""
|
|
return self.policy.cond_count()
|
|
|
|
@property
|
|
def constraint_count(self):
|
|
"""The number of standard constraints."""
|
|
return sum(1 for c in self.constraints() if c.ruletype == "constrain")
|
|
|
|
@property
|
|
def default_count(self):
|
|
"""The number of default_* rules."""
|
|
return sum(1 for d in self.defaults())
|
|
|
|
@property
|
|
def dontaudit_count(self):
|
|
"""The number of dontaudit rules."""
|
|
return self.policy.avrule_dontaudit_count()
|
|
|
|
@property
|
|
def fs_use_count(self):
|
|
"""fs_use_* statements."""
|
|
return self.policy.fs_use_count()
|
|
|
|
@property
|
|
def genfscon_count(self):
|
|
"""The number of genfscon statements."""
|
|
return self.policy.genfscon_count()
|
|
|
|
@property
|
|
def initialsids_count(self):
|
|
"""The number of initial sid statements."""
|
|
return self.policy.isid_count()
|
|
|
|
@property
|
|
def level_count(self):
|
|
"""The number of levels."""
|
|
return sum(1 for _ in self.levels())
|
|
|
|
@property
|
|
def mlsconstraint_count(self):
|
|
"""The number of MLS constraints."""
|
|
return sum(1 for c in self.constraints() if c.ruletype == "mlsconstrain")
|
|
|
|
@property
|
|
def mlsvalidatetrans_count(self):
|
|
"""The number of MLS validatetrans."""
|
|
return sum(1 for v in self.constraints() if v.ruletype == "mlsvalidatetrans")
|
|
|
|
@property
|
|
def netifcon_count(self):
|
|
"""The number of netifcon statements."""
|
|
return self.policy.netifcon_count()
|
|
|
|
@property
|
|
def neverallow_count(self):
|
|
"""The number of neverallow rules."""
|
|
return self.policy.avrule_neverallow_count()
|
|
|
|
@property
|
|
def nodecon_count(self):
|
|
"""The number of nodecon statements."""
|
|
return self.policy.nodecon_count()
|
|
|
|
@property
|
|
def permission_count(self):
|
|
"""The number of permissions."""
|
|
return sum(len(c.perms) for c in chain(self.commons(), self.classes()))
|
|
|
|
@property
|
|
def permissives_count(self):
|
|
"""The number of permissive types."""
|
|
return self.policy.permissive_count()
|
|
|
|
@property
|
|
def polcap_count(self):
|
|
"""The number of policy capabilities."""
|
|
return self.policy.polcap_count()
|
|
|
|
@property
|
|
def portcon_count(self):
|
|
"""The number of portcon statements."""
|
|
return self.policy.portcon_count()
|
|
|
|
@property
|
|
def range_transition_count(self):
|
|
"""The number of range_transition rules."""
|
|
return self.policy.range_trans_count()
|
|
|
|
@property
|
|
def role_count(self):
|
|
"""The number of roles."""
|
|
return self.policy.role_count()
|
|
|
|
@property
|
|
def role_allow_count(self):
|
|
"""The number of (role) allow rules."""
|
|
return self.policy.role_allow_count()
|
|
|
|
@property
|
|
def role_transition_count(self):
|
|
"""The number of role_transition rules."""
|
|
return self.policy.role_trans_count()
|
|
|
|
@property
|
|
def type_attribute_count(self):
|
|
"""The number of (type) attributes."""
|
|
return sum(1 for _ in self.typeattributes())
|
|
|
|
@property
|
|
def type_count(self):
|
|
"""The number of types."""
|
|
return sum(1 for _ in self.types())
|
|
|
|
@property
|
|
def type_change_count(self):
|
|
"""The number of type_change rules."""
|
|
return self.policy.terule_change_count()
|
|
|
|
@property
|
|
def type_member_count(self):
|
|
"""The number of type_member rules."""
|
|
return self.policy.terule_member_count()
|
|
|
|
@property
|
|
def type_transition_count(self):
|
|
"""The number of type_transition rules."""
|
|
return self.policy.terule_trans_count() + self.policy.filename_trans_count()
|
|
|
|
@property
|
|
def user_count(self):
|
|
"""The number of users."""
|
|
return self.policy.user_count()
|
|
|
|
@property
|
|
def validatetrans_count(self):
|
|
"""The number of validatetrans."""
|
|
return sum(1 for v in self.constraints() if v.ruletype == "validatetrans")
|
|
|
|
#
|
|
# Policy components lookup functions
|
|
#
|
|
def lookup_boolean(self, name):
|
|
"""Look up a Boolean."""
|
|
return boolcond.boolean_factory(self.policy, name)
|
|
|
|
def lookup_class(self, name):
|
|
"""Look up an object class."""
|
|
return objclass.class_factory(self.policy, name)
|
|
|
|
def lookup_common(self, name):
|
|
"""Look up a common permission set."""
|
|
return objclass.common_factory(self.policy, name)
|
|
|
|
def lookup_initialsid(self, name):
|
|
"""Look up an initial sid."""
|
|
return initsid.initialsid_factory(self.policy, name)
|
|
|
|
def lookup_level(self, level):
|
|
"""Look up a MLS level."""
|
|
return mls.level_factory(self.policy, level)
|
|
|
|
def lookup_sensitivity(self, name):
|
|
"""Look up a MLS sensitivity by name."""
|
|
return mls.sensitivity_factory(self.policy, name)
|
|
|
|
def lookup_range(self, range_):
|
|
"""Look up a MLS range."""
|
|
return mls.range_factory(self.policy, range_)
|
|
|
|
def lookup_role(self, name):
|
|
"""Look up a role by name."""
|
|
return role.role_factory(self.policy, name)
|
|
|
|
def lookup_type(self, name):
|
|
"""Look up a type by name."""
|
|
return typeattr.type_factory(self.policy, name, deref=True)
|
|
|
|
def lookup_type_or_attr(self, name):
|
|
"""Look up a type or type attribute by name."""
|
|
return typeattr.type_or_attr_factory(self.policy, name, deref=True)
|
|
|
|
def lookup_typeattr(self, name):
|
|
"""Look up a type attribute by name."""
|
|
return typeattr.attribute_factory(self.policy, name)
|
|
|
|
def lookup_user(self, name):
|
|
"""Look up a user by name."""
|
|
return user.user_factory(self.policy, name)
|
|
|
|
#
|
|
# Policy components generators
|
|
#
|
|
|
|
def bools(self):
|
|
"""Generator which yields all Booleans."""
|
|
for bool_ in self.policy.bool_iter():
|
|
yield boolcond.boolean_factory(self.policy, bool_)
|
|
|
|
def categories(self):
|
|
"""Generator which yields all MLS categories."""
|
|
for cat in self.policy.cat_iter():
|
|
try:
|
|
yield mls.category_factory(self.policy, cat)
|
|
except TypeError:
|
|
# libqpol unfortunately iterates over aliases too
|
|
pass
|
|
|
|
def classes(self):
|
|
"""Generator which yields all object classes."""
|
|
for class_ in self.policy.class_iter():
|
|
yield objclass.class_factory(self.policy, class_)
|
|
|
|
def commons(self):
|
|
"""Generator which yields all commons."""
|
|
for common in self.policy.common_iter():
|
|
yield objclass.common_factory(self.policy, common)
|
|
|
|
def defaults(self):
|
|
"""Generator which yields all default_* statements."""
|
|
for default_ in self.policy.default_iter():
|
|
try:
|
|
for default_obj in default.default_factory(self.policy, default_):
|
|
yield default_obj
|
|
except exception.NoDefaults:
|
|
# qpol iterates over all classes. Handle case
|
|
# where a class has no default_* settings.
|
|
pass
|
|
|
|
def levels(self):
|
|
"""Generator which yields all level declarations."""
|
|
for level in self.policy.level_iter():
|
|
|
|
try:
|
|
yield mls.level_decl_factory(self.policy, level)
|
|
except TypeError:
|
|
# libqpol unfortunately iterates over levels and sens aliases
|
|
pass
|
|
|
|
def polcaps(self):
|
|
"""Generator which yields all policy capabilities."""
|
|
for cap in self.policy.polcap_iter():
|
|
yield polcap.polcap_factory(self.policy, cap)
|
|
|
|
def roles(self):
|
|
"""Generator which yields all roles."""
|
|
for role_ in self.policy.role_iter():
|
|
yield role.role_factory(self.policy, role_)
|
|
|
|
def sensitivities(self):
|
|
"""Generator which yields all sensitivities."""
|
|
# see mls.py for more info on why level_iter is used here.
|
|
for sens in self.policy.level_iter():
|
|
try:
|
|
yield mls.sensitivity_factory(self.policy, sens)
|
|
except TypeError:
|
|
# libqpol unfortunately iterates over sens and aliases
|
|
pass
|
|
|
|
def types(self):
|
|
"""Generator which yields all types."""
|
|
for type_ in self.policy.type_iter():
|
|
try:
|
|
yield typeattr.type_factory(self.policy, type_)
|
|
except TypeError:
|
|
# libqpol unfortunately iterates over attributes and aliases
|
|
pass
|
|
|
|
def typeattributes(self):
|
|
"""Generator which yields all (type) attributes."""
|
|
for type_ in self.policy.type_iter():
|
|
try:
|
|
yield typeattr.attribute_factory(self.policy, type_)
|
|
except TypeError:
|
|
# libqpol unfortunately iterates over attributes and aliases
|
|
pass
|
|
|
|
def users(self):
|
|
"""Generator which yields all users."""
|
|
for user_ in self.policy.user_iter():
|
|
yield user.user_factory(self.policy, user_)
|
|
|
|
#
|
|
# Policy rules generators
|
|
#
|
|
def mlsrules(self):
|
|
"""Generator which yields all MLS rules."""
|
|
for rule in self.policy.range_trans_iter():
|
|
yield mlsrule.mls_rule_factory(self.policy, rule)
|
|
|
|
def rbacrules(self):
|
|
"""Generator which yields all RBAC rules."""
|
|
for rule in chain(self.policy.role_allow_iter(),
|
|
self.policy.role_trans_iter()):
|
|
yield rbacrule.rbac_rule_factory(self.policy, rule)
|
|
|
|
def terules(self):
|
|
"""Generator which yields all type enforcement rules."""
|
|
for rule in chain(self.policy.avrule_iter(),
|
|
self.policy.terule_iter(),
|
|
self.policy.filename_trans_iter()):
|
|
yield terule.te_rule_factory(self.policy, rule)
|
|
|
|
#
|
|
# Policy rule type validators
|
|
#
|
|
@staticmethod
|
|
def validate_constraint_ruletype(types):
|
|
"""Validate constraint types."""
|
|
return constraint.validate_ruletype(types)
|
|
|
|
@staticmethod
|
|
def validate_default_ruletype(types):
|
|
"""Validate default_* types."""
|
|
return default.validate_ruletype(types)
|
|
|
|
@staticmethod
|
|
def validate_default_value(value):
|
|
"""Validate default_* values."""
|
|
return default.validate_default_value(value)
|
|
|
|
@staticmethod
|
|
def validate_default_range(value):
|
|
"""Validate default_range range."""
|
|
return default.validate_default_range(value)
|
|
|
|
@staticmethod
|
|
def validate_fs_use_ruletype(types):
|
|
"""Validate fs_use_* rule types."""
|
|
return fscontext.validate_ruletype(types)
|
|
|
|
@staticmethod
|
|
def validate_mls_ruletype(types):
|
|
"""Validate MLS rule types."""
|
|
return mlsrule.validate_ruletype(types)
|
|
|
|
@staticmethod
|
|
def validate_rbac_ruletype(types):
|
|
"""Validate RBAC rule types."""
|
|
return rbacrule.validate_ruletype(types)
|
|
|
|
@staticmethod
|
|
def validate_te_ruletype(types):
|
|
"""Validate type enforcement rule types."""
|
|
return terule.validate_ruletype(types)
|
|
|
|
#
|
|
# Constraints generators
|
|
#
|
|
|
|
def constraints(self):
|
|
"""Generator which yields all constraints (regular and MLS)."""
|
|
for constraint_ in chain(self.policy.constraint_iter(),
|
|
self.policy.validatetrans_iter()):
|
|
|
|
yield constraint.constraint_factory(self.policy, constraint_)
|
|
|
|
#
|
|
# In-policy Labeling statement generators
|
|
#
|
|
def fs_uses(self):
|
|
"""Generator which yields all fs_use_* statements."""
|
|
for fs_use in self.policy.fs_use_iter():
|
|
yield fscontext.fs_use_factory(self.policy, fs_use)
|
|
|
|
def genfscons(self):
|
|
"""Generator which yields all genfscon statements."""
|
|
for fscon in self.policy.genfscon_iter():
|
|
yield fscontext.genfscon_factory(self.policy, fscon)
|
|
|
|
def initialsids(self):
|
|
"""Generator which yields all initial SID statements."""
|
|
for sid in self.policy.isid_iter():
|
|
yield initsid.initialsid_factory(self.policy, sid)
|
|
|
|
def netifcons(self):
|
|
"""Generator which yields all netifcon statements."""
|
|
for ifcon in self.policy.netifcon_iter():
|
|
yield netcontext.netifcon_factory(self.policy, ifcon)
|
|
|
|
def nodecons(self):
|
|
"""Generator which yields all nodecon statements."""
|
|
for node in self.policy.nodecon_iter():
|
|
yield netcontext.nodecon_factory(self.policy, node)
|
|
|
|
def portcons(self):
|
|
"""Generator which yields all portcon statements."""
|
|
for port in self.policy.portcon_iter():
|
|
yield netcontext.portcon_factory(self.policy, port)
|