monitor.c revision 1.4
1/*	$OpenBSD: monitor.c,v 1.4 2004/11/29 22:24:57 henning Exp $	*/
2
3/*
4 * Copyright (c) 2004 Moritz Jodeit <moritz@jodeit.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 <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <syslog.h>
34#include <unistd.h>
35
36#include "extern.h"
37#include "monitor.h"
38
39enum monitor_command {
40	CMD_USER,
41	CMD_PASS,
42	CMD_BIND
43};
44
45enum monitor_state {
46	PREAUTH,
47	POSTAUTH
48};
49
50#ifdef HASSETPROCTITLE
51extern char	remotehost[];
52#endif
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;
63volatile sig_atomic_t	quit = 0;
64
65void	send_data(int, void *, size_t);
66void	recv_data(int, void *, size_t);
67int	recv_cmd(int, void *, size_t);
68int	handle_cmds(void);
69void	set_monitor_signals(void);
70void	sig_pass_to_slave(int);
71void	sig_chld(int);
72void	fatalx(char *, ...);
73void	debugmsg(char *, ...);
74
75/*
76 * Send data over a socket and exit if something fails.
77 */
78void
79send_data(int sock, void *buf, size_t len)
80{
81	ssize_t n;
82	size_t pos;
83	char *ptr = buf;
84
85	for (pos = 0; len > pos; pos += n) {
86		n = write(sock, ptr + pos, len - pos);
87
88		if (n == -1 && !(errno == EINTR || errno == EAGAIN))
89			fatalx("send_data: %m");
90
91		if (n == 0)
92			fatalx("send_data: connection closed");
93	}
94}
95
96/*
97 * Receive data from socket and exit if something fails.
98 */
99void
100recv_data(int sock, void *buf, size_t len)
101{
102	ssize_t n;
103	size_t pos;
104	char *ptr = buf;
105
106	for (pos = 0; len > pos; pos += n) {
107		n = read(sock, ptr + pos, len - pos);
108
109		if (n == -1 && !(errno == EINTR || errno == EAGAIN))
110			fatalx("recv_data: %m");
111
112		if (n == 0)
113			fatalx("recv_data: connection closed");
114	}
115}
116
117/*
118 * Receive command from socket and return 1 if something fails.
119 * If command was received successfuly, 0 is returned.
120 */
121int
122recv_cmd(int sock, void *buf, size_t len)
123{
124	ssize_t n;
125
126	n = read(sock, buf, len);
127	if (n <= 0)
128		return (1);
129
130	return (0);
131}
132
133void
134set_monitor_signals(void)
135{
136	struct sigaction act;
137	int i;
138
139	sigemptyset(&act.sa_mask);
140	act.sa_flags = 0;
141
142	act.sa_handler = SIG_DFL;
143	for (i = 1; i < _NSIG; i++)
144		sigaction(i, &act, NULL);
145
146	act.sa_handler = sig_chld;
147	sigaction(SIGCHLD, &act, NULL);
148
149	act.sa_handler = sig_pass_to_slave;
150	sigaction(SIGHUP, &act, NULL);
151	sigaction(SIGINT, &act, NULL);
152	sigaction(SIGQUIT, &act, NULL);
153	sigaction(SIGTERM, &act, NULL);
154}
155
156/*
157 * Creates the privileged monitor process. It returns twice.
158 * It returns 1 for the unprivileged slave process and 0 for the
159 * user-privileged slave process after successful authentication.
160 */
161int
162monitor_init(void)
163{
164	struct passwd *pw;
165	int pair[2];
166
167	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1)
168		fatalx("socketpair failed");
169
170	fd_monitor = pair[0];
171	fd_slave = pair[1];
172
173	set_monitor_signals();
174
175	slave_pid = fork();
176	if (slave_pid == -1)
177		fatalx("fork of unprivileged slave failed");
178	if (slave_pid == 0) {
179		/* Unprivileged slave */
180		set_slave_signals();
181
182		if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL)
183			fatalx("privilege separation user %s not found",
184			    FTPD_PRIVSEP_USER);
185		endpwent();
186
187		if (chroot(pw->pw_dir) == -1)
188			fatalx("chroot %s: %m", pw->pw_dir);
189		if (chdir("/") == -1)
190			fatalx("chdir /: %m");
191
192		if (setgroups(1, &pw->pw_gid) == -1)
193			fatalx("setgroups: %m");
194		if (setegid(pw->pw_gid) == -1)
195			fatalx("setegid failed");
196		if (setgid(pw->pw_gid) == -1)
197			fatalx("setgid failed");
198		if (seteuid(pw->pw_uid) == -1)
199			fatalx("seteuid failed");
200		if (setuid(pw->pw_uid) == -1)
201			fatalx("setuid failed");
202		close(fd_slave);
203		return (1);
204	}
205
206#ifdef HASSETPROCTITLE
207	setproctitle("%s: [priv pre-auth]", remotehost);
208#endif
209
210	if (handle_cmds() == 1) {
211		debugmsg("slave lost. monitor quits now.");
212		_exit(0);
213	}
214
215	/* User-privileged slave */
216	return (0);
217}
218
219/*
220 * Creates the user-privileged slave process. It is called
221 * from the privileged monitor process and returns twice. It returns 0
222 * for the user-privileged slave process and 1 for the monitor process.
223 */
224int
225monitor_post_auth()
226{
227	slave_pid = fork();
228	if (slave_pid == -1)
229		fatalx("fork of user-privileged slave failed");
230
231	snprintf(ttyline, sizeof(ttyline), "ftp%ld",
232	    slave_pid == 0 ? (long)getpid() : (long)slave_pid);
233
234	if (slave_pid == 0) {
235		/* User privileged slave */
236		close(fd_slave);
237		set_slave_signals();
238		return (0);
239	}
240
241	/* We have to keep stdout open, because reply() needs it. */
242	if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
243		fatalx("cannot open %s: %m", _PATH_DEVNULL);
244	dup2(nullfd, STDIN_FILENO);
245	dup2(nullfd, STDERR_FILENO);
246	close(nullfd);
247	close(fd_monitor);
248
249	return (1);
250}
251
252/*
253 * Handles commands received from the slave process. It returns twice.
254 * It returns 0 for the user-privileged slave process after successful
255 * authentication and 1 if the user-privileged slave died.
256 */
257int
258handle_cmds(void)
259{
260	enum monitor_command cmd;
261	enum auth_ret auth;
262	int err, s, slavequit, serrno;
263	size_t len;
264	struct sockaddr sa;
265	socklen_t salen;
266	char *name, *pw;
267
268	while (quit == 0) {
269		if (recv_cmd(fd_slave, &cmd, sizeof(cmd)) != 0) {
270			if (quit == 1)
271				break;
272			else
273				continue;
274		}
275
276		switch (cmd) {
277		case CMD_USER:
278			debugmsg("CMD_USER received");
279
280			recv_data(fd_slave, &len, sizeof(len));
281			if ((name = malloc(len + 1)) == NULL)
282				fatalx("malloc: %m");
283			if (len > 0)
284				recv_data(fd_slave, name, len);
285			name[len] = '\0';
286
287			user(name);
288			free(name);
289			break;
290		case CMD_PASS:
291			debugmsg("CMD_PASS received");
292
293			recv_data(fd_slave, &len, sizeof(len));
294			if ((pw = malloc(len + 1)) == NULL)
295				fatalx("malloc: %m");
296			if (len > 0)
297				recv_data(fd_slave, pw, len);
298			pw[len] = '\0';
299
300			auth = pass(pw);
301			bzero(pw, len);
302			free(pw);
303
304			switch (auth) {
305			case AUTH_FAILED:
306				/* Authentication failure */
307				debugmsg("authentication failed");
308				slavequit = 0;
309				send_data(fd_slave, &slavequit,
310				    sizeof(slavequit));
311				break;
312			case AUTH_SLAVE:
313				/* User-privileged slave */
314				debugmsg("user-privileged slave started");
315				return (0);
316				/* NOTREACHED */
317				break;
318			case AUTH_MONITOR:
319				/* Post-auth monitor */
320				debugmsg("monitor went into post-auth phase");
321				state = POSTAUTH;
322#ifdef HASSETPROCTITLE
323				setproctitle("%s: [priv post-auth]",
324				    remotehost);
325#endif
326				slavequit = 1;
327
328				send_data(fd_slave, &slavequit,
329				    sizeof(slavequit));
330				break;
331			default:
332				fatalx("bad return value from pass()");
333				/* NOTREACHED */
334				break;
335			}
336			break;
337		case CMD_BIND:
338			debugmsg("CMD_BIND received");
339
340			if (state != POSTAUTH)
341				fatalx("CMD_BIND received in invalid state");
342
343			s = recv_fd(fd_slave);
344
345			recv_data(fd_slave, &salen, sizeof(salen));
346			if (salen == 0 || salen > sizeof(sa))
347				fatalx("monitor received invalid sockaddr len");
348
349			bzero(&sa, sizeof(sa));
350			recv_data(fd_slave, &sa, salen);
351
352			if (sa.sa_len != salen)
353				fatalx("monitor received invalid sockaddr len");
354
355			if (sa.sa_family != AF_INET && sa.sa_family != AF_INET6)
356				fatalx("monitor received invalid addr family");
357
358			err = bind(s, &sa, salen);
359			serrno = errno;
360
361			if (s >= 0)
362				close(s);
363
364			send_data(fd_slave, &err, sizeof(err));
365			if (err == -1)
366				send_data(fd_slave, &serrno, sizeof(serrno));
367			break;
368		default:
369			fatalx("monitor received unknown command %d", cmd);
370			/* NOTREACHED */
371			break;
372		}
373	}
374
375	return (1);
376}
377
378void
379sig_pass_to_slave(int signo)
380{
381	sigset_t allsigs, oldsigs;
382	int olderrno = errno;
383
384	sigfillset(&allsigs);
385	sigprocmask(SIG_BLOCK, &allsigs, &oldsigs);
386
387	if (slave_pid > 0)
388		kill(slave_pid, signo);
389
390	sigprocmask(SIG_SETMASK, &oldsigs, NULL);
391	errno = olderrno;
392}
393
394/* ARGSUSED */
395void
396sig_chld(int signo)
397{
398	pid_t pid;
399	sigset_t allsigs, oldsigs;
400	int stat, olderrno = errno;
401
402	sigfillset(&allsigs);
403	sigprocmask(SIG_BLOCK, &allsigs, &oldsigs);
404
405	do {
406		pid = waitpid(-1, &stat, WNOHANG);
407	} while (pid == -1 && errno == EINTR);
408
409	if (pid == slave_pid && stat != PREAUTH_SLAVE_DIED) {
410		quit = 1;
411		slave_pid = -1;
412	}
413
414	sigprocmask(SIG_SETMASK, &oldsigs, NULL);
415	errno = olderrno;
416}
417
418void
419kill_slave(void)
420{
421	if (slave_pid > 0)
422		kill(slave_pid, SIGQUIT);
423}
424
425void
426fatalx(char *fmt, ...)
427{
428	va_list ap;
429
430	va_start(ap, fmt);
431	vsyslog(LOG_ERR, fmt, ap);
432	va_end(ap);
433
434	kill_slave();
435
436	_exit(0);
437}
438
439void
440debugmsg(char *fmt, ...)
441{
442	va_list ap;
443
444	if (debug) {
445		va_start(ap, fmt);
446		vsyslog(LOG_DEBUG, fmt, ap);
447		va_end(ap);
448	}
449}
450
451void
452monitor_user(char *name)
453{
454	enum monitor_command cmd;
455	size_t len;
456
457	cmd = CMD_USER;
458	send_data(fd_monitor, &cmd, sizeof(cmd));
459
460	len = strlen(name);
461	send_data(fd_monitor, &len, sizeof(len));
462	if (len > 0)
463		send_data(fd_monitor, name, len);
464}
465
466int
467monitor_pass(char *pass)
468{
469	enum monitor_command cmd;
470	int quitnow;
471	size_t len;
472
473	cmd = CMD_PASS;
474	send_data(fd_monitor, &cmd, sizeof(cmd));
475
476	len = strlen(pass);
477	send_data(fd_monitor, &len, sizeof(len));
478	if (len > 0)
479		send_data(fd_monitor, pass, len);
480
481	recv_data(fd_monitor, &quitnow, sizeof(quitnow));
482
483	return (quitnow);
484}
485
486int
487monitor_bind(int s, struct sockaddr *name, socklen_t namelen)
488{
489	enum monitor_command cmd;
490	int ret, serrno;
491
492	cmd = CMD_BIND;
493	send_data(fd_monitor, &cmd, sizeof(cmd));
494
495	send_fd(fd_monitor, s);
496	send_data(fd_monitor, &namelen, sizeof(namelen));
497	send_data(fd_monitor, name, namelen);
498
499	recv_data(fd_monitor, &ret, sizeof(ret));
500	if (ret == -1) {
501		recv_data(fd_monitor, &serrno, sizeof(serrno));
502		errno = serrno;
503	}
504
505	return (ret);
506}
507