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 24 #include <semanage/modules.h> 25 26 enum client_modes { 27 NO_MODE, INSTALL_M, REMOVE_M, 28 LIST_M, RELOAD, PRIORITY_M, ENABLE_M, DISABLE_M 29 }; 30 /* list of modes in which one ought to commit afterwards */ 31 static const int do_commit[] = { 32 0, 1, 1, 33 0, 0, 0, 1, 1, 34 }; 35 36 struct command { 37 enum client_modes mode; 38 char *arg; 39 }; 40 static struct command *commands = NULL; 41 static int num_commands = 0; 42 43 /* options given on command line */ 44 static int verbose; 45 static int reload; 46 static int no_reload; 47 static int build; 48 static int disable_dontaudit; 49 static int preserve_tunables; 50 static int ignore_module_cache; 51 static uint16_t priority; 52 53 static semanage_handle_t *sh = NULL; 54 static char *store; 55 static char *store_root; 56 57 extern char *optarg; 58 extern int optind; 59 60 static void cleanup(void) 61 { 62 while (--num_commands >= 0) { 63 free(commands[num_commands].arg); 64 } 65 free(commands); 66 } 67 68 /* Signal handlers. */ 69 static void handle_signal(int sig_num) 70 { 71 if (sig_num == SIGINT || sig_num == SIGQUIT || sig_num == SIGTERM) { 72 /* catch these signals, and then drop them */ 73 } 74 } 75 76 static void set_store(char *storename) 77 { 78 /* For now this only supports a store name, later on this 79 * should support an address for a remote connection */ 80 81 if ((store = strdup(storename)) == NULL) { 82 fprintf(stderr, "Out of memory!\n"); 83 goto bad; 84 } 85 86 return; 87 88 bad: 89 cleanup(); 90 exit(1); 91 } 92 93 static void set_store_root(char *path) 94 { 95 if ((store_root = strdup(path)) == NULL) { 96 fprintf(stderr, "Out of memory!\n"); 97 goto bad; 98 } 99 100 return; 101 102 bad: 103 cleanup(); 104 exit(1); 105 } 106 107 /* Establish signal handlers for the process. */ 108 static void create_signal_handlers(void) 109 { 110 if (signal(SIGINT, handle_signal) == SIG_ERR || 111 signal(SIGQUIT, handle_signal) == SIG_ERR || 112 signal(SIGTERM, handle_signal) == SIG_ERR) { 113 fprintf(stderr, "Could not set up signal handler.\n"); 114 exit(255); 115 } 116 } 117 118 static void usage(char *progname) 119 { 120 printf("usage: %s [options]... MODE [MODES]...\n", progname); 121 printf("Manage SELinux policy modules.\n"); 122 printf("MODES:\n"); 123 printf(" -R, --reload reload policy\n"); 124 printf(" -B, --build build and reload policy\n"); 125 printf(" -i,--install=MODULE_PKG install a new module\n"); 126 printf(" -r,--remove=MODULE_NAME remove existing module\n"); 127 printf(" -l,--list-modules=[KIND] display list of installed modules\n"); 128 printf(" KIND: standard list highest priority, enabled modules\n"); 129 printf(" full list all modules\n"); 130 printf(" -X,--priority=PRIORITY set priority for following operations (1-999)\n"); 131 printf(" -e,--enable=MODULE_NAME enable module\n"); 132 printf(" -d,--disable=MODULE_NAME disable module\n"); 133 printf("Other options:\n"); 134 printf(" -s,--store name of the store to operate on\n"); 135 printf(" -N,-n,--noreload do not reload policy after commit\n"); 136 printf(" -h,--help print this message and quit\n"); 137 printf(" -v,--verbose be verbose\n"); 138 printf(" -D,--disable_dontaudit Remove dontaudits from policy\n"); 139 printf(" -P,--preserve_tunables Preserve tunables in policy\n"); 140 printf(" -C,--ignore-module-cache Rebuild CIL modules compiled from HLL files\n"); 141 printf(" -p,--path use an alternate path for the policy root\n"); 142 printf(" -S,--store-path use an alternate path for the policy store root\n"); 143 } 144 145 /* Sets the global mode variable to new_mode, but only if no other 146 * mode has been given. */ 147 static void set_mode(enum client_modes new_mode, char *arg) 148 { 149 struct command *c; 150 char *s; 151 if ((c = realloc(commands, sizeof(*c) * (num_commands + 1))) == NULL) { 152 fprintf(stderr, "Out of memory!\n"); 153 cleanup(); 154 exit(1); 155 } 156 commands = c; 157 commands[num_commands].mode = new_mode; 158 commands[num_commands].arg = NULL; 159 num_commands++; 160 if (arg != NULL) { 161 if ((s = strdup(arg)) == NULL) { 162 fprintf(stderr, "Out of memory!\n"); 163 cleanup(); 164 exit(1); 165 } 166 commands[num_commands - 1].arg = s; 167 } 168 } 169 170 /* Parse command line and set global options. */ 171 static void parse_command_line(int argc, char **argv) 172 { 173 static struct option opts[] = { 174 {"store", required_argument, NULL, 's'}, 175 {"base", required_argument, NULL, 'b'}, 176 {"help", 0, NULL, 'h'}, 177 {"install", required_argument, NULL, 'i'}, 178 {"list-modules", optional_argument, NULL, 'l'}, 179 {"verbose", 0, NULL, 'v'}, 180 {"remove", required_argument, NULL, 'r'}, 181 {"upgrade", required_argument, NULL, 'u'}, 182 {"reload", 0, NULL, 'R'}, 183 {"noreload", 0, NULL, 'n'}, 184 {"build", 0, NULL, 'B'}, 185 {"disable_dontaudit", 0, NULL, 'D'}, 186 {"preserve_tunables", 0, NULL, 'P'}, 187 {"ignore-module-cache", 0, NULL, 'C'}, 188 {"priority", required_argument, NULL, 'X'}, 189 {"enable", required_argument, NULL, 'e'}, 190 {"disable", required_argument, NULL, 'd'}, 191 {"path", required_argument, NULL, 'p'}, 192 {"store-path", required_argument, NULL, 'S'}, 193 {NULL, 0, NULL, 0} 194 }; 195 int i; 196 verbose = 0; 197 reload = 0; 198 no_reload = 0; 199 priority = 400; 200 while ((i = 201 getopt_long(argc, argv, "s:b:hi:l::vqr:u:RnNBDCPX:e:d:p:S:", opts, 202 NULL)) != -1) { 203 switch (i) { 204 case 'b': 205 fprintf(stderr, "The --base option is deprecated. Use --install instead.\n"); 206 set_mode(INSTALL_M, optarg); 207 break; 208 case 'h': 209 usage(argv[0]); 210 exit(0); 211 case 'i': 212 set_mode(INSTALL_M, optarg); 213 break; 214 case 'l': 215 set_mode(LIST_M, optarg); 216 break; 217 case 'v': 218 verbose = 1; 219 break; 220 case 'r': 221 set_mode(REMOVE_M, optarg); 222 break; 223 case 'u': 224 fprintf(stderr, "The --upgrade option is deprecated. Use --install instead.\n"); 225 set_mode(INSTALL_M, optarg); 226 break; 227 case 's': 228 set_store(optarg); 229 break; 230 case 'p': 231 semanage_set_root(optarg); 232 break; 233 case 'S': 234 set_store_root(optarg); 235 break; 236 case 'R': 237 reload = 1; 238 break; 239 case 'n': 240 no_reload = 1; 241 break; 242 case 'N': 243 no_reload = 1; 244 break; 245 case 'B': 246 build = 1; 247 break; 248 case 'D': 249 disable_dontaudit = 1; 250 break; 251 case 'P': 252 preserve_tunables = 1; 253 break; 254 case 'C': 255 ignore_module_cache = 1; 256 break; 257 case 'X': 258 set_mode(PRIORITY_M, optarg); 259 break; 260 case 'e': 261 set_mode(ENABLE_M, optarg); 262 break; 263 case 'd': 264 set_mode(DISABLE_M, optarg); 265 break; 266 case '?': 267 default:{ 268 usage(argv[0]); 269 exit(1); 270 } 271 } 272 } 273 if ((build || reload) && num_commands) { 274 fprintf(stderr, 275 "build or reload should not be used with other commands\n"); 276 usage(argv[0]); 277 exit(1); 278 } 279 if (num_commands == 0 && reload == 0 && build == 0) { 280 fprintf(stderr, "At least one mode must be specified.\n"); 281 usage(argv[0]); 282 exit(1); 283 } 284 285 if (optind < argc) { 286 int mode; 287 /* if -i/u/r was the last command treat any remaining 288 * arguments as args. Will allow 'semodule -i *.pp' to 289 * work as expected. 290 */ 291 292 if (commands && commands[num_commands - 1].mode == INSTALL_M) { 293 mode = INSTALL_M; 294 } else if (commands && commands[num_commands - 1].mode == REMOVE_M) { 295 mode = REMOVE_M; 296 } else { 297 fprintf(stderr, "unknown additional arguments:\n"); 298 while (optind < argc) 299 fprintf(stderr, " %s", argv[optind++]); 300 fprintf(stderr, "\n\n"); 301 usage(argv[0]); 302 exit(1); 303 } 304 while (optind < argc) 305 set_mode(mode, argv[optind++]); 306 } 307 } 308 309 int main(int argc, char *argv[]) 310 { 311 int i, commit = 0; 312 int result; 313 int status = EXIT_FAILURE; 314 char *genhomedirconargv[] = { "genhomedircon", "-B", "-n" }; 315 create_signal_handlers(); 316 if (strcmp(basename(argv[0]), "genhomedircon") == 0) { 317 argc = 3; 318 argv=genhomedirconargv; 319 } 320 parse_command_line(argc, argv); 321 322 if (build) 323 commit = 1; 324 325 sh = semanage_handle_create(); 326 if (!sh) { 327 fprintf(stderr, "%s: Could not create semanage handle\n", 328 argv[0]); 329 goto cleanup_nohandle; 330 } 331 332 if (store) { 333 /* Set the store we want to connect to, before connecting. 334 * this will always set a direct connection now, an additional 335 * option will need to be used later to specify a policy server 336 * location */ 337 semanage_select_store(sh, store, SEMANAGE_CON_DIRECT); 338 } 339 340 if (store_root) { 341 semanage_set_store_root(sh, store_root); 342 } 343 344 /* create store if necessary, for bootstrapping */ 345 semanage_set_create_store(sh, 1); 346 347 if ((result = semanage_connect(sh)) < 0) { 348 fprintf(stderr, "%s: Could not connect to policy handler\n", 349 argv[0]); 350 goto cleanup; 351 } 352 353 if (reload) { 354 if ((result = semanage_reload_policy(sh)) < 0) { 355 fprintf(stderr, "%s: Could not reload policy\n", 356 argv[0]); 357 goto cleanup; 358 } 359 } 360 361 if (build) { 362 if ((result = semanage_begin_transaction(sh)) < 0) { 363 fprintf(stderr, "%s: Could not begin transaction: %s\n", 364 argv[0], errno ? strerror(errno) : ""); 365 goto cleanup; 366 } 367 } 368 369 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 370 fprintf(stderr, 371 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 372 argv[0], 373 priority); 374 goto cleanup; 375 } 376 377 for (i = 0; i < num_commands; i++) { 378 enum client_modes mode = commands[i].mode; 379 char *mode_arg = commands[i].arg; 380 381 switch (mode) { 382 case INSTALL_M:{ 383 if (verbose) { 384 printf 385 ("Attempting to install module '%s':\n", 386 mode_arg); 387 } 388 result = 389 semanage_module_install_file(sh, mode_arg); 390 break; 391 } 392 case REMOVE_M:{ 393 if (verbose) { 394 printf 395 ("Attempting to remove module '%s':\n", 396 mode_arg); 397 } 398 result = semanage_module_remove(sh, mode_arg); 399 if ( result == -2 ) { 400 continue; 401 } 402 break; 403 } 404 case LIST_M:{ 405 semanage_module_info_t *modinfos = NULL; 406 int modinfos_len = 0; 407 semanage_module_info_t *m = NULL; 408 int j = 0; 409 410 if (verbose) { 411 printf 412 ("Attempting to list active modules:\n"); 413 } 414 415 if (mode_arg == NULL || strcmp(mode_arg, "standard") == 0) { 416 result = semanage_module_list(sh, 417 &modinfos, 418 &modinfos_len); 419 if (result < 0) goto cleanup_list; 420 421 if (modinfos_len == 0) { 422 printf("No modules.\n"); 423 } 424 425 const char *name = NULL; 426 427 for (j = 0; j < modinfos_len; j++) { 428 m = semanage_module_list_nth(modinfos, j); 429 430 result = semanage_module_info_get_name(sh, m, &name); 431 if (result != 0) goto cleanup_list; 432 433 printf("%s\n", name); 434 } 435 } 436 else if (strcmp(mode_arg, "full") == 0) { 437 /* get the modules */ 438 result = semanage_module_list_all(sh, 439 &modinfos, 440 &modinfos_len); 441 if (result != 0) goto cleanup_list; 442 443 if (modinfos_len == 0) { 444 printf("No modules.\n"); 445 } 446 447 /* calculate column widths */ 448 size_t column[4] = { 0, 0, 0, 0 }; 449 450 /* fixed width columns */ 451 column[0] = sizeof("000") - 1; 452 column[3] = sizeof("disabled") - 1; 453 454 /* variable width columns */ 455 const char *tmp = NULL; 456 size_t size; 457 for (j = 0; j < modinfos_len; j++) { 458 m = semanage_module_list_nth(modinfos, j); 459 460 result = semanage_module_info_get_name(sh, m, &tmp); 461 if (result != 0) goto cleanup_list; 462 463 size = strlen(tmp); 464 if (size > column[1]) column[1] = size; 465 466 result = semanage_module_info_get_lang_ext(sh, m, &tmp); 467 if (result != 0) goto cleanup_list; 468 469 size = strlen(tmp); 470 if (size > column[3]) column[3] = size; 471 } 472 473 /* print out each module */ 474 for (j = 0; j < modinfos_len; j++) { 475 uint16_t pri = 0; 476 const char *name = NULL; 477 int enabled = 0; 478 const char *lang_ext = NULL; 479 480 m = semanage_module_list_nth(modinfos, j); 481 482 result = semanage_module_info_get_priority(sh, m, &pri); 483 if (result != 0) goto cleanup_list; 484 485 result = semanage_module_info_get_name(sh, m, &name); 486 if (result != 0) goto cleanup_list; 487 488 result = semanage_module_info_get_enabled(sh, m, &enabled); 489 if (result != 0) goto cleanup_list; 490 491 result = semanage_module_info_get_lang_ext(sh, m, &lang_ext); 492 if (result != 0) goto cleanup_list; 493 494 printf("%0*u %-*s %-*s %-*s\n", 495 (int)column[0], pri, 496 (int)column[1], name, 497 (int)column[2], lang_ext, 498 (int)column[3], enabled ? "" : "disabled"); 499 } 500 } 501 else { 502 result = -1; 503 } 504 505 cleanup_list: 506 for (j = 0; j < modinfos_len; j++) { 507 m = semanage_module_list_nth(modinfos, j); 508 semanage_module_info_destroy(sh, m); 509 } 510 511 free(modinfos); 512 513 break; 514 } 515 case PRIORITY_M:{ 516 char *endptr = NULL; 517 priority = (uint16_t)strtoul(mode_arg, &endptr, 10); 518 519 if ((result = semanage_set_default_priority(sh, priority)) != 0) { 520 fprintf(stderr, 521 "%s: Invalid priority %d (needs to be between 1 and 999)\n", 522 argv[0], 523 priority); 524 goto cleanup; 525 } 526 527 break; 528 } 529 case ENABLE_M:{ 530 if (verbose) { 531 printf 532 ("Attempting to enable module '%s':\n", 533 mode_arg); 534 } 535 536 semanage_module_key_t *modkey = NULL; 537 538 result = semanage_module_key_create(sh, &modkey); 539 if (result != 0) goto cleanup_enable; 540 541 result = semanage_module_key_set_name(sh, modkey, mode_arg); 542 if (result != 0) goto cleanup_enable; 543 544 result = semanage_module_set_enabled(sh, modkey, 1); 545 if (result != 0) goto cleanup_enable; 546 547 cleanup_enable: 548 semanage_module_key_destroy(sh, modkey); 549 free(modkey); 550 551 break; 552 } 553 case DISABLE_M:{ 554 if (verbose) { 555 printf 556 ("Attempting to disable module '%s':\n", 557 mode_arg); 558 } 559 560 semanage_module_key_t *modkey = NULL; 561 562 result = semanage_module_key_create(sh, &modkey); 563 if (result != 0) goto cleanup_disable; 564 565 result = semanage_module_key_set_name(sh, modkey, mode_arg); 566 if (result != 0) goto cleanup_disable; 567 568 result = semanage_module_set_enabled(sh, modkey, 0); 569 if (result != 0) goto cleanup_disable; 570 571 cleanup_disable: 572 semanage_module_key_destroy(sh, modkey); 573 free(modkey); 574 575 break; 576 } 577 default:{ 578 fprintf(stderr, 579 "%s: Unknown mode specified.\n", 580 argv[0]); 581 usage(argv[0]); 582 goto cleanup; 583 } 584 } 585 commit += do_commit[mode]; 586 if (result < 0) { 587 fprintf(stderr, "%s: Failed on %s!\n", argv[0], 588 mode_arg ? : "list"); 589 goto cleanup; 590 } else if (verbose) { 591 printf("Ok: return value of %d.\n", result); 592 } 593 } 594 595 if (commit) { 596 if (verbose) 597 printf("Committing changes:\n"); 598 if (no_reload) 599 semanage_set_reload(sh, 0); 600 if (build) 601 semanage_set_rebuild(sh, 1); 602 if (disable_dontaudit) 603 semanage_set_disable_dontaudit(sh, 1); 604 else if (build) 605 semanage_set_disable_dontaudit(sh, 0); 606 if (preserve_tunables) 607 semanage_set_preserve_tunables(sh, 1); 608 if (ignore_module_cache) 609 semanage_set_ignore_module_cache(sh, 1); 610 611 result = semanage_commit(sh); 612 } 613 614 if (result < 0) { 615 fprintf(stderr, "%s: Failed!\n", argv[0]); 616 goto cleanup; 617 } else if (commit && verbose) { 618 printf("Ok: transaction number %d.\n", result); 619 } 620 621 if (semanage_disconnect(sh) < 0) { 622 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 623 goto cleanup; 624 } 625 status = EXIT_SUCCESS; 626 627 cleanup: 628 if (semanage_is_connected(sh)) { 629 if (semanage_disconnect(sh) < 0) { 630 fprintf(stderr, "%s: Error disconnecting\n", argv[0]); 631 } 632 } 633 semanage_handle_destroy(sh); 634 635 cleanup_nohandle: 636 cleanup(); 637 exit(status); 638 } 639