diff --git a/libapol/policyrep/__init__.py b/libapol/policyrep/__init__.py index f42d81b..6297cab 100644 --- a/libapol/policyrep/__init__.py +++ b/libapol/policyrep/__init__.py @@ -50,7 +50,6 @@ import mlsrule # Constraints import constraint -import mlsconstraint # In-policy Labeling import initsid @@ -189,6 +188,26 @@ class SELinuxPolicy(object): # Constraints generators # + def constraints(self): + """Generator which yields all constraints.""" + + qiter = self.policy.get_constraint_iter() + while not qiter.end(): + c = constraint.Constraint(self.policy, qpol.qpol_constraint_from_void(qiter.get_item())) + if not c.ismls: + yield c + qiter.next() + + def mlsconstraints(self): + """Generator which yields all MLS constraints.""" + + qiter = self.policy.get_constraint_iter() + while not qiter.end(): + c = constraint.Constraint(self.policy, qpol.qpol_constraint_from_void(qiter.get_item())) + if c.ismls: + yield c + qiter.next() + # # In-policy Labeling statement generators # diff --git a/libapol/policyrep/constraint.py b/libapol/policyrep/constraint.py index 6e1755f..9562138 100644 --- a/libapol/policyrep/constraint.py +++ b/libapol/policyrep/constraint.py @@ -16,14 +16,205 @@ # License along with SETools. If not, see # . # -import symbol +import string + import setools.qpol as qpol +import symbol +import objclass + class Constraint(symbol.PolicySymbol): - """A constraint rule.""" - pass + """A constraint rule (regular or MLS).""" + + _expr_type_to_text = { + qpol.QPOL_CEXPR_TYPE_NOT: "not", + qpol.QPOL_CEXPR_TYPE_AND: "and", + qpol.QPOL_CEXPR_TYPE_OR: "\n\tor"} + + _expr_op_to_text = { + qpol.QPOL_CEXPR_OP_EQ: "==", + qpol.QPOL_CEXPR_OP_NEQ: "!=", + qpol.QPOL_CEXPR_OP_DOM: "dom", + qpol.QPOL_CEXPR_OP_DOMBY: "domby", + qpol.QPOL_CEXPR_OP_INCOMP: "incomp"} + + _sym_to_text = { + qpol.QPOL_CEXPR_SYM_USER: "u1", + qpol.QPOL_CEXPR_SYM_ROLE: "r1", + qpol.QPOL_CEXPR_SYM_TYPE: "t1", + qpol.QPOL_CEXPR_SYM_USER + qpol.QPOL_CEXPR_SYM_TARGET: "u2", + qpol.QPOL_CEXPR_SYM_ROLE + qpol.QPOL_CEXPR_SYM_TARGET: "r2", + qpol.QPOL_CEXPR_SYM_TYPE + qpol.QPOL_CEXPR_SYM_TARGET: "t2", + qpol.QPOL_CEXPR_SYM_L1L2: "l1", + qpol.QPOL_CEXPR_SYM_L1H2: "l1", + qpol.QPOL_CEXPR_SYM_H1L2: "h1", + qpol.QPOL_CEXPR_SYM_H1H2: "h1", + qpol.QPOL_CEXPR_SYM_L1H1: "l1", + qpol.QPOL_CEXPR_SYM_L2H2: "l2", + qpol.QPOL_CEXPR_SYM_L1L2 + qpol.QPOL_CEXPR_SYM_TARGET: "l2", + qpol.QPOL_CEXPR_SYM_L1H2 + qpol.QPOL_CEXPR_SYM_TARGET: "h2", + qpol.QPOL_CEXPR_SYM_H1L2 + qpol.QPOL_CEXPR_SYM_TARGET: "l2", + qpol.QPOL_CEXPR_SYM_H1H2 + qpol.QPOL_CEXPR_SYM_TARGET: "h2", + qpol.QPOL_CEXPR_SYM_L1H1 + qpol.QPOL_CEXPR_SYM_TARGET: "h1", + qpol.QPOL_CEXPR_SYM_L2H2 + qpol.QPOL_CEXPR_SYM_TARGET: "h2"} + + _expr_type_to_precedence = { + qpol.QPOL_CEXPR_TYPE_NOT: 3, + qpol.QPOL_CEXPR_TYPE_AND: 2, + qpol.QPOL_CEXPR_TYPE_OR: 1} + + # all operators have the same precedence + _expr_op_precedence = 4 + + def __str__(self): + if self.ismls: + rule_string = "mlsconstrain {0.tclass} ".format(self) + else: + rule_string = "constrain {0.tclass} ".format(self) + + perms = self.perms + if len(perms) > 1: + rule_string += "{{ {0} }} (\n".format(string.join(perms)) + else: + # convert to list since sets cannot be indexed + rule_string += "{0} (\n".format(list(perms)[0]) + + rule_string += "\t{0}\n);".format(self.__build_expression()) + + return rule_string + + def __build_expression(self): + # qpol representation is in postfix notation. This code + # converts it to infix notation. Parentheses are added + # to ensure correct expressions, though they may end up + # being overused. Set previous operator at start to the + # highest precedence (op) so if there is a single binary + # operator, no parentheses are output + + expr_string = "" + qpol_iter = self.qpol_symbol.get_expr_iter(self.policy) + + stack = [] + prev_oper = self._expr_op_precedence + while not qpol_iter.end(): + expr_node = qpol.qpol_constraint_expr_node_from_void( + qpol_iter.get_item()) + + op = expr_node.get_op(self.policy) + sym_type = expr_node.get_sym_type(self.policy) + expr_type = expr_node.get_expr_type(self.policy) + + if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR: + stack.append([self._sym_to_text[sym_type], + self._expr_op_to_text[op], + self._sym_to_text[sym_type + qpol.QPOL_CEXPR_SYM_TARGET]]) + prev_oper = self._expr_op_precedence + elif expr_type == qpol.QPOL_CEXPR_TYPE_NAMES: + names = [] + names_iter = expr_node.get_names_iter(self.policy) + while not names_iter.end(): + names.append(qpol.to_str(names_iter.get_item())) + names_iter.next() + + if not names: + names_str = "" + elif len(names) == 1: + names_str = names[0] + else: + names_str = "{{ {0} }}".format(string.join(names)) + + stack.append([self._sym_to_text[sym_type], + self._expr_op_to_text[op], + names_str]) + prev_oper = self._expr_op_precedence + elif expr_type == qpol.QPOL_CEXPR_TYPE_NOT: + # unary operator + operand = stack.pop() + stack.append([self._expr_type_to_text[expr_type], + "(", + operand, + ")"]) + prev_oper = self._expr_type_to_precedence[expr_type] + else: + operand1 = stack.pop() + operand2 = stack.pop() + + # if previous operator is of higher precedence + # no parentheses are needed. + if self._expr_type_to_precedence[expr_type] < prev_oper: + stack.append([operand1, + self._expr_type_to_text[expr_type], + operand2]) + else: + stack.append(["(", + operand1, + self._expr_type_to_text[expr_type], + operand2, + ")"]) + + prev_oper = self._expr_type_to_precedence[expr_type] + + qpol_iter.next() + + return self.__unwind_subexpression(stack) + + def __unwind_subexpression(self, expr): + ret = [] + + # do a string.join on sublists (subexpressions) + for i in expr: + if isinstance(i, list): + ret.append(self.__unwind_subexpression(i)) + else: + ret.append(i) + + return string.join(ret) + + @property + def ismls(self): + try: + return self._ismls + except AttributeError: + self._ismls = False + + qpol_iter = self.qpol_symbol.get_expr_iter(self.policy) + while not qpol_iter.end(): + expr_node = qpol.qpol_constraint_expr_node_from_void( + qpol_iter.get_item()) + + sym_type = expr_node.get_sym_type(self.policy) + expr_type = expr_node.get_expr_type(self.policy) + + if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR and sym_type >= qpol.QPOL_CEXPR_SYM_L1L2: + self._ismls = True + break + + qpol_iter.next() + + return self._ismls + + @property + def perms(self): + """The constraint's permission set.""" + + iter = self.qpol_symbol.get_perm_iter(self.policy) + + p = set() + while not iter.end(): + p.add(qpol.to_str(iter.get_item())) + iter.next() + + return p + + def statement(self): + return str(self) + + @property + def tclass(self): + """Object class for this constraint.""" + return objclass.ObjClass(self.policy, self.qpol_symbol.get_class(self.policy)) class ValidateTrans(symbol.PolicySymbol): diff --git a/libapol/policyrep/mlsconstraint.py b/libapol/policyrep/mlsconstraint.py deleted file mode 100644 index a985b78..0000000 --- a/libapol/policyrep/mlsconstraint.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2014, 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 -# . -# -import constraint -import setools.qpol as qpol - - -class MLSConstraint(constraint.Constraint): - - """An MLS constraint rule.""" - pass - - -class MLSValidateTrans(constraint.ValidateTrans): - - """An MLS validate transition rule.""" - pass