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 
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 
cleanup(void)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. */
handle_signal(int sig_num)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 
set_store(char * storename)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 
set_store_root(char * path)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. */
create_signal_handlers(void)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 
usage(char * progname)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. */
set_mode(enum client_modes new_mode,char * arg)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. */
parse_command_line(int argc,char ** argv)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 
main(int argc,char * argv[])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