1/*	$OpenBSD: monitor.c,v 1.31 2023/03/08 04:43:05 guenther 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		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, 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 * 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_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				/* 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) == -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) == -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, 0) == -1 &&
320				    errno == EINTR)
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_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			/* NOTREACHED */
382		}
383	}
384}
385
386void
387sig_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
397void
398sig_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
412void
413kill_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
423void
424fatalx(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
437void
438debugmsg(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
449void
450monitor_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
464int
465monitor_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
484int
485monitor_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
503int
504monitor_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}
524