summaryrefslogtreecommitdiff
path: root/lib/python2.7/site-packages/sepolgen/policygen.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/python2.7/site-packages/sepolgen/policygen.py')
-rw-r--r--lib/python2.7/site-packages/sepolgen/policygen.py400
1 files changed, 400 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/sepolgen/policygen.py b/lib/python2.7/site-packages/sepolgen/policygen.py
new file mode 100644
index 0000000..34c8401
--- /dev/null
+++ b/lib/python2.7/site-packages/sepolgen/policygen.py
@@ -0,0 +1,400 @@
+# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
+#
+# Copyright (C) 2006 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+classes and algorithms for the generation of SELinux policy.
+"""
+
+import itertools
+import textwrap
+
+import selinux.audit2why as audit2why
+try:
+ from setools import *
+except:
+ pass
+
+from . import refpolicy
+from . import objectmodel
+from . import access
+from . import interfaces
+from . import matching
+from . import util
+# Constants for the level of explanation from the generation
+# routines
+NO_EXPLANATION = 0
+SHORT_EXPLANATION = 1
+LONG_EXPLANATION = 2
+
+class PolicyGenerator:
+ """Generate a reference policy module from access vectors.
+
+ PolicyGenerator generates a new reference policy module
+ or updates an existing module based on requested access
+ in the form of access vectors.
+
+ It generates allow rules and optionally module require
+ statements and reference policy interfaces. By default
+ only allow rules are generated. The methods .set_gen_refpol
+ and .set_gen_requires turns on interface generation and
+ requires generation respectively.
+
+ PolicyGenerator can also optionally add comments explaining
+ why a particular access was allowed based on the audit
+ messages that generated the access. The access vectors
+ passed in must have the .audit_msgs field set correctly
+ and .explain set to SHORT|LONG_EXPLANATION to enable this
+ feature.
+
+ The module created by PolicyGenerator can be passed to
+ output.ModuleWriter to output a text representation.
+ """
+ def __init__(self, module=None):
+ """Initialize a PolicyGenerator with an optional
+ existing module.
+
+ If the module paramater is not None then access
+ will be added to the passed in module. Otherwise
+ a new reference policy module will be created.
+ """
+ self.ifgen = None
+ self.explain = NO_EXPLANATION
+ self.gen_requires = False
+ if module:
+ self.moduel = module
+ else:
+ self.module = refpolicy.Module()
+
+ self.dontaudit = False
+
+ self.domains = None
+ def set_gen_refpol(self, if_set=None, perm_maps=None):
+ """Set whether reference policy interfaces are generated.
+
+ To turn on interface generation pass in an interface set
+ to use for interface generation. To turn off interface
+ generation pass in None.
+
+ If interface generation is enabled requires generation
+ will also be enabled.
+ """
+ if if_set:
+ self.ifgen = InterfaceGenerator(if_set, perm_maps)
+ self.gen_requires = True
+ else:
+ self.ifgen = None
+ self.__set_module_style()
+
+
+ def set_gen_requires(self, status=True):
+ """Set whether module requires are generated.
+
+ Passing in true will turn on requires generation and
+ False will disable generation. If requires generation is
+ disabled interface generation will also be disabled and
+ can only be re-enabled via .set_gen_refpol.
+ """
+ self.gen_requires = status
+
+ def set_gen_explain(self, explain=SHORT_EXPLANATION):
+ """Set whether access is explained.
+ """
+ self.explain = explain
+
+ def set_gen_dontaudit(self, dontaudit):
+ self.dontaudit = dontaudit
+
+ def __set_module_style(self):
+ if self.ifgen:
+ refpolicy = True
+ else:
+ refpolicy = False
+ for mod in self.module.module_declarations():
+ mod.refpolicy = refpolicy
+
+ def set_module_name(self, name, version="1.0"):
+ """Set the name of the module and optionally the version.
+ """
+ # find an existing module declaration
+ m = None
+ for mod in self.module.module_declarations():
+ m = mod
+ if not m:
+ m = refpolicy.ModuleDeclaration()
+ self.module.children.insert(0, m)
+ m.name = name
+ m.version = version
+ if self.ifgen:
+ m.refpolicy = True
+ else:
+ m.refpolicy = False
+
+ def get_module(self):
+ # Generate the requires
+ if self.gen_requires:
+ gen_requires(self.module)
+
+ """Return the generated module"""
+ return self.module
+
+ def __add_allow_rules(self, avs):
+ for av in avs:
+ rule = refpolicy.AVRule(av)
+ if self.dontaudit:
+ rule.rule_type = rule.DONTAUDIT
+ rule.comment = ""
+ if self.explain:
+ rule.comment = str(refpolicy.Comment(explain_access(av, verbosity=self.explain)))
+ if av.type == audit2why.ALLOW:
+ rule.comment += "\n#!!!! This avc is allowed in the current policy"
+ if av.type == audit2why.DONTAUDIT:
+ rule.comment += "\n#!!!! This avc has a dontaudit rule in the current policy"
+
+ if av.type == audit2why.BOOLEAN:
+ if len(av.data) > 1:
+ rule.comment += "\n#!!!! This avc can be allowed using one of the these booleans:\n# %s" % ", ".join([x[0] for x in av.data])
+ else:
+ rule.comment += "\n#!!!! This avc can be allowed using the boolean '%s'" % av.data[0][0]
+
+ if av.type == audit2why.CONSTRAINT:
+ rule.comment += "\n#!!!! This avc is a constraint violation. You would need to modify the attributes of either the source or target types to allow this access."
+ rule.comment += "\n#Constraint rule: "
+ rule.comment += "\n#\t" + av.data[0]
+ for reason in av.data[1:]:
+ rule.comment += "\n#\tPossible cause is the source %s and target %s are different." % reason
+
+ try:
+ if ( av.type == audit2why.TERULE and
+ "write" in av.perms and
+ ( "dir" in av.obj_class or "open" in av.perms )):
+ if not self.domains:
+ self.domains = seinfo(ATTRIBUTE, name="domain")[0]["types"]
+ types=[]
+
+ for i in [x[TCONTEXT] for x in sesearch([ALLOW], {SCONTEXT: av.src_type, CLASS: av.obj_class, PERMS: av.perms})]:
+ if i not in self.domains:
+ types.append(i)
+ if len(types) == 1:
+ rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following type:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
+ elif len(types) >= 1:
+ rule.comment += "\n#!!!! The source type '%s' can write to a '%s' of the following types:\n# %s\n" % ( av.src_type, av.obj_class, ", ".join(types))
+ except:
+ pass
+ self.module.children.append(rule)
+
+
+ def add_access(self, av_set):
+ """Add the access from the access vector set to this
+ module.
+ """
+ # Use the interface generator to split the access
+ # into raw allow rules and interfaces. After this
+ # a will contain a list of access that should be
+ # used as raw allow rules and the interfaces will
+ # be added to the module.
+ if self.ifgen:
+ raw_allow, ifcalls = self.ifgen.gen(av_set, self.explain)
+ self.module.children.extend(ifcalls)
+ else:
+ raw_allow = av_set
+
+ # Generate the raw allow rules from the filtered list
+ self.__add_allow_rules(raw_allow)
+
+ def add_role_types(self, role_type_set):
+ for role_type in role_type_set:
+ self.module.children.append(role_type)
+
+def explain_access(av, ml=None, verbosity=SHORT_EXPLANATION):
+ """Explain why a policy statement was generated.
+
+ Return a string containing a text explanation of
+ why a policy statement was generated. The string is
+ commented and wrapped and can be directly inserted
+ into a policy.
+
+ Params:
+ av - access vector representing the access. Should
+ have .audit_msgs set appropriately.
+ verbosity - the amount of explanation provided. Should
+ be set to NO_EXPLANATION, SHORT_EXPLANATION, or
+ LONG_EXPLANATION.
+ Returns:
+ list of strings - strings explaining the access or an empty
+ string if verbosity=NO_EXPLANATION or there is not sufficient
+ information to provide an explanation.
+ """
+ s = []
+
+ def explain_interfaces():
+ if not ml:
+ return
+ s.append(" Interface options:")
+ for match in ml.all():
+ ifcall = call_interface(match.interface, ml.av)
+ s.append(' %s # [%d]' % (ifcall.to_string(), match.dist))
+
+
+ # Format the raw audit data to explain why the
+ # access was requested - either long or short.
+ if verbosity == LONG_EXPLANATION:
+ for msg in av.audit_msgs:
+ s.append(' %s' % msg.header)
+ s.append(' scontext="%s" tcontext="%s"' %
+ (str(msg.scontext), str(msg.tcontext)))
+ s.append(' class="%s" perms="%s"' %
+ (msg.tclass, refpolicy.list_to_space_str(msg.accesses)))
+ s.append(' comm="%s" exe="%s" path="%s"' % (msg.comm, msg.exe, msg.path))
+ s.extend(textwrap.wrap('message="' + msg.message + '"', 80, initial_indent=" ",
+ subsequent_indent=" "))
+ explain_interfaces()
+ elif verbosity:
+ s.append(' src="%s" tgt="%s" class="%s", perms="%s"' %
+ (av.src_type, av.tgt_type, av.obj_class, av.perms.to_space_str()))
+ # For the short display we are only going to use the additional information
+ # from the first audit message. For the vast majority of cases this info
+ # will always be the same anyway.
+ if len(av.audit_msgs) > 0:
+ msg = av.audit_msgs[0]
+ s.append(' comm="%s" exe="%s" path="%s"' % (msg.comm, msg.exe, msg.path))
+ explain_interfaces()
+ return s
+
+def call_interface(interface, av):
+ params = []
+ args = []
+
+ params.extend(interface.params.values())
+ params.sort(key=lambda param: param.num, reverse=True)
+
+ ifcall = refpolicy.InterfaceCall()
+ ifcall.ifname = interface.name
+
+ for i in range(len(params)):
+ if params[i].type == refpolicy.SRC_TYPE:
+ ifcall.args.append(av.src_type)
+ elif params[i].type == refpolicy.TGT_TYPE:
+ ifcall.args.append(av.tgt_type)
+ elif params[i].type == refpolicy.OBJ_CLASS:
+ ifcall.args.append(av.obj_class)
+ else:
+ print(params[i].type)
+ assert(0)
+
+ assert(len(ifcall.args) > 0)
+
+ return ifcall
+
+class InterfaceGenerator:
+ def __init__(self, ifs, perm_maps=None):
+ self.ifs = ifs
+ self.hack_check_ifs(ifs)
+ self.matcher = matching.AccessMatcher(perm_maps)
+ self.calls = []
+
+ def hack_check_ifs(self, ifs):
+ # FIXME: Disable interfaces we can't call - this is a hack.
+ # Because we don't handle roles, multiple paramaters, etc.,
+ # etc., we must make certain we can actually use a returned
+ # interface.
+ for x in ifs.interfaces.values():
+ params = []
+ params.extend(x.params.values())
+ params.sort(key=lambda param: param.num, reverse=True)
+ for i in range(len(params)):
+ # Check that the paramater position matches
+ # the number (e.g., $1 is the first arg). This
+ # will fail if the parser missed something.
+ if (i + 1) != params[i].num:
+ x.enabled = False
+ break
+ # Check that we can handle the param type (currently excludes
+ # roles.
+ if params[i].type not in [refpolicy.SRC_TYPE, refpolicy.TGT_TYPE,
+ refpolicy.OBJ_CLASS]:
+ x.enabled = False
+ break
+
+ def gen(self, avs, verbosity):
+ raw_av = self.match(avs)
+ ifcalls = []
+ for ml in self.calls:
+ ifcall = call_interface(ml.best().interface, ml.av)
+ if verbosity:
+ ifcall.comment = refpolicy.Comment(explain_access(ml.av, ml, verbosity))
+ ifcalls.append((ifcall, ml))
+
+ d = []
+ for ifcall, ifs in ifcalls:
+ found = False
+ for o_ifcall in d:
+ if o_ifcall.matches(ifcall):
+ if o_ifcall.comment and ifcall.comment:
+ o_ifcall.comment.merge(ifcall.comment)
+ found = True
+ if not found:
+ d.append(ifcall)
+
+ return (raw_av, d)
+
+
+ def match(self, avs):
+ raw_av = []
+ for av in avs:
+ ans = matching.MatchList()
+ self.matcher.search_ifs(self.ifs, av, ans)
+ if len(ans):
+ self.calls.append(ans)
+ else:
+ raw_av.append(av)
+
+ return raw_av
+
+
+def gen_requires(module):
+ """Add require statements to the module.
+ """
+ def collect_requires(node):
+ r = refpolicy.Require()
+ for avrule in node.avrules():
+ r.types.update(avrule.src_types)
+ r.types.update(avrule.tgt_types)
+ for obj in avrule.obj_classes:
+ r.add_obj_class(obj, avrule.perms)
+
+ for ifcall in node.interface_calls():
+ for arg in ifcall.args:
+ # FIXME - handle non-type arguments when we
+ # can actually figure those out.
+ r.types.add(arg)
+
+ for role_type in node.role_types():
+ r.roles.add(role_type.role)
+ r.types.update(role_type.types)
+
+ r.types.discard("self")
+
+ node.children.insert(0, r)
+
+ # FUTURE - this is untested on modules with any sort of
+ # nesting
+ for node in module.nodes():
+ collect_requires(node)
+
+