login.c revision 1.62 1 /* $NetBSD: login.c,v 1.62 2001/01/01 20:19:06 thorpej 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 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT(
39 "@(#) Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994\n\
40 The Regents of the University of California. All rights reserved.\n");
41 #endif /* not lint */
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)login.c 8.4 (Berkeley) 4/2/94";
46 #endif
47 __RCSID("$NetBSD: login.c,v 1.62 2001/01/01 20:19:06 thorpej Exp $");
48 #endif /* not lint */
49
50 /*
51 * login [ name ]
52 * login -h hostname (for telnetd, etc.)
53 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
54 */
55
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 #include <sys/file.h>
61 #include <sys/wait.h>
62
63 #include <err.h>
64 #include <errno.h>
65 #include <grp.h>
66 #include <pwd.h>
67 #include <setjmp.h>
68 #include <signal.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <syslog.h>
73 #include <time.h>
74 #include <ttyent.h>
75 #include <tzfile.h>
76 #include <unistd.h>
77 #include <utmp.h>
78 #include <util.h>
79 #ifdef SKEY
80 #include <skey.h>
81 #endif
82 #ifdef KERBEROS5
83 #include <krb5/krb5.h>
84 #include <com_err.h>
85 #endif
86 #ifdef LOGIN_CAP
87 #include <login_cap.h>
88 #endif
89
90 #include "pathnames.h"
91
92 #ifdef KERBEROS5
93 int login_krb5_get_tickets = 1;
94 int login_krb4_get_tickets = 0;
95 int login_krb5_forwardable_tgt = 0;
96 int login_krb5_retain_ccache = 0;
97 #endif
98
99 void badlogin __P((char *));
100 void checknologin __P((char *));
101 void dolastlog __P((int));
102 void getloginname __P((void));
103 int main __P((int, char *[]));
104 void motd __P((char *));
105 int rootterm __P((char *));
106 void sigint __P((int));
107 void sleepexit __P((int));
108 const char *stypeof __P((const char *));
109 void timedout __P((int));
110 #if defined(KERBEROS)
111 int klogin __P((struct passwd *, char *, char *, char *));
112 void kdestroy __P((void));
113 #endif
114 #ifdef KERBEROS5
115 int k5login __P((struct passwd *, char *, char *, char *));
116 void k5destroy __P((void));
117 int k5_read_creds __P((char*));
118 int k5_write_creds __P((void));
119 #endif
120 #if defined(KERBEROS) || defined(KERBEROS5)
121 void dofork __P((void));
122 #endif
123
124 #define TTYGRPNAME "tty" /* name of group to own ttys */
125
126 #define DEFAULT_BACKOFF 3
127 #define DEFAULT_RETRIES 10
128
129 /*
130 * This bounds the time given to login. Not a define so it can
131 * be patched on machines where it's too small.
132 */
133 u_int timeout = 300;
134
135 #if defined(KERBEROS) || defined(KERBEROS5)
136 int notickets = 1;
137 char *instance;
138 int has_ccache = 0;
139 #endif
140 #ifdef KERBEROS
141 extern char *krbtkfile_env;
142 extern int krb_configured;
143 #endif
144 #ifdef KERBEROS5
145 extern krb5_context kcontext;
146 extern int have_forward;
147 extern char *krb5tkfile_env;
148 extern int krb5_configured;
149 #endif
150
151 #if defined(KERBEROS) && defined(KERBEROS5)
152 #define KERBEROS_CONFIGURED (krb_configured || krb5_configured)
153 #elif defined(KERBEROS)
154 #define KERBEROS_CONFIGURED krb_configured
155 #elif defined(KERBEROS5)
156 #define KERBEROS_CONFIGURED krb5_configured
157 #endif
158
159 struct passwd *pwd;
160 int failures;
161 char term[64], *envinit[1], *hostname, *username, *tty;
162
163 static const char copyrightstr[] = "\
164 Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001\n\
165 \tThe NetBSD Foundation, Inc. All rights reserved.\n\
166 Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994\n\
167 \tThe Regents of the University of California. All rights reserved.\n\n";
168
169 int
170 main(argc, argv)
171 int argc;
172 char *argv[];
173 {
174 extern char **environ;
175 struct group *gr;
176 struct stat st;
177 struct timeval tp;
178 struct utmp utmp;
179 int ask, ch, cnt, fflag, hflag, pflag, sflag, quietlog, rootlogin, rval;
180 int Fflag;
181 uid_t uid, saved_uid;
182 gid_t saved_gid, saved_gids[NGROUPS_MAX];
183 int nsaved_gids;
184 char *domain, *p, *ttyn, *pwprompt;
185 const char *salt;
186 char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
187 char localhost[MAXHOSTNAMELEN + 1];
188 int need_chpass, require_chpass;
189 int login_retries = DEFAULT_RETRIES,
190 login_backoff = DEFAULT_BACKOFF;
191 time_t pw_warntime = _PASSWORD_WARNDAYS * SECSPERDAY;
192 #ifdef KERBEROS5
193 krb5_error_code kerror;
194 #endif
195 #if defined(KERBEROS) || defined(KERBEROS5)
196 int got_tickets = 0;
197 #endif
198 #ifdef LOGIN_CAP
199 char *shell = NULL;
200 login_cap_t *lc = NULL;
201 #endif
202
203 tbuf[0] = '\0';
204 rval = 0;
205 pwprompt = NULL;
206 need_chpass = require_chpass = 0;
207
208 (void)signal(SIGALRM, timedout);
209 (void)alarm(timeout);
210 (void)signal(SIGQUIT, SIG_IGN);
211 (void)signal(SIGINT, SIG_IGN);
212 (void)setpriority(PRIO_PROCESS, 0, 0);
213
214 openlog("login", LOG_ODELAY, LOG_AUTH);
215
216 /*
217 * -p is used by getty to tell login not to destroy the environment
218 * -f is used to skip a second login authentication
219 * -h is used by other servers to pass the name of the remote
220 * host to login so that it may be placed in utmp and wtmp
221 * -s is used to force use of S/Key or equivalent.
222 */
223 domain = NULL;
224 if (gethostname(localhost, sizeof(localhost)) < 0)
225 syslog(LOG_ERR, "couldn't get local hostname: %m");
226 else
227 domain = strchr(localhost, '.');
228 localhost[sizeof(localhost) - 1] = '\0';
229
230 Fflag = fflag = hflag = pflag = sflag = 0;
231 #ifdef KERBEROS5
232 have_forward = 0;
233 #endif
234 uid = getuid();
235 while ((ch = getopt(argc, argv, "Ffh:ps")) != -1)
236 switch (ch) {
237 case 'F':
238 Fflag = 1;
239 /* FALLTHROUGH */
240 case 'f':
241 fflag = 1;
242 break;
243 case 'h':
244 if (uid)
245 errx(1, "-h option: %s", strerror(EPERM));
246 hflag = 1;
247 if (domain && (p = strchr(optarg, '.')) != NULL &&
248 strcasecmp(p, domain) == 0)
249 *p = 0;
250 hostname = optarg;
251 break;
252 case 'p':
253 pflag = 1;
254 break;
255 case 's':
256 sflag = 1;
257 break;
258 default:
259 case '?':
260 (void)fprintf(stderr,
261 "usage: login [-fps] [-h hostname] [username]\n");
262 exit(1);
263 }
264 argc -= optind;
265 argv += optind;
266
267 if (*argv) {
268 username = *argv;
269 ask = 0;
270 } else
271 ask = 1;
272
273 for (cnt = getdtablesize(); cnt > 2; cnt--)
274 (void)close(cnt);
275
276 ttyn = ttyname(STDIN_FILENO);
277 if (ttyn == NULL || *ttyn == '\0') {
278 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY);
279 ttyn = tname;
280 }
281 if ((tty = strrchr(ttyn, '/')) != NULL)
282 ++tty;
283 else
284 tty = ttyn;
285
286 #ifdef LOGIN_CAP
287 /* Get "login-retries" and "login-backoff" from default class */
288 if ((lc = login_getclass(NULL)) != NULL) {
289 login_retries = (int)login_getcapnum(lc, "login-retries",
290 DEFAULT_RETRIES, DEFAULT_RETRIES);
291 login_backoff = (int)login_getcapnum(lc, "login-backoff",
292 DEFAULT_BACKOFF, DEFAULT_BACKOFF);
293 login_close(lc);
294 lc = NULL;
295 }
296 #endif
297
298 #ifdef KERBEROS5
299 kerror = krb5_init_context(&kcontext);
300 if (kerror) {
301 /*
302 * If Kerberos is not configured, that is, we are
303 * not using Kerberos, do not log the error message.
304 * However, if Kerberos is configured, and the
305 * context init fails for some other reason, we need
306 * to issue a no tickets warning to the user when the
307 * login succeeds.
308 */
309 if (kerror != ENXIO) { /* XXX NetBSD-local Heimdal hack */
310 syslog(LOG_NOTICE,
311 "%s when initializing Kerberos context",
312 error_message(kerror));
313 krb5_configured = 1;
314 }
315 login_krb5_get_tickets = 0;
316 }
317 #endif KERBEROS5
318
319 for (cnt = 0;; ask = 1) {
320 #if defined(KERBEROS)
321 kdestroy();
322 #endif
323 #if defined(KERBEROS5)
324 if (login_krb5_get_tickets)
325 k5destroy();
326 #endif
327 if (ask) {
328 fflag = 0;
329 getloginname();
330 }
331 rootlogin = 0;
332 #ifdef KERBEROS
333 if ((instance = strchr(username, '.')) != NULL)
334 *instance++ = '\0';
335 else
336 instance = "";
337 #endif
338 #ifdef KERBEROS5
339 if ((instance = strchr(username, '/')) != NULL)
340 *instance++ = '\0';
341 else
342 instance = "";
343 #endif
344 if (strlen(username) > MAXLOGNAME)
345 username[MAXLOGNAME] = '\0';
346
347 /*
348 * Note if trying multiple user names; log failures for
349 * previous user name, but don't bother logging one failure
350 * for nonexistent name (mistyped username).
351 */
352 if (failures && strcmp(tbuf, username)) {
353 if (failures > (pwd ? 0 : 1))
354 badlogin(tbuf);
355 failures = 0;
356 }
357 (void)strncpy(tbuf, username, sizeof(tbuf) - 1);
358 tbuf[sizeof(tbuf) - 1] = '\0';
359
360 if ((pwd = getpwnam(username)) != NULL)
361 salt = pwd->pw_passwd;
362 else
363 salt = "xx";
364
365 #ifdef LOGIN_CAP
366 /*
367 * Establish the class now, before we might goto
368 * within the next block. pwd can be NULL since it
369 * falls back to the "default" class if it is.
370 */
371 lc = login_getclass(pwd ? pwd->pw_class : NULL);
372 #endif
373 /*
374 * if we have a valid account name, and it doesn't have a
375 * password, or the -f option was specified and the caller
376 * is root or the caller isn't changing their uid, don't
377 * authenticate.
378 */
379 if (pwd) {
380 if (pwd->pw_uid == 0)
381 rootlogin = 1;
382
383 if (fflag && (uid == 0 || uid == pwd->pw_uid)) {
384 /* already authenticated */
385 #ifdef KERBEROS5
386 if (login_krb5_get_tickets && Fflag)
387 k5_read_creds(username);
388 #endif
389 break;
390 } else if (pwd->pw_passwd[0] == '\0') {
391 /* pretend password okay */
392 rval = 0;
393 goto ttycheck;
394 }
395 }
396
397 fflag = 0;
398
399 (void)setpriority(PRIO_PROCESS, 0, -4);
400
401 #ifdef SKEY
402 if (skey_haskey(username) == 0) {
403 static char skprompt[80];
404 const char *skinfo = skey_keyinfo(username);
405
406 (void)snprintf(skprompt, sizeof(skprompt)-1,
407 "Password [%s]:",
408 skinfo ? skinfo : "error getting challenge");
409 pwprompt = skprompt;
410 } else
411 #endif
412 pwprompt = "Password:";
413
414 p = getpass(pwprompt);
415
416 if (pwd == NULL) {
417 rval = 1;
418 goto skip;
419 }
420 #ifdef KERBEROS
421 if (
422 #ifdef KERBEROS5
423 /* allow a user to get both krb4 and krb5 tickets, if
424 * desired. If krb5 is compiled in, the default action
425 * is to ignore krb4 and get krb5 tickets, but the user
426 * can override this in the krb5.conf. */
427 login_krb4_get_tickets &&
428 #endif
429 klogin(pwd, instance, localhost, p) == 0) {
430 rval = 0;
431 got_tickets = 1;
432 }
433 #endif
434 #ifdef KERBEROS5
435 if (login_krb5_get_tickets &&
436 k5login(pwd, instance, localhost, p) == 0) {
437 rval = 0;
438 got_tickets = 1;
439 }
440 #endif
441 #if defined(KERBEROS) || defined(KERBEROS5)
442 if (got_tickets)
443 goto skip;
444 #endif
445 #ifdef SKEY
446 if (skey_haskey(username) == 0 &&
447 skey_passcheck(username, p) != -1) {
448 rval = 0;
449 goto skip;
450 }
451 #endif
452 if (!sflag && *pwd->pw_passwd != '\0' &&
453 !strcmp(crypt(p, pwd->pw_passwd), pwd->pw_passwd)) {
454 rval = 0;
455 require_chpass = 1;
456 goto skip;
457 }
458 rval = 1;
459
460 skip:
461 memset(p, 0, strlen(p));
462
463 (void)setpriority(PRIO_PROCESS, 0, 0);
464
465 ttycheck:
466 /*
467 * If trying to log in as root without Kerberos,
468 * but with insecure terminal, refuse the login attempt.
469 */
470 if (pwd && !rval && rootlogin && !rootterm(tty)) {
471 (void)fprintf(stderr,
472 "%s login refused on this terminal.\n",
473 pwd->pw_name);
474 if (hostname)
475 syslog(LOG_NOTICE,
476 "LOGIN %s REFUSED FROM %s ON TTY %s",
477 pwd->pw_name, hostname, tty);
478 else
479 syslog(LOG_NOTICE,
480 "LOGIN %s REFUSED ON TTY %s",
481 pwd->pw_name, tty);
482 continue;
483 }
484
485 if (pwd && !rval)
486 break;
487
488 (void)printf("Login incorrect\n");
489 failures++;
490 cnt++;
491 /* we allow 10 tries, but after 3 we start backing off */
492 if (cnt > login_backoff) {
493 if (cnt >= login_retries) {
494 badlogin(username);
495 sleepexit(1);
496 }
497 sleep((u_int)((cnt - 3) * 5));
498 }
499 }
500
501 /* committed to login -- turn off timeout */
502 (void)alarm((u_int)0);
503
504 endpwent();
505
506 /* if user not super-user, check for disabled logins */
507 #ifdef LOGIN_CAP
508 if (!login_getcapbool(lc, "ignorenologin", rootlogin))
509 checknologin(login_getcapstr(lc, "nologin", NULL, NULL));
510 #else
511 if (!rootlogin)
512 checknologin(NULL);
513 #endif
514
515 #ifdef LOGIN_CAP
516 quietlog = login_getcapbool(lc, "hushlogin", 0);
517 #else
518 quietlog = 0;
519 #endif
520 /* Temporarily give up special privileges so we can change */
521 /* into NFS-mounted homes that are exported for non-root */
522 /* access and have mode 7x0 */
523 saved_uid = geteuid();
524 saved_gid = getegid();
525 nsaved_gids = getgroups(NGROUPS_MAX, saved_gids);
526
527 (void)setegid(pwd->pw_gid);
528 initgroups(username, pwd->pw_gid);
529 (void)seteuid(pwd->pw_uid);
530
531 if (chdir(pwd->pw_dir) < 0) {
532 #ifdef LOGIN_CAP
533 if (login_getcapbool(lc, "requirehome", 0)) {
534 (void)printf("Home directory %s required\n",
535 pwd->pw_dir);
536 sleepexit(1);
537 }
538 #endif
539 (void)printf("No home directory %s!\n", pwd->pw_dir);
540 if (chdir("/"))
541 exit(0);
542 pwd->pw_dir = "/";
543 (void)printf("Logging in with home = \"/\".\n");
544 }
545
546 if (!quietlog)
547 quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0;
548
549 /* regain special privileges */
550 (void)seteuid(saved_uid);
551 setgroups(nsaved_gids, saved_gids);
552 (void)setegid(saved_gid);
553
554 #ifdef LOGIN_CAP
555 pw_warntime = login_getcaptime(lc, "password-warn",
556 _PASSWORD_WARNDAYS * SECSPERDAY,
557 _PASSWORD_WARNDAYS * SECSPERDAY);
558 #endif
559
560 if (pwd->pw_change || pwd->pw_expire)
561 (void)gettimeofday(&tp, (struct timezone *)NULL);
562 if (pwd->pw_expire) {
563 if (tp.tv_sec >= pwd->pw_expire) {
564 (void)printf("Sorry -- your account has expired.\n");
565 sleepexit(1);
566 } else if (pwd->pw_expire - tp.tv_sec < pw_warntime &&
567 !quietlog)
568 (void)printf("Warning: your account expires on %s",
569 ctime(&pwd->pw_expire));
570 }
571 if (pwd->pw_change) {
572 if (pwd->pw_change == _PASSWORD_CHGNOW)
573 need_chpass = 1;
574 else if (tp.tv_sec >= pwd->pw_change) {
575 (void)printf("Sorry -- your password has expired.\n");
576 sleepexit(1);
577 } else if (pwd->pw_change - tp.tv_sec < pw_warntime &&
578 !quietlog)
579 (void)printf("Warning: your password expires on %s",
580 ctime(&pwd->pw_change));
581
582 }
583 /* Nothing else left to fail -- really log in. */
584 memset((void *)&utmp, 0, sizeof(utmp));
585 (void)time(&utmp.ut_time);
586 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
587 if (hostname)
588 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
589 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
590 login(&utmp);
591
592 dolastlog(quietlog);
593
594 (void)chown(ttyn, pwd->pw_uid,
595 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
596
597 if (ttyaction(ttyn, "login", pwd->pw_name))
598 (void)printf("Warning: ttyaction failed.\n");
599
600 #if defined(KERBEROS) || defined(KERBEROS5)
601 /* Fork so that we can call kdestroy */
602 if (
603 #ifdef KERBEROS5
604 ! login_krb5_retain_ccache &&
605 #endif
606 has_ccache)
607 dofork();
608 #endif
609
610 /* Destroy environment unless user has requested its preservation. */
611 if (!pflag)
612 environ = envinit;
613
614 #ifdef LOGIN_CAP
615 if (setusercontext(lc, pwd, pwd->pw_uid,
616 LOGIN_SETALL & ~LOGIN_SETPATH) != 0) {
617 syslog(LOG_ERR, "setusercontext failed");
618 exit(1);
619 }
620 #else
621 (void)setgid(pwd->pw_gid);
622
623 initgroups(username, pwd->pw_gid);
624
625 if (setlogin(pwd->pw_name) < 0)
626 syslog(LOG_ERR, "setlogin() failure: %m");
627
628 /* Discard permissions last so can't get killed and drop core. */
629 if (rootlogin)
630 (void)setuid(0);
631 else
632 (void)setuid(pwd->pw_uid);
633 #endif
634
635 if (*pwd->pw_shell == '\0')
636 pwd->pw_shell = _PATH_BSHELL;
637 #ifdef LOGIN_CAP
638 if ((shell = login_getcapstr(lc, "shell", NULL, NULL)) != NULL) {
639 if ((shell = strdup(shell)) == NULL) {
640 syslog(LOG_NOTICE, "Cannot alloc mem");
641 sleepexit(1);
642 }
643 pwd->pw_shell = shell;
644 }
645 #endif
646
647 (void)setenv("HOME", pwd->pw_dir, 1);
648 (void)setenv("SHELL", pwd->pw_shell, 1);
649 if (term[0] == '\0') {
650 char *tt = (char *)stypeof(tty);
651 #ifdef LOGIN_CAP
652 if (tt == NULL)
653 tt = login_getcapstr(lc, "term", NULL, NULL);
654 #endif
655 /* unknown term -> "su" */
656 (void)strncpy(term, tt != NULL ? tt : "su", sizeof(term));
657 }
658 (void)setenv("TERM", term, 0);
659 (void)setenv("LOGNAME", pwd->pw_name, 1);
660 (void)setenv("USER", pwd->pw_name, 1);
661
662 #ifdef LOGIN_CAP
663 setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH);
664 #else
665 (void)setenv("PATH", _PATH_DEFPATH, 0);
666 #endif
667
668 #ifdef KERBEROS
669 if (krbtkfile_env)
670 (void)setenv("KRBTKFILE", krbtkfile_env, 1);
671 #endif
672 #ifdef KERBEROS5
673 if (krb5tkfile_env)
674 (void)setenv("KRB5CCNAME", krb5tkfile_env, 1);
675 #endif
676
677 if (tty[sizeof("tty")-1] == 'd')
678 syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name);
679
680 /* If fflag is on, assume caller/authenticator has logged root login. */
681 if (rootlogin && fflag == 0) {
682 if (hostname)
683 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s",
684 username, tty, hostname);
685 else
686 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s",
687 username, tty);
688 }
689
690 #if defined(KERBEROS) || defined(KERBEROS5)
691 if (KERBEROS_CONFIGURED && !quietlog && notickets == 1)
692 (void)printf("Warning: no Kerberos tickets issued.\n");
693 #endif
694
695 if (!quietlog) {
696 char *fname;
697 #ifdef LOGIN_CAP
698 fname = login_getcapstr(lc, "copyright", NULL, NULL);
699 if (fname != NULL && access(fname, F_OK) == 0)
700 motd(fname);
701 else
702 #endif
703 (void)printf(copyrightstr);
704
705 #ifdef LOGIN_CAP
706 fname = login_getcapstr(lc, "welcome", NULL, NULL);
707 if (fname == NULL || access(fname, F_OK) != 0)
708 #endif
709 fname = _PATH_MOTDFILE;
710 motd(fname);
711
712 (void)snprintf(tbuf,
713 sizeof(tbuf), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
714 if (stat(tbuf, &st) == 0 && st.st_size != 0)
715 (void)printf("You have %smail.\n",
716 (st.st_mtime > st.st_atime) ? "new " : "");
717 }
718
719 #ifdef LOGIN_CAP
720 login_close(lc);
721 #endif
722
723 (void)signal(SIGALRM, SIG_DFL);
724 (void)signal(SIGQUIT, SIG_DFL);
725 (void)signal(SIGINT, SIG_DFL);
726 (void)signal(SIGTSTP, SIG_IGN);
727
728 tbuf[0] = '-';
729 (void)strncpy(tbuf + 1, (p = strrchr(pwd->pw_shell, '/')) ?
730 p + 1 : pwd->pw_shell, sizeof(tbuf) - 2);
731
732 /* Wait to change password until we're unprivileged */
733 if (need_chpass) {
734 if (!require_chpass)
735 (void)printf(
736 "Warning: your password has expired. Please change it as soon as possible.\n");
737 else {
738 int status;
739
740 (void)printf(
741 "Your password has expired. Please choose a new one.\n");
742 switch (fork()) {
743 case -1:
744 warn("fork");
745 sleepexit(1);
746 case 0:
747 execl(_PATH_BINPASSWD, "passwd", 0);
748 _exit(1);
749 default:
750 if (wait(&status) == -1 ||
751 WEXITSTATUS(status))
752 sleepexit(1);
753 }
754 }
755 }
756
757 #ifdef KERBEROS5
758 if (login_krb5_get_tickets)
759 k5_write_creds();
760 #endif
761 execlp(pwd->pw_shell, tbuf, 0);
762 err(1, "%s", pwd->pw_shell);
763 }
764
765 #if defined(KERBEROS) || defined(KERBEROS5)
766 #define NBUFSIZ (MAXLOGNAME + 1 + 5) /* .root suffix */
767 #else
768 #define NBUFSIZ (MAXLOGNAME + 1)
769 #endif
770
771 #if defined(KERBEROS) || defined(KERBEROS5)
772 /*
773 * This routine handles cleanup stuff, and the like.
774 * It exists only in the child process.
775 */
776 #include <sys/wait.h>
777 void
778 dofork()
779 {
780 int child;
781
782 if (!(child = fork()))
783 return; /* Child process */
784
785 /*
786 * Setup stuff? This would be things we could do in parallel
787 * with login
788 */
789 (void)chdir("/"); /* Let's not keep the fs busy... */
790
791 /* If we're the parent, watch the child until it dies */
792 while (wait(0) != child)
793 ;
794
795 /* Cleanup stuff */
796 /* Run kdestroy to destroy tickets */
797 #ifdef KERBEROS
798 kdestroy();
799 #endif
800 #ifdef KERBEROS5
801 if (login_krb5_get_tickets)
802 k5destroy();
803 #endif
804
805 /* Leave */
806 exit(0);
807 }
808 #endif
809
810 void
811 getloginname()
812 {
813 int ch;
814 char *p;
815 static char nbuf[NBUFSIZ];
816
817 for (;;) {
818 (void)printf("login: ");
819 for (p = nbuf; (ch = getchar()) != '\n'; ) {
820 if (ch == EOF) {
821 badlogin(username);
822 exit(0);
823 }
824 if (p < nbuf + (NBUFSIZ - 1))
825 *p++ = ch;
826 }
827 if (p > nbuf) {
828 if (nbuf[0] == '-')
829 (void)fprintf(stderr,
830 "login names may not start with '-'.\n");
831 else {
832 *p = '\0';
833 username = nbuf;
834 break;
835 }
836 }
837 }
838 }
839
840 int
841 rootterm(ttyn)
842 char *ttyn;
843 {
844 struct ttyent *t;
845
846 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
847 }
848
849 jmp_buf motdinterrupt;
850
851 void
852 motd(fname)
853 char *fname;
854 {
855 int fd, nchars;
856 sig_t oldint;
857 char tbuf[8192];
858
859 if ((fd = open(fname ? fname : _PATH_MOTDFILE, O_RDONLY, 0)) < 0)
860 return;
861 oldint = signal(SIGINT, sigint);
862 if (setjmp(motdinterrupt) == 0)
863 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
864 (void)write(fileno(stdout), tbuf, nchars);
865 (void)signal(SIGINT, oldint);
866 (void)close(fd);
867 }
868
869 /* ARGSUSED */
870 void
871 sigint(signo)
872 int signo;
873 {
874
875 longjmp(motdinterrupt, 1);
876 }
877
878 /* ARGSUSED */
879 void
880 timedout(signo)
881 int signo;
882 {
883
884 (void)fprintf(stderr, "Login timed out after %d seconds\n", timeout);
885 exit(0);
886 }
887
888 void
889 checknologin(fname)
890 char *fname;
891 {
892 int fd, nchars;
893 char tbuf[8192];
894
895 if ((fd = open(fname ? fname : _PATH_NOLOGIN, O_RDONLY, 0)) >= 0) {
896 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0)
897 (void)write(fileno(stdout), tbuf, nchars);
898 sleepexit(0);
899 }
900 }
901
902 void
903 dolastlog(quiet)
904 int quiet;
905 {
906 struct lastlog ll;
907 int fd;
908
909 if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
910 (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)), SEEK_SET);
911 if (!quiet) {
912 if (read(fd, (char *)&ll, sizeof(ll)) == sizeof(ll) &&
913 ll.ll_time != 0) {
914 (void)printf("Last login: %.*s ",
915 24, (char *)ctime(&ll.ll_time));
916 if (*ll.ll_host != '\0')
917 (void)printf("from %.*s\n",
918 (int)sizeof(ll.ll_host),
919 ll.ll_host);
920 else
921 (void)printf("on %.*s\n",
922 (int)sizeof(ll.ll_line),
923 ll.ll_line);
924 }
925 (void)lseek(fd, (off_t)(pwd->pw_uid * sizeof(ll)),
926 SEEK_SET);
927 }
928 memset((void *)&ll, 0, sizeof(ll));
929 (void)time(&ll.ll_time);
930 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
931 if (hostname)
932 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
933 (void)write(fd, (char *)&ll, sizeof(ll));
934 (void)close(fd);
935 }
936 }
937
938 void
939 badlogin(name)
940 char *name;
941 {
942
943 if (failures == 0)
944 return;
945 if (hostname) {
946 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s FROM %s",
947 failures, failures > 1 ? "S" : "", hostname);
948 syslog(LOG_AUTHPRIV|LOG_NOTICE,
949 "%d LOGIN FAILURE%s FROM %s, %s",
950 failures, failures > 1 ? "S" : "", hostname, name);
951 } else {
952 syslog(LOG_NOTICE, "%d LOGIN FAILURE%s ON %s",
953 failures, failures > 1 ? "S" : "", tty);
954 syslog(LOG_AUTHPRIV|LOG_NOTICE,
955 "%d LOGIN FAILURE%s ON %s, %s",
956 failures, failures > 1 ? "S" : "", tty, name);
957 }
958 }
959
960 const char *
961 stypeof(ttyid)
962 const char *ttyid;
963 {
964 struct ttyent *t;
965
966 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type : NULL);
967 }
968
969 void
970 sleepexit(eval)
971 int eval;
972 {
973
974 (void)sleep(5);
975 exit(eval);
976 }
977