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