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