1 /* $OpenBSD: privsep.c,v 1.17 2009/12/24 10:06:35 sobrado 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 "pflogd.h" 41 42 enum cmd_types { 43 PRIV_SET_SNAPLEN, /* set the snaplength */ 44 PRIV_MOVE_LOG, /* move logfile away */ 45 PRIV_OPEN_LOG /* open logfile for appending */ 46 }; 47 48 static int priv_fd = -1; 49 static volatile pid_t child_pid = -1; 50 51 volatile sig_atomic_t gotsig_chld = 0; 52 53 static void sig_pass_to_chld(int); 54 static void sig_chld(int); 55 static int may_read(int, void *, size_t); 56 static void must_read(int, void *, size_t); 57 static void must_write(int, void *, size_t); 58 static int set_snaplen(int snap); 59 static int move_log(const char *name); 60 61 extern char *filename; 62 extern pcap_t *hpcap; 63 64 /* based on syslogd privsep */ 65 int 66 priv_init(void) 67 { 68 int i, fd, socks[2], cmd; 69 int snaplen, ret, olderrno; 70 struct passwd *pw; 71 72 for (i = 1; i < _NSIG; i++) 73 signal(i, SIG_DFL); 74 75 /* Create sockets */ 76 if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1) 77 err(1, "socketpair() failed"); 78 79 pw = getpwnam("_pflogd"); 80 if (pw == NULL) 81 errx(1, "unknown user _pflogd"); 82 endpwent(); 83 84 child_pid = fork(); 85 if (child_pid < 0) 86 err(1, "fork() failed"); 87 88 if (!child_pid) { 89 gid_t gidset[1]; 90 91 /* Child - drop privileges and return */ 92 if (chroot(pw->pw_dir) != 0) 93 err(1, "unable to chroot"); 94 if (chdir("/") != 0) 95 err(1, "unable to chdir"); 96 97 gidset[0] = pw->pw_gid; 98 if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1) 99 err(1, "setresgid() failed"); 100 if (setgroups(1, gidset) == -1) 101 err(1, "setgroups() failed"); 102 if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1) 103 err(1, "setresuid() failed"); 104 close(socks[0]); 105 priv_fd = socks[1]; 106 return 0; 107 } 108 109 /* Father */ 110 /* Pass ALRM/TERM/HUP/INT/QUIT through to child, and accept CHLD */ 111 signal(SIGALRM, sig_pass_to_chld); 112 signal(SIGTERM, sig_pass_to_chld); 113 signal(SIGHUP, sig_pass_to_chld); 114 signal(SIGINT, sig_pass_to_chld); 115 signal(SIGQUIT, sig_pass_to_chld); 116 signal(SIGCHLD, sig_chld); 117 118 setproctitle("[priv]"); 119 close(socks[1]); 120 121 while (!gotsig_chld) { 122 if (may_read(socks[0], &cmd, sizeof(int))) 123 break; 124 switch (cmd) { 125 case PRIV_SET_SNAPLEN: 126 logmsg(LOG_DEBUG, 127 "[priv]: msg PRIV_SET_SNAPLENGTH received"); 128 must_read(socks[0], &snaplen, sizeof(int)); 129 130 ret = set_snaplen(snaplen); 131 if (ret) { 132 logmsg(LOG_NOTICE, 133 "[priv]: set_snaplen failed for snaplen %d", 134 snaplen); 135 } 136 137 must_write(socks[0], &ret, sizeof(int)); 138 break; 139 140 case PRIV_OPEN_LOG: 141 logmsg(LOG_DEBUG, 142 "[priv]: msg PRIV_OPEN_LOG received"); 143 /* create or append logs but do not follow symlinks */ 144 fd = open(filename, 145 O_RDWR|O_CREAT|O_APPEND|O_NONBLOCK|O_NOFOLLOW, 146 0600); 147 olderrno = errno; 148 send_fd(socks[0], fd); 149 if (fd < 0) 150 logmsg(LOG_NOTICE, 151 "[priv]: failed to open %s: %s", 152 filename, strerror(olderrno)); 153 else 154 close(fd); 155 break; 156 157 case PRIV_MOVE_LOG: 158 logmsg(LOG_DEBUG, 159 "[priv]: msg PRIV_MOVE_LOG received"); 160 ret = move_log(filename); 161 must_write(socks[0], &ret, sizeof(int)); 162 break; 163 164 default: 165 logmsg(LOG_ERR, "[priv]: unknown command %d", cmd); 166 _exit(1); 167 /* NOTREACHED */ 168 } 169 } 170 171 _exit(1); 172 } 173 174 /* this is called from parent */ 175 static int 176 set_snaplen(int snap) 177 { 178 if (hpcap == NULL) 179 return (1); 180 181 hpcap->snapshot = snap; 182 set_pcap_filter(); 183 184 return 0; 185 } 186 187 static int 188 move_log(const char *name) 189 { 190 char ren[PATH_MAX]; 191 int len; 192 193 for (;;) { 194 int fd; 195 196 len = snprintf(ren, sizeof(ren), "%s.bad.%08x", 197 name, arc4random()); 198 if (len >= sizeof(ren)) { 199 logmsg(LOG_ERR, "[priv] new name too long"); 200 return (1); 201 } 202 203 /* lock destination */ 204 fd = open(ren, O_CREAT|O_EXCL, 0); 205 if (fd >= 0) { 206 close(fd); 207 break; 208 } 209 /* if file exists, try another name */ 210 if (errno != EEXIST && errno != EINTR) { 211 logmsg(LOG_ERR, "[priv] failed to create new name: %s", 212 strerror(errno)); 213 return (1); 214 } 215 } 216 217 if (rename(name, ren)) { 218 logmsg(LOG_ERR, "[priv] failed to rename %s to %s: %s", 219 name, ren, strerror(errno)); 220 return (1); 221 } 222 223 logmsg(LOG_NOTICE, 224 "[priv]: log file %s moved to %s", name, ren); 225 226 return (0); 227 } 228 229 /* 230 * send the snaplength to privileged process 231 */ 232 int 233 priv_set_snaplen(int snaplen) 234 { 235 int cmd, ret; 236 237 if (priv_fd < 0) 238 errx(1, "%s: called from privileged portion", __func__); 239 240 cmd = PRIV_SET_SNAPLEN; 241 242 must_write(priv_fd, &cmd, sizeof(int)); 243 must_write(priv_fd, &snaplen, sizeof(int)); 244 245 must_read(priv_fd, &ret, sizeof(int)); 246 247 /* also set hpcap->snapshot in child */ 248 if (ret == 0) 249 hpcap->snapshot = snaplen; 250 251 return (ret); 252 } 253 254 /* Open log-file */ 255 int 256 priv_open_log(void) 257 { 258 int cmd, fd; 259 260 if (priv_fd < 0) 261 errx(1, "%s: called from privileged portion", __func__); 262 263 cmd = PRIV_OPEN_LOG; 264 must_write(priv_fd, &cmd, sizeof(int)); 265 fd = receive_fd(priv_fd); 266 267 return (fd); 268 } 269 /* Move-away and reopen log-file */ 270 int 271 priv_move_log(void) 272 { 273 int cmd, ret; 274 275 if (priv_fd < 0) 276 errx(1, "%s: called from privileged portion\n", __func__); 277 278 cmd = PRIV_MOVE_LOG; 279 must_write(priv_fd, &cmd, sizeof(int)); 280 must_read(priv_fd, &ret, sizeof(int)); 281 282 return (ret); 283 } 284 285 /* If priv parent gets a TERM or HUP, pass it through to child instead */ 286 static void 287 sig_pass_to_chld(int sig) 288 { 289 int oerrno = errno; 290 291 if (child_pid != -1) 292 kill(child_pid, sig); 293 errno = oerrno; 294 } 295 296 /* if parent gets a SIGCHLD, it will exit */ 297 static void 298 sig_chld(int sig) 299 { 300 gotsig_chld = 1; 301 } 302 303 /* Read all data or return 1 for error. */ 304 static int 305 may_read(int fd, void *buf, size_t n) 306 { 307 char *s = buf; 308 ssize_t res, pos = 0; 309 310 while (n > pos) { 311 res = read(fd, s + pos, n - pos); 312 switch (res) { 313 case -1: 314 if (errno == EINTR || errno == EAGAIN) 315 continue; 316 case 0: 317 return (1); 318 default: 319 pos += res; 320 } 321 } 322 return (0); 323 } 324 325 /* Read data with the assertion that it all must come through, or 326 * else abort the process. Based on atomicio() from openssh. */ 327 static void 328 must_read(int fd, void *buf, size_t n) 329 { 330 char *s = buf; 331 ssize_t res, pos = 0; 332 333 while (n > pos) { 334 res = read(fd, s + pos, n - pos); 335 switch (res) { 336 case -1: 337 if (errno == EINTR || errno == EAGAIN) 338 continue; 339 case 0: 340 _exit(0); 341 default: 342 pos += res; 343 } 344 } 345 } 346 347 /* Write data with the assertion that it all has to be written, or 348 * else abort the process. Based on atomicio() from openssh. */ 349 static void 350 must_write(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 = write(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