1/* opielogin.c: The infamous /bin/login
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5Reserved. The Inner Net License Version 2 applies to these portions of
6the software.
7You should have received a copy of the license with this software. If
8you didn't get a copy, you may request one from <license@inner.net>.
9
10Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11McDonald, All Rights Reserved. All Rights under this copyright are assigned
12to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13License Agreement applies to this software.
14
15	History:
16
17	Modified by cmetz for OPIE 2.4. Omit "/dev/" in lastlog entry.
18		Don't chdir for invalid users. Fixed bug where getloginname()
19		didn't actually change spaces to underscores. Use struct
20		opie_key for key blocks. Do the home directory chdir() after
21		doing the setuid() in case we're on superuser-mapped NFS.
22		Initialize some variables explicitly. Call opieverify() if
23		login times out. Use opiestrncpy().
24	Modified by cmetz for OPIE 2.32. Partially handle environment
25		variables on the command line (a better implementation is
26		coming soon). Handle failure to issue a challenge more
27		gracefully.
28	Modified by cmetz for OPIE 2.31. Use _PATH_NOLOGIN. Move Solaris
29	        drain bamage kluge after rflag check; it breaks rlogin.
30		Use TCSAFLUSH instead of TCSANOW (except where it flushes
31		data we need). Sleep before kluging for Solaris.
32	Modified by cmetz for OPIE 2.3. Process login environment files.
33	        Made logindevperm/fbtab handling more generic. Kluge around
34                Solaris drain bamage differently (maybe better?). Maybe
35		allow cleartext logins even when opiechallenge() fails.
36		Changed the conditions on when time.h and sys/time.h are
37		included. Send debug info to syslog. Use opielogin() instead
38		of dealing with utmp/setlogin() here.
39	Modified by cmetz for OPIE 2.22. Call setlogin(). Decreased default
40	        timeout to two minutes. Use opiereadpass() flags to get
41		around Solaris drain bamage.
42	Modified by cmetz for OPIE 2.21. Took the sizeof() the wrong thing.
43        Modified by cmetz for OPIE 2.2. Changed prompts to ask for OTP
44                response where appropriate. Simple though small speed-up.
45                Don't allow cleartext if echo on. Don't try to clear
46                non-blocking I/O. Use opiereadpass(). Don't mess with
47                termios (as much, at least) -- that's opiereadpass()'s
48                job. Change opiereadpass() calls to add echo arg. Fixed
49                CONTROL macro. Don't modify argv (at least, unless
50                we have a reason to). Allow user in if ruserok() says
51                so. Removed useless strings (I don't think that
52                removing the ucb copyright one is a problem -- please
53                let me know if I'm wrong). Use FUNCTION declaration et
54                al. Moved definition of TRUE here. Ifdef around more
55                headers. Make everything static. Removed support for
56                omitting domain name if same domain -- it generally
57                didn't work and it would be a big portability problem.
58                Use opiereadpass() in getloginname() and then post-
59                process. Added code to grab hpux time zone from
60                /etc/src.sh. Renamed MAIL_DIR to PATH_MAIL. Removed
61                dupe catchexit and extraneous closelog. openlog() as
62                soon as possible because SunOS syslog is broken.
63                Don't print an extra blank line before a new Response
64                prompt.
65        Modified at NRL for OPIE 2.2. Changed strip_crlf to stripcrlf.
66                Do opiebackspace() on entries.
67	Modified at NRL for OPIE 2.1. Since we don't seem to use the
68	        result of opiechallenge() anymore, discard it. Changed
69		BSD4_3 to HAVE_GETTTYNAM. Other symbol changes for
70		autoconf. Removed obselete usage comment. Removed
71		des_crypt.h. File renamed to opielogin.c. Added bletch
72		for setpriority. Added slash between MAIL_DIR and name.
73        Modified at NRL for OPIE 2.02. Flush stdio after printing login
74                prompt. Fixed Solaris shadow password problem introduced
75                in OPIE 2.01 (the shadow password structure is spwd, not
76                spasswd).
77        Modified at NRL for OPIE 2.01. Changed password lookup handling
78                to use a static structure to avoid problems with drain-
79                bamaged shadow password packages. Make sure to close
80                syslog by function to avoid problems with drain bamaged
81                syslog implementations. Log a few interesting errors.
82	Modified at NRL for OPIE 2.0.
83	Modified at Bellcore for the Bellcore S/Key Version 1 software
84		distribution.
85	Originally from BSD.
86*/
87/*
88 * Portions of this software are
89 * Copyright (c) 1980,1987 Regents of the University of California.
90 * All rights reserved.  The Berkeley software License Agreement
91 * specifies the terms and conditions for redistribution.
92 */
93
94#include "opie_cfg.h"	/* OPIE: defines symbols for filenames & pathnames */
95#if HAVE_SYS_PARAM_H
96#include <sys/param.h>
97#endif /* HAVE_SYS_PARAM_H */
98#include <sys/stat.h>
99#include <sys/types.h>
100
101#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
102#include <sys/resource.h>
103#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
104
105#if TIME_WITH_SYS_TIME
106# include <sys/time.h>
107# include <time.h>
108#else /* TIME_WITH_SYS_TIME */
109#if HAVE_SYS_TIME_H
110#include <sys/time.h>
111#else /* HAVE_SYS_TIME_H */
112#include <time.h>
113#endif /* HAVE_SYS_TIME_H */
114#endif /* TIME_WITH_SYS_TIME */
115
116#if HAVE_SYS_FILE_H
117#include <sys/file.h>
118#endif /* HAVE_SYS_FILE_H */
119#include <signal.h>
120#if HAVE_PWD_H
121#include <pwd.h>	/* POSIX Password routines */
122#endif /* HAVE_PWD_H */
123#include <stdio.h>
124#include <errno.h>
125#if HAVE_UNISTD_H
126#include <unistd.h>	/* Basic POSIX macros and functions */
127#endif /* HAVE_UNISTD_H */
128#include <termios.h>	/* POSIX terminal I/O */
129#if HAVE_STRING_H
130#include <string.h>	/* ANSI C string functions */
131#endif /* HAVE_STRING_H */
132#include <fcntl.h>	/* File I/O functions */
133#include <syslog.h>
134#include <grp.h>
135#include <netdb.h>
136#include <netinet/in.h>	/* contains types needed for next include file */
137#include <arpa/inet.h>	/* Inet addr<-->ascii functions */
138#if HAVE_STDLIB_H
139#include <stdlib.h>
140#endif /* HAVE_STDLIB_H */
141#if HAVE_SYS_SELECT_H
142#include <sys/select.h>
143#endif /* HAVE_SYS_SELECT_H */
144
145#ifdef	QUOTA
146#include <sys/quota.h>
147#endif
148
149#if HAVE_GETTTYNAM
150#include <sys/ioctl.h>	/* non-portable routines used only a few places */
151#include <ttyent.h>
152#endif /* HAVE_GETTTYNAM */
153
154#include "opie.h"
155
156#define TTYGID(gid)	tty_gid(gid)	/* gid that owns all ttys */
157
158#define NMAX	32
159#define HMAX	256
160
161#if HAVE_LASTLOG_H
162#include <lastlog.h>
163#endif /* HAVE_LASTLOG_H */
164
165static int rflag = 0;
166static int usererr = -1;
167static int stopmotd = 0;
168static char rusername[NMAX + 1];
169static char name[NMAX + 1] = "";
170static char minusnam[16] = "-";
171static char *envinit[1];	/* now set by setenv calls */
172static char term[64] = "";	/* important to initialise to a NULL string */
173static char host[HMAX + 1] = "";
174static struct passwd nouser;
175static struct passwd thisuser;
176
177#if HAVE_SHADOW_H
178#include <shadow.h>
179#endif /* HAVE_SHADOW_H */
180
181static char *ttyprompt;
182
183#ifdef PERMSFILE
184extern char *home;
185#endif	/* PERMSFILE */
186
187static struct termios attr;
188
189extern int errno;
190
191static int ouroptind;
192static char *ouroptarg;
193
194#if HAVE_LASTLOG_H
195#ifndef _PATH_LASTLOG
196#define _PATH_LASTLOG "/var/adm/lastlog"
197#endif /* _PATH_LASTLOG */
198
199static char lastlog[] = _PATH_LASTLOG;
200#endif /* HAVE_LASTLOG_H */
201
202/*
203 * The "timeout" variable bounds the time given to login.
204 * We initialize it here for safety and so that it can be
205 * patched on machines where the default value is not appropriate.
206 */
207static int timeout = 120;
208
209static void getstr __P((char *, int, char *));
210
211#if HAVE_CRYPT_H
212#include <crypt.h>
213#endif /* HAVE_CRYPT_H */
214
215#undef TRUE
216#define TRUE -1
217
218static int need_opieverify = 0;
219static struct opie opie;
220
221#ifdef TIOCSWINSZ
222/* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is
223available on BSDish systems and at least Solaris 2.x, but portability to
224other systems is questionable. Use within this source code module is
225protected by suitable defines.
226
227I'd be interested in hearing about a more portable approach. rja */
228
229static struct winsize win = {0, 0, 0, 0};
230#endif
231
232
233/*------------------ BEGIN REAL CODE --------------------------------*/
234
235/* We allow the malloc()s to potentially leak data out because we can
236only call this routine about four times in the lifetime of this process
237and the kernel will free all heap memory when we exit or exec. */
238static int lookupuser FUNCTION_NOARGS
239{
240  struct passwd *pwd;
241#if HAVE_SHADOW
242  struct spwd *spwd;
243#endif /* HAVE_SHADOW */
244
245  memcpy(&thisuser, &nouser, sizeof(thisuser));
246
247  if (!(pwd = getpwnam(name)))
248    return -1;
249
250  thisuser.pw_uid = pwd->pw_uid;
251  thisuser.pw_gid = pwd->pw_gid;
252
253  if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1)))
254    goto lookupuserbad;
255  strcpy(thisuser.pw_name, pwd->pw_name);
256
257  if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1)))
258    goto lookupuserbad;
259  strcpy(thisuser.pw_dir, pwd->pw_dir);
260
261  if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1)))
262    goto lookupuserbad;
263  strcpy(thisuser.pw_shell, pwd->pw_shell);
264
265#if HAVE_SHADOW
266  if (!(spwd = getspnam(name)))
267	goto lookupuserbad;
268
269  pwd->pw_passwd = spwd->sp_pwdp;
270
271  endspent();
272#endif /* HAVE_SHADOW */
273
274  if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1)))
275    goto lookupuserbad;
276  strcpy(thisuser.pw_passwd, pwd->pw_passwd);
277
278  endpwent();
279
280  return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#'));
281
282lookupuserbad:
283  memcpy(&thisuser, &nouser, sizeof(thisuser));
284  return -1;
285}
286
287static VOIDRET getloginname FUNCTION_NOARGS
288{
289  char *namep, d;
290  int flags;
291  static int first = 1;
292
293  memset(name, 0, sizeof(name));
294
295  d = 0;
296  while (name[0] == '\0') {
297    flags = 1;
298    if (ttyprompt) {
299      if (first) {
300	flags = 4;
301	first--;
302      } else
303	printf(ttyprompt);
304    } else
305      printf("login: ");
306    fflush(stdout);
307    if (++d == 3)
308      exit(0);
309    if (!opiereadpass(name, sizeof(name)-1, flags)) {
310      syslog(LOG_CRIT, "End-of-file (or other error?) on stdin!");
311      exit(0);
312    }
313    for (namep = name; *namep; namep++) {
314      if (*namep == ' ')
315        *namep = '_';
316    }
317  }
318}
319
320static VOIDRET timedout FUNCTION((i), int i)
321{
322  /* input variable declared just to keep the compiler quiet */
323  printf("Login timed out after %d seconds\n", timeout);
324  syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout);
325
326  if (need_opieverify)
327    opieverify(&opie, NULL);
328
329  exit(0);
330}
331
332#if !HAVE_MOTD_IN_PROFILE
333static VOIDRET catch FUNCTION((i), int i)
334{
335  /* the input variable is declared to keep the compiler quiet */
336  signal(SIGINT, SIG_IGN);
337  stopmotd++;
338}
339#endif /* !HAVE_MOTD_IN_PROFILE */
340
341static VOIDRET catchexit FUNCTION_NOARGS
342{
343  int i;
344  tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr);
345  putchar('\n');
346  closelog();
347  for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
348    close(i);
349}
350
351static int rootterm FUNCTION((ttyn), char *ttyn)
352{
353#if HAVE_GETTTYNAM
354/* The getttynam() call and the ttyent structure first appeared in 4.3 BSD and
355are not portable to System V systems such as Solaris 2.x. or modern versions
356of IRIX rja */
357  register struct ttyent *t;
358  char *tty;
359
360  tty = strrchr(ttyn, '/');
361
362  if (tty == NULL)
363    tty = ttyn;
364  else
365    tty++;
366
367  if ((t = getttynam(tty)) != NULL)
368    return (t->ty_status & TTY_SECURE);
369
370  return (1);	/* when in doubt, allow root logins */
371
372#elif HAVE_ETC_DEFAULT_LOGIN
373
374  FILE *filno;
375  char line[128];
376  char *next, *next2;
377
378/* SVR4 only permits two security modes for root logins: 1) only from CONSOLE,
379if the string "CONSOLE=/dev/console" exists and is not commented out with "#"
380characters, or 2) from anywhere.
381
382So we open /etc/default/login file grab the file contents one line at a time
383verify that the line being tested isn't commented out check for the substring
384"CONSOLE" and decide whether to permit this attempted root login/su. */
385
386  if ((filno = fopen("/etc/default/login", "r")) != NULL) {
387    while (fgets(line, 128, filno) != NULL) {
388      next = line;
389
390      if ((line[0] != '#') && (next = strstr(line, "CONSOLE"))) {
391	next += 7;	/* get past the string "CONSOLE" */
392
393	while (*next && (*next == ' ') || (*next == '\t'))
394	  next++;
395
396	if (*(next++) != '=')
397	  break;	/* some weird character, get next line */
398
399	next2 = next;
400	while (*next2 && (*next2 != '\t') && (*next2 != ' ') &&
401	       (*next2 != '\n'))
402	  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