143653Sbostic /*- 236515Sbostic * Copyright (c) 1980, 1987, 1988 The Regents of the University of California. 336515Sbostic * All rights reserved. 436515Sbostic * 543653Sbostic * %sccs.include.redist.c% 619843Sdist */ 719843Sdist 812678Ssam #ifndef lint 919843Sdist char copyright[] = 1036515Sbostic "@(#) Copyright (c) 1980, 1987, 1988 The Regents of the University of California.\n\ 1119843Sdist All rights reserved.\n"; 1236515Sbostic #endif /* not lint */ 1312678Ssam 1419843Sdist #ifndef lint 15*44568Smarc static char sccsid[] = "@(#)login.c 5.62 (Berkeley) 06/29/90"; 1636515Sbostic #endif /* not lint */ 1719843Sdist 181043Sbill /* 191043Sbill * login [ name ] 2031695Skarels * login -h hostname (for telnetd, etc.) 2131695Skarels * login -f name (for pre-authenticated login: datakit, xterm, etc.) 221043Sbill */ 231043Sbill 2412984Ssam #include <sys/param.h> 2512687Ssam #include <sys/stat.h> 2612687Ssam #include <sys/time.h> 2712687Ssam #include <sys/resource.h> 2816453Sroot #include <sys/file.h> 2939271Skfall #include <sgtty.h> 3012687Ssam 311043Sbill #include <utmp.h> 321043Sbill #include <signal.h> 3312678Ssam #include <errno.h> 3416453Sroot #include <ttyent.h> 3516453Sroot #include <syslog.h> 3626862Smckusick #include <grp.h> 3736460Sbostic #include <pwd.h> 3836515Sbostic #include <setjmp.h> 3936460Sbostic #include <stdio.h> 4042063Sbostic #include <string.h> 4137203Sbostic #include <tzfile.h> 4237203Sbostic #include "pathnames.h" 431043Sbill 4436460Sbostic #define TTYGRPNAME "tty" /* name of group to own ttys */ 4526862Smckusick 4612687Ssam /* 4736460Sbostic * This bounds the time given to login. Not a define so it can 4836460Sbostic * be patched on machines where it's too small. 4912687Ssam */ 5031509Sbostic int timeout = 300; 5143653Sbostic #ifdef KERBEROS 5243653Sbostic int notickets = 1; 5343653Sbostic #endif 546005Swnj 5536647Skarels struct passwd *pwd; 5636647Skarels int failures; 5740490Sbostic char term[64], *envinit[1], *hostname, *username, *tty; 586005Swnj 591043Sbill main(argc, argv) 6036460Sbostic int argc; 6136460Sbostic char **argv; 621043Sbill { 6343653Sbostic extern int optind; 6436460Sbostic extern char *optarg, **environ; 6536880Sbostic struct timeval tp; 6636460Sbostic struct group *gr; 6736460Sbostic register int ch; 6836647Skarels register char *p; 6939271Skfall int ask, fflag, hflag, pflag, cnt, uid; 70*44568Smarc int quietlog, rval; 7143653Sbostic char *domain, *salt, *ttyn; 7237692Sbostic char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; 7338034Skfall char localhost[MAXHOSTNAMELEN]; 7436880Sbostic char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass(); 7536460Sbostic time_t time(); 7636460Sbostic off_t lseek(); 7743653Sbostic void timedout(); 781043Sbill 7936460Sbostic (void)signal(SIGALRM, timedout); 8036460Sbostic (void)alarm((u_int)timeout); 8136460Sbostic (void)signal(SIGQUIT, SIG_IGN); 8236460Sbostic (void)signal(SIGINT, SIG_IGN); 8336460Sbostic (void)setpriority(PRIO_PROCESS, 0, 0); 8436460Sbostic 8542502Sbostic openlog("login", LOG_ODELAY, LOG_AUTH); 8642502Sbostic 8712687Ssam /* 8818549Ssam * -p is used by getty to tell login not to destroy the environment 8931695Skarels * -f is used to skip a second login authentication 9036523Skarels * -h is used by other servers to pass the name of the remote 9136523Skarels * host to login so that it may be placed in utmp and wtmp 9212687Ssam */ 9338034Skfall domain = NULL; 9438034Skfall if (gethostname(localhost, sizeof(localhost)) < 0) 9538034Skfall syslog(LOG_ERR, "couldn't get local hostname: %m"); 9638034Skfall else 9738034Skfall domain = index(localhost, '.'); 9836460Sbostic 9937203Sbostic fflag = hflag = pflag = 0; 10039271Skfall uid = getuid(); 10137203Sbostic while ((ch = getopt(argc, argv, "fh:p")) != EOF) 10236515Sbostic switch (ch) { 10336460Sbostic case 'f': 10436460Sbostic fflag = 1; 10536460Sbostic break; 10636460Sbostic case 'h': 10739271Skfall if (uid) { 10837203Sbostic (void)fprintf(stderr, 10936460Sbostic "login: -h for super-user only.\n"); 11032313Sbostic exit(1); 11131695Skarels } 11236460Sbostic hflag = 1; 11336460Sbostic if (domain && (p = index(optarg, '.')) && 11436553Sbostic strcasecmp(p, domain) == 0) 11536460Sbostic *p = 0; 11636460Sbostic hostname = optarg; 11736460Sbostic break; 11836460Sbostic case 'p': 11918549Ssam pflag = 1; 12036460Sbostic break; 12136460Sbostic case '?': 12236460Sbostic default: 12339271Skfall if (!uid) 12439271Skfall syslog(LOG_ERR, "invalid flag %c", ch); 12537203Sbostic (void)fprintf(stderr, 12637203Sbostic "usage: login [-fp] [username]\n"); 12736460Sbostic exit(1); 12818549Ssam } 12936460Sbostic argc -= optind; 13036460Sbostic argv += optind; 13136549Sbostic if (*argv) { 13236647Skarels username = *argv; 13343653Sbostic if (strlen(username) > UT_NAMESIZE) 13443653Sbostic username[UT_NAMESIZE] = '\0'; 13536549Sbostic ask = 0; 13636647Skarels } else 13736549Sbostic ask = 1; 13836460Sbostic 139*44568Smarc (void) ioctl(0, TIOCNXCL, 0); 140*44568Smarc (void) fcntl(0, F_SETFL, 0); 14136460Sbostic 14236460Sbostic for (cnt = getdtablesize(); cnt > 2; cnt--) 14336460Sbostic close(cnt); 14436460Sbostic 1451043Sbill ttyn = ttyname(0); 14637692Sbostic if (ttyn == NULL || *ttyn == '\0') { 14737692Sbostic (void)sprintf(tname, "%s??", _PATH_TTY); 14837692Sbostic ttyn = tname; 14937692Sbostic } 15036460Sbostic if (tty = rindex(ttyn, '/')) 15136460Sbostic ++tty; 15236460Sbostic else 15316453Sroot tty = ttyn; 15436460Sbostic 15536549Sbostic for (cnt = 0;; ask = 1) { 15636549Sbostic if (ask) { 15736515Sbostic fflag = 0; 15836460Sbostic getloginname(); 15936515Sbostic } 16036647Skarels /* 16139271Skfall * Note if trying multiple user names; log failures for 16239271Skfall * previous user name, but don't bother logging one failure 16336647Skarels * for nonexistent name (mistyped username). 16436647Skarels */ 16536647Skarels if (failures && strcmp(tbuf, username)) { 16636647Skarels if (failures > (pwd ? 0 : 1)) 16736549Sbostic badlogin(tbuf); 16836647Skarels failures = 0; 16936549Sbostic } 17036647Skarels (void)strcpy(tbuf, username); 17143659Sbostic 17236515Sbostic if (pwd = getpwnam(username)) 17336515Sbostic salt = pwd->pw_passwd; 17443659Sbostic else 17543659Sbostic salt = "xx"; 17636460Sbostic 17712687Ssam /* 17843674Sbostic * if we have a valid account name, and it doesn't have a 17943674Sbostic * password, or the -f option was specified and the caller 18043674Sbostic * is root or the caller isn't changing their uid, don't 18143674Sbostic * authenticate. 18212687Ssam */ 18343674Sbostic if (pwd && (*pwd->pw_passwd == '\0' || 18443674Sbostic fflag && (uid == 0 || uid == pwd->pw_uid))) 18536460Sbostic break; 18644348Skarels fflag = 0; 18712687Ssam 18839271Skfall /* 18939271Skfall * If trying to log in as root, but with insecure terminal, 19039271Skfall * refuse the login attempt. 19139271Skfall */ 19243659Sbostic if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { 19339271Skfall (void)fprintf(stderr, 19439271Skfall "%s login refused on this terminal.\n", 19539271Skfall pwd->pw_name); 19639271Skfall if (hostname) 19739271Skfall syslog(LOG_NOTICE, 19839271Skfall "LOGIN %s REFUSED FROM %s ON TTY %s", 19939271Skfall pwd->pw_name, hostname, tty); 20039271Skfall else 20139271Skfall syslog(LOG_NOTICE, 20239271Skfall "LOGIN %s REFUSED ON TTY %s", 20339271Skfall pwd->pw_name, tty); 20439271Skfall continue; 20539271Skfall } 20639271Skfall 20743653Sbostic (void)setpriority(PRIO_PROCESS, 0, -4); 20843659Sbostic 20943653Sbostic p = getpass("Password:"); 21036608Skfall 21143659Sbostic if (pwd) { 21243653Sbostic #ifdef KERBEROS 21343659Sbostic rval = klogin(pwd, localhost, p); 21443659Sbostic if (rval == 1) 21543659Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21643659Sbostic #else 21743653Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21837203Sbostic #endif 21943659Sbostic } 22043653Sbostic bzero(p, strlen(p)); 22143659Sbostic 22243659Sbostic (void)setpriority(PRIO_PROCESS, 0, 0); 22343659Sbostic 22443659Sbostic if (pwd && !rval) 22536460Sbostic break; 22636460Sbostic 22743659Sbostic (void)printf("Login incorrect\n"); 22836647Skarels failures++; 22936549Sbostic /* we allow 10 tries, but after 3 we start backing off */ 23036549Sbostic if (++cnt > 3) { 23136549Sbostic if (cnt >= 10) { 23236549Sbostic badlogin(username); 23336549Sbostic (void)ioctl(0, TIOCHPCL, (struct sgttyb *)NULL); 23436549Sbostic sleepexit(1); 23536549Sbostic } 23636549Sbostic sleep((u_int)((cnt - 3) * 5)); 2372822Swnj } 23836460Sbostic } 2391043Sbill 24036460Sbostic /* committed to login -- turn off timeout */ 24136460Sbostic (void)alarm((u_int)0); 24236460Sbostic 24337692Sbostic /* paranoia... */ 24437692Sbostic endpwent(); 24537692Sbostic 24643663Sbostic /* if user not super-user, check for disabled logins */ 24743663Sbostic if (pwd->pw_uid) 24843663Sbostic checknologin(); 24943663Sbostic 25036515Sbostic if (chdir(pwd->pw_dir) < 0) { 25137203Sbostic (void)printf("No directory %s!\n", pwd->pw_dir); 25236515Sbostic if (chdir("/")) 25336515Sbostic exit(0); 25436515Sbostic pwd->pw_dir = "/"; 25537203Sbostic (void)printf("Logging in with home = \"/\".\n"); 25636515Sbostic } 25736515Sbostic 25837203Sbostic quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 25937203Sbostic 26036880Sbostic if (pwd->pw_change || pwd->pw_expire) 26136880Sbostic (void)gettimeofday(&tp, (struct timezone *)NULL); 26236880Sbostic if (pwd->pw_change) 26336880Sbostic if (tp.tv_sec >= pwd->pw_change) { 26437203Sbostic (void)printf("Sorry -- your password has expired.\n"); 26536880Sbostic sleepexit(1); 26636880Sbostic } 26738216Sbostic else if (pwd->pw_change - tp.tv_sec < 26843660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 26943660Sbostic (void)printf("Warning: your password expires on %s", 27043660Sbostic ctime(&pwd->pw_expire)); 27136880Sbostic if (pwd->pw_expire) 27236880Sbostic if (tp.tv_sec >= pwd->pw_expire) { 27337203Sbostic (void)printf("Sorry -- your account has expired.\n"); 27436880Sbostic sleepexit(1); 27536880Sbostic } 27638216Sbostic else if (pwd->pw_expire - tp.tv_sec < 27743660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 27843660Sbostic (void)printf("Warning: your account expires on %s", 27943660Sbostic ctime(&pwd->pw_expire)); 28036880Sbostic 28136515Sbostic /* nothing else left to fail -- really log in */ 28236460Sbostic { 28336460Sbostic struct utmp utmp; 28436460Sbostic 28543653Sbostic bzero((void *)&utmp, sizeof(utmp)); 28636460Sbostic (void)time(&utmp.ut_time); 28736460Sbostic strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 28836591Sbostic if (hostname) 28936591Sbostic strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 29036460Sbostic strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 29136460Sbostic login(&utmp); 29236460Sbostic } 29336460Sbostic 29436549Sbostic dolastlog(quietlog); 29536460Sbostic 29636460Sbostic (void)chown(ttyn, pwd->pw_uid, 29736460Sbostic (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 29836460Sbostic (void)chmod(ttyn, 0620); 29936460Sbostic (void)setgid(pwd->pw_gid); 30036460Sbostic 30136460Sbostic initgroups(username, pwd->pw_gid); 30236460Sbostic 30336515Sbostic if (*pwd->pw_shell == '\0') 30437203Sbostic pwd->pw_shell = _PATH_BSHELL; 30536515Sbostic 30636460Sbostic /* destroy environment unless user has requested preservation */ 30718549Ssam if (!pflag) 30818549Ssam environ = envinit; 30936515Sbostic (void)setenv("HOME", pwd->pw_dir, 1); 31036515Sbostic (void)setenv("SHELL", pwd->pw_shell, 1); 31118549Ssam if (term[0] == '\0') 31236647Skarels strncpy(term, stypeof(tty), sizeof(term)); 31336515Sbostic (void)setenv("TERM", term, 0); 31436515Sbostic (void)setenv("USER", pwd->pw_name, 1); 31537252Sbostic (void)setenv("PATH", _PATH_DEFPATH, 0); 31618549Ssam 31716453Sroot if (tty[sizeof("tty")-1] == 'd') 31818549Ssam syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 31944348Skarels /* if fflag is on, assume caller/authenticator has logged root login */ 32044348Skarels if (pwd->pw_uid == 0 && fflag == 0) 32136460Sbostic if (hostname) 32236647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", 32336549Sbostic tty, hostname); 32425230Smckusick else 32536647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); 32636460Sbostic 32743653Sbostic #ifdef KERBEROS 32843653Sbostic if (!quietlog && notickets == 1) 32943653Sbostic (void)printf("Warning: no Kerberos tickets issued.\n"); 33043653Sbostic #endif 33143653Sbostic 3326329Swnj if (!quietlog) { 33317664Sserge struct stat st; 33418549Ssam 33536460Sbostic motd(); 33637203Sbostic (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); 33736460Sbostic if (stat(tbuf, &st) == 0 && st.st_size != 0) 33837203Sbostic (void)printf("You have %smail.\n", 33936460Sbostic (st.st_mtime > st.st_atime) ? "new " : ""); 3402822Swnj } 34136460Sbostic 34236460Sbostic (void)signal(SIGALRM, SIG_DFL); 34336460Sbostic (void)signal(SIGQUIT, SIG_DFL); 34436460Sbostic (void)signal(SIGINT, SIG_DFL); 34536460Sbostic (void)signal(SIGTSTP, SIG_IGN); 34636460Sbostic 34736460Sbostic tbuf[0] = '-'; 34836460Sbostic strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? 34936460Sbostic p + 1 : pwd->pw_shell); 35037203Sbostic 35140009Ssklower if (setlogin(pwd->pw_name) < 0) 35240009Ssklower syslog(LOG_ERR, "setlogin() failure: %m"); 35337692Sbostic 35437203Sbostic /* discard permissions last so can't get killed and drop core */ 35537203Sbostic (void)setuid(pwd->pw_uid); 35637203Sbostic 35736460Sbostic execlp(pwd->pw_shell, tbuf, 0); 35837203Sbostic (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); 3591043Sbill exit(0); 3601043Sbill } 3611043Sbill 36236460Sbostic getloginname() 36312687Ssam { 36436460Sbostic register int ch; 36536460Sbostic register char *p; 36636460Sbostic static char nbuf[UT_NAMESIZE + 1]; 36712687Ssam 36836460Sbostic for (;;) { 36937203Sbostic (void)printf("login: "); 37036515Sbostic for (p = nbuf; (ch = getchar()) != '\n'; ) { 37136549Sbostic if (ch == EOF) { 37236549Sbostic badlogin(username); 37312687Ssam exit(0); 37436549Sbostic } 37536515Sbostic if (p < nbuf + UT_NAMESIZE) 37636460Sbostic *p++ = ch; 37712687Ssam } 37836460Sbostic if (p > nbuf) 37936460Sbostic if (nbuf[0] == '-') 38037203Sbostic (void)fprintf(stderr, 38136460Sbostic "login names may not start with '-'.\n"); 38236460Sbostic else { 38336460Sbostic *p = '\0'; 38436460Sbostic username = nbuf; 38536515Sbostic break; 38636460Sbostic } 38712687Ssam } 38812687Ssam } 38912687Ssam 39043653Sbostic void 39112687Ssam timedout() 39212687Ssam { 39337203Sbostic (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); 39412687Ssam exit(0); 39512687Ssam } 39612687Ssam 39736647Skarels rootterm(ttyn) 39836647Skarels char *ttyn; 3991043Sbill { 40036460Sbostic struct ttyent *t; 4016466Swnj 40236647Skarels return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); 4031043Sbill } 4041043Sbill 40536515Sbostic jmp_buf motdinterrupt; 40636515Sbostic 40736460Sbostic motd() 4082822Swnj { 40936515Sbostic register int fd, nchars; 41038726Skfall sig_t oldint; 41138726Skfall int sigint(); 41236515Sbostic char tbuf[8192]; 4132822Swnj 41437203Sbostic if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) 41536460Sbostic return; 41636460Sbostic oldint = signal(SIGINT, sigint); 41736515Sbostic if (setjmp(motdinterrupt) == 0) 41836515Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 41936515Sbostic (void)write(fileno(stdout), tbuf, nchars); 42036460Sbostic (void)signal(SIGINT, oldint); 42136515Sbostic (void)close(fd); 42236460Sbostic } 42336460Sbostic 42436460Sbostic sigint() 42536460Sbostic { 42636515Sbostic longjmp(motdinterrupt, 1); 42736460Sbostic } 42836460Sbostic 42936460Sbostic checknologin() 43036460Sbostic { 43136460Sbostic register int fd, nchars; 43236515Sbostic char tbuf[8192]; 43336460Sbostic 43437203Sbostic if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 43536460Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 43636460Sbostic (void)write(fileno(stdout), tbuf, nchars); 43736460Sbostic sleepexit(0); 4382822Swnj } 4392822Swnj } 4402822Swnj 44136549Sbostic dolastlog(quiet) 44236460Sbostic int quiet; 4431043Sbill { 44436460Sbostic struct lastlog ll; 44536460Sbostic int fd; 44636880Sbostic char *ctime(); 4471043Sbill 44837203Sbostic if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 44936515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 45036460Sbostic if (!quiet) { 45136460Sbostic if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 45236460Sbostic ll.ll_time != 0) { 45337203Sbostic (void)printf("Last login: %.*s ", 45436460Sbostic 24-5, (char *)ctime(&ll.ll_time)); 45536460Sbostic if (*ll.ll_host != '\0') 45637203Sbostic (void)printf("from %.*s\n", 45736460Sbostic sizeof(ll.ll_host), ll.ll_host); 45836460Sbostic else 45937203Sbostic (void)printf("on %.*s\n", 46036460Sbostic sizeof(ll.ll_line), ll.ll_line); 46136460Sbostic } 46236515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 46336460Sbostic } 46443653Sbostic bzero((void *)&ll, sizeof(ll)); 46536460Sbostic (void)time(&ll.ll_time); 46636460Sbostic strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 46736591Sbostic if (hostname) 46836591Sbostic strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 46936460Sbostic (void)write(fd, (char *)&ll, sizeof(ll)); 47036460Sbostic (void)close(fd); 4711043Sbill } 4721043Sbill } 4731043Sbill 47436549Sbostic badlogin(name) 47536549Sbostic char *name; 47636549Sbostic { 47736647Skarels if (failures == 0) 47836549Sbostic return; 47936549Sbostic if (hostname) 48036647Skarels syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s, %s", 48136647Skarels failures, failures > 1 ? "S" : "", hostname, name); 48236549Sbostic else 48336647Skarels syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s, %s", 48436647Skarels failures, failures > 1 ? "S" : "", tty, name); 48536549Sbostic } 48636549Sbostic 4872822Swnj #undef UNKNOWN 48836460Sbostic #define UNKNOWN "su" 4891043Sbill 4901043Sbill char * 49136647Skarels stypeof(ttyid) 49236647Skarels char *ttyid; 4931043Sbill { 49436460Sbostic struct ttyent *t; 4951043Sbill 49636647Skarels return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 4971043Sbill } 4986005Swnj 49936460Sbostic sleepexit(eval) 50036460Sbostic int eval; 50126862Smckusick { 50236460Sbostic sleep((u_int)5); 50336460Sbostic exit(eval); 50426862Smckusick } 505