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*45431Sbostic static char sccsid[] = "@(#)login.c 5.64 (Berkeley) 10/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; 7044568Smarc 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 13936460Sbostic for (cnt = getdtablesize(); cnt > 2; cnt--) 14036460Sbostic close(cnt); 14136460Sbostic 1421043Sbill ttyn = ttyname(0); 14337692Sbostic if (ttyn == NULL || *ttyn == '\0') { 14437692Sbostic (void)sprintf(tname, "%s??", _PATH_TTY); 14537692Sbostic ttyn = tname; 14637692Sbostic } 14736460Sbostic if (tty = rindex(ttyn, '/')) 14836460Sbostic ++tty; 14936460Sbostic else 15016453Sroot tty = ttyn; 15136460Sbostic 15236549Sbostic for (cnt = 0;; ask = 1) { 15336549Sbostic if (ask) { 15436515Sbostic fflag = 0; 15536460Sbostic getloginname(); 15636515Sbostic } 15736647Skarels /* 15839271Skfall * Note if trying multiple user names; log failures for 15939271Skfall * previous user name, but don't bother logging one failure 16036647Skarels * for nonexistent name (mistyped username). 16136647Skarels */ 16236647Skarels if (failures && strcmp(tbuf, username)) { 16336647Skarels if (failures > (pwd ? 0 : 1)) 16436549Sbostic badlogin(tbuf); 16536647Skarels failures = 0; 16636549Sbostic } 16736647Skarels (void)strcpy(tbuf, username); 16843659Sbostic 16936515Sbostic if (pwd = getpwnam(username)) 17036515Sbostic salt = pwd->pw_passwd; 17143659Sbostic else 17243659Sbostic salt = "xx"; 17336460Sbostic 17412687Ssam /* 17543674Sbostic * if we have a valid account name, and it doesn't have a 17643674Sbostic * password, or the -f option was specified and the caller 17743674Sbostic * is root or the caller isn't changing their uid, don't 17843674Sbostic * authenticate. 17912687Ssam */ 18043674Sbostic if (pwd && (*pwd->pw_passwd == '\0' || 18143674Sbostic fflag && (uid == 0 || uid == pwd->pw_uid))) 18236460Sbostic break; 18344348Skarels fflag = 0; 18412687Ssam 18539271Skfall /* 18639271Skfall * If trying to log in as root, but with insecure terminal, 18739271Skfall * refuse the login attempt. 18839271Skfall */ 18943659Sbostic if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { 19039271Skfall (void)fprintf(stderr, 19139271Skfall "%s login refused on this terminal.\n", 19239271Skfall pwd->pw_name); 19339271Skfall if (hostname) 19439271Skfall syslog(LOG_NOTICE, 19539271Skfall "LOGIN %s REFUSED FROM %s ON TTY %s", 19639271Skfall pwd->pw_name, hostname, tty); 19739271Skfall else 19839271Skfall syslog(LOG_NOTICE, 19939271Skfall "LOGIN %s REFUSED ON TTY %s", 20039271Skfall pwd->pw_name, tty); 20139271Skfall continue; 20239271Skfall } 20339271Skfall 20443653Sbostic (void)setpriority(PRIO_PROCESS, 0, -4); 20543659Sbostic 20643653Sbostic p = getpass("Password:"); 20736608Skfall 20843659Sbostic if (pwd) { 20943653Sbostic #ifdef KERBEROS 21043659Sbostic rval = klogin(pwd, localhost, p); 21143659Sbostic if (rval == 1) 21243659Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21343659Sbostic #else 21443653Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21537203Sbostic #endif 21643659Sbostic } 21743653Sbostic bzero(p, strlen(p)); 21843659Sbostic 21943659Sbostic (void)setpriority(PRIO_PROCESS, 0, 0); 22043659Sbostic 22143659Sbostic if (pwd && !rval) 22236460Sbostic break; 22336460Sbostic 22443659Sbostic (void)printf("Login incorrect\n"); 22536647Skarels failures++; 22636549Sbostic /* we allow 10 tries, but after 3 we start backing off */ 22736549Sbostic if (++cnt > 3) { 22836549Sbostic if (cnt >= 10) { 22936549Sbostic badlogin(username); 23036549Sbostic sleepexit(1); 23136549Sbostic } 23236549Sbostic sleep((u_int)((cnt - 3) * 5)); 2332822Swnj } 23436460Sbostic } 2351043Sbill 23636460Sbostic /* committed to login -- turn off timeout */ 23736460Sbostic (void)alarm((u_int)0); 23836460Sbostic 23937692Sbostic /* paranoia... */ 24037692Sbostic endpwent(); 24137692Sbostic 24243663Sbostic /* if user not super-user, check for disabled logins */ 24343663Sbostic if (pwd->pw_uid) 24443663Sbostic checknologin(); 24543663Sbostic 24636515Sbostic if (chdir(pwd->pw_dir) < 0) { 24737203Sbostic (void)printf("No directory %s!\n", pwd->pw_dir); 24836515Sbostic if (chdir("/")) 24936515Sbostic exit(0); 25036515Sbostic pwd->pw_dir = "/"; 25137203Sbostic (void)printf("Logging in with home = \"/\".\n"); 25236515Sbostic } 25336515Sbostic 25437203Sbostic quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 25537203Sbostic 25636880Sbostic if (pwd->pw_change || pwd->pw_expire) 25736880Sbostic (void)gettimeofday(&tp, (struct timezone *)NULL); 25836880Sbostic if (pwd->pw_change) 25936880Sbostic if (tp.tv_sec >= pwd->pw_change) { 26037203Sbostic (void)printf("Sorry -- your password has expired.\n"); 26136880Sbostic sleepexit(1); 26236880Sbostic } 26338216Sbostic else if (pwd->pw_change - tp.tv_sec < 26443660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 26543660Sbostic (void)printf("Warning: your password expires on %s", 26643660Sbostic ctime(&pwd->pw_expire)); 26736880Sbostic if (pwd->pw_expire) 26836880Sbostic if (tp.tv_sec >= pwd->pw_expire) { 26937203Sbostic (void)printf("Sorry -- your account has expired.\n"); 27036880Sbostic sleepexit(1); 27136880Sbostic } 27238216Sbostic else if (pwd->pw_expire - tp.tv_sec < 27343660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 27443660Sbostic (void)printf("Warning: your account expires on %s", 27543660Sbostic ctime(&pwd->pw_expire)); 27636880Sbostic 27736515Sbostic /* nothing else left to fail -- really log in */ 27836460Sbostic { 27936460Sbostic struct utmp utmp; 28036460Sbostic 28143653Sbostic bzero((void *)&utmp, sizeof(utmp)); 28236460Sbostic (void)time(&utmp.ut_time); 28336460Sbostic strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 28436591Sbostic if (hostname) 28536591Sbostic strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 28636460Sbostic strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 28736460Sbostic login(&utmp); 28836460Sbostic } 28936460Sbostic 29036549Sbostic dolastlog(quietlog); 29136460Sbostic 29236460Sbostic (void)chown(ttyn, pwd->pw_uid, 29336460Sbostic (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 29436460Sbostic (void)chmod(ttyn, 0620); 29536460Sbostic (void)setgid(pwd->pw_gid); 29636460Sbostic 29736460Sbostic initgroups(username, pwd->pw_gid); 29836460Sbostic 29936515Sbostic if (*pwd->pw_shell == '\0') 30037203Sbostic pwd->pw_shell = _PATH_BSHELL; 30136515Sbostic 30236460Sbostic /* destroy environment unless user has requested preservation */ 30318549Ssam if (!pflag) 30418549Ssam environ = envinit; 30536515Sbostic (void)setenv("HOME", pwd->pw_dir, 1); 30636515Sbostic (void)setenv("SHELL", pwd->pw_shell, 1); 30718549Ssam if (term[0] == '\0') 30836647Skarels strncpy(term, stypeof(tty), sizeof(term)); 30936515Sbostic (void)setenv("TERM", term, 0); 31036515Sbostic (void)setenv("USER", pwd->pw_name, 1); 31137252Sbostic (void)setenv("PATH", _PATH_DEFPATH, 0); 31218549Ssam 31316453Sroot if (tty[sizeof("tty")-1] == 'd') 31418549Ssam syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 31544348Skarels /* if fflag is on, assume caller/authenticator has logged root login */ 31644348Skarels if (pwd->pw_uid == 0 && fflag == 0) 31736460Sbostic if (hostname) 31836647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", 31936549Sbostic tty, hostname); 32025230Smckusick else 32136647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); 32236460Sbostic 32343653Sbostic #ifdef KERBEROS 32443653Sbostic if (!quietlog && notickets == 1) 32543653Sbostic (void)printf("Warning: no Kerberos tickets issued.\n"); 32643653Sbostic #endif 32743653Sbostic 3286329Swnj if (!quietlog) { 32917664Sserge struct stat st; 33018549Ssam 33136460Sbostic motd(); 33237203Sbostic (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); 33336460Sbostic if (stat(tbuf, &st) == 0 && st.st_size != 0) 33437203Sbostic (void)printf("You have %smail.\n", 33536460Sbostic (st.st_mtime > st.st_atime) ? "new " : ""); 3362822Swnj } 33736460Sbostic 33836460Sbostic (void)signal(SIGALRM, SIG_DFL); 33936460Sbostic (void)signal(SIGQUIT, SIG_DFL); 34036460Sbostic (void)signal(SIGINT, SIG_DFL); 34136460Sbostic (void)signal(SIGTSTP, SIG_IGN); 34236460Sbostic 34336460Sbostic tbuf[0] = '-'; 34436460Sbostic strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? 34536460Sbostic p + 1 : pwd->pw_shell); 34637203Sbostic 34740009Ssklower if (setlogin(pwd->pw_name) < 0) 34840009Ssklower syslog(LOG_ERR, "setlogin() failure: %m"); 34937692Sbostic 35037203Sbostic /* discard permissions last so can't get killed and drop core */ 35137203Sbostic (void)setuid(pwd->pw_uid); 35237203Sbostic 35336460Sbostic execlp(pwd->pw_shell, tbuf, 0); 35437203Sbostic (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); 3551043Sbill exit(0); 3561043Sbill } 3571043Sbill 35836460Sbostic getloginname() 35912687Ssam { 36036460Sbostic register int ch; 36136460Sbostic register char *p; 36236460Sbostic static char nbuf[UT_NAMESIZE + 1]; 36312687Ssam 36436460Sbostic for (;;) { 36537203Sbostic (void)printf("login: "); 36636515Sbostic for (p = nbuf; (ch = getchar()) != '\n'; ) { 36736549Sbostic if (ch == EOF) { 36836549Sbostic badlogin(username); 36912687Ssam exit(0); 37036549Sbostic } 37136515Sbostic if (p < nbuf + UT_NAMESIZE) 37236460Sbostic *p++ = ch; 37312687Ssam } 37436460Sbostic if (p > nbuf) 37536460Sbostic if (nbuf[0] == '-') 37637203Sbostic (void)fprintf(stderr, 37736460Sbostic "login names may not start with '-'.\n"); 37836460Sbostic else { 37936460Sbostic *p = '\0'; 38036460Sbostic username = nbuf; 38136515Sbostic break; 38236460Sbostic } 38312687Ssam } 38412687Ssam } 38512687Ssam 38643653Sbostic void 38712687Ssam timedout() 38812687Ssam { 38937203Sbostic (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); 39012687Ssam exit(0); 39112687Ssam } 39212687Ssam 39336647Skarels rootterm(ttyn) 39436647Skarels char *ttyn; 3951043Sbill { 39636460Sbostic struct ttyent *t; 3976466Swnj 39836647Skarels return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); 3991043Sbill } 4001043Sbill 40136515Sbostic jmp_buf motdinterrupt; 40236515Sbostic 40336460Sbostic motd() 4042822Swnj { 40536515Sbostic register int fd, nchars; 40638726Skfall sig_t oldint; 40738726Skfall int sigint(); 40836515Sbostic char tbuf[8192]; 4092822Swnj 41037203Sbostic if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) 41136460Sbostic return; 41236460Sbostic oldint = signal(SIGINT, sigint); 41336515Sbostic if (setjmp(motdinterrupt) == 0) 41436515Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 41536515Sbostic (void)write(fileno(stdout), tbuf, nchars); 41636460Sbostic (void)signal(SIGINT, oldint); 41736515Sbostic (void)close(fd); 41836460Sbostic } 41936460Sbostic 42036460Sbostic sigint() 42136460Sbostic { 42236515Sbostic longjmp(motdinterrupt, 1); 42336460Sbostic } 42436460Sbostic 42536460Sbostic checknologin() 42636460Sbostic { 42736460Sbostic register int fd, nchars; 42836515Sbostic char tbuf[8192]; 42936460Sbostic 43037203Sbostic if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 43136460Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 43236460Sbostic (void)write(fileno(stdout), tbuf, nchars); 43336460Sbostic sleepexit(0); 4342822Swnj } 4352822Swnj } 4362822Swnj 43736549Sbostic dolastlog(quiet) 43836460Sbostic int quiet; 4391043Sbill { 44036460Sbostic struct lastlog ll; 44136460Sbostic int fd; 44236880Sbostic char *ctime(); 4431043Sbill 44437203Sbostic if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 44536515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 44636460Sbostic if (!quiet) { 44736460Sbostic if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 44836460Sbostic ll.ll_time != 0) { 44937203Sbostic (void)printf("Last login: %.*s ", 45036460Sbostic 24-5, (char *)ctime(&ll.ll_time)); 45136460Sbostic if (*ll.ll_host != '\0') 45237203Sbostic (void)printf("from %.*s\n", 45336460Sbostic sizeof(ll.ll_host), ll.ll_host); 45436460Sbostic else 45537203Sbostic (void)printf("on %.*s\n", 45636460Sbostic sizeof(ll.ll_line), ll.ll_line); 45736460Sbostic } 45836515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 45936460Sbostic } 46043653Sbostic bzero((void *)&ll, sizeof(ll)); 46136460Sbostic (void)time(&ll.ll_time); 46236460Sbostic strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 46336591Sbostic if (hostname) 46436591Sbostic strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 46536460Sbostic (void)write(fd, (char *)&ll, sizeof(ll)); 46636460Sbostic (void)close(fd); 4671043Sbill } 4681043Sbill } 4691043Sbill 47036549Sbostic badlogin(name) 47136549Sbostic char *name; 47236549Sbostic { 47336647Skarels if (failures == 0) 47436549Sbostic return; 475*45431Sbostic if (hostname) { 476*45431Sbostic syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 477*45431Sbostic failures, failures > 1 ? "S" : "", hostname); 478*45431Sbostic syslog(LOG_AUTHPRIV|LOG_NOTICE, 479*45431Sbostic "%d LOGIN FAILURE%s FROM %s, %s", 48036647Skarels failures, failures > 1 ? "S" : "", hostname, name); 481*45431Sbostic } else { 482*45431Sbostic syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 483*45431Sbostic failures, failures > 1 ? "S" : "", tty); 484*45431Sbostic syslog(LOG_AUTHPRIV|LOG_NOTICE, 485*45431Sbostic "%d LOGIN FAILURE%s ON %s, %s", 48636647Skarels failures, failures > 1 ? "S" : "", tty, name); 487*45431Sbostic } 48836549Sbostic } 48936549Sbostic 4902822Swnj #undef UNKNOWN 49136460Sbostic #define UNKNOWN "su" 4921043Sbill 4931043Sbill char * 49436647Skarels stypeof(ttyid) 49536647Skarels char *ttyid; 4961043Sbill { 49736460Sbostic struct ttyent *t; 4981043Sbill 49936647Skarels return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 5001043Sbill } 5016005Swnj 50236460Sbostic sleepexit(eval) 50336460Sbostic int eval; 50426862Smckusick { 50536460Sbostic sleep((u_int)5); 50636460Sbostic exit(eval); 50726862Smckusick } 508