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
49typedef struct hash_entry hash_entry;
50typedef enum key_dir key_dir;
51typedef enum data_type data_type;
52typedef enum rule_map_switch rule_map_switch;
53typedef enum map_match map_match;
54typedef struct key_map key_map;
55typedef struct kvp kvp;
56typedef struct rule_map rule_map;
57typedef struct policy_info policy_info;
58typedef struct list_element list_element;
59typedef struct list list;
60typedef struct key_map_regex key_map_regex;
61typedef struct file_info file_info;
62
63enum map_match {
64	map_no_matches,
65	map_input_matched,
66	map_matched
67};
68
69const 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 */
79enum key_dir {
80	dir_in, dir_out
81};
82
83struct list_element {
84	list_element *next;
85};
86
87struct list {
88	list_element *head;
89	list_element *tail;
90	void (*freefn)(list_element *e);
91};
92
93struct 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 */
102struct 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 */
114struct 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 */
123struct 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
134struct 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 */
142struct 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
152struct 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
158static void input_file_list_freefn(list_element *e);
159static void line_order_list_freefn(list_element *e);
160static void rule_map_free(rule_map *rm, bool is_in_htable);
161
162/** Set to !0 to enable verbose logging */
163static int logging_verbose = 0;
164
165/** file handle to the output file */
166static file_info out_file;
167
168static list input_file_list = list_init(input_file_list_freefn);
169
170static 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 */
185static list line_order_list = list_init(line_order_list_freefn);
186
187/*
188 * List of hash_entrys for never allow rules.
189 */
190static list nallow_list = list_init(line_order_list_freefn);
191
192/* validation call backs */
193static bool validate_bool(char *value, char **errmsg);
194static bool validate_levelFrom(char *value, char **errmsg);
195static bool validate_selinux_type(char *value, char **errmsg);
196static 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 */
202key_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 */
225void 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 */
243static 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 */
260static 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
266static 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 */
284static void __attribute__ ((format(printf, 3, 4)))
285log_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 */
307static 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
319static 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
330static 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
354static 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
364static 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
374static 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
392static 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 */
423static 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
460out:
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 */
472static 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 */
495static 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 */
566static 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
604static 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 */
620static 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 */
681static 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
778oom:
779	log_error("Out of memory!\n");
780err:
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 */
794static 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
806static 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 */
916static 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 */
967static 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;
1020oom:
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__);
1028err:
1029	exit(EXIT_FAILURE);
1030}
1031
1032static 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
1111err:
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);
1118oom:
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 */
1129static 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
1139static 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 */
1174static 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 */
1195static 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
1225int 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