- move all tty_* variables to struct login_context - move all tty initialization code to init_tty() - LOG_ERR on failed tty ch{mod,own} [based on SUSE pam_login - note that we don't write any this error to stderr] Signed-off-by: Karel Zak <kzak@xxxxxxxxxx> --- login-utils/login.c | 228 +++++++++++++++++++++++++++------------------------ 1 files changed, 122 insertions(+), 106 deletions(-) diff --git a/login-utils/login.c b/login-utils/login.c index 9528908..e1bf81c 100644 --- a/login-utils/login.c +++ b/login-utils/login.c @@ -81,6 +81,21 @@ #endif #define TTYGRPNAME "tty" /* name of group to own ttys */ +#define VCS_PATH_MAX 64 + +/* + * Login control struct + */ +struct login_context { + const char *tty_path; /* ttyname() return value */ + const char *tty_name; /* tty_path without /dev prefix */ + const char *tty_number; /* end of the tty_path */ + +#ifdef LOGIN_CHOWN_VCS + char vcsn[VCS_PATH_MAX]; /* virtual console name */ + char vcsan[VCS_PATH_MAX]; +#endif +}; /* * This bounds the time given to login. Not a define so it can @@ -93,23 +108,26 @@ struct passwd *pwd; static struct passwd pwdcopy; char hostaddress[16]; /* used in checktty.c */ char *hostname; /* idem */ -static char *username, *tty_name, *tty_number; +static char *username; static pid_t pid; static void timedout(int); static void sigint(int); static void motd(void); -static void dolastlog(int quiet); - -/* Nice and simple code provided by Linus Torvalds 16-Feb-93 */ -/* Nonblocking stuff by Maciej W. Rozycki, macro@xxxxxxxxxxxxx, 1999. - He writes: "Login performs open() on a tty in a blocking mode. - In some cases it may make login wait in open() for carrier infinitely, - for example if the line is a simplistic case of a three-wire serial - connection. I believe login should open the line in the non-blocking mode - leaving the decision to make a connection to getty (where it actually - belongs). */ -static void opentty(const char *tty) +static void dolastlog(struct login_context *cxt, int quiet); + +/* + * Nice and simple code provided by Linus Torvalds 16-Feb-93 + * Nonblocking stuff by Maciej W. Rozycki, macro@xxxxxxxxxxxxx, 1999. + * + * He writes: "Login performs open() on a tty in a blocking mode. + * In some cases it may make login wait in open() for carrier infinitely, + * for example if the line is a simplistic case of a three-wire serial + * connection. I believe login should open the line in the non-blocking mode + * leaving the decision to make a connection to getty (where it actually + * belongs). + */ +static void open_tty(const char *tty) { int i, fd, flags; @@ -138,30 +156,81 @@ static void opentty(const char *tty) close(fd); } -/* In case login is suid it was possible to use a hardlink as stdin - and exploit races for a local root exploit. (Wojciech Purczynski). */ -/* More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn); - here ttyname() might return "/tmp/x", a hardlink to a pseudotty. */ -/* All of this is a problem only when login is suid, which it isnt. */ -static void check_ttyname(char *ttyn) +/* + * Reads the currect terminal path and initialize cxt->tty_* variables. + */ +static void init_tty(struct login_context *cxt) { - struct stat statbuf; + const char *p; + struct stat st; + struct termios tt, ttt; + + cxt->tty_path = ttyname(0); /* libc calls istty() here */ - if (ttyn == NULL - || *ttyn == '\0' - || lstat(ttyn, &statbuf) - || !S_ISCHR(statbuf.st_mode) - || (statbuf.st_nlink > 1 && strncmp(ttyn, "/dev/", 5)) - || (access(ttyn, R_OK | W_OK) != 0)) { + /* + * In case login is suid it was possible to use a hardlink as stdin + * and exploit races for a local root exploit. (Wojciech Purczynski). + * + * More precisely, the problem is ttyn := ttyname(0); ...; chown(ttyn); + * here ttyname() might return "/tmp/x", a hardlink to a pseudotty. + * All of this is a problem only when login is suid, which it isnt. + */ + if (!cxt->tty_path || !*cxt->tty_path || + lstat(cxt->tty_path, &st) != 0 || !S_ISCHR(st.st_mode) || + (st.st_nlink > 1 && strncmp(cxt->tty_path, "/dev/", 5)) || + access(cxt->tty_path, R_OK | W_OK) != 0) { syslog(LOG_ERR, _("FATAL: bad tty")); sleepexit(EXIT_FAILURE); } + + if (strncmp(cxt->tty_path, "/dev/", 5) == 0) + cxt->tty_name = cxt->tty_path + 5; + else + cxt->tty_name = cxt->tty_path; + + for (p = cxt->tty_name; p && *p; p++) { + if (isdigit(*p)) { + cxt->tty_number = p; + break; + } + } + +#ifdef LOGIN_CHOWN_VCS + /* find names of Virtual Console devices, for later mode change */ + snprintf(cxt->vcsn, sizeof(cxt->vcsn), "/dev/vcs%s", cxt->tty_number); + snprintf(cxt->vcsan, sizeof(cxt->vcsan), "/dev/vcsa%s", cxt->tty_number); +#endif + + tcgetattr(0, &tt); + ttt = tt; + ttt.c_cflag &= ~HUPCL; + + if ((fchown(0, 0, 0) || fchmod(0, TTY_MODE)) && errno != EROFS) { + + syslog(LOG_ERR, _("FATAL: %s: change permissions failed: %m"), + cxt->tty_path); + sleepexit(EXIT_FAILURE); + } + + /* Kill processes left on this tty */ + tcsetattr(0, TCSAFLUSH, &ttt); + + signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ + vhangup(); + signal(SIGHUP, SIG_DFL); + + /* open stdin,stdout,stderr to the tty */ + open_tty(cxt->tty_path); + + /* restore tty modes */ + tcsetattr(0, TCSAFLUSH, &tt); } + #ifdef LOGIN_CHOWN_VCS /* true if the filedescriptor fd is a console tty, very Linux specific */ -static int consoletty(int fd) +static int is_consoletty(int fd) { struct stat stb; @@ -309,7 +378,7 @@ int main(int argc, char **argv) register char *p; int fflag, hflag, pflag, cnt; int quietlog, passwd_req; - char *domain, *ttyn; + char *domain; char tbuf[PATH_MAX + 2]; char *termenv; char *childArgv[10]; @@ -319,10 +388,9 @@ int main(int argc, char **argv) pam_handle_t *pamh = NULL; struct pam_conv conv = { misc_conv, NULL }; struct sigaction sa, oldsa_hup, oldsa_term; -#ifdef LOGIN_CHOWN_VCS - char vcsn[20], vcsan[20]; -#endif + struct login_context cxt; + memset(&cxt, 0, sizeof(cxt)); pid = getpid(); signal(SIGALRM, timedout); @@ -347,7 +415,7 @@ int main(int argc, char **argv) gethostname(tbuf, sizeof(tbuf)); domain = strchr(tbuf, '.'); - username = tty_name = hostname = NULL; + username = hostname = NULL; fflag = hflag = pflag = 0; passwd_req = 1; @@ -424,64 +492,12 @@ int main(int argc, char **argv) for (cnt = getdtablesize(); cnt > 2; cnt--) close(cnt); - /* note that libc checks that the file descriptor is a terminal, so we don't - * have to call isatty() here */ - ttyn = ttyname(0); - check_ttyname(ttyn); - - if (strncmp(ttyn, "/dev/", 5) == 0) - tty_name = ttyn + 5; - else - tty_name = ttyn; - - if (strncmp(ttyn, "/dev/tty", 8) == 0) - tty_number = ttyn + 8; - else { - char *p = ttyn; - while (*p && !isdigit(*p)) - p++; - tty_number = p; - } - -#ifdef LOGIN_CHOWN_VCS - /* find names of Virtual Console devices, for later mode change */ - snprintf(vcsn, sizeof(vcsn), "/dev/vcs%s", tty_number); - snprintf(vcsan, sizeof(vcsan), "/dev/vcsa%s", tty_number); -#endif - - /* set pgid to pid */ - setpgrp(); - /* this means that setsid() will fail */ - - { - struct termios tt, ttt; - - tcgetattr(0, &tt); - ttt = tt; - ttt.c_cflag &= ~HUPCL; - - /* These can fail, e.g. with ttyn on a read-only filesystem */ - if (fchown(0, 0, 0)) { - ; /* glibc warn_unused_result */ - } - - fchmod(0, TTY_MODE); - - /* Kill processes left on this tty */ - tcsetattr(0, TCSAFLUSH, &ttt); - signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ - vhangup(); - signal(SIGHUP, SIG_DFL); - - /* open stdin,stdout,stderr to the tty */ - opentty(ttyn); - - /* restore tty modes */ - tcsetattr(0, TCSAFLUSH, &tt); - } + setpgrp(); /* set pgid to pid this means that setsid() will fail */ openlog("login", LOG_ODELAY, LOG_AUTHPRIV); + init_tty(&cxt); + /* * username is initialized to NULL * and if specified on the command line it is set. @@ -504,7 +520,7 @@ int main(int argc, char **argv) if (is_pam_failure(retcode)) loginpam_err(pamh, retcode); - retcode = pam_set_item(pamh, PAM_TTY, tty_name); + retcode = pam_set_item(pamh, PAM_TTY, cxt.tty_name); if (is_pam_failure(retcode)) loginpam_err(pamh, retcode); @@ -554,8 +570,8 @@ int main(int argc, char **argv) _("FAILED LOGIN %d FROM %s FOR %s, %s"), failcount, hostname, username, pam_strerror(pamh, retcode)); - logbtmp(tty_name, username, hostname); - logaudit(tty_name, username, hostname, NULL, 0); + logbtmp(cxt.tty_name, username, hostname); + logaudit(cxt.tty_name, username, hostname, NULL, 0); fprintf(stderr, _("Login incorrect\n\n")); pam_set_item(pamh, PAM_USER, NULL); @@ -577,8 +593,8 @@ int main(int argc, char **argv) ("FAILED LOGIN SESSION FROM %s FOR %s, %s"), hostname, username, pam_strerror(pamh, retcode)); - logbtmp(tty_name, username, hostname); - logaudit(tty_name, username, hostname, NULL, 0); + logbtmp(cxt.tty_name, username, hostname); + logaudit(cxt.tty_name, username, hostname, NULL, 0); fprintf(stderr, _("\nLogin incorrect\n")); pam_end(pamh, retcode); @@ -728,7 +744,7 @@ int main(int argc, char **argv) if (utp == NULL) { setutent(); ut.ut_type = LOGIN_PROCESS; - strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line)); + strncpy(ut.ut_line, cxt.tty_name, sizeof(ut.ut_line)); utp = getutline(&ut); } @@ -740,10 +756,10 @@ int main(int argc, char **argv) } if (ut.ut_id[0] == 0) - strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id)); + strncpy(ut.ut_id, cxt.tty_number, sizeof(ut.ut_id)); strncpy(ut.ut_user, username, sizeof(ut.ut_user)); - xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line)); + xstrncpy(ut.ut_line, cxt.tty_name, sizeof(ut.ut_line)); #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */ gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; @@ -792,8 +808,8 @@ int main(int argc, char **argv) #endif } - logaudit(tty_name, username, hostname, pwd, 1); - dolastlog(quietlog); + logaudit(cxt.tty_name, username, hostname, pwd, 1); + dolastlog(&cxt, quietlog); if (fchown(0, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) @@ -804,7 +820,7 @@ int main(int argc, char **argv) #ifdef LOGIN_CHOWN_VCS /* if tty is one of the VC's then change owner and mode of the special /dev/vcs devices as well */ - if (consoletty(0)) { + if (is_consoletty(0)) { if (chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid))) warn(_("change terminal owner failed")); @@ -876,8 +892,8 @@ int main(int argc, char **argv) setproctitle("login", username); - if (!strncmp(tty_name, "ttyS", 4)) - syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, + if (!strncmp(cxt.tty_name, "ttyS", 4)) + syslog(LOG_INFO, _("DIALUP AT %s BY %s"), cxt.tty_name, pwd->pw_name); /* allow tracking of good logins. @@ -886,15 +902,15 @@ int main(int argc, char **argv) if (pwd->pw_uid == 0) { if (hostname) syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"), - tty_name, hostname); + cxt.tty_name, hostname); else - syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name); + syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), cxt.tty_name); } else { if (hostname) syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), - tty_name, pwd->pw_name, hostname); + cxt.tty_name, pwd->pw_name, hostname); else - syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name, + syslog(LOG_INFO, _("LOGIN ON %s BY %s"), cxt.tty_name, pwd->pw_name); } @@ -999,7 +1015,7 @@ int main(int argc, char **argv) setsid(); /* make sure we have a controlling tty */ - opentty(ttyn); + open_tty(cxt.tty_path); openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */ /* @@ -1113,7 +1129,7 @@ void sigint(int sig __attribute__ ((__unused__))) longjmp(motdinterrupt, 1); } -void dolastlog(int quiet) +void dolastlog(struct login_context *cxt, int quiet) { struct lastlog ll; int fd; @@ -1147,7 +1163,7 @@ void dolastlog(int quiet) ll.ll_time = t; /* ll_time is always 32bit */ } - xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line)); + xstrncpy(ll.ll_line, cxt->tty_name, sizeof(ll.ll_line)); if (hostname) xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); -- 1.7.6.4 -- To unsubscribe from this list: send the line "unsubscribe util-linux" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html