| /* This program is derived from 4.3 BSD software and is |
| subject to the copyright notice below. |
| |
| The port to HP-UX has been motivated by the incapability |
| of 'rlogin'/'rlogind' as per HP-UX 6.5 (and 7.0) to transfer window sizes. |
| |
| Changes: |
| |
| - General HP-UX portation. Use of facilities not available |
| in HP-UX (e.g. setpriority) has been eliminated. |
| Utmp/wtmp handling has been ported. |
| |
| - The program uses BSD command line options to be used |
| in connection with e.g. 'rlogind' i.e. 'new login'. |
| |
| - HP features left out: logging of bad login attempts in /etc/btmp, |
| they are sent to syslog |
| |
| password expiry |
| |
| '*' as login shell, add it if you need it |
| |
| - BSD features left out: quota checks |
| password expiry |
| analysis of terminal type (tset feature) |
| |
| - BSD features thrown in: Security logging to syslogd. |
| This requires you to have a (ported) syslog |
| system -- 7.0 comes with syslog |
| |
| 'Lastlog' feature. |
| |
| - A lot of nitty gritty details has been adjusted in favour of |
| HP-UX, e.g. /etc/securetty, default paths and the environment |
| variables assigned by 'login'. |
| |
| - We do *nothing* to setup/alter tty state, under HP-UX this is |
| to be done by getty/rlogind/telnetd/some one else. |
| |
| Michael Glad (glad@daimi.dk) |
| Computer Science Department |
| Aarhus University |
| Denmark |
| |
| 1990-07-04 |
| |
| 1991-09-24 glad@daimi.aau.dk: HP-UX 8.0 port: |
| - now explictly sets non-blocking mode on descriptors |
| - strcasecmp is now part of HP-UX |
| |
| 1992-02-05 poe@daimi.aau.dk: Ported the stuff to Linux 0.12 |
| From 1992 till now (1997) this code for Linux has been maintained at |
| ftp.daimi.aau.dk:/pub/linux/poe/ |
| */ |
| |
| /* |
| * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms are permitted |
| * provided that the above copyright notice and this paragraph are |
| * duplicated in all such forms and that any documentation, |
| * advertising materials, and other materials related to such |
| * distribution and use acknowledge that the software was developed |
| * by the University of California, Berkeley. The name of the |
| * University may not be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #ifndef lint |
| char copyright[] = |
| "@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\ |
| All rights reserved.\n"; |
| #endif /* not lint */ |
| |
| #ifndef lint |
| static char sccsid[] = "@(#)login.c 5.40 (Berkeley) 5/9/89"; |
| #endif /* not lint */ |
| |
| /* |
| * login [ name ] |
| * login -h hostname (for telnetd, etc.) |
| * login -f name (for pre-authenticated login: datakit, xterm, etc.) |
| */ |
| |
| #define _GNU_SOURCE /* to get definition of snprintf */ |
| |
| /* #define TESTING */ |
| |
| #ifdef TESTING |
| #include "param.h" |
| #else |
| #include <sys/param.h> |
| #endif |
| |
| #include <stdio.h> |
| #include <ctype.h> |
| #include <unistd.h> |
| #include <getopt.h> |
| #include <memory.h> |
| #include <time.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| #include <sys/file.h> |
| #include <termios.h> |
| #include <string.h> |
| #define index strchr |
| #define rindex strrchr |
| #include <sys/ioctl.h> |
| #include <sys/wait.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <grp.h> |
| #include <pwd.h> |
| #include <utmp.h> |
| #include <setjmp.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/syslog.h> |
| #include <sys/sysmacros.h> |
| #include <netdb.h> |
| |
| #ifdef __linux__ |
| # include <sys/sysmacros.h> |
| # include <linux/major.h> |
| #endif |
| |
| #ifdef TESTING |
| # include "utmp.h" |
| #else |
| # include <utmp.h> |
| #endif |
| |
| #ifdef SHADOW_PWD |
| # include <shadow.h> |
| #endif |
| |
| #ifdef USE_PAM |
| # include <security/pam_appl.h> |
| # include <security/pam_misc.h> |
| # define PAM_MAX_LOGIN_TRIES 3 |
| # define PAM_FAIL_CHECK if (retcode != PAM_SUCCESS) { \ |
| fprintf(stderr,"\n%s\n",pam_strerror(retcode)); \ |
| syslog(LOG_ERR,"%s",pam_strerror(retcode)); \ |
| pam_end(pamh, retcode); exit(1); \ |
| } |
| # define PAM_END { retcode = pam_close_session(pamh,0); \ |
| pam_end(pamh,retcode); } |
| # define PAM_END { \ |
| retcode = pam_close_session(pamh,0); pam_end(pamh,retcode); \ |
| } |
| #endif |
| |
| #ifndef __linux__ |
| # include <tzfile.h> |
| #endif |
| #include <lastlog.h> |
| |
| #define SLEEP_EXIT_TIMEOUT 5 |
| |
| #if 0 |
| /* from before we had a lastlog.h file in linux */ |
| struct lastlog |
| { long ll_time; |
| char ll_line[12]; |
| char ll_host[16]; |
| }; |
| #endif |
| |
| #include "pathnames.h" |
| |
| #define P_(s) () |
| void opentty P_((const char *tty)); |
| void getloginname P_((void)); |
| void timedout P_((void)); |
| int rootterm P_((char *ttyn)); |
| void motd P_((void)); |
| void sigint P_((void)); |
| void checknologin P_((void)); |
| void dolastlog P_((int quiet)); |
| void badlogin P_((const char *name)); |
| char *stypeof P_((char *ttyid)); |
| void checktty P_((char *user, char *tty, struct passwd *pwd)); |
| void sleepexit P_((int eval)); |
| #ifdef CRYPTOCARD |
| int cryptocard P_((void)); |
| #endif |
| #undef P_ |
| |
| #ifdef KERBEROS |
| #include <kerberos/krb.h> |
| #include <sys/termios.h> |
| char realm[REALM_SZ]; |
| int kerror = KSUCCESS, notickets = 1; |
| #endif |
| |
| #ifdef USE_TTY_GROUP |
| # define TTY_MODE 0620 |
| #else |
| # define TTY_MODE 0600 |
| #endif |
| |
| #define TTYGRPNAME "tty" /* name of group to own ttys */ |
| /**# define TTYGRPNAME "other" **/ |
| |
| #ifndef MAXPATHLEN |
| # define MAXPATHLEN 1024 |
| #endif |
| |
| /* |
| * This bounds the time given to login. Not a define so it can |
| * be patched on machines where it's too small. |
| */ |
| #ifndef __linux__ |
| int timeout = 300; |
| #else |
| int timeout = 60; |
| #endif |
| |
| struct passwd *pwd; |
| int failures = 1; |
| char term[64], *hostname, *username, *tty; |
| struct hostent hostaddress; |
| char thishost[100]; |
| |
| #ifndef __linux__ |
| struct sgttyb sgttyb; |
| struct tchars tc = { |
| CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK |
| }; |
| struct ltchars ltc = { |
| CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT |
| }; |
| #endif |
| |
| const char *months[] = |
| { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", |
| "Sep", "Oct", "Nov", "Dec" }; |
| |
| /* provided by Linus Torvalds 16-Feb-93 */ |
| void |
| opentty(const char * tty) |
| { |
| int i; |
| int fd = open(tty, O_RDWR); |
| |
| for (i = 0 ; i < fd ; i++) |
| close(i); |
| for (i = 0 ; i < 3 ; i++) |
| dup2(fd, i); |
| if (fd >= 3) |
| close(fd); |
| } |
| |
| /* true if the filedescriptor fd is a console tty, very Linux specific */ |
| static int |
| consoletty(int fd) |
| { |
| #ifdef __linux__ |
| struct stat stb; |
| |
| if ((fstat(fd, &stb) >= 0) |
| && (major(stb.st_rdev) == TTY_MAJOR) |
| && (minor(stb.st_rdev) < 64)) { |
| return 1; |
| } |
| #endif |
| return 0; |
| } |
| |
| |
| int |
| main(argc, argv) |
| int argc; |
| char **argv; |
| { |
| extern int errno, optind; |
| extern char *optarg, **environ; |
| struct group *gr; |
| register int ch; |
| register char *p; |
| int ask, fflag, hflag, pflag, cnt; |
| int quietlog, passwd_req, ioctlval; |
| char *domain, *salt, *ttyn, *pp; |
| char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; |
| char *ctime(), *ttyname(), *stypeof(); |
| time_t time(); |
| void timedout(); |
| char *termenv; |
| char vcsn[20], vcsan[20]; |
| char * childArgv[10]; |
| char * buff; |
| int childArgc = 0; |
| int error = 0; |
| #ifdef USE_PAM |
| int retcode; |
| pam_handle_t *pamh = NULL; |
| struct pam_conv conv = { misc_conv, NULL }; |
| pid_t childPid; |
| int childStatus; |
| void * oldSigHandler; |
| #endif |
| |
| |
| #ifdef __linux__ |
| char tmp[100]; |
| /* Just as arbitrary as mountain time: */ |
| /* (void)setenv("TZ", "MET-1DST",0); */ |
| #endif |
| |
| signal(SIGALRM, timedout); |
| alarm((unsigned int)timeout); |
| signal(SIGQUIT, SIG_IGN); |
| signal(SIGINT, SIG_IGN); |
| |
| setpriority(PRIO_PROCESS, 0, 0); |
| #ifdef HAVE_QUOTA |
| quota(Q_SETUID, 0, 0, 0); |
| #endif |
| |
| /* |
| * -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)); |
| strncpy(thishost, tbuf, sizeof(thishost)-1); |
| thishost[sizeof(thishost)-1] = 0; |
| domain = index(tbuf, '.'); |
| |
| username = tty = hostname = NULL; |
| fflag = hflag = pflag = 0; |
| passwd_req = 1; |
| while ((ch = getopt(argc, argv, "fh:p")) != EOF) |
| switch (ch) { |
| case 'f': |
| fflag = 1; |
| break; |
| |
| case 'h': |
| if (getuid()) { |
| fprintf(stderr, |
| "login: -h for super-user only.\n"); |
| exit(1); |
| } |
| hflag = 1; |
| if (domain && (p = index(optarg, '.')) && |
| strcasecmp(p, domain) == 0) |
| *p = 0; |
| hostname = optarg; |
| { |
| struct hostent *he = gethostbyname(hostname); |
| if (he) { |
| memcpy(&hostaddress, he, sizeof(hostaddress)); |
| } else { |
| memset(&hostaddress, 0, sizeof(hostaddress)); |
| } |
| } |
| break; |
| |
| case 'p': |
| pflag = 1; |
| break; |
| |
| case '?': |
| default: |
| fprintf(stderr, |
| "usage: login [-fp] [username]\n"); |
| exit(1); |
| } |
| argc -= optind; |
| argv += optind; |
| if (*argv) { |
| username = *argv; |
| ask = 0; |
| } else |
| ask = 1; |
| |
| #ifndef __linux__ |
| ioctlval = 0; |
| ioctl(0, TIOCLSET, &ioctlval); |
| ioctl(0, TIOCNXCL, 0); |
| fcntl(0, F_SETFL, ioctlval); |
| ioctl(0, TIOCGETP, &sgttyb); |
| sgttyb.sg_erase = CERASE; |
| sgttyb.sg_kill = CKILL; |
| ioctl(0, TIOCSLTC, <c); |
| ioctl(0, TIOCSETC, &tc); |
| ioctl(0, TIOCSETP, &sgttyb); |
| |
| /* |
| * Be sure that we're in |
| * blocking mode!!! |
| * This is really for HPUX |
| */ |
| ioctlval = 0; |
| ioctl(0, FIOSNBIO, &ioctlval); |
| #endif |
| |
| for (cnt = getdtablesize(); cnt > 2; cnt--) |
| close(cnt); |
| |
| ttyn = ttyname(0); |
| if (ttyn == NULL || *ttyn == '\0') { |
| snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); |
| ttyn = tname; |
| } |
| |
| /* find names of Virtual Console devices, for later mode change */ |
| { |
| char *p = ttyn; |
| /* find number of tty */ |
| while (*p && !isdigit(*p)) p++; |
| |
| strcpy(vcsn, "/dev/vcs"); strcat(vcsn, p); |
| strcpy(vcsan, "/dev/vcsa"); strcat(vcsan, p); |
| } |
| |
| setpgrp(); |
| |
| { |
| struct termios tt, ttt; |
| |
| tcgetattr(0, &tt); |
| ttt = tt; |
| ttt.c_cflag &= ~HUPCL; |
| |
| if((chown(ttyn, 0, 0) == 0) && (chmod(ttyn, 0622) == 0)) { |
| tcsetattr(0,TCSAFLUSH,&ttt); |
| signal(SIGHUP, SIG_IGN); /* so vhangup() wont kill us */ |
| vhangup(); |
| signal(SIGHUP, SIG_DFL); |
| } |
| |
| setsid(); |
| |
| /* re-open stdin,stdout,stderr after vhangup() closed them */ |
| /* if it did, after 0.99.5 it doesn't! */ |
| opentty(ttyn); |
| tcsetattr(0,TCSAFLUSH,&tt); |
| } |
| |
| if ((tty = rindex(ttyn, '/'))) |
| ++tty; |
| else |
| tty = ttyn; |
| |
| openlog("login", LOG_ODELAY, LOG_AUTHPRIV); |
| |
| #ifdef USE_PAM |
| /* username is initialized to NULL |
| and if specified on the command line it is set. |
| Therefore, we are safe not setting it to anything |
| */ |
| |
| retcode = pam_start("login",username, &conv, &pamh); |
| if(retcode != PAM_SUCCESS) { |
| fprintf(stderr,"login: PAM Failure, aborting: %s\n", |
| pam_strerror(retcode)); |
| syslog(LOG_ERR,"Couldn't initialize PAM: %s", pam_strerror(retcode)); |
| exit(99); |
| } |
| /* 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); |
| PAM_FAIL_CHECK; |
| retcode = pam_set_item(pamh, PAM_TTY, tty); |
| PAM_FAIL_CHECK; |
| /* 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; |
| |
| /* 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++ < PAM_MAX_LOGIN_TRIES) && |
| ((retcode == PAM_AUTH_ERR) || |
| (retcode == PAM_USER_UNKNOWN) || |
| (retcode == PAM_CRED_INSUFFICIENT) || |
| (retcode == PAM_AUTHINFO_UNAVAIL))) { |
| pam_get_item(pamh, PAM_USER, (const void **) &username); |
| syslog(LOG_NOTICE,"FAILED LOGIN %d FROM %s FOR %s, %s", |
| failcount, hostname,username,pam_strerror(retcode)); |
| fprintf(stderr,"Login incorrect\n\n"); |
| pam_set_item(pamh,PAM_USER,NULL); |
| retcode = pam_authenticate(pamh, 0); |
| } |
| |
| if (retcode != PAM_SUCCESS) { |
| pam_get_item(pamh, PAM_USER, (const void **) &username); |
| |
| if (retcode == PAM_MAXTRIES) |
| syslog(LOG_NOTICE,"TOO MANY LOGIN TRIES (%d) FROM %s FOR " |
| "%s, %s", failcount, hostname, username, |
| pam_strerror(retcode)); |
| else |
| syslog(LOG_NOTICE,"FAILED LOGIN SESSION FROM %s FOR %s, %s", |
| hostname, username, pam_strerror(retcode)); |
| |
| fprintf(stderr,"\nLogin incorrect\n"); |
| pam_end(pamh, retcode); |
| exit(0); |
| } |
| |
| retcode = pam_acct_mgmt(pamh, 0); |
| |
| if(retcode == PAM_AUTHTOKEN_REQD) { |
| retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); |
| } |
| |
| PAM_FAIL_CHECK; |
| } |
| |
| /* Grab the user information out of the password file for future usage |
| First get the username that we are actually using, though. |
| */ |
| retcode = pam_get_item(pamh, PAM_USER, (const void **) &username); |
| setpwent(); |
| pwd = getpwnam(username); |
| if (pwd) initgroups(username, pwd->pw_gid); |
| |
| retcode = pam_setcred(pamh, PAM_CRED_ESTABLISH); |
| PAM_FAIL_CHECK; |
| |
| retcode = pam_open_session(pamh, 0); |
| PAM_FAIL_CHECK; |
| |
| #else /* ! USE_PAM */ |
| |
| for (cnt = 0;; ask = 1) { |
| ioctlval = 0; |
| # ifndef __linux__ |
| ioctl(0, TIOCSETD, &ioctlval); |
| # endif |
| |
| if (ask) { |
| fflag = 0; |
| getloginname(); |
| } |
| |
| /* Dirty patch to fix a gigantic security hole when using |
| yellow pages. This problem should be solved by the |
| libraries, and not by programs, but this must be fixed |
| urgently! If the first char of the username is '+', we |
| avoid login success. |
| Feb 95 <alvaro@etsit.upm.es> */ |
| |
| if (username[0] == '+') { |
| puts("Illegal username"); |
| badlogin(username); |
| sleepexit(1); |
| } |
| |
| /* (void)strcpy(tbuf, username); why was this here? */ |
| if ((pwd = getpwnam(username))) { |
| # ifdef SHADOW_PWD |
| struct spwd *sp; |
| |
| if ((sp = getspnam(username))) |
| pwd->pw_passwd = sp->sp_pwdp; |
| # endif |
| salt = pwd->pw_passwd; |
| } else |
| salt = "xx"; |
| |
| if (pwd) { |
| initgroups(username, pwd->pw_gid); |
| checktty(username, tty, pwd); /* in checktty.c */ |
| } |
| |
| /* if user not super-user, check for disabled logins */ |
| if (pwd == NULL || pwd->pw_uid) |
| checknologin(); |
| |
| /* |
| * Disallow automatic login to root; if not invoked by |
| * root, disallow if the uid's differ. |
| */ |
| if (fflag && pwd) { |
| int uid = getuid(); |
| |
| passwd_req = pwd->pw_uid == 0 || |
| (uid && uid != pwd->pw_uid); |
| } |
| |
| /* |
| * If trying to log in as root, but with insecure terminal, |
| * refuse the login attempt. |
| */ |
| if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { |
| fprintf(stderr, |
| "%s login refused on this terminal.\n", |
| pwd->pw_name); |
| |
| if (hostname) |
| syslog(LOG_NOTICE, |
| "LOGIN %s REFUSED FROM %s ON TTY %s", |
| pwd->pw_name, hostname, tty); |
| else |
| syslog(LOG_NOTICE, |
| "LOGIN %s REFUSED ON TTY %s", |
| pwd->pw_name, tty); |
| continue; |
| } |
| |
| /* |
| * If no pre-authentication and a password exists |
| * for this user, prompt for one and verify it. |
| */ |
| if (!passwd_req || (pwd && !*pwd->pw_passwd)) |
| break; |
| |
| setpriority(PRIO_PROCESS, 0, -4); |
| pp = getpass("Password: "); |
| |
| # ifdef CRYPTOCARD |
| if (strncmp(pp, "CRYPTO", 6) == 0) { |
| if (pwd && cryptocard()) break; |
| } |
| # endif /* CRYPTOCARD */ |
| |
| p = crypt(pp, salt); |
| setpriority(PRIO_PROCESS, 0, 0); |
| |
| # ifdef KERBEROS |
| /* |
| * If not present in pw file, act as we normally would. |
| * If we aren't Kerberos-authenticated, try the normal |
| * pw file for a password. If that's ok, log the user |
| * in without issueing any tickets. |
| */ |
| |
| if (pwd && !krb_get_lrealm(realm,1)) { |
| /* |
| * get TGT for local realm; be careful about uid's |
| * here for ticket file ownership |
| */ |
| setreuid(geteuid(),pwd->pw_uid); |
| kerror = krb_get_pw_in_tkt(pwd->pw_name, "", realm, |
| "krbtgt", realm, DEFAULT_TKT_LIFE, pp); |
| setuid(0); |
| if (kerror == INTK_OK) { |
| memset(pp, 0, strlen(pp)); |
| notickets = 0; /* user got ticket */ |
| break; |
| } |
| } |
| # endif /* KERBEROS */ |
| memset(pp, 0, strlen(pp)); |
| if (pwd && !strcmp(p, pwd->pw_passwd)) |
| break; |
| |
| printf("Login incorrect\n"); |
| badlogin(username); /* log ALL bad logins */ |
| failures++; |
| |
| /* we allow 10 tries, but after 3 we start backing off */ |
| if (++cnt > 3) { |
| if (cnt >= 10) { |
| sleepexit(1); |
| } |
| sleep((unsigned int)((cnt - 3) * 5)); |
| } |
| } |
| #endif /* !USE_PAM */ |
| |
| /* committed to login -- turn off timeout */ |
| alarm((unsigned int)0); |
| |
| #ifdef HAVE_QUOTA |
| if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) { |
| switch(errno) { |
| case EUSERS: |
| fprintf(stderr, |
| "Too many users logged on already.\nTry again later.\n"); |
| break; |
| case EPROCLIM: |
| fprintf(stderr, |
| "You have too many processes running.\n"); |
| break; |
| default: |
| perror("quota (Q_SETUID)"); |
| } |
| sleepexit(0); |
| } |
| #endif |
| |
| /* paranoia... */ |
| #ifdef SHADOW_PWD |
| endspent(); |
| #endif |
| 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[MAXPATHLEN]; |
| uid_t ruid = getuid(); |
| gid_t egid = getegid(); |
| |
| snprintf(tmpstr, sizeof(tmpstr), |
| "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN); |
| |
| setregid(-1, pwd->pw_gid); |
| setreuid(0, pwd->pw_uid); |
| quietlog = (access(tmpstr, R_OK) == 0); |
| setuid(0); /* setreuid doesn't do it alone! */ |
| setreuid(ruid, 0); |
| setregid(-1, egid); |
| } |
| |
| #ifndef __linux__ |
| # ifdef KERBEROS |
| if (notickets && !quietlog) |
| printf("Warning: no Kerberos tickets issued\n"); |
| # endif |
| |
| # ifndef USE_PAM /* PAM does all of this for us */ |
| # define TWOWEEKS (14*24*60*60) |
| if (pwd->pw_change || pwd->pw_expire) { |
| struct timeval tp; |
| |
| gettimeofday(&tp, (struct timezone *)NULL); |
| |
| if (pwd->pw_change) { |
| if (tp.tv_sec >= pwd->pw_change) { |
| printf("Sorry -- your password has expired.\n"); |
| sleepexit(1); |
| } |
| else if (tp.tv_sec - pwd->pw_change < TWOWEEKS && !quietlog) { |
| struct tm *ttp; |
| ttp = localtime(&pwd->pw_change); |
| printf("Warning: your password expires on %s %d, %d\n", |
| months[ttp->tm_mon], ttp->tm_mday, |
| TM_YEAR_BASE + ttp->tm_year); |
| } |
| } |
| |
| if (pwd->pw_expire) { |
| if (tp.tv_sec >= pwd->pw_expire) { |
| printf("Sorry -- your account has expired.\n"); |
| sleepexit(1); |
| } |
| else if (tp.tv_sec - pwd->pw_expire < TWOWEEKS && !quietlog) { |
| struct tm *ttp; |
| ttp = localtime(&pwd->pw_expire); |
| printf("Warning: your account expires on %s %d, %d\n", |
| months[ttp->tm_mon], ttp->tm_mday, |
| TM_YEAR_BASE + ttp->tm_year); |
| } |
| } |
| } |
| # endif /* !USE_PAM */ |
| |
| /* nothing else left to fail -- really log in */ |
| { |
| struct utmp utmp; |
| |
| memset((char *)&utmp, 0, sizeof(utmp)); |
| time(&utmp.ut_time); |
| strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); |
| /* ut_name may legally be non-null-terminated */ |
| if (hostname) { |
| strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); |
| utmp.ut_host[sizeof(utmp.ut_host)-1] = 0; |
| } |
| strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); |
| utmp.ut_line[sizeof(utmp.ut_line)-1] = 0; |
| login(&utmp); |
| } |
| #else |
| /* for linux, write entries in utmp and wtmp */ |
| { |
| struct utmp ut; |
| int wtmp; |
| struct utmp *utp; |
| time_t t; |
| pid_t mypid = getpid(); |
| |
| utmpname(_PATH_UTMP); |
| setutent(); |
| while ((utp = getutent()) |
| && !(utp->ut_pid == mypid)) /* nothing */; |
| |
| if (utp) { |
| memcpy(&ut, utp, sizeof(ut)); |
| } else { |
| /* some gettys/telnetds don't initialize utmp... */ |
| memset(&ut, 0, sizeof(ut)); |
| } |
| /* endutent(); superfluous, error for glibc */ |
| |
| if (ut.ut_id[0] == 0) |
| strncpy(ut.ut_id, ttyn + 8, sizeof(ut.ut_id)); |
| |
| strncpy(ut.ut_user, username, sizeof(ut.ut_user)); |
| strncpy(ut.ut_line, ttyn + 5, sizeof(ut.ut_line)); |
| ut.ut_line[sizeof(ut.ut_line)-1] = 0; |
| time(&t); |
| ut.ut_time = t; /* ut_time is not always a time_t */ |
| /* (we might test #ifdef _HAVE_UT_TV ) */ |
| ut.ut_type = USER_PROCESS; |
| ut.ut_pid = mypid; |
| if (hostname) { |
| strncpy(ut.ut_host, hostname, sizeof(ut.ut_host)); |
| ut.ut_host[sizeof(ut.ut_host)-1] = 0; |
| if (hostaddress.h_addr_list) |
| memcpy(&ut.ut_addr, hostaddress.h_addr_list[0], |
| sizeof(ut.ut_addr)); |
| } |
| |
| pututline(&ut); |
| endutent(); |
| |
| { |
| int lf; |
| 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); |
| } |
| } |
| } |
| #endif |
| |
| dolastlog(quietlog); |
| |
| #ifndef __linux__ |
| if (!hflag) { /* XXX */ |
| static struct winsize win = { 0, 0, 0, 0 }; |
| |
| ioctl(0, TIOCSWINSZ, &win); |
| } |
| #endif |
| chown(ttyn, pwd->pw_uid, |
| (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); |
| chmod(ttyn, 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)) { |
| chown(vcsn, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)); |
| chown(vcsan, pwd->pw_uid, (gr ? gr->gr_gid : pwd->pw_gid)); |
| chmod(vcsn, TTY_MODE); |
| chmod(vcsan, TTY_MODE); |
| } |
| |
| setgid(pwd->pw_gid); |
| |
| #ifdef HAVE_QUOTA |
| quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); |
| #endif |
| |
| if (*pwd->pw_shell == '\0') |
| pwd->pw_shell = _PATH_BSHELL; |
| #ifndef __linux__ |
| /* turn on new line discipline for the csh */ |
| else if (!strcmp(pwd->pw_shell, _PATH_CSHELL)) { |
| ioctlval = NTTYDISC; |
| ioctl(0, TIOCSETD, &ioctlval); |
| } |
| #endif |
| |
| /* 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*)); |
| } |
| |
| #ifndef __linux__ |
| setenv("HOME", pwd->pw_dir, 1); |
| setenv("SHELL", pwd->pw_shell, 1); |
| if (term[0] == '\0') { |
| strncpy(term, stypeof(tty), sizeof(term)); |
| term[sizeof(term)-1] = 0; |
| } |
| setenv("TERM", term, 0); |
| setenv("USER", pwd->pw_name, 1); |
| setenv("PATH", _PATH_DEFPATH, 0); |
| #else |
| 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 */ |
| snprintf(tmp, sizeof(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); |
| #endif |
| |
| #ifdef USE_PAM |
| { |
| int i; |
| const char * const * env; |
| |
| env = (const char * const *)pam_getenvlist(pamh); |
| |
| if (env != NULL) { |
| for (i=0; env[i++]; ) { |
| putenv(env[i-1]); |
| /* D(("env[%d] = %s", i-1,env[i-1])); */ |
| } |
| } |
| } |
| #endif |
| |
| if (tty[sizeof("tty")-1] == 'S') |
| syslog(LOG_INFO, "DIALUP AT %s BY %s", tty, pwd->pw_name); |
| |
| /* allow tracking of good logins. |
| -steve philp (sphilp@mail.alliance.net) */ |
| |
| if (pwd->pw_uid == 0) { |
| if (hostname) |
| syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", |
| tty, hostname); |
| else |
| syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); |
| } else { |
| if (hostname) |
| syslog(LOG_INFO, "LOGIN ON %s BY %s FROM %s", tty, |
| pwd->pw_name, hostname); |
| else |
| syslog(LOG_INFO, "LOGIN ON %s BY %s", tty, |
| pwd->pw_name); |
| } |
| |
| if (!quietlog) { |
| struct stat st; |
| |
| motd(); |
| snprintf(tbuf, sizeof(tbuf), |
| "%s/%s", _PATH_MAILDIR, pwd->pw_name); |
| if (stat(tbuf, &st) == 0 && st.st_size != 0) |
| printf("You have %smail.\n", |
| (st.st_mtime > st.st_atime) ? "new " : ""); |
| } |
| |
| signal(SIGALRM, SIG_DFL); |
| signal(SIGQUIT, SIG_DFL); |
| signal(SIGINT, SIG_DFL); |
| signal(SIGTSTP, SIG_IGN); |
| signal(SIGHUP, 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"); |
| #ifdef USE_PAM |
| PAM_END; |
| #endif |
| exit(1); |
| } |
| |
| /* wait until here to change directory! */ |
| if (chdir(pwd->pw_dir) < 0) { |
| printf("No directory %s!\n", pwd->pw_dir); |
| if (chdir("/")) { |
| #ifdef USE_PAM |
| PAM_END; |
| #endif |
| exit(0); |
| } |
| 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 = malloc(strlen(pwd->pw_shell) + 6); |
| |
| if (!buff) { |
| fprintf(stderr, "login: no memory for shell script.\n"); |
| exit(0); |
| } |
| |
| strcpy(buff, "exec "); |
| strcat(buff, pwd->pw_shell); |
| childArgv[childArgc++] = "/bin/sh"; |
| childArgv[childArgc++] = "-sh"; |
| childArgv[childArgc++] = "-c"; |
| childArgv[childArgc++] = buff; |
| } else { |
| tbuf[0] = '-'; |
| strncpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ? |
| p + 1 : pwd->pw_shell), |
| sizeof(tbuf)-1); |
| tbuf[sizeof(tbuf)-1] = 0; |
| |
| childArgv[childArgc++] = pwd->pw_shell; |
| childArgv[childArgc++] = tbuf; |
| } |
| |
| childArgv[childArgc++] = NULL; |
| |
| #ifndef USE_PAM |
| execvp(childArgv[0], childArgv + 1); |
| error = 1; |
| #else /* USE_PAM */ |
| oldSigHandler = signal(SIGINT, SIG_IGN); |
| childPid = fork(); |
| if (childPid < 0) { |
| /* error in fork() */ |
| fprintf(stderr,"login: failure forking: %s", strerror(errno)); |
| PAM_END; |
| exit(0); |
| } else if (childPid) { |
| /* parent */ |
| wait(&childStatus); |
| signal(SIGINT, oldSigHandler); |
| PAM_END; |
| |
| if (!WIFEXITED(&childStatus)) error = 1; |
| } else { |
| /* child */ |
| execvp(childArgv[0], childArgv + 1); |
| exit(1); |
| } |
| #endif /* USE_PAM */ |
| |
| if (error) { |
| if (!strcmp(childArgv[0], "/bin/sh")) |
| fprintf(stderr, "login: couldn't exec shell script: %s.\n", |
| strerror(errno)); |
| else |
| fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); |
| } |
| |
| exit(0); |
| } |
| |
| void |
| getloginname() |
| { |
| register int ch; |
| register char *p; |
| static char nbuf[UT_NAMESIZE + 1]; |
| int cnt, cnt2; |
| |
| cnt2 = 0; |
| for (;;) { |
| cnt = 0; |
| printf("\n%s login: ", thishost); fflush(stdout); |
| for (p = nbuf; (ch = getchar()) != '\n'; ) { |
| if (ch == EOF) { |
| badlogin("EOF"); |
| exit(0); |
| } |
| if (p < nbuf + UT_NAMESIZE) |
| *p++ = ch; |
| |
| cnt++; |
| if (cnt > UT_NAMESIZE + 20) { |
| fprintf(stderr, "login name much too long.\n"); |
| badlogin("NAME too long"); |
| exit(0); |
| } |
| } |
| if (p > nbuf) |
| if (nbuf[0] == '-') |
| fprintf(stderr, |
| "login names may not start with '-'.\n"); |
| else { |
| *p = '\0'; |
| username = nbuf; |
| break; |
| } |
| |
| cnt2++; |
| if (cnt2 > 50) { |
| fprintf(stderr, "too many bare linefeeds.\n"); |
| badlogin("EXCESSIVE linefeeds"); |
| exit(0); |
| } |
| } |
| } |
| |
| void |
| timedout() |
| { |
| struct termio ti; |
| |
| fprintf(stderr, "Login timed out after %d seconds\n", timeout); |
| |
| /* reset echo */ |
| ioctl(0, TCGETA, &ti); |
| ti.c_lflag |= ECHO; |
| ioctl(0, TCSETA, &ti); |
| exit(0); |
| } |
| |
| int |
| rootterm(ttyn) |
| char *ttyn; |
| #ifndef __linux__ |
| { |
| struct ttyent *t; |
| |
| return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); |
| } |
| #else |
| { |
| int fd; |
| char buf[100],*p; |
| int cnt, more; |
| |
| fd = open(SECURETTY, O_RDONLY); |
| if(fd < 0) return 1; |
| |
| /* read each line in /etc/securetty, if a line matches our ttyline |
| then root is allowed to login on this tty, and we should return |
| true. */ |
| for(;;) { |
| p = buf; cnt = 100; |
| while(--cnt >= 0 && (more = read(fd, p, 1)) == 1 && *p != '\n') p++; |
| if(more && *p == '\n') { |
| *p = '\0'; |
| if(!strcmp(buf, ttyn)) { |
| close(fd); |
| return 1; |
| } else |
| continue; |
| } else { |
| close(fd); |
| return 0; |
| } |
| } |
| } |
| #endif |
| |
| jmp_buf motdinterrupt; |
| |
| void |
| motd() |
| { |
| register int fd, nchars; |
| void (*oldint)(), sigint(); |
| 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) |
| write(fileno(stdout), tbuf, nchars); |
| signal(SIGINT, oldint); |
| close(fd); |
| } |
| |
| void |
| sigint() |
| { |
| longjmp(motdinterrupt, 1); |
| } |
| |
| #ifndef USE_PAM /* PAM takes care of this */ |
| void |
| checknologin() |
| { |
| register int fd, nchars; |
| char tbuf[8192]; |
| |
| if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { |
| while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) |
| write(fileno(stdout), tbuf, nchars); |
| sleepexit(0); |
| } |
| } |
| #endif |
| |
| void |
| dolastlog(quiet) |
| 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), L_SET); |
| if (!quiet) { |
| if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && |
| ll.ll_time != 0) { |
| printf("Last login: %.*s ", |
| 24-5, (char *)ctime(&ll.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), L_SET); |
| } |
| memset((char *)&ll, 0, sizeof(ll)); |
| time(&ll.ll_time); |
| strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); |
| ll.ll_line[sizeof(ll.ll_line)-1] = 0; |
| if (hostname) { |
| strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); |
| ll.ll_host[sizeof(ll.ll_host)-1] = 0; |
| } |
| write(fd, (char *)&ll, sizeof(ll)); |
| close(fd); |
| } |
| } |
| |
| void |
| badlogin(const char *name) |
| { |
| if (hostname) |
| syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s", |
| failures, failures > 1 ? "S" : "", hostname, name); |
| else |
| syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s", |
| failures, failures > 1 ? "S" : "", tty, name); |
| } |
| |
| #undef UNKNOWN |
| #define UNKNOWN "su" |
| |
| #ifndef __linux__ |
| char * |
| stypeof(ttyid) |
| char *ttyid; |
| { |
| struct ttyent *t; |
| |
| return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); |
| } |
| #endif |
| |
| /* should not be called from PAM code... Why? */ |
| void |
| sleepexit(eval) |
| int eval; |
| { |
| sleep(SLEEP_EXIT_TIMEOUT); |
| exit(eval); |
| } |