summaryrefslogtreecommitdiffstats
path: root/libqpol/src/policy_extend.c
diff options
context:
space:
mode:
Diffstat (limited to 'libqpol/src/policy_extend.c')
-rw-r--r--libqpol/src/policy_extend.c1397
1 files changed, 1397 insertions, 0 deletions
diff --git a/libqpol/src/policy_extend.c b/libqpol/src/policy_extend.c
new file mode 100644
index 0000000..5325a87
--- /dev/null
+++ b/libqpol/src/policy_extend.c
@@ -0,0 +1,1397 @@
+/**
+ * @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 char *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, (const 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();
+ 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 (policy->p->p.policyvers > 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 0;
+}
+
+/**
+ * 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->class;
+ 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->class;
+ 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 0;
+
+ 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;
+}