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