Annotation of src/usr.bin/login/login.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: login.c,v 1.12 1994/12/23 06:53:01 jtc Exp $ */
2:
3: /*-
4: * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
5: * The Regents of the University of California. All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: * 3. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: static char copyright[] =
38: "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
39: The Regents of the University of California. All rights reserved.\n";
40: #endif /* not lint */
41:
42: #ifndef lint
43: #if 0
44: static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
45: #endif
46: static char rcsid[] = "$NetBSD: login.c,v 1.12 1994/12/23 06:53:01 jtc Exp $";
47: #endif /* not lint */
48:
49: /*
50: * login [ name ]
51: * login -h hostname (for telnetd, etc.)
52: * login -f name (for pre-authenticated login: datakit, xterm, etc.)
53: */
54:
55: #include <sys/param.h>
56: #include <sys/stat.h>
57: #include <sys/time.h>
58: #include <sys/resource.h>
59: #include <sys/file.h>
60:
61: #include <err.h>
62: #include <errno.h>
63: #include <grp.h>
64: #include <pwd.h>
65: #include <setjmp.h>
66: #include <signal.h>
67: #include <stdio.h>
68: #include <stdlib.h>
69: #include <string.h>
70: #include <syslog.h>
71: #include <ttyent.h>
72: #include <tzfile.h>
73: #include <unistd.h>
74: #include <utmp.h>
75:
76: #include "pathnames.h"
77:
78: void badlogin __P((char *));
79: void checknologin __P((void));
80: void dolastlog __P((int));
81: void getloginname __P((void));
82: void motd __P((void));
83: int rootterm __P((char *));
84: void sigint __P((int));
85: void sleepexit __P((int));
86: char *stypeof __P((char *));
87: void timedout __P((int));
88: int pwcheck __P((char *, char *, char *, char *));
89: #if defined(KERBEROS) || defined(KERBEROS5)
90: int klogin __P((struct passwd *, char *, char *, char *));
91: void kdestroy __P((void));
92: void dofork __P((void));
93: #endif
94:
95: extern void login __P((struct utmp *));
96:
97: #define TTYGRPNAME "tty" /* name of group to own ttys */
98:
99: /*
100: * This bounds the time given to login. Not a define so it can
101: * be patched on machines where it's too small.
102: */
103: u_int timeout = 300;
104:
105: #if defined(KERBEROS) || defined(KERBEROS5)
106: int notickets = 1;
107: char *instance;
108: char *krbtkfile_env;
109: int authok;
110: #endif
111:
112: struct passwd *pwd;
113: int failures;
114: char term[64], *envinit[1], *hostname, *username, *tty;
115:
116: int
117: main(argc, argv)
118: int argc;
119: char *argv[];
120: {
121: extern char **environ;
122: struct group *gr;
123: struct stat st;
124: struct timeval tp;
125: struct utmp utmp;
126: int ask, ch, cnt, fflag, hflag, pflag, quietlog, rootlogin, rval;
127: uid_t uid;
128: char *domain, *p, *salt, *ttyn;
129: char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
130: char localhost[MAXHOSTNAMELEN];
131:
132: (void)signal(SIGALRM, timedout);
133: (void)alarm(timeout);
134: (void)signal(SIGQUIT, SIG_IGN);
135: (void)signal(SIGINT, SIG_IGN);
136: (void)setpriority(PRIO_PROCESS, 0, 0);
137:
138: openlog("login", LOG_ODELAY, LOG_AUTH);
139:
140: /*
141: * -p is used by getty to tell login not to destroy the environment
142: * -f is used to skip a second login authentication
143: * -h is used by other servers to pass the name of the remote
144: * host to login so that it may be placed in utmp and wtmp
145: */
146: domain = NULL;
147: if (gethostname(localhost, sizeof(localhost)) < 0)
148: syslog(LOG_ERR, "couldn't get local hostname: %m");
149: else
150: domain = strchr(localhost, '.');
151:
152: fflag = hflag = pflag = 0;
153: uid = getuid();
154: while ((ch = getopt(argc, argv, "fh:p")) != EOF)
155: switch (ch) {
156: case 'f':
157: fflag = 1;
158: break;
159: case 'h':
160: if (uid)
161: errx(1, "-h option: %s", strerror(EPERM));
162: hflag = 1;
163: if (domain && (p = strchr(optarg, '.')) &&
164: strcasecmp(p, domain) == 0)
165: *p = 0;
166: hostname = optarg;
167: break;
168: case 'p':
169: pflag = 1;
170: break;
171: case '?':
172: default:
173: if (!uid)
174: syslog(LOG_ERR, "invalid flag %c", ch);
175: (void)fprintf(stderr,
176: "usage: login [-fp] [-h hostname] [username]\n");
177: exit(1);
178: }
179: argc -= optind;
180: argv += optind;
181:
182: if (*argv) {
183: username = *argv;
184: ask = 0;
185: } else
186: ask = 1;
187:
188: for (cnt = getdtablesize(); cnt > 2; cnt--)
189: (void)close(cnt);
190:
191: ttyn = ttyname(STDIN_FILENO);
192: if (ttyn == NULL || *ttyn == '\0') {
193: (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
194: ttyn = tname;
195: }
196: if (tty = strrchr(ttyn, '/'))
197: ++tty;
198: else
199: tty = ttyn;
200:
201: for (cnt = 0;; ask = 1) {
202: #if defined(KERBEROS) || defined(KERBEROS5)
203: kdestroy();
204: #endif
205: if (ask) {
206: fflag = 0;
207: getloginname();
208: }
209: rootlogin = 0;
210: #ifdef KERBEROS
211: if ((instance = strchr(username, '.')) != NULL) {
212: if (strncmp(instance, ".root", 5) == 0)
213: rootlogin = 1;
214: *instance++ = '\0';
215: } else
216: instance = "";
217: #endif
218: #ifdef KERBEROS5
219: if ((instance = strchr(username, '/')) != NULL) {
220: if (strncmp(instance, "/root", 5) == 0)
221: rootlogin = 1;
222: *instance++ = '\0';
223: } else
224: instance = "";
225: #endif
226: if (strlen(username) > UT_NAMESIZE)
227: username[UT_NAMESIZE] = '\0';
228:
229: /*
230: * Note if trying multiple user names; log failures for
231: * previous user name, but don't bother logging one failure
232: * for nonexistent name (mistyped username).
233: */
234: if (failures && strcmp(tbuf, username)) {
235: if (failures > (pwd ? 0 : 1))
236: badlogin(tbuf);
237: failures = 0;
238: }
239: (void)strcpy(tbuf, username);
240:
241: if (pwd = getpwnam(username))
242: salt = pwd->pw_passwd;
243: else
244: salt = "xx";
245:
246: /*
247: * if we have a valid account name, and it doesn't have a
248: * password, or the -f option was specified and the caller
249: * is root or the caller isn't changing their uid, don't
250: * authenticate.
251: */
252: if (pwd) {
253: if (pwd->pw_uid == 0)
254: rootlogin = 1;
255:
256: if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
257: /* already authenticated */
258: break;
259: } else if (pwd->pw_passwd[0] == '\0') {
260: /* pretend password okay */
261: rval = 0;
262: goto ttycheck;
263: }
264: }
265:
266: fflag = 0;
267:
268: (void)setpriority(PRIO_PROCESS, 0, -4);
269:
270: p = getpass("Password:");
271:
272: if (pwd) {
273: #if defined(KERBEROS) || defined(KERBEROS5)
274: rval = klogin(pwd, instance, localhost, p);
275: if (rval != 0 && rootlogin && pwd->pw_uid != 0)
276: rootlogin = 0;
277: if (rval == 0)
278: authok = 1;
279: else if (rval == 1) {
280: if (pwd->pw_uid != 0)
281: rootlogin = 0;
282: rval = pwcheck(username, p, salt, pwd->pw_passwd);
283: }
284: #else
285: rval = pwcheck(username, p, salt, pwd->pw_passwd);
286: #endif
287: }
288: memset(p, 0, strlen(p));
289:
290: (void)setpriority(PRIO_PROCESS, 0, 0);
291:
292: ttycheck:
293: /*
294: * If trying to log in as root without Kerberos,
295: * but with insecure terminal, refuse the login attempt.
296: */
297: #if defined(KERBEROS) || defined(KERBEROS5)
298: if (authok == 0)
299: #endif
300: if (pwd && !rval && rootlogin && !rootterm(tty)) {
301: (void)fprintf(stderr,
302: "%s login refused on this terminal.\n",
303: pwd->pw_name);
304: if (hostname)
305: syslog(LOG_NOTICE,
306: "LOGIN %s REFUSED FROM %s ON TTY %s",
307: pwd->pw_name, hostname, tty);
308: else
309: syslog(LOG_NOTICE,
310: "LOGIN %s REFUSED ON TTY %s",
311: pwd->pw_name, tty);
312: continue;
313: }
314:
315: if (pwd && !rval)
316: break;
317:
318: (void)printf("Login incorrect\n");
319: failures++;
320: /* we allow 10 tries, but after 3 we start backing off */
321: if (++cnt > 3) {
322: if (cnt >= 10) {
323: badlogin(username);
324: sleepexit(1);
325: }
326: sleep((u_int)((cnt - 3) * 5));
327: }
328: }
329:
330: /* committed to login -- turn off timeout */
331: (void)alarm((u_int)0);
332:
333: endpwent();
334:
335: /* if user not super-user, check for disabled logins */
336: if (!rootlogin)
337: checknologin();
338:
339: if (chdir(pwd->pw_dir) < 0) {
340: (void)printf("No home directory %s!\n", pwd->pw_dir);
341: if (chdir("/"))
342: exit(0);
343: pwd->pw_dir = "/";
344: (void)printf("Logging in with home = \"/\".\n");
345: }
346:
347: quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
348:
349: if (pwd->pw_change || pwd->pw_expire)
350: (void)gettimeofday(&tp, (struct timezone *)NULL);
351: if (pwd->pw_change)
352: if (tp.tv_sec >= pwd->pw_change) {
353: (void)printf("Sorry -- your password has expired.\n");
354: sleepexit(1);
355: } else if (pwd->pw_change - tp.tv_sec <
356: 2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
357: (void)printf("Warning: your password expires on %s",
358: ctime(&pwd->pw_change));
359: if (pwd->pw_expire)
360: if (tp.tv_sec >= pwd->pw_expire) {
361: (void)printf("Sorry -- your account has expired.\n");
362: sleepexit(1);
363: } else if (pwd->pw_expire - tp.tv_sec <
364: 2 * DAYSPERWEEK * SECSPERDAY && !quietlog)
365: (void)printf("Warning: your account expires on %s",
366: ctime(&pwd->pw_expire));
367:
368: /* Nothing else left to fail -- really log in. */
369: memset((void *)&utmp, 0, sizeof(utmp));
370: (void)time(&utmp.ut_time);
371: (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
372: if (hostname)
373: (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
374: (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
375: login(&utmp);
376:
377: dolastlog(quietlog);
378:
379: (void)chown(ttyn, pwd->pw_uid,
380: (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
381: #if defined(KERBEROS) || defined(KERBEROS5)
382: /* Fork so that we can call kdestroy */
383: if (krbtkfile_env)
384: dofork();
385: #endif
386: (void)setgid(pwd->pw_gid);
387:
388: initgroups(username, pwd->pw_gid);
389:
390: if (*pwd->pw_shell == '\0')
391: pwd->pw_shell = _PATH_BSHELL;
392:
393: /* Destroy environment unless user has requested its preservation. */
394: if (!pflag)
395: environ = envinit;
396: (void)setenv("HOME", pwd->pw_dir, 1);
397: (void)setenv("SHELL", pwd->pw_shell, 1);
398: if (term[0] == '\0')
399: (void)strncpy(term, stypeof(tty), sizeof(term));
400: (void)setenv("TERM", term, 0);
401: (void)setenv("LOGNAME", pwd->pw_name, 1);
402: (void)setenv("USER", pwd->pw_name, 1);
403: (void)setenv("PATH", _PATH_DEFPATH, 0);
404: #ifdef KERBEROS
405: if (krbtkfile_env)
406: (void)setenv("KRBTKFILE", krbtkfile_env, 1);
407: #endif
408: #ifdef KERBEROS5
409: if (krbtkfile_env)
410: (void)setenv("KRB5CCNAME", krbtkfile_env, 1);
411: #endif
412:
413: if (tty[sizeof("tty")-1] == 'd')
414: syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
415:
416: /* If fflag is on, assume caller/authenticator has logged root login. */
417: if (rootlogin && fflag == 0)
418: if (hostname)
419: syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
420: username, tty, hostname);
421: else
422: syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
423:
424: #if defined(KERBEROS) || defined(KERBEROS5)
425: if (!quietlog && notickets == 1)
426: (void)printf("Warning: no Kerberos tickets issued.\n");
427: #endif
428:
429: if (!quietlog) {
430: (void)printf("%s\n\t%s %s\n\n",
431: "Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994",
432: "The Regents of the University of California. ",
433: "All rights reserved.");
434: motd();
435: (void)snprintf(tbuf,
436: sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
437: if (stat(tbuf, &st) == 0 && st.st_size != 0)
438: (void)printf("You have %smail.\n",
439: (st.st_mtime > st.st_atime) ? "new " : "");
440: }
441:
442: (void)signal(SIGALRM, SIG_DFL);
443: (void)signal(SIGQUIT, SIG_DFL);
444: (void)signal(SIGINT, SIG_DFL);
445: (void)signal(SIGTSTP, SIG_IGN);
446:
447: tbuf[0] = '-';
448: (void)strcpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
449: p + 1 : pwd->pw_shell);
450:
451: if (setlogin(pwd->pw_name) < 0)
452: syslog(LOG_ERR, "setlogin() failure: %m");
453:
454: /* Discard permissions last so can't get killed and drop core. */
455: if (rootlogin)
456: (void) setuid(0);
457: else
458: (void) setuid(pwd->pw_uid);
459:
460: execlp(pwd->pw_shell, tbuf, 0);
461: err(1, "%s", pwd->pw_shell);
462: }
463:
464: int
465: pwcheck(user, p, salt, passwd)
466: char *user, *p, *salt, *passwd;
467: {
468: #ifdef SKEY
469: if (strcasecmp(p, "s/key") == 0) {
470: if (skey_haskey(user)) {
471: fprintf(stderr, "You have no s/key. ");
472: return 1;
473: } else {
474: return skey_authenticate(user);
475: }
476: }
477: #endif
478: return strcmp(crypt(p, salt), passwd);
479: }
480:
481: #if defined(KERBEROS) || defined(KERBEROS5)
482: #define NBUFSIZ (UT_NAMESIZE + 1 + 5) /* .root suffix */
483: #else
484: #define NBUFSIZ (UT_NAMESIZE + 1)
485: #endif
486:
487: #if defined(KERBEROS) || defined(KERBEROS5)
488: /*
489: * This routine handles cleanup stuff, and the like.
490: * It exists only in the child process.
491: */
492: #include <sys/wait.h>
493: void
494: dofork()
495: {
496: int child;
497:
498: if (!(child = fork()))
499: return; /* Child process */
500:
501: /* Setup stuff? This would be things we could do in parallel with login */
502: (void) chdir("/"); /* Let's not keep the fs busy... */
503:
504: /* If we're the parent, watch the child until it dies */
505: while (wait(0) != child)
506: ;
507:
508: /* Cleanup stuff */
509: /* Run kdestroy to destroy tickets */
510: kdestroy();
511:
512: /* Leave */
513: exit(0);
514: }
515: #endif
516:
517: void
518: getloginname()
519: {
520: int ch;
521: char *p;
522: static char nbuf[NBUFSIZ];
523:
524: for (;;) {
525: (void)printf("login: ");
526: for (p = nbuf; (ch = getchar()) != '\n'; ) {
527: if (ch == EOF) {
528: badlogin(username);
529: exit(0);
530: }
531: if (p < nbuf + (NBUFSIZ - 1))
532: *p++ = ch;
533: }
534: if (p > nbuf)
535: if (nbuf[0] == '-')
536: (void)fprintf(stderr,
537: "login names may not start with '-'.\n");
538: else {
539: *p = '\0';
540: username = nbuf;
541: break;
542: }
543: }
544: }
545:
546: int
547: rootterm(ttyn)
548: char *ttyn;
549: {
550: struct ttyent *t;
551:
552: return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
553: }
554:
555: jmp_buf motdinterrupt;
556:
557: void
558: motd()
559: {
560: int fd, nchars;
561: sig_t oldint;
562: char tbuf[8192];
563:
564: if ((fd = open(_PATH_MOTDFILE, O_RDONLY, 0)) < 0)
565: return;
566: oldint = signal(SIGINT, sigint);
567: if (setjmp(motdinterrupt) == 0)
568: while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
569: (void)write(fileno(stdout), tbuf, nchars);
570: (void)signal(SIGINT, oldint);
571: (void)close(fd);
572: }
573:
574: /* ARGSUSED */
575: void
576: sigint(signo)
577: int signo;
578: {
579: longjmp(motdinterrupt, 1);
580: }
581:
582: /* ARGSUSED */
583: void
584: timedout(signo)
585: int signo;
586: {
587: (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
588: exit(0);
589: }
590:
591: void
592: checknologin()
593: {
594: int fd, nchars;
595: char tbuf[8192];
596:
597: if ((fd = open(_PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
598: while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
599: (void)write(fileno(stdout), tbuf, nchars);
600: sleepexit(0);
601: }
602: }
603:
604: void
605: dolastlog(quiet)
606: int quiet;
607: {
608: struct lastlog ll;
609: int fd;
610:
611: if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
612: (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
613: if (!quiet) {
614: if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
615: ll.ll_time != 0) {
616: (void)printf("Last login: %.*s ",
617: 24-5, (char *)ctime(&ll.ll_time));
618: if (*ll.ll_host != '\0')
619: (void)printf("from %.*s\n",
620: (int)sizeof(ll.ll_host),
621: ll.ll_host);
622: else
623: (void)printf("on %.*s\n",
624: (int)sizeof(ll.ll_line),
625: ll.ll_line);
626: }
627: (void)lseek(fd, (off_t)pwd->pw_uid * sizeof(ll), L_SET);
628: }
629: memset((void *)&ll, 0, sizeof(ll));
630: (void)time(&ll.ll_time);
631: (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
632: if (hostname)
633: (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
634: (void)write(fd, (char *)&ll, sizeof(ll));
635: (void)close(fd);
636: }
637: }
638:
639: void
640: badlogin(name)
641: char *name;
642: {
643: if (failures == 0)
644: return;
645: if (hostname) {
646: syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
647: failures, failures > 1 ? "S" : "", hostname);
648: syslog(LOG_AUTHPRIV|LOG_NOTICE,
649: "%d LOGIN FAILURE%s FROM %s, %s",
650: failures, failures > 1 ? "S" : "", hostname, name);
651: } else {
652: syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
653: failures, failures > 1 ? "S" : "", tty);
654: syslog(LOG_AUTHPRIV|LOG_NOTICE,
655: "%d LOGIN FAILURE%s ON %s, %s",
656: failures, failures > 1 ? "S" : "", tty, name);
657: }
658: }
659:
660: #undef UNKNOWN
661: #define UNKNOWN "su"
662:
663: char *
664: stypeof(ttyid)
665: char *ttyid;
666: {
667: struct ttyent *t;
668:
669: return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : UNKNOWN);
670: }
671:
672: void
673: sleepexit(eval)
674: int eval;
675: {
676: (void)sleep(5);
677: exit(eval);
678: }