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