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