/* Authors: Joshua Brindle <jbrindle@tresys.com>
|
*
|
* Assertion checker for avtab entries, taken from
|
* checkpolicy.c by Stephen Smalley <sds@tycho.nsa.gov>
|
*
|
* Copyright (C) 2005 Tresys Technology, LLC
|
*
|
* This library 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.
|
*
|
* This library 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 this library; if not, write to the Free Software
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
*/
|
|
#include <sepol/policydb/avtab.h>
|
#include <sepol/policydb/policydb.h>
|
#include <sepol/policydb/expand.h>
|
#include <sepol/policydb/util.h>
|
|
#include "private.h"
|
#include "debug.h"
|
|
struct avtab_match_args {
|
sepol_handle_t *handle;
|
policydb_t *p;
|
avrule_t *avrule;
|
avtab_t *avtab;
|
unsigned long errors;
|
};
|
|
static void report_failure(sepol_handle_t *handle, policydb_t *p, const avrule_t *avrule,
|
unsigned int stype, unsigned int ttype,
|
const class_perm_node_t *curperm, uint32_t perms)
|
{
|
if (avrule->source_filename) {
|
ERR(handle, "neverallow on line %lu of %s (or line %lu of policy.conf) violated by allow %s %s:%s {%s };",
|
avrule->source_line, avrule->source_filename, avrule->line,
|
p->p_type_val_to_name[stype],
|
p->p_type_val_to_name[ttype],
|
p->p_class_val_to_name[curperm->tclass - 1],
|
sepol_av_to_string(p, curperm->tclass, perms));
|
} else if (avrule->line) {
|
ERR(handle, "neverallow on line %lu violated by allow %s %s:%s {%s };",
|
avrule->line, p->p_type_val_to_name[stype],
|
p->p_type_val_to_name[ttype],
|
p->p_class_val_to_name[curperm->tclass - 1],
|
sepol_av_to_string(p, curperm->tclass, perms));
|
} else {
|
ERR(handle, "neverallow violated by allow %s %s:%s {%s };",
|
p->p_type_val_to_name[stype],
|
p->p_type_val_to_name[ttype],
|
p->p_class_val_to_name[curperm->tclass - 1],
|
sepol_av_to_string(p, curperm->tclass, perms));
|
}
|
}
|
|
static int match_any_class_permissions(class_perm_node_t *cp, uint32_t class, uint32_t data)
|
{
|
for (; cp; cp = cp->next) {
|
if ((cp->tclass == class) && (cp->data & data)) {
|
break;
|
}
|
}
|
if (!cp)
|
return 0;
|
|
return 1;
|
}
|
|
static int extended_permissions_and(uint32_t *perms1, uint32_t *perms2) {
|
size_t i;
|
for (i = 0; i < EXTENDED_PERMS_LEN; i++) {
|
if (perms1[i] & perms2[i])
|
return 1;
|
}
|
|
return 0;
|
}
|
|
static int check_extended_permissions(av_extended_perms_t *neverallow, avtab_extended_perms_t *allow)
|
{
|
int rc = 0;
|
if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
|
if (neverallow->driver == allow->driver)
|
rc = extended_permissions_and(neverallow->perms, allow->perms);
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
|
rc = xperm_test(neverallow->driver, allow->perms);
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
|
rc = xperm_test(allow->driver, neverallow->perms);
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
|
rc = extended_permissions_and(neverallow->perms, allow->perms);
|
}
|
|
return rc;
|
}
|
|
/* Compute which allowed extended permissions violate the neverallow rule */
|
static void extended_permissions_violated(avtab_extended_perms_t *result,
|
av_extended_perms_t *neverallow,
|
avtab_extended_perms_t *allow)
|
{
|
size_t i;
|
if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
|
result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
|
result->driver = allow->driver;
|
for (i = 0; i < EXTENDED_PERMS_LEN; i++)
|
result->perms[i] = neverallow->perms[i] & allow->perms[i];
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLFUNCTION)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
|
result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
|
result->driver = neverallow->driver;
|
memcpy(result->perms, neverallow->perms, sizeof(result->perms));
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLFUNCTION)) {
|
result->specified = AVTAB_XPERMS_IOCTLFUNCTION;
|
result->driver = allow->driver;
|
memcpy(result->perms, allow->perms, sizeof(result->perms));
|
} else if ((neverallow->specified == AVRULE_XPERMS_IOCTLDRIVER)
|
&& (allow->specified == AVTAB_XPERMS_IOCTLDRIVER)) {
|
result->specified = AVTAB_XPERMS_IOCTLDRIVER;
|
for (i = 0; i < EXTENDED_PERMS_LEN; i++)
|
result->perms[i] = neverallow->perms[i] & allow->perms[i];
|
}
|
}
|
|
/* Same scenarios of interest as check_assertion_extended_permissions */
|
static int report_assertion_extended_permissions(sepol_handle_t *handle,
|
policydb_t *p, const avrule_t *avrule,
|
unsigned int stype, unsigned int ttype,
|
const class_perm_node_t *curperm, uint32_t perms,
|
avtab_key_t *k, avtab_t *avtab)
|
{
|
avtab_ptr_t node;
|
avtab_key_t tmp_key;
|
avtab_extended_perms_t *xperms;
|
avtab_extended_perms_t error;
|
ebitmap_t *sattr = &p->type_attr_map[stype];
|
ebitmap_t *tattr = &p->type_attr_map[ttype];
|
ebitmap_node_t *snode, *tnode;
|
unsigned int i, j;
|
int rc = 1;
|
int ret = 0;
|
|
memcpy(&tmp_key, k, sizeof(avtab_key_t));
|
tmp_key.specified = AVTAB_XPERMS_ALLOWED;
|
|
ebitmap_for_each_bit(sattr, snode, i) {
|
if (!ebitmap_node_get_bit(snode, i))
|
continue;
|
ebitmap_for_each_bit(tattr, tnode, j) {
|
if (!ebitmap_node_get_bit(tnode, j))
|
continue;
|
tmp_key.source_type = i + 1;
|
tmp_key.target_type = j + 1;
|
for (node = avtab_search_node(avtab, &tmp_key);
|
node;
|
node = avtab_search_node_next(node, tmp_key.specified)) {
|
xperms = node->datum.xperms;
|
if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
|
&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
|
continue;
|
|
rc = check_extended_permissions(avrule->xperms, xperms);
|
/* failure on the extended permission check_extended_permissions */
|
if (rc) {
|
extended_permissions_violated(&error, avrule->xperms, xperms);
|
ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
|
"allowxperm %s %s:%s %s;",
|
avrule->source_line, avrule->source_filename, avrule->line,
|
p->p_type_val_to_name[i],
|
p->p_type_val_to_name[j],
|
p->p_class_val_to_name[curperm->tclass - 1],
|
sepol_extended_perms_to_string(&error));
|
|
rc = 0;
|
ret++;
|
}
|
}
|
}
|
}
|
|
/* failure on the regular permissions */
|
if (rc) {
|
ERR(handle, "neverallowxperm on line %lu of %s (or line %lu of policy.conf) violated by\n"
|
"allow %s %s:%s {%s };",
|
avrule->source_line, avrule->source_filename, avrule->line,
|
p->p_type_val_to_name[stype],
|
p->p_type_val_to_name[ttype],
|
p->p_class_val_to_name[curperm->tclass - 1],
|
sepol_av_to_string(p, curperm->tclass, perms));
|
ret++;
|
|
}
|
|
return ret;
|
}
|
|
static int report_assertion_avtab_matches(avtab_key_t *k, avtab_datum_t *d, void *args)
|
{
|
int rc = 0;
|
struct avtab_match_args *a = (struct avtab_match_args *)args;
|
sepol_handle_t *handle = a->handle;
|
policydb_t *p = a->p;
|
avtab_t *avtab = a->avtab;
|
avrule_t *avrule = a->avrule;
|
class_perm_node_t *cp;
|
uint32_t perms;
|
ebitmap_t src_matches, tgt_matches, self_matches, matches;
|
ebitmap_node_t *snode, *tnode;
|
unsigned int i, j;
|
|
if ((k->specified & AVTAB_ALLOWED) == 0)
|
return 0;
|
|
if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
|
return 0;
|
|
ebitmap_init(&src_matches);
|
ebitmap_init(&tgt_matches);
|
ebitmap_init(&self_matches);
|
ebitmap_init(&matches);
|
|
rc = ebitmap_and(&src_matches, &avrule->stypes.types,
|
&p->attr_type_map[k->source_type - 1]);
|
if (rc)
|
goto oom;
|
|
if (ebitmap_length(&src_matches) == 0)
|
goto exit;
|
|
rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
|
if (rc)
|
goto oom;
|
|
if (avrule->flags == RULE_SELF) {
|
rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1]);
|
if (rc)
|
goto oom;
|
rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
|
if (rc)
|
goto oom;
|
|
if (ebitmap_length(&self_matches) > 0) {
|
rc = ebitmap_union(&tgt_matches, &self_matches);
|
if (rc)
|
goto oom;
|
}
|
}
|
|
if (ebitmap_length(&tgt_matches) == 0)
|
goto exit;
|
|
for (cp = avrule->perms; cp; cp = cp->next) {
|
|
perms = cp->data & d->data;
|
if ((cp->tclass != k->target_class) || !perms) {
|
continue;
|
}
|
|
ebitmap_for_each_bit(&src_matches, snode, i) {
|
if (!ebitmap_node_get_bit(snode, i))
|
continue;
|
ebitmap_for_each_bit(&tgt_matches, tnode, j) {
|
if (!ebitmap_node_get_bit(tnode, j))
|
continue;
|
|
if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
|
a->errors += report_assertion_extended_permissions(handle,p, avrule,
|
i, j, cp, perms, k, avtab);
|
} else {
|
a->errors++;
|
report_failure(handle, p, avrule, i, j, cp, perms);
|
}
|
}
|
}
|
}
|
goto exit;
|
|
oom:
|
ERR(NULL, "Out of memory - unable to check neverallows");
|
|
exit:
|
ebitmap_destroy(&src_matches);
|
ebitmap_destroy(&tgt_matches);
|
ebitmap_destroy(&self_matches);
|
ebitmap_destroy(&matches);
|
return rc;
|
}
|
|
int report_assertion_failures(sepol_handle_t *handle, policydb_t *p, avrule_t *avrule)
|
{
|
int rc;
|
struct avtab_match_args args;
|
|
args.handle = handle;
|
args.p = p;
|
args.avrule = avrule;
|
args.errors = 0;
|
|
rc = avtab_map(&p->te_avtab, report_assertion_avtab_matches, &args);
|
if (rc)
|
goto oom;
|
|
rc = avtab_map(&p->te_cond_avtab, report_assertion_avtab_matches, &args);
|
if (rc)
|
goto oom;
|
|
return args.errors;
|
|
oom:
|
return rc;
|
}
|
|
/*
|
* Look up the extended permissions in avtab and verify that neverallowed
|
* permissions are not granted.
|
*/
|
static int check_assertion_extended_permissions_avtab(avrule_t *avrule, avtab_t *avtab,
|
unsigned int stype, unsigned int ttype,
|
avtab_key_t *k, policydb_t *p)
|
{
|
avtab_ptr_t node;
|
avtab_key_t tmp_key;
|
avtab_extended_perms_t *xperms;
|
av_extended_perms_t *neverallow_xperms = avrule->xperms;
|
ebitmap_t *sattr = &p->type_attr_map[stype];
|
ebitmap_t *tattr = &p->type_attr_map[ttype];
|
ebitmap_node_t *snode, *tnode;
|
unsigned int i, j;
|
int rc = 1;
|
|
memcpy(&tmp_key, k, sizeof(avtab_key_t));
|
tmp_key.specified = AVTAB_XPERMS_ALLOWED;
|
|
ebitmap_for_each_bit(sattr, snode, i) {
|
if (!ebitmap_node_get_bit(snode, i))
|
continue;
|
ebitmap_for_each_bit(tattr, tnode, j) {
|
if (!ebitmap_node_get_bit(tnode, j))
|
continue;
|
tmp_key.source_type = i + 1;
|
tmp_key.target_type = j + 1;
|
for (node = avtab_search_node(avtab, &tmp_key);
|
node;
|
node = avtab_search_node_next(node, tmp_key.specified)) {
|
xperms = node->datum.xperms;
|
|
if ((xperms->specified != AVTAB_XPERMS_IOCTLFUNCTION)
|
&& (xperms->specified != AVTAB_XPERMS_IOCTLDRIVER))
|
continue;
|
rc = check_extended_permissions(neverallow_xperms, xperms);
|
if (rc)
|
break;
|
}
|
}
|
}
|
|
return rc;
|
}
|
|
/*
|
* When the ioctl permission is granted on an avtab entry that matches an
|
* avrule neverallowxperm entry, enumerate over the matching
|
* source/target/class sets to determine if the extended permissions exist
|
* and if the neverallowed ioctls are granted.
|
*
|
* Four scenarios of interest:
|
* 1. PASS - the ioctl permission is not granted for this source/target/class
|
* This case is handled in check_assertion_avtab_match
|
* 2. PASS - The ioctl permission is granted AND the extended permission
|
* is NOT granted
|
* 3. FAIL - The ioctl permission is granted AND no extended permissions
|
* exist
|
* 4. FAIL - The ioctl permission is granted AND the extended permission is
|
* granted
|
*/
|
static int check_assertion_extended_permissions(avrule_t *avrule, avtab_t *avtab,
|
avtab_key_t *k, policydb_t *p)
|
{
|
ebitmap_t src_matches, tgt_matches, self_matches, matches;
|
unsigned int i, j;
|
ebitmap_node_t *snode, *tnode;
|
class_perm_node_t *cp;
|
int rc;
|
int ret = 1;
|
|
ebitmap_init(&src_matches);
|
ebitmap_init(&tgt_matches);
|
ebitmap_init(&self_matches);
|
ebitmap_init(&matches);
|
|
rc = ebitmap_and(&src_matches, &avrule->stypes.types,
|
&p->attr_type_map[k->source_type - 1]);
|
if (rc)
|
goto oom;
|
|
if (ebitmap_length(&src_matches) == 0)
|
goto exit;
|
|
rc = ebitmap_and(&tgt_matches, &avrule->ttypes.types,
|
&p->attr_type_map[k->target_type -1]);
|
if (rc)
|
goto oom;
|
|
if (avrule->flags == RULE_SELF) {
|
rc = ebitmap_and(&matches, &p->attr_type_map[k->source_type - 1],
|
&p->attr_type_map[k->target_type - 1]);
|
if (rc)
|
goto oom;
|
rc = ebitmap_and(&self_matches, &avrule->stypes.types, &matches);
|
if (rc)
|
goto oom;
|
|
if (ebitmap_length(&self_matches) > 0) {
|
rc = ebitmap_union(&tgt_matches, &self_matches);
|
if (rc)
|
goto oom;
|
}
|
}
|
|
if (ebitmap_length(&tgt_matches) == 0)
|
goto exit;
|
|
for (cp = avrule->perms; cp; cp = cp->next) {
|
if (cp->tclass != k->target_class)
|
continue;
|
ebitmap_for_each_bit(&src_matches, snode, i) {
|
if (!ebitmap_node_get_bit(snode, i))
|
continue;
|
ebitmap_for_each_bit(&tgt_matches, tnode, j) {
|
if (!ebitmap_node_get_bit(tnode, j))
|
continue;
|
|
ret = check_assertion_extended_permissions_avtab(
|
avrule, avtab, i, j, k, p);
|
if (ret)
|
goto exit;
|
}
|
}
|
}
|
goto exit;
|
|
oom:
|
ERR(NULL, "Out of memory - unable to check neverallows");
|
|
exit:
|
ebitmap_destroy(&src_matches);
|
ebitmap_destroy(&tgt_matches);
|
ebitmap_destroy(&matches);
|
return ret;
|
}
|
|
static int check_assertion_avtab_match(avtab_key_t *k, avtab_datum_t *d, void *args)
|
{
|
int rc, rc2 = 0;
|
struct avtab_match_args *a = (struct avtab_match_args *)args;
|
policydb_t *p = a->p;
|
avrule_t *avrule = a->avrule;
|
avtab_t *avtab = a->avtab;
|
|
if ((k->specified & AVTAB_ALLOWED) == 0)
|
goto exit;
|
|
if (!match_any_class_permissions(avrule->perms, k->target_class, d->data))
|
goto exit;
|
|
rc = ebitmap_match_any(&avrule->stypes.types, &p->attr_type_map[k->source_type - 1]);
|
if (rc == 0)
|
goto exit;
|
|
if (avrule->flags == RULE_SELF) {
|
/* If the neverallow uses SELF, then it is not enough that the
|
* neverallow's source matches the src and tgt of the rule being checked.
|
* It must match the same thing in the src and tgt, so AND the source
|
* and target together and check for a match on the result.
|
*/
|
ebitmap_t match;
|
rc = ebitmap_and(&match, &p->attr_type_map[k->source_type - 1], &p->attr_type_map[k->target_type - 1] );
|
if (rc) {
|
ebitmap_destroy(&match);
|
goto oom;
|
}
|
rc2 = ebitmap_match_any(&avrule->stypes.types, &match);
|
ebitmap_destroy(&match);
|
}
|
|
/* neverallow may have tgts even if it uses SELF */
|
rc = ebitmap_match_any(&avrule->ttypes.types, &p->attr_type_map[k->target_type -1]);
|
if (rc == 0 && rc2 == 0)
|
goto exit;
|
|
if (avrule->specified == AVRULE_XPERMS_NEVERALLOW) {
|
rc = check_assertion_extended_permissions(avrule, avtab, k, p);
|
if (rc == 0)
|
goto exit;
|
}
|
return 1;
|
|
exit:
|
return 0;
|
|
oom:
|
ERR(NULL, "Out of memory - unable to check neverallows");
|
return rc;
|
}
|
|
int check_assertion(policydb_t *p, avrule_t *avrule)
|
{
|
int rc;
|
struct avtab_match_args args;
|
|
args.handle = NULL;
|
args.p = p;
|
args.avrule = avrule;
|
args.errors = 0;
|
args.avtab = &p->te_avtab;
|
|
rc = avtab_map(&p->te_avtab, check_assertion_avtab_match, &args);
|
|
if (rc == 0) {
|
args.avtab = &p->te_cond_avtab;
|
rc = avtab_map(&p->te_cond_avtab, check_assertion_avtab_match, &args);
|
}
|
|
return rc;
|
}
|
|
int check_assertions(sepol_handle_t * handle, policydb_t * p,
|
avrule_t * avrules)
|
{
|
int rc;
|
avrule_t *a;
|
unsigned long errors = 0;
|
|
if (!avrules) {
|
/* Since assertions are stored in avrules, if it is NULL
|
there won't be any to check. This also prevents an invalid
|
free if the avtabs are never initialized */
|
return 0;
|
}
|
|
for (a = avrules; a != NULL; a = a->next) {
|
if (!(a->specified & (AVRULE_NEVERALLOW | AVRULE_XPERMS_NEVERALLOW)))
|
continue;
|
rc = check_assertion(p, a);
|
if (rc) {
|
rc = report_assertion_failures(handle, p, a);
|
if (rc < 0) {
|
ERR(handle, "Error occurred while checking neverallows");
|
return -1;
|
}
|
errors += rc;
|
}
|
}
|
|
if (errors)
|
ERR(handle, "%lu neverallow failures occurred", errors);
|
|
return errors ? -1 : 0;
|
}
|