All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy
@ 2015-03-31 17:17 James Carter
  2015-03-31 17:17 ` [PATCH 1/3 v3] libsepol, policycoreutils: Move functions to convert a module package to CIL James Carter
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: James Carter @ 2015-03-31 17:17 UTC (permalink / raw)
  To: selinux

V3 fixes another whitespace issue.
V2 fixes some whitespace issues and make the new libsepol file LGPL instead of GPL.

This patch set moves the code to generate CIL from pp.c in policycoreutils/hll/pp to libsepol, adds a new function to generate CIL from a module policydb, and modifies checkpolicy and checkmodule to support generating CIL as their output.

The primary motivation of this work is to allow SE for Android to use the CIl compiler. Converting the policy.conf to CIL and then compiling to the kernel binary policy results in a policy that is about 20% smaller. The smaller size is because type expressions with negations are converted to type attribute sets in CIL instead of being expanded. 

James Carter (3):
  libsepol, policycoreutils: Move functions to convert a module package
    to CIL
  libsepol: add function to generate CIL from a module policydb
  checkpolicy: Add support for generating CIL

 checkpolicy/checkmodule.c              |   59 +-
 checkpolicy/checkpolicy.c              |   79 +-
 libsepol/include/sepol/module_to_cil.h |    8 +
 libsepol/src/module_to_cil.c           | 4010 ++++++++++++++++++++++++++++++++
 policycoreutils/hll/pp/pp.c            | 3830 +-----------------------------
 5 files changed, 4107 insertions(+), 3879 deletions(-)
 create mode 100644 libsepol/include/sepol/module_to_cil.h
 create mode 100644 libsepol/src/module_to_cil.c

-- 
1.9.3

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH 1/3 v3] libsepol, policycoreutils: Move functions to convert a module package to CIL
  2015-03-31 17:17 [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy James Carter
@ 2015-03-31 17:17 ` James Carter
  2015-03-31 17:18 ` [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb James Carter
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 11+ messages in thread
From: James Carter @ 2015-03-31 17:17 UTC (permalink / raw)
  To: selinux

Move code to convert a policy module to CIL from the policy package to
CIL conversion tool, pp, in policycoreutils to libsepol. The only changes
to the code are the additions of the prefix "sepol_" to the functions
sepol_module_package_to_cil() and sepol_ppfile_to_module_package(). This
code is being changed from GPL to LGPL with permission from Tresys.

Convert pp to use the renamed functions in libsepol.

Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
---
 libsepol/include/sepol/module_to_cil.h |    6 +
 libsepol/src/module_to_cil.c           | 3873 ++++++++++++++++++++++++++++++++
 policycoreutils/hll/pp/pp.c            | 3830 +------------------------------
 3 files changed, 3882 insertions(+), 3827 deletions(-)
 create mode 100644 libsepol/include/sepol/module_to_cil.h
 create mode 100644 libsepol/src/module_to_cil.c

diff --git a/libsepol/include/sepol/module_to_cil.h b/libsepol/include/sepol/module_to_cil.h
new file mode 100644
index 0000000..1d0225c
--- /dev/null
+++ b/libsepol/include/sepol/module_to_cil.h
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+#include <sepol/module.h>
+
+int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg);
+int sepol_ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg);
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
new file mode 100644
index 0000000..8326826
--- /dev/null
+++ b/libsepol/src/module_to_cil.c
@@ -0,0 +1,3873 @@
+/* Authors: Steve Lawrence <slawrence@tresys.com>
+ *
+ * Functions to convert policy module to CIL
+ *
+ * Copyright (C) 2015 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 <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <sepol/module.h>
+#include <sepol/module_to_cil.h>
+#include <sepol/policydb/conditional.h>
+#include <sepol/policydb/hashtab.h>
+#include <sepol/policydb/polcaps.h>
+#include <sepol/policydb/policydb.h>
+#include <sepol/policydb/services.h>
+#include <sepol/policydb/util.h>
+
+#ifdef __GNUC__
+#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#else
+#  define UNUSED(x) UNUSED_ ## x
+#endif
+
+FILE *out_file;
+
+#define STACK_SIZE 16
+#define DEFAULT_LEVEL "systemlow"
+#define DEFAULT_OBJECT "object_r"
+#define GEN_REQUIRE_ATTR "cil_gen_require"
+
+__attribute__ ((format(printf, 1, 2)))
+static void log_err(const char *fmt, ...)
+{
+	va_list argptr;
+	va_start(argptr, fmt);
+	if (vfprintf(stderr, fmt, argptr) < 0) {
+		_exit(EXIT_FAILURE);
+	}
+	va_end(argptr);
+	if (fprintf(stderr, "\n") < 0) {
+		_exit(EXIT_FAILURE);
+	}
+}
+
+static void cil_indent(int indent)
+{
+	if (fprintf(out_file, "%*s", indent * 4, "") < 0) {
+		log_err("Failed to write to output");
+		_exit(EXIT_FAILURE);
+	}
+}
+
+__attribute__ ((format(printf, 1, 2)))
+static void cil_printf(const char *fmt, ...) {
+	va_list argptr;
+	va_start(argptr, fmt);
+	if (vfprintf(out_file, fmt, argptr) < 0) {
+		log_err("Failed to write to output");
+		_exit(EXIT_FAILURE);
+	}
+	va_end(argptr);
+}
+
+__attribute__ ((format(printf, 2, 3)))
+static void cil_println(int indent, const char *fmt, ...)
+{
+	cil_indent(indent);
+	va_list argptr;
+	va_start(argptr, fmt);
+	if (vfprintf(out_file, fmt, argptr) < 0) {
+		log_err("Failed to write to output");
+		_exit(EXIT_FAILURE);
+	}
+	va_end(argptr);
+	if (fprintf(out_file, "\n") < 0) {
+		log_err("Failed to write to output");
+		_exit(EXIT_FAILURE);
+	}
+}
+
+struct map_args {
+	struct policydb *pdb;
+	struct avrule_block *block;
+	struct stack *decl_stack;
+	int scope;
+	int indent;
+	int sym_index;
+};
+
+struct stack {
+	 void **stack;
+	 int pos;
+	 int size;
+};
+
+struct role_list_node {
+	char *role_name;
+	role_datum_t *role;
+};
+
+struct attr_list_node {
+	char *attribute;
+	int is_type;
+	union {
+		struct type_set *ts;
+		struct role_set *rs;
+	} set;
+};
+
+struct list_node {
+	void *data;
+	struct list_node *next;
+};
+
+struct list {
+	struct list_node *head;
+};
+
+/* A linked list of all roles stored in the pdb
+ * which is iterated to determine types associated
+ * with each role when printing role_type statements
+ */
+static struct list *role_list;
+
+static void list_destroy(struct list **list)
+{
+	struct list_node *curr = (*list)->head;
+	struct list_node *tmp;
+
+	while (curr != NULL) {
+		tmp = curr->next;
+		free(curr);
+		curr = tmp;
+	}
+
+	free(*list);
+	*list = NULL;
+}
+
+static void role_list_destroy(void)
+{
+	struct list_node *curr = role_list->head;
+
+	while (curr != NULL) {
+		free(curr->data);
+		curr->data = NULL;
+		curr = curr->next;
+	}
+
+	list_destroy(&role_list);
+}
+
+static void attr_list_destroy(struct list **attr_list)
+{
+	if (attr_list == NULL || *attr_list == NULL) {
+		return;
+	}
+
+	struct list_node *curr = (*attr_list)->head;
+	struct attr_list_node *attr;
+
+	while (curr != NULL) {
+		attr = curr->data;
+		if (attr != NULL) {
+			free(attr->attribute);
+		}
+
+		free(curr->data);
+		curr->data = NULL;
+		curr = curr->next;
+	}
+
+	list_destroy(attr_list);
+}
+
+static int list_init(struct list **list)
+{
+	int rc = -1;
+	struct list *l = calloc(1, sizeof(*l));
+	if (l == NULL) {
+		goto exit;
+	}
+
+	*list = l;
+
+	return 0;
+
+exit:
+	list_destroy(&l);
+	return rc;
+}
+
+static int list_prepend(struct list *list, void *data)
+{
+	int rc = -1;
+	struct list_node *node = calloc(1, sizeof(*node));
+	if (node == NULL) {
+		goto exit;
+	}
+
+	node->data = data;
+	node->next = list->head;
+	list->head = node;
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+static int roles_gather_map(char *key, void *data, void *args)
+{
+	struct role_list_node *role_node;
+	role_datum_t *role = data;
+	int rc = -1;
+
+	role_node = calloc(1, sizeof(*role_node));
+	if (role_node == NULL) {
+		return rc;
+	}
+
+	role_node->role_name = key;
+	role_node->role = role;
+
+	rc = list_prepend((struct list *)args, role_node);
+	return rc;
+}
+
+static int role_list_create(hashtab_t roles_tab)
+{
+	int rc = -1;
+
+	rc = list_init(&role_list);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = hashtab_map(roles_tab, roles_gather_map, role_list);
+
+exit:
+	return rc;
+}
+
+// array of lists, where each list contains all the aliases defined in the scope at index i
+static struct list **typealias_lists;
+static uint32_t typealias_lists_len;
+
+static int typealiases_gather_map(char *key, void *data, void *arg)
+{
+	int rc = -1;
+	struct type_datum *type = data;
+	struct policydb *pdb = arg;
+	struct scope_datum *scope;
+	uint32_t i;
+	uint32_t scope_id;
+
+	if (type->primary != 1) {
+		scope = hashtab_search(pdb->scope[SYM_TYPES].table, key);
+		if (scope == NULL) {
+			return -1;
+		}
+
+		for (i = 0; i < scope->decl_ids_len; i++) {
+			scope_id = scope->decl_ids[i];
+			if (typealias_lists[scope_id] == NULL) {
+				rc = list_init(&typealias_lists[scope_id]);
+				if (rc != 0) {
+					goto exit;
+				}
+			}
+			list_prepend(typealias_lists[scope_id], key);
+		}
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static void typealias_list_destroy(void)
+{
+	uint32_t i;
+	for (i = 0; i < typealias_lists_len; i++) {
+		if (typealias_lists[i] != NULL) {
+			list_destroy(&typealias_lists[i]);
+		}
+	}
+	typealias_lists_len = 0;
+	free(typealias_lists);
+	typealias_lists = NULL;
+}
+
+static int typealias_list_create(struct policydb *pdb)
+{
+	uint32_t max_decl_id = 0;
+	struct avrule_decl *decl;
+	struct avrule_block *block;
+	uint32_t rc = -1;
+
+	for (block = pdb->global; block != NULL; block = block->next) {
+		decl = block->branch_list;
+		if (decl->decl_id > max_decl_id) {
+			max_decl_id = decl->decl_id;
+		}
+	}
+
+	typealias_lists = calloc(max_decl_id + 1, sizeof(*typealias_lists));
+	typealias_lists_len = max_decl_id + 1;
+
+	rc = hashtab_map(pdb->p_types.table, typealiases_gather_map, pdb);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	return 0;
+
+exit:
+	typealias_list_destroy();
+
+	return rc;
+}
+
+
+static int stack_destroy(struct stack **stack)
+{
+	if (stack == NULL || *stack == NULL) {
+		return 0;
+	}
+
+	free((*stack)->stack);
+	free(*stack);
+	*stack = NULL;
+
+	return 0;
+}
+
+static int stack_init(struct stack **stack)
+{
+	int rc = -1;
+	struct stack *s = calloc(1, sizeof(*s));
+	if (s == NULL) {
+		goto exit;
+	}
+
+	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
+	if (s->stack == NULL) {
+		goto exit;
+	}
+
+	s->pos = -1;
+	s->size = STACK_SIZE;
+
+	*stack = s;
+
+	return 0;
+
+exit:
+	stack_destroy(&s);
+	return rc;
+}
+
+static int stack_push(struct stack *stack, void *ptr)
+{
+	int rc = -1;
+	void *new_stack;
+
+	if (stack->pos + 1 == stack->size) {
+		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
+		if (new_stack == NULL) {
+			goto exit;
+		}
+		stack->stack = new_stack;
+		stack->size *= 2;
+	}
+
+	stack->pos++;
+	stack->stack[stack->pos] = ptr;
+
+	rc = 0;
+exit:
+	return rc;
+}
+
+static void *stack_pop(struct stack *stack)
+{
+	if (stack->pos == -1) {
+		return NULL;
+	}
+
+	stack->pos--;
+	return stack->stack[stack->pos + 1];
+}
+
+static void *stack_peek(struct stack *stack)
+{
+	if (stack->pos == -1) {
+		return NULL;
+	}
+
+	return stack->stack[stack->pos];
+}
+
+static int is_id_in_scope_with_start(struct policydb *pdb, struct stack *decl_stack, int start, uint32_t symbol_type, char *id)
+{
+	int i;
+	uint32_t j;
+	struct avrule_decl *decl;
+	struct scope_datum *scope;
+
+	scope = hashtab_search(pdb->scope[symbol_type].table, id);
+	if (scope == NULL) {
+		return 0;
+	}
+
+	for (i = start; i >= 0; i--) {
+		decl = decl_stack->stack[i];
+
+		for (j = 0; j < scope->decl_ids_len; j++) {
+			if (scope->decl_ids[j] == decl->decl_id) {
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int is_id_in_ancestor_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
+{
+	int start = decl_stack->pos - 1;
+
+	return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
+}
+
+static int is_id_in_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
+{
+	int start = decl_stack->pos;
+
+	return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
+}
+
+static int semantic_level_to_cil(struct policydb *pdb, int sens_offset, struct mls_semantic_level *level)
+{
+	struct mls_semantic_cat *cat;
+
+	cil_printf("(%s ", pdb->p_sens_val_to_name[level->sens - sens_offset]);
+
+	if (level->cat != NULL) {
+		cil_printf("(");
+	}
+
+	for (cat = level->cat; cat != NULL; cat = cat->next) {
+		if (cat->low == cat->high) {
+			cil_printf("%s", pdb->p_cat_val_to_name[cat->low - 1]);
+		} else {
+			cil_printf("range %s %s", pdb->p_cat_val_to_name[cat->low - 1], pdb->p_cat_val_to_name[cat->high - 1]);
+		}
+
+		if (cat->next != NULL) {
+			cil_printf(" ");
+		}
+	}
+
+	if (level->cat != NULL) {
+		cil_printf(")");
+	}
+
+	cil_printf(")");
+
+	return 0;
+}
+
+static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const struct class_perm_node *classperms)
+{
+	int rc = -1;
+	const char *rule;
+	const struct class_perm_node *classperm;
+	char *perms;
+
+	switch (type) {
+	case AVRULE_ALLOWED:
+		rule = "allow";
+		break;
+	case AVRULE_AUDITALLOW:
+		rule = "auditallow";
+		break;
+	case AVRULE_AUDITDENY:
+		rule = "auditdenty";
+		break;
+	case AVRULE_DONTAUDIT:
+		rule = "dontaudit";
+		break;
+	case AVRULE_NEVERALLOW:
+		rule = "neverallow";
+		break;
+	case AVRULE_TRANSITION:
+		rule = "typetransition";
+		break;
+	case AVRULE_MEMBER:
+		rule = "typemember";
+		break;
+	case AVRULE_CHANGE:
+		rule = "typechange";
+		break;
+	default:
+		log_err("Unknown avrule type: %i", type);
+		rc = -1;
+		goto exit;
+	}
+
+	for (classperm = classperms; classperm != NULL; classperm = classperm->next) {
+		if (type & AVRULE_AV) {
+			perms = sepol_av_to_string(pdb, classperm->tclass, classperm->data);
+			if (perms == NULL) {
+				log_err("Failed to generate permission string");
+				rc = -1;
+				goto exit;
+			}
+			cil_println(indent, "(%s %s %s (%s (%s)))",
+					rule, src, tgt,
+					pdb->p_class_val_to_name[classperm->tclass - 1],
+					perms + 1);
+		} else {
+			cil_println(indent, "(%s %s %s %s %s)",
+					rule, src, tgt,
+					pdb->p_class_val_to_name[classperm->tclass - 1],
+					pdb->p_type_val_to_name[classperm->data - 1]);
+		}
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int num_digits(int n)
+{
+	int num = 1;
+	while (n >= 10) {
+		n /= 10;
+		num++;
+	}
+	return num;
+}
+
+static int set_to_cil_attr(struct policydb *pdb, int is_type, char ***names, uint32_t *num_names)
+{
+	static unsigned int num_attrs = 0;
+	int rc = -1;
+	int len, rlen;
+	const char *attr_infix;
+	char *attr;
+
+	num_attrs++;
+
+	if (is_type) {
+		attr_infix = "_typeattr_";
+	} else {
+		attr_infix = "_roleattr_";
+	}
+
+	len = strlen(pdb->name) + strlen(attr_infix) + num_digits(num_attrs) + 1;
+	attr = malloc(len);
+	if (attr == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+	rlen = snprintf(attr, len, "%s%s%i", pdb->name, attr_infix, num_attrs);
+	if (rlen < 0 || rlen >= len) {
+		log_err("Failed to generate attribute name");
+		rc = -1;
+		goto exit;
+	}
+
+	*names = malloc(sizeof(**names));
+	if (*names == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+
+	*names[0] = attr;
+	*num_names = 1;
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+static int cil_print_attr_strs(int indent, struct policydb *pdb, int is_type, struct ebitmap *pos, struct ebitmap *neg, uint32_t flags, char *attr)
+{
+	// CIL doesn't support anonymous positive/negative/complemented sets.  So
+	// instead we create a CIL type/roleattributeset that matches the set. If
+	// the set has a negative set, then convert it to is (P & !N), where P is
+	// the list of members in the positive set , and N is the list of members
+	// in the negative set. Additonally, if the set is complemented, then wrap
+	// the whole thing with a negation.
+
+	int rc = 0;
+	struct ebitmap_node *node;
+	unsigned int i;
+	char *statement;
+	int has_positive = pos && (ebitmap_cardinality(pos) > 0);
+	int has_negative = neg && (ebitmap_cardinality(neg) > 0);
+	char **val_to_name;
+
+	if (is_type) {
+		statement = "type";
+		val_to_name = pdb->p_type_val_to_name;
+	} else {
+		statement = "role";
+		val_to_name = pdb->p_role_val_to_name;
+	}
+
+	cil_println(indent, "(%sattribute %s)", statement, attr);
+	cil_indent(indent);
+	cil_printf("(%sattributeset %s ", statement, attr);
+
+	if (flags & TYPE_STAR) {
+		cil_printf("(all)");
+	}
+
+	if (flags & TYPE_COMP) {
+		cil_printf("(not ");
+	}
+
+	if (has_positive && has_negative) {
+		cil_printf("(and ");
+	}
+
+	if (has_positive) {
+		cil_printf("(");
+		ebitmap_for_each_bit(pos, node, i) {
+			if (!ebitmap_get_bit(pos, i)) {
+				continue;
+			}
+			cil_printf("%s ", val_to_name[i]);
+		}
+		cil_printf(") ");
+	}
+
+	if (has_negative) {
+		cil_printf("(not (");
+
+		ebitmap_for_each_bit(neg, node, i) {
+			if (!ebitmap_get_bit(neg, i)) {
+				continue;
+			}
+			cil_printf("%s ", val_to_name[i]);
+		}
+
+		cil_printf("))");
+	}
+
+	if (has_positive && has_negative) {
+		cil_printf(")");
+	}
+
+	if (flags & TYPE_COMP) {
+		cil_printf(")");
+	}
+
+	cil_printf(")\n");
+
+	return rc;
+}
+
+static int ebitmap_to_cil(struct policydb *pdb, struct ebitmap *map, int type)
+{
+	struct ebitmap_node *node;
+	uint32_t i;
+	char **val_to_name = pdb->sym_val_to_name[type];
+
+	ebitmap_for_each_bit(map, node, i) {
+		if (!ebitmap_get_bit(map, i)) {
+			continue;
+		}
+		cil_printf("%s ", val_to_name[i]);
+	}
+
+	return 0;
+}
+
+static int ebitmap_to_names(char** vals_to_names, struct ebitmap map, char ***names, uint32_t *num_names)
+{
+	int rc = -1;
+	struct ebitmap_node *node;
+	uint32_t i;
+	uint32_t num = 0;
+	uint32_t max = 8;
+	char **name_arr = NULL;
+
+	name_arr = malloc(sizeof(*name_arr) * max);
+	if (name_arr == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+	ebitmap_for_each_bit(&map, node, i) {
+		if (!ebitmap_get_bit(&map, i)) {
+			continue;
+		}
+
+		if (num + 1 == max) {
+			max *= 2;
+			name_arr = realloc(name_arr, sizeof(*name_arr) * max);
+			if (name_arr == NULL) {
+				log_err("Out of memory");
+				rc = -1;
+				goto exit;
+			}
+		}
+
+		name_arr[num] = strdup(vals_to_names[i]);
+		if (name_arr[num] == NULL) {
+			log_err("Out of memory");
+			rc = -1;
+			goto exit;
+		}
+		num++;
+	}
+
+	*names = name_arr;
+	*num_names = num;
+
+	return 0;
+
+exit:
+	for (i = 0; i < num; i++) {
+		free(name_arr[i]);
+	}
+	free(name_arr);
+	return rc;
+}
+
+static int cil_add_attr_to_list(struct list *attr_list, char *attribute, int is_type, void *set)
+{
+	struct attr_list_node *attr_list_node = NULL;
+	int rc = -1;
+
+	attr_list_node = calloc(1, sizeof(*attr_list_node));
+	if (attr_list_node == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = list_prepend(attr_list, attr_list_node);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	attr_list_node->attribute = strdup(attribute);
+	if (attr_list_node->attribute == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+	attr_list_node->is_type = is_type;
+	if (is_type) {
+		attr_list_node->set.ts = set;
+	} else {
+		attr_list_node->set.rs = set;
+	}
+
+	return rc;
+
+exit:
+	if (attr_list_node != NULL) {
+		free(attr_list_node->attribute);
+	}
+	free(attr_list_node);
+	return rc;
+}
+
+/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
+static int typeset_to_names(struct policydb *pdb, struct type_set *ts, char ***names, uint32_t *num_names, char **generated_attribute)
+{
+	int rc = -1;
+	if (ebitmap_cardinality(&ts->negset) > 0 || ts->flags != 0) {
+		rc = set_to_cil_attr(pdb, 1, names, num_names);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		*generated_attribute = *names[0];
+	} else {
+		rc = ebitmap_to_names(pdb->p_type_val_to_name, ts->types, names, num_names);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
+static int roleset_to_names(struct policydb *pdb, struct role_set *rs, char ***names, uint32_t *num_names, char **generated_attribute)
+{
+	int rc = -1;
+	if (rs->flags != 0) {
+		rc = set_to_cil_attr(pdb, 0, names, num_names);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		*generated_attribute = *names[0];
+	} else {
+		rc = ebitmap_to_names(pdb->p_role_val_to_name, rs->roles, names, num_names);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int process_roleset(int indent, struct policydb *pdb, struct role_set *rs, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
+{
+	int rc = -1;
+	char *generated_attribute = NULL;
+	*num_type_names = 0;
+
+	rc = roleset_to_names(pdb, rs, type_names, num_type_names, &generated_attribute);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	if (generated_attribute == NULL) {
+		goto exit;
+	}
+
+	if (attr_list == NULL) {
+		rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
+		if (rc != 0) {
+			goto exit;
+		}
+	} else {
+		rc = cil_add_attr_to_list(attr_list, generated_attribute, 0, rs);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+exit:
+	return rc;
+}
+
+static int process_typeset(int indent, struct policydb *pdb, struct type_set *ts, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
+{
+	int rc = -1;
+	char *generated_attribute = NULL;
+	*num_type_names = 0;
+
+	rc = typeset_to_names(pdb, ts, type_names, num_type_names, &generated_attribute);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	if (generated_attribute == NULL) {
+		rc = 0;
+		goto exit;
+	}
+
+	if (attr_list == NULL) {
+		rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
+		if (rc != 0) {
+			goto exit;
+		}
+	} else {
+		rc = cil_add_attr_to_list(attr_list, generated_attribute, 1, ts);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+exit:
+	return rc;
+}
+
+static void names_destroy(char ***names, uint32_t *num_names)
+{
+	char **arr = *names;
+	uint32_t num = *num_names;
+	uint32_t i;
+
+	for (i = 0; i < num; i++) {
+		free(arr[i]);
+		arr[i] = NULL;
+	}
+	free(arr);
+
+	*names = NULL;
+	*num_names = 0;
+}
+
+static int roletype_role_in_ancestor_to_cil(struct policydb *pdb, struct stack *decl_stack, char *type_name, int indent)
+{
+	struct list_node *curr;
+	char **tnames = NULL;
+	uint32_t num_tnames, i;
+	struct role_list_node *role_node = NULL;
+	int rc;
+	struct type_set *ts;
+
+	curr = role_list->head;
+	for (curr = role_list->head; curr != NULL; curr = curr->next) {
+		role_node = curr->data;
+		if (!is_id_in_ancestor_scope(pdb, decl_stack, role_node->role_name, SYM_ROLES)) {
+			continue;
+		}
+
+		ts = &role_node->role->types;
+		rc = process_typeset(indent, pdb, ts, NULL, &tnames, &num_tnames);
+		if (rc != 0) {
+			goto exit;
+		}
+		for (i = 0; i < num_tnames; i++) {
+			if (!strcmp(type_name, tnames[i])) {
+				cil_println(indent, "(roletype %s %s)", role_node->role_name, type_name);
+			}
+		}
+		names_destroy(&tnames, &num_tnames);
+	}
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+
+static int name_list_to_string(char **names, int num_names, char **string)
+{
+	// create a space separated string of the names
+	int rc = -1;
+	int len = 0;
+	int i;
+	char *str;
+	char *strpos;
+	int name_len;
+	int rlen;
+
+	for (i = 0; i < num_names; i++) {
+		len += strlen(names[i]);
+	}
+
+	// add spaces + null terminator
+	len += (num_names - 1) + 1;
+
+	str = malloc(len);
+	if (str == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+	strpos = str;
+
+	for (i = 0; i < num_names; i++) {
+		name_len = strlen(names[i]);
+		rlen = snprintf(strpos, len - (strpos - str), "%s", names[i]);
+		if (rlen < 0 || rlen >= len) {
+			log_err("Failed to generate name list");
+			rc = -1;
+			goto exit;
+		}
+
+		if (i < num_names - 1) {
+			strpos[name_len] = ' ';
+		}
+		strpos += name_len + 1;
+	}
+
+	*string = str;
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int avrule_list_to_cil(int indent, struct policydb *pdb, struct avrule *avrule_list, struct list *attr_list)
+{
+	int rc = -1;
+	struct avrule *avrule;
+	char **snames = NULL;
+	char **tnames = NULL;
+	uint32_t num_snames;
+	uint32_t num_tnames;
+	uint32_t s;
+	uint32_t t;
+	struct type_set *ts;
+
+	for (avrule = avrule_list; avrule != NULL; avrule = avrule->next) {
+		ts = &avrule->stypes;
+		rc = process_typeset(indent, pdb, ts, attr_list, &snames, &num_snames);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		ts = &avrule->ttypes;
+		rc = process_typeset(indent, pdb, ts, attr_list, &tnames, &num_tnames);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (s = 0; s < num_snames; s++) {
+			for (t = 0; t < num_tnames; t++) {
+				rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->perms);
+				if (rc != 0) {
+					goto exit;
+				}
+			}
+
+			if (avrule->flags & RULE_SELF) {
+				rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->perms);
+				if (rc != 0) {
+					goto exit;
+				}
+			}
+		}
+
+		names_destroy(&snames, &num_snames);
+		names_destroy(&tnames, &num_tnames);
+	}
+
+	return 0;
+
+exit:
+	names_destroy(&snames, &num_snames);
+	names_destroy(&tnames, &num_tnames);
+
+	return rc;
+}
+
+static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr *cond_expr, uint32_t flags)
+{
+	int rc = -1;
+	struct cond_expr *curr;
+	struct stack *stack = NULL;
+	int len = 0;
+	int rlen;
+	char *new_val = NULL;
+	char *val1 = NULL;
+	char *val2 = NULL;
+	int num_params;
+	const char *op;
+	const char *fmt_str;
+	const char *type;
+
+	rc = stack_init(&stack);
+	if (rc != 0) {
+		log_err("Out of memory");
+		goto exit;
+	}
+
+	for (curr = cond_expr; curr != NULL; curr = curr->next) {
+		if (curr->expr_type == COND_BOOL) {
+			val1 = pdb->p_bool_val_to_name[curr->bool - 1];
+			// length of boolean + 2 parens + null terminator
+			len = strlen(val1) + 2 + 1;
+			new_val = malloc(len);
+			if (new_val == NULL) {
+				log_err("Out of memory");
+				rc = -1;
+				goto exit;
+			}
+			rlen = snprintf(new_val, len, "(%s)", val1);
+			if (rlen < 0 || rlen >= len) {
+				log_err("Failed to generate conditional expression");
+				rc = -1;
+				goto exit;
+			}
+			num_params = 0;
+		} else {
+			switch(curr->expr_type) {
+			case COND_NOT:	op = "not";	break;
+			case COND_OR:	op = "or";	break;
+			case COND_AND:	op = "and";	break;
+			case COND_XOR:	op = "xor";	break;
+			case COND_EQ:	op = "eq";	break;
+			case COND_NEQ:	op = "neq";	break;
+			default:
+				rc = -1;
+				goto exit;
+			}
+
+			num_params = curr->expr_type == COND_NOT ? 1 : 2;
+
+			if (num_params == 1) {
+				val1 = stack_pop(stack);
+				val2 = strdup("");
+				if (val2 == NULL) {
+					log_err("Out of memory");
+					rc = -1;
+					goto exit;
+				}
+				fmt_str = "(%s %s)";
+			} else {
+				val2 = stack_pop(stack);
+				val1 = stack_pop(stack);
+				fmt_str = "(%s %s %s)";
+			}
+
+			if (val1 == NULL || val2 == NULL) {
+				log_err("Invalid conditional expression");
+				rc = -1;
+				goto exit;
+			}
+
+			// length = length of parameters +
+			//          length of operator +
+			//          1 space preceeding each parameter +
+			//          2 parens around the whole expression
+			//          + null terminator
+			len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
+			new_val = malloc(len);
+			if (new_val == NULL) {
+				log_err("Out of memory");
+				rc = -1;
+				goto exit;
+			}
+
+			// although we always supply val2 and there isn't always a 2nd
+			// value, it should only be used when there are actually two values
+			// in the format strings
+			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
+			if (rlen < 0 || rlen >= len) {
+				log_err("Failed to generate conditional expression");
+				rc = -1;
+				goto exit;
+			}
+
+			free(val1);
+			free(val2);
+			val1 = NULL;
+			val2 = NULL;
+		}
+
+		rc = stack_push(stack, new_val);
+		if (rc != 0) {
+			log_err("Out of memory");
+			goto exit;
+		}
+		new_val = NULL;
+	}
+
+	if (flags & COND_NODE_FLAGS_TUNABLE) {
+		type = "tunableif";
+	} else {
+		type = "booleanif";
+	}
+
+	val1 = stack_pop(stack);
+	if (val1 == NULL || stack_peek(stack) != NULL) {
+		log_err("Invalid conditional expression");
+		rc = -1;
+		goto exit;
+	}
+
+	cil_println(indent, "(%s %s", type, val1);
+	free(val1);
+	val1 = NULL;
+
+	rc = 0;
+
+exit:
+	free(new_val);
+	free(val1);
+	free(val2);
+	while ((val1 = stack_pop(stack)) != NULL) {
+		free(val1);
+	}
+	stack_destroy(&stack);
+
+	return rc;
+}
+
+static int cil_print_attr_list(int indent, struct policydb *pdb, struct list *attr_list)
+{
+	struct list_node *curr;
+	struct attr_list_node *attr_list_node;
+	int rc = 0;
+	struct type_set *ts;
+	struct role_set *rs;
+	char *generated_attribute;
+
+	for (curr = attr_list->head; curr != NULL; curr = curr->next) {
+		attr_list_node = curr->data;
+		generated_attribute = attr_list_node->attribute;
+		if (generated_attribute == NULL) {
+			return -1;
+		}
+
+		if (attr_list_node->is_type) {
+			ts = attr_list_node->set.ts;
+			rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
+			if (rc != 0) {
+				return rc;
+			}
+		} else {
+			rs = attr_list_node->set.rs;
+			rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
+			if (rc != 0) {
+				return rc;
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int cond_list_to_cil(int indent, struct policydb *pdb, struct cond_node *cond_list)
+{
+	int rc = -1;
+	struct cond_node *cond;
+	struct list *attr_list;
+
+	rc = list_init(&attr_list);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	for (cond = cond_list; cond != NULL; cond = cond->next) {
+
+		rc = cond_expr_to_cil(indent, pdb, cond->expr, cond->flags);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		if (cond->avtrue_list != NULL) {
+			cil_println(indent + 1, "(true");
+			rc = avrule_list_to_cil(indent + 2, pdb, cond->avtrue_list, attr_list);
+			if (rc != 0) {
+				goto exit;
+			}
+			cil_println(indent + 1, ")");
+		}
+
+		if (cond->avfalse_list != NULL) {
+			cil_println(indent + 1, "(false");
+			rc = avrule_list_to_cil(indent + 2, pdb, cond->avfalse_list, attr_list);
+			if (rc != 0) {
+				goto exit;
+			}
+			cil_println(indent + 1, ")");
+		}
+
+		cil_println(indent, ")");
+	}
+
+	rc = cil_print_attr_list(indent, pdb, attr_list);
+
+exit:
+	attr_list_destroy(&attr_list);
+	return rc;
+}
+
+static int role_trans_to_cil(int indent, struct policydb *pdb, struct role_trans_rule *rules)
+{
+	int rc = -1;
+	struct role_trans_rule *rule;
+	char **role_names = NULL;
+	uint32_t num_role_names = 0;
+	char **type_names = NULL;
+	uint32_t num_type_names = 0;
+	uint32_t type;
+	uint32_t role;
+	uint32_t i;
+	struct ebitmap_node *node;
+	struct type_set *ts;
+	struct role_set *rs;
+
+
+	for (rule = rules; rule != NULL; rule = rule->next) {
+		rs = &rule->roles;
+		rc = process_roleset(indent, pdb, rs, NULL, &role_names, &num_role_names);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		ts = &rule->types;
+		rc = process_typeset(indent, pdb, ts, NULL, &type_names, &num_type_names);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (role = 0; role < num_role_names; role++) {
+			for (type = 0; type < num_type_names; type++) {
+				ebitmap_for_each_bit(&rule->classes, node, i) {
+					if (!ebitmap_get_bit(&rule->classes, i)) {
+						continue;
+					}
+					cil_println(indent, "(roletransition %s %s %s %s)", role_names[role],
+					                                                    type_names[type],
+					                                                    pdb->p_class_val_to_name[i],
+					                                                    pdb->p_role_val_to_name[rule->new_role - 1]);
+				}
+			}
+		}
+
+		names_destroy(&role_names, &num_role_names);
+		names_destroy(&type_names, &num_type_names);
+	}
+
+	rc = 0;
+
+exit:
+	names_destroy(&role_names, &num_role_names);
+	names_destroy(&type_names, &num_type_names);
+
+	return rc;
+}
+
+static int role_allows_to_cil(int indent, struct policydb *pdb, struct role_allow_rule *rules)
+{
+	int rc = -1;
+	struct role_allow_rule *rule;
+	char **roles = NULL;
+	uint32_t num_roles = 0;
+	char **new_roles = NULL;
+	uint32_t num_new_roles = 0;
+	uint32_t i;
+	uint32_t j;
+	struct role_set *rs;
+
+	for (rule = rules; rule != NULL; rule = rule->next) {
+		rs = &rule->roles;
+		rc = process_roleset(indent, pdb, rs, NULL, &roles, &num_roles);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rs = &rule->new_roles;
+		rc = process_roleset(indent, pdb, rs, NULL, &new_roles, &num_new_roles);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (i = 0; i < num_roles; i++) {
+			for (j = 0; j < num_new_roles; j++) {
+				cil_println(indent, "(roleallow %s %s)", roles[i], new_roles[j]);
+			}
+		}
+
+		names_destroy(&roles, &num_roles);
+		names_destroy(&new_roles, &num_new_roles);
+	}
+
+	rc = 0;
+
+exit:
+	names_destroy(&roles, &num_roles);
+	names_destroy(&new_roles, &num_new_roles);
+
+	return rc;
+}
+
+static int range_trans_to_cil(int indent, struct policydb *pdb, struct range_trans_rule *rules)
+{
+	int rc = -1;
+	struct range_trans_rule *rule;
+	char **stypes = NULL;
+	uint32_t num_stypes = 0;
+	char **ttypes = NULL;
+	uint32_t num_ttypes = 0;
+	struct ebitmap_node *node;
+	uint32_t i;
+	uint32_t stype;
+	uint32_t ttype;
+	struct type_set *ts;
+
+	if (!pdb->mls) {
+		return 0;
+	}
+
+	for (rule = rules; rule != NULL; rule = rule->next) {
+		ts = &rule->stypes;
+		rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		ts = &rule->ttypes;
+		rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (stype = 0; stype < num_stypes; stype++) {
+			for (ttype = 0; ttype < num_ttypes; ttype++) {
+				ebitmap_for_each_bit(&rule->tclasses, node, i) {
+					if (!ebitmap_get_bit(&rule->tclasses, i)) {
+						continue;
+					}
+
+					cil_indent(indent);
+					cil_printf("(rangetransition %s %s %s ", stypes[stype], ttypes[ttype], pdb->p_class_val_to_name[i]);
+
+					cil_printf("(");
+
+					rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[0]);
+					if (rc != 0) {
+						goto exit;
+					}
+
+					cil_printf(" ");
+
+					rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[1]);
+					if (rc != 0) {
+						goto exit;
+					}
+
+					cil_printf("))\n");
+				}
+
+			}
+		}
+
+		names_destroy(&stypes, &num_stypes);
+		names_destroy(&ttypes, &num_ttypes);
+	}
+
+	rc = 0;
+
+exit:
+	names_destroy(&stypes, &num_stypes);
+	names_destroy(&ttypes, &num_ttypes);
+
+	return rc;
+}
+
+static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filename_trans_rule *rules)
+{
+	int rc = -1;
+	char **stypes = NULL;
+	uint32_t num_stypes = 0;
+	char **ttypes = NULL;
+	uint32_t num_ttypes = 0;
+	uint32_t stype;
+	uint32_t ttype;
+	struct type_set *ts;
+
+	struct filename_trans_rule *rule;
+
+	for (rule = rules; rule != NULL; rule = rule->next) {
+		ts = &rule->stypes;
+		rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		ts = &rule->ttypes;
+		rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (stype = 0; stype < num_stypes; stype++) {
+			for (ttype = 0; ttype < num_ttypes; ttype++) {
+				cil_println(indent, "(typetransition %s %s %s \"%s\" %s)", stypes[stype],
+				                                                       ttypes[ttype],
+				                                                       pdb->p_class_val_to_name[rule->tclass - 1],
+				                                                       rule->name,
+				                                                       pdb->p_type_val_to_name[rule->otype - 1]);
+			}
+		}
+
+		names_destroy(&stypes, &num_stypes);
+		names_destroy(&ttypes, &num_ttypes);
+	}
+
+	rc = 0;
+exit:
+	names_destroy(&stypes, &num_stypes);
+	names_destroy(&ttypes, &num_ttypes);
+
+	return rc;
+}
+
+struct class_perm_datum {
+	char *name;
+	uint32_t val;
+};
+
+struct class_perm_array {
+	struct class_perm_datum *perms;
+	uint32_t count;
+};
+
+static int class_perm_to_array(char *key, void *data, void *args)
+{
+	struct class_perm_array *arr = args;
+	struct perm_datum *datum = data;
+	arr->perms[arr->count].name = key;
+	arr->perms[arr->count].val = datum->s.value;
+	arr->count++;
+
+	return 0;
+}
+
+static int class_perm_cmp(const void *a, const void *b)
+{
+	const struct class_perm_datum *aa = a;
+	const struct class_perm_datum *bb = b;
+
+	return aa->val - bb->val;
+}
+
+static int common_to_cil(char *key, void *data, void *UNUSED(arg))
+{
+	int rc = -1;
+	struct common_datum *common = data;
+	struct class_perm_array arr;
+	uint32_t i;
+
+	arr.count = 0;
+	arr.perms = calloc(common->permissions.nprim, sizeof(*arr.perms));
+	rc = hashtab_map(common->permissions.table, class_perm_to_array, &arr);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
+
+	cil_printf("(common %s (", key);
+	for (i = 0; i < arr.count; i++) {
+		cil_printf("%s ", arr.perms[i].name);
+	}
+	cil_printf("))\n");
+
+	rc = 0;
+
+exit:
+	free(arr.perms);
+	return rc;
+}
+
+
+static int constraint_expr_to_string(int indent, struct policydb *pdb, struct constraint_expr *exprs, char **expr_string)
+{
+	int rc = -1;
+	struct constraint_expr *expr;
+	struct stack *stack = NULL;
+	int len = 0;
+	int rlen;
+	char *new_val = NULL;
+	char *val1 = NULL;
+	char *val2 = NULL;
+	uint32_t num_params;
+	const char *op;
+	const char *fmt_str;
+	const char *attr1;
+	const char *attr2;
+	char *names;
+	char **name_list = NULL;
+	uint32_t num_names = 0;
+	struct type_set *ts;
+
+	rc = stack_init(&stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	for (expr = exprs; expr != NULL; expr = expr->next) {
+		if (expr->expr_type == CEXPR_ATTR || expr->expr_type == CEXPR_NAMES) {
+			switch (expr->op) {
+			case CEXPR_EQ:      op = "eq";     break;
+			case CEXPR_NEQ:     op = "neq";    break;
+			case CEXPR_DOM:     op = "dom";    break;
+			case CEXPR_DOMBY:   op = "domby";  break;
+			case CEXPR_INCOMP:  op = "incomp"; break;
+			default:
+				log_err("Unknown constraint operator type: %i", expr->op);
+				rc = -1;
+				goto exit;
+			}
+
+			switch (expr->attr) {
+			case CEXPR_USER:                 attr1 = "u1"; attr2 = "u2"; break;
+			case CEXPR_USER | CEXPR_TARGET:  attr1 = "u2"; attr2 = "";   break;
+			case CEXPR_USER | CEXPR_XTARGET: attr1 = "u3"; attr2 = "";   break;
+			case CEXPR_ROLE:                 attr1 = "r1"; attr2 = "r2"; break;
+			case CEXPR_ROLE | CEXPR_TARGET:  attr1 = "r2"; attr2 = "";   break;
+			case CEXPR_ROLE | CEXPR_XTARGET: attr1 = "r3"; attr2 = "";   break;
+			case CEXPR_TYPE:                 attr1 = "t1"; attr2 = "";   break;
+			case CEXPR_TYPE | CEXPR_TARGET:  attr1 = "t2"; attr2 = "";   break;
+			case CEXPR_TYPE | CEXPR_XTARGET: attr1 = "t3"; attr2 = "";   break;
+			case CEXPR_L1L2:                 attr1 = "l1"; attr2 = "l2"; break;
+			case CEXPR_L1H2:                 attr1 = "l1"; attr2 = "h2"; break;
+			case CEXPR_H1L2:                 attr1 = "h1"; attr2 = "l2"; break;
+			case CEXPR_H1H2:                 attr1 = "h1"; attr2 = "h2"; break;
+			case CEXPR_L1H1:                 attr1 = "l1"; attr2 = "h1"; break;
+			case CEXPR_L2H2:                 attr1 = "l2"; attr2 = "h2"; break;
+			default:
+				log_err("Unknown expression attribute type: %i", expr->attr);
+				rc = -1;
+				goto exit;
+			}
+
+			if (expr->expr_type == CEXPR_ATTR) {
+				// length of values/attrs + 2 separating spaces + 2 parens + null terminator
+				len = strlen(op) + strlen(attr1) + strlen(attr2) + 2 + 2 + 1;
+				new_val = malloc(len);
+				if (new_val == NULL) {
+					log_err("Out of memory");
+					rc = -1;
+					goto exit;
+				}
+				rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, attr2);
+				if (rlen < 0 || rlen >= len) {
+					log_err("Failed to generate constraint expression");
+					rc = -1;
+					goto exit;
+				}
+			} else {
+				if (expr->attr & CEXPR_TYPE) {
+					ts = expr->type_names;
+					rc = process_typeset(indent, pdb, ts, NULL, &name_list, &num_names);
+					if (rc != 0) {
+						goto exit;
+					}
+				} else if (expr->attr & CEXPR_USER) {
+					rc = ebitmap_to_names(pdb->p_user_val_to_name, expr->names, &name_list, &num_names);
+					if (rc != 0) {
+						goto exit;
+					}
+				} else if (expr->attr & CEXPR_ROLE) {
+					rc = ebitmap_to_names(pdb->p_role_val_to_name, expr->names, &name_list, &num_names);
+					if (rc != 0) {
+						goto exit;
+					}
+				}
+				rc = name_list_to_string(name_list, num_names, &names);
+				if (rc != 0) {
+					goto exit;
+				}
+
+				// length of values/oper + 2 spaces + 2 parens + null terminator
+				len = strlen(op) + strlen(attr1) +  strlen(names) + 2 + 2 + 1;
+				new_val = malloc(len);
+				if (new_val == NULL) {
+					log_err("Out of memory");
+					rc = -1;
+					goto exit;
+				}
+				rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, names);
+				if (rlen < 0 || rlen >= len) {
+					log_err("Failed to generate constraint expression");
+					rc = -1;
+					goto exit;
+				}
+
+				names_destroy(&name_list, &num_names);
+				free(names);
+			}
+
+			num_params = 0;
+		} else {
+			switch (expr->expr_type) {
+			case CEXPR_NOT: op = "not"; break;
+			case CEXPR_AND: op = "and"; break;
+			case CEXPR_OR:  op = "or"; break;
+			default:
+				log_err("Unknown constraint expression type: %i", expr->expr_type);
+				rc = -1;
+				goto exit;
+			}
+
+			num_params = expr->expr_type == CEXPR_NOT ? 1 : 2;
+
+			if (num_params == 1) {
+				val1 = stack_pop(stack);
+				val2 = strdup("");
+				if (val2 == NULL) {
+					log_err("Out of memory");
+					rc = -1;
+					goto exit;
+				}
+				fmt_str = "(%s %s)";
+			} else {
+				val2 = stack_pop(stack);
+				val1 = stack_pop(stack);
+				fmt_str = "(%s %s %s)";
+			}
+
+			if (val1 == NULL || val2 == NULL) {
+				log_err("Invalid constraint expression");
+				rc = -1;
+				goto exit;
+			}
+
+			// length = length of parameters +
+			//          length of operator +
+			//          1 space preceeding each parameter +
+			//          2 parens around the whole expression
+			//          + null terminator
+			len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
+			new_val = malloc(len);
+			if (new_val == NULL) {
+				log_err("Out of memory");
+				rc = -1;
+				goto exit;
+			}
+
+			// although we always supply val2 and there isn't always a 2nd
+			// value, it should only be used when there are actually two values
+			// in the format strings
+			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
+			if (rlen < 0 || rlen >= len) {
+				log_err("Failed to generate constraint expression");
+				rc = -1;
+				goto exit;
+			}
+
+			free(val1);
+			free(val2);
+			val1 = NULL;
+			val2 = NULL;
+		}
+
+		rc = stack_push(stack, new_val);
+		if (rc != 0) {
+			log_err("Out of memory");
+			goto exit;
+		}
+
+		new_val = NULL;
+	}
+
+	new_val = stack_pop(stack);
+	if (new_val == NULL || stack_peek(stack) != NULL) {
+		log_err("Invalid constraint expression");
+		rc = -1;
+		goto exit;
+	}
+
+	*expr_string = new_val;
+	new_val = NULL;
+
+	rc = 0;
+
+exit:
+	names_destroy(&name_list, &num_names);
+
+	free(new_val);
+	free(val1);
+	free(val2);
+	while ((val1 = stack_pop(stack)) != NULL) {
+		free(val1);
+	}
+	stack_destroy(&stack);
+
+	return rc;
+}
+
+
+static int constraints_to_cil(int indent, struct policydb *pdb, char *classkey, struct class_datum *class, struct constraint_node *constraints, int is_constraint)
+{
+	int rc = -1;
+	struct constraint_node *node;
+	char *expr = NULL;
+	const char *mls;
+	char *perms;
+
+	mls = pdb->mls ? "mls" : "";
+
+	for (node = constraints; node != NULL; node = node->next) {
+
+		rc = constraint_expr_to_string(indent, pdb, node->expr, &expr);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		if (is_constraint) {
+			perms = sepol_av_to_string(pdb, class->s.value, node->permissions);
+			cil_println(indent, "(%sconstrain (%s (%s)) %s)", mls, classkey, perms + 1, expr);
+		} else {
+			cil_println(indent, "(%svalidatetrans %s %s)", mls, classkey, expr);
+		}
+
+		free(expr);
+		expr = NULL;
+	}
+
+	rc = 0;
+
+exit:
+	free(expr);
+	return rc;
+}
+
+static int class_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
+{
+	int rc = -1;
+	struct class_datum *class = datum;
+	const char *dflt;
+	struct class_perm_array arr;
+	uint32_t i;
+
+	if (scope == SCOPE_REQ) {
+		return 0;
+	}
+
+	arr.count = 0;
+	arr.perms = calloc(class->permissions.nprim, sizeof(*arr.perms));
+	rc = hashtab_map(class->permissions.table, class_perm_to_array, &arr);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
+
+	cil_indent(indent);
+	cil_printf("(class %s (", key);
+	for (i = 0; i < arr.count; i++) {
+		cil_printf("%s ", arr.perms[i].name);
+	}
+	cil_printf("))\n");
+
+	if (class->comkey != NULL) {
+		cil_println(indent, "(classcommon %s %s)", key, class->comkey);
+	}
+
+	if (class->default_user != 0) {
+		switch (class->default_user) {
+		case DEFAULT_SOURCE:	dflt = "source";	break;
+		case DEFAULT_TARGET:	dflt = "target";	break;
+		default:
+			log_err("Unknown default user value: %i", class->default_user);
+			rc = -1;
+			goto exit;
+		}
+		cil_println(indent, "(defaultuser %s %s)", key, dflt);
+	}
+
+	if (class->default_role != 0) {
+		switch (class->default_role) {
+		case DEFAULT_SOURCE:	dflt = "source";	break;
+		case DEFAULT_TARGET:	dflt = "target";	break;
+		default:
+			log_err("Unknown default role value: %i", class->default_role);
+			rc = -1;
+			goto exit;
+		}
+		cil_println(indent, "(defaultrole %s %s)", key, dflt);
+	}
+
+	if (class->default_type != 0) {
+		switch (class->default_type) {
+		case DEFAULT_SOURCE:	dflt = "source";	break;
+		case DEFAULT_TARGET:	dflt = "target";	break;
+		default:
+			log_err("Unknown default type value: %i", class->default_type);
+			rc = -1;
+			goto exit;
+		}
+		cil_println(indent, "(defaulttype %s %s)", key, dflt);
+	}
+
+	if (class->default_range != 0) {
+		switch (class->default_range) {
+		case DEFAULT_SOURCE_LOW:		dflt = "source low";	break;
+		case DEFAULT_SOURCE_HIGH:		dflt = "source high";	break;
+		case DEFAULT_SOURCE_LOW_HIGH:	dflt = "source low-high";	break;
+		case DEFAULT_TARGET_LOW:		dflt = "target low";	break;
+		case DEFAULT_TARGET_HIGH:		dflt = "target high";	break;
+		case DEFAULT_TARGET_LOW_HIGH:	dflt = "target low-high";	break;
+		default:
+			log_err("Unknown default range value: %i", class->default_range);
+			rc = -1;
+			goto exit;
+		}
+		cil_println(indent, "(defaultrange %s %s)", key, dflt);
+
+	}
+
+	if (class->constraints != NULL) {
+		rc = constraints_to_cil(indent, pdb, key, class, class->constraints, 1);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	if (class->validatetrans != NULL) {
+		rc = constraints_to_cil(indent, pdb, key, class, class->validatetrans, 0);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	rc = 0;
+
+exit:
+	free(arr.perms);
+	return rc;
+}
+
+static int class_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
+{
+	struct ebitmap_node *node;
+	uint32_t i;
+
+	if (ebitmap_cardinality(&order) == 0) {
+		return 0;
+	}
+
+	cil_indent(indent);
+	cil_printf("(classorder (");
+
+	ebitmap_for_each_bit(&order, node, i) {
+		if (!ebitmap_get_bit(&order, i)) {
+			continue;
+		}
+		cil_printf("%s ", pdb->sym_val_to_name[SYM_CLASSES][i]);
+	}
+
+	cil_printf("))\n");
+
+	return 0;
+}
+
+static int role_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
+{
+	int rc = -1;
+	struct ebitmap_node *node;
+	uint32_t i;
+	char **types = NULL;
+	uint32_t num_types = 0;
+	struct role_datum *role = datum;
+	struct type_set *ts;
+
+	if (scope == SCOPE_REQ) {
+		// if a role/roleattr is in the REQ scope, then it could cause an
+		// optional block to fail, even if it is never used. However in CIL,
+		// symbols must be used in order to cause an optional block to fail. So
+		// for symbols in the REQ scope, add them to a roleattribute as a way
+		// to 'use' them in the optional without affecting the resulting policy.
+		cil_println(indent, "(roleattributeset " GEN_REQUIRE_ATTR " %s)", key);
+	}
+
+	switch (role->flavor) {
+	case ROLE_ROLE:
+		if (scope == SCOPE_DECL) {
+			// Only declare certain roles if we are reading a base module.
+			// These roles are defined in the base module and sometimes in
+			// other non-base modules. If we generated the roles regardless of
+			// the policy type, it would result in duplicate declarations,
+			// which isn't allowed in CIL. Patches have been made to refpolicy
+			// to remove these duplicate role declarations, but we need to be
+			// backwards compatable and support older policies. Since we know
+			// these roles are always declared in base, only print them when we
+			// see them in the base module. If the declarations appear in a
+			// non-base module, ignore their declarations.
+			//
+			// Note that this is a hack, and if a policy author does not define
+			// one of these roles in base, the declaration will not appeaer in
+			// the resulting policy, likely resulting in a compilation error in
+			// CIL.
+			int is_base_role = (!strcmp(key, "user_r") ||
+			                    !strcmp(key, "staff_r") ||
+			                    !strcmp(key, "sysadm_r") ||
+			                    !strcmp(key, "system_r") ||
+			                    !strcmp(key, "unconfined_r"));
+			if ((is_base_role && pdb->policy_type == SEPOL_POLICY_BASE) || !is_base_role) {
+				cil_println(indent, "(role %s)", key);
+			}
+		}
+
+		if (ebitmap_cardinality(&role->dominates) > 1) {
+			log_err("Warning: role 'dominance' statement unsupported in CIL. Dropping from output.");
+		}
+
+		ts = &role->types;
+		rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		for (i = 0; i < num_types; i++) {
+			if (is_id_in_scope(pdb, decl_stack, types[i], SYM_TYPES)) {
+				cil_println(indent, "(roletype %s %s)", key, types[i]);
+			}
+		}
+
+		if (role->bounds > 0) {
+			cil_println(indent, "(rolebounds %s %s)", key, pdb->p_role_val_to_name[role->bounds - 1]);
+		}
+		break;
+
+	case ROLE_ATTRIB:
+		if (scope == SCOPE_DECL) {
+			cil_println(indent, "(roleattribute %s)", key);
+		}
+
+		if (ebitmap_cardinality(&role->roles) > 0) {
+			cil_indent(indent);
+			cil_printf("(roleattributeset %s (", key);
+			ebitmap_for_each_bit(&role->roles, node, i) {
+				if (!ebitmap_get_bit(&role->roles, i)) {
+					continue;
+				}
+				cil_printf("%s ", pdb->p_role_val_to_name[i]);
+			}
+			cil_printf("))\n");
+		}
+
+		ts = &role->types;
+		rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
+		if (rc != 0) {
+			goto exit;
+		}
+
+
+		for (i = 0; i < num_types; i++) {
+			cil_println(indent, "(roletype %s %s)", key, types[i]);
+		}
+
+		break;
+
+	default:
+		log_err("Unknown role type: %i", role->flavor);
+		rc = -1;
+		goto exit;
+	}
+
+	rc = 0;
+exit:
+	names_destroy(&types, &num_types);
+
+	return rc;
+}
+
+static int type_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
+{
+	int rc = -1;
+	struct type_datum *type = datum;
+
+	if (scope == SCOPE_REQ) {
+		// if a type/typeattr is in the REQ scope, then it could cause an
+		// optional block to fail, even if it is never used. However in CIL,
+		// symbols must be used in order to cause an optional block to fail. So
+		// for symbols in the REQ scope, add them to a typeattribute as a way
+		// to 'use' them in the optional without affecting the resulting policy.
+		cil_println(indent, "(typeattributeset " GEN_REQUIRE_ATTR " %s)", key);
+	}
+
+	rc = roletype_role_in_ancestor_to_cil(pdb, decl_stack, key, indent);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	switch(type->flavor) {
+	case TYPE_TYPE:
+		if (scope == SCOPE_DECL) {
+			cil_println(indent, "(type %s)", key);
+			// object_r is implicit in checkmodule, but not with CIL,
+			// create it as part of base
+			cil_println(indent, "(roletype " DEFAULT_OBJECT " %s)", key);
+		}
+
+		if (type->flags & TYPE_FLAGS_PERMISSIVE) {
+			cil_println(indent, "(typepermissive %s)", key);
+		}
+
+		if (type->bounds > 0) {
+			cil_println(indent, "(typebounds %s %s)", pdb->p_type_val_to_name[type->bounds - 1], key);
+		}
+		break;
+	case TYPE_ATTRIB:
+		if (scope == SCOPE_DECL) {
+			cil_println(indent, "(typeattribute %s)", key);
+		}
+
+		if (ebitmap_cardinality(&type->types) > 0) {
+			cil_indent(indent);
+			cil_printf("(typeattributeset %s (", key);
+			ebitmap_to_cil(pdb, &type->types, SYM_TYPES);
+			cil_printf("))\n");
+		}
+		break;
+	default:
+		log_err("Unknown flavor (%i) of type %s", type->flavor, key);
+		rc = -1;
+		goto exit;
+	}
+
+	rc = 0;
+
+exit:
+	return rc;
+}
+
+static int user_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
+{
+	struct user_datum *user = datum;
+	struct ebitmap roles = user->roles.roles;
+	struct mls_semantic_level level = user->dfltlevel;
+	struct mls_semantic_range range = user->range;
+	struct ebitmap_node *node;
+	uint32_t i;
+	int sens_offset = 1;
+
+	if (scope == SCOPE_DECL) {
+		cil_println(indent, "(user %s)", key);
+		// object_r is implicit in checkmodule, but not with CIL, create it
+		// as part of base
+		cil_println(indent, "(userrole %s " DEFAULT_OBJECT ")", key);
+	}
+
+	ebitmap_for_each_bit(&roles, node, i) {
+		if (!ebitmap_get_bit(&roles, i)) {
+			continue;
+		}
+		cil_println(indent, "(userrole %s %s)", key, pdb->p_role_val_to_name[i]);
+	}
+
+	if (block->flags & AVRULE_OPTIONAL) {
+		// sensitivites in user statements in optionals do not have the
+		// standard -1 offest
+		sens_offset = 0;
+	}
+
+	cil_indent(indent);
+	cil_printf("(userlevel %s ", key);
+	if (pdb->mls) {
+		semantic_level_to_cil(pdb, sens_offset, &level);
+	} else {
+		cil_printf(DEFAULT_LEVEL);
+	}
+	cil_printf(")\n");
+
+	cil_indent(indent);
+	cil_printf("(userrange %s (", key);
+	if (pdb->mls) {
+		semantic_level_to_cil(pdb, sens_offset, &range.level[0]);
+		cil_printf(" ");
+		semantic_level_to_cil(pdb, sens_offset, &range.level[1]);
+	} else {
+		cil_printf(DEFAULT_LEVEL " " DEFAULT_LEVEL);
+	}
+	cil_printf("))\n");
+
+
+	return 0;
+}
+
+static int boolean_to_cil(int indent, struct policydb *UNUSED(pdb), struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
+{
+	struct cond_bool_datum *boolean = datum;
+	const char *type;
+
+	if (scope == SCOPE_DECL) {
+		if (boolean->flags & COND_BOOL_FLAGS_TUNABLE) {
+			type = "tunable";
+		} else {
+			type = "boolean";
+		}
+
+		cil_println(indent, "(%s %s %s)", type, key, boolean->state ? "true" : "false");
+	}
+
+	return 0;
+}
+
+static int sens_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
+{
+	struct level_datum *level = datum;
+
+	if (scope == SCOPE_DECL) {
+		if (!level->isalias) {
+			cil_println(indent, "(sensitivity %s)", key);
+		} else {
+			cil_println(indent, "(sensitivityalias %s)", key);
+			cil_println(indent, "(sensitivityaliasactual %s %s)", key, pdb->p_sens_val_to_name[level->level->sens - 1]);
+		}
+	}
+
+	if (ebitmap_cardinality(&level->level->cat) > 0) {
+		cil_indent(indent);
+		cil_printf("(sensitivitycategory %s (", key);
+		ebitmap_to_cil(pdb, &level->level->cat, SYM_CATS);
+		cil_printf("))\n");
+	}
+
+	return 0;
+}
+
+static int sens_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
+{
+	struct ebitmap_node *node;
+	uint32_t i;
+
+	if (ebitmap_cardinality(&order) == 0) {
+		return 0;
+	}
+
+	cil_indent(indent);
+	cil_printf("(sensitivityorder (");
+
+	ebitmap_for_each_bit(&order, node, i) {
+		if (!ebitmap_get_bit(&order, i)) {
+			continue;
+		}
+		cil_printf("%s ", pdb->p_sens_val_to_name[i]);
+	}
+
+	cil_printf("))\n");
+
+	return 0;
+}
+
+static int cat_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
+{
+	struct cat_datum *cat = datum;
+
+	if (scope == SCOPE_REQ) {
+		return 0;
+	}
+
+	if (!cat->isalias) {
+		cil_println(indent, "(category %s)", key);
+	} else {
+		cil_println(indent, "(categoryalias %s)", key);
+		cil_println(indent, "(categoryaliasactual %s %s)", key, pdb->p_cat_val_to_name[cat->s.value - 1]);
+	}
+
+	return 0;
+}
+
+static int cat_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
+{
+	int rc = -1;
+	struct ebitmap_node *node;
+	uint32_t i;
+
+	if (ebitmap_cardinality(&order) == 0) {
+		rc = 0;
+		goto exit;
+	}
+
+	cil_indent(indent);
+	cil_printf("(categoryorder (");
+
+	ebitmap_for_each_bit(&order, node, i) {
+		if (!ebitmap_get_bit(&order, i)) {
+			continue;
+		}
+		cil_printf("%s ", pdb->p_cat_val_to_name[i]);
+	}
+
+	cil_printf("))\n");
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int polcaps_to_cil(struct policydb *pdb)
+{
+	int rc = -1;
+	struct ebitmap *map;
+	struct ebitmap_node *node;
+	uint32_t i;
+	const char *name;
+
+	map = &pdb->policycaps;
+
+	ebitmap_for_each_bit(map, node, i) {
+		if (!ebitmap_get_bit(map, i)) {
+			continue;
+		}
+		name = sepol_polcap_getname(i);
+		if (name == NULL) {
+			log_err("Unknown policy capability id: %i", i);
+			rc = -1;
+			goto exit;
+		}
+
+		cil_println(0, "(policycap %s)", name);
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int level_to_cil(struct policydb *pdb, struct mls_level *level)
+{
+	struct ebitmap *map = &level->cat;
+
+	cil_printf("(%s", pdb->p_sens_val_to_name[level->sens - 1]);
+
+	if (ebitmap_cardinality(map) > 0) {
+		cil_printf("(");
+		ebitmap_to_cil(pdb, map, SYM_CATS);
+		cil_printf(")");
+	}
+
+	cil_printf(")");
+
+	return 0;
+}
+
+static int context_to_cil(struct policydb *pdb, struct context_struct *con)
+{
+	cil_printf("(%s %s %s (",
+		pdb->p_user_val_to_name[con->user - 1],
+		pdb->p_role_val_to_name[con->role - 1],
+		pdb->p_type_val_to_name[con->type - 1]);
+
+	if (pdb->mls) {
+		level_to_cil(pdb, &con->range.level[0]);
+		cil_printf(" ");
+		level_to_cil(pdb, &con->range.level[1]);
+	} else {
+		cil_printf(DEFAULT_LEVEL);
+		cil_printf(" ");
+		cil_printf(DEFAULT_LEVEL);
+	}
+
+	cil_printf("))");
+
+	return 0;
+}
+
+static int ocontext_isid_to_cil(struct policydb *pdb, const char **sid_to_string, struct ocontext *isids)
+{
+	int rc = -1;
+
+	struct ocontext *isid;
+
+	struct sid_item {
+		const char *sid_key;
+		struct sid_item *next;
+	};
+
+	struct sid_item *head = NULL;
+	struct sid_item *item = NULL;
+
+	for (isid = isids; isid != NULL; isid = isid->next) {
+		cil_println(0, "(sid %s)", sid_to_string[isid->sid[0]]);
+		cil_printf("(sidcontext %s ", sid_to_string[isid->sid[0]]);
+		context_to_cil(pdb, &isid->context[0]);
+		cil_printf(")\n");
+
+		// get the sid names in the correct order (reverse from the isids
+		// ocontext) for sidorder statement
+		item = malloc(sizeof(*item));
+		if (item == NULL) {
+			log_err("Out of memory");
+			rc = -1;
+			goto exit;
+		}
+		item->sid_key = sid_to_string[isid->sid[0]];
+		item->next = head;
+		head = item;
+	}
+
+	if (head != NULL) {
+		cil_printf("(sidorder (");
+		for (item = head; item != NULL; item = item->next) {
+			cil_printf("%s ", item->sid_key);
+		}
+		cil_printf("))\n");
+	}
+
+	rc = 0;
+
+exit:
+	while(head) {
+		item = head;
+		head = item->next;
+		free(item);
+	}
+	return rc;
+}
+
+static int ocontext_selinux_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
+{
+	int rc = -1;
+
+	// initial sid names aren't actually stored in the pp files, need to a have
+	// a mapping, taken from the linux kernel
+	static const char *selinux_sid_to_string[] = {
+		"null",
+		"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",
+		NULL
+	};
+
+	rc = ocontext_isid_to_cil(pdb, selinux_sid_to_string, isids);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int ocontext_selinux_fs_to_cil(struct policydb *UNUSED(pdb), struct ocontext *fss)
+{
+	if (fss != NULL) {
+		log_err("Warning: 'fscon' statement unsupported in CIL. Dropping from output.");
+	}
+
+	return 0;
+}
+
+static int ocontext_selinux_port_to_cil(struct policydb *pdb, struct ocontext *portcons)
+{
+	int rc = -1;
+	struct ocontext *portcon;
+	const char *protocol;
+	uint16_t high;
+	uint16_t low;
+
+	for (portcon = portcons; portcon != NULL; portcon = portcon->next) {
+
+		switch (portcon->u.port.protocol) {
+		case IPPROTO_TCP: protocol = "tcp"; break;
+		case IPPROTO_UDP: protocol = "udp"; break;
+		default:
+			log_err("Unknown portcon protocol: %i", portcon->u.port.protocol);
+			rc = -1;
+			goto exit;
+		}
+
+		low = portcon->u.port.low_port;
+		high = portcon->u.port.high_port;
+
+		if (low == high) {
+			cil_printf("(portcon %s %i ", protocol, low);
+		} else {
+			cil_printf("(portcon %s (%i %i) ", protocol, low, high);
+		}
+
+		context_to_cil(pdb, &portcon->context[0]);
+
+		cil_printf(")\n");
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int ocontext_selinux_netif_to_cil(struct policydb *pdb, struct ocontext *netifs)
+{
+	struct ocontext *netif;
+
+	for (netif = netifs; netif != NULL; netif = netif->next) {
+		cil_printf("(netifcon %s ", netif->u.name);
+		context_to_cil(pdb, &netif->context[0]);
+
+		cil_printf(" ");
+		context_to_cil(pdb, &netif->context[1]);
+		cil_printf(")\n");
+	}
+
+	return 0;
+}
+
+static int ocontext_selinux_node_to_cil(struct policydb *pdb, struct ocontext *nodes)
+{
+	int rc = -1;
+	struct ocontext *node;
+	char addr[INET_ADDRSTRLEN];
+	char mask[INET_ADDRSTRLEN];
+
+	for (node = nodes; node != NULL; node = node->next) {
+		if (inet_ntop(AF_INET, &node->u.node.addr, addr, INET_ADDRSTRLEN) == NULL) {
+			log_err("Nodecon address is invalid: %s", strerror(errno));
+			rc = -1;
+			goto exit;
+		}
+
+		if (inet_ntop(AF_INET, &node->u.node.mask, mask, INET_ADDRSTRLEN) == NULL) {
+			log_err("Nodecon mask is invalid: %s", strerror(errno));
+			rc = -1;
+			goto exit;
+		}
+
+		cil_printf("(nodecon %s %s ", addr, mask);
+
+		context_to_cil(pdb, &node->context[0]);
+
+		cil_printf(")\n");
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int ocontext_selinux_node6_to_cil(struct policydb *pdb, struct ocontext *nodes)
+{
+	int rc = -1;
+	struct ocontext *node;
+	char addr[INET6_ADDRSTRLEN];
+	char mask[INET6_ADDRSTRLEN];
+
+	for (node = nodes; node != NULL; node = node->next) {
+		if (inet_ntop(AF_INET6, &node->u.node6.addr, addr, INET6_ADDRSTRLEN) == NULL) {
+			log_err("Nodecon address is invalid: %s", strerror(errno));
+			rc = -1;
+			goto exit;
+		}
+
+		if (inet_ntop(AF_INET6, &node->u.node6.mask, mask, INET6_ADDRSTRLEN) == NULL) {
+			log_err("Nodecon mask is invalid: %s", strerror(errno));
+			rc = -1;
+			goto exit;
+		}
+
+		cil_printf("(nodecon %s %s ", addr, mask);
+
+		context_to_cil(pdb, &node->context[0]);
+
+		cil_printf(")\n");
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+
+static int ocontext_selinux_fsuse_to_cil(struct policydb *pdb, struct ocontext *fsuses)
+{
+	int rc = -1;
+	struct ocontext *fsuse;
+	const char *behavior;
+
+
+	for (fsuse = fsuses; fsuse != NULL; fsuse = fsuse->next) {
+		switch (fsuse->v.behavior) {
+		case SECURITY_FS_USE_XATTR: behavior = "xattr"; break;
+		case SECURITY_FS_USE_TRANS: behavior = "trans"; break;
+		case SECURITY_FS_USE_TASK:  behavior = "task"; break;
+		default:
+			log_err("Unknown fsuse behavior: %i", fsuse->v.behavior);
+			rc = -1;
+			goto exit;
+		}
+
+		cil_printf("(fsuse %s %s ", behavior, fsuse->u.name);
+
+		context_to_cil(pdb, &fsuse->context[0]);
+
+		cil_printf(")\n");
+
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+
+static int ocontext_xen_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
+{
+	int rc = -1;
+
+	// initial sid names aren't actually stored in the pp files, need to a have
+	// a mapping, taken from the xen kernel
+	static const char *xen_sid_to_string[] = {
+		"null",
+		"xen",
+		"dom0",
+		"domio",
+		"domxen",
+		"unlabeled",
+		"security",
+		"ioport",
+		"iomem",
+		"irq",
+		"device",
+		NULL,
+	};
+
+	rc = ocontext_isid_to_cil(pdb, xen_sid_to_string, isids);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int ocontext_xen_pirq_to_cil(struct policydb *pdb, struct ocontext *pirqs)
+{
+	struct ocontext *pirq;
+
+	for (pirq = pirqs; pirq != NULL; pirq = pirq->next) {
+		cil_printf("(pirqcon %i ", pirq->u.pirq);
+		context_to_cil(pdb, &pirq->context[0]);
+		cil_printf(")\n");
+	}
+
+	return 0;
+}
+
+static int ocontext_xen_ioport_to_cil(struct policydb *pdb, struct ocontext *ioports)
+{
+	struct ocontext *ioport;
+	uint32_t low;
+	uint32_t high;
+
+	for (ioport = ioports; ioport != NULL; ioport = ioport->next) {
+		low = ioport->u.ioport.low_ioport;
+		high = ioport->u.ioport.high_ioport;
+
+		if (low == high) {
+			cil_printf("(ioportcon %i ", low);
+		} else {
+			cil_printf("(ioportcon (%i %i) ", low, high);
+		}
+
+		context_to_cil(pdb, &ioport->context[0]);
+
+		cil_printf(")\n");
+	}
+
+	return 0;
+}
+
+static int ocontext_xen_iomem_to_cil(struct policydb *pdb, struct ocontext *iomems)
+{
+	struct ocontext *iomem;
+	uint64_t low;
+	uint64_t high;
+
+	for (iomem = iomems; iomem != NULL; iomem = iomem->next) {
+		low = iomem->u.iomem.low_iomem;
+		high = iomem->u.iomem.high_iomem;
+
+		if (low == high) {
+			cil_printf("(iomemcon %#lX ", (unsigned long)low);
+		} else {
+			cil_printf("(iomemcon (%#lX %#lX) ", (unsigned long)low, (unsigned long)high);
+		}
+
+		context_to_cil(pdb, &iomem->context[0]);
+
+		cil_printf(")\n");
+	}
+
+	return 0;
+}
+
+static int ocontext_xen_pcidevice_to_cil(struct policydb *pdb, struct ocontext *pcids)
+{
+	struct ocontext *pcid;
+
+	for (pcid = pcids; pcid != NULL; pcid = pcid->next) {
+		cil_printf("(pcidevicecon %#lx ", (unsigned long)pcid->u.device);
+		context_to_cil(pdb, &pcid->context[0]);
+		cil_printf(")\n");
+	}
+
+	return 0;
+}
+
+static int ocontexts_to_cil(struct policydb *pdb)
+{
+	int rc = -1;
+	int ocon;
+
+	static int (**ocon_funcs)(struct policydb *pdb, struct ocontext *ocon);
+	static int (*ocon_selinux_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
+		ocontext_selinux_isid_to_cil,
+		ocontext_selinux_fs_to_cil,
+		ocontext_selinux_port_to_cil,
+		ocontext_selinux_netif_to_cil,
+		ocontext_selinux_node_to_cil,
+		ocontext_selinux_fsuse_to_cil,
+		ocontext_selinux_node6_to_cil,
+	};
+	static int (*ocon_xen_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
+		ocontext_xen_isid_to_cil,
+		ocontext_xen_pirq_to_cil,
+		ocontext_xen_ioport_to_cil,
+		ocontext_xen_iomem_to_cil,
+		ocontext_xen_pcidevice_to_cil,
+		NULL,
+		NULL,
+	};
+
+	switch (pdb->target_platform) {
+	case SEPOL_TARGET_SELINUX:
+		ocon_funcs = ocon_selinux_funcs;
+		break;
+	case SEPOL_TARGET_XEN:
+		ocon_funcs = ocon_xen_funcs;
+		break;
+	default:
+		log_err("Unknown target platform: %i", pdb->target_platform);
+		rc = -1;
+		goto exit;
+	}
+
+	for (ocon = 0; ocon < OCON_NUM; ocon++) {
+		if (ocon_funcs[ocon] != NULL) {
+			rc = ocon_funcs[ocon](pdb, pdb->ocontexts[ocon]);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int genfscon_to_cil(struct policydb *pdb)
+{
+	struct genfs *genfs;
+	struct ocontext *ocon;
+
+	for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) {
+		for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) {
+			cil_printf("(genfscon %s %s ", genfs->fstype, ocon->u.name);
+			context_to_cil(pdb, &ocon->context[0]);
+			cil_printf(")\n");
+		}
+	}
+
+	return 0;
+}
+
+static int level_string_to_cil(char *levelstr)
+{
+	int rc = -1;
+	char *sens = NULL;
+	char *cats = NULL;
+	int matched;
+	char *saveptr = NULL;
+	char *token = NULL;
+	char *ranged = NULL;
+
+	matched = sscanf(levelstr, "%m[^:]:%ms", &sens, &cats);
+	if (matched < 1 || matched > 2) {
+		log_err("Invalid level: %s", levelstr);
+		rc = -1;
+		goto exit;
+	}
+
+	cil_printf("(%s", sens);
+
+	if (matched == 2) {
+		cil_printf("(");
+		token = strtok_r(cats, ",", &saveptr);
+		while (token != NULL) {
+			ranged = strchr(token, '.');
+			if (ranged == NULL) {
+				cil_printf("%s ", token);
+			} else {
+				*ranged = '\0';
+				cil_printf("(range %s %s) ", token, ranged + 1);
+			}
+			token = strtok_r(NULL, ",", &saveptr);
+		}
+		cil_printf(")");
+	}
+
+	cil_printf(")");
+
+	rc = 0;
+exit:
+	free(sens);
+	free(cats);
+	return rc;
+}
+
+static int level_range_string_to_cil(char *levelrangestr)
+{
+	char *ranged = NULL;
+	char *low;
+	char *high;
+
+	ranged = strchr(levelrangestr, '-');
+	if (ranged == NULL) {
+		low = high = levelrangestr;
+	} else {
+		*ranged = '\0';
+		low = levelrangestr;
+		high = ranged + 1;
+	}
+
+	level_string_to_cil(low);
+	cil_printf(" ");
+	level_string_to_cil(high);
+
+	return 0;
+}
+
+static int context_string_to_cil(char *contextstr)
+{
+	int rc = -1;
+	int matched;
+	char *user = NULL;
+	char *role = NULL;
+	char *type = NULL;
+	char *level = NULL;
+
+	matched = sscanf(contextstr, "%m[^:]:%m[^:]:%m[^:]:%ms", &user, &role, &type, &level);
+	if (matched < 3 || matched > 4) {
+		log_err("Invalid context: %s", contextstr);
+		rc = -1;
+		goto exit;
+	}
+
+	cil_printf("(%s %s %s (", user, role, type);
+
+	if (matched == 3) {
+		cil_printf(DEFAULT_LEVEL);
+		cil_printf(" ");
+		cil_printf(DEFAULT_LEVEL);
+	} else {
+		level_range_string_to_cil(level);
+	}
+
+	cil_printf("))");
+
+	rc = 0;
+
+exit:
+	free(user);
+	free(role);
+	free(type);
+	free(level);
+
+	return rc;
+}
+
+static int seusers_to_cil(struct sepol_module_package *mod_pkg)
+{
+	int rc = -1;
+	FILE *fp = NULL;
+	char *seusers = sepol_module_package_get_seusers(mod_pkg);
+	size_t seusers_len = sepol_module_package_get_seusers_len(mod_pkg);
+	size_t len = 0;
+	char *line = NULL;
+	ssize_t line_len = 0;
+	char *buf = NULL;
+
+	char *user = NULL;
+	char *seuser = NULL;
+	char *level = NULL;
+	int matched;
+
+	if (seusers_len == 0) {
+		return 0;
+	}
+
+	fp = fmemopen(seusers, seusers_len, "r");
+
+	while ((line_len = getline(&line, &len, fp)) != -1) {
+		buf = line;
+		buf[line_len - 1] = '\0';
+		while (*buf && isspace(buf[0])) {
+			buf++;
+		}
+		if (buf[0] == '#' || buf[0] == '\0') {
+			continue;
+		}
+
+		matched = sscanf(buf, "%m[^:]:%m[^:]:%ms", &user, &seuser, &level);
+
+		if (matched < 2 || matched > 3) {
+			log_err("Invalid seuser line: %s", line);
+			rc = -1;
+			goto exit;
+		}
+
+		if (!strcmp(user, "__default__")) {
+			cil_printf("(selinuxuserdefault %s (", seuser);
+		} else {
+			cil_printf("(selinuxuser %s %s (", user, seuser);
+		}
+
+		switch (matched) {
+		case 2:
+			cil_printf("systemlow systemlow");
+			break;
+		case 3:
+			level_range_string_to_cil(level);
+			break;
+		}
+
+		cil_printf("))\n");
+
+		free(user);
+		free(seuser);
+		free(level);
+		user = seuser = level = NULL;
+	}
+	if (ferror(fp)) {
+		cil_printf("Failed to read seusers\n");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = 0;
+
+exit:
+	if (fp != NULL) {
+		fclose(fp);
+	}
+	free(line);
+	free(user);
+	free(seuser);
+	free(level);
+
+	return rc;
+}
+
+static int netfilter_contexts_to_cil(struct sepol_module_package *mod_pkg)
+{
+	size_t netcons_len = sepol_module_package_get_netfilter_contexts_len(mod_pkg);
+
+	if (netcons_len > 0) {
+		log_err("Warning: netfilter_contexts are unsupported in CIL. Dropping from output.");
+	}
+
+	return 0;
+}
+
+static int user_extra_to_cil(struct sepol_module_package *mod_pkg)
+{
+	int rc = -1;
+	char *userx = sepol_module_package_get_user_extra(mod_pkg);
+	size_t userx_len = sepol_module_package_get_user_extra_len(mod_pkg);
+	FILE *fp = NULL;
+	size_t len = 0;
+	char *line = NULL;
+	ssize_t line_len = 0;
+	int matched;
+	char *user = NULL;
+	char *prefix = NULL;
+
+	if (userx_len == 0) {
+		return 0;
+	}
+
+	fp = fmemopen(userx, userx_len, "r");
+
+	while ((line_len = getline(&line, &len, fp)) != -1) {
+		line[line_len - 1] = '\0';
+
+		matched = sscanf(line, "user %ms prefix %m[^;];", &user, &prefix);
+		if (matched != 2) {
+			rc = -1;
+			log_err("Invalid file context line: %s", line);
+			goto exit;
+		}
+
+		cil_println(0, "(userprefix %s %s)", user, prefix);
+		free(user);
+		free(prefix);
+		user = prefix = NULL;
+	}
+
+	if (ferror(fp)) {
+		cil_printf("Failed to read user_extra\n");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = 0;
+exit:
+	if (fp != NULL) {
+		fclose(fp);
+	}
+	free(line);
+	free(user);
+	free(prefix);
+
+	return rc;
+}
+
+static int file_contexts_to_cil(struct sepol_module_package *mod_pkg)
+{
+	int rc = -1;
+	char *fc = sepol_module_package_get_file_contexts(mod_pkg);
+	size_t fc_len = sepol_module_package_get_file_contexts_len(mod_pkg);
+	FILE *fp = NULL;
+	size_t len = 0;
+	char *line = NULL;
+	char *buf = NULL;
+	ssize_t line_len = 0;
+	int matched;
+	char *regex = NULL;
+	char *mode = NULL;
+	char *context = NULL;
+	const char *cilmode;
+
+	if (fc_len == 0) {
+		return 0;
+	}
+
+	fp = fmemopen(fc, fc_len, "r");
+	while ((line_len = getline(&line, &len, fp)) != -1) {
+		buf = line;
+		if (buf[line_len - 1] == '\n') {
+			buf[line_len - 1] = '\0';
+		}
+		while (*buf && isspace(buf[0])) {
+			buf++;
+		}
+		if (buf[0] == '#' || buf[0] == '\0') {
+			continue;
+		}
+
+		matched = sscanf(buf, "%ms %ms %ms", &regex, &mode, &context);
+		if (matched < 2 || matched > 3) {
+			rc = -1;
+			log_err("Invalid file context line: %s", line);
+			goto exit;
+		}
+
+		if (matched == 2) {
+			context = mode;
+			mode = NULL;
+		}
+
+		if (mode == NULL) {
+			cilmode = "any";
+		} else if (!strcmp(mode, "--")) {
+			cilmode = "file";
+		} else if (!strcmp(mode, "-d")) {
+			cilmode = "dir";
+		} else if (!strcmp(mode, "-c")) {
+			cilmode = "char";
+		} else if (!strcmp(mode, "-b")) {
+			cilmode = "block";
+		} else if (!strcmp(mode, "-s")) {
+			cilmode = "socket";
+		} else if (!strcmp(mode, "-p")) {
+			cilmode = "pipe";
+		} else if (!strcmp(mode, "-l")) {
+			cilmode = "symlink";
+		} else {
+			rc = -1;
+			log_err("Invalid mode in file context line: %s", line);
+			goto exit;
+		}
+
+		cil_printf("(filecon \"%s\" %s ", regex, cilmode);
+
+		if (!strcmp(context, "<<none>>")) {
+			cil_printf("()");
+		} else {
+			context_string_to_cil(context);
+		}
+
+		cil_printf(")\n");
+
+		free(regex);
+		free(mode);
+		free(context);
+		regex = mode = context = NULL;
+	}
+
+	if (ferror(fp)) {
+		cil_printf("Failed to read user_extra\n");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = 0;
+exit:
+	free(line);
+	free(regex);
+	free(mode);
+	free(context);
+	if (fp != NULL) {
+		fclose(fp);
+	}
+
+	return rc;
+}
+
+
+static int (*func_to_cil[SYM_NUM])(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack, char *key, void *datum, int scope) = {
+	NULL,	// commons, only stored in the global symtab, handled elsewhere
+	class_to_cil,
+	role_to_cil,
+	type_to_cil,
+	user_to_cil,
+	boolean_to_cil,
+	sens_to_cil,
+	cat_to_cil
+};
+
+static int typealiases_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack)
+{
+	struct type_datum *alias_datum;
+	char *alias_name;
+	struct list_node *curr;
+	struct avrule_decl *decl = stack_peek(decl_stack);
+	struct list *alias_list = typealias_lists[decl->decl_id];
+	int rc = -1;
+
+	if (alias_list == NULL) {
+		return 0;
+	}
+
+	for (curr = alias_list->head; curr != NULL; curr = curr->next) {
+		alias_name = curr->data;
+		alias_datum = hashtab_search(pdb->p_types.table, alias_name);
+		if (alias_datum == NULL) {
+			rc = -1;
+			goto exit;
+		}
+
+		cil_println(indent, "(typealias %s)", alias_name);
+		cil_println(indent, "(typealiasactual %s %s)", alias_name, pdb->p_type_val_to_name[alias_datum->s.value - 1]);
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int declared_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
+{
+	int rc = -1;
+	struct ebitmap map;
+	struct ebitmap_node *node;
+	unsigned int i;
+	char * key;
+	struct scope_datum *scope;
+	int sym;
+	void *datum;
+	struct avrule_decl *decl = stack_peek(decl_stack);
+
+	for (sym = 0; sym < SYM_NUM; sym++) {
+		if (func_to_cil[sym] == NULL) {
+			continue;
+		}
+
+		map = decl->declared.scope[sym];
+		ebitmap_for_each_bit(&map, node, i) {
+			if (!ebitmap_get_bit(&map, i)) {
+				continue;
+			}
+			key = pdb->sym_val_to_name[sym][i];
+			datum = hashtab_search(pdb->symtab[sym].table, key);
+			if (datum == NULL) {
+				rc = -1;
+				goto exit;
+			}
+			scope = hashtab_search(pdb->scope[sym].table, key);
+			if (scope == NULL) {
+				rc = -1;
+				goto exit;
+			}
+			rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, scope->scope);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+
+		if (sym == SYM_CATS) {
+			rc = cat_order_to_cil(indent, pdb, map);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+
+		if (sym == SYM_LEVELS) {
+			rc = sens_order_to_cil(indent, pdb, map);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+
+		if (sym == SYM_CLASSES) {
+			rc = class_order_to_cil(indent, pdb, map);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+static int required_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
+{
+	int rc = -1;
+	struct ebitmap map;
+	struct ebitmap_node *node;
+	unsigned int i;
+	unsigned int j;
+	char * key;
+	int sym;
+	void *datum;
+	struct avrule_decl *decl = stack_peek(decl_stack);
+	struct scope_datum *scope_datum;
+
+	for (sym = 0; sym < SYM_NUM; sym++) {
+		if (func_to_cil[sym] == NULL) {
+			continue;
+		}
+
+		map = decl->required.scope[sym];
+		ebitmap_for_each_bit(&map, node, i) {
+			if (!ebitmap_get_bit(&map, i)) {
+				continue;
+			}
+			key = pdb->sym_val_to_name[sym][i];
+
+			scope_datum = hashtab_search(pdb->scope[sym].table, key);
+			for (j = 0; j < scope_datum->decl_ids_len; j++) {
+				if (scope_datum->decl_ids[j] == decl->decl_id) {
+					break;
+				}
+			}
+			if (j >= scope_datum->decl_ids_len) {
+				// Symbols required in the global scope are also in the
+				// required scope ebitmap of all avrule decls (i.e. required
+				// in all optionals). So we need to look at the scopes of each
+				// symbol in this avrule_decl to determine if it actually is
+				// required in this decl, or if it's just required in the
+				// global scope. If we got here, then this symbol is not
+				// actually required in this scope, so skip it.
+				continue;
+			}
+
+			datum = hashtab_search(pdb->symtab[sym].table, key);
+			if (datum == NULL) {
+				rc = -1;
+				goto exit;
+			}
+			rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, SCOPE_REQ);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+
+static int additive_scopes_to_cil_map(char *key, void *data, void *arg)
+{
+	int rc = -1;
+	struct map_args *args = arg;
+
+	rc = func_to_cil[args->sym_index](args->indent, args->pdb, args->block, args->decl_stack, key, data, SCOPE_REQ);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int additive_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
+{
+	int rc = -1;
+	struct map_args args;
+	args.pdb = pdb;
+	args.block = block;
+	args.decl_stack = decl_stack;
+	args.indent = indent;
+	struct avrule_decl *decl = stack_peek(decl_stack);
+
+	for (args.sym_index = 0; args.sym_index < SYM_NUM; args.sym_index++) {
+		rc = hashtab_map(decl->symtab[args.sym_index].table, additive_scopes_to_cil_map, &args);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int is_scope_superset(struct scope_index *sup, struct scope_index *sub)
+{
+	// returns 1 if sup is a superset of sub, returns 0 otherwise
+
+	int rc = 0;
+
+	uint32_t i;
+	struct ebitmap sup_map;
+	struct ebitmap sub_map;
+	struct ebitmap res;
+
+	ebitmap_init(&res);
+
+	for (i = 0; i < SYM_NUM; i++) {
+		sup_map = sup->scope[i];
+		sub_map = sub->scope[i];
+
+		ebitmap_and(&res, &sup_map, &sub_map);
+		if (!ebitmap_cmp(&res, &sub_map)) {
+			goto exit;
+		}
+		ebitmap_destroy(&res);
+	}
+
+	if (sup->class_perms_len < sub->class_perms_len) {
+		goto exit;
+	}
+
+	for (i = 0; i < sub->class_perms_len; i++) {
+		sup_map = sup->class_perms_map[i];
+		sub_map = sub->class_perms_map[i];
+
+		ebitmap_and(&res, &sup_map, &sub_map);
+		if (!ebitmap_cmp(&res, &sub_map)) {
+			goto exit;
+		}
+		ebitmap_destroy(&res);
+	}
+
+	rc = 1;
+
+exit:
+
+	ebitmap_destroy(&res);
+	return rc;
+}
+
+static int blocks_to_cil(struct policydb *pdb)
+{
+	int rc = -1;
+	struct avrule_block *block;
+	struct avrule_decl *decl;
+	struct avrule_decl *decl_tmp;
+	int indent = 0;
+	struct stack *stack;
+	struct list *attr_list;
+
+	rc = stack_init(&stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	for (block = pdb->global; block != NULL; block = block->next) {
+		rc = list_init(&attr_list);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		decl = block->branch_list;
+		if (decl == NULL) {
+			continue;
+		}
+
+		if (decl->next != NULL) {
+			log_err("Warning: 'else' blocks in optional statements are unsupported in CIL. Dropping from output.");
+		}
+
+		if (block->flags & AVRULE_OPTIONAL) {
+			while (stack->pos > 0) {
+				decl_tmp = stack_peek(stack);
+				if (is_scope_superset(&decl->required, &decl_tmp->required)) {
+					break;
+				}
+
+				stack_pop(stack);
+				indent--;
+				cil_println(indent, ")");
+			}
+
+			cil_println(indent, "(optional %s_optional_%i", pdb->name, decl->decl_id);
+			indent++;
+		}
+
+		stack_push(stack, decl);
+
+		if (stack->pos == 0) {
+			// type aliases and commons are only stored in the global symtab.
+			// However, to get scoping correct, we assume they are in the
+			// global block
+			struct map_args args;
+			args.pdb = pdb;
+			args.block = block;
+			args.decl_stack = stack;
+			args.indent = 0;
+			args.scope = SCOPE_DECL;
+
+			rc = hashtab_map(pdb->p_commons.table, common_to_cil, &args);
+			if (rc != 0) {
+				goto exit;
+			}
+		}
+
+		rc = typealiases_to_cil(indent, pdb, block, stack);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = declared_scopes_to_cil(indent, pdb, block, stack);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = required_scopes_to_cil(indent, pdb, block, stack);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = additive_scopes_to_cil(indent, pdb, block, stack);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = avrule_list_to_cil(indent, pdb, decl->avrules, attr_list);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = role_trans_to_cil(indent, pdb, decl->role_tr_rules);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = role_allows_to_cil(indent, pdb, decl->role_allow_rules);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = range_trans_to_cil(indent, pdb, decl->range_tr_rules);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = cond_list_to_cil(indent, pdb, decl->cond_list);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		rc = cil_print_attr_list(indent, pdb, attr_list);
+		if (rc != 0) {
+			goto exit;
+		}
+		attr_list_destroy(&attr_list);
+	}
+
+	while (indent > 0) {
+		indent--;
+		cil_println(indent, ")");
+	}
+
+	rc = 0;
+
+exit:
+	stack_destroy(&stack);
+	attr_list_destroy(&attr_list);
+
+	return rc;
+}
+
+static int handle_unknown_to_cil(struct policydb *pdb)
+{
+	int rc = -1;
+	const char *hu;
+
+	switch (pdb->handle_unknown) {
+	case SEPOL_DENY_UNKNOWN:
+		hu = "deny";
+		break;
+	case SEPOL_REJECT_UNKNOWN:
+		hu = "reject";
+		break;
+	case SEPOL_ALLOW_UNKNOWN:
+		hu = "allow";
+		break;
+	default:
+		log_err("Unknown value for handle-unknown: %i", pdb->handle_unknown);
+		rc = -1;
+		goto exit;
+	}
+
+	cil_println(0, "(handleunknown %s)", hu);
+
+	return 0;
+
+exit:
+	return rc;
+}
+
+static int generate_mls(struct policydb *pdb)
+{
+	const char *mls_str = pdb->mls ? "true" : "false";
+	cil_println(0, "(mls %s)", mls_str);
+
+	return 0;
+}
+
+static int generate_default_level(void)
+{
+	cil_println(0, "(sensitivity s0)");
+	cil_println(0, "(sensitivityorder (s0))");
+	cil_println(0, "(level " DEFAULT_LEVEL " (s0))");
+
+	return 0;
+}
+
+static int generate_default_object(void)
+{
+	cil_println(0, "(role " DEFAULT_OBJECT ")");
+
+	return 0;
+}
+
+static int generate_gen_require_attribute(void)
+{
+	cil_println(0, "(typeattribute " GEN_REQUIRE_ATTR ")");
+	cil_println(0, "(roleattribute " GEN_REQUIRE_ATTR ")");
+
+	return 0;
+}
+
+static int fix_module_name(struct policydb *pdb)
+{
+	char *letter;
+	int rc = -1;
+
+	// The base module doesn't have its name set, but we use that for some
+	// autogenerated names, like optionals and attributes, to prevent naming
+	// collisions. However, they sometimes need to be fixed up.
+
+	// the base module isn't given a name, so just call it "base"
+	if (pdb->policy_type == POLICY_BASE) {
+		pdb->name = strdup("base");
+		if (pdb->name == NULL) {
+			log_err("Out of memory");
+			rc = -1;
+			goto exit;
+		}
+	}
+
+	// CIL is more restrictive in module names than checkmodule. Convert bad
+	// characters to underscores
+	for (letter = pdb->name; *letter != '\0'; letter++) {
+		if (isalnum(*letter)) {
+			continue;
+		}
+
+		*letter = '_';
+	}
+
+	return 0;
+exit:
+	return rc;
+}
+
+int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
+{
+	int rc = -1;
+	struct sepol_policydb *pdb;
+
+	out_file = fp;
+
+	pdb = sepol_module_package_get_policy(mod_pkg);
+	if (pdb == NULL) {
+		log_err("Failed to get policydb");
+		rc = -1;
+		goto exit;
+	}
+
+	if (pdb->p.policy_type != SEPOL_POLICY_BASE &&
+		pdb->p.policy_type != SEPOL_POLICY_MOD) {
+		log_err("Policy pakcage is not a base or module");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = fix_module_name(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	if (pdb->p.policy_type == SEPOL_POLICY_BASE && !pdb->p.mls) {
+		// If this is a base non-mls policy, we need to define a default level
+		// range that can be used for contexts by other non-mls modules, since
+		// CIL requires that all contexts have a range, even if they are
+		// ignored as in non-mls policies
+		rc = generate_default_level();
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	if (pdb->p.policy_type == SEPOL_POLICY_BASE) {
+		// object_r is implicit in checkmodule, but not with CIL, create it
+		// as part of base
+		rc = generate_default_object();
+		if (rc != 0) {
+			goto exit;
+		}
+
+		// default attribute to be used to mimic gen_require in CIL
+		rc = generate_gen_require_attribute();
+		if (rc != 0) {
+			goto exit;
+		}
+
+		// handle_unknown is used from only the base module
+		rc = handle_unknown_to_cil(&pdb->p);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		// mls is used from only the base module
+		rc = generate_mls(&pdb->p);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
+
+	rc = role_list_create(pdb->p.p_roles.table);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = typealias_list_create(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = polcaps_to_cil(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = ocontexts_to_cil(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = genfscon_to_cil(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = seusers_to_cil(mod_pkg);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = netfilter_contexts_to_cil(mod_pkg);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = user_extra_to_cil(mod_pkg);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = file_contexts_to_cil(mod_pkg);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	// now print everything that is scoped
+	rc = blocks_to_cil(&pdb->p);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = 0;
+
+exit:
+	role_list_destroy();
+	typealias_list_destroy();
+
+	return rc;
+}
+
+static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
+{
+	int rc = -1;
+	char *d = NULL;
+	size_t d_len = 0;
+	size_t read_len = 0;
+	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
+
+	d = malloc(max_len);
+	if (d == NULL) {
+		log_err("Out of memory");
+		rc = -1;
+		goto exit;
+	}
+
+	while ((read_len = fread(d + d_len, 1, max_len - d_len, fp)) > 0) {
+		d_len += read_len;
+		if (d_len == max_len) {
+			max_len *= 2;
+			d = realloc(d, max_len);
+			if (d == NULL) {
+				log_err("Out of memory");
+				rc = -1;
+				goto exit;
+			}
+		}
+	}
+
+	if (ferror(fp) != 0) {
+		log_err("Failed to read pp file");
+		rc = -1;
+		goto exit;
+	}
+
+	*data = d;
+	*data_len = d_len;
+
+	return 0;
+
+exit:
+	free(d);
+	return rc;
+}
+
+int sepol_ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg)
+{
+	int rc = -1;
+	FILE *f = NULL;
+	struct sepol_policy_file *pf = NULL;
+	struct sepol_module_package *pkg = NULL;
+	char *data = NULL;
+	size_t data_len;
+	int fd;
+	struct stat sb;
+
+	rc = sepol_policy_file_create(&pf);
+	if (rc != 0) {
+		log_err("Failed to create policy file");
+		goto exit;
+	}
+
+	fd = fileno(fp);
+	if (fstat(fd, &sb) == -1) {
+		rc = -1;
+		goto exit;
+	}
+
+	if (S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode)) {
+		// libsepol fails when trying to read a policy package from a pipe or a
+		// socket due its use of lseek. In this case, read the data into a
+		// buffer and provide that to libsepol
+		rc = fp_to_buffer(fp, &data, &data_len);
+		if (rc != 0) {
+			goto exit;
+		}
+
+		sepol_policy_file_set_mem(pf, data, data_len);
+	} else {
+		sepol_policy_file_set_fp(pf, fp);
+	}
+
+	rc = sepol_module_package_create(&pkg);
+	if (rc != 0) {
+		log_err("Failed to create module package");
+		goto exit;
+	}
+
+	rc = sepol_module_package_read(pkg, pf, 0);
+	if (rc != 0) {
+		log_err("Failed to read policy package");
+		goto exit;
+	}
+
+	*mod_pkg = pkg;
+
+exit:
+	free(data);
+
+	sepol_policy_file_free(pf);
+	if (f != NULL) {
+		fclose(f);
+	}
+
+	if (rc != 0) {
+		sepol_module_package_free(pkg);
+	}
+
+	return rc;
+}
diff --git a/policycoreutils/hll/pp/pp.c b/policycoreutils/hll/pp/pp.c
index 60c493d..866734f 100644
--- a/policycoreutils/hll/pp/pp.c
+++ b/policycoreutils/hll/pp/pp.c
@@ -17,43 +17,19 @@
  * 02110-1301, USA.
  */
 
-#include <arpa/inet.h>
-#include <ctype.h>
 #include <errno.h>
-#include <fcntl.h>
 #include <getopt.h>
 #include <libgen.h>
-#include <netinet/in.h>
 #include <signal.h>
 #include <stdarg.h>
-#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <unistd.h>
 
 #include <sepol/module.h>
-#include <sepol/policydb/conditional.h>
-#include <sepol/policydb/hashtab.h>
-#include <sepol/policydb/polcaps.h>
-#include <sepol/policydb/policydb.h>
-#include <sepol/policydb/services.h>
-#include <sepol/policydb/util.h>
-
-#ifdef __GNUC__
-#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
-#else
-#  define UNUSED(x) UNUSED_ ## x
-#endif
+#include <sepol/module_to_cil.h>
 
 char *progname;
-FILE *out_file;
-
-#define STACK_SIZE 16
-#define DEFAULT_LEVEL "systemlow"
-#define DEFAULT_OBJECT "object_r"
-#define GEN_REQUIRE_ATTR "cil_gen_require"
 
 __attribute__ ((format(printf, 1, 2)))
 static void log_err(const char *fmt, ...)
@@ -69,3805 +45,6 @@ static void log_err(const char *fmt, ...)
 	}
 }
 
-static void cil_indent(int indent)
-{
-	if (fprintf(out_file, "%*s", indent * 4, "") < 0) {
-		log_err("Failed to write to output");
-		_exit(EXIT_FAILURE);
-	}
-}
-
-__attribute__ ((format(printf, 1, 2)))
-static void cil_printf(const char *fmt, ...) {
-	va_list argptr;
-	va_start(argptr, fmt);
-	if (vfprintf(out_file, fmt, argptr) < 0) {
-		log_err("Failed to write to output");
-		_exit(EXIT_FAILURE);
-	}
-	va_end(argptr);
-}
-
-__attribute__ ((format(printf, 2, 3)))
-static void cil_println(int indent, const char *fmt, ...)
-{
-	cil_indent(indent);
-	va_list argptr;
-	va_start(argptr, fmt);
-	if (vfprintf(out_file, fmt, argptr) < 0) {
-		log_err("Failed to write to output");
-		_exit(EXIT_FAILURE);
-	}
-	va_end(argptr);
-	if (fprintf(out_file, "\n") < 0) {
-		log_err("Failed to write to output");
-		_exit(EXIT_FAILURE);
-	}
-}
-
-struct map_args {
-	struct policydb *pdb;
-	struct avrule_block *block;
-	struct stack *decl_stack;
-	int scope;
-	int indent;
-	int sym_index;
-};
-
-struct stack {
-	 void **stack;
-	 int pos;
-	 int size;
-};
-
-struct role_list_node {
-	char *role_name;
-	role_datum_t *role;
-};
-
-struct attr_list_node {
-	char *attribute;
-	int is_type;
-	union {
-		struct type_set *ts;
-		struct role_set *rs;
-	} set;
-};
-
-struct list_node {
-	void *data;
-	struct list_node *next;
-};
-
-struct list {
-	struct list_node *head;
-};
-
-/* A linked list of all roles stored in the pdb
- * which is iterated to determine types associated
- * with each role when printing role_type statements
- */
-static struct list *role_list;
-
-static void list_destroy(struct list **list)
-{
-	struct list_node *curr = (*list)->head;
-	struct list_node *tmp;
-
-	while (curr != NULL) {
-		tmp = curr->next;
-		free(curr);
-		curr = tmp;
-	}
-
-	free(*list);
-	*list = NULL;
-}
-
-static void role_list_destroy(void)
-{
-	struct list_node *curr = role_list->head;
-
-	while (curr != NULL) {
-		free(curr->data);
-		curr->data = NULL;
-		curr = curr->next;
-	}
-
-	list_destroy(&role_list);
-}
-
-static void attr_list_destroy(struct list **attr_list)
-{
-	if (attr_list == NULL || *attr_list == NULL) {
-		return;
-	}
-
-	struct list_node *curr = (*attr_list)->head;
-	struct attr_list_node *attr;
-
-	while (curr != NULL) {
-		attr = curr->data;
-		if (attr != NULL) {
-			free(attr->attribute);
-		}
-
-		free(curr->data);
-		curr->data = NULL;
-		curr = curr->next;
-	}
-
-	list_destroy(attr_list);
-}
-
-static int list_init(struct list **list)
-{
-	int rc = -1;
-	struct list *l = calloc(1, sizeof(*l));
-	if (l == NULL) {
-		goto exit;
-	}
-
-	*list = l;
-
-	return 0;
-
-exit:
-	list_destroy(&l);
-	return rc;
-}
-
-static int list_prepend(struct list *list, void *data)
-{
-	int rc = -1;
-	struct list_node *node = calloc(1, sizeof(*node));
-	if (node == NULL) {
-		goto exit;
-	}
-
-	node->data = data;
-	node->next = list->head;
-	list->head = node;
-
-	rc = 0;
-
-exit:
-	return rc;
-}
-
-static int roles_gather_map(char *key, void *data, void *args)
-{
-	struct role_list_node *role_node;
-	role_datum_t *role = data;
-	int rc = -1;
-
-	role_node = calloc(1, sizeof(*role_node));
-	if (role_node == NULL) {
-		return rc;
-	}
-
-	role_node->role_name = key;
-	role_node->role = role;
-
-	rc = list_prepend((struct list *)args, role_node);
-	return rc;
-}
-
-static int role_list_create(hashtab_t roles_tab)
-{
-	int rc = -1;
-
-	rc = list_init(&role_list);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = hashtab_map(roles_tab, roles_gather_map, role_list);
-
-exit:
-	return rc;
-}
-
-// array of lists, where each list contains all the aliases defined in the scope at index i
-static struct list **typealias_lists;
-static uint32_t typealias_lists_len;
-
-static int typealiases_gather_map(char *key, void *data, void *arg)
-{
-	int rc = -1;
-	struct type_datum *type = data;
-	struct policydb *pdb = arg;
-	struct scope_datum *scope;
-	uint32_t i;
-	uint32_t scope_id;
-
-	if (type->primary != 1) {
-		scope = hashtab_search(pdb->scope[SYM_TYPES].table, key);
-		if (scope == NULL) {
-			return -1;
-		}
-
-		for (i = 0; i < scope->decl_ids_len; i++) {
-			scope_id = scope->decl_ids[i];
-			if (typealias_lists[scope_id] == NULL) {
-				rc = list_init(&typealias_lists[scope_id]);
-				if (rc != 0) {
-					goto exit;
-				}
-			}
-			list_prepend(typealias_lists[scope_id], key);
-		}
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static void typealias_list_destroy(void)
-{
-	uint32_t i;
-	for (i = 0; i < typealias_lists_len; i++) {
-		if (typealias_lists[i] != NULL) {
-			list_destroy(&typealias_lists[i]);
-		}
-	}
-	typealias_lists_len = 0;
-	free(typealias_lists);
-	typealias_lists = NULL;
-}
-
-static int typealias_list_create(struct policydb *pdb)
-{
-	uint32_t max_decl_id = 0;
-	struct avrule_decl *decl;
-	struct avrule_block *block;
-	uint32_t rc = -1;
-
-	for (block = pdb->global; block != NULL; block = block->next) {
-		decl = block->branch_list;
-		if (decl->decl_id > max_decl_id) {
-			max_decl_id = decl->decl_id;
-		}
-	}
-
-	typealias_lists = calloc(max_decl_id + 1, sizeof(*typealias_lists));
-	typealias_lists_len = max_decl_id + 1;
-
-	rc = hashtab_map(pdb->p_types.table, typealiases_gather_map, pdb);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	return 0;
-
-exit:
-	typealias_list_destroy();
-
-	return rc;
-}
-
-
-static int stack_destroy(struct stack **stack)
-{
-	if (stack == NULL || *stack == NULL) {
-		return 0;
-	}
-
-	free((*stack)->stack);
-	free(*stack);
-	*stack = NULL;
-
-	return 0;
-}
-
-static int stack_init(struct stack **stack)
-{
-	int rc = -1;
-	struct stack *s = calloc(1, sizeof(*s));
-	if (s == NULL) {
-		goto exit;
-	}
-
-	s->stack = malloc(sizeof(*s->stack) * STACK_SIZE);
-	if (s->stack == NULL) {
-		goto exit;
-	}
-
-	s->pos = -1;
-	s->size = STACK_SIZE;
-
-	*stack = s;
-
-	return 0;
-
-exit:
-	stack_destroy(&s);
-	return rc;
-}
-
-static int stack_push(struct stack *stack, void *ptr)
-{
-	int rc = -1;
-	void *new_stack;
-
-	if (stack->pos + 1 == stack->size) {
-		new_stack = realloc(stack->stack, sizeof(*stack->stack) * (stack->size * 2));
-		if (new_stack == NULL) {
-			goto exit;
-		}
-		stack->stack = new_stack;
-		stack->size *= 2;
-	}
-
-	stack->pos++;
-	stack->stack[stack->pos] = ptr;
-
-	rc = 0;
-exit:
-	return rc;
-}
-
-static void *stack_pop(struct stack *stack)
-{
-	if (stack->pos == -1) {
-		return NULL;
-	}
-
-	stack->pos--;
-	return stack->stack[stack->pos + 1];
-}
-
-static void *stack_peek(struct stack *stack)
-{
-	if (stack->pos == -1) {
-		return NULL;
-	}
-
-	return stack->stack[stack->pos];
-}
-
-static int is_id_in_scope_with_start(struct policydb *pdb, struct stack *decl_stack, int start, uint32_t symbol_type, char *id)
-{
-	int i;
-	uint32_t j;
-	struct avrule_decl *decl;
-	struct scope_datum *scope;
-
-	scope = hashtab_search(pdb->scope[symbol_type].table, id);
-	if (scope == NULL) {
-		return 0;
-	}
-
-	for (i = start; i >= 0; i--) {
-		decl = decl_stack->stack[i];
-
-		for (j = 0; j < scope->decl_ids_len; j++) {
-			if (scope->decl_ids[j] == decl->decl_id) {
-				return 1;
-			}
-		}
-	}
-
-	return 0;
-}
-
-static int is_id_in_ancestor_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
-{
-	int start = decl_stack->pos - 1;
-
-	return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
-}
-
-static int is_id_in_scope(struct policydb *pdb, struct stack *decl_stack, char *type, uint32_t symbol_type)
-{
-	int start = decl_stack->pos;
-
-	return is_id_in_scope_with_start(pdb, decl_stack, start, symbol_type, type);
-}
-
-static int semantic_level_to_cil(struct policydb *pdb, int sens_offset, struct mls_semantic_level *level)
-{
-	struct mls_semantic_cat *cat;
-
-	cil_printf("(%s ", pdb->p_sens_val_to_name[level->sens - sens_offset]);
-
-	if (level->cat != NULL) {
-		cil_printf("(");
-	}
-
-	for (cat = level->cat; cat != NULL; cat = cat->next) {
-		if (cat->low == cat->high) {
-			cil_printf("%s", pdb->p_cat_val_to_name[cat->low - 1]);
-		} else {
-			cil_printf("range %s %s", pdb->p_cat_val_to_name[cat->low - 1], pdb->p_cat_val_to_name[cat->high - 1]);
-		}
-
-		if (cat->next != NULL) {
-			cil_printf(" ");
-		}
-	}
-
-	if (level->cat != NULL) {
-		cil_printf(")");
-	}
-
-	cil_printf(")");
-
-	return 0;
-}
-
-static int avrule_to_cil(int indent, struct policydb *pdb, uint32_t type, const char *src, const char *tgt, const struct class_perm_node *classperms)
-{
-	int rc = -1;
-	const char *rule;
-	const struct class_perm_node *classperm;
-	char *perms;
-
-	switch (type) {
-	case AVRULE_ALLOWED:
-		rule = "allow";
-		break;
-	case AVRULE_AUDITALLOW:
-		rule = "auditallow";
-		break;
-	case AVRULE_AUDITDENY:
-		rule = "auditdenty";
-		break;
-	case AVRULE_DONTAUDIT:
-		rule = "dontaudit";
-		break;
-	case AVRULE_NEVERALLOW:
-		rule = "neverallow";
-		break;
-	case AVRULE_TRANSITION:
-		rule = "typetransition";
-		break;
-	case AVRULE_MEMBER:
-		rule = "typemember";
-		break;
-	case AVRULE_CHANGE:
-		rule = "typechange";
-		break;
-	default:
-		log_err("Unknown avrule type: %i", type);
-		rc = -1;
-		goto exit;
-	}
-
-	for (classperm = classperms; classperm != NULL; classperm = classperm->next) {
-		if (type & AVRULE_AV) {
-			perms = sepol_av_to_string(pdb, classperm->tclass, classperm->data);
-			if (perms == NULL) {
-				log_err("Failed to generate permission string");
-				rc = -1;
-				goto exit;
-			}
-			cil_println(indent, "(%s %s %s (%s (%s)))",
-					rule, src, tgt,
-					pdb->p_class_val_to_name[classperm->tclass - 1],
-					perms + 1);
-		} else {
-			cil_println(indent, "(%s %s %s %s %s)",
-					rule, src, tgt,
-					pdb->p_class_val_to_name[classperm->tclass - 1],
-					pdb->p_type_val_to_name[classperm->data - 1]);
-		}
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int num_digits(int n)
-{
-	int num = 1;
-	while (n >= 10) {
-		n /= 10;
-		num++;
-	}
-	return num;
-}
-
-static int set_to_cil_attr(struct policydb *pdb, int is_type, char ***names, uint32_t *num_names)
-{
-	static unsigned int num_attrs = 0;
-	int rc = -1;
-	int len, rlen;
-	const char *attr_infix;
-	char *attr;
-
-	num_attrs++;
-
-	if (is_type) {
-		attr_infix = "_typeattr_";
-	} else {
-		attr_infix = "_roleattr_";
-	}
-
-	len = strlen(pdb->name) + strlen(attr_infix) + num_digits(num_attrs) + 1;
-	attr = malloc(len);
-	if (attr == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-	rlen = snprintf(attr, len, "%s%s%i", pdb->name, attr_infix, num_attrs);
-	if (rlen < 0 || rlen >= len) {
-		log_err("Failed to generate attribute name");
-		rc = -1;
-		goto exit;
-	}
-
-	*names = malloc(sizeof(**names));
-	if (*names == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-
-	*names[0] = attr;
-	*num_names = 1;
-
-	rc = 0;
-
-exit:
-	return rc;
-}
-
-static int cil_print_attr_strs(int indent, struct policydb *pdb, int is_type, struct ebitmap *pos, struct ebitmap *neg, uint32_t flags, char *attr)
-{
-	// CIL doesn't support anonymous positive/negative/complemented sets.  So
-	// instead we create a CIL type/roleattributeset that matches the set. If
-	// the set has a negative set, then convert it to is (P & !N), where P is
-	// the list of members in the positive set , and N is the list of members
-	// in the negative set. Additonally, if the set is complemented, then wrap
-	// the whole thing with a negation.
-
-	int rc = 0;
-	struct ebitmap_node *node;
-	unsigned int i;
-	char *statement;
-	int has_positive = pos && (ebitmap_cardinality(pos) > 0);
-	int has_negative = neg && (ebitmap_cardinality(neg) > 0);
-	char **val_to_name;
-
-	if (is_type) {
-		statement = "type";
-		val_to_name = pdb->p_type_val_to_name;
-	} else {
-		statement = "role";
-		val_to_name = pdb->p_role_val_to_name;
-	}
-
-	cil_println(indent, "(%sattribute %s)", statement, attr);
-	cil_indent(indent);
-	cil_printf("(%sattributeset %s ", statement, attr);
-
-	if (flags & TYPE_STAR) {
-		cil_printf("(all)");
-	}
-
-	if (flags & TYPE_COMP) {
-		cil_printf("(not ");
-	}
-
-	if (has_positive && has_negative) {
-		cil_printf("(and ");
-	}
-
-	if (has_positive) {
-		cil_printf("(");
-		ebitmap_for_each_bit(pos, node, i) {
-			if (!ebitmap_get_bit(pos, i)) {
-				continue;
-			}
-			cil_printf("%s ", val_to_name[i]);
-		}
-		cil_printf(") ");
-	}
-
-	if (has_negative) {
-		cil_printf("(not (");
-
-		ebitmap_for_each_bit(neg, node, i) {
-			if (!ebitmap_get_bit(neg, i)) {
-				continue;
-			}
-			cil_printf("%s ", val_to_name[i]);
-		}
-
-		cil_printf("))");
-	}
-
-	if (has_positive && has_negative) {
-		cil_printf(")");
-	}
-
-	if (flags & TYPE_COMP) {
-		cil_printf(")");
-	}
-
-	cil_printf(")\n");
-
-	return rc;
-}
-
-static int ebitmap_to_cil(struct policydb *pdb, struct ebitmap *map, int type)
-{
-	struct ebitmap_node *node;
-	uint32_t i;
-	char **val_to_name = pdb->sym_val_to_name[type];
-
-	ebitmap_for_each_bit(map, node, i) {
-		if (!ebitmap_get_bit(map, i)) {
-			continue;
-		}
-		cil_printf("%s ", val_to_name[i]);
-	}
-
-	return 0;
-}
-
-static int ebitmap_to_names(char** vals_to_names, struct ebitmap map, char ***names, uint32_t *num_names)
-{
-	int rc = -1;
-	struct ebitmap_node *node;
-	uint32_t i;
-	uint32_t num = 0;
-	uint32_t max = 8;
-	char **name_arr = NULL;
-
-	name_arr = malloc(sizeof(*name_arr) * max);
-	if (name_arr == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-	ebitmap_for_each_bit(&map, node, i) {
-		if (!ebitmap_get_bit(&map, i)) {
-			continue;
-		}
-
-		if (num + 1 == max) {
-			max *= 2;
-			name_arr = realloc(name_arr, sizeof(*name_arr) * max);
-			if (name_arr == NULL) {
-				log_err("Out of memory");
-				rc = -1;
-				goto exit;
-			}
-		}
-
-		name_arr[num] = strdup(vals_to_names[i]);
-		if (name_arr[num] == NULL) {
-			log_err("Out of memory");
-			rc = -1;
-			goto exit;
-		}
-		num++;
-	}
-
-	*names = name_arr;
-	*num_names = num;
-
-	return 0;
-
-exit:
-	for (i = 0; i < num; i++) {
-		free(name_arr[i]);
-	}
-	free(name_arr);
-	return rc;
-}
-
-static int cil_add_attr_to_list(struct list *attr_list, char *attribute, int is_type, void *set)
-{
-	struct attr_list_node *attr_list_node = NULL;
-	int rc = -1;
-
-	attr_list_node = calloc(1, sizeof(*attr_list_node));
-	if (attr_list_node == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-	rc = list_prepend(attr_list, attr_list_node);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	attr_list_node->attribute = strdup(attribute);
-	if (attr_list_node->attribute == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-	attr_list_node->is_type = is_type;
-	if (is_type) {
-		attr_list_node->set.ts = set;
-	} else {
-		attr_list_node->set.rs = set;
-	}
-
-	return rc;
-
-exit:
-	if (attr_list_node != NULL) {
-		free(attr_list_node->attribute);
-	}
-	free(attr_list_node);
-	return rc;
-}
-
-/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
-static int typeset_to_names(struct policydb *pdb, struct type_set *ts, char ***names, uint32_t *num_names, char **generated_attribute)
-{
-	int rc = -1;
-	if (ebitmap_cardinality(&ts->negset) > 0 || ts->flags != 0) {
-		rc = set_to_cil_attr(pdb, 1, names, num_names);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		*generated_attribute = *names[0];
-	} else {
-		rc = ebitmap_to_names(pdb->p_type_val_to_name, ts->types, names, num_names);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-/* generated_attribute is only set if a new attribute was generated in set_to_cil_attr */
-static int roleset_to_names(struct policydb *pdb, struct role_set *rs, char ***names, uint32_t *num_names, char **generated_attribute)
-{
-	int rc = -1;
-	if (rs->flags != 0) {
-		rc = set_to_cil_attr(pdb, 0, names, num_names);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		*generated_attribute = *names[0];
-	} else {
-		rc = ebitmap_to_names(pdb->p_role_val_to_name, rs->roles, names, num_names);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int process_roleset(int indent, struct policydb *pdb, struct role_set *rs, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
-{
-	int rc = -1;
-	char *generated_attribute = NULL;
-	*num_type_names = 0;
-
-	rc = roleset_to_names(pdb, rs, type_names, num_type_names, &generated_attribute);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	if (generated_attribute == NULL) {
-		goto exit;
-	}
-
-	if (attr_list == NULL) {
-		rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
-		if (rc != 0) {
-			goto exit;
-		}
-	} else {
-		rc = cil_add_attr_to_list(attr_list, generated_attribute, 0, rs);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-exit:
-	return rc;
-}
-
-static int process_typeset(int indent, struct policydb *pdb, struct type_set *ts, struct list *attr_list, char ***type_names, uint32_t *num_type_names)
-{
-	int rc = -1;
-	char *generated_attribute = NULL;
-	*num_type_names = 0;
-
-	rc = typeset_to_names(pdb, ts, type_names, num_type_names, &generated_attribute);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	if (generated_attribute == NULL) {
-		rc = 0;
-		goto exit;
-	}
-
-	if (attr_list == NULL) {
-		rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
-		if (rc != 0) {
-			goto exit;
-		}
-	} else {
-		rc = cil_add_attr_to_list(attr_list, generated_attribute, 1, ts);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-exit:
-	return rc;
-}
-
-static void names_destroy(char ***names, uint32_t *num_names)
-{
-	char **arr = *names;
-	uint32_t num = *num_names;
-	uint32_t i;
-
-	for (i = 0; i < num; i++) {
-		free(arr[i]);
-		arr[i] = NULL;
-	}
-	free(arr);
-
-	*names = NULL;
-	*num_names = 0;
-}
-
-static int roletype_role_in_ancestor_to_cil(struct policydb *pdb, struct stack *decl_stack, char *type_name, int indent)
-{
-	struct list_node *curr;
-	char **tnames = NULL;
-	uint32_t num_tnames, i;
-	struct role_list_node *role_node = NULL;
-	int rc;
-	struct type_set *ts;
-
-	curr = role_list->head;
-	for (curr = role_list->head; curr != NULL; curr = curr->next) {
-		role_node = curr->data;
-		if (!is_id_in_ancestor_scope(pdb, decl_stack, role_node->role_name, SYM_ROLES)) {
-			continue;
-		}
-
-		ts = &role_node->role->types;
-		rc = process_typeset(indent, pdb, ts, NULL, &tnames, &num_tnames);
-		if (rc != 0) {
-			goto exit;
-		}
-		for (i = 0; i < num_tnames; i++) {
-			if (!strcmp(type_name, tnames[i])) {
-				cil_println(indent, "(roletype %s %s)", role_node->role_name, type_name);
-			}
-		}
-		names_destroy(&tnames, &num_tnames);
-	}
-
-	rc = 0;
-
-exit:
-	return rc;
-}
-
-
-static int name_list_to_string(char **names, int num_names, char **string)
-{
-	// create a space separated string of the names
-	int rc = -1;
-	int len = 0;
-	int i;
-	char *str;
-	char *strpos;
-	int name_len;
-	int rlen;
-
-	for (i = 0; i < num_names; i++) {
-		len += strlen(names[i]);
-	}
-
-	// add spaces + null terminator
-	len += (num_names - 1) + 1;
-
-	str = malloc(len);
-	if (str == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-	strpos = str;
-
-	for (i = 0; i < num_names; i++) {
-		name_len = strlen(names[i]);
-		rlen = snprintf(strpos, len - (strpos - str), "%s", names[i]);
-		if (rlen < 0 || rlen >= len) {
-			log_err("Failed to generate name list");
-			rc = -1;
-			goto exit;
-		}
-
-		if (i < num_names - 1) {
-			strpos[name_len] = ' ';
-		}
-		strpos += name_len + 1;
-	}
-
-	*string = str;
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int avrule_list_to_cil(int indent, struct policydb *pdb, struct avrule *avrule_list, struct list *attr_list)
-{
-	int rc = -1;
-	struct avrule *avrule;
-	char **snames = NULL;
-	char **tnames = NULL;
-	uint32_t num_snames;
-	uint32_t num_tnames;
-	uint32_t s;
-	uint32_t t;
-	struct type_set *ts;
-
-	for (avrule = avrule_list; avrule != NULL; avrule = avrule->next) {
-		ts = &avrule->stypes;
-		rc = process_typeset(indent, pdb, ts, attr_list, &snames, &num_snames);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		ts = &avrule->ttypes;
-		rc = process_typeset(indent, pdb, ts, attr_list, &tnames, &num_tnames);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (s = 0; s < num_snames; s++) {
-			for (t = 0; t < num_tnames; t++) {
-				rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], tnames[t], avrule->perms);
-				if (rc != 0) {
-					goto exit;
-				}
-			}
-
-			if (avrule->flags & RULE_SELF) {
-				rc = avrule_to_cil(indent, pdb, avrule->specified, snames[s], "self", avrule->perms);
-				if (rc != 0) {
-					goto exit;
-				}
-			}
-		}
-
-		names_destroy(&snames, &num_snames);
-		names_destroy(&tnames, &num_tnames);
-	}
-
-	return 0;
-
-exit:
-	names_destroy(&snames, &num_snames);
-	names_destroy(&tnames, &num_tnames);
-
-	return rc;
-}
-
-static int cond_expr_to_cil(int indent, struct policydb *pdb, struct cond_expr *cond_expr, uint32_t flags)
-{
-	int rc = -1;
-	struct cond_expr *curr;
-	struct stack *stack = NULL;
-	int len = 0;
-	int rlen;
-	char *new_val = NULL;
-	char *val1 = NULL;
-	char *val2 = NULL;
-	int num_params;
-	const char *op;
-	const char *fmt_str;
-	const char *type;
-
-	rc = stack_init(&stack);
-	if (rc != 0) {
-		log_err("Out of memory");
-		goto exit;
-	}
-
-	for (curr = cond_expr; curr != NULL; curr = curr->next) {
-		if (curr->expr_type == COND_BOOL) {
-			val1 = pdb->p_bool_val_to_name[curr->bool - 1];
-			// length of boolean + 2 parens + null terminator
-			len = strlen(val1) + 2 + 1;
-			new_val = malloc(len);
-			if (new_val == NULL) {
-				log_err("Out of memory");
-				rc = -1;
-				goto exit;
-			}
-			rlen = snprintf(new_val, len, "(%s)", val1);
-			if (rlen < 0 || rlen >= len) {
-				log_err("Failed to generate conditional expression");
-				rc = -1;
-				goto exit;
-			}
-			num_params = 0;
-		} else {
-			switch(curr->expr_type) {
-			case COND_NOT:	op = "not";	break;
-			case COND_OR:	op = "or";	break;
-			case COND_AND:	op = "and";	break;
-			case COND_XOR:	op = "xor";	break;
-			case COND_EQ:	op = "eq";	break;
-			case COND_NEQ:	op = "neq";	break;
-			default:
-				rc = -1;
-				goto exit;
-			}
-
-			num_params = curr->expr_type == COND_NOT ? 1 : 2;
-
-			if (num_params == 1) {
-				val1 = stack_pop(stack);
-				val2 = strdup("");
-				if (val2 == NULL) {
-					log_err("Out of memory");
-					rc = -1;
-					goto exit;
-				}
-				fmt_str = "(%s %s)";
-			} else {
-				val2 = stack_pop(stack);
-				val1 = stack_pop(stack);
-				fmt_str = "(%s %s %s)";
-			}
-
-			if (val1 == NULL || val2 == NULL) {
-				log_err("Invalid conditional expression");
-				rc = -1;
-				goto exit;
-			}
-
-			// length = length of parameters +
-			//          length of operator +
-			//          1 space preceeding each parameter +
-			//          2 parens around the whole expression
-			//          + null terminator
-			len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
-			new_val = malloc(len);
-			if (new_val == NULL) {
-				log_err("Out of memory");
-				rc = -1;
-				goto exit;
-			}
-
-			// although we always supply val2 and there isn't always a 2nd
-			// value, it should only be used when there are actually two values
-			// in the format strings
-			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
-			if (rlen < 0 || rlen >= len) {
-				log_err("Failed to generate conditional expression");
-				rc = -1;
-				goto exit;
-			}
-
-			free(val1);
-			free(val2);
-			val1 = NULL;
-			val2 = NULL;
-		}
-
-		rc = stack_push(stack, new_val);
-		if (rc != 0) {
-			log_err("Out of memory");
-			goto exit;
-		}
-		new_val = NULL;
-	}
-
-	if (flags & COND_NODE_FLAGS_TUNABLE) {
-		type = "tunableif";
-	} else {
-		type = "booleanif";
-	}
-
-	val1 = stack_pop(stack);
-	if (val1 == NULL || stack_peek(stack) != NULL) {
-		log_err("Invalid conditional expression");
-		rc = -1;
-		goto exit;
-	}
-
-	cil_println(indent, "(%s %s", type, val1);
-	free(val1);
-	val1 = NULL;
-
-	rc = 0;
-
-exit:
-	free(new_val);
-	free(val1);
-	free(val2);
-	while ((val1 = stack_pop(stack)) != NULL) {
-		free(val1);
-	}
-	stack_destroy(&stack);
-
-	return rc;
-}
-
-static int cil_print_attr_list(int indent, struct policydb *pdb, struct list *attr_list)
-{
-	struct list_node *curr;
-	struct attr_list_node *attr_list_node;
-	int rc = 0;
-	struct type_set *ts;
-	struct role_set *rs;
-	char *generated_attribute;
-
-	for (curr = attr_list->head; curr != NULL; curr = curr->next) {
-		attr_list_node = curr->data;
-		generated_attribute = attr_list_node->attribute;
-		if (generated_attribute == NULL) {
-			return -1;
-		}
-
-		if (attr_list_node->is_type) {
-			ts = attr_list_node->set.ts;
-			rc = cil_print_attr_strs(indent, pdb, 1, &ts->types, &ts->negset, ts->flags, generated_attribute);
-			if (rc != 0) {
-				return rc;
-			}
-		} else {
-			rs = attr_list_node->set.rs;
-			rc = cil_print_attr_strs(indent, pdb, 0, &rs->roles, NULL, rs->flags, generated_attribute);
-			if (rc != 0) {
-				return rc;
-			}
-		}
-	}
-
-	return rc;
-}
-
-static int cond_list_to_cil(int indent, struct policydb *pdb, struct cond_node *cond_list)
-{
-	int rc = -1;
-	struct cond_node *cond;
-	struct list *attr_list;
-
-	rc = list_init(&attr_list);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	for (cond = cond_list; cond != NULL; cond = cond->next) {
-
-		rc = cond_expr_to_cil(indent, pdb, cond->expr, cond->flags);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		if (cond->avtrue_list != NULL) {
-			cil_println(indent + 1, "(true");
-			rc = avrule_list_to_cil(indent + 2, pdb, cond->avtrue_list, attr_list);
-			if (rc != 0) {
-				goto exit;
-			}
-			cil_println(indent + 1, ")");
-		}
-
-		if (cond->avfalse_list != NULL) {
-			cil_println(indent + 1, "(false");
-			rc = avrule_list_to_cil(indent + 2, pdb, cond->avfalse_list, attr_list);
-			if (rc != 0) {
-				goto exit;
-			}
-			cil_println(indent + 1, ")");
-		}
-
-		cil_println(indent, ")");
-	}
-
-	rc = cil_print_attr_list(indent, pdb, attr_list);
-
-exit:
-	attr_list_destroy(&attr_list);
-	return rc;
-}
-
-static int role_trans_to_cil(int indent, struct policydb *pdb, struct role_trans_rule *rules)
-{
-	int rc = -1;
-	struct role_trans_rule *rule;
-	char **role_names = NULL;
-	uint32_t num_role_names = 0;
-	char **type_names = NULL;
-	uint32_t num_type_names = 0;
-	uint32_t type;
-	uint32_t role;
-	uint32_t i;
-	struct ebitmap_node *node;
-	struct type_set *ts;
-	struct role_set *rs;
-
-
-	for (rule = rules; rule != NULL; rule = rule->next) {
-		rs = &rule->roles;
-		rc = process_roleset(indent, pdb, rs, NULL, &role_names, &num_role_names);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		ts = &rule->types;
-		rc = process_typeset(indent, pdb, ts, NULL, &type_names, &num_type_names);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (role = 0; role < num_role_names; role++) {
-			for (type = 0; type < num_type_names; type++) {
-				ebitmap_for_each_bit(&rule->classes, node, i) {
-					if (!ebitmap_get_bit(&rule->classes, i)) {
-						continue;
-					}
-					cil_println(indent, "(roletransition %s %s %s %s)", role_names[role],
-					                                                    type_names[type],
-					                                                    pdb->p_class_val_to_name[i],
-					                                                    pdb->p_role_val_to_name[rule->new_role - 1]);
-				}
-			}
-		}
-
-		names_destroy(&role_names, &num_role_names);
-		names_destroy(&type_names, &num_type_names);
-	}
-
-	rc = 0;
-
-exit:
-	names_destroy(&role_names, &num_role_names);
-	names_destroy(&type_names, &num_type_names);
-
-	return rc;
-}
-
-static int role_allows_to_cil(int indent, struct policydb *pdb, struct role_allow_rule *rules)
-{
-	int rc = -1;
-	struct role_allow_rule *rule;
-	char **roles = NULL;
-	uint32_t num_roles = 0;
-	char **new_roles = NULL;
-	uint32_t num_new_roles = 0;
-	uint32_t i;
-	uint32_t j;
-	struct role_set *rs;
-
-	for (rule = rules; rule != NULL; rule = rule->next) {
-		rs = &rule->roles;
-		rc = process_roleset(indent, pdb, rs, NULL, &roles, &num_roles);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rs = &rule->new_roles;
-		rc = process_roleset(indent, pdb, rs, NULL, &new_roles, &num_new_roles);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (i = 0; i < num_roles; i++) {
-			for (j = 0; j < num_new_roles; j++) {
-				cil_println(indent, "(roleallow %s %s)", roles[i], new_roles[j]);
-			}
-		}
-
-		names_destroy(&roles, &num_roles);
-		names_destroy(&new_roles, &num_new_roles);
-	}
-
-	rc = 0;
-
-exit:
-	names_destroy(&roles, &num_roles);
-	names_destroy(&new_roles, &num_new_roles);
-
-	return rc;
-}
-
-static int range_trans_to_cil(int indent, struct policydb *pdb, struct range_trans_rule *rules)
-{
-	int rc = -1;
-	struct range_trans_rule *rule;
-	char **stypes = NULL;
-	uint32_t num_stypes = 0;
-	char **ttypes = NULL;
-	uint32_t num_ttypes = 0;
-	struct ebitmap_node *node;
-	uint32_t i;
-	uint32_t stype;
-	uint32_t ttype;
-	struct type_set *ts;
-
-	if (!pdb->mls) {
-		return 0;
-	}
-
-	for (rule = rules; rule != NULL; rule = rule->next) {
-		ts = &rule->stypes;
-		rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		ts = &rule->ttypes;
-		rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (stype = 0; stype < num_stypes; stype++) {
-			for (ttype = 0; ttype < num_ttypes; ttype++) {
-				ebitmap_for_each_bit(&rule->tclasses, node, i) {
-					if (!ebitmap_get_bit(&rule->tclasses, i)) {
-						continue;
-					}
-
-					cil_indent(indent);
-					cil_printf("(rangetransition %s %s %s ", stypes[stype], ttypes[ttype], pdb->p_class_val_to_name[i]);
-
-					cil_printf("(");
-
-					rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[0]);
-					if (rc != 0) {
-						goto exit;
-					}
-
-					cil_printf(" ");
-
-					rc = semantic_level_to_cil(pdb, 1, &rule->trange.level[1]);
-					if (rc != 0) {
-						goto exit;
-					}
-
-					cil_printf("))\n");
-				}
-
-			}
-		}
-
-		names_destroy(&stypes, &num_stypes);
-		names_destroy(&ttypes, &num_ttypes);
-	}
-
-	rc = 0;
-
-exit:
-	names_destroy(&stypes, &num_stypes);
-	names_destroy(&ttypes, &num_ttypes);
-
-	return rc;
-}
-
-static int filename_trans_to_cil(int indent, struct policydb *pdb, struct filename_trans_rule *rules)
-{
-	int rc = -1;
-	char **stypes = NULL;
-	uint32_t num_stypes = 0;
-	char **ttypes = NULL;
-	uint32_t num_ttypes = 0;
-	uint32_t stype;
-	uint32_t ttype;
-	struct type_set *ts;
-
-	struct filename_trans_rule *rule;
-
-	for (rule = rules; rule != NULL; rule = rule->next) {
-		ts = &rule->stypes;
-		rc = process_typeset(indent, pdb, ts, NULL, &stypes, &num_stypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		ts = &rule->ttypes;
-		rc = process_typeset(indent, pdb, ts, NULL, &ttypes, &num_ttypes);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (stype = 0; stype < num_stypes; stype++) {
-			for (ttype = 0; ttype < num_ttypes; ttype++) {
-				cil_println(indent, "(typetransition %s %s %s \"%s\" %s)", stypes[stype],
-				                                                       ttypes[ttype],
-				                                                       pdb->p_class_val_to_name[rule->tclass - 1],
-				                                                       rule->name,
-				                                                       pdb->p_type_val_to_name[rule->otype - 1]);
-			}
-		}
-
-		names_destroy(&stypes, &num_stypes);
-		names_destroy(&ttypes, &num_ttypes);
-	}
-
-	rc = 0;
-exit:
-	names_destroy(&stypes, &num_stypes);
-	names_destroy(&ttypes, &num_ttypes);
-
-	return rc;
-}
-
-struct class_perm_datum {
-	char *name;
-	uint32_t val;
-};
-
-struct class_perm_array {
-	struct class_perm_datum *perms;
-	uint32_t count;
-};
-
-static int class_perm_to_array(char *key, void *data, void *args)
-{
-	struct class_perm_array *arr = args;
-	struct perm_datum *datum = data;
-	arr->perms[arr->count].name = key;
-	arr->perms[arr->count].val = datum->s.value;
-	arr->count++;
-
-	return 0;
-}
-
-static int class_perm_cmp(const void *a, const void *b)
-{
-	const struct class_perm_datum *aa = a;
-	const struct class_perm_datum *bb = b;
-
-	return aa->val - bb->val;
-}
-
-static int common_to_cil(char *key, void *data, void *UNUSED(arg))
-{
-	int rc = -1;
-	struct common_datum *common = data;
-	struct class_perm_array arr;
-	uint32_t i;
-
-	arr.count = 0;
-	arr.perms = calloc(common->permissions.nprim, sizeof(*arr.perms));
-	rc = hashtab_map(common->permissions.table, class_perm_to_array, &arr);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
-
-	cil_printf("(common %s (", key);
-	for (i = 0; i < arr.count; i++) {
-		cil_printf("%s ", arr.perms[i].name);
-	}
-	cil_printf("))\n");
-
-	rc = 0;
-
-exit:
-	free(arr.perms);
-	return rc;
-}
-
-
-static int constraint_expr_to_string(int indent, struct policydb *pdb, struct constraint_expr *exprs, char **expr_string)
-{
-	int rc = -1;
-	struct constraint_expr *expr;
-	struct stack *stack = NULL;
-	int len = 0;
-	int rlen;
-	char *new_val = NULL;
-	char *val1 = NULL;
-	char *val2 = NULL;
-	uint32_t num_params;
-	const char *op;
-	const char *fmt_str;
-	const char *attr1;
-	const char *attr2;
-	char *names;
-	char **name_list = NULL;
-	uint32_t num_names = 0;
-	struct type_set *ts;
-
-	rc = stack_init(&stack);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	for (expr = exprs; expr != NULL; expr = expr->next) {
-		if (expr->expr_type == CEXPR_ATTR || expr->expr_type == CEXPR_NAMES) {
-			switch (expr->op) {
-			case CEXPR_EQ:      op = "eq";     break;
-			case CEXPR_NEQ:     op = "neq";    break;
-			case CEXPR_DOM:     op = "dom";    break;
-			case CEXPR_DOMBY:   op = "domby";  break;
-			case CEXPR_INCOMP:  op = "incomp"; break;
-			default:
-				log_err("Unknown constraint operator type: %i", expr->op);
-				rc = -1;
-				goto exit;
-			}
-
-			switch (expr->attr) {
-			case CEXPR_USER:                 attr1 = "u1"; attr2 = "u2"; break;
-			case CEXPR_USER | CEXPR_TARGET:  attr1 = "u2"; attr2 = "";   break;
-			case CEXPR_USER | CEXPR_XTARGET: attr1 = "u3"; attr2 = "";   break;
-			case CEXPR_ROLE:                 attr1 = "r1"; attr2 = "r2"; break;
-			case CEXPR_ROLE | CEXPR_TARGET:  attr1 = "r2"; attr2 = "";   break;
-			case CEXPR_ROLE | CEXPR_XTARGET: attr1 = "r3"; attr2 = "";   break;
-			case CEXPR_TYPE:                 attr1 = "t1"; attr2 = "";   break;
-			case CEXPR_TYPE | CEXPR_TARGET:  attr1 = "t2"; attr2 = "";   break;
-			case CEXPR_TYPE | CEXPR_XTARGET: attr1 = "t3"; attr2 = "";   break;
-			case CEXPR_L1L2:                 attr1 = "l1"; attr2 = "l2"; break;
-			case CEXPR_L1H2:                 attr1 = "l1"; attr2 = "h2"; break;
-			case CEXPR_H1L2:                 attr1 = "h1"; attr2 = "l2"; break;
-			case CEXPR_H1H2:                 attr1 = "h1"; attr2 = "h2"; break;
-			case CEXPR_L1H1:                 attr1 = "l1"; attr2 = "h1"; break;
-			case CEXPR_L2H2:                 attr1 = "l2"; attr2 = "h2"; break;
-			default:
-				log_err("Unknown expression attribute type: %i", expr->attr);
-				rc = -1;
-				goto exit;
-			}
-
-			if (expr->expr_type == CEXPR_ATTR) {
-				// length of values/attrs + 2 separating spaces + 2 parens + null terminator
-				len = strlen(op) + strlen(attr1) + strlen(attr2) + 2 + 2 + 1;
-				new_val = malloc(len);
-				if (new_val == NULL) {
-					log_err("Out of memory");
-					rc = -1;
-					goto exit;
-				}
-				rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, attr2);
-				if (rlen < 0 || rlen >= len) {
-					log_err("Failed to generate constraint expression");
-					rc = -1;
-					goto exit;
-				}
-			} else {
-				if (expr->attr & CEXPR_TYPE) {
-					ts = expr->type_names;
-					rc = process_typeset(indent, pdb, ts, NULL, &name_list, &num_names);
-					if (rc != 0) {
-						goto exit;
-					}
-				} else if (expr->attr & CEXPR_USER) {
-					rc = ebitmap_to_names(pdb->p_user_val_to_name, expr->names, &name_list, &num_names);
-					if (rc != 0) {
-						goto exit;
-					}
-				} else if (expr->attr & CEXPR_ROLE) {
-					rc = ebitmap_to_names(pdb->p_role_val_to_name, expr->names, &name_list, &num_names);
-					if (rc != 0) {
-						goto exit;
-					}
-				}
-				rc = name_list_to_string(name_list, num_names, &names);
-				if (rc != 0) {
-					goto exit;
-				}
-
-				// length of values/oper + 2 spaces + 2 parens + null terminator
-				len = strlen(op) + strlen(attr1) +  strlen(names) + 2 + 2 + 1;
-				new_val = malloc(len);
-				if (new_val == NULL) {
-					log_err("Out of memory");
-					rc = -1;
-					goto exit;
-				}
-				rlen = snprintf(new_val, len, "(%s %s %s)", op, attr1, names);
-				if (rlen < 0 || rlen >= len) {
-					log_err("Failed to generate constraint expression");
-					rc = -1;
-					goto exit;
-				}
-
-				names_destroy(&name_list, &num_names);
-				free(names);
-			}
-
-			num_params = 0;
-		} else {
-			switch (expr->expr_type) {
-			case CEXPR_NOT: op = "not"; break;
-			case CEXPR_AND: op = "and"; break;
-			case CEXPR_OR:  op = "or"; break;
-			default:
-				log_err("Unknown constraint expression type: %i", expr->expr_type);
-				rc = -1;
-				goto exit;
-			}
-
-			num_params = expr->expr_type == CEXPR_NOT ? 1 : 2;
-
-			if (num_params == 1) {
-				val1 = stack_pop(stack);
-				val2 = strdup("");
-				if (val2 == NULL) {
-					log_err("Out of memory");
-					rc = -1;
-					goto exit;
-				}
-				fmt_str = "(%s %s)";
-			} else {
-				val2 = stack_pop(stack);
-				val1 = stack_pop(stack);
-				fmt_str = "(%s %s %s)";
-			}
-
-			if (val1 == NULL || val2 == NULL) {
-				log_err("Invalid constraint expression");
-				rc = -1;
-				goto exit;
-			}
-
-			// length = length of parameters +
-			//          length of operator +
-			//          1 space preceeding each parameter +
-			//          2 parens around the whole expression
-			//          + null terminator
-			len = strlen(val1) + strlen(val2) + strlen(op) + (num_params * 1) + 2 + 1;
-			new_val = malloc(len);
-			if (new_val == NULL) {
-				log_err("Out of memory");
-				rc = -1;
-				goto exit;
-			}
-
-			// although we always supply val2 and there isn't always a 2nd
-			// value, it should only be used when there are actually two values
-			// in the format strings
-			rlen = snprintf(new_val, len, fmt_str, op, val1, val2);
-			if (rlen < 0 || rlen >= len) {
-				log_err("Failed to generate constraint expression");
-				rc = -1;
-				goto exit;
-			}
-
-			free(val1);
-			free(val2);
-			val1 = NULL;
-			val2 = NULL;
-		}
-
-		rc = stack_push(stack, new_val);
-		if (rc != 0) {
-			log_err("Out of memory");
-			goto exit;
-		}
-
-		new_val = NULL;
-	}
-
-	new_val = stack_pop(stack);
-	if (new_val == NULL || stack_peek(stack) != NULL) {
-		log_err("Invalid constraint expression");
-		rc = -1;
-		goto exit;
-	}
-
-	*expr_string = new_val;
-	new_val = NULL;
-
-	rc = 0;
-
-exit:
-	names_destroy(&name_list, &num_names);
-
-	free(new_val);
-	free(val1);
-	free(val2);
-	while ((val1 = stack_pop(stack)) != NULL) {
-		free(val1);
-	}
-	stack_destroy(&stack);
-
-	return rc;
-}
-
-
-static int constraints_to_cil(int indent, struct policydb *pdb, char *classkey, struct class_datum *class, struct constraint_node *constraints, int is_constraint)
-{
-	int rc = -1;
-	struct constraint_node *node;
-	char *expr = NULL;
-	const char *mls;
-	char *perms;
-
-	mls = pdb->mls ? "mls" : "";
-
-	for (node = constraints; node != NULL; node = node->next) {
-
-		rc = constraint_expr_to_string(indent, pdb, node->expr, &expr);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		if (is_constraint) {
-			perms = sepol_av_to_string(pdb, class->s.value, node->permissions);
-			cil_println(indent, "(%sconstrain (%s (%s)) %s)", mls, classkey, perms + 1, expr);
-		} else {
-			cil_println(indent, "(%svalidatetrans %s %s)", mls, classkey, expr);
-		}
-
-		free(expr);
-		expr = NULL;
-	}
-
-	rc = 0;
-
-exit:
-	free(expr);
-	return rc;
-}
-
-static int class_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
-{
-	int rc = -1;
-	struct class_datum *class = datum;
-	const char *dflt;
-	struct class_perm_array arr;
-	uint32_t i;
-
-	if (scope == SCOPE_REQ) {
-		return 0;
-	}
-
-	arr.count = 0;
-	arr.perms = calloc(class->permissions.nprim, sizeof(*arr.perms));
-	rc = hashtab_map(class->permissions.table, class_perm_to_array, &arr);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	qsort(arr.perms, arr.count, sizeof(*arr.perms), class_perm_cmp);
-
-	cil_indent(indent);
-	cil_printf("(class %s (", key);
-	for (i = 0; i < arr.count; i++) {
-		cil_printf("%s ", arr.perms[i].name);
-	}
-	cil_printf("))\n");
-
-	if (class->comkey != NULL) {
-		cil_println(indent, "(classcommon %s %s)", key, class->comkey);
-	}
-
-	if (class->default_user != 0) {
-		switch (class->default_user) {
-		case DEFAULT_SOURCE:	dflt = "source";	break;
-		case DEFAULT_TARGET:	dflt = "target";	break;
-		default:
-			log_err("Unknown default user value: %i", class->default_user);
-			rc = -1;
-			goto exit;
-		}
-		cil_println(indent, "(defaultuser %s %s)", key, dflt);
-	}
-
-	if (class->default_role != 0) {
-		switch (class->default_role) {
-		case DEFAULT_SOURCE:	dflt = "source";	break;
-		case DEFAULT_TARGET:	dflt = "target";	break;
-		default:
-			log_err("Unknown default role value: %i", class->default_role);
-			rc = -1;
-			goto exit;
-		}
-		cil_println(indent, "(defaultrole %s %s)", key, dflt);
-	}
-
-	if (class->default_type != 0) {
-		switch (class->default_type) {
-		case DEFAULT_SOURCE:	dflt = "source";	break;
-		case DEFAULT_TARGET:	dflt = "target";	break;
-		default:
-			log_err("Unknown default type value: %i", class->default_type);
-			rc = -1;
-			goto exit;
-		}
-		cil_println(indent, "(defaulttype %s %s)", key, dflt);
-	}
-
-	if (class->default_range != 0) {
-		switch (class->default_range) {
-		case DEFAULT_SOURCE_LOW:		dflt = "source low";	break;
-		case DEFAULT_SOURCE_HIGH:		dflt = "source high";	break;
-		case DEFAULT_SOURCE_LOW_HIGH:	dflt = "source low-high";	break;
-		case DEFAULT_TARGET_LOW:		dflt = "target low";	break;
-		case DEFAULT_TARGET_HIGH:		dflt = "target high";	break;
-		case DEFAULT_TARGET_LOW_HIGH:	dflt = "target low-high";	break;
-		default:
-			log_err("Unknown default range value: %i", class->default_range);
-			rc = -1;
-			goto exit;
-		}
-		cil_println(indent, "(defaultrange %s %s)", key, dflt);
-
-	}
-
-	if (class->constraints != NULL) {
-		rc = constraints_to_cil(indent, pdb, key, class, class->constraints, 1);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	if (class->validatetrans != NULL) {
-		rc = constraints_to_cil(indent, pdb, key, class, class->validatetrans, 0);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	rc = 0;
-
-exit:
-	free(arr.perms);
-	return rc;
-}
-
-static int class_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
-{
-	struct ebitmap_node *node;
-	uint32_t i;
-
-	if (ebitmap_cardinality(&order) == 0) {
-		return 0;
-	}
-
-	cil_indent(indent);
-	cil_printf("(classorder (");
-
-	ebitmap_for_each_bit(&order, node, i) {
-		if (!ebitmap_get_bit(&order, i)) {
-			continue;
-		}
-		cil_printf("%s ", pdb->sym_val_to_name[SYM_CLASSES][i]);
-	}
-
-	cil_printf("))\n");
-
-	return 0;
-}
-
-static int role_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
-{
-	int rc = -1;
-	struct ebitmap_node *node;
-	uint32_t i;
-	char **types = NULL;
-	uint32_t num_types = 0;
-	struct role_datum *role = datum;
-	struct type_set *ts;
-
-	if (scope == SCOPE_REQ) {
-		// if a role/roleattr is in the REQ scope, then it could cause an
-		// optional block to fail, even if it is never used. However in CIL,
-		// symbols must be used in order to cause an optional block to fail. So
-		// for symbols in the REQ scope, add them to a roleattribute as a way
-		// to 'use' them in the optional without affecting the resulting policy.
-		cil_println(indent, "(roleattributeset " GEN_REQUIRE_ATTR " %s)", key);
-	}
-
-	switch (role->flavor) {
-	case ROLE_ROLE:
-		if (scope == SCOPE_DECL) {
-			// Only declare certain roles if we are reading a base module.
-			// These roles are defined in the base module and sometimes in
-			// other non-base modules. If we generated the roles regardless of
-			// the policy type, it would result in duplicate declarations,
-			// which isn't allowed in CIL. Patches have been made to refpolicy
-			// to remove these duplicate role declarations, but we need to be
-			// backwards compatable and support older policies. Since we know
-			// these roles are always declared in base, only print them when we
-			// see them in the base module. If the declarations appear in a
-			// non-base module, ignore their declarations.
-			//
-			// Note that this is a hack, and if a policy author does not define
-			// one of these roles in base, the declaration will not appeaer in
-			// the resulting policy, likely resulting in a compilation error in
-			// CIL.
-			int is_base_role = (!strcmp(key, "user_r") ||
-			                    !strcmp(key, "staff_r") ||
-			                    !strcmp(key, "sysadm_r") ||
-			                    !strcmp(key, "system_r") ||
-			                    !strcmp(key, "unconfined_r"));
-			if ((is_base_role && pdb->policy_type == SEPOL_POLICY_BASE) || !is_base_role) {
-				cil_println(indent, "(role %s)", key);
-			}
-		}
-
-		if (ebitmap_cardinality(&role->dominates) > 1) {
-			log_err("Warning: role 'dominance' statement unsupported in CIL. Dropping from output.");
-		}
-
-		ts = &role->types;
-		rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		for (i = 0; i < num_types; i++) {
-			if (is_id_in_scope(pdb, decl_stack, types[i], SYM_TYPES)) {
-				cil_println(indent, "(roletype %s %s)", key, types[i]);
-			}
-		}
-
-		if (role->bounds > 0) {
-			cil_println(indent, "(rolebounds %s %s)", key, pdb->p_role_val_to_name[role->bounds - 1]);
-		}
-		break;
-
-	case ROLE_ATTRIB:
-		if (scope == SCOPE_DECL) {
-			cil_println(indent, "(roleattribute %s)", key);
-		}
-
-		if (ebitmap_cardinality(&role->roles) > 0) {
-			cil_indent(indent);
-			cil_printf("(roleattributeset %s (", key);
-			ebitmap_for_each_bit(&role->roles, node, i) {
-				if (!ebitmap_get_bit(&role->roles, i)) {
-					continue;
-				}
-				cil_printf("%s ", pdb->p_role_val_to_name[i]);
-			}
-			cil_printf("))\n");
-		}
-
-		ts = &role->types;
-		rc = process_typeset(indent, pdb, ts, NULL, &types, &num_types);
-		if (rc != 0) {
-			goto exit;
-		}
-
-
-		for (i = 0; i < num_types; i++) {
-			cil_println(indent, "(roletype %s %s)", key, types[i]);
-		}
-
-		break;
-
-	default:
-		log_err("Unknown role type: %i", role->flavor);
-		rc = -1;
-		goto exit;
-	}
-
-	rc = 0;
-exit:
-	names_destroy(&types, &num_types);
-
-	return rc;
-}
-
-static int type_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack, char *key, void *datum, int scope)
-{
-	int rc = -1;
-	struct type_datum *type = datum;
-
-	if (scope == SCOPE_REQ) {
-		// if a type/typeattr is in the REQ scope, then it could cause an
-		// optional block to fail, even if it is never used. However in CIL,
-		// symbols must be used in order to cause an optional block to fail. So
-		// for symbols in the REQ scope, add them to a typeattribute as a way
-		// to 'use' them in the optional without affecting the resulting policy.
-		cil_println(indent, "(typeattributeset " GEN_REQUIRE_ATTR " %s)", key);
-	}
-
-	rc = roletype_role_in_ancestor_to_cil(pdb, decl_stack, key, indent);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	switch(type->flavor) {
-	case TYPE_TYPE:
-		if (scope == SCOPE_DECL) {
-			cil_println(indent, "(type %s)", key);
-			// object_r is implicit in checkmodule, but not with CIL,
-			// create it as part of base
-			cil_println(indent, "(roletype " DEFAULT_OBJECT " %s)", key);
-		}
-
-		if (type->flags & TYPE_FLAGS_PERMISSIVE) {
-			cil_println(indent, "(typepermissive %s)", key);
-		}
-
-		if (type->bounds > 0) {
-			cil_println(indent, "(typebounds %s %s)", pdb->p_type_val_to_name[type->bounds - 1], key);
-		}
-		break;
-	case TYPE_ATTRIB:
-		if (scope == SCOPE_DECL) {
-			cil_println(indent, "(typeattribute %s)", key);
-		}
-
-		if (ebitmap_cardinality(&type->types) > 0) {
-			cil_indent(indent);
-			cil_printf("(typeattributeset %s (", key);
-			ebitmap_to_cil(pdb, &type->types, SYM_TYPES);
-			cil_printf("))\n");
-		}
-		break;
-	default:
-		log_err("Unknown flavor (%i) of type %s", type->flavor, key);
-		rc = -1;
-		goto exit;
-	}
-
-	rc = 0;
-
-exit:
-	return rc;
-}
-
-static int user_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
-{
-	struct user_datum *user = datum;
-	struct ebitmap roles = user->roles.roles;
-	struct mls_semantic_level level = user->dfltlevel;
-	struct mls_semantic_range range = user->range;
-	struct ebitmap_node *node;
-	uint32_t i;
-	int sens_offset = 1;
-
-	if (scope == SCOPE_DECL) {
-		cil_println(indent, "(user %s)", key);
-		// object_r is implicit in checkmodule, but not with CIL, create it
-		// as part of base
-		cil_println(indent, "(userrole %s " DEFAULT_OBJECT ")", key);
-	}
-
-	ebitmap_for_each_bit(&roles, node, i) {
-		if (!ebitmap_get_bit(&roles, i)) {
-			continue;
-		}
-		cil_println(indent, "(userrole %s %s)", key, pdb->p_role_val_to_name[i]);
-	}
-
-	if (block->flags & AVRULE_OPTIONAL) {
-		// sensitivites in user statements in optionals do not have the
-		// standard -1 offest
-		sens_offset = 0;
-	}
-
-	cil_indent(indent);
-	cil_printf("(userlevel %s ", key);
-	if (pdb->mls) {
-		semantic_level_to_cil(pdb, sens_offset, &level);
-	} else {
-		cil_printf(DEFAULT_LEVEL);
-	}
-	cil_printf(")\n");
-
-	cil_indent(indent);
-	cil_printf("(userrange %s (", key);
-	if (pdb->mls) {
-		semantic_level_to_cil(pdb, sens_offset, &range.level[0]);
-		cil_printf(" ");
-		semantic_level_to_cil(pdb, sens_offset, &range.level[1]);
-	} else {
-		cil_printf(DEFAULT_LEVEL " " DEFAULT_LEVEL);
-	}
-	cil_printf("))\n");
-
-
-	return 0;
-}
-
-static int boolean_to_cil(int indent, struct policydb *UNUSED(pdb), struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
-{
-	struct cond_bool_datum *boolean = datum;
-	const char *type;
-
-	if (scope == SCOPE_DECL) {
-		if (boolean->flags & COND_BOOL_FLAGS_TUNABLE) {
-			type = "tunable";
-		} else {
-			type = "boolean";
-		}
-
-		cil_println(indent, "(%s %s %s)", type, key, boolean->state ? "true" : "false");
-	}
-
-	return 0;
-}
-
-static int sens_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum, int scope)
-{
-	struct level_datum *level = datum;
-
-	if (scope == SCOPE_DECL) {
-		if (!level->isalias) {
-			cil_println(indent, "(sensitivity %s)", key);
-		} else {
-			cil_println(indent, "(sensitivityalias %s)", key);
-			cil_println(indent, "(sensitivityaliasactual %s %s)", key, pdb->p_sens_val_to_name[level->level->sens - 1]);
-		}
-	}
-
-	if (ebitmap_cardinality(&level->level->cat) > 0) {
-		cil_indent(indent);
-		cil_printf("(sensitivitycategory %s (", key);
-		ebitmap_to_cil(pdb, &level->level->cat, SYM_CATS);
-		cil_printf("))\n");
-	}
-
-	return 0;
-}
-
-static int sens_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
-{
-	struct ebitmap_node *node;
-	uint32_t i;
-
-	if (ebitmap_cardinality(&order) == 0) {
-		return 0;
-	}
-
-	cil_indent(indent);
-	cil_printf("(sensitivityorder (");
-
-	ebitmap_for_each_bit(&order, node, i) {
-		if (!ebitmap_get_bit(&order, i)) {
-			continue;
-		}
-		cil_printf("%s ", pdb->p_sens_val_to_name[i]);
-	}
-
-	cil_printf("))\n");
-
-	return 0;
-}
-
-static int cat_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *UNUSED(decl_stack), char *key, void *datum,  int scope)
-{
-	struct cat_datum *cat = datum;
-
-	if (scope == SCOPE_REQ) {
-		return 0;
-	}
-
-	if (!cat->isalias) {
-		cil_println(indent, "(category %s)", key);
-	} else {
-		cil_println(indent, "(categoryalias %s)", key);
-		cil_println(indent, "(categoryaliasactual %s %s)", key, pdb->p_cat_val_to_name[cat->s.value - 1]);
-	}
-
-	return 0;
-}
-
-static int cat_order_to_cil(int indent, struct policydb *pdb, struct ebitmap order)
-{
-	int rc = -1;
-	struct ebitmap_node *node;
-	uint32_t i;
-
-	if (ebitmap_cardinality(&order) == 0) {
-		rc = 0;
-		goto exit;
-	}
-
-	cil_indent(indent);
-	cil_printf("(categoryorder (");
-
-	ebitmap_for_each_bit(&order, node, i) {
-		if (!ebitmap_get_bit(&order, i)) {
-			continue;
-		}
-		cil_printf("%s ", pdb->p_cat_val_to_name[i]);
-	}
-
-	cil_printf("))\n");
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int polcaps_to_cil(struct policydb *pdb)
-{
-	int rc = -1;
-	struct ebitmap *map;
-	struct ebitmap_node *node;
-	uint32_t i;
-	const char *name;
-
-	map = &pdb->policycaps;
-
-	ebitmap_for_each_bit(map, node, i) {
-		if (!ebitmap_get_bit(map, i)) {
-			continue;
-		}
-		name = sepol_polcap_getname(i);
-		if (name == NULL) {
-			log_err("Unknown policy capability id: %i", i);
-			rc = -1;
-			goto exit;
-		}
-
-		cil_println(0, "(policycap %s)", name);
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int level_to_cil(struct policydb *pdb, struct mls_level *level)
-{
-	struct ebitmap *map = &level->cat;
-
-	cil_printf("(%s", pdb->p_sens_val_to_name[level->sens - 1]);
-
-	if (ebitmap_cardinality(map) > 0) {
-		cil_printf("(");
-		ebitmap_to_cil(pdb, map, SYM_CATS);
-		cil_printf(")");
-	}
-
-	cil_printf(")");
-
-	return 0;
-}
-
-static int context_to_cil(struct policydb *pdb, struct context_struct *con)
-{
-	cil_printf("(%s %s %s (",
-		pdb->p_user_val_to_name[con->user - 1],
-		pdb->p_role_val_to_name[con->role - 1],
-		pdb->p_type_val_to_name[con->type - 1]);
-
-	if (pdb->mls) {
-		level_to_cil(pdb, &con->range.level[0]);
-		cil_printf(" ");
-		level_to_cil(pdb, &con->range.level[1]);
-	} else {
-		cil_printf(DEFAULT_LEVEL);
-		cil_printf(" ");
-		cil_printf(DEFAULT_LEVEL);
-	}
-
-	cil_printf("))");
-
-	return 0;
-}
-
-static int ocontext_isid_to_cil(struct policydb *pdb, const char **sid_to_string, struct ocontext *isids)
-{
-	int rc = -1;
-
-	struct ocontext *isid;
-
-	struct sid_item {
-		const char *sid_key;
-		struct sid_item *next;
-	};
-
-	struct sid_item *head = NULL;
-	struct sid_item *item = NULL;
-
-	for (isid = isids; isid != NULL; isid = isid->next) {
-		cil_println(0, "(sid %s)", sid_to_string[isid->sid[0]]);
-		cil_printf("(sidcontext %s ", sid_to_string[isid->sid[0]]);
-		context_to_cil(pdb, &isid->context[0]);
-		cil_printf(")\n");
-
-		// get the sid names in the correct order (reverse from the isids
-		// ocontext) for sidorder statement
-		item = malloc(sizeof(*item));
-		if (item == NULL) {
-			log_err("Out of memory");
-			rc = -1;
-			goto exit;
-		}
-		item->sid_key = sid_to_string[isid->sid[0]];
-		item->next = head;
-		head = item;
-	}
-
-	if (head != NULL) {
-		cil_printf("(sidorder (");
-		for (item = head; item != NULL; item = item->next) {
-			cil_printf("%s ", item->sid_key);
-		}
-		cil_printf("))\n");
-	}
-
-	rc = 0;
-
-exit:
-	while(head) {
-		item = head;
-		head = item->next;
-		free(item);
-	}
-	return rc;
-}
-
-static int ocontext_selinux_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
-{
-	int rc = -1;
-
-	// initial sid names aren't actually stored in the pp files, need to a have
-	// a mapping, taken from the linux kernel
-	static const char *selinux_sid_to_string[] = {
-		"null",
-		"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",
-		NULL
-	};
-
-	rc = ocontext_isid_to_cil(pdb, selinux_sid_to_string, isids);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int ocontext_selinux_fs_to_cil(struct policydb *UNUSED(pdb), struct ocontext *fss)
-{
-	if (fss != NULL) {
-		log_err("Warning: 'fscon' statement unsupported in CIL. Dropping from output.");
-	}
-
-	return 0;
-}
-
-static int ocontext_selinux_port_to_cil(struct policydb *pdb, struct ocontext *portcons)
-{
-	int rc = -1;
-	struct ocontext *portcon;
-	const char *protocol;
-	uint16_t high;
-	uint16_t low;
-
-	for (portcon = portcons; portcon != NULL; portcon = portcon->next) {
-
-		switch (portcon->u.port.protocol) {
-		case IPPROTO_TCP: protocol = "tcp"; break;
-		case IPPROTO_UDP: protocol = "udp"; break;
-		default:
-			log_err("Unknown portcon protocol: %i", portcon->u.port.protocol);
-			rc = -1;
-			goto exit;
-		}
-
-		low = portcon->u.port.low_port;
-		high = portcon->u.port.high_port;
-
-		if (low == high) {
-			cil_printf("(portcon %s %i ", protocol, low);
-		} else {
-			cil_printf("(portcon %s (%i %i) ", protocol, low, high);
-		}
-
-		context_to_cil(pdb, &portcon->context[0]);
-
-		cil_printf(")\n");
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int ocontext_selinux_netif_to_cil(struct policydb *pdb, struct ocontext *netifs)
-{
-	struct ocontext *netif;
-
-	for (netif = netifs; netif != NULL; netif = netif->next) {
-		cil_printf("(netifcon %s ", netif->u.name);
-		context_to_cil(pdb, &netif->context[0]);
-
-		cil_printf(" ");
-		context_to_cil(pdb, &netif->context[1]);
-		cil_printf(")\n");
-	}
-
-	return 0;
-}
-
-static int ocontext_selinux_node_to_cil(struct policydb *pdb, struct ocontext *nodes)
-{
-	int rc = -1;
-	struct ocontext *node;
-	char addr[INET_ADDRSTRLEN];
-	char mask[INET_ADDRSTRLEN];
-
-	for (node = nodes; node != NULL; node = node->next) {
-		if (inet_ntop(AF_INET, &node->u.node.addr, addr, INET_ADDRSTRLEN) == NULL) {
-			log_err("Nodecon address is invalid: %s", strerror(errno));
-			rc = -1;
-			goto exit;
-		}
-
-		if (inet_ntop(AF_INET, &node->u.node.mask, mask, INET_ADDRSTRLEN) == NULL) {
-			log_err("Nodecon mask is invalid: %s", strerror(errno));
-			rc = -1;
-			goto exit;
-		}
-
-		cil_printf("(nodecon %s %s ", addr, mask);
-
-		context_to_cil(pdb, &node->context[0]);
-
-		cil_printf(")\n");
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int ocontext_selinux_node6_to_cil(struct policydb *pdb, struct ocontext *nodes)
-{
-	int rc = -1;
-	struct ocontext *node;
-	char addr[INET6_ADDRSTRLEN];
-	char mask[INET6_ADDRSTRLEN];
-
-	for (node = nodes; node != NULL; node = node->next) {
-		if (inet_ntop(AF_INET6, &node->u.node6.addr, addr, INET6_ADDRSTRLEN) == NULL) {
-			log_err("Nodecon address is invalid: %s", strerror(errno));
-			rc = -1;
-			goto exit;
-		}
-
-		if (inet_ntop(AF_INET6, &node->u.node6.mask, mask, INET6_ADDRSTRLEN) == NULL) {
-			log_err("Nodecon mask is invalid: %s", strerror(errno));
-			rc = -1;
-			goto exit;
-		}
-
-		cil_printf("(nodecon %s %s ", addr, mask);
-
-		context_to_cil(pdb, &node->context[0]);
-
-		cil_printf(")\n");
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-
-static int ocontext_selinux_fsuse_to_cil(struct policydb *pdb, struct ocontext *fsuses)
-{
-	int rc = -1;
-	struct ocontext *fsuse;
-	const char *behavior;
-
-
-	for (fsuse = fsuses; fsuse != NULL; fsuse = fsuse->next) {
-		switch (fsuse->v.behavior) {
-		case SECURITY_FS_USE_XATTR: behavior = "xattr"; break;
-		case SECURITY_FS_USE_TRANS: behavior = "trans"; break;
-		case SECURITY_FS_USE_TASK:  behavior = "task"; break;
-		default:
-			log_err("Unknown fsuse behavior: %i", fsuse->v.behavior);
-			rc = -1;
-			goto exit;
-		}
-
-		cil_printf("(fsuse %s %s ", behavior, fsuse->u.name);
-
-		context_to_cil(pdb, &fsuse->context[0]);
-
-		cil_printf(")\n");
-
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-
-static int ocontext_xen_isid_to_cil(struct policydb *pdb, struct ocontext *isids)
-{
-	int rc = -1;
-
-	// initial sid names aren't actually stored in the pp files, need to a have
-	// a mapping, taken from the xen kernel
-	static const char *xen_sid_to_string[] = {
-		"null",
-		"xen",
-		"dom0",
-		"domio",
-		"domxen",
-		"unlabeled",
-		"security",
-		"ioport",
-		"iomem",
-		"irq",
-		"device",
-		NULL,
-	};
-
-	rc = ocontext_isid_to_cil(pdb, xen_sid_to_string, isids);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int ocontext_xen_pirq_to_cil(struct policydb *pdb, struct ocontext *pirqs)
-{
-	struct ocontext *pirq;
-
-	for (pirq = pirqs; pirq != NULL; pirq = pirq->next) {
-		cil_printf("(pirqcon %i ", pirq->u.pirq);
-		context_to_cil(pdb, &pirq->context[0]);
-		cil_printf(")\n");
-	}
-
-	return 0;
-}
-
-static int ocontext_xen_ioport_to_cil(struct policydb *pdb, struct ocontext *ioports)
-{
-	struct ocontext *ioport;
-	uint32_t low;
-	uint32_t high;
-
-	for (ioport = ioports; ioport != NULL; ioport = ioport->next) {
-		low = ioport->u.ioport.low_ioport;
-		high = ioport->u.ioport.high_ioport;
-
-		if (low == high) {
-			cil_printf("(ioportcon %i ", low);
-		} else {
-			cil_printf("(ioportcon (%i %i) ", low, high);
-		}
-
-		context_to_cil(pdb, &ioport->context[0]);
-
-		cil_printf(")\n");
-	}
-
-	return 0;
-}
-
-static int ocontext_xen_iomem_to_cil(struct policydb *pdb, struct ocontext *iomems)
-{
-	struct ocontext *iomem;
-	uint64_t low;
-	uint64_t high;
-
-	for (iomem = iomems; iomem != NULL; iomem = iomem->next) {
-		low = iomem->u.iomem.low_iomem;
-		high = iomem->u.iomem.high_iomem;
-
-		if (low == high) {
-			cil_printf("(iomemcon %#lX ", (unsigned long)low);
-		} else {
-			cil_printf("(iomemcon (%#lX %#lX) ", (unsigned long)low, (unsigned long)high);
-		}
-
-		context_to_cil(pdb, &iomem->context[0]);
-
-		cil_printf(")\n");
-	}
-
-	return 0;
-}
-
-static int ocontext_xen_pcidevice_to_cil(struct policydb *pdb, struct ocontext *pcids)
-{
-	struct ocontext *pcid;
-
-	for (pcid = pcids; pcid != NULL; pcid = pcid->next) {
-		cil_printf("(pcidevicecon %#lx ", (unsigned long)pcid->u.device);
-		context_to_cil(pdb, &pcid->context[0]);
-		cil_printf(")\n");
-	}
-
-	return 0;
-}
-
-static int ocontexts_to_cil(struct policydb *pdb)
-{
-	int rc = -1;
-	int ocon;
-
-	static int (**ocon_funcs)(struct policydb *pdb, struct ocontext *ocon);
-	static int (*ocon_selinux_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
-		ocontext_selinux_isid_to_cil,
-		ocontext_selinux_fs_to_cil,
-		ocontext_selinux_port_to_cil,
-		ocontext_selinux_netif_to_cil,
-		ocontext_selinux_node_to_cil,
-		ocontext_selinux_fsuse_to_cil,
-		ocontext_selinux_node6_to_cil,
-	};
-	static int (*ocon_xen_funcs[OCON_NUM])(struct policydb *pdb, struct ocontext *ocon) = {
-		ocontext_xen_isid_to_cil,
-		ocontext_xen_pirq_to_cil,
-		ocontext_xen_ioport_to_cil,
-		ocontext_xen_iomem_to_cil,
-		ocontext_xen_pcidevice_to_cil,
-		NULL,
-		NULL,
-	};
-
-	switch (pdb->target_platform) {
-	case SEPOL_TARGET_SELINUX:
-		ocon_funcs = ocon_selinux_funcs;
-		break;
-	case SEPOL_TARGET_XEN:
-		ocon_funcs = ocon_xen_funcs;
-		break;
-	default:
-		log_err("Unknown target platform: %i", pdb->target_platform);
-		rc = -1;
-		goto exit;
-	}
-
-	for (ocon = 0; ocon < OCON_NUM; ocon++) {
-		if (ocon_funcs[ocon] != NULL) {
-			rc = ocon_funcs[ocon](pdb, pdb->ocontexts[ocon]);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int genfscon_to_cil(struct policydb *pdb)
-{
-	struct genfs *genfs;
-	struct ocontext *ocon;
-
-	for (genfs = pdb->genfs; genfs != NULL; genfs = genfs->next) {
-		for (ocon = genfs->head; ocon != NULL; ocon = ocon->next) {
-			cil_printf("(genfscon %s %s ", genfs->fstype, ocon->u.name);
-			context_to_cil(pdb, &ocon->context[0]);
-			cil_printf(")\n");
-		}
-	}
-
-	return 0;
-}
-
-static int level_string_to_cil(char *levelstr)
-{
-	int rc = -1;
-	char *sens = NULL;
-	char *cats = NULL;
-	int matched;
-	char *saveptr = NULL;
-	char *token = NULL;
-	char *ranged = NULL;
-
-	matched = sscanf(levelstr, "%m[^:]:%ms", &sens, &cats);
-	if (matched < 1 || matched > 2) {
-		log_err("Invalid level: %s", levelstr);
-		rc = -1;
-		goto exit;
-	}
-
-	cil_printf("(%s", sens);
-
-	if (matched == 2) {
-		cil_printf("(");
-		token = strtok_r(cats, ",", &saveptr);
-		while (token != NULL) {
-			ranged = strchr(token, '.');
-			if (ranged == NULL) {
-				cil_printf("%s ", token);
-			} else {
-				*ranged = '\0';
-				cil_printf("(range %s %s) ", token, ranged + 1);
-			}
-			token = strtok_r(NULL, ",", &saveptr);
-		}
-		cil_printf(")");
-	}
-
-	cil_printf(")");
-
-	rc = 0;
-exit:
-	free(sens);
-	free(cats);
-	return rc;
-}
-
-static int level_range_string_to_cil(char *levelrangestr)
-{
-	char *ranged = NULL;
-	char *low;
-	char *high;
-
-	ranged = strchr(levelrangestr, '-');
-	if (ranged == NULL) {
-		low = high = levelrangestr;
-	} else {
-		*ranged = '\0';
-		low = levelrangestr;
-		high = ranged + 1;
-	}
-
-	level_string_to_cil(low);
-	cil_printf(" ");
-	level_string_to_cil(high);
-
-	return 0;
-}
-
-static int context_string_to_cil(char *contextstr)
-{
-	int rc = -1;
-	int matched;
-	char *user = NULL;
-	char *role = NULL;
-	char *type = NULL;
-	char *level = NULL;
-
-	matched = sscanf(contextstr, "%m[^:]:%m[^:]:%m[^:]:%ms", &user, &role, &type, &level);
-	if (matched < 3 || matched > 4) {
-		log_err("Invalid context: %s", contextstr);
-		rc = -1;
-		goto exit;
-	}
-
-	cil_printf("(%s %s %s (", user, role, type);
-
-	if (matched == 3) {
-		cil_printf(DEFAULT_LEVEL);
-		cil_printf(" ");
-		cil_printf(DEFAULT_LEVEL);
-	} else {
-		level_range_string_to_cil(level);
-	}
-
-	cil_printf("))");
-
-	rc = 0;
-
-exit:
-	free(user);
-	free(role);
-	free(type);
-	free(level);
-
-	return rc;
-}
-
-static int seusers_to_cil(struct sepol_module_package *mod_pkg)
-{
-	int rc = -1;
-	FILE *fp = NULL;
-	char *seusers = sepol_module_package_get_seusers(mod_pkg);
-	size_t seusers_len = sepol_module_package_get_seusers_len(mod_pkg);
-	size_t len = 0;
-	char *line = NULL;
-	ssize_t line_len = 0;
-	char *buf = NULL;
-
-	char *user = NULL;
-	char *seuser = NULL;
-	char *level = NULL;
-	int matched;
-
-	if (seusers_len == 0) {
-		return 0;
-	}
-
-	fp = fmemopen(seusers, seusers_len, "r");
-
-	while ((line_len = getline(&line, &len, fp)) != -1) {
-		buf = line;
-		buf[line_len - 1] = '\0';
-		while (*buf && isspace(buf[0])) {
-			buf++;
-		}
-		if (buf[0] == '#' || buf[0] == '\0') {
-			continue;
-		}
-
-		matched = sscanf(buf, "%m[^:]:%m[^:]:%ms", &user, &seuser, &level);
-
-		if (matched < 2 || matched > 3) {
-			log_err("Invalid seuser line: %s", line);
-			rc = -1;
-			goto exit;
-		}
-
-		if (!strcmp(user, "__default__")) {
-			cil_printf("(selinuxuserdefault %s (", seuser);
-		} else {
-			cil_printf("(selinuxuser %s %s (", user, seuser);
-		}
-
-		switch (matched) {
-		case 2:
-			cil_printf("systemlow systemlow");
-			break;
-		case 3:
-			level_range_string_to_cil(level);
-			break;
-		}
-
-		cil_printf("))\n");
-
-		free(user);
-		free(seuser);
-		free(level);
-		user = seuser = level = NULL;
-	}
-	if (ferror(fp)) {
-		cil_printf("Failed to read seusers\n");
-		rc = -1;
-		goto exit;
-	}
-
-	rc = 0;
-
-exit:
-	if (fp != NULL) {
-		fclose(fp);
-	}
-	free(line);
-	free(user);
-	free(seuser);
-	free(level);
-
-	return rc;
-}
-
-static int netfilter_contexts_to_cil(struct sepol_module_package *mod_pkg)
-{
-	size_t netcons_len = sepol_module_package_get_netfilter_contexts_len(mod_pkg);
-
-	if (netcons_len > 0) {
-		log_err("Warning: netfilter_contexts are unsupported in CIL. Dropping from output.");
-	}
-
-	return 0;
-}
-
-static int user_extra_to_cil(struct sepol_module_package *mod_pkg)
-{
-	int rc = -1;
-	char *userx = sepol_module_package_get_user_extra(mod_pkg);
-	size_t userx_len = sepol_module_package_get_user_extra_len(mod_pkg);
-	FILE *fp = NULL;
-	size_t len = 0;
-	char *line = NULL;
-	ssize_t line_len = 0;
-	int matched;
-	char *user = NULL;
-	char *prefix = NULL;
-
-	if (userx_len == 0) {
-		return 0;
-	}
-
-	fp = fmemopen(userx, userx_len, "r");
-
-	while ((line_len = getline(&line, &len, fp)) != -1) {
-		line[line_len - 1] = '\0';
-
-		matched = sscanf(line, "user %ms prefix %m[^;];", &user, &prefix);
-		if (matched != 2) {
-			rc = -1;
-			log_err("Invalid file context line: %s", line);
-			goto exit;
-		}
-
-		cil_println(0, "(userprefix %s %s)", user, prefix);
-		free(user);
-		free(prefix);
-		user = prefix = NULL;
-	}
-
-	if (ferror(fp)) {
-		cil_printf("Failed to read user_extra\n");
-		rc = -1;
-		goto exit;
-	}
-
-	rc = 0;
-exit:
-	if (fp != NULL) {
-		fclose(fp);
-	}
-	free(line);
-	free(user);
-	free(prefix);
-
-	return rc;
-}
-
-static int file_contexts_to_cil(struct sepol_module_package *mod_pkg)
-{
-	int rc = -1;
-	char *fc = sepol_module_package_get_file_contexts(mod_pkg);
-	size_t fc_len = sepol_module_package_get_file_contexts_len(mod_pkg);
-	FILE *fp = NULL;
-	size_t len = 0;
-	char *line = NULL;
-	char *buf = NULL;
-	ssize_t line_len = 0;
-	int matched;
-	char *regex = NULL;
-	char *mode = NULL;
-	char *context = NULL;
-	const char *cilmode;
-
-	if (fc_len == 0) {
-		return 0;
-	}
-
-	fp = fmemopen(fc, fc_len, "r");
-	while ((line_len = getline(&line, &len, fp)) != -1) {
-		buf = line;
-		if (buf[line_len - 1] == '\n') {
-			buf[line_len - 1] = '\0';
-		}
-		while (*buf && isspace(buf[0])) {
-			buf++;
-		}
-		if (buf[0] == '#' || buf[0] == '\0') {
-			continue;
-		}
-
-		matched = sscanf(buf, "%ms %ms %ms", &regex, &mode, &context);
-		if (matched < 2 || matched > 3) {
-			rc = -1;
-			log_err("Invalid file context line: %s", line);
-			goto exit;
-		}
-
-		if (matched == 2) {
-			context = mode;
-			mode = NULL;
-		}
-
-		if (mode == NULL) {
-			cilmode = "any";
-		} else if (!strcmp(mode, "--")) {
-			cilmode = "file";
-		} else if (!strcmp(mode, "-d")) {
-			cilmode = "dir";
-		} else if (!strcmp(mode, "-c")) {
-			cilmode = "char";
-		} else if (!strcmp(mode, "-b")) {
-			cilmode = "block";
-		} else if (!strcmp(mode, "-s")) {
-			cilmode = "socket";
-		} else if (!strcmp(mode, "-p")) {
-			cilmode = "pipe";
-		} else if (!strcmp(mode, "-l")) {
-			cilmode = "symlink";
-		} else {
-			rc = -1;
-			log_err("Invalid mode in file context line: %s", line);
-			goto exit;
-		}
-
-		cil_printf("(filecon \"%s\" %s ", regex, cilmode);
-
-		if (!strcmp(context, "<<none>>")) {
-			cil_printf("()");
-		} else {
-			context_string_to_cil(context);
-		}
-
-		cil_printf(")\n");
-
-		free(regex);
-		free(mode);
-		free(context);
-		regex = mode = context = NULL;
-	}
-
-	if (ferror(fp)) {
-		cil_printf("Failed to read user_extra\n");
-		rc = -1;
-		goto exit;
-	}
-
-	rc = 0;
-exit:
-	free(line);
-	free(regex);
-	free(mode);
-	free(context);
-	if (fp != NULL) {
-		fclose(fp);
-	}
-
-	return rc;
-}
-
-
-static int (*func_to_cil[SYM_NUM])(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack, char *key, void *datum, int scope) = {
-	NULL,	// commons, only stored in the global symtab, handled elsewhere
-	class_to_cil,
-	role_to_cil,
-	type_to_cil,
-	user_to_cil,
-	boolean_to_cil,
-	sens_to_cil,
-	cat_to_cil
-};
-
-static int typealiases_to_cil(int indent, struct policydb *pdb, struct avrule_block *UNUSED(block), struct stack *decl_stack)
-{
-	struct type_datum *alias_datum;
-	char *alias_name;
-	struct list_node *curr;
-	struct avrule_decl *decl = stack_peek(decl_stack);
-	struct list *alias_list = typealias_lists[decl->decl_id];
-	int rc = -1;
-
-	if (alias_list == NULL) {
-		return 0;
-	}
-
-	for (curr = alias_list->head; curr != NULL; curr = curr->next) {
-		alias_name = curr->data;
-		alias_datum = hashtab_search(pdb->p_types.table, alias_name);
-		if (alias_datum == NULL) {
-			rc = -1;
-			goto exit;
-		}
-
-		cil_println(indent, "(typealias %s)", alias_name);
-		cil_println(indent, "(typealiasactual %s %s)", alias_name, pdb->p_type_val_to_name[alias_datum->s.value - 1]);
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int declared_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
-{
-	int rc = -1;
-	struct ebitmap map;
-	struct ebitmap_node *node;
-	unsigned int i;
-	char * key;
-	struct scope_datum *scope;
-	int sym;
-	void *datum;
-	struct avrule_decl *decl = stack_peek(decl_stack);
-
-	for (sym = 0; sym < SYM_NUM; sym++) {
-		if (func_to_cil[sym] == NULL) {
-			continue;
-		}
-
-		map = decl->declared.scope[sym];
-		ebitmap_for_each_bit(&map, node, i) {
-			if (!ebitmap_get_bit(&map, i)) {
-				continue;
-			}
-			key = pdb->sym_val_to_name[sym][i];
-			datum = hashtab_search(pdb->symtab[sym].table, key);
-			if (datum == NULL) {
-				rc = -1;
-				goto exit;
-			}
-			scope = hashtab_search(pdb->scope[sym].table, key);
-			if (scope == NULL) {
-				rc = -1;
-				goto exit;
-			}
-			rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, scope->scope);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-
-		if (sym == SYM_CATS) {
-			rc = cat_order_to_cil(indent, pdb, map);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-
-		if (sym == SYM_LEVELS) {
-			rc = sens_order_to_cil(indent, pdb, map);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-
-		if (sym == SYM_CLASSES) {
-			rc = class_order_to_cil(indent, pdb, map);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int required_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
-{
-	int rc = -1;
-	struct ebitmap map;
-	struct ebitmap_node *node;
-	unsigned int i;
-	unsigned int j;
-	char * key;
-	int sym;
-	void *datum;
-	struct avrule_decl *decl = stack_peek(decl_stack);
-	struct scope_datum *scope_datum;
-
-	for (sym = 0; sym < SYM_NUM; sym++) {
-		if (func_to_cil[sym] == NULL) {
-			continue;
-		}
-
-		map = decl->required.scope[sym];
-		ebitmap_for_each_bit(&map, node, i) {
-			if (!ebitmap_get_bit(&map, i)) {
-				continue;
-			}
-			key = pdb->sym_val_to_name[sym][i];
-
-			scope_datum = hashtab_search(pdb->scope[sym].table, key);
-			for (j = 0; j < scope_datum->decl_ids_len; j++) {
-				if (scope_datum->decl_ids[j] == decl->decl_id) {
-					break;
-				}
-			}
-			if (j >= scope_datum->decl_ids_len) {
-				// Symbols required in the global scope are also in the
-				// required scope ebitmap of all avrule decls (i.e. required
-				// in all optionals). So we need to look at the scopes of each
-				// symbol in this avrule_decl to determine if it actually is
-				// required in this decl, or if it's just required in the
-				// global scope. If we got here, then this symbol is not
-				// actually required in this scope, so skip it.
-				continue;
-			}
-
-			datum = hashtab_search(pdb->symtab[sym].table, key);
-			if (datum == NULL) {
-				rc = -1;
-				goto exit;
-			}
-			rc = func_to_cil[sym](indent, pdb, block, decl_stack, key, datum, SCOPE_REQ);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-
-static int additive_scopes_to_cil_map(char *key, void *data, void *arg)
-{
-	int rc = -1;
-	struct map_args *args = arg;
-
-	rc = func_to_cil[args->sym_index](args->indent, args->pdb, args->block, args->decl_stack, key, data, SCOPE_REQ);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int additive_scopes_to_cil(int indent, struct policydb *pdb, struct avrule_block *block, struct stack *decl_stack)
-{
-	int rc = -1;
-	struct map_args args;
-	args.pdb = pdb;
-	args.block = block;
-	args.decl_stack = decl_stack;
-	args.indent = indent;
-	struct avrule_decl *decl = stack_peek(decl_stack);
-
-	for (args.sym_index = 0; args.sym_index < SYM_NUM; args.sym_index++) {
-		rc = hashtab_map(decl->symtab[args.sym_index].table, additive_scopes_to_cil_map, &args);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int is_scope_superset(struct scope_index *sup, struct scope_index *sub)
-{
-	// returns 1 if sup is a superset of sub, returns 0 otherwise
-
-	int rc = 0;
-
-	uint32_t i;
-	struct ebitmap sup_map;
-	struct ebitmap sub_map;
-	struct ebitmap res;
-
-	ebitmap_init(&res);
-
-	for (i = 0; i < SYM_NUM; i++) {
-		sup_map = sup->scope[i];
-		sub_map = sub->scope[i];
-
-		ebitmap_and(&res, &sup_map, &sub_map);
-		if (!ebitmap_cmp(&res, &sub_map)) {
-			goto exit;
-		}
-		ebitmap_destroy(&res);
-	}
-
-	if (sup->class_perms_len < sub->class_perms_len) {
-		goto exit;
-	}
-
-	for (i = 0; i < sub->class_perms_len; i++) {
-		sup_map = sup->class_perms_map[i];
-		sub_map = sub->class_perms_map[i];
-
-		ebitmap_and(&res, &sup_map, &sub_map);
-		if (!ebitmap_cmp(&res, &sub_map)) {
-			goto exit;
-		}
-		ebitmap_destroy(&res);
-	}
-
-	rc = 1;
-
-exit:
-
-	ebitmap_destroy(&res);
-	return rc;
-}
-
-static int blocks_to_cil(struct policydb *pdb)
-{
-	int rc = -1;
-	struct avrule_block *block;
-	struct avrule_decl *decl;
-	struct avrule_decl *decl_tmp;
-	int indent = 0;
-	struct stack *stack;
-	struct list *attr_list;
-
-	rc = stack_init(&stack);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	for (block = pdb->global; block != NULL; block = block->next) {
-		rc = list_init(&attr_list);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		decl = block->branch_list;
-		if (decl == NULL) {
-			continue;
-		}
-
-		if (decl->next != NULL) {
-			log_err("Warning: 'else' blocks in optional statements are unsupported in CIL. Dropping from output.");
-		}
-
-		if (block->flags & AVRULE_OPTIONAL) {
-			while (stack->pos > 0) {
-				decl_tmp = stack_peek(stack);
-				if (is_scope_superset(&decl->required, &decl_tmp->required)) {
-					break;
-				}
-
-				stack_pop(stack);
-				indent--;
-				cil_println(indent, ")");
-			}
-
-			cil_println(indent, "(optional %s_optional_%i", pdb->name, decl->decl_id);
-			indent++;
-		}
-
-		stack_push(stack, decl);
-
-		if (stack->pos == 0) {
-			// type aliases and commons are only stored in the global symtab.
-			// However, to get scoping correct, we assume they are in the
-			// global block
-			struct map_args args;
-			args.pdb = pdb;
-			args.block = block;
-			args.decl_stack = stack;
-			args.indent = 0;
-			args.scope = SCOPE_DECL;
-
-			rc = hashtab_map(pdb->p_commons.table, common_to_cil, &args);
-			if (rc != 0) {
-				goto exit;
-			}
-		}
-
-		rc = typealiases_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = declared_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = required_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = additive_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = avrule_list_to_cil(indent, pdb, decl->avrules, attr_list);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = role_trans_to_cil(indent, pdb, decl->role_tr_rules);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = role_allows_to_cil(indent, pdb, decl->role_allow_rules);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = range_trans_to_cil(indent, pdb, decl->range_tr_rules);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = cond_list_to_cil(indent, pdb, decl->cond_list);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		rc = cil_print_attr_list(indent, pdb, attr_list);
-		if (rc != 0) {
-			goto exit;
-		}
-		attr_list_destroy(&attr_list);
-	}
-
-	while (indent > 0) {
-		indent--;
-		cil_println(indent, ")");
-	}
-
-	rc = 0;
-
-exit:
-	stack_destroy(&stack);
-	attr_list_destroy(&attr_list);
-
-	return rc;
-}
-
-static int handle_unknown_to_cil(struct policydb *pdb)
-{
-	int rc = -1;
-	const char *hu;
-
-	switch (pdb->handle_unknown) {
-	case SEPOL_DENY_UNKNOWN:
-		hu = "deny";
-		break;
-	case SEPOL_REJECT_UNKNOWN:
-		hu = "reject";
-		break;
-	case SEPOL_ALLOW_UNKNOWN:
-		hu = "allow";
-		break;
-	default:
-		log_err("Unknown value for handle-unknown: %i", pdb->handle_unknown);
-		rc = -1;
-		goto exit;
-	}
-
-	cil_println(0, "(handleunknown %s)", hu);
-
-	return 0;
-
-exit:
-	return rc;
-}
-
-static int generate_mls(struct policydb *pdb)
-{
-	const char *mls_str = pdb->mls ? "true" : "false";
-	cil_println(0, "(mls %s)", mls_str);
-
-	return 0;
-}
-
-static int generate_default_level(void)
-{
-	cil_println(0, "(sensitivity s0)");
-	cil_println(0, "(sensitivityorder (s0))");
-	cil_println(0, "(level " DEFAULT_LEVEL " (s0))");
-
-	return 0;
-}
-
-static int generate_default_object(void)
-{
-	cil_println(0, "(role " DEFAULT_OBJECT ")");
-
-	return 0;
-}
-
-static int generate_gen_require_attribute(void)
-{
-	cil_println(0, "(typeattribute " GEN_REQUIRE_ATTR ")");
-	cil_println(0, "(roleattribute " GEN_REQUIRE_ATTR ")");
-
-	return 0;
-}
-
-static int fix_module_name(struct policydb *pdb)
-{
-	char *letter;
-	int rc = -1;
-
-	// The base module doesn't have its name set, but we use that for some
-	// autogenerated names, like optionals and attributes, to prevent naming
-	// collisions. However, they sometimes need to be fixed up.
-
-	// the base module isn't given a name, so just call it "base"
-	if (pdb->policy_type == POLICY_BASE) {
-		pdb->name = strdup("base");
-		if (pdb->name == NULL) {
-			log_err("Out of memory");
-			rc = -1;
-			goto exit;
-		}
-	}
-
-	// CIL is more restrictive in module names than checkmodule. Convert bad
-	// characters to underscores
-	for (letter = pdb->name; *letter != '\0'; letter++) {
-		if (isalnum(*letter)) {
-			continue;
-		}
-
-		*letter = '_';
-	}
-
-	return 0;
-exit:
-	return rc;
-}
-
-static int module_package_to_cil(struct sepol_module_package *mod_pkg)
-{
-	int rc = -1;
-	struct sepol_policydb *pdb;
-
-	pdb = sepol_module_package_get_policy(mod_pkg);
-	if (pdb == NULL) {
-		log_err("Failed to get policydb");
-		rc = -1;
-		goto exit;
-	}
-
-	if (pdb->p.policy_type != SEPOL_POLICY_BASE &&
-		pdb->p.policy_type != SEPOL_POLICY_MOD) {
-		log_err("Policy pakcage is not a base or module");
-		rc = -1;
-		goto exit;
-	}
-
-	rc = fix_module_name(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	if (pdb->p.policy_type == SEPOL_POLICY_BASE && !pdb->p.mls) {
-		// If this is a base non-mls policy, we need to define a default level
-		// range that can be used for contexts by other non-mls modules, since
-		// CIL requires that all contexts have a range, even if they are
-		// ignored as in non-mls policies
-		rc = generate_default_level();
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	if (pdb->p.policy_type == SEPOL_POLICY_BASE) {
-		// object_r is implicit in checkmodule, but not with CIL, create it
-		// as part of base
-		rc = generate_default_object();
-		if (rc != 0) {
-			goto exit;
-		}
-
-		// default attribute to be used to mimic gen_require in CIL
-		rc = generate_gen_require_attribute();
-		if (rc != 0) {
-			goto exit;
-		}
-
-		// handle_unknown is used from only the base module
-		rc = handle_unknown_to_cil(&pdb->p);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		// mls is used from only the base module	
-		rc = generate_mls(&pdb->p);
-		if (rc != 0) {
-			goto exit;
-		}
-	}
-
-	rc = role_list_create(pdb->p.p_roles.table);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = typealias_list_create(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = polcaps_to_cil(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = ocontexts_to_cil(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = genfscon_to_cil(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = seusers_to_cil(mod_pkg);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = netfilter_contexts_to_cil(mod_pkg);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = user_extra_to_cil(mod_pkg);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = file_contexts_to_cil(mod_pkg);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	// now print everything that is scoped
-	rc = blocks_to_cil(&pdb->p);
-	if (rc != 0) {
-		goto exit;
-	}
-
-	rc = 0;
-
-exit:
-	role_list_destroy();
-	typealias_list_destroy();
-
-	return rc;
-}
-
-static int fp_to_buffer(FILE *fp, char **data, size_t *data_len)
-{
-	int rc = -1;
-	char *d = NULL;
-	size_t d_len = 0;
-	size_t read_len = 0;
-	size_t max_len = 1 << 17; // start at 128KB, this is enough to hold about half of all the existing pp files
-
-	d = malloc(max_len);
-	if (d == NULL) {
-		log_err("Out of memory");
-		rc = -1;
-		goto exit;
-	}
-
-	while ((read_len = fread(d + d_len, 1, max_len - d_len, fp)) > 0) {
-		d_len += read_len;
-		if (d_len == max_len) {
-			max_len *= 2;
-			d = realloc(d, max_len);
-			if (d == NULL) {
-				log_err("Out of memory");
-				rc = -1;
-				goto exit;
-			}
-		}
-	}
-
-	if (ferror(fp) != 0) {
-		log_err("Failed to read pp file");
-		rc = -1;
-		goto exit;
-	}
-
-	*data = d;
-	*data_len = d_len;
-
-	return 0;
-
-exit:
-	free(d);
-	return rc;
-}
-
-static int ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg)
-{
-	int rc = -1;
-	FILE *f = NULL;
-	struct sepol_policy_file *pf = NULL;
-	struct sepol_module_package *pkg = NULL;
-	char *data = NULL;
-	size_t data_len;
-	int fd;
-	struct stat sb;
-
-	rc = sepol_policy_file_create(&pf);
-	if (rc != 0) {
-		log_err("Failed to create policy file");
-		goto exit;
-	}
-
-	fd = fileno(fp);
-	if (fstat(fd, &sb) == -1) {
-		rc = -1;
-		goto exit;
-	}
-
-	if (S_ISFIFO(sb.st_mode) || S_ISSOCK(sb.st_mode)) {
-		// libsepol fails when trying to read a policy package from a pipe or a
-		// socket due its use of lseek. In this case, read the data into a
-		// buffer and provide that to libsepol
-		rc = fp_to_buffer(fp, &data, &data_len);
-		if (rc != 0) {
-			goto exit;
-		}
-
-		sepol_policy_file_set_mem(pf, data, data_len);
-	} else {
-		sepol_policy_file_set_fp(pf, fp);
-	}
-
-	rc = sepol_module_package_create(&pkg);
-	if (rc != 0) {
-		log_err("Failed to create module package");
-		goto exit;
-	}
-
-	rc = sepol_module_package_read(pkg, pf, 0);
-	if (rc != 0) {
-		log_err("Failed to read policy package");
-		goto exit;
-	}
-
-	*mod_pkg = pkg;
-
-exit:
-	free(data);
-
-	sepol_policy_file_free(pf);
-	if (f != NULL) {
-		fclose(f);
-	}
-
-	if (rc != 0) {
-		sepol_module_package_free(pkg);
-	}
-
-	return rc;
-}
-
 static void usage(int err)
 {
 	fprintf(stderr, "Usage: %s [OPTIONS] [IN_FILE [OUT_FILE]]\n", progname);
@@ -3932,21 +109,20 @@ int main(int argc, char **argv)
 	} else {
 		out = stdout;
 	}
-	out_file = out;
 
 	if (argc >= optind + 3) {
 		log_err("Too many arguments");
 		usage(1);
 	}
 
-	rc = ppfile_to_module_package(in, &mod_pkg);
+	rc = sepol_ppfile_to_module_package(in, &mod_pkg);
 	if (rc != 0) {
 		goto exit;
 	}
 	fclose(in);
 	in = NULL;
 
-	rc = module_package_to_cil(mod_pkg);
+	rc = sepol_module_package_to_cil(out, mod_pkg);
 	if (rc != 0) {
 		goto exit;
 	}
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb
  2015-03-31 17:17 [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy James Carter
  2015-03-31 17:17 ` [PATCH 1/3 v3] libsepol, policycoreutils: Move functions to convert a module package to CIL James Carter
@ 2015-03-31 17:18 ` James Carter
  2015-03-31 19:26   ` Steve Lawrence
  2015-03-31 17:18 ` [PATCH 3/3 v3] checkpolicy: Add support for generating CIL James Carter
  2015-03-31 19:15 ` [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy Yuli Khodorkovskiy
  3 siblings, 1 reply; 11+ messages in thread
From: James Carter @ 2015-03-31 17:18 UTC (permalink / raw)
  To: selinux

Add a new function, sepol_module_policydb_to_cil, that generates
CIL from a module (not kernel) policydb. Refactor
sepol_module_package_to_cil() to use the new function.

Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
---
 libsepol/include/sepol/module_to_cil.h |   2 +
 libsepol/src/module_to_cil.c           | 367 ++++++++++++++++++++++-----------
 2 files changed, 254 insertions(+), 115 deletions(-)

diff --git a/libsepol/include/sepol/module_to_cil.h b/libsepol/include/sepol/module_to_cil.h
index 1d0225c..18bb3bf 100644
--- a/libsepol/include/sepol/module_to_cil.h
+++ b/libsepol/include/sepol/module_to_cil.h
@@ -1,6 +1,8 @@
 #include <stdlib.h>
 
 #include <sepol/module.h>
+#include <sepol/policydb/policydb.h>
 
+int sepol_module_policydb_to_cil(FILE *fp, struct policydb *pdb, int linked);
 int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg);
 int sepol_ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg);
diff --git a/libsepol/src/module_to_cil.c b/libsepol/src/module_to_cil.c
index 8326826..d1d2efe 100644
--- a/libsepol/src/module_to_cil.c
+++ b/libsepol/src/module_to_cil.c
@@ -3407,126 +3407,177 @@ exit:
 	return rc;
 }
 
-static int blocks_to_cil(struct policydb *pdb)
+static int block_to_cil(struct policydb *pdb, struct avrule_block *block, struct stack *stack, int indent)
 {
 	int rc = -1;
-	struct avrule_block *block;
 	struct avrule_decl *decl;
-	struct avrule_decl *decl_tmp;
-	int indent = 0;
-	struct stack *stack;
 	struct list *attr_list;
 
-	rc = stack_init(&stack);
+	decl = block->branch_list;
+
+	rc = list_init(&attr_list);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	for (block = pdb->global; block != NULL; block = block->next) {
-		rc = list_init(&attr_list);
-		if (rc != 0) {
-			goto exit;
-		}
+	rc = typealiases_to_cil(indent, pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		decl = block->branch_list;
-		if (decl == NULL) {
-			continue;
-		}
+	rc = declared_scopes_to_cil(indent, pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		if (decl->next != NULL) {
-			log_err("Warning: 'else' blocks in optional statements are unsupported in CIL. Dropping from output.");
-		}
+	rc = required_scopes_to_cil(indent, pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		if (block->flags & AVRULE_OPTIONAL) {
-			while (stack->pos > 0) {
-				decl_tmp = stack_peek(stack);
-				if (is_scope_superset(&decl->required, &decl_tmp->required)) {
-					break;
-				}
+	rc = additive_scopes_to_cil(indent, pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
 
-				stack_pop(stack);
-				indent--;
-				cil_println(indent, ")");
-			}
+	rc = avrule_list_to_cil(indent, pdb, decl->avrules, attr_list);
+	if (rc != 0) {
+		goto exit;
+	}
 
-			cil_println(indent, "(optional %s_optional_%i", pdb->name, decl->decl_id);
-			indent++;
-		}
+	rc = role_trans_to_cil(indent, pdb, decl->role_tr_rules);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		stack_push(stack, decl);
+	rc = role_allows_to_cil(indent, pdb, decl->role_allow_rules);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		if (stack->pos == 0) {
-			// type aliases and commons are only stored in the global symtab.
-			// However, to get scoping correct, we assume they are in the
-			// global block
-			struct map_args args;
-			args.pdb = pdb;
-			args.block = block;
-			args.decl_stack = stack;
-			args.indent = 0;
-			args.scope = SCOPE_DECL;
+	rc = range_trans_to_cil(indent, pdb, decl->range_tr_rules);
+	if (rc != 0) {
+		goto exit;
+	}
 
-			rc = hashtab_map(pdb->p_commons.table, common_to_cil, &args);
-			if (rc != 0) {
-				goto exit;
+	rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = cond_list_to_cil(indent, pdb, decl->cond_list);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = cil_print_attr_list(indent, pdb, attr_list);
+	if (rc != 0) {
+		goto exit;
+	}
+
+exit:
+	attr_list_destroy(&attr_list);
+	return rc;
+}
+
+static int module_block_to_cil(struct policydb *pdb, struct avrule_block *block, struct stack *stack, int *indent)
+{
+	int rc = 0;
+	struct avrule_decl *decl;
+	struct avrule_decl *decl_tmp;
+
+	decl = block->branch_list;
+	if (decl == NULL) {
+		goto exit;
+	}
+
+	if (decl->next != NULL) {
+		log_err("Warning: 'else' blocks in optional statements are unsupported in CIL. Dropping from output.");
+	}
+
+	if (block->flags & AVRULE_OPTIONAL) {
+		while (stack->pos > 0) {
+			decl_tmp = stack_peek(stack);
+			if (is_scope_superset(&decl->required, &decl_tmp->required)) {
+				break;
 			}
-		}
 
-		rc = typealiases_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
+			stack_pop(stack);
+			(*indent)--;
+			cil_println(*indent, ")");
 		}
 
-		rc = declared_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
+		cil_println(*indent, "(optional %s_optional_%i", pdb->name, decl->decl_id);
+		(*indent)++;
+	}
 
-		rc = required_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
+	stack_push(stack, decl);
 
-		rc = additive_scopes_to_cil(indent, pdb, block, stack);
-		if (rc != 0) {
-			goto exit;
-		}
+	rc = block_to_cil(pdb, block, stack, *indent);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		rc = avrule_list_to_cil(indent, pdb, decl->avrules, attr_list);
-		if (rc != 0) {
-			goto exit;
-		}
+exit:
+	return rc;
+}
 
-		rc = role_trans_to_cil(indent, pdb, decl->role_tr_rules);
-		if (rc != 0) {
-			goto exit;
-		}
+static int global_block_to_cil(struct policydb *pdb, struct avrule_block *block, struct stack *stack)
+{
+	int rc = 0;
+	struct avrule_decl *decl;
 
-		rc = role_allows_to_cil(indent, pdb, decl->role_allow_rules);
-		if (rc != 0) {
-			goto exit;
-		}
+	decl = block->branch_list;
+	if (decl == NULL) {
+		goto exit;
+	}
 
-		rc = range_trans_to_cil(indent, pdb, decl->range_tr_rules);
-		if (rc != 0) {
-			goto exit;
-		}
+	if (decl->next != NULL) {
+		log_err("Warning: 'else' not allowed in global block. Dropping from output.");
+	}
 
-		rc = filename_trans_to_cil(indent, pdb, decl->filename_trans_rules);
-		if (rc != 0) {
-			goto exit;
-		}
+	stack_push(stack, decl);
 
-		rc = cond_list_to_cil(indent, pdb, decl->cond_list);
-		if (rc != 0) {
-			goto exit;
-		}
+	// type aliases and commons are only stored in the global symtab.
+	// However, to get scoping correct, we assume they are in the
+	// global block
+	rc = hashtab_map(pdb->p_commons.table, common_to_cil, NULL);
+	if (rc != 0) {
+		goto exit;
+	}
 
-		rc = cil_print_attr_list(indent, pdb, attr_list);
+	rc = block_to_cil(pdb, block, stack, 0);
+	if (rc != 0) {
+		goto exit;
+	}
+
+exit:
+	return rc;
+}
+
+static int blocks_to_cil(struct policydb *pdb)
+{
+	int rc = -1;
+	struct avrule_block *block;
+	int indent = 0;
+	struct stack *stack;
+
+	rc = stack_init(&stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	block = pdb->global;
+	rc = global_block_to_cil(pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	for (block = block->next; block != NULL; block = block->next) {
+		rc = module_block_to_cil(pdb, block, stack, &indent);
 		if (rc != 0) {
 			goto exit;
 		}
-		attr_list_destroy(&attr_list);
 	}
 
 	while (indent > 0) {
@@ -3534,11 +3585,71 @@ static int blocks_to_cil(struct policydb *pdb)
 		cil_println(indent, ")");
 	}
 
-	rc = 0;
+exit:
+	stack_destroy(&stack);
+
+	return rc;
+}
+
+static int linked_block_to_cil(struct policydb *pdb, struct avrule_block *block, struct stack *stack)
+{
+	int rc = 0;
+	struct avrule_decl *decl;
+
+	decl = block->branch_list;
+	if (decl == NULL) {
+		goto exit;
+	}
+
+	if (!decl->enabled) {
+		if (decl->next != NULL) {
+			decl = decl->next;
+		} else {
+			goto exit;
+		}
+	}
+
+	stack_push(stack, decl);
+
+	rc = block_to_cil(pdb, block, stack, 0);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	stack_pop(stack);
+
+exit:
+	return rc;
+}
+
+static int linked_blocks_to_cil(struct policydb *pdb)
+{
+	// Convert base module that has been linked to CIL
+	// Since it is linked, all optional blocks have been resolved
+	int rc = -1;
+	struct avrule_block *block;
+	struct stack *stack;
+
+	rc = stack_init(&stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	block = pdb->global;
+	rc = global_block_to_cil(pdb, block, stack);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	for (block = block->next; block != NULL; block = block->next) {
+		rc = linked_block_to_cil(pdb, block, stack);
+		if (rc != 0) {
+			goto exit;
+		}
+	}
 
 exit:
 	stack_destroy(&stack);
-	attr_list_destroy(&attr_list);
 
 	return rc;
 }
@@ -3638,33 +3749,30 @@ exit:
 	return rc;
 }
 
-int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
+int sepol_module_policydb_to_cil(FILE *fp, struct policydb *pdb, int linked)
 {
 	int rc = -1;
-	struct sepol_policydb *pdb;
 
 	out_file = fp;
 
-	pdb = sepol_module_package_get_policy(mod_pkg);
 	if (pdb == NULL) {
-		log_err("Failed to get policydb");
-		rc = -1;
+		rc = 0;
 		goto exit;
 	}
 
-	if (pdb->p.policy_type != SEPOL_POLICY_BASE &&
-		pdb->p.policy_type != SEPOL_POLICY_MOD) {
+	if (pdb->policy_type != SEPOL_POLICY_BASE &&
+		pdb->policy_type != SEPOL_POLICY_MOD) {
 		log_err("Policy pakcage is not a base or module");
 		rc = -1;
 		goto exit;
 	}
 
-	rc = fix_module_name(&pdb->p);
+	rc = fix_module_name(pdb);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	if (pdb->p.policy_type == SEPOL_POLICY_BASE && !pdb->p.mls) {
+	if (pdb->policy_type == SEPOL_POLICY_BASE && !pdb->mls) {
 		// If this is a base non-mls policy, we need to define a default level
 		// range that can be used for contexts by other non-mls modules, since
 		// CIL requires that all contexts have a range, even if they are
@@ -3675,7 +3783,7 @@ int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
 		}
 	}
 
-	if (pdb->p.policy_type == SEPOL_POLICY_BASE) {
+	if (pdb->policy_type == SEPOL_POLICY_BASE) {
 		// object_r is implicit in checkmodule, but not with CIL, create it
 		// as part of base
 		rc = generate_default_object();
@@ -3690,65 +3798,97 @@ int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
 		}
 
 		// handle_unknown is used from only the base module
-		rc = handle_unknown_to_cil(&pdb->p);
+		rc = handle_unknown_to_cil(pdb);
 		if (rc != 0) {
 			goto exit;
 		}
 
 		// mls is used from only the base module
-		rc = generate_mls(&pdb->p);
+		rc = generate_mls(pdb);
 		if (rc != 0) {
 			goto exit;
 		}
 	}
 
-	rc = role_list_create(pdb->p.p_roles.table);
+	rc = role_list_create(pdb->p_roles.table);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = typealias_list_create(&pdb->p);
+	rc = typealias_list_create(pdb);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = polcaps_to_cil(&pdb->p);
+	rc = polcaps_to_cil(pdb);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = ocontexts_to_cil(&pdb->p);
+	rc = ocontexts_to_cil(pdb);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = genfscon_to_cil(&pdb->p);
+	rc = genfscon_to_cil(pdb);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = seusers_to_cil(mod_pkg);
+	// now print everything that is scoped
+	if (linked) {
+		rc = linked_blocks_to_cil(pdb);
+	} else {
+		rc = blocks_to_cil(pdb);
+	}
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = netfilter_contexts_to_cil(mod_pkg);
+	rc = 0;
+
+exit:
+	role_list_destroy();
+	typealias_list_destroy();
+
+	return rc;
+}
+
+int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
+{
+	int rc = -1;
+	struct sepol_policydb *pdb;
+
+	out_file = fp;
+
+	pdb = sepol_module_package_get_policy(mod_pkg);
+	if (pdb == NULL) {
+		log_err("Failed to get policydb");
+		rc = -1;
+		goto exit;
+	}
+
+	rc = sepol_module_policydb_to_cil(fp, &pdb->p, 0);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = user_extra_to_cil(mod_pkg);
+	rc = seusers_to_cil(mod_pkg);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	rc = file_contexts_to_cil(mod_pkg);
+	rc = netfilter_contexts_to_cil(mod_pkg);
 	if (rc != 0) {
 		goto exit;
 	}
 
-	// now print everything that is scoped
-	rc = blocks_to_cil(&pdb->p);
+	rc = user_extra_to_cil(mod_pkg);
+	if (rc != 0) {
+		goto exit;
+	}
+
+	rc = file_contexts_to_cil(mod_pkg);
 	if (rc != 0) {
 		goto exit;
 	}
@@ -3756,9 +3896,6 @@ int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg)
 	rc = 0;
 
 exit:
-	role_list_destroy();
-	typealias_list_destroy();
-
 	return rc;
 }
 
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/3 v3] checkpolicy: Add support for generating CIL
  2015-03-31 17:17 [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy James Carter
  2015-03-31 17:17 ` [PATCH 1/3 v3] libsepol, policycoreutils: Move functions to convert a module package to CIL James Carter
  2015-03-31 17:18 ` [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb James Carter
@ 2015-03-31 17:18 ` James Carter
  2015-03-31 19:20   ` Steve Lawrence
  2015-03-31 19:15 ` [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy Yuli Khodorkovskiy
  3 siblings, 1 reply; 11+ messages in thread
From: James Carter @ 2015-03-31 17:18 UTC (permalink / raw)
  To: selinux

Add support to checkpolicy and checkmodule for generating CIL as their
output.

Add new options "-C" and "--cil" to specify CIL as the output format.

Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
---
 checkpolicy/checkmodule.c | 59 +++++++++++++++++++++--------------
 checkpolicy/checkpolicy.c | 79 ++++++++++++++++++++++++++++++-----------------
 2 files changed, 86 insertions(+), 52 deletions(-)

diff --git a/checkpolicy/checkmodule.c b/checkpolicy/checkmodule.c
index 0255928..b1be640 100644
--- a/checkpolicy/checkmodule.c
+++ b/checkpolicy/checkmodule.c
@@ -20,6 +20,7 @@
 #include <errno.h>
 #include <sys/mman.h>
 
+#include <sepol/module_to_cil.h>
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/services.h>
 #include <sepol/policydb/conditional.h>
@@ -108,20 +109,9 @@ static int read_binary_policy(policydb_t * p, const char *file, const char *prog
 	return 0;
 }
 
-static int write_binary_policy(policydb_t * p, const char *file, char *progname)
+static int write_binary_policy(policydb_t * p, FILE *outfp)
 {
-	FILE *outfp = NULL;
 	struct policy_file pf;
-	int ret;
-
-	printf("%s:  writing binary representation (version %d) to %s\n",
-	       progname, policyvers, file);
-
-	outfp = fopen(file, "w");
-	if (!outfp) {
-		perror(file);
-		exit(1);
-	}
 
 	p->policy_type = policy_type;
 	p->policyvers = policyvers;
@@ -130,13 +120,7 @@ static int write_binary_policy(policydb_t * p, const char *file, char *progname)
 	policy_file_init(&pf);
 	pf.type = PF_USE_STDIO;
 	pf.fp = outfp;
-	ret = policydb_write(p, &pf);
-	if (ret) {
-		fprintf(stderr, "%s:  error writing %s\n", progname, file);
-		return -1;
-	}
-	fclose(outfp);
-	return 0;
+	return policydb_write(p, &pf);
 }
 
 static void usage(char *progname)
@@ -162,7 +146,7 @@ static void usage(char *progname)
 int main(int argc, char **argv)
 {
 	const char *file = txtfile, *outfile = NULL;
-	unsigned int binary = 0;
+	unsigned int binary = 0, cil = 0;
 	int ch;
 	int show_version = 0;
 	policydb_t modpolicydb;
@@ -173,10 +157,11 @@ int main(int argc, char **argv)
 		{"version", no_argument, NULL, 'V'},
 		{"handle-unknown", required_argument, NULL, 'U'},
 		{"mls", no_argument, NULL, 'M'},
+		{"cil", no_argument, NULL, 'C'},
 		{NULL, 0, NULL, 0}
 	};
 
-	while ((ch = getopt_long(argc, argv, "ho:bVU:mM", long_options, NULL)) != -1) {
+	while ((ch = getopt_long(argc, argv, "ho:bVU:mMC", long_options, NULL)) != -1) {
 		switch (ch) {
 		case 'h':
 			usage(argv[0]);
@@ -212,6 +197,9 @@ int main(int argc, char **argv)
 		case 'M':
 			mlspol = 1;
 			break;
+		case 'C':
+			cil = 1;
+			break;
 		default:
 			usage(argv[0]);
 		}
@@ -269,7 +257,7 @@ int main(int argc, char **argv)
 		}
 	}
 
-	if (modpolicydb.policy_type == POLICY_BASE) {
+	if (modpolicydb.policy_type == POLICY_BASE && !cil) {
 		/* Verify that we can successfully expand the base module. */
 		policydb_t kernpolicydb;
 
@@ -295,10 +283,33 @@ int main(int argc, char **argv)
 
 	printf("%s:  policy configuration loaded\n", argv[0]);
 
-	if (outfile &&
-	    write_binary_policy(&modpolicydb, outfile, argv[0]) == -1) {
+	if (outfile) {
+		FILE *outfp = fopen(outfile, "w");
+
+		if (!outfp) {
+			perror(outfile);
+			exit(1);
+		}
+
+		if (!cil) {
+			printf("%s:  writing binary representation (version %d) to %s\n",
+				   argv[0], policyvers, file);
+
+			if (write_binary_policy(&modpolicydb, outfp) != 0) {
+				fprintf(stderr, "%s:  error writing %s\n", argv[0], outfile);
+				exit(1);
+			}
+		} else {
+			printf("%s:  writing CIL to %s\n",argv[0], outfile);
+			sepol_module_policydb_to_cil(outfp, &modpolicydb, 0);
+		}
+
+		fclose(outfp);
+	} else if (cil) {
+		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
 		exit(1);
 	}
+
 	policydb_destroy(&modpolicydb);
 
 	return 0;
diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
index 61a2e89..d96399d 100644
--- a/checkpolicy/checkpolicy.c
+++ b/checkpolicy/checkpolicy.c
@@ -74,6 +74,7 @@
 #include <ctype.h>
 #endif
 
+#include <sepol/module_to_cil.h>
 #include <sepol/policydb/policydb.h>
 #include <sepol/policydb/services.h>
 #include <sepol/policydb/conditional.h>
@@ -376,6 +377,7 @@ static int check_level(hashtab_key_t key, hashtab_datum_t datum, void *arg __att
 
 int main(int argc, char **argv)
 {
+	policydb_t parse_policy;
 	sepol_security_class_t tclass;
 	sepol_security_id_t ssid, tsid, *sids, oldsid, newsid, tasksid;
 	sepol_security_context_t scontext;
@@ -386,7 +388,7 @@ int main(int argc, char **argv)
 	size_t scontext_len, pathlen;
 	unsigned int i;
 	unsigned int protocol, port;
-	unsigned int binary = 0, debug = 0;
+	unsigned int binary = 0, debug = 0, cil = 0;
 	struct val_to_name v;
 	int ret, ch, fd, target = SEPOL_TARGET_SELINUX;
 	unsigned int nel, uret;
@@ -408,11 +410,12 @@ int main(int argc, char **argv)
 		{"version", no_argument, NULL, 'V'},
 		{"handle-unknown", required_argument, NULL, 'U'},
 		{"mls", no_argument, NULL, 'M'},
+		{"cil", no_argument, NULL, 'C'},
 		{"help", no_argument, NULL, 'h'},
 		{NULL, 0, NULL, 0}
 	};
 
-	while ((ch = getopt_long(argc, argv, "o:t:dbU:MVc:h", long_options, NULL)) != -1) {
+	while ((ch = getopt_long(argc, argv, "o:t:dbU:MCVc:h", long_options, NULL)) != -1) {
 		switch (ch) {
 		case 'o':
 			outfile = optarg;
@@ -455,6 +458,9 @@ int main(int argc, char **argv)
 		case 'M':
 			mlspol = 1;
 			break;
+		case 'C':
+			cil = 1;
+			break;
 		case 'c':{
 				long int n;
 				errno = 0;
@@ -505,6 +511,11 @@ int main(int argc, char **argv)
 	sepol_set_sidtab(&sidtab);
 
 	if (binary) {
+		if (cil) {
+			fprintf(stderr,	"%s:  Converting kernel policy to CIL is not supported\n",
+				argv[0]);
+			exit(1);
+		}
 		fd = open(file, O_RDONLY);
 		if (fd < 0) {
 			fprintf(stderr, "Can't open '%s':  %s\n",
@@ -557,8 +568,6 @@ int main(int argc, char **argv)
 			}
 		}
 	} else {
-		policydb_t parse_policy;
-
 		if (policydb_init(&parse_policy))
 			exit(1);
 		/* We build this as a base policy first since that is all the parser understands */
@@ -577,23 +586,24 @@ int main(int argc, char **argv)
 		if (hashtab_map(policydbp->p_levels.table, check_level, NULL))
 			exit(1);
 
-		if (policydb_init(&policydb)) {
-			fprintf(stderr, "%s:  policydb_init failed\n", argv[0]);
-			exit(1);
-		}
-
 		/* Linking takes care of optional avrule blocks */
-		if (link_modules(NULL, &parse_policy, NULL, 0, 0)) {
+		if (link_modules(NULL, policydbp, NULL, 0, 0)) {
 			fprintf(stderr, "Error while resolving optionals\n");
 			exit(1);
 		}
 
-		if (expand_module(NULL, &parse_policy, &policydb, 0, 1)) {
-			fprintf(stderr, "Error while expanding policy\n");
-			exit(1);
+		if (!cil) {
+			if (policydb_init(&policydb)) {
+				fprintf(stderr, "%s:  policydb_init failed\n", argv[0]);
+				exit(1);
+			}
+			if (expand_module(NULL, policydbp, &policydb, 0, 1)) {
+				fprintf(stderr, "Error while expanding policy\n");
+				exit(1);
+			}
+			policydb_destroy(policydbp);
+			policydbp = &policydb;
 		}
-		policydb_destroy(&parse_policy);
-		policydbp = &policydb;
 	}
 
 	if (policydb_load_isids(&policydb, &sidtab))
@@ -602,29 +612,42 @@ int main(int argc, char **argv)
 	printf("%s:  policy configuration loaded\n", argv[0]);
 
 	if (outfile) {
-		printf
-		    ("%s:  writing binary representation (version %d) to %s\n",
-		     argv[0], policyvers, outfile);
 		outfp = fopen(outfile, "w");
 		if (!outfp) {
 			perror(outfile);
 			exit(1);
 		}
 
-		policydb.policy_type = POLICY_KERN;
 		policydb.policyvers = policyvers;
 
-		policy_file_init(&pf);
-		pf.type = PF_USE_STDIO;
-		pf.fp = outfp;
-		ret = policydb_write(&policydb, &pf);
-		if (ret) {
-			fprintf(stderr, "%s:  error writing %s\n",
-				argv[0], outfile);
-			exit(1);
+		if (!cil) {
+			printf
+				("%s:  writing binary representation (version %d) to %s\n",
+				 argv[0], policyvers, outfile);
+			policydb.policy_type = POLICY_KERN;
+
+			policy_file_init(&pf);
+			pf.type = PF_USE_STDIO;
+			pf.fp = outfp;
+			ret = policydb_write(&policydb, &pf);
+			if (ret) {
+				fprintf(stderr, "%s:  error writing %s\n",
+						argv[0], outfile);
+				exit(1);
+			}
+		} else {
+			printf("%s:  writing CIL to %s\n",argv[0], outfile);
+			sepol_module_policydb_to_cil(outfp, policydbp, 1);
+		}
+
+		if (outfile) {
+			fclose(outfp);
 		}
-		fclose(outfp);
+	} else if (cil) {
+		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
+		exit(1);
 	}
+
 	if (!debug) {
 		policydb_destroy(&policydb);
 		exit(0);
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* RE: [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy
  2015-03-31 17:17 [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy James Carter
                   ` (2 preceding siblings ...)
  2015-03-31 17:18 ` [PATCH 3/3 v3] checkpolicy: Add support for generating CIL James Carter
@ 2015-03-31 19:15 ` Yuli Khodorkovskiy
  2015-03-31 19:25   ` James Carter
  3 siblings, 1 reply; 11+ messages in thread
From: Yuli Khodorkovskiy @ 2015-03-31 19:15 UTC (permalink / raw)
  To: James Carter, selinux



>-----Original Message-----
>From: Selinux [mailto:selinux-bounces@tycho.nsa.gov] On Behalf Of James
>Carter
>Sent: Tuesday, March 31, 2015 1:18 PM
>To: selinux@tycho.nsa.gov
>Subject: [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add
>support for generating CIL to libsepol and checkpolicy
>
>V3 fixes another whitespace issue.
>V2 fixes some whitespace issues and make the new libsepol file LGPL
>instead of GPL.
>
>This patch set moves the code to generate CIL from pp.c in
>policycoreutils/hll/pp to libsepol, adds a new function to generate CIL from
>a module policydb, and modifies checkpolicy and checkmodule to support
>generating CIL as their output.
>
>The primary motivation of this work is to allow SE for Android to use the
>CIl compiler. Converting the policy.conf to CIL and then compiling to the
>kernel binary policy results in a policy that is about 20% smaller. The
>smaller size is because type expressions with negations are converted to
>type attribute sets in CIL instead of being expanded.
>
>James Carter (3):
>  libsepol, policycoreutils: Move functions to convert a module package
>    to CIL
>  libsepol: add function to generate CIL from a module policydb
>  checkpolicy: Add support for generating CIL
>
> checkpolicy/checkmodule.c              |   59 +-
> checkpolicy/checkpolicy.c              |   79 +-
> libsepol/include/sepol/module_to_cil.h |    8 +
> libsepol/src/module_to_cil.c           | 4010
>++++++++++++++++++++++++++++++++
> policycoreutils/hll/pp/pp.c            | 3830 +-----------------------------
> 5 files changed, 4107 insertions(+), 3879 deletions(-)  create mode 100644
>libsepol/include/sepol/module_to_cil.h
> create mode 100644 libsepol/src/module_to_cil.c
>
>--
>1.9.3
>
>_______________________________________________
>Selinux mailing list
>Selinux@tycho.nsa.gov
>To unsubscribe, send email to Selinux-leave@tycho.nsa.gov.
>To get help, send an email containing "help" to Selinux-
>request@tycho.nsa.gov.

Jim,

Can you modify the usage and man pages for checkpolicy and checkmodule to include the new CIL options?

Does it make sense to add sepol_ppfile_to_module_package and sepol_module_package_to_cil to the libsepol map file and make pp link dynamically with libsepol?

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3 v3] checkpolicy: Add support for generating CIL
  2015-03-31 17:18 ` [PATCH 3/3 v3] checkpolicy: Add support for generating CIL James Carter
@ 2015-03-31 19:20   ` Steve Lawrence
  2015-03-31 19:28     ` James Carter
  0 siblings, 1 reply; 11+ messages in thread
From: Steve Lawrence @ 2015-03-31 19:20 UTC (permalink / raw)
  To: James Carter, selinux

On 03/31/2015 01:18 PM, James Carter wrote:
> Add support to checkpolicy and checkmodule for generating CIL as their
> output.
> 
> Add new options "-C" and "--cil" to specify CIL as the output format.
> 
> Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
> ---
>  checkpolicy/checkmodule.c | 59 +++++++++++++++++++++--------------
>  checkpolicy/checkpolicy.c | 79 ++++++++++++++++++++++++++++++-----------------
>  2 files changed, 86 insertions(+), 52 deletions(-)
> 
> diff --git a/checkpolicy/checkmodule.c b/checkpolicy/checkmodule.c
> index 0255928..b1be640 100644
> --- a/checkpolicy/checkmodule.c
> +++ b/checkpolicy/checkmodule.c

[snip]

> @@ -295,10 +283,33 @@ int main(int argc, char **argv)
>  
>  	printf("%s:  policy configuration loaded\n", argv[0]);
>  
> -	if (outfile &&
> -	    write_binary_policy(&modpolicydb, outfile, argv[0]) == -1) {
> +	if (outfile) {
> +		FILE *outfp = fopen(outfile, "w");
> +
> +		if (!outfp) {
> +			perror(outfile);
> +			exit(1);
> +		}
> +
> +		if (!cil) {
> +			printf("%s:  writing binary representation (version %d) to %s\n",
> +				   argv[0], policyvers, file);
> +
> +			if (write_binary_policy(&modpolicydb, outfp) != 0) {
> +				fprintf(stderr, "%s:  error writing %s\n", argv[0], outfile);
> +				exit(1);
> +			}
> +		} else {
> +			printf("%s:  writing CIL to %s\n",argv[0], outfile);
> +			sepol_module_policydb_to_cil(outfp, &modpolicydb, 0);

Maybe add a check for the return code of sepol_module_policydb_to_cil
and fprintf an error message, like above with write_binary_policy?

> +		}
> +
> +		fclose(outfp);
> +	} else if (cil) {
> +		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
>  		exit(1);
>  	}
> +
>  	policydb_destroy(&modpolicydb);
>  
>  	return 0;
> diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
> index 61a2e89..d96399d 100644
> --- a/checkpolicy/checkpolicy.c
> +++ b/checkpolicy/checkpolicy.c

[snip]

> @@ -602,29 +612,42 @@ int main(int argc, char **argv)
>  	printf("%s:  policy configuration loaded\n", argv[0]);
>  
>  	if (outfile) {
> -		printf
> -		    ("%s:  writing binary representation (version %d) to %s\n",
> -		     argv[0], policyvers, outfile);
>  		outfp = fopen(outfile, "w");
>  		if (!outfp) {
>  			perror(outfile);
>  			exit(1);
>  		}
>  
> -		policydb.policy_type = POLICY_KERN;
>  		policydb.policyvers = policyvers;
>  
> -		policy_file_init(&pf);
> -		pf.type = PF_USE_STDIO;
> -		pf.fp = outfp;
> -		ret = policydb_write(&policydb, &pf);
> -		if (ret) {
> -			fprintf(stderr, "%s:  error writing %s\n",
> -				argv[0], outfile);
> -			exit(1);
> +		if (!cil) {
> +			printf
> +				("%s:  writing binary representation (version %d) to %s\n",
> +				 argv[0], policyvers, outfile);
> +			policydb.policy_type = POLICY_KERN;
> +
> +			policy_file_init(&pf);
> +			pf.type = PF_USE_STDIO;
> +			pf.fp = outfp;
> +			ret = policydb_write(&policydb, &pf);
> +			if (ret) {
> +				fprintf(stderr, "%s:  error writing %s\n",
> +						argv[0], outfile);
> +				exit(1);
> +			}
> +		} else {
> +			printf("%s:  writing CIL to %s\n",argv[0], outfile);
> +			sepol_module_policydb_to_cil(outfp, policydbp, 1);

Same issue here. Add error checking/message?

> +		}
> +
> +		if (outfile) {
> +			fclose(outfp);
>  		}
> -		fclose(outfp);
> +	} else if (cil) {
> +		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
> +		exit(1);
>  	}
> +
>  	if (!debug) {
>  		policydb_destroy(&policydb);
>  		exit(0);
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy
  2015-03-31 19:15 ` [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy Yuli Khodorkovskiy
@ 2015-03-31 19:25   ` James Carter
  0 siblings, 0 replies; 11+ messages in thread
From: James Carter @ 2015-03-31 19:25 UTC (permalink / raw)
  To: Yuli Khodorkovskiy, selinux

On 03/31/2015 03:15 PM, Yuli Khodorkovskiy wrote:
>
>
>> -----Original Message-----
>> From: Selinux [mailto:selinux-bounces@tycho.nsa.gov] On Behalf Of James
>> Carter
>> Sent: Tuesday, March 31, 2015 1:18 PM
>> To: selinux@tycho.nsa.gov
>> Subject: [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add
>> support for generating CIL to libsepol and checkpolicy
>>
>> V3 fixes another whitespace issue.
>> V2 fixes some whitespace issues and make the new libsepol file LGPL
>> instead of GPL.
>>
>> This patch set moves the code to generate CIL from pp.c in
>> policycoreutils/hll/pp to libsepol, adds a new function to generate CIL from
>> a module policydb, and modifies checkpolicy and checkmodule to support
>> generating CIL as their output.
>>
>> The primary motivation of this work is to allow SE for Android to use the
>> CIl compiler. Converting the policy.conf to CIL and then compiling to the
>> kernel binary policy results in a policy that is about 20% smaller. The
>> smaller size is because type expressions with negations are converted to
>> type attribute sets in CIL instead of being expanded.
>>
>> James Carter (3):
>>   libsepol, policycoreutils: Move functions to convert a module package
>>     to CIL
>>   libsepol: add function to generate CIL from a module policydb
>>   checkpolicy: Add support for generating CIL
>>
>> checkpolicy/checkmodule.c              |   59 +-
>> checkpolicy/checkpolicy.c              |   79 +-
>> libsepol/include/sepol/module_to_cil.h |    8 +
>> libsepol/src/module_to_cil.c           | 4010
>> ++++++++++++++++++++++++++++++++
>> policycoreutils/hll/pp/pp.c            | 3830 +-----------------------------
>> 5 files changed, 4107 insertions(+), 3879 deletions(-)  create mode 100644
>> libsepol/include/sepol/module_to_cil.h
>> create mode 100644 libsepol/src/module_to_cil.c
>>
>> --
>> 1.9.3
>>
>> _______________________________________________
>> Selinux mailing list
>> Selinux@tycho.nsa.gov
>> To unsubscribe, send email to Selinux-leave@tycho.nsa.gov.
>> To get help, send an email containing "help" to Selinux-
>> request@tycho.nsa.gov.
>
> Jim,
>
> Can you modify the usage and man pages for checkpolicy and checkmodule to include the new CIL options?
>

Yes. I should have done that.

> Does it make sense to add sepol_ppfile_to_module_package and sepol_module_package_to_cil to the libsepol map file and make pp link dynamically with libsepol?
>
Makes sense to me.

Thanks for the feedback.

-- 
James Carter <jwcart2@tycho.nsa.gov>
National Security Agency

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb
  2015-03-31 17:18 ` [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb James Carter
@ 2015-03-31 19:26   ` Steve Lawrence
  2015-03-31 19:42     ` James Carter
  0 siblings, 1 reply; 11+ messages in thread
From: Steve Lawrence @ 2015-03-31 19:26 UTC (permalink / raw)
  To: James Carter, selinux

On 03/31/2015 01:18 PM, James Carter wrote:
> Add a new function, sepol_module_policydb_to_cil, that generates
> CIL from a module (not kernel) policydb. Refactor
> sepol_module_package_to_cil() to use the new function.
> 
> Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
> ---
>  libsepol/include/sepol/module_to_cil.h |   2 +
>  libsepol/src/module_to_cil.c           | 367 ++++++++++++++++++++++-----------
>  2 files changed, 254 insertions(+), 115 deletions(-)
> 
> diff --git a/libsepol/include/sepol/module_to_cil.h b/libsepol/include/sepol/module_to_cil.h
> index 1d0225c..18bb3bf 100644
> --- a/libsepol/include/sepol/module_to_cil.h
> +++ b/libsepol/include/sepol/module_to_cil.h
> @@ -1,6 +1,8 @@
>  #include <stdlib.h>
>  
>  #include <sepol/module.h>
> +#include <sepol/policydb/policydb.h>
>  
> +int sepol_module_policydb_to_cil(FILE *fp, struct policydb *pdb, int linked);
>  int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg);
>  int sepol_ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg);

One potential issue with these functions is that they aren't thread safe
due to the use of globals with role_list and typealias_lists. When pp
was a single binary this wasn't a big deal (and greatly simplified the
code), but now that it's part of libsepol it could potentially cause
issues. Not sure if it's worth the complexity to change it so either
they aren't global variables, or make them thread local variables.
Alternatively, we could just document the functions as not thread safe.

- Steve

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/3 v3] checkpolicy: Add support for generating CIL
  2015-03-31 19:20   ` Steve Lawrence
@ 2015-03-31 19:28     ` James Carter
  0 siblings, 0 replies; 11+ messages in thread
From: James Carter @ 2015-03-31 19:28 UTC (permalink / raw)
  To: Steve Lawrence, selinux

On 03/31/2015 03:20 PM, Steve Lawrence wrote:
> On 03/31/2015 01:18 PM, James Carter wrote:
>> Add support to checkpolicy and checkmodule for generating CIL as their
>> output.
>>
>> Add new options "-C" and "--cil" to specify CIL as the output format.
>>
>> Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
>> ---
>>   checkpolicy/checkmodule.c | 59 +++++++++++++++++++++--------------
>>   checkpolicy/checkpolicy.c | 79 ++++++++++++++++++++++++++++++-----------------
>>   2 files changed, 86 insertions(+), 52 deletions(-)
>>
>> diff --git a/checkpolicy/checkmodule.c b/checkpolicy/checkmodule.c
>> index 0255928..b1be640 100644
>> --- a/checkpolicy/checkmodule.c
>> +++ b/checkpolicy/checkmodule.c
>
> [snip]
>
>> @@ -295,10 +283,33 @@ int main(int argc, char **argv)
>>
>>   	printf("%s:  policy configuration loaded\n", argv[0]);
>>
>> -	if (outfile &&
>> -	    write_binary_policy(&modpolicydb, outfile, argv[0]) == -1) {
>> +	if (outfile) {
>> +		FILE *outfp = fopen(outfile, "w");
>> +
>> +		if (!outfp) {
>> +			perror(outfile);
>> +			exit(1);
>> +		}
>> +
>> +		if (!cil) {
>> +			printf("%s:  writing binary representation (version %d) to %s\n",
>> +				   argv[0], policyvers, file);
>> +
>> +			if (write_binary_policy(&modpolicydb, outfp) != 0) {
>> +				fprintf(stderr, "%s:  error writing %s\n", argv[0], outfile);
>> +				exit(1);
>> +			}
>> +		} else {
>> +			printf("%s:  writing CIL to %s\n",argv[0], outfile);
>> +			sepol_module_policydb_to_cil(outfp, &modpolicydb, 0);
>
> Maybe add a check for the return code of sepol_module_policydb_to_cil
> and fprintf an error message, like above with write_binary_policy?
>
>> +		}
>> +
>> +		fclose(outfp);
>> +	} else if (cil) {
>> +		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
>>   		exit(1);
>>   	}
>> +
>>   	policydb_destroy(&modpolicydb);
>>
>>   	return 0;
>> diff --git a/checkpolicy/checkpolicy.c b/checkpolicy/checkpolicy.c
>> index 61a2e89..d96399d 100644
>> --- a/checkpolicy/checkpolicy.c
>> +++ b/checkpolicy/checkpolicy.c
>
> [snip]
>
>> @@ -602,29 +612,42 @@ int main(int argc, char **argv)
>>   	printf("%s:  policy configuration loaded\n", argv[0]);
>>
>>   	if (outfile) {
>> -		printf
>> -		    ("%s:  writing binary representation (version %d) to %s\n",
>> -		     argv[0], policyvers, outfile);
>>   		outfp = fopen(outfile, "w");
>>   		if (!outfp) {
>>   			perror(outfile);
>>   			exit(1);
>>   		}
>>
>> -		policydb.policy_type = POLICY_KERN;
>>   		policydb.policyvers = policyvers;
>>
>> -		policy_file_init(&pf);
>> -		pf.type = PF_USE_STDIO;
>> -		pf.fp = outfp;
>> -		ret = policydb_write(&policydb, &pf);
>> -		if (ret) {
>> -			fprintf(stderr, "%s:  error writing %s\n",
>> -				argv[0], outfile);
>> -			exit(1);
>> +		if (!cil) {
>> +			printf
>> +				("%s:  writing binary representation (version %d) to %s\n",
>> +				 argv[0], policyvers, outfile);
>> +			policydb.policy_type = POLICY_KERN;
>> +
>> +			policy_file_init(&pf);
>> +			pf.type = PF_USE_STDIO;
>> +			pf.fp = outfp;
>> +			ret = policydb_write(&policydb, &pf);
>> +			if (ret) {
>> +				fprintf(stderr, "%s:  error writing %s\n",
>> +						argv[0], outfile);
>> +				exit(1);
>> +			}
>> +		} else {
>> +			printf("%s:  writing CIL to %s\n",argv[0], outfile);
>> +			sepol_module_policydb_to_cil(outfp, policydbp, 1);
>
> Same issue here. Add error checking/message?

Yes, both of these should be checking for an error and printing an error message.

Jim

>
>> +		}
>> +
>> +		if (outfile) {
>> +			fclose(outfp);
>>   		}
>> -		fclose(outfp);
>> +	} else if (cil) {
>> +		fprintf(stderr, "%s:  No file to write CIL was specified\n", argv[0]);
>> +		exit(1);
>>   	}
>> +
>>   	if (!debug) {
>>   		policydb_destroy(&policydb);
>>   		exit(0);
>>


-- 
James Carter <jwcart2@tycho.nsa.gov>
National Security Agency

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb
  2015-03-31 19:26   ` Steve Lawrence
@ 2015-03-31 19:42     ` James Carter
  2015-04-01 12:13       ` Steve Lawrence
  0 siblings, 1 reply; 11+ messages in thread
From: James Carter @ 2015-03-31 19:42 UTC (permalink / raw)
  To: Steve Lawrence, selinux

On 03/31/2015 03:26 PM, Steve Lawrence wrote:
> On 03/31/2015 01:18 PM, James Carter wrote:
>> Add a new function, sepol_module_policydb_to_cil, that generates
>> CIL from a module (not kernel) policydb. Refactor
>> sepol_module_package_to_cil() to use the new function.
>>
>> Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
>> ---
>>   libsepol/include/sepol/module_to_cil.h |   2 +
>>   libsepol/src/module_to_cil.c           | 367 ++++++++++++++++++++++-----------
>>   2 files changed, 254 insertions(+), 115 deletions(-)
>>
>> diff --git a/libsepol/include/sepol/module_to_cil.h b/libsepol/include/sepol/module_to_cil.h
>> index 1d0225c..18bb3bf 100644
>> --- a/libsepol/include/sepol/module_to_cil.h
>> +++ b/libsepol/include/sepol/module_to_cil.h
>> @@ -1,6 +1,8 @@
>>   #include <stdlib.h>
>>
>>   #include <sepol/module.h>
>> +#include <sepol/policydb/policydb.h>
>>
>> +int sepol_module_policydb_to_cil(FILE *fp, struct policydb *pdb, int linked);
>>   int sepol_module_package_to_cil(FILE *fp, struct sepol_module_package *mod_pkg);
>>   int sepol_ppfile_to_module_package(FILE *fp, struct sepol_module_package **mod_pkg);
>
> One potential issue with these functions is that they aren't thread safe
> due to the use of globals with role_list and typealias_lists. When pp
> was a single binary this wasn't a big deal (and greatly simplified the
> code), but now that it's part of libsepol it could potentially cause
> issues. Not sure if it's worth the complexity to change it so either
> they aren't global variables, or make them thread local variables.
> Alternatively, we could just document the functions as not thread safe.

I was initially going to remove the globals but I wasn't sure if they were a 
problem and I wanted to minimize the changes to the code to make review easier.

I am willing to convert them. Should I do that as a part of this patch set?
Jim


-- 
James Carter <jwcart2@tycho.nsa.gov>
National Security Agency

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb
  2015-03-31 19:42     ` James Carter
@ 2015-04-01 12:13       ` Steve Lawrence
  0 siblings, 0 replies; 11+ messages in thread
From: Steve Lawrence @ 2015-04-01 12:13 UTC (permalink / raw)
  To: James Carter, selinux

On 03/31/2015 03:42 PM, James Carter wrote:
> On 03/31/2015 03:26 PM, Steve Lawrence wrote:
>> On 03/31/2015 01:18 PM, James Carter wrote:
>>> Add a new function, sepol_module_policydb_to_cil, that generates
>>> CIL from a module (not kernel) policydb. Refactor
>>> sepol_module_package_to_cil() to use the new function.
>>>
>>> Signed-off-by: James Carter <jwcart2@tycho.nsa.gov>
>>> ---
>>>   libsepol/include/sepol/module_to_cil.h |   2 +
>>>   libsepol/src/module_to_cil.c           | 367
>>> ++++++++++++++++++++++-----------
>>>   2 files changed, 254 insertions(+), 115 deletions(-)
>>>
>>> diff --git a/libsepol/include/sepol/module_to_cil.h
>>> b/libsepol/include/sepol/module_to_cil.h
>>> index 1d0225c..18bb3bf 100644
>>> --- a/libsepol/include/sepol/module_to_cil.h
>>> +++ b/libsepol/include/sepol/module_to_cil.h
>>> @@ -1,6 +1,8 @@
>>>   #include <stdlib.h>
>>>
>>>   #include <sepol/module.h>
>>> +#include <sepol/policydb/policydb.h>
>>>
>>> +int sepol_module_policydb_to_cil(FILE *fp, struct policydb *pdb, int
>>> linked);
>>>   int sepol_module_package_to_cil(FILE *fp, struct
>>> sepol_module_package *mod_pkg);
>>>   int sepol_ppfile_to_module_package(FILE *fp, struct
>>> sepol_module_package **mod_pkg);
>>
>> One potential issue with these functions is that they aren't thread safe
>> due to the use of globals with role_list and typealias_lists. When pp
>> was a single binary this wasn't a big deal (and greatly simplified the
>> code), but now that it's part of libsepol it could potentially cause
>> issues. Not sure if it's worth the complexity to change it so either
>> they aren't global variables, or make them thread local variables.
>> Alternatively, we could just document the functions as not thread safe.
> 
> I was initially going to remove the globals but I wasn't sure if they
> were a problem and I wanted to minimize the changes to the code to make
> review easier.
> 
> I am willing to convert them. Should I do that as a part of this patch set?
> Jim
> 
> 

I don't think it needs to be done as part of this patchset. The only
things using these functions are checkpolicy and pp, which aren't
threaded, so there's no potential for problems at the moment.

- Steve

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2015-04-01 12:13 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-31 17:17 [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy James Carter
2015-03-31 17:17 ` [PATCH 1/3 v3] libsepol, policycoreutils: Move functions to convert a module package to CIL James Carter
2015-03-31 17:18 ` [PATCH 2/3 v3] libsepol: add function to generate CIL from a module policydb James Carter
2015-03-31 19:26   ` Steve Lawrence
2015-03-31 19:42     ` James Carter
2015-04-01 12:13       ` Steve Lawrence
2015-03-31 17:18 ` [PATCH 3/3 v3] checkpolicy: Add support for generating CIL James Carter
2015-03-31 19:20   ` Steve Lawrence
2015-03-31 19:28     ` James Carter
2015-03-31 19:15 ` [PATCH 0/3 v3] libsepol, policycoreutils, and checkpolicy: Add support for generating CIL to libsepol and checkpolicy Yuli Khodorkovskiy
2015-03-31 19:25   ` James Carter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.