Improve readability of Constraint expression rendering.

Fix all four constraint rule classes; (mls)validatetrans statements now
render correctly.
This commit is contained in:
Chris PeBenito 2015-02-14 17:19:58 -05:00
parent 7bacae7596
commit a08873765c

View File

@ -1,4 +1,4 @@
# Copyright 2014, Tresys Technology, LLC # Copyright 2014-2015, Tresys Technology, LLC
# #
# This file is part of SETools. # This file is part of SETools.
# #
@ -28,24 +28,22 @@ def _is_mls(policy, symbol):
sym_type = expr_node.sym_type(policy) sym_type = expr_node.sym_type(policy)
expr_type = expr_node.expr_type(policy) expr_type = expr_node.expr_type(policy)
if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR and \ if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR and sym_type >= qpol.QPOL_CEXPR_SYM_L1L2:
sym_type >= qpol.QPOL_CEXPR_SYM_L1L2:
return True return True
return False return False
def constraint_factory(policy, symbol): def constraint_factory(policy, symbol):
"""Factory function for creating regular (non-MLS) constraint objects.""" """Factory function for creating regular constraint objects."""
try: try:
if _is_mls(policy, symbol): if _is_mls(policy, symbol):
raise TypeError( raise TypeError("Symbol is not a constrain.")
"Constraint symbol is not a regular (non-MLS) constraint.")
except AttributeError: except AttributeError:
raise TypeError("Constraints cannot be looked-up.") raise TypeError("Constraints cannot be looked-up.")
return Constraint(policy, symbol) return Constraint(policy, symbol, "constrain")
def mlsconstraint_factory(policy, symbol): def mlsconstraint_factory(policy, symbol):
@ -53,24 +51,23 @@ def mlsconstraint_factory(policy, symbol):
try: try:
if not _is_mls(policy, symbol): if not _is_mls(policy, symbol):
raise TypeError("Constraint symbol is not a MLS constraint.") raise TypeError("Symbol is not a mlsconstrain.")
except AttributeError: except AttributeError:
raise TypeError("MLS constraints cannot be looked-up.") raise TypeError("mlsconstrain cannot be looked-up.")
return MLSConstraint(policy, symbol) return Constraint(policy, symbol, "mlsconstrain")
def validatetrans_factory(policy, symbol): def validatetrans_factory(policy, symbol):
"""Factory function for creating regular (non-MLS) validatetrans objects.""" """Factory function for creating regular validatetrans objects."""
try: try:
if _is_mls(policy, symbol): if _is_mls(policy, symbol):
raise TypeError( raise TypeError("Symbol is not a validatetrans.")
"Validatetrans symbol is not a regular (non-MLS) constraint.")
except AttributeError: except AttributeError:
raise TypeError("Validatetrans cannot be looked-up.") raise TypeError("validatetrans cannot be looked-up.")
return ValidateTrans(policy, symbol) return Validatetrans(policy, symbol, "validatetrans")
def mlsvalidatetrans_factory(policy, symbol): def mlsvalidatetrans_factory(policy, symbol):
@ -78,16 +75,16 @@ def mlsvalidatetrans_factory(policy, symbol):
try: try:
if not _is_mls(policy, symbol): if not _is_mls(policy, symbol):
raise TypeError("Validatetrans symbol is not a MLS validatetrans.") raise TypeError("Symbol is not a mlsvalidatetrans.")
except AttributeError: except AttributeError:
raise TypeError("MLS validatetrans cannot be looked-up.") raise TypeError("mlsvalidatetrans cannot be looked-up.")
return MLSValidateTrans(policy, symbol) return Validatetrans(policy, symbol, "mlsvalidatetrans")
class Constraint(symbol.PolicySymbol): class BaseConstraint(symbol.PolicySymbol):
"""A regular (non-MLS) constraint rule.""" """Abstract base class for constraint rules."""
_expr_type_to_text = { _expr_type_to_text = {
qpol.QPOL_CEXPR_TYPE_NOT: "not", qpol.QPOL_CEXPR_TYPE_NOT: "not",
@ -108,6 +105,9 @@ class Constraint(symbol.PolicySymbol):
qpol.QPOL_CEXPR_SYM_USER + qpol.QPOL_CEXPR_SYM_TARGET: "u2", 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_ROLE + qpol.QPOL_CEXPR_SYM_TARGET: "r2",
qpol.QPOL_CEXPR_SYM_TYPE + qpol.QPOL_CEXPR_SYM_TARGET: "t2", qpol.QPOL_CEXPR_SYM_TYPE + qpol.QPOL_CEXPR_SYM_TARGET: "t2",
qpol.QPOL_CEXPR_SYM_USER + qpol.QPOL_CEXPR_SYM_XTARGET: "u3",
qpol.QPOL_CEXPR_SYM_ROLE + qpol.QPOL_CEXPR_SYM_XTARGET: "r3",
qpol.QPOL_CEXPR_SYM_TYPE + qpol.QPOL_CEXPR_SYM_XTARGET: "t3",
qpol.QPOL_CEXPR_SYM_L1L2: "l1", qpol.QPOL_CEXPR_SYM_L1L2: "l1",
qpol.QPOL_CEXPR_SYM_L1H2: "l1", qpol.QPOL_CEXPR_SYM_L1H2: "l1",
qpol.QPOL_CEXPR_SYM_H1L2: "h1", qpol.QPOL_CEXPR_SYM_H1L2: "h1",
@ -121,29 +121,23 @@ class Constraint(symbol.PolicySymbol):
qpol.QPOL_CEXPR_SYM_L1H1 + qpol.QPOL_CEXPR_SYM_TARGET: "h1", qpol.QPOL_CEXPR_SYM_L1H1 + qpol.QPOL_CEXPR_SYM_TARGET: "h1",
qpol.QPOL_CEXPR_SYM_L2H2 + qpol.QPOL_CEXPR_SYM_TARGET: "h2"} qpol.QPOL_CEXPR_SYM_L2H2 + qpol.QPOL_CEXPR_SYM_TARGET: "h2"}
# Boolean operators
_expr_type_to_precedence = { _expr_type_to_precedence = {
qpol.QPOL_CEXPR_TYPE_NOT: 3, qpol.QPOL_CEXPR_TYPE_NOT: 3,
qpol.QPOL_CEXPR_TYPE_AND: 2, qpol.QPOL_CEXPR_TYPE_AND: 2,
qpol.QPOL_CEXPR_TYPE_OR: 1} qpol.QPOL_CEXPR_TYPE_OR: 1}
# all operators have the same precedence # Logical operators have the same precedence
_expr_op_precedence = 4 _logical_op_precedence = 4
def __init__(self, policy, qpol_symbol, ruletype):
symbol.PolicySymbol.__init__(self, policy, qpol_symbol)
self.ruletype = ruletype
def __str__(self): def __str__(self):
rule_string = "constrain {0.tclass} ".format(self) raise NotImplementedError
perms = self.perms def _build_expression(self):
if len(perms) > 1:
rule_string += "{{ {0} }} (\n".format(' '.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 # qpol representation is in postfix notation. This code
# converts it to infix notation. Parentheses are added # converts it to infix notation. Parentheses are added
# to ensure correct expressions, though they may end up # to ensure correct expressions, though they may end up
@ -153,57 +147,61 @@ class Constraint(symbol.PolicySymbol):
expr_string = "" expr_string = ""
stack = [] stack = []
prev_oper = self._expr_op_precedence prev_op_precedence = self._logical_op_precedence
for expr_node in self.qpol_symbol.expr_iter(self.policy): for expr_node in self.qpol_symbol.expr_iter(self.policy):
op = expr_node.op(self.policy) op = expr_node.op(self.policy)
sym_type = expr_node.sym_type(self.policy) sym_type = expr_node.sym_type(self.policy)
expr_type = expr_node.expr_type(self.policy) expr_type = expr_node.expr_type(self.policy)
if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR: if expr_type == qpol.QPOL_CEXPR_TYPE_ATTR:
stack.append([self._sym_to_text[sym_type], # logical operator with symbol (e.g. u1 == u2)
self._expr_op_to_text[op], operand1 = self._sym_to_text[sym_type]
self._sym_to_text[sym_type + qpol.QPOL_CEXPR_SYM_TARGET]]) operand2 = self._sym_to_text[sym_type + qpol.QPOL_CEXPR_SYM_TARGET]
prev_oper = self._expr_op_precedence operator = self._expr_op_to_text[op]
stack.append([operand1, operator, operand2])
prev_op_precedence = self._logical_op_precedence
elif expr_type == qpol.QPOL_CEXPR_TYPE_NAMES: elif expr_type == qpol.QPOL_CEXPR_TYPE_NAMES:
# logical operator with type or attribute list (e.g. t1 == { spam_t eggs_t })
operand1 = self._sym_to_text[sym_type]
operator = self._expr_op_to_text[op]
names = list(expr_node.names_iter(self.policy)) names = list(expr_node.names_iter(self.policy))
if not names: if not names:
names_str = "<empty set>" operand2 = "<empty set>"
elif len(names) == 1: elif len(names) == 1:
names_str = names[0] operand2 = names[0]
else: else:
names_str = "{{ {0} }}".format(' '.join(names)) operand2 = "{{ {0} }}".format(' '.join(names))
stack.append([self._sym_to_text[sym_type], stack.append([operand1, operator, operand2])
self._expr_op_to_text[op],
names_str]) prev_op_precedence = self._logical_op_precedence
prev_oper = self._expr_op_precedence
elif expr_type == qpol.QPOL_CEXPR_TYPE_NOT: elif expr_type == qpol.QPOL_CEXPR_TYPE_NOT:
# unary operator # unary operator (not)
operand = stack.pop() operand = stack.pop()
stack.append([self._expr_type_to_text[expr_type], operator = self._expr_type_to_text[expr_type]
"(",
operand, stack.append([operator, "(", operand, ")"])
")"])
prev_oper = self._expr_type_to_precedence[expr_type] prev_op_precedence = self._expr_type_to_precedence[expr_type]
else: else:
# binary operator (and/or)
operand1 = stack.pop() operand1 = stack.pop()
operand2 = stack.pop() operand2 = stack.pop()
operator = self._expr_type_to_text[expr_type]
op_precedence = self._expr_type_to_precedence[expr_type]
# if previous operator is of higher precedence # if previous operator is of higher precedence
# no parentheses are needed. # no parentheses are needed.
if self._expr_type_to_precedence[expr_type] < prev_oper: if op_precedence < prev_op_precedence:
stack.append([operand1, stack.append([operand1, operator, operand2])
self._expr_type_to_text[expr_type],
operand2])
else: else:
stack.append(["(", stack.append(["(", operand1, operator, operand2, ")"])
operand1,
self._expr_type_to_text[expr_type],
operand2,
")"])
prev_oper = self._expr_type_to_precedence[expr_type] prev_op_precedence = op_precedence
return self.__unwind_subexpression(stack) return self.__unwind_subexpression(stack)
@ -219,12 +217,6 @@ class Constraint(symbol.PolicySymbol):
return ' '.join(ret) return ' '.join(ret)
@property
def perms(self):
"""The constraint's permission set."""
return set(self.qpol_symbol.perm_iter(self.policy))
def statement(self): def statement(self):
return str(self) return str(self)
@ -234,10 +226,10 @@ class Constraint(symbol.PolicySymbol):
return objclass.class_factory(self.policy, self.qpol_symbol.object_class(self.policy)) return objclass.class_factory(self.policy, self.qpol_symbol.object_class(self.policy))
class MLSConstraint(Constraint): class Constraint(BaseConstraint):
def __str__(self): def __str__(self):
rule_string = "mlsconstrain {0.tclass} ".format(self) rule_string = "{0.ruletype} {0.tclass} ".format(self)
perms = self.perms perms = self.perms
if len(perms) > 1: if len(perms) > 1:
@ -246,44 +238,17 @@ class MLSConstraint(Constraint):
# convert to list since sets cannot be indexed # convert to list since sets cannot be indexed
rule_string += "{0} (\n".format(list(perms)[0]) rule_string += "{0} (\n".format(list(perms)[0])
rule_string += "\t{0}\n);".format(self.__build_expression()) rule_string += "\t{0}\n);".format(self._build_expression())
return rule_string return rule_string
@property
def perms(self):
"""The constraint's permission set."""
return set(self.qpol_symbol.perm_iter(self.policy))
class ValidateTrans(Constraint):
"""A regular validate transition rule.""" class Validatetrans(BaseConstraint):
def __str__(self): def __str__(self):
rule_string = "validatetrans {0.tclass} ".format(self) return "{0.ruletype} {0.tclass}\n\t{1}\n);".format(self, self._build_expression())
perms = self.perms
if len(perms) > 1:
rule_string += "{{ {0} }} (\n".format(' '.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
class MLSValidateTrans(Constraint):
"""A MLS validate transition rule."""
def __str__(self):
rule_string = "mlsvalidatetrans {0.tclass} ".format(self)
perms = self.perms
if len(perms) > 1:
rule_string += "{{ {0} }} (\n".format(' '.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