1 /* $OpenBSD: privsep.c,v 1.35 2021/07/12 15:09:19 beck 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/types.h> 20 #include <sys/time.h> 21 #include <sys/socket.h> 22 #include <sys/ioctl.h> 23 24 #include <net/if.h> 25 #include <net/bpf.h> 26 27 #include <err.h> 28 #include <errno.h> 29 #include <fcntl.h> 30 #include <limits.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 <netdb.h> 41 #include <resolv.h> 42 #include "pflogd.h" 43 44 enum cmd_types { 45 PRIV_INIT_PCAP, /* init pcap fdpass bpf */ 46 PRIV_SET_SNAPLEN, /* set the snaplength */ 47 PRIV_OPEN_LOG, /* open logfile for appending */ 48 }; 49 50 static int priv_fd = -1; 51 static volatile pid_t child_pid = -1; 52 53 static void sig_pass_to_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 extern char *filename; 60 extern char *interface; 61 extern char errbuf[PCAP_ERRBUF_SIZE]; 62 extern pcap_t *hpcap; 63 64 /* based on syslogd privsep */ 65 void 66 priv_init(int Pflag, int argc, char *argv[]) 67 { 68 int i, fd = -1, bpfd = -1, nargc, socks[2], cmd; 69 int snaplen, ret, olderrno; 70 struct passwd *pw; 71 char **nargv; 72 unsigned int buflen; 73 74 pw = getpwnam("_pflogd"); 75 if (pw == NULL) 76 errx(1, "unknown user _pflogd"); 77 endpwent(); 78 79 if (Pflag) { 80 gid_t gidset[1]; 81 82 /* Child - drop privileges and return */ 83 if (chroot(pw->pw_dir) != 0) 84 err(1, "unable to chroot"); 85 if (chdir("/") != 0) 86 err(1, "unable to chdir"); 87 88 gidset[0] = pw->pw_gid; 89 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) 90 err(1, "setresgid() failed"); 91 if (setgroups(1, gidset) == -1) 92 err(1, "setgroups() failed"); 93 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 94 err(1, "setresuid() failed"); 95 priv_fd = 3; 96 return; 97 } 98 99 /* Create sockets */ 100 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 101 err(1, "socketpair() failed"); 102 103 child_pid = fork(); 104 if (child_pid == -1) 105 err(1, "fork() failed"); 106 107 if (!child_pid) { 108 close(socks[0]); 109 if (dup2(socks[1], 3) == -1) 110 err(1, "dup2 unpriv sock failed"); 111 close(socks[1]); 112 if ((nargv = reallocarray(NULL, argc + 2, 113 sizeof(char *))) == NULL) 114 err(1, "alloc unpriv argv failed"); 115 nargc = 0; 116 nargv[nargc++] = argv[0]; 117 nargv[nargc++] = "-P"; 118 for (i = 1; i < argc; i++) 119 nargv[nargc++] = argv[i]; 120 nargv[nargc] = NULL; 121 execvp(nargv[0], nargv); 122 err(1, "exec unpriv '%s' failed", nargv[0]); 123 } 124 close(socks[1]); 125 126 /* Father */ 127 /* Pass ALRM/TERM/HUP/INT/QUIT through to child */ 128 signal(SIGALRM, sig_pass_to_chld); 129 signal(SIGTERM, sig_pass_to_chld); 130 signal(SIGHUP, sig_pass_to_chld); 131 signal(SIGINT, sig_pass_to_chld); 132 signal(SIGQUIT, sig_pass_to_chld); 133 134 setproctitle("[priv]"); 135 136 if (unveil(_PATH_RESCONF, "r") == -1) 137 err(1, "unveil %s", _PATH_RESCONF); 138 if (unveil(_PATH_HOSTS, "r") == -1) 139 err(1, "unveil %s", _PATH_HOSTS); 140 if (unveil(_PATH_SERVICES, "r") == -1) 141 err(1, "unveil %s", _PATH_SERVICES); 142 if (unveil("/dev/bpf", "r") == -1) 143 err(1, "unveil /dev/bpf"); 144 if (unveil(filename, "rwc") == -1) 145 err(1, "unveil %s", filename); 146 if (unveil(NULL, NULL) == -1) 147 err(1, "unveil"); 148 149 #if 0 150 /* This needs to do bpf ioctl */ 151 BROKEN if (pledge("stdio rpath wpath cpath sendfd proc bpf", NULL) == -1) 152 err(1, "pledge"); 153 #endif 154 155 while (1) { 156 if (may_read(socks[0], &cmd, sizeof(int))) 157 break; 158 switch (cmd) { 159 case PRIV_INIT_PCAP: 160 logmsg(LOG_DEBUG, 161 "[priv]: msg PRIV_INIT_PCAP received"); 162 /* initialize pcap */ 163 if (hpcap != NULL || init_pcap()) { 164 logmsg(LOG_ERR, "[priv]: Exiting, init failed"); 165 _exit(1); 166 } 167 buflen = hpcap->bufsize; /* BIOCGBLEN for unpriv proc */ 168 must_write(socks[0], &buflen, sizeof(unsigned int)); 169 fd = pcap_fileno(hpcap); 170 send_fd(socks[0], fd); 171 if (fd < 0) { 172 logmsg(LOG_ERR, "[priv]: Exiting, init failed"); 173 _exit(1); 174 } 175 break; 176 177 case PRIV_SET_SNAPLEN: 178 logmsg(LOG_DEBUG, 179 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 180 must_read(socks[0], &snaplen, sizeof(int)); 181 182 ret = set_snaplen(snaplen); 183 if (ret) { 184 logmsg(LOG_NOTICE, 185 "[priv]: set_snaplen failed for snaplen %d", 186 snaplen); 187 } 188 189 must_write(socks[0], &ret, sizeof(int)); 190 break; 191 192 case PRIV_OPEN_LOG: 193 logmsg(LOG_DEBUG, 194 "[priv]: msg PRIV_OPEN_LOG received"); 195 /* create or append logs but do not follow symlinks */ 196 if (bpfd != -1) { 197 close(bpfd); 198 bpfd = -1; 199 } 200 bpfd = open(filename, 201 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 202 0600); 203 olderrno = errno; 204 send_fd(socks[0], bpfd); 205 if (bpfd == -1) 206 logmsg(LOG_NOTICE, 207 "[priv]: failed to open %s: %s", 208 filename, strerror(olderrno)); 209 break; 210 211 default: 212 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 213 _exit(1); 214 /* NOTREACHED */ 215 } 216 } 217 218 exit(1); 219 } 220 221 /* this is called from parent */ 222 static int 223 set_snaplen(int snap) 224 { 225 if (hpcap == NULL) 226 return (1); 227 228 hpcap->snapshot = snap; 229 set_pcap_filter(); 230 231 return 0; 232 } 233 234 /* receive bpf fd from privileged process using fdpass and init pcap */ 235 int 236 priv_init_pcap(int snaplen) 237 { 238 int cmd, fd; 239 unsigned int buflen; 240 241 if (priv_fd < 0) 242 errx(1, "%s: called from privileged portion", __func__); 243 244 cmd = PRIV_INIT_PCAP; 245 246 must_write(priv_fd, &cmd, sizeof(int)); 247 must_read(priv_fd, &buflen, sizeof(unsigned int)); 248 fd = receive_fd(priv_fd); 249 if (fd < 0) 250 return (-1); 251 252 /* XXX temporary until pcap_open_live_fd API */ 253 hpcap = pcap_create(interface, errbuf); 254 if (hpcap == NULL) 255 return (-1); 256 257 /* XXX copies from pcap_open_live/pcap_activate */ 258 hpcap->fd = fd; 259 pcap_set_snaplen(hpcap, snaplen); 260 pcap_set_promisc(hpcap, 1); 261 pcap_set_timeout(hpcap, PCAP_TO_MS); 262 hpcap->oldstyle = 1; 263 hpcap->linktype = DLT_PFLOG; 264 hpcap->bufsize = buflen; /* XXX bpf BIOCGBLEN */ 265 hpcap->buffer = malloc(hpcap->bufsize); 266 if (hpcap->buffer == NULL) 267 return (-1); 268 hpcap->activated = 1; 269 270 return (0); 271 } 272 273 /* 274 * send the snaplength to privileged process 275 */ 276 int 277 priv_set_snaplen(int snaplen) 278 { 279 int cmd, ret; 280 281 if (priv_fd < 0) 282 errx(1, "%s: called from privileged portion", __func__); 283 284 cmd = PRIV_SET_SNAPLEN; 285 286 must_write(priv_fd, &cmd, sizeof(int)); 287 must_write(priv_fd, &snaplen, sizeof(int)); 288 289 must_read(priv_fd, &ret, sizeof(int)); 290 291 /* also set hpcap->snapshot in child */ 292 if (ret == 0) 293 hpcap->snapshot = snaplen; 294 295 return (ret); 296 } 297 298 /* Open log-file */ 299 int 300 priv_open_log(void) 301 { 302 int cmd, fd; 303 304 if (priv_fd < 0) 305 errx(1, "%s: called from privileged portion", __func__); 306 307 cmd = PRIV_OPEN_LOG; 308 must_write(priv_fd, &cmd, sizeof(int)); 309 fd = receive_fd(priv_fd); 310 311 return (fd); 312 } 313 314 /* If priv parent gets a TERM or HUP, pass it through to child instead */ 315 static void 316 sig_pass_to_chld(int sig) 317 { 318 int oerrno = errno; 319 320 if (child_pid != -1) 321 kill(child_pid, sig); 322 errno = oerrno; 323 } 324 325 /* Read all data or return 1 for error. */ 326 static int 327 may_read(int fd, void *buf, size_t n) 328 { 329 char *s = buf; 330 ssize_t res, pos = 0; 331 332 while (n > pos) { 333 res = read(fd, s + pos, n - pos); 334 switch (res) { 335 case -1: 336 if (errno == EINTR || errno == EAGAIN) 337 continue; 338 case 0: 339 return (1); 340 default: 341 pos += res; 342 } 343 } 344 return (0); 345 } 346 347 /* Read data with the assertion that it all must come through, or 348 * else abort the process. Based on atomicio() from openssh. */ 349 static void 350 must_read(int fd, void *buf, size_t n) 351 { 352 char *s = buf; 353 ssize_t res, pos = 0; 354 355 while (n > pos) { 356 res = read(fd, s + pos, n - pos); 357 switch (res) { 358 case -1: 359 if (errno == EINTR || errno == EAGAIN) 360 continue; 361 case 0: 362 _exit(0); 363 default: 364 pos += res; 365 } 366 } 367 } 368 369 /* Write data with the assertion that it all has to be written, or 370 * else abort the process. Based on atomicio() from openssh. */ 371 static void 372 must_write(int fd, void *buf, size_t n) 373 { 374 char *s = buf; 375 ssize_t res, pos = 0; 376 377 while (n > pos) { 378 res = write(fd, s + pos, n - pos); 379 switch (res) { 380 case -1: 381 if (errno == EINTR || errno == EAGAIN) 382 continue; 383 case 0: 384 _exit(0); 385 default: 386 pos += res; 387 } 388 } 389 } 390