blob: 764d47694629b308d2ce6109abe59127953bc7a6 [file] [log] [blame]
/* Authors: Karl MacMillan <kmacmillan@tresys.com>
* Joshua Brindle <jbrindle@tresys.com>
* Jason Tang <jtang@tresys.com>
*
* Copyright (C) 2004-2005 Tresys Technology, LLC
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*/
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <semanage/modules.h>
enum client_modes { NO_MODE, INSTALL_M, UPGRADE_M, BASE_M, ENABLE_M, DISABLE_M, REMOVE_M,
LIST_M, RELOAD
};
/* list of modes in which one ought to commit afterwards */
static const int do_commit[] = {
0, 1, 1, 1, 1, 1, 1,
0, 0
};
struct command {
enum client_modes mode;
char *arg;
};
static struct command *commands = NULL;
static int num_commands = 0;
/* options given on command line */
static int verbose;
static int reload;
static int no_reload;
static int create_store;
static int build;
static int disable_dontaudit;
static int preserve_tunables;
static semanage_handle_t *sh = NULL;
static char *store;
extern char *optarg;
extern int optind;
static void cleanup(void)
{
while (--num_commands >= 0) {
free(commands[num_commands].arg);
}
free(commands);
}
/* Signal handlers. */
static void handle_signal(int sig_num)
{
if (sig_num == SIGINT || sig_num == SIGQUIT || sig_num == SIGTERM) {
/* catch these signals, and then drop them */
}
}
static void set_store(char *storename)
{
/* For now this only supports a store name, later on this
* should support an address for a remote connection */
if ((store = strdup(storename)) == NULL) {
fprintf(stderr, "Out of memory!\n");
goto bad;
}
return;
bad:
cleanup();
exit(1);
}
/* Establish signal handlers for the process. */
static void create_signal_handlers(void)
{
if (signal(SIGINT, handle_signal) == SIG_ERR ||
signal(SIGQUIT, handle_signal) == SIG_ERR ||
signal(SIGTERM, handle_signal) == SIG_ERR) {
fprintf(stderr, "Could not set up signal handler.\n");
exit(255);
}
}
static void usage(char *progname)
{
printf("usage: %s [options]... MODE [MODES]...\n", progname);
printf("Manage SELinux policy modules.\n");
printf("MODES:\n");
printf(" -R, --reload reload policy\n");
printf(" -B, --build build and reload policy\n");
printf(" -i,--install=MODULE_PKG install a new module\n");
printf(" -u,--upgrade=MODULE_PKG upgrade existing module\n");
printf(" -b,--base=MODULE_PKG install new base module\n");
printf(" -e,--enable=MODULE_PKG enable existing module\n");
printf(" -d,--disable=MODULE_PKG disable existing module\n");
printf(" -r,--remove=MODULE_NAME remove existing module\n");
printf
(" -l,--list-modules display list of installed modules\n");
printf("Other options:\n");
printf(" -s,--store name of the store to operate on\n");
printf(" -n,--noreload do not reload policy after commit\n");
printf(" -h,--help print this message and quit\n");
printf(" -v,--verbose be verbose\n");
printf(" -D,--disable_dontaudit Remove dontaudits from policy\n");
printf(" -P,--preserve_tunables Preserve tunables in policy\n");
printf(" -p,--path use an alternate path for the policy root\n");
}
/* Sets the global mode variable to new_mode, but only if no other
* mode has been given. */
static void set_mode(enum client_modes new_mode, char *arg)
{
struct command *c;
char *s;
if ((c = realloc(commands, sizeof(*c) * (num_commands + 1))) == NULL) {
fprintf(stderr, "Out of memory!\n");
cleanup();
exit(1);
}
commands = c;
commands[num_commands].mode = new_mode;
commands[num_commands].arg = NULL;
num_commands++;
if (arg != NULL) {
if ((s = strdup(arg)) == NULL) {
fprintf(stderr, "Out of memory!\n");
cleanup();
exit(1);
}
commands[num_commands - 1].arg = s;
}
}
/* Parse command line and set global options. */
static void parse_command_line(int argc, char **argv)
{
static struct option opts[] = {
{"store", required_argument, NULL, 's'},
{"base", required_argument, NULL, 'b'},
{"help", 0, NULL, 'h'},
{"install", required_argument, NULL, 'i'},
{"list-modules", 0, NULL, 'l'},
{"verbose", 0, NULL, 'v'},
{"enable", required_argument, NULL, 'e'},
{"disable", required_argument, NULL, 'd'},
{"remove", required_argument, NULL, 'r'},
{"upgrade", required_argument, NULL, 'u'},
{"reload", 0, NULL, 'R'},
{"noreload", 0, NULL, 'n'},
{"build", 0, NULL, 'B'},
{"disable_dontaudit", 0, NULL, 'D'},
{"preserve_tunables", 0, NULL, 'P'},
{"path", required_argument, NULL, 'p'},
{NULL, 0, NULL, 0}
};
int i;
verbose = 0;
reload = 0;
no_reload = 0;
create_store = 0;
while ((i =
getopt_long(argc, argv, "p:s:b:hi:lvqe:d:r:u:RnBDP", opts,
NULL)) != -1) {
switch (i) {
case 'b':
set_mode(BASE_M, optarg);
create_store = 1;
break;
case 'h':
usage(argv[0]);
exit(0);
case 'i':
set_mode(INSTALL_M, optarg);
break;
case 'l':
set_mode(LIST_M, NULL);
break;
case 'v':
verbose = 1;
break;
case 'e':
set_mode(ENABLE_M, optarg);
break;
case 'd':
set_mode(DISABLE_M, optarg);
break;
case 'r':
set_mode(REMOVE_M, optarg);
break;
case 'p':
semanage_set_root(optarg);
break;
case 'u':
set_mode(UPGRADE_M, optarg);
break;
case 's':
set_store(optarg);
break;
case 'R':
reload = 1;
break;
case 'n':
no_reload = 1;
break;
case 'B':
build = 1;
break;
case 'D':
disable_dontaudit = 1;
break;
case 'P':
preserve_tunables = 1;
break;
case '?':
default:{
usage(argv[0]);
exit(1);
}
}
}
if ((build || reload) && num_commands) {
fprintf(stderr,
"build or reload should not be used with other commands\n");
usage(argv[0]);
exit(1);
}
if (num_commands == 0 && reload == 0 && build == 0) {
fprintf(stderr, "At least one mode must be specified.\n");
usage(argv[0]);
exit(1);
}
if (optind < argc) {
int mode;
/* if -i/u/r was the last command treat any remaining
* arguments as args. Will allow 'semodule -i *.pp' to
* work as expected.
*/
if (commands && commands[num_commands - 1].mode == INSTALL_M) {
mode = INSTALL_M;
} else if (commands && commands[num_commands - 1].mode == UPGRADE_M) {
mode = UPGRADE_M;
} else if (commands && commands[num_commands - 1].mode == REMOVE_M) {
mode = REMOVE_M;
} else if (commands && commands[num_commands - 1].mode == ENABLE_M) {
mode = ENABLE_M;
} else if (commands && commands[num_commands - 1].mode == DISABLE_M) {
mode = DISABLE_M;
} else {
fprintf(stderr, "unknown additional arguments:\n");
while (optind < argc)
fprintf(stderr, " %s", argv[optind++]);
fprintf(stderr, "\n\n");
usage(argv[0]);
exit(1);
}
while (optind < argc)
set_mode(mode, argv[optind++]);
}
}
int main(int argc, char *argv[])
{
int i, commit = 0;
int result;
int status = EXIT_FAILURE;
create_signal_handlers();
parse_command_line(argc, argv);
if (build)
commit = 1;
sh = semanage_handle_create();
if (!sh) {
fprintf(stderr, "%s: Could not create semanage handle\n",
argv[0]);
goto cleanup_nohandle;
}
if (store) {
/* Set the store we want to connect to, before connecting.
* this will always set a direct connection now, an additional
* option will need to be used later to specify a policy server
* location */
semanage_select_store(sh, store, SEMANAGE_CON_DIRECT);
}
/* if installing base module create store if necessary, for bootstrapping */
semanage_set_create_store(sh, create_store);
if (!create_store) {
if (!semanage_is_managed(sh)) {
fprintf(stderr,
"%s: SELinux policy is not managed or store cannot be accessed.\n",
argv[0]);
goto cleanup;
}
if (semanage_access_check(sh) < SEMANAGE_CAN_READ) {
fprintf(stderr, "%s: Cannot read policy store.\n",
argv[0]);
goto cleanup;
}
}
if ((result = semanage_connect(sh)) < 0) {
fprintf(stderr, "%s: Could not connect to policy handler\n",
argv[0]);
goto cleanup;
}
if (reload) {
if ((result = semanage_reload_policy(sh)) < 0) {
fprintf(stderr, "%s: Could not reload policy\n",
argv[0]);
goto cleanup;
}
}
if (build) {
if ((result = semanage_begin_transaction(sh)) < 0) {
fprintf(stderr, "%s: Could not begin transaction: %s\n",
argv[0], errno ? strerror(errno) : "");
goto cleanup;
}
}
for (i = 0; i < num_commands; i++) {
enum client_modes mode = commands[i].mode;
char *mode_arg = commands[i].arg;
switch (mode) {
case INSTALL_M:{
if (verbose) {
printf
("Attempting to install module '%s':\n",
mode_arg);
}
result =
semanage_module_install_file(sh, mode_arg);
break;
}
case UPGRADE_M:{
if (verbose) {
printf
("Attempting to upgrade module '%s':\n",
mode_arg);
}
result =
semanage_module_upgrade_file(sh, mode_arg);
break;
}
case BASE_M:{
if (verbose) {
printf
("Attempting to install base module '%s':\n",
mode_arg);
}
result =
semanage_module_install_base_file(sh, mode_arg);
break;
}
case ENABLE_M:{
if (verbose) {
printf
("Attempting to enable module '%s':\n",
mode_arg);
}
result = semanage_module_enable(sh, mode_arg);
if ( result == -2 ) {
continue;
}
break;
}
case DISABLE_M:{
if (verbose) {
printf
("Attempting to disable module '%s':\n",
mode_arg);
}
result = semanage_module_disable(sh, mode_arg);
if ( result == -2 ) {
continue;
}
break;
}
case REMOVE_M:{
if (verbose) {
printf
("Attempting to remove module '%s':\n",
mode_arg);
}
result = semanage_module_remove(sh, mode_arg);
if ( result == -2 ) {
continue;
}
break;
}
case LIST_M:{
semanage_module_info_t *modinfo;
int num_modules;
if (verbose) {
printf
("Attempting to list active modules:\n");
}
if ((result =
semanage_module_list(sh, &modinfo,
&num_modules)) >= 0) {
int j;
if (num_modules == 0) {
printf("No modules.\n");
}
for (j = 0; j < num_modules; j++) {
semanage_module_info_t *m =
semanage_module_list_nth
(modinfo, j);
printf("%s\t%s\t%s\n",
semanage_module_get_name
(m),
semanage_module_get_version
(m),
(semanage_module_get_enabled(m) ? "" : "Disabled"));
semanage_module_info_datum_destroy
(m);
}
free(modinfo);
}
break;
}
default:{
fprintf(stderr,
"%s: Unknown mode specified.\n",
argv[0]);
usage(argv[0]);
goto cleanup;
}
}
commit += do_commit[mode];
if (result < 0) {
fprintf(stderr, "%s: Failed on %s!\n", argv[0],
mode_arg ? : "list");
goto cleanup;
} else if (verbose) {
printf("Ok: return value of %d.\n", result);
}
}
if (commit) {
if (verbose)
printf("Committing changes:\n");
if (no_reload)
semanage_set_reload(sh, 0);
if (build)
semanage_set_rebuild(sh, 1);
if (disable_dontaudit)
semanage_set_disable_dontaudit(sh, 1);
else if (build)
semanage_set_disable_dontaudit(sh, 0);
if (preserve_tunables)
semanage_set_preserve_tunables(sh, 1);
result = semanage_commit(sh);
}
if (result < 0) {
fprintf(stderr, "%s: Failed!\n", argv[0]);
goto cleanup;
} else if (commit && verbose) {
printf("Ok: transaction number %d.\n", result);
}
if (semanage_disconnect(sh) < 0) {
fprintf(stderr, "%s: Error disconnecting\n", argv[0]);
goto cleanup;
}
status = EXIT_SUCCESS;
cleanup:
if (semanage_is_connected(sh)) {
if (semanage_disconnect(sh) < 0) {
fprintf(stderr, "%s: Error disconnecting\n", argv[0]);
}
}
semanage_handle_destroy(sh);
cleanup_nohandle:
cleanup();
exit(status);
}