From 9c124af8868a7d3908c03ec369e28daef17d5f12 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 30 Mar 2010 15:26:58 +0200 Subject: SELinux login management Adds a new option -Z to sss_useradd and sss_usermod. This option allows user to specify the SELinux login context for the user. On deleting the user with sss_userdel, the login mapping is deleted, so subsequent adding of the same user would result in the default login context unless -Z is specified again. MLS security is not supported as of this patch. --- src/Makefile.am | 7 + src/conf_macros.m4 | 16 +++ src/configure.ac | 5 + src/external/selinux.m4 | 12 ++ src/man/sss_useradd.8.xml | 12 ++ src/man/sss_usermod.8.xml | 11 ++ src/tools/selinux.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++ src/tools/sss_useradd.c | 11 ++ src/tools/sss_userdel.c | 9 ++ src/tools/sss_usermod.c | 11 ++ src/tools/tools_util.h | 2 + 11 files changed, 434 insertions(+) (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index e5c12df8..e56d7b16 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -295,6 +295,9 @@ TOOLS_LIBS = \ if BUILD_SELINUX TOOLS_LIBS += $(SELINUX_LIBS) endif +if BUILD_SEMANAGE + TOOLS_LIBS += $(SEMANAGE_LIBS) +endif dist_noinst_HEADERS = \ monitor/monitor.h \ @@ -529,11 +532,15 @@ FILES_TESTS_LIBS = \ if BUILD_SELINUX FILES_TESTS_LIBS += $(SELINUX_LIBS) endif +if BUILD_SEMANAGE + FILES_TESTS_LIBS += $(SEMANAGE_LIBS) +endif files_tests_SOURCES = \ $(SSSD_DEBUG_OBJ) \ tests/files-tests.c \ util/check_and_open.c \ + tools/selinux.c \ tools/files.c files_tests_CFLAGS = \ $(AM_CFLAGS) \ diff --git a/src/conf_macros.m4 b/src/conf_macros.m4 index 4a41da2b..4630f315 100644 --- a/src/conf_macros.m4 +++ b/src/conf_macros.m4 @@ -217,3 +217,19 @@ AC_DEFUN([WITH_NSCD], fi ]) +AC_DEFUN([WITH_SEMANAGE], + [ AC_ARG_WITH([semanage], + [AC_HELP_STRING([--with-semanage], + [Whether to build with SELinux user management support [yes]] + ) + ], + [], + with_semanage=yes + ) + if test x"$with_semanage" == xyes; then + HAVE_SEMANAGE=1 + AC_SUBST(HAVE_SEMANAGE) + AC_DEFINE_UNQUOTED(HAVE_SEMANAGE, 1, [Build with SELinux support]) + fi + AM_CONDITIONAL([BUILD_SEMANAGE], [test x"$with_semanage" = xyes]) + ]) diff --git a/src/configure.ac b/src/configure.ac index 52162b7c..40dac37c 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -72,6 +72,7 @@ WITH_KRB5_PLUGIN_PATH WITH_PYTHON_BINDINGS WITH_SELINUX WITH_NSCD +WITH_SEMANAGE m4_include([external/platform.m4]) m4_include([external/pkg.m4]) @@ -130,6 +131,10 @@ if test x$HAVE_SELINUX != x; then AM_CHECK_SELINUX fi +if test x$HAVE_SEMANAGE != x -a x$HAVE_SELINUX != x; then + AM_CHECK_SEMANAGE +fi + AC_CHECK_HEADERS([sys/inotify.h]) AC_CHECK_HEADERS([sasl/sasl.h],,AC_MSG_ERROR([Could not find SASL headers])) diff --git a/src/external/selinux.m4 b/src/external/selinux.m4 index 0c5d5294..d1b961a4 100644 --- a/src/external/selinux.m4 +++ b/src/external/selinux.m4 @@ -11,3 +11,15 @@ AC_DEFUN([AM_CHECK_SELINUX], AC_SUBST(SELINUX_LIBS) ]) +dnl A macro to check the availability of SELinux management library +AC_DEFUN([AM_CHECK_SEMANAGE], +[ + AC_CHECK_HEADERS(semanage/semanage.h, + [AC_CHECK_LIB(semanage, semanage_handle_create, + [SEMANAGE_LIBS="-lsemanage"], + [AC_MSG_ERROR([libsemanage is missing])] + ) + ], + [AC_MSG_ERROR([libsemanage is missing])]) + AC_SUBST(SEMANAGE_LIBS) +]) diff --git a/src/man/sss_useradd.8.xml b/src/man/sss_useradd.8.xml index 7620ffda..4b745ab9 100644 --- a/src/man/sss_useradd.8.xml +++ b/src/man/sss_useradd.8.xml @@ -160,6 +160,18 @@ + + + , + SELINUX_USER + + + + The SELinux user for the user's login. If not specified, + the system default will be used. + + + diff --git a/src/man/sss_usermod.8.xml b/src/man/sss_usermod.8.xml index b94fc738..2b437085 100644 --- a/src/man/sss_usermod.8.xml +++ b/src/man/sss_usermod.8.xml @@ -119,6 +119,17 @@ + + + , + SELINUX_USER + + + + The SELinux user for the user's login. + + + diff --git a/src/tools/selinux.c b/src/tools/selinux.c index 9fa660c6..23951289 100644 --- a/src/tools/selinux.c +++ b/src/tools/selinux.c @@ -21,12 +21,23 @@ #include "config.h" +#define _GNU_SOURCE +#include + #ifdef HAVE_SELINUX #include #endif +#ifdef HAVE_SEMANAGE +#include +#endif + #include "util/util.h" +#ifndef DEFAULT_SERANGE +#define DEFAULT_SERANGE "s0" +#endif + #ifdef HAVE_SELINUX /* * selinux_file_context - Set the security context before any file or @@ -79,3 +90,330 @@ int reset_selinux_file_context(void) return EOK; } #endif /* HAVE_SELINUX */ + +#ifdef HAVE_SEMANAGE +/* turn libselinux messages into SSSD DEBUG() calls */ +static void sss_semanage_error_callback(void *varg, + semanage_handle_t *handle, + const char *fmt, ...) +{ + int level = -1; + int ret; + char * message = NULL; + va_list ap; + + switch (semanage_msg_get_level(handle)) { + case SEMANAGE_MSG_ERR: + level = 1; + break; + case SEMANAGE_MSG_WARN: + level = 4; + break; + case SEMANAGE_MSG_INFO: + level = 6; + break; + } + + va_start(ap, fmt); + ret = vasprintf(&message, fmt, ap); + if (ret < 0) { + /* ENOMEM */ + return; + } + va_end(ap); + + if (level <= debug_level) { + if (debug_timestamps) { + time_t rightnow = time(NULL); + char stamp[25]; + memcpy(stamp, ctime(&rightnow), 24); + stamp[24] = '\0'; + debug_fn("(%s) [%s] [libsemanage] (%d): %s\n", + stamp, debug_prg_name, level, message); + } else { + debug_fn("[%s] [libsemanage] (%d): %s\n", + debug_prg_name, level, message); + } + } + free(message); +} + +static semanage_handle_t *sss_semanage_init(void) +{ + int ret; + semanage_handle_t *handle = NULL; + + handle = semanage_handle_create(); + if (!handle) { + DEBUG(1, ("Cannot create SELinux management handle\n")); + return NULL; + } + + semanage_msg_set_callback(handle, + sss_semanage_error_callback, + NULL); + + ret = semanage_is_managed(handle); + if (ret != 1) { + DEBUG(1, ("SELinux policy not managed\n")); + goto fail; + } + + ret = semanage_access_check(handle); + if (ret < SEMANAGE_CAN_READ) { + DEBUG(1, ("Cannot read SELinux policy store\n")); + goto fail; + } + + ret = semanage_connect(handle); + if (ret != 0) { + DEBUG(1, ("Cannot estabilish SELinux management connection\n")); + goto fail; + } + + ret = semanage_begin_transaction(handle); + if (ret != 0) { + DEBUG(1, ("Cannot begin SELinux transaction\n")); + goto fail; + } + + return handle; +fail: + semanage_handle_destroy(handle); + return NULL; +} + +static int sss_semanage_user_add(semanage_handle_t *handle, + semanage_seuser_key_t *key, + const char *login_name, + const char *seuser_name) +{ + int ret; + semanage_seuser_t *seuser = NULL; + + ret = semanage_seuser_create(handle, &seuser); + if (ret != 0) { + DEBUG(1, ("Cannot create SELinux login mapping for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_set_name(handle, seuser, login_name); + if (ret != 0) { + DEBUG(1, ("Could not set name for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_set_mlsrange(handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + DEBUG(1, ("Could not set serange for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_set_sename(handle, seuser, seuser_name); + if (ret != 0) { + DEBUG(1, ("Could not set SELinux user for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_modify_local(handle, key, seuser); + if (ret != 0) { + DEBUG(1, ("Could not add login mapping for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = EOK; +done: + semanage_seuser_free(seuser); + return ret; +} + +static int sss_semanage_user_mod(semanage_handle_t *handle, + semanage_seuser_key_t *key, + const char *login_name, + const char *seuser_name) +{ + int ret; + semanage_seuser_t *seuser = NULL; + + semanage_seuser_query(handle, key, &seuser); + if (seuser == NULL) { + DEBUG(1, ("Could not query seuser for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_set_mlsrange(handle, seuser, DEFAULT_SERANGE); + if (ret != 0) { + DEBUG(1, ("Could not set serange for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_set_sename(handle, seuser, seuser_name); + if (ret != 0) { + DEBUG(1, ("Could not set sename for %s\n", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_seuser_modify_local(handle, key, seuser); + if (ret != 0) { + DEBUG(1, (("Could not modify login mapping for %s\n"), login_name)); + ret = EIO; + goto done; + } + + ret = EOK; +done: + semanage_seuser_free(seuser); + return ret; +} + +int set_seuser(const char *login_name, const char *seuser_name) +{ + semanage_handle_t *handle = NULL; + semanage_seuser_key_t *key = NULL; + int ret; + int seuser_exists = 0; + + if (seuser_name == NULL) { + /* don't care, just let system pick the defaults */ + return EOK; + } + + handle = sss_semanage_init(); + if (!handle) { + DEBUG(1, ("Cannot init SELinux management\n")); + ret = EIO; + goto done; + } + + ret = semanage_seuser_key_create(handle, login_name, &key); + if (ret != 0) { + DEBUG(1, ("Cannot create SELinux user key\n")); + ret = EIO; + goto done; + } + + ret = semanage_seuser_exists(handle, key, &seuser_exists); + if (ret < 0) { + DEBUG(1, ("Cannot verify the SELinux user\n")); + ret = EIO; + goto done; + } + + if (seuser_exists) { + ret = sss_semanage_user_mod(handle, key, login_name, seuser_name); + if (ret != 0) { + DEBUG(1, ("Cannot modify SELinux user mapping\n")); + ret = EIO; + goto done; + } + } else { + ret = sss_semanage_user_add(handle, key, login_name, seuser_name); + if (ret != 0) { + DEBUG(1, ("Cannot add SELinux user mapping\n")); + ret = EIO; + goto done; + } + } + + ret = semanage_commit(handle); + if (ret != 0) { + DEBUG(1, ("Cannot commit SELinux transaction\n")); + ret = EIO; + goto done; + } + + ret = EOK; +done: + semanage_seuser_key_free(key); + semanage_handle_destroy(handle); + return ret; +} + +int del_seuser(const char *login_name) +{ + semanage_handle_t *handle = NULL; + semanage_seuser_key_t *key = NULL; + int ret; + int exists = 0; + + handle = sss_semanage_init(); + if (!handle) { + DEBUG(1, ("Cannot init SELinux management\n")); + ret = EIO; + goto done; + } + + ret = semanage_seuser_key_create(handle, login_name, &key); + if (ret != 0) { + DEBUG(1, ("Cannot create SELinux user key\n")); + ret = EIO; + goto done; + } + + ret = semanage_seuser_exists(handle, key, &exists); + if (ret < 0) { + DEBUG(1, ("Cannot verify the SELinux user\n")); + ret = EIO; + goto done; + } + + if (!exists) { + DEBUG(5, ("Login mapping for %s is not defined, OK if default mapping " + "was used\n", login_name)); + ret = EOK; /* probably default mapping */ + goto done; + } + + ret = semanage_seuser_exists_local(handle, key, &exists); + if (ret < 0) { + DEBUG(1, ("Cannot verify the SELinux user\n")); + ret = EIO; + goto done; + } + + if (!exists) { + DEBUG(1, ("Login mapping for %s is defined in policy, " + "cannot be deleted", login_name)); + ret = ENOENT; + goto done; + } + + ret = semanage_seuser_del_local(handle, key); + if (ret != 0) { + DEBUG(1, ("Could not delete login mapping for %s", login_name)); + ret = EIO; + goto done; + } + + ret = semanage_commit(handle); + if (ret != 0) { + DEBUG(1, ("Cannot commit SELinux transaction\n")); + ret = EIO; + goto done; + } + + ret = EOK; +done: + semanage_handle_destroy(handle); + return ret; +} + +#else /* HAVE_SEMANAGE */ +int set_seuser(const char *login_name, const char *seuser_name) +{ + return EOK; +} + +int del_seuser(const char *login_name) +{ + return EOK; +} +#endif /* HAVE_SEMANAGE */ diff --git a/src/tools/sss_useradd.c b/src/tools/sss_useradd.c index 2d88e75e..6c6b5851 100644 --- a/src/tools/sss_useradd.c +++ b/src/tools/sss_useradd.c @@ -109,6 +109,7 @@ int main(int argc, const char **argv) int pc_create_home = 0; const char *pc_username = NULL; const char *pc_skeldir = NULL; + const char *pc_selinux_user = NULL; struct poptOption long_options[] = { POPT_AUTOHELP { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0, _("The debug level to run with"), NULL }, @@ -121,6 +122,7 @@ int main(int argc, const char **argv) { "create-home", 'm', POPT_ARG_NONE, NULL, 'm', _("Create user's directory if it does not exist"), NULL }, { "no-create-home", 'M', POPT_ARG_NONE, NULL, 'M', _("Never create user's directory, overrides config"), NULL }, { "skel", 'k', POPT_ARG_STRING, &pc_skeldir, 0, _("Specify an alternative skeleton directory"), NULL }, + { "selinux-user", 'Z', POPT_ARG_STRING, &pc_selinux_user, 0, _("The SELinux user for user's login"), NULL }, POPT_TABLEEND }; poptContext pc = NULL; @@ -270,6 +272,15 @@ int main(int argc, const char **argv) end_transaction(tctx); + /* Set SELinux login context - must be done after transaction is done + * b/c libselinux calls getpwnam */ + ret = set_seuser(tctx->octx->name, pc_selinux_user); + if (ret != EOK) { + ERROR("Cannot set SELinux login context\n"); + ret = EXIT_FAILURE; + goto fini; + } + /* Create user's home directory and/or mail spool */ if (tctx->octx->create_homedir) { /* We need to know the UID and GID of the user, if diff --git a/src/tools/sss_userdel.c b/src/tools/sss_userdel.c index e74424d8..464c22e7 100644 --- a/src/tools/sss_userdel.c +++ b/src/tools/sss_userdel.c @@ -278,6 +278,15 @@ int main(int argc, const char **argv) end_transaction(tctx); + /* Set SELinux login context - must be done after transaction is done + * b/c libselinux calls getpwnam */ + ret = del_seuser(tctx->octx->name); + if (ret != EOK) { + ERROR("Cannot reset SELinux login context\n"); + ret = EXIT_FAILURE; + goto fini; + } + if (!pc_kick) { ret = is_logged_in(tctx, tctx->octx->uid); switch(ret) { diff --git a/src/tools/sss_usermod.c b/src/tools/sss_usermod.c index a272bc55..65431fa5 100644 --- a/src/tools/sss_usermod.c +++ b/src/tools/sss_usermod.c @@ -41,6 +41,7 @@ int main(int argc, const char **argv) char *pc_home = NULL; char *pc_shell = NULL; int pc_debug = 0; + const char *pc_selinux_user = NULL; struct poptOption long_options[] = { POPT_AUTOHELP { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0, _("The debug level to run with"), NULL }, @@ -53,6 +54,7 @@ int main(int argc, const char **argv) { "remove-group", 'r', POPT_ARG_STRING, NULL, 'r', _("Groups to remove this user from"), NULL }, { "lock", 'L', POPT_ARG_NONE, NULL, 'L', _("Lock the account"), NULL }, { "unlock", 'U', POPT_ARG_NONE, NULL, 'U', _("Unlock the account"), NULL }, + { "selinux-user", 'Z', POPT_ARG_STRING, &pc_selinux_user, 0, _("The SELinux user for user's login"), NULL }, POPT_TABLEEND }; poptContext pc = NULL; @@ -233,6 +235,15 @@ int main(int argc, const char **argv) end_transaction(tctx); + /* Set SELinux login context - must be done after transaction is done + * b/c libselinux calls getpwnam */ + ret = set_seuser(tctx->octx->name, pc_selinux_user); + if (ret != EOK) { + ERROR("Cannot set SELinux login context\n"); + ret = EXIT_FAILURE; + goto fini; + } + done: if (tctx->error) { ret = tctx->error; diff --git a/src/tools/tools_util.h b/src/tools/tools_util.h index 2ac18535..ac882868 100644 --- a/src/tools/tools_util.h +++ b/src/tools/tools_util.h @@ -115,5 +115,7 @@ int flush_nscd_cache(TALLOC_CTX *mem_ctx, enum nscd_db flush_db); /* from selinux.c */ int selinux_file_context(const char *dst_name); int reset_selinux_file_context(void); +int set_seuser(const char *login_name, const char *seuser_name); +int del_seuser(const char *login_name); #endif /* __TOOLS_UTIL_H__ */ -- cgit