privsep.c revision 1.2
1251881Speter/* $OpenBSD: privsep.c,v 1.12 2004/07/14 19:07:03 henning Exp $ */ 2251881Speter 3251881Speter/* 4251881Speter * Copyright (c) 2003 Can Erkin Acar 5251881Speter * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> 6251881Speter * 7251881Speter * Permission to use, copy, modify, and distribute this software for any 8251881Speter * purpose with or without fee is hereby granted, provided that the above 9251881Speter * copyright notice and this permission notice appear in all copies. 10251881Speter * 11251881Speter * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12251881Speter * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13251881Speter * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14251881Speter * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15251881Speter * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16251881Speter * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17251881Speter * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18251881Speter */ 19251881Speter#include <sys/ioctl.h> 20251881Speter#include <sys/types.h> 21251881Speter#include <sys/time.h> 22251881Speter#include <sys/socket.h> 23251881Speter#include <sys/ioctl.h> 24251881Speter 25251881Speter#include <net/if.h> 26251881Speter#include <net/bpf.h> 27251881Speter 28251881Speter#include <string.h> 29251881Speter 30251881Speter#include <err.h> 31251881Speter#include <errno.h> 32251881Speter#include <fcntl.h> 33251881Speter#include <pcap.h> 34251881Speter#include <pcap-int.h> 35251881Speter#include <pwd.h> 36251881Speter#include <signal.h> 37251881Speter#include <stdio.h> 38251881Speter#include <stdlib.h> 39251881Speter#include <syslog.h> 40251881Speter#include <unistd.h> 41251881Speter#include "pflogd.h" 42251881Speter 43251881Speterenum cmd_types { 44251881Speter PRIV_SET_SNAPLEN, /* set the snaplength */ 45251881Speter PRIV_OPEN_LOG /* open logfile for appending */ 46251881Speter}; 47251881Speter 48251881Speterstatic int priv_fd = -1; 49251881Speterstatic volatile pid_t child_pid = -1; 50251881Speter 51251881Spetervolatile sig_atomic_t gotsig_chld = 0; 52251881Speter 53251881Speterstatic void sig_pass_to_chld(int); 54251881Speterstatic void sig_chld(int); 55251881Speterstatic int may_read(int, void *, size_t); 56251881Speterstatic void must_read(int, void *, size_t); 57251881Speterstatic void must_write(int, void *, size_t); 58251881Speterstatic int set_snaplen(int snap); 59251881Speter 60251881Speter/* bpf filter expression common to parent and child */ 61251881Speterextern char *filter; 62251881Speterextern char *errbuf; 63251881Speterextern char *filename; 64251881Speterextern pcap_t *hpcap; 65251881Speter 66251881Speter/* based on syslogd privsep */ 67251881Speterint 68251881Speterpriv_init(void) 69251881Speter{ 70251881Speter int i, fd, socks[2], cmd; 71251881Speter int snaplen, ret, olderrno; 72251881Speter struct passwd *pw; 73251881Speter 74251881Speter for (i = 1; i < _NSIG; i++) 75251881Speter signal(i, SIG_DFL); 76251881Speter 77251881Speter /* Create sockets */ 78251881Speter if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 79251881Speter err(1, "socketpair() failed"); 80251881Speter 81251881Speter pw = getpwnam("_pflogd"); 82251881Speter if (pw == NULL) 83251881Speter errx(1, "unknown user _pflogd"); 84251881Speter endpwent(); 85251881Speter 86251881Speter child_pid = fork(); 87251881Speter if (child_pid < 0) 88251881Speter err(1, "fork() failed"); 89251881Speter 90251881Speter if (!child_pid) { 91251881Speter gid_t gidset[1]; 92251881Speter 93251881Speter /* Child - drop privileges and return */ 94 if (chroot(pw->pw_dir) != 0) 95 err(1, "unable to chroot"); 96 if (chdir("/") != 0) 97 err(1, "unable to chdir"); 98 99 gidset[0] = pw->pw_gid; 100 if (setgroups(1, gidset) == -1) 101 err(1, "setgroups() failed"); 102#ifdef __OpenBSD__ 103 if (setegid(pw->pw_gid) == -1) 104 err(1, "setegid() failed"); 105#endif 106 if (setgid(pw->pw_gid) == -1) 107 err(1, "setgid() failed"); 108#ifdef __OpenBSD__ 109 if (seteuid(pw->pw_uid) == -1) 110 err(1, "seteuid() failed"); 111#endif 112 if (setuid(pw->pw_uid) == -1) 113 err(1, "setuid() failed"); 114 close(socks[0]); 115 priv_fd = socks[1]; 116 return 0; 117 } 118 119 /* Father */ 120 /* Pass ALRM/TERM/HUP through to child, and accept CHLD */ 121 signal(SIGALRM, sig_pass_to_chld); 122 signal(SIGTERM, sig_pass_to_chld); 123 signal(SIGHUP, sig_pass_to_chld); 124 signal(SIGCHLD, sig_chld); 125 126 setproctitle("[priv]"); 127 close(socks[1]); 128 129 while (!gotsig_chld) { 130 if (may_read(socks[0], &cmd, sizeof(int))) 131 break; 132 switch (cmd) { 133 case PRIV_SET_SNAPLEN: 134 logmsg(LOG_DEBUG, 135 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 136 must_read(socks[0], &snaplen, sizeof(int)); 137 138 ret = set_snaplen(snaplen); 139 if (ret) { 140 logmsg(LOG_NOTICE, 141 "[priv]: set_snaplen failed for snaplen %d", 142 snaplen); 143 } 144 145 must_write(socks[0], &ret, sizeof(int)); 146 break; 147 148 case PRIV_OPEN_LOG: 149 logmsg(LOG_DEBUG, 150 "[priv]: msg PRIV_OPEN_LOG received"); 151 /* create or append logs but do not follow symlinks */ 152 fd = open(filename, 153 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 154 0600); 155 olderrno = errno; 156 send_fd(socks[0], fd); 157 if (fd < 0) 158 logmsg(LOG_NOTICE, 159 "[priv]: failed to open %s: %s", 160 filename, strerror(olderrno)); 161 else 162 close(fd); 163 break; 164 165 default: 166 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 167 _exit(1); 168 /* NOTREACHED */ 169 } 170 } 171 172 _exit(1); 173} 174 175/* this is called from parent */ 176static int 177set_snaplen(int snap) 178{ 179 if (hpcap == NULL) 180 return (1); 181 182 hpcap->snapshot = snap; 183 set_pcap_filter(); 184 185 return 0; 186} 187 188 189/* 190 * send the snaplength to privileged process 191 */ 192int 193priv_set_snaplen(int snaplen) 194{ 195 int cmd, ret; 196 197 if (priv_fd < 0) 198 errx(1, "%s: called from privileged portion", __func__); 199 200 cmd = PRIV_SET_SNAPLEN; 201 202 must_write(priv_fd, &cmd, sizeof(int)); 203 must_write(priv_fd, &snaplen, sizeof(int)); 204 205 must_read(priv_fd, &ret, sizeof(int)); 206 207 /* also set hpcap->snapshot in child */ 208 if (ret == 0) 209 hpcap->snapshot = snaplen; 210 211 return (ret); 212} 213 214/* Open log-file */ 215int 216priv_open_log(void) 217{ 218 int cmd, fd; 219 220 if (priv_fd < 0) 221 errx(1, "%s: called from privileged portion", __func__); 222 223 cmd = PRIV_OPEN_LOG; 224 must_write(priv_fd, &cmd, sizeof(int)); 225 fd = receive_fd(priv_fd); 226 227 return (fd); 228} 229 230/* If priv parent gets a TERM or HUP, pass it through to child instead */ 231static void 232sig_pass_to_chld(int sig) 233{ 234 int oerrno = errno; 235 236 if (child_pid != -1) 237 kill(child_pid, sig); 238 errno = oerrno; 239} 240 241/* if parent gets a SIGCHLD, it will exit */ 242static void 243sig_chld(int sig) 244{ 245 gotsig_chld = 1; 246} 247 248/* Read all data or return 1 for error. */ 249static int 250may_read(int fd, void *buf, size_t n) 251{ 252 char *s = buf; 253 ssize_t res, pos = 0; 254 255 while (n > pos) { 256 res = read(fd, s + pos, n - pos); 257 switch (res) { 258 case -1: 259 if (errno == EINTR || errno == EAGAIN) 260 continue; 261 case 0: 262 return (1); 263 default: 264 pos += res; 265 } 266 } 267 return (0); 268} 269 270/* Read data with the assertion that it all must come through, or 271 * else abort the process. Based on atomicio() from openssh. */ 272static void 273must_read(int fd, void *buf, size_t n) 274{ 275 char *s = buf; 276 ssize_t res, pos = 0; 277 278 while (n > pos) { 279 res = read(fd, s + pos, n - pos); 280 switch (res) { 281 case -1: 282 if (errno == EINTR || errno == EAGAIN) 283 continue; 284 case 0: 285 _exit(0); 286 default: 287 pos += res; 288 } 289 } 290} 291 292/* Write data with the assertion that it all has to be written, or 293 * else abort the process. Based on atomicio() from openssh. */ 294static void 295must_write(int fd, void *buf, size_t n) 296{ 297 char *s = buf; 298 ssize_t res, pos = 0; 299 300 while (n > pos) { 301 res = write(fd, s + pos, n - pos); 302 switch (res) { 303 case -1: 304 if (errno == EINTR || errno == EAGAIN) 305 continue; 306 case 0: 307 _exit(0); 308 default: 309 pos += res; 310 } 311 } 312} 313