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