Bug Summary

File:src/libexec/ftpd/monitor.c
Warning:line 178, column 14
Access to field 'pw_dir' results in a dereference of a null pointer (loaded from variable 'pw')

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 monitor.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/libexec/ftpd/obj -resource-dir /usr/local/lib/clang/13.0.0 -I /usr/src/libexec/ftpd -I /usr/src/libexec/ftpd/../../bin/ls -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/libexec/ftpd/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/libexec/ftpd/monitor.c
1/* $OpenBSD: monitor.c,v 1.30 2021/10/24 21:24:20 deraadt Exp $ */
2
3/*
4 * Copyright (c) 2004 Moritz Jodeit <moritz@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/socket.h>
21#include <sys/wait.h>
22#include <netinet/in.h>
23
24#include <errno(*__errno()).h>
25#include <fcntl.h>
26#include <paths.h>
27#include <pwd.h>
28#include <signal.h>
29#include <stdarg.h>
30#include <stdint.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <syslog.h>
35#include <unistd.h>
36
37#include "monitor.h"
38#include "extern.h"
39
40enum monitor_command {
41 CMD_USER,
42 CMD_PASS,
43 CMD_SOCKET,
44 CMD_BIND
45};
46
47enum monitor_state {
48 PREAUTH,
49 POSTAUTH
50};
51
52extern char remotehost[];
53extern char ttyline[20];
54extern int debug;
55
56extern void set_slave_signals(void);
57
58int fd_monitor = -1;
59int fd_slave = -1;
60int nullfd;
61pid_t slave_pid = -1;
62enum monitor_state state = PREAUTH;
63
64void send_data(int, void *, size_t);
65void recv_data(int, void *, size_t);
66void handle_cmds(void);
67void set_monitor_signals(void);
68void sig_pass_to_slave(int);
69void sig_chld(int);
70void fatalx(char *, ...);
71void debugmsg(char *, ...);
72
73/*
74 * Send data over a socket and exit if something fails.
75 */
76void
77send_data(int sock, void *buf, size_t len)
78{
79 ssize_t n;
80 size_t pos = 0;
81 char *ptr = buf;
82
83 while (len > pos) {
84 switch (n = write(sock, ptr + pos, len - pos)) {
85 case 0:
86 kill_slave("write failure");
87 _exit(0);
88 /* NOTREACHED */
89 case -1:
90 if (errno(*__errno()) != EINTR4 && errno(*__errno()) != EAGAIN35)
91 fatalx("send_data: %m");
92 break;
93 default:
94 pos += n;
95 }
96 }
97}
98
99/*
100 * Receive data from socket and exit if something fails.
101 */
102void
103recv_data(int sock, void *buf, size_t len)
104{
105 ssize_t n;
106 size_t pos = 0;
107 char *ptr = buf;
108
109 while (len > pos) {
110 switch (n = read(sock, ptr + pos, len - pos)) {
111 case 0:
112 kill_slave(NULL((void *)0));
113 _exit(0);
114 /* NOTREACHED */
115 case -1:
116 if (errno(*__errno()) != EINTR4 && errno(*__errno()) != EAGAIN35)
117 fatalx("recv_data: %m");
118 break;
119 default:
120 pos += n;
121 }
122 }
123}
124
125void
126set_monitor_signals(void)
127{
128 struct sigaction act;
129 int i;
130
131 sigfillset(&act.sa_mask);
132 act.sa_flags = SA_RESTART0x0002;
133
134 act.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0;
135 for (i = 1; i < _NSIG33; i++)
136 sigaction(i, &act, NULL((void *)0));
137
138 act.sa_handler__sigaction_u.__sa_handler = sig_chld;
139 sigaction(SIGCHLD20, &act, NULL((void *)0));
140
141 act.sa_handler__sigaction_u.__sa_handler = sig_pass_to_slave;
142 sigaction(SIGHUP1, &act, NULL((void *)0));
143 sigaction(SIGINT2, &act, NULL((void *)0));
144 sigaction(SIGQUIT3, &act, NULL((void *)0));
145 sigaction(SIGTERM15, &act, NULL((void *)0));
146}
147
148/*
149 * Creates the privileged monitor process. It returns twice.
150 * It returns 1 for the unprivileged slave process and 0 for the
151 * user-privileged slave process after successful authentication.
152 */
153int
154monitor_init(void)
155{
156 struct passwd *pw;
157 int pair[2];
158
159 if (socketpair(AF_LOCAL1, SOCK_STREAM1, PF_UNSPEC0, pair) == -1)
1
Assuming the condition is false
2
Taking false branch
160 fatalx("socketpair failed");
161
162 fd_monitor = pair[0];
163 fd_slave = pair[1];
164
165 set_monitor_signals();
166
167 slave_pid = fork();
168 if (slave_pid == -1)
3
Assuming the condition is false
4
Taking false branch
169 fatalx("fork of unprivileged slave failed");
170 if (slave_pid == 0) {
5
Assuming 'slave_pid' is equal to 0
6
Taking true branch
171 /* Unprivileged slave */
172 set_slave_signals();
173
174 if ((pw = getpwnam(FTPD_PRIVSEP_USER"_ftp")) == NULL((void *)0))
7
Value assigned to 'pw'
8
Assuming pointer value is null
9
Taking true branch
175 fatalx("privilege separation user %s not found",
176 FTPD_PRIVSEP_USER"_ftp");
177
178 if (chroot(pw->pw_dir) == -1)
10
Access to field 'pw_dir' results in a dereference of a null pointer (loaded from variable 'pw')
179 fatalx("chroot %s: %m", pw->pw_dir);
180 if (chdir("/") == -1)
181 fatalx("chdir /: %m");
182
183 if (setgroups(1, &pw->pw_gid) == -1)
184 fatalx("setgroups: %m");
185 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
186 fatalx("setresgid failed");
187 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
188 fatalx("setresuid failed");
189
190 endpwent();
191 close(fd_slave);
192 return (1);
193 }
194
195 setproctitle("%s: [priv pre-auth]", remotehost);
196
197 handle_cmds();
198
199 /* User-privileged slave */
200 return (0);
201}
202
203/*
204 * Creates the user-privileged slave process. It is called
205 * from the privileged monitor process and returns twice. It returns 0
206 * for the user-privileged slave process and 1 for the monitor process.
207 */
208int
209monitor_post_auth(void)
210{
211 slave_pid = fork();
212 if (slave_pid == -1)
213 fatalx("fork of user-privileged slave failed");
214
215 snprintf(ttyline, sizeof(ttyline), "ftp%ld",
216 slave_pid == 0 ? (long)getpid() : (long)slave_pid);
217
218 if (slave_pid == 0) {
219 /* User privileged slave */
220 close(fd_slave);
221 set_slave_signals();
222 return (0);
223 }
224
225 /* We have to keep stdout open, because reply() needs it. */
226 if ((nullfd = open(_PATH_DEVNULL"/dev/null", O_RDWR0x0002)) == -1)
227 fatalx("cannot open %s: %m", _PATH_DEVNULL"/dev/null");
228 dup2(nullfd, STDIN_FILENO0);
229 dup2(nullfd, STDERR_FILENO2);
230 close(nullfd);
231 close(fd_monitor);
232
233 return (1);
234}
235
236/*
237 * Handles commands received from the slave process. It will not return
238 * except in one situation: After successful authentication it will
239 * return as the user-privileged slave process.
240 */
241void
242handle_cmds(void)
243{
244 enum monitor_command cmd;
245 enum auth_ret auth;
246 int err, s, slavequit, serrno, domain;
247 pid_t preauth_slave_pid;
248 size_t len;
249 union sockunion sa;
250 socklen_t salen;
251 char *name, *pw;
252
253 for (;;) {
254 recv_data(fd_slave, &cmd, sizeof(cmd));
255
256 switch (cmd) {
257 case CMD_USER:
258 debugmsg("CMD_USER received");
259
260 recv_data(fd_slave, &len, sizeof(len));
261 if (len == SIZE_MAX0xffffffffffffffffUL)
262 fatalx("monitor received invalid user length");
263 if ((name = malloc(len + 1)) == NULL((void *)0))
264 fatalx("malloc: %m");
265 if (len > 0)
266 recv_data(fd_slave, name, len);
267 name[len] = '\0';
268
269 user(name);
270 free(name);
271 break;
272 case CMD_PASS:
273 debugmsg("CMD_PASS received");
274
275 recv_data(fd_slave, &len, sizeof(len));
276 if (len == SIZE_MAX0xffffffffffffffffUL)
277 fatalx("monitor received invalid pass length");
278 if ((pw = malloc(len + 1)) == NULL((void *)0))
279 fatalx("malloc: %m");
280 if (len > 0)
281 recv_data(fd_slave, pw, len);
282 pw[len] = '\0';
283
284 preauth_slave_pid = slave_pid;
285
286 auth = pass(pw);
287 freezero(pw, len);
288
289 switch (auth) {
290 case AUTH_FAILED:
291 /* Authentication failure */
292 debugmsg("authentication failed");
293 slavequit = 0;
294 send_data(fd_slave, &slavequit,
295 sizeof(slavequit));
296 break;
297 case AUTH_SLAVE:
298 if (pledge("stdio rpath wpath cpath inet recvfd"
299 " sendfd proc tty getpw", NULL((void *)0)) == -1)
300 fatalx("pledge");
301 /* User-privileged slave */
302 debugmsg("user-privileged slave started");
303 return;
304 /* NOTREACHED */
305 case AUTH_MONITOR:
306 if (pledge("stdio inet sendfd recvfd proc",
307 NULL((void *)0)) == -1)
308 fatalx("pledge");
309 /* Post-auth monitor */
310 debugmsg("monitor went into post-auth phase");
311 state = POSTAUTH;
312 setproctitle("%s: [priv post-auth]",
313 remotehost);
314 slavequit = 1;
315
316 send_data(fd_slave, &slavequit,
317 sizeof(slavequit));
318
319 while (waitpid(preauth_slave_pid, NULL((void *)0), 0) == -1 &&
320 errno(*__errno()) == EINTR4)
321 ;
322 break;
323 default:
324 fatalx("bad return value from pass()");
325 /* NOTREACHED */
326 }
327 break;
328 case CMD_SOCKET:
329 debugmsg("CMD_SOCKET received");
330
331 if (state != POSTAUTH)
332 fatalx("CMD_SOCKET received in invalid state");
333
334 recv_data(fd_slave, &domain, sizeof(domain));
335 if (domain != AF_INET2 && domain != AF_INET624)
336 fatalx("monitor received invalid addr family");
337
338 s = socket(domain, SOCK_STREAM1, 0);
339 serrno = errno(*__errno());
340
341 send_fd(fd_slave, s);
342 if (s == -1)
343 send_data(fd_slave, &serrno, sizeof(serrno));
344 else
345 close(s);
346 break;
347 case CMD_BIND:
348 debugmsg("CMD_BIND received");
349
350 if (state != POSTAUTH)
351 fatalx("CMD_BIND received in invalid state");
352
353 s = recv_fd(fd_slave);
354
355 recv_data(fd_slave, &salen, sizeof(salen));
356 if (salen == 0 || salen > sizeof(sa))
357 fatalx("monitor received invalid sockaddr len");
358
359 bzero(&sa, sizeof(sa));
360 recv_data(fd_slave, &sa, salen);
361
362 if (sa.su_si.si_len != salen)
363 fatalx("monitor received invalid sockaddr len");
364
365 if (sa.su_si.si_family != AF_INET2 &&
366 sa.su_si.si_family != AF_INET624)
367 fatalx("monitor received invalid addr family");
368
369 err = bind(s, (struct sockaddr *)&sa, salen);
370 serrno = errno(*__errno());
371
372 if (s >= 0)
373 close(s);
374
375 send_data(fd_slave, &err, sizeof(err));
376 if (err == -1)
377 send_data(fd_slave, &serrno, sizeof(serrno));
378 break;
379 default:
380 fatalx("monitor received unknown command %d", cmd);
381 /* NOTREACHED */
382 }
383 }
384}
385
386void
387sig_pass_to_slave(int signo)
388{
389 int olderrno = errno(*__errno());
390
391 if (slave_pid > 0)
392 kill(slave_pid, signo);
393
394 errno(*__errno()) = olderrno;
395}
396
397/* ARGSUSED */
398void
399sig_chld(int signo)
400{
401 pid_t pid;
402 int stat, olderrno = errno(*__errno());
403
404 do {
405 pid = waitpid(slave_pid, &stat, WNOHANG1);
406 if (pid > 0)
407 _exit(0);
408 } while (pid == -1 && errno(*__errno()) == EINTR4);
409
410 errno(*__errno()) = olderrno;
411}
412
413void
414kill_slave(char *reason)
415{
416 if (slave_pid > 0) {
417 if (reason)
418 syslog(LOG_NOTICE5, "kill slave %d: %s",
419 slave_pid, reason);
420 kill(slave_pid, SIGQUIT3);
421 }
422}
423
424void
425fatalx(char *fmt, ...)
426{
427 va_list ap;
428
429 va_start(ap, fmt)__builtin_va_start(ap, fmt);
430 vsyslog(LOG_ERR3, fmt, ap);
431 va_end(ap)__builtin_va_end(ap);
432
433 kill_slave("fatal error");
434
435 _exit(0);
436}
437
438void
439debugmsg(char *fmt, ...)
440{
441 va_list ap;
442
443 if (debug) {
444 va_start(ap, fmt)__builtin_va_start(ap, fmt);
445 vsyslog(LOG_DEBUG7, fmt, ap);
446 va_end(ap)__builtin_va_end(ap);
447 }
448}
449
450void
451monitor_user(char *name)
452{
453 enum monitor_command cmd;
454 size_t len;
455
456 cmd = CMD_USER;
457 send_data(fd_monitor, &cmd, sizeof(cmd));
458
459 len = strlen(name);
460 send_data(fd_monitor, &len, sizeof(len));
461 if (len > 0)
462 send_data(fd_monitor, name, len);
463}
464
465int
466monitor_pass(char *pass)
467{
468 enum monitor_command cmd;
469 int quitnow;
470 size_t len;
471
472 cmd = CMD_PASS;
473 send_data(fd_monitor, &cmd, sizeof(cmd));
474
475 len = strlen(pass);
476 send_data(fd_monitor, &len, sizeof(len));
477 if (len > 0)
478 send_data(fd_monitor, pass, len);
479
480 recv_data(fd_monitor, &quitnow, sizeof(quitnow));
481
482 return (quitnow);
483}
484
485int
486monitor_socket(int domain)
487{
488 enum monitor_command cmd;
489 int s, serrno;
490
491 cmd = CMD_SOCKET;
492 send_data(fd_monitor, &cmd, sizeof(cmd));
493 send_data(fd_monitor, &domain, sizeof(domain));
494
495 s = recv_fd(fd_monitor);
496 if (s == -1) {
497 recv_data(fd_monitor, &serrno, sizeof(serrno));
498 errno(*__errno()) = serrno;
499 }
500
501 return (s);
502}
503
504int
505monitor_bind(int s, struct sockaddr *name, socklen_t namelen)
506{
507 enum monitor_command cmd;
508 int ret, serrno;
509
510 cmd = CMD_BIND;
511 send_data(fd_monitor, &cmd, sizeof(cmd));
512
513 send_fd(fd_monitor, s);
514 send_data(fd_monitor, &namelen, sizeof(namelen));
515 send_data(fd_monitor, name, namelen);
516
517 recv_data(fd_monitor, &ret, sizeof(ret));
518 if (ret == -1) {
519 recv_data(fd_monitor, &serrno, sizeof(serrno));
520 errno(*__errno()) = serrno;
521 }
522
523 return (ret);
524}