jcs's openbsd hax
openbsd
1/* $OpenBSD: monitor.c,v 1.32 2025/05/08 15:22:49 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.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 != EINTR && errno != EAGAIN)
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);
113 _exit(0);
114 /* NOTREACHED */
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
125void
126set_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 * 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_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1)
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)
169 fatalx("fork of unprivileged slave failed");
170 if (slave_pid == 0) {
171 /* Unprivileged slave */
172 set_slave_signals();
173
174 if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL)
175 fatalx("privilege separation user %s not found",
176 FTPD_PRIVSEP_USER);
177
178 if (chroot(pw->pw_dir) == -1)
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 close(fd_slave);
191 return (1);
192 }
193
194 setproctitle("%s: [priv pre-auth]", remotehost);
195
196 handle_cmds();
197
198 /* User-privileged slave */
199 return (0);
200}
201
202/*
203 * Creates the user-privileged slave process. It is called
204 * from the privileged monitor process and returns twice. It returns 0
205 * for the user-privileged slave process and 1 for the monitor process.
206 */
207int
208monitor_post_auth(void)
209{
210 slave_pid = fork();
211 if (slave_pid == -1)
212 fatalx("fork of user-privileged slave failed");
213
214 snprintf(ttyline, sizeof(ttyline), "ftp%ld",
215 slave_pid == 0 ? (long)getpid() : (long)slave_pid);
216
217 if (slave_pid == 0) {
218 /* User privileged slave */
219 close(fd_slave);
220 set_slave_signals();
221 return (0);
222 }
223
224 /* We have to keep stdout open, because reply() needs it. */
225 if ((nullfd = open(_PATH_DEVNULL, O_RDWR)) == -1)
226 fatalx("cannot open %s: %m", _PATH_DEVNULL);
227 dup2(nullfd, STDIN_FILENO);
228 dup2(nullfd, STDERR_FILENO);
229 close(nullfd);
230 close(fd_monitor);
231
232 return (1);
233}
234
235/*
236 * Handles commands received from the slave process. It will not return
237 * except in one situation: After successful authentication it will
238 * return as the user-privileged slave process.
239 */
240void
241handle_cmds(void)
242{
243 enum monitor_command cmd;
244 enum auth_ret auth;
245 int err, s, slavequit, serrno, domain;
246 pid_t preauth_slave_pid;
247 size_t len;
248 union sockunion sa;
249 socklen_t salen;
250 char *name, *pw;
251
252 for (;;) {
253 recv_data(fd_slave, &cmd, sizeof(cmd));
254
255 switch (cmd) {
256 case CMD_USER:
257 debugmsg("CMD_USER received");
258
259 recv_data(fd_slave, &len, sizeof(len));
260 if (len == SIZE_MAX)
261 fatalx("monitor received invalid user length");
262 if ((name = malloc(len + 1)) == NULL)
263 fatalx("malloc: %m");
264 if (len > 0)
265 recv_data(fd_slave, name, len);
266 name[len] = '\0';
267
268 user(name);
269 free(name);
270 break;
271 case CMD_PASS:
272 debugmsg("CMD_PASS received");
273
274 recv_data(fd_slave, &len, sizeof(len));
275 if (len == SIZE_MAX)
276 fatalx("monitor received invalid pass length");
277 if ((pw = malloc(len + 1)) == NULL)
278 fatalx("malloc: %m");
279 if (len > 0)
280 recv_data(fd_slave, pw, len);
281 pw[len] = '\0';
282
283 preauth_slave_pid = slave_pid;
284
285 auth = pass(pw);
286 freezero(pw, len);
287
288 switch (auth) {
289 case AUTH_FAILED:
290 /* Authentication failure */
291 debugmsg("authentication failed");
292 slavequit = 0;
293 send_data(fd_slave, &slavequit,
294 sizeof(slavequit));
295 break;
296 case AUTH_SLAVE:
297 if (pledge("stdio rpath wpath cpath inet recvfd"
298 " sendfd proc tty getpw", NULL) == -1)
299 fatalx("pledge");
300 /* User-privileged slave */
301 debugmsg("user-privileged slave started");
302 return;
303 /* NOTREACHED */
304 case AUTH_MONITOR:
305 if (pledge("stdio inet sendfd recvfd proc",
306 NULL) == -1)
307 fatalx("pledge");
308 /* Post-auth monitor */
309 debugmsg("monitor went into post-auth phase");
310 state = POSTAUTH;
311 setproctitle("%s: [priv post-auth]",
312 remotehost);
313 slavequit = 1;
314
315 send_data(fd_slave, &slavequit,
316 sizeof(slavequit));
317
318 while (waitpid(preauth_slave_pid, NULL, 0) == -1 &&
319 errno == EINTR)
320 ;
321 break;
322 default:
323 fatalx("bad return value from pass()");
324 /* NOTREACHED */
325 }
326 break;
327 case CMD_SOCKET:
328 debugmsg("CMD_SOCKET received");
329
330 if (state != POSTAUTH)
331 fatalx("CMD_SOCKET received in invalid state");
332
333 recv_data(fd_slave, &domain, sizeof(domain));
334 if (domain != AF_INET && domain != AF_INET6)
335 fatalx("monitor received invalid addr family");
336
337 s = socket(domain, SOCK_STREAM, 0);
338 serrno = errno;
339
340 send_fd(fd_slave, s);
341 if (s == -1)
342 send_data(fd_slave, &serrno, sizeof(serrno));
343 else
344 close(s);
345 break;
346 case CMD_BIND:
347 debugmsg("CMD_BIND received");
348
349 if (state != POSTAUTH)
350 fatalx("CMD_BIND received in invalid state");
351
352 s = recv_fd(fd_slave);
353
354 recv_data(fd_slave, &salen, sizeof(salen));
355 if (salen == 0 || salen > sizeof(sa))
356 fatalx("monitor received invalid sockaddr len");
357
358 bzero(&sa, sizeof(sa));
359 recv_data(fd_slave, &sa, salen);
360
361 if (sa.su_si.si_len != salen)
362 fatalx("monitor received invalid sockaddr len");
363
364 if (sa.su_si.si_family != AF_INET &&
365 sa.su_si.si_family != AF_INET6)
366 fatalx("monitor received invalid addr family");
367
368 err = bind(s, (struct sockaddr *)&sa, salen);
369 serrno = errno;
370
371 if (s >= 0)
372 close(s);
373
374 send_data(fd_slave, &err, sizeof(err));
375 if (err == -1)
376 send_data(fd_slave, &serrno, sizeof(serrno));
377 break;
378 default:
379 fatalx("monitor received unknown command %d", cmd);
380 /* NOTREACHED */
381 }
382 }
383}
384
385void
386sig_pass_to_slave(int signo)
387{
388 int olderrno = errno;
389
390 if (slave_pid > 0)
391 kill(slave_pid, signo);
392
393 errno = olderrno;
394}
395
396void
397sig_chld(int signo)
398{
399 pid_t pid;
400 int stat, olderrno = errno;
401
402 do {
403 pid = waitpid(slave_pid, &stat, WNOHANG);
404 if (pid > 0)
405 _exit(0);
406 } while (pid == -1 && errno == EINTR);
407
408 errno = olderrno;
409}
410
411void
412kill_slave(char *reason)
413{
414 if (slave_pid > 0) {
415 if (reason)
416 syslog(LOG_NOTICE, "kill slave %d: %s",
417 slave_pid, reason);
418 kill(slave_pid, SIGQUIT);
419 }
420}
421
422void
423fatalx(char *fmt, ...)
424{
425 va_list ap;
426
427 va_start(ap, fmt);
428 vsyslog(LOG_ERR, fmt, ap);
429 va_end(ap);
430
431 kill_slave("fatal error");
432
433 _exit(0);
434}
435
436void
437debugmsg(char *fmt, ...)
438{
439 va_list ap;
440
441 if (debug) {
442 va_start(ap, fmt);
443 vsyslog(LOG_DEBUG, fmt, ap);
444 va_end(ap);
445 }
446}
447
448void
449monitor_user(char *name)
450{
451 enum monitor_command cmd;
452 size_t len;
453
454 cmd = CMD_USER;
455 send_data(fd_monitor, &cmd, sizeof(cmd));
456
457 len = strlen(name);
458 send_data(fd_monitor, &len, sizeof(len));
459 if (len > 0)
460 send_data(fd_monitor, name, len);
461}
462
463int
464monitor_pass(char *pass)
465{
466 enum monitor_command cmd;
467 int quitnow;
468 size_t len;
469
470 cmd = CMD_PASS;
471 send_data(fd_monitor, &cmd, sizeof(cmd));
472
473 len = strlen(pass);
474 send_data(fd_monitor, &len, sizeof(len));
475 if (len > 0)
476 send_data(fd_monitor, pass, len);
477
478 recv_data(fd_monitor, &quitnow, sizeof(quitnow));
479
480 return (quitnow);
481}
482
483int
484monitor_socket(int domain)
485{
486 enum monitor_command cmd;
487 int s, serrno;
488
489 cmd = CMD_SOCKET;
490 send_data(fd_monitor, &cmd, sizeof(cmd));
491 send_data(fd_monitor, &domain, sizeof(domain));
492
493 s = recv_fd(fd_monitor);
494 if (s == -1) {
495 recv_data(fd_monitor, &serrno, sizeof(serrno));
496 errno = serrno;
497 }
498
499 return (s);
500}
501
502int
503monitor_bind(int s, struct sockaddr *name, socklen_t namelen)
504{
505 enum monitor_command cmd;
506 int ret, serrno;
507
508 cmd = CMD_BIND;
509 send_data(fd_monitor, &cmd, sizeof(cmd));
510
511 send_fd(fd_monitor, s);
512 send_data(fd_monitor, &namelen, sizeof(namelen));
513 send_data(fd_monitor, name, namelen);
514
515 recv_data(fd_monitor, &ret, sizeof(ret));
516 if (ret == -1) {
517 recv_data(fd_monitor, &serrno, sizeof(serrno));
518 errno = serrno;
519 }
520
521 return (ret);
522}