Change range constructor to use regular levels instead of semantic ones.

This affords better error reporting since levels can be invalid for
several reasons.

* Fix memory leak in qpol_semantic_level
* Add unit tests
This commit is contained in:
Chris PeBenito 2015-02-22 09:24:39 -05:00
parent 9c7ece4ad2
commit a8d7f740b2
7 changed files with 310 additions and 55 deletions

View File

@ -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
}

View File

@ -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);
}

View File

@ -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)

View File

@ -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;
};

View File

@ -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 <http://www.gnu.org/licenses/>.
#
from . import mls

136
tests/policyrep/mls.conf Normal file
View File

@ -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

97
tests/policyrep/mls.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
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")