1 /* $OpenBSD: monitor.c,v 1.12 2006/12/25 08:17:17 deraadt Exp $ */ 2 3 /* 4 * Copyright (c) 2005 H�kan Olsson. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/types.h> 30 #include <sys/ioctl.h> 31 #include <sys/socket.h> 32 #include <sys/stat.h> 33 #include <sys/sysctl.h> 34 #include <sys/wait.h> 35 #include <net/pfkeyv2.h> 36 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <pwd.h> 40 #include <signal.h> 41 #include <string.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 45 #include "monitor.h" 46 #include "sasyncd.h" 47 48 struct m_state { 49 pid_t pid; 50 int s; 51 } m_state; 52 53 volatile sig_atomic_t sigchld = 0; 54 55 static void got_sigchld(int); 56 static void sig_to_child(int); 57 static void m_priv_pfkey_snap(int); 58 static void m_priv_isakmpd_activate(void); 59 static void m_priv_isakmpd_passivate(void); 60 static ssize_t m_write(int, void *, size_t); 61 static ssize_t m_read(int, void *, size_t); 62 63 pid_t 64 monitor_init(void) 65 { 66 struct passwd *pw = getpwnam(SASYNCD_USER); 67 extern char *__progname; 68 char root[MAXPATHLEN]; 69 int p[2]; 70 71 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0) { 72 log_err("%s: socketpair failed - %s", __progname, 73 strerror(errno)); 74 exit(1); 75 } 76 77 if (!pw) { 78 log_err("%s: getpwnam(\"%s\") failed", __progname, 79 SASYNCD_USER); 80 exit(1); 81 } 82 strlcpy(root, pw->pw_dir, sizeof root); 83 endpwent(); 84 85 signal(SIGCHLD, got_sigchld); 86 signal(SIGTERM, sig_to_child); 87 signal(SIGHUP, sig_to_child); 88 signal(SIGINT, sig_to_child); 89 90 m_state.pid = fork(); 91 92 if (m_state.pid == -1) { 93 log_err("%s: fork failed - %s", __progname, strerror(errno)); 94 exit(1); 95 } else if (m_state.pid == 0) { 96 /* Child */ 97 m_state.s = p[0]; 98 close(p[1]); 99 100 if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) { 101 log_err("%s: chroot failed", __progname); 102 exit(1); 103 } 104 105 if (setgroups(1, &pw->pw_gid) || 106 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 107 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { 108 log_err("%s: failed to drop privileges", __progname); 109 exit(1); 110 } 111 } else { 112 /* Parent */ 113 setproctitle("[priv]"); 114 m_state.s = p[1]; 115 close(p[0]); 116 } 117 return m_state.pid; 118 } 119 120 static void 121 got_sigchld(int s) 122 { 123 sigchld = 1; 124 } 125 126 static void 127 sig_to_child(int s) 128 { 129 if (m_state.pid != -1) 130 kill(m_state.pid, s); 131 } 132 133 static void 134 monitor_drain_input(void) 135 { 136 int one = 1; 137 u_int8_t tmp; 138 139 ioctl(m_state.s, FIONBIO, &one); 140 while (m_read(m_state.s, &tmp, 1) > 0) 141 ; 142 ioctl(m_state.s, FIONBIO, 0); 143 } 144 145 /* We only use privsep to get in-kernel SADB and SPD snapshots via sysctl */ 146 void 147 monitor_loop(void) 148 { 149 u_int32_t v; 150 ssize_t r; 151 152 for (;;) { 153 if (sigchld) { 154 pid_t pid; 155 int status; 156 do { 157 pid = waitpid(m_state.pid, &status, WNOHANG); 158 } while (pid == -1 && errno == EINTR); 159 160 if (pid == m_state.pid && 161 (WIFEXITED(status) || WIFSIGNALED(status))) 162 break; 163 } 164 165 /* Wait for next task */ 166 if ((r = m_read(m_state.s, &v, sizeof v)) < 1) { 167 if (r == -1) 168 log_err(0, "monitor_loop: read() "); 169 break; 170 } 171 172 switch (v) { 173 case MONITOR_GETSNAP: 174 /* Get the data. */ 175 m_priv_pfkey_snap(m_state.s); 176 break; 177 case MONITOR_CARPINC: 178 carp_demote(CARP_INC, 1); 179 break; 180 case MONITOR_CARPDEC: 181 carp_demote(CARP_DEC, 1); 182 break; 183 case MONITOR_ISAKMPD_ACTIVATE: 184 m_priv_isakmpd_activate(); 185 break; 186 case MONITOR_ISAKMPD_PASSIVATE: 187 m_priv_isakmpd_passivate(); 188 break; 189 } 190 } 191 192 monitor_carpundemote(NULL); 193 194 if (!sigchld) 195 log_msg(0, "monitor_loop: priv process exiting abnormally"); 196 exit(0); 197 } 198 199 void 200 monitor_carpundemote(void *v) 201 { 202 u_int32_t mtype = MONITOR_CARPDEC; 203 if (!carp_demoted) 204 return; 205 if (m_write(m_state.s, &mtype, sizeof mtype) < 1) 206 log_msg(1, "monitor_carpundemote: unable to write to monitor"); 207 else 208 carp_demoted = 0; 209 } 210 211 void 212 monitor_carpdemote(void *v) 213 { 214 u_int32_t mtype = MONITOR_CARPINC; 215 if (carp_demoted) 216 return; 217 if (m_write(m_state.s, &mtype, sizeof mtype) < 1) 218 log_msg(1, "monitor_carpdemote: unable to write to monitor"); 219 else 220 carp_demoted = 1; 221 } 222 223 int 224 monitor_get_pfkey_snap(u_int8_t **sadb, u_int32_t *sadbsize, u_int8_t **spd, 225 u_int32_t *spdsize) 226 { 227 u_int32_t v; 228 ssize_t rbytes; 229 230 v = MONITOR_GETSNAP; 231 if (m_write(m_state.s, &v, sizeof v) < 1) 232 return -1; 233 234 /* Read SADB data. */ 235 *sadb = *spd = NULL; 236 *spdsize = 0; 237 if (m_read(m_state.s, sadbsize, sizeof *sadbsize) < 1) 238 return -1; 239 if (*sadbsize) { 240 *sadb = (u_int8_t *)malloc(*sadbsize); 241 if (!*sadb) { 242 log_err("monitor_get_pfkey_snap: malloc()"); 243 monitor_drain_input(); 244 return -1; 245 } 246 rbytes = m_read(m_state.s, *sadb, *sadbsize); 247 if (rbytes < 1) { 248 memset(*sadb, 0, *sadbsize); 249 free(*sadb); 250 return -1; 251 } 252 } 253 254 /* Read SPD data */ 255 if (m_read(m_state.s, spdsize, sizeof *spdsize) < 1) { 256 if (*sadbsize) { 257 memset(*sadb, 0, *sadbsize); 258 free(*sadb); 259 } 260 return -1; 261 } 262 if (*spdsize) { 263 *spd = (u_int8_t *)malloc(*spdsize); 264 if (!*spd) { 265 log_err("monitor_get_pfkey_snap: malloc()"); 266 monitor_drain_input(); 267 if (*sadbsize) { 268 memset(*sadb, 0, *sadbsize); 269 free(*sadb); 270 } 271 return -1; 272 } 273 rbytes = m_read(m_state.s, *spd, *spdsize); 274 if (rbytes < 1) { 275 memset(*spd, 0, *spdsize); 276 free(*spd); 277 if (*sadbsize) { 278 memset(*sadb, 0, *sadbsize); 279 free(*sadb); 280 } 281 return -1; 282 } 283 } 284 285 log_msg(3, "monitor_get_pfkey_snap: got %u bytes SADB, %u bytes SPD", 286 *sadbsize, *spdsize); 287 return 0; 288 } 289 290 int 291 monitor_isakmpd_active(int active) 292 { 293 u_int32_t cmd = 294 active ? MONITOR_ISAKMPD_ACTIVATE : MONITOR_ISAKMPD_PASSIVATE; 295 if (write(m_state.s, &cmd, sizeof cmd) < 1) 296 return -1; 297 return 0; 298 } 299 300 /* Privileged */ 301 static void 302 m_priv_pfkey_snap(int s) 303 { 304 u_int8_t *sadb_buf = 0, *spd_buf = 0; 305 size_t sadb_buflen = 0, spd_buflen = 0, sz; 306 int mib[5]; 307 u_int32_t v; 308 309 mib[0] = CTL_NET; 310 mib[1] = PF_KEY; 311 mib[2] = PF_KEY_V2; 312 mib[3] = NET_KEY_SADB_DUMP; 313 mib[4] = 0; /* Unspec SA type */ 314 315 /* First, fetch SADB data */ 316 if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 317 || sz == 0) { 318 sadb_buflen = 0; 319 goto try_spd; 320 } 321 322 sadb_buflen = sz; 323 if ((sadb_buf = malloc(sadb_buflen)) == NULL) { 324 log_err("m_priv_pfkey_snap: malloc"); 325 sadb_buflen = 0; 326 goto out; 327 } 328 329 if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, NULL, 0) 330 == -1) { 331 log_err("m_priv_pfkey_snap: sysctl"); 332 sadb_buflen = 0; 333 goto out; 334 } 335 336 /* Next, fetch SPD data */ 337 try_spd: 338 mib[3] = NET_KEY_SPD_DUMP; 339 340 if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 341 || sz == 0) { 342 spd_buflen = 0; 343 goto out; 344 } 345 346 spd_buflen = sz; 347 if ((spd_buf = malloc(spd_buflen)) == NULL) { 348 log_err("m_priv_pfkey_snap: malloc"); 349 spd_buflen = 0; 350 goto out; 351 } 352 353 if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, 0) 354 == -1) { 355 log_err("m_priv_pfkey_snap: sysctl"); 356 spd_buflen = 0; 357 goto out; 358 } 359 360 out: 361 /* Return SADB data */ 362 v = (u_int32_t)sadb_buflen; 363 if (m_write(s, &v, sizeof v) == -1) { 364 log_err("m_priv_pfkey_snap: write"); 365 return; 366 } 367 if (sadb_buflen) { 368 if (m_write(s, sadb_buf, sadb_buflen) == -1) 369 log_err("m_priv_pfkey_snap: write"); 370 memset(sadb_buf, 0, sadb_buflen); 371 free(sadb_buf); 372 } 373 374 /* Return SPD data */ 375 v = (u_int32_t)spd_buflen; 376 if (m_write(s, &v, sizeof v) == -1) { 377 log_err("m_priv_pfkey_snap: write"); 378 return; 379 } 380 if (spd_buflen) { 381 if (m_write(s, spd_buf, spd_buflen) == -1) 382 log_err("m_priv_pfkey_snap: write"); 383 memset(spd_buf, 0, spd_buflen); 384 free(spd_buf); 385 } 386 return; 387 } 388 389 static void 390 m_priv_isakmpd_fifocmd(const char *cmd) 391 { 392 struct stat sb; 393 int fd = -1; 394 395 if ((fd = open(ISAKMPD_FIFO, O_WRONLY)) == -1) { 396 log_err("m_priv_isakmpd_fifocmd: open(%s)", ISAKMPD_FIFO); 397 goto out; 398 } 399 if (fstat(fd, &sb) == -1) { 400 log_err("m_priv_isakmpd_fifocmd: fstat(%s)", ISAKMPD_FIFO); 401 goto out; 402 } 403 if (!S_ISFIFO(sb.st_mode)) { 404 log_err("m_priv_isakmpd_fifocmd: %s not a fifo", ISAKMPD_FIFO); 405 goto out; 406 } 407 408 if (write(fd, cmd, strlen(cmd)) == -1) { 409 log_err("m_priv_isakmpd_fifocmd write"); 410 goto out; 411 } 412 out: 413 if (fd != -1) 414 close(fd); 415 /* No values returned. */ 416 return; 417 } 418 419 static void 420 m_priv_isakmpd_activate(void) 421 { 422 m_priv_isakmpd_fifocmd("M active\n"); 423 } 424 425 static void 426 m_priv_isakmpd_passivate(void) 427 { 428 m_priv_isakmpd_fifocmd("M passive\n"); 429 } 430 431 ssize_t 432 m_write(int sock, void *buf, size_t len) 433 { 434 ssize_t n; 435 size_t pos = 0; 436 char *ptr = buf; 437 438 while (len > pos) { 439 switch (n = write(sock, ptr + pos, len - pos)) { 440 case -1: 441 if (errno == EINTR || errno == EAGAIN) 442 continue; 443 /* FALLTHROUGH */ 444 case 0: 445 return n; 446 /* NOTREACHED */ 447 default: 448 pos += n; 449 } 450 } 451 return pos; 452 } 453 454 ssize_t 455 m_read(int sock, void *buf, size_t len) 456 { 457 ssize_t n; 458 size_t pos = 0; 459 char *ptr = buf; 460 461 while (len > pos) { 462 switch (n = read(sock, ptr + pos, len - pos)) { 463 case -1: 464 if (errno == EINTR || errno == EAGAIN) 465 continue; 466 /* FALLTHROUGH */ 467 case 0: 468 return n; 469 /* NOTREACHED */ 470 default: 471 pos += n; 472 } 473 } 474 return pos; 475 } 476