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