1 /* $OpenBSD: privsep.c,v 1.13 2004/12/22 09:21:02 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 20 #include <sys/cdefs.h> 21 __FBSDID("$FreeBSD$"); 22 23 #include <sys/param.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 <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> 40 #include <syslog.h> 41 #include <unistd.h> 42 #include "pflogd.h" 43 44 enum cmd_types { 45 PRIV_SET_SNAPLEN, /* set the snaplength */ 46 PRIV_OPEN_LOG /* open logfile for appending */ 47 }; 48 49 static int priv_fd = -1; 50 static volatile pid_t child_pid = -1; 51 52 volatile sig_atomic_t gotsig_chld = 0; 53 54 static void sig_pass_to_chld(int); 55 static void sig_chld(int); 56 static int may_read(int, void *, size_t); 57 static void must_read(int, void *, size_t); 58 static void must_write(int, void *, size_t); 59 static int set_snaplen(int snap); 60 61 /* bpf filter expression common to parent and child */ 62 extern char *filter; 63 extern char *errbuf; 64 extern char *filename; 65 extern pcap_t *hpcap; 66 67 /* based on syslogd privsep */ 68 int 69 priv_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; 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/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 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 */ 179 static int 180 set_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 191 192 /* 193 * send the snaplength to privileged process 194 */ 195 int 196 priv_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 */ 218 int 219 priv_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 } 232 233 /* If priv parent gets a TERM or HUP, pass it through to child instead */ 234 static void 235 sig_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 */ 245 static void 246 sig_chld(int sig) 247 { 248 gotsig_chld = 1; 249 } 250 251 /* Read all data or return 1 for error. */ 252 static int 253 may_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. */ 275 static void 276 must_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. */ 297 static void 298 must_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 } 316