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