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