1/* Authors: Karl MacMillan <kmacmillan@tresys.com> 2 * Joshua Brindle <jbrindle@tresys.com> 3 * Jason Tang <jtang@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 27enum 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 */ 32static const int do_commit[] = { 33 0, 1, 1, 0, 0, 0, 34 0, 0, 0, 1, 1, 35}; 36 37struct command { 38 enum client_modes mode; 39 char *arg; 40}; 41static struct command *commands = NULL; 42static int num_commands = 0; 43 44/* options given on command line */ 45static int verbose; 46static int reload; 47static int no_reload; 48static int build; 49static int disable_dontaudit; 50static int preserve_tunables; 51static int ignore_module_cache; 52static uint16_t priority; 53static int priority_set = 0; 54 55static semanage_handle_t *sh = NULL; 56static char *store; 57static char *store_root; 58int extract_cil = 0; 59 60extern char *optarg; 61extern int optind; 62 63static void cleanup(void) 64{ 65 while (--num_commands >= 0) { 66 free(commands[num_commands].arg); 67 } 68 free(commands); 69} 70 71/* Signal handlers. */ 72static 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 79static 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 96static 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. */ 111static 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 121static void usage(char *progname) 122{ 123 printf("usage: %s [options]... MODE [MODES]...\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(" -i,--install=MODULE_PKG install a new module\n"); 129 printf(" -r,--remove=MODULE_NAME remove existing module\n"); 130 printf(" -l,--list-modules=[KIND] display list of installed modules\n"); 131 printf(" KIND: standard list highest priority, enabled modules\n"); 132 printf(" full list all modules\n"); 133 printf(" -X,--priority=PRIORITY set priority for following operations (1-999)\n"); 134 printf(" -e,--enable=MODULE_NAME enable module\n"); 135 printf(" -d,--disable=MODULE_NAME disable module\n"); 136 printf(" -E,--extract=MODULE_NAME extract module\n"); 137 printf("Other options:\n"); 138 printf(" -s,--store name of the store to operate on\n"); 139 printf(" -N,-n,--noreload do not reload policy after commit\n"); 140 printf(" -h,--help print this message and quit\n"); 141 printf(" -v,--verbose be verbose\n"); 142 printf(" -D,--disable_dontaudit Remove dontaudits from policy\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. */ 153static 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. */ 177static 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::vqr: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; 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 if (commands && commands[num_commands - 1].mode == INSTALL_M) { 321 mode = INSTALL_M; 322 } else if (commands && commands[num_commands - 1].mode == REMOVE_M) { 323 mode = REMOVE_M; 324 } else if (commands && commands[num_commands - 1].mode == EXTRACT_M) { 325 mode = EXTRACT_M; 326 } else { 327 fprintf(stderr, "unknown additional arguments:\n"); 328 while (optind < argc) 329 fprintf(stderr, " %s", argv[optind++]); 330 fprintf(stderr, "\n\n"); 331 usage(argv[0]); 332 exit(1); 333 } 334 while (optind < argc) 335 set_mode(mode, argv[optind++]); 336 } 337} 338 339int main(int argc, char *argv[]) 340{ 341 int i, commit = 0; 342 int result; 343 int status = EXIT_FAILURE; 344 char *genhomedirconargv[] = { "genhomedircon", "-B", "-n" }; 345 create_signal_handlers(); 346 if (strcmp(basename(argv[0]), "genhomedircon") == 0) { 347 argc = 3; 348 argv=genhomedirconargv; 349 } 350 parse_command_line(argc, argv); 351 352 if (build) 353 commit = 1; 354 355 sh = semanage_handle_create(); 356 if (!sh) { 357 fprintf(stderr, "%s: Could not create semanage handle\n", 358 argv[0]); 359 goto cleanup_nohandle; 360 } 361 362 if (store) { 363 /* Set the store we want to connect to, before connecting. 364 * this will always set a direct connection now, an additional 365 * option will need to be used later to specify a policy server 366 * location */ 367 semanage_select_store(sh, store, SEMANAGE_CON_DIRECT); 368 } 369 370 if (store_root) { 371 semanage_set_store_root(sh, store_root); 372 } 373 374 /* create store if necessary, for bootstrapping */ 375 semanage_set_create_store(sh, 1); 376 377 if ((result = semanage_connect(sh)) < 0) { 378 fprintf(stderr, "%s: Could not connect to policy handler\n", 379 argv[0]); 380 goto cleanup; 381 } 382 383 if (reload) { 384 if ((result = semanage_reload_policy(sh)) < 0) { 385 fprintf(stderr, "%s: Could not reload policy\n", 386 argv[0]); 387 goto cleanup; 388 } 389 } 390 391 if (build) { 392 if ((result = semanage_begin_transaction(sh)) < 0) { 393 fprintf(stderr, "%s: Could not begin transaction: %s\n", 394 argv[0], errno ? strerror(errno) : ""); 395 goto cleanup; 396 } 397 } 398 399 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 400 fprintf(stderr, 401 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 402 argv[0], 403 priority); 404 goto cleanup; 405 } 406 407 for (i = 0; i < num_commands; i++) { 408 enum client_modes mode = commands[i].mode; 409 char *mode_arg = commands[i].arg; 410 411 switch (mode) { 412 case INSTALL_M:{ 413 if (verbose) { 414 printf 415 ("Attempting to install module '%s':\n", 416 mode_arg); 417 } 418 result = 419 semanage_module_install_file(sh, mode_arg); 420 break; 421 } 422 case EXTRACT_M:{ 423 semanage_module_info_t *extract_info = NULL; 424 semanage_module_key_t *modkey = NULL; 425 uint16_t curr_priority; 426 void *data = NULL; 427 size_t data_len = 0; 428 char output_path[PATH_MAX]; 429 const char *output_name = NULL; 430 const char *lang_ext = NULL; 431 int rlen; 432 FILE *output_fd = NULL; 433 434 result = semanage_module_key_create(sh, &modkey); 435 if (result != 0) { 436 goto cleanup_extract; 437 } 438 439 result = semanage_module_key_set_name(sh, modkey, mode_arg); 440 if (result != 0) { 441 goto cleanup_extract; 442 } 443 444 if (priority_set == 0) { 445 result = semanage_module_get_module_info(sh, modkey, &extract_info); 446 if (result != 0) { 447 goto cleanup_extract; 448 } 449 450 semanage_module_info_get_priority(sh, extract_info, &curr_priority); 451 printf("Module '%s' does not exist at the default priority '%d'. " 452 "Extracting at highest existing priority '%d'.\n", mode_arg, priority, curr_priority); 453 priority = curr_priority; 454 } 455 456 result = semanage_module_key_set_priority(sh, modkey, priority); 457 if (result != 0) { 458 goto cleanup_extract; 459 } 460 461 if (verbose) { 462 printf 463 ("Attempting to extract module '%s':\n", 464 mode_arg); 465 } 466 result = semanage_module_extract(sh, modkey, extract_cil, &data, &data_len, &extract_info); 467 if (result != 0) { 468 goto cleanup_extract; 469 } 470 471 if (extract_cil) { 472 lang_ext = "cil"; 473 } else { 474 result = semanage_module_info_get_lang_ext(sh, extract_info, &lang_ext); 475 if (result != 0) { 476 goto cleanup_extract; 477 } 478 } 479 480 result = semanage_module_info_get_name(sh, extract_info, &output_name); 481 if (result != 0) { 482 goto cleanup_extract; 483 } 484 485 rlen = snprintf(output_path, PATH_MAX, "%s.%s", output_name, lang_ext); 486 if (rlen < 0 || rlen >= PATH_MAX) { 487 fprintf(stderr, "%s: Failed to generate output path.\n", argv[0]); 488 result = -1; 489 goto cleanup_extract; 490 } 491 492 if (access(output_path, F_OK) == 0) { 493 fprintf(stderr, "%s: %s is already extracted with extension %s.\n", argv[0], mode_arg, lang_ext); 494 result = -1; 495 goto cleanup_extract; 496 } 497 498 output_fd = fopen(output_path, "w"); 499 if (output_fd == NULL) { 500 fprintf(stderr, "%s: Unable to open %s\n", argv[0], output_path); 501 result = -1; 502 goto cleanup_extract; 503 } 504 505 if (fwrite(data, 1, data_len, output_fd) < data_len) { 506 fprintf(stderr, "%s: Unable to write to %s\n", argv[0], output_path); 507 result = -1; 508 goto cleanup_extract; 509 } 510cleanup_extract: 511 if (output_fd != NULL) { 512 fclose(output_fd); 513 } 514 if (data_len > 0) { 515 munmap(data, data_len); 516 } 517 semanage_module_info_destroy(sh, extract_info); 518 free(extract_info); 519 semanage_module_key_destroy(sh, modkey); 520 free(modkey); 521 break; 522 } 523 case CIL_M: 524 extract_cil = 1; 525 break; 526 case HLL_M: 527 extract_cil = 0; 528 break; 529 case REMOVE_M:{ 530 if (verbose) { 531 printf 532 ("Attempting to remove module '%s':\n", 533 mode_arg); 534 } 535 result = semanage_module_remove(sh, mode_arg); 536 if ( result == -2 ) { 537 continue; 538 } 539 break; 540 } 541 case LIST_M:{ 542 semanage_module_info_t *modinfos = NULL; 543 int modinfos_len = 0; 544 semanage_module_info_t *m = NULL; 545 int j = 0; 546 547 if (verbose) { 548 printf 549 ("Attempting to list active modules:\n"); 550 } 551 552 if (mode_arg == NULL || strcmp(mode_arg, "standard") == 0) { 553 result = semanage_module_list(sh, 554 &modinfos, 555 &modinfos_len); 556 if (result < 0) goto cleanup_list; 557 558 if (modinfos_len == 0) { 559 printf("No modules.\n"); 560 } 561 562 const char *name = NULL; 563 564 for (j = 0; j < modinfos_len; j++) { 565 m = semanage_module_list_nth(modinfos, j); 566 567 result = semanage_module_info_get_name(sh, m, &name); 568 if (result != 0) goto cleanup_list; 569 570 printf("%s\n", name); 571 } 572 } 573 else if (strcmp(mode_arg, "full") == 0) { 574 /* get the modules */ 575 result = semanage_module_list_all(sh, 576 &modinfos, 577 &modinfos_len); 578 if (result != 0) goto cleanup_list; 579 580 if (modinfos_len == 0) { 581 printf("No modules.\n"); 582 } 583 584 /* calculate column widths */ 585 size_t column[4] = { 0, 0, 0, 0 }; 586 587 /* fixed width columns */ 588 column[0] = sizeof("000") - 1; 589 column[3] = sizeof("disabled") - 1; 590 591 /* variable width columns */ 592 const char *tmp = NULL; 593 size_t size; 594 for (j = 0; j < modinfos_len; j++) { 595 m = semanage_module_list_nth(modinfos, j); 596 597 result = semanage_module_info_get_name(sh, m, &tmp); 598 if (result != 0) goto cleanup_list; 599 600 size = strlen(tmp); 601 if (size > column[1]) column[1] = size; 602 603 result = semanage_module_info_get_lang_ext(sh, m, &tmp); 604 if (result != 0) goto cleanup_list; 605 606 size = strlen(tmp); 607 if (size > column[3]) column[3] = size; 608 } 609 610 /* print out each module */ 611 for (j = 0; j < modinfos_len; j++) { 612 uint16_t pri = 0; 613 const char *name = NULL; 614 int enabled = 0; 615 const char *lang_ext = NULL; 616 617 m = semanage_module_list_nth(modinfos, j); 618 619 result = semanage_module_info_get_priority(sh, m, &pri); 620 if (result != 0) goto cleanup_list; 621 622 result = semanage_module_info_get_name(sh, m, &name); 623 if (result != 0) goto cleanup_list; 624 625 result = semanage_module_info_get_enabled(sh, m, &enabled); 626 if (result != 0) goto cleanup_list; 627 628 result = semanage_module_info_get_lang_ext(sh, m, &lang_ext); 629 if (result != 0) goto cleanup_list; 630 631 printf("%0*u %-*s %-*s %-*s\n", 632 (int)column[0], pri, 633 (int)column[1], name, 634 (int)column[2], lang_ext, 635 (int)column[3], enabled ? "" : "disabled"); 636 } 637 } 638 else { 639 result = -1; 640 } 641 642cleanup_list: 643 for (j = 0; j < modinfos_len; j++) { 644 m = semanage_module_list_nth(modinfos, j); 645 semanage_module_info_destroy(sh, m); 646 } 647 648 free(modinfos); 649 650 break; 651 } 652 case PRIORITY_M:{ 653 char *endptr = NULL; 654 priority = (uint16_t)strtoul(mode_arg, &endptr, 10); 655 priority_set = 1; 656 657 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 658 fprintf(stderr, 659 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 660 argv[0], 661 priority); 662 goto cleanup; 663 } 664 665 break; 666 } 667 case ENABLE_M:{ 668 if (verbose) { 669 printf 670 ("Attempting to enable module '%s':\n", 671 mode_arg); 672 } 673 674 semanage_module_key_t *modkey = NULL; 675 676 result = semanage_module_key_create(sh, &modkey); 677 if (result != 0) goto cleanup_enable; 678 679 result = semanage_module_key_set_name(sh, modkey, mode_arg); 680 if (result != 0) goto cleanup_enable; 681 682 result = semanage_module_set_enabled(sh, modkey, 1); 683 if (result != 0) goto cleanup_enable; 684 685cleanup_enable: 686 semanage_module_key_destroy(sh, modkey); 687 free(modkey); 688 689 break; 690 } 691 case DISABLE_M:{ 692 if (verbose) { 693 printf 694 ("Attempting to disable module '%s':\n", 695 mode_arg); 696 } 697 698 semanage_module_key_t *modkey = NULL; 699 700 result = semanage_module_key_create(sh, &modkey); 701 if (result != 0) goto cleanup_disable; 702 703 result = semanage_module_key_set_name(sh, modkey, mode_arg); 704 if (result != 0) goto cleanup_disable; 705 706 result = semanage_module_set_enabled(sh, modkey, 0); 707 if (result != 0) goto cleanup_disable; 708 709cleanup_disable: 710 semanage_module_key_destroy(sh, modkey); 711 free(modkey); 712 713 break; 714 } 715 default:{ 716 fprintf(stderr, 717 "%s: Unknown mode specified.\n", 718 argv[0]); 719 usage(argv[0]); 720 goto cleanup; 721 } 722 } 723 commit += do_commit[mode]; 724 if (result < 0) { 725 fprintf(stderr, "%s: Failed on %s!\n", argv[0], 726 mode_arg ? : "list"); 727 goto cleanup; 728 } else if (verbose) { 729 printf("Ok: return value of %d.\n", result); 730 } 731 } 732 733 if (commit) { 734 if (verbose) 735 printf("Committing changes:\n"); 736 if (no_reload) 737 semanage_set_reload(sh, 0); 738 if (build) 739 semanage_set_rebuild(sh, 1); 740 if (disable_dontaudit) 741 semanage_set_disable_dontaudit(sh, 1); 742 else if (build) 743 semanage_set_disable_dontaudit(sh, 0); 744 if (preserve_tunables) 745 semanage_set_preserve_tunables(sh, 1); 746 if (ignore_module_cache) 747 semanage_set_ignore_module_cache(sh, 1); 748 749 result = semanage_commit(sh); 750 } 751 752 if (result < 0) { 753 fprintf(stderr, "%s: Failed!\n", argv[0]); 754 goto cleanup; 755 } else if (commit && verbose) { 756 printf("Ok: transaction number %d.\n", result); 757 } 758 759 if (semanage_disconnect(sh) < 0) { 760 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 761 goto cleanup; 762 } 763 status = EXIT_SUCCESS; 764 765 cleanup: 766 if (semanage_is_connected(sh)) { 767 if (semanage_disconnect(sh) < 0) { 768 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 769 } 770 } 771 semanage_handle_destroy(sh); 772 773 cleanup_nohandle: 774 cleanup(); 775 exit(status); 776} 777