initial-freebsd-port-work-20010414
[openafs.git] / src / kauth / klogin.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Copyright (c) 1980 Regents of the University of California.
12  * All rights reserved.  The Berkeley software License Agreement
13  * specifies the terms and conditions for redistribution.
14  */
15
16 #ifndef lint
17 char copyright[] =
18 "@(#) Copyright (c) 1980 Regents of the University of California.\n\
19  All rights reserved.\n";
20 #endif /* not lint */
21
22 #ifndef lint
23 static char sccsid[] = "@(#)login.c     5.15 (Berkeley) 4/12/86";
24 #endif /* not lint */
25
26 /*
27  * login [ name ]
28  * login -r hostname (for rlogind)
29  * login -h hostname (for telnetd, etc.)
30  */
31 #include <afs/param.h>
32 #if !defined(AFS_SUN_ENV) && !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV) && !defined(AFS_SGI_ENV) && !defined(AFS_SUN5_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) &&!defined(AFS_FBSD_ENV)
33 #include <sys/param.h>
34
35 #define quota(a,b,c,d) 0
36
37 #include <sys/stat.h>
38 #include <sys/time.h>
39 #include <sys/resource.h>
40 #include <sys/file.h>
41 #include <afs/afsutil.h>
42 #include <sgtty.h>
43 #include <utmp.h>
44 #include <signal.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <lastlog.h>
48 #include <errno.h>
49 #include <ttyent.h>
50
51
52 #include <syslog.h>
53
54 #include <grp.h>
55
56 static gid_t tty_gid(int default_gid);
57 static void getloginname(register struct utmp *up);
58
59 #define TTYGRPNAME      "tty"           /* name of group to own ttys */
60 #define TTYGID(gid)     tty_gid(gid)    /* gid that owns all ttys */
61
62 #define SCMPN(a, b)     strncmp(a, b, sizeof(a))
63 #define SCPYN(a, b)     strncpy(a, b, sizeof(a))
64
65 #define NMAX    sizeof(utmp.ut_name)
66 #define HMAX    sizeof(utmp.ut_host)
67
68 #define FALSE   0
69 #define TRUE    -1
70
71 char    nolog[] =       "/etc/nologin";
72 char    qlog[]  =       ".hushlogin";
73 char    maildir[30] =   "/usr/spool/mail/";
74 char    lastlog[] =     "/usr/adm/lastlog";
75 struct  passwd nouser;
76 struct  sgttyb ttyb;
77 struct  utmp utmp;
78 char    minusnam[16] = "-";
79 char    *envinit[] = { 0 };             /* now set by setenv calls */
80 /*
81  * This bounds the time given to login.  We initialize it here
82  * so it can be patched on machines where it's too small.
83  */
84 int     timeout = 60;
85
86 char    term[64];
87
88 struct  passwd *pwd;
89 char    *strcat(), *rindex(), *index(), *malloc(), *realloc();
90 static void timedout(void);
91 static void showmotd(void);
92 static void doremoteterm(char *term, struct sgttyb *tp);
93 static void setenv(char *var, char *value, int clobber);
94 char    *ttyname();
95 char    *crypt();
96 char    *getpass();
97 char    *stypeof();
98 extern  char **environ;
99
100
101 struct  tchars tc = {
102         CINTR, CQUIT, CSTART, CSTOP, CEOT, CBRK
103 };
104 struct  ltchars ltc = {
105         CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT
106 };
107
108 struct winsize win = { 0, 0, 0, 0 };
109
110 int     rflag;
111 int     usererr = -1;
112 char    rusername[NMAX+1], lusername[NMAX+1];
113 char    rpassword[NMAX+1];
114 char    name[NMAX+1];
115 char    *rhost;
116
117 int osi_audit(void)
118 {
119     /* this sucks but it works for now.
120     */
121     return 0;
122 }
123
124
125 #include "AFS_component_version_number.c"
126
127 int main(
128   int argc,
129   char **argv)
130 {
131         register char *namep;
132         int pflag = 0, hflag = 0, t, f, c;
133         int invalid, quietlog;
134         FILE *nlfd;
135         char *ttyn, *tty;
136         int ldisc = 0, zero = 0, i;
137         char **envnew;
138
139 #ifdef  AFS_AIX32_ENV
140     /*
141      * The following signal action for AIX is necessary so that in case of a 
142      * crash (i.e. core is generated) we can include the user's data section 
143      * in the core dump. Unfortunately, by default, only a partial core is
144      * generated which, in many cases, isn't too useful.
145      */
146     struct sigaction nsa;
147     
148     sigemptyset(&nsa.sa_mask);
149     nsa.sa_handler = SIG_DFL;
150     nsa.sa_flags = SA_FULLDUMP;
151     sigaction(SIGSEGV, &nsa, NULL);
152 #endif
153         signal(SIGALRM, timedout);
154         alarm(timeout);
155         signal(SIGQUIT, SIG_IGN);
156         signal(SIGINT, SIG_IGN);
157         setpriority(PRIO_PROCESS, 0, 0);
158         quota(Q_SETUID, 0, 0, 0);
159
160         /* create a dummy user */
161         bzero (&nouser, sizeof(nouser));
162         nouser.pw_name = "";
163         nouser.pw_passwd = "*";
164         nouser.pw_dir = nouser.pw_shell = "";
165         nouser.pw_uid = nouser.pw_gid = -1;
166
167         /*
168          * -p is used by getty to tell login not to destroy the environment
169          * -r is used by rlogind to cause the autologin protocol;
170          * -h is used by other servers to pass the name of the
171          * remote host to login so that it may be placed in utmp and wtmp
172          */
173         while (argc > 1) {
174                 if (strcmp(argv[1], "-r") == 0) {
175                         if (rflag || hflag) {
176                                 printf("Only one of -r and -h allowed\n");
177                                 exit(1);
178                         }
179                         rflag = 1;
180                         usererr = doremotelogin(argv[2]);
181                         SCPYN(utmp.ut_host, argv[2]);
182                         argc -= 2;
183                         argv += 2;
184                         continue;
185                 }
186                 if (strcmp(argv[1], "-h") == 0 && getuid() == 0) {
187                         if (rflag || hflag) {
188                                 printf("Only one of -r and -h allowed\n");
189                                 exit(1);
190                         }
191                         hflag = 1;
192                         SCPYN(utmp.ut_host, argv[2]);
193                         argc -= 2;
194                         argv += 2;
195                         continue;
196                 }
197                 if (strcmp(argv[1], "-p") == 0) {
198                         argc--;
199                         argv++;
200                         pflag = 1;
201                         continue;
202                 }
203                 break;
204         }
205         ioctl(0, TIOCLSET, &zero);
206         ioctl(0, TIOCNXCL, 0);
207         ioctl(0, FIONBIO, &zero);
208         ioctl(0, FIOASYNC, &zero);
209         ioctl(0, TIOCGETP, &ttyb);
210         /*
211          * If talking to an rlogin process,
212          * propagate the terminal type and
213          * baud rate across the network.
214          */
215         if (rflag)
216                 doremoteterm(term, &ttyb);
217         ttyb.sg_erase = CERASE;
218         ttyb.sg_kill = CKILL;
219         ioctl(0, TIOCSLTC, &ltc);
220         ioctl(0, TIOCSETC, &tc);
221         ioctl(0, TIOCSETP, &ttyb);
222         for (t = getdtablesize(); t > 2; t--)
223                 close(t);
224         ttyn = ttyname(0);
225         if (ttyn == (char *)0 || *ttyn == '\0')
226                 ttyn = "/dev/tty??";
227         tty = rindex(ttyn, '/');
228         if (tty == NULL)
229                 tty = ttyn;
230         else
231                 tty++;
232         openlog("login", LOG_ODELAY, LOG_AUTH);
233         t = 0;
234         invalid = FALSE;
235         do {
236                 ldisc = 0;
237                 ioctl(0, TIOCSETD, &ldisc);
238                 SCPYN(utmp.ut_name, "");
239                 /*
240                  * Name specified, take it.
241                  */
242                 if (argc > 1) {
243                         SCPYN(utmp.ut_name, argv[1]);
244                         argc = 0;
245                 }
246                 /*
247                  * If remote login take given name,
248                  * otherwise prompt user for something.
249                  */
250                 if (rflag && !invalid)
251                         SCPYN(utmp.ut_name, lusername);
252                 else
253                         getloginname(&utmp);
254                 invalid = FALSE;
255                 if (!strcmp(pwd->pw_shell, "/bin/csh")) {
256                         ldisc = NTTYDISC;
257                         ioctl(0, TIOCSETD, &ldisc);
258                 }
259                 /*
260                  * If no remote login authentication and
261                  * a password exists for this user, prompt
262                  * for one and verify it.
263                  */
264                 if (usererr == -1 && *pwd->pw_passwd != '\0') {
265 #ifdef KAUTH
266                     char password[BUFSIZ];
267                     char *reason;
268                     if (ka_UserReadPassword
269                         ("Password:", password, sizeof(password), &reason)) {
270                         printf("Unable to login because %s,\n", reason);
271                         invalid = TRUE;
272                     }
273                     else if (ka_UserAuthenticate
274                         (pwd->pw_name, /*inst*/0, /*realm*/0, password,
275                          /*sepag*/1, &reason)) {
276                         printf("Unable to authenticate to AFS because %s.\n",
277                                reason);
278                         printf("  proceeding with local authentication... \n");
279                         /* try local login */
280                         namep = crypt(password, pwd->pw_passwd);
281                         if (strcmp(namep, pwd->pw_passwd)) invalid = TRUE;
282                     }
283 #else
284                     char *pp;
285                     setpriority(PRIO_PROCESS, 0, -4);
286                     pp = getpass("Password:");
287                     namep = crypt(pp, pwd->pw_passwd);
288                     setpriority(PRIO_PROCESS, 0, 0);
289                     if (strcmp(namep, pwd->pw_passwd)) invalid = TRUE;
290 #endif
291                 }
292                 /*
293                  * If user not super-user, check for logins disabled.
294                  */
295                 if (pwd->pw_uid != 0 && (nlfd = fopen(nolog, "r")) != 0) {
296                         while ((c = getc(nlfd)) != EOF)
297                                 putchar(c);
298                         fflush(stdout);
299                         sleep(5);
300                         exit(0);
301                 }
302                 /*
303                  * If valid so far and root is logging in,
304                  * see if root logins on this terminal are permitted.
305                  */
306                 if (!invalid && pwd->pw_uid == 0 && !rootterm(tty)) {
307                         if (utmp.ut_host[0])
308                                 syslog(LOG_CRIT,
309                                     "ROOT LOGIN REFUSED ON %s FROM %.*s",
310                                     tty, HMAX, utmp.ut_host);
311                         else
312                                 syslog(LOG_CRIT,
313                                     "ROOT LOGIN REFUSED ON %s", tty);
314                         invalid = TRUE;
315                 }
316                 if (invalid) {
317                         printf("Login incorrect\n");
318                         if (++t >= 5) {
319                                 if (utmp.ut_host[0])
320                                         syslog(LOG_CRIT,
321                                             "REPEATED LOGIN FAILURES ON %s FROM %.*s, %.*s",
322                                             tty, HMAX, utmp.ut_host,
323                                             NMAX, utmp.ut_name);
324                                 else
325                                         syslog(LOG_CRIT,
326                                             "REPEATED LOGIN FAILURES ON %s, %.*s",
327                                                 tty, NMAX, utmp.ut_name);
328                                 ioctl(0, TIOCHPCL, (struct sgttyb *) 0);
329                                 close(0), close(1), close(2);
330                                 sleep(10);
331                                 exit(1);
332                         }
333                 }
334                 if (*pwd->pw_shell == '\0')
335                         pwd->pw_shell = "/bin/sh";
336                 if (chdir(pwd->pw_dir) < 0 && !invalid ) {
337                         if (chdir("/") < 0) {
338                                 printf("No directory!\n");
339                                 invalid = TRUE;
340                         } else {
341                                 printf("No directory! %s\n",
342                                    "Logging in with home=/");
343                                 pwd->pw_dir = "/";
344                         }
345                 }
346                 /*
347                  * Remote login invalid must have been because
348                  * of a restriction of some sort, no extra chances.
349                  */
350                 if (!usererr && invalid)
351                         exit(1);
352         } while (invalid);
353 /* committed to login turn off timeout */
354         alarm(0);
355
356         if (quota(Q_SETUID, pwd->pw_uid, 0, 0) < 0 && errno != EINVAL) {
357                 if (errno == EUSERS)
358                         printf("%s.\n%s.\n",
359                            "Too many users logged on already",
360                            "Try again later");
361                 else if (errno == EPROCLIM)
362                         printf("You have too many processes running.\n");
363                 else
364                         perror("quota (Q_SETUID)");
365                 sleep(5);
366                 exit(0);
367         }
368         time(&utmp.ut_time);
369         t = ttyslot();
370         if (t > 0 && (f = open("/etc/utmp", O_WRONLY)) >= 0) {
371                 lseek(f, (afs_int32)(t*sizeof(utmp)), 0);
372                 SCPYN(utmp.ut_line, tty);
373                 write(f, (char *)&utmp, sizeof(utmp));
374                 close(f);
375         }
376         if ((f = open("/usr/adm/wtmp", O_WRONLY|O_APPEND)) >= 0) {
377                 write(f, (char *)&utmp, sizeof(utmp));
378                 close(f);
379         }
380         quietlog = access(qlog, F_OK) == 0;
381         if ((f = open(lastlog, O_RDWR)) >= 0) {
382                 struct lastlog ll;
383
384                 lseek(f, (afs_int32)pwd->pw_uid * sizeof (struct lastlog), 0);
385                 if (read(f, (char *) &ll, sizeof ll) == sizeof ll &&
386                     ll.ll_time != 0 && !quietlog) {
387                         printf("Last login: %.*s ",
388                             24-5, (char *)ctime(&ll.ll_time));
389                         if (*ll.ll_host != '\0')
390                                 printf("from %.*s\n",
391                                     sizeof (ll.ll_host), ll.ll_host);
392                         else
393                                 printf("on %.*s\n",
394                                     sizeof (ll.ll_line), ll.ll_line);
395                 }
396                 lseek(f, (afs_int32)pwd->pw_uid * sizeof (struct lastlog), 0);
397                 time(&ll.ll_time);
398                 SCPYN(ll.ll_line, tty);
399                 SCPYN(ll.ll_host, utmp.ut_host);
400                 write(f, (char *) &ll, sizeof ll);
401                 close(f);
402         }
403         chown(ttyn, pwd->pw_uid, TTYGID(pwd->pw_gid));
404         if (!hflag && !rflag)                                   /* XXX */
405                 ioctl(0, TIOCSWINSZ, &win);
406         chmod(ttyn, 0620);
407         setgid(pwd->pw_gid);
408         strncpy(name, utmp.ut_name, NMAX);
409         name[NMAX] = '\0';
410         initgroups(name, pwd->pw_gid);
411         quota(Q_DOWARN, pwd->pw_uid, (dev_t)-1, 0);
412         setuid(pwd->pw_uid);
413         /* destroy environment unless user has asked to preserve it */
414         if (!pflag)
415                 environ = envinit;
416
417         /* set up environment, this time without destruction */
418         /* copy the environment before setenving */
419         i = 0;
420         while (environ[i] != NULL)
421                 i++;
422         envnew = (char **) malloc(sizeof (char *) * (i + 1));
423         for (; i >= 0; i--)
424                 envnew[i] = environ[i];
425         environ = envnew;
426
427         setenv("HOME=", pwd->pw_dir, 1);
428         setenv("SHELL=", pwd->pw_shell, 1);
429         if (term[0] == '\0')
430                 strncpy(term, stypeof(tty), sizeof(term));
431         setenv("TERM=", term, 0);
432         setenv("USER=", pwd->pw_name, 1);
433         setenv("PATH=", ":/usr/ucb:/bin:/usr/bin", 0);
434
435         if ((namep = rindex(pwd->pw_shell, '/')) == NULL)
436                 namep = pwd->pw_shell;
437         else
438                 namep++;
439         strcat(minusnam, namep);
440         if (tty[sizeof("tty")-1] == 'd')
441                 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
442         if (pwd->pw_uid == 0)
443                 if (utmp.ut_host[0])
444                         syslog(LOG_NOTICE, "ROOT LOGIN %s FROM %.*s",
445                             tty, HMAX, utmp.ut_host);
446                 else
447                         syslog(LOG_NOTICE, "ROOT LOGIN %s", tty);
448         if (!quietlog) {
449                 struct stat st;
450
451                 showmotd();
452                 strcat(maildir, pwd->pw_name);
453                 if (stat(maildir, &st) == 0 && st.st_size != 0)
454                         printf("You have %smail.\n",
455                                 (st.st_mtime > st.st_atime) ? "new " : "");
456         }
457         signal(SIGALRM, SIG_DFL);
458         signal(SIGQUIT, SIG_DFL);
459         signal(SIGINT, SIG_DFL);
460         signal(SIGTSTP, SIG_IGN);
461         execlp(pwd->pw_shell, minusnam, 0);
462         perror(pwd->pw_shell);
463         printf("No shell\n");
464         exit(0);
465 }
466
467 static void getloginname(register struct utmp *up)
468 {
469         register char *namep;
470         char c;
471
472         while (up->ut_name[0] == '\0') {
473                 namep = up->ut_name;
474                 printf("login: ");
475                 while ((c = getchar()) != '\n') {
476                         if (c == ' ')
477                                 c = '_';
478                         if (c == (char)EOF)
479                                 exit(0);
480                         if (namep < up->ut_name+NMAX)
481                                 *namep++ = c;
482                 }
483         }
484         strncpy(lusername, up->ut_name, NMAX);
485         lusername[NMAX] = 0;
486         if ((pwd = getpwnam(lusername)) == NULL)
487                 pwd = &nouser;
488 }
489
490 static void timedout(void)
491 {
492
493         printf("Login timed out after %d seconds\n", timeout);
494         exit(0);
495 }
496
497 int stopmotd;
498 static void catch(void)
499 {
500
501         signal(SIGINT, SIG_IGN);
502         stopmotd++;
503 }
504
505 int rootterm(char *tty)
506 {
507         register struct ttyent *t;
508
509         if ((t = getttynam(tty)) != NULL) {
510                 if (t->ty_status & TTY_SECURE)
511                         return (1);
512         }
513         return (0);
514 }
515
516 static void showmotd(void)
517 {
518         FILE *mf;
519         register c;
520
521         signal(SIGINT, catch);
522         if ((mf = fopen("/etc/motd", "r")) != NULL) {
523                 while ((c = getc(mf)) != EOF && stopmotd == 0)
524                         putchar(c);
525                 fclose(mf);
526         }
527         signal(SIGINT, SIG_IGN);
528 }
529
530 #undef  UNKNOWN
531 #define UNKNOWN "su"
532
533 char *stypeof(char *ttyid)
534 {
535         register struct ttyent *t;
536
537         if (ttyid == NULL || (t = getttynam(ttyid)) == NULL)
538                 return (UNKNOWN);
539         return (t->ty_type);
540 }
541
542 int doremotelogin(char *host)
543 {
544         getstr(rusername, sizeof (rusername), "remuser");
545         getstr(lusername, sizeof (lusername), "locuser");
546         getstr(term, sizeof(term), "Terminal type");
547         if (getuid()) {
548                 pwd = &nouser;
549                 return(-1);
550         }
551         pwd = getpwnam(lusername);
552         if (pwd == NULL) {
553                 pwd = &nouser;
554                 return(-1);
555         }
556         return(ruserok(host, (pwd->pw_uid == 0), rusername, lusername));
557 }
558
559 int getstr(
560   char *buf,
561   int cnt,
562   char *err)
563 {
564         char c;
565
566         do {
567                 if (read(0, &c, 1) != 1)
568                         exit(1);
569                 if (--cnt < 0) {
570                         printf("%s too long\r\n", err);
571                         exit(1);
572                 }
573                 *buf++ = c;
574         } while (c != 0);
575 }
576
577 char    *speeds[] =
578     { "0", "50", "75", "110", "134", "150", "200", "300",
579       "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" };
580 #define NSPEEDS (sizeof (speeds) / sizeof (speeds[0]))
581
582 static void doremoteterm(
583   char *term,
584   struct sgttyb *tp)
585 {
586         register char *cp = index(term, '/'), **cpp;
587         char *speed;
588
589         if (cp) {
590                 *cp++ = '\0';
591                 speed = cp;
592                 cp = index(speed, '/');
593                 if (cp)
594                         *cp++ = '\0';
595                 for (cpp = speeds; cpp < &speeds[NSPEEDS]; cpp++)
596                         if (strcmp(*cpp, speed) == 0) {
597                                 tp->sg_ispeed = tp->sg_ospeed = cpp-speeds;
598                                 break;
599                         }
600         }
601         tp->sg_flags = ECHO|CRMOD|ANYP|XTABS;
602 }
603
604 /*
605  * Set the value of var to be arg in the Unix 4.2 BSD environment env.
606  * Var should end with '='.
607  * (bindings are of the form "var=value")
608  * This procedure assumes the memory for the first level of environ
609  * was allocated using malloc.
610  */
611 static void setenv(
612   char *var,
613   char *value,
614   int clobber)
615 {
616         extern char **environ;
617         int index = 0;
618         int varlen = strlen(var);
619         int vallen = strlen(value);
620
621         for (index = 0; environ[index] != NULL; index++) {
622                 if (strncmp(environ[index], var, varlen) == 0) {
623                         /* found it */
624                         if (!clobber)
625                                 return;
626                         environ[index] = malloc(varlen + vallen + 1);
627                         strcpy(environ[index], var);
628                         strcat(environ[index], value);
629                         return;
630                 }
631         }
632         environ = (char **) realloc(environ, sizeof (char *) * (index + 2));
633         if (environ == NULL) {
634                 fprintf(stderr, "login: malloc out of memory\n");
635                 exit(1);
636         }
637         environ[index] = malloc(varlen + vallen + 1);
638         strcpy(environ[index], var);
639         strcat(environ[index], value);
640         environ[++index] = NULL;
641 }
642
643 static gid_t tty_gid(int default_gid)
644 {
645         struct group *getgrnam(), *gr;
646         gid_t gid = default_gid;
647
648         gr = getgrnam(TTYGRPNAME);
649         if (gr != (struct group *) 0)
650                 gid = gr->gr_gid;
651
652         endgrent();
653
654         return (gid);
655 }
656 #else
657
658 #include "AFS_component_version_number.c"
659
660 main() {}
661 #endif  /*!defined(AFS_SUN_ENV) && !defined(AFS_AIX_ENV) && !defined(AFS_HPUX_ENV)*/
662