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