getty,login: tighten up handling of ctty, pgrp, and tty attr restoring on timeout

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2011-10-22 06:27:41 +02:00
parent ef5a2d757a
commit 7449e18190
4 changed files with 171 additions and 105 deletions

View File

@ -279,6 +279,7 @@ and inspect it by
Again, if TOSTOP is set but the background process ignores or blocks Again, if TOSTOP is set but the background process ignores or blocks
the SIGTTOU signal, or if its process group is orphaned (see below), the SIGTTOU signal, or if its process group is orphaned (see below),
then the write() returns an EIO error, and no signal is sent. then the write() returns an EIO error, and no signal is sent.
[vda: correction. SUS says that if SIGTTOU is blocked/ignored, write succeeds. ]
<p> <p>
</p><h3>Orphaned process groups</h3> </p><h3>Orphaned process groups</h3>

View File

@ -30,14 +30,23 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
struct sigaction sa, oldsa; struct sigaction sa, oldsa;
struct termios tio, oldtio; struct termios tio, oldtio;
tcgetattr(fd, &oldtio); fputs(prompt, stdout);
fflush_all();
tcflush(fd, TCIFLUSH); tcflush(fd, TCIFLUSH);
tcgetattr(fd, &oldtio);
tio = oldtio; tio = oldtio;
#ifndef IUCLC #if 0
# define IUCLC 0 /* Switch off UPPERCASE->lowercase conversion (never used since 198x)
#endif * and XON/XOFF (why we want to mess with this??)
*/
# ifndef IUCLC
# define IUCLC 0
# endif
tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY); tio.c_iflag &= ~(IUCLC|IXON|IXOFF|IXANY);
tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|TOSTOP); #endif
/* Switch off echo */
tio.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL);
tcsetattr(fd, TCSANOW, &tio); tcsetattr(fd, TCSANOW, &tio);
memset(&sa, 0, sizeof(sa)); memset(&sa, 0, sizeof(sa));
@ -50,9 +59,6 @@ char* FAST_FUNC bb_ask(const int fd, int timeout, const char *prompt)
alarm(timeout); alarm(timeout);
} }
fputs(prompt, stdout);
fflush_all();
if (!passwd) if (!passwd)
passwd = xmalloc(sizeof_passwd); passwd = xmalloc(sizeof_passwd);
ret = passwd; ret = passwd;

View File

@ -83,16 +83,16 @@ static FILE *dbf;
#define MAX_SPEED 10 /* max. nr. of baud rates */ #define MAX_SPEED 10 /* max. nr. of baud rates */
struct globals { struct globals {
unsigned timeout; /* time-out period */ unsigned timeout;
const char *login; /* login program */ const char *login; /* login program */
const char *fakehost; const char *fakehost;
const char *tty; /* name of tty */ const char *tty_name;
char *initstring; /* modem init string */ char *initstring; /* modem init string */
const char *issue; /* alternative issue file */ const char *issue; /* alternative issue file */
int numspeed; /* number of baud rates to try */ int numspeed; /* number of baud rates to try */
int speeds[MAX_SPEED]; /* baud rates to be tried */ int speeds[MAX_SPEED]; /* baud rates to be tried */
unsigned char eol; /* end-of-line char seen (CR or NL) */ unsigned char eol; /* end-of-line char seen (CR or NL) */
struct termios termios; /* terminal mode bits */ struct termios tty_attrs;
char line_buf[128]; char line_buf[128];
}; };
@ -181,15 +181,14 @@ static void parse_args(char **argv)
debug("after getopt\n"); debug("after getopt\n");
/* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ /* We loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
G.tty = argv[0]; /* tty name */ G.tty_name = argv[0];
ts = argv[1]; /* baud rate(s) */ ts = argv[1]; /* baud rate(s) */
if (isdigit(argv[0][0])) { if (isdigit(argv[0][0])) {
/* A number first, assume it's a speed (BSD style) */ /* A number first, assume it's a speed (BSD style) */
G.tty = ts; /* tty name is in argv[1] */ G.tty_name = ts; /* tty name is in argv[1] */
ts = argv[0]; /* baud rate(s) */ ts = argv[0]; /* baud rate(s) */
} }
parse_speeds(ts); parse_speeds(ts);
applet_name = xasprintf("getty: %s", G.tty);
if (argv[2]) if (argv[2])
xsetenv("TERM", argv[2]); xsetenv("TERM", argv[2]);
@ -201,42 +200,49 @@ static void parse_args(char **argv)
static void open_tty(void) static void open_tty(void)
{ {
/* Set up new standard input, unless we are given an already opened port */ /* Set up new standard input, unless we are given an already opened port */
if (NOT_LONE_DASH(G.tty)) { if (NOT_LONE_DASH(G.tty_name)) {
if (G.tty[0] != '/') if (G.tty_name[0] != '/')
G.tty = xasprintf("/dev/%s", G.tty); /* will leak it */ G.tty_name = xasprintf("/dev/%s", G.tty_name); /* will leak it */
/* Open the tty as standard input */ /* Open the tty as standard input */
debug("open(2)\n"); debug("open(2)\n");
close(0); close(0);
xopen(G.tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ xopen(G.tty_name, O_RDWR | O_NONBLOCK); /* uses fd 0 */
/* Set proper protections and ownership */ /* Set proper protections and ownership */
fchown(0, 0, 0); /* 0:0 */ fchown(0, 0, 0); /* 0:0 */
fchmod(0, 0620); /* crw--w---- */ fchmod(0, 0620); /* crw--w---- */
} else { } else {
char *n;
/* /*
* Standard input should already be connected to an open port. Make * Standard input should already be connected to an open port.
* sure it is open for read/write. * Make sure it is open for read/write.
*/ */
if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR) if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
bb_error_msg_and_die("stdin is not open for read/write"); bb_error_msg_and_die("stdin is not open for read/write");
/* Try to get real tty name instead of "-" */
n = xmalloc_ttyname(0);
if (n)
G.tty_name = n;
} }
applet_name = xasprintf("getty: %s", skip_dev_pfx(G.tty_name));
} }
static void set_termios(void) static void set_tty_attrs(void)
{ {
if (tcsetattr_stdin_TCSANOW(&G.termios) < 0) if (tcsetattr_stdin_TCSANOW(&G.tty_attrs) < 0)
bb_perror_msg_and_die("tcsetattr"); bb_perror_msg_and_die("tcsetattr");
} }
/* We manipulate termios this way: /* We manipulate tty_attrs this way:
* - first, we read existing termios settings * - first, we read existing tty_attrs
* - termios_init modifies some parts and sets it * - init_tty_attrs modifies some parts and sets it
* - auto_baud and/or BREAK processing can set different speed and set termios * - auto_baud and/or BREAK processing can set different speed and set tty attrs
* - termios_final again modifies some parts and sets termios before * - finalize_tty_attrs again modifies some parts and sets tty attrs before
* execing login * execing login
*/ */
static void termios_init(int speed) static void init_tty_attrs(int speed)
{ {
/* Try to drain output buffer, with 5 sec timeout. /* Try to drain output buffer, with 5 sec timeout.
* Added on request from users of ~600 baud serial interface * Added on request from users of ~600 baud serial interface
@ -255,14 +261,14 @@ static void termios_init(int speed)
/* Set speed if it wasn't specified as "0" on command line */ /* Set speed if it wasn't specified as "0" on command line */
if (speed != B0) if (speed != B0)
cfsetspeed(&G.termios, speed); cfsetspeed(&G.tty_attrs, speed);
/* Initial termios settings: 8-bit characters, raw mode, blocking i/o. /* Initial settings: 8-bit characters, raw mode, blocking i/o.
* Special characters are set after we have read the login name; all * Special characters are set after we have read the login name; all
* reads will be done in raw mode anyway. * reads will be done in raw mode anyway.
*/ */
/* Clear all bits except: */ /* Clear all bits except: */
G.termios.c_cflag &= (0 G.tty_attrs.c_cflag &= (0
/* 2 stop bits (1 otherwise) /* 2 stop bits (1 otherwise)
* Enable parity bit (both on input and output) * Enable parity bit (both on input and output)
* Odd parity (else even) * Odd parity (else even)
@ -280,42 +286,42 @@ static void termios_init(int speed)
#endif #endif
); );
/* Set: 8 bits; hang up (drop DTR) on last close; enable receive */ /* Set: 8 bits; hang up (drop DTR) on last close; enable receive */
G.termios.c_cflag |= CS8 | HUPCL | CREAD; G.tty_attrs.c_cflag |= CS8 | HUPCL | CREAD;
if (option_mask32 & F_LOCAL) { if (option_mask32 & F_LOCAL) {
/* ignore Carrier Detect pin: /* ignore Carrier Detect pin:
* opens don't block when CD is low, * opens don't block when CD is low,
* losing CD doesn't hang up processes whose ctty is this tty * losing CD doesn't hang up processes whose ctty is this tty
*/ */
G.termios.c_cflag |= CLOCAL; G.tty_attrs.c_cflag |= CLOCAL;
} }
#ifdef CRTSCTS #ifdef CRTSCTS
if (option_mask32 & F_RTSCTS) if (option_mask32 & F_RTSCTS)
G.termios.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */ G.tty_attrs.c_cflag |= CRTSCTS; /* flow control using RTS/CTS pins */
#endif #endif
G.termios.c_iflag = 0; G.tty_attrs.c_iflag = 0;
G.termios.c_lflag = 0; G.tty_attrs.c_lflag = 0;
/* non-raw output; add CR to each NL */ /* non-raw output; add CR to each NL */
G.termios.c_oflag = OPOST | ONLCR; G.tty_attrs.c_oflag = OPOST | ONLCR;
G.termios.c_cc[VMIN] = 1; /* block reads if < 1 char is available */ G.tty_attrs.c_cc[VMIN] = 1; /* block reads if < 1 char is available */
G.termios.c_cc[VTIME] = 0; /* no timeout (reads block forever) */ G.tty_attrs.c_cc[VTIME] = 0; /* no timeout (reads block forever) */
#ifdef __linux__ #ifdef __linux__
G.termios.c_line = 0; G.tty_attrs.c_line = 0;
#endif #endif
set_termios(); set_tty_attrs();
debug("term_io 2\n"); debug("term_io 2\n");
} }
static void termios_final(void) static void finalize_tty_attrs(void)
{ {
/* software flow control on output (stop sending if XOFF is recvd); /* software flow control on output (stop sending if XOFF is recvd);
* and on input (send XOFF when buffer is full) * and on input (send XOFF when buffer is full)
*/ */
G.termios.c_iflag |= IXON | IXOFF; G.tty_attrs.c_iflag |= IXON | IXOFF;
if (G.eol == '\r') { if (G.eol == '\r') {
G.termios.c_iflag |= ICRNL; /* map CR on input to NL */ G.tty_attrs.c_iflag |= ICRNL; /* map CR on input to NL */
} }
/* Other bits in c_iflag: /* Other bits in c_iflag:
* IXANY Any recvd char enables output (any char is also a XON) * IXANY Any recvd char enables output (any char is also a XON)
@ -342,7 +348,7 @@ static void termios_final(void)
* echo kill char specially, not as ^c (ECHOKE controls how exactly); * echo kill char specially, not as ^c (ECHOKE controls how exactly);
* erase all input via BS-SP-BS on kill char (else go to next line) * erase all input via BS-SP-BS on kill char (else go to next line)
*/ */
G.termios.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; G.tty_attrs.c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
/* Other bits in c_lflag: /* Other bits in c_lflag:
* XCASE Map uppercase to \lowercase [tried, doesn't work] * XCASE Map uppercase to \lowercase [tried, doesn't work]
* ECHONL Echo NL even if ECHO is not set * ECHONL Echo NL even if ECHO is not set
@ -360,17 +366,17 @@ static void termios_final(void)
* (why "stty sane" unsets this bit?) * (why "stty sane" unsets this bit?)
*/ */
G.termios.c_cc[VINTR] = DEF_INTR; G.tty_attrs.c_cc[VINTR] = DEF_INTR;
G.termios.c_cc[VQUIT] = DEF_QUIT; G.tty_attrs.c_cc[VQUIT] = DEF_QUIT;
G.termios.c_cc[VEOF] = DEF_EOF; G.tty_attrs.c_cc[VEOF] = DEF_EOF;
G.termios.c_cc[VEOL] = DEF_EOL; G.tty_attrs.c_cc[VEOL] = DEF_EOL;
#ifdef VSWTC #ifdef VSWTC
G.termios.c_cc[VSWTC] = DEF_SWITCH; G.tty_attrs.c_cc[VSWTC] = DEF_SWITCH;
#endif #endif
#ifdef VSWTCH #ifdef VSWTCH
G.termios.c_cc[VSWTCH] = DEF_SWITCH; G.tty_attrs.c_cc[VSWTCH] = DEF_SWITCH;
#endif #endif
G.termios.c_cc[VKILL] = DEF_KILL; G.tty_attrs.c_cc[VKILL] = DEF_KILL;
/* Other control chars: /* Other control chars:
* VEOL2 * VEOL2
* VERASE, VWERASE - (word) erase. we may set VERASE in get_logname * VERASE, VWERASE - (word) erase. we may set VERASE in get_logname
@ -380,7 +386,7 @@ static void termios_final(void)
* VSTART, VSTOP - chars used for IXON/IXOFF * VSTART, VSTOP - chars used for IXON/IXOFF
*/ */
set_termios(); set_tty_attrs();
} }
/* extract baud rate from modem status message */ /* extract baud rate from modem status message */
@ -403,8 +409,8 @@ static void auto_baud(void)
* modem status messages is enabled. * modem status messages is enabled.
*/ */
G.termios.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */ G.tty_attrs.c_cc[VMIN] = 0; /* don't block reads (min read is 0 chars) */
set_termios(); set_tty_attrs();
/* /*
* Wait for a while, then read everything the modem has said so far and * Wait for a while, then read everything the modem has said so far and
@ -420,15 +426,15 @@ static void auto_baud(void)
if (isdigit(*bp)) { if (isdigit(*bp)) {
speed = bcode(bp); speed = bcode(bp);
if (speed > 0) if (speed > 0)
cfsetspeed(&G.termios, speed); cfsetspeed(&G.tty_attrs, speed);
break; break;
} }
} }
} }
/* Restore terminal settings */ /* Restore terminal settings */
G.termios.c_cc[VMIN] = 1; /* restore to value set by termios_init */ G.tty_attrs.c_cc[VMIN] = 1; /* restore to value set by init_tty_attrs */
set_termios(); set_tty_attrs();
} }
/* get user name, establish parity, speed, erase, kill, eol; /* get user name, establish parity, speed, erase, kill, eol;
@ -449,7 +455,7 @@ static char *get_logname(void)
/* Write issue file and prompt */ /* Write issue file and prompt */
#ifdef ISSUE #ifdef ISSUE
if (!(option_mask32 & F_NOISSUE)) if (!(option_mask32 & F_NOISSUE))
print_login_issue(G.issue, G.tty); print_login_issue(G.issue, G.tty_name);
#endif #endif
print_login_prompt(); print_login_prompt();
@ -479,7 +485,7 @@ static char *get_logname(void)
goto got_logname; goto got_logname;
case BS: case BS:
case DEL: case DEL:
G.termios.c_cc[VERASE] = c; G.tty_attrs.c_cc[VERASE] = c;
if (bp > G.line_buf) { if (bp > G.line_buf) {
full_write(STDOUT_FILENO, "\010 \010", 3); full_write(STDOUT_FILENO, "\010 \010", 3);
bp--; bp--;
@ -510,11 +516,17 @@ static char *get_logname(void)
return G.line_buf; return G.line_buf;
} }
static void alarm_handler(int sig UNUSED_PARAM)
{
finalize_tty_attrs();
_exit(EXIT_SUCCESS);
}
int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int getty_main(int argc UNUSED_PARAM, char **argv) int getty_main(int argc UNUSED_PARAM, char **argv)
{ {
int n; int n;
pid_t pid; pid_t pid, tsid;
char *logname; char *logname;
INIT_G(); INIT_G();
@ -527,14 +539,35 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
/* Parse command-line arguments */ /* Parse command-line arguments */
parse_args(argv); parse_args(argv);
logmode = LOGMODE_NONE; /* Create new session and pgrp, lose controlling tty */
pid = setsid(); /* this also gives us our pid :) */
if (pid < 0) {
int fd;
/* :(
* docs/ctty.htm says:
* "This is allowed only when the current process
* is not a process group leader".
* Thus, setsid() will fail if we _already_ are
* a session leader - which is quite possible for getty!
*/
pid = getpid();
if (getsid(0) != pid)
bb_perror_msg_and_die("setsid");
/* Looks like we are already a session leader.
* In this case (setsid failed) we may still have ctty,
* and it may be different from tty we need to control!
* If we still have ctty, on Linux ioctl(TIOCSCTTY)
* (which we are going to call a bit later) always fails.
* Try to drop ctty now to prevent that.
*/
fd = open("/dev/tty", O_RDWR);
if (fd >= 0) {
ioctl(fd, TIOCNOTTY);
close(fd);
}
}
/* Create new session, lose controlling tty, if any */ /* Close stdio, and stray descriptors, just in case */
/* docs/ctty.htm says:
* "This is allowed only when the current process
* is not a process group leader" - is this a problem? */
setsid();
/* close stdio, and stray descriptors, just in case */
n = xopen(bb_dev_null, O_RDWR); n = xopen(bb_dev_null, O_RDWR);
/* dup2(n, 0); - no, we need to handle "getty - 9600" too */ /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
xdup2(n, 1); xdup2(n, 1);
@ -558,13 +591,25 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
#endif #endif
/* Open the tty as standard input, if it is not "-" */ /* Open the tty as standard input, if it is not "-" */
/* If it's not "-" and not taken yet, it will become our ctty */
debug("calling open_tty\n"); debug("calling open_tty\n");
open_tty(); open_tty();
ndelay_off(0); ndelay_off(STDIN_FILENO);
debug("duping\n"); debug("duping\n");
xdup2(0, 1); xdup2(STDIN_FILENO, 1);
xdup2(0, 2); xdup2(STDIN_FILENO, 2);
/* Steal ctty if we don't have it yet */
tsid = tcgetsid(STDIN_FILENO);
if (tsid < 0 || pid != tsid) {
if (ioctl(STDIN_FILENO, TIOCSCTTY, /*force:*/ (long)1) < 0)
bb_perror_msg_and_die("TIOCSCTTY");
}
#ifdef __linux__
/* Make ourself a foreground process group within our session */
if (tcsetpgrp(STDIN_FILENO, pid) < 0)
bb_perror_msg_and_die("tcsetpgrp");
#endif
/* /*
* The following ioctl will fail if stdin is not a tty, but also when * The following ioctl will fail if stdin is not a tty, but also when
@ -574,25 +619,15 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
* by patching the SunOS kernel variable "zsadtrlow" to a larger value; * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
* 5 seconds seems to be a good value. * 5 seconds seems to be a good value.
*/ */
if (tcgetattr(STDIN_FILENO, &G.termios) < 0) if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0)
bb_perror_msg_and_die("tcgetattr"); bb_perror_msg_and_die("tcgetattr");
pid = getpid();
#ifdef __linux__
// FIXME: do we need this? Otherwise "-" case seems to be broken...
// /* Forcibly make fd 0 our controlling tty, even if another session
// * has it as a ctty. (Another session loses ctty). */
// ioctl(STDIN_FILENO, TIOCSCTTY, (void*)1);
/* Make ourself a foreground process group within our session */
tcsetpgrp(STDIN_FILENO, pid);
#endif
/* Update the utmp file. This tty is ours now! */ /* Update the utmp file. This tty is ours now! */
update_utmp(pid, LOGIN_PROCESS, G.tty, "LOGIN", G.fakehost); update_utmp(pid, LOGIN_PROCESS, G.tty_name, "LOGIN", G.fakehost);
/* Initialize the termios settings (raw mode, eight-bit, blocking i/o) */ /* Initialize tty attrs (raw mode, eight-bit, blocking i/o) */
debug("calling termios_init\n"); debug("calling init_tty_attrs\n");
termios_init(G.speeds[0]); init_tty_attrs(G.speeds[0]);
/* Write the modem init string and DON'T flush the buffers */ /* Write the modem init string and DON'T flush the buffers */
if (option_mask32 & F_INITSTRING) { if (option_mask32 & F_INITSTRING) {
@ -606,8 +641,8 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
auto_baud(); auto_baud();
/* Set the optional timer */ /* Set the optional timer */
signal(SIGALRM, alarm_handler);
alarm(G.timeout); /* if 0, alarm is not set */ alarm(G.timeout); /* if 0, alarm is not set */
//BUG: death by signal won't restore termios
/* Optionally wait for CR or LF before writing /etc/issue */ /* Optionally wait for CR or LF before writing /etc/issue */
if (option_mask32 & F_WAITCRLF) { if (option_mask32 & F_WAITCRLF) {
@ -622,7 +657,7 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
logname = NULL; logname = NULL;
if (!(option_mask32 & F_NOPROMPT)) { if (!(option_mask32 & F_NOPROMPT)) {
/* NB: termios_init already set line speed /* NB: init_tty_attrs already set line speed
* to G.speeds[0] */ * to G.speeds[0] */
int baud_index = 0; int baud_index = 0;
@ -634,16 +669,15 @@ int getty_main(int argc UNUSED_PARAM, char **argv)
break; break;
/* We are here only if G.numspeed > 1 */ /* We are here only if G.numspeed > 1 */
baud_index = (baud_index + 1) % G.numspeed; baud_index = (baud_index + 1) % G.numspeed;
cfsetspeed(&G.termios, G.speeds[baud_index]); cfsetspeed(&G.tty_attrs, G.speeds[baud_index]);
set_termios(); set_tty_attrs();
} }
} }
/* Disable timer */ /* Disable timer */
alarm(0); alarm(0);
/* Finalize the termios settings */ finalize_tty_attrs();
termios_final();
/* Now the newline character should be properly written */ /* Now the newline character should be properly written */
full_write(STDOUT_FILENO, "\n", 1); full_write(STDOUT_FILENO, "\n", 1);

View File

@ -41,6 +41,13 @@ enum {
TTYNAME_SIZE = 32, TTYNAME_SIZE = 32,
}; };
struct globals {
struct termios tty_attrs;
} FIX_ALIASING;
#define G (*(struct globals*)&bb_common_bufsiz1)
#define INIT_G() do { } while (0)
#if ENABLE_FEATURE_NOLOGIN #if ENABLE_FEATURE_NOLOGIN
static void die_if_nologin(void) static void die_if_nologin(void)
{ {
@ -206,15 +213,21 @@ static void motd(void)
static void alarm_handler(int sig UNUSED_PARAM) static void alarm_handler(int sig UNUSED_PARAM)
{ {
/* This is the escape hatch! Poor serial line users and the like /* This is the escape hatch! Poor serial line users and the like
* arrive here when their connection is broken. * arrive here when their connection is broken.
* We don't want to block here */ * We don't want to block here */
ndelay_on(1); ndelay_on(STDOUT_FILENO);
printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT); /* Test for correct attr restoring:
* run "getty 0 -" from a shell, enter bogus username, stop at
* password prompt, let it time out. Without the tcsetattr below,
* when you are back at shell prompt, echo will be still off.
*/
tcsetattr_stdin_TCSANOW(&G.tty_attrs);
printf("\r\nLogin timed out after %u seconds\r\n", TIMEOUT);
fflush_all(); fflush_all();
/* unix API is brain damaged regarding O_NONBLOCK, /* unix API is brain damaged regarding O_NONBLOCK,
* we should undo it, or else we can affect other processes */ * we should undo it, or else we can affect other processes */
ndelay_off(1); ndelay_off(STDOUT_FILENO);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
@ -250,9 +263,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
pid_t child_pid; pid_t child_pid;
#endif #endif
username[0] = '\0'; INIT_G();
signal(SIGALRM, alarm_handler);
alarm(TIMEOUT);
/* More of suid paranoia if called by non-root: */ /* More of suid paranoia if called by non-root: */
/* Clear dangerous stuff, set PATH */ /* Clear dangerous stuff, set PATH */
@ -264,6 +275,7 @@ int login_main(int argc UNUSED_PARAM, char **argv)
* (The name of the function is misleading. Not daemonizing here.) */ * (The name of the function is misleading. Not daemonizing here.) */
bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL); bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
username[0] = '\0';
opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
if (opt & LOGIN_OPT_f) { if (opt & LOGIN_OPT_f) {
if (!run_by_root) if (!run_by_root)
@ -274,9 +286,19 @@ int login_main(int argc UNUSED_PARAM, char **argv)
if (argv[0]) /* user from command line (getty) */ if (argv[0]) /* user from command line (getty) */
safe_strncpy(username, argv[0], sizeof(username)); safe_strncpy(username, argv[0], sizeof(username));
/* Let's find out and memorize our tty */ /* Save tty attributes - and by doing it, check that it's indeed a tty */
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) if (tcgetattr(STDIN_FILENO, &G.tty_attrs) < 0
|| !isatty(STDOUT_FILENO)
/*|| !isatty(STDERR_FILENO) - no, guess some people might want to redirect this */
) {
return EXIT_FAILURE; /* Must be a terminal */ return EXIT_FAILURE; /* Must be a terminal */
}
/* We install timeout handler only _after_ we saved G.tty_attrs */
signal(SIGALRM, alarm_handler);
alarm(TIMEOUT);
/* Find out and memorize our tty name */
full_tty = xmalloc_ttyname(STDIN_FILENO); full_tty = xmalloc_ttyname(STDIN_FILENO);
if (!full_tty) if (!full_tty)
full_tty = xstrdup("UNKNOWN"); full_tty = xstrdup("UNKNOWN");
@ -391,7 +413,10 @@ int login_main(int argc UNUSED_PARAM, char **argv)
if (!pw->pw_passwd[0]) if (!pw->pw_passwd[0])
break; break;
fake_it: fake_it:
/* authorization takes place here */ /* Password reading and authorization takes place here.
* Note that reads (in no-echo mode) trash tty attributes.
* If we get interrupted by SIGALRM, we need to restore attrs.
*/
if (correct_password(pw)) if (correct_password(pw))
break; break;
#endif /* ENABLE_PAM */ #endif /* ENABLE_PAM */