semodule.c revision e4bc1b223debcc6747fef4d7a2a0a320c0208a88
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
23#include <semanage/modules.h>
24
25enum client_modes { NO_MODE, INSTALL_M, UPGRADE_M, BASE_M, ENABLE_M, DISABLE_M, REMOVE_M,
26	LIST_M, RELOAD
27};
28/* list of modes in which one ought to commit afterwards */
29static const int do_commit[] = {
30	0, 1, 1, 1, 1, 1, 1,
31	0, 0
32};
33
34struct command {
35	enum client_modes mode;
36	char *arg;
37};
38static struct command *commands = NULL;
39static int num_commands = 0;
40
41/* options given on command line */
42static int verbose;
43static int reload;
44static int no_reload;
45static int create_store;
46static int build;
47static int disable_dontaudit;
48static int preserve_tunables;
49
50static semanage_handle_t *sh = NULL;
51static char *store;
52
53extern char *optarg;
54extern int optind;
55
56static void cleanup(void)
57{
58	while (--num_commands >= 0) {
59		free(commands[num_commands].arg);
60	}
61	free(commands);
62}
63
64/* Signal handlers. */
65static void handle_signal(int sig_num)
66{
67	if (sig_num == SIGINT || sig_num == SIGQUIT || sig_num == SIGTERM) {
68		/* catch these signals, and then drop them */
69	}
70}
71
72static void set_store(char *storename)
73{
74	/* For now this only supports a store name, later on this
75	 * should support an address for a remote connection */
76
77	if ((store = strdup(storename)) == NULL) {
78		fprintf(stderr, "Out of memory!\n");
79		goto bad;
80	}
81
82	return;
83
84      bad:
85	cleanup();
86	exit(1);
87}
88
89/* Establish signal handlers for the process. */
90static void create_signal_handlers(void)
91{
92	if (signal(SIGINT, handle_signal) == SIG_ERR ||
93	    signal(SIGQUIT, handle_signal) == SIG_ERR ||
94	    signal(SIGTERM, handle_signal) == SIG_ERR) {
95		fprintf(stderr, "Could not set up signal handler.\n");
96		exit(255);
97	}
98}
99
100static void usage(char *progname)
101{
102	printf("usage:  %s [options]... MODE [MODES]...\n", progname);
103	printf("Manage SELinux policy modules.\n");
104	printf("MODES:\n");
105	printf("  -R, --reload		    reload policy\n");
106	printf("  -B, --build		    build and reload policy\n");
107	printf("  -i,--install=MODULE_PKG   install a new module\n");
108	printf("  -u,--upgrade=MODULE_PKG   upgrade existing module\n");
109	printf("  -b,--base=MODULE_PKG      install new base module\n");
110	printf("  -e,--enable=MODULE_PKG    enable existing module\n");
111	printf("  -d,--disable=MODULE_PKG   disable existing module\n");
112 	printf("  -r,--remove=MODULE_NAME   remove existing module\n");
113	printf
114	    ("  -l,--list-modules         display list of installed modules\n");
115	printf("Other options:\n");
116	printf("  -s,--store	   name of the store to operate on\n");
117	printf("  -n,--noreload	   do not reload policy after commit\n");
118	printf("  -h,--help        print this message and quit\n");
119	printf("  -v,--verbose     be verbose\n");
120	printf("  -D,--disable_dontaudit	Remove dontaudits from policy\n");
121	printf("  -P,--preserve_tunables	Preserve tunables in policy\n");
122}
123
124/* Sets the global mode variable to new_mode, but only if no other
125 * mode has been given. */
126static void set_mode(enum client_modes new_mode, char *arg)
127{
128	struct command *c;
129	char *s;
130	if ((c = realloc(commands, sizeof(*c) * (num_commands + 1))) == NULL) {
131		fprintf(stderr, "Out of memory!\n");
132		cleanup();
133		exit(1);
134	}
135	commands = c;
136	commands[num_commands].mode = new_mode;
137	commands[num_commands].arg = NULL;
138	num_commands++;
139	if (arg != NULL) {
140		if ((s = strdup(arg)) == NULL) {
141			fprintf(stderr, "Out of memory!\n");
142			cleanup();
143			exit(1);
144		}
145		commands[num_commands - 1].arg = s;
146	}
147}
148
149/* Parse command line and set global options. */
150static void parse_command_line(int argc, char **argv)
151{
152	static struct option opts[] = {
153		{"store", required_argument, NULL, 's'},
154		{"base", required_argument, NULL, 'b'},
155		{"help", 0, NULL, 'h'},
156		{"install", required_argument, NULL, 'i'},
157		{"list-modules", 0, NULL, 'l'},
158		{"verbose", 0, NULL, 'v'},
159		{"enable", required_argument, NULL, 'e'},
160		{"disable", required_argument, NULL, 'd'},
161		{"remove", required_argument, NULL, 'r'},
162		{"upgrade", required_argument, NULL, 'u'},
163		{"reload", 0, NULL, 'R'},
164		{"noreload", 0, NULL, 'n'},
165		{"build", 0, NULL, 'B'},
166		{"disable_dontaudit", 0, NULL, 'D'},
167		{"preserve_tunables", 0, NULL, 'P'},
168		{"path", required_argument, NULL, 'p'},
169		{NULL, 0, NULL, 0}
170	};
171	int i;
172	verbose = 0;
173	reload = 0;
174	no_reload = 0;
175	create_store = 0;
176	while ((i =
177		getopt_long(argc, argv, "p:s:b:hi:lvqe:d:r:u:RnBDP", opts,
178			    NULL)) != -1) {
179		switch (i) {
180		case 'b':
181			set_mode(BASE_M, optarg);
182			create_store = 1;
183			break;
184		case 'h':
185			usage(argv[0]);
186			exit(0);
187		case 'i':
188			set_mode(INSTALL_M, optarg);
189			break;
190		case 'l':
191			set_mode(LIST_M, NULL);
192			break;
193		case 'v':
194			verbose = 1;
195			break;
196		case 'e':
197			set_mode(ENABLE_M, optarg);
198			break;
199		case 'd':
200			set_mode(DISABLE_M, optarg);
201			break;
202		case 'r':
203			set_mode(REMOVE_M, optarg);
204			break;
205		case 'p':
206			semanage_set_root(optarg);
207			break;
208		case 'u':
209			set_mode(UPGRADE_M, optarg);
210			break;
211		case 's':
212			set_store(optarg);
213			break;
214		case 'R':
215			reload = 1;
216			break;
217		case 'n':
218			no_reload = 1;
219			break;
220		case 'B':
221			build = 1;
222			break;
223		case 'D':
224			disable_dontaudit = 1;
225			break;
226		case 'P':
227			preserve_tunables = 1;
228			break;
229		case '?':
230		default:{
231				usage(argv[0]);
232				exit(1);
233			}
234		}
235	}
236	if ((build || reload) && num_commands) {
237		fprintf(stderr,
238			"build or reload should not be used with other commands\n");
239		usage(argv[0]);
240		exit(1);
241	}
242	if (num_commands == 0 && reload == 0 && build == 0) {
243		fprintf(stderr, "At least one mode must be specified.\n");
244		usage(argv[0]);
245		exit(1);
246	}
247
248	if (optind < argc) {
249		int mode;
250		/* if -i/u/r was the last command treat any remaining
251		 * arguments as args. Will allow 'semodule -i *.pp' to
252		 * work as expected.
253		 */
254
255		if (commands && commands[num_commands - 1].mode == INSTALL_M) {
256			mode = INSTALL_M;
257		} else if (commands && commands[num_commands - 1].mode == UPGRADE_M) {
258			mode = UPGRADE_M;
259		} else if (commands && commands[num_commands - 1].mode == REMOVE_M) {
260			mode = REMOVE_M;
261		} else if (commands && commands[num_commands - 1].mode == ENABLE_M) {
262			mode = ENABLE_M;
263		} else if (commands && commands[num_commands - 1].mode == DISABLE_M) {
264			mode = DISABLE_M;
265		} else {
266			fprintf(stderr, "unknown additional arguments:\n");
267			while (optind < argc)
268				fprintf(stderr, " %s", argv[optind++]);
269			fprintf(stderr, "\n\n");
270			usage(argv[0]);
271			exit(1);
272		}
273		while (optind < argc)
274			set_mode(mode, argv[optind++]);
275	}
276}
277
278int main(int argc, char *argv[])
279{
280	int i, commit = 0;
281	int result;
282	int status = EXIT_FAILURE;
283
284	create_signal_handlers();
285	parse_command_line(argc, argv);
286
287	if (build)
288		commit = 1;
289
290	sh = semanage_handle_create();
291	if (!sh) {
292		fprintf(stderr, "%s:  Could not create semanage handle\n",
293			argv[0]);
294		goto cleanup_nohandle;
295	}
296
297	if (store) {
298		/* Set the store we want to connect to, before connecting.
299		 * this will always set a direct connection now, an additional
300		 * option will need to be used later to specify a policy server
301		 * location */
302		semanage_select_store(sh, store, SEMANAGE_CON_DIRECT);
303	}
304
305	/* if installing base module create store if necessary, for bootstrapping */
306	semanage_set_create_store(sh, create_store);
307
308	if (!create_store) {
309		if (!semanage_is_managed(sh)) {
310			fprintf(stderr,
311				"%s: SELinux policy is not managed or store cannot be accessed.\n",
312				argv[0]);
313			goto cleanup;
314		}
315
316		if (semanage_access_check(sh) < SEMANAGE_CAN_READ) {
317			fprintf(stderr, "%s: Cannot read policy store.\n",
318				argv[0]);
319			goto cleanup;
320		}
321	}
322
323	if ((result = semanage_connect(sh)) < 0) {
324		fprintf(stderr, "%s:  Could not connect to policy handler\n",
325			argv[0]);
326		goto cleanup;
327	}
328
329	if (reload) {
330		if ((result = semanage_reload_policy(sh)) < 0) {
331			fprintf(stderr, "%s:  Could not reload policy\n",
332				argv[0]);
333			goto cleanup;
334		}
335	}
336
337	if (build) {
338		if ((result = semanage_begin_transaction(sh)) < 0) {
339			fprintf(stderr, "%s:  Could not begin transaction:  %s\n",
340				argv[0], errno ? strerror(errno) : "");
341			goto cleanup;
342		}
343	}
344
345	for (i = 0; i < num_commands; i++) {
346		enum client_modes mode = commands[i].mode;
347		char *mode_arg = commands[i].arg;
348		switch (mode) {
349		case INSTALL_M:{
350				if (verbose) {
351					printf
352					    ("Attempting to install module '%s':\n",
353					     mode_arg);
354				}
355				result =
356				    semanage_module_install_file(sh, mode_arg);
357				break;
358			}
359		case UPGRADE_M:{
360				if (verbose) {
361					printf
362					    ("Attempting to upgrade module '%s':\n",
363					     mode_arg);
364				}
365				result =
366				    semanage_module_upgrade_file(sh, mode_arg);
367				break;
368			}
369		case BASE_M:{
370				if (verbose) {
371					printf
372					    ("Attempting to install base module '%s':\n",
373					     mode_arg);
374				}
375				result =
376				    semanage_module_install_base_file(sh, mode_arg);
377				break;
378			}
379		case ENABLE_M:{
380				if (verbose) {
381					printf
382					    ("Attempting to enable module '%s':\n",
383					     mode_arg);
384				}
385				result = semanage_module_enable(sh, mode_arg);
386				if ( result == -2 ) {
387					continue;
388				}
389				break;
390			}
391		case DISABLE_M:{
392				if (verbose) {
393					printf
394					    ("Attempting to disable module '%s':\n",
395					     mode_arg);
396				}
397				result = semanage_module_disable(sh, mode_arg);
398				if ( result == -2 ) {
399					continue;
400				}
401				break;
402			}
403		case REMOVE_M:{
404				if (verbose) {
405					printf
406					    ("Attempting to remove module '%s':\n",
407					     mode_arg);
408				}
409				result = semanage_module_remove(sh, mode_arg);
410				if ( result == -2 ) {
411					continue;
412				}
413				break;
414			}
415		case LIST_M:{
416				semanage_module_info_t *modinfo;
417				int num_modules;
418				if (verbose) {
419					printf
420					    ("Attempting to list active modules:\n");
421				}
422				if ((result =
423				     semanage_module_list(sh, &modinfo,
424							  &num_modules)) >= 0) {
425					int j;
426					if (num_modules == 0) {
427						printf("No modules.\n");
428					}
429					for (j = 0; j < num_modules; j++) {
430						semanage_module_info_t *m =
431						    semanage_module_list_nth
432						    (modinfo, j);
433						printf("%s\t%s\t%s\n",
434						       semanage_module_get_name
435						       (m),
436						       semanage_module_get_version
437						       (m),
438						       (semanage_module_get_enabled(m) ? "" : "Disabled"));
439						semanage_module_info_datum_destroy
440						    (m);
441					}
442					free(modinfo);
443				}
444				break;
445			}
446		default:{
447				fprintf(stderr,
448					"%s:  Unknown mode specified.\n",
449					argv[0]);
450				usage(argv[0]);
451				goto cleanup;
452			}
453		}
454		commit += do_commit[mode];
455		if (result < 0) {
456			fprintf(stderr, "%s:  Failed on %s!\n", argv[0],
457				mode_arg ? : "list");
458			goto cleanup;
459		} else if (verbose) {
460			printf("Ok: return value of %d.\n", result);
461		}
462	}
463
464	if (commit) {
465		if (verbose)
466			printf("Committing changes:\n");
467		if (no_reload)
468			semanage_set_reload(sh, 0);
469		if (build)
470			semanage_set_rebuild(sh, 1);
471		if (disable_dontaudit)
472			semanage_set_disable_dontaudit(sh, 1);
473		else if (build)
474			semanage_set_disable_dontaudit(sh, 0);
475		if (preserve_tunables)
476			semanage_set_preserve_tunables(sh, 1);
477
478		result = semanage_commit(sh);
479	}
480
481	if (result < 0) {
482		fprintf(stderr, "%s:  Failed!\n", argv[0]);
483		goto cleanup;
484	} else if (commit && verbose) {
485		printf("Ok: transaction number %d.\n", result);
486	}
487
488	if (semanage_disconnect(sh) < 0) {
489		fprintf(stderr, "%s:  Error disconnecting\n", argv[0]);
490		goto cleanup;
491	}
492	status = EXIT_SUCCESS;
493
494      cleanup:
495	if (semanage_is_connected(sh)) {
496		if (semanage_disconnect(sh) < 0) {
497			fprintf(stderr, "%s:  Error disconnecting\n", argv[0]);
498		}
499	}
500	semanage_handle_destroy(sh);
501
502      cleanup_nohandle:
503	cleanup();
504	exit(status);
505}
506