1 /* $NetBSD: privsep.c,v 1.5 2008/02/20 18:19:18 matt Exp $ */ 2 /* $OpenBSD: privsep.c,v 1.13 2004/12/22 09:21:02 otto Exp $ */ 3 4 /* 5 * Copyright (c) 2003 Can Erkin Acar 6 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 #include <sys/ioctl.h> 21 #include <sys/types.h> 22 #include <sys/time.h> 23 #include <sys/socket.h> 24 #include <sys/ioctl.h> 25 26 #include <net/if.h> 27 #include <net/bpf.h> 28 29 #include <string.h> 30 31 #include <err.h> 32 #include <errno.h> 33 #include <fcntl.h> 34 #include <pcap.h> 35 /* 36 * If we're going to include parts of the libpcap internals we MUST 37 * set the feature-test macros they expect, or they may misbehave. 38 */ 39 #define HAVE_STRLCPY 40 #define HAVE_SNPRINTF 41 #define HAVE_VSNPRINTF 42 #include <pcap-int.h> 43 #include <pwd.h> 44 #include <signal.h> 45 #include <stdio.h> 46 #include <stdlib.h> 47 #include <syslog.h> 48 #include <unistd.h> 49 #include "pflogd.h" 50 51 enum cmd_types { 52 PRIV_SET_SNAPLEN, /* set the snaplength */ 53 PRIV_OPEN_LOG /* open logfile for appending */ 54 }; 55 56 static int priv_fd = -1; 57 static volatile pid_t child_pid = -1; 58 59 volatile sig_atomic_t gotsig_chld = 0; 60 61 static void sig_pass_to_chld(int); 62 static void sig_chld(int); 63 static int may_read(int, void *, size_t); 64 static void must_read(int, void *, size_t); 65 static void must_write(int, void *, size_t); 66 static int set_snaplen(int snap); 67 68 /* bpf filter expression common to parent and child */ 69 extern char *filter; 70 extern char errbuf[]; 71 extern char *filename; 72 extern pcap_t *hpcap; 73 74 /* based on syslogd privsep */ 75 int 76 priv_init(void) 77 { 78 int i, fd, socks[2], cmd; 79 int snaplen, ret, olderrno; 80 struct passwd *pw; 81 82 for (i = 1; i < _NSIG; i++) 83 signal(i, SIG_DFL); 84 85 /* Create sockets */ 86 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 87 err(1, "socketpair() failed"); 88 89 pw = getpwnam("_pflogd"); 90 if (pw == NULL) 91 errx(1, "unknown user _pflogd"); 92 endpwent(); 93 94 child_pid = fork(); 95 if (child_pid < 0) 96 err(1, "fork() failed"); 97 98 if (!child_pid) { 99 gid_t gidset[1]; 100 101 /* Child - drop privileges and return */ 102 if (chroot(pw->pw_dir) != 0) 103 err(1, "unable to chroot"); 104 if (chdir("/") != 0) 105 err(1, "unable to chdir"); 106 107 gidset[0] = pw->pw_gid; 108 if (setgroups(1, gidset) == -1) 109 err(1, "setgroups() failed"); 110 #ifdef __OpenBSD__ 111 if (setegid(pw->pw_gid) == -1) 112 err(1, "setegid() failed"); 113 #endif 114 if (setgid(pw->pw_gid) == -1) 115 err(1, "setgid() failed"); 116 #ifdef __OpenBSD__ 117 if (seteuid(pw->pw_uid) == -1) 118 err(1, "seteuid() failed"); 119 #endif 120 if (setuid(pw->pw_uid) == -1) 121 err(1, "setuid() failed"); 122 close(socks[0]); 123 priv_fd = socks[1]; 124 return 0; 125 } 126 127 /* Father */ 128 /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ 129 signal(SIGALRM, sig_pass_to_chld); 130 signal(SIGTERM, sig_pass_to_chld); 131 signal(SIGHUP, sig_pass_to_chld); 132 signal(SIGINT, sig_pass_to_chld); 133 signal(SIGQUIT, sig_pass_to_chld); 134 signal(SIGCHLD, sig_chld); 135 136 setproctitle("[priv]"); 137 close(socks[1]); 138 139 while (!gotsig_chld) { 140 if (may_read(socks[0], &cmd, sizeof(int))) 141 break; 142 switch (cmd) { 143 case PRIV_SET_SNAPLEN: 144 logmsg(LOG_DEBUG, 145 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 146 must_read(socks[0], &snaplen, sizeof(int)); 147 148 ret = set_snaplen(snaplen); 149 if (ret) { 150 logmsg(LOG_NOTICE, 151 "[priv]: set_snaplen failed for snaplen %d", 152 snaplen); 153 } 154 155 must_write(socks[0], &ret, sizeof(int)); 156 break; 157 158 case PRIV_OPEN_LOG: 159 logmsg(LOG_DEBUG, 160 "[priv]: msg PRIV_OPEN_LOG received"); 161 /* create or append logs but do not follow symlinks */ 162 fd = open(filename, 163 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 164 0600); 165 olderrno = errno; 166 send_fd(socks[0], fd); 167 if (fd < 0) 168 logmsg(LOG_NOTICE, 169 "[priv]: failed to open %s: %s", 170 filename, strerror(olderrno)); 171 else 172 close(fd); 173 break; 174 175 default: 176 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 177 _exit(1); 178 /* NOTREACHED */ 179 } 180 } 181 182 _exit(1); 183 } 184 185 /* this is called from parent */ 186 static int 187 set_snaplen(int snap) 188 { 189 if (hpcap == NULL) 190 return (1); 191 192 hpcap->snapshot = snap; 193 set_pcap_filter(); 194 195 return 0; 196 } 197 198 199 /* 200 * send the snaplength to privileged process 201 */ 202 int 203 priv_set_snaplen(int snaplen) 204 { 205 int cmd, ret; 206 207 if (priv_fd < 0) 208 errx(1, "%s: called from privileged portion", __func__); 209 210 cmd = PRIV_SET_SNAPLEN; 211 212 must_write(priv_fd, &cmd, sizeof(int)); 213 must_write(priv_fd, &snaplen, sizeof(int)); 214 215 must_read(priv_fd, &ret, sizeof(int)); 216 217 /* also set hpcap->snapshot in child */ 218 if (ret == 0) 219 hpcap->snapshot = snaplen; 220 221 return (ret); 222 } 223 224 /* Open log-file */ 225 int 226 priv_open_log(void) 227 { 228 int cmd, fd; 229 230 if (priv_fd < 0) 231 errx(1, "%s: called from privileged portion", __func__); 232 233 cmd = PRIV_OPEN_LOG; 234 must_write(priv_fd, &cmd, sizeof(int)); 235 fd = receive_fd(priv_fd); 236 237 return (fd); 238 } 239 240 /* If priv parent gets a TERM or HUP, pass it through to child instead */ 241 static void 242 sig_pass_to_chld(int sig) 243 { 244 int oerrno = errno; 245 246 if (child_pid != -1) 247 kill(child_pid, sig); 248 errno = oerrno; 249 } 250 251 /* if parent gets a SIGCHLD, it will exit */ 252 static void 253 sig_chld(int sig) 254 { 255 gotsig_chld = 1; 256 } 257 258 /* Read all data or return 1 for error. */ 259 static int 260 may_read(int fd, void *buf, size_t n) 261 { 262 char *s = buf; 263 ssize_t res, pos = 0; 264 265 while (n > pos) { 266 res = read(fd, s + pos, n - pos); 267 switch (res) { 268 case -1: 269 if (errno == EINTR || errno == EAGAIN) 270 continue; 271 case 0: 272 return (1); 273 default: 274 pos += res; 275 } 276 } 277 return (0); 278 } 279 280 /* Read data with the assertion that it all must come through, or 281 * else abort the process. Based on atomicio() from openssh. */ 282 static void 283 must_read(int fd, void *buf, size_t n) 284 { 285 char *s = buf; 286 ssize_t res, pos = 0; 287 288 while (n > pos) { 289 res = read(fd, s + pos, n - pos); 290 switch (res) { 291 case -1: 292 if (errno == EINTR || errno == EAGAIN) 293 continue; 294 case 0: 295 _exit(0); 296 default: 297 pos += res; 298 } 299 } 300 } 301 302 /* Write data with the assertion that it all has to be written, or 303 * else abort the process. Based on atomicio() from openssh. */ 304 static void 305 must_write(int fd, void *buf, size_t n) 306 { 307 char *s = buf; 308 ssize_t res, pos = 0; 309 310 while (n > pos) { 311 res = write(fd, s + pos, n - pos); 312 switch (res) { 313 case -1: 314 if (errno == EINTR || errno == EAGAIN) 315 continue; 316 case 0: 317 _exit(0); 318 default: 319 pos += res; 320 } 321 } 322 } 323