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