1 /* $OpenBSD: login.c,v 1.74 2023/03/08 04:43:11 guenther Exp $ */
2 /* $NetBSD: login.c,v 1.13 1996/05/15 23:50:16 jtc Exp $ */
3
4 /*-
5 * Copyright (c) 1980, 1987, 1988, 1991, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32 /*-
33 * Copyright (c) 1995 Berkeley Software Design, Inc. All rights reserved.
34 *
35 * Redistribution and use in source and binary forms, with or without
36 * modification, are permitted provided that the following conditions
37 * are met:
38 * 1. Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
40 * 2. Redistributions in binary form must reproduce the above copyright
41 * notice, this list of conditions and the following disclaimer in the
42 * documentation and/or other materials provided with the distribution.
43 * 3. All advertising materials mentioning features or use of this software
44 * must display the following acknowledgement:
45 * This product includes software developed by Berkeley Software Design,
46 * Inc.
47 * 4. The name of Berkeley Software Design, Inc. may not be used to endorse
48 * or promote products derived from this software without specific prior
49 * written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND
52 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE
55 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * SUCH DAMAGE.
62 *
63 * BSDI $From: login.c,v 2.28 1999/09/08 22:35:36 prb Exp $
64 */
65
66 /*
67 * login [ name ]
68 * login -h hostname (for telnetd, etc.)
69 * login -f name (for pre-authenticated login: datakit, xterm, etc.)
70 * login -p (preserve existing environment; for getty)
71 */
72
73 #include <sys/socket.h>
74 #include <sys/stat.h>
75 #include <sys/time.h>
76 #include <sys/resource.h>
77 #include <sys/wait.h>
78
79 #include <err.h>
80 #include <errno.h>
81 #include <fcntl.h>
82 #include <grp.h>
83 #include <login_cap.h>
84 #include <netdb.h>
85 #include <pwd.h>
86 #include <signal.h>
87 #include <stdarg.h>
88 #include <stdio.h>
89 #include <stdlib.h>
90 #include <string.h>
91 #include <syslog.h>
92 #include <ttyent.h>
93 #include <unistd.h>
94 #include <limits.h>
95 #include <utmp.h>
96 #include <util.h>
97 #include <bsd_auth.h>
98
99 #include "pathnames.h"
100
101 void badlogin(char *);
102 void dolastlog(int);
103 void getloginname(void);
104 void motd(void);
105 void quickexit(int);
106 int rootterm(char *);
107 void sigint(int);
108 void sighup(int);
109 void sleepexit(int);
110 char *stypeof(char *);
111 void timedout(int);
112 int main(int, char **);
113
114 extern int check_failedlogin(uid_t);
115 extern void log_failedlogin(uid_t, char *, char *, char *);
116
117 #define TTYGRPNAME "tty" /* name of group to own ttys */
118
119 #define SECSPERDAY (24 * 60 * 60)
120 #define TWOWEEKS (2 * 7 * SECSPERDAY)
121
122 /*
123 * This bounds the time given to login; may be overridden by /etc/login.conf.
124 */
125 u_int timeout = 300;
126
127 struct passwd *pwd;
128 login_cap_t *lc = NULL;
129 auth_session_t *as = NULL;
130 int failures;
131 int needbanner = 1;
132 char term[64], *hostname, *tty;
133 char *style;
134 char *username = NULL, *rusername = NULL;
135
136 extern char **environ;
137
138 int
main(int argc,char * argv[])139 main(int argc, char *argv[])
140 {
141 char *domain, *p, *ttyn, *shell, *fullname, *instance;
142 char *lipaddr, *script, *ripaddr, *style, *type, *fqdn;
143 char tbuf[PATH_MAX + 2], tname[sizeof(_PATH_TTY) + 10];
144 char localhost[HOST_NAME_MAX+1], *copyright;
145 char mail[sizeof(_PATH_MAILDIR) + 1 + NAME_MAX];
146 int ask, ch, cnt, fflag, pflag, quietlog, rootlogin, lastchance;
147 int error, homeless, needto, authok, tries, backoff;
148 struct addrinfo *ai, hints;
149 struct rlimit cds, scds;
150 quad_t expire, warning;
151 struct utmp utmp;
152 struct group *gr;
153 struct stat st;
154 uid_t uid;
155
156 openlog("login", LOG_ODELAY, LOG_AUTH);
157
158 fqdn = lipaddr = ripaddr = fullname = type = NULL;
159 authok = 0;
160 tries = 10;
161 backoff = 3;
162
163 domain = NULL;
164 if (gethostname(localhost, sizeof(localhost)) == -1) {
165 syslog(LOG_ERR, "couldn't get local hostname: %m");
166 strlcpy(localhost, "localhost", sizeof(localhost));
167 } else if ((domain = strchr(localhost, '.'))) {
168 domain++;
169 if (*domain && strchr(domain, '.') == NULL)
170 domain = localhost;
171 }
172
173 if ((as = auth_open()) == NULL) {
174 syslog(LOG_ERR, "auth_open: %m");
175 err(1, "unable to initialize BSD authentication");
176 }
177 auth_setoption(as, "login", "yes");
178
179 /*
180 * -p is used by getty to tell login not to destroy the environment
181 * -f is used to skip a second login authentication
182 * -h is used by other servers to pass the name of the remote
183 * host to login so that it may be placed in utmp and wtmp
184 */
185 fflag = pflag = 0;
186 uid = getuid();
187 while ((ch = getopt(argc, argv, "fh:pu:L:R:")) != -1)
188 switch (ch) {
189 case 'f':
190 fflag = 1;
191 break;
192 case 'h':
193 if (uid) {
194 warnc(EPERM, "-h option");
195 quickexit(1);
196 }
197 free(fqdn);
198 if ((fqdn = strdup(optarg)) == NULL) {
199 warn(NULL);
200 quickexit(1);
201 }
202 auth_setoption(as, "fqdn", fqdn);
203 if (domain && (p = strchr(optarg, '.')) &&
204 strcasecmp(p+1, domain) == 0)
205 *p = 0;
206 hostname = optarg;
207 auth_setoption(as, "hostname", hostname);
208 break;
209 case 'L':
210 if (uid) {
211 warnc(EPERM, "-L option");
212 quickexit(1);
213 }
214 if (lipaddr) {
215 warnx("duplicate -L option");
216 quickexit(1);
217 }
218 lipaddr = optarg;
219 memset(&hints, 0, sizeof(hints));
220 hints.ai_family = PF_UNSPEC;
221 hints.ai_flags = AI_CANONNAME;
222 error = getaddrinfo(lipaddr, NULL, &hints, &ai);
223 if (!error) {
224 strlcpy(localhost, ai->ai_canonname,
225 sizeof(localhost));
226 freeaddrinfo(ai);
227 } else
228 strlcpy(localhost, lipaddr, sizeof(localhost));
229 auth_setoption(as, "local_addr", lipaddr);
230 break;
231 case 'p':
232 pflag = 1;
233 break;
234 case 'R':
235 if (uid) {
236 warnc(EPERM, "-R option");
237 quickexit(1);
238 }
239 if (ripaddr) {
240 warnx("duplicate -R option");
241 quickexit(1);
242 }
243 ripaddr = optarg;
244 auth_setoption(as, "remote_addr", ripaddr);
245 break;
246 case 'u':
247 if (uid) {
248 warnc(EPERM, "-u option");
249 quickexit(1);
250 }
251 rusername = optarg;
252 break;
253 default:
254 if (!uid)
255 syslog(LOG_ERR, "invalid flag %c", ch);
256 (void)fprintf(stderr,
257 "usage: login [-fp] [-h hostname] [-L local-addr] "
258 "[-R remote-addr] [-u username]\n\t[user]\n");
259 quickexit(1);
260 }
261 argc -= optind;
262 argv += optind;
263
264 if (*argv) {
265 username = *argv;
266 ask = 0;
267 } else
268 ask = 1;
269
270 /*
271 * If effective user is not root, just run su(1) to emulate login(1).
272 */
273 if (geteuid() != 0) {
274 char *av[5], **ap;
275
276 auth_close(as);
277 closelog();
278 closefrom(STDERR_FILENO + 1);
279
280 ap = av;
281 *ap++ = _PATH_SU;
282 *ap++ = "-L";
283 if (!pflag)
284 *ap++ = "-l";
285 if (!ask)
286 *ap++ = username;
287 *ap = NULL;
288 execv(_PATH_SU, av);
289 warn("unable to exec %s", _PATH_SU);
290 _exit(1);
291 }
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, '/')))
299 ++tty;
300 else
301 tty = ttyn;
302
303 /*
304 * Since login deals with sensitive information, turn off coredumps.
305 */
306 if (getrlimit(RLIMIT_CORE, &scds) == -1) {
307 syslog(LOG_ERR, "couldn't get core dump size: %m");
308 scds.rlim_cur = scds.rlim_max = QUAD_MIN;
309 }
310 cds.rlim_cur = cds.rlim_max = 0;
311 if (setrlimit(RLIMIT_CORE, &cds) == -1) {
312 syslog(LOG_ERR, "couldn't set core dump size to 0: %m");
313 scds.rlim_cur = scds.rlim_max = QUAD_MIN;
314 }
315
316 (void)signal(SIGALRM, timedout);
317 if (argc > 1) {
318 needto = 0;
319 (void)alarm(timeout);
320 } else
321 needto = 1;
322 (void)signal(SIGQUIT, SIG_IGN);
323 (void)signal(SIGINT, SIG_IGN);
324 (void)signal(SIGHUP, SIG_IGN);
325 (void)setpriority(PRIO_PROCESS, 0, 0);
326
327 /* get the default login class */
328 if ((lc = login_getclass(0)) == NULL) { /* get the default class */
329 warnx("Failure to retrieve default class");
330 quickexit(1);
331 }
332 timeout = (u_int)login_getcapnum(lc, "login-timeout", 300, 300);
333 if ((script = login_getcapstr(lc, "classify", NULL, NULL)) != NULL) {
334 unsetenv("AUTH_TYPE");
335 unsetenv("REMOTE_NAME");
336 if (script[0] != '/') {
337 syslog(LOG_ERR, "Invalid classify script: %s", script);
338 warnx("Classification failure");
339 quickexit(1);
340 }
341 shell = strrchr(script, '/') + 1;
342 auth_setstate(as, AUTH_OKAY);
343 if (fflag) {
344 auth_call(as, script, shell, "-f", "--", username,
345 (char *)NULL);
346 } else {
347 auth_call(as, script, shell, "--", username,
348 (char *)NULL);
349 }
350 if (!(auth_getstate(as) & AUTH_ALLOW))
351 quickexit(1);
352 auth_setenv(as);
353 if ((p = getenv("AUTH_TYPE")) != NULL &&
354 strncmp(p, "auth-", 5) == 0)
355 type = p;
356 if ((p = getenv("REMOTE_NAME")) != NULL)
357 hostname = p;
358 /*
359 * we may have changed some values, reset them
360 */
361 auth_clroptions(as);
362 if (type)
363 auth_setoption(as, "auth_type", type);
364 if (fqdn)
365 auth_setoption(as, "fqdn", fqdn);
366 if (hostname)
367 auth_setoption(as, "hostname", hostname);
368 if (lipaddr)
369 auth_setoption(as, "local_addr", lipaddr);
370 if (ripaddr)
371 auth_setoption(as, "remote_addr", ripaddr);
372 }
373
374 /*
375 * Request that things like the approval script print things
376 * to stdout (in particular, the nologins files)
377 */
378 auth_setitem(as, AUTHV_INTERACTIVE, "True");
379
380 for (cnt = 0;; ask = 1) {
381 /*
382 * Clean up our current authentication session.
383 * Options are not cleared so we need to clear any
384 * we might set below.
385 */
386 auth_clean(as);
387 auth_clroption(as, "style");
388 auth_clroption(as, "lastchance");
389
390 lastchance = 0;
391
392 if (ask) {
393 fflag = 0;
394 getloginname();
395 }
396 if (needto) {
397 needto = 0;
398 alarm(timeout);
399 }
400 if ((style = strchr(username, ':')) != NULL)
401 *style++ = '\0';
402 free(fullname);
403 if (auth_setitem(as, AUTHV_NAME, username) < 0 ||
404 (fullname = strdup(username)) == NULL) {
405 syslog(LOG_ERR, "%m");
406 warn(NULL);
407 quickexit(1);
408 }
409 rootlogin = 0;
410 if ((instance = strchr(username, '/')) != NULL) {
411 if (strncmp(instance + 1, "root", 4) == 0)
412 rootlogin = 1;
413 *instance = '\0';
414 }
415
416 if (strlen(username) > UT_NAMESIZE)
417 username[UT_NAMESIZE] = '\0';
418
419 /*
420 * Note if trying multiple user names; log failures for
421 * previous user name, but don't bother logging one failure
422 * for nonexistent name (mistyped username).
423 */
424 if (failures && strcmp(tbuf, username)) {
425 if (failures > (pwd ? 0 : 1))
426 badlogin(tbuf);
427 failures = 0;
428 }
429 (void)strlcpy(tbuf, username, sizeof(tbuf));
430
431 if ((pwd = getpwnam(username)) != NULL &&
432 auth_setpwd(as, pwd) < 0) {
433 syslog(LOG_ERR, "%m");
434 warn(NULL);
435 quickexit(1);
436 }
437
438 lc = login_getclass(pwd ? pwd->pw_class : NULL);
439 if (!lc)
440 goto failed;
441
442 style = login_getstyle(lc, style, type);
443 if (!style)
444 goto failed;
445
446 /*
447 * We allow "login-tries" attempts to login but start
448 * slowing down after "login-backoff" attempts.
449 */
450 tries = (int)login_getcapnum(lc, "login-tries", 10, 10);
451 backoff = (int)login_getcapnum(lc, "login-backoff", 3, 3);
452
453 /*
454 * Turn off the fflag if we have an invalid user
455 * or we are not root and we are trying to change uids.
456 */
457 if (!pwd || (uid && uid != pwd->pw_uid))
458 fflag = 0;
459
460 if (pwd && pwd->pw_uid == 0)
461 rootlogin = 1;
462
463 /*
464 * If we do not have the force flag authenticate the user
465 */
466 if (!fflag) {
467 lastchance =
468 login_getcaptime(lc, "password-dead", 0, 0) != 0;
469 if (lastchance)
470 auth_setoption(as, "lastchance", "yes");
471 /*
472 * Once we start asking for a password
473 * we want to log a failure on a hup.
474 */
475 signal(SIGHUP, sighup);
476 auth_verify(as, style, NULL, lc->lc_class, NULL);
477 authok = auth_getstate(as);
478 /*
479 * If their password expired and it has not been
480 * too long since then, give the user one last
481 * chance to change their password
482 */
483 if ((authok & AUTH_PWEXPIRED) && lastchance) {
484 authok = AUTH_OKAY;
485 } else
486 lastchance = 0;
487 if ((authok & AUTH_ALLOW) == 0)
488 goto failed;
489 if (auth_setoption(as, "style", style) < 0) {
490 syslog(LOG_ERR, "%m");
491 warn(NULL);
492 quickexit(1);
493 }
494 }
495 /*
496 * explicitly reject users without password file entries
497 */
498 if (pwd == NULL)
499 goto failed;
500
501 /*
502 * If trying to log in as root on an insecure terminal,
503 * refuse the login attempt unless the authentication
504 * style explicitly says a root login is okay.
505 */
506 if (pwd && rootlogin && !rootterm(tty))
507 goto failed;
508
509 if (fflag) {
510 type = 0;
511 style = "forced";
512 }
513 break;
514
515 failed:
516 if (authok & AUTH_SILENT)
517 quickexit(0);
518 if (rootlogin && !rootterm(tty)) {
519 warnx("%s login refused on this terminal.",
520 fullname);
521 if (hostname)
522 syslog(LOG_NOTICE,
523 "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",
524 fullname, rusername ? rusername : "",
525 rusername ? "@" : "", hostname, tty);
526 else
527 syslog(LOG_NOTICE,
528 "LOGIN %s REFUSED ON TTY %s",
529 fullname, tty);
530 } else {
531 if (!as || (p = auth_getvalue(as, "errormsg")) == NULL)
532 p = "Login incorrect";
533 (void)printf("%s\n", p);
534 }
535 failures++;
536 if (pwd)
537 log_failedlogin(pwd->pw_uid, hostname, rusername, tty);
538 /*
539 * By default, we allow 10 tries, but after 3 we start
540 * backing off to slow down password guessers.
541 */
542 if (++cnt > backoff) {
543 if (cnt >= tries) {
544 badlogin(username);
545 sleepexit(1);
546 }
547 sleep(1);
548 }
549 }
550
551 /* committed to login -- turn off timeout */
552 (void)alarm(0);
553
554 endpwent();
555
556 shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell);
557 if (*shell == '\0')
558 shell = _PATH_BSHELL;
559 else if (strlen(shell) >= PATH_MAX) {
560 syslog(LOG_ERR, "shell path too long: %s", shell);
561 warnx("invalid shell");
562 quickexit(1);
563 }
564
565 /* Destroy environment unless user has requested its preservation. */
566 if (!pflag) {
567 if ((environ = calloc(1, sizeof (char *))) == NULL)
568 err(1, "calloc");
569 } else {
570 char **cpp, **cpp2;
571
572 for (cpp2 = cpp = environ; *cpp; cpp++) {
573 if (strncmp(*cpp, "LD_", 3) &&
574 strncmp(*cpp, "ENV=", 4) &&
575 strncmp(*cpp, "BASH_ENV=", 9) &&
576 strncmp(*cpp, "IFS=", 4))
577 *cpp2++ = *cpp;
578 }
579 *cpp2 = 0;
580 }
581 /* Note: setusercontext(3) will set PATH */
582 if (setenv("HOME", pwd->pw_dir, 1) == -1 ||
583 setenv("SHELL", pwd->pw_shell, 1) == -1) {
584 warn("unable to setenv()");
585 quickexit(1);
586 }
587 if (term[0] == '\0')
588 (void)strlcpy(term, stypeof(tty), sizeof(term));
589 (void)snprintf(mail, sizeof(mail), "%s/%s", _PATH_MAILDIR,
590 pwd->pw_name);
591 if (setenv("TERM", term, 0) == -1 ||
592 setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
593 setenv("USER", pwd->pw_name, 1) == -1 ||
594 setenv("MAIL", mail, 1) == -1) {
595 warn("unable to setenv()");
596 quickexit(1);
597 }
598 if (hostname) {
599 if (setenv("REMOTEHOST", hostname, 1) == -1) {
600 warn("unable to setenv()");
601 quickexit(1);
602 }
603 }
604 if (rusername) {
605 if (setenv("REMOTEUSER", rusername, 1) == -1) {
606 warn("unable to setenv()");
607 quickexit(1);
608 }
609 }
610
611 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH)) {
612 warn("unable to set user context");
613 quickexit(1);
614 }
615 auth_setenv(as);
616
617 /* if user not super-user, check for disabled logins */
618 if (!rootlogin)
619 auth_checknologin(lc);
620
621 setegid(pwd->pw_gid);
622 seteuid(pwd->pw_uid);
623
624 homeless = chdir(pwd->pw_dir);
625 if (homeless) {
626 if (login_getcapbool(lc, "requirehome", 0)) {
627 (void)printf("No home directory %s!\n", pwd->pw_dir);
628 quickexit(1);
629 }
630 if (chdir("/"))
631 quickexit(0);
632 }
633
634 quietlog = ((strcmp(pwd->pw_shell, "/sbin/nologin") == 0) ||
635 login_getcapbool(lc, "hushlogin", 0) ||
636 (access(_PATH_HUSHLOGIN, F_OK) == 0));
637
638 seteuid(0);
639 setegid(0); /* XXX use a saved gid instead? */
640
641 if ((p = auth_getvalue(as, "warnmsg")) != NULL)
642 (void)printf("WARNING: %s\n\n", p);
643
644 expire = auth_check_expire(as);
645 if (expire < 0) {
646 (void)printf("Sorry -- your account has expired.\n");
647 quickexit(1);
648 } else if (expire > 0 && !quietlog) {
649 warning = login_getcaptime(lc, "expire-warn",
650 TWOWEEKS, TWOWEEKS);
651 if (expire < warning)
652 (void)printf("Warning: your account expires on %s",
653 ctime(&pwd->pw_expire));
654 }
655
656 /* Nothing else left to fail -- really log in. */
657 (void)signal(SIGHUP, SIG_DFL);
658 memset(&utmp, 0, sizeof(utmp));
659 (void)time(&utmp.ut_time);
660 (void)strncpy(utmp.ut_name, username, sizeof(utmp.ut_name));
661 if (hostname)
662 (void)strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host));
663 (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
664 login(&utmp);
665
666 if (!quietlog)
667 (void)check_failedlogin(pwd->pw_uid);
668 dolastlog(quietlog);
669
670 login_fbtab(tty, pwd->pw_uid, pwd->pw_gid);
671
672 (void)chown(ttyn, pwd->pw_uid,
673 (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
674
675 /* If fflag is on, assume caller/authenticator has logged root login. */
676 if (rootlogin && fflag == 0) {
677 if (hostname)
678 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s%s%s",
679 username, tty, rusername ? rusername : "",
680 rusername ? "@" : "", hostname);
681 else
682 syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty);
683 }
684
685 if (!quietlog) {
686 if ((copyright =
687 login_getcapstr(lc, "copyright", NULL, NULL)) != NULL)
688 auth_cat(copyright);
689 motd();
690 if (stat(mail, &st) == 0 && st.st_size != 0)
691 (void)printf("You have %smail.\n",
692 (st.st_mtime > st.st_atime) ? "new " : "");
693 }
694
695 (void)signal(SIGALRM, SIG_DFL);
696 (void)signal(SIGQUIT, SIG_DFL);
697 (void)signal(SIGHUP, SIG_DFL);
698 (void)signal(SIGINT, SIG_DFL);
699 (void)signal(SIGTSTP, SIG_IGN);
700
701 tbuf[0] = '-';
702 (void)strlcpy(tbuf + 1, (p = strrchr(shell, '/')) ?
703 p + 1 : shell, sizeof(tbuf) - 1);
704
705 if ((scds.rlim_cur != QUAD_MIN || scds.rlim_max != QUAD_MIN) &&
706 setrlimit(RLIMIT_CORE, &scds) == -1)
707 syslog(LOG_ERR, "couldn't reset core dump size: %m");
708
709 if (lastchance)
710 (void)printf("WARNING: Your password has expired."
711 " You must change your password, now!\n");
712
713 if (setusercontext(lc, pwd, rootlogin ? 0 : pwd->pw_uid,
714 LOGIN_SETALL & ~LOGIN_SETPATH) < 0) {
715 warn("unable to set user context");
716 quickexit(1);
717 }
718
719 if (homeless) {
720 (void)printf("No home directory %s!\n", pwd->pw_dir);
721 (void)printf("Logging in with home = \"/\".\n");
722 (void)setenv("HOME", "/", 1);
723 }
724
725 if (auth_approval(as, lc, NULL, "login") == 0) {
726 if (auth_getstate(as) & AUTH_EXPIRED)
727 (void)printf("Sorry -- your account has expired.\n");
728 else
729 (void)printf("approval failure\n");
730 quickexit(1);
731 }
732
733 /*
734 * The last thing we do is discard all of the open file descriptors.
735 * Last because the C library may have some open.
736 */
737 closefrom(STDERR_FILENO + 1);
738
739 /*
740 * Close the authentication session, make sure it is marked
741 * as okay so no files are removed.
742 */
743 auth_setstate(as, AUTH_OKAY);
744 auth_close(as);
745
746 execlp(shell, tbuf, (char *)NULL);
747 err(1, "%s", shell);
748 }
749
750 /*
751 * Allow for a '.' and 16 characters for any instance as well as
752 * space for a ':' and 16 characters defining the authentication type.
753 */
754 #define NBUFSIZ (UT_NAMESIZE + 1 + 16 + 1 + 16)
755
756 void
getloginname(void)757 getloginname(void)
758 {
759 static char nbuf[NBUFSIZ], *p;
760 int ch;
761
762 for (;;) {
763 (void)printf("login: ");
764 for (p = nbuf; (ch = getchar()) != '\n'; ) {
765 if (ch == EOF) {
766 badlogin(username);
767 quickexit(0);
768 }
769 if (p < nbuf + (NBUFSIZ - 1))
770 *p++ = ch;
771 }
772 if (p > nbuf) {
773 if (nbuf[0] == '-')
774 (void)fprintf(stderr,
775 "login names may not start with '-'.\n");
776 else {
777 *p = '\0';
778 username = nbuf;
779 break;
780 }
781 }
782 }
783 }
784
785 int
rootterm(char * ttyn)786 rootterm(char *ttyn)
787 {
788 struct ttyent *t;
789
790 /* XXX - stash output of getttynam() elsewhere */
791 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE);
792 }
793
794 void
motd(void)795 motd(void)
796 {
797 char tbuf[8192], *motd;
798 int fd, nchars;
799 struct sigaction sa, osa;
800
801 motd = login_getcapstr(lc, "welcome", _PATH_MOTDFILE, _PATH_MOTDFILE);
802
803 if ((fd = open(motd, O_RDONLY)) == -1)
804 return;
805
806 memset(&sa, 0, sizeof(sa));
807 sa.sa_handler = sigint;
808 sigemptyset(&sa.sa_mask);
809 sa.sa_flags = 0; /* don't set SA_RESTART */
810 (void)sigaction(SIGINT, &sa, &osa);
811
812 /* read and spew motd until EOF, error, or SIGINT */
813 while ((nchars = read(fd, tbuf, sizeof(tbuf))) > 0 &&
814 write(STDOUT_FILENO, tbuf, nchars) == nchars)
815 ;
816
817 (void)sigaction(SIGINT, &osa, NULL);
818 (void)close(fd);
819 }
820
821 void
sigint(int signo)822 sigint(int signo)
823 {
824 return; /* just interrupt syscall */
825 }
826
827 void
timedout(int signo)828 timedout(int signo)
829 {
830 dprintf(STDERR_FILENO,
831 "Login timed out after %d seconds\n", timeout);
832 if (username)
833 badlogin(username);
834 _exit(0);
835 }
836
837 void
dolastlog(int quiet)838 dolastlog(int quiet)
839 {
840 struct lastlog ll;
841 off_t pos;
842 int fd;
843
844 if ((fd = open(_PATH_LASTLOG, O_RDWR)) >= 0) {
845 pos = (off_t)pwd->pw_uid * sizeof(ll);
846 if (!quiet) {
847 if (pread(fd, &ll, sizeof(ll), pos) == sizeof(ll) &&
848 ll.ll_time != 0) {
849 (void)printf("Last login: %.*s ",
850 24-5, (char *)ctime(&ll.ll_time));
851 (void)printf("on %.*s",
852 (int)sizeof(ll.ll_line),
853 ll.ll_line);
854 if (*ll.ll_host != '\0')
855 (void)printf(" from %.*s",
856 (int)sizeof(ll.ll_host),
857 ll.ll_host);
858 (void)putchar('\n');
859 }
860 }
861 memset(&ll, 0, sizeof(ll));
862 (void)time(&ll.ll_time);
863 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
864 if (hostname)
865 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
866 (void)pwrite(fd, &ll, sizeof(ll), pos);
867 (void)close(fd);
868 }
869 }
870
871 void
badlogin(char * name)872 badlogin(char *name)
873 {
874 struct syslog_data sdata = SYSLOG_DATA_INIT;
875
876 if (failures == 0)
877 return;
878 if (hostname) {
879 syslog_r(LOG_NOTICE, &sdata,
880 "%d LOGIN FAILURE%s FROM %s%s%s",
881 failures, failures > 1 ? "S" : "",
882 rusername ? rusername : "", rusername ? "@" : "", hostname);
883 syslog_r(LOG_AUTHPRIV|LOG_NOTICE, &sdata,
884 "%d LOGIN FAILURE%s FROM %s%s%s, %s",
885 failures, failures > 1 ? "S" : "",
886 rusername ? rusername : "", rusername ? "@" : "",
887 hostname, name);
888 } else {
889 syslog_r(LOG_NOTICE, &sdata,
890 "%d LOGIN FAILURE%s ON %s",
891 failures, failures > 1 ? "S" : "", tty);
892 syslog_r(LOG_AUTHPRIV|LOG_NOTICE, &sdata,
893 "%d LOGIN FAILURE%s ON %s, %s",
894 failures, failures > 1 ? "S" : "", tty, name);
895 }
896 }
897
898 #undef UNKNOWN
899 #define UNKNOWN "su"
900
901 char *
stypeof(char * ttyid)902 stypeof(char *ttyid)
903 {
904 struct ttyent *t;
905
906 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type :
907 login_getcapstr(lc, "term", UNKNOWN, UNKNOWN));
908 }
909
910 void
sleepexit(int eval)911 sleepexit(int eval)
912 {
913 auth_close(as);
914 (void)sleep(5);
915 exit(eval);
916 }
917
918 void
quickexit(int eval)919 quickexit(int eval)
920 {
921 if (as)
922 auth_close(as);
923 exit(eval);
924 }
925
926
927 void
sighup(int signum)928 sighup(int signum)
929 {
930 if (username)
931 badlogin(username);
932 _exit(0);
933 }
934