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