xref: /dragonfly/usr.sbin/pflogd/privsep.c (revision 73610d44)
1 /*	$OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $	*/
2 
3 /*
4  * Copyright (c) 2003 Can Erkin Acar
5  * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/socket.h>
22 #include <sys/ioctl.h>
23 
24 #include <net/if.h>
25 #include <net/bpf.h>
26 
27 #include <err.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h>
31 #include <pwd.h>
32 #include <signal.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <pcap.h>
37 #include <pcap-int.h>
38 #include <syslog.h>
39 #include <unistd.h>
40 #include "pflogd.h"
41 
42 enum cmd_types {
43 	PRIV_SET_SNAPLEN,	/* set the snaplength */
44 	PRIV_MOVE_LOG,		/* move logfile away */
45 	PRIV_OPEN_LOG		/* open logfile for appending */
46 };
47 
48 static int priv_fd = -1;
49 static volatile pid_t child_pid = -1;
50 
51 volatile sig_atomic_t gotsig_chld = 0;
52 
53 static void sig_pass_to_chld(int);
54 static void sig_chld(int);
55 static int  may_read(int, void *, size_t);
56 static void must_read(int, void *, size_t);
57 static void must_write(int, void *, size_t);
58 static int  set_snaplen(int snap);
59 static int  move_log(const char *name);
60 
61 extern char *filename;
62 extern pcap_t *hpcap;
63 
64 /* based on syslogd privsep */
65 int
66 priv_init(void)
67 {
68 	int i, fd, socks[2], cmd;
69 	int snaplen, ret, olderrno;
70 	struct passwd *pw;
71 
72 	for (i = 1; i < NSIG; i++)
73 		signal(i, SIG_DFL);
74 
75 	/* Create sockets */
76 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
77 		err(1, "socketpair() failed");
78 
79 	pw = getpwnam("_pflogd");
80 	if (pw == NULL)
81 		errx(1, "unknown user _pflogd");
82 	endpwent();
83 
84 	child_pid = fork();
85 	if (child_pid < 0)
86 		err(1, "fork() failed");
87 
88 	if (!child_pid) {
89 		gid_t gidset[1];
90 
91 		/* Child - drop privileges and return */
92 		if (chroot(pw->pw_dir) != 0)
93 			err(1, "unable to chroot");
94 		if (chdir("/") != 0)
95 			err(1, "unable to chdir");
96 
97 		gidset[0] = pw->pw_gid;
98 		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
99 			err(1, "setresgid() failed");
100 		if (setgroups(1, gidset) == -1)
101 			err(1, "setgroups() failed");
102 		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
103 			err(1, "setresuid() failed");
104 		close(socks[0]);
105 		priv_fd = socks[1];
106 		return 0;
107 	}
108 
109 	/* Father */
110 	/* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
111 	signal(SIGALRM, sig_pass_to_chld);
112 	signal(SIGTERM, sig_pass_to_chld);
113 	signal(SIGHUP,  sig_pass_to_chld);
114 	signal(SIGINT,  sig_pass_to_chld);
115 	signal(SIGQUIT,  sig_pass_to_chld);
116 	signal(SIGCHLD, sig_chld);
117 
118 	setproctitle("[priv]");
119 	close(socks[1]);
120 
121 	while (!gotsig_chld) {
122 		if (may_read(socks[0], &cmd, sizeof(int)))
123 			break;
124 		switch (cmd) {
125 		case PRIV_SET_SNAPLEN:
126 			logmsg(LOG_DEBUG,
127 			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
128 			must_read(socks[0], &snaplen, sizeof(int));
129 
130 			ret = set_snaplen(snaplen);
131 			if (ret) {
132 				logmsg(LOG_NOTICE,
133 				   "[priv]: set_snaplen failed for snaplen %d",
134 				   snaplen);
135 			}
136 
137 			must_write(socks[0], &ret, sizeof(int));
138 			break;
139 
140 		case PRIV_OPEN_LOG:
141 			logmsg(LOG_DEBUG,
142 			    "[priv]: msg PRIV_OPEN_LOG received");
143 			/* create or append logs but do not follow symlinks */
144 			fd = open(filename,
145 			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
146 			    0600);
147 			olderrno = errno;
148 			send_fd(socks[0], fd);
149 			if (fd < 0)
150 				logmsg(LOG_NOTICE,
151 				    "[priv]: failed to open %s: %s",
152 				    filename, strerror(olderrno));
153 			else
154 				close(fd);
155 			break;
156 
157 		case PRIV_MOVE_LOG:
158 			logmsg(LOG_DEBUG,
159 			    "[priv]: msg PRIV_MOVE_LOG received");
160 			ret = move_log(filename);
161 			must_write(socks[0], &ret, sizeof(int));
162 			break;
163 
164 		default:
165 			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
166 			_exit(1);
167 			/* NOTREACHED */
168 		}
169 	}
170 
171 	_exit(1);
172 }
173 
174 /* this is called from parent */
175 static int
176 set_snaplen(int snap)
177 {
178 	if (hpcap == NULL)
179 		return (1);
180 
181 	hpcap->snapshot = snap;
182 	set_pcap_filter();
183 
184 	return 0;
185 }
186 
187 static int
188 move_log(const char *name)
189 {
190 	char ren[PATH_MAX];
191 	u_int32_t len;
192 
193 	for (;;) {
194 		int fd;
195 
196 		len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
197 		    name, arc4random());
198 		if (len >= sizeof(ren)) {
199 			logmsg(LOG_ERR, "[priv] new name too long");
200 			return (1);
201 		}
202 
203 		/* lock destinanion */
204 		fd = open(ren, O_CREAT|O_EXCL, 0);
205 		if (fd >= 0) {
206 			close(fd);
207 			break;
208 		}
209 		/* if file exists, try another name */
210 		if (errno != EEXIST && errno != EINTR) {
211 			logmsg(LOG_ERR, "[priv] failed to create new name: %s",
212 			    strerror(errno));
213 			return (1);
214 		}
215 	}
216 
217 	if (rename(name, ren)) {
218 		logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
219 		    name, ren, strerror(errno));
220 		return (1);
221 	}
222 
223 	logmsg(LOG_NOTICE,
224 	       "[priv]: log file %s moved to %s", name, ren);
225 
226 	return (0);
227 }
228 
229 /*
230  * send the snaplength to privileged process
231  */
232 int
233 priv_set_snaplen(int snaplen)
234 {
235 	int cmd, ret;
236 
237 	if (priv_fd < 0)
238 		errx(1, "%s: called from privileged portion", __func__);
239 
240 	cmd = PRIV_SET_SNAPLEN;
241 
242 	must_write(priv_fd, &cmd, sizeof(int));
243 	must_write(priv_fd, &snaplen, sizeof(int));
244 
245 	must_read(priv_fd, &ret, sizeof(int));
246 
247 	/* also set hpcap->snapshot in child */
248 	if (ret == 0)
249 		hpcap->snapshot = snaplen;
250 
251 	return (ret);
252 }
253 
254 /* Open log-file */
255 int
256 priv_open_log(void)
257 {
258 	int cmd, fd;
259 
260 	if (priv_fd < 0)
261 		errx(1, "%s: called from privileged portion", __func__);
262 
263 	cmd = PRIV_OPEN_LOG;
264 	must_write(priv_fd, &cmd, sizeof(int));
265 	fd = receive_fd(priv_fd);
266 
267 	return (fd);
268 }
269 /* Move-away and reopen log-file */
270 int
271 priv_move_log(void)
272 {
273 	int cmd, ret;
274 
275 	if (priv_fd < 0)
276 		errx(1, "%s: called from privileged portion\n", __func__);
277 
278 	cmd = PRIV_MOVE_LOG;
279 	must_write(priv_fd, &cmd, sizeof(int));
280 	must_read(priv_fd, &ret, sizeof(int));
281 
282 	return (ret);
283 }
284 
285 /* If priv parent gets a TERM or HUP, pass it through to child instead */
286 static void
287 sig_pass_to_chld(int sig)
288 {
289 	int oerrno = errno;
290 
291 	if (child_pid != -1)
292 		kill(child_pid, sig);
293 	errno = oerrno;
294 }
295 
296 /* if parent gets a SIGCHLD, it will exit */
297 static void
298 sig_chld(int sig __unused)
299 {
300 	gotsig_chld = 1;
301 }
302 
303 /* Read all data or return 1 for error.  */
304 static int
305 may_read(int fd, void *buf, size_t n)
306 {
307 	char *s = buf;
308 	ssize_t res;
309 	size_t pos = 0;
310 
311 	while (n > pos) {
312 		res = read(fd, s + pos, n - pos);
313 		switch (res) {
314 		case -1:
315 			if (errno == EINTR || errno == EAGAIN)
316 				continue;
317 		case 0:
318 			return (1);
319 		default:
320 			pos += res;
321 		}
322 	}
323 	return (0);
324 }
325 
326 /* Read data with the assertion that it all must come through, or
327  * else abort the process.  Based on atomicio() from openssh. */
328 static void
329 must_read(int fd, void *buf, size_t n)
330 {
331 	char *s = buf;
332 	ssize_t res;
333 	size_t pos = 0;
334 
335 	while (n > pos) {
336 		res = read(fd, s + pos, n - pos);
337 		switch (res) {
338 		case -1:
339 			if (errno == EINTR || errno == EAGAIN)
340 				continue;
341 		case 0:
342 			_exit(0);
343 		default:
344 			pos += res;
345 		}
346 	}
347 }
348 
349 /* Write data with the assertion that it all has to be written, or
350  * else abort the process.  Based on atomicio() from openssh. */
351 static void
352 must_write(int fd, void *buf, size_t n)
353 {
354 	char *s = buf;
355 	ssize_t res;
356 	size_t pos = 0;
357 
358 	while (n > pos) {
359 		res = write(fd, s + pos, n - pos);
360 		switch (res) {
361 		case -1:
362 			if (errno == EINTR || errno == EAGAIN)
363 				continue;
364 		case 0:
365 			_exit(0);
366 		default:
367 			pos += res;
368 		}
369 	}
370 }
371