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