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