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