118316Swollman/* $NetBSD: privsep.c,v 1.8 2019/02/03 10:48:47 mrg Exp $ */ 218316Swollman/* $OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $ */ 318316Swollman 418316Swollman/* 518316Swollman * Copyright (c) 2003 Can Erkin Acar 618316Swollman * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> 718316Swollman * 818316Swollman * Permission to use, copy, modify, and distribute this software for any 918316Swollman * purpose with or without fee is hereby granted, provided that the above 1018316Swollman * copyright notice and this permission notice appear in all copies. 1118316Swollman * 1218316Swollman * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1318316Swollman * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1418316Swollman * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1518316Swollman * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1618316Swollman * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1718316Swollman * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1818316Swollman * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1918316Swollman */ 2018316Swollman#include <sys/types.h> 2118316Swollman#include <sys/time.h> 2218316Swollman#include <sys/socket.h> 2318316Swollman#include <sys/ioctl.h> 2418316Swollman 2518316Swollman#include <net/if.h> 2618316Swollman#include <net/bpf.h> 2718316Swollman 2818316Swollman#include <string.h> 2918316Swollman 3046303Smarkm#include <err.h> 3150476Speter#include <errno.h> 3218316Swollman#include <fcntl.h> 3318316Swollman#include <limits.h> 3418316Swollman#include <pcap.h> 3518316Swollman/* 3618316Swollman * If we're going to include parts of the libpcap internals we MUST 3718316Swollman * set the feature-test macros they expect, or they may misbehave. 3818316Swollman */ 3918316Swollman#define HAVE_STRLCPY 4018316Swollman#define HAVE_SNPRINTF 4118316Swollman#define HAVE_VSNPRINTF 4218316Swollman#include <pcap-int.h> 4318316Swollman#include <pwd.h> 4418316Swollman#include <signal.h> 4518316Swollman#include <stdio.h> 4618316Swollman#include <stdlib.h> 4718316Swollman#include <syslog.h> 4818316Swollman#include <unistd.h> 4918316Swollman#include "pflogd.h" 5018316Swollman 5118316Swollmanenum cmd_types { 5218316Swollman PRIV_SET_SNAPLEN, /* set the snaplength */ 5318316Swollman PRIV_MOVE_LOG, /* move logfile away */ 5418316Swollman PRIV_OPEN_LOG /* open logfile for appending */ 5518316Swollman}; 5618316Swollman 5718316Swollmanstatic int priv_fd = -1; 5818316Swollmanstatic volatile pid_t child_pid = -1; 5918316Swollman 6018316Swollmanvolatile sig_atomic_t gotsig_chld = 0; 6118316Swollman 6218316Swollmanstatic void sig_pass_to_chld(int); 6318316Swollmanstatic void sig_chld(int); 6418316Swollmanstatic int may_read(int, void *, size_t); 6518316Swollmanstatic void must_read(int, void *, size_t); 6618316Swollmanstatic void must_write(int, void *, size_t); 6718316Swollmanstatic int set_snaplen(uint32_t); 6818316Swollmanstatic int move_log(const char *); 6918316Swollman 7018316Swollmanextern char *filename; 7118316Swollmanextern pcap_t *hpcap; 7218316Swollman 7318316Swollman/* based on syslogd privsep */ 7418316Swollmanint 7518316Swollmanpriv_init(void) 7618316Swollman{ 7718316Swollman int i, fd, socks[2], cmd; 7818316Swollman int snaplen, ret, olderrno; 7918316Swollman struct passwd *pw; 8018316Swollman 8118316Swollman for (i = 1; i < _NSIG; i++) 8218316Swollman signal(i, SIG_DFL); 83190715Sphk 8418316Swollman /* Create sockets */ 8518316Swollman if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 8618316Swollman err(1, "socketpair() failed"); 8718316Swollman 8818316Swollman pw = getpwnam("_pflogd"); 8918316Swollman if (pw == NULL) 9018316Swollman errx(1, "unknown user _pflogd"); 9118316Swollman endpwent(); 9218316Swollman 93190715Sphk child_pid = fork(); 9418316Swollman if (child_pid < 0) 9518316Swollman err(1, "fork() failed"); 9618316Swollman 9718316Swollman if (!child_pid) { 9818316Swollman gid_t gidset[1]; 9918316Swollman 10018316Swollman /* Child - drop privileges and return */ 10118316Swollman if (chroot(pw->pw_dir) != 0) 10218316Swollman err(1, "unable to chroot"); 10346303Smarkm if (chdir("/") != 0) 10418316Swollman err(1, "unable to chdir"); 10518316Swollman 10618316Swollman gidset[0] = pw->pw_gid; 10718316Swollman#ifdef __OpenBSD__ 10818316Swollman if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) 10918316Swollman err(1, "setresgid() failed"); 11018316Swollman#else 11118316Swollman if (setgid(pw->pw_gid) == -1) 11292883Simp err(1, "setgid() failed"); 11392883Simp#endif 11418316Swollman if (setgroups(1, gidset) == -1) 11592883Simp err(1, "setgroups() failed"); 11692883Simp#ifdef __OpenBSD__ 11718316Swollman if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 11892883Simp err(1, "setresuid() failed"); 11918316Swollman#else 12092883Simp if (setuid(pw->pw_uid) == -1) 12118316Swollman err(1, "setuid() failed"); 12292883Simp#endif 12318316Swollman close(socks[0]); 12492883Simp priv_fd = socks[1]; 12518316Swollman return 0; 12692883Simp } 12718316Swollman 12818316Swollman /* Father */ 12918316Swollman /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ 13018316Swollman signal(SIGALRM, sig_pass_to_chld); 13118316Swollman signal(SIGTERM, sig_pass_to_chld); 13218316Swollman signal(SIGHUP, sig_pass_to_chld); 13318316Swollman signal(SIGINT, sig_pass_to_chld); 13418316Swollman signal(SIGQUIT, sig_pass_to_chld); 13546303Smarkm signal(SIGCHLD, sig_chld); 13646303Smarkm 13746303Smarkm setproctitle("[priv]"); 13818316Swollman close(socks[1]); 13992883Simp 140190712Sphk while (!gotsig_chld) { 14192883Simp if (may_read(socks[0], &cmd, sizeof(int))) 14292883Simp break; 14392883Simp switch (cmd) { 14446303Smarkm case PRIV_SET_SNAPLEN: 14518316Swollman logmsg(LOG_DEBUG, 146 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 147 must_read(socks[0], &snaplen, sizeof(int)); 148 149 ret = set_snaplen(snaplen); 150 if (ret) { 151 logmsg(LOG_NOTICE, 152 "[priv]: set_snaplen failed for snaplen %d", 153 snaplen); 154 } 155 156 must_write(socks[0], &ret, sizeof(int)); 157 break; 158 159 case PRIV_OPEN_LOG: 160 logmsg(LOG_DEBUG, 161 "[priv]: msg PRIV_OPEN_LOG received"); 162 /* create or append logs but do not follow symlinks */ 163 fd = open(filename, 164 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 165 0600); 166 olderrno = errno; 167 send_fd(socks[0], fd); 168 if (fd < 0) 169 logmsg(LOG_NOTICE, 170 "[priv]: failed to open %s: %s", 171 filename, strerror(olderrno)); 172 else 173 close(fd); 174 break; 175 176 case PRIV_MOVE_LOG: 177 logmsg(LOG_DEBUG, 178 "[priv]: msg PRIV_MOVE_LOG received"); 179 ret = move_log(filename); 180 must_write(socks[0], &ret, sizeof(int)); 181 break; 182 183 default: 184 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 185 _exit(1); 186 /* NOTREACHED */ 187 } 188 } 189 190 _exit(1); 191} 192 193/* this is called from parent */ 194static int 195set_snaplen(uint32_t snap) 196{ 197 if (hpcap == NULL) 198 return (1); 199 200 hpcap->snapshot = snap; 201 set_pcap_filter(); 202 203 return 0; 204} 205 206static int 207move_log(const char *name) 208{ 209 char ren[PATH_MAX]; 210 int len; 211 212 for (;;) { 213 int fd; 214 215 len = snprintf(ren, sizeof(ren), "%s.bad.%08x", 216 name, arc4random()); 217 if ((size_t)len >= sizeof(ren)) { 218 logmsg(LOG_ERR, "[priv] new name too long"); 219 return (1); 220 } 221 222 /* lock destinanion */ 223 fd = open(ren, O_CREAT|O_EXCL, 0); 224 if (fd >= 0) { 225 close(fd); 226 break; 227 } 228 /* if file exists, try another name */ 229 if (errno != EEXIST && errno != EINTR) { 230 logmsg(LOG_ERR, "[priv] failed to create new name: %s", 231 strerror(errno)); 232 return (1); 233 } 234 } 235 236 if (rename(name, ren)) { 237 logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s", 238 name, ren, strerror(errno)); 239 return (1); 240 } 241 242 logmsg(LOG_NOTICE, 243 "[priv]: log file %s moved to %s", name, ren); 244 245 return (0); 246} 247 248/* 249 * send the snaplength to privileged process 250 */ 251int 252priv_set_snaplen(int snaplen) 253{ 254 int cmd, ret; 255 256 if (priv_fd < 0) 257 errx(1, "%s: called from privileged portion", __func__); 258 259 cmd = PRIV_SET_SNAPLEN; 260 261 must_write(priv_fd, &cmd, sizeof(int)); 262 must_write(priv_fd, &snaplen, sizeof(int)); 263 264 must_read(priv_fd, &ret, sizeof(int)); 265 266 /* also set hpcap->snapshot in child */ 267 if (ret == 0) 268 hpcap->snapshot = snaplen; 269 270 return (ret); 271} 272 273/* Open log-file */ 274int 275priv_open_log(void) 276{ 277 int cmd, fd; 278 279 if (priv_fd < 0) 280 errx(1, "%s: called from privileged portion", __func__); 281 282 cmd = PRIV_OPEN_LOG; 283 must_write(priv_fd, &cmd, sizeof(int)); 284 fd = receive_fd(priv_fd); 285 286 return (fd); 287} 288/* Move-away and reopen log-file */ 289int 290priv_move_log(void) 291{ 292 int cmd, ret; 293 294 if (priv_fd < 0) 295 errx(1, "%s: called from privileged portion\n", __func__); 296 297 cmd = PRIV_MOVE_LOG; 298 must_write(priv_fd, &cmd, sizeof(int)); 299 must_read(priv_fd, &ret, sizeof(int)); 300 301 return (ret); 302} 303 304/* If priv parent gets a TERM or HUP, pass it through to child instead */ 305static void 306sig_pass_to_chld(int sig) 307{ 308 int oerrno = errno; 309 310 if (child_pid != -1) 311 kill(child_pid, sig); 312 errno = oerrno; 313} 314 315/* if parent gets a SIGCHLD, it will exit */ 316static void 317sig_chld(int sig) 318{ 319 gotsig_chld = 1; 320} 321 322/* Read all data or return 1 for error. */ 323static int 324may_read(int fd, void *buf, size_t n) 325{ 326 char *s = buf; 327 ssize_t res, pos = 0; 328 329 while (n > (size_t)pos) { 330 res = read(fd, s + pos, n - pos); 331 switch (res) { 332 case -1: 333 if (errno == EINTR || errno == EAGAIN) 334 continue; 335 /* FALLTHROUGH */ 336 case 0: 337 return (1); 338 default: 339 pos += res; 340 } 341 } 342 return (0); 343} 344 345/* Read data with the assertion that it all must come through, or 346 * else abort the process. Based on atomicio() from openssh. */ 347static void 348must_read(int fd, void *buf, size_t n) 349{ 350 char *s = buf; 351 ssize_t res, pos = 0; 352 353 while (n > (size_t)pos) { 354 res = read(fd, s + pos, n - pos); 355 switch (res) { 356 case -1: 357 if (errno == EINTR || errno == EAGAIN) 358 continue; 359 /* FALLTHROUGH */ 360 case 0: 361 _exit(0); 362 default: 363 pos += res; 364 } 365 } 366} 367 368/* Write data with the assertion that it all has to be written, or 369 * else abort the process. Based on atomicio() from openssh. */ 370static void 371must_write(int fd, void *buf, size_t n) 372{ 373 char *s = buf; 374 ssize_t res, pos = 0; 375 376 while (n > (size_t)pos) { 377 res = write(fd, s + pos, n - pos); 378 switch (res) { 379 case -1: 380 if (errno == EINTR || errno == EAGAIN) 381 continue; 382 /* FALLTHROUGH */ 383 case 0: 384 _exit(0); 385 default: 386 pos += res; 387 } 388 } 389} 390