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