Bug Summary

File:src/usr.bin/login/login.c
Warning:line 416, column 7
Null pointer passed as 1st argument to string length 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 false
8
Loop condition is false. Execution continues on line 261
188 switch (ch) {
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) {
211 warnc(EPERM1, "-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_UNSPEC0;
221 hints.ai_flags = AI_CANONNAME2;
222 error = getaddrinfo(lipaddr, NULL((void *)0), &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(EPERM1, "-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(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) {
9
Assuming the condition is false
10
Taking false branch
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) {
11
Assuming the condition is false
12
Taking false branch
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') {
13
Assuming 'ttyn' is not equal to NULL
14
Assuming the condition is false
15
Taking false branch
295 (void)snprintf(tname, sizeof(tname), "%s??", _PATH_TTY"/dev/tty");
296 ttyn = tname;
297 }
298 if ((tty = strrchr(ttyn, '/')))
16
Assuming 'tty' is null
17
Taking false branch
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) {
18
Assuming the condition is false
19
Taking false branch
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) {
20
Assuming the condition is true
21
Taking true branch
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) {
22
Assuming 'argc' is > 1
23
Taking true branch
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 */
24
Assuming the condition is false
25
Taking false branch
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)) {
26
Assuming the condition is false
27
Taking false branch
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) {
28
Loop condition is true. Entering loop body
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
28.1
'ask' is 1
) {
29
Taking true branch
393 fflag = 0;
394 getloginname();
395 }
396 if (needto
29.1
'needto' is 0
) {
30
Taking false branch
397 needto = 0;
398 alarm(timeout);
399 }
400 if ((style = strchr(username, ':')) != NULL((void *)0))
31
Assuming the condition is false
32
Taking false branch
401 *style++ = '\0';
402 free(fullname);
403 if (auth_setitem(as, AUTHV_NAME, username) < 0 ||
33
Assuming the condition is false
35
Taking false branch
404 (fullname = strdup(username)) == NULL((void *)0)) {
34
Assuming the condition is false
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)) {
36
Assuming the condition is false
37
Taking false branch
411 if (strncmp(instance + 1, "root", 4) == 0)
412 rootlogin = 1;
413 *instance = '\0';
414 }
415
416 if (strlen(username) > UT_NAMESIZE32)
38
Null pointer passed as 1st argument to string length function
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}