Bug Summary

File:src/usr.bin/login/login.c
Warning:line 228, column 5
Null pointer passed as 2nd argument to string copy function

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name login.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/login/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/login/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/login/login.c
1/* $OpenBSD: login.c,v 1.73 2021/10/24 21:24:16 deraadt 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(*__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
101void badlogin(char *);
102void dolastlog(int);
103void getloginname(void);
104void motd(void);
105void quickexit(int);
106int rootterm(char *);
107void sigint(int);
108void sighup(int);
109void sleepexit(int);
110char *stypeof(char *);
111void timedout(int);
112int main(int, char **);
113
114extern int check_failedlogin(uid_t);
115extern void log_failedlogin(uid_t, char *, char *, char *);
116
117#define TTYGRPNAME"tty" "tty" /* name of group to own ttys */
118
119#define SECSPERDAY(24 * 60 * 60) (24 * 60 * 60)
120#define TWOWEEKS(2 * 7 * (24 * 60 * 60)) (2 * 7 * SECSPERDAY(24 * 60 * 60))
121
122/*
123 * This bounds the time given to login; may be overridden by /etc/login.conf.
124 */
125u_int timeout = 300;
126
127struct passwd *pwd;
128login_cap_t *lc = NULL((void *)0);
129auth_session_t *as = NULL((void *)0);
130int failures;
131int needbanner = 1;
132char term[64], *hostname, *tty;
133char *style;
134char *username = NULL((void *)0), *rusername = NULL((void *)0);
135
136extern char **environ;
137
138int
139main(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_MAX1024 + 2], tname[sizeof(_PATH_TTY"/dev/tty") + 10];
144 char localhost[HOST_NAME_MAX255+1], *copyright;
145 char mail[sizeof(_PATH_MAILDIR"/var/mail") + 1 + NAME_MAX255];
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_ODELAY0x04, LOG_AUTH(4<<3));
157
158 fqdn = lipaddr = ripaddr = fullname = type = NULL((void *)0);
159 authok = 0;
160 tries = 10;
161 backoff = 3;
162
163 domain = NULL((void *)0);
164 if (gethostname(localhost, sizeof(localhost)) == -1) {
1
Assuming the condition is false
2
Taking false branch
165 syslog(LOG_ERR3, "couldn't get local hostname: %m");
166 strlcpy(localhost, "localhost", sizeof(localhost));
167 } else if ((domain = strchr(localhost, '.'))) {
3
Assuming 'domain' is null
4
Taking false branch
168 domain++;
169 if (*domain && strchr(domain, '.') == NULL((void *)0))
170 domain = localhost;
171 }
172
173 if ((as = auth_open()) == NULL((void *)0)) {
5
Assuming the condition is false
6
Taking false branch
174 syslog(LOG_ERR3, "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)
7
Assuming the condition is true
8
Loop condition is true. Entering loop body
14
Assuming the condition is true
15
Loop condition is true. Entering loop body
21
Assuming the condition is true
22
Loop condition is true. Entering loop body
188 switch (ch) {
9
Control jumps to 'case 82:' at line 234
16
Control jumps to 'case 82:' at line 234
23
Control jumps to 'case 76:' at line 209
189 case 'f':
190 fflag = 1;
191 break;
192 case 'h':
193 if (uid) {
194 warnc(EPERM1, "-h option");
195 quickexit(1);
196 }
197 free(fqdn);
198 if ((fqdn = strdup(optarg)) == NULL((void *)0)) {
199 warn(NULL((void *)0));
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
23.1
'uid' is 0
) {
24
Taking false branch
211 warnc(EPERM1, "-L option");
212 quickexit(1);
213 }
214 if (lipaddr
24.1
'lipaddr' is null
) {
25
Taking false branch
215 warnx("duplicate -L option");
216 quickexit(1);
217 }
218 lipaddr = optarg;
26
Null pointer value stored to 'lipaddr'
219 memset(&hints, 0, sizeof(hints));
220 hints.ai_family = PF_UNSPEC0;
221 hints.ai_flags = AI_CANONNAME2;
222 error = getaddrinfo(lipaddr, NULL((void *)0), &hints, &ai);
223 if (!error) {
27
Assuming 'error' is not equal to 0
28
Taking false branch
224 strlcpy(localhost, ai->ai_canonname,
225 sizeof(localhost));
226 freeaddrinfo(ai);
227 } else
228 strlcpy(localhost, lipaddr, sizeof(localhost));
29
Null pointer passed as 2nd argument to string copy function
229 auth_setoption(as, "local_addr", lipaddr);
230 break;
231 case 'p':
232 pflag = 1;
233 break;
234 case 'R':
235 if (uid
16.1
'uid' is 0
) {
10
Assuming 'uid' is 0
11
Taking false branch
17
Taking false branch
236 warnc(EPERM1, "-R option");
237 quickexit(1);
238 }
239 if (ripaddr
11.1
'ripaddr' is null
) {
12
Taking false branch
18
Assuming 'ripaddr' is null
19
Taking false branch
240 warnx("duplicate -R option");
241 quickexit(1);
242 }
243 ripaddr = optarg;
244 auth_setoption(as, "remote_addr", ripaddr);
245 break;
13
Execution continues on line 187
20
Execution continues on line 187
246 case 'u':
247 if (uid) {
248 warnc(EPERM1, "-u option");
249 quickexit(1);
250 }
251 rusername = optarg;
252 break;
253 default:
254 if (!uid)
255 syslog(LOG_ERR3, "invalid flag %c", ch);
256 (void)fprintf(stderr(&__sF[2]),
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_FILENO2 + 1);
279
280 ap = av;
281 *ap++ = _PATH_SU"/usr/bin/su";
282 *ap++ = "-L";
283 if (!pflag)
284 *ap++ = "-l";
285 if (!ask)
286 *ap++ = username;
287 *ap = NULL((void *)0);
288 execv(_PATH_SU"/usr/bin/su", av);
289 warn("unable to exec %s", _PATH_SU"/usr/bin/su");
290 _exit(1);
291 }
292
293 ttyn = ttyname(STDIN_FILENO0);
294 if (ttyn == NULL((void *)0) || *ttyn == '\0') {
295 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY"/dev/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_CORE4, &scds) == -1) {
307 syslog(LOG_ERR3, "couldn't get core dump size: %m");
308 scds.rlim_cur = scds.rlim_max = QUAD_MIN(-0x7fffffffffffffffLL-1);
309 }
310 cds.rlim_cur = cds.rlim_max = 0;
311 if (setrlimit(RLIMIT_CORE4, &cds) == -1) {
312 syslog(LOG_ERR3, "couldn't set core dump size to 0: %m");
313 scds.rlim_cur = scds.rlim_max = QUAD_MIN(-0x7fffffffffffffffLL-1);
314 }
315
316 (void)signal(SIGALRM14, timedout);
317 if (argc > 1) {
318 needto = 0;
319 (void)alarm(timeout);
320 } else
321 needto = 1;
322 (void)signal(SIGQUIT3, SIG_IGN(void (*)(int))1);
323 (void)signal(SIGINT2, SIG_IGN(void (*)(int))1);
324 (void)signal(SIGHUP1, SIG_IGN(void (*)(int))1);
325 (void)setpriority(PRIO_PROCESS0, 0, 0);
326
327 /* get the default login class */
328 if ((lc = login_getclass(0)) == NULL((void *)0)) { /* 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((void *)0), NULL((void *)0))) != NULL((void *)0)) {
334 unsetenv("AUTH_TYPE");
335 unsetenv("REMOTE_NAME");
336 if (script[0] != '/') {
337 syslog(LOG_ERR3, "Invalid classify script: %s", script);
338 warnx("Classification failure");
339 quickexit(1);
340 }
341 shell = strrchr(script, '/') + 1;
342 auth_setstate(as, AUTH_OKAY0x01);
343 if (fflag) {
344 auth_call(as, script, shell, "-f", "--", username,
345 (char *)NULL((void *)0));
346 } else {
347 auth_call(as, script, shell, "--", username,
348 (char *)NULL((void *)0));
349 }
350 if (!(auth_getstate(as) & AUTH_ALLOW(0x01 | 0x02 | 0x04)))
351 quickexit(1);
352 auth_setenv(as);
353 if ((p = getenv("AUTH_TYPE")) != NULL((void *)0) &&
354 strncmp(p, "auth-", 5) == 0)
355 type = p;
356 if ((p = getenv("REMOTE_NAME")) != NULL((void *)0))
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((void *)0))
401 *style++ = '\0';
402 free(fullname);
403 if (auth_setitem(as, AUTHV_NAME, username) < 0 ||
404 (fullname = strdup(username)) == NULL((void *)0)) {
405 syslog(LOG_ERR3, "%m");
406 warn(NULL((void *)0));
407 quickexit(1);
408 }
409 rootlogin = 0;
410 if ((instance = strchr(username, '/')) != NULL((void *)0)) {
411 if (strncmp(instance + 1, "root", 4) == 0)
412 rootlogin = 1;
413 *instance = '\0';
414 }
415
416 if (strlen(username) > UT_NAMESIZE32)
417 username[UT_NAMESIZE32] = '\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((void *)0) &&
432 auth_setpwd(as, pwd) < 0) {
433 syslog(LOG_ERR3, "%m");
434 warn(NULL((void *)0));
435 quickexit(1);
436 }
437
438 lc = login_getclass(pwd ? pwd->pw_class : NULL((void *)0));
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(SIGHUP1, sighup);
476 auth_verify(as, style, NULL((void *)0), lc->lc_class, NULL((void *)0));
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_PWEXPIRED0x40) && lastchance) {
484 authok = AUTH_OKAY0x01;
485 } else
486 lastchance = 0;
487 if ((authok & AUTH_ALLOW(0x01 | 0x02 | 0x04)) == 0)
488 goto failed;
489 if (auth_setoption(as, "style", style) < 0) {
490 syslog(LOG_ERR3, "%m");
491 warn(NULL((void *)0));
492 quickexit(1);
493 }
494 }
495 /*
496 * explicitly reject users without password file entries
497 */
498 if (pwd == NULL((void *)0))
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
515failed:
516 if (authok & AUTH_SILENT0x08)
517 quickexit(0);
518 if (rootlogin && !rootterm(tty)) {
519 warnx("%s login refused on this terminal.",
520 fullname);
521 if (hostname)
522 syslog(LOG_NOTICE5,
523 "LOGIN %s REFUSED FROM %s%s%s ON TTY %s",
524 fullname, rusername ? rusername : "",
525 rusername ? "@" : "", hostname, tty);
526 else
527 syslog(LOG_NOTICE5,
528 "LOGIN %s REFUSED ON TTY %s",
529 fullname, tty);
530 } else {
531 if (!as || (p = auth_getvalue(as, "errormsg")) == NULL((void *)0))
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"/bin/sh";
559 else if (strlen(shell) >= PATH_MAX1024) {
560 syslog(LOG_ERR3, "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((void *)0))
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"/var/mail",
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_SETPATH0x0004)) {
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".hushlogin", F_OK0) == 0));
637
638 seteuid(0);
639 setegid(0); /* XXX use a saved gid instead? */
640
641 if ((p = auth_getvalue(as, "warnmsg")) != NULL((void *)0))
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(2 * 7 * (24 * 60 * 60)), TWOWEEKS(2 * 7 * (24 * 60 * 60)));
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(SIGHUP1, SIG_DFL(void (*)(int))0);
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"tty")) ? 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_NOTICE5, "ROOT LOGIN (%s) ON %s FROM %s%s%s",
679 username, tty, rusername ? rusername : "",
680 rusername ? "@" : "", hostname);
681 else
682 syslog(LOG_NOTICE5, "ROOT LOGIN (%s) ON %s", username, tty);
683 }
684
685 if (!quietlog) {
686 if ((copyright =
687 login_getcapstr(lc, "copyright", NULL((void *)0), NULL((void *)0))) != NULL((void *)0))
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_mtimest_mtim.tv_sec > st.st_atimest_atim.tv_sec) ? "new " : "");
693 }
694
695 (void)signal(SIGALRM14, SIG_DFL(void (*)(int))0);
696 (void)signal(SIGQUIT3, SIG_DFL(void (*)(int))0);
697 (void)signal(SIGHUP1, SIG_DFL(void (*)(int))0);
698 (void)signal(SIGINT2, SIG_DFL(void (*)(int))0);
699 (void)signal(SIGTSTP18, SIG_IGN(void (*)(int))1);
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(-0x7fffffffffffffffLL-1) || scds.rlim_max != QUAD_MIN(-0x7fffffffffffffffLL-1)) &&
706 setrlimit(RLIMIT_CORE4, &scds) == -1)
707 syslog(LOG_ERR3, "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_SETALL0x00ff & ~LOGIN_SETPATH0x0004) < 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((void *)0), "login") == 0) {
726 if (auth_getstate(as) & AUTH_EXPIRED0x20)
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_FILENO2 + 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_OKAY0x01);
744 auth_close(as);
745
746 execlp(shell, tbuf, (char *)NULL((void *)0));
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(32 + 1 + 16 + 1 + 16) (UT_NAMESIZE32 + 1 + 16 + 1 + 16)
755
756void
757getloginname(void)
758{
759 static char nbuf[NBUFSIZ(32 + 1 + 16 + 1 + 16)], *p;
760 int ch;
761
762 for (;;) {
763 (void)printf("login: ");
764 for (p = nbuf; (ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) != '\n'; ) {
765 if (ch == EOF(-1)) {
766 badlogin(username);
767 quickexit(0);
768 }
769 if (p < nbuf + (NBUFSIZ(32 + 1 + 16 + 1 + 16) - 1))
770 *p++ = ch;
771 }
772 if (p > nbuf) {
773 if (nbuf[0] == '-')
774 (void)fprintf(stderr(&__sF[2]),
775 "login names may not start with '-'.\n");
776 else {
777 *p = '\0';
778 username = nbuf;
779 break;
780 }
781 }
782 }
783}
784
785int
786rootterm(char *ttyn)
787{
788 struct ttyent *t;
789
790 /* XXX - stash output of getttynam() elsewhere */
791 return ((t = getttynam(ttyn)) && t->ty_status & TTY_SECURE0x02);
792}
793
794void
795motd(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"/etc/motd", _PATH_MOTDFILE"/etc/motd");
802
803 if ((fd = open(motd, O_RDONLY0x0000)) == -1)
804 return;
805
806 memset(&sa, 0, sizeof(sa));
807 sa.sa_handler__sigaction_u.__sa_handler = sigint;
808 sigemptyset(&sa.sa_mask);
809 sa.sa_flags = 0; /* don't set SA_RESTART */
810 (void)sigaction(SIGINT2, &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_FILENO1, tbuf, nchars) == nchars)
815 ;
816
817 (void)sigaction(SIGINT2, &osa, NULL((void *)0));
818 (void)close(fd);
819}
820
821/* ARGSUSED */
822void
823sigint(int signo)
824{
825 return; /* just interrupt syscall */
826}
827
828/* ARGSUSED */
829void
830timedout(int signo)
831{
832 dprintf(STDERR_FILENO2,
833 "Login timed out after %d seconds\n", timeout);
834 if (username)
835 badlogin(username);
836 _exit(0);
837}
838
839void
840dolastlog(int quiet)
841{
842 struct lastlog ll;
843 off_t pos;
844 int fd;
845
846 if ((fd = open(_PATH_LASTLOG"/var/log/lastlog", O_RDWR0x0002)) >= 0) {
847 pos = (off_t)pwd->pw_uid * sizeof(ll);
848 if (!quiet) {
849 if (pread(fd, &ll, sizeof(ll), pos) == sizeof(ll) &&
850 ll.ll_time != 0) {
851 (void)printf("Last login: %.*s ",
852 24-5, (char *)ctime(&ll.ll_time));
853 (void)printf("on %.*s",
854 (int)sizeof(ll.ll_line),
855 ll.ll_line);
856 if (*ll.ll_host != '\0')
857 (void)printf(" from %.*s",
858 (int)sizeof(ll.ll_host),
859 ll.ll_host);
860 (void)putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
;
861 }
862 }
863 memset(&ll, 0, sizeof(ll));
864 (void)time(&ll.ll_time);
865 (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
866 if (hostname)
867 (void)strncpy(ll.ll_host, hostname, sizeof(ll.ll_host));
868 (void)pwrite(fd, &ll, sizeof(ll), pos);
869 (void)close(fd);
870 }
871}
872
873void
874badlogin(char *name)
875{
876 struct syslog_data sdata = SYSLOG_DATA_INIT{0, (const char *)0, (1<<3), 0xff};
877
878 if (failures == 0)
879 return;
880 if (hostname) {
881 syslog_r(LOG_NOTICE5, &sdata,
882 "%d LOGIN FAILURE%s FROM %s%s%s",
883 failures, failures > 1 ? "S" : "",
884 rusername ? rusername : "", rusername ? "@" : "", hostname);
885 syslog_r(LOG_AUTHPRIV(10<<3)|LOG_NOTICE5, &sdata,
886 "%d LOGIN FAILURE%s FROM %s%s%s, %s",
887 failures, failures > 1 ? "S" : "",
888 rusername ? rusername : "", rusername ? "@" : "",
889 hostname, name);
890 } else {
891 syslog_r(LOG_NOTICE5, &sdata,
892 "%d LOGIN FAILURE%s ON %s",
893 failures, failures > 1 ? "S" : "", tty);
894 syslog_r(LOG_AUTHPRIV(10<<3)|LOG_NOTICE5, &sdata,
895 "%d LOGIN FAILURE%s ON %s, %s",
896 failures, failures > 1 ? "S" : "", tty, name);
897 }
898}
899
900#undef UNKNOWN"su"
901#define UNKNOWN"su" "su"
902
903char *
904stypeof(char *ttyid)
905{
906 struct ttyent *t;
907
908 return (ttyid && (t = getttynam(ttyid)) ? t->ty_type :
909 login_getcapstr(lc, "term", UNKNOWN"su", UNKNOWN"su"));
910}
911
912void
913sleepexit(int eval)
914{
915 auth_close(as);
916 (void)sleep(5);
917 exit(eval);
918}
919
920void
921quickexit(int eval)
922{
923 if (as)
924 auth_close(as);
925 exit(eval);
926}
927
928
929void
930sighup(int signum)
931{
932 if (username)
933 badlogin(username);
934 _exit(0);
935}