login.c revision 1.12 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 }
679