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