Bug Summary

File:src/usr.bin/su/su.c
Warning:line 151, column 25
Access to field 'pw_name' results in a dereference of a null pointer (loaded from variable 'pwd')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name su.c -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 -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -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/su/obj -resource-dir /usr/local/llvm16/lib/clang/16 -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/su/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -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/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/su/su.c
1/* $OpenBSD: su.c,v 1.89 2022/12/22 19:53:23 kn Exp $ */
2
3/*
4 * Copyright (c) 1988 The Regents of the University of California.
5 * 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/time.h>
33#include <sys/resource.h>
34
35#include <err.h>
36#include <errno(*__errno()).h>
37#include <grp.h>
38#include <login_cap.h>
39#include <paths.h>
40#include <pwd.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <unistd.h>
46#include <limits.h>
47#include <utmp.h>
48#include <stdarg.h>
49#include <bsd_auth.h>
50
51char *getloginname(void);
52char *ontty(void);
53int chshell(const char *);
54int verify_user(char *, struct passwd *, char *, login_cap_t *,
55 auth_session_t *);
56void usage(void);
57void auth_err(auth_session_t *, int, const char *, ...);
58void auth_errx(auth_session_t *, int, const char *, ...);
59
60int
61main(int argc, char **argv)
62{
63 int asme = 0, asthem = 0, ch, fastlogin = 0, emlogin = 0, prio;
64 int altshell = 0, homeless = 0;
65 char *user, *shell = NULL((void *)0), *avshell, *username, **np;
66 char *class = NULL((void *)0), *style = NULL((void *)0), *p;
67 enum { UNSET, YES, NO } iscsh = UNSET;
68 char avshellbuf[PATH_MAX1024];
69 extern char **environ;
70 auth_session_t *as;
71 struct passwd *pwd;
72 login_cap_t *lc;
73 uid_t ruid;
74 u_int flags;
75
76 if (pledge("stdio unveil rpath getpw proc exec id", NULL((void *)0)) == -1)
1
Assuming the condition is false
2
Taking false branch
77 err(1, "pledge");
78
79 while ((ch = getopt(argc, argv, "a:c:fKLlms:-")) != -1)
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 118
80 switch (ch) {
81 case 'a':
82 if (style)
83 usage();
84 style = optarg;
85 break;
86 case 'c':
87 if (class)
88 usage();
89 class = optarg;
90 break;
91 case 'f':
92 fastlogin = 1;
93 break;
94 case 'K':
95 if (style)
96 usage();
97 style = "passwd";
98 break;
99 case 'L':
100 emlogin = 1;
101 break;
102 case 'l':
103 case '-':
104 asme = 0;
105 asthem = 1;
106 break;
107 case 'm':
108 asme = 1;
109 asthem = 0;
110 break;
111 case 's':
112 altshell = 1;
113 shell = optarg;
114 break;
115 default:
116 usage();
117 }
118 argv += optind;
119
120 errno(*__errno()) = 0;
121 prio = getpriority(PRIO_PROCESS0, 0);
122 if (errno(*__errno()))
5
Assuming the condition is false
6
Taking false branch
123 prio = 0;
124 setpriority(PRIO_PROCESS0, 0, -2);
125 openlog("su", LOG_CONS0x02, 0);
126
127 if ((as = auth_open()) == NULL((void *)0)) {
7
Assuming the condition is false
8
Taking false branch
128 syslog(LOG_ERR3, "auth_open: %m");
129 err(1, "unable to initialize BSD authentication");
130 }
131 auth_setoption(as, "login", "yes");
132
133 /* get current login name and shell */
134 ruid = getuid();
135 username = getlogin();
136
137 if (ruid && class)
9
Assuming 'ruid' is 0
138 auth_errx(as, 1, "only the superuser may specify a login class");
139
140 if (ruid
9.1
'ruid' is 0
&& altshell)
141 auth_errx(as, 1, "only the superuser may specify a login shell");
142
143 if (username != NULL((void *)0))
10
Assuming 'username' is equal to NULL
144 auth_setoption(as, "invokinguser", username);
145
146 if (username
10.1
'username' is equal to NULL
== NULL((void *)0) || (pwd = getpwnam(username)) == NULL((void *)0) ||
147 pwd->pw_uid != ruid)
148 pwd = getpwuid(ruid);
11
Value assigned to 'pwd'
149 if (pwd == NULL((void *)0))
12
Assuming 'pwd' is equal to NULL
13
Taking true branch
150 auth_errx(as, 1, "who are you?");
151 if ((username = strdup(pwd->pw_name)) == NULL((void *)0))
14
Access to field 'pw_name' results in a dereference of a null pointer (loaded from variable 'pwd')
152 auth_err(as, 1, NULL((void *)0));
153 if (asme && !altshell) {
154 if (pwd->pw_shell && *pwd->pw_shell) {
155 if ((shell = strdup(pwd->pw_shell)) == NULL((void *)0))
156 auth_err(as, 1, NULL((void *)0));
157 } else {
158 shell = _PATH_BSHELL"/bin/sh";
159 iscsh = NO;
160 }
161 }
162
163 if (unveil(_PATH_LOGIN_CONF"/etc/login.conf", "r") == -1)
164 err(1, "unveil %s", _PATH_LOGIN_CONF"/etc/login.conf");
165 if (unveil(_PATH_LOGIN_CONF"/etc/login.conf" ".db", "r") == -1)
166 err(1, "unveil %s.db", _PATH_LOGIN_CONF"/etc/login.conf");
167 if (unveil(_PATH_LOGIN_CONF_D"/etc/login.conf.d", "r") == -1)
168 err(1, "unveil %s", _PATH_LOGIN_CONF_D"/etc/login.conf.d");
169 if (unveil(_PATH_AUTHPROGDIR"/usr/libexec/auth", "x") == -1)
170 err(1, "unveil %s", _PATH_AUTHPROGDIR"/usr/libexec/auth");
171 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
172 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
173 if (unveil(_PATH_DEVDB"/var/run/dev.db", "r") == -1)
174 err(1, "unveil %s", _PATH_DEVDB"/var/run/dev.db");
175 if (unveil(_PATH_NOLOGIN"/etc/nologin", "r") == -1)
176 err(1, "unveil %s", _PATH_NOLOGIN"/etc/nologin");
177
178 for (;;) {
179 char *pw_class = class;
180
181 /* get target user, default to root unless in -L mode */
182 if (*argv) {
183 user = *argv;
184 } else if (emlogin) {
185 if ((user = getloginname()) == NULL((void *)0)) {
186 auth_close(as);
187 exit(1);
188 }
189 } else {
190 user = "root";
191 }
192 /* style may be specified as part of the username */
193 if ((p = strchr(user, ':')) != NULL((void *)0)) {
194 *p++ = '\0';
195 style = p; /* XXX overrides -a flag */
196 }
197
198 /*
199 * Clean and setup our current authentication session.
200 * Note that options *are* not cleared.
201 */
202 auth_clean(as);
203 if (auth_setitem(as, AUTHV_INTERACTIVE, "True") != 0 ||
204 auth_setitem(as, AUTHV_NAME, user) != 0)
205 auth_err(as, 1, NULL((void *)0));
206 if ((user = auth_getitem(as, AUTHV_NAME)) == NULL((void *)0))
207 auth_errx(as, 1, "internal error");
208 if (auth_setpwd(as, NULL((void *)0)) || (pwd = auth_getpwd(as)) == NULL((void *)0)) {
209 if (emlogin)
210 pwd = NULL((void *)0);
211 else
212 auth_errx(as, 1, "unknown login %s", user);
213 }
214
215 /* If the user specified a login class, use it */
216 if (pw_class == NULL((void *)0) && pwd != NULL((void *)0))
217 pw_class = pwd->pw_class;
218 if ((lc = login_getclass(pw_class)) == NULL((void *)0))
219 auth_errx(as, 1, "no such login class: %s",
220 pw_class ? pw_class : LOGIN_DEFCLASS"default");
221
222 if ((ruid == 0 && !emlogin) ||
223 verify_user(username, pwd, style, lc, as) == 0)
224 break;
225 syslog(LOG_AUTH(4<<3)|LOG_WARNING4, "BAD SU %s to %s%s",
226 username, user, ontty());
227 if (!emlogin) {
228 fprintf(stderr(&__sF[2]), "Sorry\n");
229 auth_close(as);
230 exit(1);
231 }
232 fprintf(stderr(&__sF[2]), "Login incorrect\n");
233 }
234 if (pwd == NULL((void *)0))
235 auth_errx(as, 1, "internal error");
236
237 if (pledge("stdio unveil rpath getpw exec id", NULL((void *)0)) == -1)
238 err(1, "pledge");
239
240 if (!altshell) {
241 if (asme) {
242 /* must be root to override non-std target shell */
243 if (ruid && !chshell(pwd->pw_shell))
244 auth_errx(as, 1, "permission denied (shell).");
245 } else if (pwd->pw_shell && *pwd->pw_shell) {
246 if ((shell = strdup(pwd->pw_shell)) == NULL((void *)0))
247 auth_err(as, 1, NULL((void *)0));
248 iscsh = UNSET;
249 } else {
250 shell = _PATH_BSHELL"/bin/sh";
251 iscsh = NO;
252 }
253 }
254
255 if (unveil(shell, "x") == -1)
256 err(1, "unveil %s", shell);
257 if (unveil(pwd->pw_dir, "r") == -1)
258 err(1, "unveil %s", pwd->pw_dir);
259
260 if ((p = strrchr(shell, '/')))
261 avshell = p+1;
262 else
263 avshell = shell;
264
265 /* if we're forking a csh, we want to slightly muck the args */
266 if (iscsh == UNSET)
267 iscsh = strcmp(avshell, "csh") ? NO : YES;
268
269 if (!asme) {
270 if (asthem) {
271 p = getenv("TERM");
272 if ((environ = calloc(1, sizeof (char *))) == NULL((void *)0))
273 auth_errx(as, 1, "calloc");
274 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH0x0004))
275 auth_err(as, 1, "unable to set user context");
276 if (p && setenv("TERM", p, 1) == -1)
277 auth_err(as, 1, "unable to set environment");
278
279 setegid(pwd->pw_gid);
280 seteuid(pwd->pw_uid);
281
282 homeless = chdir(pwd->pw_dir);
283 if (homeless == -1) {
284 if (login_getcapbool(lc, "requirehome", 0)) {
285 auth_err(as, 1, "%s", pwd->pw_dir);
286 } else {
287 if (unveil("/", "r") == -1)
288 err(1, "unveil /");
289 printf("No home directory %s!\n", pwd->pw_dir);
290 printf("Logging in with home = \"/\".\n");
291 if (chdir("/") == -1)
292 auth_err(as, 1, "/");
293 }
294 }
295 setegid(0); /* XXX use a saved gid instead? */
296 seteuid(0);
297 } else if (pwd->pw_uid == 0) {
298 if (setusercontext(lc,
299 pwd, pwd->pw_uid, LOGIN_SETPATH0x0004|LOGIN_SETUMASK0x0020))
300 auth_err(as, 1, "unable to set user context");
301 }
302 if (asthem || pwd->pw_uid) {
303 if (setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
304 setenv("USER", pwd->pw_name, 1) == -1)
305 auth_err(as, 1, "unable to set environment");
306 }
307 if (setenv("HOME", homeless ? "/" : pwd->pw_dir, 1) == -1 ||
308 setenv("SHELL", shell, 1) == -1)
309 auth_err(as, 1, "unable to set environment");
310 } else if (altshell) {
311 if (setenv("SHELL", shell, 1) == -1)
312 auth_err(as, 1, "unable to set environment");
313 }
314 if (pledge("stdio rpath getpw exec id", NULL((void *)0)) == -1)
315 err(1, "pledge");
316
317 np = *argv ? argv : argv - 1;
318
319 if (iscsh == YES) {
320 if (fastlogin)
321 *np-- = "-f";
322 if (asme)
323 *np-- = "-m";
324
325 if (asthem)
326 avshellbuf[0] = '-';
327 else {
328 /* csh strips the first character... */
329 avshellbuf[0] = '_';
330 }
331 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
332 avshell = avshellbuf;
333 } else if (asthem && !fastlogin) {
334 avshellbuf[0] = '-';
335 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
336 avshell = avshellbuf;
337 }
338
339 *np = avshell;
340
341 if (ruid != 0)
342 syslog(LOG_NOTICE5|LOG_AUTH(4<<3), "%s to %s%s",
343 username, user, ontty());
344
345 setpriority(PRIO_PROCESS0, 0, prio);
346 if (emlogin) {
347 flags = LOGIN_SETALL0x01ff & ~LOGIN_SETPATH0x0004;
348 /*
349 * Only call setlogin() if this process is a session leader.
350 * In practice, this means the login name will be set only if
351 * we are exec'd by a shell. This is important because
352 * otherwise the parent shell's login name would change too.
353 */
354 if (getsid(0) != getpid())
355 flags &= ~LOGIN_SETLOGIN0x0002;
356 } else {
357 flags = LOGIN_SETRESOURCES0x0010|LOGIN_SETGROUP0x0001|LOGIN_SETUSER0x0040;
358 if (!asme)
359 flags |= LOGIN_SETRTABLE0x0100;
360 if (asthem)
361 flags |= LOGIN_SETENV0x0080|LOGIN_SETPRIORITY0x0008|LOGIN_SETUMASK0x0020;
362 }
363 if (setusercontext(lc, pwd, pwd->pw_uid, flags) != 0)
364 auth_err(as, 1, "unable to set user context");
365
366 if (pledge("stdio rpath exec", NULL((void *)0)) == -1)
367 err(1, "pledge");
368
369 if (pwd->pw_uid && auth_approval(as, lc, pwd->pw_name, "su") == 0)
370 auth_errx(as, 1, "approval failure");
371 auth_close(as);
372
373 execv(shell, np);
374 err(1, "%s", shell);
375}
376
377int
378verify_user(char *from, struct passwd *pwd, char *style,
379 login_cap_t *lc, auth_session_t *as)
380{
381 struct group *gr;
382 char **g, *cp;
383 int authok;
384
385 /*
386 * If we are trying to become root and the default style
387 * is being used, don't bother to look it up (we might be
388 * be su'ing up to fix /etc/login.conf)
389 */
390 if ((pwd == NULL((void *)0) || pwd->pw_uid != 0 || style == NULL((void *)0) ||
391 strcmp(style, LOGIN_DEFSTYLE"passwd") != 0) &&
392 (style = login_getstyle(lc, style, "auth-su")) == NULL((void *)0))
393 auth_errx(as, 1, "invalid authentication type");
394
395 /*
396 * Let the authentication program know whether they are
397 * in group wheel or not (if trying to become super user)
398 */
399 if (pwd != NULL((void *)0) && pwd->pw_uid == 0 && (gr = getgrgid(0)) != NULL((void *)0) &&
400 gr->gr_mem != NULL((void *)0) && *(gr->gr_mem) != NULL((void *)0)) {
401 for (g = gr->gr_mem; *g; ++g) {
402 if (strcmp(from, *g) == 0) {
403 auth_setoption(as, "wheel", "yes");
404 break;
405 }
406 }
407 if (!*g)
408 auth_setoption(as, "wheel", "no");
409 }
410
411 auth_verify(as, style, NULL((void *)0), lc->lc_class, (char *)NULL((void *)0));
412 authok = auth_getstate(as);
413 if ((authok & AUTH_ALLOW(0x01 | 0x02 | 0x04)) == 0) {
414 if ((cp = auth_getvalue(as, "errormsg")) != NULL((void *)0))
415 fprintf(stderr(&__sF[2]), "%s\n", cp);
416 return(1);
417 }
418 return(0);
419}
420
421int
422chshell(const char *sh)
423{
424 char *cp;
425 int found = 0;
426
427 setusershell();
428 while ((cp = getusershell()) != NULL((void *)0)) {
429 if (strcmp(cp, sh) == 0) {
430 found = 1;
431 break;
432 }
433 }
434 endusershell();
435 return (found);
436}
437
438char *
439ontty(void)
440{
441 static char buf[PATH_MAX1024 + 4];
442 char *p;
443
444 buf[0] = 0;
445 if ((p = ttyname(STDERR_FILENO2)))
446 snprintf(buf, sizeof(buf), " on %s", p);
447 return (buf);
448}
449
450/*
451 * Allow for a '.' and 16 characters for any instance as well as
452 * space for a ':' and 16 characters defining the authentication type.
453 */
454#define NBUFSIZ(32 + 1 + 16 + 1 + 16) (UT_NAMESIZE32 + 1 + 16 + 1 + 16)
455
456char *
457getloginname(void)
458{
459 static char nbuf[NBUFSIZ(32 + 1 + 16 + 1 + 16)], *p;
460 int ch;
461
462 for (;;) {
463 printf("login: ");
464 for (p = nbuf; (ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) != '\n'; ) {
465 if (ch == EOF(-1))
466 return (NULL((void *)0));
467 if (p < nbuf + (NBUFSIZ(32 + 1 + 16 + 1 + 16) - 1))
468 *p++ = ch;
469 }
470 if (p > nbuf) {
471 if (nbuf[0] == '-') {
472 fprintf(stderr(&__sF[2]),
473 "login names may not start with '-'.\n");
474 } else {
475 *p = '\0';
476 break;
477 }
478 }
479 }
480 return (nbuf);
481}
482
483void
484usage(void)
485{
486 extern char *__progname;
487
488 fprintf(stderr(&__sF[2]), "usage: %s [-fKLlm] [-a auth-type] [-c login-class] "
489 "[-s login-shell]\n"
490 "%-*s[login [shell-argument ...]]\n", __progname,
491 (int)strlen(__progname) + 8, "");
492 exit(1);
493}
494
495void
496auth_err(auth_session_t *as, int eval, const char *fmt, ...)
497{
498 va_list ap;
499
500 va_start(ap, fmt)__builtin_va_start((ap), fmt);
501 vwarn(fmt, ap);
502 va_end(ap)__builtin_va_end((ap));
503 auth_close(as);
504 exit(eval);
505}
506
507void
508auth_errx(auth_session_t *as, int eval, const char *fmt, ...)
509{
510 va_list ap;
511
512 va_start(ap, fmt)__builtin_va_start((ap), fmt);
513 vwarnx(fmt, ap);
514 va_end(ap)__builtin_va_end((ap));
515 auth_close(as);
516 exit(eval);
517}