Yeah, this patch is horrible, but necessary before a real changes to the code... Signed-off-by: Karel Zak <kzak@xxxxxxxxxx> --- login-utils/login.c | 1591 ++++++++++++++++++++++++++------------------------- 1 files changed, 806 insertions(+), 785 deletions(-) diff --git a/login-utils/login.c b/login-utils/login.c index 4ed1da3..9528908 100644 --- a/login-utils/login.c +++ b/login-utils/login.c @@ -52,11 +52,12 @@ #include <lastlog.h> #include <security/pam_appl.h> #include <security/pam_misc.h> + #ifdef HAVE_LIBAUDIT # include <libaudit.h> #endif #ifdef HAVE_CRYPT_H -#include <crypt.h> +# include <crypt.h> #endif #include "c.h" @@ -74,12 +75,12 @@ #define LOGIN_TIMEOUT 60 #ifdef USE_TTY_GROUP -# define TTY_MODE 0620 +# define TTY_MODE 0620 #else -# define TTY_MODE 0600 +# define TTY_MODE 0600 #endif -#define TTYGRPNAME "tty" /* name of group to own ttys */ +#define TTYGRPNAME "tty" /* name of group to own ttys */ /* * This bounds the time given to login. Not a define so it can @@ -90,15 +91,15 @@ int timeout = LOGIN_TIMEOUT; 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 pid_t pid; +char hostaddress[16]; /* used in checktty.c */ +char *hostname; /* idem */ +static char *username, *tty_name, *tty_number; +static pid_t pid; -static void timedout (int); -static void sigint (int); -static void motd (void); -static void dolastlog (int quiet); +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. @@ -108,8 +109,8 @@ static void dolastlog (int quiet); 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 opentty(const char *tty) +{ int i, fd, flags; fd = open(tty, O_RDWR | O_NONBLOCK); @@ -142,8 +143,8 @@ opentty(const char * tty) { /* 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) { +static void check_ttyname(char *ttyn) +{ struct stat statbuf; if (ttyn == NULL @@ -160,16 +161,16 @@ check_ttyname(char *ttyn) { #ifdef LOGIN_CHOWN_VCS /* true if the filedescriptor fd is a console tty, very Linux specific */ -static int -consoletty(int fd) { - struct stat stb; - - if ((fstat(fd, &stb) >= 0) - && (major(stb.st_rdev) == TTY_MAJOR) - && (minor(stb.st_rdev) < 64)) { - return 1; - } - return 0; +static int consoletty(int fd) +{ + struct stat stb; + + if ((fstat(fd, &stb) >= 0) + && (major(stb.st_rdev) == TTY_MAJOR) + && (minor(stb.st_rdev) < 64)) { + return 1; + } + return 0; } #endif @@ -179,7 +180,8 @@ consoletty(int fd) { * The most common login failure is to give password instead of username. */ static void -logbtmp(const char *line, const char *username, const char *hostname) { +logbtmp(const char *line, const char *username, const char *hostname) +{ struct utmp ut; struct timeval tv; @@ -191,7 +193,7 @@ logbtmp(const char *line, const char *username, const char *hostname) { strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); xstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); -#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */ +#if defined(_HAVE_UT_TV) /* in <utmpbits.h> included by <utmp.h> */ gettimeofday(&tv, NULL); ut.ut_tv.tv_sec = tv.tv_sec; ut.ut_tv.tv_usec = tv.tv_usec; @@ -199,23 +201,23 @@ logbtmp(const char *line, const char *username, const char *hostname) { { time_t t; time(&t); - ut.ut_time = t; /* ut_time is not always a time_t */ + ut.ut_time = t; /* ut_time is not always a time_t */ } #endif - ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */ + ut.ut_type = LOGIN_PROCESS; /* XXX doesn't matter */ ut.ut_pid = pid; if (hostname) { xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); if (hostaddress[0]) - memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6)); + memcpy(&ut.ut_addr_v6, hostaddress, + sizeof(ut.ut_addr_v6)); } #if HAVE_UPDWTMP /* bad luck for ancient systems */ updwtmp(_PATH_BTMP, &ut); #endif } - static int child_pid = 0; static volatile int got_sig = 0; @@ -227,21 +229,20 @@ static volatile int got_sig = 0; * Also, parent who is session leader is able (before setsid() in child) to * inform child when controlling tty goes away (e.g. modem hangup, SIGHUP). */ -static void -sig_handler(int signal) +static void sig_handler(int signal) { - if(child_pid) + if (child_pid) kill(-child_pid, signal); else got_sig = 1; - if(signal == SIGTERM) - kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */ + if (signal == SIGTERM) + kill(-child_pid, SIGHUP); /* because the shell often ignores SIGTERM */ } #ifdef HAVE_LIBAUDIT static void logaudit(const char *tty, const char *username, const char *hostname, - struct passwd *pwd, int status) + struct passwd *pwd, int status) { int audit_fd; @@ -252,26 +253,27 @@ logaudit(const char *tty, const char *username, const char *hostname, pwd = getpwnam(username); audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, - NULL, "login", username ? username : "(unknown)", - pwd ? pwd->pw_uid : (unsigned int) -1, hostname, NULL, tty, status); + NULL, "login", username ? username : "(unknown)", + pwd ? pwd->pw_uid : (unsigned int)-1, hostname, + NULL, tty, status); close(audit_fd); } -#else /* ! HAVE_LIBAUDIT */ -# define logaudit(tty, username, hostname, pwd, status) -#endif /* HAVE_LIBAUDIT */ +#else /* ! HAVE_LIBAUDIT */ +#define logaudit(tty, username, hostname, pwd, status) +#endif /* HAVE_LIBAUDIT */ /* encapsulate stupid "void **" pam_get_item() API */ -static int loginpam_get_username(pam_handle_t *pamh, char **name) +static int loginpam_get_username(pam_handle_t * pamh, char **name) { - const void *item = (void *) *name; + const void *item = (void *)*name; int rc; rc = pam_get_item(pamh, PAM_USER, &item); - *name = (char *) item; + *name = (char *)item; return rc; } -static int loginpam_err(pam_handle_t *pamh, int retcode) +static int loginpam_err(pam_handle_t * pamh, int retcode) { const char *msg = pam_strerror(pamh, retcode); @@ -290,8 +292,7 @@ static int loginpam_err(pam_handle_t *pamh, int retcode) * The open(2) seems as the surest solution. * -- kzak@xxxxxxxxxx (10-Apr-2009) */ -int -effective_access(const char *path, int mode) +int effective_access(const char *path, int mode) { int fd = open(path, mode); if (fd != -1) @@ -299,741 +300,759 @@ effective_access(const char *path, int mode) return fd == -1 ? -1 : 0; } -int -main(int argc, char **argv) +int main(int argc, char **argv) { - extern int optind; - extern char *optarg, **environ; - struct group *gr; - register int ch; - register char *p; - int fflag, hflag, pflag, cnt; - int quietlog, passwd_req; - char *domain, *ttyn; - char tbuf[PATH_MAX + 2]; - char *termenv; - char *childArgv[10]; - char *buff; - int childArgc = 0; - int retcode; - pam_handle_t *pamh = NULL; - struct pam_conv conv = { misc_conv, NULL }; - struct sigaction sa, oldsa_hup, oldsa_term; + extern int optind; + extern char *optarg, **environ; + struct group *gr; + register int ch; + register char *p; + int fflag, hflag, pflag, cnt; + int quietlog, passwd_req; + char *domain, *ttyn; + char tbuf[PATH_MAX + 2]; + char *termenv; + char *childArgv[10]; + char *buff; + int childArgc = 0; + int retcode; + 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]; + char vcsn[20], vcsan[20]; #endif - pid = getpid(); - - signal(SIGALRM, timedout); - siginterrupt(SIGALRM,1); /* we have to interrupt syscalls like ioclt() */ - alarm((unsigned int)timeout); - signal(SIGQUIT, SIG_IGN); - signal(SIGINT, SIG_IGN); - - setlocale(LC_ALL, ""); - bindtextdomain(PACKAGE, LOCALEDIR); - textdomain(PACKAGE); - - setpriority(PRIO_PROCESS, 0, 0); - initproctitle(argc, argv); - - /* - * -p is used by getty to tell login not to destroy the environment - * -f is used to skip a second login authentication - * -h is used by other servers to pass the name of the remote - * host to login so that it may be placed in utmp and wtmp - */ - gethostname(tbuf, sizeof(tbuf)); - domain = strchr(tbuf, '.'); - - username = tty_name = hostname = NULL; - fflag = hflag = pflag = 0; - passwd_req = 1; - - while ((ch = getopt(argc, argv, "fh:p")) != -1) - switch (ch) { - case 'f': - fflag = 1; - break; - - case 'h': - if (getuid()) { - fprintf(stderr, - _("login: -h for super-user only.\n")); - exit(EXIT_FAILURE); - } - hflag = 1; - if (domain && (p = strchr(optarg, '.')) && - strcasecmp(p, domain) == 0) - *p = 0; - - hostname = strdup(optarg); /* strdup: Ambrose C. Li */ - { - struct addrinfo hints, *info = NULL; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_ADDRCONFIG; - - hostaddress[0] = 0; - - if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) { - if (info->ai_family == AF_INET) { - struct sockaddr_in *sa = - (struct sockaddr_in *) info->ai_addr; - memcpy(hostaddress, &(sa->sin_addr), - sizeof(sa->sin_addr)); + pid = getpid(); + + signal(SIGALRM, timedout); + siginterrupt(SIGALRM, 1); /* we have to interrupt syscalls like ioclt() */ + alarm((unsigned int)timeout); + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + + setpriority(PRIO_PROCESS, 0, 0); + initproctitle(argc, argv); + + /* + * -p is used by getty to tell login not to destroy the environment + * -f is used to skip a second login authentication + * -h is used by other servers to pass the name of the remote + * host to login so that it may be placed in utmp and wtmp + */ + gethostname(tbuf, sizeof(tbuf)); + domain = strchr(tbuf, '.'); + + username = tty_name = hostname = NULL; + fflag = hflag = pflag = 0; + passwd_req = 1; + + while ((ch = getopt(argc, argv, "fh:p")) != -1) + switch (ch) { + case 'f': + fflag = 1; + break; + + case 'h': + if (getuid()) { + fprintf(stderr, + _("login: -h for super-user only.\n")); + exit(EXIT_FAILURE); } - else if (info->ai_family == AF_INET6) { - struct sockaddr_in6 *sa = - (struct sockaddr_in6 *) info->ai_addr; - memcpy(hostaddress, &(sa->sin6_addr), - sizeof(sa->sin6_addr)); + hflag = 1; + if (domain && (p = strchr(optarg, '.')) && + strcasecmp(p, domain) == 0) + *p = 0; + + hostname = strdup(optarg); /* strdup: Ambrose C. Li */ + { + struct addrinfo hints, *info = NULL; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_ADDRCONFIG; + + hostaddress[0] = 0; + + if (getaddrinfo(hostname, NULL, &hints, &info) + == 0 && info) { + if (info->ai_family == AF_INET) { + struct sockaddr_in *sa = + (struct sockaddr_in *)info-> + ai_addr; + memcpy(hostaddress, + &(sa->sin_addr), + sizeof(sa->sin_addr)); + } else if (info->ai_family == AF_INET6) { + struct sockaddr_in6 *sa = + (struct sockaddr_in6 *) + info->ai_addr; + memcpy(hostaddress, + &(sa->sin6_addr), + sizeof(sa->sin6_addr)); + } + freeaddrinfo(info); + } } - freeaddrinfo(info); + break; + + case 'p': + pflag = 1; + break; + + case '?': + default: + fprintf(stderr, _("usage: login [-fp] [username]\n")); + exit(EXIT_FAILURE); } - } - break; - - case 'p': - pflag = 1; - break; - - case '?': - default: - fprintf(stderr, - _("usage: login [-fp] [username]\n")); - exit(EXIT_FAILURE); - } - argc -= optind; - argv += optind; - - if (*argv) { - char *p = *argv; - username = strdup(p); - - /* wipe name - some people mistype their password here */ - /* (of course we are too late, but perhaps this helps a little ..) */ - while(*p) - *p++ = ' '; - } - - 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; - } + argc -= optind; + argv += optind; + + if (*argv) { + char *p = *argv; + username = strdup(p); + + /* wipe name - some people mistype their password here */ + /* (of course we are too late, but perhaps this helps a little ..) */ + while (*p) + *p++ = ' '; + } + + 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); + /* 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 */ + /* set pgid to pid */ + setpgrp(); + /* this means that setsid() will fail */ - { - struct termios tt, ttt; + { + struct termios tt, ttt; - tcgetattr(0, &tt); - ttt = tt; - ttt.c_cflag &= ~HUPCL; + 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 */ + /* 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); } - fchmod(0, TTY_MODE); + openlog("login", LOG_ODELAY, LOG_AUTHPRIV); - /* Kill processes left on this tty */ - tcsetattr(0,TCSAFLUSH,&ttt); - signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ - vhangup(); - signal(SIGHUP, SIG_DFL); + /* + * username is initialized to NULL + * and if specified on the command line it is set. + * Therefore, we are safe not setting it to anything + */ - /* open stdin,stdout,stderr to the tty */ - opentty(ttyn); + retcode = pam_start(hflag ? "remote" : "login", username, &conv, &pamh); + if (retcode != PAM_SUCCESS) { + warnx(_("PAM failure, aborting: %s"), + pam_strerror(pamh, retcode)); + syslog(LOG_ERR, _("Couldn't initialize PAM: %s"), + pam_strerror(pamh, retcode)); + exit(EXIT_FAILURE); + } - /* restore tty modes */ - tcsetattr(0,TCSAFLUSH,&tt); - } + /* hostname & tty are either set to NULL or their correct values, + * depending on how much we know + */ + retcode = pam_set_item(pamh, PAM_RHOST, hostname); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); - openlog("login", LOG_ODELAY, LOG_AUTHPRIV); + retcode = pam_set_item(pamh, PAM_TTY, tty_name); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); - /* - * username is initialized to NULL - * and if specified on the command line it is set. - * Therefore, we are safe not setting it to anything - */ + /* + * Andrew.Taylor@xxxxxxxxxxxxxx: Provide a user prompt to PAM + * so that the "login: " prompt gets localized. Unfortunately, + * PAM doesn't have an interface to specify the "Password: " string + * (yet). + */ + retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: ")); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); + + if (username) { + /* we need't the original username. We have to follow PAM. */ + free(username); + username = NULL; + } - retcode = pam_start(hflag?"remote":"login",username, &conv, &pamh); - if(retcode != PAM_SUCCESS) { - warnx(_("PAM failure, aborting: %s"), pam_strerror(pamh, retcode)); - syslog(LOG_ERR, _("Couldn't initialize PAM: %s"), - pam_strerror(pamh, retcode)); - exit(EXIT_FAILURE); - } - - /* hostname & tty are either set to NULL or their correct values, - * depending on how much we know - */ - retcode = pam_set_item(pamh, PAM_RHOST, hostname); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - retcode = pam_set_item(pamh, PAM_TTY, tty_name); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - /* - * Andrew.Taylor@xxxxxxxxxxxxxx: Provide a user prompt to PAM - * so that the "login: " prompt gets localized. Unfortunately, - * PAM doesn't have an interface to specify the "Password: " string - * (yet). - */ - retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: ")); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - if (username) { - /* we need't the original username. We have to follow PAM. */ - free(username); - username = NULL; - } - - /* if fflag == 1, then the user has already been authenticated */ - if (fflag && (getuid() == 0)) - passwd_req = 0; - else - passwd_req = 1; + /* if fflag == 1, then the user has already been authenticated */ + if (fflag && (getuid() == 0)) + passwd_req = 0; + else + passwd_req = 1; + + if (passwd_req == 1) { + int failcount = 0; + + /* if we didn't get a user on the command line, set it to NULL */ + loginpam_get_username(pamh, &username); + + /* there may be better ways to deal with some of these + conditions, but at least this way I don't think we'll + be giving away information... */ + /* Perhaps someday we can trust that all PAM modules will + pay attention to failure count and get rid of MAX_LOGIN_TRIES? */ + + retcode = pam_authenticate(pamh, 0); + while ((failcount++ < LOGIN_MAX_TRIES) && + ((retcode == PAM_AUTH_ERR) || + (retcode == PAM_USER_UNKNOWN) || + (retcode == PAM_CRED_INSUFFICIENT) || + (retcode == PAM_AUTHINFO_UNAVAIL))) { + loginpam_get_username(pamh, &username); + + syslog(LOG_NOTICE, + _("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); + + fprintf(stderr, _("Login incorrect\n\n")); + pam_set_item(pamh, PAM_USER, NULL); + retcode = pam_authenticate(pamh, 0); + } - if(passwd_req == 1) { - int failcount=0; - - /* if we didn't get a user on the command line, set it to NULL */ - loginpam_get_username(pamh, &username); - - /* there may be better ways to deal with some of these - conditions, but at least this way I don't think we'll - be giving away information... */ - /* Perhaps someday we can trust that all PAM modules will - pay attention to failure count and get rid of MAX_LOGIN_TRIES? */ - - retcode = pam_authenticate(pamh, 0); - while((failcount++ < LOGIN_MAX_TRIES) && - ((retcode == PAM_AUTH_ERR) || - (retcode == PAM_USER_UNKNOWN) || - (retcode == PAM_CRED_INSUFFICIENT) || - (retcode == PAM_AUTHINFO_UNAVAIL))) { - loginpam_get_username(pamh, &username); - - syslog(LOG_NOTICE,_("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); - - fprintf(stderr,_("Login incorrect\n\n")); - pam_set_item(pamh,PAM_USER,NULL); - retcode = pam_authenticate(pamh, 0); + if (is_pam_failure(retcode)) { + loginpam_get_username(pamh, &username); + + if (retcode == PAM_MAXTRIES) + syslog(LOG_NOTICE, + _ + ("TOO MANY LOGIN TRIES (%d) FROM %s FOR " + "%s, %s"), failcount, hostname, + username, pam_strerror(pamh, retcode)); + else + syslog(LOG_NOTICE, + _ + ("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); + + fprintf(stderr, _("\nLogin incorrect\n")); + pam_end(pamh, retcode); + exit(EXIT_SUCCESS); + } } - if (is_pam_failure(retcode)) { - loginpam_get_username(pamh, &username); - - if (retcode == PAM_MAXTRIES) - syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR " - "%s, %s"), failcount, hostname, username, - pam_strerror(pamh, retcode)); - else - syslog(LOG_NOTICE,_("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); - - fprintf(stderr,_("\nLogin incorrect\n")); - pam_end(pamh, retcode); - exit(EXIT_SUCCESS); + /* + * Authentication may be skipped (for example, during krlogin, rlogin, etc...), + * but it doesn't mean that we can skip other account checks. The account + * could be disabled or password expired (althought kerberos ticket is valid). + * -- kzak@xxxxxxxxxx (22-Feb-2006) + */ + retcode = pam_acct_mgmt(pamh, 0); + + if (retcode == PAM_NEW_AUTHTOK_REQD) + retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); + + /* + * Grab the user information out of the password file for future usage + * First get the username that we are actually using, though. + */ + retcode = loginpam_get_username(pamh, &username); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); + + if (!username || !*username) { + warnx(_("\nSession setup problem, abort.")); + syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."), + __FUNCTION__, __LINE__); + pam_end(pamh, PAM_SYSTEM_ERR); + exit(EXIT_FAILURE); } - } - - /* - * Authentication may be skipped (for example, during krlogin, rlogin, etc...), - * but it doesn't mean that we can skip other account checks. The account - * could be disabled or password expired (althought kerberos ticket is valid). - * -- kzak@xxxxxxxxxx (22-Feb-2006) - */ - retcode = pam_acct_mgmt(pamh, 0); - - if (retcode == PAM_NEW_AUTHTOK_REQD) - retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - /* - * Grab the user information out of the password file for future usage - * First get the username that we are actually using, though. - */ - retcode = loginpam_get_username(pamh, &username); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - if (!username || !*username) { - warnx(_("\nSession setup problem, abort.")); - syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."), - __FUNCTION__, __LINE__); - pam_end(pamh, PAM_SYSTEM_ERR); - exit(EXIT_FAILURE); - } - if (!(pwd = getpwnam(username))) { - warnx(_("\nSession setup problem, abort.")); - syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."), - username, __FUNCTION__, __LINE__); - pam_end(pamh, PAM_SYSTEM_ERR); - exit(EXIT_FAILURE); - } - - /* - * Create a copy of the pwd struct - otherwise it may get - * clobbered by PAM - */ - memcpy(&pwdcopy, pwd, sizeof(*pwd)); - pwd = &pwdcopy; - pwd->pw_name = strdup(pwd->pw_name); - pwd->pw_passwd = strdup(pwd->pw_passwd); - pwd->pw_gecos = strdup(pwd->pw_gecos); - pwd->pw_dir = strdup(pwd->pw_dir); - pwd->pw_shell = strdup(pwd->pw_shell); - if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos || - !pwd->pw_dir || !pwd->pw_shell) { - warnx(_("out of memory")); - syslog(LOG_ERR, "Out of memory"); - pam_end(pamh, PAM_SYSTEM_ERR); - exit(EXIT_FAILURE); - } - username = pwd->pw_name; - - /* - * Initialize the supplementary group list. - * This should be done before pam_setcred because - * the PAM modules might add groups during pam_setcred. - */ - if (initgroups(username, pwd->pw_gid) < 0) { - syslog(LOG_ERR, "initgroups: %m"); - warnx(_("\nSession setup problem, abort.")); - pam_end(pamh, PAM_SYSTEM_ERR); - exit(EXIT_FAILURE); - } - - retcode = pam_open_session(pamh, 0); - if (is_pam_failure(retcode)) - loginpam_err(pamh, retcode); - - retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); - if (is_pam_failure(retcode)) { - pam_close_session(pamh, 0); - loginpam_err(pamh, retcode); - } - - /* committed to login -- turn off timeout */ - alarm((unsigned int)0); - - endpwent(); - - /* This requires some explanation: As root we may not be able to - read the directory of the user if it is on an NFS mounted - filesystem. We temporarily set our effective uid to the user-uid - making sure that we keep root privs. in the real uid. - - A portable solution would require a fork(), but we rely on Linux - having the BSD setreuid() */ - { - char tmpstr[PATH_MAX]; - uid_t ruid = getuid(); - gid_t egid = getegid(); - - /* avoid snprintf - old systems do not have it, or worse, - have a libc in which snprintf is the same as sprintf */ - if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > PATH_MAX) - quietlog = 0; - else { - sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN); - setregid(-1, pwd->pw_gid); - setreuid(0, pwd->pw_uid); - quietlog = (effective_access(tmpstr, O_RDONLY) == 0); - setuid(0); /* setreuid doesn't do it alone! */ - setreuid(ruid, 0); - setregid(-1, egid); + if (!(pwd = getpwnam(username))) { + warnx(_("\nSession setup problem, abort.")); + syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."), + username, __FUNCTION__, __LINE__); + pam_end(pamh, PAM_SYSTEM_ERR); + exit(EXIT_FAILURE); } - } - /* for linux, write entries in utmp and wtmp */ - { - struct utmp ut; - struct utmp *utp; - struct timeval tv; + /* + * Create a copy of the pwd struct - otherwise it may get + * clobbered by PAM + */ + memcpy(&pwdcopy, pwd, sizeof(*pwd)); + pwd = &pwdcopy; + pwd->pw_name = strdup(pwd->pw_name); + pwd->pw_passwd = strdup(pwd->pw_passwd); + pwd->pw_gecos = strdup(pwd->pw_gecos); + pwd->pw_dir = strdup(pwd->pw_dir); + pwd->pw_shell = strdup(pwd->pw_shell); + if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos || + !pwd->pw_dir || !pwd->pw_shell) { + warnx(_("out of memory")); + syslog(LOG_ERR, "Out of memory"); + pam_end(pamh, PAM_SYSTEM_ERR); + exit(EXIT_FAILURE); + } + username = pwd->pw_name; - utmpname(_PATH_UTMP); - setutent(); - - /* Find pid in utmp. -login sometimes overwrites the runlevel entry in /var/run/utmp, -confusing sysvinit. I added a test for the entry type, and the problem -was gone. (In a runlevel entry, st_pid is not really a pid but some number -calculated from the previous and current runlevel). -Michael Riepe <michael@xxxxxxxxxxxxxxxxxxxx> - */ - while ((utp = getutent())) - if (utp->ut_pid == pid - && utp->ut_type >= INIT_PROCESS - && utp->ut_type <= DEAD_PROCESS) - break; + /* + * Initialize the supplementary group list. + * This should be done before pam_setcred because + * the PAM modules might add groups during pam_setcred. + */ + if (initgroups(username, pwd->pw_gid) < 0) { + syslog(LOG_ERR, "initgroups: %m"); + warnx(_("\nSession setup problem, abort.")); + pam_end(pamh, PAM_SYSTEM_ERR); + exit(EXIT_FAILURE); + } + + retcode = pam_open_session(pamh, 0); + if (is_pam_failure(retcode)) + loginpam_err(pamh, retcode); - /* If we can't find a pre-existing entry by pid, try by line. - BSD network daemons may rely on this. (anonymous) */ - if (utp == NULL) { - setutent(); - ut.ut_type = LOGIN_PROCESS; - strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line)); - utp = getutline(&ut); + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (is_pam_failure(retcode)) { + pam_close_session(pamh, 0); + loginpam_err(pamh, retcode); } - if (utp) { - memcpy(&ut, utp, sizeof(ut)); - } else { - /* some gettys/telnetds don't initialize utmp... */ - memset(&ut, 0, sizeof(ut)); + /* committed to login -- turn off timeout */ + alarm((unsigned int)0); + + endpwent(); + + { + /* + * Check per accout setting. + * + * This requires some explanation: As root we may not be able to + * read the directory of the user if it is on an NFS mounted + * filesystem. We temporarily set our effective uid to the user-uid + * making sure that we keep root privs. in the real uid. + * + * A portable solution would require a fork(), but we rely on Linux + * having the BSD setreuid() + */ + char tmpstr[PATH_MAX]; + uid_t ruid = getuid(); + gid_t egid = getegid(); + + /* avoid snprintf - old systems do not have it, or worse, + have a libc in which snprintf is the same as sprintf */ + if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > + PATH_MAX) + quietlog = 0; + else { + sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN); + setregid(-1, pwd->pw_gid); + setreuid(0, pwd->pw_uid); + quietlog = (effective_access(tmpstr, O_RDONLY) == 0); + setuid(0); /* setreuid doesn't do it alone! */ + setreuid(ruid, 0); + setregid(-1, egid); + } } - if (ut.ut_id[0] == 0) - strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id)); + /* for linux, write entries in utmp and wtmp */ + { + struct utmp ut; + struct utmp *utp; + struct timeval tv; + + utmpname(_PATH_UTMP); + setutent(); + + /* Find pid in utmp. + login sometimes overwrites the runlevel entry in /var/run/utmp, + confusing sysvinit. I added a test for the entry type, and the problem + was gone. (In a runlevel entry, st_pid is not really a pid but some number + calculated from the previous and current runlevel). + Michael Riepe <michael@xxxxxxxxxxxxxxxxxxxx> + */ + while ((utp = getutent())) + if (utp->ut_pid == pid + && utp->ut_type >= INIT_PROCESS + && utp->ut_type <= DEAD_PROCESS) + break; + + /* If we can't find a pre-existing entry by pid, try by line. + BSD network daemons may rely on this. (anonymous) */ + if (utp == NULL) { + setutent(); + ut.ut_type = LOGIN_PROCESS; + strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line)); + utp = getutline(&ut); + } - strncpy(ut.ut_user, username, sizeof(ut.ut_user)); - xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line)); + if (utp) { + memcpy(&ut, utp, sizeof(ut)); + } else { + /* some gettys/telnetds don't initialize utmp... */ + memset(&ut, 0, sizeof(ut)); + } + + if (ut.ut_id[0] == 0) + strncpy(ut.ut_id, 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)); #ifdef _HAVE_UT_TV /* in <utmpbits.h> included by <utmp.h> */ - gettimeofday(&tv, NULL); - ut.ut_tv.tv_sec = tv.tv_sec; - ut.ut_tv.tv_usec = tv.tv_usec; + gettimeofday(&tv, NULL); + ut.ut_tv.tv_sec = tv.tv_sec; + ut.ut_tv.tv_usec = tv.tv_usec; #else - { - time_t t; - time(&t); - ut.ut_time = t; /* ut_time is not always a time_t */ - /* glibc2 #defines it as ut_tv.tv_sec */ - } + { + time_t t; + time(&t); + ut.ut_time = t; /* ut_time is not always a time_t */ + /* glibc2 #defines it as ut_tv.tv_sec */ + } #endif - ut.ut_type = USER_PROCESS; - ut.ut_pid = pid; - if (hostname) { - xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); - if (hostaddress[0]) - memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6)); - } + ut.ut_type = USER_PROCESS; + ut.ut_pid = pid; + if (hostname) { + xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); + if (hostaddress[0]) + memcpy(&ut.ut_addr_v6, hostaddress, + sizeof(ut.ut_addr_v6)); + } - pututline(&ut); - endutent(); + pututline(&ut); + endutent(); #if HAVE_UPDWTMP - updwtmp(_PATH_WTMP, &ut); + updwtmp(_PATH_WTMP, &ut); #else - /* Probably all this locking below is just nonsense, - and the short version is OK as well. */ - { - int lf, wtmp; - if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) { - flock(lf, LOCK_EX); - if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) { - write(wtmp, (char *)&ut, sizeof(ut)); - close(wtmp); + /* Probably all this locking below is just nonsense, + and the short version is OK as well. */ + { + int lf, wtmp; + if ((lf = + open(_PATH_WTMPLOCK, O_CREAT | O_WRONLY, + 0660)) >= 0) { + flock(lf, LOCK_EX); + if ((wtmp = + open(_PATH_WTMP, + O_APPEND | O_WRONLY)) >= 0) { + write(wtmp, (char *)&ut, sizeof(ut)); + close(wtmp); + } + flock(lf, LOCK_UN); + close(lf); + } } - flock(lf, LOCK_UN); - close(lf); - } - } #endif - } + } - logaudit(tty_name, username, hostname, pwd, 1); - dolastlog(quietlog); + logaudit(tty_name, username, hostname, pwd, 1); + dolastlog(quietlog); - if (fchown(0, pwd->pw_uid, - (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) - warn(_("change terminal owner failed")); + if (fchown(0, pwd->pw_uid, + (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) + warn(_("change terminal owner failed")); - fchmod(0, TTY_MODE); + fchmod(0, TTY_MODE); #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 (chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid))) - warn(_("change terminal owner failed")); - if (chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid))) - warn(_("change terminal owner failed")); - - chmod(vcsn, TTY_MODE); - chmod(vcsan, TTY_MODE); - } + /* 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 (chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid))) + warn(_("change terminal owner failed")); + if (chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid))) + warn(_("change terminal owner failed")); + + chmod(vcsn, TTY_MODE); + chmod(vcsan, TTY_MODE); + } #endif - if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) { - syslog(LOG_ALERT, _("setgid() failed")); - exit(EXIT_FAILURE); - } - - - if (*pwd->pw_shell == '\0') - pwd->pw_shell = _PATH_BSHELL; - - /* preserve TERM even without -p flag */ - { - char *ep; - - if(!((ep = getenv("TERM")) && (termenv = strdup(ep)))) - termenv = "dumb"; - } - - /* destroy environment unless user has requested preservation */ - if (!pflag) - { - environ = (char**)malloc(sizeof(char*)); - memset(environ, 0, sizeof(char*)); - } - - setenv("HOME", pwd->pw_dir, 0); /* legal to override */ - if(pwd->pw_uid) - setenv("PATH", _PATH_DEFPATH, 1); - else - setenv("PATH", _PATH_DEFPATH_ROOT, 1); - - setenv("SHELL", pwd->pw_shell, 1); - setenv("TERM", termenv, 1); - - /* mailx will give a funny error msg if you forget this one */ - { - char tmp[PATH_MAX]; - /* avoid snprintf */ - if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < PATH_MAX) { - sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name); - setenv("MAIL",tmp,0); - } - } - - /* LOGNAME is not documented in login(1) but - HP-UX 6.5 does it. We'll not allow modifying it. - */ - setenv("LOGNAME", pwd->pw_name, 1); - - { - int i; - char ** env = pam_getenvlist(pamh); - - if (env != NULL) { - for (i=0; env[i]; i++) { - putenv(env[i]); - /* D(("env[%d] = %s", i,env[i])); */ - } + if (setgid(pwd->pw_gid) < 0 && pwd->pw_gid) { + syslog(LOG_ALERT, _("setgid() failed")); + exit(EXIT_FAILURE); } - } - setproctitle("login", username); + if (*pwd->pw_shell == '\0') + pwd->pw_shell = _PATH_BSHELL; - if (!strncmp(tty_name, "ttyS", 4)) - syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name); + /* preserve TERM even without -p flag */ + { + char *ep; - /* allow tracking of good logins. - -steve philp (sphilp@xxxxxxxxxxxxxxxxx) */ + if (!((ep = getenv("TERM")) && (termenv = strdup(ep)))) + termenv = "dumb"; + } - if (pwd->pw_uid == 0) { - if (hostname) - syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"), - tty_name, hostname); - else - syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name); - } else { - if (hostname) - syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name, - pwd->pw_name, hostname); + /* destroy environment unless user has requested preservation */ + if (!pflag) { + environ = (char **)malloc(sizeof(char *)); + memset(environ, 0, sizeof(char *)); + } + + setenv("HOME", pwd->pw_dir, 0); /* legal to override */ + if (pwd->pw_uid) + setenv("PATH", _PATH_DEFPATH, 1); else - syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name, - pwd->pw_name); - } + setenv("PATH", _PATH_DEFPATH_ROOT, 1); - if (!quietlog) { - motd(); + setenv("SHELL", pwd->pw_shell, 1); + setenv("TERM", termenv, 1); -#ifdef LOGIN_STAT_MAIL - /* - * This turns out to be a bad idea: when the mail spool - * is NFS mounted, and the NFS connection hangs, the - * login hangs, even root cannot login. - * Checking for mail should be done from the shell. + /* mailx will give a funny error msg if you forget this one */ + { + char tmp[PATH_MAX]; + /* avoid snprintf */ + if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < PATH_MAX) { + sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name); + setenv("MAIL", tmp, 0); + } + } + + /* LOGNAME is not documented in login(1) but + HP-UX 6.5 does it. We'll not allow modifying it. */ + setenv("LOGNAME", pwd->pw_name, 1); + { - struct stat st; - char *mail; + int i; + char **env = pam_getenvlist(pamh); + + if (env != NULL) { + for (i = 0; env[i]; i++) { + putenv(env[i]); + /* D(("env[%d] = %s", i,env[i])); */ + } + } + } + + setproctitle("login", username); + + if (!strncmp(tty_name, "ttyS", 4)) + syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, + pwd->pw_name); + + /* allow tracking of good logins. + -steve philp (sphilp@xxxxxxxxxxxxxxxxx) */ - mail = getenv("MAIL"); - if (mail && stat(mail, &st) == 0 && st.st_size != 0) { - if (st.st_mtime > st.st_atime) - printf(_("You have new mail.\n")); + if (pwd->pw_uid == 0) { + if (hostname) + syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"), + tty_name, hostname); else - printf(_("You have mail.\n")); - } + syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name); + } else { + if (hostname) + syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), + tty_name, pwd->pw_name, hostname); + else + syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name, + pwd->pw_name); } + + if (!quietlog) { + motd(); + +#ifdef LOGIN_STAT_MAIL + /* + * This turns out to be a bad idea: when the mail spool + * is NFS mounted, and the NFS connection hangs, the + * login hangs, even root cannot login. + * Checking for mail should be done from the shell. + */ + { + struct stat st; + char *mail; + + mail = getenv("MAIL"); + if (mail && stat(mail, &st) == 0 && st.st_size != 0) { + if (st.st_mtime > st.st_atime) + printf(_("You have new mail.\n")); + else + printf(_("You have mail.\n")); + } + } #endif - } - - signal(SIGALRM, SIG_DFL); - signal(SIGQUIT, SIG_DFL); - signal(SIGTSTP, SIG_IGN); - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - sigaction(SIGINT, &sa, NULL); - - sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */ - - /* - * detach the controlling tty - * -- we needn't the tty in parent who waits for child only. - * The child calls setsid() that detach from the tty as well. - */ - ioctl(0, TIOCNOTTY, NULL); - - /* - * We have care about SIGTERM, because leave PAM session without - * pam_close_session() is pretty bad thing. - */ - sa.sa_handler = sig_handler; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGTERM, &sa, &oldsa_term); - - closelog(); - - /* - * We must fork before setuid() because we need to call - * pam_close_session() as root. - */ - - child_pid = fork(); - if (child_pid < 0) { - /* error in fork() */ - warn(_("failure forking")); - pam_setcred(pamh, PAM_DELETE_CRED); - pam_end(pamh, pam_close_session(pamh, 0)); - exit(EXIT_FAILURE); - } - - if (child_pid) { - /* parent - wait for child to finish, then cleanup session */ - close(0); - close(1); - close(2); - sa.sa_handler = SIG_IGN; - sigaction(SIGQUIT, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - - /* wait as long as any child is there */ - while(wait(NULL) == -1 && errno == EINTR) - ; - openlog("login", LOG_ODELAY, LOG_AUTHPRIV); - pam_setcred(pamh, PAM_DELETE_CRED); - pam_end(pamh, pam_close_session(pamh, 0)); - exit(EXIT_SUCCESS); - } - - /* child */ - - /* restore to old state */ - sigaction(SIGHUP, &oldsa_hup, NULL); - sigaction(SIGTERM, &oldsa_term, NULL); - if(got_sig) - exit(EXIT_FAILURE); - - /* - * Problem: if the user's shell is a shell like ash that doesnt do - * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every - * process in the pgrp, will kill us. - */ - - /* start new session */ - setsid(); - - /* make sure we have a controlling tty */ - opentty(ttyn); - openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */ - - /* - * TIOCSCTTY: steal tty from other process group. - */ - if (ioctl(0, TIOCSCTTY, 1)) - syslog(LOG_ERR, _("TIOCSCTTY failed: %m")); - signal(SIGINT, SIG_DFL); - - /* discard permissions last so can't get killed and drop core */ - if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) { - syslog(LOG_ALERT, _("setuid() failed")); - exit(EXIT_FAILURE); - } - - /* wait until here to change directory! */ - if (chdir(pwd->pw_dir) < 0) { - warn(_("%s: change directory failed"), pwd->pw_dir); - if (chdir("/")) - exit(EXIT_FAILURE); - pwd->pw_dir = "/"; - printf(_("Logging in with home = \"/\".\n")); - } - - /* if the shell field has a space: treat it like a shell script */ - if (strchr(pwd->pw_shell, ' ')) { - buff = xmalloc(strlen(pwd->pw_shell) + 6); - - strcpy(buff, "exec "); - strcat(buff, pwd->pw_shell); - childArgv[childArgc++] = "/bin/sh"; - childArgv[childArgc++] = "-sh"; - childArgv[childArgc++] = "-c"; - childArgv[childArgc++] = buff; - } else { - tbuf[0] = '-'; - xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ? - p + 1 : pwd->pw_shell), - sizeof(tbuf)-1); - - childArgv[childArgc++] = pwd->pw_shell; - childArgv[childArgc++] = tbuf; - } - - childArgv[childArgc++] = NULL; - - execvp(childArgv[0], childArgv + 1); - - if (!strcmp(childArgv[0], "/bin/sh")) - warn(_("couldn't exec shell script")); - else - warn(_("no shell")); - - exit(EXIT_SUCCESS); + } + + signal(SIGALRM, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTSTP, SIG_IGN); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sigaction(SIGINT, &sa, NULL); + + sigaction(SIGHUP, &sa, &oldsa_hup); /* ignore when TIOCNOTTY */ + + /* + * detach the controlling tty + * -- we needn't the tty in parent who waits for child only. + * The child calls setsid() that detach from the tty as well. + */ + ioctl(0, TIOCNOTTY, NULL); + + /* + * We have care about SIGTERM, because leave PAM session without + * pam_close_session() is pretty bad thing. + */ + sa.sa_handler = sig_handler; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGTERM, &sa, &oldsa_term); + + closelog(); + + /* + * We must fork before setuid() because we need to call + * pam_close_session() as root. + */ + + child_pid = fork(); + if (child_pid < 0) { + /* error in fork() */ + warn(_("failure forking")); + pam_setcred(pamh, PAM_DELETE_CRED); + pam_end(pamh, pam_close_session(pamh, 0)); + exit(EXIT_FAILURE); + } + + if (child_pid) { + /* parent - wait for child to finish, then cleanup session */ + close(0); + close(1); + close(2); + sa.sa_handler = SIG_IGN; + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + + /* wait as long as any child is there */ + while (wait(NULL) == -1 && errno == EINTR) ; + openlog("login", LOG_ODELAY, LOG_AUTHPRIV); + pam_setcred(pamh, PAM_DELETE_CRED); + pam_end(pamh, pam_close_session(pamh, 0)); + exit(EXIT_SUCCESS); + } + + /* child */ + + /* restore to old state */ + sigaction(SIGHUP, &oldsa_hup, NULL); + sigaction(SIGTERM, &oldsa_term, NULL); + if (got_sig) + exit(EXIT_FAILURE); + + /* + * Problem: if the user's shell is a shell like ash that doesnt do + * setsid() or setpgrp(), then a ctrl-\, sending SIGQUIT to every + * process in the pgrp, will kill us. + */ + + /* start new session */ + setsid(); + + /* make sure we have a controlling tty */ + opentty(ttyn); + openlog("login", LOG_ODELAY, LOG_AUTHPRIV); /* reopen */ + + /* + * TIOCSCTTY: steal tty from other process group. + */ + if (ioctl(0, TIOCSCTTY, 1)) + syslog(LOG_ERR, _("TIOCSCTTY failed: %m")); + signal(SIGINT, SIG_DFL); + + /* discard permissions last so can't get killed and drop core */ + if (setuid(pwd->pw_uid) < 0 && pwd->pw_uid) { + syslog(LOG_ALERT, _("setuid() failed")); + exit(EXIT_FAILURE); + } + + /* wait until here to change directory! */ + if (chdir(pwd->pw_dir) < 0) { + warn(_("%s: change directory failed"), pwd->pw_dir); + if (chdir("/")) + exit(EXIT_FAILURE); + pwd->pw_dir = "/"; + printf(_("Logging in with home = \"/\".\n")); + } + + /* if the shell field has a space: treat it like a shell script */ + if (strchr(pwd->pw_shell, ' ')) { + buff = xmalloc(strlen(pwd->pw_shell) + 6); + + strcpy(buff, "exec "); + strcat(buff, pwd->pw_shell); + childArgv[childArgc++] = "/bin/sh"; + childArgv[childArgc++] = "-sh"; + childArgv[childArgc++] = "-c"; + childArgv[childArgc++] = buff; + } else { + tbuf[0] = '-'; + xstrncpy(tbuf + 1, ((p = strrchr(pwd->pw_shell, '/')) ? + p + 1 : pwd->pw_shell), sizeof(tbuf) - 1); + + childArgv[childArgc++] = pwd->pw_shell; + childArgv[childArgc++] = tbuf; + } + + childArgv[childArgc++] = NULL; + + execvp(childArgv[0], childArgv + 1); + + if (!strcmp(childArgv[0], "/bin/sh")) + warn(_("couldn't exec shell script")); + else + warn(_("no shell")); + + exit(EXIT_SUCCESS); } /* @@ -1046,19 +1065,19 @@ Michael Riepe <michael@xxxxxxxxxxxxxxxxxxxx> * the process just exits if the second timeout expires. */ -static void -timedout2(int sig __attribute__((__unused__))) { +static void timedout2(int sig __attribute__ ((__unused__))) +{ struct termios ti; /* reset echo */ tcgetattr(0, &ti); ti.c_lflag |= ECHO; tcsetattr(0, TCSANOW, &ti); - exit(EXIT_SUCCESS); /* %% */ + exit(EXIT_SUCCESS); /* %% */ } -static void -timedout(int sig __attribute__((__unused__))) { +static void timedout(int sig __attribute__ ((__unused__))) +{ signal(SIGALRM, timedout2); alarm(10); /* TRANSLATORS: The standard value for %d is 60. */ @@ -1070,75 +1089,77 @@ timedout(int sig __attribute__((__unused__))) { jmp_buf motdinterrupt; -void -motd(void) { - int fd, nchars; - void (*oldint)(int); - char tbuf[8192]; - - if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) - return; - oldint = signal(SIGINT, sigint); - if (setjmp(motdinterrupt) == 0) - while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) { - if (write(fileno(stdout), tbuf, nchars)) { - ; /* glibc warn_unused_result */ - } - } - signal(SIGINT, oldint); - close(fd); +void motd(void) +{ + int fd, nchars; + void (*oldint) (int); + char tbuf[8192]; + + if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) + return; + oldint = signal(SIGINT, sigint); + if (setjmp(motdinterrupt) == 0) + while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) { + if (write(fileno(stdout), tbuf, nchars)) { + ; /* glibc warn_unused_result */ + } + } + signal(SIGINT, oldint); + close(fd); } -void -sigint(int sig __attribute__((__unused__))) { - longjmp(motdinterrupt, 1); +void sigint(int sig __attribute__ ((__unused__))) +{ + longjmp(motdinterrupt, 1); } -void -dolastlog(int quiet) { - struct lastlog ll; - int fd; - - if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { - lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET); - if (!quiet) { - if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && - ll.ll_time != 0) { - time_t ll_time = (time_t) ll.ll_time; - - printf(_("Last login: %.*s "), - 24-5, ctime(&ll_time)); - - if (*ll.ll_host != '\0') - printf(_("from %.*s\n"), - (int)sizeof(ll.ll_host), ll.ll_host); - else - printf(_("on %.*s\n"), - (int)sizeof(ll.ll_line), ll.ll_line); - } - lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), SEEK_SET); - } - memset((char *)&ll, 0, sizeof(ll)); +void dolastlog(int quiet) +{ + struct lastlog ll; + int fd; + + if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { + lseek(fd, (off_t) pwd->pw_uid * sizeof(ll), SEEK_SET); + if (!quiet) { + if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && + ll.ll_time != 0) { + time_t ll_time = (time_t) ll.ll_time; + + printf(_("Last login: %.*s "), + 24 - 5, ctime(&ll_time)); + + if (*ll.ll_host != '\0') + printf(_("from %.*s\n"), + (int)sizeof(ll.ll_host), + ll.ll_host); + else + printf(_("on %.*s\n"), + (int)sizeof(ll.ll_line), + ll.ll_line); + } + lseek(fd, (off_t) pwd->pw_uid * sizeof(ll), SEEK_SET); + } + memset((char *)&ll, 0, sizeof(ll)); - { - time_t t; - time(&t); - ll.ll_time = t; /* ll_time is always 32bit */ - } + { + time_t t; + time(&t); + ll.ll_time = t; /* ll_time is always 32bit */ + } - xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line)); - if (hostname) - xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); + xstrncpy(ll.ll_line, tty_name, sizeof(ll.ll_line)); + if (hostname) + xstrncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); - if (write(fd, (char *)&ll, sizeof(ll)) < 0) - warn(_("write lastlog failed")); - close(fd); - } + if (write(fd, (char *)&ll, sizeof(ll)) < 0) + warn(_("write lastlog failed")); + close(fd); + } } /* Should not be called from PAM code... */ -void -sleepexit(int eval) { - sleep(LOGIN_EXIT_TIMEOUT); - exit(eval); +void sleepexit(int eval) +{ + sleep(LOGIN_EXIT_TIMEOUT); + exit(eval); } -- 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