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