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