opielogin.c revision 225736
1204076Spjd/* opielogin.c: The infamous /bin/login
2204076Spjd
3211885Spjd%%% portions-copyright-cmetz-96
4204076SpjdPortions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5204076SpjdReserved. The Inner Net License Version 2 applies to these portions of
6204076Spjdthe software.
7204076SpjdYou should have received a copy of the license with this software. If
8204076Spjdyou didn't get a copy, you may request one from <license@inner.net>.
9204076Spjd
10204076SpjdPortions of this software are Copyright 1995 by Randall Atkinson and Dan
11204076SpjdMcDonald, All Rights Reserved. All Rights under this copyright are assigned
12204076Spjdto the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13204076SpjdLicense Agreement applies to this software.
14204076Spjd
15204076Spjd	History:
16204076Spjd
17204076Spjd	Modified by cmetz for OPIE 2.4. Omit "/dev/" in lastlog entry.
18204076Spjd		Don't chdir for invalid users. Fixed bug where getloginname()
19204076Spjd		didn't actually change spaces to underscores. Use struct
20204076Spjd		opie_key for key blocks. Do the home directory chdir() after
21204076Spjd		doing the setuid() in case we're on superuser-mapped NFS.
22204076Spjd		Initialize some variables explicitly. Call opieverify() if
23204076Spjd		login times out. Use opiestrncpy().
24204076Spjd	Modified by cmetz for OPIE 2.32. Partially handle environment
25204076Spjd		variables on the command line (a better implementation is
26204076Spjd		coming soon). Handle failure to issue a challenge more
27204076Spjd		gracefully.
28204076Spjd	Modified by cmetz for OPIE 2.31. Use _PATH_NOLOGIN. Move Solaris
29204076Spjd	        drain bamage kluge after rflag check; it breaks rlogin.
30204076Spjd		Use TCSAFLUSH instead of TCSANOW (except where it flushes
31204076Spjd		data we need). Sleep before kluging for Solaris.
32204076Spjd	Modified by cmetz for OPIE 2.3. Process login environment files.
33204076Spjd	        Made logindevperm/fbtab handling more generic. Kluge around
34204076Spjd                Solaris drain bamage differently (maybe better?). Maybe
35211885Spjd		allow cleartext logins even when opiechallenge() fails.
36204076Spjd		Changed the conditions on when time.h and sys/time.h are
37204076Spjd		included. Send debug info to syslog. Use opielogin() instead
38204076Spjd		of dealing with utmp/setlogin() here.
39211885Spjd	Modified by cmetz for OPIE 2.22. Call setlogin(). Decreased default
40204076Spjd	        timeout to two minutes. Use opiereadpass() flags to get
41211885Spjd		around Solaris drain bamage.
42211885Spjd	Modified by cmetz for OPIE 2.21. Took the sizeof() the wrong thing.
43211885Spjd        Modified by cmetz for OPIE 2.2. Changed prompts to ask for OTP
44211885Spjd                response where appropriate. Simple though small speed-up.
45211885Spjd                Don't allow cleartext if echo on. Don't try to clear
46204076Spjd                non-blocking I/O. Use opiereadpass(). Don't mess with
47204076Spjd                termios (as much, at least) -- that's opiereadpass()'s
48204076Spjd                job. Change opiereadpass() calls to add echo arg. Fixed
49204076Spjd                CONTROL macro. Don't modify argv (at least, unless
50211885Spjd                we have a reason to). Allow user in if ruserok() says
51204076Spjd                so. Removed useless strings (I don't think that
52204076Spjd                removing the ucb copyright one is a problem -- please
53204076Spjd                let me know if I'm wrong). Use FUNCTION declaration et
54204076Spjd                al. Moved definition of TRUE here. Ifdef around more
55211885Spjd                headers. Make everything static. Removed support for
56204076Spjd                omitting domain name if same domain -- it generally
57211885Spjd                didn't work and it would be a big portability problem.
58211885Spjd                Use opiereadpass() in getloginname() and then post-
59211885Spjd                process. Added code to grab hpux time zone from
60211885Spjd                /etc/src.sh. Renamed MAIL_DIR to PATH_MAIL. Removed
61211885Spjd                dupe catchexit and extraneous closelog. openlog() as
62211885Spjd                soon as possible because SunOS syslog is broken.
63211885Spjd                Don't print an extra blank line before a new Response
64211885Spjd                prompt.
65211885Spjd        Modified at NRL for OPIE 2.2. Changed strip_crlf to stripcrlf.
66211885Spjd                Do opiebackspace() on entries.
67211885Spjd	Modified at NRL for OPIE 2.1. Since we don't seem to use the
68211885Spjd	        result of opiechallenge() anymore, discard it. Changed
69211885Spjd		BSD4_3 to HAVE_GETTTYNAM. Other symbol changes for
70211885Spjd		autoconf. Removed obselete usage comment. Removed
71211885Spjd		des_crypt.h. File renamed to opielogin.c. Added bletch
72211885Spjd		for setpriority. Added slash between MAIL_DIR and name.
73211885Spjd        Modified at NRL for OPIE 2.02. Flush stdio after printing login
74211885Spjd                prompt. Fixed Solaris shadow password problem introduced
75211885Spjd                in OPIE 2.01 (the shadow password structure is spwd, not
76211885Spjd                spasswd).
77211885Spjd        Modified at NRL for OPIE 2.01. Changed password lookup handling
78211885Spjd                to use a static structure to avoid problems with drain-
79211885Spjd                bamaged shadow password packages. Make sure to close
80211885Spjd                syslog by function to avoid problems with drain bamaged
81211885Spjd                syslog implementations. Log a few interesting errors.
82211885Spjd	Modified at NRL for OPIE 2.0.
83211885Spjd	Modified at Bellcore for the Bellcore S/Key Version 1 software
84211885Spjd		distribution.
85211976Spjd	Originally from BSD.
86211976Spjd*/
87211976Spjd/*
88204076Spjd * Portions of this software are
89204076Spjd * Copyright (c) 1980,1987 Regents of the University of California.
90204076Spjd * All rights reserved.  The Berkeley software License Agreement
91204076Spjd * specifies the terms and conditions for redistribution.
92204076Spjd */
93204076Spjd
94204076Spjd#include "opie_cfg.h"	/* OPIE: defines symbols for filenames & pathnames */
95204076Spjd#if HAVE_SYS_PARAM_H
96204076Spjd#include <sys/param.h>
97204076Spjd#endif /* HAVE_SYS_PARAM_H */
98204076Spjd#include <sys/stat.h>
99204076Spjd#include <sys/types.h>
100204076Spjd
101204076Spjd#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
102211884Spjd#include <sys/resource.h>
103211884Spjd#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
104211884Spjd
105211884Spjd#if TIME_WITH_SYS_TIME
106211884Spjd# include <sys/time.h>
107211884Spjd# include <time.h>
108211884Spjd#else /* TIME_WITH_SYS_TIME */
109211884Spjd#if HAVE_SYS_TIME_H
110211884Spjd#include <sys/time.h>
111211884Spjd#else /* HAVE_SYS_TIME_H */
112211884Spjd#include <time.h>
113211884Spjd#endif /* HAVE_SYS_TIME_H */
114211884Spjd#endif /* TIME_WITH_SYS_TIME */
115211884Spjd
116211884Spjd#if HAVE_SYS_FILE_H
117204076Spjd#include <sys/file.h>
118204076Spjd#endif /* HAVE_SYS_FILE_H */
119204076Spjd#include <signal.h>
120204076Spjd#if HAVE_PWD_H
121204076Spjd#include <pwd.h>	/* POSIX Password routines */
122204076Spjd#endif /* HAVE_PWD_H */
123204076Spjd#include <stdio.h>
124204076Spjd#include <errno.h>
125204076Spjd#if HAVE_UNISTD_H
126204076Spjd#include <unistd.h>	/* Basic POSIX macros and functions */
127204076Spjd#endif /* HAVE_UNISTD_H */
128204076Spjd#include <termios.h>	/* POSIX terminal I/O */
129204076Spjd#if HAVE_STRING_H
130204076Spjd#include <string.h>	/* ANSI C string functions */
131204076Spjd#endif /* HAVE_STRING_H */
132204076Spjd#include <fcntl.h>	/* File I/O functions */
133204076Spjd#include <syslog.h>
134204076Spjd#include <grp.h>
135204076Spjd#include <netdb.h>
136204076Spjd#include <netinet/in.h>	/* contains types needed for next include file */
137204076Spjd#include <arpa/inet.h>	/* Inet addr<-->ascii functions */
138204076Spjd#if HAVE_STDLIB_H
139204076Spjd#include <stdlib.h>
140204076Spjd#endif /* HAVE_STDLIB_H */
141204076Spjd#if HAVE_SYS_SELECT_H
142204076Spjd#include <sys/select.h>
143204076Spjd#endif /* HAVE_SYS_SELECT_H */
144204076Spjd
145204076Spjd#ifdef	QUOTA
146204076Spjd#include <sys/quota.h>
147204076Spjd#endif
148204076Spjd
149211885Spjd#if HAVE_GETTTYNAM
150211885Spjd#include <sys/ioctl.h>	/* non-portable routines used only a few places */
151211885Spjd#include <ttyent.h>
152211885Spjd#endif /* HAVE_GETTTYNAM */
153211976Spjd
154211976Spjd#include "opie.h"
155211885Spjd
156211885Spjd#define TTYGID(gid)	tty_gid(gid)	/* gid that owns all ttys */
157211885Spjd
158211885Spjd#define NMAX	32
159211885Spjd#define HMAX	256
160211976Spjd
161211976Spjd#if HAVE_LASTLOG_H
162211976Spjd#include <lastlog.h>
163211976Spjd#endif /* HAVE_LASTLOG_H */
164211976Spjd
165211976Spjdstatic int rflag = 0;
166211976Spjdstatic int usererr = -1;
167211976Spjdstatic int stopmotd = 0;
168211976Spjdstatic char rusername[NMAX + 1];
169211976Spjdstatic char name[NMAX + 1] = "";
170211976Spjdstatic char minusnam[16] = "-";
171211976Spjdstatic char *envinit[1];	/* now set by setenv calls */
172211976Spjdstatic char term[64] = "";	/* important to initialise to a NULL string */
173211976Spjdstatic char host[HMAX + 1] = "";
174211976Spjdstatic struct passwd nouser;
175211976Spjdstatic struct passwd thisuser;
176211976Spjd
177211976Spjd#if HAVE_SHADOW_H
178211976Spjd#include <shadow.h>
179211976Spjd#endif /* HAVE_SHADOW_H */
180211976Spjd
181211976Spjdstatic char *ttyprompt;
182211885Spjd
183211885Spjd#ifdef PERMSFILE
184211885Spjdextern char *home;
185211885Spjd#endif	/* PERMSFILE */
186211885Spjd
187211885Spjdstatic struct termios attr;
188211885Spjd
189211885Spjdextern int errno;
190211885Spjd
191211885Spjdstatic int ouroptind;
192211885Spjdstatic char *ouroptarg;
193211885Spjd
194211885Spjd#if HAVE_LASTLOG_H
195211885Spjd#ifndef _PATH_LASTLOG
196211885Spjd#define _PATH_LASTLOG "/var/adm/lastlog"
197211885Spjd#endif /* _PATH_LASTLOG */
198211885Spjd
199211885Spjdstatic char lastlog[] = _PATH_LASTLOG;
200211885Spjd#endif /* HAVE_LASTLOG_H */
201211885Spjd
202211885Spjd/*
203211885Spjd * The "timeout" variable bounds the time given to login.
204211885Spjd * We initialize it here for safety and so that it can be
205211885Spjd * patched on machines where the default value is not appropriate.
206211885Spjd */
207211885Spjdstatic int timeout = 120;
208211885Spjd
209211885Spjdstatic void getstr __P((char *, int, char *));
210211885Spjd
211211885Spjd#if HAVE_CRYPT_H
212211885Spjd#include <crypt.h>
213211885Spjd#endif /* HAVE_CRYPT_H */
214211885Spjd
215211885Spjd#undef TRUE
216211885Spjd#define TRUE -1
217211885Spjd
218211885Spjdstatic int need_opieverify = 0;
219211885Spjdstatic struct opie opie;
220211885Spjd
221211885Spjd#ifdef TIOCSWINSZ
222211885Spjd/* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is
223211885Spjdavailable on BSDish systems and at least Solaris 2.x, but portability to
224211885Spjdother systems is questionable. Use within this source code module is
225211885Spjdprotected by suitable defines.
226211885Spjd
227211885SpjdI'd be interested in hearing about a more portable approach. rja */
228211885Spjd
229211885Spjdstatic struct winsize win = {0, 0, 0, 0};
230211885Spjd#endif
231211885Spjd
232211885Spjd
233211885Spjd/*------------------ BEGIN REAL CODE --------------------------------*/
234211885Spjd
235211885Spjd/* We allow the malloc()s to potentially leak data out because we can
236211885Spjdonly call this routine about four times in the lifetime of this process
237211885Spjdand the kernel will free all heap memory when we exit or exec. */
238211885Spjdstatic int lookupuser FUNCTION_NOARGS
239211885Spjd{
240211885Spjd  struct passwd *pwd;
241211885Spjd#if HAVE_SHADOW
242211885Spjd  struct spwd *spwd;
243211885Spjd#endif /* HAVE_SHADOW */
244211885Spjd
245211885Spjd  memcpy(&thisuser, &nouser, sizeof(thisuser));
246211885Spjd
247211885Spjd  if (!(pwd = getpwnam(name)))
248211885Spjd    return -1;
249211885Spjd
250211885Spjd  thisuser.pw_uid = pwd->pw_uid;
251211885Spjd  thisuser.pw_gid = pwd->pw_gid;
252211885Spjd
253211885Spjd  if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1)))
254211885Spjd    goto lookupuserbad;
255211885Spjd  strcpy(thisuser.pw_name, pwd->pw_name);
256211885Spjd
257211885Spjd  if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1)))
258211885Spjd    goto lookupuserbad;
259211885Spjd  strcpy(thisuser.pw_dir, pwd->pw_dir);
260211885Spjd
261211885Spjd  if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1)))
262211885Spjd    goto lookupuserbad;
263211885Spjd  strcpy(thisuser.pw_shell, pwd->pw_shell);
264211885Spjd
265211885Spjd#if HAVE_SHADOW
266211885Spjd  if (!(spwd = getspnam(name)))
267211885Spjd	goto lookupuserbad;
268211976Spjd
269211976Spjd  pwd->pw_passwd = spwd->sp_pwdp;
270211976Spjd
271211976Spjd  endspent();
272211976Spjd#endif /* HAVE_SHADOW */
273211976Spjd
274211976Spjd  if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1)))
275211976Spjd    goto lookupuserbad;
276211976Spjd  strcpy(thisuser.pw_passwd, pwd->pw_passwd);
277211976Spjd
278211976Spjd  endpwent();
279211976Spjd
280211976Spjd  return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#'));
281211976Spjd
282211976Spjdlookupuserbad:
283211976Spjd  memcpy(&thisuser, &nouser, sizeof(thisuser));
284211976Spjd  return -1;
285211976Spjd}
286211976Spjd
287211976Spjdstatic VOIDRET getloginname FUNCTION_NOARGS
288211976Spjd{
289211976Spjd  char *namep, d;
290211976Spjd  int flags;
291211976Spjd  static int first = 1;
292211976Spjd
293211976Spjd  memset(name, 0, sizeof(name));
294211976Spjd
295211976Spjd  d = 0;
296211885Spjd  while (name[0] == '\0') {
297211885Spjd    flags = 1;
298211885Spjd    if (ttyprompt) {
299211885Spjd      if (first) {
300211885Spjd	flags = 4;
301211885Spjd	first--;
302211885Spjd      } else
303211885Spjd	printf(ttyprompt);
304211885Spjd    } else
305211885Spjd      printf("login: ");
306211885Spjd    fflush(stdout);
307211885Spjd    if (++d == 3)
308211976Spjd      exit(0);
309211976Spjd    if (!opiereadpass(name, sizeof(name)-1, flags)) {
310211976Spjd      syslog(LOG_CRIT, "End-of-file (or other error?) on stdin!");
311211885Spjd      exit(0);
312211885Spjd    }
313211885Spjd    for (namep = name; *namep; namep++) {
314211885Spjd      if (*namep == ' ')
315211885Spjd        *namep = '_';
316211885Spjd    }
317211885Spjd  }
318211885Spjd}
319211885Spjd
320211885Spjdstatic VOIDRET timedout FUNCTION((i), int i)
321211885Spjd{
322211885Spjd  /* input variable declared just to keep the compiler quiet */
323211885Spjd  printf("Login timed out after %d seconds\n", timeout);
324211885Spjd  syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout);
325211885Spjd
326211885Spjd  if (need_opieverify)
327211885Spjd    opieverify(&opie, NULL);
328211885Spjd
329211885Spjd  exit(0);
330211885Spjd}
331211885Spjd
332211885Spjd#if !HAVE_MOTD_IN_PROFILE
333211885Spjdstatic VOIDRET catch FUNCTION((i), int i)
334211885Spjd{
335211885Spjd  /* the input variable is declared to keep the compiler quiet */
336211885Spjd  signal(SIGINT, SIG_IGN);
337211885Spjd  stopmotd++;
338211885Spjd}
339211885Spjd#endif /* !HAVE_MOTD_IN_PROFILE */
340211885Spjd
341211885Spjdstatic VOIDRET catchexit FUNCTION_NOARGS
342211885Spjd{
343211885Spjd  int i;
344211885Spjd  tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr);
345211885Spjd  putchar('\n');
346211885Spjd  closelog();
347211885Spjd  for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
348211885Spjd    close(i);
349211885Spjd}
350211885Spjd
351211885Spjdstatic int rootterm FUNCTION((ttyn), char *ttyn)
352204076Spjd{
353204076Spjd#if HAVE_GETTTYNAM
354204076Spjd/* The getttynam() call and the ttyent structure first appeared in 4.3 BSD and
355204076Spjdare not portable to System V systems such as Solaris 2.x. or modern versions
356204076Spjdof IRIX rja */
357211885Spjd  register struct ttyent *t;
358204076Spjd  char *tty;
359204076Spjd
360204076Spjd  tty = strrchr(ttyn, '/');
361211885Spjd
362204076Spjd  if (tty == NULL)
363204076Spjd    tty = ttyn;
364211885Spjd  else
365204076Spjd    tty++;
366204076Spjd
367211885Spjd  if ((t = getttynam(tty)) != NULL)
368204076Spjd    return (t->ty_status & TTY_SECURE);
369211885Spjd
370211885Spjd  return (1);	/* when in doubt, allow root logins */
371204076Spjd
372211885Spjd#elif HAVE_ETC_DEFAULT_LOGIN
373204076Spjd
374204076Spjd  FILE *filno;
375204076Spjd  char line[128];
376204076Spjd  char *next, *next2;
377204076Spjd
378204076Spjd/* SVR4 only permits two security modes for root logins: 1) only from CONSOLE,
379204076Spjdif the string "CONSOLE=/dev/console" exists and is not commented out with "#"
380204076Spjdcharacters, or 2) from anywhere.
381204076Spjd
382204076SpjdSo we open /etc/default/login file grab the file contents one line at a time
383211885Spjdverify that the line being tested isn't commented out check for the substring
384211885Spjd"CONSOLE" and decide whether to permit this attempted root login/su. */
385211885Spjd
386211885Spjd  if ((filno = fopen("/etc/default/login", "r")) != NULL) {
387204076Spjd    while (fgets(line, 128, filno) != NULL) {
388204076Spjd      next = line;
389204076Spjd
390211885Spjd      if ((line[0] != '#') && (next = strstr(line, "CONSOLE"))) {
391213183Spjd	next += 7;	/* get past the string "CONSOLE" */
392211885Spjd
393204076Spjd	while (*next && (*next == ' ') || (*next == '\t'))
394204076Spjd	  next++;
395204076Spjd
396204076Spjd	if (*(next++) != '=')
397204076Spjd	  break;	/* some weird character, get next line */
398204076Spjd
399211885Spjd	next2 = next;
400204076Spjd	while (*next2 && (*next2 != '\t') && (*next2 != ' ') &&
401204076Spjd	       (*next2 != '\n'))
402204076Spjd	  next2++;
403	*next2 = 0;
404
405	return !strcmp(ttyn, next);	/* Allow the login if and only if the
406					   user's terminal line matches the
407					   setting for CONSOLE */
408      }
409    }	/* end while another line could be obtained */
410  }	/* end if could open file */
411  return (1);	/* when no CONSOLE line exists, root can login from anywhere */
412#elif HAVE_SECURETTY
413  {
414    FILE *f;
415    char buffer[1024], *c;
416    int rc = 0;
417
418    if (!(f = fopen("/etc/securetty", "r")))
419      return 1;
420
421    if (c = strstr(ttyn, "/dev/"))
422      ttyn += 5;
423
424    if (c = strrchr(ttyn, '/'))
425      ttyn = ++c;
426
427    while (fgets(buffer, sizeof(buffer), f)) {
428      if (c = strrchr(buffer, '\n'))
429	*c = 0;
430
431      if (!(c = strrchr(buffer, '/')))
432	c = buffer;
433      else
434	c++;
435
436      if (!strcmp(c, ttyn))
437	rc = 1;
438    };
439
440    fclose(f);
441    return rc;
442  }
443#else
444  return (1);	/* when in doubt, allow root logins */
445#endif
446}
447
448static int doremotelogin FUNCTION((host), char *host)
449{
450  int rc;
451
452  getstr(rusername, sizeof(rusername), "remuser");
453  getstr(name, sizeof(name), "locuser");
454  getstr(term, sizeof(term), "Terminal type");
455  if (getuid()) {
456    memcpy(&thisuser, &nouser, sizeof(thisuser));
457    syslog(LOG_ERR, "getuid() failed");
458    return (-1);
459  }
460  if (lookupuser()) {
461    syslog(LOG_ERR, "lookup failed for user %s", name);
462    return (-1);
463  }
464  rc = ruserok(host, !thisuser.pw_uid, rusername, name);
465  if (rc == -1) {
466    syslog(LOG_ERR,
467    "ruserok failed, host=%s, uid=%d, remote username=%s, local username=%s",
468	   host, thisuser.pw_uid, rusername, name);
469  }
470  return rc;
471}
472
473
474static VOIDRET getstr FUNCTION((buf, cnt, err), char *buf AND int cnt AND char *err)
475{
476  char c;
477
478  do {
479    if (read(0, &c, 1) != 1)
480      exit(1);
481    if (--cnt < 0) {
482      printf("%s too long\r\n", err);
483      syslog(LOG_CRIT, "%s too long", err);
484      exit(1);
485    }
486    *buf++ = c;
487  }
488  while ((c != 0) && (c != '~'));
489}
490
491struct speed_xlat {
492  char *c;
493  int i;
494}          speeds[] = {
495
496#ifdef B0
497  {
498    "0", B0
499  },
500#endif	/* B0 */
501#ifdef B50
502  {
503    "50", B50
504  },
505#endif	/* B50 */
506#ifdef B75
507  {
508    "75", B75
509  },
510#endif	/* B75 */
511#ifdef B110
512  {
513    "110", B110
514  },
515#endif	/* B110 */
516#ifdef B134
517  {
518    "134", B134
519  },
520#endif	/* B134 */
521#ifdef B150
522  {
523    "150", B150
524  },
525#endif	/* B150 */
526#ifdef B200
527  {
528    "200", B200
529  },
530#endif	/* B200 */
531#ifdef B300
532  {
533    "300", B300
534  },
535#endif	/* B300 */
536#ifdef B600
537  {
538    "600", B600
539  },
540#endif	/* B600 */
541#ifdef B1200
542  {
543    "1200", B1200
544  },
545#endif	/* B1200 */
546#ifdef B1800
547  {
548    "1800", B1800
549  },
550#endif	/* B1800 */
551#ifdef B2400
552  {
553    "2400", B2400
554  },
555#endif	/* B2400 */
556#ifdef B4800
557  {
558    "4800", B4800
559  },
560#endif	/* B4800 */
561#ifdef B7200
562  {
563    "7200", B7200
564  },
565#endif	/* B7200 */
566#ifdef B9600
567  {
568    "9600", B9600
569  },
570#endif	/* B9600 */
571#ifdef B14400
572  {
573    "14400", B14400
574  },
575#endif	/* B14400 */
576#ifdef B19200
577  {
578    "19200", B19200
579  },
580#endif	/* B19200 */
581#ifdef B28800
582  {
583    "28800", B28800
584  },
585#endif	/* B28800 */
586#ifdef B38400
587  {
588    "38400", B38400
589  },
590#endif	/* B38400 */
591#ifdef B57600
592  {
593    "57600", B57600
594  },
595#endif	/* B57600 */
596#ifdef B115200
597  {
598    "115200", B115200
599  },
600#endif	/* B115200 */
601#ifdef B230400
602  {
603    "230400", B230400
604  },
605#endif	/* 230400 */
606  {
607    NULL, 0
608  }
609};
610
611static VOIDRET doremoteterm FUNCTION((term), char *term)
612{
613  register char *cp = strchr(term, '/');
614  char *speed;
615  struct speed_xlat *x;
616
617  if (cp) {
618    *cp++ = '\0';
619    speed = cp;
620    cp = strchr(speed, '/');
621    if (cp)
622      *cp++ = '\0';
623    for (x = speeds; x->c != NULL; x++)
624      if (strcmp(x->c, speed) == 0) {
625	cfsetispeed(&attr, x->i);
626	cfsetospeed(&attr, x->i);
627	break;
628      }
629  }
630}
631
632static int tty_gid FUNCTION((default_gid), int default_gid)
633{
634  struct group *gr;
635  int gid = default_gid;
636
637  gr = getgrnam(TTYGRPNAME);
638  if (gr != (struct group *) 0)
639    gid = gr->gr_gid;
640  endgrent();
641  return (gid);
642}
643
644int main FUNCTION((argc, argv), int argc AND char *argv[])
645{
646  extern char **environ;
647  register char *namep;
648
649  int invalid, quietlog;
650  FILE *nlfd;
651  char *tty, host[256];
652  int pflag = 0, hflag = 0, fflag = 0;
653  int t, c;
654  int i;
655  char *p;
656  char opieprompt[OPIE_CHALLENGE_MAX + 1];
657  int af_pwok;
658  int authsok = 0;
659  char *pp;
660  char buf[256];
661  int uid;
662  int opiepassed;
663
664#ifndef DEBUG
665  if (geteuid()) {
666    fprintf(stderr, "This program requires super-user privileges.\n");
667    exit(1);
668  }
669#endif /* DEBUG */
670
671  for (t = sysconf(_SC_OPEN_MAX); t > 2; t--)
672    close(t);
673
674  openlog("login", LOG_ODELAY, LOG_AUTH);
675
676  /* initialisation */
677  host[0] = '\0';
678  opieprompt[0] = '\0';
679
680  if (p = getenv("TERM")) {
681#ifdef DEBUG
682    syslog(LOG_DEBUG, "environment TERM=%s", p);
683#endif /* DEBUG */
684    opiestrncpy(term, p, sizeof(term));
685  };
686
687  memset(&nouser, 0, sizeof(nouser));
688  nouser.pw_uid = -1;
689  nouser.pw_gid = -1;
690  nouser.pw_passwd = "#nope";
691  nouser.pw_name = nouser.pw_gecos = nouser.pw_dir = nouser.pw_shell = "";
692
693#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
694  setpriority(PRIO_PROCESS, 0, 0);
695#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
696
697  signal(SIGALRM, timedout);
698  alarm(timeout);
699  signal(SIGQUIT, SIG_IGN);
700  signal(SIGINT, SIG_IGN);
701
702#if DOTTYPROMPT
703  ttyprompt = (char *) getenv("TTYPROMPT");
704#endif /* TTYPROMPT */
705
706#ifdef	QUOTA
707  quota(Q_SETUID, 0, 0, 0);
708#endif
709
710#ifdef DEBUG
711  syslog(LOG_DEBUG, "my args are: (argc=%d)", i = argc);
712  while (--i)
713    syslog(LOG_DEBUG, "%d: %s", i, argv[i]);
714#endif /* DEBUG */
715
716/* Implement our own getopt()-like functionality, but do so in a much more
717   strict manner to prevent security problems. */
718  for (ouroptind = 1; ouroptind < argc; ouroptind++) {
719    if (!argv[ouroptind])
720      continue;
721
722    if (argv[ouroptind][0] == '-') {
723      char *c = argv[ouroptind] + 1;
724
725      while(*c) {
726	switch(*(c++)) {
727	  case 'd':
728	    if (*c || (++ouroptind == argc))
729	      exit(1);
730
731/*    The '-d' option is apparently a performance hack to get around
732   ttyname() being slow. The potential does exist for it to be used
733   for malice, and it does not seem to be strictly necessary, so we
734   will just eat it. */
735	    break;
736
737	  case 'r':
738	    if (rflag || hflag || fflag) {
739	      fprintf(stderr, "Other options not allowed with -r\n");
740	      exit(1);
741	    }
742
743	    if (*c || (++ouroptind == argc))
744	      exit(1);
745
746	    if (!(ouroptarg = argv[ouroptind]))
747	      exit(1);
748
749	    rflag = -1;
750	    if (!doremotelogin(ouroptarg))
751	      rflag = 1;
752
753	    opiestrncpy(host, ouroptarg, sizeof(host));
754	    break;
755
756	  case 'h':
757	    if (!getuid()) {
758	      if (rflag || hflag || fflag) {
759		fprintf(stderr, "Other options not allowed with -h\n");
760		exit(1);
761	      }
762	      hflag = 1;
763
764	      if (*c || (++ouroptind == argc))
765		exit(1);
766
767	      if (!(ouroptarg = argv[ouroptind]))
768		exit(1);
769
770	      opiestrncpy(host, ouroptarg, sizeof(host));
771	    }
772	    break;
773
774	  case 'f':
775	    if (rflag) {
776	      fprintf(stderr, "Only one of -r and -f allowed\n");
777	      exit(1);
778	    }
779	    fflag = 1;
780
781	    if (*c || (++ouroptind == argc))
782	      exit(1);
783
784	    if (!(ouroptarg = argv[ouroptind]))
785	      exit(1);
786
787	    opiestrncpy(name, ouroptarg, sizeof(name));
788	    break;
789	  case 'p':
790	    pflag = 1;
791	    break;
792	};
793      };
794      continue;
795    };
796
797    if (strchr(argv[ouroptind], '=')) {
798      if (!strncmp(argv[ouroptind], "TERM=", 5)) {
799	opiestrncpy(term, &(argv[ouroptind][5]), sizeof(term));
800
801#ifdef DEBUG
802	syslog(LOG_DEBUG, "passed TERM=%s, ouroptind = %d", term, ouroptind);
803#endif /* DEBUG */
804      } else {
805#ifdef DEBUG
806	syslog(LOG_DEBUG, "eating %s, ouroptind = %d", argv[ouroptind], ouroptind);
807#endif /* DEBUG */
808      };
809      continue;
810    };
811
812    opiestrncpy(name, argv[ouroptind], sizeof(name));
813  };
814
815#ifdef TIOCNXCL
816  /* BSDism:  not sure how to rewrite for POSIX.  rja */
817  ioctl(0, TIOCNXCL, 0);	/* set non-exclusive use of tty */
818#endif
819
820  /* get original termio attributes */
821  if (tcgetattr(STDIN_FILENO, &attr) != 0)
822    return (-1);
823
824/* If talking to an rlogin process, propagate the terminal type and baud rate
825   across the network. */
826  if (rflag)
827    doremoteterm(term);
828  else {
829    struct termios termios;
830    fd_set fds;
831    struct timeval timeval;
832
833    memset(&timeval, 0, sizeof(struct timeval));
834
835    FD_ZERO(&fds);
836    FD_SET(0, &fds);
837
838#if HAVE_USLEEP
839    usleep(1);
840#endif /* HAVE_USLEEP */
841
842    if (select(1, &fds, NULL, NULL, &timeval)) {
843#ifdef DEBUG
844      syslog(LOG_DEBUG, "reading user name from tty buffer");
845#endif /* DEBUG */
846
847      if (tcgetattr(0, &termios)) {
848#ifdef DEBUG
849	syslog(LOG_DEBUG, "tcgetattr(0, &termios) failed");
850#endif /* DEBUG */
851	exit(1);
852      }
853
854      termios.c_lflag &= ~ECHO;
855
856      if (tcsetattr(0, TCSANOW, &termios)) {
857#ifdef DEBUG
858	syslog(LOG_DEBUG, "tcsetattr(0, &termios) failed");
859#endif /* DEBUG */
860	exit(1);
861      }
862
863      if ((i = read(0, name, sizeof(name)-1)) > 0)
864	name[i] = 0;
865      if ((p = strchr(name, '\r')))
866        *p = 0;
867      if ((p = strchr(name, '\n')))
868        *p = 0;
869    }
870  }
871
872/* Force termios portable control characters to the system default values as
873specified in termios.h. This should help the one-time password login feel the
874same as the vendor-supplied login. Common extensions are also set for
875completeness, but these are set within appropriate defines for portability. */
876
877#define CONTROL(x) (x - 64)
878
879#ifdef VEOF
880#ifdef CEOF
881  attr.c_cc[VEOF] = CEOF;
882#else	/* CEOF */
883  attr.c_cc[VEOF] = CONTROL('D');
884#endif	/* CEOF */
885#endif	/* VEOF */
886#ifdef VEOL
887#ifdef CEOL
888  attr.c_cc[VEOL] = CEOL;
889#else	/* CEOL */
890  attr.c_cc[VEOL] = CONTROL('J');
891#endif	/* CEOL */
892#endif	/* VEOL */
893#ifdef VERASE
894#ifdef CERASE
895  attr.c_cc[VERASE] = CERASE;
896#else	/* CERASE */
897  attr.c_cc[VERASE] = CONTROL('H');
898#endif	/* CERASE */
899#endif	/* VERASE */
900#ifdef VINTR
901#ifdef CINTR
902  attr.c_cc[VINTR] = CINTR;
903#else	/* CINTR */
904  attr.c_cc[VINTR] = CONTROL('C');
905#endif	/* CINTR */
906#endif	/* VINTR */
907#ifdef VKILL
908#ifdef CKILL
909  attr.c_cc[VKILL] = CKILL;
910#else	/* CKILL */
911  attr.c_cc[VKILL] = CONTROL('U');
912#endif	/* CKILL */
913#endif	/* VKILL */
914#ifdef VQUIT
915#ifdef CQUIT
916  attr.c_cc[VQUIT] = CQUIT;
917#else	/* CQUIT */
918  attr.c_cc[VQUIT] = CONTROL('\\');
919#endif	/* CQUIT */
920#endif	/* VQUIT */
921#ifdef VSUSP
922#ifdef CSUSP
923  attr.c_cc[VSUSP] = CSUSP;
924#else	/* CSUSP */
925  attr.c_cc[VSUSP] = CONTROL('Z');
926#endif	/* CSUSP */
927#endif	/* VSUSP */
928#ifdef VSTOP
929#ifdef CSTOP
930  attr.c_cc[VSTOP] = CSTOP;
931#else	/* CSTOP */
932  attr.c_cc[VSTOP] = CONTROL('S');
933#endif	/* CSTOP */
934#endif	/* VSTOP */
935#ifdef VSTART
936#ifdef CSTART
937  attr.c_cc[VSTART] = CSTART;
938#else	/* CSTART */
939  attr.c_cc[VSTART] = CONTROL('Q');
940#endif	/* CSTART */
941#endif	/* VSTART */
942#ifdef VDSUSP
943#ifdef CDSUSP
944  attr.c_cc[VDSUSP] = CDSUSP;
945#else	/* CDSUSP */
946  attr.c_cc[VDSUSP] = 0;
947#endif	/* CDSUSP */
948#endif	/* VDSUSP */
949#ifdef VEOL2
950#ifdef CEOL2
951  attr.c_cc[VEOL2] = CEOL2;
952#else	/* CEOL2 */
953  attr.c_cc[VEOL2] = 0;
954#endif	/* CEOL2 */
955#endif	/* VEOL2 */
956#ifdef VREPRINT
957#ifdef CRPRNT
958  attr.c_cc[VREPRINT] = CRPRNT;
959#else	/* CRPRNT */
960  attr.c_cc[VREPRINT] = 0;
961#endif	/* CRPRNT */
962#endif	/* VREPRINT */
963#ifdef VWERASE
964#ifdef CWERASE
965  attr.c_cc[VWERASE] = CWERASE;
966#else	/* CWERASE */
967  attr.c_cc[VWERASE] = 0;
968#endif	/* CWERASE */
969#endif	/* VWERASE */
970#ifdef VLNEXT
971#ifdef CLNEXT
972  attr.c_cc[VLNEXT] = CLNEXT;
973#else	/* CLNEXT */
974  attr.c_cc[VLNEXT] = 0;
975#endif	/* CLNEXT */
976#endif	/* VLNEXT */
977
978  attr.c_lflag |= ICANON;	/* enable canonical input processing */
979  attr.c_lflag &= ~ISIG;	/* disable INTR, QUIT,& SUSP signals */
980  attr.c_lflag |= (ECHO | ECHOE);	/* enable echo and erase */
981#ifdef ONLCR
982  /* POSIX does not specify any output processing flags, but the usage below
983     is SVID compliant and is generally portable to modern versions of UNIX. */
984  attr.c_oflag |= ONLCR;	/* map CR to CRNL on output */
985#endif
986#ifdef ICRNL
987  attr.c_iflag |= ICRNL;
988#endif	/* ICRNL */
989
990  attr.c_oflag |= OPOST;
991  attr.c_lflag |= ICANON;	/* enable canonical input */
992  attr.c_lflag |= ECHO;
993  attr.c_lflag |= ECHOE;	/* enable ERASE character */
994  attr.c_lflag |= ECHOK;	/* enable KILL to delete line */
995  attr.c_cflag |= HUPCL;	/* hangup on close */
996
997  /* Set revised termio attributes */
998  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr))
999    return (-1);
1000
1001  atexit(catchexit);
1002
1003  tty = ttyname(0);
1004
1005  if (tty == (char *) 0 || *tty == '\0')
1006    tty = "UNKNOWN";	/* was: "/dev/tty??" */
1007
1008#if HAVE_SETVBUF && defined(_IONBF)
1009#if SETVBUF_REVERSED
1010  setvbuf(stdout, _IONBF, NULL, 0);
1011  setvbuf(stderr, _IONBF, NULL, 0);
1012#else /* SETVBUF_REVERSED */
1013  setvbuf(stdout, NULL, _IONBF, 0);
1014  setvbuf(stderr, NULL, _IONBF, 0);
1015#endif /* SETVBUF_REVERSED */
1016#endif /* HAVE_SETVBUF && defined(_IONBF) */
1017
1018#ifdef DEBUG
1019  syslog(LOG_DEBUG, "tty = %s", tty);
1020#endif /* DEBUG */
1021
1022#ifdef HAVE_LOGIN_ENVFILE
1023  {
1024    FILE *f;
1025
1026    if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) {
1027      char line[128], *c, *c2;
1028
1029      while(fgets(line, sizeof(line)-1, f)) {
1030	c = line;
1031	while(*c && (isalnum(*c) || (*c == '_'))) c++;
1032	  if (*c == '=') {
1033	    *(c++) = 0;
1034	    if (c2 = strchr(c, ';'))
1035	      *c2 = 0;
1036	    if (c2 = strchr(c, '\n'))
1037	      *c2 = 0;
1038	    if (c2 = strchr(c, ' '))
1039	      continue;
1040	    if (c2 = strchr(c, '\t'))
1041	      continue;
1042	    if (!strcmp(line, "TZ"))
1043	      continue;
1044	    if (setenv(line, c, 1) < 0) {
1045	      fprintf(stderr, "setenv() failed -- environment full?\n");
1046	      break;
1047	    }
1048	  }
1049        }
1050      fclose(f);
1051    }
1052  }
1053#endif /* HAVE_LOGIN_ENVFILE */
1054
1055  t = 0;
1056  invalid = TRUE;
1057  af_pwok = opieaccessfile(host);
1058
1059  if (name[0])
1060    if (name[0] == '-') {
1061      fprintf(stderr, "User names can't start with '-'.\n");
1062      syslog(LOG_AUTH, "Attempt to use invalid username: %s.", name);
1063      exit(1);
1064    } else
1065      invalid = lookupuser();
1066
1067  do {
1068    /* If remote login take given name, otherwise prompt user for something. */
1069    if (invalid && !name[0]) {
1070      getloginname();
1071      invalid = lookupuser();
1072      authsok = 0;
1073    }
1074#ifdef DEBUG
1075    syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d", name, strlen(name), name[0]);
1076#endif /* DEBUG */
1077
1078    if (fflag) {
1079      uid = getuid();
1080
1081      if (uid != 0 && uid != thisuser.pw_uid)
1082	fflag = 0;
1083      /* Disallow automatic login for root. */
1084      if (thisuser.pw_uid == 0)
1085	fflag = 0;
1086    }
1087    if (feof(stdin))
1088      exit(0);
1089
1090    /* If no remote login authentication and a password exists for this user,
1091       prompt for and verify a password. */
1092    if (!fflag && (rflag < 1) && *thisuser.pw_passwd) {
1093#ifdef DEBUG
1094      syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d\n", name, strlen(name), name[0]);
1095#endif /* DEBUG */
1096
1097      /* Attempt a one-time password challenge */
1098      i = opiechallenge(&opie, name, opieprompt);
1099      need_opieverify = TRUE;
1100
1101      if ((i < 0) || (i > 1)) {
1102        syslog(LOG_ERR, "error: opiechallenge() returned %d, errno=%d!\n", i, errno);
1103      } else {
1104        printf("%s\n", opieprompt);
1105	authsok |= 1;
1106      }
1107
1108      if (!memcmp(&thisuser, &nouser, sizeof(thisuser)))
1109	if (host[0])
1110	  syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s.",
1111		 name, tty, host);
1112	else
1113	  syslog(LOG_WARNING, "Invalid login attempt for %s on %s.",
1114		 name, tty);
1115
1116      if (af_pwok && opiealways(thisuser.pw_dir))
1117	authsok |= 2;
1118
1119#if DEBUG
1120      syslog(LOG_DEBUG, "af_pwok = %d, authsok = %d", af_pwok, authsok);
1121#endif /* DEBUG */
1122
1123      if (!authsok)
1124	syslog(LOG_ERR, "no authentication methods are available for %s!", name);
1125
1126#if NEW_PROMPTS
1127      if ((authsok & 1) || !authsok)
1128        printf("Response");
1129      if (((authsok & 3) == 3) || !authsok)
1130        printf(" or ");
1131      if ((authsok & 2) || !authsok)
1132        printf("Password");
1133      printf(": ");
1134      fflush(stdout);
1135      if (!opiereadpass(buf, sizeof(buf), !(authsok & 2)))
1136        invalid = TRUE;
1137#else /* NEW_PROMPTS */
1138      if ((authsok & 3) == 1)
1139	printf("(OTP response required)\n");
1140      printf("Password:");
1141      fflush(stdout);
1142      if (!opiereadpass(buf, sizeof(buf), 0))
1143        invalid = TRUE;
1144#endif /* NEW_PROMPTS */
1145
1146      if (!buf[0] && (authsok & 1)) {
1147        authsok &= ~2;
1148	/* Null line entered, so display appropriate prompt & flush current
1149	   data. */
1150#if NEW_PROMPTS
1151        printf("Response: ");
1152#else /* NEW_PROMPTS */
1153	printf(" (echo on)\nPassword:");
1154#endif /* NEW_PROMPTS */
1155        if (!opiereadpass(buf, sizeof(buf), 1))
1156          invalid = TRUE;
1157      }
1158
1159      if (authsok & 1) {
1160        i = opiegetsequence(&opie);
1161        opiepassed = !opieverify(&opie, buf);
1162	need_opieverify = 0;
1163
1164#ifdef DEBUG
1165      syslog(LOG_DEBUG, "opiepassed = %d", opiepassed);
1166#endif /* DEBUG */
1167      }
1168
1169      if (!invalid) {
1170        if ((authsok & 1) && opiepassed) {
1171	  if (i < 10) {
1172	    printf("Warning: Re-initialize your OTP information");
1173            if (i < 5)
1174              printf(" NOW!");
1175            printf("\n");
1176	  }
1177        } else {
1178	  if (authsok & 2) {
1179	    pp = crypt(buf, thisuser.pw_passwd);
1180	    invalid = strcmp(pp, thisuser.pw_passwd);
1181	  } else
1182            invalid = TRUE;
1183	}
1184      }
1185    }
1186
1187    /* If user not super-user, check for logins disabled. */
1188    if (thisuser.pw_uid) {
1189      if (nlfd = fopen(_PATH_NOLOGIN, "r")) {
1190	while ((c = getc(nlfd)) != EOF)
1191	  putchar(c);
1192	fflush(stdout);
1193	sleep(5);
1194	exit(0);
1195      }
1196    }
1197    /* If valid so far and root is logging in, see if root logins on this
1198       terminal are permitted. */
1199    if (!invalid && !thisuser.pw_uid && !rootterm(tty)) {
1200      if (host[0])
1201	syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s FROM %.*s",
1202	       tty, HMAX, host);
1203      else
1204	syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s", tty);
1205      invalid = TRUE;
1206    }
1207    /* If invalid, then log failure attempt data to appropriate system
1208       logfiles and close the connection. */
1209    if (invalid) {
1210      printf("Login incorrect\n");
1211      if (host[0])
1212	  syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s, %.*s",
1213		 tty, HMAX, host, sizeof(name), name);
1214	else
1215	  syslog(LOG_ERR, "LOGIN FAILURE ON %s, %.*s",
1216		 tty, sizeof(name), name);
1217      if (++t >= 5)
1218	exit(1);
1219    }
1220    if (*thisuser.pw_shell == '\0')
1221      thisuser.pw_shell = "/bin/sh";
1222    /* Remote login invalid must have been because of a restriction of some
1223       sort, no extra chances. */
1224    if (invalid) {
1225      if (!usererr)
1226	exit(1);
1227      name[0] = 0;
1228    }
1229  }
1230  while (invalid);
1231  /* Committed to login -- turn off timeout */
1232  alarm(0);
1233
1234#ifdef	QUOTA
1235  if (quota(Q_SETUID, thisuser.pw_uid, 0, 0) < 0 && errno != EINVAL) {
1236    if (errno == EUSERS)
1237      printf("%s.\n%s.\n", "Too many users logged on already",
1238	     "Try again later");
1239    else
1240      if (errno == EPROCLIM)
1241	printf("You have too many processes running.\n");
1242      else
1243	perror("quota (Q_SETUID)");
1244    sleep(5);
1245    exit(0);
1246  }
1247#endif
1248
1249  if (opielogin(tty, name, host))
1250    syslog(LOG_ERR, "can't record login: tty %s, name %s, host %s", tty, name, host);
1251
1252  quietlog = !access(QUIET_LOGIN_FILE, F_OK);
1253
1254#if HAVE_LASTLOG_H
1255  {
1256  int f;
1257
1258  if ((f = open(lastlog, O_RDWR)) >= 0) {
1259    struct lastlog ll;
1260
1261    lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0);
1262
1263    if ((sizeof(ll) == read(f, (char *) &ll, sizeof(ll))) &&
1264	(ll.ll_time != 0) && (!quietlog)) {
1265      printf("Last login: %.*s ",
1266	     24 - 5, (char *) ctime(&ll.ll_time));
1267      if (*ll.ll_host != '\0')
1268	printf("from %.*s\n", sizeof(ll.ll_host), ll.ll_host);
1269      else
1270	printf("on %.*s\n", sizeof(ll.ll_line), ll.ll_line);
1271    }
1272    lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0);
1273
1274    time(&ll.ll_time);
1275    if (!strncmp(tty, "/dev/", 5))
1276      opiestrncpy(ll.ll_line, tty + 5, sizeof(ll.ll_line));
1277    else
1278      opiestrncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1279    opiestrncpy(ll.ll_host, host, sizeof(ll.ll_host));
1280    write(f, (char *) &ll, sizeof ll);
1281    close(f);
1282  }
1283  }
1284#endif /* HAVE_LASTLOG_H */
1285
1286  chown(tty, thisuser.pw_uid, TTYGID(thisuser.pw_gid));
1287
1288#ifdef TIOCSWINSZ
1289/* POSIX does not specify any interface to set/get window sizes, so this is
1290not portable.  It should work on most recent BSDish systems and the defines
1291should protect it on older System Vish systems.  It does work under Solaris
12922.4, though it isn't clear how many other SVR4 systems support it. I'd be
1293interested in hearing of a more portable approach. rja */
1294  if (!hflag && !rflag)
1295    ioctl(0, TIOCSWINSZ, &win);	/* set window size to 0,0,0,0 */
1296#endif
1297
1298  chmod(tty, 0622);
1299  setgid(thisuser.pw_gid);
1300  initgroups(name, thisuser.pw_gid);
1301
1302#ifdef	QUOTA
1303  quota(Q_DOWARN, thisuser.pw_uid, (dev_t) - 1, 0);
1304#endif
1305
1306#ifdef PERMSFILE
1307  home = thisuser.pw_dir;
1308  permsfile(name, tty, thisuser.pw_uid, thisuser.pw_gid);
1309  fflush(stderr);
1310#endif	/* PERMSFILE */
1311
1312  setuid(thisuser.pw_uid);
1313
1314  /* destroy environment unless user has asked to preserve it */
1315  if (!pflag)
1316    environ = envinit;
1317  setenv("HOME", thisuser.pw_dir, 1);
1318  setenv("SHELL", thisuser.pw_shell, 1);
1319
1320  if (chdir(thisuser.pw_dir) < 0) {
1321#if DEBUG
1322    syslog(LOG_DEBUG, "chdir(%s): %s(%d)", thisuser.pw_dir, strerror(errno),
1323	   errno);
1324#endif /* DEBUG */
1325    if (chdir("/") < 0) {
1326      printf("No directory!\n");
1327      invalid = TRUE;
1328    } else {
1329      printf("No directory! %s\n", "Logging in with HOME=/");
1330      strcpy(thisuser.pw_dir, "/");
1331    }
1332  }
1333
1334  if (!term[0]) {
1335#if HAVE_GETTTYNAM
1336/*
1337 * The getttynam() call and the ttyent structure first appeared in 4.3 BSD.
1338 * They are not portable to System V systems such as Solaris 2.x.
1339 *         rja
1340 */
1341  register struct ttyent *t;
1342  register char *c;
1343
1344  if (c = strrchr(tty, '/'))
1345    c++;
1346  else
1347    c = tty;
1348
1349  if (t = getttynam(c))
1350    opiestrncpy(term, t->ty_type, sizeof(term));
1351  else
1352#endif /* HAVE_GETTTYNAM */
1353    strcpy(term, "unknown");
1354  }
1355
1356  setenv("USER", name, 1);
1357  setenv("LOGNAME", name, 1);
1358  setenv("PATH", DEFAULT_PATH, 0);
1359  if (term[0]) {
1360#ifdef DEBUG
1361    syslog(LOG_DEBUG, "setting TERM=%s", term);
1362#endif	/* DEBUG */
1363    setenv("TERM", term, 1);
1364  }
1365
1366#ifdef HAVE_LOGIN_ENVFILE
1367  {
1368    FILE *f;
1369
1370    if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) {
1371      char line[128], *c, *c2;
1372
1373      while(fgets(line, sizeof(line)-1, f)) {
1374	c = line;
1375	while(*c && (isalnum(*c) || (*c == '_'))) c++;
1376	  if (*c == '=') {
1377	    *(c++) = 0;
1378	    if (c2 = strchr(c, ';'))
1379	      *c2 = 0;
1380	    if (c2 = strchr(c, '\n'))
1381	      *c2 = 0;
1382	    if (c2 = strchr(c, ' '))
1383	      continue;
1384	    if (c2 = strchr(c, '\t'))
1385	      continue;
1386	    if (setenv(line, c, 0) < 0) {
1387	      fprintf(stderr, "setenv() failed -- environment full?\n");
1388	      break;
1389	    }
1390	  }
1391        }
1392      fclose(f);
1393    }
1394  }
1395#endif /* HAVE_LOGIN_ENVFILE */
1396
1397  if ((namep = strrchr(thisuser.pw_shell, '/')) == NULL)
1398    namep = thisuser.pw_shell;
1399  else
1400    namep++;
1401  strcat(minusnam, namep);
1402  if (tty[sizeof("tty") - 1] == 'd')
1403    syslog(LOG_INFO, "DIALUP %s, %s", tty, name);
1404  if (!thisuser.pw_uid)
1405    if (host[0])
1406      syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", tty, HMAX, host);
1407    else
1408      syslog(LOG_NOTICE, "ROOT LOGIN %s", tty);
1409#if !HAVE_MOTD_IN_PROFILE
1410  if (!quietlog) {
1411    FILE *mf;
1412    register c;
1413
1414    signal(SIGINT, catch);
1415    if ((mf = fopen(MOTD_FILE, "r")) != NULL) {
1416      while ((c = getc(mf)) != EOF && !stopmotd)
1417	putchar(c);
1418      fclose(mf);
1419    }
1420    signal(SIGINT, SIG_IGN);
1421  }
1422#endif /* !HAVE_MOTD_IN_PROFILE */
1423#if !HAVE_MAILCHECK_IN_PROFILE
1424  if (!quietlog) {
1425    struct stat st;
1426    char buf[128];
1427    int len;
1428
1429    opiestrncpy(buf, PATH_MAIL, sizeof(buf) - 2);
1430
1431    len = strlen(buf);
1432    if (*(buf + len - 1) != '/') {
1433	*(buf + len) = '/';
1434	*(buf + len + 1) = 0;
1435    }
1436
1437    strcat(buf, name);
1438#if DEBUG
1439    syslog(LOG_DEBUG, "statting %s", buf);
1440#endif /* DEBUG */
1441    if (!stat(buf, &st) && st.st_size)
1442      printf("You have %smail.\n",
1443	     (st.st_mtime > st.st_atime) ? "new " : "");
1444  }
1445#endif /* !HAVE_MAILCHECK_IN_PROFILE */
1446  signal(SIGALRM, SIG_DFL);
1447  signal(SIGQUIT, SIG_DFL);
1448  signal(SIGINT, SIG_DFL);
1449  signal(SIGTSTP, SIG_IGN);
1450
1451  attr.c_lflag |= (ISIG | IEXTEN);
1452
1453  catchexit();
1454  execlp(thisuser.pw_shell, minusnam, 0);
1455  perror(thisuser.pw_shell);
1456  printf("No shell\n");
1457  exit(0);
1458}
1459