Deleted Added
full compact
privsep.c (145840) privsep.c (171172)
1/* $OpenBSD: privsep.c,v 1.13 2004/12/22 09:21:02 otto Exp $ */
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
20#include <sys/cdefs.h>
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
20#include <sys/cdefs.h>
21__FBSDID("$FreeBSD: head/contrib/pf/pflogd/privsep.c 145840 2005-05-03 16:55:20Z mlaier $");
21__FBSDID("$FreeBSD: head/contrib/pf/pflogd/privsep.c 171172 2007-07-03 12:30:03Z mlaier $");
22
22
23#include <sys/param.h>
23#include <sys/types.h>
24#include <sys/time.h>
25#include <sys/socket.h>
26
27#include <net/if.h>
28#include <net/bpf.h>
29
30#include <err.h>
31#include <errno.h>
32#include <fcntl.h>
24#include <sys/time.h>
25#include <sys/socket.h>
26
27#include <net/if.h>
28#include <net/bpf.h>
29
30#include <err.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <limits.h>
34#ifndef __FreeBSD__
35#include <pcap.h>
36#include <pcap-int.h>
37#endif
33#include <pwd.h>
34#include <signal.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <pwd.h>
39#include <signal.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#ifdef __FreeBSD__
44/* XXX: pcap pollutes namespace with strlcpy if not present previously */
38#include <pcap.h>
39#include <pcap-int.h>
45#include <pcap.h>
46#include <pcap-int.h>
47#endif
40#include <syslog.h>
41#include <unistd.h>
42#include "pflogd.h"
43
44enum cmd_types {
45 PRIV_SET_SNAPLEN, /* set the snaplength */
48#include <syslog.h>
49#include <unistd.h>
50#include "pflogd.h"
51
52enum cmd_types {
53 PRIV_SET_SNAPLEN, /* set the snaplength */
54 PRIV_MOVE_LOG, /* move logfile away */
46 PRIV_OPEN_LOG /* open logfile for appending */
47};
48
49static int priv_fd = -1;
50static volatile pid_t child_pid = -1;
51
52volatile sig_atomic_t gotsig_chld = 0;
53
54static void sig_pass_to_chld(int);
55static void sig_chld(int);
56static int may_read(int, void *, size_t);
57static void must_read(int, void *, size_t);
58static void must_write(int, void *, size_t);
59static int set_snaplen(int snap);
55 PRIV_OPEN_LOG /* open logfile for appending */
56};
57
58static int priv_fd = -1;
59static volatile pid_t child_pid = -1;
60
61volatile sig_atomic_t gotsig_chld = 0;
62
63static void sig_pass_to_chld(int);
64static void sig_chld(int);
65static int may_read(int, void *, size_t);
66static void must_read(int, void *, size_t);
67static void must_write(int, void *, size_t);
68static int set_snaplen(int snap);
69static int move_log(const char *name);
60
70
61/* bpf filter expression common to parent and child */
62extern char *filter;
63extern char *errbuf;
64extern char *filename;
65extern pcap_t *hpcap;
66
67/* based on syslogd privsep */
68int
69priv_init(void)
70{
71 int i, fd, socks[2], cmd;
72 int snaplen, ret, olderrno;
73 struct passwd *pw;
74
75#ifdef __FreeBSD__
76 for (i = 1; i < NSIG; i++)
77#else
78 for (i = 1; i < _NSIG; i++)
79#endif
80 signal(i, SIG_DFL);
81
82 /* Create sockets */
83 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
84 err(1, "socketpair() failed");
85
86 pw = getpwnam("_pflogd");
87 if (pw == NULL)
88 errx(1, "unknown user _pflogd");
89 endpwent();
90
91 child_pid = fork();
92 if (child_pid < 0)
93 err(1, "fork() failed");
94
95 if (!child_pid) {
96 gid_t gidset[1];
97
98 /* Child - drop privileges and return */
99 if (chroot(pw->pw_dir) != 0)
100 err(1, "unable to chroot");
101 if (chdir("/") != 0)
102 err(1, "unable to chdir");
103
104 gidset[0] = pw->pw_gid;
71extern char *filename;
72extern pcap_t *hpcap;
73
74/* based on syslogd privsep */
75int
76priv_init(void)
77{
78 int i, fd, socks[2], cmd;
79 int snaplen, ret, olderrno;
80 struct passwd *pw;
81
82#ifdef __FreeBSD__
83 for (i = 1; i < NSIG; i++)
84#else
85 for (i = 1; i < _NSIG; i++)
86#endif
87 signal(i, SIG_DFL);
88
89 /* Create sockets */
90 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
91 err(1, "socketpair() failed");
92
93 pw = getpwnam("_pflogd");
94 if (pw == NULL)
95 errx(1, "unknown user _pflogd");
96 endpwent();
97
98 child_pid = fork();
99 if (child_pid < 0)
100 err(1, "fork() failed");
101
102 if (!child_pid) {
103 gid_t gidset[1];
104
105 /* Child - drop privileges and return */
106 if (chroot(pw->pw_dir) != 0)
107 err(1, "unable to chroot");
108 if (chdir("/") != 0)
109 err(1, "unable to chdir");
110
111 gidset[0] = pw->pw_gid;
112 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1)
113 err(1, "setresgid() failed");
105 if (setgroups(1, gidset) == -1)
106 err(1, "setgroups() failed");
114 if (setgroups(1, gidset) == -1)
115 err(1, "setgroups() failed");
107 if (setegid(pw->pw_gid) == -1)
108 err(1, "setegid() failed");
109 if (setgid(pw->pw_gid) == -1)
110 err(1, "setgid() failed");
111 if (seteuid(pw->pw_uid) == -1)
112 err(1, "seteuid() failed");
113 if (setuid(pw->pw_uid) == -1)
114 err(1, "setuid() failed");
116 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
117 err(1, "setresuid() failed");
115 close(socks[0]);
116 priv_fd = socks[1];
117 return 0;
118 }
119
120 /* Father */
121 /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
122 signal(SIGALRM, sig_pass_to_chld);
123 signal(SIGTERM, sig_pass_to_chld);
124 signal(SIGHUP, sig_pass_to_chld);
125 signal(SIGINT, sig_pass_to_chld);
126 signal(SIGQUIT, sig_pass_to_chld);
127 signal(SIGCHLD, sig_chld);
128
129 setproctitle("[priv]");
130 close(socks[1]);
131
132 while (!gotsig_chld) {
133 if (may_read(socks[0], &cmd, sizeof(int)))
134 break;
135 switch (cmd) {
136 case PRIV_SET_SNAPLEN:
137 logmsg(LOG_DEBUG,
138 "[priv]: msg PRIV_SET_SNAPLENGTH received");
139 must_read(socks[0], &snaplen, sizeof(int));
140
141 ret = set_snaplen(snaplen);
142 if (ret) {
143 logmsg(LOG_NOTICE,
144 "[priv]: set_snaplen failed for snaplen %d",
145 snaplen);
146 }
147
148 must_write(socks[0], &ret, sizeof(int));
149 break;
150
151 case PRIV_OPEN_LOG:
152 logmsg(LOG_DEBUG,
153 "[priv]: msg PRIV_OPEN_LOG received");
154 /* create or append logs but do not follow symlinks */
155 fd = open(filename,
156 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
157 0600);
158 olderrno = errno;
159 send_fd(socks[0], fd);
160 if (fd < 0)
161 logmsg(LOG_NOTICE,
162 "[priv]: failed to open %s: %s",
163 filename, strerror(olderrno));
164 else
165 close(fd);
166 break;
167
118 close(socks[0]);
119 priv_fd = socks[1];
120 return 0;
121 }
122
123 /* Father */
124 /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */
125 signal(SIGALRM, sig_pass_to_chld);
126 signal(SIGTERM, sig_pass_to_chld);
127 signal(SIGHUP, sig_pass_to_chld);
128 signal(SIGINT, sig_pass_to_chld);
129 signal(SIGQUIT, sig_pass_to_chld);
130 signal(SIGCHLD, sig_chld);
131
132 setproctitle("[priv]");
133 close(socks[1]);
134
135 while (!gotsig_chld) {
136 if (may_read(socks[0], &cmd, sizeof(int)))
137 break;
138 switch (cmd) {
139 case PRIV_SET_SNAPLEN:
140 logmsg(LOG_DEBUG,
141 "[priv]: msg PRIV_SET_SNAPLENGTH received");
142 must_read(socks[0], &snaplen, sizeof(int));
143
144 ret = set_snaplen(snaplen);
145 if (ret) {
146 logmsg(LOG_NOTICE,
147 "[priv]: set_snaplen failed for snaplen %d",
148 snaplen);
149 }
150
151 must_write(socks[0], &ret, sizeof(int));
152 break;
153
154 case PRIV_OPEN_LOG:
155 logmsg(LOG_DEBUG,
156 "[priv]: msg PRIV_OPEN_LOG received");
157 /* create or append logs but do not follow symlinks */
158 fd = open(filename,
159 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW,
160 0600);
161 olderrno = errno;
162 send_fd(socks[0], fd);
163 if (fd < 0)
164 logmsg(LOG_NOTICE,
165 "[priv]: failed to open %s: %s",
166 filename, strerror(olderrno));
167 else
168 close(fd);
169 break;
170
171 case PRIV_MOVE_LOG:
172 logmsg(LOG_DEBUG,
173 "[priv]: msg PRIV_MOVE_LOG received");
174 ret = move_log(filename);
175 must_write(socks[0], &ret, sizeof(int));
176 break;
177
168 default:
169 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
170 _exit(1);
171 /* NOTREACHED */
172 }
173 }
174
175 _exit(1);
176}
177
178/* this is called from parent */
179static int
180set_snaplen(int snap)
181{
182 if (hpcap == NULL)
183 return (1);
184
185 hpcap->snapshot = snap;
186 set_pcap_filter();
187
188 return 0;
189}
190
178 default:
179 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd);
180 _exit(1);
181 /* NOTREACHED */
182 }
183 }
184
185 _exit(1);
186}
187
188/* this is called from parent */
189static int
190set_snaplen(int snap)
191{
192 if (hpcap == NULL)
193 return (1);
194
195 hpcap->snapshot = snap;
196 set_pcap_filter();
197
198 return 0;
199}
200
201static int
202move_log(const char *name)
203{
204 char ren[PATH_MAX];
205 int len;
191
206
207 for (;;) {
208 int fd;
209
210 len = snprintf(ren, sizeof(ren), "%s.bad.%08x",
211 name, arc4random());
212 if (len >= sizeof(ren)) {
213 logmsg(LOG_ERR, "[priv] new name too long");
214 return (1);
215 }
216
217 /* lock destinanion */
218 fd = open(ren, O_CREAT|O_EXCL, 0);
219 if (fd >= 0) {
220 close(fd);
221 break;
222 }
223 /* if file exists, try another name */
224 if (errno != EEXIST && errno != EINTR) {
225 logmsg(LOG_ERR, "[priv] failed to create new name: %s",
226 strerror(errno));
227 return (1);
228 }
229 }
230
231 if (rename(name, ren)) {
232 logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s",
233 name, ren, strerror(errno));
234 return (1);
235 }
236
237 logmsg(LOG_NOTICE,
238 "[priv]: log file %s moved to %s", name, ren);
239
240 return (0);
241}
242
192/*
193 * send the snaplength to privileged process
194 */
195int
196priv_set_snaplen(int snaplen)
197{
198 int cmd, ret;
199
200 if (priv_fd < 0)
201 errx(1, "%s: called from privileged portion", __func__);
202
203 cmd = PRIV_SET_SNAPLEN;
204
205 must_write(priv_fd, &cmd, sizeof(int));
206 must_write(priv_fd, &snaplen, sizeof(int));
207
208 must_read(priv_fd, &ret, sizeof(int));
209
210 /* also set hpcap->snapshot in child */
211 if (ret == 0)
212 hpcap->snapshot = snaplen;
213
214 return (ret);
215}
216
217/* Open log-file */
218int
219priv_open_log(void)
220{
221 int cmd, fd;
222
223 if (priv_fd < 0)
224 errx(1, "%s: called from privileged portion", __func__);
225
226 cmd = PRIV_OPEN_LOG;
227 must_write(priv_fd, &cmd, sizeof(int));
228 fd = receive_fd(priv_fd);
229
230 return (fd);
231}
243/*
244 * send the snaplength to privileged process
245 */
246int
247priv_set_snaplen(int snaplen)
248{
249 int cmd, ret;
250
251 if (priv_fd < 0)
252 errx(1, "%s: called from privileged portion", __func__);
253
254 cmd = PRIV_SET_SNAPLEN;
255
256 must_write(priv_fd, &cmd, sizeof(int));
257 must_write(priv_fd, &snaplen, sizeof(int));
258
259 must_read(priv_fd, &ret, sizeof(int));
260
261 /* also set hpcap->snapshot in child */
262 if (ret == 0)
263 hpcap->snapshot = snaplen;
264
265 return (ret);
266}
267
268/* Open log-file */
269int
270priv_open_log(void)
271{
272 int cmd, fd;
273
274 if (priv_fd < 0)
275 errx(1, "%s: called from privileged portion", __func__);
276
277 cmd = PRIV_OPEN_LOG;
278 must_write(priv_fd, &cmd, sizeof(int));
279 fd = receive_fd(priv_fd);
280
281 return (fd);
282}
283/* Move-away and reopen log-file */
284int
285priv_move_log(void)
286{
287 int cmd, ret;
232
288
289 if (priv_fd < 0)
290 errx(1, "%s: called from privileged portion\n", __func__);
291
292 cmd = PRIV_MOVE_LOG;
293 must_write(priv_fd, &cmd, sizeof(int));
294 must_read(priv_fd, &ret, sizeof(int));
295
296 return (ret);
297}
298
233/* If priv parent gets a TERM or HUP, pass it through to child instead */
234static void
235sig_pass_to_chld(int sig)
236{
237 int oerrno = errno;
238
239 if (child_pid != -1)
240 kill(child_pid, sig);
241 errno = oerrno;
242}
243
244/* if parent gets a SIGCHLD, it will exit */
245static void
246sig_chld(int sig)
247{
248 gotsig_chld = 1;
249}
250
251/* Read all data or return 1 for error. */
252static int
253may_read(int fd, void *buf, size_t n)
254{
255 char *s = buf;
256 ssize_t res, pos = 0;
257
258 while (n > pos) {
259 res = read(fd, s + pos, n - pos);
260 switch (res) {
261 case -1:
262 if (errno == EINTR || errno == EAGAIN)
263 continue;
264 case 0:
265 return (1);
266 default:
267 pos += res;
268 }
269 }
270 return (0);
271}
272
273/* Read data with the assertion that it all must come through, or
274 * else abort the process. Based on atomicio() from openssh. */
275static void
276must_read(int fd, void *buf, size_t n)
277{
278 char *s = buf;
279 ssize_t res, pos = 0;
280
281 while (n > pos) {
282 res = read(fd, s + pos, n - pos);
283 switch (res) {
284 case -1:
285 if (errno == EINTR || errno == EAGAIN)
286 continue;
287 case 0:
288 _exit(0);
289 default:
290 pos += res;
291 }
292 }
293}
294
295/* Write data with the assertion that it all has to be written, or
296 * else abort the process. Based on atomicio() from openssh. */
297static void
298must_write(int fd, void *buf, size_t n)
299{
300 char *s = buf;
301 ssize_t res, pos = 0;
302
303 while (n > pos) {
304 res = write(fd, s + pos, n - pos);
305 switch (res) {
306 case -1:
307 if (errno == EINTR || errno == EAGAIN)
308 continue;
309 case 0:
310 _exit(0);
311 default:
312 pos += res;
313 }
314 }
315}
299/* If priv parent gets a TERM or HUP, pass it through to child instead */
300static void
301sig_pass_to_chld(int sig)
302{
303 int oerrno = errno;
304
305 if (child_pid != -1)
306 kill(child_pid, sig);
307 errno = oerrno;
308}
309
310/* if parent gets a SIGCHLD, it will exit */
311static void
312sig_chld(int sig)
313{
314 gotsig_chld = 1;
315}
316
317/* Read all data or return 1 for error. */
318static int
319may_read(int fd, void *buf, size_t n)
320{
321 char *s = buf;
322 ssize_t res, pos = 0;
323
324 while (n > pos) {
325 res = read(fd, s + pos, n - pos);
326 switch (res) {
327 case -1:
328 if (errno == EINTR || errno == EAGAIN)
329 continue;
330 case 0:
331 return (1);
332 default:
333 pos += res;
334 }
335 }
336 return (0);
337}
338
339/* Read data with the assertion that it all must come through, or
340 * else abort the process. Based on atomicio() from openssh. */
341static void
342must_read(int fd, void *buf, size_t n)
343{
344 char *s = buf;
345 ssize_t res, pos = 0;
346
347 while (n > pos) {
348 res = read(fd, s + pos, n - pos);
349 switch (res) {
350 case -1:
351 if (errno == EINTR || errno == EAGAIN)
352 continue;
353 case 0:
354 _exit(0);
355 default:
356 pos += res;
357 }
358 }
359}
360
361/* Write data with the assertion that it all has to be written, or
362 * else abort the process. Based on atomicio() from openssh. */
363static void
364must_write(int fd, void *buf, size_t n)
365{
366 char *s = buf;
367 ssize_t res, pos = 0;
368
369 while (n > pos) {
370 res = write(fd, s + pos, n - pos);
371 switch (res) {
372 case -1:
373 if (errno == EINTR || errno == EAGAIN)
374 continue;
375 case 0:
376 _exit(0);
377 default:
378 pos += res;
379 }
380 }
381}