Bug Summary

File:src/usr.bin/su/su.c
Warning:line 265, column 11
Null pointer passed as 1st argument to string comparison 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 su.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/su/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/su/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/su/su.c
1/* $OpenBSD: su.c,v 1.84 2021/07/12 15:09:20 beck 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
11
Taking false branch
144 auth_setoption(as, "invokinguser", username);
145
146 if (username
11.1
'username' is equal to NULL
== NULL((void *)0) || (pwd = getpwnam(username)) == NULL((void *)0) ||
147 pwd->pw_uid != ruid)
148 pwd = getpwuid(ruid);
149 if (pwd == NULL((void *)0))
12
Assuming 'pwd' is not equal to NULL
13
Taking false branch
150 auth_errx(as, 1, "who are you?");
151 if ((username = strdup(pwd->pw_name)) == NULL((void *)0))
14
Assuming the condition is false
15
Taking false branch
152 auth_err(as, 1, NULL((void *)0));
153 if (asme
15.1
'asme' is 0
&& !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)
16
Assuming the condition is false
17
Taking false branch
164 err(1, "unveil %s", _PATH_LOGIN_CONF"/etc/login.conf");
165 if (unveil(_PATH_LOGIN_CONF"/etc/login.conf" ".db", "r") == -1)
18
Assuming the condition is false
19
Taking false branch
166 err(1, "unveil %s.db", _PATH_LOGIN_CONF"/etc/login.conf");
167 if (unveil(_PATH_AUTHPROGDIR"/usr/libexec/auth", "x") == -1)
20
Assuming the condition is false
21
Taking false branch
168 err(1, "unveil %s", _PATH_AUTHPROGDIR"/usr/libexec/auth");
169 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
22
Assuming the condition is false
23
Taking false branch
170 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
171 if (unveil(_PATH_DEVDB"/var/run/dev.db", "r") == -1)
24
Assuming the condition is false
25
Taking false branch
172 err(1, "unveil %s", _PATH_DEVDB"/var/run/dev.db");
173 if (unveil(_PATH_NOLOGIN"/etc/nologin", "r") == -1)
26
Assuming the condition is false
27
Taking false branch
174 err(1, "unveil %s", _PATH_NOLOGIN"/etc/nologin");
175
176 for (;;) {
28
Loop condition is true. Entering loop body
177 char *pw_class = class;
178
179 /* get target user, default to root unless in -L mode */
180 if (*argv) {
29
Assuming the condition is true
30
Taking true branch
181 user = *argv;
182 } else if (emlogin) {
183 if ((user = getloginname()) == NULL((void *)0)) {
184 auth_close(as);
185 exit(1);
186 }
187 } else {
188 user = "root";
189 }
190 /* style may be specified as part of the username */
191 if ((p = strchr(user, ':')) != NULL((void *)0)) {
31
Assuming the condition is false
32
Taking false branch
192 *p++ = '\0';
193 style = p; /* XXX overrides -a flag */
194 }
195
196 /*
197 * Clean and setup our current authentication session.
198 * Note that options *are* not cleared.
199 */
200 auth_clean(as);
201 if (auth_setitem(as, AUTHV_INTERACTIVE, "True") != 0 ||
33
Assuming the condition is true
202 auth_setitem(as, AUTHV_NAME, user) != 0)
203 auth_err(as, 1, NULL((void *)0));
204 if ((user = auth_getitem(as, AUTHV_NAME)) == NULL((void *)0))
34
Assuming the condition is false
35
Taking false branch
205 auth_errx(as, 1, "internal error");
206 if (auth_setpwd(as, NULL((void *)0)) || (pwd = auth_getpwd(as)) == NULL((void *)0)) {
36
Assuming the condition is false
37
Assuming the condition is false
38
Taking false branch
207 if (emlogin)
208 pwd = NULL((void *)0);
209 else
210 auth_errx(as, 1, "unknown login %s", user);
211 }
212
213 /* If the user specified a login class, use it */
214 if (pw_class
38.1
'pw_class' is equal to NULL
== NULL((void *)0) && pwd
38.2
'pwd' is not equal to NULL
!= NULL((void *)0))
39
Taking true branch
215 pw_class = pwd->pw_class;
216 if ((lc = login_getclass(pw_class)) == NULL((void *)0))
40
Assuming the condition is false
41
Taking false branch
217 auth_errx(as, 1, "no such login class: %s",
218 pw_class ? pw_class : LOGIN_DEFCLASS"default");
219
220 if ((ruid
41.1
'ruid' is equal to 0
== 0 && !emlogin
41.2
'emlogin' is 0
) ||
221 verify_user(username, pwd, style, lc, as) == 0)
222 break;
42
Execution continues on line 232
223 syslog(LOG_AUTH(4<<3)|LOG_WARNING4, "BAD SU %s to %s%s",
224 username, user, ontty());
225 if (!emlogin) {
226 fprintf(stderr(&__sF[2]), "Sorry\n");
227 auth_close(as);
228 exit(1);
229 }
230 fprintf(stderr(&__sF[2]), "Login incorrect\n");
231 }
232 if (pwd
42.1
'pwd' is not equal to NULL
== NULL((void *)0))
43
Taking false branch
233 auth_errx(as, 1, "internal error");
234
235 if (pledge("stdio unveil rpath getpw exec id", NULL((void *)0)) == -1)
44
Assuming the condition is false
45
Taking false branch
236 err(1, "pledge");
237
238 if (!altshell
45.1
'altshell' is 0
) {
46
Taking true branch
239 if (asme
46.1
'asme' is 0
) {
47
Taking false branch
240 /* must be root to override non-std target shell */
241 if (ruid && !chshell(pwd->pw_shell))
242 auth_errx(as, 1, "permission denied (shell).");
243 } else if (pwd->pw_shell && *pwd->pw_shell) {
48
Assuming field 'pw_shell' is non-null
49
Assuming the condition is true
50
Taking true branch
244 if ((shell = strdup(pwd->pw_shell)) == NULL((void *)0))
51
Value assigned to 'shell'
52
Assuming pointer value is null
53
Taking true branch
245 auth_err(as, 1, NULL((void *)0));
246 iscsh = UNSET;
247 } else {
248 shell = _PATH_BSHELL"/bin/sh";
249 iscsh = NO;
250 }
251 }
252
253 if (unveil(shell, "x") == -1)
54
Assuming the condition is false
55
Taking false branch
254 err(1, "unveil %s", shell);
255 if (unveil(pwd->pw_dir, "r") == -1)
56
Assuming the condition is false
57
Taking false branch
256 err(1, "unveil %s", pwd->pw_dir);
257
258 if ((p = strrchr(shell, '/')))
58
Assuming 'p' is null
59
Taking false branch
259 avshell = p+1;
260 else
261 avshell = shell;
60
Null pointer value stored to 'avshell'
262
263 /* if we're forking a csh, we want to slightly muck the args */
264 if (iscsh
60.1
'iscsh' is equal to UNSET
== UNSET)
61
Taking true branch
265 iscsh = strcmp(avshell, "csh") ? NO : YES;
62
Null pointer passed as 1st argument to string comparison function
266
267 if (!asme) {
268 if (asthem) {
269 p = getenv("TERM");
270 if ((environ = calloc(1, sizeof (char *))) == NULL((void *)0))
271 auth_errx(as, 1, "calloc");
272 if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH0x0004))
273 auth_err(as, 1, "unable to set user context");
274 if (p && setenv("TERM", p, 1) == -1)
275 auth_err(as, 1, "unable to set environment");
276
277 setegid(pwd->pw_gid);
278 seteuid(pwd->pw_uid);
279
280 homeless = chdir(pwd->pw_dir);
281 if (homeless == -1) {
282 if (login_getcapbool(lc, "requirehome", 0)) {
283 auth_err(as, 1, "%s", pwd->pw_dir);
284 } else {
285 if (unveil("/", "r") == -1)
286 err(1, "unveil /");
287 printf("No home directory %s!\n", pwd->pw_dir);
288 printf("Logging in with home = \"/\".\n");
289 if (chdir("/") == -1)
290 auth_err(as, 1, "/");
291 }
292 }
293 setegid(0); /* XXX use a saved gid instead? */
294 seteuid(0);
295 } else if (pwd->pw_uid == 0) {
296 if (setusercontext(lc,
297 pwd, pwd->pw_uid, LOGIN_SETPATH0x0004|LOGIN_SETUMASK0x0020))
298 auth_err(as, 1, "unable to set user context");
299 }
300 if (asthem || pwd->pw_uid) {
301 if (setenv("LOGNAME", pwd->pw_name, 1) == -1 ||
302 setenv("USER", pwd->pw_name, 1) == -1)
303 auth_err(as, 1, "unable to set environment");
304 }
305 if (setenv("HOME", homeless ? "/" : pwd->pw_dir, 1) == -1 ||
306 setenv("SHELL", shell, 1) == -1)
307 auth_err(as, 1, "unable to set environment");
308 } else if (altshell) {
309 if (setenv("SHELL", shell, 1) == -1)
310 auth_err(as, 1, "unable to set environment");
311 }
312 if (pledge("stdio rpath getpw exec id", NULL((void *)0)) == -1)
313 err(1, "pledge");
314
315 np = *argv ? argv : argv - 1;
316
317 if (iscsh == YES) {
318 if (fastlogin)
319 *np-- = "-f";
320 if (asme)
321 *np-- = "-m";
322
323 if (asthem)
324 avshellbuf[0] = '-';
325 else {
326 /* csh strips the first character... */
327 avshellbuf[0] = '_';
328 }
329 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
330 avshell = avshellbuf;
331 } else if (asthem && !fastlogin) {
332 avshellbuf[0] = '-';
333 strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1);
334 avshell = avshellbuf;
335 }
336
337 *np = avshell;
338
339 if (ruid != 0)
340 syslog(LOG_NOTICE5|LOG_AUTH(4<<3), "%s to %s%s",
341 username, user, ontty());
342
343 setpriority(PRIO_PROCESS0, 0, prio);
344 if (emlogin) {
345 flags = LOGIN_SETALL0x00ff & ~LOGIN_SETPATH0x0004;
346 /*
347 * Only call setlogin() if this process is a session leader.
348 * In practice, this means the login name will be set only if
349 * we are exec'd by a shell. This is important because
350 * otherwise the parent shell's login name would change too.
351 */
352 if (getsid(0) != getpid())
353 flags &= ~LOGIN_SETLOGIN0x0002;
354 } else {
355 flags = LOGIN_SETRESOURCES0x0010|LOGIN_SETGROUP0x0001|LOGIN_SETUSER0x0040;
356 if (asthem)
357 flags |= LOGIN_SETENV0x0080|LOGIN_SETPRIORITY0x0008|LOGIN_SETUMASK0x0020;
358 }
359 if (setusercontext(lc, pwd, pwd->pw_uid, flags) != 0)
360 auth_err(as, 1, "unable to set user context");
361
362 if (pledge("stdio rpath exec", NULL((void *)0)) == -1)
363 err(1, "pledge");
364
365 if (pwd->pw_uid && auth_approval(as, lc, pwd->pw_name, "su") == 0)
366 auth_errx(as, 1, "approval failure");
367 auth_close(as);
368
369 execv(shell, np);
370 err(1, "%s", shell);
371}
372
373int
374verify_user(char *from, struct passwd *pwd, char *style,
375 login_cap_t *lc, auth_session_t *as)
376{
377 struct group *gr;
378 char **g, *cp;
379 int authok;
380
381 /*
382 * If we are trying to become root and the default style
383 * is being used, don't bother to look it up (we might be
384 * be su'ing up to fix /etc/login.conf)
385 */
386 if ((pwd == NULL((void *)0) || pwd->pw_uid != 0 || style == NULL((void *)0) ||
387 strcmp(style, LOGIN_DEFSTYLE"passwd") != 0) &&
388 (style = login_getstyle(lc, style, "auth-su")) == NULL((void *)0))
389 auth_errx(as, 1, "invalid authentication type");
390
391 /*
392 * Let the authentication program know whether they are
393 * in group wheel or not (if trying to become super user)
394 */
395 if (pwd != NULL((void *)0) && pwd->pw_uid == 0 && (gr = getgrgid(0)) != NULL((void *)0) &&
396 gr->gr_mem != NULL((void *)0) && *(gr->gr_mem) != NULL((void *)0)) {
397 for (g = gr->gr_mem; *g; ++g) {
398 if (strcmp(from, *g) == 0) {
399 auth_setoption(as, "wheel", "yes");
400 break;
401 }
402 }
403 if (!*g)
404 auth_setoption(as, "wheel", "no");
405 }
406
407 auth_verify(as, style, NULL((void *)0), lc->lc_class, (char *)NULL((void *)0));
408 authok = auth_getstate(as);
409 if ((authok & AUTH_ALLOW(0x01 | 0x02 | 0x04)) == 0) {
410 if ((cp = auth_getvalue(as, "errormsg")) != NULL((void *)0))
411 fprintf(stderr(&__sF[2]), "%s\n", cp);
412 return(1);
413 }
414 return(0);
415}
416
417int
418chshell(const char *sh)
419{
420 char *cp;
421 int found = 0;
422
423 setusershell();
424 while ((cp = getusershell()) != NULL((void *)0)) {
425 if (strcmp(cp, sh) == 0) {
426 found = 1;
427 break;
428 }
429 }
430 endusershell();
431 return (found);
432}
433
434char *
435ontty(void)
436{
437 static char buf[PATH_MAX1024 + 4];
438 char *p;
439
440 buf[0] = 0;
441 if ((p = ttyname(STDERR_FILENO2)))
442 snprintf(buf, sizeof(buf), " on %s", p);
443 return (buf);
444}
445
446/*
447 * Allow for a '.' and 16 characters for any instance as well as
448 * space for a ':' and 16 characters defining the authentication type.
449 */
450#define NBUFSIZ(32 + 1 + 16 + 1 + 16) (UT_NAMESIZE32 + 1 + 16 + 1 + 16)
451
452char *
453getloginname(void)
454{
455 static char nbuf[NBUFSIZ(32 + 1 + 16 + 1 + 16)], *p;
456 int ch;
457
458 for (;;) {
459 printf("login: ");
460 for (p = nbuf; (ch = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
) != '\n'; ) {
461 if (ch == EOF(-1))
462 return (NULL((void *)0));
463 if (p < nbuf + (NBUFSIZ(32 + 1 + 16 + 1 + 16) - 1))
464 *p++ = ch;
465 }
466 if (p > nbuf) {
467 if (nbuf[0] == '-') {
468 fprintf(stderr(&__sF[2]),
469 "login names may not start with '-'.\n");
470 } else {
471 *p = '\0';
472 break;
473 }
474 }
475 }
476 return (nbuf);
477}
478
479void
480usage(void)
481{
482 extern char *__progname;
483
484 fprintf(stderr(&__sF[2]), "usage: %s [-fKLlm] [-a auth-type] [-c login-class] "
485 "[-s login-shell]\n"
486 "%-*s[login [shell arguments]]\n", __progname,
487 (int)strlen(__progname) + 8, "");
488 exit(1);
489}
490
491void
492auth_err(auth_session_t *as, int eval, const char *fmt, ...)
493{
494 va_list ap;
495
496 va_start(ap, fmt)__builtin_va_start(ap, fmt);
497 vwarn(fmt, ap);
498 va_end(ap)__builtin_va_end(ap);
499 auth_close(as);
500 exit(eval);
501}
502
503void
504auth_errx(auth_session_t *as, int eval, const char *fmt, ...)
505{
506 va_list ap;
507
508 va_start(ap, fmt)__builtin_va_start(ap, fmt);
509 vwarnx(fmt, ap);
510 va_end(ap)__builtin_va_end(ap);
511 auth_close(as);
512 exit(eval);
513}