1171172Smlaier/* $OpenBSD: privsep.c,v 1.16 2006/10/25 20:55:04 moritz Exp $ */ 2130614Smlaier 3130614Smlaier/* 4130614Smlaier * Copyright (c) 2003 Can Erkin Acar 5130614Smlaier * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> 6130614Smlaier * 7130614Smlaier * Permission to use, copy, modify, and distribute this software for any 8130614Smlaier * purpose with or without fee is hereby granted, provided that the above 9130614Smlaier * copyright notice and this permission notice appear in all copies. 10130614Smlaier * 11130614Smlaier * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12130614Smlaier * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13130614Smlaier * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14130614Smlaier * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15130614Smlaier * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16130614Smlaier * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17130614Smlaier * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18130614Smlaier */ 19130617Smlaier 20130617Smlaier#include <sys/cdefs.h> 21130617Smlaier__FBSDID("$FreeBSD$"); 22130617Smlaier 23171172Smlaier#include <sys/types.h> 24130614Smlaier#include <sys/time.h> 25130614Smlaier#include <sys/socket.h> 26223637Sbz#include <sys/ioctl.h> 27130614Smlaier 28130614Smlaier#include <net/if.h> 29130614Smlaier#include <net/bpf.h> 30130614Smlaier 31130614Smlaier#include <err.h> 32130614Smlaier#include <errno.h> 33130614Smlaier#include <fcntl.h> 34171172Smlaier#include <limits.h> 35171172Smlaier#include <pcap.h> 36171172Smlaier#include <pcap-int.h> 37130614Smlaier#include <pwd.h> 38130614Smlaier#include <signal.h> 39130614Smlaier#include <stdio.h> 40130614Smlaier#include <stdlib.h> 41130614Smlaier#include <string.h> 42130614Smlaier#include <syslog.h> 43130614Smlaier#include <unistd.h> 44130614Smlaier#include "pflogd.h" 45130614Smlaier 46130614Smlaierenum cmd_types { 47130614Smlaier PRIV_SET_SNAPLEN, /* set the snaplength */ 48171172Smlaier PRIV_MOVE_LOG, /* move logfile away */ 49130614Smlaier PRIV_OPEN_LOG /* open logfile for appending */ 50130614Smlaier}; 51130614Smlaier 52130614Smlaierstatic int priv_fd = -1; 53130614Smlaierstatic volatile pid_t child_pid = -1; 54130614Smlaier 55130614Smlaiervolatile sig_atomic_t gotsig_chld = 0; 56130614Smlaier 57130614Smlaierstatic void sig_pass_to_chld(int); 58130614Smlaierstatic void sig_chld(int); 59130614Smlaierstatic int may_read(int, void *, size_t); 60130614Smlaierstatic void must_read(int, void *, size_t); 61130614Smlaierstatic void must_write(int, void *, size_t); 62130614Smlaierstatic int set_snaplen(int snap); 63171172Smlaierstatic int move_log(const char *name); 64130614Smlaier 65130614Smlaierextern char *filename; 66130614Smlaierextern pcap_t *hpcap; 67130614Smlaier 68130614Smlaier/* based on syslogd privsep */ 69130614Smlaierint 70130614Smlaierpriv_init(void) 71130614Smlaier{ 72130614Smlaier int i, fd, socks[2], cmd; 73145840Smlaier int snaplen, ret, olderrno; 74130614Smlaier struct passwd *pw; 75130614Smlaier 76130617Smlaier#ifdef __FreeBSD__ 77130617Smlaier for (i = 1; i < NSIG; i++) 78130617Smlaier#else 79130614Smlaier for (i = 1; i < _NSIG; i++) 80130617Smlaier#endif 81130614Smlaier signal(i, SIG_DFL); 82130614Smlaier 83130614Smlaier /* Create sockets */ 84130614Smlaier if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 85130614Smlaier err(1, "socketpair() failed"); 86130614Smlaier 87130614Smlaier pw = getpwnam("_pflogd"); 88130614Smlaier if (pw == NULL) 89130614Smlaier errx(1, "unknown user _pflogd"); 90130614Smlaier endpwent(); 91130614Smlaier 92130614Smlaier child_pid = fork(); 93130614Smlaier if (child_pid < 0) 94130614Smlaier err(1, "fork() failed"); 95130614Smlaier 96130614Smlaier if (!child_pid) { 97130614Smlaier gid_t gidset[1]; 98130614Smlaier 99130614Smlaier /* Child - drop privileges and return */ 100130614Smlaier if (chroot(pw->pw_dir) != 0) 101130614Smlaier err(1, "unable to chroot"); 102130614Smlaier if (chdir("/") != 0) 103130614Smlaier err(1, "unable to chdir"); 104130614Smlaier 105130614Smlaier gidset[0] = pw->pw_gid; 106171172Smlaier if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) 107171172Smlaier err(1, "setresgid() failed"); 108130614Smlaier if (setgroups(1, gidset) == -1) 109130614Smlaier err(1, "setgroups() failed"); 110171172Smlaier if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 111171172Smlaier err(1, "setresuid() failed"); 112130614Smlaier close(socks[0]); 113130614Smlaier priv_fd = socks[1]; 114130614Smlaier return 0; 115130614Smlaier } 116130614Smlaier 117130614Smlaier /* Father */ 118145840Smlaier /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ 119130614Smlaier signal(SIGALRM, sig_pass_to_chld); 120130614Smlaier signal(SIGTERM, sig_pass_to_chld); 121130614Smlaier signal(SIGHUP, sig_pass_to_chld); 122145840Smlaier signal(SIGINT, sig_pass_to_chld); 123145840Smlaier signal(SIGQUIT, sig_pass_to_chld); 124130614Smlaier signal(SIGCHLD, sig_chld); 125130614Smlaier 126130614Smlaier setproctitle("[priv]"); 127130614Smlaier close(socks[1]); 128130614Smlaier 129130614Smlaier while (!gotsig_chld) { 130130614Smlaier if (may_read(socks[0], &cmd, sizeof(int))) 131130614Smlaier break; 132130614Smlaier switch (cmd) { 133130614Smlaier case PRIV_SET_SNAPLEN: 134130614Smlaier logmsg(LOG_DEBUG, 135130614Smlaier "[priv]: msg PRIV_SET_SNAPLENGTH received"); 136130614Smlaier must_read(socks[0], &snaplen, sizeof(int)); 137130614Smlaier 138130614Smlaier ret = set_snaplen(snaplen); 139130614Smlaier if (ret) { 140130614Smlaier logmsg(LOG_NOTICE, 141130614Smlaier "[priv]: set_snaplen failed for snaplen %d", 142130614Smlaier snaplen); 143130614Smlaier } 144130614Smlaier 145130614Smlaier must_write(socks[0], &ret, sizeof(int)); 146130614Smlaier break; 147130614Smlaier 148130614Smlaier case PRIV_OPEN_LOG: 149130614Smlaier logmsg(LOG_DEBUG, 150130614Smlaier "[priv]: msg PRIV_OPEN_LOG received"); 151130614Smlaier /* create or append logs but do not follow symlinks */ 152130614Smlaier fd = open(filename, 153130614Smlaier O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 154130614Smlaier 0600); 155145840Smlaier olderrno = errno; 156145840Smlaier send_fd(socks[0], fd); 157130614Smlaier if (fd < 0) 158130614Smlaier logmsg(LOG_NOTICE, 159130614Smlaier "[priv]: failed to open %s: %s", 160145840Smlaier filename, strerror(olderrno)); 161145840Smlaier else 162145840Smlaier close(fd); 163130614Smlaier break; 164130614Smlaier 165171172Smlaier case PRIV_MOVE_LOG: 166171172Smlaier logmsg(LOG_DEBUG, 167171172Smlaier "[priv]: msg PRIV_MOVE_LOG received"); 168171172Smlaier ret = move_log(filename); 169171172Smlaier must_write(socks[0], &ret, sizeof(int)); 170171172Smlaier break; 171171172Smlaier 172130614Smlaier default: 173130614Smlaier logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 174130614Smlaier _exit(1); 175130614Smlaier /* NOTREACHED */ 176130614Smlaier } 177130614Smlaier } 178130614Smlaier 179130614Smlaier _exit(1); 180130614Smlaier} 181130614Smlaier 182130614Smlaier/* this is called from parent */ 183130614Smlaierstatic int 184130614Smlaierset_snaplen(int snap) 185130614Smlaier{ 186130614Smlaier if (hpcap == NULL) 187130614Smlaier return (1); 188130614Smlaier 189130614Smlaier hpcap->snapshot = snap; 190130614Smlaier set_pcap_filter(); 191130614Smlaier 192130614Smlaier return 0; 193130614Smlaier} 194130614Smlaier 195171172Smlaierstatic int 196171172Smlaiermove_log(const char *name) 197171172Smlaier{ 198171172Smlaier char ren[PATH_MAX]; 199171172Smlaier int len; 200130614Smlaier 201171172Smlaier for (;;) { 202171172Smlaier int fd; 203171172Smlaier 204171172Smlaier len = snprintf(ren, sizeof(ren), "%s.bad.%08x", 205171172Smlaier name, arc4random()); 206171172Smlaier if (len >= sizeof(ren)) { 207171172Smlaier logmsg(LOG_ERR, "[priv] new name too long"); 208171172Smlaier return (1); 209171172Smlaier } 210171172Smlaier 211171172Smlaier /* lock destinanion */ 212171172Smlaier fd = open(ren, O_CREAT|O_EXCL, 0); 213171172Smlaier if (fd >= 0) { 214171172Smlaier close(fd); 215171172Smlaier break; 216171172Smlaier } 217171172Smlaier /* if file exists, try another name */ 218171172Smlaier if (errno != EEXIST && errno != EINTR) { 219171172Smlaier logmsg(LOG_ERR, "[priv] failed to create new name: %s", 220171172Smlaier strerror(errno)); 221171172Smlaier return (1); 222171172Smlaier } 223171172Smlaier } 224171172Smlaier 225171172Smlaier if (rename(name, ren)) { 226171172Smlaier logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s", 227171172Smlaier name, ren, strerror(errno)); 228171172Smlaier return (1); 229171172Smlaier } 230171172Smlaier 231171172Smlaier logmsg(LOG_NOTICE, 232171172Smlaier "[priv]: log file %s moved to %s", name, ren); 233171172Smlaier 234171172Smlaier return (0); 235171172Smlaier} 236171172Smlaier 237130614Smlaier/* 238130614Smlaier * send the snaplength to privileged process 239130614Smlaier */ 240130614Smlaierint 241130614Smlaierpriv_set_snaplen(int snaplen) 242130614Smlaier{ 243130614Smlaier int cmd, ret; 244130614Smlaier 245130614Smlaier if (priv_fd < 0) 246130614Smlaier errx(1, "%s: called from privileged portion", __func__); 247130614Smlaier 248130614Smlaier cmd = PRIV_SET_SNAPLEN; 249130614Smlaier 250130614Smlaier must_write(priv_fd, &cmd, sizeof(int)); 251130614Smlaier must_write(priv_fd, &snaplen, sizeof(int)); 252130614Smlaier 253130614Smlaier must_read(priv_fd, &ret, sizeof(int)); 254130614Smlaier 255130614Smlaier /* also set hpcap->snapshot in child */ 256130614Smlaier if (ret == 0) 257130614Smlaier hpcap->snapshot = snaplen; 258130614Smlaier 259130614Smlaier return (ret); 260130614Smlaier} 261130614Smlaier 262130614Smlaier/* Open log-file */ 263130614Smlaierint 264130614Smlaierpriv_open_log(void) 265130614Smlaier{ 266130614Smlaier int cmd, fd; 267130614Smlaier 268130614Smlaier if (priv_fd < 0) 269145840Smlaier errx(1, "%s: called from privileged portion", __func__); 270130614Smlaier 271130614Smlaier cmd = PRIV_OPEN_LOG; 272130614Smlaier must_write(priv_fd, &cmd, sizeof(int)); 273130614Smlaier fd = receive_fd(priv_fd); 274130614Smlaier 275130614Smlaier return (fd); 276130614Smlaier} 277171172Smlaier/* Move-away and reopen log-file */ 278171172Smlaierint 279171172Smlaierpriv_move_log(void) 280171172Smlaier{ 281171172Smlaier int cmd, ret; 282130614Smlaier 283171172Smlaier if (priv_fd < 0) 284171172Smlaier errx(1, "%s: called from privileged portion\n", __func__); 285171172Smlaier 286171172Smlaier cmd = PRIV_MOVE_LOG; 287171172Smlaier must_write(priv_fd, &cmd, sizeof(int)); 288171172Smlaier must_read(priv_fd, &ret, sizeof(int)); 289171172Smlaier 290171172Smlaier return (ret); 291171172Smlaier} 292171172Smlaier 293130614Smlaier/* If priv parent gets a TERM or HUP, pass it through to child instead */ 294130614Smlaierstatic void 295130614Smlaiersig_pass_to_chld(int sig) 296130614Smlaier{ 297130614Smlaier int oerrno = errno; 298130614Smlaier 299130614Smlaier if (child_pid != -1) 300130614Smlaier kill(child_pid, sig); 301130614Smlaier errno = oerrno; 302130614Smlaier} 303130614Smlaier 304130614Smlaier/* if parent gets a SIGCHLD, it will exit */ 305130614Smlaierstatic void 306130614Smlaiersig_chld(int sig) 307130614Smlaier{ 308130614Smlaier gotsig_chld = 1; 309130614Smlaier} 310130614Smlaier 311130614Smlaier/* Read all data or return 1 for error. */ 312130614Smlaierstatic int 313130614Smlaiermay_read(int fd, void *buf, size_t n) 314130614Smlaier{ 315130614Smlaier char *s = buf; 316130614Smlaier ssize_t res, pos = 0; 317130614Smlaier 318130614Smlaier while (n > pos) { 319130614Smlaier res = read(fd, s + pos, n - pos); 320130614Smlaier switch (res) { 321130614Smlaier case -1: 322130614Smlaier if (errno == EINTR || errno == EAGAIN) 323130614Smlaier continue; 324130614Smlaier case 0: 325130614Smlaier return (1); 326130614Smlaier default: 327130614Smlaier pos += res; 328130614Smlaier } 329130614Smlaier } 330130614Smlaier return (0); 331130614Smlaier} 332130614Smlaier 333130614Smlaier/* Read data with the assertion that it all must come through, or 334130614Smlaier * else abort the process. Based on atomicio() from openssh. */ 335130614Smlaierstatic void 336130614Smlaiermust_read(int fd, void *buf, size_t n) 337130614Smlaier{ 338130614Smlaier char *s = buf; 339130614Smlaier ssize_t res, pos = 0; 340130614Smlaier 341130614Smlaier while (n > pos) { 342130614Smlaier res = read(fd, s + pos, n - pos); 343130614Smlaier switch (res) { 344130614Smlaier case -1: 345130614Smlaier if (errno == EINTR || errno == EAGAIN) 346130614Smlaier continue; 347130614Smlaier case 0: 348130614Smlaier _exit(0); 349130614Smlaier default: 350130614Smlaier pos += res; 351130614Smlaier } 352130614Smlaier } 353130614Smlaier} 354130614Smlaier 355130614Smlaier/* Write data with the assertion that it all has to be written, or 356130614Smlaier * else abort the process. Based on atomicio() from openssh. */ 357130614Smlaierstatic void 358130614Smlaiermust_write(int fd, void *buf, size_t n) 359130614Smlaier{ 360130614Smlaier char *s = buf; 361130614Smlaier ssize_t res, pos = 0; 362130614Smlaier 363130614Smlaier while (n > pos) { 364130614Smlaier res = write(fd, s + pos, n - pos); 365130614Smlaier switch (res) { 366130614Smlaier case -1: 367130614Smlaier if (errno == EINTR || errno == EAGAIN) 368130614Smlaier continue; 369130614Smlaier case 0: 370130614Smlaier _exit(0); 371130614Smlaier default: 372130614Smlaier pos += res; 373130614Smlaier } 374130614Smlaier } 375130614Smlaier} 376