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
27 enum 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 */
32 static const int do_commit[] = {
33 0, 1, 1, 0, 0, 0,
34 0, 0, 0, 1, 1,
35 };
36
37 struct command {
38 enum client_modes mode;
39 char *arg;
40 };
41 static struct command *commands = NULL;
42 static int num_commands = 0;
43
44 /* options given on command line */
45 static int verbose;
46 static int reload;
47 static int no_reload;
48 static int build;
49 static int disable_dontaudit;
50 static int preserve_tunables;
51 static int ignore_module_cache;
52 static uint16_t priority;
53 static int priority_set = 0;
54
55 static semanage_handle_t *sh = NULL;
56 static char *store;
57 static char *store_root;
58 int extract_cil = 0;
59
60 extern char *optarg;
61 extern int optind;
62
cleanup(void)63 static void cleanup(void)
64 {
65 while (--num_commands >= 0) {
66 free(commands[num_commands].arg);
67 }
68 free(commands);
69 }
70
71 /* Signal handlers. */
handle_signal(int sig_num)72 static 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
set_store(char * storename)79 static 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
set_store_root(char * path)96 static 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. */
create_signal_handlers(void)111 static 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
usage(char * progname)121 static 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. */
set_mode(enum client_modes new_mode,char * arg)153 static 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. */
parse_command_line(int argc,char ** argv)177 static 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
main(int argc,char * argv[])339 int 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 }
510 cleanup_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
642 cleanup_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
685 cleanup_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
709 cleanup_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