clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name monitor.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/libexec/ftpd/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/libexec/ftpd -I /usr/src/libexec/ftpd/../../bin/ls -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/libexec/ftpd/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/libexec/ftpd/monitor.c
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
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.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 | |
40 | enum monitor_command { |
41 | CMD_USER, |
42 | CMD_PASS, |
43 | CMD_SOCKET, |
44 | CMD_BIND |
45 | }; |
46 | |
47 | enum monitor_state { |
48 | PREAUTH, |
49 | POSTAUTH |
50 | }; |
51 | |
52 | extern char remotehost[]; |
53 | extern char ttyline[20]; |
54 | extern int debug; |
55 | |
56 | extern void set_slave_signals(void); |
57 | |
58 | int fd_monitor = -1; |
59 | int fd_slave = -1; |
60 | int nullfd; |
61 | pid_t slave_pid = -1; |
62 | enum monitor_state state = PREAUTH; |
63 | |
64 | void send_data(int, void *, size_t); |
65 | void recv_data(int, void *, size_t); |
66 | void handle_cmds(void); |
67 | void set_monitor_signals(void); |
68 | void sig_pass_to_slave(int); |
69 | void sig_chld(int); |
70 | void fatalx(char *, ...); |
71 | void debugmsg(char *, ...); |
72 | |
73 | |
74 | |
75 | |
76 | void |
77 | send_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 | |
89 | case -1: |
90 | if (errno != EINTR && errno != EAGAIN) |
91 | fatalx("send_data: %m"); |
92 | break; |
93 | default: |
94 | pos += n; |
95 | } |
96 | } |
97 | } |
98 | |
99 | |
100 | |
101 | |
102 | void |
103 | recv_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); |
113 | _exit(0); |
114 | |
115 | case -1: |
116 | if (errno != EINTR && errno != EAGAIN) |
117 | fatalx("recv_data: %m"); |
118 | break; |
119 | default: |
120 | pos += n; |
121 | } |
122 | } |
123 | } |
124 | |
125 | void |
126 | set_monitor_signals(void) |
127 | { |
128 | struct sigaction act; |
129 | int i; |
130 | |
131 | sigfillset(&act.sa_mask); |
132 | act.sa_flags = SA_RESTART; |
133 | |
134 | act.sa_handler = SIG_DFL; |
135 | for (i = 1; i < _NSIG; i++) |
136 | sigaction(i, &act, NULL); |
137 | |
138 | act.sa_handler = sig_chld; |
139 | sigaction(SIGCHLD, &act, NULL); |
140 | |
141 | act.sa_handler = sig_pass_to_slave; |
142 | sigaction(SIGHUP, &act, NULL); |
143 | sigaction(SIGINT, &act, NULL); |
144 | sigaction(SIGQUIT, &act, NULL); |
145 | sigaction(SIGTERM, &act, NULL); |
146 | } |
147 | |
148 | |
149 | |
150 | |
151 | |
152 | |
153 | int |
154 | monitor_init(void) |
155 | { |
156 | struct passwd *pw; |
157 | int pair[2]; |
158 | |
159 | if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1) |
| 1 | Assuming the condition is false | |
|
| |
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 | |
|
| |
169 | fatalx("fork of unprivileged slave failed"); |
170 | if (slave_pid == 0) { |
| 5 | | Assuming 'slave_pid' is equal to 0 | |
|
| |
171 | |
172 | set_slave_signals(); |
173 | |
174 | if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL) |
| |
| 8 | | Assuming pointer value is null | |
|
| |
175 | fatalx("privilege separation user %s not found", |
176 | FTPD_PRIVSEP_USER); |
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 | |
200 | return (0); |
201 | } |
202 | |
203 | |
204 | |
205 | |
206 | |
207 | |
208 | int |
209 | monitor_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 | |
220 | close(fd_slave); |
221 | set_slave_signals(); |
222 | return (0); |
223 | } |
224 | |
225 | |
226 | if ((nullfd = open(_PATH_DEVNULL, O_RDWR)) == -1) |
227 | fatalx("cannot open %s: %m", _PATH_DEVNULL); |
228 | dup2(nullfd, STDIN_FILENO); |
229 | dup2(nullfd, STDERR_FILENO); |
230 | close(nullfd); |
231 | close(fd_monitor); |
232 | |
233 | return (1); |
234 | } |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | void |
242 | handle_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_MAX) |
262 | fatalx("monitor received invalid user length"); |
263 | if ((name = malloc(len + 1)) == NULL) |
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_MAX) |
277 | fatalx("monitor received invalid pass length"); |
278 | if ((pw = malloc(len + 1)) == NULL) |
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 | |
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) == -1) |
300 | fatalx("pledge"); |
301 | |
302 | debugmsg("user-privileged slave started"); |
303 | return; |
304 | |
305 | case AUTH_MONITOR: |
306 | if (pledge("stdio inet sendfd recvfd proc", |
307 | NULL) == -1) |
308 | fatalx("pledge"); |
309 | |
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, 0) == -1 && |
320 | errno == EINTR) |
321 | ; |
322 | break; |
323 | default: |
324 | fatalx("bad return value from pass()"); |
325 | |
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_INET && domain != AF_INET6) |
336 | fatalx("monitor received invalid addr family"); |
337 | |
338 | s = socket(domain, SOCK_STREAM, 0); |
339 | serrno = 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_INET && |
366 | sa.su_si.si_family != AF_INET6) |
367 | fatalx("monitor received invalid addr family"); |
368 | |
369 | err = bind(s, (struct sockaddr *)&sa, salen); |
370 | serrno = 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 | |
382 | } |
383 | } |
384 | } |
385 | |
386 | void |
387 | sig_pass_to_slave(int signo) |
388 | { |
389 | int olderrno = errno; |
390 | |
391 | if (slave_pid > 0) |
392 | kill(slave_pid, signo); |
393 | |
394 | errno = olderrno; |
395 | } |
396 | |
397 | void |
398 | sig_chld(int signo) |
399 | { |
400 | pid_t pid; |
401 | int stat, olderrno = errno; |
402 | |
403 | do { |
404 | pid = waitpid(slave_pid, &stat, WNOHANG); |
405 | if (pid > 0) |
406 | _exit(0); |
407 | } while (pid == -1 && errno == EINTR); |
408 | |
409 | errno = olderrno; |
410 | } |
411 | |
412 | void |
413 | kill_slave(char *reason) |
414 | { |
415 | if (slave_pid > 0) { |
416 | if (reason) |
417 | syslog(LOG_NOTICE, "kill slave %d: %s", |
418 | slave_pid, reason); |
419 | kill(slave_pid, SIGQUIT); |
420 | } |
421 | } |
422 | |
423 | void |
424 | fatalx(char *fmt, ...) |
425 | { |
426 | va_list ap; |
427 | |
428 | va_start(ap, fmt); |
429 | vsyslog(LOG_ERR, fmt, ap); |
430 | va_end(ap); |
431 | |
432 | kill_slave("fatal error"); |
433 | |
434 | _exit(0); |
435 | } |
436 | |
437 | void |
438 | debugmsg(char *fmt, ...) |
439 | { |
440 | va_list ap; |
441 | |
442 | if (debug) { |
443 | va_start(ap, fmt); |
444 | vsyslog(LOG_DEBUG, fmt, ap); |
445 | va_end(ap); |
446 | } |
447 | } |
448 | |
449 | void |
450 | monitor_user(char *name) |
451 | { |
452 | enum monitor_command cmd; |
453 | size_t len; |
454 | |
455 | cmd = CMD_USER; |
456 | send_data(fd_monitor, &cmd, sizeof(cmd)); |
457 | |
458 | len = strlen(name); |
459 | send_data(fd_monitor, &len, sizeof(len)); |
460 | if (len > 0) |
461 | send_data(fd_monitor, name, len); |
462 | } |
463 | |
464 | int |
465 | monitor_pass(char *pass) |
466 | { |
467 | enum monitor_command cmd; |
468 | int quitnow; |
469 | size_t len; |
470 | |
471 | cmd = CMD_PASS; |
472 | send_data(fd_monitor, &cmd, sizeof(cmd)); |
473 | |
474 | len = strlen(pass); |
475 | send_data(fd_monitor, &len, sizeof(len)); |
476 | if (len > 0) |
477 | send_data(fd_monitor, pass, len); |
478 | |
479 | recv_data(fd_monitor, &quitnow, sizeof(quitnow)); |
480 | |
481 | return (quitnow); |
482 | } |
483 | |
484 | int |
485 | monitor_socket(int domain) |
486 | { |
487 | enum monitor_command cmd; |
488 | int s, serrno; |
489 | |
490 | cmd = CMD_SOCKET; |
491 | send_data(fd_monitor, &cmd, sizeof(cmd)); |
492 | send_data(fd_monitor, &domain, sizeof(domain)); |
493 | |
494 | s = recv_fd(fd_monitor); |
495 | if (s == -1) { |
496 | recv_data(fd_monitor, &serrno, sizeof(serrno)); |
497 | errno = serrno; |
498 | } |
499 | |
500 | return (s); |
501 | } |
502 | |
503 | int |
504 | monitor_bind(int s, struct sockaddr *name, socklen_t namelen) |
505 | { |
506 | enum monitor_command cmd; |
507 | int ret, serrno; |
508 | |
509 | cmd = CMD_BIND; |
510 | send_data(fd_monitor, &cmd, sizeof(cmd)); |
511 | |
512 | send_fd(fd_monitor, s); |
513 | send_data(fd_monitor, &namelen, sizeof(namelen)); |
514 | send_data(fd_monitor, name, namelen); |
515 | |
516 | recv_data(fd_monitor, &ret, sizeof(ret)); |
517 | if (ret == -1) { |
518 | recv_data(fd_monitor, &serrno, sizeof(serrno)); |
519 | errno = serrno; |
520 | } |
521 | |
522 | return (ret); |
523 | } |