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