opielogin.c revision 59118
1/* opielogin.c: The infamous /bin/login
2
3%%% portions-copyright-cmetz-96
4Portions of this software are Copyright 1996-1998 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.32. Partially handle environment
18		variables on the command line (a better implementation is
19		coming soon). Handle failure to issue a challenge more
20		gracefully.
21	Modified by cmetz for OPIE 2.31. Use _PATH_NOLOGIN. Move Solaris
22	        drain bamage kluge after rflag check; it breaks rlogin.
23		Use TCSAFLUSH instead of TCSANOW (except where it flushes
24		data we need). Sleep before kluging for Solaris.
25	Modified by cmetz for OPIE 2.3. Process login environment files.
26	        Made logindevperm/fbtab handling more generic. Kluge around
27                Solaris drain bamage differently (maybe better?). Maybe
28		allow cleartext logins even when opiechallenge() fails.
29		Changed the conditions on when time.h and sys/time.h are
30		included. Send debug info to syslog. Use opielogin() instead
31		of dealing with utmp/setlogin() here.
32	Modified by cmetz for OPIE 2.22. Call setlogin(). Decreased default
33	        timeout to two minutes. Use opiereadpass() flags to get
34		around Solaris drain bamage.
35	Modified by cmetz for OPIE 2.21. Took the sizeof() the wrong thing.
36        Modified by cmetz for OPIE 2.2. Changed prompts to ask for OTP
37                response where appropriate. Simple though small speed-up.
38                Don't allow cleartext if echo on. Don't try to clear
39                non-blocking I/O. Use opiereadpass(). Don't mess with
40                termios (as much, at least) -- that's opiereadpass()'s
41                job. Change opiereadpass() calls to add echo arg. Fixed
42                CONTROL macro. Don't modify argv (at least, unless
43                we have a reason to). Allow user in if ruserok() says
44                so. Removed useless strings (I don't think that
45                removing the ucb copyright one is a problem -- please
46                let me know if I'm wrong). Use FUNCTION declaration et
47                al. Moved definition of TRUE here. Ifdef around more
48                headers. Make everything static. Removed support for
49                omitting domain name if same domain -- it generally
50                didn't work and it would be a big portability problem.
51                Use opiereadpass() in getloginname() and then post-
52                process. Added code to grab hpux time zone from
53                /etc/src.sh. Renamed MAIL_DIR to PATH_MAIL. Removed
54                dupe catchexit and extraneous closelog. openlog() as
55                soon as possible because SunOS syslog is broken.
56                Don't print an extra blank line before a new Response
57                prompt.
58        Modified at NRL for OPIE 2.2. Changed strip_crlf to stripcrlf.
59                Do opiebackspace() on entries.
60	Modified at NRL for OPIE 2.1. Since we don't seem to use the
61	        result of opiechallenge() anymore, discard it. Changed
62		BSD4_3 to HAVE_GETTTYNAM. Other symbol changes for
63		autoconf. Removed obselete usage comment. Removed
64		des_crypt.h. File renamed to opielogin.c. Added bletch
65		for setpriority. Added slash between MAIL_DIR and name.
66        Modified at NRL for OPIE 2.02. Flush stdio after printing login
67                prompt. Fixed Solaris shadow password problem introduced
68                in OPIE 2.01 (the shadow password structure is spwd, not
69                spasswd).
70        Modified at NRL for OPIE 2.01. Changed password lookup handling
71                to use a static structure to avoid problems with drain-
72                bamaged shadow password packages. Make sure to close
73                syslog by function to avoid problems with drain bamaged
74                syslog implementations. Log a few interesting errors.
75	Modified at NRL for OPIE 2.0.
76	Modified at Bellcore for the Bellcore S/Key Version 1 software
77		distribution.
78	Originally from BSD.
79*/
80/*
81 * Portions of this software are
82 * Copyright (c) 1980,1987 Regents of the University of California.
83 * All rights reserved.  The Berkeley software License Agreement
84 * specifies the terms and conditions for redistribution.
85 */
86
87#include "opie_cfg.h"	/* OPIE: defines symbols for filenames & pathnames */
88#if HAVE_SYS_PARAM_H
89#include <sys/param.h>
90#endif /* HAVE_SYS_PARAM_H */
91#include <sys/stat.h>
92#include <sys/types.h>
93
94#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
95#include <sys/resource.h>
96#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
97
98#if TIME_WITH_SYS_TIME
99# include <sys/time.h>
100# include <time.h>
101#else /* TIME_WITH_SYS_TIME */
102#if HAVE_SYS_TIME_H
103#include <sys/time.h>
104#else /* HAVE_SYS_TIME_H */
105#include <time.h>
106#endif /* HAVE_SYS_TIME_H */
107#endif /* TIME_WITH_SYS_TIME */
108
109#if HAVE_SYS_FILE_H
110#include <sys/file.h>
111#endif /* HAVE_SYS_FILE_H */
112#include <signal.h>
113#if HAVE_PWD_H
114#include <pwd.h>	/* POSIX Password routines */
115#endif /* HAVE_PWD_H */
116#include <stdio.h>
117#include <errno.h>
118#if HAVE_UNISTD_H
119#include <unistd.h>	/* Basic POSIX macros and functions */
120#endif /* HAVE_UNISTD_H */
121#include <termios.h>	/* POSIX terminal I/O */
122#if HAVE_STRING_H
123#include <string.h>	/* ANSI C string functions */
124#endif /* HAVE_STRING_H */
125#include <fcntl.h>	/* File I/O functions */
126#include <syslog.h>
127#include <grp.h>
128#include <netdb.h>
129#include <netinet/in.h>	/* contains types needed for next include file */
130#include <arpa/inet.h>	/* Inet addr<-->ascii functions */
131#if HAVE_STDLIB_H
132#include <stdlib.h>
133#endif /* HAVE_STDLIB_H */
134#if HAVE_SYS_SELECT_H
135#include <sys/select.h>
136#endif /* HAVE_SYS_SELECT_H */
137
138#ifdef	QUOTA
139#include <sys/quota.h>
140#endif
141
142#if HAVE_GETTTYNAM
143#include <sys/ioctl.h>	/* non-portable routines used only a few places */
144#include <ttyent.h>
145#endif /* HAVE_GETTTYNAM */
146
147#include "opie.h"
148
149#define TTYGID(gid)	tty_gid(gid)	/* gid that owns all ttys */
150
151#define NMAX	32
152#define HMAX	256
153
154#if HAVE_LASTLOG_H
155#include <lastlog.h>
156#endif /* HAVE_LASTLOG_H */
157
158static int rflag = 0;
159static int usererr = -1;
160static int stopmotd;
161static char rusername[NMAX + 1];
162static char name[NMAX + 1] = "";
163static char minusnam[16] = "-";
164static char *envinit[1];	/* now set by setenv calls */
165static char term[64] = "\0";	/* important to initialise to a NULL string */
166static char host[HMAX + 1] = "\0";
167static struct passwd nouser;
168static struct passwd thisuser;
169
170#if HAVE_SHADOW_H
171#include <shadow.h>
172#endif /* HAVE_SHADOW_H */
173
174static char *ttyprompt;
175
176#ifdef PERMSFILE
177extern char *home;
178#endif	/* PERMSFILE */
179
180static struct termios attr;
181
182extern int errno;
183
184static int ouroptind;
185static char *ouroptarg;
186
187#if HAVE_LASTLOG_H
188#ifndef _PATH_LASTLOG
189#define _PATH_LASTLOG "/var/adm/lastlog"
190#endif /* _PATH_LASTLOG */
191
192static char lastlog[] = _PATH_LASTLOG;
193#endif /* HAVE_LASTLOG_H */
194
195/*
196 * The "timeout" variable bounds the time given to login.
197 * We initialize it here for safety and so that it can be
198 * patched on machines where the default value is not appropriate.
199 */
200static int timeout = 120;
201
202static void getstr __P((char *, int, char *));
203
204#if HAVE_CRYPT_H
205#include <crypt.h>
206#endif /* HAVE_CRYPT_H */
207
208#undef TRUE
209#define TRUE -1
210
211#ifdef TIOCSWINSZ
212/* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is
213available on BSDish systems and at least Solaris 2.x, but portability to
214other systems is questionable. Use within this source code module is
215protected by suitable defines.
216
217I'd be interested in hearing about a more portable approach. rja */
218
219static struct winsize win = {0, 0, 0, 0};
220#endif
221
222
223/*------------------ BEGIN REAL CODE --------------------------------*/
224
225/* We allow the malloc()s to potentially leak data out because we can
226only call this routine about four times in the lifetime of this process
227and the kernel will free all heap memory when we exit or exec. */
228static int lookupuser FUNCTION_NOARGS
229{
230  struct passwd *pwd;
231#if HAVE_SHADOW
232  struct spwd *spwd;
233#endif /* HAVE_SHADOW */
234
235  memcpy(&thisuser, &nouser, sizeof(thisuser));
236
237  if (!(pwd = getpwnam(name)))
238    return -1;
239
240  thisuser.pw_uid = pwd->pw_uid;
241  thisuser.pw_gid = pwd->pw_gid;
242
243  if (!(thisuser.pw_name = malloc(strlen(pwd->pw_name) + 1)))
244    goto lookupuserbad;
245  strcpy(thisuser.pw_name, pwd->pw_name);
246
247  if (!(thisuser.pw_dir = malloc(strlen(pwd->pw_dir) + 1)))
248    goto lookupuserbad;
249  strcpy(thisuser.pw_dir, pwd->pw_dir);
250
251  if (!(thisuser.pw_shell = malloc(strlen(pwd->pw_shell) + 1)))
252    goto lookupuserbad;
253  strcpy(thisuser.pw_shell, pwd->pw_shell);
254
255#if HAVE_SHADOW
256  if (!(spwd = getspnam(name)))
257	goto lookupuserbad;
258
259  pwd->pw_passwd = spwd->sp_pwdp;
260
261  endspent();
262#endif /* HAVE_SHADOW */
263
264  if (!(thisuser.pw_passwd = malloc(strlen(pwd->pw_passwd) + 1)))
265    goto lookupuserbad;
266  strcpy(thisuser.pw_passwd, pwd->pw_passwd);
267
268  endpwent();
269
270  return ((thisuser.pw_passwd[0] == '*') || (thisuser.pw_passwd[0] == '#'));
271
272lookupuserbad:
273  memcpy(&thisuser, &nouser, sizeof(thisuser));
274  return -1;
275}
276
277static VOIDRET getloginname FUNCTION_NOARGS
278{
279  register char *namep;
280  char c, d;
281  int flags;
282  static int first = 1;
283
284  memset(name, 0, sizeof(name));
285
286  d = 0;
287  while (name[0] == '\0') {
288    flags = 1;
289    if (ttyprompt) {
290      if (first) {
291	flags = 4;
292	first--;
293      } else
294	printf(ttyprompt);
295    } else
296      printf("login: ");
297    fflush(stdout);
298    if (++d == 3)
299      exit(0);
300    if (!opiereadpass(name, sizeof(name)-1, flags)) {
301      syslog(LOG_CRIT, "End-of-file (or other error?) on stdin!");
302      exit(0);
303    }
304    for (namep = name; *namep; namep++) {
305        if (c == ' ')
306          c = '_';
307    }
308  }
309}
310
311static VOIDRET timedout FUNCTION((i), int i)
312{
313  /* input variable declared just to keep the compiler quiet */
314  printf("Login timed out after %d seconds\n", timeout);
315  syslog(LOG_CRIT, "Login timed out after %d seconds!", timeout);
316  exit(0);
317}
318
319#if !HAVE_MOTD_IN_PROFILE
320static VOIDRET catch FUNCTION((i), int i)
321{
322  /* the input variable is declared to keep the compiler quiet */
323  signal(SIGINT, SIG_IGN);
324  stopmotd++;
325}
326#endif /* !HAVE_MOTD_IN_PROFILE */
327
328static VOIDRET catchexit FUNCTION_NOARGS
329{
330  int i;
331  tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr);
332  putchar('\n');
333  closelog();
334  for (i = sysconf(_SC_OPEN_MAX); i > 2; i--)
335    close(i);
336}
337
338static int rootterm FUNCTION((ttyn), char *ttyn)
339{
340#if HAVE_GETTTYNAM
341/* The getttynam() call and the ttyent structure first appeared in 4.3 BSD and
342are not portable to System V systems such as Solaris 2.x. or modern versions
343of IRIX rja */
344  register struct ttyent *t;
345  char *tty;
346
347  tty = strrchr(ttyn, '/');
348
349  if (tty == NULL)
350    tty = ttyn;
351  else
352    tty++;
353
354  if ((t = getttynam(tty)) != NULL)
355    return (t->ty_status & TTY_SECURE);
356
357  return (1);	/* when in doubt, allow root logins */
358
359#elif HAVE_ETC_DEFAULT_LOGIN
360
361  FILE *filno;
362  char line[128];
363  char *next, *next2;
364
365/* SVR4 only permits two security modes for root logins: 1) only from CONSOLE,
366if the string "CONSOLE=/dev/console" exists and is not commented out with "#"
367characters, or 2) from anywhere.
368
369So we open /etc/default/login file grab the file contents one line at a time
370verify that the line being tested isn't commented out check for the substring
371"CONSOLE" and decide whether to permit this attempted root login/su. */
372
373  if ((filno = fopen("/etc/default/login", "r")) != NULL) {
374    while (fgets(line, 128, filno) != NULL) {
375      next = line;
376
377      if ((line[0] != '#') && (next = strstr(line, "CONSOLE"))) {
378	next += 7;	/* get past the string "CONSOLE" */
379
380	while (*next && (*next == ' ') || (*next == '\t'))
381	  next++;
382
383	if (*(next++) != '=')
384	  break;	/* some weird character, get next line */
385
386	next2 = next;
387	while (*next2 && (*next2 != '\t') && (*next2 != ' ') &&
388	       (*next2 != '\n'))
389	  next2++;
390	*next2 = 0;
391
392	return !strcmp(ttyn, next);	/* Allow the login if and only if the
393					   user's terminal line matches the
394					   setting for CONSOLE */
395      }
396    }	/* end while another line could be obtained */
397  }	/* end if could open file */
398  return (1);	/* when no CONSOLE line exists, root can login from anywhere */
399#elif HAVE_SECURETTY
400  {
401    FILE *f;
402    char buffer[1024], *c;
403    int rc = 0;
404
405    if (!(f = fopen("/etc/securetty", "r")))
406      return 1;
407
408    if (c = strstr(ttyn, "/dev/"))
409      ttyn += 5;
410
411    if (c = strrchr(ttyn, '/'))
412      ttyn = ++c;
413
414    while (fgets(buffer, sizeof(buffer), f)) {
415      if (c = strrchr(buffer, '\n'))
416	*c = 0;
417
418      if (!(c = strrchr(buffer, '/')))
419	c = buffer;
420      else
421	c++;
422
423      if (!strcmp(c, ttyn))
424	rc = 1;
425    };
426
427    fclose(f);
428    return rc;
429  }
430#else
431  return (1);	/* when in doubt, allow root logins */
432#endif
433}
434
435static int doremotelogin FUNCTION((host), char *host)
436{
437  int rc;
438
439  getstr(rusername, sizeof(rusername), "remuser");
440  getstr(name, sizeof(name), "locuser");
441  getstr(term, sizeof(term), "Terminal type");
442  if (getuid()) {
443    memcpy(&thisuser, &nouser, sizeof(thisuser));
444    syslog(LOG_ERR, "getuid() failed");
445    return (-1);
446  }
447  if (lookupuser()) {
448    syslog(LOG_ERR, "lookup failed for user %s", name);
449    return (-1);
450  }
451  rc = ruserok(host, !thisuser.pw_uid, rusername, name);
452  if (rc == -1) {
453    syslog(LOG_ERR,
454    "ruserok failed, host=%s, uid=%d, remote username=%s, local username=%s",
455	   host, thisuser.pw_uid, rusername, name);
456  }
457  return rc;
458}
459
460
461static VOIDRET getstr FUNCTION((buf, cnt, err), char *buf AND int cnt AND char *err)
462{
463  char c;
464
465  do {
466    if (read(0, &c, 1) != 1)
467      exit(1);
468    if (--cnt < 0) {
469      printf("%s too long\r\n", err);
470      syslog(LOG_CRIT, "%s too long", err);
471      exit(1);
472    }
473    *buf++ = c;
474  }
475  while ((c != 0) && (c != '~'));
476}
477
478struct speed_xlat {
479  char *c;
480  int i;
481}          speeds[] = {
482
483#ifdef B0
484  {
485    "0", B0
486  },
487#endif	/* B0 */
488#ifdef B50
489  {
490    "50", B50
491  },
492#endif	/* B50 */
493#ifdef B75
494  {
495    "75", B75
496  },
497#endif	/* B75 */
498#ifdef B110
499  {
500    "110", B110
501  },
502#endif	/* B110 */
503#ifdef B134
504  {
505    "134", B134
506  },
507#endif	/* B134 */
508#ifdef B150
509  {
510    "150", B150
511  },
512#endif	/* B150 */
513#ifdef B200
514  {
515    "200", B200
516  },
517#endif	/* B200 */
518#ifdef B300
519  {
520    "300", B300
521  },
522#endif	/* B300 */
523#ifdef B600
524  {
525    "600", B600
526  },
527#endif	/* B600 */
528#ifdef B1200
529  {
530    "1200", B1200
531  },
532#endif	/* B1200 */
533#ifdef B1800
534  {
535    "1800", B1800
536  },
537#endif	/* B1800 */
538#ifdef B2400
539  {
540    "2400", B2400
541  },
542#endif	/* B2400 */
543#ifdef B4800
544  {
545    "4800", B4800
546  },
547#endif	/* B4800 */
548#ifdef B7200
549  {
550    "7200", B7200
551  },
552#endif	/* B7200 */
553#ifdef B9600
554  {
555    "9600", B9600
556  },
557#endif	/* B9600 */
558#ifdef B14400
559  {
560    "14400", B14400
561  },
562#endif	/* B14400 */
563#ifdef B19200
564  {
565    "19200", B19200
566  },
567#endif	/* B19200 */
568#ifdef B28800
569  {
570    "28800", B28800
571  },
572#endif	/* B28800 */
573#ifdef B38400
574  {
575    "38400", B38400
576  },
577#endif	/* B38400 */
578#ifdef B57600
579  {
580    "57600", B57600
581  },
582#endif	/* B57600 */
583#ifdef B115200
584  {
585    "115200", B115200
586  },
587#endif	/* B115200 */
588#ifdef B230400
589  {
590    "230400", B230400
591  },
592#endif	/* 230400 */
593  {
594    NULL, 0
595  }
596};
597
598static VOIDRET doremoteterm FUNCTION((term), char *term)
599{
600  register char *cp = strchr(term, '/');
601  char *speed;
602  struct speed_xlat *x;
603
604  if (cp) {
605    *cp++ = '\0';
606    speed = cp;
607    cp = strchr(speed, '/');
608    if (cp)
609      *cp++ = '\0';
610    for (x = speeds; x->c != NULL; x++)
611      if (strcmp(x->c, speed) == 0) {
612	cfsetispeed(&attr, x->i);
613	cfsetospeed(&attr, x->i);
614	break;
615      }
616  }
617}
618
619static int tty_gid FUNCTION((default_gid), int default_gid)
620{
621  struct group *gr;
622  int gid = default_gid;
623
624  gr = getgrnam(TTYGRPNAME);
625  if (gr != (struct group *) 0)
626    gid = gr->gr_gid;
627  endgrent();
628  return (gid);
629}
630
631int main FUNCTION((argc, argv), int argc AND char *argv[])
632{
633  extern char **environ;
634  register char *namep;
635  struct opie opie;
636
637  int invalid, quietlog;
638  FILE *nlfd;
639  char *tty, host[256];
640  int pflag = 0, hflag = 0, fflag = 0;
641  int t, c;
642  int i;
643  char *p;
644  char opieprompt[OPIE_CHALLENGE_MAX + 1];
645  int af_pwok;
646  int authsok;
647  char *pp;
648  char buf[256];
649  int uid;
650  int opiepassed;
651
652#ifndef DEBUG
653  if (geteuid()) {
654    fprintf(stderr, "This program requires super-user priveleges.\n");
655    exit(1);
656  }
657#endif /* DEBUG */
658
659  for (t = sysconf(_SC_OPEN_MAX); t > 2; t--)
660    close(t);
661
662  openlog("login", LOG_ODELAY, LOG_AUTH);
663
664  /* initialisation */
665  host[0] = '\0';
666  opieprompt[0] = '\0';
667
668  if (p = getenv("TERM")) {
669#ifdef DEBUG
670    syslog(LOG_DEBUG, "environment TERM=%s", p);
671#endif /* DEBUG */
672    strncpy(term, p, sizeof(term));
673  };
674
675  memset(&nouser, 0, sizeof(nouser));
676  nouser.pw_uid = -1;
677  nouser.pw_gid = -1;
678  nouser.pw_passwd = "#nope";
679  nouser.pw_name = nouser.pw_gecos = nouser.pw_dir = nouser.pw_shell = "";
680
681#if HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H
682  setpriority(PRIO_PROCESS, 0, 0);
683#endif /* HAVE_SETPRIORITY && HAVE_SYS_RESOURCE_H */
684
685  signal(SIGALRM, timedout);
686  alarm(timeout);
687  signal(SIGQUIT, SIG_IGN);
688  signal(SIGINT, SIG_IGN);
689
690#if DOTTYPROMPT
691  ttyprompt = (char *) getenv("TTYPROMPT");
692#endif /* TTYPROMPT */
693
694#ifdef	QUOTA
695  quota(Q_SETUID, 0, 0, 0);
696#endif
697
698#ifdef DEBUG
699  {
700    int foo;
701
702    syslog(LOG_DEBUG, "my args are: (argc=%d)", foo = argc);
703    while (--foo)
704      syslog(LOG_DEBUG, "%d: %s", foo, argv[foo]);
705  }
706#endif /* DEBUG */
707
708/* Implement our own getopt()-like functionality, but do so in a much more
709   strict manner to prevent security problems. */
710  for (ouroptind = 1; ouroptind < argc; ouroptind++) {
711    if (!argv[ouroptind])
712      continue;
713
714    if (argv[ouroptind][0] == '-') {
715      char *c = argv[ouroptind] + 1;
716
717      while(*c) {
718	switch(*(c++)) {
719	  case 'd':
720	    if (*c || (++ouroptind == argc))
721	      exit(1);
722
723/*    The '-d' option is apparently a performance hack to get around
724   ttyname() being slow. The potential does exist for it to be used
725   for malice, and it does not seem to be strictly necessary, so we
726   will just eat it. */
727	    break;
728
729	  case 'r':
730	    if (rflag || hflag || fflag) {
731	      fprintf(stderr, "Other options not allowed with -r\n");
732	      exit(1);
733	    }
734
735	    if (*c || (++ouroptind == argc))
736	      exit(1);
737
738	    if (!(ouroptarg = argv[ouroptind]))
739	      exit(1);
740
741	    rflag = -1;
742	    if (!doremotelogin(ouroptarg))
743	      rflag = 1;
744
745	    strncpy(host, ouroptarg, sizeof(host));
746	    break;
747
748	  case 'h':
749	    if (!getuid()) {
750	      if (rflag || hflag || fflag) {
751		fprintf(stderr, "Other options not allowed with -h\n");
752		exit(1);
753	      }
754	      hflag = 1;
755
756	      if (*c || (++ouroptind == argc))
757		exit(1);
758
759	      if (!(ouroptarg = argv[ouroptind]))
760		exit(1);
761
762	      strncpy(host, ouroptarg, sizeof(host));
763	    }
764	    break;
765
766	  case 'f':
767	    if (rflag) {
768	      fprintf(stderr, "Only one of -r and -f allowed\n");
769	      exit(1);
770	    }
771	    fflag = 1;
772
773	    if (*c || (++ouroptind == argc))
774	      exit(1);
775
776	    if (!(ouroptarg = argv[ouroptind]))
777	      exit(1);
778
779	    strncpy(name, ouroptarg, sizeof(name));
780	    break;
781	  case 'p':
782	    pflag = 1;
783	    break;
784	};
785      };
786      continue;
787    };
788
789    if (strchr(argv[ouroptind], '=')) {
790      if (!strncmp(argv[ouroptind], "TERM=", 5)) {
791	strncpy(term, &(argv[ouroptind][5]), sizeof(term));
792	term[sizeof(term) - 1] = 0;
793#ifdef DEBUG
794	syslog(LOG_DEBUG, "passed TERM=%s, ouroptind = %d", term, ouroptind);
795#endif /* DEBUG */
796      } else {
797#ifdef DEBUG
798	syslog(LOG_DEBUG, "eating %s, ouroptind = %d", argv[ouroptind], ouroptind);
799#endif /* DEBUG */
800      };
801      continue;
802    };
803
804    strncpy(name, argv[ouroptind], sizeof(name));
805  };
806
807#ifdef TIOCNXCL
808  /* BSDism:  not sure how to rewrite for POSIX.  rja */
809  ioctl(0, TIOCNXCL, 0);	/* set non-exclusive use of tty */
810#endif
811
812  /* get original termio attributes */
813  if (tcgetattr(STDIN_FILENO, &attr) != 0)
814    return (-1);
815
816/* If talking to an rlogin process, propagate the terminal type and baud rate
817   across the network. */
818  if (rflag)
819    doremoteterm(term);
820  else {
821    struct termios termios;
822    fd_set fds;
823    struct timeval timeval;
824
825    memset(&timeval, 0, sizeof(struct timeval));
826
827    FD_ZERO(&fds);
828    FD_SET(0, &fds);
829
830#if HAVE_USLEEP
831    usleep(1);
832#endif /* HAVE_USLEEP */
833
834    if (select(1, &fds, NULL, NULL, &timeval)) {
835#ifdef DEBUG
836      syslog(LOG_DEBUG, "reading user name from tty buffer");
837#endif /* DEBUG */
838
839      if (tcgetattr(0, &termios)) {
840#ifdef DEBUG
841	syslog(LOG_DEBUG, "tcgetattr(0, &termios) failed");
842#endif /* DEBUG */
843	exit(1);
844      }
845
846      termios.c_lflag &= ~ECHO;
847
848      if (tcsetattr(0, TCSANOW, &termios)) {
849#ifdef DEBUG
850	syslog(LOG_DEBUG, "tcsetattr(0, &termios) failed");
851#endif /* DEBUG */
852	exit(1);
853      }
854
855      if ((i = read(0, name, sizeof(name)-1)) > 0)
856	name[i] = 0;
857      if ((p = strchr(name, '\r')))
858        *p = 0;
859      if ((p = strchr(name, '\n')))
860        *p = 0;
861    }
862  }
863
864/* Force termios portable control characters to the system default values as
865specified in termios.h. This should help the one-time password login feel the
866same as the vendor-supplied login. Common extensions are also set for
867completeness, but these are set within appropriate defines for portability. */
868
869#define CONTROL(x) (x - 64)
870
871#ifdef VEOF
872#ifdef CEOF
873  attr.c_cc[VEOF] = CEOF;
874#else	/* CEOF */
875  attr.c_cc[VEOF] = CONTROL('D');
876#endif	/* CEOF */
877#endif	/* VEOF */
878#ifdef VEOL
879#ifdef CEOL
880  attr.c_cc[VEOL] = CEOL;
881#else	/* CEOL */
882  attr.c_cc[VEOL] = CONTROL('J');
883#endif	/* CEOL */
884#endif	/* VEOL */
885#ifdef VERASE
886#ifdef CERASE
887  attr.c_cc[VERASE] = CERASE;
888#else	/* CERASE */
889  attr.c_cc[VERASE] = CONTROL('H');
890#endif	/* CERASE */
891#endif	/* VERASE */
892#ifdef VINTR
893#ifdef CINTR
894  attr.c_cc[VINTR] = CINTR;
895#else	/* CINTR */
896  attr.c_cc[VINTR] = CONTROL('C');
897#endif	/* CINTR */
898#endif	/* VINTR */
899#ifdef VKILL
900#ifdef CKILL
901  attr.c_cc[VKILL] = CKILL;
902#else	/* CKILL */
903  attr.c_cc[VKILL] = CONTROL('U');
904#endif	/* CKILL */
905#endif	/* VKILL */
906#ifdef VQUIT
907#ifdef CQUIT
908  attr.c_cc[VQUIT] = CQUIT;
909#else	/* CQUIT */
910  attr.c_cc[VQUIT] = CONTROL('\\');
911#endif	/* CQUIT */
912#endif	/* VQUIT */
913#ifdef VSUSP
914#ifdef CSUSP
915  attr.c_cc[VSUSP] = CSUSP;
916#else	/* CSUSP */
917  attr.c_cc[VSUSP] = CONTROL('Z');
918#endif	/* CSUSP */
919#endif	/* VSUSP */
920#ifdef VSTOP
921#ifdef CSTOP
922  attr.c_cc[VSTOP] = CSTOP;
923#else	/* CSTOP */
924  attr.c_cc[VSTOP] = CONTROL('S');
925#endif	/* CSTOP */
926#endif	/* VSTOP */
927#ifdef VSTART
928#ifdef CSTART
929  attr.c_cc[VSTART] = CSTART;
930#else	/* CSTART */
931  attr.c_cc[VSTART] = CONTROL('Q');
932#endif	/* CSTART */
933#endif	/* VSTART */
934#ifdef VDSUSP
935#ifdef CDSUSP
936  attr.c_cc[VDSUSP] = CDSUSP;
937#else	/* CDSUSP */
938  attr.c_cc[VDSUSP] = 0;
939#endif	/* CDSUSP */
940#endif	/* VDSUSP */
941#ifdef VEOL2
942#ifdef CEOL2
943  attr.c_cc[VEOL2] = CEOL2;
944#else	/* CEOL2 */
945  attr.c_cc[VEOL2] = 0;
946#endif	/* CEOL2 */
947#endif	/* VEOL2 */
948#ifdef VREPRINT
949#ifdef CRPRNT
950  attr.c_cc[VREPRINT] = CRPRNT;
951#else	/* CRPRNT */
952  attr.c_cc[VREPRINT] = 0;
953#endif	/* CRPRNT */
954#endif	/* VREPRINT */
955#ifdef VWERASE
956#ifdef CWERASE
957  attr.c_cc[VWERASE] = CWERASE;
958#else	/* CWERASE */
959  attr.c_cc[VWERASE] = 0;
960#endif	/* CWERASE */
961#endif	/* VWERASE */
962#ifdef VLNEXT
963#ifdef CLNEXT
964  attr.c_cc[VLNEXT] = CLNEXT;
965#else	/* CLNEXT */
966  attr.c_cc[VLNEXT] = 0;
967#endif	/* CLNEXT */
968#endif	/* VLNEXT */
969
970  attr.c_lflag |= ICANON;	/* enable canonical input processing */
971  attr.c_lflag &= ~ISIG;	/* disable INTR, QUIT,& SUSP signals */
972  attr.c_lflag |= (ECHO | ECHOE);	/* enable echo and erase */
973#ifdef ONLCR
974  /* POSIX does not specify any output processing flags, but the usage below
975     is SVID compliant and is generally portable to modern versions of UNIX. */
976  attr.c_oflag |= ONLCR;	/* map CR to CRNL on output */
977#endif
978#ifdef ICRNL
979  attr.c_iflag |= ICRNL;
980#endif	/* ICRNL */
981
982  attr.c_oflag |= OPOST;
983  attr.c_lflag |= ICANON;	/* enable canonical input */
984  attr.c_lflag |= ECHO;
985  attr.c_lflag |= ECHOE;	/* enable ERASE character */
986  attr.c_lflag |= ECHOK;	/* enable KILL to delete line */
987  attr.c_cflag |= HUPCL;	/* hangup on close */
988
989  /* Set revised termio attributes */
990  if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &attr))
991    return (-1);
992
993  atexit(catchexit);
994
995  tty = ttyname(0);
996
997  if (tty == (char *) 0 || *tty == '\0')
998    tty = "UNKNOWN";	/* was: "/dev/tty??" */
999
1000#if HAVE_SETVBUF && defined(_IONBF)
1001#if SETVBUF_REVERSED
1002  setvbuf(stdout, _IONBF, NULL, 0);
1003  setvbuf(stderr, _IONBF, NULL, 0);
1004#else /* SETVBUF_REVERSED */
1005  setvbuf(stdout, NULL, _IONBF, 0);
1006  setvbuf(stderr, NULL, _IONBF, 0);
1007#endif /* SETVBUF_REVERSED */
1008#endif /* HAVE_SETVBUF && defined(_IONBF) */
1009
1010#ifdef DEBUG
1011  syslog(LOG_DEBUG, "tty = %s", tty);
1012#endif /* DEBUG */
1013
1014#ifdef HAVE_LOGIN_ENVFILE
1015  {
1016    FILE *f;
1017
1018    if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) {
1019      char line[128], *c, *c2;
1020
1021      while(fgets(line, sizeof(line)-1, f)) {
1022	c = line;
1023	while(*c && (isalnum(*c) || (*c == '_'))) c++;
1024	  if (*c == '=') {
1025	    *(c++) = 0;
1026	    if (c2 = strchr(c, ';'))
1027	      *c2 = 0;
1028	    if (c2 = strchr(c, '\n'))
1029	      *c2 = 0;
1030	    if (c2 = strchr(c, ' '))
1031	      continue;
1032	    if (c2 = strchr(c, '\t'))
1033	      continue;
1034	    if (!strcmp(line, "TZ"))
1035	      continue;
1036	    if (setenv(line, c, 1) < 0) {
1037	      fprintf(stderr, "setenv() failed -- environment full?\n");
1038	      break;
1039	    }
1040	  }
1041        }
1042      fclose(f);
1043    }
1044  }
1045#endif /* HAVE_LOGIN_ENVFILE */
1046
1047  t = 0;
1048  invalid = TRUE;
1049  af_pwok = opieaccessfile(host);
1050
1051  if (name[0])
1052    if (name[0] == '-') {
1053      fprintf(stderr, "User names can't start with '-'.\n");
1054      syslog(LOG_AUTH, "Attempt to use invalid username: %s.", name);
1055      exit(1);
1056    } else
1057      invalid = lookupuser();
1058
1059  do {
1060    /* If remote login take given name, otherwise prompt user for something. */
1061    if (invalid && !name[0]) {
1062      getloginname();
1063      invalid = lookupuser();
1064      authsok = 0;
1065    }
1066#ifdef DEBUG
1067    syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d", name, strlen(name), name[0]);
1068#endif /* DEBUG */
1069
1070    if (fflag) {
1071      uid = getuid();
1072
1073      if (uid != 0 && uid != thisuser.pw_uid)
1074	fflag = 0;
1075      /* Disallow automatic login for root. */
1076      if (thisuser.pw_uid == 0)
1077	fflag = 0;
1078    }
1079    if (feof(stdin))
1080      exit(0);
1081
1082    /* If no remote login authentication and a password exists for this user,
1083       prompt for and verify a password. */
1084    if (!fflag && (rflag < 1) && *thisuser.pw_passwd) {
1085#ifdef DEBUG
1086      syslog(LOG_DEBUG, "login name is +%s+, of length %d, [0] = %d\n", name, strlen(name), name[0]);
1087#endif /* DEBUG */
1088
1089      /* Attempt a one-time password challenge */
1090      i = opiechallenge(&opie, name, opieprompt);
1091
1092      if ((i < 0) || (i > 1)) {
1093        syslog(LOG_ERR, "error: opiechallenge() returned %d, errno=%d!\n", i, errno);
1094      } else {
1095        printf("%s\n", opieprompt);
1096	authsok |= 1;
1097      }
1098
1099      if (!memcmp(&thisuser, &nouser, sizeof(thisuser)))
1100	if (host[0])
1101	  syslog(LOG_WARNING, "Invalid login attempt for %s on %s from %s.",
1102		 name, tty, host);
1103	else
1104	  syslog(LOG_WARNING, "Invalid login attempt for %s on %s.",
1105		 name, tty);
1106
1107      if (af_pwok && opiealways(thisuser.pw_dir))
1108	authsok |= 2;
1109
1110#if DEBUG
1111      syslog(LOG_DEBUG, "af_pwok = %d, authsok = %d", af_pwok, authsok);
1112#endif /* DEBUG */
1113
1114      if (!authsok)
1115	syslog(LOG_ERR, "no authentication methods are available for %s!", name);
1116
1117#if NEW_PROMPTS
1118      if ((authsok & 1) || !authsok)
1119        printf("Response");
1120      if (((authsok & 3) == 3) || !authsok)
1121        printf(" or ");
1122      if ((authsok & 2) || !authsok)
1123        printf("Password");
1124      printf(": ");
1125      fflush(stdout);
1126      if (!opiereadpass(buf, sizeof(buf), !(authsok & 2)))
1127        invalid = TRUE;
1128#else /* NEW_PROMPTS */
1129      if (!(authsok & 1) && authsok)
1130	printf("(OTP response required)\n");
1131      printf("Password:");
1132      fflush(stdout);
1133      if (!opiereadpass(buf, sizeof(buf), 0))
1134        invalid = TRUE;
1135#endif /* NEW_PROMPTS */
1136
1137      if (!buf[0] && (authsok & 1)) {
1138        authsok &= ~2;
1139	/* Null line entered, so display appropriate prompt & flush current
1140	   data. */
1141#if NEW_PROMPTS
1142        printf("Response: ");
1143#else /* NEW_PROMPTS */
1144	printf(" (echo on)\nPassword:");
1145#endif /* NEW_PROMPTS */
1146        if (!opiereadpass(buf, sizeof(buf), 1))
1147          invalid = TRUE;
1148      }
1149
1150      if (authsok & 1) {
1151        i = opiegetsequence(&opie);
1152        opiepassed = !opieverify(&opie, buf);
1153
1154#ifdef DEBUG
1155      syslog(LOG_DEBUG, "opiepassed = %d", opiepassed);
1156#endif /* DEBUG */
1157      }
1158
1159      if (!invalid) {
1160        if ((authsok & 1) && opiepassed) {
1161	  if (i < 10) {
1162	    printf("Warning: Re-initialize your OTP information");
1163            if (i < 5)
1164              printf(" NOW!");
1165            printf("\n");
1166	  }
1167        } else {
1168	  if (authsok & 2) {
1169	    pp = crypt(buf, thisuser.pw_passwd);
1170	    invalid = strcmp(pp, thisuser.pw_passwd);
1171	  } else
1172            invalid = TRUE;
1173	}
1174      }
1175    }
1176
1177    /* If user not super-user, check for logins disabled. */
1178    if (thisuser.pw_uid) {
1179      if (nlfd = fopen(_PATH_NOLOGIN, "r")) {
1180	while ((c = getc(nlfd)) != EOF)
1181	  putchar(c);
1182	fflush(stdout);
1183	sleep(5);
1184	exit(0);
1185      }
1186    }
1187    /* If valid so far and root is logging in, see if root logins on this
1188       terminal are permitted. */
1189    if (!invalid && !thisuser.pw_uid && !rootterm(tty)) {
1190      if (host[0])
1191	syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s FROM %.*s",
1192	       tty, HMAX, host);
1193      else
1194	syslog(LOG_CRIT, "ROOT LOGIN REFUSED ON %s", tty);
1195      invalid = TRUE;
1196    }
1197    /* If invalid, then log failure attempt data to appropriate system
1198       logfiles and close the connection. */
1199    if (invalid) {
1200      printf("Login incorrect\n");
1201      if (host[0])
1202	  syslog(LOG_ERR, "LOGIN FAILURE ON %s FROM %.*s, %.*s",
1203		 tty, HMAX, host, sizeof(name), name);
1204	else
1205	  syslog(LOG_ERR, "LOGIN FAILURE ON %s, %.*s",
1206		 tty, sizeof(name), name);
1207      if (++t >= 5)
1208	exit(1);
1209    }
1210    if (*thisuser.pw_shell == '\0')
1211      thisuser.pw_shell = "/bin/sh";
1212    if ((chdir(thisuser.pw_dir) < 0) && !invalid) {
1213      if (chdir("/") < 0) {
1214	printf("No directory!\n");
1215	invalid = TRUE;
1216      } else {
1217	printf("No directory! %s\n", "Logging in with HOME=/");
1218        strcpy(thisuser.pw_dir, "/");
1219      }
1220    }
1221    /* Remote login invalid must have been because of a restriction of some
1222       sort, no extra chances. */
1223    if (invalid) {
1224      if (!usererr)
1225	exit(1);
1226      name[0] = 0;
1227    }
1228  }
1229  while (invalid);
1230  /* Committed to login -- turn off timeout */
1231  alarm(0);
1232
1233#ifdef	QUOTA
1234  if (quota(Q_SETUID, thisuser.pw_uid, 0, 0) < 0 && errno != EINVAL) {
1235    if (errno == EUSERS)
1236      printf("%s.\n%s.\n", "Too many users logged on already",
1237	     "Try again later");
1238    else
1239      if (errno == EPROCLIM)
1240	printf("You have too many processes running.\n");
1241      else
1242	perror("quota (Q_SETUID)");
1243    sleep(5);
1244    exit(0);
1245  }
1246#endif
1247
1248  if (opielogin(tty, name, host))
1249    syslog(LOG_ERR, "can't record login: tty %s, name %s, host %s", tty, name, host);
1250
1251  quietlog = !access(QUIET_LOGIN_FILE, F_OK);
1252
1253#if HAVE_LASTLOG_H
1254  {
1255  int f;
1256
1257  if ((f = open(lastlog, O_RDWR)) >= 0) {
1258    struct lastlog ll;
1259
1260    lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0);
1261
1262    if ((sizeof(ll) == read(f, (char *) &ll, sizeof(ll))) &&
1263	(ll.ll_time != 0) && (!quietlog)) {
1264      printf("Last login: %.*s ",
1265	     24 - 5, (char *) ctime(&ll.ll_time));
1266      if (*ll.ll_host != '\0')
1267	printf("from %.*s\n", sizeof(ll.ll_host), ll.ll_host);
1268      else
1269	printf("on %.*s\n", sizeof(ll.ll_line), ll.ll_line);
1270    }
1271    lseek(f, (long)thisuser.pw_uid * sizeof(struct lastlog), 0);
1272
1273    time(&ll.ll_time);
1274    strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
1275    strncpy(ll.ll_host, host, sizeof(ll.ll_host));
1276    write(f, (char *) &ll, sizeof ll);
1277    close(f);
1278  }
1279  }
1280#endif /* HAVE_LASTLOG_H */
1281
1282  chown(tty, thisuser.pw_uid, TTYGID(thisuser.pw_gid));
1283
1284#ifdef TIOCSWINSZ
1285/* POSIX does not specify any interface to set/get window sizes, so this is
1286not portable.  It should work on most recent BSDish systems and the defines
1287should protect it on older System Vish systems.  It does work under Solaris
12882.4, though it isn't clear how many other SVR4 systems support it. I'd be
1289interested in hearing of a more portable approach. rja */
1290  if (!hflag && !rflag)
1291    ioctl(0, TIOCSWINSZ, &win);	/* set window size to 0,0,0,0 */
1292#endif
1293
1294  chmod(tty, 0622);
1295  setgid(thisuser.pw_gid);
1296  initgroups(name, thisuser.pw_gid);
1297
1298#ifdef	QUOTA
1299  quota(Q_DOWARN, thisuser.pw_uid, (dev_t) - 1, 0);
1300#endif
1301
1302#ifdef PERMSFILE
1303  home = thisuser.pw_dir;
1304  permsfile(name, tty, thisuser.pw_uid, thisuser.pw_gid);
1305  fflush(stderr);
1306#endif	/* PERMSFILE */
1307
1308  setuid(thisuser.pw_uid);
1309
1310  /* destroy environment unless user has asked to preserve it */
1311  if (!pflag)
1312    environ = envinit;
1313  setenv("HOME", thisuser.pw_dir, 1);
1314  setenv("SHELL", thisuser.pw_shell, 1);
1315  if (!term[0]) {
1316#if HAVE_GETTTYNAM
1317/*
1318 * The getttynam() call and the ttyent structure first appeared in 4.3 BSD.
1319 * They are not portable to System V systems such as Solaris 2.x.
1320 *         rja
1321 */
1322  register struct ttyent *t;
1323  register char *c;
1324
1325  if (c = strrchr(tty, '/'))
1326    c++;
1327  else
1328    c = tty;
1329
1330  if (t = getttynam(c))
1331    strncpy(term, t->ty_type, sizeof(term));
1332  else
1333#endif /* HAVE_GETTTYNAM */
1334    strcpy(term, "unknown");
1335  }
1336
1337  setenv("USER", name, 1);
1338  setenv("LOGNAME", name, 1);
1339  setenv("PATH", DEFAULT_PATH, 0);
1340  if (term[0]) {
1341#ifdef DEBUG
1342    syslog(LOG_DEBUG, "setting TERM=%s", term);
1343#endif	/* DEBUG */
1344    setenv("TERM", term, 1);
1345  }
1346
1347#ifdef HAVE_LOGIN_ENVFILE
1348  {
1349    FILE *f;
1350
1351    if (f = fopen(HAVE_LOGIN_ENVFILE, "r")) {
1352      char line[128], *c, *c2;
1353
1354      while(fgets(line, sizeof(line)-1, f)) {
1355	c = line;
1356	while(*c && (isalnum(*c) || (*c == '_'))) c++;
1357	  if (*c == '=') {
1358	    *(c++) = 0;
1359	    if (c2 = strchr(c, ';'))
1360	      *c2 = 0;
1361	    if (c2 = strchr(c, '\n'))
1362	      *c2 = 0;
1363	    if (c2 = strchr(c, ' '))
1364	      continue;
1365	    if (c2 = strchr(c, '\t'))
1366	      continue;
1367	    if (setenv(line, c, 0) < 0) {
1368	      fprintf(stderr, "setenv() failed -- environment full?\n");
1369	      break;
1370	    }
1371	  }
1372        }
1373      fclose(f);
1374    }
1375  }
1376#endif /* HAVE_LOGIN_ENVFILE */
1377
1378  if ((namep = strrchr(thisuser.pw_shell, '/')) == NULL)
1379    namep = thisuser.pw_shell;
1380  else
1381    namep++;
1382  strcat(minusnam, namep);
1383  if (tty[sizeof("tty") - 1] == 'd')
1384    syslog(LOG_INFO, "DIALUP %s, %s", tty, name);
1385  if (!thisuser.pw_uid)
1386    if (host[0])
1387      syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s", tty, HMAX, host);
1388    else
1389      syslog(LOG_NOTICE, "ROOT LOGIN %s", tty);
1390#if !HAVE_MOTD_IN_PROFILE
1391  if (!quietlog) {
1392    FILE *mf;
1393    register c;
1394
1395    signal(SIGINT, catch);
1396    if ((mf = fopen(MOTD_FILE, "r")) != NULL) {
1397      while ((c = getc(mf)) != EOF && !stopmotd)
1398	putchar(c);
1399      fclose(mf);
1400    }
1401    signal(SIGINT, SIG_IGN);
1402  }
1403#endif /* !HAVE_MOTD_IN_PROFILE */
1404#if !HAVE_MAILCHECK_IN_PROFILE
1405  if (!quietlog) {
1406    struct stat st;
1407    char buf[128];
1408    int len;
1409
1410    strncpy(buf, PATH_MAIL, sizeof(buf) - 2);
1411    buf[sizeof(buf) - 2] = 0;
1412
1413    len = strlen(buf);
1414    if (*(buf + len - 1) != '/') {
1415	*(buf + len) = '/';
1416	*(buf + len + 1) = 0;
1417    }
1418
1419    strcat(buf, name);
1420#if DEBUG
1421    syslog(LOG_DEBUG, "statting %s", buf);
1422#endif /* DEBUG */
1423    if (!stat(buf, &st) && st.st_size)
1424      printf("You have %smail.\n",
1425	     (st.st_mtime > st.st_atime) ? "new " : "");
1426  }
1427#endif /* !HAVE_MAILCHECK_IN_PROFILE */
1428  signal(SIGALRM, SIG_DFL);
1429  signal(SIGQUIT, SIG_DFL);
1430  signal(SIGINT, SIG_DFL);
1431  signal(SIGTSTP, SIG_IGN);
1432
1433  attr.c_lflag |= (ISIG | IEXTEN);
1434
1435  catchexit();
1436  execlp(thisuser.pw_shell, minusnam, 0);
1437  perror(thisuser.pw_shell);
1438  printf("No shell\n");
1439  exit(0);
1440}
1441