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