Add HAMMER support to the installer
[dragonfly.git] / contrib / opie / opielogin.c
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 }