Move code to convert a policy module to CIL from the policy package to CIL conversion tool, pp, in policycoreutils to libsepol and add a "sepol_" prefix to the functions. Convert pp to use the renamed functions in libsepol. Signed-off-by: James Carter <jwcart2@xxxxxxxxxxxxx> --- libsepol/include/sepol/module_to_cil.h | 6 + libsepol/src/module_to_cil.c | 3871 ++++++++++++++++++++++++++++++++ policycoreutils/hll/pp/pp.c | 3830 +------------------------------ 3 files changed, 3880 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..f1e13b5 --- /dev/null +++ b/libsepol/src/module_to_cil.c @@ -0,0 +1,3871 @@ +/* + * Copyright (C) 2014 Tresys Technology, LLC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, 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", ®ex, &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", ®ex, &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; } -- James Carter <jwcart2@xxxxxxxxxxxxx> National Security Agency _______________________________________________ Selinux mailing list Selinux@xxxxxxxxxxxxx To unsubscribe, send email to Selinux-leave@xxxxxxxxxxxxx. To get help, send an email containing "help" to Selinux-request@xxxxxxxxxxxxx.