Bug Summary

File:src/usr.bin/chpass/chpass.c
Warning:line 180, 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.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name chpass.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/chpass/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.bin/chpass/../../lib/libc/include -internal-isystem /usr/local/llvm16/lib/clang/16/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 -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/chpass/chpass.c
1/* $OpenBSD: chpass.c,v 1.50 2023/03/08 04:43:10 guenther 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 default:
88 usage();
89 }
90 argc -= optind;
91 argv += optind;
92
93 uid = getuid();
94
95 if (op == EDITENTRY || op == NEWSH)
96 switch(argc) {
97 case 0:
98 pw = getpwuid_shadow(uid);
99 if (!pw)
100 errx(1, "unknown user: uid %u", uid);
101 break;
102 case 1:
103 pw = getpwnam_shadow(*argv);
104 if (!pw)
105 errx(1, "unknown user: %s", *argv);
106 if (uid && uid != pw->pw_uid)
107 baduser();
108 break;
109 default:
110 usage();
111 }
112
113 if (op == LOADENTRY) {
114 if (argc != 0)
115 errx(1, "option -a does not accept user argument");
116 if (uid)
117 baduser();
118 pw = &lpw;
119 if (!pw_scan(arg, pw, NULL((void *)0)))
120 exit(1);
121 opw = getpwnam_shadow(pw->pw_name);
122 }
123 if (opw == NULL((void *)0) && (opw = pw_dup(pw)) == NULL((void *)0))
124 err(1, NULL((void *)0));
125
126 /* Edit the user passwd information if requested. */
127 if (op == EDITENTRY) {
128 char tempname[] = _PATH_VARTMP"/var/tmp/" "pw.XXXXXXXXXX";
129 int edit_status;
130
131 if ((pw = pw_dup(pw)) == NULL((void *)0))
132 pw_error(NULL((void *)0), 1, 1);
133 dfd = mkostemp(tempname, O_CLOEXEC0x10000);
134 if (dfd == -1)
135 pw_error(tempname, 1, 1);
136 display(tempname, dfd, pw);
137
138 if (unveil(_PATH_BSHELL"/bin/sh", "x") == -1)
139 err(1, "unveil %s", _PATH_BSHELL"/bin/sh");
140 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
141 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
142 if (unveil(tempname, "rc") == -1)
143 err(1, "unveil %s", tempname);
144 if (pledge("stdio rpath wpath cpath id proc exec unveil",
145 NULL((void *)0)) == -1)
146 err(1, "pledge");
147
148 edit_status = edit(tempname, pw);
149 close(dfd);
150 unlink(tempname);
151
152 switch (edit_status) {
153 case EDIT_OK0:
154 break;
155 case EDIT_NOCHANGE1:
156 pw_error(NULL((void *)0), 0, 0);
157 break;
158 case EDIT_ERROR-1:
159 default:
160 pw_error(tempname, 1, 1);
161 break;
162 }
163 }
164
165 if (op == NEWSH) {
166 if (unveil(_PATH_SHELLS"/etc/shells", "r") == -1)
167 err(1, "unveil %s", _PATH_SHELLS"/etc/shells");
168 if (pledge("stdio rpath wpath cpath id proc exec unveil",
169 NULL((void *)0)) == -1)
170 err(1, "pledge");
171
172 /* protect p_shell -- it thinks NULL is /bin/sh */
173 if (!arg[0])
174 usage();
175 if (p_shell(arg, pw, NULL((void *)0)))
176 pw_error(NULL((void *)0), 0, 1);
177 }
178
179 /* Drop user's real uid and block all signals to avoid a DoS. */
180 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
181 sigfillset(&fullset);
182 sigdelset(&fullset, SIGINT2);
183 sigprocmask(SIG_BLOCK1, &fullset, NULL((void *)0));
184
185 if (unveil(_PATH_MASTERPASSWD_LOCK"/etc/ptmp", "rwc") == -1)
186 err(1, "unveil %s", _PATH_MASTERPASSWD_LOCK"/etc/ptmp");
187 if (unveil(_PATH_MASTERPASSWD"/etc/master.passwd", "r") == -1)
188 err(1, "unveil %s", _PATH_MASTERPASSWD"/etc/master.passwd");
189 if (unveil(_PATH_PWD_MKDB"/usr/sbin/pwd_mkdb", "x") == -1)
190 err(1, "unveil %s", _PATH_PWD_MKDB"/usr/sbin/pwd_mkdb");
191 if (pledge("stdio rpath wpath cpath proc exec", NULL((void *)0)) == -1)
192 err(1, "pledge");
193
194 /* Get the passwd lock file and open the passwd file for reading. */
195 pw_init();
196 for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
197 if (i == 4)
198 (void)fputs("Attempting to lock password file, "
199 "please wait or press ^C to abort", stderr(&__sF[2]));
200 (void)signal(SIGINT2, kbintr);
201 if (i % 16 == 0)
202 fputc('.', stderr(&__sF[2]));
203 usleep(250000);
204 (void)signal(SIGINT2, SIG_IGN(void (*)(int))1);
205 }
206 if (i >= 4)
207 fputc('\n', stderr(&__sF[2]));
208 pfd = open(_PATH_MASTERPASSWD"/etc/master.passwd", O_RDONLY0x0000|O_CLOEXEC0x10000);
209 if (pfd == -1)
210 pw_error(_PATH_MASTERPASSWD"/etc/master.passwd", 1, 1);
211
212 /* Copy the passwd file to the lock file, updating pw. */
213 pw_copy(pfd, tfd, pw, opw);
214
215 /* If username changed we need to rebuild the entire db. */
216 arg = !strcmp(opw->pw_name, pw->pw_name) ? pw->pw_name : NULL((void *)0);
217
218 /* Now finish the passwd file update. */
219 if (pw_mkdb(arg, 0) == -1)
220 pw_error(NULL((void *)0), 0, 1);
221 exit(0);
222}
223
224void
225baduser(void)
226{
227
228 errx(1, "%s", strerror(EACCES13));
229}
230
231void
232kbintr(int signo)
233{
234 dprintf(STDERR_FILENO2, "\n%s: %s unchanged\n",
235 __progname, _PATH_MASTERPASSWD"/etc/master.passwd");
236 _exit(1);
237}
238
239void
240usage(void)
241{
242
243 (void)fprintf(stderr(&__sF[2]), "usage: %s [-s newshell] [user]\n", __progname);
244 (void)fprintf(stderr(&__sF[2]), " %s -a list\n", __progname);
245 exit(1);
246}