setools/libqpol/policy_extend.c
Chris PeBenito 3937946900 Update to libsepol 2.4 parser.
There was a struct change internally. Now setools4 requires libsepol 2.4.
2015-02-04 14:34:47 -05:00

1398 lines
36 KiB
C

/**
* @file
* Implementation of the interface for loading and using an extended
* policy image.
*
* @author Jeremy A. Mowery jmowery@tresys.com
* @author Jason Tang jtang@tresys.com
* @author Jeremy Solt jsolt@tresys.com
*
* Copyright (C) 2006-2008 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 <config.h>
#include <sepol/policydb/policydb.h>
#include <sepol/policydb/conditional.h>
#include <sepol/policydb/avtab.h>
#include <sepol/policydb/hashtab.h>
#include <sepol/policydb/flask.h>
#include <sepol/policydb/ebitmap.h>
#include <sepol/policydb/expand.h>
#ifdef HAVE_SEPOL_ERRCODES
#include <sepol/errcodes.h>
#endif
#include <qpol/policy.h>
#include <qpol/policy_extend.h>
#include <qpol/iterator.h>
#include <selinux/selinux.h>
#include <errno.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "qpol_internal.h"
#include "iterator_internal.h"
#include "syn_rule_internal.h"
#ifdef SETOOLS_DEBUG
#include <math.h>
#endif
#define OBJECT_R "object_r"
#define QPOL_SYN_RULE_TABLE_BITS 16
#define QPOL_SYN_RULE_TABLE_SIZE (1 << QPOL_SYN_RULE_TABLE_BITS)
#define QPOL_SYN_RULE_TABLE_MASK (QPOL_SYN_RULE_TABLE_SIZE - 1)
/* original hashing function below */
/*
#define QPOL_SYN_RULE_TABLE_HASH(rule_key) \
((rule_key->class_val + \
(rule_key->target_val << 2) +\
(rule_key->source_val << 9)) & \
QPOL_SYN_RULE_TABLE_MASK)
*/
/* new hashing function, introduced in SETools 3.3 */
#define QPOL_SYN_RULE_TABLE_HASH(rule_key) \
(((((rule_key->source_val & 0xff) << 8) | (rule_key->target_val & 0xff)) ^ \
(rule_key->class_val & 0xf) ^ \
((int) ((size_t) rule_key->cond) & 0xfff0)) & QPOL_SYN_RULE_TABLE_MASK)
typedef struct qpol_syn_rule_key
{
uint32_t rule_type;
uint32_t source_val;
uint32_t target_val;
uint32_t class_val;
cond_node_t *cond;
} qpol_syn_rule_key_t;
typedef struct qpol_syn_rule_list
{
struct qpol_syn_rule *rule;
struct qpol_syn_rule_list *next;
} qpol_syn_rule_list_t;
typedef struct qpol_syn_rule_node
{
qpol_syn_rule_key_t key;
qpol_syn_rule_list_t *rules;
struct qpol_syn_rule_node *next;
} qpol_syn_rule_node_t;
typedef struct qpol_syn_rule_table
{
qpol_syn_rule_node_t **buckets;
} qpol_syn_rule_table_t;
typedef struct qpol_extended_image
{
qpol_syn_rule_table_t *syn_rule_table;
struct qpol_syn_rule **syn_rule_master_list;
size_t master_list_sz;
} qpol_extended_image_t;
struct extend_bogus_alias_struct
{
qpol_policy_t *q;
int num_bogus_aliases;
};
static int extend_find_bogus_alias(hashtab_key_t key __attribute__ ((unused)), hashtab_datum_t datum, void *args)
{
struct extend_bogus_alias_struct *e = (struct extend_bogus_alias_struct *)args;
/* within libqpol, qpol_type_t is the same a libsepol's type_datum_t */
qpol_type_t *qtype = (qpol_type_t *) datum;
type_datum_t *type = (type_datum_t *) datum;
unsigned char isalias;
qpol_type_get_isalias(e->q, qtype, &isalias);
return isalias && type->s.value == 0;
}
static void extend_remove_bogus_alias(hashtab_key_t key, hashtab_datum_t datum, void *args)
{
struct extend_bogus_alias_struct *e = (struct extend_bogus_alias_struct *)args;
free(key);
type_datum_t *type = (type_datum_t *) datum;
type_datum_destroy(type);
free(type);
e->num_bogus_aliases++;
}
/**
* Search the policy for aliases that have a value of 0. These come
* from modular policies with disabled aliases, but end up being
* written to the policy anyways due to a bug in libsepol. These
* bogus aliases are removed from the policy.
* @param policy Policy that may contain broken aliases. This policy
* will be altered by this function.
* @return 0 on success and < 0 on failure; if the call fails, errno
* will be set. On failure, the policy state may be inconsistent.
*/
static int qpol_policy_remove_bogus_aliases(qpol_policy_t * policy)
{
policydb_t *db = NULL;
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
struct extend_bogus_alias_struct e = { policy, 0 };
hashtab_map_remove_on_error(db->p_types.table, extend_find_bogus_alias, extend_remove_bogus_alias, &e);
#ifdef SETOOLS_DEBUG
if (e.num_bogus_aliases > 0) {
WARN(policy, "%s", "This policy contained disabled aliases; they have been removed.");
}
#endif
return 0;
}
/**
* Builds data for the attributes and inserts them into the policydb.
* This function modifies the policydb. Names created for attributes
* are of the form @ttr<value> where value is the value of the attribute
* as a four digit number (prepended with 0's as needed).
* @param policy The policy from which to read the attribute map and
* create the type data for the attributes. This policy will be altered
* by this function.
* @return Returns 0 on success and < 0 on failure; if the call fails,
* errno will be set. On failure, the policy state may be inconsistent
* especially in the case where the hashtab functions return the error.
*/
static int qpol_policy_build_attrs_from_map(qpol_policy_t * policy)
{
policydb_t *db = NULL;
size_t i;
uint32_t bit = 0, count = 0;
ebitmap_node_t *node = NULL;
type_datum_t *tmp_type = NULL, *orig_type;
char *tmp_name = NULL, buff[10];
int error = 0, retv;
INFO(policy, "%s", "Generating attributes for policy. (Step 4 of 5)");
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return -1;
}
db = &policy->p->p;
memset(&buff, 0, 10 * sizeof(char));
for (i = 0; i < db->p_types.nprim; i++) {
count = 0;
ebitmap_for_each_bit(&db->attr_type_map[i], node, bit) {
if (ebitmap_node_get_bit(node, bit))
count++;
}
if (count == 0) {
continue;
}
/* first create a new type_datum_t for the attribute,
* with the attribute's type_list consisting of types
* with this attribute */
/* Does not exist */
if (db->p_type_val_to_name[i] == NULL){
snprintf(buff, 9, "@ttr%04zd", i + 1);
tmp_name = strdup(buff);
if (!tmp_name) {
error = errno;
goto err;
}
}
/* Already exists */
else
tmp_name = db->p_type_val_to_name[i];
tmp_type = calloc(1, sizeof(type_datum_t));
if (!tmp_type) {
error = errno;
goto err;
}
tmp_type->primary = 1;
tmp_type->flavor = TYPE_ATTRIB;
tmp_type->s.value = i + 1;
if (ebitmap_cpy(&tmp_type->types, &db->attr_type_map[i])) {
error = ENOMEM;
goto err;
}
/* now go through each of the member types, and set
* their type_list bit to point back */
ebitmap_for_each_bit(&tmp_type->types, node, bit) {
if (ebitmap_node_get_bit(node, bit)) {
orig_type = db->type_val_to_struct[bit];
if (ebitmap_set_bit(&orig_type->types, tmp_type->s.value - 1, 1)) {
error = ENOMEM;
goto err;
}
}
}
/* Does not exist - insert new */
if (db->p_type_val_to_name[i] == NULL){
retv = hashtab_insert(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type);
if (retv) {
if (retv == SEPOL_ENOMEM)
error = db->p_types.table ? ENOMEM : EINVAL;
else
error = EEXIST;
goto err;
}
}
/* Already exists - replace old */
else {
retv = hashtab_replace(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type, NULL, NULL);
if (retv) {
if (retv == SEPOL_ENOMEM)
error = db->p_types.table ? ENOMEM : EINVAL;
else
error = EEXIST;
goto err;
}
}
db->p_type_val_to_name[i] = tmp_name;
db->type_val_to_struct[i] = tmp_type;
/* memory now owned by symtab do not free */
tmp_name = NULL;
tmp_type = NULL;
}
return STATUS_SUCCESS;
err:
free(tmp_name);
type_datum_destroy(tmp_type);
free(tmp_type);
ERR(policy, "%s", strerror(error));
errno = error;
return STATUS_ERR;
}
/**
* Builds data for empty attributes and inserts them into the policydb.
* This function modifies the policydb. Names created for the attributes
* are of the form @ttr<value> where value is the value of the attribute
* as a four digit number (prepended with 0's as needed).
* @param policy The policy to which to add type data for attributes.
* This policy will be altered by this function.
* @return Returns 0 on success and < 0 on failure; if the call fails,
* errno will be set. On failure, the policy state may be inconsistent
* especially in the case where the hashtab functions return the error.
*/
static int qpol_policy_fill_attr_holes(qpol_policy_t * policy)
{
policydb_t *db = NULL;
char *tmp_name = NULL, buff[10];
int error = 0, retv = 0;
ebitmap_t tmp_bmap = { NULL, 0 };
type_datum_t *tmp_type = NULL;
size_t i;
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
memset(&buff, 0, 10 * sizeof(char));
for (i = 0; i < db->p_types.nprim; i++) {
if (db->type_val_to_struct[i])
continue;
snprintf(buff, 9, "@ttr%04zd", i + 1);
tmp_name = strdup(buff);
if (!tmp_name) {
error = errno;
goto err;
}
tmp_type = calloc(1, sizeof(type_datum_t));
if (!tmp_type) {
error = errno;
goto err;
}
tmp_type->primary = 1;
tmp_type->flavor = TYPE_ATTRIB;
tmp_type->s.value = i + 1;
tmp_type->types = tmp_bmap;
retv = hashtab_insert(db->p_types.table, (hashtab_key_t) tmp_name, (hashtab_datum_t) tmp_type);
if (retv) {
if (retv == SEPOL_ENOMEM)
error = db->p_types.table ? ENOMEM : EINVAL;
else
error = EEXIST;
goto err;
}
db->p_type_val_to_name[i] = tmp_name;
db->type_val_to_struct[i] = tmp_type;
/* memory now owned by symtab do not free */
tmp_name = NULL;
tmp_type = NULL;
}
return STATUS_SUCCESS;
err:
free(tmp_type);
free(tmp_name);
ERR(policy, "%s", strerror(error));
errno = error;
return STATUS_ERR;
}
static const char *const sidnames[] = {
"undefined",
"kernel",
"security",
"unlabeled",
"fs",
"file",
"file_labels",
"init",
"any_socket",
"port",
"netif",
"netmsg",
"node",
"igmp_packet",
"icmp_socket",
"tcp_socket",
"sysctl_modprobe",
"sysctl",
"sysctl_fs",
"sysctl_kernel",
"sysctl_net",
"sysctl_net_unix",
"sysctl_vm",
"sysctl_dev",
"kmod",
"policy",
"scmp_packet",
"devnull"
};
/**
* Uses names from flask to fill in the isid names which are not normally
* saved. This function modified the policydb.
* @param policy Policy to which to add sid names.
* This policy will be altered by this function.
* @return 0 on success and < 0 on failure; if the call fails,
* errno will be set. On failure, the policy state may be inconsistent.
*/
static int qpol_policy_add_isid_names(qpol_policy_t * policy)
{
policydb_t *db = NULL;
ocontext_t *sid = NULL;
uint32_t val = 0;
int error = 0;
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
for (sid = db->ocontexts[OCON_ISID]; sid; sid = sid->next) {
val = (uint32_t) sid->sid[0];
if (val > SECINITSID_NUM)
val = 0;
if (!sid->u.name) {
sid->u.name = strdup(sidnames[val]);
if (!sid->u.name) {
error = errno;
ERR(policy, "%s", strerror(error));
errno = error;
return STATUS_ERR;
}
}
}
return 0;
}
static int extend_assign_role_to_user(hashtab_key_t k __attribute__ ((unused)), hashtab_datum_t d, void *args)
{
user_datum_t *user = (user_datum_t *) d;
uint32_t *value = (uint32_t *) args;
if (ebitmap_set_bit(&user->roles.roles, *value - 1, 1)) {
return -1;
}
return 0;
}
/**
* Modify the special role 'object_r' by assigning it to all users,
* and all types to object_r. This function modifies the policydb.
* @param policy Policy containing object_r. This policy will be
* altered by this function.
* @return 0 on success and < 0 on failure; if the call fails, errno
* will be set. On failure, the policy state may be inconsistent.
*/
static int qpol_policy_add_object_r(qpol_policy_t * policy)
{
policydb_t *db = NULL;
int error = 0;
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
hashtab_datum_t datum = hashtab_search(db->p_roles.table, (hashtab_key_t)OBJECT_R);
if (datum == NULL) {
ERR(policy, "%s", OBJECT_R " not found in policy!");
errno = EIO;
assert(0);
return STATUS_ERR;
}
role_datum_t *role = (role_datum_t *) datum;
uint32_t value = role->s.value;
if (hashtab_map(db->p_users.table, extend_assign_role_to_user, &value) < 0) {
return STATUS_ERR;
}
qpol_iterator_t *iter;
if (qpol_policy_get_type_iter(policy, &iter) < 0) {
return STATUS_ERR;
}
for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
const qpol_type_t *type;
unsigned char isattr, isalias;
if (qpol_iterator_get_item(iter, (void **)&type) < 0 ||
qpol_type_get_isattr(policy, type, &isattr) < 0 || qpol_type_get_isalias(policy, type, &isalias) < 0) {
error = errno;
qpol_iterator_destroy(&iter);
errno = error;
return STATUS_ERR;
}
if (isattr || isalias) {
continue;
}
if (qpol_type_get_value(policy, type, &value) < 0) {
error = errno;
qpol_iterator_destroy(&iter);
errno = error;
return STATUS_ERR;
}
if (ebitmap_set_bit(&role->types.types, value - 1, 1)) {
error = errno;
qpol_iterator_destroy(&iter);
errno = error;
return STATUS_ERR;
}
}
qpol_iterator_destroy(&iter);
return 0;
}
/**
* If the given policy's version is higher than the running system's
* version, then mark it as different. In a future version of
* libqpol, accessors will return data as if the policy were really
* the new version rather than what it actually is.
*/
static int qpol_policy_match_system(qpol_policy_t * policy)
{
int kernvers = security_policyvers();
unsigned int currentvers = policy->p->p.policyvers;
int error;
if (kernvers < 0) {
error = errno;
ERR(policy, "%s", "Could not determine running system's policy version.");
errno = error;
return -1;
}
if (currentvers > (unsigned)kernvers) {
if (sepol_policydb_set_vers(policy->p, kernvers)) {
error = errno;
ERR(policy, "Could not downgrade policy to version %d.", kernvers);
errno = error;
return -1;
}
WARN(policy, "Policy would be downgraded from version %d to %d.", currentvers, kernvers);
}
return 0;
}
/**
* Walks the conditional list and adds links for reverse look up from
* a te/av rule to the conditional from which it came.
* @param policy The policy to which to add conditional trace backs.
* This policy will be altered by this function.
* @return 0 on success and < 0 on failure; if the call fails,
* errno will be set. On failure, the policy state may be inconsistent.
*/
static int qpol_policy_add_cond_rule_traceback(qpol_policy_t * policy)
{
policydb_t *db = NULL;
cond_node_t *cond = NULL;
cond_av_list_t *list_ptr = NULL;
qpol_iterator_t *iter = NULL;
avtab_ptr_t rule = NULL;
int error = 0;
uint32_t rules = 0;
INFO(policy, "%s", "Building conditional rules tables. (Step 5 of 5)");
if (!policy) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return STATUS_ERR;
}
db = &policy->p->p;
rules = (QPOL_RULE_ALLOW | QPOL_RULE_AUDITALLOW | QPOL_RULE_DONTAUDIT);
if (!(policy->options & QPOL_POLICY_OPTION_NO_NEVERALLOWS))
rules |= QPOL_RULE_NEVERALLOW;
/* mark all unconditional rules as enabled */
if (qpol_policy_get_avrule_iter(policy, rules, &iter))
return STATUS_ERR;
for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
if (qpol_iterator_get_item(iter, (void **)&rule)) {
error = errno;
ERR(policy, "%s", strerror(error));
errno = error;
return STATUS_ERR;
}
rule->parse_context = NULL;
rule->merged = QPOL_COND_RULE_ENABLED;
}
qpol_iterator_destroy(&iter);
if (qpol_policy_get_terule_iter(policy, (QPOL_RULE_TYPE_TRANS | QPOL_RULE_TYPE_CHANGE | QPOL_RULE_TYPE_MEMBER), &iter))
return STATUS_ERR;
for (; !qpol_iterator_end(iter); qpol_iterator_next(iter)) {
if (qpol_iterator_get_item(iter, (void **)&rule)) {
error = errno;
ERR(policy, "%s", strerror(error));
errno = error;
return STATUS_ERR;
}
rule->parse_context = NULL;
rule->merged = QPOL_COND_RULE_ENABLED;
}
qpol_iterator_destroy(&iter);
for (cond = db->cond_list; cond; cond = cond->next) {
/* evaluate cond */
cond->cur_state = cond_evaluate_expr(db, cond->expr);
if (cond->cur_state < 0) {
ERR(policy, "Error evaluating conditional: %s", strerror(EILSEQ));
errno = EILSEQ;
return STATUS_ERR;
}
/* walk true list */
for (list_ptr = cond->true_list; list_ptr; list_ptr = list_ptr->next) {
/* field not used after parse, now stores cond */
list_ptr->node->parse_context = (void *)cond;
/* field not used (except by write),
* now storing list and enabled flags */
list_ptr->node->merged = QPOL_COND_RULE_LIST;
if (cond->cur_state)
list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
}
/* walk false list */
for (list_ptr = cond->false_list; list_ptr; list_ptr = list_ptr->next) {
/* field not used after parse, now stores cond */
list_ptr->node->parse_context = (void *)cond;
/* field not used (except by write),
* now storing list and enabled flags */
list_ptr->node->merged = 0; /* i.e. !QPOL_COND_RULE_LIST */
if (!cond->cur_state)
list_ptr->node->merged |= QPOL_COND_RULE_ENABLED;
}
}
return 0;
}
/**
* Free all allocated memory used by a qpol_syn_rule.
* @param r Reference pointer to the rule to destroy.
*/
static void qpol_syn_rule_destroy(struct qpol_syn_rule **r)
{
if (!r || !(*r))
return;
free(*r);
*r = NULL;
}
/**
* Free all memory used by a syn rule list.
* @param list Reference pointer to the head node of
* the syn rule list to destroy. All nodes in the list will
* be destroyed.
*/
static void qpol_syn_rule_list_destroy(qpol_syn_rule_list_t ** list)
{
qpol_syn_rule_list_t *cur = NULL, *next = NULL;
if (!list || !(*list))
return;
for (cur = *list; cur; cur = next) {
next = cur->next;
free(cur);
}
}
/**
* Free all memory used by a syn rule node in the rule table.
* @param node Reference pointer to the first node in the chain.
* All nodes in the chain will be destroyed.
*/
static void qpol_syn_rule_node_destroy(qpol_syn_rule_node_t ** node)
{
qpol_syn_rule_node_t *cur = NULL, *next = NULL;
if (!node || !(*node))
return;
for (cur = *node; cur; cur = next) {
next = cur->next;
qpol_syn_rule_list_destroy(&cur->rules);
free(cur);
}
}
/**
* Free all memory used by the syntactic rule table.
* @param t Reference pointer to the table to destroy.
*/
static void qpol_syn_rule_table_destroy(qpol_syn_rule_table_t ** t)
{
size_t i = 0;
if (!t || !(*t))
return;
for (i = 0; i < QPOL_SYN_RULE_TABLE_SIZE; i++)
qpol_syn_rule_node_destroy(&((*t)->buckets[i]));
free((*t)->buckets);
free(*t);
*t = NULL;
}
/**
* Find the node in the syntactic rule hash table corresponding to a key.
* @param table The table to search.
* @param key The key for which to search.
* @return a valid qpol_syn_rule_node_t pointer on success or NULL on failure.
*/
static qpol_syn_rule_node_t *qpol_syn_rule_table_find_node_by_key(const qpol_syn_rule_table_t * table,
const qpol_syn_rule_key_t * key)
{
qpol_syn_rule_node_t *node = NULL;
for (node = table->buckets[QPOL_SYN_RULE_TABLE_HASH(key)]; node; node = node->next) {
if ((node->key.rule_type & key->rule_type) &&
(node->key.source_val == key->source_val) &&
(node->key.target_val == key->target_val) &&
(node->key.class_val == key->class_val) && (node->key.cond == key->cond))
return node;
}
return NULL;
}
/**
* Given a syn rule key and a syn rule, adds the key/rule pair to the
* syn rule table. Note that this function takes ownership of the
* key.
*
* @param policy Policy associated with the rule.
* @param table The table to which to add the rule.
* @param key Hashtable key for rule lookup.
* @param rule The rule to add.
* @return 0 on success and < 0 on failure; if the call fails,
* errno will be set and the table may be in an inconsistent state.
*/
static int qpol_syn_rule_table_insert_entry(qpol_policy_t * policy,
qpol_syn_rule_table_t * table, qpol_syn_rule_key_t * key, struct qpol_syn_rule *rule)
{
int error = 0;
qpol_syn_rule_node_t *table_node = NULL;
qpol_syn_rule_list_t *list_entry = NULL;
if (!(list_entry = malloc(sizeof(qpol_syn_rule_list_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
return -1;
}
list_entry->rule = rule;
table_node = qpol_syn_rule_table_find_node_by_key(table, key);
if (table_node) {
list_entry->next = table_node->rules;
table_node->rules = list_entry;
} else {
list_entry->next = NULL;
if (!(table_node = malloc(sizeof(qpol_syn_rule_node_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
free(list_entry);
return -1;
}
table_node->key = *key;
table_node->rules = list_entry;
size_t hash = QPOL_SYN_RULE_TABLE_HASH(key);
table_node->next = table->buckets[hash];
table->buckets[hash] = table_node;
}
return error;
}
/**
* Add a syntactic rule (sepol's avrule_t) to the syntactic rule table.
* @param policy Policy associated with the rule.
* @param table The table to which to add the rule.
* @param rule The rule to add.
* @param cond The conditional associated with the rule (NULL if
* unconditional). with the rule (needed for conditional tracking).
* @param branch If the rule is conditional, then 0 if in the true
* branch, 1 if in else.
* @return 0 on success and < 0 on failure; if the call fails,
* errno will be set and the table may be in an inconsistent state.
*/
static int qpol_syn_rule_table_insert_sepol_avrule(qpol_policy_t * policy, qpol_syn_rule_table_t * table, avrule_t * rule,
cond_node_t * cond, int branch)
{
int error = 0;
qpol_syn_rule_key_t key = { 0, 0, 0, 0, NULL };
struct qpol_syn_rule *new_rule = NULL;
ebitmap_t source_types, source_types2, target_types, target_types2;
ebitmap_node_t *snode = NULL, *tnode = NULL;
unsigned int i, j;
class_perm_node_t *class_node = NULL;
if (!(new_rule = malloc(sizeof(struct qpol_syn_rule)))) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
new_rule->rule = rule;
new_rule->cond = cond;
new_rule->cond_branch = branch;
policy->ext->syn_rule_master_list[policy->ext->master_list_sz] = new_rule;
policy->ext->master_list_sz++;
if (type_set_expand(&rule->stypes, &source_types, &policy->p->p, 0) ||
type_set_expand(&rule->stypes, &source_types2, &policy->p->p, 1)) {
ERR(policy, "%s", strerror(ENOMEM));
error = ENOMEM;
goto err;
}
if (type_set_expand(&rule->ttypes, &target_types, &policy->p->p, 0) ||
type_set_expand(&rule->ttypes, &target_types2, &policy->p->p, 1)) {
ERR(policy, "%s", strerror(ENOMEM));
error = ENOMEM;
goto err;
}
if (ebitmap_union(&source_types, &source_types2) || ebitmap_union(&target_types, &target_types2)) {
ERR(policy, "%s", strerror(ENOMEM));
error = ENOMEM;
goto err;
}
ebitmap_for_each_bit(&source_types, snode, i) {
if (!ebitmap_get_bit(&source_types, i))
continue;
if (rule->flags & RULE_SELF) {
for (class_node = rule->perms; class_node; class_node = class_node->next) {
key.rule_type = rule->specified;
key.source_val = key.target_val = i + 1;
key.class_val = class_node->tclass;
key.cond = cond;
if (qpol_syn_rule_table_insert_entry(policy, table, &key, new_rule))
goto err;
}
}
ebitmap_for_each_bit(&target_types, tnode, j) {
if (!ebitmap_get_bit(&target_types, j))
continue;
for (class_node = rule->perms; class_node; class_node = class_node->next) {
key.rule_type = rule->specified;
key.source_val = i + 1;
key.target_val = j + 1;
key.class_val = class_node->tclass;
key.cond = cond;
if (qpol_syn_rule_table_insert_entry(policy, table, &key, new_rule))
goto err;
}
}
}
ebitmap_destroy(&source_types);
ebitmap_destroy(&source_types2);
ebitmap_destroy(&target_types);
ebitmap_destroy(&target_types2);
return error;
err:
ebitmap_destroy(&source_types);
ebitmap_destroy(&source_types2);
ebitmap_destroy(&target_types);
ebitmap_destroy(&target_types2);
return -1;
}
int qpol_policy_build_syn_rule_table(qpol_policy_t * policy)
{
int error = 0, created = 0;
avrule_block_t *cur_block = NULL;
avrule_decl_t *decl = NULL;
avrule_t *cur_rule = NULL;
cond_node_t *cur_cond = NULL, *remapped_cond;
if (!policy) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return -1;
}
if (!policy->ext) {
policy->ext = calloc(1, sizeof(qpol_extended_image_t));
if (!policy->ext) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
}
if (policy->ext->syn_rule_table)
return 0; /* already built */
policy->ext->syn_rule_table = calloc(1, sizeof(qpol_syn_rule_table_t));
if (!policy->ext->syn_rule_table) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
policy->ext->syn_rule_table->buckets = calloc(QPOL_SYN_RULE_TABLE_SIZE, sizeof(qpol_syn_rule_node_t *));
if (!policy->ext->syn_rule_table->buckets) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
policy->ext->master_list_sz = 0;
for (cur_block = policy->p->p.global; cur_block; cur_block = cur_block->next) {
decl = cur_block->enabled;
if (!decl)
continue;
for (cur_rule = decl->avrules; cur_rule; cur_rule = cur_rule->next) {
policy->ext->master_list_sz++;
}
for (cur_cond = decl->cond_list; cur_cond; cur_cond = cur_cond->next) {
for (cur_rule = cur_cond->avtrue_list; cur_rule; cur_rule = cur_rule->next) {
policy->ext->master_list_sz++;
}
for (cur_rule = cur_cond->avfalse_list; cur_rule; cur_rule = cur_rule->next) {
policy->ext->master_list_sz++;
}
}
}
if (policy->ext->master_list_sz == 0) {
policy->ext->syn_rule_master_list = NULL;
return 0; /* policy is not a source policy */
}
INFO(policy, "%s", "Building syntactic rules tables.");
policy->ext->syn_rule_master_list = calloc(policy->ext->master_list_sz, sizeof(struct qpol_syn_rule *));
if (!policy->ext->syn_rule_master_list) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
/* reset size as it will represent the current number of elements inserted */
policy->ext->master_list_sz = 0;
for (cur_block = policy->p->p.global; cur_block; cur_block = cur_block->next) {
decl = cur_block->enabled;
if (!decl)
continue;
for (cur_rule = decl->avrules; cur_rule; cur_rule = cur_rule->next) {
if (qpol_syn_rule_table_insert_sepol_avrule(policy, policy->ext->syn_rule_table, cur_rule, NULL, 0)) {
error = errno;
goto err;
}
}
for (cur_cond = decl->cond_list; cur_cond; cur_cond = cur_cond->next) {
/* convert the cond within an avrule_decl to
* the expanded cond */
remapped_cond = cond_node_find(&policy->p->p, cur_cond, policy->p->p.cond_list, &created);
if (created || !remapped_cond) {
cond_node_destroy(remapped_cond);
error = EIO;
ERR(policy, "%s", "Inconsistent conditional records");
assert(0);
goto err;
}
for (cur_rule = cur_cond->avtrue_list; cur_rule; cur_rule = cur_rule->next) {
if (qpol_syn_rule_table_insert_sepol_avrule
(policy, policy->ext->syn_rule_table, cur_rule, remapped_cond, 0)) {
error = errno;
goto err;
}
}
for (cur_rule = cur_cond->avfalse_list; cur_rule; cur_rule = cur_rule->next) {
if (qpol_syn_rule_table_insert_sepol_avrule
(policy, policy->ext->syn_rule_table, cur_rule, remapped_cond, 1)) {
error = errno;
goto err;
}
}
}
}
#ifdef SETOOLS_DEBUG
/*
* Debugging code to measure the how well the syntactic rules
* are being hashed. Calculate the min, max, and std
* deviation.
*/
size_t bucket;
float o2 = 0.0f;
long total_entries = 0;
for (bucket = 0; bucket < QPOL_SYN_RULE_TABLE_SIZE; bucket++) {
qpol_syn_rule_node_t *n = policy->ext->syn_rule_table->buckets[bucket];
while (n != NULL) {
total_entries++;
n = n->next;
}
}
float expected_value = total_entries * 1.0f / QPOL_SYN_RULE_TABLE_SIZE;
size_t min_items = total_entries;
size_t max_items = 0;
for (bucket = 0; bucket < QPOL_SYN_RULE_TABLE_SIZE; bucket++) {
size_t num_items = 0;
qpol_syn_rule_node_t *n = policy->ext->syn_rule_table->buckets[bucket];
while (n != NULL) {
num_items++;
n = n->next;
}
if (num_items > max_items) {
max_items = num_items;
}
if (num_items < min_items) {
min_items = num_items;
}
o2 += (num_items - expected_value) * (num_items - expected_value);
}
float stddev = sqrtf(o2 / (QPOL_SYN_RULE_TABLE_SIZE - 1));
fprintf(stderr, "libqpol synrule table %d bits: total entries %lu, expected %g\n", QPOL_SYN_RULE_TABLE_BITS, total_entries,
expected_value);
fprintf(stderr, " min %zd, max %zd, stddev %g\n", min_items, max_items, stddev);
#endif
return 0;
err:
if (policy->ext)
qpol_syn_rule_table_destroy(&policy->ext->syn_rule_table);
errno = error;
return -1;
}
/**
* Free all memory used by a qpol extended image and set it to NULL.
* @param ext The extended image to destroy.
*/
void qpol_extended_image_destroy(qpol_extended_image_t ** ext)
{
size_t i = 0;
if (!ext || !(*ext))
return;
qpol_syn_rule_table_destroy(&((*ext)->syn_rule_table));
for (i = 0; i < (*ext)->master_list_sz; i++) {
qpol_syn_rule_destroy(&((*ext)->syn_rule_master_list[i]));
}
free((*ext)->syn_rule_master_list);
free(*ext);
*ext = NULL;
}
int policy_extend(qpol_policy_t * policy)
{
int retv, error;
policydb_t *db = NULL;
if (policy == NULL) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return -1;
}
db = &policy->p->p;
retv = qpol_policy_remove_bogus_aliases(policy);
if (retv) {
error = errno;
goto err;
}
if (db->attr_type_map) {
retv = qpol_policy_build_attrs_from_map(policy);
if (retv) {
error = errno;
goto err;
}
if (db->policy_type == POLICY_KERN) {
retv = qpol_policy_fill_attr_holes(policy);
if (retv) {
error = errno;
goto err;
}
}
}
retv = qpol_policy_add_isid_names(policy);
if (retv) {
error = errno;
goto err;
}
retv = qpol_policy_add_object_r(policy);
if (retv) {
error = errno;
goto err;
}
if ((policy->options & QPOL_POLICY_OPTION_MATCH_SYSTEM) && qpol_policy_match_system(policy)) {
error = errno;
goto err;
}
if (policy->options & QPOL_POLICY_OPTION_NO_RULES)
return STATUS_SUCCESS;
retv = qpol_policy_add_cond_rule_traceback(policy);
if (retv) {
error = errno;
goto err;
}
return STATUS_SUCCESS;
err:
/* no need to call ERR here as it will already have been called */
qpol_extended_image_destroy(&policy->ext);
errno = error;
return STATUS_ERR;
}
typedef struct syn_rule_state
{
qpol_syn_rule_node_t *node;
qpol_syn_rule_list_t *cur;
} syn_rule_state_t;
static int syn_rule_state_end(const qpol_iterator_t * iter)
{
syn_rule_state_t *srs = NULL;
if (!iter || !(srs = qpol_iterator_state(iter))) {
errno = EINVAL;
return STATUS_ERR;
}
return (srs->cur ? 0 : 1);
}
static void *syn_rule_state_get_cur(const qpol_iterator_t * iter)
{
syn_rule_state_t *srs = NULL;
if (!iter || !(srs = qpol_iterator_state(iter)) || qpol_iterator_end(iter)) {
errno = EINVAL;
return NULL;
}
return srs->cur->rule;
}
static int syn_rule_state_next(qpol_iterator_t * iter)
{
syn_rule_state_t *srs = NULL;
if (!iter || !(srs = qpol_iterator_state(iter))) {
errno = EINVAL;
return STATUS_ERR;
}
if (qpol_iterator_end(iter)) {
errno = ERANGE;
return STATUS_ERR;
}
srs->cur = srs->cur->next;
return STATUS_SUCCESS;
}
static size_t syn_rule_state_size(const qpol_iterator_t * iter)
{
size_t count = 0;
qpol_syn_rule_list_t *tmp = NULL;
syn_rule_state_t *srs = NULL;
if (!iter || !(srs = qpol_iterator_state(iter))) {
errno = EINVAL;
return 0;
}
for (tmp = srs->node->rules; tmp; tmp = tmp->next)
count++;
return count;
}
int qpol_avrule_get_syn_avrule_iter(const qpol_policy_t * policy, const struct qpol_avrule *rule, qpol_iterator_t ** iter)
{
qpol_syn_rule_key_t *key = NULL;
const qpol_type_t *tmp_type;
const qpol_class_t *tmp_class;
const qpol_cond_t *tmp_cond;
syn_rule_state_t *srs = NULL;
uint32_t tmp_val;
int error = 0;
if (iter)
*iter = NULL;
if (!policy || !policy->ext || !rule || !iter) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return -1;
}
/* build key */
if (!(key = calloc(1, sizeof(qpol_syn_rule_key_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
if (qpol_avrule_get_rule_type(policy, rule, &tmp_val)) {
error = errno;
goto err;
}
key->rule_type = (tmp_val == QPOL_RULE_DONTAUDIT ? (AVRULE_AUDITDENY | AVRULE_DONTAUDIT) : tmp_val);
if (qpol_avrule_get_source_type(policy, rule, &tmp_type)) {
error = errno;
goto err;
}
if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
error = errno;
goto err;
}
key->source_val = tmp_val;
if (qpol_avrule_get_target_type(policy, rule, &tmp_type)) {
error = errno;
goto err;
}
if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
error = errno;
goto err;
}
key->target_val = tmp_val;
if (qpol_avrule_get_object_class(policy, rule, &tmp_class)) {
error = errno;
goto err;
}
if (qpol_class_get_value(policy, tmp_class, &tmp_val)) {
error = errno;
goto err;
}
key->class_val = tmp_val;
if (qpol_avrule_get_cond(policy, rule, &tmp_cond)) {
error = errno;
goto err;
}
key->cond = (cond_node_t *) tmp_cond;
/* build state object */
if (!(srs = calloc(1, sizeof(syn_rule_state_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
srs->node = qpol_syn_rule_table_find_node_by_key(policy->ext->syn_rule_table, key);
if (!srs->node) {
ERR(policy, "%s", "Unable to locate syntactic rules for semantic av rule");
errno = ENOENT;
goto err;
}
srs->cur = srs->node->rules;
if (qpol_iterator_create(policy, (void *)srs,
syn_rule_state_get_cur, syn_rule_state_next, syn_rule_state_end, syn_rule_state_size, free, iter))
{
error = errno;
goto err;
}
free(key);
return 0;
err:
free(key);
free(srs);
errno = error;
return -1;
}
int qpol_terule_get_syn_terule_iter(const qpol_policy_t * policy, const struct qpol_terule *rule, qpol_iterator_t ** iter)
{
qpol_syn_rule_key_t *key = NULL;
const qpol_type_t *tmp_type;
const qpol_class_t *tmp_class;
const qpol_cond_t *tmp_cond;
syn_rule_state_t *srs = NULL;
uint32_t tmp_val;
int error = 0;
if (iter)
*iter = NULL;
if (!policy || !policy->ext || !rule || !iter) {
ERR(policy, "%s", strerror(EINVAL));
errno = EINVAL;
return -1;
}
/* build key */
if (!(key = calloc(1, sizeof(qpol_syn_rule_key_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
if (qpol_terule_get_rule_type(policy, rule, &tmp_val)) {
error = errno;
goto err;
}
key->rule_type = tmp_val;
if (qpol_terule_get_source_type(policy, rule, &tmp_type)) {
error = errno;
goto err;
}
if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
error = errno;
goto err;
}
key->source_val = tmp_val;
if (qpol_terule_get_target_type(policy, rule, &tmp_type)) {
error = errno;
goto err;
}
if (qpol_type_get_value(policy, tmp_type, &tmp_val)) {
error = errno;
goto err;
}
key->target_val = tmp_val;
if (qpol_terule_get_object_class(policy, rule, &tmp_class)) {
error = errno;
goto err;
}
if (qpol_class_get_value(policy, tmp_class, &tmp_val)) {
error = errno;
goto err;
}
key->class_val = tmp_val;
if (qpol_terule_get_cond(policy, rule, &tmp_cond)) {
error = errno;
goto err;
}
key->cond = (cond_node_t *) tmp_cond;
/* build state object */
if (!(srs = calloc(1, sizeof(syn_rule_state_t)))) {
error = errno;
ERR(policy, "%s", strerror(error));
goto err;
}
srs->node = qpol_syn_rule_table_find_node_by_key(policy->ext->syn_rule_table, key);
if (!srs->node) {
ERR(policy, "%s", "Unable to locate syntactic rules for semantic te rule");
error = ENOENT;
goto err;
}
srs->cur = srs->node->rules;
if (qpol_iterator_create(policy, (void *)srs,
syn_rule_state_get_cur, syn_rule_state_next, syn_rule_state_end, syn_rule_state_size, free, iter))
{
error = errno;
goto err;
}
free(key);
return 0;
err:
free(key);
free(srs);
errno = error;
return -1;
}