diff --git a/libqpol/include/qpol/mls_query.h b/libqpol/include/qpol/mls_query.h
index e365d0b..e723dc3 100644
--- a/libqpol/include/qpol/mls_query.h
+++ b/libqpol/include/qpol/mls_query.h
@@ -258,9 +258,10 @@ extern "C"
extern int qpol_policy_get_semantic_level_by_name(const qpol_policy_t * policy, const char *name, const qpol_semantic_level_t ** datum);
extern int qpol_semantic_level_add_cats_by_name(const qpol_policy_t * policy, const qpol_semantic_level_t * level, const char *low, const char *high);
extern int qpol_mls_level_from_semantic_level(const qpol_policy_t * policy, const qpol_semantic_level_t * src, qpol_mls_level_t **dest);
+ extern void qpol_semantic_level_destroy(qpol_semantic_level_t * level);
/* semantic ranges */
- extern int qpol_policy_get_mls_range_from_semantic_levels(const qpol_policy_t * policy, const qpol_semantic_level_t * low, const qpol_semantic_level_t *high, qpol_mls_range_t **dest);
+ extern int qpol_policy_get_mls_range_from_mls_levels(const qpol_policy_t * policy, const qpol_mls_level_t * low, const qpol_mls_level_t *high, qpol_mls_range_t **dest);
#ifdef __cplusplus
}
diff --git a/libqpol/mls_query.c b/libqpol/mls_query.c
index 36a8436..9c27c98 100644
--- a/libqpol/mls_query.c
+++ b/libqpol/mls_query.c
@@ -541,12 +541,11 @@ int qpol_cat_get_alias_iter(const qpol_policy_t * policy, const qpol_cat_t * dat
}
/* mls range */
-int qpol_policy_get_mls_range_from_semantic_levels(const qpol_policy_t * policy, const qpol_semantic_level_t *low, const qpol_semantic_level_t *high, qpol_mls_range_t **dest)
+int qpol_policy_get_mls_range_from_mls_levels(const qpol_policy_t * policy, const qpol_mls_level_t *low, const qpol_mls_level_t *high, qpol_mls_range_t **dest)
{
policydb_t *db = NULL;
mls_range_t *internal_range = NULL;
- mls_semantic_range_t *internal_semantic = NULL;
- mls_semantic_level_t *internal_low = NULL, *internal_high = NULL;
+ mls_level_t *internal_low = NULL, *internal_high = NULL;
if (policy == NULL || low == NULL || high == NULL || dest == NULL) {
if (dest != NULL)
@@ -556,50 +555,40 @@ int qpol_policy_get_mls_range_from_semantic_levels(const qpol_policy_t * policy,
return STATUS_ERR;
}
- internal_low = (mls_semantic_level_t*)low;
- internal_high = (mls_semantic_level_t*)high;
+ db = &policy->p->p;
*dest = NULL;
+ internal_low = (mls_level_t*)low;
+ internal_high = (mls_level_t*)high;
- internal_semantic = malloc(sizeof(mls_semantic_range_t));
- if (!internal_semantic) {
+ if (!mls_level_dom(internal_high, internal_low)) {
ERR(policy, "%s", strerror(EINVAL));
+ errno = EINVAL;
return STATUS_ERR;
}
- mls_semantic_range_init(internal_semantic);
internal_range = malloc(sizeof(mls_range_t));
if (!internal_range) {
- mls_semantic_range_destroy(internal_semantic);
- ERR(policy, "%s", strerror(EINVAL));
+ ERR(policy, "%s", strerror(errno));
return STATUS_ERR;
}
mls_range_init(internal_range);
- db = &policy->p->p;
- if(mls_semantic_level_cpy(&internal_semantic->level[0], internal_low) < 0) {
- goto err;
- }
- if (mls_semantic_level_cpy(&internal_semantic->level[1], internal_high) < 0) {
+ if (mls_level_cpy(&internal_range->level[0], internal_low) < 0) {
goto err;
}
- if(mls_semantic_range_expand(internal_semantic, internal_range, db, policy->sh) < 0) {
- ERR(policy, "%s", strerror(EINVAL));
+ if (mls_level_cpy(&internal_range->level[1], internal_high) < 0) {
goto err;
}
*dest = (qpol_mls_range_t*) internal_range;
- mls_semantic_range_destroy(internal_semantic);
- free(internal_semantic);
return STATUS_SUCCESS;
err:
mls_range_destroy(internal_range);
- mls_semantic_range_destroy(internal_semantic);
free(internal_range);
- free(internal_semantic);
- errno = EINVAL;
+ errno = ENOMEM;
return STATUS_ERR;
}
@@ -835,3 +824,14 @@ int qpol_semantic_level_add_cats_by_name(const qpol_policy_t * policy, const qpo
errno = ENOENT;
return STATUS_ERR;
}
+
+void qpol_semantic_level_destroy(qpol_semantic_level_t * level)
+{
+
+ if (level == NULL) {
+ return;
+ }
+
+ mls_semantic_level_destroy((mls_semantic_level_t*) level);
+ free(level);
+}
diff --git a/setools/policyrep/mls.py b/setools/policyrep/mls.py
index 59d70ad..6d3eeb2 100644
--- a/setools/policyrep/mls.py
+++ b/setools/policyrep/mls.py
@@ -87,15 +87,21 @@ def sensitivity_factory(policy, symbol):
raise InvalidSensitivity("{0} is not a valid sensitivity".format(symbol))
-def _build_semantic_level(policy, level):
- """Parse the level string and construct a qpol semantic representation."""
- sens_split = level.split(":")
+def level_factory(policy, symbol):
+ """
+ Factory function for creating MLS level objects (e.g. levels used
+ in contexts of labeling statements)
+ """
+ if isinstance(symbol, qpol.qpol_mls_level_t):
+ return MLSLevel(policy, symbol)
+
+ sens_split = symbol.split(":")
sens = sens_split[0]
try:
semantic_level = qpol.qpol_semantic_level_t(policy, sens)
except ValueError:
- raise InvalidLevel("{0} is not a valid sensitivity".format(sens))
+ raise InvalidLevel("{0} is invalid ({1} is not a valid sensitivity)".format(symbol, sens))
try:
cats = sens_split[1]
@@ -109,34 +115,24 @@ def _build_semantic_level(policy, level):
try:
semantic_level.add_cats(policy, catrange[0], catrange[1])
except ValueError:
- raise InvalidLevel("{0} is not a valid category range".format(group))
+ raise InvalidLevel("{0} is invalid ({1} is not a valid category range)".
+ format(symbol, group))
elif len(catrange) == 1:
try:
semantic_level.add_cats(policy, catrange[0], catrange[0])
except ValueError:
- raise InvalidLevel("{0} is not a valid category".format(group))
+ raise InvalidLevel("{0} is invalid ({1} is not a valid category)".
+ format(symbol, group))
else:
- # may not be possible to get here
- raise InvalidLevel("{0} is not a valid category range".format(group))
-
- return semantic_level
-
-
-def level_factory(policy, symbol):
- """
- Factory function for creating MLS level objects (e.g. levels used
- in contexts of labeling statements)
- """
- if isinstance(symbol, qpol.qpol_mls_level_t):
- return MLSLevel(policy, symbol)
-
- semantic_level = _build_semantic_level(policy, symbol)
+ raise InvalidLevel("{0} is invalid (level parsing error)".format(symbol))
# convert to level object
try:
policy_level = qpol.qpol_mls_level_t(policy, semantic_level)
except ValueError:
- raise InvalidLevel("{0} is not a valid level".format(symbol))
+ raise InvalidLevel(
+ "{0} is invalid (one or more categories are not associated with the sensitivity)".
+ format(symbol))
return MLSLevel(policy, policy_level)
@@ -165,12 +161,12 @@ def range_factory(policy, symbol):
levels = symbol.split("-")
try:
- low = _build_semantic_level(policy, levels[0])
+ low = level_factory(policy, levels[0])
except InvalidLevel as e:
raise InvalidRange("{0} is not a valid range ({1}).".format(symbol, e))
try:
- high = _build_semantic_level(policy, levels[1])
+ high = level_factory(policy, levels[1])
except InvalidLevel as e:
raise InvalidRange("{0} is not a valid range ({1}).".format(symbol, e))
except IndexError:
@@ -178,11 +174,10 @@ def range_factory(policy, symbol):
# convert to range object
try:
- policy_range = qpol.qpol_mls_range_t(policy, low, high)
+ policy_range = qpol.qpol_mls_range_t(policy, low.qpol_symbol, high.qpol_symbol)
except ValueError:
- # this can be due to one of the semantic levels being invalid (category not allowed in
- # level) or the high level does not dominate the low level. Can't tell which one.
- raise InvalidLevel("{0} is not a valid range".format(symbol))
+ raise InvalidRange("{0} is not a valid range ({1} is not dominated by {2})".
+ format(symbol, levels[0], levels[1]))
return MLSRange(policy, policy_range)
diff --git a/setools/policyrep/qpol.i b/setools/policyrep/qpol.i
index 6c6c0c9..f140ae9 100644
--- a/setools/policyrep/qpol.i
+++ b/setools/policyrep/qpol.i
@@ -1523,17 +1523,25 @@ typedef struct qpol_cat {} qpol_cat_t;
/* qpol mls range */
typedef struct qpol_mls_range {} qpol_mls_range_t;
%extend qpol_mls_range {
+ # TODO: determine how to conditionally destroy this range.
+ # It should only be destroyed if it was looked up (user-entered)
+ # Otherwise qpol will destroy the others when the policy closes.
%exception qpol_mls_range {
$action
if (!result) {
- PyErr_SetString(PyExc_ValueError, "Invalid range.");
+ if (errno == EINVAL) {
+ PyErr_SetString(PyExc_ValueError, "Invalid range.");
+ } else {
+ PyErr_SetFromErrno(PyExc_OSError);
+ }
+
return NULL;
}
}
- qpol_mls_range(qpol_policy_t *p, qpol_semantic_level_t *l, qpol_semantic_level_t *h) {
+ qpol_mls_range(qpol_policy_t *p, qpol_mls_level_t *l, qpol_mls_level_t *h) {
qpol_mls_range_t *range;
- qpol_policy_get_mls_range_from_semantic_levels(p, l, h, &range);
+ qpol_policy_get_mls_range_from_mls_levels(p, l, h, &range);
return range;
}
@@ -1586,7 +1594,7 @@ typedef struct qpol_semantic_level {} qpol_semantic_level_t;
};
~qpol_semantic_level() {
- /* mls_semantic_level_destroy(self); */
+ qpol_semantic_level_destroy(self);
return;
};
diff --git a/tests/policyrep/__init__.py b/tests/policyrep/__init__.py
new file mode 100644
index 0000000..359be6a
--- /dev/null
+++ b/tests/policyrep/__init__.py
@@ -0,0 +1,18 @@
+# Copyright 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 General Public License as published by
+# the Free Software Foundation, either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SETools. If not, see .
+#
+from . import mls
diff --git a/tests/policyrep/mls.conf b/tests/policyrep/mls.conf
new file mode 100644
index 0000000..1b2db89
--- /dev/null
+++ b/tests/policyrep/mls.conf
@@ -0,0 +1,136 @@
+class infoflow
+class infoflow2
+class infoflow3
+class infoflow4
+class infoflow5
+class infoflow6
+class infoflow7
+
+sid kernel
+sid security
+
+common infoflow
+{
+ low_w
+ med_w
+ hi_w
+ low_r
+ med_r
+ hi_r
+}
+
+class infoflow
+inherits infoflow
+
+class infoflow2
+inherits infoflow
+{
+ super_w
+ super_r
+}
+
+class infoflow3
+{
+ null
+}
+
+class infoflow4
+inherits infoflow
+
+class infoflow5
+inherits infoflow
+
+class infoflow6
+inherits infoflow
+
+class infoflow7
+inherits infoflow
+{
+ super_w
+ super_r
+ super_none
+ super_both
+ super_unmapped
+}
+
+sensitivity s0;
+sensitivity s1;
+sensitivity s2;
+
+dominance { s0 s1 s2 }
+
+category c0;
+category c1;
+category c2;
+category c3;
+category c4;
+category c5;
+category c6;
+category c7;
+category c8;
+category c9;
+category c10;
+category c11;
+category c12;
+category c13;
+
+#level decl
+level s0:c0.c2;
+level s1:c0.c13;
+level s2:c0.c13;
+
+#some constraints
+mlsconstrain infoflow hi_r ((l1 dom l2) or (t1 == mls_exempt));
+
+attribute mls_exempt;
+
+type system;
+role system;
+role system types system;
+
+role role20_r;
+role role21a_r;
+role role21b_r;
+role role21c_r;
+
+role role20_r types system;
+role role21a_r types system;
+role role21b_r types system;
+role role21c_r types system;
+
+type type30;
+type type31a;
+type type31b;
+type type31c;
+role system types { type30 type31a type31b type31c };
+
+allow system self:infoflow hi_w;
+
+#users
+user system roles { system role20_r role21a_r role21b_r role21c_r } level s0 range s0 - s2:c0.c4;
+user user10 roles system level s0 range s0 - s2:c0.c4;
+user user11a roles system level s0 range s0 - s2:c0.c4;
+user user11b roles system level s0 range s0 - s2:c0.c4;
+user user11c roles system level s0 range s0 - s2:c0.c4;
+
+#normal constraints
+constrain infoflow hi_w (u1 == u2);
+
+#isids
+sid kernel system:system:system:s0
+sid security system:system:system:s0
+
+#fs_use
+fs_use_trans devpts system:object_r:system:s0;
+fs_use_xattr ext3 system:object_r:system:s0;
+fs_use_task pipefs system:object_r:system:s0;
+
+#genfscon
+genfscon proc / system:object_r:system:s1
+genfscon proc /sys system:object_r:system:s0
+genfscon selinuxfs / system:object_r:system:s2:c0.c4
+portcon tcp 1 system:system:system:s0:c0.c1
+netifcon eth0 system:object_r:system:s0 system:object_r:system:s0
+nodecon 127.0.0.1 255.255.255.255 system:object_r:system:s0
+nodecon ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff system:object_r:system:s0
+
diff --git a/tests/policyrep/mls.py b/tests/policyrep/mls.py
new file mode 100644
index 0000000..cdbf40a
--- /dev/null
+++ b/tests/policyrep/mls.py
@@ -0,0 +1,97 @@
+# Copyright 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 General Public License as published by
+# the Free Software Foundation, either version 2 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with SETools. If not, see .
+#
+import unittest
+
+from setools import SELinuxPolicy
+from setools.policyrep.mls import level_factory, InvalidLevel, range_factory, InvalidRange
+
+
+class BoolQueryTest(unittest.TestCase):
+
+ def setUp(self):
+ self.p = SELinuxPolicy("tests/policyrep/mls.conf")
+
+ def test_300_level_lookup_no_cats(self):
+ """Level lookup with no categories."""
+ levelobj = level_factory(self.p.policy, "s2")
+ self.assertEqual(str(levelobj), "s2")
+
+ def test_301_level_lookup_cat_range(self):
+ """Level lookup with category range."""
+ levelobj = level_factory(self.p.policy, "s1:c0.c13")
+ self.assertEqual(str(levelobj), "s1:c0.c13")
+
+ def test_302_level_lookup_complex_cats(self):
+ """Level lookup with complex category set."""
+ levelobj = level_factory(self.p.policy, "s2:c0.c5,c7,c9.c11,c13")
+ self.assertEqual(str(levelobj), "s2:c0.c5,c7,c9.c11,c13")
+
+ def test_303_level_lookup_bad1(self):
+ """Level lookup with garbage."""
+ self.assertRaises(InvalidLevel, level_factory, self.p.policy, "FAIL")
+
+ def test_304_level_lookup_bad2(self):
+ """Level lookup with : in garbage."""
+ self.assertRaises(InvalidLevel, level_factory, self.p.policy, "FAIL:BAD")
+
+ def test_305_level_lookup_bad_cat(self):
+ """Level lookup with invalid category."""
+ self.assertRaises(InvalidLevel, level_factory, self.p.policy, "s0:FAIL")
+
+ def test_306_level_lookup_bad_cat_range(self):
+ """Level lookup with backwards category range."""
+ self.assertRaises(InvalidLevel, level_factory, self.p.policy, "s0:c4.c0")
+
+ def test_306_level_lookup_cat_not_assoc(self):
+ """Level lookup with category not associated with sensitivity."""
+ # c4 is not associated with s0.
+ self.assertRaises(InvalidLevel, level_factory, self.p.policy, "s0:c0,c4")
+
+ def test_400_range_lookup_single_level(self):
+ """Range lookup with single-level range."""
+ rangeobj = range_factory(self.p.policy, "s0")
+ self.assertEqual(str(rangeobj), "s0")
+
+ def test_401_range_lookup_single_level_redundant(self):
+ """Range lookup with single-level range (same range listed twice)."""
+ rangeobj = range_factory(self.p.policy, "s1-s1")
+ self.assertEqual(str(rangeobj), "s1")
+
+ def test_402_range_lookup_simple(self):
+ """Range lookup with simple range."""
+ rangeobj = range_factory(self.p.policy, "s0-s1:c0.c10")
+ self.assertEqual(str(rangeobj), "s0 - s1:c0.c10")
+
+ def test_403_range_lookup_no_cats(self):
+ """Range lookup with no categories."""
+ rangeobj = range_factory(self.p.policy, "s0-s1")
+ self.assertEqual(str(rangeobj), "s0 - s1")
+
+ def test_404_range_lookup_complex(self):
+ """Range lookup with complex category set."""
+ rangeobj = range_factory(self.p.policy, "s0:c0.c2-s2:c0.c5,c7,c9.c11,c13")
+ self.assertEqual(str(rangeobj), "s0:c0.c2 - s2:c0.c5,c7,c9.c11,c13")
+
+ def test_405_range_lookup_non_dom(self):
+ """Range lookup with non-dominating high level."""
+ self.assertRaises(InvalidRange, range_factory, self.p.policy, "s1-s0")
+
+ def test_406_range_lookup_invalid_range(self):
+ """Range lookup with an invalid range (low)."""
+ # c13 is not associated with s0.
+ self.assertRaises(InvalidRange, range_factory, self.p.policy, "s0:c13-s2:c13")