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