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