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 #include <sys/ioctl.h> 20 #include <sys/types.h> 21 #include <sys/time.h> 22 #include <sys/socket.h> 23 #include <sys/ioctl.h> 24 25 #include <net/if.h> 26 #include <net/bpf.h> 27 28 #include <err.h> 29 #include <errno.h> 30 #include <fcntl.h> 31 #include <pcap.h> 32 #include <pcap-int.h> 33 #include <pwd.h> 34 #include <signal.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <syslog.h> 39 #include <unistd.h> 40 #include "pflogd.h" 41 42 enum cmd_types { 43 PRIV_SET_SNAPLEN, /* set the snaplength */ 44 PRIV_OPEN_LOG /* open logfile for appending */ 45 }; 46 47 static int priv_fd = -1; 48 static volatile pid_t child_pid = -1; 49 50 volatile sig_atomic_t gotsig_chld = 0; 51 52 static void sig_pass_to_chld(int); 53 static void sig_chld(int); 54 static int may_read(int, void *, size_t); 55 static void must_read(int, void *, size_t); 56 static void must_write(int, void *, size_t); 57 static int set_snaplen(int snap); 58 59 /* bpf filter expression common to parent and child */ 60 extern char *filter; 61 extern char *errbuf; 62 extern char *filename; 63 extern pcap_t *hpcap; 64 65 /* based on syslogd privsep */ 66 int 67 priv_init(void) 68 { 69 int i, fd, socks[2], cmd; 70 int snaplen, ret, olderrno; 71 struct passwd *pw; 72 73 for (i = 1; i < _NSIG; i++) 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/INT/QUIT 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(SIGINT, sig_pass_to_chld); 120 signal(SIGQUIT, sig_pass_to_chld); 121 signal(SIGCHLD, sig_chld); 122 123 setproctitle("[priv]"); 124 close(socks[1]); 125 126 while (!gotsig_chld) { 127 if (may_read(socks[0], &cmd, sizeof(int))) 128 break; 129 switch (cmd) { 130 case PRIV_SET_SNAPLEN: 131 logmsg(LOG_DEBUG, 132 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 133 must_read(socks[0], &snaplen, sizeof(int)); 134 135 ret = set_snaplen(snaplen); 136 if (ret) { 137 logmsg(LOG_NOTICE, 138 "[priv]: set_snaplen failed for snaplen %d", 139 snaplen); 140 } 141 142 must_write(socks[0], &ret, sizeof(int)); 143 break; 144 145 case PRIV_OPEN_LOG: 146 logmsg(LOG_DEBUG, 147 "[priv]: msg PRIV_OPEN_LOG received"); 148 /* create or append logs but do not follow symlinks */ 149 fd = open(filename, 150 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 151 0600); 152 olderrno = errno; 153 send_fd(socks[0], fd); 154 if (fd < 0) 155 logmsg(LOG_NOTICE, 156 "[priv]: failed to open %s: %s", 157 filename, strerror(olderrno)); 158 else 159 close(fd); 160 break; 161 162 default: 163 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 164 _exit(1); 165 /* NOTREACHED */ 166 } 167 } 168 169 _exit(1); 170 } 171 172 /* this is called from parent */ 173 static int 174 set_snaplen(int snap) 175 { 176 if (hpcap == NULL) 177 return (1); 178 179 hpcap->snapshot = snap; 180 set_pcap_filter(); 181 182 return 0; 183 } 184 185 186 /* 187 * send the snaplength to privileged process 188 */ 189 int 190 priv_set_snaplen(int snaplen) 191 { 192 int cmd, ret; 193 194 if (priv_fd < 0) 195 errx(1, "%s: called from privileged portion", __func__); 196 197 cmd = PRIV_SET_SNAPLEN; 198 199 must_write(priv_fd, &cmd, sizeof(int)); 200 must_write(priv_fd, &snaplen, sizeof(int)); 201 202 must_read(priv_fd, &ret, sizeof(int)); 203 204 /* also set hpcap->snapshot in child */ 205 if (ret == 0) 206 hpcap->snapshot = snaplen; 207 208 return (ret); 209 } 210 211 /* Open log-file */ 212 int 213 priv_open_log(void) 214 { 215 int cmd, fd; 216 217 if (priv_fd < 0) 218 errx(1, "%s: called from privileged portion", __func__); 219 220 cmd = PRIV_OPEN_LOG; 221 must_write(priv_fd, &cmd, sizeof(int)); 222 fd = receive_fd(priv_fd); 223 224 return (fd); 225 } 226 227 /* If priv parent gets a TERM or HUP, pass it through to child instead */ 228 static void 229 sig_pass_to_chld(int sig) 230 { 231 int oerrno = errno; 232 233 if (child_pid != -1) 234 kill(child_pid, sig); 235 errno = oerrno; 236 } 237 238 /* if parent gets a SIGCHLD, it will exit */ 239 static void 240 sig_chld(int sig) 241 { 242 gotsig_chld = 1; 243 } 244 245 /* Read all data or return 1 for error. */ 246 static int 247 may_read(int fd, void *buf, size_t n) 248 { 249 char *s = buf; 250 ssize_t res, pos = 0; 251 252 while (n > pos) { 253 res = read(fd, s + pos, n - pos); 254 switch (res) { 255 case -1: 256 if (errno == EINTR || errno == EAGAIN) 257 continue; 258 case 0: 259 return (1); 260 default: 261 pos += res; 262 } 263 } 264 return (0); 265 } 266 267 /* Read data with the assertion that it all must come through, or 268 * else abort the process. Based on atomicio() from openssh. */ 269 static void 270 must_read(int fd, void *buf, size_t n) 271 { 272 char *s = buf; 273 ssize_t res, pos = 0; 274 275 while (n > pos) { 276 res = read(fd, s + pos, n - pos); 277 switch (res) { 278 case -1: 279 if (errno == EINTR || errno == EAGAIN) 280 continue; 281 case 0: 282 _exit(0); 283 default: 284 pos += res; 285 } 286 } 287 } 288 289 /* Write data with the assertion that it all has to be written, or 290 * else abort the process. Based on atomicio() from openssh. */ 291 static void 292 must_write(int fd, void *buf, size_t n) 293 { 294 char *s = buf; 295 ssize_t res, 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