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