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