privsep.c revision 1.1.1.2
1/* $OpenBSD: privsep.c,v 1.12 2004/07/14 19:07:03 henning 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 42enum cmd_types { 43 PRIV_SET_SNAPLEN, /* set the snaplength */ 44 PRIV_OPEN_LOG /* open logfile for appending */ 45}; 46 47static int priv_fd = -1; 48static volatile pid_t child_pid = -1; 49 50volatile sig_atomic_t gotsig_chld = 0; 51 52static void sig_pass_to_chld(int); 53static void sig_chld(int); 54static int may_read(int, void *, size_t); 55static void must_read(int, void *, size_t); 56static void must_write(int, void *, size_t); 57static int set_snaplen(int snap); 58 59/* bpf filter expression common to parent and child */ 60extern char *filter; 61extern char *errbuf; 62extern char *filename; 63extern pcap_t *hpcap; 64 65/* based on syslogd privsep */ 66int 67priv_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 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(SIGCHLD, sig_chld); 120 121 setproctitle("[priv]"); 122 close(socks[1]); 123 124 while (!gotsig_chld) { 125 if (may_read(socks[0], &cmd, sizeof(int))) 126 break; 127 switch (cmd) { 128 case PRIV_SET_SNAPLEN: 129 logmsg(LOG_DEBUG, 130 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 131 must_read(socks[0], &snaplen, sizeof(int)); 132 133 ret = set_snaplen(snaplen); 134 if (ret) { 135 logmsg(LOG_NOTICE, 136 "[priv]: set_snaplen failed for snaplen %d", 137 snaplen); 138 } 139 140 must_write(socks[0], &ret, sizeof(int)); 141 break; 142 143 case PRIV_OPEN_LOG: 144 logmsg(LOG_DEBUG, 145 "[priv]: msg PRIV_OPEN_LOG received"); 146 /* create or append logs but do not follow symlinks */ 147 fd = open(filename, 148 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 149 0600); 150 olderrno = errno; 151 send_fd(socks[0], fd); 152 if (fd < 0) 153 logmsg(LOG_NOTICE, 154 "[priv]: failed to open %s: %s", 155 filename, strerror(olderrno)); 156 else 157 close(fd); 158 break; 159 160 default: 161 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 162 _exit(1); 163 /* NOTREACHED */ 164 } 165 } 166 167 _exit(1); 168} 169 170/* this is called from parent */ 171static int 172set_snaplen(int snap) 173{ 174 if (hpcap == NULL) 175 return (1); 176 177 hpcap->snapshot = snap; 178 set_pcap_filter(); 179 180 return 0; 181} 182 183 184/* 185 * send the snaplength to privileged process 186 */ 187int 188priv_set_snaplen(int snaplen) 189{ 190 int cmd, ret; 191 192 if (priv_fd < 0) 193 errx(1, "%s: called from privileged portion", __func__); 194 195 cmd = PRIV_SET_SNAPLEN; 196 197 must_write(priv_fd, &cmd, sizeof(int)); 198 must_write(priv_fd, &snaplen, sizeof(int)); 199 200 must_read(priv_fd, &ret, sizeof(int)); 201 202 /* also set hpcap->snapshot in child */ 203 if (ret == 0) 204 hpcap->snapshot = snaplen; 205 206 return (ret); 207} 208 209/* Open log-file */ 210int 211priv_open_log(void) 212{ 213 int cmd, fd; 214 215 if (priv_fd < 0) 216 errx(1, "%s: called from privileged portion", __func__); 217 218 cmd = PRIV_OPEN_LOG; 219 must_write(priv_fd, &cmd, sizeof(int)); 220 fd = receive_fd(priv_fd); 221 222 return (fd); 223} 224 225/* If priv parent gets a TERM or HUP, pass it through to child instead */ 226static void 227sig_pass_to_chld(int sig) 228{ 229 int oerrno = errno; 230 231 if (child_pid != -1) 232 kill(child_pid, sig); 233 errno = oerrno; 234} 235 236/* if parent gets a SIGCHLD, it will exit */ 237static void 238sig_chld(int sig) 239{ 240 gotsig_chld = 1; 241} 242 243/* Read all data or return 1 for error. */ 244static int 245may_read(int fd, void *buf, size_t n) 246{ 247 char *s = buf; 248 ssize_t res, 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. */ 267static void 268must_read(int fd, void *buf, size_t n) 269{ 270 char *s = buf; 271 ssize_t res, pos = 0; 272 273 while (n > pos) { 274 res = read(fd, s + pos, n - pos); 275 switch (res) { 276 case -1: 277 if (errno == EINTR || errno == EAGAIN) 278 continue; 279 case 0: 280 _exit(0); 281 default: 282 pos += res; 283 } 284 } 285} 286 287/* Write data with the assertion that it all has to be written, or 288 * else abort the process. Based on atomicio() from openssh. */ 289static void 290must_write(int fd, void *buf, size_t n) 291{ 292 char *s = buf; 293 ssize_t res, pos = 0; 294 295 while (n > pos) { 296 res = write(fd, s + pos, n - pos); 297 switch (res) { 298 case -1: 299 if (errno == EINTR || errno == EAGAIN) 300 continue; 301 case 0: 302 _exit(0); 303 default: 304 pos += res; 305 } 306 } 307} 308