/* $OpenBSD: monitor.c,v 1.1 2005/05/24 02:35:39 ho Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sasyncd.h" struct m_state { pid_t pid; int s; } m_state; volatile sig_atomic_t sigchld = 0; static void got_sigchld(int); static void sig_to_child(int); static void m_priv_pfkey_snap(int); pid_t monitor_init(void) { struct passwd *pw = getpwnam(SASYNCD_USER); extern char *__progname; char root[MAXPATHLEN]; int p[2]; if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) != 0) { log_err("%s: socketpair failed - %s", __progname, strerror(errno)); exit(1); } if (!pw) { log_err("%s: getpwnam(\"%s\") failed", __progname, SASYNCD_USER); exit(1); } strlcpy(root, pw->pw_dir, sizeof root); endpwent(); signal(SIGCHLD, got_sigchld); signal(SIGTERM, sig_to_child); signal(SIGHUP, sig_to_child); signal(SIGINT, sig_to_child); if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) { log_err("%s: chroot failed", __progname); exit(1); } m_state.pid = fork(); if (m_state.pid == -1) { log_err("%s: fork failed - %s", __progname, strerror(errno)); exit(1); } else if (m_state.pid == 0) { /* Child */ m_state.s = p[0]; close(p[1]); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) { log_err("%s: failed to drop privileges", __progname); exit(1); } } else { /* Parent */ setproctitle("[priv]"); m_state.s = p[1]; close(p[0]); } return m_state.pid; } static void got_sigchld(int s) { sigchld = 1; } static void sig_to_child(int s) { if (m_state.pid != -1) kill(m_state.pid, s); } /* We only use privsep to get in-kernel SADB and SPD snapshots via sysctl */ void monitor_loop(void) { extern volatile sig_atomic_t daemon_shutdown; pid_t pid; int status; u_int32_t v; while (!daemon_shutdown) { if (sigchld) { do { pid = waitpid(m_state.pid, &status, WNOHANG); } while (pid == -1 && errno == EINTR); if (pid == m_state.pid && (WIFEXITED(status) || WIFSIGNALED(status))) { daemon_shutdown++; break; } } /* Wait for next snapshot task. Disregard read data. */ if (read(m_state.s, &v, sizeof v) != (ssize_t)sizeof v) break; /* Get the data. */ m_priv_pfkey_snap(m_state.s); } exit(0); } int monitor_get_pfkey_snap(u_int8_t **sadb, u_int32_t *sadbsize, u_int8_t **spd, u_int32_t *spdsize) { int one = 1; u_int8_t tmp; u_int32_t rbytes; /* We write a (any) value to the monitor socket to start a snapshot. */ *sadbsize = 0; if (write(m_state.s, sadbsize, sizeof *sadbsize) < 1) return -1; /* Read SADB data. */ if (read(m_state.s, sadbsize, sizeof *sadbsize) < 1) return -1; *sadb = (u_int8_t *)malloc(*sadbsize); if (!*sadb) { log_err("monitor_get_pfkey_snap: malloc()"); /* Drain input */ ioctl(m_state.s, FIONBIO, &one); while (read(m_state.s, &tmp, 1) > 0); ioctl(m_state.s, FIONBIO, 0); return -1; } rbytes = read(m_state.s, *sadb, *sadbsize); if (rbytes != *sadbsize) { if (rbytes > 0) memset(*sadb, 0, rbytes); free(*sadb); return -1; } /* Read SPD data */ if (read(m_state.s, spdsize, sizeof *spdsize) < 1) { memset(*sadb, 0, *sadbsize); free(*sadb); return -1; } *spd = (u_int8_t *)malloc(*spdsize); if (!*spd) { log_err("monitor_get_pfkey_snap: malloc()"); /* Drain input */ ioctl(m_state.s, FIONBIO, &one); while (read(m_state.s, &tmp, 1) > 0); ioctl(m_state.s, FIONBIO, 0); memset(*sadb, 0, *sadbsize); free(*sadb); return -1; } rbytes = read(m_state.s, *spd, *spdsize); if (rbytes != *spdsize) { if (rbytes > 0) memset(*spd, 0, rbytes); memset(*sadb, 0, *sadbsize); free(*spd); free(*sadb); return -1; } log_msg(5, "monitor_get_pfkey_snap: got %d bytes SADB, %d bytes SPD", *sadbsize, *spdsize); return 0; } /* Privileged */ static void m_priv_pfkey_snap(int s) { u_int8_t *sadb_buf, *spd_buf; size_t sadb_buflen = 0, spd_buflen = 0, sz; int mib[5]; u_int32_t v; mib[0] = CTL_NET; mib[1] = PF_KEY; mib[2] = PF_KEY_V2; mib[3] = NET_KEY_SADB_DUMP; mib[4] = 0; /* Unspec SA type */ /* First, fetch SADB data */ if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 || sz == 0) { sadb_buflen = 0; goto try_spd; } sadb_buflen = sz; if ((sadb_buf = malloc(sadb_buflen)) == NULL) { log_err("m_priv_pfkey_snap: malloc"); sadb_buflen = 0; goto out; } if (sysctl(mib, sizeof mib / sizeof mib[0], sadb_buf, &sz, NULL, 0) == -1) { log_err("m_priv_pfkey_snap: sysctl"); sadb_buflen = 0; goto out; } /* Next, fetch SPD data */ try_spd: mib[3] = NET_KEY_SPD_DUMP; if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 || sz == 0) { spd_buflen = 0; goto out; } spd_buflen = sz; if ((spd_buf = malloc(spd_buflen)) == NULL) { log_err("m_priv_pfkey_snap: malloc"); spd_buflen = 0; goto out; } if (sysctl(mib, sizeof mib / sizeof mib[0], spd_buf, &sz, NULL, 0) == -1) { log_err("m_priv_pfkey_snap: sysctl"); spd_buflen = 0; goto out; } out: /* Return SADB data */ v = (u_int32_t)sadb_buflen; if (write(s, &v, sizeof v) == -1) { log_err("m_priv_pfkey_snap: write"); return; } if (sadb_buflen) if (write(s, sadb_buf, sadb_buflen) == -1) log_err("m_priv_pfkey_snap: write"); if (sadb_buf) { memset(sadb_buf, 0, sadb_buflen); free(sadb_buf); } /* Return SPD data */ v = (u_int32_t)spd_buflen; if (write(s, &v, sizeof v) == -1) { log_err("m_priv_pfkey_snap: write"); return; } if (spd_buflen) if (write(s, spd_buf, spd_buflen) == -1) log_err("m_priv_pfkey_snap: write"); if (spd_buf) { memset(spd_buf, 0, spd_buflen); free(spd_buf); } return; }