112678Ssam #ifndef lint 2*16453Sroot static char *sccsid = "@(#)login.c 4.34 (Berkeley) 84/05/07"; 312678Ssam #endif 412678Ssam 51043Sbill /* 61043Sbill * login [ name ] 712687Ssam * login -r hostname (for rlogind) 812687Ssam * login -h hostname (for telnetd, etc.) 91043Sbill */ 101043Sbill 1112984Ssam #include <sys/param.h> 1212687Ssam #include <sys/quota.h> 1312687Ssam #include <sys/stat.h> 1412687Ssam #include <sys/time.h> 1512687Ssam #include <sys/resource.h> 16*16453Sroot #include <sys/file.h> 1712687Ssam 181043Sbill #include <sgtty.h> 191043Sbill #include <utmp.h> 201043Sbill #include <signal.h> 211043Sbill #include <pwd.h> 221043Sbill #include <stdio.h> 231043Sbill #include <lastlog.h> 2412678Ssam #include <errno.h> 25*16453Sroot #include <ttyent.h> 26*16453Sroot #include <syslog.h> 271043Sbill 28*16453Sroot #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 292822Swnj #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 302822Swnj 316197Sroot #define NMAX sizeof(utmp.ut_name) 321043Sbill 332822Swnj #define FALSE 0 342822Swnj #define TRUE -1 352822Swnj 362822Swnj char nolog[] = "/etc/nologin"; 372822Swnj char qlog[] = ".hushlogin"; 381043Sbill char maildir[30] = "/usr/spool/mail/"; 391043Sbill char lastlog[] = "/usr/adm/lastlog"; 409867Ssam struct passwd nouser = {"", "nope", -1, -1, -1, "", "", "", "" }; 411043Sbill struct sgttyb ttyb; 421043Sbill struct utmp utmp; 431043Sbill char minusnam[16] = "-"; 4412687Ssam /* 4512687Ssam * This bounds the time given to login. We initialize it here 4612687Ssam * so it can be patched on machines where it's too small. 4712687Ssam */ 4812687Ssam int timeout = 60; 496005Swnj 501043Sbill char homedir[64] = "HOME="; 511043Sbill char shell[64] = "SHELL="; 521043Sbill char term[64] = "TERM="; 532822Swnj char user[20] = "USER="; 546005Swnj 556005Swnj char *envinit[] = 5612687Ssam { homedir, shell, "PATH=:/usr/ucb:/bin:/usr/bin", term, user, 0 }; 576005Swnj 581043Sbill struct passwd *pwd; 596005Swnj char *strcat(), *rindex(), *index(); 6012687Ssam int timedout(); 611043Sbill char *ttyname(); 621043Sbill char *crypt(); 631043Sbill char *getpass(); 641043Sbill char *stypeof(); 651043Sbill extern char **environ; 6612678Ssam extern int errno; 671043Sbill 6813074Ssam struct tchars tc = { 6913074Ssam CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK 701365Sbill }; 7113074Ssam struct ltchars ltc = { 7213074Ssam CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT 7313074Ssam }; 741365Sbill 756005Swnj int rflag; 766197Sroot char rusername[NMAX+1], lusername[NMAX+1]; 776005Swnj char rpassword[NMAX+1]; 786878Smckusick char name[NMAX+1]; 796197Sroot char *rhost; 806005Swnj 811043Sbill main(argc, argv) 8212687Ssam char *argv[]; 831043Sbill { 841043Sbill register char *namep; 85*16453Sroot int t, f, c; 8612687Ssam int invalid, quietlog; 872822Swnj FILE *nlfd; 88*16453Sroot char *ttyn, *tty; 896197Sroot int ldisc = 0, zero = 0; 901043Sbill 9112687Ssam signal(SIGALRM, timedout); 9212687Ssam alarm(timeout); 931043Sbill signal(SIGQUIT, SIG_IGN); 941043Sbill signal(SIGINT, SIG_IGN); 9512687Ssam setpriority(PRIO_PROCESS, 0, 0); 9612678Ssam quota(Q_SETUID, 0, 0, 0); 9712687Ssam /* 9812687Ssam * -r is used by rlogind to cause the autologin protocol; 9912687Ssam * -h is used by other servers to pass the name of the 10012687Ssam * remote host to login so that it may be placed in utmp and wtmp 10112687Ssam */ 10212687Ssam if (argc > 1) { 10312687Ssam if (strcmp(argv[1], "-r") == 0) { 10412687Ssam rflag = doremotelogin(argv[2]); 10512687Ssam SCPYN(utmp.ut_host, argv[2]); 10612687Ssam argc = 0; 1076197Sroot } 10812687Ssam if (strcmp(argv[1], "-h") == 0 && getuid() == 0) { 10912687Ssam SCPYN(utmp.ut_host, argv[2]); 11012687Ssam argc = 0; 1116197Sroot } 1126005Swnj } 11313074Ssam ioctl(0, TIOCLSET, &zero); 1141547Sbill ioctl(0, TIOCNXCL, 0); 1156329Swnj ioctl(0, FIONBIO, &zero); 1166329Swnj ioctl(0, FIOASYNC, &zero); 11713074Ssam ioctl(0, TIOCGETP, &ttyb); 11812687Ssam /* 11912687Ssam * If talking to an rlogin process, 12012687Ssam * propagate the terminal type and 12112687Ssam * baud rate across the network. 12212687Ssam */ 12312687Ssam if (rflag) 12412687Ssam doremoteterm(term, &ttyb); 12513074Ssam ioctl(0, TIOCSLTC, <c); 12613074Ssam ioctl(0, TIOCSETC, &tc); 12713074Ssam ioctl(0, TIOCSETP, &ttyb); 12812687Ssam for (t = getdtablesize(); t > 3; t--) 1291043Sbill close(t); 1301043Sbill ttyn = ttyname(0); 131*16453Sroot if (ttyn == (char *)0) 1321043Sbill ttyn = "/dev/tty??"; 133*16453Sroot tty = rindex(ttyn, '/'); 134*16453Sroot if (tty == NULL) 135*16453Sroot tty = ttyn; 136*16453Sroot else 137*16453Sroot tty++; 138*16453Sroot openlog("login", 0, 0); 139*16453Sroot t = 0; 1402822Swnj do { 1412822Swnj ldisc = 0; 1422822Swnj ioctl(0, TIOCSETD, &ldisc); 1432822Swnj invalid = FALSE; 1442822Swnj SCPYN(utmp.ut_name, ""); 14512687Ssam /* 14612687Ssam * Name specified, take it. 14712687Ssam */ 14812687Ssam if (argc > 1) { 1492822Swnj SCPYN(utmp.ut_name, argv[1]); 1502822Swnj argc = 0; 1511043Sbill } 15212687Ssam /* 15312687Ssam * If remote login take given name, 15412687Ssam * otherwise prompt user for something. 15512687Ssam */ 1566329Swnj if (rflag) { 1579867Ssam SCPYN(utmp.ut_name, lusername); 15812687Ssam /* autologin failed, prompt for passwd */ 1596329Swnj if (rflag == -1) 1606329Swnj rflag = 0; 16113470Ssam } else 16212687Ssam getloginname(&utmp); 1632822Swnj if (!strcmp(pwd->pw_shell, "/bin/csh")) { 1642822Swnj ldisc = NTTYDISC; 1652822Swnj ioctl(0, TIOCSETD, &ldisc); 1662822Swnj } 16712687Ssam /* 16812687Ssam * If no remote login authentication and 16912687Ssam * a password exists for this user, prompt 17012687Ssam * for one and verify it. 17112687Ssam */ 17212687Ssam if (!rflag && *pwd->pw_passwd != '\0') { 17312687Ssam char *pp; 17412687Ssam 17512687Ssam setpriority(PRIO_PROCESS, 0, -4); 17612687Ssam pp = getpass("Password:"); 17712687Ssam namep = crypt(pp, pwd->pw_passwd); 17812687Ssam setpriority(PRIO_PROCESS, 0, 0); 17912687Ssam if (strcmp(namep, pwd->pw_passwd)) 18012687Ssam invalid = TRUE; 1812822Swnj } 18212687Ssam /* 18312687Ssam * If user not super-user, check for logins disabled. 18412687Ssam */ 1852822Swnj if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) > 0) { 1862822Swnj while ((c = getc(nlfd)) != EOF) 1872822Swnj putchar(c); 1882822Swnj fflush(stdout); 1892822Swnj sleep(5); 1902822Swnj exit(0); 1912822Swnj } 19212687Ssam /* 19312687Ssam * If valid so far and root is logging in, 19412687Ssam * see if root logins on this terminal are permitted. 19512687Ssam */ 196*16453Sroot if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) { 197*16453Sroot syslog(LOG_INFO, "ROOT LOGIN REFUSED %s", tty); 1982822Swnj invalid = TRUE; 1992822Swnj } 2002822Swnj if (invalid) { 2011043Sbill printf("Login incorrect\n"); 202*16453Sroot if (++t >= 5) { 203*16453Sroot syslog(LOG_INFO, 204*16453Sroot "REPEATED LOGIN FAILURES %s, %s", 205*16453Sroot tty, utmp.ut_name); 206*16453Sroot ioctl(0, TIOCHPCL, (struct sgttyb *) 0); 207*16453Sroot close(0); 208*16453Sroot close(1); 209*16453Sroot close(2); 210*16453Sroot sleep(10); 211*16453Sroot exit(1); 212*16453Sroot } 2131043Sbill } 2142822Swnj if (*pwd->pw_shell == '\0') 2152822Swnj pwd->pw_shell = "/bin/sh"; 2162822Swnj if (chdir(pwd->pw_dir) < 0 && !invalid ) { 2172822Swnj if (chdir("/") < 0) { 2182822Swnj printf("No directory!\n"); 2192822Swnj invalid = TRUE; 2202822Swnj } else { 22112687Ssam printf("No directory! %s\n", 22212687Ssam "Logging in with home=/"); 2232822Swnj pwd->pw_dir = "/"; 2242822Swnj } 2251043Sbill } 22612687Ssam /* 22712687Ssam * Remote login invalid must have been because 22812687Ssam * of a restriction of some sort, no extra chances. 22912687Ssam */ 2306005Swnj if (rflag && invalid) 2316005Swnj exit(1); 2322822Swnj } while (invalid); 23312687Ssam /* committed to login turn off timeout */ 23412687Ssam alarm(0); 2351043Sbill 23612678Ssam if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0) { 23712678Ssam if (errno == EUSERS) 23812678Ssam printf("%s.\n%s.\n", 23912678Ssam "Too many users logged on already", 24012678Ssam "Try again later"); 24112678Ssam else if (errno == EPROCLIM) 24212678Ssam printf("You have too many processes running.\n"); 24312678Ssam else 24412678Ssam perror("setuid"); 24512678Ssam sleep(5); 24612678Ssam exit(0); 24712678Ssam } 2481043Sbill time(&utmp.ut_time); 2491043Sbill t = ttyslot(); 250*16453Sroot if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) { 2511043Sbill lseek(f, (long)(t*sizeof(utmp)), 0); 252*16453Sroot SCPYN(utmp.ut_line, tty); 2531043Sbill write(f, (char *)&utmp, sizeof(utmp)); 2541043Sbill close(f); 2551043Sbill } 256*16453Sroot if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) { 2571043Sbill write(f, (char *)&utmp, sizeof(utmp)); 2581043Sbill close(f); 2591043Sbill } 260*16453Sroot quietlog = access(qlog, F_OK) == 0; 261*16453Sroot if ((f = open(lastlog, O_RDWR)) >= 0) { 2622822Swnj struct lastlog ll; 2632822Swnj 2642822Swnj lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0); 2652822Swnj if (read(f, (char *) &ll, sizeof ll) == sizeof ll && 26612687Ssam ll.ll_time != 0 && !quietlog) { 26712687Ssam printf("Last login: %.*s ", 26812687Ssam 24-5, (char *)ctime(&ll.ll_time)); 26912687Ssam if (*ll.ll_host != '\0') 27012687Ssam printf("from %.*s\n", 27112687Ssam sizeof (ll.ll_host), ll.ll_host); 27212687Ssam else 27312687Ssam printf("on %.*s\n", 27412687Ssam sizeof (ll.ll_line), ll.ll_line); 2752822Swnj } 2762822Swnj lseek(f, (long)pwd->pw_uid * sizeof (struct lastlog), 0); 2772822Swnj time(&ll.ll_time); 278*16453Sroot SCPYN(ll.ll_line, tty); 27912687Ssam SCPYN(ll.ll_host, utmp.ut_host); 2802822Swnj write(f, (char *) &ll, sizeof ll); 2812822Swnj close(f); 2822822Swnj } 2831043Sbill chown(ttyn, pwd->pw_uid, pwd->pw_gid); 2849867Ssam chmod(ttyn, 0622); 2851043Sbill setgid(pwd->pw_gid); 2866878Smckusick strncpy(name, utmp.ut_name, NMAX); 2876878Smckusick name[NMAX] = '\0'; 2889224Ssam initgroups(name, pwd->pw_gid); 28912678Ssam quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0); 2901043Sbill setuid(pwd->pw_uid); 2911043Sbill environ = envinit; 2921043Sbill strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); 2931043Sbill strncat(shell, pwd->pw_shell, sizeof(shell)-7); 2946329Swnj if (term[strlen("TERM=")] == 0) 295*16453Sroot strncat(term, stypeof(tty), sizeof(term)-6); 2962822Swnj strncat(user, pwd->pw_name, sizeof(user)-6); 2971043Sbill if ((namep = rindex(pwd->pw_shell, '/')) == NULL) 2981043Sbill namep = pwd->pw_shell; 2991043Sbill else 3001043Sbill namep++; 3011043Sbill strcat(minusnam, namep); 302*16453Sroot if (tty[sizeof("tty")-1] == 'd') 303*16453Sroot syslog(LOG_INFO, "DIALUP %s %s", tty, pwd->pw_name); 3046329Swnj if (!quietlog) { 3052822Swnj showmotd(); 3062822Swnj strcat(maildir, pwd->pw_name); 307*16453Sroot if (access(maildir, R_OK) == 0) { 3082822Swnj struct stat statb; 3092822Swnj stat(maildir, &statb); 3102822Swnj if (statb.st_size) 3112822Swnj printf("You have mail.\n"); 3122822Swnj } 3132822Swnj } 31412687Ssam signal(SIGALRM, SIG_DFL); 3151043Sbill signal(SIGQUIT, SIG_DFL); 3161043Sbill signal(SIGINT, SIG_DFL); 3173935Sroot signal(SIGTSTP, SIG_IGN); 3181043Sbill execlp(pwd->pw_shell, minusnam, 0); 3192822Swnj perror(pwd->pw_shell); 3201043Sbill printf("No shell\n"); 3211043Sbill exit(0); 3221043Sbill } 3231043Sbill 32412687Ssam getloginname(up) 32512687Ssam register struct utmp *up; 32612687Ssam { 32712687Ssam register char *namep; 32812712Ssam char c; 32912687Ssam 33012687Ssam while (up->ut_name[0] == '\0') { 33114897Sedward namep = up->ut_name; 33212712Ssam printf("login: "); 33312687Ssam while ((c = getchar()) != '\n') { 33412687Ssam if (c == ' ') 33512687Ssam c = '_'; 33612687Ssam if (c == EOF) 33712687Ssam exit(0); 33812687Ssam if (namep < up->ut_name+NMAX) 33912687Ssam *namep++ = c; 34012687Ssam } 34112687Ssam } 34214897Sedward strncpy(lusername, up->ut_name, NMAX); 34314897Sedward lusername[NMAX] = 0; 34414897Sedward if ((pwd = getpwnam(lusername)) == NULL) 34512687Ssam pwd = &nouser; 34612687Ssam } 34712687Ssam 34812687Ssam timedout() 34912687Ssam { 35012687Ssam 35112687Ssam printf("Login timed out after %d seconds\n", timeout); 35212687Ssam exit(0); 35312687Ssam } 35412687Ssam 3551043Sbill int stopmotd; 3561043Sbill catch() 3571043Sbill { 3586466Swnj 3591043Sbill signal(SIGINT, SIG_IGN); 3601043Sbill stopmotd++; 3611043Sbill } 3621043Sbill 3632822Swnj rootterm(tty) 3646466Swnj char *tty; 3652822Swnj { 366*16453Sroot register struct ttyent *t; 3672822Swnj 368*16453Sroot if ((t = getttynam(tty)) != NULL) { 369*16453Sroot if (t->ty_status & TTY_SECURE) 370*16453Sroot return (1); 3712822Swnj } 372*16453Sroot return (0); 3732822Swnj } 3742822Swnj 3751043Sbill showmotd() 3761043Sbill { 3771043Sbill FILE *mf; 3781043Sbill register c; 3791043Sbill 3801043Sbill signal(SIGINT, catch); 381*16453Sroot if ((mf = fopen("/etc/motd", "r")) != NULL) { 3822822Swnj while ((c = getc(mf)) != EOF && stopmotd == 0) 3831043Sbill putchar(c); 3841043Sbill fclose(mf); 3851043Sbill } 3861043Sbill signal(SIGINT, SIG_IGN); 3871043Sbill } 3881043Sbill 3892822Swnj #undef UNKNOWN 3901043Sbill #define UNKNOWN "su" 3911043Sbill 3921043Sbill char * 3931043Sbill stypeof(ttyid) 39412687Ssam char *ttyid; 3951043Sbill { 396*16453Sroot register struct ttyent *t; 3971043Sbill 398*16453Sroot if (ttyid == NULL || (t = getttynam(ttyid)) == NULL) 3991043Sbill return (UNKNOWN); 400*16453Sroot return (t->ty_type); 4011043Sbill } 4026005Swnj 40312687Ssam doremotelogin(host) 40412687Ssam char *host; 40512687Ssam { 40612687Ssam FILE *hostf; 40712687Ssam int first = 1; 40812687Ssam 40912687Ssam getstr(rusername, sizeof (rusername), "remuser"); 41012687Ssam getstr(lusername, sizeof (lusername), "locuser"); 41112687Ssam getstr(term+5, sizeof(term)-5, "Terminal type"); 41213470Ssam if (getuid()) { 41313470Ssam pwd = &nouser; 41412687Ssam goto bad; 41513470Ssam } 41612687Ssam pwd = getpwnam(lusername); 41713470Ssam if (pwd == NULL) { 41813470Ssam pwd = &nouser; 41912687Ssam goto bad; 42013470Ssam } 42112687Ssam hostf = pwd->pw_uid ? fopen("/etc/hosts.equiv", "r") : 0; 42212687Ssam again: 42312687Ssam if (hostf) { 42412687Ssam char ahost[32]; 42512687Ssam 42612687Ssam while (fgets(ahost, sizeof (ahost), hostf)) { 42712687Ssam char *user; 42812687Ssam 42912687Ssam if ((user = index(ahost, '\n')) != 0) 43012687Ssam *user++ = '\0'; 43112687Ssam if ((user = index(ahost, ' ')) != 0) 43212687Ssam *user++ = '\0'; 43312687Ssam if (!strcmp(host, ahost) && 43412687Ssam !strcmp(rusername, user ? user : lusername)) { 43512687Ssam fclose(hostf); 43612687Ssam return (1); 43712687Ssam } 43812687Ssam } 43912687Ssam fclose(hostf); 44012687Ssam } 44112687Ssam if (first == 1) { 44212687Ssam char *rhosts = ".rhosts"; 44312687Ssam struct stat sbuf; 44412687Ssam 44512687Ssam first = 0; 44612687Ssam if (chdir(pwd->pw_dir) < 0) 44712687Ssam goto again; 44812687Ssam if (lstat(rhosts, &sbuf) < 0) 44912687Ssam goto again; 45012687Ssam if ((sbuf.st_mode & S_IFMT) == S_IFLNK) { 45112687Ssam printf("login: .rhosts is a soft link.\r\n"); 45212687Ssam goto bad; 45312687Ssam } 45412687Ssam hostf = fopen(rhosts, "r"); 45512687Ssam fstat(fileno(hostf), &sbuf); 45612687Ssam if (sbuf.st_uid && sbuf.st_uid != pwd->pw_uid) { 45712687Ssam printf("login: Bad .rhosts ownership.\r\n"); 45812687Ssam fclose(hostf); 45912687Ssam goto bad; 46012687Ssam } 46112687Ssam goto again; 46212687Ssam } 46312687Ssam bad: 46412687Ssam return (-1); 46512687Ssam } 46612687Ssam 4676005Swnj getstr(buf, cnt, err) 4686005Swnj char *buf; 4696005Swnj int cnt; 4706005Swnj char *err; 4716005Swnj { 4726005Swnj char c; 4736005Swnj 4746005Swnj do { 4756005Swnj if (read(0, &c, 1) != 1) 4766005Swnj exit(1); 4776005Swnj if (--cnt < 0) { 4786005Swnj printf("%s too long\r\n", err); 4796005Swnj exit(1); 4806005Swnj } 4816005Swnj *buf++ = c; 4826005Swnj } while (c != 0); 4836005Swnj } 4846329Swnj 48512687Ssam char *speeds[] = 48612687Ssam { "0", "50", "75", "110", "134", "150", "200", "300", 48712687Ssam "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; 48812687Ssam #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0])) 48912687Ssam 49012687Ssam doremoteterm(term, tp) 49112687Ssam char *term; 49212687Ssam struct sgttyb *tp; 49312687Ssam { 49412687Ssam char *cp = index(term, '/'); 49512687Ssam register int i; 49612687Ssam 49712687Ssam if (cp) { 49812687Ssam *cp++ = 0; 49912687Ssam for (i = 0; i < NSPEEDS; i++) 50012687Ssam if (!strcmp(speeds[i], cp)) { 50112687Ssam tp->sg_ispeed = tp->sg_ospeed = i; 50212687Ssam break; 50312687Ssam } 50412687Ssam } 50512687Ssam tp->sg_flags = ECHO|CRMOD|ANYP|XTABS; 50612687Ssam } 507