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