Home | History | Annotate | Download | only in semodule
      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