1 /* Authors: Karl MacMillan <kmacmillan (at) tresys.com> 2 * Joshua Brindle <jbrindle (at) tresys.com> 3 * Jason Tang <jtang (at) tresys.com> 4 * 5 * Copyright (C) 2004-2005 Tresys Technology, LLC 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation, version 2. 9 */ 10 11 #include <fcntl.h> 12 #include <getopt.h> 13 #include <signal.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <errno.h> 17 #include <string.h> 18 #include <unistd.h> 19 #include <sys/mman.h> 20 #include <sys/stat.h> 21 #include <sys/types.h> 22 #include <libgen.h> 23 #include <limits.h> 24 25 #include <semanage/modules.h> 26 27 enum client_modes { 28 NO_MODE, INSTALL_M, REMOVE_M, EXTRACT_M, CIL_M, HLL_M, 29 LIST_M, RELOAD, PRIORITY_M, ENABLE_M, DISABLE_M 30 }; 31 /* list of modes in which one ought to commit afterwards */ 32 static const int do_commit[] = { 33 0, 1, 1, 0, 0, 0, 34 0, 0, 0, 1, 1, 35 }; 36 37 struct command { 38 enum client_modes mode; 39 char *arg; 40 }; 41 static struct command *commands = NULL; 42 static int num_commands = 0; 43 44 /* options given on command line */ 45 static int verbose; 46 static int reload; 47 static int no_reload; 48 static int build; 49 static int disable_dontaudit; 50 static int preserve_tunables; 51 static int ignore_module_cache; 52 static uint16_t priority; 53 static int priority_set = 0; 54 55 static semanage_handle_t *sh = NULL; 56 static char *store; 57 static char *store_root; 58 int extract_cil = 0; 59 60 extern char *optarg; 61 extern int optind; 62 63 static void cleanup(void) 64 { 65 while (--num_commands >= 0) { 66 free(commands[num_commands].arg); 67 } 68 free(commands); 69 } 70 71 /* Signal handlers. */ 72 static void handle_signal(int sig_num) 73 { 74 if (sig_num == SIGINT || sig_num == SIGQUIT || sig_num == SIGTERM) { 75 /* catch these signals, and then drop them */ 76 } 77 } 78 79 static void set_store(char *storename) 80 { 81 /* For now this only supports a store name, later on this 82 * should support an address for a remote connection */ 83 84 if ((store = strdup(storename)) == NULL) { 85 fprintf(stderr, "Out of memory!\n"); 86 goto bad; 87 } 88 89 return; 90 91 bad: 92 cleanup(); 93 exit(1); 94 } 95 96 static void set_store_root(char *path) 97 { 98 if ((store_root = strdup(path)) == NULL) { 99 fprintf(stderr, "Out of memory!\n"); 100 goto bad; 101 } 102 103 return; 104 105 bad: 106 cleanup(); 107 exit(1); 108 } 109 110 /* Establish signal handlers for the process. */ 111 static void create_signal_handlers(void) 112 { 113 if (signal(SIGINT, handle_signal) == SIG_ERR || 114 signal(SIGQUIT, handle_signal) == SIG_ERR || 115 signal(SIGTERM, handle_signal) == SIG_ERR) { 116 fprintf(stderr, "Could not set up signal handler.\n"); 117 exit(255); 118 } 119 } 120 121 static void usage(char *progname) 122 { 123 printf("usage: %s [option]... MODE...\n", progname); 124 printf("Manage SELinux policy modules.\n"); 125 printf("MODES:\n"); 126 printf(" -R, --reload reload policy\n"); 127 printf(" -B, --build build and reload policy\n"); 128 printf(" -D,--disable_dontaudit Remove dontaudits from policy\n"); 129 printf(" -i,--install=MODULE_PKG install a new module\n"); 130 printf(" -r,--remove=MODULE_NAME remove existing module at desired priority\n"); 131 printf(" -l[KIND],--list-modules[=KIND] display list of installed modules\n"); 132 printf(" KIND: standard list highest priority, enabled modules\n"); 133 printf(" full list all modules\n"); 134 printf(" -X,--priority=PRIORITY set priority for following operations (1-999)\n"); 135 printf(" -e,--enable=MODULE_NAME enable module\n"); 136 printf(" -d,--disable=MODULE_NAME disable module\n"); 137 printf(" -E,--extract=MODULE_NAME extract module\n"); 138 printf("Options:\n"); 139 printf(" -s,--store name of the store to operate on\n"); 140 printf(" -N,-n,--noreload do not reload policy after commit\n"); 141 printf(" -h,--help print this message and quit\n"); 142 printf(" -v,--verbose be verbose\n"); 143 printf(" -P,--preserve_tunables Preserve tunables in policy\n"); 144 printf(" -C,--ignore-module-cache Rebuild CIL modules compiled from HLL files\n"); 145 printf(" -p,--path use an alternate path for the policy root\n"); 146 printf(" -S,--store-path use an alternate path for the policy store root\n"); 147 printf(" -c, --cil extract module as cil. This only affects module extraction.\n"); 148 printf(" -H, --hll extract module as hll. This only affects module extraction.\n"); 149 } 150 151 /* Sets the global mode variable to new_mode, but only if no other 152 * mode has been given. */ 153 static void set_mode(enum client_modes new_mode, char *arg) 154 { 155 struct command *c; 156 char *s; 157 if ((c = realloc(commands, sizeof(*c) * (num_commands + 1))) == NULL) { 158 fprintf(stderr, "Out of memory!\n"); 159 cleanup(); 160 exit(1); 161 } 162 commands = c; 163 commands[num_commands].mode = new_mode; 164 commands[num_commands].arg = NULL; 165 num_commands++; 166 if (arg != NULL) { 167 if ((s = strdup(arg)) == NULL) { 168 fprintf(stderr, "Out of memory!\n"); 169 cleanup(); 170 exit(1); 171 } 172 commands[num_commands - 1].arg = s; 173 } 174 } 175 176 /* Parse command line and set global options. */ 177 static void parse_command_line(int argc, char **argv) 178 { 179 static struct option opts[] = { 180 {"store", required_argument, NULL, 's'}, 181 {"base", required_argument, NULL, 'b'}, 182 {"help", 0, NULL, 'h'}, 183 {"install", required_argument, NULL, 'i'}, 184 {"extract", required_argument, NULL, 'E'}, 185 {"cil", 0, NULL, 'c'}, 186 {"hll", 0, NULL, 'H'}, 187 {"list-modules", optional_argument, NULL, 'l'}, 188 {"verbose", 0, NULL, 'v'}, 189 {"remove", required_argument, NULL, 'r'}, 190 {"upgrade", required_argument, NULL, 'u'}, 191 {"reload", 0, NULL, 'R'}, 192 {"noreload", 0, NULL, 'n'}, 193 {"build", 0, NULL, 'B'}, 194 {"disable_dontaudit", 0, NULL, 'D'}, 195 {"preserve_tunables", 0, NULL, 'P'}, 196 {"ignore-module-cache", 0, NULL, 'C'}, 197 {"priority", required_argument, NULL, 'X'}, 198 {"enable", required_argument, NULL, 'e'}, 199 {"disable", required_argument, NULL, 'd'}, 200 {"path", required_argument, NULL, 'p'}, 201 {"store-path", required_argument, NULL, 'S'}, 202 {NULL, 0, NULL, 0} 203 }; 204 int extract_selected = 0; 205 int cil_hll_set = 0; 206 int i; 207 verbose = 0; 208 reload = 0; 209 no_reload = 0; 210 priority = 400; 211 while ((i = 212 getopt_long(argc, argv, "s:b:hi:l::vr:u:RnNBDCPX:e:d:p:S:E:cH", opts, 213 NULL)) != -1) { 214 switch (i) { 215 case 'b': 216 fprintf(stderr, "The --base option is deprecated. Use --install instead.\n"); 217 set_mode(INSTALL_M, optarg); 218 break; 219 case 'h': 220 usage(argv[0]); 221 exit(0); 222 case 'i': 223 set_mode(INSTALL_M, optarg); 224 break; 225 case 'E': 226 set_mode(EXTRACT_M, optarg); 227 extract_selected = 1; 228 break; 229 case 'c': 230 set_mode(CIL_M, NULL); 231 cil_hll_set = 1; 232 break; 233 case 'H': 234 set_mode(HLL_M, NULL); 235 cil_hll_set = 1; 236 break; 237 case 'l': 238 set_mode(LIST_M, optarg); 239 break; 240 case 'v': 241 verbose = 1; 242 break; 243 case 'r': 244 set_mode(REMOVE_M, optarg); 245 break; 246 case 'u': 247 fprintf(stderr, "The --upgrade option is deprecated. Use --install instead.\n"); 248 set_mode(INSTALL_M, optarg); 249 break; 250 case 's': 251 set_store(optarg); 252 break; 253 case 'p': 254 semanage_set_root(optarg); 255 break; 256 case 'S': 257 set_store_root(optarg); 258 break; 259 case 'R': 260 reload = 1; 261 break; 262 case 'n': 263 no_reload = 1; 264 break; 265 case 'N': 266 no_reload = 1; 267 break; 268 case 'B': 269 build = 1; 270 break; 271 case 'D': 272 disable_dontaudit = 1; 273 break; 274 case 'P': 275 preserve_tunables = 1; 276 break; 277 case 'C': 278 ignore_module_cache = 1; 279 break; 280 case 'X': 281 set_mode(PRIORITY_M, optarg); 282 break; 283 case 'e': 284 set_mode(ENABLE_M, optarg); 285 break; 286 case 'd': 287 set_mode(DISABLE_M, optarg); 288 break; 289 case '?': 290 default:{ 291 usage(argv[0]); 292 exit(1); 293 } 294 } 295 } 296 if ((build || reload) && num_commands) { 297 fprintf(stderr, 298 "build or reload should not be used with other commands\n"); 299 usage(argv[0]); 300 exit(1); 301 } 302 if (num_commands == 0 && reload == 0 && build == 0) { 303 fprintf(stderr, "At least one mode must be specified.\n"); 304 usage(argv[0]); 305 exit(1); 306 } 307 if (extract_selected == 0 && cil_hll_set == 1) { 308 fprintf(stderr, "--cil and --hll require a module to export with the --extract option.\n"); 309 usage(argv[0]); 310 exit(1); 311 } 312 313 if (optind < argc) { 314 int mode = commands ? (int) commands[num_commands - 1].mode : -1; 315 /* if -i/u/r/E was the last command treat any remaining 316 * arguments as args. Will allow 'semodule -i *.pp' to 317 * work as expected. 318 */ 319 320 switch (mode) { 321 case INSTALL_M: 322 case REMOVE_M: 323 case EXTRACT_M: 324 case ENABLE_M: 325 case DISABLE_M: 326 while (optind < argc) 327 set_mode(mode, argv[optind++]); 328 break; 329 default: 330 fprintf(stderr, "unknown additional arguments:\n"); 331 while (optind < argc) 332 fprintf(stderr, " %s", argv[optind++]); 333 fprintf(stderr, "\n\n"); 334 usage(argv[0]); 335 exit(1); 336 } 337 } 338 } 339 340 int main(int argc, char *argv[]) 341 { 342 int i, commit = 0; 343 int result; 344 int status = EXIT_FAILURE; 345 const char *genhomedirconargv[] = { "genhomedircon", "-B", "-n" }; 346 create_signal_handlers(); 347 if (strcmp(basename(argv[0]), "genhomedircon") == 0) { 348 argc = 3; 349 argv = (char **)genhomedirconargv; 350 } 351 parse_command_line(argc, argv); 352 353 if (build) 354 commit = 1; 355 356 sh = semanage_handle_create(); 357 if (!sh) { 358 fprintf(stderr, "%s: Could not create semanage handle\n", 359 argv[0]); 360 goto cleanup_nohandle; 361 } 362 363 if (store) { 364 /* Set the store we want to connect to, before connecting. 365 * this will always set a direct connection now, an additional 366 * option will need to be used later to specify a policy server 367 * location */ 368 semanage_select_store(sh, store, SEMANAGE_CON_DIRECT); 369 } 370 371 if (store_root) { 372 semanage_set_store_root(sh, store_root); 373 } 374 375 /* create store if necessary, for bootstrapping */ 376 semanage_set_create_store(sh, 1); 377 378 if ((result = semanage_connect(sh)) < 0) { 379 fprintf(stderr, "%s: Could not connect to policy handler\n", 380 argv[0]); 381 goto cleanup; 382 } 383 384 if (reload) { 385 if ((result = semanage_reload_policy(sh)) < 0) { 386 fprintf(stderr, "%s: Could not reload policy\n", 387 argv[0]); 388 goto cleanup; 389 } 390 } 391 392 if (build) { 393 if ((result = semanage_begin_transaction(sh)) < 0) { 394 fprintf(stderr, "%s: Could not begin transaction: %s\n", 395 argv[0], errno ? strerror(errno) : ""); 396 goto cleanup; 397 } 398 } 399 400 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 401 fprintf(stderr, 402 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 403 argv[0], 404 priority); 405 goto cleanup; 406 } 407 408 for (i = 0; i < num_commands; i++) { 409 enum client_modes mode = commands[i].mode; 410 char *mode_arg = commands[i].arg; 411 412 switch (mode) { 413 case INSTALL_M:{ 414 if (verbose) { 415 printf 416 ("Attempting to install module '%s':\n", 417 mode_arg); 418 } 419 result = 420 semanage_module_install_file(sh, mode_arg); 421 break; 422 } 423 case EXTRACT_M:{ 424 semanage_module_info_t *extract_info = NULL; 425 semanage_module_key_t *modkey = NULL; 426 uint16_t curr_priority; 427 void *data = NULL; 428 size_t data_len = 0; 429 char output_path[PATH_MAX]; 430 const char *output_name = NULL; 431 const char *lang_ext = NULL; 432 int rlen; 433 FILE *output_fd = NULL; 434 435 result = semanage_module_key_create(sh, &modkey); 436 if (result != 0) { 437 goto cleanup_extract; 438 } 439 440 result = semanage_module_key_set_name(sh, modkey, mode_arg); 441 if (result != 0) { 442 goto cleanup_extract; 443 } 444 445 if (priority_set == 0) { 446 result = semanage_module_get_module_info(sh, modkey, &extract_info); 447 if (result != 0) { 448 goto cleanup_extract; 449 } 450 451 semanage_module_info_get_priority(sh, extract_info, &curr_priority); 452 printf("Module '%s' does not exist at the default priority '%d'. " 453 "Extracting at highest existing priority '%d'.\n", mode_arg, priority, curr_priority); 454 priority = curr_priority; 455 } 456 457 result = semanage_module_key_set_priority(sh, modkey, priority); 458 if (result != 0) { 459 goto cleanup_extract; 460 } 461 462 if (verbose) { 463 printf 464 ("Attempting to extract module '%s':\n", 465 mode_arg); 466 } 467 result = semanage_module_extract(sh, modkey, extract_cil, &data, &data_len, &extract_info); 468 if (result != 0) { 469 goto cleanup_extract; 470 } 471 472 if (extract_cil) { 473 lang_ext = "cil"; 474 } else { 475 result = semanage_module_info_get_lang_ext(sh, extract_info, &lang_ext); 476 if (result != 0) { 477 goto cleanup_extract; 478 } 479 } 480 481 result = semanage_module_info_get_name(sh, extract_info, &output_name); 482 if (result != 0) { 483 goto cleanup_extract; 484 } 485 486 rlen = snprintf(output_path, PATH_MAX, "%s.%s", output_name, lang_ext); 487 if (rlen < 0 || rlen >= PATH_MAX) { 488 fprintf(stderr, "%s: Failed to generate output path.\n", argv[0]); 489 result = -1; 490 goto cleanup_extract; 491 } 492 493 if (access(output_path, F_OK) == 0) { 494 fprintf(stderr, "%s: %s is already extracted with extension %s.\n", argv[0], mode_arg, lang_ext); 495 result = -1; 496 goto cleanup_extract; 497 } 498 499 output_fd = fopen(output_path, "w"); 500 if (output_fd == NULL) { 501 fprintf(stderr, "%s: Unable to open %s\n", argv[0], output_path); 502 result = -1; 503 goto cleanup_extract; 504 } 505 506 if (fwrite(data, 1, data_len, output_fd) < data_len) { 507 fprintf(stderr, "%s: Unable to write to %s\n", argv[0], output_path); 508 result = -1; 509 goto cleanup_extract; 510 } 511 cleanup_extract: 512 if (output_fd != NULL) { 513 fclose(output_fd); 514 } 515 if (data_len > 0) { 516 munmap(data, data_len); 517 } 518 semanage_module_info_destroy(sh, extract_info); 519 free(extract_info); 520 semanage_module_key_destroy(sh, modkey); 521 free(modkey); 522 break; 523 } 524 case CIL_M: 525 extract_cil = 1; 526 break; 527 case HLL_M: 528 extract_cil = 0; 529 break; 530 case REMOVE_M:{ 531 if (verbose) { 532 printf 533 ("Attempting to remove module '%s':\n", 534 mode_arg); 535 } 536 result = semanage_module_remove(sh, mode_arg); 537 if ( result == -2 ) { 538 continue; 539 } 540 break; 541 } 542 case LIST_M:{ 543 semanage_module_info_t *modinfos = NULL; 544 int modinfos_len = 0; 545 semanage_module_info_t *m = NULL; 546 int j = 0; 547 548 if (verbose) { 549 printf 550 ("Attempting to list active modules:\n"); 551 } 552 553 if (mode_arg == NULL || strcmp(mode_arg, "standard") == 0) { 554 result = semanage_module_list(sh, 555 &modinfos, 556 &modinfos_len); 557 if (result < 0) goto cleanup_list; 558 559 if (modinfos_len == 0) { 560 printf("No modules.\n"); 561 } 562 563 const char *name = NULL; 564 565 for (j = 0; j < modinfos_len; j++) { 566 m = semanage_module_list_nth(modinfos, j); 567 568 result = semanage_module_info_get_name(sh, m, &name); 569 if (result != 0) goto cleanup_list; 570 571 printf("%s\n", name); 572 } 573 } 574 else if (strcmp(mode_arg, "full") == 0) { 575 /* get the modules */ 576 result = semanage_module_list_all(sh, 577 &modinfos, 578 &modinfos_len); 579 if (result != 0) goto cleanup_list; 580 581 if (modinfos_len == 0) { 582 printf("No modules.\n"); 583 } 584 585 /* calculate column widths */ 586 size_t column[4] = { 0, 0, 0, 0 }; 587 588 /* fixed width columns */ 589 column[0] = sizeof("000") - 1; 590 column[3] = sizeof("disabled") - 1; 591 592 /* variable width columns */ 593 const char *tmp = NULL; 594 size_t size; 595 for (j = 0; j < modinfos_len; j++) { 596 m = semanage_module_list_nth(modinfos, j); 597 598 result = semanage_module_info_get_name(sh, m, &tmp); 599 if (result != 0) goto cleanup_list; 600 601 size = strlen(tmp); 602 if (size > column[1]) column[1] = size; 603 604 result = semanage_module_info_get_lang_ext(sh, m, &tmp); 605 if (result != 0) goto cleanup_list; 606 607 size = strlen(tmp); 608 if (size > column[3]) column[3] = size; 609 } 610 611 /* print out each module */ 612 for (j = 0; j < modinfos_len; j++) { 613 uint16_t pri = 0; 614 const char *name = NULL; 615 int enabled = 0; 616 const char *lang_ext = NULL; 617 618 m = semanage_module_list_nth(modinfos, j); 619 620 result = semanage_module_info_get_priority(sh, m, &pri); 621 if (result != 0) goto cleanup_list; 622 623 result = semanage_module_info_get_name(sh, m, &name); 624 if (result != 0) goto cleanup_list; 625 626 result = semanage_module_info_get_enabled(sh, m, &enabled); 627 if (result != 0) goto cleanup_list; 628 629 result = semanage_module_info_get_lang_ext(sh, m, &lang_ext); 630 if (result != 0) goto cleanup_list; 631 632 printf("%0*u %-*s %-*s %-*s\n", 633 (int)column[0], pri, 634 (int)column[1], name, 635 (int)column[2], lang_ext, 636 (int)column[3], enabled ? "" : "disabled"); 637 } 638 } 639 else { 640 result = -1; 641 } 642 643 cleanup_list: 644 for (j = 0; j < modinfos_len; j++) { 645 m = semanage_module_list_nth(modinfos, j); 646 semanage_module_info_destroy(sh, m); 647 } 648 649 free(modinfos); 650 651 break; 652 } 653 case PRIORITY_M:{ 654 char *endptr = NULL; 655 priority = (uint16_t)strtoul(mode_arg, &endptr, 10); 656 priority_set = 1; 657 658 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 659 fprintf(stderr, 660 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 661 argv[0], 662 priority); 663 goto cleanup; 664 } 665 666 break; 667 } 668 case ENABLE_M:{ 669 if (verbose) { 670 printf 671 ("Attempting to enable module '%s':\n", 672 mode_arg); 673 } 674 675 semanage_module_key_t *modkey = NULL; 676 677 result = semanage_module_key_create(sh, &modkey); 678 if (result != 0) goto cleanup_enable; 679 680 result = semanage_module_key_set_name(sh, modkey, mode_arg); 681 if (result != 0) goto cleanup_enable; 682 683 result = semanage_module_set_enabled(sh, modkey, 1); 684 if (result != 0) goto cleanup_enable; 685 686 cleanup_enable: 687 semanage_module_key_destroy(sh, modkey); 688 free(modkey); 689 690 break; 691 } 692 case DISABLE_M:{ 693 if (verbose) { 694 printf 695 ("Attempting to disable module '%s':\n", 696 mode_arg); 697 } 698 699 semanage_module_key_t *modkey = NULL; 700 701 result = semanage_module_key_create(sh, &modkey); 702 if (result != 0) goto cleanup_disable; 703 704 result = semanage_module_key_set_name(sh, modkey, mode_arg); 705 if (result != 0) goto cleanup_disable; 706 707 result = semanage_module_set_enabled(sh, modkey, 0); 708 if (result != 0) goto cleanup_disable; 709 710 cleanup_disable: 711 semanage_module_key_destroy(sh, modkey); 712 free(modkey); 713 714 break; 715 } 716 default:{ 717 fprintf(stderr, 718 "%s: Unknown mode specified.\n", 719 argv[0]); 720 usage(argv[0]); 721 goto cleanup; 722 } 723 } 724 commit += do_commit[mode]; 725 if (result < 0) { 726 fprintf(stderr, "%s: Failed on %s!\n", argv[0], 727 mode_arg ? : "list"); 728 goto cleanup; 729 } else if (verbose) { 730 printf("Ok: return value of %d.\n", result); 731 } 732 } 733 734 if (commit) { 735 if (verbose) 736 printf("Committing changes:\n"); 737 if (no_reload) 738 semanage_set_reload(sh, 0); 739 if (build) 740 semanage_set_rebuild(sh, 1); 741 if (disable_dontaudit) 742 semanage_set_disable_dontaudit(sh, 1); 743 else if (build) 744 semanage_set_disable_dontaudit(sh, 0); 745 if (preserve_tunables) 746 semanage_set_preserve_tunables(sh, 1); 747 if (ignore_module_cache) 748 semanage_set_ignore_module_cache(sh, 1); 749 750 result = semanage_commit(sh); 751 } 752 753 if (result < 0) { 754 fprintf(stderr, "%s: Failed!\n", argv[0]); 755 goto cleanup; 756 } else if (commit && verbose) { 757 printf("Ok: transaction number %d.\n", result); 758 } 759 760 if (semanage_disconnect(sh) < 0) { 761 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 762 goto cleanup; 763 } 764 status = EXIT_SUCCESS; 765 766 cleanup: 767 if (semanage_is_connected(sh)) { 768 if (semanage_disconnect(sh) < 0) { 769 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 770 } 771 } 772 semanage_handle_destroy(sh); 773 774 cleanup_nohandle: 775 cleanup(); 776 exit(status); 777 } 778