Home | History | Annotate | Download | only in check_seapp
      1 #include <stdio.h>
      2 #include <stdarg.h>
      3 #include <ctype.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <unistd.h>
      7 #include <string.h>
      8 #include <errno.h>
      9 #include <stdint.h>
     10 #include <search.h>
     11 #include <sepol/sepol.h>
     12 #include <sepol/policydb/policydb.h>
     13 
     14 #define TABLE_SIZE 1024
     15 #define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
     16 #define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
     17 #define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
     18 #define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
     19 #define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
     20 
     21 typedef struct line_order_list line_order_list;
     22 typedef struct hash_entry hash_entry;
     23 typedef enum key_dir key_dir;
     24 typedef enum data_type data_type;
     25 typedef enum rule_map_switch rule_map_switch;
     26 typedef enum map_match map_match;
     27 typedef struct key_map key_map;
     28 typedef struct kvp kvp;
     29 typedef struct rule_map rule_map;
     30 typedef struct policy_info policy_info;
     31 
     32 enum map_match {
     33 	map_no_matches,
     34 	map_input_matched,
     35 	map_matched
     36 };
     37 
     38 /**
     39  * Whether or not the "key" from a key vaue pair is considered an
     40  * input or an output.
     41  */
     42 enum key_dir {
     43 	dir_in, dir_out
     44 };
     45 
     46 /**
     47  * Used as options to rule_map_free()
     48  *
     49  * This is needed to get around the fact that GNU C's hash_map doesn't copy the key, so
     50  * we cannot free a key when overrding rule_map's in the table.
     51  */
     52 enum rule_map_switch {
     53 	rule_map_preserve_key, /** Used to preserve the key in the rule_map, ie don't free it*/
     54 	rule_map_destroy_key   /** Used when you need a full free of the rule_map structure*/
     55 };
     56 
     57 /**
     58  * The expected "type" of data the value in the key
     59  * value pair should be.
     60  */
     61 enum data_type {
     62 	dt_bool, dt_string
     63 };
     64 
     65 /**
     66  * This list is used to store a double pointer to each
     67  * hash table / line rule combination. This way a replacement
     68  * in the hash table automatically updates the list. The list
     69  * is also used to keep "first encountered" ordering amongst
     70  * the encountered key value pairs in the rules file.
     71  */
     72 struct line_order_list {
     73 	hash_entry *e;
     74 	line_order_list *next;
     75 };
     76 
     77 /**
     78  * The workhorse of the logic. This struct maps key value pairs to
     79  * an associated set of meta data maintained in rule_map_new()
     80  */
     81 struct key_map {
     82 	char *name;
     83 	key_dir dir;
     84 	data_type type;
     85 	char *data;
     86 };
     87 
     88 /**
     89  * Key value pair struct, this represents the raw kvp values coming
     90  * from the rules files.
     91  */
     92 struct kvp {
     93 	char *key;
     94 	char *value;
     95 };
     96 
     97 /**
     98  * Rules are made up of meta data and an associated set of kvp stored in a
     99  * key_map array.
    100  */
    101 struct rule_map {
    102 	char *key; /** key value before hashing */
    103 	int length; /** length of the key map */
    104 	int lineno; /** Line number rule was encounter on */
    105 	rule_map *next; /** next pointer used in hash table for chaining on collision */
    106 	key_map m[]; /** key value mapping */
    107 };
    108 
    109 struct hash_entry {
    110 	rule_map *r; /** The rule map to store at that location */
    111 };
    112 
    113 /**
    114  * Data associated for a policy file
    115  */
    116 struct policy_info {
    117 
    118 	char *policy_file_name; /** policy file path name */
    119 	FILE *policy_file;      /** file handle to the policy file */
    120 	sepol_policydb_t *db;
    121 	sepol_policy_file_t *pf;
    122 	sepol_handle_t *handle;
    123 	sepol_context_t *con;
    124 };
    125 
    126 /** Set to !0 to enable verbose logging */
    127 static int logging_verbose = 0;
    128 
    129 /** file handle to the output file */
    130 static FILE *output_file = NULL;
    131 
    132 /** file handle to the input file */
    133 static FILE *input_file = NULL;
    134 
    135 /** output file path name */
    136 static char *out_file_name = NULL;
    137 
    138 /** input file path name */
    139 static char *in_file_name = NULL;
    140 
    141 static policy_info pol = {
    142 	.policy_file_name = NULL,
    143 	.policy_file = NULL,
    144 	.db = NULL,
    145 	.pf = NULL,
    146 	.handle = NULL,
    147 	.con = NULL
    148 };
    149 
    150 /**
    151  * The heart of the mapping process, this must be updated if a new key value pair is added
    152  * to a rule.
    153  */
    154 key_map rules[] = {
    155                 /*Inputs*/
    156                 { .name = "isSystemServer", .type = dt_bool,   .dir = dir_in,  .data = NULL },
    157                 { .name = "user",           .type = dt_string, .dir = dir_in,  .data = NULL },
    158                 { .name = "seinfo",         .type = dt_string, .dir = dir_in,  .data = NULL },
    159                 { .name = "name",           .type = dt_string, .dir = dir_in,  .data = NULL },
    160                 { .name = "sebool",         .type = dt_string, .dir = dir_in,  .data = NULL },
    161                 /*Outputs*/
    162                 { .name = "domain",         .type = dt_string, .dir = dir_out, .data = NULL },
    163                 { .name = "type",           .type = dt_string, .dir = dir_out, .data = NULL },
    164                 { .name = "levelFromUid",   .type = dt_bool,   .dir = dir_out, .data = NULL },
    165                 { .name = "level",          .type = dt_string, .dir = dir_out, .data = NULL },
    166 			};
    167 
    168 /**
    169  * Head pointer to a linked list of
    170  * rule map table entries, used for
    171  * preserving the order of entries
    172  * based on "first encounter"
    173  */
    174 static line_order_list *list_head = NULL;
    175 
    176 /**
    177  * Pointer to the tail of the list for
    178  * quick appends to the end of the list
    179  */
    180 static line_order_list *list_tail = NULL;
    181 
    182 /**
    183  * Send a logging message to a file
    184  * @param out
    185  * 	Output file to send message too
    186  * @param prefix
    187  * 	A special prefix to write to the file, such as "Error:"
    188  * @param fmt
    189  * 	The printf style formatter to use, such as "%d"
    190  */
    191 static void log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
    192 	fprintf(out, "%s", prefix);
    193 	va_list args;
    194 	va_start(args, fmt);
    195 	vfprintf(out, fmt, args);
    196 	va_end(args);
    197 }
    198 
    199 /**
    200  * Checks for a type in the policy.
    201  * @param db
    202  * 	The policy db to search
    203  * @param type
    204  * 	The type to search for
    205  * @return
    206  * 	1 if the type is found, 0 otherwise.
    207  * @warning
    208  * 	This function always returns 1 if libsepol is not linked
    209  * 	statically to this executable and LINK_SEPOL_STATIC is not
    210  * 	defined.
    211  */
    212 int check_type(sepol_policydb_t *db, char *type) {
    213 
    214 	int rc = 1;
    215 #if defined(LINK_SEPOL_STATIC)
    216 	policydb_t *d = (policydb_t *)db;
    217 	hashtab_datum_t dat;
    218 	dat = hashtab_search(d->p_types.table, type);
    219 	rc = (dat == NULL) ? 0 : 1;
    220 #endif
    221 	return rc;
    222 }
    223 
    224 /**
    225  * Validates a key_map against a set of enforcement rules, this
    226  * function exits the application on a type that cannot be properly
    227  * checked
    228  *
    229  * @param m
    230  * 	The key map to check
    231  * @param lineno
    232  * 	The line number in the source file for the corresponding key map
    233  */
    234 static int key_map_validate(key_map *m, int lineno) {
    235 
    236 	int rc = 1;
    237 	int ret = 1;
    238 	int resp;
    239 	char *key = m->name;
    240 	char *value = m->data;
    241 	data_type type = m->type;
    242 	sepol_bool_key_t *se_key;
    243 
    244 	log_info("Validating %s=%s\n", key, value);
    245 
    246 	 /* Booleans can always be checked for sanity */
    247 	if (type == dt_bool && (!strcmp("true", value) || !strcmp("false", value))) {
    248 		goto out;
    249 	}
    250 	else if (type == dt_bool) {
    251 		log_error("Expected boolean value got: %s=%s on line: %d in file: %s\n",
    252 				key, value, lineno, out_file_name);
    253 		rc = 0;
    254 		goto out;
    255 	}
    256 
    257 	/*
    258 	 * If their is no policy file present,
    259 	 * then it is not in strict mode so just return.
    260 	 * User and name cannot really be checked.
    261 	 */
    262 	if (!pol.policy_file) {
    263 		goto out;
    264 	}
    265 	else if (!strcasecmp(key, "sebool")) {
    266 
    267 		ret = sepol_bool_key_create(pol.handle, value, &se_key);
    268 		if (ret < 0) {
    269 			log_error("Could not create selinux boolean key, error: %s\n",
    270 					strerror(errno));
    271 			rc = 0;
    272 			goto out;
    273 		}
    274 
    275 		ret = sepol_bool_exists(pol.handle, pol.db, se_key, &resp);
    276 		if (ret < 0) {
    277 			log_error("Could not check selinux boolean, error: %s\n",
    278 					strerror(errno));
    279 			rc = 0;
    280 			sepol_bool_key_free(se_key);
    281 			goto out;
    282 		}
    283 
    284 		if(!resp) {
    285 			log_error("Could not find selinux boolean \"%s\" on line: %d in file: %s\n",
    286 					value, lineno, out_file_name);
    287 			rc = 0;
    288 			sepol_bool_key_free(se_key);
    289 			goto out;
    290 		}
    291 		sepol_bool_key_free(se_key);
    292 	}
    293 	else if (!strcasecmp(key, "type") || !strcasecmp(key, "domain")) {
    294 
    295 		if(!check_type(pol.db, value)) {
    296 			log_error("Could not find selinux type \"%s\" on line: %d in file: %s\n", value,
    297 					lineno, out_file_name);
    298 			rc = 0;
    299 		}
    300 		goto out;
    301 	}
    302 	else if (!strcasecmp(key, "level")) {
    303 
    304 		ret = sepol_mls_check(pol.handle, pol.db, value);
    305 		if (ret < 0) {
    306 			log_error("Could not find selinux level \"%s\", on line: %d in file: %s\n", value,
    307 					lineno, out_file_name);
    308 			rc = 0;
    309 			goto out;
    310 		}
    311 	}
    312 
    313 out:
    314 	log_info("Key map validate returning: %d\n", rc);
    315 	return rc;
    316 }
    317 
    318 /**
    319  * Prints a rule map back to a file
    320  * @param fp
    321  * 	The file handle to print too
    322  * @param r
    323  * 	The rule map to print
    324  */
    325 static void rule_map_print(FILE *fp, rule_map *r) {
    326 
    327 	int i;
    328 	key_map *m;
    329 
    330 	for (i = 0; i < r->length; i++) {
    331 		m = &(r->m[i]);
    332 		if (i < r->length - 1)
    333 			fprintf(fp, "%s=%s ", m->name, m->data);
    334 		else
    335 			fprintf(fp, "%s=%s", m->name, m->data);
    336 	}
    337 }
    338 
    339 /**
    340  * Compare two rule maps for equality
    341  * @param rmA
    342  * 	a rule map to check
    343  * @param rmB
    344  * 	a rule map to check
    345  * @return
    346  *  a map_match enum indicating the result
    347  */
    348 static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
    349 
    350 	int i;
    351 	int j;
    352 	int inputs_found = 0;
    353 	int num_of_matched_inputs = 0;
    354 	int input_mode = 0;
    355 	int matches = 0;
    356 	key_map *mA;
    357 	key_map *mB;
    358 
    359 	if (rmA->length != rmB->length)
    360 		return map_no_matches;
    361 
    362 	for (i = 0; i < rmA->length; i++) {
    363 		mA = &(rmA->m[i]);
    364 
    365 		for (j = 0; j < rmB->length; j++) {
    366 			mB = &(rmB->m[j]);
    367 			input_mode = 0;
    368 
    369 			if (mA->type != mB->type)
    370 				continue;
    371 
    372 			if (strcmp(mA->name, mB->name))
    373 				continue;
    374 
    375 			if (strcmp(mA->data, mB->data))
    376 				continue;
    377 
    378 			if (mB->dir != mA->dir)
    379 				continue;
    380 			else if (mB->dir == dir_in) {
    381 				input_mode = 1;
    382 				inputs_found++;
    383 			}
    384 
    385 			if (input_mode) {
    386 				log_info("Matched input lines: type=%s name=%s data=%s dir=%d\n", mA->type, mA->name, mA->data, mA->dir);
    387 				num_of_matched_inputs++;
    388 			}
    389 
    390 			/* Match found, move on */
    391 			log_info("Matched lines: type=%s name=%s data=%s dir=%d\n", mA->type, mA->name, mA->data, mA->dir);
    392 			matches++;
    393 			break;
    394 		}
    395 	}
    396 
    397 	/* If they all matched*/
    398 	if (matches == rmA->length) {
    399 		log_info("Rule map cmp MATCH\n");
    400 		return map_matched;
    401 	}
    402 
    403 	/* They didn't all match but the input's did */
    404 	else if (num_of_matched_inputs == inputs_found) {
    405 		log_info("Rule map cmp INPUT MATCH\n");
    406 		return map_input_matched;
    407 	}
    408 
    409 	/* They didn't all match, and the inputs didn't match, ie it didn't
    410 	 * match */
    411 	else {
    412 		log_info("Rule map cmp NO MATCH\n");
    413 		return map_no_matches;
    414 	}
    415 }
    416 
    417 /**
    418  * Frees a rule map
    419  * @param rm
    420  * 	rule map to be freed.
    421  */
    422 static void rule_map_free(rule_map *rm, rule_map_switch s) {
    423 
    424 	int i;
    425 	int len = rm->length;
    426 	for (i = 0; i < len; i++) {
    427 		key_map *m = &(rm->m[i]);
    428 		free(m->data);
    429 	}
    430 
    431 	if(s == rule_map_destroy_key && rm->key)
    432 		free(rm->key);
    433 
    434 	free(rm);
    435 }
    436 
    437 static void free_kvp(kvp *k) {
    438 	free(k->key);
    439 	free(k->value);
    440 }
    441 
    442 /**
    443  * Given a set of key value pairs, this will construct a new rule map.
    444  * On error this function calls exit.
    445  * @param keys
    446  * 	Keys from a rule line to map
    447  * @param num_of_keys
    448  * 	The length of the keys array
    449  * @param lineno
    450  * 	The line number the keys were extracted from
    451  * @return
    452  * 	A rule map pointer.
    453  */
    454 static rule_map *rule_map_new(kvp keys[], unsigned int num_of_keys, int lineno) {
    455 
    456 	unsigned int i = 0, j = 0;
    457 	rule_map *new_map = NULL;
    458 	kvp *k = NULL;
    459 	key_map *r = NULL, *x = NULL;
    460 
    461 	new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
    462 	if (!new_map)
    463 		goto oom;
    464 
    465 	new_map->length = num_of_keys;
    466 	new_map->lineno = lineno;
    467 
    468 	/* For all the keys in a rule line*/
    469 	for (i = 0; i < num_of_keys; i++) {
    470 		k = &(keys[i]);
    471 		r = &(new_map->m[i]);
    472 
    473 		for (j = 0; j < KVP_NUM_OF_RULES; j++) {
    474 			x = &(rules[j]);
    475 
    476 			/* Only assign key name to map name */
    477 			if (strcasecmp(k->key, x->name)) {
    478 				if (i == KVP_NUM_OF_RULES) {
    479 					log_error("No match for key: %s\n", k->key);
    480 					goto err;
    481 				}
    482 				continue;
    483 			}
    484 
    485 			memcpy(r, x, sizeof(key_map));
    486 
    487 			/* Assign rule map value to one from file */
    488 			r->data = strdup(k->value);
    489 			if (!r->data)
    490 				goto oom;
    491 
    492 			/* Enforce type check*/
    493 			log_info("Validating keys!\n");
    494 			if (!key_map_validate(r, lineno)) {
    495 				log_error("Could not validate\n");
    496 				goto err;
    497 			}
    498 
    499 			/* Only build key off of inputs*/
    500 			if (r->dir == dir_in) {
    501 				char *tmp;
    502 				int key_len = strlen(k->key);
    503 				int val_len = strlen(k->value);
    504 				int l = (new_map->key) ? strlen(new_map->key) : 0;
    505 				l = l + key_len + val_len;
    506 				l += 1;
    507 
    508 				tmp = realloc(new_map->key, l);
    509 				if (!tmp)
    510 					goto oom;
    511 
    512 				if (!new_map->key)
    513 					memset(tmp, 0, l);
    514 
    515 				new_map->key = tmp;
    516 
    517 				strncat(new_map->key, k->key, key_len);
    518 				strncat(new_map->key, k->value, val_len);
    519 			}
    520 			break;
    521 		}
    522 		free_kvp(k);
    523 	}
    524 
    525 	if (new_map->key == NULL) {
    526 		log_error("Strange, no keys found, input file corrupt perhaps?\n");
    527 		goto err;
    528 	}
    529 
    530 	return new_map;
    531 
    532 oom:
    533 	log_error("Out of memory!\n");
    534 err:
    535 	if(new_map) {
    536 		rule_map_free(new_map, rule_map_destroy_key);
    537 		for (; i < num_of_keys; i++) {
    538 			k = &(keys[i]);
    539 			free_kvp(k);
    540 		}
    541 	}
    542 	exit(EXIT_FAILURE);
    543 }
    544 
    545 /**
    546  * Print the usage of the program
    547  */
    548 static void usage() {
    549 	printf(
    550 	        "checkseapp [options] <input file>\n"
    551 		        "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
    552 		        "and allows later declarations to override previous ones on a match.\n"
    553 		        "Options:\n"
    554 		        "-h - print this help message\n"
    555 		        "-v - enable verbose debugging informations\n"
    556 		        "-p policy file - specify policy file for strict checking of output selectors\n"
    557 		        "-o output file - specify output file, default is stdout\n");
    558 }
    559 
    560 static void init() {
    561 
    562 	/* If not set on stdin already */
    563 	if(!input_file) {
    564 		log_info("Opening input file: %s\n", in_file_name);
    565 		input_file = fopen(in_file_name, "r");
    566 		if (!input_file) {
    567 			log_error("Could not open file: %s error: %s\n", in_file_name, strerror(errno));
    568 			exit(EXIT_FAILURE);
    569 		}
    570 	}
    571 
    572 	/* If not set on std out already */
    573 	if(!output_file) {
    574 		output_file = fopen(out_file_name, "w+");
    575 		if (!output_file) {
    576 			log_error("Could not open file: %s error: %s\n", out_file_name, strerror(errno));
    577 			exit(EXIT_FAILURE);
    578 		}
    579 	}
    580 
    581 	if (pol.policy_file_name) {
    582 
    583 		log_info("Opening policy file: %s\n", pol.policy_file_name);
    584 		pol.policy_file = fopen(pol.policy_file_name, "rb");
    585 		if (!pol.policy_file) {
    586 			log_error("Could not open file: %s error: %s\n",
    587 					pol.policy_file_name, strerror(errno));
    588 			exit(EXIT_FAILURE);
    589 		}
    590 
    591 		pol.handle = sepol_handle_create();
    592 		if (!pol.handle) {
    593 			log_error("Could not create sepolicy handle: %s\n",
    594 					strerror(errno));
    595 			exit(EXIT_FAILURE);
    596 		}
    597 
    598 		if (sepol_policy_file_create(&pol.pf) < 0) {
    599 			log_error("Could not create sepolicy file: %s!\n",
    600 					strerror(errno));
    601 			exit(EXIT_FAILURE);
    602 		}
    603 
    604 		sepol_policy_file_set_fp(pol.pf, pol.policy_file);
    605 		sepol_policy_file_set_handle(pol.pf, pol.handle);
    606 
    607 		if (sepol_policydb_create(&pol.db) < 0) {
    608 			log_error("Could not create sepolicy db: %s!\n",
    609 					strerror(errno));
    610 			exit(EXIT_FAILURE);
    611 		}
    612 
    613 		if (sepol_policydb_read(pol.db, pol.pf) < 0) {
    614 			log_error("Could not lod policy file to db: %s!\n",
    615 					strerror(errno));
    616 			exit(EXIT_FAILURE);
    617 		}
    618 	}
    619 
    620 	log_info("Policy file set to: %s\n", (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
    621 	log_info("Input file set to: %s\n", (in_file_name == NULL) ? "stdin" : in_file_name);
    622 	log_info("Output file set to: %s\n", (out_file_name == NULL) ? "stdout" : out_file_name);
    623 
    624 #if !defined(LINK_SEPOL_STATIC)
    625 	log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
    626 #endif
    627 
    628 }
    629 
    630 /**
    631  * Handle parsing and setting the global flags for the command line
    632  * options. This function calls exit on failure.
    633  * @param argc
    634  * 	argument count
    635  * @param argv
    636  * 	argument list
    637  */
    638 static void handle_options(int argc, char *argv[]) {
    639 
    640 	int c;
    641 	int num_of_args;
    642 
    643 	while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
    644 		switch (c) {
    645 		case 'h':
    646 			usage();
    647 			exit(EXIT_SUCCESS);
    648 		case 'o':
    649 			out_file_name = optarg;
    650 			break;
    651 		case 'p':
    652 			pol.policy_file_name = optarg;
    653 			break;
    654 		case 'v':
    655 			log_set_verbose();
    656 			break;
    657 		case '?':
    658 			if (optopt == 'o' || optopt == 'p')
    659 				log_error("Option -%c requires an argument.\n", optopt);
    660 			else if (isprint (optopt))
    661 				log_error("Unknown option `-%c'.\n", optopt);
    662 			else {
    663 				log_error(
    664 						"Unknown option character `\\x%x'.\n",
    665 						optopt);
    666 				exit(EXIT_FAILURE);
    667 			}
    668 			break;
    669 		default:
    670 			exit(EXIT_FAILURE);
    671 		}
    672 	}
    673 
    674 	num_of_args = argc - optind;
    675 
    676 	if (num_of_args > 1) {
    677 		log_error("Too many arguments, expected 0 or 1, argument, got %d\n", num_of_args);
    678 		usage();
    679 		exit(EXIT_FAILURE);
    680 	} else if (num_of_args == 1) {
    681 		in_file_name = argv[argc - 1];
    682 	} else {
    683 		input_file = stdin;
    684 		in_file_name = "stdin";
    685 	}
    686 
    687 	if (!out_file_name) {
    688 		output_file = stdout;
    689 		out_file_name = "stdout";
    690 	}
    691 }
    692 
    693 /**
    694  * Adds a rule_map double pointer, ie the hash table pointer to the list.
    695  * By using a double pointer, the hash table can have a line be overridden
    696  * and the value is updated in the list. This function calls exit on failure.
    697  * @param rm
    698  * 	the rule_map to add.
    699  */
    700 static void list_add(hash_entry *e) {
    701 
    702 	line_order_list *node = malloc(sizeof(line_order_list));
    703 	if (node == NULL)
    704 		goto oom;
    705 
    706 	node->next = NULL;
    707 	node->e = e;
    708 
    709 	if (list_head == NULL)
    710 		list_head = list_tail = node;
    711 	else {
    712 		list_tail->next = node;
    713 		list_tail = list_tail->next;
    714 	}
    715 	return;
    716 
    717 oom:
    718 	log_error("Out of memory!\n");
    719 	exit(EXIT_FAILURE);
    720 }
    721 
    722 /**
    723  * Free's the rule map list, which ultimatley contains
    724  * all the malloc'd rule_maps.
    725  */
    726 static void list_free() {
    727 	line_order_list *cursor, *tmp;
    728 	hash_entry *e;
    729 
    730 	cursor = list_head;
    731 	while (cursor) {
    732 		e = cursor->e;
    733 		rule_map_free(e->r, rule_map_destroy_key);
    734 		tmp = cursor;
    735 		cursor = cursor->next;
    736 		free(e);
    737 		free(tmp);
    738 	}
    739 }
    740 
    741 /**
    742  * Adds a rule to the hash table and to the ordered list if needed.
    743  * @param rm
    744  * 	The rule map to add.
    745  */
    746 static void rule_add(rule_map *rm) {
    747 
    748 	map_match cmp;
    749 	ENTRY e;
    750 	ENTRY *f;
    751 	hash_entry *entry;
    752 	hash_entry *tmp;
    753 	char *preserved_key;
    754 
    755 	e.key = rm->key;
    756 
    757 	log_info("Searching for key: %s\n", e.key);
    758 	/* Check to see if it has already been added*/
    759 	f = hsearch(e, FIND);
    760 
    761 	/*
    762 	 * Since your only hashing on a partial key, the inputs we need to handle
    763 	 * when you want to override the outputs for a given input set, as well as
    764 	 * checking for duplicate entries.
    765 	 */
    766 	if(f) {
    767 		log_info("Existing entry found!\n");
    768 		tmp = (hash_entry *)f->data;
    769 		cmp = rule_map_cmp(rm, tmp->r);
    770 		log_info("Comparing on rule map ret: %d\n", cmp);
    771 		/* Override be freeing the old rule map and updating
    772 		   the pointer */
    773 		if(cmp != map_matched) {
    774 
    775 			/*
    776 			 * DO NOT free key pointers given to the hash map, instead
    777 			 * free the new key. The ordering here is critical!
    778 			 */
    779 			preserved_key = tmp->r->key;
    780 			rule_map_free(tmp->r, rule_map_preserve_key);
    781 			free(rm->key);
    782 			rm->key = preserved_key;
    783 			tmp->r = rm;
    784 		}
    785 		/* Duplicate */
    786 		else {
    787 			log_error("Duplicate line detected in file: %s\n"
    788 					"Lines %d and %d match!\n",
    789 					out_file_name, tmp->r->lineno, rm->lineno);
    790 			rule_map_free(rm, rule_map_destroy_key);
    791 			goto err;
    792 		}
    793 	}
    794 	/* It wasn't found, just add the rule map to the table */
    795 	else {
    796 
    797 		entry = malloc(sizeof(hash_entry));
    798 		if (!entry)
    799 			goto oom;
    800 
    801 		entry->r = rm;
    802 		e.data = entry;
    803 
    804 		f = hsearch(e, ENTER);
    805 		if(f == NULL) {
    806 			goto oom;
    807 		}
    808 
    809 		/* new entries must be added to the ordered list */
    810 		entry->r = rm;
    811 		list_add(entry);
    812 	}
    813 
    814 	return;
    815 oom:
    816 	if (e.key)
    817 		free(e.key);
    818 	if (entry)
    819 		free(entry);
    820 	if (rm)
    821 		free(rm);
    822 	log_error("Out of memory in function: %s\n", __FUNCTION__);
    823 err:
    824 	exit(EXIT_FAILURE);
    825 }
    826 
    827 /**
    828  * Parses the seapp_contexts file and adds them to the
    829  * hash table and ordered list entries when it encounters them.
    830  * Calls exit on failure.
    831  */
    832 static void parse() {
    833 
    834 	char line_buf[BUFSIZ];
    835 	char *token;
    836 	unsigned lineno = 0;
    837 	char *p, *name = NULL, *value = NULL, *saveptr;
    838 	size_t len;
    839 	kvp keys[KVP_NUM_OF_RULES];
    840 	int token_cnt = 0;
    841 
    842 	while (fgets(line_buf, sizeof line_buf - 1, input_file)) {
    843 
    844 		lineno++;
    845 		log_info("Got line %d\n", lineno);
    846 		len = strlen(line_buf);
    847 		if (line_buf[len - 1] == '\n')
    848 			line_buf[len - 1] = 0;
    849 		p = line_buf;
    850 		while (isspace(*p))
    851 			p++;
    852 		if (*p == '#' || *p == 0)
    853 			continue;
    854 
    855 		token = strtok_r(p, " \t", &saveptr);
    856 		if (!token)
    857 			goto err;
    858 
    859 		token_cnt = 0;
    860 		memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
    861 		while (1) {
    862 
    863 			name = token;
    864 			value = strchr(name, '=');
    865 			if (!value)
    866 				goto err;
    867 			*value++ = 0;
    868 
    869 			keys[token_cnt].key = strdup(name);
    870 			if (!keys[token_cnt].key)
    871 				goto oom;
    872 
    873 			keys[token_cnt].value = strdup(value);
    874 			if (!keys[token_cnt].value)
    875 				goto oom;
    876 
    877 			token_cnt++;
    878 
    879 			token = strtok_r(NULL, " \t", &saveptr);
    880 			if (!token)
    881 				break;
    882 
    883 		} /*End token parsing */
    884 
    885 		rule_map *r = rule_map_new(keys, token_cnt, lineno);
    886 		rule_add(r);
    887 
    888 	} /* End file parsing */
    889 	return;
    890 
    891 err:
    892 	log_error("reading %s, line %u, name %s, value %s\n",
    893 			in_file_name, lineno, name, value);
    894 	exit(EXIT_FAILURE);
    895 oom:
    896 	log_error("In function %s:  Out of memory\n", __FUNCTION__);
    897 	exit(EXIT_FAILURE);
    898 }
    899 
    900 /**
    901  * Should be called after parsing to cause the printing of the rule_maps
    902  * stored in the ordered list, head first, which preserves the "first encountered"
    903  * ordering.
    904  */
    905 static void output() {
    906 
    907 	rule_map *r;
    908 	line_order_list *cursor;
    909 	cursor = list_head;
    910 
    911 	while (cursor) {
    912 		r = cursor->e->r;
    913 		rule_map_print(output_file, r);
    914 		cursor = cursor->next;
    915 		fprintf(output_file, "\n");
    916 	}
    917 }
    918 
    919 /**
    920  * This function is registered to the at exit handler and should clean up
    921  * the programs dynamic resources, such as memory and fd's.
    922  */
    923 static void cleanup() {
    924 
    925 	/* Only close this when it was opened by me and not the crt */
    926 	if (out_file_name && output_file) {
    927 		log_info("Closing file: %s\n", out_file_name);
    928 		fclose(output_file);
    929 	}
    930 
    931 	/* Only close this when it was opened by me  and not the crt */
    932 	if (in_file_name && input_file) {
    933 		log_info("Closing file: %s\n", in_file_name);
    934 		fclose(input_file);
    935 	}
    936 
    937 	if (pol.policy_file) {
    938 
    939 		log_info("Closing file: %s\n", pol.policy_file_name);
    940 		fclose(pol.policy_file);
    941 
    942 		if (pol.db)
    943 			sepol_policydb_free(pol.db);
    944 
    945 		if (pol.pf)
    946 			sepol_policy_file_free(pol.pf);
    947 
    948 		if (pol.handle)
    949 			sepol_handle_destroy(pol.handle);
    950 	}
    951 
    952 	log_info("Freeing list\n");
    953 	list_free();
    954 	hdestroy();
    955 }
    956 
    957 int main(int argc, char *argv[]) {
    958 	if (!hcreate(TABLE_SIZE)) {
    959 		log_error("Could not create hash table: %s\n", strerror(errno));
    960 		exit(EXIT_FAILURE);
    961 	}
    962 	atexit(cleanup);
    963 	handle_options(argc, argv);
    964 	init();
    965 	log_info("Starting to parse\n");
    966 	parse();
    967 	log_info("Parsing completed, generating output\n");
    968 	output();
    969 	log_info("Success, generated output\n");
    970 	exit(EXIT_SUCCESS);
    971 }
    972