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