diff --git a/coreutils/id.c b/coreutils/id.c index a0364675f..e183402fa 100644 --- a/coreutils/id.c +++ b/coreutils/id.c @@ -89,7 +89,7 @@ int id_main(int argc, char **argv) if (flags & JUST_CONTEXT) { selinux_or_die(); if (argc - optind == 1) { - bb_error_msg_and_die("can't print security context when user specified"); + bb_error_msg_and_die("user name can't be passed with -Z"); } if (getcon(&scontext)) { diff --git a/coreutils/install.c b/coreutils/install.c index 83facad9d..1f65407b1 100644 --- a/coreutils/install.c +++ b/coreutils/install.c @@ -1,6 +1,7 @@ /* vi: set sw=4 ts=4: */ /* - * Copyright (C) 2003 by Glenn McGrath + * Copyright (C) 2003 by Glenn McGrath + * SELinux support: by Yuichi Nakamura * * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. * diff --git a/include/libbb.h b/include/libbb.h index e07fa7587..69652b666 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -41,6 +41,7 @@ #if ENABLE_SELINUX #include +#include #endif #if ENABLE_LOCALE_SUPPORT @@ -598,6 +599,8 @@ extern void run_shell(const char *shell, int loginshell, const char *command, co #if ENABLE_SELINUX extern void renew_current_security_context(void); extern void set_current_security_context(security_context_t sid); +extern context_t set_security_context_component(security_context_t cur_context, + char *user, char *role, char *type, char *range); #endif extern void selinux_or_die(void); extern int restricted_shell(const char *shell); diff --git a/libbb/Kbuild b/libbb/Kbuild index a53b17f44..ffded6a68 100644 --- a/libbb/Kbuild +++ b/libbb/Kbuild @@ -106,6 +106,7 @@ lib-$(CONFIG_SU) += correct_password.o lib-$(CONFIG_LOGIN) += correct_password.o lib-$(CONFIG_DF) += find_mount_point.o lib-$(CONFIG_MKFS_MINIX) += find_mount_point.o +lib-$(CONFIG_SELINUX) += selinux_common.o # We shouldn't build xregcomp.c if we don't need it - this ensures we don't # require regex.h to be in the include dir even if we don't need it thereby diff --git a/libbb/selinux_common.c b/libbb/selinux_common.c new file mode 100644 index 000000000..70d63a465 --- /dev/null +++ b/libbb/selinux_common.c @@ -0,0 +1,30 @@ +/* + * libbb/selinux_common.c + * -- common SELinux utility functions + * + * Copyright 2007 KaiGai Kohei + */ +#include "busybox.h" +#include + +context_t set_security_context_component(security_context_t cur_context, + char *user, char *role, char *type, char *range) +{ + context_t con = context_new(cur_context); + if (!con) + return NULL; + + if (user && context_user_set(con, user)) + goto error; + if (type && context_type_set(con, type)) + goto error; + if (range && context_range_set(con, range)) + goto error; + if (role && context_role_set(con, role)) + goto error; + return con; + +error: + context_free(con); + return NULL; +} diff --git a/selinux/chcon.c b/selinux/chcon.c new file mode 100644 index 000000000..d7ff40816 --- /dev/null +++ b/selinux/chcon.c @@ -0,0 +1,175 @@ +/* + * chcon -- change security context, based on coreutils-5.97-13 + * + * Port to busybox: KaiGai Kohei + * + * Copyright (C) 2006 - 2007 KaiGai Kohei + */ +#include "busybox.h" +#include +#include + +#define OPT_RECURSIVE (1<<0) /* 'R' */ +#define OPT_CHANHES (1<<1) /* 'c' */ +#define OPT_NODEREFERENCE (1<<2) /* 'h' */ +#define OPT_QUIET (1<<3) /* 'f' */ +#define OPT_USER (1<<4) /* 'u' */ +#define OPT_ROLE (1<<5) /* 'r' */ +#define OPT_TYPE (1<<6) /* 't' */ +#define OPT_RANGE (1<<7) /* 'l' */ +#define OPT_VERBOSE (1<<8) /* 'v' */ +#define OPT_REFERENCE ((1<<9) * ENABLE_FEATURE_CHCON_LONG_OPTIONS) +#define OPT_COMPONENT_SPECIFIED (OPT_USER | OPT_ROLE | OPT_TYPE | OPT_RANGE) + +static char *user = NULL; +static char *role = NULL; +static char *type = NULL; +static char *range = NULL; +static char *specified_context = NULL; + +static int change_filedir_context(const char *fname, struct stat *stbuf, void *userData, int depth) +{ + context_t context = NULL; + security_context_t file_context = NULL; + security_context_t context_string; + int rc = FALSE; + int status = 0; + + if (option_mask32 & OPT_NODEREFERENCE) { + status = lgetfilecon(fname, &file_context); + } else { + status = getfilecon(fname, &file_context); + } + if (status < 0 && errno != ENODATA) { + if ((option_mask32 & OPT_QUIET) == 0) + bb_error_msg("cannot obtain security context: %s", fname); + goto skip; + } + + if (file_context == NULL && specified_context == NULL) { + bb_error_msg("cannot apply partial context to unlabeled file %s", fname); + goto skip; + } + + if (specified_context == NULL) { + context = set_security_context_component(file_context, + user, role, type, range); + if (!context) { + bb_error_msg("cannot compute security context from %s", file_context); + goto skip; + } + } else { + context = context_new(specified_context); + if (!context) { + bb_error_msg("invalid context: %s", specified_context); + goto skip; + } + } + + context_string = context_str(context); + if (!context_string) { + bb_error_msg("cannot obtain security context in text expression"); + goto skip; + } + + if (file_context == NULL || strcmp(context_string, file_context) != 0) { + int fail; + + if (option_mask32 & OPT_NODEREFERENCE) { + fail = lsetfilecon(fname, context_string); + } else { + fail = setfilecon(fname, context_string); + } + if ((option_mask32 & OPT_VERBOSE) || ((option_mask32 & OPT_CHANHES) && !fail)) { + printf(!fail + ? "context of %s changed to %s\n" + : "failed to change context of %s to %s\n", + fname, context_string); + } + if (!fail) { + rc = TRUE; + } else if ((option_mask32 & OPT_QUIET) == 0) { + bb_error_msg("failed to change context of %s to %s", + fname, context_string); + } + } else if (option_mask32 & OPT_VERBOSE) { + printf("context of %s retained as %s\n", fname, context_string); + rc = TRUE; + } +skip: + /* FIXME: aren't these work ok on NULL ptr? Remove if() then */ + if (context) + context_free(context); + if (file_context) + freecon(file_context); + + return rc; +} + +#if ENABLE_FEATURE_CHCON_LONG_OPTIONS +static struct option chcon_options[] = { + { "recursive", 0, NULL, 'R' }, + { "changes", 0, NULL, 'c' }, + { "no-dereference", 0, NULL, 'h' }, + { "silent", 0, NULL, 'f' }, + { "quiet", 0, NULL, 'f' }, + { "user", 1, NULL, 'u' }, + { "role", 1, NULL, 'r' }, + { "type", 1, NULL, 't' }, + { "range", 1, NULL, 'l' }, + { "verbose", 0, NULL, 'v' }, + { "reference", 1, NULL, 0xff }, /* no short option */ + { NULL, 0, NULL, 0 }, +}; +#endif + +int chcon_main(int argc, char *argv[]); +int chcon_main(int argc, char *argv[]) +{ + char *reference_file; + char *fname; + int i, errors = 0; + +#if ENABLE_FEATURE_CHCON_LONG_OPTIONS + applet_long_options = chcon_options; +#endif + opt_complementary = "-1" /* at least 1 param */ + ":?:f--v:v--f" /* 'verbose' and 'quiet' are exclusive */ + ":\xff--urtl:u--\xff:r--\xff:t--\xff:l--\xff"; + getopt32(argc, argv, "Rchf:u:r:t:l:v", + &user, &role, &type, &range, &reference_file); + argv += optind; + +#if ENABLE_FEATURE_CHCON_LONG_OPTIONS + if (option_mask32 & OPT_REFERENCE) { + /* FIXME: lgetfilecon() should be used when '-h' is specified. + But current implementation follows the original one. */ + if (getfilecon(reference_file, &specified_context) < 0) + bb_perror_msg_and_die("getfilecon('%s') failed", reference_file); + } else +#endif + if ((option_mask32 & OPT_COMPONENT_SPECIFIED) == 0) { + specified_context = *argv++; + /* specified_context is never NULL - + * "-1" in opt_complementary prevents this. */ + if (!argv[0]) + bb_error_msg_and_die("too few arguments"); + } + + for (i = 0; (fname = argv[i]) != NULL; i++) { + int fname_len = strlen(fname); + while (fname_len > 1 && fname[fname_len - 1] == '/') + fname_len--; + fname[fname_len] = '\0'; + + if (recursive_action(fname, + option_mask32 & OPT_RECURSIVE, + FALSE, /* followLinks */ + FALSE, /* depthFirst */ + change_filedir_context, + change_filedir_context, + NULL, 0) != TRUE) + errors = 1; + } + return errors; +} diff --git a/selinux/runcon.c b/selinux/runcon.c new file mode 100644 index 000000000..24e436feb --- /dev/null +++ b/selinux/runcon.c @@ -0,0 +1,137 @@ +/* + * runcon [ context | + * ( [ -c ] [ -r role ] [-t type] [ -u user ] [ -l levelrange ] ) + * command [arg1 [arg2 ...] ] + * + * attempt to run the specified command with the specified context. + * + * -r role : use the current context with the specified role + * -t type : use the current context with the specified type + * -u user : use the current context with the specified user + * -l level : use the current context with the specified level range + * -c : compute process transition context before modifying + * + * Contexts are interpreted as follows: + * + * Number of MLS + * components system? + * + * 1 - type + * 2 - role:type + * 3 Y role:type:range + * 3 N user:role:type + * 4 Y user:role:type:range + * 4 N error + * + * Port to busybox: KaiGai Kohei + * - based on coreutils-5.97 (in Fedora Core 6) + */ +#include "busybox.h" +#include +#include +#include + +static context_t runcon_compute_new_context(char *user, char *role, char *type, char *range, + char *command, int compute_trans) +{ + context_t con; + security_context_t cur_context; + + if (getcon(&cur_context)) + bb_error_msg_and_die("cannot get current context"); + + if (compute_trans) { + security_context_t file_context, new_context; + + if (getfilecon(command, &file_context) < 0) + bb_error_msg_and_die("cannot retrieve attributes of '%s'", + command); + if (security_compute_create(cur_context, file_context, + SECCLASS_PROCESS, &new_context)) + bb_error_msg_and_die("unable to compute a new context"); + cur_context = new_context; + } + + con = context_new(cur_context); + if (!con) + bb_error_msg_and_die("'%s' is not a valid context", cur_context); + if (user && context_user_set(con, user)) + bb_error_msg_and_die("failed to set new user '%s'", user); + if (type && context_type_set(con, type)) + bb_error_msg_and_die("failed to set new type '%s'", type); + if (range && context_range_set(con, range)) + bb_error_msg_and_die("failed to set new range '%s'", range); + if (role && context_role_set(con, role)) + bb_error_msg_and_die("failed to set new role '%s'", role); + + return con; +} + +#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS +static const struct option runcon_options[] = { + { "user", 1, NULL, 'u' }, + { "role", 1, NULL, 'r' }, + { "type", 1, NULL, 't' }, + { "range", 1, NULL, 'l' }, + { "compute", 0, NULL, 'c' }, + { "help", 0, NULL, 'h' }, + { NULL, 0, NULL, 0 }, +}; +#endif + +#define OPTS_ROLE (1<<0) /* r */ +#define OPTS_TYPE (1<<1) /* t */ +#define OPTS_USER (1<<2) /* u */ +#define OPTS_RANGE (1<<3) /* l */ +#define OPTS_COMPUTE (1<<4) /* c */ +#define OPTS_HELP (1<<5) /* h */ +#define OPTS_CONTEXT_COMPONENT (OPTS_ROLE | OPTS_TYPE | OPTS_USER | OPTS_RANGE) + +int runcon_main(int argc, char *argv[]); +int runcon_main(int argc, char *argv[]) +{ + char *role = NULL; + char *range = NULL; + char *user = NULL; + char *type = NULL; + char *context = NULL; + unsigned opts; + context_t con; + + selinux_or_die(); + +#if ENABLE_FEATURE_RUNCON_LONG_OPTIONS + applet_long_options = runcon_options; +#endif + opt_complementary = "-1"; + opts = getopt32(argc, argv, "r:t:u:l:ch", &role, &type, &user, &range); + argv += optind; + + if (!(opts & OPTS_CONTEXT_COMPONENT)) { + context = *argv++; + if (!argv[0]) + bb_error_msg_and_die("no command found"); + } + + if (context) { + con = context_new(context); + if (!con) + bb_error_msg_and_die("'%s' is not a valid context", context); + } else { + con = runcon_compute_new_context(user, role, type, range, + argv[0], opts & OPTS_COMPUTE); + } + + if (security_check_context(context_str(con))) + bb_error_msg_and_die("'%s' is not a valid context", + context_str(con)); + + if (setexeccon(context_str(con))) + bb_error_msg_and_die("cannot set up security context '%s'", + context_str(con)); + + execvp(argv[0], argv); + + bb_perror_msg_and_die("cannot execute '%s'", command); + return 1; +}