From: "Christian Göttsche" <cgzones@googlemail.com>
To: selinux@vger.kernel.org
Subject: [UTIL-LINUX PATCH] sulogin: relabel terminal according to SELinux policy
Date: Wed, 13 Dec 2023 17:14:12 +0100 [thread overview]
Message-ID: <20231213161412.23022-1-cgzones@googlemail.com> (raw)
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@googlemail.com>
---
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
next reply other threads:[~2023-12-13 16:14 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-13 16:14 Christian Göttsche [this message]
2024-01-05 19:14 ` [UTIL-LINUX PATCH] sulogin: relabel terminal according to SELinux policy James Carter
2024-01-17 14:50 ` Christian Göttsche
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231213161412.23022-1-cgzones@googlemail.com \
--to=cgzones@googlemail.com \
--cc=selinux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).