On Wed, Dec 13, 2023 at 11:14 AM Christian Göttsche <cgzones@xxxxxxxxxxxxxx> wrote: > > The common SELinux practice is to have a distinct label for terminals in > use by logged in users. This allows to differentiate access on the > associated terminal (e.g. user_tty_device_t) vs foreign ones (e.g. > tty_device_t or sysadm_tty_device_t). Therefore the application > performing the user login and setting up the associated terminal should > label that terminal according to the loaded SELinux policy. Commonly > this is done by pam_selinux(7). Since sulogin(8) does not use pam(7) > perform the necessary steps manually. > > Fixes: https://github.com/util-linux/util-linux/issues/1578 > > Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> The SELinux parts look ok to me. Reviewed-by: James Carter <jwcart2@xxxxxxxxx> > --- > Upstream pull-request: https://github.com/util-linux/util-linux/pull/2650 > --- > login-utils/sulogin-consoles.c | 4 + > login-utils/sulogin-consoles.h | 4 + > login-utils/sulogin.c | 156 +++++++++++++++++++++++++++++---- > 3 files changed, 146 insertions(+), 18 deletions(-) > > diff --git a/login-utils/sulogin-consoles.c b/login-utils/sulogin-consoles.c > index 9ae525556..0dca949f4 100644 > --- a/login-utils/sulogin-consoles.c > +++ b/login-utils/sulogin-consoles.c > @@ -341,6 +341,10 @@ int append_console(struct list_head *consoles, const char * const name) > tail->id = last ? last->id + 1 : 0; > tail->pid = -1; > memset(&tail->tio, 0, sizeof(tail->tio)); > +#ifdef HAVE_LIBSELINUX > + tail->reset_tty_context = NULL; > + tail->user_tty_context = NULL; > +#endif > > return 0; > } > diff --git a/login-utils/sulogin-consoles.h b/login-utils/sulogin-consoles.h > index 12032c997..608c4f84f 100644 > --- a/login-utils/sulogin-consoles.h > +++ b/login-utils/sulogin-consoles.h > @@ -44,6 +44,10 @@ struct console { > pid_t pid; > struct chardata cp; > struct termios tio; > +#ifdef HAVE_LIBSELINUX > + char *reset_tty_context; > + char *user_tty_context; > +#endif > }; > > extern int detect_consoles(const char *device, int fallback, > diff --git a/login-utils/sulogin.c b/login-utils/sulogin.c > index 019f35092..2682c30fb 100644 > --- a/login-utils/sulogin.c > +++ b/login-utils/sulogin.c > @@ -99,6 +99,81 @@ static int locked_account_password(const char * const passwd) > return 0; > } > > +#ifdef HAVE_LIBSELINUX > +/* > + * Cached check whether SELinux is enabled. > + */ > +static int is_selinux_enabled_cached(void) > +{ > + static int cache = -1; > + > + if (cache == -1) > + cache = is_selinux_enabled(); > + > + return cache; > +} > + > +/* Computed SELinux login context. */ > +static char *login_context; > + > +/* > + * Compute SELinux login context. > + */ > +static void compute_login_context(void) > +{ > + char *seuser = NULL; > + char *level = NULL; > + > + if (is_selinux_enabled_cached() == 0) > + goto cleanup; > + > + if (getseuserbyname("root", &seuser, &level) == -1) { > + warnx(_("failed to compute seuser")); > + goto cleanup; > + } > + > + if (get_default_context_with_level(seuser, level, NULL, &login_context) == -1) { > + warnx(_("failed to compute default context")); > + goto cleanup; > + } > + > +cleanup: > + free(seuser); > + free(level); > +} > + > +/* > + * Compute SELinux terminal context. > + */ > +static void tcinit_selinux(struct console *con) > +{ > + security_class_t tclass; > + > + if (!login_context) > + return; > + > + if (fgetfilecon(con->fd, &con->reset_tty_context) == -1) { > + warn(_("failed to get context of terminal %s"), con->tty); > + return; > + } > + > + tclass = string_to_security_class("chr_file"); > + if (tclass == 0) { > + warnx(_("security class chr_file not available")); > + freecon(con->reset_tty_context); > + con->reset_tty_context = NULL; > + return; > + } > + > + if (security_compute_relabel(login_context, con->reset_tty_context, tclass, &con->user_tty_context) == -1) { > + warnx(_("failed to compute relabel context of terminal")); > + freecon(con->reset_tty_context); > + con->reset_tty_context = NULL; > + return; > + } > +} > +#endif > + > /* > * Fix the tty modes and set reasonable defaults. > */ > @@ -132,6 +207,10 @@ static void tcinit(struct console *con) > errno = 0; > #endif > > +#ifdef HAVE_LIBSELINUX > + tcinit_selinux(con); > +#endif > + > #ifdef TIOCGSERIAL > if (ioctl(fd, TIOCGSERIAL, &serinfo) >= 0) > con->flags |= CON_SERIAL; > @@ -785,7 +864,7 @@ out: > /* > * Password was OK, execute a shell. > */ > -static void sushell(struct passwd *pwd) > +static void sushell(struct passwd *pwd, struct console *con) > { > char shell[PATH_MAX]; > char home[PATH_MAX]; > @@ -842,22 +921,21 @@ static void sushell(struct passwd *pwd) > mask_signal(SIGHUP, SIG_DFL, NULL); > > #ifdef HAVE_LIBSELINUX > - if (is_selinux_enabled() > 0) { > - char *scon = NULL; > - char *seuser = NULL; > - char *level = NULL; > - > - if (getseuserbyname("root", &seuser, &level) == 0) { > - if (get_default_context_with_level(seuser, level, 0, &scon) == 0) { > - if (setexeccon(scon) != 0) > - warnx(_("setexeccon failed")); > - freecon(scon); > - } > + if (is_selinux_enabled_cached() == 1) { > + if (con->user_tty_context) { > + if (fsetfilecon(con->fd, con->user_tty_context) == -1) > + warn(_("failed to set context to %s for terminal %s"), con->user_tty_context, con->tty); > + } > + > + if (login_context) { > + if (setexeccon(login_context) == -1) > + warn(_("failed to set exec context to %s"), login_context); > } > - free(seuser); > - free(level); > } > +#else > + (void)con; > #endif > + > execl(su_shell, shell, (char *)NULL); > warn(_("failed to execute %s"), su_shell); > > @@ -866,6 +944,30 @@ static void sushell(struct passwd *pwd) > warn(_("failed to execute %s"), "/bin/sh"); > } > > +#ifdef HAVE_LIBSELINUX > +static void tcreset_selinux(struct list_head *consoles) { > + struct list_head *ptr; > + struct console *con; > + > + if (is_selinux_enabled_cached() == 0) > + return; > + > + list_for_each(ptr, consoles) { > + con = list_entry(ptr, struct console, entry); > + > + if (con->fd < 0) > + continue; > + if (!con->reset_tty_context) > + continue; > + if (fsetfilecon(con->fd, con->reset_tty_context) == -1) > + warn(_("failed to reset context to %s for terminal %s"), con->reset_tty_context, con->tty); > + > + freecon(con->reset_tty_context); > + con->reset_tty_context = NULL; > + } > +} > +#endif > + > static void usage(void) > { > FILE *out = stdout; > @@ -1015,6 +1117,10 @@ int main(int argc, char **argv) > return EXIT_FAILURE; > } > > +#ifdef HAVE_LIBSELINUX > + compute_login_context(); > +#endif > + > /* > * Ask for the password on the consoles. > */ > @@ -1034,9 +1140,18 @@ int main(int argc, char **argv) > } > ptr = (&consoles)->next; > > - if (ptr->next == &consoles) { > - con = list_entry(ptr, struct console, entry); > - goto nofork; > +#ifdef HAVE_LIBSELINUX > + /* > + * Always fork with SELinux enabled, so the parent can restore the > + * terminal context afterwards. > + */ > + if (is_selinux_enabled_cached() == 0) > +#endif > + { > + if (ptr->next == &consoles) { > + con = list_entry(ptr, struct console, entry); > + goto nofork; > + } > } > > > @@ -1087,7 +1202,7 @@ int main(int argc, char **argv) > #endif > if (doshell) { > /* sushell() unmask signals */ > - sushell(pwd); > + sushell(pwd, con); > > mask_signal(SIGQUIT, SIG_IGN, &saved_sigquit); > mask_signal(SIGTSTP, SIG_IGN, &saved_sigtstp); > @@ -1193,5 +1308,10 @@ int main(int argc, char **argv) > } while (1); > > mask_signal(SIGCHLD, SIG_DFL, NULL); > + > +#ifdef HAVE_LIBSELINUX > + tcreset_selinux(&consoles); > +#endif > + > return EXIT_SUCCESS; > } > -- > 2.43.0 > >