]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
Avoid falling prey to tiocsti
authorSerge Hallyn <serge@hallyn.com>
Tue, 9 Jun 2026 01:08:04 +0000 (20:08 -0500)
committerSerge Hallyn <serge@hallyn.com>
Wed, 8 Jul 2026 16:15:15 +0000 (11:15 -0500)
We've long known (see
https://www.halfdog.net/Security/2012/TtyPushbackPrivilegeEscalation/
and https://jdebp.uk/FGA/dont-abuse-su-for-dropping-privileges.html)
that su should be used to gain but not drop privilege.  Much more
recently, linux added the ability to prevent TIOCSTI through a
configurable /proc/sys/dev/tty/legacy_tiocsti setting.

If /proc/sys/dev/tty/legacy_tiocsti is set to 0, then we are protected
from the callee injecting commands on caller's tty through TIOCSTI.
If it's 1, or doesn't exist, then we are not.  That can be dangerous
if caller is root.  We currently give up the controlling terminal
for non-interactive uses of su (-c).  Let's do that for interactive
calls as well, only in the dangerous case.

src/su.c

index 8cea0d1f040a0502cab847e36a5bbc3bfede7840..e4dc72098dc02003a4a526dcd56e381a960f8935 100644 (file)
--- a/src/su.c
+++ b/src/su.c
@@ -56,6 +56,7 @@
 #ifdef USE_PAM
 #include "pam_defs.h"
 #endif                         /* USE_PAM */
+#include "io/fgets/fgets.h"
 #include "pwauth.h"
 #include "prototypes.h"
 #include "shadowlog.h"
@@ -996,6 +997,24 @@ static void set_environment (struct passwd *pw)
 
 }
 
+// See linux.git 83efeeeb3d04 (2022-10-22; "tty: Allow TIOCSTI to be disabled")
+static bool legacy_tiocsti_is_disabled(void)
+{
+       char buf[3];
+       FILE *fp;
+       void *ret;
+
+       fp = fopen("/proc/sys/dev/tty/legacy_tiocsti", "r");
+       if (NULL == fp)
+               return false;
+       ret = fgets_a(buf, fp);
+       fclose(fp);
+       if (ret == NULL)
+               return false;
+
+       return streq(buf, "0\n");
+}
+
 /*
  * su - switch user id
  *
@@ -1010,6 +1029,7 @@ int main (int argc, char **argv)
 {
        const char *cp;
        struct passwd *pw = NULL;
+       bool need_pty_prot;
 
 #ifdef USE_PAM
        int ret;
@@ -1023,6 +1043,8 @@ int main (int argc, char **argv)
 
        save_caller_context();
 
+       need_pty_prot = caller_is_root && !legacy_tiocsti_is_disabled();
+
        OPENLOG (Prog);
 
        process_flags (argc, argv);
@@ -1152,7 +1174,7 @@ int main (int argc, char **argv)
 
        set_environment (pw);
 
-       if (!doshell) {
+       if (!doshell || need_pty_prot) {
                /* There is no need for a controlling terminal.
                 * This avoids the callee to inject commands on
                 * the caller's tty. */