143653Sbostic /*- 2*47437Skarels * Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University 3*47437Skarels * of California. All rights reserved. 436515Sbostic * 543653Sbostic * %sccs.include.redist.c% 619843Sdist */ 719843Sdist 812678Ssam #ifndef lint 919843Sdist char copyright[] = 10*47437Skarels "@(#) Copyright (c) 1980, 1987, 1988, 1991 The Regents of the University of California.\n\ 1119843Sdist All rights reserved.\n"; 1236515Sbostic #endif /* not lint */ 1312678Ssam 1419843Sdist #ifndef lint 15*47437Skarels static char sccsid[] = "@(#)login.c 5.67 (Berkeley) 03/15/91"; 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> 2912687Ssam 301043Sbill #include <utmp.h> 311043Sbill #include <signal.h> 3212678Ssam #include <errno.h> 3316453Sroot #include <ttyent.h> 3416453Sroot #include <syslog.h> 3526862Smckusick #include <grp.h> 3636460Sbostic #include <pwd.h> 3736515Sbostic #include <setjmp.h> 3836460Sbostic #include <stdio.h> 3942063Sbostic #include <string.h> 4037203Sbostic #include <tzfile.h> 4137203Sbostic #include "pathnames.h" 421043Sbill 4336460Sbostic #define TTYGRPNAME "tty" /* name of group to own ttys */ 4426862Smckusick 4512687Ssam /* 4636460Sbostic * This bounds the time given to login. Not a define so it can 4736460Sbostic * be patched on machines where it's too small. 4812687Ssam */ 4931509Sbostic int timeout = 300; 5043653Sbostic #ifdef KERBEROS 5143653Sbostic int notickets = 1; 5243653Sbostic #endif 536005Swnj 5436647Skarels struct passwd *pwd; 5536647Skarels int failures; 5640490Sbostic char term[64], *envinit[1], *hostname, *username, *tty; 576005Swnj 581043Sbill main(argc, argv) 5936460Sbostic int argc; 6036460Sbostic char **argv; 611043Sbill { 6243653Sbostic extern int optind; 6336460Sbostic extern char *optarg, **environ; 6436880Sbostic struct timeval tp; 6536460Sbostic struct group *gr; 6636460Sbostic register int ch; 6736647Skarels register char *p; 6839271Skfall int ask, fflag, hflag, pflag, cnt, uid; 6944568Smarc int quietlog, rval; 7043653Sbostic char *domain, *salt, *ttyn; 7137692Sbostic char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10]; 7238034Skfall char localhost[MAXHOSTNAMELEN]; 7336880Sbostic char *ctime(), *ttyname(), *stypeof(), *crypt(), *getpass(); 7436460Sbostic time_t time(); 7536460Sbostic off_t lseek(); 7643653Sbostic void timedout(); 771043Sbill 7836460Sbostic (void)signal(SIGALRM, timedout); 7936460Sbostic (void)alarm((u_int)timeout); 8036460Sbostic (void)signal(SIGQUIT, SIG_IGN); 8136460Sbostic (void)signal(SIGINT, SIG_IGN); 8236460Sbostic (void)setpriority(PRIO_PROCESS, 0, 0); 8336460Sbostic 8442502Sbostic openlog("login", LOG_ODELAY, LOG_AUTH); 8542502Sbostic 8612687Ssam /* 8718549Ssam * -p is used by getty to tell login not to destroy the environment 8831695Skarels * -f is used to skip a second login authentication 8936523Skarels * -h is used by other servers to pass the name of the remote 9036523Skarels * host to login so that it may be placed in utmp and wtmp 9112687Ssam */ 9238034Skfall domain = NULL; 9338034Skfall if (gethostname(localhost, sizeof(localhost)) < 0) 9438034Skfall syslog(LOG_ERR, "couldn't get local hostname: %m"); 9538034Skfall else 9638034Skfall domain = index(localhost, '.'); 9736460Sbostic 9837203Sbostic fflag = hflag = pflag = 0; 9939271Skfall uid = getuid(); 10037203Sbostic while ((ch = getopt(argc, argv, "fh:p")) != EOF) 10136515Sbostic switch (ch) { 10236460Sbostic case 'f': 10336460Sbostic fflag = 1; 10436460Sbostic break; 10536460Sbostic case 'h': 10639271Skfall if (uid) { 10737203Sbostic (void)fprintf(stderr, 10836460Sbostic "login: -h for super-user only.\n"); 10932313Sbostic exit(1); 11031695Skarels } 11136460Sbostic hflag = 1; 11236460Sbostic if (domain && (p = index(optarg, '.')) && 11336553Sbostic strcasecmp(p, domain) == 0) 11436460Sbostic *p = 0; 11536460Sbostic hostname = optarg; 11636460Sbostic break; 11736460Sbostic case 'p': 11818549Ssam pflag = 1; 11936460Sbostic break; 12036460Sbostic case '?': 12136460Sbostic default: 12239271Skfall if (!uid) 12339271Skfall syslog(LOG_ERR, "invalid flag %c", ch); 12437203Sbostic (void)fprintf(stderr, 12537203Sbostic "usage: login [-fp] [username]\n"); 12636460Sbostic exit(1); 12718549Ssam } 12836460Sbostic argc -= optind; 12936460Sbostic argv += optind; 13036549Sbostic if (*argv) { 13136647Skarels username = *argv; 13243653Sbostic if (strlen(username) > UT_NAMESIZE) 13343653Sbostic username[UT_NAMESIZE] = '\0'; 13436549Sbostic ask = 0; 13536647Skarels } else 13636549Sbostic ask = 1; 13736460Sbostic 13836460Sbostic for (cnt = getdtablesize(); cnt > 2; cnt--) 13936460Sbostic close(cnt); 14036460Sbostic 1411043Sbill ttyn = ttyname(0); 14237692Sbostic if (ttyn == NULL || *ttyn == '\0') { 14337692Sbostic (void)sprintf(tname, "%s??", _PATH_TTY); 14437692Sbostic ttyn = tname; 14537692Sbostic } 14636460Sbostic if (tty = rindex(ttyn, '/')) 14736460Sbostic ++tty; 14836460Sbostic else 14916453Sroot tty = ttyn; 15036460Sbostic 15136549Sbostic for (cnt = 0;; ask = 1) { 15236549Sbostic if (ask) { 15336515Sbostic fflag = 0; 15436460Sbostic getloginname(); 15536515Sbostic } 15636647Skarels /* 15739271Skfall * Note if trying multiple user names; log failures for 15839271Skfall * previous user name, but don't bother logging one failure 15936647Skarels * for nonexistent name (mistyped username). 16036647Skarels */ 16136647Skarels if (failures && strcmp(tbuf, username)) { 16236647Skarels if (failures > (pwd ? 0 : 1)) 16336549Sbostic badlogin(tbuf); 16436647Skarels failures = 0; 16536549Sbostic } 16636647Skarels (void)strcpy(tbuf, username); 16743659Sbostic 16836515Sbostic if (pwd = getpwnam(username)) 16936515Sbostic salt = pwd->pw_passwd; 17043659Sbostic else 17143659Sbostic salt = "xx"; 17236460Sbostic 17312687Ssam /* 17443674Sbostic * if we have a valid account name, and it doesn't have a 17543674Sbostic * password, or the -f option was specified and the caller 17643674Sbostic * is root or the caller isn't changing their uid, don't 17743674Sbostic * authenticate. 17812687Ssam */ 17943674Sbostic if (pwd && (*pwd->pw_passwd == '\0' || 18043674Sbostic fflag && (uid == 0 || uid == pwd->pw_uid))) 18136460Sbostic break; 18244348Skarels fflag = 0; 18312687Ssam 18439271Skfall /* 18539271Skfall * If trying to log in as root, but with insecure terminal, 18639271Skfall * refuse the login attempt. 18739271Skfall */ 18843659Sbostic if (pwd && pwd->pw_uid == 0 && !rootterm(tty)) { 18939271Skfall (void)fprintf(stderr, 19039271Skfall "%s login refused on this terminal.\n", 19139271Skfall pwd->pw_name); 19239271Skfall if (hostname) 19339271Skfall syslog(LOG_NOTICE, 19439271Skfall "LOGIN %s REFUSED FROM %s ON TTY %s", 19539271Skfall pwd->pw_name, hostname, tty); 19639271Skfall else 19739271Skfall syslog(LOG_NOTICE, 19839271Skfall "LOGIN %s REFUSED ON TTY %s", 19939271Skfall pwd->pw_name, tty); 20039271Skfall continue; 20139271Skfall } 20239271Skfall 20343653Sbostic (void)setpriority(PRIO_PROCESS, 0, -4); 20443659Sbostic 20543653Sbostic p = getpass("Password:"); 20636608Skfall 20743659Sbostic if (pwd) { 20843653Sbostic #ifdef KERBEROS 20943659Sbostic rval = klogin(pwd, localhost, p); 21043659Sbostic if (rval == 1) 21143659Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21243659Sbostic #else 21343653Sbostic rval = strcmp(crypt(p, salt), pwd->pw_passwd); 21437203Sbostic #endif 21543659Sbostic } 21643653Sbostic bzero(p, strlen(p)); 21743659Sbostic 21843659Sbostic (void)setpriority(PRIO_PROCESS, 0, 0); 21943659Sbostic 22043659Sbostic if (pwd && !rval) 22136460Sbostic break; 22236460Sbostic 22343659Sbostic (void)printf("Login incorrect\n"); 22436647Skarels failures++; 22536549Sbostic /* we allow 10 tries, but after 3 we start backing off */ 22636549Sbostic if (++cnt > 3) { 22736549Sbostic if (cnt >= 10) { 22836549Sbostic badlogin(username); 22936549Sbostic sleepexit(1); 23036549Sbostic } 23136549Sbostic sleep((u_int)((cnt - 3) * 5)); 2322822Swnj } 23336460Sbostic } 2341043Sbill 23536460Sbostic /* committed to login -- turn off timeout */ 23636460Sbostic (void)alarm((u_int)0); 23736460Sbostic 23837692Sbostic /* paranoia... */ 23937692Sbostic endpwent(); 24037692Sbostic 24143663Sbostic /* if user not super-user, check for disabled logins */ 24243663Sbostic if (pwd->pw_uid) 24343663Sbostic checknologin(); 24443663Sbostic 24536515Sbostic if (chdir(pwd->pw_dir) < 0) { 24637203Sbostic (void)printf("No directory %s!\n", pwd->pw_dir); 24736515Sbostic if (chdir("/")) 24836515Sbostic exit(0); 24936515Sbostic pwd->pw_dir = "/"; 25037203Sbostic (void)printf("Logging in with home = \"/\".\n"); 25136515Sbostic } 25236515Sbostic 25337203Sbostic quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; 25437203Sbostic 25536880Sbostic if (pwd->pw_change || pwd->pw_expire) 25636880Sbostic (void)gettimeofday(&tp, (struct timezone *)NULL); 25736880Sbostic if (pwd->pw_change) 25836880Sbostic if (tp.tv_sec >= pwd->pw_change) { 25937203Sbostic (void)printf("Sorry -- your password has expired.\n"); 26036880Sbostic sleepexit(1); 26136880Sbostic } 26238216Sbostic else if (pwd->pw_change - tp.tv_sec < 26343660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 26443660Sbostic (void)printf("Warning: your password expires on %s", 26543660Sbostic ctime(&pwd->pw_expire)); 26636880Sbostic if (pwd->pw_expire) 26736880Sbostic if (tp.tv_sec >= pwd->pw_expire) { 26837203Sbostic (void)printf("Sorry -- your account has expired.\n"); 26936880Sbostic sleepexit(1); 27036880Sbostic } 27138216Sbostic else if (pwd->pw_expire - tp.tv_sec < 27243660Sbostic 2 * DAYSPERWEEK * SECSPERDAY && !quietlog) 27343660Sbostic (void)printf("Warning: your account expires on %s", 27443660Sbostic ctime(&pwd->pw_expire)); 27536880Sbostic 27636515Sbostic /* nothing else left to fail -- really log in */ 27736460Sbostic { 27836460Sbostic struct utmp utmp; 27936460Sbostic 28043653Sbostic bzero((void *)&utmp, sizeof(utmp)); 28136460Sbostic (void)time(&utmp.ut_time); 28236460Sbostic strncpy(utmp.ut_name, username, sizeof(utmp.ut_name)); 28336591Sbostic if (hostname) 28436591Sbostic strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); 28536460Sbostic strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line)); 28636460Sbostic login(&utmp); 28736460Sbostic } 28836460Sbostic 28936549Sbostic dolastlog(quietlog); 29036460Sbostic 29136460Sbostic (void)chown(ttyn, pwd->pw_uid, 29236460Sbostic (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid); 29336460Sbostic (void)chmod(ttyn, 0620); 29436460Sbostic (void)setgid(pwd->pw_gid); 29536460Sbostic 29636460Sbostic initgroups(username, pwd->pw_gid); 29736460Sbostic 29836515Sbostic if (*pwd->pw_shell == '\0') 29937203Sbostic pwd->pw_shell = _PATH_BSHELL; 30036515Sbostic 30136460Sbostic /* destroy environment unless user has requested preservation */ 30218549Ssam if (!pflag) 30318549Ssam environ = envinit; 30436515Sbostic (void)setenv("HOME", pwd->pw_dir, 1); 30536515Sbostic (void)setenv("SHELL", pwd->pw_shell, 1); 30618549Ssam if (term[0] == '\0') 30736647Skarels strncpy(term, stypeof(tty), sizeof(term)); 30836515Sbostic (void)setenv("TERM", term, 0); 30936515Sbostic (void)setenv("USER", pwd->pw_name, 1); 31037252Sbostic (void)setenv("PATH", _PATH_DEFPATH, 0); 31118549Ssam 31216453Sroot if (tty[sizeof("tty")-1] == 'd') 31318549Ssam syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); 31444348Skarels /* if fflag is on, assume caller/authenticator has logged root login */ 31544348Skarels if (pwd->pw_uid == 0 && fflag == 0) 31636460Sbostic if (hostname) 31736647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s FROM %s", 31836549Sbostic tty, hostname); 31925230Smckusick else 32036647Skarels syslog(LOG_NOTICE, "ROOT LOGIN ON %s", tty); 32136460Sbostic 32243653Sbostic #ifdef KERBEROS 32343653Sbostic if (!quietlog && notickets == 1) 32443653Sbostic (void)printf("Warning: no Kerberos tickets issued.\n"); 32543653Sbostic #endif 32643653Sbostic 3276329Swnj if (!quietlog) { 32817664Sserge struct stat st; 32918549Ssam 330*47437Skarels printf( 331*47437Skarels "Copyright (c) 1980,1983,1986,1988,1990,1991 The Regents of the University\n%s", 332*47437Skarels "of California. All rights reserved.\n\n"); 33336460Sbostic motd(); 33437203Sbostic (void)sprintf(tbuf, "%s/%s", _PATH_MAILDIR, pwd->pw_name); 33536460Sbostic if (stat(tbuf, &st) == 0 && st.st_size != 0) 33637203Sbostic (void)printf("You have %smail.\n", 33736460Sbostic (st.st_mtime > st.st_atime) ? "new " : ""); 3382822Swnj } 33936460Sbostic 34036460Sbostic (void)signal(SIGALRM, SIG_DFL); 34136460Sbostic (void)signal(SIGQUIT, SIG_DFL); 34236460Sbostic (void)signal(SIGINT, SIG_DFL); 34336460Sbostic (void)signal(SIGTSTP, SIG_IGN); 34436460Sbostic 34536460Sbostic tbuf[0] = '-'; 34636460Sbostic strcpy(tbuf + 1, (p = rindex(pwd->pw_shell, '/')) ? 34736460Sbostic p + 1 : pwd->pw_shell); 34837203Sbostic 34940009Ssklower if (setlogin(pwd->pw_name) < 0) 35040009Ssklower syslog(LOG_ERR, "setlogin() failure: %m"); 35137692Sbostic 35237203Sbostic /* discard permissions last so can't get killed and drop core */ 35337203Sbostic (void)setuid(pwd->pw_uid); 35437203Sbostic 35536460Sbostic execlp(pwd->pw_shell, tbuf, 0); 35637203Sbostic (void)fprintf(stderr, "login: no shell: %s.\n", strerror(errno)); 3571043Sbill exit(0); 3581043Sbill } 3591043Sbill 36036460Sbostic getloginname() 36112687Ssam { 36236460Sbostic register int ch; 36336460Sbostic register char *p; 36436460Sbostic static char nbuf[UT_NAMESIZE + 1]; 36512687Ssam 36636460Sbostic for (;;) { 36737203Sbostic (void)printf("login: "); 36836515Sbostic for (p = nbuf; (ch = getchar()) != '\n'; ) { 36936549Sbostic if (ch == EOF) { 37036549Sbostic badlogin(username); 37112687Ssam exit(0); 37236549Sbostic } 37336515Sbostic if (p < nbuf + UT_NAMESIZE) 37436460Sbostic *p++ = ch; 37512687Ssam } 37636460Sbostic if (p > nbuf) 37736460Sbostic if (nbuf[0] == '-') 37837203Sbostic (void)fprintf(stderr, 37936460Sbostic "login names may not start with '-'.\n"); 38036460Sbostic else { 38136460Sbostic *p = '\0'; 38236460Sbostic username = nbuf; 38336515Sbostic break; 38436460Sbostic } 38512687Ssam } 38612687Ssam } 38712687Ssam 38843653Sbostic void 38912687Ssam timedout() 39012687Ssam { 39137203Sbostic (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout); 39212687Ssam exit(0); 39312687Ssam } 39412687Ssam 39536647Skarels rootterm(ttyn) 39636647Skarels char *ttyn; 3971043Sbill { 39836460Sbostic struct ttyent *t; 3996466Swnj 40036647Skarels return((t = getttynam(ttyn)) && t->ty_status&TTY_SECURE); 4011043Sbill } 4021043Sbill 40336515Sbostic jmp_buf motdinterrupt; 40436515Sbostic 40536460Sbostic motd() 4062822Swnj { 40736515Sbostic register int fd, nchars; 40838726Skfall sig_t oldint; 40946835Sbostic void sigint(); 41036515Sbostic char tbuf[8192]; 4112822Swnj 41237203Sbostic if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0) 41336460Sbostic return; 41436460Sbostic oldint = signal(SIGINT, sigint); 41536515Sbostic if (setjmp(motdinterrupt) == 0) 41636515Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 41736515Sbostic (void)write(fileno(stdout), tbuf, nchars); 41836460Sbostic (void)signal(SIGINT, oldint); 41936515Sbostic (void)close(fd); 42036460Sbostic } 42136460Sbostic 42246835Sbostic void 42336460Sbostic sigint() 42436460Sbostic { 42536515Sbostic longjmp(motdinterrupt, 1); 42636460Sbostic } 42736460Sbostic 42836460Sbostic checknologin() 42936460Sbostic { 43036460Sbostic register int fd, nchars; 43136515Sbostic char tbuf[8192]; 43236460Sbostic 43337203Sbostic if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) { 43436460Sbostic while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0) 43536460Sbostic (void)write(fileno(stdout), tbuf, nchars); 43636460Sbostic sleepexit(0); 4372822Swnj } 4382822Swnj } 4392822Swnj 44036549Sbostic dolastlog(quiet) 44136460Sbostic int quiet; 4421043Sbill { 44336460Sbostic struct lastlog ll; 44436460Sbostic int fd; 44536880Sbostic char *ctime(); 4461043Sbill 44737203Sbostic if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) { 44836515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 44936460Sbostic if (!quiet) { 45036460Sbostic if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) && 45136460Sbostic ll.ll_time != 0) { 45237203Sbostic (void)printf("Last login: %.*s ", 45336460Sbostic 24-5, (char *)ctime(&ll.ll_time)); 45436460Sbostic if (*ll.ll_host != '\0') 45537203Sbostic (void)printf("from %.*s\n", 45636460Sbostic sizeof(ll.ll_host), ll.ll_host); 45736460Sbostic else 45837203Sbostic (void)printf("on %.*s\n", 45936460Sbostic sizeof(ll.ll_line), ll.ll_line); 46036460Sbostic } 46136515Sbostic (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET); 46236460Sbostic } 46343653Sbostic bzero((void *)&ll, sizeof(ll)); 46436460Sbostic (void)time(&ll.ll_time); 46536460Sbostic strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 46636591Sbostic if (hostname) 46736591Sbostic strncpy(ll.ll_host, hostname, sizeof(ll.ll_host)); 46836460Sbostic (void)write(fd, (char *)&ll, sizeof(ll)); 46936460Sbostic (void)close(fd); 4701043Sbill } 4711043Sbill } 4721043Sbill 47336549Sbostic badlogin(name) 47436549Sbostic char *name; 47536549Sbostic { 47636647Skarels if (failures == 0) 47736549Sbostic return; 47845431Sbostic if (hostname) { 47945431Sbostic syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s", 48045431Sbostic failures, failures > 1 ? "S" : "", hostname); 48145431Sbostic syslog(LOG_AUTHPRIV|LOG_NOTICE, 48245431Sbostic "%d LOGIN FAILURE%s FROM %s, %s", 48336647Skarels failures, failures > 1 ? "S" : "", hostname, name); 48445431Sbostic } else { 48545431Sbostic syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s", 48645431Sbostic failures, failures > 1 ? "S" : "", tty); 48745431Sbostic syslog(LOG_AUTHPRIV|LOG_NOTICE, 48845431Sbostic "%d LOGIN FAILURE%s ON %s, %s", 48936647Skarels failures, failures > 1 ? "S" : "", tty, name); 49045431Sbostic } 49136549Sbostic } 49236549Sbostic 4932822Swnj #undef UNKNOWN 49436460Sbostic #define UNKNOWN "su" 4951043Sbill 4961043Sbill char * 49736647Skarels stypeof(ttyid) 49836647Skarels char *ttyid; 4991043Sbill { 50036460Sbostic struct ttyent *t; 5011043Sbill 50236647Skarels return(ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN); 5031043Sbill } 5046005Swnj 50536460Sbostic sleepexit(eval) 50636460Sbostic int eval; 50726862Smckusick { 50836460Sbostic sleep((u_int)5); 50936460Sbostic exit(eval); 51026862Smckusick } 511