xref: /NextBSD/contrib/opie/opielogin.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /* opielogin.c: The infamous /bin/login
2 
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9 
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License 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 
165 static int rflag = 0;
166 static int usererr = -1;
167 static int stopmotd = 0;
168 static char rusername[NMAX + 1];
169 static char name[NMAX + 1] = "";
170 static char minusnam[16] = "-";
171 static char *envinit[1];	/* now set by setenv calls */
172 static char term[64] = "";	/* important to initialise to a NULL string */
173 static char host[HMAX + 1] = "";
174 static struct passwd nouser;
175 static struct passwd thisuser;
176 
177 #if HAVE_SHADOW_H
178 #include <shadow.h>
179 #endif /* HAVE_SHADOW_H */
180 
181 static char *ttyprompt;
182 
183 #ifdef PERMSFILE
184 extern char *home;
185 #endif	/* PERMSFILE */
186 
187 static struct termios attr;
188 
189 extern int errno;
190 
191 static int ouroptind;
192 static char *ouroptarg;
193 
194 #if HAVE_LASTLOG_H
195 #ifndef _PATH_LASTLOG
196 #define _PATH_LASTLOG "/var/adm/lastlog"
197 #endif /* _PATH_LASTLOG */
198 
199 static 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  */
207 static int timeout = 120;
208 
209 static 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 
218 static int need_opieverify = 0;
219 static struct opie opie;
220 
221 #ifdef TIOCSWINSZ
222 /* Windowing variable relating to JWINSIZE/TIOCSWINSZ/TIOCGWINSZ. This is
223 available on BSDish systems and at least Solaris 2.x, but portability to
224 other systems is questionable. Use within this source code module is
225 protected by suitable defines.
226 
227 I'd be interested in hearing about a more portable approach. rja */
228 
229 static 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
236 only call this routine about four times in the lifetime of this process
237 and the kernel will free all heap memory when we exit or exec. */
238 static 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 
282 lookupuserbad:
283   memcpy(&thisuser, &nouser, sizeof(thisuser));
284   return -1;
285 }
286 
287 static 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 
320 static 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
333 static 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 
341 static 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 
351 static 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
355 are not portable to System V systems such as Solaris 2.x. or modern versions
356 of 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,
379 if the string "CONSOLE=/dev/console" exists and is not commented out with "#"
380 characters, or 2) from anywhere.
381 
382 So we open /etc/default/login file grab the file contents one line at a time
383 verify 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 
448 static 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 
474 static 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 
491 struct 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 
611 static 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 
632 static 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 
644 int 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
873 specified in termios.h. This should help the one-time password login feel the
874 same as the vendor-supplied login. Common extensions are also set for
875 completeness, 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
1290 not portable.  It should work on most recent BSDish systems and the defines
1291 should protect it on older System Vish systems.  It does work under Solaris
1292 2.4, though it isn't clear how many other SVR4 systems support it. I'd be
1293 interested 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