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