Bug Summary

File:src/usr.bin/su/su.c
Warning:line 279, 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.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)
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_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);
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
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}