selinux/policycoreutils/setsebool/setsebool.c
Topi Miettinen 98fd24d6b4 setsebool: report errors from commit phase
In case there are errors when committing changes to booleans, the
errors may not be reported to user except by nonzero exit status. With
"setsebool -V" it's possible to see errors from commit phase, but
otherwise the unfixed command is silent:

 # setsebool -V -P secure_mode_insmod=off
libsemanage.semanage_install_final_tmp: Could not copy /var/lib/selinux/final/default/contexts/files/file_contexts to /etc/selinux/default/contexts/files/file_contexts. (Read-only file system).
libsemanage.semanage_install_final_tmp: Could not copy /var/lib/selinux/final/default/contexts/files/file_contexts to /etc/selinux/default/contexts/files/file_contexts. (Read-only file system).

Fixed version alerts the user about problems even without -V:
 # setsebool -P secure_mode_insmod=off
Failed to commit changes to booleans: Read-only file system

Signed-off-by: Topi Miettinen <toiwoton@gmail.com>
2020-04-28 09:26:26 +02:00

312 lines
6.7 KiB
C

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <syslog.h>
#include <getopt.h>
#include <pwd.h>
#include <selinux/selinux.h>
#include <semanage/handle.h>
#include <semanage/debug.h>
#include <semanage/booleans_policy.h>
#include <semanage/booleans_local.h>
#include <semanage/booleans_active.h>
#include <semanage/boolean_record.h>
#include <errno.h>
int permanent = 0;
int no_reload = 0;
int verbose = 0;
int setbool(char **list, size_t start, size_t end);
static __attribute__((__noreturn__)) void usage(void)
{
fputs
("\nUsage: setsebool [ -NPV ] boolean value | bool1=val1 bool2=val2...\n\n",
stderr);
exit(1);
}
int main(int argc, char **argv)
{
size_t rc;
int clflag; /* holds codes for command line flags */
if (argc < 2)
usage();
while (1) {
clflag = getopt(argc, argv, "PNV");
if (clflag == -1)
break;
switch (clflag) {
case 'P':
permanent = 1;
break;
case 'N':
no_reload = 1;
break;
case 'V':
verbose = 1;
break;
default:
usage();
break;
}
}
if (argc - optind < 1) {
fprintf(stderr, "Error: boolean name required\n");
usage();
}
/* Check to see which way we are being called. If a '=' is passed,
we'll enforce the list syntax. If not we'll enforce the original
syntax for backward compatibility. */
if (strchr(argv[optind], '=') == 0) {
int len;
char *bool_list[1];
if ((argc - optind) != 2)
usage();
/* Add 1 for the '=' */
len = strlen(argv[optind]) + strlen(argv[optind + 1]) + 2;
bool_list[0] = (char *)malloc(len);
if (bool_list[0] == NULL) {
fputs("Out of memory - aborting\n", stderr);
return 1;
}
snprintf(bool_list[0], len, "%s=%s", argv[optind],
argv[optind + 1]);
rc = setbool(bool_list, 0, 1);
free(bool_list[0]);
} else
rc = setbool(argv, optind, argc);
return rc;
}
/* Apply temporal boolean changes to policy via libselinux */
static int selinux_set_boolean_list(size_t boolcnt,
SELboolean * boollist)
{
if (security_set_boolean_list(boolcnt, boollist, 0)) {
if (errno == ENOENT)
fprintf(stderr, "Could not change active booleans: "
"Invalid boolean\n");
else if (errno) {
if (getuid() == 0) {
perror("Could not change active booleans");
} else {
perror("Could not change active booleans. Please try as root");
}
}
return -1;
}
return 0;
}
/* Apply permanent boolean changes to policy via libsemanage */
static int semanage_set_boolean_list(size_t boolcnt,
SELboolean * boollist)
{
size_t j;
semanage_handle_t *handle = NULL;
semanage_bool_t *boolean = NULL;
semanage_bool_key_t *bool_key = NULL;
int managed;
int result;
int enabled = is_selinux_enabled();
handle = semanage_handle_create();
if (handle == NULL) {
fprintf(stderr, "Could not create semanage library handle\n");
goto err;
}
if (!verbose) {
semanage_msg_set_callback(handle, NULL, NULL);
}
managed = semanage_is_managed(handle);
if (managed < 0) {
fprintf(stderr,
"Error when checking whether policy is managed\n");
goto err;
} else if (managed == 0) {
if (getuid() == 0) {
fprintf(stderr,
"Cannot set persistent booleans without managed policy.\n");
} else {
fprintf(stderr,
"Cannot set persistent booleans, please try as root.\n");
}
goto err;
}
if (semanage_connect(handle) < 0)
goto err;
if (semanage_begin_transaction(handle) < 0)
goto err;
for (j = 0; j < boolcnt; j++) {
if (semanage_bool_create(handle, &boolean) < 0)
goto err;
if (semanage_bool_set_name(handle, boolean, boollist[j].name) <
0)
goto err;
semanage_bool_set_value(boolean, boollist[j].value);
if (semanage_bool_key_extract(handle, boolean, &bool_key) < 0)
goto err;
semanage_bool_exists(handle, bool_key, &result);
if (!result) {
semanage_bool_exists_local(handle, bool_key, &result);
if (!result) {
fprintf(stderr, "Boolean %s is not defined\n", boollist[j].name);
goto err;
}
}
if (semanage_bool_modify_local(handle, bool_key,
boolean) < 0)
goto err;
if (enabled && semanage_bool_set_active(handle, bool_key, boolean) < 0) {
fprintf(stderr, "Failed to change boolean %s: %m\n",
boollist[j].name);
goto err;
}
semanage_bool_key_free(bool_key);
semanage_bool_free(boolean);
bool_key = NULL;
boolean = NULL;
}
if (no_reload)
semanage_set_reload(handle, 0);
if (semanage_commit(handle) < 0) {
fprintf(stderr, "Failed to commit changes to booleans: %m\n");
goto err;
}
semanage_disconnect(handle);
semanage_handle_destroy(handle);
return 0;
err:
semanage_bool_key_free(bool_key);
semanage_bool_free(boolean);
semanage_handle_destroy(handle);
return -1;
}
/* Given an array of strings in the form "boolname=value", a start index,
and a finish index...walk the list and set the bool. */
int setbool(char **list, size_t start, size_t end)
{
char *name, *value_ptr;
int j = 0, value;
size_t i = start;
size_t boolcnt = end - start;
struct passwd *pwd;
SELboolean *vallist = calloc(boolcnt, sizeof(SELboolean));
if (!vallist)
goto omem;
while (i < end) {
name = list[i];
value_ptr = strchr(list[i], '=');
if (value_ptr == NULL) {
fprintf(stderr,
"setsebool: '=' not found in boolean expression %s\n",
list[i]);
goto err;
}
*value_ptr = '\0';
value_ptr++;
if (strcmp(value_ptr, "1") == 0 ||
strcasecmp(value_ptr, "true") == 0 ||
strcasecmp(value_ptr, "on") == 0)
value = 1;
else if (strcmp(value_ptr, "0") == 0 ||
strcasecmp(value_ptr, "false") == 0 ||
strcasecmp(value_ptr, "off") == 0)
value = 0;
else {
fprintf(stderr, "setsebool: illegal value "
"%s for boolean %s\n", value_ptr, name);
goto err;
}
vallist[j].value = value;
vallist[j].name = strdup(name);
if (!vallist[j].name)
goto omem;
i++;
j++;
/* Now put it back */
value_ptr--;
*value_ptr = '=';
}
if (permanent) {
if (semanage_set_boolean_list(boolcnt, vallist) < 0)
goto err;
} else {
if (selinux_set_boolean_list(boolcnt, vallist) < 0)
goto err;
}
/* Now log what was done */
pwd = getpwuid(getuid());
i = start;
while (i < end) {
name = list[i];
value_ptr = strchr(name, '=');
*value_ptr = '\0';
value_ptr++;
if (pwd && pwd->pw_name)
syslog(LOG_NOTICE,
"The %s policy boolean was changed to %s by %s",
name, value_ptr, pwd->pw_name);
else
syslog(LOG_NOTICE,
"The %s policy boolean was changed to %s by uid:%d",
name, value_ptr, getuid());
i++;
}
for (i = 0; i < boolcnt; i++)
free(vallist[i].name);
free(vallist);
return 0;
omem:
fprintf(stderr, "setsebool: out of memory");
err:
if (vallist) {
for (i = 0; i < boolcnt; i++)
free(vallist[i].name);
free(vallist);
}
return -1;
}