diff --git a/Makefile.in b/Makefile.in index be2d39e5d..554155fe4 100644 --- a/Makefile.in +++ b/Makefile.in @@ -24,6 +24,8 @@ CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ LIBTOM_LIBS=$(STATIC_LTC) $(STATIC_LTM) endif +LIBSELINUX_LIBS=@LIBSELINUX_LIBS@ + OPTION_HEADERS = default_options_guard.h sysoptions.h ifneq ($(wildcard localoptions.h),) CFLAGS+=-DLOCALOPTIONS_H_EXISTS @@ -189,7 +191,7 @@ dropbearkey: $(dropbearkeyobjs) dropbearconvert: $(dropbearconvertobjs) dropbear: $(HEADERS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBSELINUX_LIBS) $(LIBS) @CRYPTLIB@ dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile $(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS) @@ -210,7 +212,7 @@ ifeq ($(MULTI),1) endif dropbearmulti$(EXEEXT): $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile - $(CC) $(LDFLAGS) -o $@ $(MULTIOBJS) $(LIBTOM_LIBS) $(LIBS) @CRYPTLIB@ + $(CC) $(LDFLAGS) -o $@ $(MULTIOBJS) $(LIBTOM_LIBS) $(LIBSELINUX_LIBS) $(LIBS) @CRYPTLIB@ multibinary: dropbearmulti$(EXEEXT) diff --git a/auth.h b/auth.h index 4bdb359dd..ad1f0db48 100644 --- a/auth.h +++ b/auth.h @@ -127,6 +127,10 @@ struct AuthState { #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT struct PubKeyOptions* pubkey_options; #endif + +#ifdef DROPBEAR_ENABLE_SELINUX + char *user_sid; +#endif }; #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT diff --git a/common-session.c b/common-session.c index aa31e4908..a0a941d71 100644 --- a/common-session.c +++ b/common-session.c @@ -136,6 +136,10 @@ void common_session_init(int sock_in, int sock_out) { ses.keys->trans.zstream = NULL; #endif +#ifdef DROPBEAR_ENABLE_SELINUX + ses.authstate.user_sid = NULL; +#endif + /* key exchange buffers */ ses.session_id = NULL; ses.kexhashbuf = NULL; diff --git a/configure.ac b/configure.ac index 7199d7cb2..34c50c734 100644 --- a/configure.ac +++ b/configure.ac @@ -611,6 +611,18 @@ AC_ARG_WITH(lastlog, ] ) +AC_MSG_CHECKING([whether to enable SELinux]) +AC_ARG_ENABLE([selinux], + [ --enable-selinux Enable SELinux support] +) +AC_MSG_RESULT([$enable_selinux]) + +AC_ARG_VAR([LIBSELINUX_LIBS],[SELinux libraries]) +if test x"$enable_selinux" = xyes; then + AC_DEFINE([DROPBEAR_ENABLE_SELINUX],[1],[Enable SELinux support]) + AC_SUBST([LIBSELINUX_LIBS],[${LIBSELINUX_LIBS:--lselinux}]) +fi + if test -z "$no_loginfunc_check"; then dnl Checks for libutil functions (login(), logout() etc, not openpty() ) AC_SEARCH_LIBS(login, util bsd, [AC_DEFINE(HAVE_LOGIN,1,[Have login() function])]) diff --git a/svr-auth.c b/svr-auth.c index 7575f9031..759533a7b 100644 --- a/svr-auth.c +++ b/svr-auth.c @@ -37,7 +37,13 @@ #include "runopts.h" #include "dbrandom.h" +#ifdef DROPBEAR_ENABLE_SELINUX +# include +# include +#endif + static int checkusername(const char *username, unsigned int userlen); +static void initselinux(const char *username); /* initialise the first time for a session, resetting all parameters */ void svr_authinitialise() { @@ -119,6 +125,8 @@ void recv_msg_userauth_request() { valid_user = 1; } + initselinux(ses.authstate.pw_name); + /* user wants to know what methods are supported */ if (methodlen == AUTH_METHOD_NONE_LEN && strncmp(methodname, AUTH_METHOD_NONE, @@ -227,6 +235,44 @@ static int check_group_membership(gid_t check_gid, const char* username, gid_t u } #endif +static void initselinux(const char *username) +{ +#ifdef DROPBEAR_ENABLE_SELINUX + char *seuser; + char *level; + int rc; + + if (!is_selinux_enabled()) + return; + + freecon(ses.authstate.user_sid); + ses.authstate.user_sid = NULL; + + rc = getseuserbyname(username, &seuser, &level); + if (rc < 0) { + dropbear_log(LOG_ERR, "getseuserbyname(%s) failed", username); + goto out; + } + + rc = get_default_context_with_level(seuser, level, NULL, + &ses.authstate.user_sid); + free(seuser); + free(level); + + if (rc < 0) { + dropbear_log(LOG_ERR, "get_default_context(%s) failed", username); + ses.authstate.user_sid = NULL; + goto out; + } + + rc = 0; + +out: + if (rc < 0 && security_getenforce() > 0) + dropbear_exit("SELinux: failed to initialie"); +#endif +} + /* Check that the username exists and isn't disallowed (root), and has a valid shell. * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */ static int checkusername(const char *username, unsigned int userlen) { diff --git a/svr-chansession.c b/svr-chansession.c index 5a5a8c875..03d68421f 100644 --- a/svr-chansession.c +++ b/svr-chansession.c @@ -38,6 +38,10 @@ #include "runopts.h" #include "auth.h" +#ifdef DROPBEAR_ENABLE_SELINUX +# include +#endif + /* Handles sessions (either shells or programs) requested by the client */ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, @@ -573,6 +577,53 @@ static void get_termmodes(const struct ChanSess *chansess) { TRACE(("leave get_termmodes")) } +static void relabelpty(const char *tty) +{ +#ifdef DROPBEAR_ENABLE_SELINUX + char *old_sid = NULL; + char *new_sid = NULL; + security_class_t class; + int rc; + + if (!is_selinux_enabled()) + return; + + rc = getfilecon(tty, &old_sid); + if (rc < 0) { + dropbear_log(LOG_ERR, "failed to get context of tty '%s'", tty); + goto out; + } + + class = string_to_security_class("chr_file"); + if (!class) { + rc = -1; + dropbear_log(LOG_ERR, "SELinux: failed to map 'chr_file'"); + goto out; + } + + rc = security_compute_relabel(ses.authstate.user_sid, old_sid, class, &new_sid); + if (rc < 0) { + dropbear_log(LOG_ERR, "failed to compute tty relabel"); + goto out; + } + + rc = setfilecon(tty, new_sid); + if (rc < 0) { + dropbear_log(LOG_ERR, "failed to set file context for '%s'", tty); + goto out; + } + + rc = 0; + +out: + freecon(new_sid); + freecon(old_sid); + + if (rc < 0 && security_getenforce() > 0) + dropbear_exit("SELinux: failed to relabel PTY"); +#endif +} + /* Set up a session pty which will be used to execute the shell or program. * The pty is allocated now, and kept for when the shell/program executes. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ @@ -621,6 +672,8 @@ static int sessionpty(struct ChanSess * chansess) { /* Read the terminal modes */ get_termmodes(chansess); + relabelpty(chansess->tty); + TRACE(("leave sessionpty")) return DROPBEAR_SUCCESS; } @@ -743,6 +796,29 @@ static int sessioncommand(struct Channel *channel, struct ChanSess *chansess, return ret; } +static void init_selinux_session(void) +{ +#ifdef DROPBEAR_ENABLE_SELINUX + char *ctx = ses.authstate.user_sid; + int rc; + + if (!is_selinux_enabled()) + return; + + rc = setexeccon(ctx); + if (rc < 0) { + dropbear_log(LOG_ERR, "setexeccon() failed"); + goto out; + } + + rc = 0; + +out: + if (rc < 0 && security_getenforce() > 0) + dropbear_exit("SELinux: failed to initialize session"); +#endif +} + /* Execute a command and set up redirection of stdin/stdout/stderr without a * pty. * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ @@ -949,6 +1025,8 @@ static void execchild(const void *user_data) { #endif /* HAVE_CLEARENV */ #endif /* DEBUG_VALGRIND */ + init_selinux_session(); + #if DROPBEAR_SVR_MULTIUSER /* We can only change uid/gid as root ... */ if (getuid() == 0) { diff --git a/svr-session.c b/svr-session.c index a81639841..0acab7888 100644 --- a/svr-session.c +++ b/svr-session.c @@ -42,6 +42,10 @@ #include "crypto_desc.h" #include "fuzz.h" +#ifdef DROPBEAR_ENABLE_SELINUX +# include +#endif + static void svr_remoteclosed(void); static void svr_algos_initialise(void); @@ -84,6 +88,10 @@ svr_session_cleanup(void) { /* free potential public key options */ svr_pubkey_options_cleanup(); +#ifdef DROPBEAR_ENABLE_SELINUX + freecon(ses.authstate.user_sid); +#endif + m_free(svr_ses.addrstring); m_free(svr_ses.remotehost); m_free(svr_ses.childpids);