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