GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: sbin/pflogd/privsep.c Lines: 0 187 0.0 %
Date: 2017-11-13 Branches: 0 103 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: privsep.c,v 1.30 2017/09/09 13:02:52 brynet 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 <pcap.h>
32
#include <pcap-int.h>
33
#include <pwd.h>
34
#include <signal.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
#include <syslog.h>
39
#include <unistd.h>
40
#include "pflogd.h"
41
42
enum cmd_types {
43
	PRIV_INIT_PCAP,		/* init pcap fdpass bpf */
44
	PRIV_SET_SNAPLEN,	/* set the snaplength */
45
	PRIV_MOVE_LOG,		/* move logfile away */
46
	PRIV_OPEN_LOG,		/* open logfile for appending */
47
};
48
49
static int priv_fd = -1;
50
static volatile pid_t child_pid = -1;
51
52
static void sig_pass_to_chld(int);
53
static int  may_read(int, void *, size_t);
54
static void must_read(int, void *, size_t);
55
static void must_write(int, void *, size_t);
56
static int  set_snaplen(int snap);
57
static int  move_log(const char *name);
58
59
extern char *filename;
60
extern char *interface;
61
extern char errbuf[PCAP_ERRBUF_SIZE];
62
extern pcap_t *hpcap;
63
64
/* based on syslogd privsep */
65
void
66
priv_init(int Pflag, int argc, char *argv[])
67
{
68
	int i, fd = -1, bpfd = -1, nargc, socks[2], cmd;
69
	int snaplen, ret, olderrno;
70
	struct passwd *pw;
71
	char **nargv;
72
	unsigned int buflen;
73
74
	pw = getpwnam("_pflogd");
75
	if (pw == NULL)
76
		errx(1, "unknown user _pflogd");
77
	endpwent();
78
79
	if (Pflag) {
80
		gid_t gidset[1];
81
82
		/* Child - drop privileges and return */
83
		if (chroot(pw->pw_dir) != 0)
84
			err(1, "unable to chroot");
85
		if (chdir("/") != 0)
86
			err(1, "unable to chdir");
87
88
		gidset[0] = pw->pw_gid;
89
		if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
90
			err(1, "setresgid() failed");
91
		if (setgroups(1, gidset) == -1)
92
			err(1, "setgroups() failed");
93
		if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
94
			err(1, "setresuid() failed");
95
		priv_fd = 3;
96
		return;
97
	}
98
99
	/* Create sockets */
100
	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
101
		err(1, "socketpair() failed");
102
103
	child_pid = fork();
104
	if (child_pid < 0)
105
		err(1, "fork() failed");
106
107
	if (!child_pid) {
108
		close(socks[0]);
109
		if (dup2(socks[1], 3) == -1)
110
			err(1, "dup2 unpriv sock failed");
111
		close(socks[1]);
112
		if ((nargv = reallocarray(NULL, argc + 2,
113
		    sizeof(char *))) == NULL)
114
			err(1, "alloc unpriv argv failed");
115
		nargc = 0;
116
		nargv[nargc++] = argv[0];
117
		nargv[nargc++] = "-P";
118
		for (i = 1; i < argc; i++)
119
			nargv[nargc++] = argv[i];
120
		nargv[nargc] = NULL;
121
		execvp(nargv[0], nargv);
122
		err(1, "exec unpriv '%s' failed", nargv[0]);
123
	}
124
	close(socks[1]);
125
126
	/* Father */
127
	/* Pass ALRM/TERM/HUP/INT/QUIT through to child */
128
	signal(SIGALRM, sig_pass_to_chld);
129
	signal(SIGTERM, sig_pass_to_chld);
130
	signal(SIGHUP,  sig_pass_to_chld);
131
	signal(SIGINT,  sig_pass_to_chld);
132
	signal(SIGQUIT, sig_pass_to_chld);
133
134
	setproctitle("[priv]");
135
136
#if 0
137
	/* This needs to do bpf ioctl */
138
BROKEN	if (pledge("stdio rpath wpath cpath sendfd proc bpf flock", NULL) == -1)
139
		err(1, "pledge");
140
#endif
141
142
	while (1) {
143
		if (may_read(socks[0], &cmd, sizeof(int)))
144
			break;
145
		switch (cmd) {
146
		case PRIV_INIT_PCAP:
147
			logmsg(LOG_DEBUG,
148
			    "[priv]: msg PRIV_INIT_PCAP received");
149
			/* initialize pcap */
150
			if (hpcap != NULL || init_pcap()) {
151
				logmsg(LOG_ERR, "[priv]: Exiting, init failed");
152
				_exit(1);
153
			}
154
			buflen = hpcap->bufsize; /* BIOCGBLEN for unpriv proc */
155
			must_write(socks[0], &buflen, sizeof(unsigned int));
156
			fd = pcap_fileno(hpcap);
157
			send_fd(socks[0], fd);
158
			if (fd < 0) {
159
				logmsg(LOG_ERR, "[priv]: Exiting, init failed");
160
				_exit(1);
161
			}
162
			break;
163
164
		case PRIV_SET_SNAPLEN:
165
			logmsg(LOG_DEBUG,
166
			    "[priv]: msg PRIV_SET_SNAPLENGTH received");
167
			must_read(socks[0], &snaplen, sizeof(int));
168
169
			ret = set_snaplen(snaplen);
170
			if (ret) {
171
				logmsg(LOG_NOTICE,
172
				   "[priv]: set_snaplen failed for snaplen %d",
173
				   snaplen);
174
			}
175
176
			must_write(socks[0], &ret, sizeof(int));
177
			break;
178
179
		case PRIV_OPEN_LOG:
180
			logmsg(LOG_DEBUG,
181
			    "[priv]: msg PRIV_OPEN_LOG received");
182
			/* create or append logs but do not follow symlinks */
183
			if (bpfd != -1) {
184
				close(bpfd);
185
				bpfd = -1;
186
			}
187
			bpfd = open(filename,
188
			    O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
189
			    0600);
190
			olderrno = errno;
191
			send_fd(socks[0], bpfd);
192
			if (bpfd < 0)
193
				logmsg(LOG_NOTICE,
194
				    "[priv]: failed to open %s: %s",
195
				    filename, strerror(olderrno));
196
			break;
197
198
		case PRIV_MOVE_LOG:
199
			logmsg(LOG_DEBUG,
200
			    "[priv]: msg PRIV_MOVE_LOG received");
201
			ret = move_log(filename);
202
			must_write(socks[0], &ret, sizeof(int));
203
			break;
204
205
		default:
206
			logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
207
			_exit(1);
208
			/* NOTREACHED */
209
		}
210
	}
211
212
	exit(1);
213
}
214
215
/* this is called from parent */
216
static int
217
set_snaplen(int snap)
218
{
219
	if (hpcap == NULL)
220
		return (1);
221
222
	hpcap->snapshot = snap;
223
	set_pcap_filter();
224
225
	return 0;
226
}
227
228
static int
229
move_log(const char *name)
230
{
231
	char ren[PATH_MAX];
232
	int len;
233
234
	for (;;) {
235
		int fd;
236
237
		len = snprintf(ren, sizeof(ren), "%s.bad.XXXXXXXX", name);
238
		if (len >= sizeof(ren)) {
239
			logmsg(LOG_ERR, "[priv] new name too long");
240
			return (1);
241
		}
242
243
		/* lock destination */
244
		fd = mkstemp(ren);
245
		if (fd >= 0) {
246
			close(fd);
247
			break;
248
		}
249
		if (errno != EINTR) {
250
			logmsg(LOG_ERR, "[priv] failed to create new name: %s",
251
			    strerror(errno));
252
			return (1);
253
		}
254
	}
255
256
	if (rename(name, ren)) {
257
		logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
258
		    name, ren, strerror(errno));
259
		unlink(ren);
260
		return (1);
261
	}
262
263
	logmsg(LOG_NOTICE,
264
	       "[priv]: log file %s moved to %s", name, ren);
265
266
	return (0);
267
}
268
269
270
/* receive bpf fd from privileged process using fdpass and init pcap */
271
int
272
priv_init_pcap(int snaplen)
273
{
274
	int cmd, fd;
275
	unsigned int buflen;
276
277
	if (priv_fd < 0)
278
		errx(1, "%s: called from privileged portion", __func__);
279
280
	cmd = PRIV_INIT_PCAP;
281
282
	must_write(priv_fd, &cmd, sizeof(int));
283
	must_read(priv_fd, &buflen, sizeof(unsigned int));
284
	fd = receive_fd(priv_fd);
285
	if (fd < 0)
286
		return (-1);
287
288
	/* XXX temporary until pcap_open_live_fd API */
289
	hpcap = pcap_create(interface, errbuf);
290
	if (hpcap == NULL)
291
		return (-1);
292
293
	/* XXX copies from pcap_open_live/pcap_activate */
294
	hpcap->fd = fd;
295
	pcap_set_snaplen(hpcap, snaplen);
296
	pcap_set_promisc(hpcap, 1);
297
	pcap_set_timeout(hpcap, PCAP_TO_MS);
298
	hpcap->oldstyle = 1;
299
	hpcap->linktype = DLT_PFLOG;
300
	hpcap->bufsize = buflen; /* XXX bpf BIOCGBLEN */
301
	hpcap->buffer = malloc(hpcap->bufsize);
302
	if (hpcap->buffer == NULL)
303
		return (-1);
304
	hpcap->activated = 1;
305
306
	return (0);
307
}
308
309
/*
310
 * send the snaplength to privileged process
311
 */
312
int
313
priv_set_snaplen(int snaplen)
314
{
315
	int cmd, ret;
316
317
	if (priv_fd < 0)
318
		errx(1, "%s: called from privileged portion", __func__);
319
320
	cmd = PRIV_SET_SNAPLEN;
321
322
	must_write(priv_fd, &cmd, sizeof(int));
323
	must_write(priv_fd, &snaplen, sizeof(int));
324
325
	must_read(priv_fd, &ret, sizeof(int));
326
327
	/* also set hpcap->snapshot in child */
328
	if (ret == 0)
329
		hpcap->snapshot = snaplen;
330
331
	return (ret);
332
}
333
334
/* Open log-file */
335
int
336
priv_open_log(void)
337
{
338
	int cmd, fd;
339
340
	if (priv_fd < 0)
341
		errx(1, "%s: called from privileged portion", __func__);
342
343
	cmd = PRIV_OPEN_LOG;
344
	must_write(priv_fd, &cmd, sizeof(int));
345
	fd = receive_fd(priv_fd);
346
347
	return (fd);
348
}
349
350
/* Move-away and reopen log-file */
351
int
352
priv_move_log(void)
353
{
354
	int cmd, ret;
355
356
	if (priv_fd < 0)
357
		errx(1, "%s: called from privileged portion", __func__);
358
359
	cmd = PRIV_MOVE_LOG;
360
	must_write(priv_fd, &cmd, sizeof(int));
361
	must_read(priv_fd, &ret, sizeof(int));
362
363
	return (ret);
364
}
365
366
/* If priv parent gets a TERM or HUP, pass it through to child instead */
367
static void
368
sig_pass_to_chld(int sig)
369
{
370
	int oerrno = errno;
371
372
	if (child_pid != -1)
373
		kill(child_pid, sig);
374
	errno = oerrno;
375
}
376
377
/* Read all data or return 1 for error.  */
378
static int
379
may_read(int fd, void *buf, size_t n)
380
{
381
	char *s = buf;
382
	ssize_t res, pos = 0;
383
384
	while (n > pos) {
385
		res = read(fd, s + pos, n - pos);
386
		switch (res) {
387
		case -1:
388
			if (errno == EINTR || errno == EAGAIN)
389
				continue;
390
		case 0:
391
			return (1);
392
		default:
393
			pos += res;
394
		}
395
	}
396
	return (0);
397
}
398
399
/* Read data with the assertion that it all must come through, or
400
 * else abort the process.  Based on atomicio() from openssh. */
401
static void
402
must_read(int fd, void *buf, size_t n)
403
{
404
	char *s = buf;
405
	ssize_t res, pos = 0;
406
407
	while (n > pos) {
408
		res = read(fd, s + pos, n - pos);
409
		switch (res) {
410
		case -1:
411
			if (errno == EINTR || errno == EAGAIN)
412
				continue;
413
		case 0:
414
			_exit(0);
415
		default:
416
			pos += res;
417
		}
418
	}
419
}
420
421
/* Write data with the assertion that it all has to be written, or
422
 * else abort the process.  Based on atomicio() from openssh. */
423
static void
424
must_write(int fd, void *buf, size_t n)
425
{
426
	char *s = buf;
427
	ssize_t res, pos = 0;
428
429
	while (n > pos) {
430
		res = write(fd, s + pos, n - pos);
431
		switch (res) {
432
		case -1:
433
			if (errno == EINTR || errno == EAGAIN)
434
				continue;
435
		case 0:
436
			_exit(0);
437
		default:
438
			pos += res;
439
		}
440
	}
441
}