Bug Summary

File:src/lib/libutil/passwd.c
Warning:line 237, column 11
This function call is prohibited after a successful vfork

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 passwd.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 -fhalf-no-semantic-interposition -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/lib/libutil/obj -resource-dir /usr/local/lib/clang/13.0.0 -D PIC -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/lib/libutil/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/lib/libutil/passwd.c
1/* $OpenBSD: passwd.c,v 1.56 2019/06/28 13:32:43 deraadt Exp $ */
2
3/*
4 * Copyright (c) 1987, 1993, 1994, 1995
5 * The Regents of the University of California. 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/types.h>
33#include <sys/stat.h>
34#include <sys/time.h>
35#include <sys/resource.h>
36#include <sys/wait.h>
37
38#include <fcntl.h>
39#include <unistd.h>
40#include <stdlib.h>
41#include <stdio.h>
42#include <string.h>
43#include <ctype.h>
44#include <pwd.h>
45#include <err.h>
46#include <errno(*__errno()).h>
47#include <paths.h>
48#include <signal.h>
49#include <limits.h>
50
51#include "util.h"
52
53static char pw_defdir[] = "/etc";
54static char *pw_dir = pw_defdir;
55static char *pw_lck;
56
57char *
58pw_file(const char *nm)
59{
60 const char *p = strrchr(nm, '/');
61 char *new_nm;
62
63 if (p)
64 p++;
65 else
66 p = nm;
67
68 if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1)
69 return NULL((void *)0);
70 return new_nm;
71}
72
73void
74pw_setdir(const char *dir)
75{
76 char *p;
77
78 if (strcmp (dir, pw_dir) == 0)
79 return;
80 if (pw_dir != pw_defdir)
81 free(pw_dir);
82 pw_dir = strdup(dir);
83 if (pw_lck) {
84 p = pw_file(pw_lck);
85 free(pw_lck);
86 pw_lck = p;
87 }
88}
89
90
91int
92pw_lock(int retries)
93{
94 int i, fd;
95 mode_t old_mode;
96
97 if (!pw_lck) {
98 errno(*__errno()) = EINVAL22;
99 return (-1);
100 }
101 /* Acquire the lock file. */
102 old_mode = umask(0);
103 fd = open(pw_lck, O_WRONLY0x0001|O_CREAT0x0200|O_EXCL0x0800|O_CLOEXEC0x10000, 0600);
104 for (i = 0; i < retries && fd == -1 && errno(*__errno()) == EEXIST17; i++) {
105 sleep(1);
106 fd = open(pw_lck, O_WRONLY0x0001|O_CREAT0x0200|O_EXCL0x0800|O_CLOEXEC0x10000, 0600);
107 }
108 (void) umask(old_mode);
109 return (fd);
110}
111
112int
113pw_mkdb(char *username, int flags)
114{
115 int pstat, ac;
116 pid_t pid;
117 char *av[8];
118 struct stat sb;
119
120 if (pw_lck == NULL((void *)0))
121 return(-1);
122
123 /* A zero length passwd file is never ok */
124 if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) {
125 warnx("%s is zero length", pw_lck);
126 return (-1);
127 }
128
129 ac = 0;
130 av[ac++] = "pwd_mkdb";
131 av[ac++] = "-d";
132 av[ac++] = pw_dir;
133 if (flags & _PASSWORD_SECUREONLY0x01)
134 av[ac++] = "-s";
135 else if (!(flags & _PASSWORD_OMITV70x02))
136 av[ac++] = "-p";
137 if (username) {
138 av[ac++] = "-u";
139 av[ac++] = username;
140 }
141 av[ac++] = pw_lck;
142 av[ac] = NULL((void *)0);
143
144 pid = vfork();
145 if (pid == -1)
146 return (-1);
147 if (pid == 0) {
148 if (pw_lck)
149 execv(_PATH_PWD_MKDB"/usr/sbin/pwd_mkdb", av);
150 _exit(1);
151 }
152 pid = waitpid(pid, &pstat, 0);
153 if (pid == -1 || !WIFEXITED(pstat)(((pstat) & 0177) == 0) || WEXITSTATUS(pstat)(int)(((unsigned)(pstat) >> 8) & 0xff) != 0)
154 return (-1);
155 return (0);
156}
157
158int
159pw_abort(void)
160{
161 return (pw_lck ? unlink(pw_lck) : -1);
162}
163
164/* Everything below this point is intended for the convenience of programs
165 * which allow a user to interactively edit the passwd file. Errors in the
166 * routines below will cause the process to abort. */
167
168static pid_t editpid = -1;
169
170static void
171pw_cont(int signo)
172{
173 int save_errno = errno(*__errno());
174
175 if (editpid != -1)
176 kill(editpid, signo);
177 errno(*__errno()) = save_errno;
178}
179
180void
181pw_init(void)
182{
183 struct rlimit rlim;
184
185 /* Unlimited resource limits. */
186 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY(((rlim_t)1 << 63) - 1);
187 (void)setrlimit(RLIMIT_CPU0, &rlim);
188 (void)setrlimit(RLIMIT_FSIZE1, &rlim);
189 (void)setrlimit(RLIMIT_STACK3, &rlim);
190 (void)setrlimit(RLIMIT_DATA2, &rlim);
191 (void)setrlimit(RLIMIT_RSS5, &rlim);
192
193 /* Don't drop core (not really necessary, but GP's). */
194 rlim.rlim_cur = rlim.rlim_max = 0;
195 (void)setrlimit(RLIMIT_CORE4, &rlim);
196
197 /* Turn off signals. */
198 (void)signal(SIGALRM14, SIG_IGN(void (*)(int))1);
199 (void)signal(SIGHUP1, SIG_IGN(void (*)(int))1);
200 (void)signal(SIGINT2, SIG_IGN(void (*)(int))1);
201 (void)signal(SIGPIPE13, SIG_IGN(void (*)(int))1);
202 (void)signal(SIGQUIT3, SIG_IGN(void (*)(int))1);
203 (void)signal(SIGTERM15, SIG_IGN(void (*)(int))1);
204 (void)signal(SIGCONT19, pw_cont);
205
206 if (!pw_lck)
207 pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK"/etc/ptmp");
208}
209
210void
211pw_edit(int notsetuid, const char *filename)
212{
213 int pstat;
214 char *p;
215 char *editor;
216 char *argp[] = {"sh", "-c", NULL((void *)0), NULL((void *)0)};
217
218 if (!filename) {
1
Assuming 'filename' is non-null
2
Taking false branch
219 filename = pw_lck;
220 if (!filename)
221 return;
222 }
223
224 if ((editor = getenv("EDITOR")) == NULL((void *)0))
3
Assuming the condition is false
4
Taking false branch
225 editor = _PATH_VI"/usr/bin/vi";
226
227 if (asprintf(&p, "%s %s", editor, filename) == -1)
5
Assuming the condition is false
6
Taking false branch
228 return;
229 argp[2] = p;
230
231 switch (editpid = vfork()) {
7
Control jumps to 'case 0:' at line 235
232 case -1: /* error */
233 free(p);
234 return;
235 case 0: /* child */
236 if (notsetuid) {
8
Assuming 'notsetuid' is not equal to 0
9
Taking true branch
237 setgid(getgid());
10
This function call is prohibited after a successful vfork
238 setuid(getuid());
239 }
240 execv(_PATH_BSHELL"/bin/sh", argp);
241 _exit(127);
242 }
243
244 free(p);
245 for (;;) {
246 editpid = waitpid(editpid, (int *)&pstat, WUNTRACED2);
247 if (editpid == -1)
248 pw_error(editor, 1, 1);
249 else if (WIFSTOPPED(pstat)(((pstat) & 0xff) == 0177))
250 raise(WSTOPSIG(pstat)(int)(((unsigned)(pstat) >> 8) & 0xff));
251 else if (WIFEXITED(pstat)(((pstat) & 0177) == 0) && WEXITSTATUS(pstat)(int)(((unsigned)(pstat) >> 8) & 0xff) == 0)
252 break;
253 else
254 pw_error(editor, 1, 1);
255 }
256 editpid = -1;
257}
258
259void
260pw_prompt(void)
261{
262 int first, c;
263
264 (void)printf("re-edit the password file? [y]: ");
265 (void)fflush(stdout(&__sF[1]));
266 first = c = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
;
267 while (c != '\n' && c != EOF(-1))
268 c = getchar()(!__isthreaded ? (--((&__sF[0]))->_r < 0 ? __srget(
(&__sF[0])) : (int)(*((&__sF[0]))->_p++)) : (getc)
((&__sF[0])))
;
269 switch (first) {
270 case EOF(-1):
271 putchar('\n')(!__isthreaded ? __sputc('\n', (&__sF[1])) : (putc)('\n',
(&__sF[1])))
;
272 /* FALLTHROUGH */
273 case 'n':
274 case 'N':
275 pw_error(NULL((void *)0), 0, 0);
276 break;
277 }
278}
279
280static int
281pw_equal(const struct passwd *pw1, const struct passwd *pw2)
282{
283 return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
284 pw1->pw_uid == pw2->pw_uid &&
285 pw1->pw_gid == pw2->pw_gid &&
286 strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
287 pw1->pw_change == pw2->pw_change &&
288 pw1->pw_expire == pw2->pw_expire &&
289 strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
290 strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
291 strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
292}
293
294static int
295pw_write_entry(FILE *to, const struct passwd *pw)
296{
297 char gidstr[16], uidstr[16];
298
299 /* Preserve gid/uid -1 */
300 if (pw->pw_gid == (gid_t)-1)
301 strlcpy(gidstr, "-1", sizeof(gidstr));
302 else
303 snprintf(gidstr, sizeof(gidstr), "%u", (u_int)pw->pw_gid);
304
305 if (pw->pw_uid == (uid_t)-1)
306 strlcpy(uidstr, "-1", sizeof(uidstr));
307 else
308 snprintf(uidstr, sizeof(uidstr), "%u", (u_int)pw->pw_uid);
309
310 return fprintf(to, "%s:%s:%s:%s:%s:%lld:%lld:%s:%s:%s\n",
311 pw->pw_name, pw->pw_passwd, uidstr, gidstr, pw->pw_class,
312 (long long)pw->pw_change, (long long)pw->pw_expire,
313 pw->pw_gecos, pw->pw_dir, pw->pw_shell);
314}
315
316void
317pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw)
318{
319 struct passwd tpw;
320 FILE *from, *to;
321 int done;
322 char *p, *ep, buf[8192];
323 char *master = pw_file(_PATH_MASTERPASSWD"/etc/master.passwd");
324
325 if (!master)
326 pw_error(NULL((void *)0), 0, 1);
327 if (!(from = fdopen(ffd, "r")))
328 pw_error(master, 1, 1);
329 if (!(to = fdopen(tfd, "w")))
330 pw_error(pw_lck ? pw_lck : NULL((void *)0), pw_lck ? 1 : 0, 1);
331
332 for (done = 0; fgets(buf, (int)sizeof(buf), from);) {
333 if ((ep = strchr(buf, '\n')) == NULL((void *)0)) {
334 warnx("%s: line too long", master);
335 pw_error(NULL((void *)0), 0, 1);
336 }
337 if (done) {
338 if (fputs(buf, to))
339 goto fail;
340 continue;
341 }
342 if (!(p = strchr(buf, ':'))) {
343 warnx("%s: corrupted entry", master);
344 pw_error(NULL((void *)0), 0, 1);
345 }
346 *p = '\0';
347 if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) {
348 *p = ':';
349 if (fputs(buf, to))
350 goto fail;
351 continue;
352 }
353 if (opw != NULL((void *)0)) {
354 *p = ':';
355 *ep = '\0';
356 if (!pw_scan(buf, &tpw, NULL((void *)0)))
357 pw_error(NULL((void *)0), 0, 1);
358 if (!pw_equal(&tpw, opw)) {
359 warnx("%s: inconsistent entry", master);
360 pw_error(NULL((void *)0), 0, 1);
361 }
362 }
363 if (pw_write_entry(to, pw) == -1)
364 goto fail;
365 done = 1;
366 }
367 if (!done && pw_write_entry(to, pw) == -1)
368 goto fail;
369
370 if (ferror(to)(!__isthreaded ? (((to)->_flags & 0x0040) != 0) : (ferror
)(to))
|| fflush(to))
371fail:
372 pw_error(NULL((void *)0), 0, 1);
373 free(master);
374 (void)fclose(to);
375}
376
377int
378pw_scan(char *bp, struct passwd *pw, int *flags)
379{
380 int root;
381 char *p, *sh;
382 const char *errstr;
383
384 if (flags != NULL((void *)0))
385 *flags = 0;
386
387 if (!(p = strsep(&bp, ":")) || *p == '\0') /* login */
388 goto fmt;
389 pw->pw_name = p;
390 root = !strcmp(pw->pw_name, "root");
391
392 if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
393 goto fmt;
394
395 if (!(p = strsep(&bp, ":"))) /* uid */
396 goto fmt;
397 pw->pw_uid = strtonum(p, -1, UID_MAX(2147483647 *2U +1U), &errstr);
398 if (errstr != NULL((void *)0)) {
399 if (*p != '\0') {
400 warnx("uid is %s", errstr);
401 return (0);
402 }
403 if (flags != NULL((void *)0))
404 *flags |= _PASSWORD_NOUID0x01;
405 }
406 if (root && pw->pw_uid) {
407 warnx("root uid should be 0");
408 return (0);
409 }
410
411 if (!(p = strsep(&bp, ":"))) /* gid */
412 goto fmt;
413 pw->pw_gid = strtonum(p, -1, GID_MAX(2147483647 *2U +1U), &errstr);
414 if (errstr != NULL((void *)0)) {
415 if (*p != '\0') {
416 warnx("gid is %s", errstr);
417 return (0);
418 }
419 if (flags != NULL((void *)0))
420 *flags |= _PASSWORD_NOGID0x02;
421 }
422
423 pw->pw_class = strsep(&bp, ":"); /* class */
424 if (!(p = strsep(&bp, ":"))) /* change */
425 goto fmt;
426 pw->pw_change = atoll(p);
427 if ((*p == '\0') && (flags != (int *)NULL((void *)0)))
428 *flags |= _PASSWORD_NOCHG0x04;
429 if (!(p = strsep(&bp, ":"))) /* expire */
430 goto fmt;
431 pw->pw_expire = atoll(p);
432 if ((*p == '\0') && (flags != (int *)NULL((void *)0)))
433 *flags |= _PASSWORD_NOEXP0x08;
434 pw->pw_gecos = strsep(&bp, ":"); /* gecos */
435 pw->pw_dir = strsep(&bp, ":"); /* directory */
436 if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
437 goto fmt;
438
439 p = pw->pw_shell;
440 if (root && *p) { /* empty == /bin/sh */
441 for (setusershell();;) {
442 if (!(sh = getusershell())) {
443 warnx("warning, unknown root shell");
444 break;
445 }
446 if (!strcmp(p, sh))
447 break;
448 }
449 endusershell();
450 }
451
452 if ((p = strsep(&bp, ":"))) { /* too many */
453fmt: warnx("corrupted entry");
454 return (0);
455 }
456
457 return (1);
458}
459
460__dead__attribute__((__noreturn__)) void
461pw_error(const char *name, int error, int eval)
462{
463 char *master = pw_file(_PATH_MASTERPASSWD"/etc/master.passwd");
464
465 if (error) {
466 if (name)
467 warn("%s", name);
468 else
469 warn(NULL((void *)0));
470 }
471 if (master) {
472 warnx("%s: unchanged", master);
473 free(master);
474 }
475
476 pw_abort();
477 exit(eval);
478}