Skip to content

Avoid falling prey to tiocsti#1660

Open
hallyn wants to merge 1 commit into
shadow-maint:masterfrom
hallyn:shadow.tiocsti
Open

Avoid falling prey to tiocsti#1660
hallyn wants to merge 1 commit into
shadow-maint:masterfrom
hallyn:shadow.tiocsti

Conversation

@hallyn

@hallyn hallyn commented Jun 24, 2026

Copy link
Copy Markdown
Member

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.

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.
@hallyn hallyn requested a review from alejandro-colomar June 24, 2026 14:31
Comment thread src/su.c
Comment on lines +110 to +121
static bool legacy_tiocsti_disabled() {
int ret;
char c;
int fd = open("/proc/sys/dev/tty/legacy_tiocsti", O_RDONLY);
if (fd == -1)
return false;
ret = read(fd, &c, 1);
close(fd);
if (ret != 1)
return false;
return c == '0';
}

@alejandro-colomar alejandro-colomar Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some style fixes (as mentioned in the email).
Also, I'd rename it by adding an _is_.
Also, I think it'd be simpler to use fgets(3) instead of read(2), since it's a text file.

#include <limits.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>

#include "io/fgets/fgets.h"
#include "string/strcmp/streq.h"

static bool
legacy_tiocsti_is_disabled(void)
{
	char  *p;
	char  ln[LINE_MAX + 1];
	FILE  *f;

	f = fopen("/proc/sys/dev/tty/legacy_tiocsti", "r");
	if (f == NULL)
		return false;

	p = fgets_a(ln, f);
	fclose(f);
	if (p == NULL)
		return false;

	return streq(ln, "0\n");
}

Comment thread src/su.c
Comment on lines +1040 to +1042
if (caller_is_root && !legacy_tiocsti_disabled())
need_pty_prot = true;

@alejandro-colomar alejandro-colomar Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By refactoring this, we can remove the initialization above.

need_pty_prot = (caller_is_root && !legacy_tiocsti_is_disabled());

Comment thread src/su.c
Comment on lines 108 to +110
/* local function prototypes */

static bool legacy_tiocsti_disabled() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this function to where the function definitions are.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants