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}
|