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