opielogin.c revision 29964
122347Spst/* opielogin.c: The infamous /bin/login 222347Spst 329964Sache%%% portions-copyright-cmetz-96 429964SachePortions of this software are Copyright 1996-1997 by Craig Metz, All Rights 522347SpstReserved. The Inner Net License Version 2 applies to these portions of 622347Spstthe software. 722347SpstYou should have received a copy of the license with this software. If 822347Spstyou didn't get a copy, you may request one from <license@inner.net>. 922347Spst 1022347SpstPortions of this software are Copyright 1995 by Randall Atkinson and Dan 1122347SpstMcDonald, All Rights Reserved. All Rights under this copyright are assigned 1222347Spstto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and 1322347SpstLicense Agreement applies to this software. 1422347Spst 1522347Spst History: 1622347Spst 1729964Sache Modified by cmetz for OPIE 2.31. Use _PATH_NOLOGIN. Move Solaris 1829964Sache drain bamage kluge after rflag check; it breaks rlogin. 1929964Sache Use TCSAFLUSH instead of TCSANOW (except where it flushes 2029964Sache data we need). Sleep before kluging for Solaris. 2122347Spst Modified by cmetz for OPIE 2.3. Process login environment files. 2222347Spst Made logindevperm/fbtab handling more generic. Kluge around 2322347Spst Solaris drain bamage differently (maybe better?). Maybe 2422347Spst allow cleartext logins even when opiechallenge() fails. 2522347Spst Changed the conditions on when time.h and sys/time.h are 2622347Spst included. Send debug info to syslog. Use opielogin() instead 2722347Spst of dealing with utmp/setlogin() here. 2822347Spst Modified by cmetz for OPIE 2.22. Call setlogin(). Decreased default 2922347Spst timeout to two minutes. Use opiereadpass() flags to get 3022347Spst around Solaris drain bamage. 3122347Spst Modified by cmetz for OPIE 2.21. Took the sizeof() the wrong thing. 3222347Spst Modified by cmetz for OPIE 2.2. Changed prompts to ask for OTP 3322347Spst response where appropriate. Simple though small speed-up. 3422347Spst Don't allow cleartext if echo on. Don't try to clear 3522347Spst non-blocking I/O. Use opiereadpass(). Don't mess with 3622347Spst termios (as much, at least) -- that's opiereadpass()'s 3722347Spst job. Change opiereadpass() calls to add echo arg. Fixed 3822347Spst CONTROL macro. Don't modify argv (at least, unless 3922347Spst we have a reason to). Allow user in if ruserok() says 4022347Spst so. Removed useless strings (I don't think that 4122347Spst removing the ucb copyright one is a problem -- please 4222347Spst let me know if I'm wrong). Use FUNCTION declaration et 4322347Spst al. Moved definition of TRUE here. Ifdef around more 4422347Spst headers. Make everything static. Removed support for 4522347Spst omitting domain name if same domain -- it generally 4622347Spst didn't work and it would be a big portability problem. 4722347Spst Use opiereadpass() in getloginname() and then post- 4822347Spst process. Added code to grab hpux time zone from 4922347Spst /etc/src.sh. Renamed MAIL_DIR to PATH_MAIL. Removed 5022347Spst dupe catchexit and extraneous closelog. openlog() as 5122347Spst soon as possible because SunOS syslog is broken. 5222347Spst Don't print an extra blank line before a new Response 5322347Spst prompt. 5422347Spst Modified at NRL for OPIE 2.2. Changed strip_crlf to stripcrlf. 5522347Spst Do opiebackspace() on entries. 5622347Spst Modified at NRL for OPIE 2.1. Since we don't seem to use the 5722347Spst result of opiechallenge() anymore, discard it. Changed 5822347Spst BSD4_3 to HAVE_GETTTYNAM. Other symbol changes for 5922347Spst autoconf. Removed obselete usage comment. Removed 6022347Spst des_crypt.h. File renamed to opielogin.c. Added bletch 6122347Spst for setpriority. Added slash between MAIL_DIR and name. 6222347Spst Modified at NRL for OPIE 2.02. Flush stdio after printing login 6322347Spst prompt. Fixed Solaris shadow password problem introduced 6422347Spst in OPIE 2.01 (the shadow password structure is spwd, not 6522347Spst spasswd). 6622347Spst Modified at NRL for OPIE 2.01. Changed password lookup handling 6722347Spst to use a static structure to avoid problems with drain- 6822347Spst bamaged shadow password packages. Make sure to close 6922347Spst syslog by function to avoid problems with drain bamaged 7022347Spst syslog implementations. Log a few interesting errors. 7122347Spst Modified at NRL for OPIE 2.0. 7222347Spst Modified at Bellcore for the Bellcore S/Key Version 1 software 7322347Spst distribution. 7422347Spst Originally from BSD. 7522347Spst*/ 7622347Spst/* 7722347Spst * Portions of this software are 7822347Spst * Copyright (c) 1980,1987 Regents of the University of California. 7922347Spst * All rights reserved. The Berkeley software License Agreement 8022347Spst * specifies the terms and conditions for redistribution. 8122347Spst */ 8222347Spst 8322347Spst#include "opie_cfg.h" /* OPIE: defines symbols for filenames & pathnames */ 8422347Spst#if HAVE_SYS_PARAM_H 8522347Spst#include <sys/param.h> 8622347Spst#endif /* HAVE_SYS_PARAM_H */ 8722347Spst#include <sys/stat.h> 8822347Spst#include <sys/types.h> 8922347Spst 9022347Spst#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H 9122347Spst#include <sys/resource.h> 9222347Spst#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ 9322347Spst 9422347Spst#if TIME_WITH_SYS_TIME 9522347Spst# include <sys/time.h> 9622347Spst# include <time.h> 9722347Spst#else /* TIME_WITH_SYS_TIME */ 9822347Spst#if HAVE_SYS_TIME_H 9922347Spst#include <sys/time.h> 10022347Spst#else /* HAVE_SYS_TIME_H */ 10122347Spst#include <time.h> 10222347Spst#endif /* HAVE_SYS_TIME_H */ 10322347Spst#endif /* TIME_WITH_SYS_TIME */ 10422347Spst 10522347Spst#if HAVE_SYS_FILE_H 10622347Spst#include <sys/file.h> 10722347Spst#endif /* HAVE_SYS_FILE_H */ 10822347Spst#include <signal.h> 10922347Spst#if HAVE_PWD_H 11022347Spst#include <pwd.h> /* POSIX Password routines */ 11122347Spst#endif /* HAVE_PWD_H */ 11222347Spst#include <stdio.h> 11322347Spst#include <errno.h> 11422347Spst#if HAVE_UNISTD_H 11522347Spst#include <unistd.h> /* Basic POSIX macros and functions */ 11622347Spst#endif /* HAVE_UNISTD_H */ 11722347Spst#include <termios.h> /* POSIX terminal I/O */ 11822347Spst#if HAVE_STRING_H 11922347Spst#include <string.h> /* ANSI C string functions */ 12022347Spst#endif /* HAVE_STRING_H */ 12122347Spst#include <fcntl.h> /* File I/O functions */ 12222347Spst#include <syslog.h> 12322347Spst#include <grp.h> 12422347Spst#include <netdb.h> 12522347Spst#include <netinet/in.h> /* contains types needed for next include file */ 12622347Spst#include <arpa/inet.h> /* Inet addr<-->ascii functions */ 12722347Spst#if HAVE_STDLIB_H 12822347Spst#include <stdlib.h> 12922347Spst#endif /* HAVE_STDLIB_H */ 13029964Sache#if HAVE_SYS_SELECT_H 13129964Sache#include <sys/select.h> 13229964Sache#endif /* HAVE_SYS_SELECT_H */ 13322347Spst 13422347Spst#ifdef QUOTA 13522347Spst#include <sys/quota.h> 13622347Spst#endif 13722347Spst 13822347Spst#if HAVE_GETTTYNAM 13922347Spst#include <sys/ioctl.h> /* non-portable routines used only a few places */ 14022347Spst#include <ttyent.h> 14122347Spst#endif /* HAVE_GETTTYNAM */ 14222347Spst 14322347Spst#include "opie.h" 14422347Spst 14522347Spst#define TTYGID(gid) tty_gid(gid) /* gid that owns all ttys */ 14622347Spst 14722347Spst#define NMAX 32 14822347Spst#define HMAX 256 14922347Spst 15022347Spst#if HAVE_LASTLOG_H 15122347Spst#include <lastlog.h> 15222347Spst#endif /* HAVE_LASTLOG_H */ 15322347Spst 15422347Spststatic int rflag = 0; 15522347Spststatic int usererr = -1; 15622347Spststatic int stopmotd; 15722347Spststatic char rusername[NMAX + 1]; 15822347Spststatic char name[NMAX + 1] = ""; 15922347Spststatic char minusnam[16] = "-"; 16022347Spststatic char *envinit[1]; /* now set by setenv calls */ 16122347Spststatic char term[64] = "\0"; /* important to initialise to a NULL string */ 16222347Spststatic char host[HMAX + 1] = "\0"; 16322347Spststatic struct passwd nouser; 16422347Spststatic struct passwd thisuser; 16522347Spst 16622347Spst#if HAVE_SHADOW_H 16722347Spst#include <shadow.h> 16822347Spst#endif /* HAVE_SHADOW_H */ 16922347Spst 17022347Spststatic char *ttyprompt; 17122347Spst 17222347Spst#ifdef PERMSFILE 17322347Spstextern char *home; 17422347Spst#endif /* PERMSFILE */ 17522347Spst 17622347Spststatic struct termios attr; 17722347Spst 17822347Spstextern int errno; 17922347Spst 18022347Spststatic int ouroptind; 18122347Spststatic char *ouroptarg; 18222347Spst 18322347Spst#if HAVE_LASTLOG_H 18422347Spst#ifndef _PATH_LASTLOG 18522347Spst#define _PATH_LASTLOG "/var/adm/lastlog" 18622347Spst#endif /* _PATH_LASTLOG */ 18722347Spst 18822347Spststatic char lastlog[] = _PATH_LASTLOG; 18922347Spst#endif /* HAVE_LASTLOG_H */ 19022347Spst 19122347Spst/* 19222347Spst * The "timeout" variable bounds the time given to login. 19322347Spst * We initialize it here for safety and so that it can be 19422347Spst * patched on machines where the default value is not appropriate. 19522347Spst */ 19622347Spststatic int timeout = 120; 19722347Spst 19822347Spststatic void getstr __P((char *, int, char *)); 19922347Spst 20022347Spst#if HAVE_CRYPT_H 20122347Spst#include <crypt.h> 20222347Spst#endif /* HAVE_CRYPT_H */ 20322347Spst 20422347Spst#undef TRUE 20522347Spst#define TRUE -1 20622347Spst 20722347Spst#ifdef TIOCSWINSZ 20822347Spst/* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is 20922347Spstavailable on BSDish systems and at least Solaris 2.x, but portability to 21022347Spstother systems is questionable. Use within this source code module is 21122347Spstprotected by suitable defines. 21222347Spst 21322347SpstI'd be interested in hearing about a more portable approach. rja */ 21422347Spst 21522347Spststatic struct winsize win = {0, 0, 0, 0}; 21622347Spst#endif 21722347Spst 21822347Spst 21922347Spst/*------------------ BEGIN REAL CODE --------------------------------*/ 22022347Spst 22122347Spst/* We allow the malloc()s to potentially leak data out because we can 22222347Spstonly call this routine about four times in the lifetime of this process 22322347Spstand the kernel will free all heap memory when we exit or exec. */ 22422347Spststatic int lookupuser FUNCTION_NOARGS 22522347Spst{ 22622347Spst struct passwd *pwd; 22722347Spst#if HAVE_SHADOW 22822347Spst struct spwd *spwd; 22922347Spst#endif /* HAVE_SHADOW */ 23022347Spst 23122347Spst memcpy(&thisuser, &nouser, sizeof(thisuser)); 23222347Spst 23322347Spst if (!(pwd = getpwnam(name))) 23422347Spst return -1; 23522347Spst 23622347Spst thisuser.pw_uid = pwd->pw_uid; 23722347Spst thisuser.pw_gid = pwd->pw_gid; 23822347Spst 23922347Spst if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1))) 24022347Spst goto lookupuserbad; 24122347Spst strcpy(thisuser.pw_name, pwd->pw_name); 24222347Spst 24322347Spst if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1))) 24422347Spst goto lookupuserbad; 24522347Spst strcpy(thisuser.pw_dir, pwd->pw_dir); 24622347Spst 24722347Spst if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1))) 24822347Spst goto lookupuserbad; 24922347Spst strcpy(thisuser.pw_shell, pwd->pw_shell); 25022347Spst 25122347Spst#if HAVE_SHADOW 25222347Spst if (!(spwd = getspnam(name))) 25322347Spst goto lookupuserbad; 25422347Spst 25522347Spst pwd->pw_passwd = spwd->sp_pwdp; 25622347Spst 25722347Spst endspent(); 25822347Spst#endif /* HAVE_SHADOW */ 25922347Spst 26022347Spst if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1))) 26122347Spst goto lookupuserbad; 26222347Spst strcpy(thisuser.pw_passwd, pwd->pw_passwd); 26322347Spst 26422347Spst endpwent(); 26522347Spst 26622347Spst return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#')); 26722347Spst 26822347Spstlookupuserbad: 26922347Spst memcpy(&thisuser, &nouser, sizeof(thisuser)); 27022347Spst return -1; 27122347Spst} 27222347Spst 27322347Spststatic VOIDRET getloginname FUNCTION_NOARGS 27422347Spst{ 27522347Spst register char *namep; 27622347Spst char c, d; 27722347Spst int flags; 27822347Spst static int first = 1; 27922347Spst 28022347Spst memset(name, 0, sizeof(name)); 28122347Spst 28222347Spst d = 0; 28322347Spst while (name[0] == '\0') { 28422347Spst flags = 1; 28522347Spst if (ttyprompt) { 28622347Spst if (first) { 28722347Spst flags = 4; 28822347Spst first--; 28922347Spst } else 29022347Spst printf(ttyprompt); 29122347Spst } else 29222347Spst printf("login: "); 29322347Spst fflush(stdout); 29422347Spst if (++d == 3) 29522347Spst exit(0); 29622347Spst if (!opiereadpass(name, sizeof(name)-1, flags)) { 29722347Spst syslog(LOG_CRIT, "End-of-file (or other error?) on stdin!"); 29822347Spst exit(0); 29922347Spst } 30022347Spst for (namep = name; *namep; namep++) { 30122347Spst if (c == ' ') 30222347Spst c = '_'; 30322347Spst } 30422347Spst } 30522347Spst} 30622347Spst 30722347Spststatic VOIDRET timedout FUNCTION((i), int i) 30822347Spst{ 30922347Spst /* input variable declared just to keep the compiler quiet */ 31022347Spst printf("Login timed out after %d seconds\n", timeout); 31122347Spst syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout); 31222347Spst exit(0); 31322347Spst} 31422347Spst 31522347Spst#if !HAVE_MOTD_IN_PROFILE 31622347Spststatic VOIDRET catch FUNCTION((i), int i) 31722347Spst{ 31822347Spst /* the input variable is declared to keep the compiler quiet */ 31922347Spst signal(SIGINT, SIG_IGN); 32022347Spst stopmotd++; 32122347Spst} 32222347Spst#endif /* !HAVE_MOTD_IN_PROFILE */ 32322347Spst 32422347Spststatic VOIDRET catchexit FUNCTION_NOARGS 32522347Spst{ 32622347Spst int i; 32729964Sache tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr); 32822347Spst putchar('\n'); 32922347Spst closelog(); 33022347Spst for (i = sysconf(_SC_OPEN_MAX); i > 2; i--) 33122347Spst close(i); 33222347Spst} 33322347Spst 33422347Spststatic int rootterm FUNCTION((ttyn), char *ttyn) 33522347Spst{ 33622347Spst#if HAVE_GETTTYNAM 33722347Spst/* The getttynam() call and the ttyent structure first appeared in 4.3 BSD and 33822347Spstare not portable to System V systems such as Solaris 2.x. or modern versions 33922347Spstof IRIX rja */ 34022347Spst register struct ttyent *t; 34122347Spst char *tty; 34222347Spst 34322347Spst tty = strrchr(ttyn, '/'); 34422347Spst 34522347Spst if (tty == NULL) 34622347Spst tty = ttyn; 34722347Spst else 34822347Spst tty++; 34922347Spst 35022347Spst if ((t = getttynam(tty)) != NULL) 35122347Spst return (t->ty_status & TTY_SECURE); 35222347Spst 35322347Spst return (1); /* when in doubt, allow root logins */ 35422347Spst 35522347Spst#elif HAVE_ETC_DEFAULT_LOGIN 35622347Spst 35722347Spst FILE *filno; 35822347Spst char line[128]; 35922347Spst char *next, *next2; 36022347Spst 36122347Spst/* SVR4 only permits two security modes for root logins: 1) only from CONSOLE, 36222347Spstif the string "CONSOLE=/dev/console" exists and is not commented out with "#" 36322347Spstcharacters, or 2) from anywhere. 36422347Spst 36522347SpstSo we open /etc/default/login file grab the file contents one line at a time 36622347Spstverify that the line being tested isn't commented out check for the substring 36722347Spst"CONSOLE" and decide whether to permit this attempted root login/su. */ 36822347Spst 36922347Spst if ((filno = fopen("/etc/default/login", "r")) != NULL) { 37022347Spst while (fgets(line, 128, filno) != NULL) { 37122347Spst next = line; 37222347Spst 37322347Spst if ((line[0] != '#') && (next = strstr(line, "CONSOLE"))) { 37422347Spst next += 7; /* get past the string "CONSOLE" */ 37522347Spst 37622347Spst while (*next && (*next == ' ') || (*next == '\t')) 37722347Spst next++; 37822347Spst 37922347Spst if (*(next++) != '=') 38022347Spst break; /* some weird character, get next line */ 38122347Spst 38222347Spst next2 = next; 38322347Spst while (*next2 && (*next2 != '\t') && (*next2 != ' ') && 38422347Spst (*next2 != '\n')) 38522347Spst next2++; 38622347Spst *next2 = 0; 38722347Spst 38822347Spst return !strcmp(ttyn, next); /* Allow the login if and only if the 38922347Spst user's terminal line matches the 39022347Spst setting for CONSOLE */ 39122347Spst } 39222347Spst } /* end while another line could be obtained */ 39322347Spst } /* end if could open file */ 39422347Spst return (1); /* when no CONSOLE line exists, root can login from anywhere */ 39522347Spst#elif HAVE_SECURETTY 39622347Spst { 39722347Spst FILE *f; 39822347Spst char buffer[1024], *c; 39922347Spst int rc = 0; 40022347Spst 40122347Spst if (!(f = fopen("/etc/securetty", "r"))) 40222347Spst return 1; 40322347Spst 40422347Spst if (c = strstr(ttyn, "/dev/")) 40522347Spst ttyn += 5; 40622347Spst 40722347Spst if (c = strrchr(ttyn, '/')) 40822347Spst ttyn = ++c; 40922347Spst 41022347Spst while (fgets(buffer, sizeof(buffer), f)) { 41122347Spst if (c = strrchr(buffer, '\n')) 41222347Spst *c = 0; 41322347Spst 41422347Spst if (!(c = strrchr(buffer, '/'))) 41522347Spst c = buffer; 41622347Spst else 41722347Spst c++; 41822347Spst 41922347Spst if (!strcmp(c, ttyn)) 42022347Spst rc = 1; 42122347Spst }; 42222347Spst 42322347Spst fclose(f); 42422347Spst return rc; 42522347Spst } 42622347Spst#else 42722347Spst return (1); /* when in doubt, allow root logins */ 42822347Spst#endif 42922347Spst} 43022347Spst 43122347Spststatic int doremotelogin FUNCTION((host), char *host) 43222347Spst{ 43322347Spst int rc; 43422347Spst 43522347Spst getstr(rusername, sizeof(rusername), "remuser"); 43622347Spst getstr(name, sizeof(name), "locuser"); 43722347Spst getstr(term, sizeof(term), "Terminal type"); 43822347Spst if (getuid()) { 43922347Spst memcpy(&thisuser, &nouser, sizeof(thisuser)); 44022347Spst syslog(LOG_ERR, "getuid() failed"); 44122347Spst return (-1); 44222347Spst } 44322347Spst if (lookupuser()) { 44422347Spst syslog(LOG_ERR, "lookup failed for user %s", name); 44522347Spst return (-1); 44622347Spst } 44722347Spst rc = ruserok(host, !thisuser.pw_uid, rusername, name); 44822347Spst if (rc == -1) { 44922347Spst syslog(LOG_ERR, 45022347Spst "ruserok failed, host=%s, uid=%d, remote username=%s, local username=%s", 45122347Spst host, thisuser.pw_uid, rusername, name); 45222347Spst } 45322347Spst return rc; 45422347Spst} 45522347Spst 45622347Spst 45722347Spststatic VOIDRET getstr FUNCTION((buf, cnt, err), char *buf AND int cnt AND char *err) 45822347Spst{ 45922347Spst char c; 46022347Spst 46122347Spst do { 46222347Spst if (read(0, &c, 1) != 1) 46322347Spst exit(1); 46422347Spst if (--cnt < 0) { 46522347Spst printf("%s too long\r\n", err); 46622347Spst syslog(LOG_CRIT, "%s too long", err); 46722347Spst exit(1); 46822347Spst } 46922347Spst *buf++ = c; 47022347Spst } 47122347Spst while ((c != 0) && (c != '~')); 47222347Spst} 47322347Spst 47422347Spststruct speed_xlat { 47522347Spst char *c; 47622347Spst int i; 47722347Spst} speeds[] = { 47822347Spst 47922347Spst#ifdef B0 48022347Spst { 48122347Spst "0", B0 48222347Spst }, 48322347Spst#endif /* B0 */ 48422347Spst#ifdef B50 48522347Spst { 48622347Spst "50", B50 48722347Spst }, 48822347Spst#endif /* B50 */ 48922347Spst#ifdef B75 49022347Spst { 49122347Spst "75", B75 49222347Spst }, 49322347Spst#endif /* B75 */ 49422347Spst#ifdef B110 49522347Spst { 49622347Spst "110", B110 49722347Spst }, 49822347Spst#endif /* B110 */ 49922347Spst#ifdef B134 50022347Spst { 50122347Spst "134", B134 50222347Spst }, 50322347Spst#endif /* B134 */ 50422347Spst#ifdef B150 50522347Spst { 50622347Spst "150", B150 50722347Spst }, 50822347Spst#endif /* B150 */ 50922347Spst#ifdef B200 51022347Spst { 51122347Spst "200", B200 51222347Spst }, 51322347Spst#endif /* B200 */ 51422347Spst#ifdef B300 51522347Spst { 51622347Spst "300", B300 51722347Spst }, 51822347Spst#endif /* B300 */ 51922347Spst#ifdef B600 52022347Spst { 52122347Spst "600", B600 52222347Spst }, 52322347Spst#endif /* B600 */ 52422347Spst#ifdef B1200 52522347Spst { 52622347Spst "1200", B1200 52722347Spst }, 52822347Spst#endif /* B1200 */ 52922347Spst#ifdef B1800 53022347Spst { 53122347Spst "1800", B1800 53222347Spst }, 53322347Spst#endif /* B1800 */ 53422347Spst#ifdef B2400 53522347Spst { 53622347Spst "2400", B2400 53722347Spst }, 53822347Spst#endif /* B2400 */ 53922347Spst#ifdef B4800 54022347Spst { 54122347Spst "4800", B4800 54222347Spst }, 54322347Spst#endif /* B4800 */ 54422347Spst#ifdef B7200 54522347Spst { 54622347Spst "7200", B7200 54722347Spst }, 54822347Spst#endif /* B7200 */ 54922347Spst#ifdef B9600 55022347Spst { 55122347Spst "9600", B9600 55222347Spst }, 55322347Spst#endif /* B9600 */ 55422347Spst#ifdef B14400 55522347Spst { 55622347Spst "14400", B14400 55722347Spst }, 55822347Spst#endif /* B14400 */ 55922347Spst#ifdef B19200 56022347Spst { 56122347Spst "19200", B19200 56222347Spst }, 56322347Spst#endif /* B19200 */ 56422347Spst#ifdef B28800 56522347Spst { 56622347Spst "28800", B28800 56722347Spst }, 56822347Spst#endif /* B28800 */ 56922347Spst#ifdef B38400 57022347Spst { 57122347Spst "38400", B38400 57222347Spst }, 57322347Spst#endif /* B38400 */ 57422347Spst#ifdef B57600 57522347Spst { 57622347Spst "57600", B57600 57722347Spst }, 57822347Spst#endif /* B57600 */ 57922347Spst#ifdef B115200 58022347Spst { 58122347Spst "115200", B115200 58222347Spst }, 58322347Spst#endif /* B115200 */ 58422347Spst#ifdef B230400 58522347Spst { 58622347Spst "230400", B230400 58722347Spst }, 58822347Spst#endif /* 230400 */ 58922347Spst { 59022347Spst NULL, 0 59122347Spst } 59222347Spst}; 59322347Spst 59422347Spststatic VOIDRET doremoteterm FUNCTION((term), char *term) 59522347Spst{ 59622347Spst register char *cp = strchr(term, '/'); 59722347Spst char *speed; 59822347Spst struct speed_xlat *x; 59922347Spst 60022347Spst if (cp) { 60122347Spst *cp++ = '\0'; 60222347Spst speed = cp; 60322347Spst cp = strchr(speed, '/'); 60422347Spst if (cp) 60522347Spst *cp++ = '\0'; 60622347Spst for (x = speeds; x->c != NULL; x++) 60722347Spst if (strcmp(x->c, speed) == 0) { 60822347Spst cfsetispeed(&attr, x->i); 60922347Spst cfsetospeed(&attr, x->i); 61022347Spst break; 61122347Spst } 61222347Spst } 61322347Spst} 61422347Spst 61522347Spststatic int tty_gid FUNCTION((default_gid), int default_gid) 61622347Spst{ 61722347Spst struct group *gr; 61822347Spst int gid = default_gid; 61922347Spst 62022347Spst gr = getgrnam(TTYGRPNAME); 62122347Spst if (gr != (struct group *) 0) 62222347Spst gid = gr->gr_gid; 62322347Spst endgrent(); 62422347Spst return (gid); 62522347Spst} 62622347Spst 62722347Spstint main FUNCTION((argc, argv), int argc AND char *argv[]) 62822347Spst{ 62922347Spst extern char **environ; 63022347Spst register char *namep; 63122347Spst struct opie opie; 63222347Spst 63322347Spst int invalid, quietlog; 63422347Spst FILE *nlfd; 63522347Spst char *tty, host[256]; 63622347Spst int pflag = 0, hflag = 0, fflag = 0; 63722347Spst int t, c; 63822347Spst int i; 63922347Spst char *p; 64022347Spst char opieprompt[OPIE_CHALLENGE_MAX + 1]; 64122347Spst int pwok, otpok, af_pwok; 64222347Spst char *pp; 64322347Spst char buf[256]; 64422347Spst int uid; 64522347Spst int opiepassed; 64622347Spst 64722347Spst#ifndef DEBUG 64822347Spst if (geteuid()) { 64922347Spst fprintf(stderr, "This program requires super-user priveleges.\n"); 65022347Spst exit(1); 65122347Spst } 65222347Spst#endif /* DEBUG */ 65322347Spst 65429964Sache for (t = sysconf(_SC_OPEN_MAX); t > 2; t--) 65529964Sache close(t); 65629964Sache 65722347Spst openlog("login", LOG_ODELAY, LOG_AUTH); 65822347Spst 65922347Spst /* initialisation */ 66022347Spst host[0] = '\0'; 66122347Spst opieprompt[0] = '\0'; 66222347Spst 66322347Spst if (p = getenv("TERM")) { 66422347Spst#ifdef DEBUG 66522347Spst syslog(LOG_DEBUG, "environment TERM=%s", p); 66622347Spst#endif /* DEBUG */ 66722347Spst strncpy(term, p, sizeof(term)); 66822347Spst }; 66922347Spst 67022347Spst memset(&nouser, 0, sizeof(nouser)); 67122347Spst nouser.pw_uid = -1; 67222347Spst nouser.pw_gid = -1; 67322347Spst nouser.pw_passwd = "#nope"; 67422347Spst nouser.pw_name = nouser.pw_gecos = nouser.pw_dir = nouser.pw_shell = ""; 67522347Spst 67622347Spst#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H 67722347Spst setpriority(PRIO_PROCESS, 0, 0); 67822347Spst#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */ 67922347Spst 68022347Spst signal(SIGALRM, timedout); 68122347Spst alarm(timeout); 68222347Spst signal(SIGQUIT, SIG_IGN); 68322347Spst signal(SIGINT, SIG_IGN); 68422347Spst 68522347Spst#if DOTTYPROMPT 68622347Spst ttyprompt = (char *) getenv("TTYPROMPT"); 68722347Spst#endif /* TTYPROMPT */ 68822347Spst 68922347Spst#ifdef QUOTA 69022347Spst quota(Q_SETUID, 0, 0, 0); 69122347Spst#endif 69222347Spst 69322347Spst#ifdef DEBUG 69422347Spst { 69522347Spst int foo; 69622347Spst 69722347Spst syslog(LOG_DEBUG, "my args are: (argc=%d)", foo = argc); 69822347Spst while (--foo) 69922347Spst syslog(LOG_DEBUG, "%d: %s", foo, argv[foo]); 70022347Spst } 70122347Spst#endif /* DEBUG */ 70222347Spst 70322347Spst/* Some OSs pass environment variables on the command line. All of them except 70422347Spst for TERM get eaten. */ 70522347Spst 70622347Spst i = argc; 70722347Spst while (--i) 70822347Spst if (strchr(argv[i], '=')) { 70922347Spst#ifdef DEBUG 71022347Spst syslog(LOG_DEBUG, "eating %s", argv[i]); 71122347Spst#endif /* DEBUG */ 71222347Spst argc--; 71322347Spst if (!strncmp(argv[i], "TERM=", 5)) { 71422347Spst strncpy(term, &(argv[i][5]), sizeof(term)); 71522347Spst term[sizeof(term) - 1] = 0; 71622347Spst#ifdef DEBUG 71722347Spst syslog(LOG_DEBUG, "passed TERM=%s, ouroptind = %d", term, i); 71822347Spst#endif /* DEBUG */ 71922347Spst } 72022347Spst } 72122347Spst/* Implement our own getopt()-like functionality, but do so in a much more 72222347Spst strict manner to prevent security problems. */ 72322347Spst for (ouroptind = 1; ouroptind < argc; ouroptind++) { 72422347Spst i = 0; 72522347Spst if (argv[ouroptind]) 72622347Spst if (argv[ouroptind][0] == '-') 72722347Spst if (i = argv[ouroptind][1]) 72822347Spst if (!argv[ouroptind][2]) 72922347Spst switch (i) { 73022347Spst case 'd': 73122347Spst if (++ouroptind == argc) 73222347Spst exit(1); 73322347Spst/* The '-d' option is apparently a performance hack to get around 73422347Spst ttyname() being slow. The potential does exist for it to be used 73522347Spst for malice, and it does not seem to be strictly necessary, so we 73622347Spst will just eat it. */ 73722347Spst break; 73822347Spst 73922347Spst case 'r': 74022347Spst if (rflag || hflag || fflag) { 74122347Spst printf("Other options not allowed with -r\n"); 74222347Spst exit(1); 74322347Spst } 74422347Spst if (++ouroptind == argc) 74522347Spst exit(1); 74622347Spst 74722347Spst ouroptarg = argv[ouroptind]; 74822347Spst 74922347Spst if (!ouroptarg) 75022347Spst exit(1); 75122347Spst 75222347Spst rflag = -1; 75322347Spst if (!doremotelogin(ouroptarg)) 75422347Spst rflag = 1; 75522347Spst 75622347Spst strncpy(host, ouroptarg, sizeof(host)); 75722347Spst break; 75822347Spst 75922347Spst case 'h': 76022347Spst if (!getuid()) { 76122347Spst if (rflag || hflag || fflag) { 76222347Spst printf("Other options not allowed with -h\n"); 76322347Spst exit(1); 76422347Spst } 76522347Spst hflag = 1; 76622347Spst 76722347Spst if (++ouroptind == argc) 76822347Spst exit(1); 76922347Spst 77022347Spst ouroptarg = argv[ouroptind]; 77122347Spst 77222347Spst if (!ouroptarg) 77322347Spst exit(1); 77422347Spst 77522347Spst strncpy(host, ouroptarg, sizeof(host)); 77622347Spst } 77722347Spst break; 77822347Spst 77922347Spst case 'f': 78022347Spst if (rflag) { 78122347Spst printf("Only one of -r and -f allowed\n"); 78222347Spst exit(1); 78322347Spst } 78422347Spst fflag = 1; 78522347Spst 78622347Spst if (++ouroptind == argc) 78722347Spst exit(1); 78822347Spst 78922347Spst ouroptarg = argv[ouroptind]; 79022347Spst 79122347Spst if (!ouroptarg) 79222347Spst exit(1); 79322347Spst 79422347Spst strncpy(name, ouroptarg, sizeof(name)); 79522347Spst break; 79622347Spst 79722347Spst case 'p': 79822347Spst pflag = 1; 79922347Spst break; 80022347Spst } else 80122347Spst i = 0; 80222347Spst if (!i) { 80322347Spst ouroptarg = argv[ouroptind++]; 80422347Spst strncpy(name, ouroptarg, sizeof(name)); 80522347Spst break; 80622347Spst } 80722347Spst } 80822347Spst 80922347Spst#ifdef TIOCNXCL 81022347Spst /* BSDism: not sure how to rewrite for POSIX. rja */ 81122347Spst ioctl(0, TIOCNXCL, 0); /* set non-exclusive use of tty */ 81222347Spst#endif 81322347Spst 81422347Spst /* get original termio attributes */ 81522347Spst if (tcgetattr(STDIN_FILENO, &attr) != 0) 81622347Spst return (-1); 81722347Spst 81822347Spst/* If talking to an rlogin process, propagate the terminal type and baud rate 81922347Spst across the network. */ 82022347Spst if (rflag) 82122347Spst doremoteterm(term); 82229964Sache else { 82329964Sache struct termios termios; 82429964Sache fd_set fds; 82529964Sache struct timeval timeval; 82629964Sache 82729964Sache memset(&timeval, 0, sizeof(struct timeval)); 82829964Sache 82929964Sache FD_ZERO(&fds); 83029964Sache FD_SET(0, &fds); 83122347Spst 83229964Sache#if HAVE_USLEEP 83329964Sache usleep(1); 83429964Sache#endif /* HAVE_USLEEP */ 83529964Sache 83629964Sache if (select(1, &fds, NULL, NULL, &timeval)) { 83729964Sache#ifdef DEBUG 83829964Sache syslog(LOG_DEBUG, "reading user name from tty buffer"); 83929964Sache#endif /* DEBUG */ 84029964Sache 84129964Sache if (tcgetattr(0, &termios)) { 84229964Sache#ifdef DEBUG 84329964Sache syslog(LOG_DEBUG, "tcgetattr(0, &termios) failed"); 84429964Sache#endif /* DEBUG */ 84529964Sache exit(1); 84629964Sache } 84729964Sache 84829964Sache termios.c_lflag &= ~ECHO; 84929964Sache 85029964Sache if (tcsetattr(0, TCSANOW, &termios)) { 85129964Sache#ifdef DEBUG 85229964Sache syslog(LOG_DEBUG, "tcsetattr(0, &termios) failed"); 85329964Sache#endif /* DEBUG */ 85429964Sache exit(1); 85529964Sache } 85629964Sache 85729964Sache if ((i = read(0, name, sizeof(name)-1)) > 0) 85829964Sache name[i] = 0; 85929964Sache if ((p = strchr(name, '\r'))) 86029964Sache *p = 0; 86129964Sache if ((p = strchr(name, '\n'))) 86229964Sache *p = 0; 86329964Sache } 86429964Sache } 86529964Sache 86622347Spst/* Force termios portable control characters to the system default values as 86722347Spstspecified in termios.h. This should help the one-time password login feel the 86822347Spstsame as the vendor-supplied login. Common extensions are also set for 86922347Spstcompleteness, but these are set within appropriate defines for portability. */ 87022347Spst 87122347Spst#define CONTROL(x) (x - 64) 87222347Spst 87322347Spst#ifdef VEOF 87422347Spst#ifdef CEOF 87522347Spst attr.c_cc[VEOF] = CEOF; 87622347Spst#else /* CEOF */ 87722347Spst attr.c_cc[VEOF] = CONTROL('D'); 87822347Spst#endif /* CEOF */ 87922347Spst#endif /* VEOF */ 88022347Spst#ifdef VEOL 88122347Spst#ifdef CEOL 88222347Spst attr.c_cc[VEOL] = CEOL; 88322347Spst#else /* CEOL */ 88422347Spst attr.c_cc[VEOL] = CONTROL('J'); 88522347Spst#endif /* CEOL */ 88622347Spst#endif /* VEOL */ 88722347Spst#ifdef VERASE 88822347Spst#ifdef CERASE 88922347Spst attr.c_cc[VERASE] = CERASE; 89022347Spst#else /* CERASE */ 89122347Spst attr.c_cc[VERASE] = CONTROL('H'); 89222347Spst#endif /* CERASE */ 89322347Spst#endif /* VERASE */ 89422347Spst#ifdef VINTR 89522347Spst#ifdef CINTR 89622347Spst attr.c_cc[VINTR] = CINTR; 89722347Spst#else /* CINTR */ 89822347Spst attr.c_cc[VINTR] = CONTROL('C'); 89922347Spst#endif /* CINTR */ 90022347Spst#endif /* VINTR */ 90122347Spst#ifdef VKILL 90222347Spst#ifdef CKILL 90322347Spst attr.c_cc[VKILL] = CKILL; 90422347Spst#else /* CKILL */ 90522347Spst attr.c_cc[VKILL] = CONTROL('U'); 90622347Spst#endif /* CKILL */ 90722347Spst#endif /* VKILL */ 90822347Spst#ifdef VQUIT 90922347Spst#ifdef CQUIT 91022347Spst attr.c_cc[VQUIT] = CQUIT; 91122347Spst#else /* CQUIT */ 91222347Spst attr.c_cc[VQUIT] = CONTROL('\\'); 91322347Spst#endif /* CQUIT */ 91422347Spst#endif /* VQUIT */ 91522347Spst#ifdef VSUSP 91622347Spst#ifdef CSUSP 91722347Spst attr.c_cc[VSUSP] = CSUSP; 91822347Spst#else /* CSUSP */ 91922347Spst attr.c_cc[VSUSP] = CONTROL('Z'); 92022347Spst#endif /* CSUSP */ 92122347Spst#endif /* VSUSP */ 92222347Spst#ifdef VSTOP 92322347Spst#ifdef CSTOP 92422347Spst attr.c_cc[VSTOP] = CSTOP; 92522347Spst#else /* CSTOP */ 92622347Spst attr.c_cc[VSTOP] = CONTROL('S'); 92722347Spst#endif /* CSTOP */ 92822347Spst#endif /* VSTOP */ 92922347Spst#ifdef VSTART 93022347Spst#ifdef CSTART 93122347Spst attr.c_cc[VSTART] = CSTART; 93222347Spst#else /* CSTART */ 93322347Spst attr.c_cc[VSTART] = CONTROL('Q'); 93422347Spst#endif /* CSTART */ 93522347Spst#endif /* VSTART */ 93622347Spst#ifdef VDSUSP 93722347Spst#ifdef CDSUSP 93822347Spst attr.c_cc[VDSUSP] = CDSUSP; 93922347Spst#else /* CDSUSP */ 94022347Spst attr.c_cc[VDSUSP] = 0; 94122347Spst#endif /* CDSUSP */ 94222347Spst#endif /* VDSUSP */ 94322347Spst#ifdef VEOL2 94422347Spst#ifdef CEOL2 94522347Spst attr.c_cc[VEOL2] = CEOL2; 94622347Spst#else /* CEOL2 */ 94722347Spst attr.c_cc[VEOL2] = 0; 94822347Spst#endif /* CEOL2 */ 94922347Spst#endif /* VEOL2 */ 95022347Spst#ifdef VREPRINT 95122347Spst#ifdef CRPRNT 95222347Spst attr.c_cc[VREPRINT] = CRPRNT; 95322347Spst#else /* CRPRNT */ 95422347Spst attr.c_cc[VREPRINT] = 0; 95522347Spst#endif /* CRPRNT */ 95622347Spst#endif /* VREPRINT */ 95722347Spst#ifdef VWERASE 95822347Spst#ifdef CWERASE 95922347Spst attr.c_cc[VWERASE] = CWERASE; 96022347Spst#else /* CWERASE */ 96122347Spst attr.c_cc[VWERASE] = 0; 96222347Spst#endif /* CWERASE */ 96322347Spst#endif /* VWERASE */ 96422347Spst#ifdef VLNEXT 96522347Spst#ifdef CLNEXT 96622347Spst attr.c_cc[VLNEXT] = CLNEXT; 96722347Spst#else /* CLNEXT */ 96822347Spst attr.c_cc[VLNEXT] = 0; 96922347Spst#endif /* CLNEXT */ 97022347Spst#endif /* VLNEXT */ 97122347Spst 97222347Spst attr.c_lflag |= ICANON; /* enable canonical input processing */ 97322347Spst attr.c_lflag &= ~ISIG; /* disable INTR, QUIT,& SUSP signals */ 97422347Spst attr.c_lflag |= (ECHO | ECHOE); /* enable echo and erase */ 97522347Spst#ifdef ONLCR 97622347Spst /* POSIX does not specify any output processing flags, but the usage below 97722347Spst is SVID compliant and is generally portable to modern versions of UNIX. */ 97822347Spst attr.c_oflag |= ONLCR; /* map CR to CRNL on output */ 97922347Spst#endif 98022347Spst#ifdef ICRNL 98122347Spst attr.c_iflag |= ICRNL; 98222347Spst#endif /* ICRNL */ 98322347Spst 98422347Spst attr.c_oflag |= OPOST; 98522347Spst attr.c_lflag |= ICANON; /* enable canonical input */ 98622347Spst attr.c_lflag |= ECHO; 98722347Spst attr.c_lflag |= ECHOE; /* enable ERASE character */ 98822347Spst attr.c_lflag |= ECHOK; /* enable KILL to delete line */ 98922347Spst attr.c_cflag |= HUPCL; /* hangup on close */ 99022347Spst 99122347Spst /* Set revised termio attributes */ 99229964Sache if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr)) 99322347Spst return (-1); 99422347Spst 99522347Spst atexit(catchexit); 99622347Spst 99722347Spst tty = ttyname(0); 99822347Spst 99922347Spst if (tty == (char *) 0 || *tty == '\0') 100022347Spst tty = "UNKNOWN"; /* was: "/dev/tty??" */ 100122347Spst 100222347Spst#if HAVE_SETVBUF && defined(_IONBF) 100322347Spst#if SETVBUF_REVERSED 100422347Spst setvbuf(stdout, _IONBF, NULL, 0); 100522347Spst setvbuf(stderr, _IONBF, NULL, 0); 100622347Spst#else /* SETVBUF_REVERSED */ 100722347Spst setvbuf(stdout, NULL, _IONBF, 0); 100822347Spst setvbuf(stderr, NULL, _IONBF, 0); 100922347Spst#endif /* SETVBUF_REVERSED */ 101022347Spst#endif /* HAVE_SETVBUF && defined(_IONBF) */ 101122347Spst 101222347Spst#ifdef DEBUG 101322347Spst syslog(LOG_DEBUG, "tty = %s", tty); 101422347Spst#endif /* DEBUG */ 101522347Spst 101622347Spst#ifdef HAVE_LOGIN_ENVFILE 101722347Spst { 101822347Spst FILE *f; 101922347Spst 102022347Spst if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) { 102122347Spst char line[128], *c, *c2; 102222347Spst 102322347Spst while(fgets(line, sizeof(line)-1, f)) { 102422347Spst c = line; 102522347Spst while(*c && (isalnum(*c) || (*c == '_'))) c++; 102622347Spst if (*c == '=') { 102722347Spst *(c++) = 0; 102822347Spst if (c2 = strchr(c, ';')) 102922347Spst *c2 = 0; 103022347Spst if (c2 = strchr(c, '\n')) 103122347Spst *c2 = 0; 103222347Spst if (c2 = strchr(c, ' ')) 103322347Spst continue; 103422347Spst if (c2 = strchr(c, '\t')) 103522347Spst continue; 103622347Spst if (!strcmp(line, "TZ")) 103722347Spst continue; 103822347Spst if (setenv(line, c, 1) < 0) { 103922347Spst fprintf(stderr, "setenv() failed -- environment full?\n"); 104022347Spst break; 104122347Spst } 104222347Spst } 104322347Spst } 104422347Spst fclose(f); 104522347Spst } 104622347Spst } 104722347Spst#endif /* HAVE_LOGIN_ENVFILE */ 104822347Spst 104922347Spst t = 0; 105022347Spst invalid = TRUE; 105122347Spst af_pwok = opieaccessfile(host); 105222347Spst 105322347Spst if (name[0]) 105422347Spst if (name[0] == '-') { 105522347Spst fprintf(stderr, "User names can't start with '-'.\n"); 105622347Spst syslog(LOG_AUTH, "Attempt to use invalid username: %s.", name); 105722347Spst exit(1); 105822347Spst } else 105922347Spst invalid = lookupuser(); 106022347Spst 106122347Spst do { 106222347Spst /* If remote login take given name, otherwise prompt user for something. */ 106322347Spst if (invalid && !name[0]) { 106422347Spst getloginname(); 106522347Spst invalid = lookupuser(); 106622347Spst } 106722347Spst#ifdef DEBUG 106822347Spst syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d", name, strlen(name), name[0]); 106922347Spst#endif /* DEBUG */ 107022347Spst 107122347Spst if (fflag) { 107222347Spst uid = getuid(); 107322347Spst 107422347Spst if (uid != 0 && uid != thisuser.pw_uid) 107522347Spst fflag = 0; 107622347Spst /* Disallow automatic login for root. */ 107722347Spst if (thisuser.pw_uid == 0) 107822347Spst fflag = 0; 107922347Spst } 108022347Spst if (feof(stdin)) 108122347Spst exit(0); 108222347Spst 108322347Spst /* If no remote login authentication and a password exists for this user, 108422347Spst prompt for and verify a password. */ 108522347Spst if (!fflag && (rflag < 1) && *thisuser.pw_passwd) { 108622347Spst#ifdef DEBUG 108722347Spst syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d\n", name, strlen(name), name[0]); 108822347Spst#endif /* DEBUG */ 108922347Spst 109022347Spst /* Attempt a one-time password challenge */ 109122347Spst i = opiechallenge(&opie, name, opieprompt); 109222347Spst 109322347Spst if ((i < 0) || (i > 1)) { 109422347Spst syslog(LOG_ERR, "error: opiechallenge() returned %d, errno=%d!\n", i, errno); 109522347Spst fprintf(stderr, "System error; can't issue challenge!\n"); 109622347Spst otpok = 0; 109722347Spst } else { 109822347Spst printf("%s\n", opieprompt); 109922347Spst otpok = 1; 110022347Spst } 110122347Spst 110222347Spst if (!memcmp(&thisuser, &nouser, sizeof(thisuser))) 110322347Spst if (host[0]) 110422347Spst syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s.", 110522347Spst name, tty, host); 110622347Spst else 110722347Spst syslog(LOG_WARNING, "Invalid login attempt for %s on %s.", 110822347Spst name, tty); 110922347Spst 111022347Spst pwok = af_pwok && opiealways(thisuser.pw_dir); 111122347Spst#if DEBUG 111222347Spst syslog(LOG_DEBUG, "af_pwok = %d, pwok = %d", af_pwok, pwok); 111322347Spst#endif /* DEBUG */ 111422347Spst 111522347Spst if (!pwok && !otpok) { 111629964Sache fprintf(stderr, "Can't authenticate %s!\n", name); 111729964Sache exit(1); 111822347Spst } 111922347Spst 112022347Spst#if NEW_PROMPTS 112122347Spst if (otpok) 112222347Spst printf("Response"); 112322347Spst if (otpok && pwok) 112422347Spst printf(" or "); 112522347Spst if (pwok) 112622347Spst printf("Password"); 112722347Spst printf(": "); 112822347Spst if (!opiereadpass(buf, sizeof(buf), !pwok)) 112922347Spst invalid = TRUE; 113022347Spst#else /* NEW_PROMPTS */ 113122347Spst if (!pwok) 113222347Spst printf("(OTP response required)\n"); 113322347Spst printf("Password:"); 113422347Spst fflush(stdout); 113522347Spst if (!opiereadpass(buf, sizeof(buf), 0)) 113622347Spst invalid = TRUE; 113722347Spst#endif /* NEW_PROMPTS */ 113822347Spst 113922347Spst if (!buf[0] && otpok) { 114022347Spst pwok = 0; 114122347Spst /* Null line entered, so display appropriate prompt & flush current 114222347Spst data. */ 114322347Spst#if NEW_PROMPTS 114422347Spst printf("Response: "); 114522347Spst#else /* NEW_PROMPTS */ 114622347Spst printf(" (echo on)\nPassword:"); 114722347Spst#endif /* NEW_PROMPTS */ 114822347Spst if (!opiereadpass(buf, sizeof(buf), 1)) 114922347Spst invalid = TRUE; 115022347Spst } 115122347Spst 115222347Spst if (otpok) { 115322347Spst i = opiegetsequence(&opie); 115422347Spst opiepassed = !opieverify(&opie, buf); 115522347Spst 115622347Spst#ifdef DEBUG 115722347Spst syslog(LOG_DEBUG, "opiepassed = %d", opiepassed); 115822347Spst#endif /* DEBUG */ 115922347Spst } 116022347Spst 116122347Spst if (!invalid) { 116222347Spst if (otpok && opiepassed) { 116322347Spst if (i < 10) { 116422347Spst printf("Warning: Re-initialize your OTP information"); 116522347Spst if (i < 5) 116622347Spst printf(" NOW!"); 116722347Spst printf("\n"); 116822347Spst } 116922347Spst } else { 117022347Spst if (pwok) { 117122347Spst pp = crypt(buf, thisuser.pw_passwd); 117222347Spst invalid = strcmp(pp, thisuser.pw_passwd); 117322347Spst } else 117422347Spst invalid = TRUE; 117522347Spst } 117622347Spst } 117722347Spst } 117822347Spst 117922347Spst /* If user not super-user, check for logins disabled. */ 118022347Spst if (thisuser.pw_uid) { 118129964Sache if (nlfd = fopen(_PATH_NOLOGIN, "r")) { 118222347Spst while ((c = getc(nlfd)) != EOF) 118322347Spst putchar(c); 118422347Spst fflush(stdout); 118522347Spst sleep(5); 118622347Spst exit(0); 118722347Spst } 118822347Spst } 118922347Spst /* If valid so far and root is logging in, see if root logins on this 119022347Spst terminal are permitted. */ 119122347Spst if (!invalid && !thisuser.pw_uid && !rootterm(tty)) { 119222347Spst if (host[0]) 119322347Spst syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s FROM %.*s", 119422347Spst tty, HMAX, host); 119522347Spst else 119622347Spst syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s", tty); 119722347Spst invalid = TRUE; 119822347Spst } 119922347Spst /* If invalid, then log failure attempt data to appropriate system 120022347Spst logfiles and close the connection. */ 120122347Spst if (invalid) { 120222347Spst printf("Login incorrect\n"); 120322347Spst if (host[0]) 120422347Spst syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s, %.*s", 120522347Spst tty, HMAX, host, sizeof(name), name); 120622347Spst else 120722347Spst syslog(LOG_ERR, "LOGIN FAILURE ON %s, %.*s", 120822347Spst tty, sizeof(name), name); 120922347Spst if (++t >= 5) 121022347Spst exit(1); 121122347Spst } 121222347Spst if (*thisuser.pw_shell == '\0') 121322347Spst thisuser.pw_shell = "/bin/sh"; 121422347Spst if ((chdir(thisuser.pw_dir) < 0) && !invalid) { 121522347Spst if (chdir("/") < 0) { 121622347Spst printf("No directory!\n"); 121722347Spst invalid = TRUE; 121822347Spst } else { 121922347Spst printf("No directory! %s\n", "Logging in with HOME=/"); 122022347Spst strcpy(thisuser.pw_dir, "/"); 122122347Spst } 122222347Spst } 122322347Spst /* Remote login invalid must have been because of a restriction of some 122422347Spst sort, no extra chances. */ 122522347Spst if (invalid) { 122622347Spst if (!usererr) 122722347Spst exit(1); 122822347Spst name[0] = 0; 122922347Spst } 123022347Spst } 123122347Spst while (invalid); 123222347Spst /* Committed to login -- turn off timeout */ 123322347Spst alarm(0); 123422347Spst 123522347Spst#ifdef QUOTA 123622347Spst if (quota(Q_SETUID, thisuser.pw_uid, 0, 0) < 0 && errno != EINVAL) { 123722347Spst if (errno == EUSERS) 123822347Spst printf("%s.\n%s.\n", "Too many users logged on already", 123922347Spst "Try again later"); 124022347Spst else 124122347Spst if (errno == EPROCLIM) 124222347Spst printf("You have too many processes running.\n"); 124322347Spst else 124422347Spst perror("quota (Q_SETUID)"); 124522347Spst sleep(5); 124622347Spst exit(0); 124722347Spst } 124822347Spst#endif 124922347Spst 125022347Spst if (opielogin(tty, name, host)) 125122347Spst syslog(LOG_ERR, "can't record login: tty %s, name %s, host %s", tty, name, host); 125222347Spst 125322347Spst quietlog = !access(QUIET_LOGIN_FILE, F_OK); 125422347Spst 125522347Spst#if HAVE_LASTLOG_H 125622347Spst { 125722347Spst int f; 125822347Spst 125922347Spst if ((f = open(lastlog, O_RDWR)) >= 0) { 126022347Spst struct lastlog ll; 126122347Spst 126222347Spst lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0); 126322347Spst 126422347Spst if ((sizeof(ll) == read(f, (char *) &ll, sizeof(ll))) && 126522347Spst (ll.ll_time != 0) && (!quietlog)) { 126622347Spst printf("Last login: %.*s ", 126722347Spst 24 - 5, (char *) ctime(&ll.ll_time)); 126822347Spst if (*ll.ll_host != '\0') 126922347Spst printf("from %.*s\n", sizeof(ll.ll_host), ll.ll_host); 127022347Spst else 127122347Spst printf("on %.*s\n", sizeof(ll.ll_line), ll.ll_line); 127222347Spst } 127322347Spst lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0); 127422347Spst 127522347Spst time(&ll.ll_time); 127622347Spst strncpy(ll.ll_line, tty, sizeof(ll.ll_line)); 127722347Spst strncpy(ll.ll_host, host, sizeof(ll.ll_host)); 127822347Spst write(f, (char *) &ll, sizeof ll); 127922347Spst close(f); 128022347Spst } 128122347Spst } 128222347Spst#endif /* HAVE_LASTLOG_H */ 128322347Spst 128422347Spst chown(tty, thisuser.pw_uid, TTYGID(thisuser.pw_gid)); 128522347Spst 128622347Spst#ifdef TIOCSWINSZ 128722347Spst/* POSIX does not specify any interface to set/get window sizes, so this is 128822347Spstnot portable. It should work on most recent BSDish systems and the defines 128922347Spstshould protect it on older System Vish systems. It does work under Solaris 129022347Spst2.4, though it isn't clear how many other SVR4 systems support it. I'd be 129122347Spstinterested in hearing of a more portable approach. rja */ 129222347Spst if (!hflag && !rflag) 129322347Spst ioctl(0, TIOCSWINSZ, &win); /* set window size to 0,0,0,0 */ 129422347Spst#endif 129522347Spst 129622347Spst chmod(tty, 0622); 129722347Spst setgid(thisuser.pw_gid); 129822347Spst initgroups(name, thisuser.pw_gid); 129922347Spst 130022347Spst#ifdef QUOTA 130122347Spst quota(Q_DOWARN, thisuser.pw_uid, (dev_t) - 1, 0); 130222347Spst#endif 130322347Spst 130422347Spst#ifdef PERMSFILE 130522347Spst home = thisuser.pw_dir; 130622347Spst permsfile(name, tty, thisuser.pw_uid, thisuser.pw_gid); 130722347Spst fflush(stderr); 130822347Spst#endif /* PERMSFILE */ 130922347Spst 131022347Spst setuid(thisuser.pw_uid); 131122347Spst 131222347Spst /* destroy environment unless user has asked to preserve it */ 131322347Spst if (!pflag) 131422347Spst environ = envinit; 131522347Spst setenv("HOME", thisuser.pw_dir, 1); 131622347Spst setenv("SHELL", thisuser.pw_shell, 1); 131722347Spst if (!term[0]) { 131822347Spst#if HAVE_GETTTYNAM 131922347Spst/* 132022347Spst * The getttynam() call and the ttyent structure first appeared in 4.3 BSD. 132122347Spst * They are not portable to System V systems such as Solaris 2.x. 132222347Spst * rja 132322347Spst */ 132422347Spst register struct ttyent *t; 132522347Spst register char *c; 132622347Spst 132722347Spst if (c = strrchr(tty, '/')) 132822347Spst c++; 132922347Spst else 133022347Spst c = tty; 133122347Spst 133222347Spst if (t = getttynam(c)) 133322347Spst strncpy(term, t->ty_type, sizeof(term)); 133422347Spst else 133522347Spst#endif /* HAVE_GETTTYNAM */ 133622347Spst strcpy(term, "unknown"); 133722347Spst } 133822347Spst 133922347Spst setenv("USER", name, 1); 134022347Spst setenv("LOGNAME", name, 1); 134122347Spst setenv("PATH", DEFAULT_PATH, 0); 134222347Spst if (term[0]) { 134322347Spst#ifdef DEBUG 134422347Spst syslog(LOG_DEBUG, "setting TERM=%s", term); 134522347Spst#endif /* DEBUG */ 134622347Spst setenv("TERM", term, 1); 134722347Spst } 134822347Spst 134922347Spst#ifdef HAVE_LOGIN_ENVFILE 135022347Spst { 135122347Spst FILE *f; 135222347Spst 135322347Spst if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) { 135422347Spst char line[128], *c, *c2; 135522347Spst 135622347Spst while(fgets(line, sizeof(line)-1, f)) { 135722347Spst c = line; 135822347Spst while(*c && (isalnum(*c) || (*c == '_'))) c++; 135922347Spst if (*c == '=') { 136022347Spst *(c++) = 0; 136122347Spst if (c2 = strchr(c, ';')) 136222347Spst *c2 = 0; 136322347Spst if (c2 = strchr(c, '\n')) 136422347Spst *c2 = 0; 136522347Spst if (c2 = strchr(c, ' ')) 136622347Spst continue; 136722347Spst if (c2 = strchr(c, '\t')) 136822347Spst continue; 136922347Spst if (setenv(line, c, 0) < 0) { 137022347Spst fprintf(stderr, "setenv() failed -- environment full?\n"); 137122347Spst break; 137222347Spst } 137322347Spst } 137422347Spst } 137522347Spst fclose(f); 137622347Spst } 137722347Spst } 137822347Spst#endif /* HAVE_LOGIN_ENVFILE */ 137922347Spst 138022347Spst if ((namep = strrchr(thisuser.pw_shell, '/')) == NULL) 138122347Spst namep = thisuser.pw_shell; 138222347Spst else 138322347Spst namep++; 138422347Spst strcat(minusnam, namep); 138522347Spst if (tty[sizeof("tty") - 1] == 'd') 138622347Spst syslog(LOG_INFO, "DIALUP %s, %s", tty, name); 138722347Spst if (!thisuser.pw_uid) 138822347Spst if (host[0]) 138922347Spst syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", tty, HMAX, host); 139022347Spst else 139122347Spst syslog(LOG_NOTICE, "ROOT LOGIN %s", tty); 139222347Spst#if !HAVE_MOTD_IN_PROFILE 139322347Spst if (!quietlog) { 139422347Spst FILE *mf; 139522347Spst register c; 139622347Spst 139722347Spst signal(SIGINT, catch); 139822347Spst if ((mf = fopen(MOTD_FILE, "r")) != NULL) { 139922347Spst while ((c = getc(mf)) != EOF && !stopmotd) 140022347Spst putchar(c); 140122347Spst fclose(mf); 140222347Spst } 140322347Spst signal(SIGINT, SIG_IGN); 140422347Spst } 140522347Spst#endif /* !HAVE_MOTD_IN_PROFILE */ 140622347Spst#if !HAVE_MAILCHECK_IN_PROFILE 140722347Spst if (!quietlog) { 140822347Spst struct stat st; 140922347Spst char buf[128]; 141022347Spst int len; 141122347Spst 141222347Spst strncpy(buf, PATH_MAIL, sizeof(buf) - 2); 141322347Spst buf[sizeof(buf) - 2] = 0; 141422347Spst 141522347Spst len = strlen(buf); 141622347Spst if (*(buf + len - 1) != '/') { 141722347Spst *(buf + len) = '/'; 141822347Spst *(buf + len + 1) = 0; 141922347Spst } 142022347Spst 142122347Spst strcat(buf, name); 142222347Spst#if DEBUG 142322347Spst syslog(LOG_DEBUG, "statting %s", buf); 142422347Spst#endif /* DEBUG */ 142522347Spst if (!stat(buf, &st) && st.st_size) 142622347Spst printf("You have %smail.\n", 142722347Spst (st.st_mtime > st.st_atime) ? "new " : ""); 142822347Spst } 142922347Spst#endif /* !HAVE_MAILCHECK_IN_PROFILE */ 143022347Spst signal(SIGALRM, SIG_DFL); 143122347Spst signal(SIGQUIT, SIG_DFL); 143222347Spst signal(SIGINT, SIG_DFL); 143322347Spst signal(SIGTSTP, SIG_IGN); 143422347Spst 143522347Spst attr.c_lflag |= (ISIG | IEXTEN); 143622347Spst 143722347Spst catchexit(); 143822347Spst execlp(thisuser.pw_shell, minusnam, 0); 143922347Spst perror(thisuser.pw_shell); 144022347Spst printf("No shell\n"); 144122347Spst exit(0); 144222347Spst} 1443