[UTIL-LINUX PATCH] sulogin: relabel terminal according to SELinux policy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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>
---
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





[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux