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