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