Bug Summary

File:src/usr.bin/chpass/chpass.c
Warning:line 181, column 2
The return value from the call to 'setuid' is not checked. If an error occurs in 'setuid', 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 chpass.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/chpass/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/usr.bin/chpass/../../lib/libc/include -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/chpass/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/chpass/chpass.c
1/* $OpenBSD: chpass.c,v 1.48 2021/10/24 21:24:16 deraadt Exp $ */
2/* $NetBSD: chpass.c,v 1.8 1996/05/15 21:50:43 jtc Exp $ */
3
4/*-
5 * Copyright (c) 1988, 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#include <sys/resource.h>
34#include <sys/stat.h>
35#include <sys/time.h>
36#include <sys/uio.h>
37
38#include <err.h>
39#include <errno(*__errno()).h>
40#include <fcntl.h>
41#include <paths.h>
42#include <pwd.h>
43#include <signal.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48#include <util.h>
49
50#include "chpass.h"
51
52extern char *__progname;
53
54enum { NEWSH, LOADENTRY, EDITENTRY } op;
55uid_t uid;
56
57void baduser(void);
58void kbintr(int);
59void usage(void);
60
61int
62main(int argc, char *argv[])
63{
64 struct passwd *pw = NULL((void *)0), *opw = NULL((void *)0), lpw;
65 int i, ch, pfd, tfd, dfd;
66 char *tz, *arg = NULL((void *)0);
67 sigset_t fullset;
68
69 /* We need to use the system timezone for date conversions. */
70 if ((tz = getenv("TZ")) != NULL((void *)0)) {
71 unsetenv("TZ");
72 tzset();
73 setenv("TZ", tz, 1);
74 }
75
76 op = EDITENTRY;
77 while ((ch = getopt(argc, argv, "a:s:")) != -1)
78 switch(ch) {
79 case 'a':
80 op = LOADENTRY;
81 arg = optarg;
82 break;
83 case 's':
84 op = NEWSH;
85 arg = optarg;
86 break;
87 case '?':
88 default:
89 usage();
90 }
91 argc -= optind;
92 argv += optind;
93
94 uid = getuid();
95
96 if (op == EDITENTRY || op == NEWSH)
97 switch(argc) {
98 case 0:
99 pw = getpwuid_shadow(uid);
100 if (!pw)
101 errx(1, "unknown user: uid %u", uid);
102 break;
103 case 1:
104 pw = getpwnam_shadow(*argv);
105 if (!pw)
106 errx(1, "unknown user: %s", *argv);
107 if (uid && uid != pw->pw_uid)
108 baduser();
109 break;
110 default:
111 usage();
112 }
113
114 if (op == LOADENTRY) {
115 if (argc != 0)
116 errx(1, "option -a does not accept user argument");
117 if (uid)
118 baduser();
119 pw = &lpw;
120 if (!pw_scan(arg, pw, NULL((void *)0)))
121 exit(1);
122 opw = getpwnam_shadow(pw->pw_name);
123 }
124 if (opw == NULL((void *)0) && (opw = pw_dup(pw)) == NULL((void *)0))
125 err(1, NULL((void *)0));
126
127 /* Edit the user passwd information if requested. */
128 if (op == EDITENTRY) {
129 char tempname[] = _PATH_VARTMP"/var/tmp/" "pw.XXXXXXXXXX";
130 int edit_status;
131
132 if ((pw = pw_dup(pw)) == NULL((void *)0))
133 pw_error(NULL((void *)0), 1, 1);
134 dfd = mkostemp(tempname, O_CLOEXEC0x10000);
135 if (dfd == -1)
136 pw_error(tempname, 1, 1);
137 display(tempname, dfd, pw);
138
139 if (unveil(_PATH_BSHELL"/bin/sh", "x") == -1)
140 err(1, "unveil %s", _PATH_BSHELL"/bin/sh");
141 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
142 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
143 if (unveil(tempname, "rc") == -1)
144 err(1, "unveil %s", tempname);
145 if (pledge("stdio rpath wpath cpath id proc exec unveil",
146 NULL((void *)0)) == -1)
147 err(1, "pledge");
148
149 edit_status = edit(tempname, pw);
150 close(dfd);
151 unlink(tempname);
152
153 switch (edit_status) {
154 case EDIT_OK0:
155 break;
156 case EDIT_NOCHANGE1:
157 pw_error(NULL((void *)0), 0, 0);
158 break;
159 case EDIT_ERROR-1:
160 default:
161 pw_error(tempname, 1, 1);
162 break;
163 }
164 }
165
166 if (op == NEWSH) {
167 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
168 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
169 if (pledge("stdio rpath wpath cpath id proc exec unveil",
170 NULL((void *)0)) == -1)
171 err(1, "pledge");
172
173 /* protect p_shell -- it thinks NULL is /bin/sh */
174 if (!arg[0])
175 usage();
176 if (p_shell(arg, pw, NULL((void *)0)))
177 pw_error(NULL((void *)0), 0, 1);
178 }
179
180 /* Drop user's real uid and block all signals to avoid a DoS. */
181 setuid(0);
The return value from the call to 'setuid' is not checked. If an error occurs in 'setuid', the following code may execute with unexpected privileges
182 sigfillset(&fullset);
183 sigdelset(&fullset, SIGINT2);
184 sigprocmask(SIG_BLOCK1, &fullset, NULL((void *)0));
185
186 if (unveil(_PATH_MASTERPASSWD_LOCK"/etc/ptmp", "rwc") == -1)
187 err(1, "unveil %s", _PATH_MASTERPASSWD_LOCK"/etc/ptmp");
188 if (unveil(_PATH_MASTERPASSWD"/etc/master.passwd", "r") == -1)
189 err(1, "unveil %s", _PATH_MASTERPASSWD"/etc/master.passwd");
190 if (unveil(_PATH_PWD_MKDB"/usr/sbin/pwd_mkdb", "x") == -1)
191 err(1, "unveil %s", _PATH_PWD_MKDB"/usr/sbin/pwd_mkdb");
192 if (pledge("stdio rpath wpath cpath proc exec", NULL((void *)0)) == -1)
193 err(1, "pledge");
194
195 /* Get the passwd lock file and open the passwd file for reading. */
196 pw_init();
197 for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
198 if (i == 4)
199 (void)fputs("Attempting to lock password file, "
200 "please wait or press ^C to abort", stderr(&__sF[2]));
201 (void)signal(SIGINT2, kbintr);
202 if (i % 16 == 0)
203 fputc('.', stderr(&__sF[2]));
204 usleep(250000);
205 (void)signal(SIGINT2, SIG_IGN(void (*)(int))1);
206 }
207 if (i >= 4)
208 fputc('\n', stderr(&__sF[2]));
209 pfd = open(_PATH_MASTERPASSWD"/etc/master.passwd", O_RDONLY0x0000|O_CLOEXEC0x10000);
210 if (pfd == -1)
211 pw_error(_PATH_MASTERPASSWD"/etc/master.passwd", 1, 1);
212
213 /* Copy the passwd file to the lock file, updating pw. */
214 pw_copy(pfd, tfd, pw, opw);
215
216 /* If username changed we need to rebuild the entire db. */
217 arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL((void *)0);
218
219 /* Now finish the passwd file update. */
220 if (pw_mkdb(arg, 0) == -1)
221 pw_error(NULL((void *)0), 0, 1);
222 exit(0);
223}
224
225void
226baduser(void)
227{
228
229 errx(1, "%s", strerror(EACCES13));
230}
231
232/* ARGSUSED */
233void
234kbintr(int signo)
235{
236 dprintf(STDERR_FILENO2, "\n%s: %s unchanged\n",
237 __progname, _PATH_MASTERPASSWD"/etc/master.passwd");
238 _exit(1);
239}
240
241void
242usage(void)
243{
244
245 (void)fprintf(stderr(&__sF[2]), "usage: %s [-s newshell] [user]\n", __progname);
246 (void)fprintf(stderr(&__sF[2]), " %s -a list\n", __progname);
247 exit(1);
248}