Bug Summary

File:src/usr.bin/su/su.c
Warning:line 277, column 4
The return value from the call to 'setegid' is not checked. If an error occurs in 'setegid', the following code may execute with unexpected privileges

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)
77 err(1, "pledge");
78
79 while ((ch = getopt(argc, argv, "a:c:fKLlms:-")) != -1)
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()))
123 prio = 0;
124 setpriority(PRIO_PROCESS0, 0, -2);
125 openlog("su", LOG_CONS0x02, 0);
126
127 if ((as = auth_open()) == NULL((void *)0)) {
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)
138 auth_errx(as, 1, "only the superuser may specify a login class");
139
140 if (ruid && altshell)
141 auth_errx(as, 1, "only the superuser may specify a login shell");
142
143 if (username != NULL((void *)0))
144 auth_setoption(as, "invokinguser", username);
145
146 if (username == NULL((void *)0) || (pwd = getpwnam(username)) == NULL((void *)0) ||
147 pwd->pw_uid != ruid)
148 pwd = getpwuid(ruid);
149 if (pwd == NULL((void *)0))
150 auth_errx(as, 1, "who are you?");
151 if ((username = strdup(pwd->pw_name)) == NULL((void *)0))
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_AUTHPROGDIR"/usr/libexec/auth", "x") == -1)
168 err(1, "unveil %s", _PATH_AUTHPROGDIR"/usr/libexec/auth");
169 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
170 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
171 if (unveil(_PATH_DEVDB"/var/run/dev.db", "r") == -1)
172 err(1, "unveil %s", _PATH_DEVDB"/var/run/dev.db");
173 if (unveil(_PATH_NOLOGIN"/etc/nologin", "r") == -1)
174 err(1, "unveil %s", _PATH_NOLOGIN"/etc/nologin");
175
176 for (;;) {
177 char *pw_class = class;
178
179 /* get target user, default to root unless in -L mode */
180 if (*argv) {
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)) {
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 ||
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))
205 auth_errx(as, 1, "internal error");
206 if (auth_setpwd(as, NULL((void *)0)) || (pwd = auth_getpwd(as)) == NULL((void *)0)) {
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 == NULL((void *)0) && pwd != NULL((void *)0))
215 pw_class = pwd->pw_class;
216 if ((lc = login_getclass(pw_class)) == NULL((void *)0))
217 auth_errx(as, 1, "no such login class: %s",
218 pw_class ? pw_class : LOGIN_DEFCLASS"default");
219
220 if ((ruid == 0 && !emlogin) ||
221 verify_user(username, pwd, style, lc, as) == 0)
222 break;
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 == NULL((void *)0))
233 auth_errx(as, 1, "internal error");
234
235 if (pledge("stdio unveil rpath getpw exec id", NULL((void *)0)) == -1)
236 err(1, "pledge");
237
238 if (!altshell) {
239 if (asme) {
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) {
244 if ((shell = strdup(pwd->pw_shell)) == NULL((void *)0))
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)
254 err(1, "unveil %s", shell);
255 if (unveil(pwd->pw_dir, "r") == -1)
256 err(1, "unveil %s", pwd->pw_dir);
257
258 if ((p = strrchr(shell, '/')))
259 avshell = p+1;
260 else
261 avshell = shell;
262
263 /* if we're forking a csh, we want to slightly muck the args */
264 if (iscsh == UNSET)
265 iscsh = strcmp(avshell, "csh") ? NO : YES;
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);
The return value from the call to 'setegid' is not checked. If an error occurs in 'setegid', the following code may execute with unexpected privileges
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}