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