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