/* 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;
}