monitor.c revision 1.14
1/*	$OpenBSD: monitor.c,v 1.14 2006/04/21 17:42:50 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 <limits.h>
27#include <paths.h>
28#include <pwd.h>
29#include <signal.h>
30#include <stdarg.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <syslog.h>
35#include <unistd.h>
36
37#include "extern.h"
38#include "monitor.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
52#ifdef HASSETPROCTITLE
53extern char	remotehost[];
54#endif
55extern char	ttyline[20];
56extern int	debug;
57
58extern void	set_slave_signals(void);
59
60int	fd_monitor = -1;
61int	fd_slave = -1;
62int	nullfd;
63pid_t	slave_pid = -1;
64enum monitor_state	state = PREAUTH;
65
66void	send_data(int, void *, size_t);
67void	recv_data(int, void *, size_t);
68void	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 = 0;
83	char *ptr = buf;
84
85	while (len > pos) {
86		switch (n = write(sock, ptr + pos, len - pos)) {
87		case 0:
88			kill_slave();
89			_exit(0);
90			/* NOTREACHED */
91		case -1:
92			if (errno != EINTR && errno != EAGAIN)
93				fatalx("send_data: %m");
94			break;
95		default:
96			pos += n;
97		}
98	}
99}
100
101/*
102 * Receive data from socket and exit if something fails.
103 */
104void
105recv_data(int sock, void *buf, size_t len)
106{
107	ssize_t n;
108	size_t pos = 0;
109	char *ptr = buf;
110
111	while (len > pos) {
112		switch (n = read(sock, ptr + pos, len - pos)) {
113		case 0:
114			kill_slave();
115			_exit(0);
116			/* NOTREACHED */
117		case -1:
118			if (errno != EINTR && errno != EAGAIN)
119				fatalx("recv_data: %m");
120			break;
121		default:
122			pos += n;
123		}
124	}
125}
126
127void
128set_monitor_signals(void)
129{
130	struct sigaction act;
131	int i;
132
133	sigfillset(&act.sa_mask);
134	act.sa_flags = SA_RESTART;
135
136	act.sa_handler = SIG_DFL;
137	for (i = 1; i < _NSIG; i++)
138		sigaction(i, &act, NULL);
139
140	act.sa_handler = sig_chld;
141	sigaction(SIGCHLD, &act, NULL);
142
143	act.sa_handler = sig_pass_to_slave;
144	sigaction(SIGHUP, &act, NULL);
145	sigaction(SIGINT, &act, NULL);
146	sigaction(SIGQUIT, &act, NULL);
147	sigaction(SIGTERM, &act, NULL);
148}
149
150/*
151 * Creates the privileged monitor process. It returns twice.
152 * It returns 1 for the unprivileged slave process and 0 for the
153 * user-privileged slave process after successful authentication.
154 */
155int
156monitor_init(void)
157{
158	struct passwd *pw;
159	int pair[2];
160
161	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, pair) == -1)
162		fatalx("socketpair failed");
163
164	fd_monitor = pair[0];
165	fd_slave = pair[1];
166
167	set_monitor_signals();
168
169	slave_pid = fork();
170	if (slave_pid == -1)
171		fatalx("fork of unprivileged slave failed");
172	if (slave_pid == 0) {
173		/* Unprivileged slave */
174		set_slave_signals();
175
176		if ((pw = getpwnam(FTPD_PRIVSEP_USER)) == NULL)
177			fatalx("privilege separation user %s not found",
178			    FTPD_PRIVSEP_USER);
179
180		if (chroot(pw->pw_dir) == -1)
181			fatalx("chroot %s: %m", pw->pw_dir);
182		if (chdir("/") == -1)
183			fatalx("chdir /: %m");
184
185		if (setgroups(1, &pw->pw_gid) == -1)
186			fatalx("setgroups: %m");
187		if (setegid(pw->pw_gid) == -1)
188			fatalx("setegid failed");
189		if (setgid(pw->pw_gid) == -1)
190			fatalx("setgid failed");
191		if (seteuid(pw->pw_uid) == -1)
192			fatalx("seteuid failed");
193		if (setuid(pw->pw_uid) == -1)
194			fatalx("setuid failed");
195
196		endpwent();
197		close(fd_slave);
198		return (1);
199	}
200
201#ifdef HASSETPROCTITLE
202	setproctitle("%s: [priv pre-auth]", remotehost);
203#endif
204
205	handle_cmds();
206
207	/* User-privileged slave */
208	return (0);
209}
210
211/*
212 * Creates the user-privileged slave process. It is called
213 * from the privileged monitor process and returns twice. It returns 0
214 * for the user-privileged slave process and 1 for the monitor process.
215 */
216int
217monitor_post_auth()
218{
219	slave_pid = fork();
220	if (slave_pid == -1)
221		fatalx("fork of user-privileged slave failed");
222
223	snprintf(ttyline, sizeof(ttyline), "ftp%ld",
224	    slave_pid == 0 ? (long)getpid() : (long)slave_pid);
225
226	if (slave_pid == 0) {
227		/* User privileged slave */
228		close(fd_slave);
229		set_slave_signals();
230		return (0);
231	}
232
233	/* We have to keep stdout open, because reply() needs it. */
234	if ((nullfd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1)
235		fatalx("cannot open %s: %m", _PATH_DEVNULL);
236	dup2(nullfd, STDIN_FILENO);
237	dup2(nullfd, STDERR_FILENO);
238	close(nullfd);
239	close(fd_monitor);
240
241	return (1);
242}
243
244/*
245 * Handles commands received from the slave process. It will not return
246 * except in one situation: After successful authentication it will
247 * return as the user-privileged slave process.
248 */
249void
250handle_cmds(void)
251{
252	enum monitor_command cmd;
253	enum auth_ret auth;
254	int err, s, slavequit, serrno, domain;
255	pid_t preauth_slave_pid;
256	size_t len;
257	struct sockaddr sa;
258	socklen_t salen;
259	char *name, *pw;
260
261	for (;;) {
262		recv_data(fd_slave, &cmd, sizeof(cmd));
263
264		switch (cmd) {
265		case CMD_USER:
266			debugmsg("CMD_USER received");
267
268			recv_data(fd_slave, &len, sizeof(len));
269			if (len == SIZE_T_MAX)
270				fatalx("monitor received invalid user length");
271			if ((name = malloc(len + 1)) == NULL)
272				fatalx("malloc: %m");
273			if (len > 0)
274				recv_data(fd_slave, name, len);
275			name[len] = '\0';
276
277			user(name);
278			free(name);
279			break;
280		case CMD_PASS:
281			debugmsg("CMD_PASS received");
282
283			recv_data(fd_slave, &len, sizeof(len));
284			if (len == SIZE_T_MAX)
285				fatalx("monitor received invalid pass length");
286			if ((pw = malloc(len + 1)) == NULL)
287				fatalx("malloc: %m");
288			if (len > 0)
289				recv_data(fd_slave, pw, len);
290			pw[len] = '\0';
291
292			preauth_slave_pid = slave_pid;
293
294			auth = pass(pw);
295			bzero(pw, len);
296			free(pw);
297
298			switch (auth) {
299			case AUTH_FAILED:
300				/* Authentication failure */
301				debugmsg("authentication failed");
302				slavequit = 0;
303				send_data(fd_slave, &slavequit,
304				    sizeof(slavequit));
305				break;
306			case AUTH_SLAVE:
307				/* User-privileged slave */
308				debugmsg("user-privileged slave started");
309				return;
310				/* NOTREACHED */
311			case AUTH_MONITOR:
312				/* Post-auth monitor */
313				debugmsg("monitor went into post-auth phase");
314				state = POSTAUTH;
315#ifdef HASSETPROCTITLE
316				setproctitle("%s: [priv post-auth]",
317				    remotehost);
318#endif
319				slavequit = 1;
320
321				send_data(fd_slave, &slavequit,
322				    sizeof(slavequit));
323
324				while (waitpid(preauth_slave_pid, NULL, 0) < 0 &&
325				    errno == EINTR)
326					;
327				break;
328			default:
329				fatalx("bad return value from pass()");
330				/* NOTREACHED */
331			}
332			break;
333		case CMD_SOCKET:
334			debugmsg("CMD_SOCKET received");
335
336			if (state != POSTAUTH)
337				fatalx("CMD_SOCKET received in invalid state");
338
339			recv_data(fd_slave, &domain, sizeof(domain));
340			if (domain != AF_INET && domain != AF_INET6)
341				fatalx("monitor received invalid addr family");
342
343			s = socket(domain, SOCK_STREAM, 0);
344			serrno = errno;
345
346			send_fd(fd_slave, s);
347			if (s == -1)
348				send_data(fd_slave, &serrno, sizeof(serrno));
349			else
350				close(s);
351			break;
352		case CMD_BIND:
353			debugmsg("CMD_BIND received");
354
355			if (state != POSTAUTH)
356				fatalx("CMD_BIND received in invalid state");
357
358			s = recv_fd(fd_slave);
359
360			recv_data(fd_slave, &salen, sizeof(salen));
361			if (salen == 0 || salen > sizeof(sa))
362				fatalx("monitor received invalid sockaddr len");
363
364			bzero(&sa, sizeof(sa));
365			recv_data(fd_slave, &sa, salen);
366
367			if (sa.sa_len != salen)
368				fatalx("monitor received invalid sockaddr len");
369
370			if (sa.sa_family != AF_INET && sa.sa_family != AF_INET6)
371				fatalx("monitor received invalid addr family");
372
373			err = bind(s, &sa, salen);
374			serrno = errno;
375
376			if (s >= 0)
377				close(s);
378
379			send_data(fd_slave, &err, sizeof(err));
380			if (err == -1)
381				send_data(fd_slave, &serrno, sizeof(serrno));
382			break;
383		default:
384			fatalx("monitor received unknown command %d", cmd);
385			/* NOTREACHED */
386		}
387	}
388	/* NOTREACHED */
389}
390
391void
392sig_pass_to_slave(int signo)
393{
394	int olderrno = errno;
395
396	if (slave_pid > 0)
397		kill(slave_pid, signo);
398
399	errno = olderrno;
400}
401
402/* ARGSUSED */
403void
404sig_chld(int signo)
405{
406	pid_t pid;
407	int stat, olderrno = errno;
408
409	do {
410		pid = waitpid(slave_pid, &stat, WNOHANG);
411		if (pid > 0)
412			_exit(0);
413	} while (pid == -1 && errno == EINTR);
414
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_socket(int domain)
488{
489	enum monitor_command cmd;
490	int s, serrno;
491
492	cmd = CMD_SOCKET;
493	send_data(fd_monitor, &cmd, sizeof(cmd));
494	send_data(fd_monitor, &domain, sizeof(domain));
495
496	s = recv_fd(fd_monitor);
497	if (s == -1) {
498		recv_data(fd_monitor, &serrno, sizeof(serrno));
499		errno = serrno;
500	}
501
502	return (s);
503}
504
505int
506monitor_bind(int s, struct sockaddr *name, socklen_t namelen)
507{
508	enum monitor_command cmd;
509	int ret, serrno;
510
511	cmd = CMD_BIND;
512	send_data(fd_monitor, &cmd, sizeof(cmd));
513
514	send_fd(fd_monitor, s);
515	send_data(fd_monitor, &namelen, sizeof(namelen));
516	send_data(fd_monitor, name, namelen);
517
518	recv_data(fd_monitor, &ret, sizeof(ret));
519	if (ret == -1) {
520		recv_data(fd_monitor, &serrno, sizeof(serrno));
521		errno = serrno;
522	}
523
524	return (ret);
525}
526