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))) 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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