Home | History | Annotate | Download | only in tools
      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 <stdbool.h>
     12 #include <sepol/sepol.h>
     13 #include <sepol/policydb/policydb.h>
     14 #include <pcre.h>
     15 
     16 #define TABLE_SIZE 1024
     17 #define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
     18 #define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
     19 #define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
     20 #define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
     21 #define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
     22 
     23 /**
     24  * Initializes an empty, static list.
     25  */
     26 #define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = free_fn }
     27 
     28 /**
     29  * given an item in the list, finds the offset for the container
     30  * it was stored in.
     31  *
     32  * @element The element from the list
     33  * @type The container type ie what you allocated that has the list_element structure in it.
     34  * @name The name of the field that is the list_element
     35  *
     36  */
     37 #define list_entry(element, type, name) \
     38 		(type *)(((uint8_t *)element) - (uint8_t *)&(((type *)NULL)->name))
     39 
     40 /**
     41  * Iterates over the list, do not free elements from the list when using this.
     42  * @list The list head to walk
     43  * @var The variable name for the cursor
     44  */
     45 #define list_for_each(list, var) \
     46 	for(var = (list)->head; var != NULL; var = var->next)
     47 
     48 
     49 typedef struct hash_entry hash_entry;
     50 typedef enum key_dir key_dir;
     51 typedef enum data_type data_type;
     52 typedef enum rule_map_switch rule_map_switch;
     53 typedef enum map_match map_match;
     54 typedef struct key_map key_map;
     55 typedef struct kvp kvp;
     56 typedef struct rule_map rule_map;
     57 typedef struct policy_info policy_info;
     58 typedef struct list_element list_element;
     59 typedef struct list list;
     60 typedef struct key_map_regex key_map_regex;
     61 typedef struct file_info file_info;
     62 
     63 enum map_match {
     64 	map_no_matches,
     65 	map_input_matched,
     66 	map_matched
     67 };
     68 
     69 const char *map_match_str[] = {
     70 	"do not match",
     71 	"match on all inputs",
     72 	"match on everything"
     73 };
     74 
     75 /**
     76  * Whether or not the "key" from a key vaue pair is considered an
     77  * input or an output.
     78  */
     79 enum key_dir {
     80 	dir_in, dir_out
     81 };
     82 
     83 struct list_element {
     84 	list_element *next;
     85 };
     86 
     87 struct list {
     88 	list_element *head;
     89 	list_element *tail;
     90 	void (*freefn)(list_element *e);
     91 };
     92 
     93 struct key_map_regex {
     94 	pcre *compiled;
     95 	pcre_extra *extra;
     96 };
     97 
     98 /**
     99  * The workhorse of the logic. This struct maps key value pairs to
    100  * an associated set of meta data maintained in rule_map_new()
    101  */
    102 struct key_map {
    103 	char *name;
    104 	key_dir dir;
    105 	char *data;
    106 	key_map_regex regex;
    107 	bool (*fn_validate)(char *value, char **errmsg);
    108 };
    109 
    110 /**
    111  * Key value pair struct, this represents the raw kvp values coming
    112  * from the rules files.
    113  */
    114 struct kvp {
    115 	char *key;
    116 	char *value;
    117 };
    118 
    119 /**
    120  * Rules are made up of meta data and an associated set of kvp stored in a
    121  * key_map array.
    122  */
    123 struct rule_map {
    124 	bool is_never_allow;
    125 	list violations;
    126 	list_element listify;
    127 	char *key; /** key value before hashing */
    128 	size_t length; /** length of the key map */
    129 	int lineno; /** Line number rule was encounter on */
    130 	char *filename; /** File it was found in */
    131 	key_map m[]; /** key value mapping */
    132 };
    133 
    134 struct hash_entry {
    135 	list_element listify;
    136 	rule_map *r; /** The rule map to store at that location */
    137 };
    138 
    139 /**
    140  * Data associated for a policy file
    141  */
    142 struct policy_info {
    143 
    144 	char *policy_file_name; /** policy file path name */
    145 	FILE *policy_file;      /** file handle to the policy file */
    146 	sepol_policydb_t *db;
    147 	sepol_policy_file_t *pf;
    148 	sepol_handle_t *handle;
    149 	sepol_context_t *con;
    150 };
    151 
    152 struct file_info {
    153 	FILE *file; /** file itself */
    154 	const char *name; /** name of file. do not free, these are not alloc'd */
    155 	list_element listify;
    156 };
    157 
    158 static void input_file_list_freefn(list_element *e);
    159 static void line_order_list_freefn(list_element *e);
    160 static void rule_map_free(rule_map *rm, bool is_in_htable);
    161 
    162 /** Set to !0 to enable verbose logging */
    163 static int logging_verbose = 0;
    164 
    165 /** file handle to the output file */
    166 static file_info out_file;
    167 
    168 static list input_file_list = list_init(input_file_list_freefn);
    169 
    170 static policy_info pol = {
    171 	.policy_file_name = NULL,
    172 	.policy_file = NULL,
    173 	.db = NULL,
    174 	.pf = NULL,
    175 	.handle = NULL,
    176 	.con = NULL
    177 };
    178 
    179 /**
    180  * Head pointer to a linked list of
    181  * rule map table entries (hash_entry), used for
    182  * preserving the order of entries
    183  * based on "first encounter"
    184  */
    185 static list line_order_list = list_init(line_order_list_freefn);
    186 
    187 /*
    188  * List of hash_entrys for never allow rules.
    189  */
    190 static list nallow_list = list_init(line_order_list_freefn);
    191 
    192 /* validation call backs */
    193 static bool validate_bool(char *value, char **errmsg);
    194 static bool validate_levelFrom(char *value, char **errmsg);
    195 static bool validate_selinux_type(char *value, char **errmsg);
    196 static bool validate_selinux_level(char *value, char **errmsg);
    197 
    198 /**
    199  * The heart of the mapping process, this must be updated if a new key value pair is added
    200  * to a rule.
    201  */
    202 key_map rules[] = {
    203                 /*Inputs*/
    204                 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
    205                 { .name = "isAutoPlayApp",  .dir = dir_in, .fn_validate = validate_bool },
    206                 { .name = "isOwner",        .dir = dir_in, .fn_validate = validate_bool },
    207                 { .name = "user",           .dir = dir_in,                              },
    208                 { .name = "seinfo",         .dir = dir_in,                              },
    209                 { .name = "name",           .dir = dir_in,                              },
    210                 { .name = "path",           .dir = dir_in,                              },
    211                 { .name = "isPrivApp",      .dir = dir_in, .fn_validate = validate_bool },
    212                 /*Outputs*/
    213                 { .name = "domain",         .dir = dir_out, .fn_validate = validate_selinux_type  },
    214                 { .name = "type",           .dir = dir_out, .fn_validate = validate_selinux_type  },
    215                 { .name = "levelFromUid",   .dir = dir_out, .fn_validate = validate_bool          },
    216                 { .name = "levelFrom",      .dir = dir_out, .fn_validate = validate_levelFrom     },
    217                 { .name = "level",          .dir = dir_out, .fn_validate = validate_selinux_level },
    218 };
    219 
    220 /**
    221  * Appends to the end of the list.
    222  * @list The list to append to
    223  * @e the element to append
    224  */
    225 void list_append(list *list, list_element *e) {
    226 
    227 	memset(e, 0, sizeof(*e));
    228 
    229 	if (list->head == NULL ) {
    230 		list->head = list->tail = e;
    231 		return;
    232 	}
    233 
    234 	list->tail->next = e;
    235 	list->tail = e;
    236 	return;
    237 }
    238 
    239 /**
    240  * Free's all the elements in the specified list.
    241  * @list The list to free
    242  */
    243 static void list_free(list *list) {
    244 
    245 	list_element *tmp;
    246 	list_element *cursor = list->head;
    247 
    248 	while (cursor) {
    249 		tmp = cursor;
    250 		cursor = cursor->next;
    251 		if (list->freefn) {
    252 			list->freefn(tmp);
    253 		}
    254 	}
    255 }
    256 
    257 /*
    258  * called when the lists are freed
    259  */
    260 static void line_order_list_freefn(list_element *e) {
    261 	hash_entry *h = list_entry(e, typeof(*h), listify);
    262 	rule_map_free(h->r, true);
    263 	free(h);
    264 }
    265 
    266 static void input_file_list_freefn(list_element *e) {
    267 	file_info *f = list_entry(e, typeof(*f), listify);
    268 
    269 	if (f->file) {
    270 		fclose(f->file);
    271 	}
    272 	free(f);
    273 }
    274 
    275 /**
    276  * Send a logging message to a file
    277  * @param out
    278  * 	Output file to send message too
    279  * @param prefix
    280  * 	A special prefix to write to the file, such as "Error:"
    281  * @param fmt
    282  * 	The printf style formatter to use, such as "%d"
    283  */
    284 static void __attribute__ ((format(printf, 3, 4)))
    285 log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
    286 
    287 	fprintf(out, "%s", prefix);
    288 	va_list args;
    289 	va_start(args, fmt);
    290 	vfprintf(out, fmt, args);
    291 	va_end(args);
    292 }
    293 
    294 /**
    295  * Checks for a type in the policy.
    296  * @param db
    297  * 	The policy db to search
    298  * @param type
    299  * 	The type to search for
    300  * @return
    301  * 	1 if the type is found, 0 otherwise.
    302  * @warning
    303  * 	This function always returns 1 if libsepol is not linked
    304  * 	statically to this executable and LINK_SEPOL_STATIC is not
    305  * 	defined.
    306  */
    307 static int check_type(sepol_policydb_t *db, char *type) {
    308 
    309 	int rc = 1;
    310 #if defined(LINK_SEPOL_STATIC)
    311 	policydb_t *d = (policydb_t *)db;
    312 	hashtab_datum_t dat;
    313 	dat = hashtab_search(d->p_types.table, type);
    314 	rc = (dat == NULL) ? 0 : 1;
    315 #endif
    316 	return rc;
    317 }
    318 
    319 static bool match_regex(key_map *assert, const key_map *check) {
    320 
    321 	char *tomatch = check->data;
    322 
    323 	int ret = pcre_exec(assert->regex.compiled, assert->regex.extra, tomatch,
    324 			strlen(tomatch), 0, 0, NULL, 0);
    325 
    326 	/* 0 from pcre_exec means matched */
    327 	return !ret;
    328 }
    329 
    330 static bool compile_regex(key_map *km, const char **errbuf, int *erroff) {
    331 
    332 	size_t size;
    333 	char *anchored;
    334 
    335 	/*
    336 	 * Explicitly anchor all regex's
    337 	 * The size is the length of the string to anchor (km->data), the anchor
    338 	 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
    339 	 */
    340 	size = strlen(km->data) + 3;
    341 	anchored = alloca(size);
    342 	sprintf(anchored, "^%s$", km->data);
    343 
    344 	km->regex.compiled = pcre_compile(anchored, PCRE_DOTALL, errbuf, erroff,
    345 			NULL );
    346 	if (!km->regex.compiled) {
    347 		return false;
    348 	}
    349 
    350 	km->regex.extra = pcre_study(km->regex.compiled, 0, errbuf);
    351 	return true;
    352 }
    353 
    354 static bool validate_bool(char *value, char **errmsg) {
    355 
    356 	if (!strcmp("true", value) || !strcmp("false", value)) {
    357 		return true;
    358 	}
    359 
    360 	*errmsg = "Expecting \"true\" or \"false\"";
    361 	return false;
    362 }
    363 
    364 static bool validate_levelFrom(char *value, char **errmsg) {
    365 
    366 	if(strcasecmp(value, "none") && strcasecmp(value, "all") &&
    367 		strcasecmp(value, "app") && strcasecmp(value, "user")) {
    368 		*errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
    369 		return false;
    370 	}
    371 	return true;
    372 }
    373 
    374 static bool validate_selinux_type(char *value, char **errmsg) {
    375 
    376 	/*
    377 	 * No policy file present means we cannot check
    378 	 * SE Linux types
    379 	 */
    380 	if (!pol.policy_file) {
    381 		return true;
    382 	}
    383 
    384 	if(!check_type(pol.db, value)) {
    385 		*errmsg = "Expecting a valid SELinux type";
    386 		return false;
    387 	}
    388 
    389 	return true;
    390 }
    391 
    392 static bool validate_selinux_level(char *value, char **errmsg) {
    393 
    394 	/*
    395 	 * No policy file present means we cannot check
    396 	 * SE Linux MLS
    397 	 */
    398 	if (!pol.policy_file) {
    399 		return true;
    400 	}
    401 
    402 	int ret = sepol_mls_check(pol.handle, pol.db, value);
    403 	if (ret < 0) {
    404 		*errmsg = "Expecting a valid SELinux MLS value";
    405 		return false;
    406 	}
    407 
    408 	return true;
    409 }
    410 
    411 /**
    412  * Validates a key_map against a set of enforcement rules, this
    413  * function exits the application on a type that cannot be properly
    414  * checked
    415  *
    416  * @param m
    417  * 	The key map to check
    418  * @param lineno
    419  * 	The line number in the source file for the corresponding key map
    420  * @return
    421  * 	true if valid, false if invalid
    422  */
    423 static bool key_map_validate(key_map *m, const char *filename, int lineno,
    424 		bool is_neverallow) {
    425 
    426 	int erroff;
    427 	const char *errbuf;
    428 	bool rc = true;
    429 	char *key = m->name;
    430 	char *value = m->data;
    431 	char *errmsg = NULL;
    432 
    433 	log_info("Validating %s=%s\n", key, value);
    434 
    435 	/*
    436 	 * Neverallows are completely skipped from sanity checking so you can match
    437 	 * un-unspecified inputs.
    438 	 */
    439 	if (is_neverallow) {
    440 		if (!m->regex.compiled) {
    441 			rc = compile_regex(m, &errbuf, &erroff);
    442 			if (!rc) {
    443 				log_error("Invalid regex on line %d : %s PCRE error: %s at offset %d",
    444 					lineno, value, errbuf, erroff);
    445 			}
    446 		}
    447 		goto out;
    448 	}
    449 
    450 	/* If the key has a validation routine, call it */
    451 	if (m->fn_validate) {
    452 		rc = m->fn_validate(value, &errmsg);
    453 
    454 		if (!rc) {
    455 			log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
    456 			lineno, filename, errmsg);
    457 		}
    458 	}
    459 
    460 out:
    461 	log_info("Key map validate returning: %d\n", rc);
    462 	return rc;
    463 }
    464 
    465 /**
    466  * Prints a rule map back to a file
    467  * @param fp
    468  * 	The file handle to print too
    469  * @param r
    470  * 	The rule map to print
    471  */
    472 static void rule_map_print(FILE *fp, rule_map *r) {
    473 
    474 	size_t i;
    475 	key_map *m;
    476 
    477 	for (i = 0; i < r->length; i++) {
    478 		m = &(r->m[i]);
    479 		if (i < r->length - 1)
    480 			fprintf(fp, "%s=%s ", m->name, m->data);
    481 		else
    482 			fprintf(fp, "%s=%s", m->name, m->data);
    483 	}
    484 }
    485 
    486 /**
    487  * Compare two rule maps for equality
    488  * @param rmA
    489  * 	a rule map to check
    490  * @param rmB
    491  * 	a rule map to check
    492  * @return
    493  *  a map_match enum indicating the result
    494  */
    495 static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
    496 
    497 	size_t i;
    498 	size_t j;
    499 	int inputs_found = 0;
    500 	int num_of_matched_inputs = 0;
    501 	int input_mode = 0;
    502 	size_t matches = 0;
    503 	key_map *mA;
    504 	key_map *mB;
    505 
    506 	for (i = 0; i < rmA->length; i++) {
    507 		mA = &(rmA->m[i]);
    508 
    509 		for (j = 0; j < rmB->length; j++) {
    510 			mB = &(rmB->m[j]);
    511 			input_mode = 0;
    512 
    513 			if (strcmp(mA->name, mB->name))
    514 				continue;
    515 
    516 			if (strcmp(mA->data, mB->data))
    517 				continue;
    518 
    519 			if (mB->dir != mA->dir)
    520 				continue;
    521 			else if (mB->dir == dir_in) {
    522 				input_mode = 1;
    523 				inputs_found++;
    524 			}
    525 
    526 			if (input_mode) {
    527 				log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
    528 				num_of_matched_inputs++;
    529 			}
    530 
    531 			/* Match found, move on */
    532 			log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
    533 			matches++;
    534 			break;
    535 		}
    536 	}
    537 
    538 	/* If they all matched*/
    539 	if (matches == rmA->length) {
    540 		log_info("Rule map cmp MATCH\n");
    541 		return map_matched;
    542 	}
    543 
    544 	/* They didn't all match but the input's did */
    545 	else if (num_of_matched_inputs == inputs_found) {
    546 		log_info("Rule map cmp INPUT MATCH\n");
    547 		return map_input_matched;
    548 	}
    549 
    550 	/* They didn't all match, and the inputs didn't match, ie it didn't
    551 	 * match */
    552 	else {
    553 		log_info("Rule map cmp NO MATCH\n");
    554 		return map_no_matches;
    555 	}
    556 }
    557 
    558 /**
    559  * Frees a rule map
    560  * @param rm
    561  * 	rule map to be freed.
    562  * @is_in_htable
    563  * 	True if the rule map has been added to the hash table, false
    564  * 	otherwise.
    565  */
    566 static void rule_map_free(rule_map *rm, bool is_in_htable) {
    567 
    568 	size_t i;
    569 	size_t len = rm->length;
    570 	for (i = 0; i < len; i++) {
    571 		key_map *m = &(rm->m[i]);
    572 		free(m->data);
    573 
    574 		if (m->regex.compiled) {
    575 			pcre_free(m->regex.compiled);
    576 		}
    577 
    578 		if (m->regex.extra) {
    579 			pcre_free_study(m->regex.extra);
    580 		}
    581 	}
    582 
    583 	/*
    584 	 * hdestroy() frees comparsion keys for non glibc
    585 	 * on GLIBC we always free on NON-GLIBC we free if
    586 	 * it is not in the htable.
    587 	 */
    588 	if (rm->key) {
    589 #ifdef __GLIBC__
    590 		/* silence unused warning */
    591 		(void)is_in_htable;
    592 		free(rm->key);
    593 #else
    594 		if (!is_in_htable) {
    595 			free(rm->key);
    596 		}
    597 #endif
    598 	}
    599 
    600 	free(rm->filename);
    601 	free(rm);
    602 }
    603 
    604 static void free_kvp(kvp *k) {
    605 	free(k->key);
    606 	free(k->value);
    607 }
    608 
    609 /**
    610  * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
    611  * It builds an assertion failure list for each rule map.
    612  * Note that this function logs all errors.
    613  *
    614  * Current Checks:
    615  * 1. That a specified name entry should have a specified seinfo entry as well.
    616  * 2. That no rule violates a neverallow
    617  * @param rm
    618  *  The rule map to check for validity.
    619  */
    620 static void rule_map_validate(rule_map *rm) {
    621 
    622 	size_t i, j;
    623 	const key_map *rule;
    624 	key_map *nrule;
    625 	hash_entry *e;
    626 	rule_map *assert;
    627 	list_element *cursor;
    628 
    629 	list_for_each(&nallow_list, cursor) {
    630 		e = list_entry(cursor, typeof(*e), listify);
    631 		assert = e->r;
    632 
    633 		size_t cnt = 0;
    634 
    635 		for (j = 0; j < assert->length; j++) {
    636 			nrule = &(assert->m[j]);
    637 
    638 			// mark that nrule->name is for a null check
    639 			bool is_null_check = !strcmp(nrule->data, "\"\"");
    640 
    641 			for (i = 0; i < rm->length; i++) {
    642 				rule = &(rm->m[i]);
    643 
    644 				if (!strcmp(rule->name, nrule->name)) {
    645 
    646 					/* the name was found, (data cannot be false) then it was specified */
    647 					is_null_check = false;
    648 
    649 					if (match_regex(nrule, rule)) {
    650 						cnt++;
    651 					}
    652 				}
    653 			}
    654 
    655 			/*
    656 			 * the nrule was marked in a null check and we never found a match on nrule, thus
    657 			 * it matched and we update the cnt
    658 			 */
    659 			if (is_null_check) {
    660 				cnt++;
    661 			}
    662 		}
    663 		if (cnt == assert->length) {
    664 			list_append(&rm->violations, &assert->listify);
    665 		}
    666 	}
    667 }
    668 
    669 /**
    670  * Given a set of key value pairs, this will construct a new rule map.
    671  * On error this function calls exit.
    672  * @param keys
    673  * 	Keys from a rule line to map
    674  * @param num_of_keys
    675  * 	The length of the keys array
    676  * @param lineno
    677  * 	The line number the keys were extracted from
    678  * @return
    679  * 	A rule map pointer.
    680  */
    681 static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
    682 		const char *filename, bool is_never_allow) {
    683 
    684 	size_t i = 0, j = 0;
    685 	rule_map *new_map = NULL;
    686 	kvp *k = NULL;
    687 	key_map *r = NULL, *x = NULL;
    688 	bool seen[KVP_NUM_OF_RULES];
    689 
    690 	for (i = 0; i < KVP_NUM_OF_RULES; i++)
    691 		seen[i] = false;
    692 
    693 	new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
    694 	if (!new_map)
    695 		goto oom;
    696 
    697 	new_map->is_never_allow = is_never_allow;
    698 	new_map->length = num_of_keys;
    699 	new_map->lineno = lineno;
    700 	new_map->filename = strdup(filename);
    701 	if (!new_map->filename) {
    702 		goto oom;
    703 	}
    704 
    705 	/* For all the keys in a rule line*/
    706 	for (i = 0; i < num_of_keys; i++) {
    707 		k = &(keys[i]);
    708 		r = &(new_map->m[i]);
    709 
    710 		for (j = 0; j < KVP_NUM_OF_RULES; j++) {
    711 			x = &(rules[j]);
    712 
    713 			/* Only assign key name to map name */
    714 			if (strcasecmp(k->key, x->name)) {
    715 				if (i == KVP_NUM_OF_RULES) {
    716 					log_error("No match for key: %s\n", k->key);
    717 					goto err;
    718 				}
    719 				continue;
    720 			}
    721 
    722 			if (seen[j]) {
    723 					log_error("Duplicated key: %s\n", k->key);
    724 					goto err;
    725 			}
    726 			seen[j] = true;
    727 
    728 			memcpy(r, x, sizeof(key_map));
    729 
    730 			/* Assign rule map value to one from file */
    731 			r->data = strdup(k->value);
    732 			if (!r->data)
    733 				goto oom;
    734 
    735 			/* Enforce type check*/
    736 			log_info("Validating keys!\n");
    737 			if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
    738 				log_error("Could not validate\n");
    739 				goto err;
    740 			}
    741 
    742 			/*
    743 			 * Only build key off of inputs with the exception of neverallows.
    744 			 * Neverallows are keyed off of all key value pairs,
    745 			 */
    746 			if (r->dir == dir_in || new_map->is_never_allow) {
    747 				char *tmp;
    748 				int key_len = strlen(k->key);
    749 				int val_len = strlen(k->value);
    750 				int l = (new_map->key) ? strlen(new_map->key) : 0;
    751 				l = l + key_len + val_len;
    752 				l += 1;
    753 
    754 				tmp = realloc(new_map->key, l);
    755 				if (!tmp)
    756 					goto oom;
    757 
    758 				if (!new_map->key)
    759 					memset(tmp, 0, l);
    760 
    761 				new_map->key = tmp;
    762 
    763 				strncat(new_map->key, k->key, key_len);
    764 				strncat(new_map->key, k->value, val_len);
    765 			}
    766 			break;
    767 		}
    768 		free_kvp(k);
    769 	}
    770 
    771 	if (new_map->key == NULL) {
    772 		log_error("Strange, no keys found, input file corrupt perhaps?\n");
    773 		goto err;
    774 	}
    775 
    776 	return new_map;
    777 
    778 oom:
    779 	log_error("Out of memory!\n");
    780 err:
    781 	if(new_map) {
    782 		rule_map_free(new_map, false);
    783 		for (; i < num_of_keys; i++) {
    784 			k = &(keys[i]);
    785 			free_kvp(k);
    786 		}
    787 	}
    788 	return NULL;
    789 }
    790 
    791 /**
    792  * Print the usage of the program
    793  */
    794 static void usage() {
    795 	printf(
    796 	        "checkseapp [options] <input file>\n"
    797 		        "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
    798 		        "and allows later declarations to override previous ones on a match.\n"
    799 		        "Options:\n"
    800 		        "-h - print this help message\n"
    801 		        "-v - enable verbose debugging informations\n"
    802 		        "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
    803 		        "-o output file - specify output file or - for stdout. No argument runs in silent mode and outputs nothing\n");
    804 }
    805 
    806 static void init() {
    807 
    808 	bool has_out_file;
    809 	list_element *cursor;
    810 	file_info *tmp;
    811 
    812 	/* input files if the list is empty, use stdin */
    813 	if (!input_file_list.head) {
    814 		log_info("Using stdin for input\n");
    815 		tmp = malloc(sizeof(*tmp));
    816 		if (!tmp) {
    817 			log_error("oom");
    818 			exit(EXIT_FAILURE);
    819 		}
    820 		tmp->name = "stdin";
    821 		tmp->file = stdin;
    822 		list_append(&input_file_list, &(tmp->listify));
    823 	}
    824 	else {
    825 		list_for_each(&input_file_list, cursor) {
    826 			tmp = list_entry(cursor, typeof(*tmp), listify);
    827 
    828 			log_info("Opening input file: \"%s\"\n", tmp->name);
    829 			tmp->file = fopen(tmp->name, "r");
    830 			if (!tmp->file) {
    831 				log_error("Could not open file: %s error: %s\n", tmp->name,
    832 						strerror(errno));
    833 				exit(EXIT_FAILURE);
    834 			}
    835 		}
    836 	}
    837 
    838 	has_out_file = out_file.name != NULL;
    839 
    840 	/* If output file is -, then use stdout, else open the path */
    841 	if (has_out_file && !strcmp(out_file.name, "-")) {
    842 		out_file.file = stdout;
    843 		out_file.name = "stdout";
    844 	}
    845 	else if (has_out_file) {
    846 		out_file.file = fopen(out_file.name, "w+");
    847 	}
    848 
    849 	if (has_out_file && !out_file.file) {
    850 		log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
    851 				strerror(errno));
    852 		exit(EXIT_FAILURE);
    853 	}
    854 
    855 	if (pol.policy_file_name) {
    856 		log_info("Opening policy file: %s\n", pol.policy_file_name);
    857 		pol.policy_file = fopen(pol.policy_file_name, "rb");
    858 		if (!pol.policy_file) {
    859 			log_error("Could not open file: %s error: %s\n",
    860 					pol.policy_file_name, strerror(errno));
    861 			exit(EXIT_FAILURE);
    862 		}
    863 
    864 		pol.handle = sepol_handle_create();
    865 		if (!pol.handle) {
    866 			log_error("Could not create sepolicy handle: %s\n",
    867 					strerror(errno));
    868 			exit(EXIT_FAILURE);
    869 		}
    870 
    871 		if (sepol_policy_file_create(&pol.pf) < 0) {
    872 			log_error("Could not create sepolicy file: %s!\n",
    873 					strerror(errno));
    874 			exit(EXIT_FAILURE);
    875 		}
    876 
    877 		sepol_policy_file_set_fp(pol.pf, pol.policy_file);
    878 		sepol_policy_file_set_handle(pol.pf, pol.handle);
    879 
    880 		if (sepol_policydb_create(&pol.db) < 0) {
    881 			log_error("Could not create sepolicy db: %s!\n",
    882 					strerror(errno));
    883 			exit(EXIT_FAILURE);
    884 		}
    885 
    886 		if (sepol_policydb_read(pol.db, pol.pf) < 0) {
    887 			log_error("Could not lod policy file to db: %s!\n",
    888 					strerror(errno));
    889 			exit(EXIT_FAILURE);
    890 		}
    891 	}
    892 
    893 	list_for_each(&input_file_list, cursor) {
    894 		tmp = list_entry(cursor, typeof(*tmp), listify);
    895 		log_info("Input file set to: \"%s\"\n", tmp->name);
    896 	}
    897 
    898 	log_info("Policy file set to: \"%s\"\n",
    899 			(pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
    900 	log_info("Output file set to: \"%s\"\n", out_file.name);
    901 
    902 #if !defined(LINK_SEPOL_STATIC)
    903 	log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
    904 #endif
    905 
    906 }
    907 
    908 /**
    909  * Handle parsing and setting the global flags for the command line
    910  * options. This function calls exit on failure.
    911  * @param argc
    912  * 	argument count
    913  * @param argv
    914  * 	argument list
    915  */
    916 static void handle_options(int argc, char *argv[]) {
    917 
    918 	int c;
    919 	file_info *input_file;
    920 
    921 	while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
    922 		switch (c) {
    923 		case 'h':
    924 			usage();
    925 			exit(EXIT_SUCCESS);
    926 		case 'o':
    927 			out_file.name = optarg;
    928 			break;
    929 		case 'p':
    930 			pol.policy_file_name = optarg;
    931 			break;
    932 		case 'v':
    933 			log_set_verbose();
    934 			break;
    935 		case '?':
    936 			if (optopt == 'o' || optopt == 'p')
    937 				log_error("Option -%c requires an argument.\n", optopt);
    938 			else if (isprint (optopt))
    939 				log_error("Unknown option `-%c'.\n", optopt);
    940 			else {
    941 				log_error(
    942 						"Unknown option character `\\x%x'.\n",
    943 						optopt);
    944 			}
    945 		default:
    946 			exit(EXIT_FAILURE);
    947 		}
    948 	}
    949 
    950 	for (c = optind; c < argc; c++) {
    951 
    952 		input_file = calloc(1, sizeof(*input_file));
    953 		if (!input_file) {
    954 			log_error("oom");
    955 			exit(EXIT_FAILURE);
    956 		}
    957 		input_file->name = argv[c];
    958 		list_append(&input_file_list, &input_file->listify);
    959 	}
    960 }
    961 
    962 /**
    963  * Adds a rule to the hash table and to the ordered list if needed.
    964  * @param rm
    965  * 	The rule map to add.
    966  */
    967 static void rule_add(rule_map *rm) {
    968 
    969 	map_match cmp;
    970 	ENTRY e;
    971 	ENTRY *f;
    972 	hash_entry *entry;
    973 	hash_entry *tmp;
    974 	list *list_to_addto;
    975 
    976 	e.key = rm->key;
    977 
    978 	log_info("Searching for key: %s\n", e.key);
    979 	/* Check to see if it has already been added*/
    980 	f = hsearch(e, FIND);
    981 
    982 	/*
    983 	 * Since your only hashing on a partial key, the inputs we need to handle
    984 	 * when you want to override the outputs for a given input set, as well as
    985 	 * checking for duplicate entries.
    986 	 */
    987 	if(f) {
    988 		log_info("Existing entry found!\n");
    989 		tmp = (hash_entry *)f->data;
    990 		cmp = rule_map_cmp(rm, tmp->r);
    991 		log_error("Duplicate line detected in file: %s\n"
    992 			  "Lines %d and %d %s!\n",
    993 			  rm->filename, tmp->r->lineno, rm->lineno,
    994 			  map_match_str[cmp]);
    995 		rule_map_free(rm, false);
    996 		goto err;
    997 	}
    998 	/* It wasn't found, just add the rule map to the table */
    999 	else {
   1000 
   1001 		entry = malloc(sizeof(hash_entry));
   1002 		if (!entry)
   1003 			goto oom;
   1004 
   1005 		entry->r = rm;
   1006 		e.data = entry;
   1007 
   1008 		f = hsearch(e, ENTER);
   1009 		if(f == NULL) {
   1010 			goto oom;
   1011 		}
   1012 
   1013 		/* new entries must be added to the ordered list */
   1014 		entry->r = rm;
   1015 		list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
   1016 		list_append(list_to_addto, &entry->listify);
   1017 	}
   1018 
   1019 	return;
   1020 oom:
   1021 	if (e.key)
   1022 		free(e.key);
   1023 	if (entry)
   1024 		free(entry);
   1025 	if (rm)
   1026 		free(rm);
   1027 	log_error("Out of memory in function: %s\n", __FUNCTION__);
   1028 err:
   1029 	exit(EXIT_FAILURE);
   1030 }
   1031 
   1032 static void parse_file(file_info *in_file) {
   1033 
   1034 	char *p;
   1035 	size_t len;
   1036 	char *token;
   1037 	char *saveptr;
   1038 	bool is_never_allow;
   1039 	bool found_whitespace;
   1040 
   1041 	size_t lineno = 0;
   1042 	char *name = NULL;
   1043 	char *value = NULL;
   1044 	size_t token_cnt = 0;
   1045 
   1046 	char line_buf[BUFSIZ];
   1047 	kvp keys[KVP_NUM_OF_RULES];
   1048 
   1049 	while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
   1050 		lineno++;
   1051 		is_never_allow = false;
   1052 		found_whitespace = false;
   1053 		log_info("Got line %zu\n", lineno);
   1054 		len = strlen(line_buf);
   1055 		if (line_buf[len - 1] == '\n')
   1056 			line_buf[len - 1] = '\0';
   1057 		p = line_buf;
   1058 
   1059 		/* neverallow lines must start with neverallow (ie ^neverallow) */
   1060 		if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
   1061 			p += strlen("neverallow");
   1062 			is_never_allow = true;
   1063 		}
   1064 
   1065 		/* strip trailing whitespace skip comments */
   1066 		while (isspace(*p)) {
   1067 			p++;
   1068 			found_whitespace = true;
   1069 		}
   1070 		if (*p == '#' || *p == '\0')
   1071 			continue;
   1072 
   1073 		token = strtok_r(p, " \t", &saveptr);
   1074 		if (!token)
   1075 			goto err;
   1076 
   1077 		token_cnt = 0;
   1078 		memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
   1079 		while (1) {
   1080 
   1081 			name = token;
   1082 			value = strchr(name, '=');
   1083 			if (!value)
   1084 				goto err;
   1085 			*value++ = 0;
   1086 
   1087 			keys[token_cnt].key = strdup(name);
   1088 			if (!keys[token_cnt].key)
   1089 				goto oom;
   1090 
   1091 			keys[token_cnt].value = strdup(value);
   1092 			if (!keys[token_cnt].value)
   1093 				goto oom;
   1094 
   1095 			token_cnt++;
   1096 
   1097 			token = strtok_r(NULL, " \t", &saveptr);
   1098 			if (!token)
   1099 				break;
   1100 
   1101 		} /*End token parsing */
   1102 
   1103 		rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
   1104 		if (!r)
   1105 			goto err;
   1106 		rule_add(r);
   1107 
   1108 	} /* End file parsing */
   1109 	return;
   1110 
   1111 err:
   1112 	log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
   1113 		in_file->name, lineno, name, value);
   1114 	if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
   1115 		log_error("perhaps whitespace before neverallow\n");
   1116 	}
   1117 	exit(EXIT_FAILURE);
   1118 oom:
   1119 	log_error("In function %s:  Out of memory\n", __FUNCTION__);
   1120 	exit(EXIT_FAILURE);
   1121 }
   1122 
   1123 /**
   1124  * Parses the seapp_contexts file and neverallow file
   1125  * and adds them to the hash table and ordered list entries
   1126  * when it encounters them.
   1127  * Calls exit on failure.
   1128  */
   1129 static void parse() {
   1130 
   1131 	file_info *current;
   1132 	list_element *cursor;
   1133 	list_for_each(&input_file_list, cursor) {
   1134 		current = list_entry(cursor, typeof(*current), listify);
   1135 		parse_file(current);
   1136 	}
   1137 }
   1138 
   1139 static void validate() {
   1140 
   1141 	list_element *cursor, *v;
   1142 	bool found_issues = false;
   1143 	hash_entry *e;
   1144 	rule_map *r;
   1145 	list_for_each(&line_order_list, cursor) {
   1146 		e = list_entry(cursor, typeof(*e), listify);
   1147 		rule_map_validate(e->r);
   1148 	}
   1149 
   1150 	list_for_each(&line_order_list, cursor) {
   1151 		e = list_entry(cursor, typeof(*e), listify);
   1152 		r = e->r;
   1153 		list_for_each(&r->violations, v) {
   1154 			found_issues = true;
   1155 			log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
   1156 			rule_map_print(stderr, e->r);
   1157 			r = list_entry(v, rule_map, listify);
   1158 			fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
   1159 			rule_map_print(stderr, r);
   1160 			fprintf(stderr, "\"\n");
   1161 		}
   1162 	}
   1163 
   1164 	if (found_issues) {
   1165 		exit(EXIT_FAILURE);
   1166 	}
   1167 }
   1168 
   1169 /**
   1170  * Should be called after parsing to cause the printing of the rule_maps
   1171  * stored in the ordered list, head first, which preserves the "first encountered"
   1172  * ordering.
   1173  */
   1174 static void output() {
   1175 
   1176 	hash_entry *e;
   1177 	list_element *cursor;
   1178 
   1179 	if (!out_file.file) {
   1180 		log_info("No output file, not outputting.\n");
   1181 		return;
   1182 	}
   1183 
   1184 	list_for_each(&line_order_list, cursor) {
   1185 		e = list_entry(cursor, hash_entry, listify);
   1186 		rule_map_print(out_file.file, e->r);
   1187 		fprintf(out_file.file, "\n");
   1188 	}
   1189 }
   1190 
   1191 /**
   1192  * This function is registered to the at exit handler and should clean up
   1193  * the programs dynamic resources, such as memory and fd's.
   1194  */
   1195 static void cleanup() {
   1196 
   1197 	/* Only close this when it was opened by me and not the crt */
   1198 	if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
   1199 		log_info("Closing file: %s\n", out_file.name);
   1200 		fclose(out_file.file);
   1201 	}
   1202 
   1203 	if (pol.policy_file) {
   1204 
   1205 		log_info("Closing file: %s\n", pol.policy_file_name);
   1206 		fclose(pol.policy_file);
   1207 
   1208 		if (pol.db)
   1209 			sepol_policydb_free(pol.db);
   1210 
   1211 		if (pol.pf)
   1212 			sepol_policy_file_free(pol.pf);
   1213 
   1214 		if (pol.handle)
   1215 			sepol_handle_destroy(pol.handle);
   1216 	}
   1217 
   1218 	log_info("Freeing lists\n");
   1219 	list_free(&input_file_list);
   1220 	list_free(&line_order_list);
   1221 	list_free(&nallow_list);
   1222 	hdestroy();
   1223 }
   1224 
   1225 int main(int argc, char *argv[]) {
   1226 	if (!hcreate(TABLE_SIZE)) {
   1227 		log_error("Could not create hash table: %s\n", strerror(errno));
   1228 		exit(EXIT_FAILURE);
   1229 	}
   1230 	atexit(cleanup);
   1231 	handle_options(argc, argv);
   1232 	init();
   1233 	log_info("Starting to parse\n");
   1234 	parse();
   1235 	log_info("Parsing completed, generating output\n");
   1236 	validate();
   1237 	output();
   1238 	log_info("Success, generated output\n");
   1239 	exit(EXIT_SUCCESS);
   1240 }
   1241