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