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 #include <pcre.h>
15
16 #define TABLE_SIZE 1024
17 #define KVP_NUM_OF_RULES (sizeof(rules) / sizeof(key_map))
18 #define log_set_verbose() do { logging_verbose = 1; log_info("Enabling verbose\n"); } while(0)
19 #define log_error(fmt, ...) log_msg(stderr, "Error: ", fmt, ##__VA_ARGS__)
20 #define log_warn(fmt, ...) log_msg(stderr, "Warning: ", fmt, ##__VA_ARGS__)
21 #define log_info(fmt, ...) if (logging_verbose ) { log_msg(stdout, "Info: ", fmt, ##__VA_ARGS__); }
22
23 /**
24 * Initializes an empty, static list.
25 */
26 #define list_init(free_fn) { .head = NULL, .tail = NULL, .freefn = free_fn }
27
28 /**
29 * given an item in the list, finds the offset for the container
30 * it was stored in.
31 *
32 * @element The element from the list
33 * @type The container type ie what you allocated that has the list_element structure in it.
34 * @name The name of the field that is the list_element
35 *
36 */
37 #define list_entry(element, type, name) \
38 (type *)(((uint8_t *)element) - (uint8_t *)&(((type *)NULL)->name))
39
40 /**
41 * Iterates over the list, do not free elements from the list when using this.
42 * @list The list head to walk
43 * @var The variable name for the cursor
44 */
45 #define list_for_each(list, var) \
46 for(var = (list)->head; var != NULL; var = var->next)
47
48
49 typedef struct hash_entry hash_entry;
50 typedef enum key_dir key_dir;
51 typedef enum data_type data_type;
52 typedef enum rule_map_switch rule_map_switch;
53 typedef enum map_match map_match;
54 typedef struct key_map key_map;
55 typedef struct kvp kvp;
56 typedef struct rule_map rule_map;
57 typedef struct policy_info policy_info;
58 typedef struct list_element list_element;
59 typedef struct list list;
60 typedef struct key_map_regex key_map_regex;
61 typedef struct file_info file_info;
62
63 enum map_match {
64 map_no_matches,
65 map_input_matched,
66 map_matched
67 };
68
69 const char *map_match_str[] = {
70 "do not match",
71 "match on all inputs",
72 "match on everything"
73 };
74
75 /**
76 * Whether or not the "key" from a key vaue pair is considered an
77 * input or an output.
78 */
79 enum key_dir {
80 dir_in, dir_out
81 };
82
83 struct list_element {
84 list_element *next;
85 };
86
87 struct list {
88 list_element *head;
89 list_element *tail;
90 void (*freefn)(list_element *e);
91 };
92
93 struct key_map_regex {
94 pcre *compiled;
95 pcre_extra *extra;
96 };
97
98 /**
99 * The workhorse of the logic. This struct maps key value pairs to
100 * an associated set of meta data maintained in rule_map_new()
101 */
102 struct key_map {
103 char *name;
104 key_dir dir;
105 char *data;
106 key_map_regex regex;
107 bool (*fn_validate)(char *value, char **errmsg);
108 };
109
110 /**
111 * Key value pair struct, this represents the raw kvp values coming
112 * from the rules files.
113 */
114 struct kvp {
115 char *key;
116 char *value;
117 };
118
119 /**
120 * Rules are made up of meta data and an associated set of kvp stored in a
121 * key_map array.
122 */
123 struct rule_map {
124 bool is_never_allow;
125 list violations;
126 list_element listify;
127 char *key; /** key value before hashing */
128 size_t length; /** length of the key map */
129 int lineno; /** Line number rule was encounter on */
130 char *filename; /** File it was found in */
131 key_map m[]; /** key value mapping */
132 };
133
134 struct hash_entry {
135 list_element listify;
136 rule_map *r; /** The rule map to store at that location */
137 };
138
139 /**
140 * Data associated for a policy file
141 */
142 struct policy_info {
143
144 char *policy_file_name; /** policy file path name */
145 FILE *policy_file; /** file handle to the policy file */
146 sepol_policydb_t *db;
147 sepol_policy_file_t *pf;
148 sepol_handle_t *handle;
149 sepol_context_t *con;
150 };
151
152 struct file_info {
153 FILE *file; /** file itself */
154 const char *name; /** name of file. do not free, these are not alloc'd */
155 list_element listify;
156 };
157
158 static void input_file_list_freefn(list_element *e);
159 static void line_order_list_freefn(list_element *e);
160 static void rule_map_free(rule_map *rm, bool is_in_htable);
161
162 /** Set to !0 to enable verbose logging */
163 static int logging_verbose = 0;
164
165 /** file handle to the output file */
166 static file_info out_file;
167
168 static list input_file_list = list_init(input_file_list_freefn);
169
170 static policy_info pol = {
171 .policy_file_name = NULL,
172 .policy_file = NULL,
173 .db = NULL,
174 .pf = NULL,
175 .handle = NULL,
176 .con = NULL
177 };
178
179 /**
180 * Head pointer to a linked list of
181 * rule map table entries (hash_entry), used for
182 * preserving the order of entries
183 * based on "first encounter"
184 */
185 static list line_order_list = list_init(line_order_list_freefn);
186
187 /*
188 * List of hash_entrys for never allow rules.
189 */
190 static list nallow_list = list_init(line_order_list_freefn);
191
192 /* validation call backs */
193 static bool validate_bool(char *value, char **errmsg);
194 static bool validate_levelFrom(char *value, char **errmsg);
195 static bool validate_selinux_type(char *value, char **errmsg);
196 static bool validate_selinux_level(char *value, char **errmsg);
197
198 /**
199 * The heart of the mapping process, this must be updated if a new key value pair is added
200 * to a rule.
201 */
202 key_map rules[] = {
203 /*Inputs*/
204 { .name = "isSystemServer", .dir = dir_in, .fn_validate = validate_bool },
205 { .name = "isAutoPlayApp", .dir = dir_in, .fn_validate = validate_bool },
206 { .name = "isOwner", .dir = dir_in, .fn_validate = validate_bool },
207 { .name = "user", .dir = dir_in, },
208 { .name = "seinfo", .dir = dir_in, },
209 { .name = "name", .dir = dir_in, },
210 { .name = "path", .dir = dir_in, },
211 { .name = "isPrivApp", .dir = dir_in, .fn_validate = validate_bool },
212 /*Outputs*/
213 { .name = "domain", .dir = dir_out, .fn_validate = validate_selinux_type },
214 { .name = "type", .dir = dir_out, .fn_validate = validate_selinux_type },
215 { .name = "levelFromUid", .dir = dir_out, .fn_validate = validate_bool },
216 { .name = "levelFrom", .dir = dir_out, .fn_validate = validate_levelFrom },
217 { .name = "level", .dir = dir_out, .fn_validate = validate_selinux_level },
218 };
219
220 /**
221 * Appends to the end of the list.
222 * @list The list to append to
223 * @e the element to append
224 */
list_append(list * list,list_element * e)225 void list_append(list *list, list_element *e) {
226
227 memset(e, 0, sizeof(*e));
228
229 if (list->head == NULL ) {
230 list->head = list->tail = e;
231 return;
232 }
233
234 list->tail->next = e;
235 list->tail = e;
236 return;
237 }
238
239 /**
240 * Free's all the elements in the specified list.
241 * @list The list to free
242 */
list_free(list * list)243 static void list_free(list *list) {
244
245 list_element *tmp;
246 list_element *cursor = list->head;
247
248 while (cursor) {
249 tmp = cursor;
250 cursor = cursor->next;
251 if (list->freefn) {
252 list->freefn(tmp);
253 }
254 }
255 }
256
257 /*
258 * called when the lists are freed
259 */
line_order_list_freefn(list_element * e)260 static void line_order_list_freefn(list_element *e) {
261 hash_entry *h = list_entry(e, typeof(*h), listify);
262 rule_map_free(h->r, true);
263 free(h);
264 }
265
input_file_list_freefn(list_element * e)266 static void input_file_list_freefn(list_element *e) {
267 file_info *f = list_entry(e, typeof(*f), listify);
268
269 if (f->file) {
270 fclose(f->file);
271 }
272 free(f);
273 }
274
275 /**
276 * Send a logging message to a file
277 * @param out
278 * Output file to send message too
279 * @param prefix
280 * A special prefix to write to the file, such as "Error:"
281 * @param fmt
282 * The printf style formatter to use, such as "%d"
283 */
284 static void __attribute__ ((format(printf, 3, 4)))
log_msg(FILE * out,const char * prefix,const char * fmt,...)285 log_msg(FILE *out, const char *prefix, const char *fmt, ...) {
286
287 fprintf(out, "%s", prefix);
288 va_list args;
289 va_start(args, fmt);
290 vfprintf(out, fmt, args);
291 va_end(args);
292 }
293
294 /**
295 * Checks for a type in the policy.
296 * @param db
297 * The policy db to search
298 * @param type
299 * The type to search for
300 * @return
301 * 1 if the type is found, 0 otherwise.
302 * @warning
303 * This function always returns 1 if libsepol is not linked
304 * statically to this executable and LINK_SEPOL_STATIC is not
305 * defined.
306 */
check_type(sepol_policydb_t * db,char * type)307 static int check_type(sepol_policydb_t *db, char *type) {
308
309 int rc = 1;
310 #if defined(LINK_SEPOL_STATIC)
311 policydb_t *d = (policydb_t *)db;
312 hashtab_datum_t dat;
313 dat = hashtab_search(d->p_types.table, type);
314 rc = (dat == NULL) ? 0 : 1;
315 #endif
316 return rc;
317 }
318
match_regex(key_map * assert,const key_map * check)319 static bool match_regex(key_map *assert, const key_map *check) {
320
321 char *tomatch = check->data;
322
323 int ret = pcre_exec(assert->regex.compiled, assert->regex.extra, tomatch,
324 strlen(tomatch), 0, 0, NULL, 0);
325
326 /* 0 from pcre_exec means matched */
327 return !ret;
328 }
329
compile_regex(key_map * km,const char ** errbuf,int * erroff)330 static bool compile_regex(key_map *km, const char **errbuf, int *erroff) {
331
332 size_t size;
333 char *anchored;
334
335 /*
336 * Explicitly anchor all regex's
337 * The size is the length of the string to anchor (km->data), the anchor
338 * characters ^ and $ and the null byte. Hence strlen(km->data) + 3
339 */
340 size = strlen(km->data) + 3;
341 anchored = alloca(size);
342 sprintf(anchored, "^%s$", km->data);
343
344 km->regex.compiled = pcre_compile(anchored, PCRE_DOTALL, errbuf, erroff,
345 NULL );
346 if (!km->regex.compiled) {
347 return false;
348 }
349
350 km->regex.extra = pcre_study(km->regex.compiled, 0, errbuf);
351 return true;
352 }
353
validate_bool(char * value,char ** errmsg)354 static bool validate_bool(char *value, char **errmsg) {
355
356 if (!strcmp("true", value) || !strcmp("false", value)) {
357 return true;
358 }
359
360 *errmsg = "Expecting \"true\" or \"false\"";
361 return false;
362 }
363
validate_levelFrom(char * value,char ** errmsg)364 static bool validate_levelFrom(char *value, char **errmsg) {
365
366 if(strcasecmp(value, "none") && strcasecmp(value, "all") &&
367 strcasecmp(value, "app") && strcasecmp(value, "user")) {
368 *errmsg = "Expecting one of: \"none\", \"all\", \"app\" or \"user\"";
369 return false;
370 }
371 return true;
372 }
373
validate_selinux_type(char * value,char ** errmsg)374 static bool validate_selinux_type(char *value, char **errmsg) {
375
376 /*
377 * No policy file present means we cannot check
378 * SE Linux types
379 */
380 if (!pol.policy_file) {
381 return true;
382 }
383
384 if(!check_type(pol.db, value)) {
385 *errmsg = "Expecting a valid SELinux type";
386 return false;
387 }
388
389 return true;
390 }
391
validate_selinux_level(char * value,char ** errmsg)392 static bool validate_selinux_level(char *value, char **errmsg) {
393
394 /*
395 * No policy file present means we cannot check
396 * SE Linux MLS
397 */
398 if (!pol.policy_file) {
399 return true;
400 }
401
402 int ret = sepol_mls_check(pol.handle, pol.db, value);
403 if (ret < 0) {
404 *errmsg = "Expecting a valid SELinux MLS value";
405 return false;
406 }
407
408 return true;
409 }
410
411 /**
412 * Validates a key_map against a set of enforcement rules, this
413 * function exits the application on a type that cannot be properly
414 * checked
415 *
416 * @param m
417 * The key map to check
418 * @param lineno
419 * The line number in the source file for the corresponding key map
420 * @return
421 * true if valid, false if invalid
422 */
key_map_validate(key_map * m,const char * filename,int lineno,bool is_neverallow)423 static bool key_map_validate(key_map *m, const char *filename, int lineno,
424 bool is_neverallow) {
425
426 int erroff;
427 const char *errbuf;
428 bool rc = true;
429 char *key = m->name;
430 char *value = m->data;
431 char *errmsg = NULL;
432
433 log_info("Validating %s=%s\n", key, value);
434
435 /*
436 * Neverallows are completely skipped from sanity checking so you can match
437 * un-unspecified inputs.
438 */
439 if (is_neverallow) {
440 if (!m->regex.compiled) {
441 rc = compile_regex(m, &errbuf, &erroff);
442 if (!rc) {
443 log_error("Invalid regex on line %d : %s PCRE error: %s at offset %d",
444 lineno, value, errbuf, erroff);
445 }
446 }
447 goto out;
448 }
449
450 /* If the key has a validation routine, call it */
451 if (m->fn_validate) {
452 rc = m->fn_validate(value, &errmsg);
453
454 if (!rc) {
455 log_error("Could not validate key \"%s\" for value \"%s\" on line: %d in file: \"%s\": %s\n", key, value,
456 lineno, filename, errmsg);
457 }
458 }
459
460 out:
461 log_info("Key map validate returning: %d\n", rc);
462 return rc;
463 }
464
465 /**
466 * Prints a rule map back to a file
467 * @param fp
468 * The file handle to print too
469 * @param r
470 * The rule map to print
471 */
rule_map_print(FILE * fp,rule_map * r)472 static void rule_map_print(FILE *fp, rule_map *r) {
473
474 size_t i;
475 key_map *m;
476
477 for (i = 0; i < r->length; i++) {
478 m = &(r->m[i]);
479 if (i < r->length - 1)
480 fprintf(fp, "%s=%s ", m->name, m->data);
481 else
482 fprintf(fp, "%s=%s", m->name, m->data);
483 }
484 }
485
486 /**
487 * Compare two rule maps for equality
488 * @param rmA
489 * a rule map to check
490 * @param rmB
491 * a rule map to check
492 * @return
493 * a map_match enum indicating the result
494 */
rule_map_cmp(rule_map * rmA,rule_map * rmB)495 static map_match rule_map_cmp(rule_map *rmA, rule_map *rmB) {
496
497 size_t i;
498 size_t j;
499 int inputs_found = 0;
500 int num_of_matched_inputs = 0;
501 int input_mode = 0;
502 size_t matches = 0;
503 key_map *mA;
504 key_map *mB;
505
506 for (i = 0; i < rmA->length; i++) {
507 mA = &(rmA->m[i]);
508
509 for (j = 0; j < rmB->length; j++) {
510 mB = &(rmB->m[j]);
511 input_mode = 0;
512
513 if (strcmp(mA->name, mB->name))
514 continue;
515
516 if (strcmp(mA->data, mB->data))
517 continue;
518
519 if (mB->dir != mA->dir)
520 continue;
521 else if (mB->dir == dir_in) {
522 input_mode = 1;
523 inputs_found++;
524 }
525
526 if (input_mode) {
527 log_info("Matched input lines: name=%s data=%s\n", mA->name, mA->data);
528 num_of_matched_inputs++;
529 }
530
531 /* Match found, move on */
532 log_info("Matched lines: name=%s data=%s", mA->name, mA->data);
533 matches++;
534 break;
535 }
536 }
537
538 /* If they all matched*/
539 if (matches == rmA->length) {
540 log_info("Rule map cmp MATCH\n");
541 return map_matched;
542 }
543
544 /* They didn't all match but the input's did */
545 else if (num_of_matched_inputs == inputs_found) {
546 log_info("Rule map cmp INPUT MATCH\n");
547 return map_input_matched;
548 }
549
550 /* They didn't all match, and the inputs didn't match, ie it didn't
551 * match */
552 else {
553 log_info("Rule map cmp NO MATCH\n");
554 return map_no_matches;
555 }
556 }
557
558 /**
559 * Frees a rule map
560 * @param rm
561 * rule map to be freed.
562 * @is_in_htable
563 * True if the rule map has been added to the hash table, false
564 * otherwise.
565 */
rule_map_free(rule_map * rm,bool is_in_htable)566 static void rule_map_free(rule_map *rm, bool is_in_htable) {
567
568 size_t i;
569 size_t len = rm->length;
570 for (i = 0; i < len; i++) {
571 key_map *m = &(rm->m[i]);
572 free(m->data);
573
574 if (m->regex.compiled) {
575 pcre_free(m->regex.compiled);
576 }
577
578 if (m->regex.extra) {
579 pcre_free_study(m->regex.extra);
580 }
581 }
582
583 /*
584 * hdestroy() frees comparsion keys for non glibc
585 * on GLIBC we always free on NON-GLIBC we free if
586 * it is not in the htable.
587 */
588 if (rm->key) {
589 #ifdef __GLIBC__
590 /* silence unused warning */
591 (void)is_in_htable;
592 free(rm->key);
593 #else
594 if (!is_in_htable) {
595 free(rm->key);
596 }
597 #endif
598 }
599
600 free(rm->filename);
601 free(rm);
602 }
603
free_kvp(kvp * k)604 static void free_kvp(kvp *k) {
605 free(k->key);
606 free(k->value);
607 }
608
609 /**
610 * Checks a rule_map for any variation of KVP's that shouldn't be allowed.
611 * It builds an assertion failure list for each rule map.
612 * Note that this function logs all errors.
613 *
614 * Current Checks:
615 * 1. That a specified name entry should have a specified seinfo entry as well.
616 * 2. That no rule violates a neverallow
617 * @param rm
618 * The rule map to check for validity.
619 */
rule_map_validate(rule_map * rm)620 static void rule_map_validate(rule_map *rm) {
621
622 size_t i, j;
623 const key_map *rule;
624 key_map *nrule;
625 hash_entry *e;
626 rule_map *assert;
627 list_element *cursor;
628
629 list_for_each(&nallow_list, cursor) {
630 e = list_entry(cursor, typeof(*e), listify);
631 assert = e->r;
632
633 size_t cnt = 0;
634
635 for (j = 0; j < assert->length; j++) {
636 nrule = &(assert->m[j]);
637
638 // mark that nrule->name is for a null check
639 bool is_null_check = !strcmp(nrule->data, "\"\"");
640
641 for (i = 0; i < rm->length; i++) {
642 rule = &(rm->m[i]);
643
644 if (!strcmp(rule->name, nrule->name)) {
645
646 /* the name was found, (data cannot be false) then it was specified */
647 is_null_check = false;
648
649 if (match_regex(nrule, rule)) {
650 cnt++;
651 }
652 }
653 }
654
655 /*
656 * the nrule was marked in a null check and we never found a match on nrule, thus
657 * it matched and we update the cnt
658 */
659 if (is_null_check) {
660 cnt++;
661 }
662 }
663 if (cnt == assert->length) {
664 list_append(&rm->violations, &assert->listify);
665 }
666 }
667 }
668
669 /**
670 * Given a set of key value pairs, this will construct a new rule map.
671 * On error this function calls exit.
672 * @param keys
673 * Keys from a rule line to map
674 * @param num_of_keys
675 * The length of the keys array
676 * @param lineno
677 * The line number the keys were extracted from
678 * @return
679 * A rule map pointer.
680 */
rule_map_new(kvp keys[],size_t num_of_keys,int lineno,const char * filename,bool is_never_allow)681 static rule_map *rule_map_new(kvp keys[], size_t num_of_keys, int lineno,
682 const char *filename, bool is_never_allow) {
683
684 size_t i = 0, j = 0;
685 rule_map *new_map = NULL;
686 kvp *k = NULL;
687 key_map *r = NULL, *x = NULL;
688 bool seen[KVP_NUM_OF_RULES];
689
690 for (i = 0; i < KVP_NUM_OF_RULES; i++)
691 seen[i] = false;
692
693 new_map = calloc(1, (num_of_keys * sizeof(key_map)) + sizeof(rule_map));
694 if (!new_map)
695 goto oom;
696
697 new_map->is_never_allow = is_never_allow;
698 new_map->length = num_of_keys;
699 new_map->lineno = lineno;
700 new_map->filename = strdup(filename);
701 if (!new_map->filename) {
702 goto oom;
703 }
704
705 /* For all the keys in a rule line*/
706 for (i = 0; i < num_of_keys; i++) {
707 k = &(keys[i]);
708 r = &(new_map->m[i]);
709
710 for (j = 0; j < KVP_NUM_OF_RULES; j++) {
711 x = &(rules[j]);
712
713 /* Only assign key name to map name */
714 if (strcasecmp(k->key, x->name)) {
715 if (i == KVP_NUM_OF_RULES) {
716 log_error("No match for key: %s\n", k->key);
717 goto err;
718 }
719 continue;
720 }
721
722 if (seen[j]) {
723 log_error("Duplicated key: %s\n", k->key);
724 goto err;
725 }
726 seen[j] = true;
727
728 memcpy(r, x, sizeof(key_map));
729
730 /* Assign rule map value to one from file */
731 r->data = strdup(k->value);
732 if (!r->data)
733 goto oom;
734
735 /* Enforce type check*/
736 log_info("Validating keys!\n");
737 if (!key_map_validate(r, filename, lineno, new_map->is_never_allow)) {
738 log_error("Could not validate\n");
739 goto err;
740 }
741
742 /*
743 * Only build key off of inputs with the exception of neverallows.
744 * Neverallows are keyed off of all key value pairs,
745 */
746 if (r->dir == dir_in || new_map->is_never_allow) {
747 char *tmp;
748 int key_len = strlen(k->key);
749 int val_len = strlen(k->value);
750 int l = (new_map->key) ? strlen(new_map->key) : 0;
751 l = l + key_len + val_len;
752 l += 1;
753
754 tmp = realloc(new_map->key, l);
755 if (!tmp)
756 goto oom;
757
758 if (!new_map->key)
759 memset(tmp, 0, l);
760
761 new_map->key = tmp;
762
763 strncat(new_map->key, k->key, key_len);
764 strncat(new_map->key, k->value, val_len);
765 }
766 break;
767 }
768 free_kvp(k);
769 }
770
771 if (new_map->key == NULL) {
772 log_error("Strange, no keys found, input file corrupt perhaps?\n");
773 goto err;
774 }
775
776 return new_map;
777
778 oom:
779 log_error("Out of memory!\n");
780 err:
781 if(new_map) {
782 rule_map_free(new_map, false);
783 for (; i < num_of_keys; i++) {
784 k = &(keys[i]);
785 free_kvp(k);
786 }
787 }
788 return NULL;
789 }
790
791 /**
792 * Print the usage of the program
793 */
usage()794 static void usage() {
795 printf(
796 "checkseapp [options] <input file>\n"
797 "Processes an seapp_contexts file specified by argument <input file> (default stdin) "
798 "and allows later declarations to override previous ones on a match.\n"
799 "Options:\n"
800 "-h - print this help message\n"
801 "-v - enable verbose debugging informations\n"
802 "-p policy file - specify policy file for strict checking of output selectors against the policy\n"
803 "-o output file - specify output file or - for stdout. No argument runs in silent mode and outputs nothing\n");
804 }
805
init()806 static void init() {
807
808 bool has_out_file;
809 list_element *cursor;
810 file_info *tmp;
811
812 /* input files if the list is empty, use stdin */
813 if (!input_file_list.head) {
814 log_info("Using stdin for input\n");
815 tmp = malloc(sizeof(*tmp));
816 if (!tmp) {
817 log_error("oom");
818 exit(EXIT_FAILURE);
819 }
820 tmp->name = "stdin";
821 tmp->file = stdin;
822 list_append(&input_file_list, &(tmp->listify));
823 }
824 else {
825 list_for_each(&input_file_list, cursor) {
826 tmp = list_entry(cursor, typeof(*tmp), listify);
827
828 log_info("Opening input file: \"%s\"\n", tmp->name);
829 tmp->file = fopen(tmp->name, "r");
830 if (!tmp->file) {
831 log_error("Could not open file: %s error: %s\n", tmp->name,
832 strerror(errno));
833 exit(EXIT_FAILURE);
834 }
835 }
836 }
837
838 has_out_file = out_file.name != NULL;
839
840 /* If output file is -, then use stdout, else open the path */
841 if (has_out_file && !strcmp(out_file.name, "-")) {
842 out_file.file = stdout;
843 out_file.name = "stdout";
844 }
845 else if (has_out_file) {
846 out_file.file = fopen(out_file.name, "w+");
847 }
848
849 if (has_out_file && !out_file.file) {
850 log_error("Could not open file: \"%s\" error: \"%s\"\n", out_file.name,
851 strerror(errno));
852 exit(EXIT_FAILURE);
853 }
854
855 if (pol.policy_file_name) {
856 log_info("Opening policy file: %s\n", pol.policy_file_name);
857 pol.policy_file = fopen(pol.policy_file_name, "rb");
858 if (!pol.policy_file) {
859 log_error("Could not open file: %s error: %s\n",
860 pol.policy_file_name, strerror(errno));
861 exit(EXIT_FAILURE);
862 }
863
864 pol.handle = sepol_handle_create();
865 if (!pol.handle) {
866 log_error("Could not create sepolicy handle: %s\n",
867 strerror(errno));
868 exit(EXIT_FAILURE);
869 }
870
871 if (sepol_policy_file_create(&pol.pf) < 0) {
872 log_error("Could not create sepolicy file: %s!\n",
873 strerror(errno));
874 exit(EXIT_FAILURE);
875 }
876
877 sepol_policy_file_set_fp(pol.pf, pol.policy_file);
878 sepol_policy_file_set_handle(pol.pf, pol.handle);
879
880 if (sepol_policydb_create(&pol.db) < 0) {
881 log_error("Could not create sepolicy db: %s!\n",
882 strerror(errno));
883 exit(EXIT_FAILURE);
884 }
885
886 if (sepol_policydb_read(pol.db, pol.pf) < 0) {
887 log_error("Could not lod policy file to db: %s!\n",
888 strerror(errno));
889 exit(EXIT_FAILURE);
890 }
891 }
892
893 list_for_each(&input_file_list, cursor) {
894 tmp = list_entry(cursor, typeof(*tmp), listify);
895 log_info("Input file set to: \"%s\"\n", tmp->name);
896 }
897
898 log_info("Policy file set to: \"%s\"\n",
899 (pol.policy_file_name == NULL) ? "None" : pol.policy_file_name);
900 log_info("Output file set to: \"%s\"\n", out_file.name);
901
902 #if !defined(LINK_SEPOL_STATIC)
903 log_warn("LINK_SEPOL_STATIC is not defined\n""Not checking types!");
904 #endif
905
906 }
907
908 /**
909 * Handle parsing and setting the global flags for the command line
910 * options. This function calls exit on failure.
911 * @param argc
912 * argument count
913 * @param argv
914 * argument list
915 */
handle_options(int argc,char * argv[])916 static void handle_options(int argc, char *argv[]) {
917
918 int c;
919 file_info *input_file;
920
921 while ((c = getopt(argc, argv, "ho:p:v")) != -1) {
922 switch (c) {
923 case 'h':
924 usage();
925 exit(EXIT_SUCCESS);
926 case 'o':
927 out_file.name = optarg;
928 break;
929 case 'p':
930 pol.policy_file_name = optarg;
931 break;
932 case 'v':
933 log_set_verbose();
934 break;
935 case '?':
936 if (optopt == 'o' || optopt == 'p')
937 log_error("Option -%c requires an argument.\n", optopt);
938 else if (isprint (optopt))
939 log_error("Unknown option `-%c'.\n", optopt);
940 else {
941 log_error(
942 "Unknown option character `\\x%x'.\n",
943 optopt);
944 }
945 default:
946 exit(EXIT_FAILURE);
947 }
948 }
949
950 for (c = optind; c < argc; c++) {
951
952 input_file = calloc(1, sizeof(*input_file));
953 if (!input_file) {
954 log_error("oom");
955 exit(EXIT_FAILURE);
956 }
957 input_file->name = argv[c];
958 list_append(&input_file_list, &input_file->listify);
959 }
960 }
961
962 /**
963 * Adds a rule to the hash table and to the ordered list if needed.
964 * @param rm
965 * The rule map to add.
966 */
rule_add(rule_map * rm)967 static void rule_add(rule_map *rm) {
968
969 map_match cmp;
970 ENTRY e;
971 ENTRY *f;
972 hash_entry *entry;
973 hash_entry *tmp;
974 list *list_to_addto;
975
976 e.key = rm->key;
977
978 log_info("Searching for key: %s\n", e.key);
979 /* Check to see if it has already been added*/
980 f = hsearch(e, FIND);
981
982 /*
983 * Since your only hashing on a partial key, the inputs we need to handle
984 * when you want to override the outputs for a given input set, as well as
985 * checking for duplicate entries.
986 */
987 if(f) {
988 log_info("Existing entry found!\n");
989 tmp = (hash_entry *)f->data;
990 cmp = rule_map_cmp(rm, tmp->r);
991 log_error("Duplicate line detected in file: %s\n"
992 "Lines %d and %d %s!\n",
993 rm->filename, tmp->r->lineno, rm->lineno,
994 map_match_str[cmp]);
995 rule_map_free(rm, false);
996 goto err;
997 }
998 /* It wasn't found, just add the rule map to the table */
999 else {
1000
1001 entry = malloc(sizeof(hash_entry));
1002 if (!entry)
1003 goto oom;
1004
1005 entry->r = rm;
1006 e.data = entry;
1007
1008 f = hsearch(e, ENTER);
1009 if(f == NULL) {
1010 goto oom;
1011 }
1012
1013 /* new entries must be added to the ordered list */
1014 entry->r = rm;
1015 list_to_addto = rm->is_never_allow ? &nallow_list : &line_order_list;
1016 list_append(list_to_addto, &entry->listify);
1017 }
1018
1019 return;
1020 oom:
1021 if (e.key)
1022 free(e.key);
1023 if (entry)
1024 free(entry);
1025 if (rm)
1026 free(rm);
1027 log_error("Out of memory in function: %s\n", __FUNCTION__);
1028 err:
1029 exit(EXIT_FAILURE);
1030 }
1031
parse_file(file_info * in_file)1032 static void parse_file(file_info *in_file) {
1033
1034 char *p;
1035 size_t len;
1036 char *token;
1037 char *saveptr;
1038 bool is_never_allow;
1039 bool found_whitespace;
1040
1041 size_t lineno = 0;
1042 char *name = NULL;
1043 char *value = NULL;
1044 size_t token_cnt = 0;
1045
1046 char line_buf[BUFSIZ];
1047 kvp keys[KVP_NUM_OF_RULES];
1048
1049 while (fgets(line_buf, sizeof(line_buf) - 1, in_file->file)) {
1050 lineno++;
1051 is_never_allow = false;
1052 found_whitespace = false;
1053 log_info("Got line %zu\n", lineno);
1054 len = strlen(line_buf);
1055 if (line_buf[len - 1] == '\n')
1056 line_buf[len - 1] = '\0';
1057 p = line_buf;
1058
1059 /* neverallow lines must start with neverallow (ie ^neverallow) */
1060 if (!strncasecmp(p, "neverallow", strlen("neverallow"))) {
1061 p += strlen("neverallow");
1062 is_never_allow = true;
1063 }
1064
1065 /* strip trailing whitespace skip comments */
1066 while (isspace(*p)) {
1067 p++;
1068 found_whitespace = true;
1069 }
1070 if (*p == '#' || *p == '\0')
1071 continue;
1072
1073 token = strtok_r(p, " \t", &saveptr);
1074 if (!token)
1075 goto err;
1076
1077 token_cnt = 0;
1078 memset(keys, 0, sizeof(kvp) * KVP_NUM_OF_RULES);
1079 while (1) {
1080
1081 name = token;
1082 value = strchr(name, '=');
1083 if (!value)
1084 goto err;
1085 *value++ = 0;
1086
1087 keys[token_cnt].key = strdup(name);
1088 if (!keys[token_cnt].key)
1089 goto oom;
1090
1091 keys[token_cnt].value = strdup(value);
1092 if (!keys[token_cnt].value)
1093 goto oom;
1094
1095 token_cnt++;
1096
1097 token = strtok_r(NULL, " \t", &saveptr);
1098 if (!token)
1099 break;
1100
1101 } /*End token parsing */
1102
1103 rule_map *r = rule_map_new(keys, token_cnt, lineno, in_file->name, is_never_allow);
1104 if (!r)
1105 goto err;
1106 rule_add(r);
1107
1108 } /* End file parsing */
1109 return;
1110
1111 err:
1112 log_error("Reading file: \"%s\" line: %zu name: \"%s\" value: \"%s\"\n",
1113 in_file->name, lineno, name, value);
1114 if(found_whitespace && name && !strcasecmp(name, "neverallow")) {
1115 log_error("perhaps whitespace before neverallow\n");
1116 }
1117 exit(EXIT_FAILURE);
1118 oom:
1119 log_error("In function %s: Out of memory\n", __FUNCTION__);
1120 exit(EXIT_FAILURE);
1121 }
1122
1123 /**
1124 * Parses the seapp_contexts file and neverallow file
1125 * and adds them to the hash table and ordered list entries
1126 * when it encounters them.
1127 * Calls exit on failure.
1128 */
parse()1129 static void parse() {
1130
1131 file_info *current;
1132 list_element *cursor;
1133 list_for_each(&input_file_list, cursor) {
1134 current = list_entry(cursor, typeof(*current), listify);
1135 parse_file(current);
1136 }
1137 }
1138
validate()1139 static void validate() {
1140
1141 list_element *cursor, *v;
1142 bool found_issues = false;
1143 hash_entry *e;
1144 rule_map *r;
1145 list_for_each(&line_order_list, cursor) {
1146 e = list_entry(cursor, typeof(*e), listify);
1147 rule_map_validate(e->r);
1148 }
1149
1150 list_for_each(&line_order_list, cursor) {
1151 e = list_entry(cursor, typeof(*e), listify);
1152 r = e->r;
1153 list_for_each(&r->violations, v) {
1154 found_issues = true;
1155 log_error("Rule in File \"%s\" on line %d: \"", e->r->filename, e->r->lineno);
1156 rule_map_print(stderr, e->r);
1157 r = list_entry(v, rule_map, listify);
1158 fprintf(stderr, "\" violates neverallow in File \"%s\" on line %d: \"", r->filename, r->lineno);
1159 rule_map_print(stderr, r);
1160 fprintf(stderr, "\"\n");
1161 }
1162 }
1163
1164 if (found_issues) {
1165 exit(EXIT_FAILURE);
1166 }
1167 }
1168
1169 /**
1170 * Should be called after parsing to cause the printing of the rule_maps
1171 * stored in the ordered list, head first, which preserves the "first encountered"
1172 * ordering.
1173 */
output()1174 static void output() {
1175
1176 hash_entry *e;
1177 list_element *cursor;
1178
1179 if (!out_file.file) {
1180 log_info("No output file, not outputting.\n");
1181 return;
1182 }
1183
1184 list_for_each(&line_order_list, cursor) {
1185 e = list_entry(cursor, hash_entry, listify);
1186 rule_map_print(out_file.file, e->r);
1187 fprintf(out_file.file, "\n");
1188 }
1189 }
1190
1191 /**
1192 * This function is registered to the at exit handler and should clean up
1193 * the programs dynamic resources, such as memory and fd's.
1194 */
cleanup()1195 static void cleanup() {
1196
1197 /* Only close this when it was opened by me and not the crt */
1198 if (out_file.name && strcmp(out_file.name, "stdout") && out_file.file) {
1199 log_info("Closing file: %s\n", out_file.name);
1200 fclose(out_file.file);
1201 }
1202
1203 if (pol.policy_file) {
1204
1205 log_info("Closing file: %s\n", pol.policy_file_name);
1206 fclose(pol.policy_file);
1207
1208 if (pol.db)
1209 sepol_policydb_free(pol.db);
1210
1211 if (pol.pf)
1212 sepol_policy_file_free(pol.pf);
1213
1214 if (pol.handle)
1215 sepol_handle_destroy(pol.handle);
1216 }
1217
1218 log_info("Freeing lists\n");
1219 list_free(&input_file_list);
1220 list_free(&line_order_list);
1221 list_free(&nallow_list);
1222 hdestroy();
1223 }
1224
main(int argc,char * argv[])1225 int main(int argc, char *argv[]) {
1226 if (!hcreate(TABLE_SIZE)) {
1227 log_error("Could not create hash table: %s\n", strerror(errno));
1228 exit(EXIT_FAILURE);
1229 }
1230 atexit(cleanup);
1231 handle_options(argc, argv);
1232 init();
1233 log_info("Starting to parse\n");
1234 parse();
1235 log_info("Parsing completed, generating output\n");
1236 validate();
1237 output();
1238 log_info("Success, generated output\n");
1239 exit(EXIT_SUCCESS);
1240 }
1241