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