From f0e0a94e032e55c13bc54f1cffe243f04872278e Mon Sep 17 00:00:00 2001 From: William Roberts <w.roberts@sta.samsung.com> Date: Mon, 27 Aug 2012 15:41:15 -0700 Subject: [PATCH] Support overrides in seapp_contexts Provides support for overriding seapp_contexts declerations in per device seapp_contexts files. Change-Id: I23a0ffa1d24f1ce57825b168f29a2e885d3e1c51 --- Android.mk | 12 +- check_seapp/Android.mk | 15 + check_seapp/check_seapp.c | 955 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 979 insertions(+), 3 deletions(-) create mode 100644 check_seapp/Android.mk create mode 100644 check_seapp/check_seapp.c diff --git a/Android.mk b/Android.mk index 5eb3925b3..5481541b2 100644 --- a/Android.mk +++ b/Android.mk @@ -1,6 +1,9 @@ ifeq ($(HAVE_SELINUX),true) LOCAL_PATH:= $(call my-dir) + +include $(call all-makefiles-under,$(LOCAL_PATH)) + include $(CLEAR_VARS) # SELinux policy version. @@ -71,13 +74,16 @@ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT) include $(BUILD_SYSTEM)/base_rules.mk -seapp_contexts := $(intermediates)/seapp_contexts -$(seapp_contexts): $(LOCAL_PATH)/seapp_contexts $(LOCAL_POLICY_SC) +seapp_contexts.conf := $(intermediates)/seapp_contexts.conf +$(seapp_contexts.conf): $(LOCAL_PATH)/seapp_contexts $(LOCAL_POLICY_SC) @mkdir -p $(dir $@) $(hide) m4 -s $^ > $@ -seapp_contexts := +$(LOCAL_BUILT_MODULE) : $(seapp_contexts.conf) $(TARGET_ROOT_OUT)/sepolicy.$(POLICYVERS) $(HOST_OUT_EXECUTABLES)/checkseapp + @mkdir -p $(dir $@) + $(hide) $(HOST_OUT_EXECUTABLES)/checkseapp -p $(TARGET_ROOT_OUT)/sepolicy.24 -o $@ $< +seapp_contexts.conf := ################################## include $(CLEAR_VARS) diff --git a/check_seapp/Android.mk b/check_seapp/Android.mk new file mode 100644 index 000000000..e4a4acc00 --- /dev/null +++ b/check_seapp/Android.mk @@ -0,0 +1,15 @@ +## +# checkseapp +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := checkseapp +LOCAL_MODULE_TAGS := optional +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../libsepol/include/ +LOCAL_CFLAGS := -DLINK_SEPOL_STATIC +LOCAL_SRC_FILES := check_seapp/check_seapp.c +LOCAL_STATIC_LIBRARIES := libsepol +LOCAL_MODULE_CLASS := EXECUTABLES + +include $(BUILD_HOST_EXECUTABLE) diff --git a/check_seapp/check_seapp.c b/check_seapp/check_seapp.c new file mode 100644 index 000000000..d398d9172 --- /dev/null +++ b/check_seapp/check_seapp.c @@ -0,0 +1,955 @@ +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <search.h> +#include <sepol/sepol.h> +#include <sepol/policydb/policydb.h> + +#define TABLE_SIZE 1024 +#define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map)) +#define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0) +#define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__) +#define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__) +#define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); } + +typedef struct line_order_list line_order_list; +typedef struct hash_entry hash_entry; +typedef enum key_dir key_dir; +typedef enum data_type data_type; +typedef enum rule_map_switch rule_map_switch; +typedef struct key_map key_map; +typedef struct kvp kvp; +typedef struct rule_map rule_map; +typedef struct policy_info policy_info; + +/** + * Whether or not the "key" from a key vaue pair is considered an + * input or an output. + */ +enum key_dir { + dir_in, dir_out +}; + +/** + * Used as options to rule_map_free() + * + * This is needed to get around the fact that GNU C's hash_map doesn't copy the key, so + * we cannot free a key when overrding rule_map's in the table. + */ +enum rule_map_switch { + rule_map_preserve_key, /** Used to preserve the key in the rule_map, ie don't free it*/ + rule_map_destroy_key /** Used when you need a full free of the rule_map structure*/ +}; + +/** + * The expected "type" of data the value in the key + * value pair should be. + */ +enum data_type { + dt_bool, dt_string +}; + +/** + * This list is used to store a double pointer to each + * hash table / line rule combination. This way a replacement + * in the hash table automatically updates the list. The list + * is also used to keep "first encountered" ordering amongst + * the encountered key value pairs in the rules file. + */ +struct line_order_list { + hash_entry *e; + line_order_list *next; +}; + +/** + * The workhorse of the logic. This struct maps key value pairs to + * an associated set of meta data maintained in rule_map_new() + */ +struct key_map { + char *name; + key_dir dir; + data_type type; + char *data; +}; + +/** + * Key value pair struct, this represents the raw kvp values coming + * from the rules files. + */ +struct kvp { + char *key; + char *value; +}; + +/** + * Rules are made up of meta data and an associated set of kvp stored in a + * key_map array. + */ +struct rule_map { + char *key; /** key value before hashing */ + int length; /** length of the key map */ + int lineno; /** Line number rule was encounter on */ + rule_map *next; /** next pointer used in hash table for chaining on collision */ + key_map m[]; /** key value mapping */ +}; + +struct hash_entry { + rule_map *r; /** The rule map to store at that location */ +}; + +/** + * Data associated for a policy file + */ +struct policy_info { + + char *policy_file_name; /** policy file path name */ + FILE *policy_file; /** file handle to the policy file */ + sepol_policydb_t *db; + sepol_policy_file_t *pf; + sepol_handle_t *handle; + sepol_context_t *con; +}; + +/** Set to !0 to enable verbose logging */ +static int logging_verbose = 0; + +/** file handle to the output file */ +static FILE *output_file = NULL; + +/** file handle to the input file */ +static FILE *input_file = NULL; + +/** output file path name */ +static char *out_file_name = NULL; + +/** input file path name */ +static char *in_file_name = NULL; + +static policy_info pol = { + .policy_file_name = NULL, + .policy_file = NULL, + .db = NULL, + .pf = NULL, + .handle = NULL, + .con = NULL +}; + +/** + * The heart of the mapping process, this must be updated if a new key value pair is added + * to a rule. + */ +key_map rules[] = { + /*Inputs*/ + { .name = "isSystemServer", .type = dt_bool, .dir = dir_in, .data = NULL }, + { .name = "user", .type = dt_string, .dir = dir_in, .data = NULL }, + { .name = "seinfo", .type = dt_string, .dir = dir_in, .data = NULL }, + { .name = "name", .type = dt_string, .dir = dir_in, .data = NULL }, + { .name = "sebool", .type = dt_string, .dir = dir_in, .data = NULL }, + /*Outputs*/ + { .name = "domain", .type = dt_string, .dir = dir_out, .data = NULL }, + { .name = "type", .type = dt_string, .dir = dir_out, .data = NULL }, + { .name = "levelFromUid", .type = dt_bool, .dir = dir_out, .data = NULL }, + { .name = "level", .type = dt_string, .dir = dir_out, .data = NULL }, + }; + +/** + * Head pointer to a linked list of + * rule map table entries, used for + * preserving the order of entries + * based on "first encounter" + */ +static line_order_list *list_head = NULL; + +/** + * Pointer to the tail of the list for + * quick appends to the end of the list + */ +static line_order_list *list_tail = NULL; + +/** + * Send a logging message to a file + * @param out + * Output file to send message too + * @param prefix + * A special prefix to write to the file, such as "Error:" + * @param fmt + * The printf style formatter to use, such as "%d" + */ +static void log_msg(FILE *out, const char *prefix, const char *fmt, ...) { + fprintf(out, "%s", prefix); + va_list args; + va_start(args, fmt); + vfprintf(out, fmt, args); + va_end(args); +} + +/** + * Checks for a type in the policy. + * @param db + * The policy db to search + * @param type + * The type to search for + * @return + * 1 if the type is found, 0 otherwise. + * @warning + * This function always returns 1 if libsepol is not linked + * statically to this executable and LINK_SEPOL_STATIC is not + * defined. + */ +int check_type(sepol_policydb_t *db, char *type) { + + int rc = 1; +#if defined(LINK_SEPOL_STATIC) + policydb_t *d = (policydb_t *)db; + hashtab_datum_t dat; + dat = hashtab_search(d->p_types.table, type); + rc = (dat == NULL) ? 0 : 1; +#endif + return rc; +} + +/** + * Validates a key_map against a set of enforcement rules, this + * function exits the application on a type that cannot be properly + * checked + * + * @param m + * The key map to check + * @param lineno + * The line number in the source file for the corresponding key map + */ +static int key_map_validate(key_map *m, int lineno) { + + int rc = 1; + int ret = 1; + int i; + int resp; + char *key = m->name; + char *value = m->data; + data_type type = m->type; + sepol_bool_key_t *se_key; + + /* Booleans can always be checked for sanity */ + if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) { + goto out; + } + else if (type == dt_bool) { + log_error("Line number: %d, Expected boolean value got: %s=%s\n", + lineno, key, value); + rc = 0; + goto out; + } + + /* + * If their is no policy file present, + * then it is not in strict mode so just return. + * User and name cannot really be checked. + */ + if (!pol.policy_file) { + goto out; + } + else if (!strcasecmp(key, "sebool")) { + + ret = sepol_bool_key_create(pol.handle, value, &se_key); + if (ret < 0) { + log_error("Could not create selinux boolean key, error: %s\n", + strerror(errno)); + rc = 0; + goto out; + } + + ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp); + if (ret < 0) { + log_error("Could not check selinux boolean, error: %s\n", + strerror(errno)); + rc = 0; + goto bool_err; + } + + if(!resp) { + log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n", + value, lineno, out_file_name); + rc = 0; + goto bool_err; + } + } + else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) { + + if(!check_type(pol.db, value)) { + log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value, + lineno, out_file_name); + rc = 0; + } + goto out; + } + + /* + * Ideally this should check if the category level + * is defined in the policy. Since their doesn't appear + * to be a shared object option to extract this information + * for now, well just ensure it is a integer value. + */ + else if (!strcasecmp(key, "level")) { + + i=0; + while(value[i] != '\0') { + if(!isdigit(value[i])) { + log_error("level: %s on line: %d is not a valid integer\n", value, lineno); + rc = 0; + goto out; + } + i++; + } + } + +out: + return rc; + +bool_err: + sepol_bool_key_free(se_key); + goto out; + +} + +/** + * Prints a rule map back to a file + * @param fp + * The file handle to print too + * @param r + * The rule map to print + */ +static void rule_map_print(FILE *fp, rule_map *r) { + + int i; + key_map *m; + + for (i = 0; i < r->length; i++) { + m = &(r->m[i]); + if (i < r->length - 1) + fprintf(fp, "%s=%s ", m->name, m->data); + else + fprintf(fp, "%s=%s", m->name, m->data); + } +} + +/** + * Compare two rule maps for equality + * @param rmA + * a rule map to check + * @param rmB + * a rule map to check + * @return + * 0 - If the rules input selectors are different, ie not a match + * 1 - If the input selectors match, ie needs an override + * -1 - If the input and output selectors match, ie duplicate line + */ +static int rule_map_cmp(rule_map *rmA, rule_map *rmB) { + + int i; + int j; + int inputs_found = 0; + int num_of_matched_inputs = 0; + int input_mode = 0; + int matches = 0; + key_map *mA; + key_map *mB; + + if (rmA->length != rmB->length) + return 0; + + for (i = 0; i < rmA->length; i++) { + mA = &(rmA->m[i]); + + for (j = 0; j < rmB->length; j++) { + mB = &(rmB->m[j]); + input_mode = 0; + + if (mA->type != mB->type) + continue; + + if (strcmp(mA->name, mB->name)) + continue; + + if (strcmp(mA->data, mB->data)) + continue; + + if (mB->dir != mA->dir) + continue; + else if (mB->dir == dir_in) { + input_mode = 1; + inputs_found++; + } + + if (input_mode) + num_of_matched_inputs++; + + /* Match found, move on */ + matches++; + break; + } + } + + /* If they all matched*/ + if (matches == rmA->length) + return -1; + + /* They didn't all match but the input's did */ + else if (num_of_matched_inputs == inputs_found) + return 1; + + /* They didn't all match, and the inputs didn't match, ie it didn't + * match */ + else + return 0; +} + +/** + * Frees a rule map + * @param rm + * rule map to be freed. + */ +static void rule_map_free(rule_map *rm, rule_map_switch s) { + + int i; + int len = rm->length; + for (i = 0; i < len; i++) { + key_map *m = &(rm->m[i]); + free(m->data); + } + + if(s == rule_map_destroy_key && rm->key) + free(rm->key); + + free(rm); +} + +static void free_kvp(kvp *k) { + free(k->key); + free(k->value); +} + +/** + * Given a set of key value pairs, this will construct a new rule map. + * On error this function calls exit. + * @param keys + * Keys from a rule line to map + * @param num_of_keys + * The length of the keys array + * @param lineno + * The line number the keys were extracted from + * @return + * A rule map pointer. + */ +static rule_map *rule_map_new(kvp keys[], unsigned int num_of_keys, int lineno) { + + unsigned int i = 0, j = 0; + rule_map *new_map = NULL; + kvp *k = NULL; + key_map *r = NULL, *x = NULL; + + new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map)); + if (!new_map) + goto oom; + + new_map->length = num_of_keys; + new_map->lineno = lineno; + + /* For all the keys in a rule line*/ + for (i = 0; i < num_of_keys; i++) { + k = &(keys[i]); + r = &(new_map->m[i]); + + for (j = 0; j < KVP_NUM_OF_RULES; j++) { + x = &(rules[j]); + + /* Only assign key name to map name */ + if (strcasecmp(k->key, x->name)) { + if (i == KVP_NUM_OF_RULES) { + log_error("No match for key: %s\n", k->key); + goto err; + } + continue; + } + + memcpy(r, x, sizeof(key_map)); + + /* Assign rule map value to one from file */ + r->data = strdup(k->value); + if (!r->data) + goto oom; + + /* Enforce type check*/ + if (!key_map_validate(r, lineno)) { + log_error("Could not validate\n"); + goto err; + } + + /* Only build key off of inputs*/ + if (r->dir == dir_in) { + char *tmp; + int l = strlen(k->key); + l += strlen(k->value); + l += (new_map->key) ? strlen(new_map->key) : 0; + l += 1; + + tmp = realloc(new_map->key, l); + if (!tmp) + goto oom; + + new_map->key = tmp; + + strcat(new_map->key, k->key); + strcat(new_map->key, k->value); + } + break; + } + free_kvp(k); + } + + if (new_map->key == NULL) { + log_error("Strange, no keys found, input file corrupt perhaps?\n"); + goto err; + } + + return new_map; + +oom: + log_error("Out of memory!\n"); +err: + if(new_map) { + rule_map_free(new_map, rule_map_destroy_key); + for (; i < num_of_keys; i++) { + k = &(keys[i]); + free_kvp(k); + } + } + exit(EXIT_FAILURE); +} + +/** + * Print the usage of the program + */ +static void usage() { + printf( + "checkseapp [options] <input file>\n" + "Processes an seapp_contexts file specified by argument <input file> (default stdin) " + "and allows later decelerations to override previous ones on a match.\n" + "Options:\n" + "-h - print this help message\n" + "-v - enable verbose debugging informations\n" + "-p policy file - specify policy file for strict checking of output selectors\n" + "-o output file - specify output file, default is stdout\n"); +} + +static void init() { + + /* If not set on stdin already */ + if(!input_file) { + log_info("Opening input file: %s\n", in_file_name); + input_file = fopen(in_file_name, "r"); + if (!input_file) { + log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + /* If not set on std out already */ + if(!output_file) { + output_file = fopen(out_file_name, "w+"); + if (!output_file) { + log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (pol.policy_file_name) { + + log_info("Opening policy file: %s\n", pol.policy_file_name); + pol.policy_file = fopen(pol.policy_file_name, "rb"); + if (!pol.policy_file) { + log_error("Could not open file: %s error: %s\n", + pol.policy_file_name, strerror(errno)); + exit(EXIT_FAILURE); + } + + pol.handle = sepol_handle_create(); + if (!pol.handle) { + log_error("Could not create sepolicy handle: %s\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (sepol_policy_file_create(&pol.pf) < 0) { + log_error("Could not create sepolicy file: %s!\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + sepol_policy_file_set_fp(pol.pf, pol.policy_file); + sepol_policy_file_set_handle(pol.pf, pol.handle); + + if (sepol_policydb_create(&pol.db) < 0) { + log_error("Could not create sepolicy db: %s!\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + + if (sepol_policydb_read(pol.db, pol.pf) < 0) { + log_error("Could not lod policy file to db: %s!\n", + strerror(errno)); + exit(EXIT_FAILURE); + } + } + + log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name); + log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name); + log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name); + +} + +/** + * Handle parsing and setting the global flags for the command line + * options. This function calls exit on failure. + * @param argc + * argument count + * @param argv + * argument list + */ +static void handle_options(int argc, char *argv[]) { + + int c; + int num_of_args; + + while ((c = getopt(argc, argv, "ho:p:v")) != -1) { + switch (c) { + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'o': + out_file_name = optarg; + break; + case 'p': + pol.policy_file_name = optarg; + break; + case 'v': + log_set_verbose(); + break; + case '?': + if (optopt == 'o' || optopt == 'p') + log_error("Option -%c requires an argument.\n", optopt); + else if (isprint (optopt)) + log_error("Unknown option `-%c'.\n", optopt); + else { + log_error( + "Unknown option character `\\x%x'.\n", + optopt); + exit(EXIT_FAILURE); + } + break; + default: + exit(EXIT_FAILURE); + } + } + + num_of_args = argc - optind; + + if (num_of_args > 1) { + log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args); + usage(); + exit(EXIT_FAILURE); + } else if (num_of_args == 1) { + in_file_name = argv[argc - 1]; + } else { + input_file = stdin; + in_file_name = "stdin"; + } + + if (!out_file_name) { + output_file = stdout; + out_file_name = "stdout"; + } +} + +/** + * Adds a rule_map double pointer, ie the hash table pointer to the list. + * By using a double pointer, the hash table can have a line be overridden + * and the value is updated in the list. This function calls exit on failure. + * @param rm + * the rule_map to add. + */ +static void list_add(hash_entry *e) { + + line_order_list *node = malloc(sizeof(line_order_list)); + if (node == NULL) + goto oom; + + node->next = NULL; + node->e = e; + + if (list_head == NULL) + list_head = list_tail = node; + else { + list_tail->next = node; + list_tail = list_tail->next; + } + return; + +oom: + log_error("Out of memory!\n"); + exit(EXIT_FAILURE); +} + +/** + * Free's the rule map list, which ultimatley contains + * all the malloc'd rule_maps. + */ +static void list_free() { + line_order_list *cursor, *tmp; + hash_entry *e; + + cursor = list_head; + while (cursor) { + e = cursor->e; + rule_map_free(e->r, rule_map_destroy_key); + tmp = cursor; + cursor = cursor->next; + free(e); + free(tmp); + } +} + +/** + * Adds a rule to the hash table and to the ordered list if needed. + * @param rm + * The rule map to add. + */ +static void rule_add(rule_map *rm) { + + int cmp; + ENTRY e; + ENTRY *f; + hash_entry *entry; + hash_entry *tmp; + char *preserved_key; + + e.key = rm->key; + + /* Check to see if it has already been added*/ + f = hsearch(e, FIND); + + /* + * Since your only hashing on a partial key, the inputs we need to handle + * when you want to override the outputs for a given input set, as well as + * checking for duplicate entries. + */ + if(f) { + tmp = (hash_entry *)f->data; + cmp = rule_map_cmp(rm, tmp->r); + + /* Override be freeing the old rule map and updating + the pointer */ + if(cmp == 1) { + + /* + * DO NOT free key pointers given to the hash map, instead + * free the new key. The ordering here is critical! + */ + preserved_key = tmp->r->key; + rule_map_free(tmp->r, rule_map_preserve_key); + free(rm->key); + rm->key = preserved_key; + tmp->r = rm; + } + /* Duplicate */ + else { + log_error("Duplicate line detected in file: %s\n" + "Lines %d and %d match!\n", + out_file_name, tmp->r->lineno, rm->lineno); + rule_map_free(rm, rule_map_destroy_key); + goto err; + } + } + /* It wasn't found, just add the rule map to the table */ + else { + + entry = malloc(sizeof(hash_entry)); + if (!entry) + goto oom; + + entry->r = rm; + e.data = entry; + + f = hsearch(e, ENTER); + if(f == NULL) { + goto oom; + } + + /* new entries must be added to the ordered list */ + entry->r = rm; + list_add(entry); + } + + return; +oom: + if (e.key) + free(e.key); + if (entry) + free(entry); + if (rm) + free(rm); + log_error("Out of memory in function: %s\n", __FUNCTION__); +err: + exit(EXIT_FAILURE); +} + +/** + * Parses the seapp_contexts file and adds them to the + * hash table and ordered list entries when it encounters them. + * Calls exit on failure. + */ +static void parse() { + + char line_buf[BUFSIZ]; + char *token; + unsigned lineno = 0; + char *p, *name = NULL, *value = NULL, *saveptr; + size_t len; + kvp keys[KVP_NUM_OF_RULES]; + int token_cnt = 0; + + while (fgets(line_buf, sizeof line_buf - 1, input_file)) { + + lineno++; + log_info("Got line %d\n", lineno); + len = strlen(line_buf); + if (line_buf[len - 1] == '\n') + line_buf[len - 1] = 0; + p = line_buf; + while (isspace(*p)) + p++; + if (*p == '#' || *p == 0) + continue; + + token = strtok_r(p, " \t", &saveptr); + if (!token) + goto err; + + token_cnt = 0; + memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES); + while (1) { + name = token; + value = strchr(name, '='); + if (!value) + goto err; + *value++ = 0; + + keys[token_cnt].key = strdup(name); + if (!keys[token_cnt].key) + goto oom; + + keys[token_cnt].value = strdup(value); + if (!keys[token_cnt].value) + goto oom; + + token_cnt++; + + token = strtok_r(NULL, " \t", &saveptr); + if (!token) + break; + + } /*End token parsing */ + + rule_map *r = rule_map_new(keys, token_cnt, lineno); + rule_add(r); + + } /* End file parsing */ + return; + +err: + log_error("reading %s, line %u, name %s, value %s\n", + in_file_name, lineno, name, value); + exit(EXIT_FAILURE); +oom: + log_error("In function %s: Out of memory\n", __FUNCTION__); + exit(EXIT_FAILURE); +} + +/** + * Should be called after parsing to cause the printing of the rule_maps + * stored in the ordered list, head first, which preserves the "first encountered" + * ordering. + */ +static void output() { + + rule_map *r; + line_order_list *cursor; + cursor = list_head; + + while (cursor) { + r = cursor->e->r; + rule_map_print(output_file, r); + cursor = cursor->next; + if (cursor) + fprintf(output_file, "\n"); + } +} + +/** + * This function is registered to the at exit handler and should clean up + * the programs dynamic resources, such as memory and fd's. + */ +static void cleanup() { + + /* Only close this when it was opened by me and not the crt */ + if (out_file_name && output_file) { + log_info("Closing file: %s\n", out_file_name); + fclose(output_file); + } + + /* Only close this when it was opened by me and not the crt */ + if (in_file_name && input_file) { + log_info("Closing file: %s\n", in_file_name); + fclose(input_file); + } + + if (pol.policy_file) { + + log_info("Closing file: %s\n", pol.policy_file_name); + fclose(pol.policy_file); + + if (pol.db) + sepol_policydb_free(pol.db); + + if (pol.pf) + sepol_policy_file_free(pol.pf); + + if (pol.handle) + sepol_handle_destroy(pol.handle); + } + + log_info("Freeing list\n"); + list_free(); + hdestroy(); +} + +int main(int argc, char *argv[]) { + if (!hcreate(TABLE_SIZE)) { + log_error("Could not create hash table: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + atexit(cleanup); + handle_options(argc, argv); + init(); + log_info("Starting to parse\n"); + parse(); + log_info("Parsing completed, generating output\n"); + output(); + log_info("Success, generated output\n"); + exit(EXIT_SUCCESS); +} -- GitLab