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