diff options
author | Hakan Olsson <ho@cvs.openbsd.org> | 2005-05-24 02:35:40 +0000 |
---|---|---|
committer | Hakan Olsson <ho@cvs.openbsd.org> | 2005-05-24 02:35:40 +0000 |
commit | 93175c1a0ddac825fcbbaefd254266e8480d00fb (patch) | |
tree | efbe66ccc2a853659a6300caf0bc0b715068dda6 | |
parent | 16cf272d570a15a6d09a6b724b312f101a26ff74 (diff) |
When peers connect, have the master daemon look at in-kernel SAs and feed
these to the new peer. Adds privsep as fetching SADB and SPD kernel data
requires privileges.
-rw-r--r-- | usr.sbin/sasyncd/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/sasyncd/monitor.c | 314 | ||||
-rw-r--r-- | usr.sbin/sasyncd/net.c | 11 | ||||
-rw-r--r-- | usr.sbin/sasyncd/pfkey.c | 118 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.c | 40 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.h | 9 |
6 files changed, 411 insertions, 85 deletions
diff --git a/usr.sbin/sasyncd/Makefile b/usr.sbin/sasyncd/Makefile index 3170f0f083f..33965457f1c 100644 --- a/usr.sbin/sasyncd/Makefile +++ b/usr.sbin/sasyncd/Makefile @@ -1,7 +1,7 @@ -# $Id: Makefile,v 1.4 2005/05/23 19:53:27 ho Exp $ +# $Id: Makefile,v 1.5 2005/05/24 02:35:39 ho Exp $ PROG= sasyncd -SRCS= sasyncd.c carp.c conf.y log.c net.c net_ctl.c pfkey.c timer.c +SRCS= sasyncd.c carp.c conf.y log.c monitor.c net.c net_ctl.c pfkey.c timer.c MAN= sasyncd.8 sasyncd.conf.5 .ifdef DEBUG diff --git a/usr.sbin/sasyncd/monitor.c b/usr.sbin/sasyncd/monitor.c new file mode 100644 index 00000000000..91baa0bea38 --- /dev/null +++ b/usr.sbin/sasyncd/monitor.c @@ -0,0 +1,314 @@ +/* $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 <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/wait.h> +#include <net/pfkeyv2.h> + +#include <errno.h> +#include <pwd.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#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; +} diff --git a/usr.sbin/sasyncd/net.c b/usr.sbin/sasyncd/net.c index c8ac1f5a8ad..71812e2735b 100644 --- a/usr.sbin/sasyncd/net.c +++ b/usr.sbin/sasyncd/net.c @@ -1,4 +1,4 @@ -/* $OpenBSD: net.c,v 1.4 2005/05/23 19:53:27 ho Exp $ */ +/* $OpenBSD: net.c,v 1.5 2005/05/24 02:35:39 ho Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. @@ -75,7 +75,7 @@ static int net_set_sa(struct sockaddr *, char *, in_port_t); static void net_check_peers(void *); /* Pretty-print a buffer. */ -static void +void dump_buf(int lvl, u_int8_t *b, u_int32_t len, char *title) { u_int32_t i, off, blen; @@ -386,6 +386,9 @@ net_handle_messages(fd_set *fds) p->socket = newsock; log_msg(1, "net: peer \"%s\" connected", p->name); + if (cfgstate.runstate == MASTER) + timer_add("pfkey_snapshot", 2, + pfkey_snapshot, p); } if (!found) { log_msg(1, "net: found no matching peer for " @@ -724,6 +727,10 @@ net_connect(void) } log_msg(1, "net_connect: peer \"%s\" connected, fd %d", p->name, p->socket); + + /* Schedule a pfkey sync to the newly connected peer. */ + if (cfgstate.runstate == MASTER) + timer_add("pfkey_snapshot", 2, pfkey_snapshot, p); } timerclear(&iv.it_value); diff --git a/usr.sbin/sasyncd/pfkey.c b/usr.sbin/sasyncd/pfkey.c index 0ed4993a99f..b5b82fcfb96 100644 --- a/usr.sbin/sasyncd/pfkey.c +++ b/usr.sbin/sasyncd/pfkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pfkey.c,v 1.3 2005/05/23 19:53:27 ho Exp $ */ +/* $OpenBSD: pfkey.c,v 1.4 2005/05/24 02:35:39 ho Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. @@ -37,6 +37,7 @@ #include <sys/queue.h> #include <sys/sysctl.h> #include <net/pfkeyv2.h> +#include <netinet/ip_ipsp.h> #include <errno.h> #include <stdio.h> @@ -64,7 +65,7 @@ static const char *msgtypes[] = { #define CHUNK sizeof(u_int64_t) -static const char *pfkey_print_type(struct sadb_msg *msg); +static const char *pfkey_print_type(struct sadb_msg *); static int pfkey_write(u_int8_t *buf, ssize_t len) @@ -103,10 +104,14 @@ pfkey_set_promisc(void) static const char * pfkey_print_type(struct sadb_msg *msg) { + static char uk[20]; + if (msg->sadb_msg_type < sizeof msgtypes / sizeof msgtypes[0]) return msgtypes[msg->sadb_msg_type]; - else - return "<unknown>"; + else { + snprintf(uk, sizeof uk, "<unknown(%d)>", msg->sadb_msg_type); + return uk; + } } static int @@ -118,9 +123,10 @@ pfkey_handle_message(struct sadb_msg *m) * Report errors, but ignore for DELETE (both isakmpd and kernel will * expire the SA, if the kernel is first, DELETE returns failure). */ - if (msg->sadb_msg_errno && msg->sadb_msg_type != SADB_DELETE) { + if (msg->sadb_msg_errno && msg->sadb_msg_type != SADB_DELETE && + msg->sadb_msg_pid == (u_int32_t)getpid()) { errno = msg->sadb_msg_errno; - log_err("pfkey error (%s)", pfkey_print_type(msg)); + log_msg(1, "pfkey error (%s)", pfkey_print_type(msg)); } /* We only want promiscuous messages here, skip all others. */ @@ -142,15 +148,12 @@ pfkey_handle_message(struct sadb_msg *m) return 0; } - log_msg(3, "pfkey_handle_message: got %s len %u seq %d", - pfkey_print_type(msg), msg->sadb_msg_len * CHUNK, - msg->sadb_msg_seq); - switch (msg->sadb_msg_type) { case SADB_X_PROMISC: case SADB_DUMP: case SADB_GET: case SADB_GETSPI: + /* case SADB_REGISTER: */ /* Some messages should not be synced. */ free(m); break; @@ -313,51 +316,68 @@ pfkey_shutdown(void) void pfkey_snapshot(void *v) { - struct sadb_msg *m; - struct sadb_ext *e; - u_int8_t *buf; - size_t sz, mlen, elen; - int mib[5]; - - 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 */ - - if (timer_add("pfkey_snapshot", 60, pfkey_snapshot, NULL)) - log_err("pfkey_snapshot: failed to renew event"); + struct syncpeer *p = (struct syncpeer *)v; + struct sadb_msg *m; + struct ipsec_policy *ip; + u_int8_t *sadb, *spd, *max, *next, *sendbuf; + u_int32_t sadbsz, spdsz; - if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 - || sz == 0) + if (!p) return; - if ((buf = malloc(sz)) == NULL) { - log_err("malloc"); - return; - } - if (sysctl(mib, sizeof mib / sizeof mib[0], buf, &sz, NULL, 0) == -1) { - log_err("sysctl"); + + if (monitor_get_pfkey_snap(&sadb, &sadbsz, &spd, &spdsz)) { + log_msg(0, "pfkey_snapshot: failed to get pfkey snapshot"); return; } - m = (struct sadb_msg *)buf; - while (m < (struct sadb_msg *)(buf + sz) && m->sadb_msg_len > 0) { - mlen = m->sadb_msg_len * CHUNK; - - fprintf(stderr, "pfkey_snapshot: sadb_msg %p type %s len %u\n", - m, pfkey_print_type(m), mlen); - - e = (struct sadb_ext *)(m + 1); - while ((u_int8_t *)e - (u_int8_t *)m < mlen && - e->sadb_ext_len > 0) { - elen = e->sadb_ext_len * CHUNK; - fprintf(stderr, "ext %p len %u\n", e, elen); - e = (struct sadb_ext *)((u_int8_t *)e + elen); + /* Parse SADB data */ + if (sadbsz) + dump_buf(5, sadb, sadbsz, "pfkey_snapshot: SADB data"); + + max = sadb + sadbsz; + for (next = sadb; next < max; next += m->sadb_msg_len * CHUNK) { + m = (struct sadb_msg *)next; + + fprintf(stderr, "pfkey_snapshot: SPD %p type %s len %u\n", + m, pfkey_print_type(m), m->sadb_msg_len * CHUNK); + + if (m->sadb_msg_len == 0) + break; + + /* Tweak and send */ + m->sadb_msg_type = SADB_ADD; + m->sadb_msg_errno = 0; + m->sadb_msg_reserved = 0; + + /* Allocate a buffer for the msg, net_queue() will free it. */ + sendbuf = (u_int8_t *)malloc(m->sadb_msg_len * CHUNK); + if (sendbuf) { + memcpy(sendbuf, m, m->sadb_msg_len * CHUNK); + net_queue(p, MSG_PFKEYDATA, sendbuf, + m->sadb_msg_len * CHUNK); } - /* ... */ - m = (struct sadb_msg *)((u_int8_t *)m + mlen); } - memset(buf, 0, sz); - free(buf); + + /* Parse SPD data */ + if (spdsz) + dump_buf(5, spd, spdsz, "pfkey_snapshot: SPD data"); + + max = spd + spdsz; + for (next = spd; next < max; next += sizeof(struct ipsec_policy)) { + ip = (struct ipsec_policy *)next; + + fprintf(stderr, "pfkey_snapshot: SPD %p (%d)\n",ip, + sizeof(struct ipsec_policy)); + + if (ip->ipo_flags & IPSP_POLICY_SOCKET) + continue; + } + + /* Cleanup. */ + memset(sadb, 0, sadbsz); + free(sadb); + memset(spd, 0, spdsz); + free(spd); + log_msg(0, "pfkey_snapshot: done"); return; } diff --git a/usr.sbin/sasyncd/sasyncd.c b/usr.sbin/sasyncd/sasyncd.c index c9923279737..0a76273b1b6 100644 --- a/usr.sbin/sasyncd/sasyncd.c +++ b/usr.sbin/sasyncd/sasyncd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sasyncd.c,v 1.7 2005/05/22 20:35:48 ho Exp $ */ +/* $OpenBSD: sasyncd.c,v 1.8 2005/05/24 02:35:39 ho Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. @@ -43,32 +43,7 @@ #include "sasyncd.h" -static void -privdrop(void) -{ - struct passwd *pw = getpwnam(SASYNCD_USER); - extern char *__progname; - - if (!pw) { - log_err("%s: getpwnam(\"%s\") failed", __progname, - SASYNCD_USER); - exit(1); - } - - if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) { - log_err("%s: chroot failed", __progname); - exit(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); - } -} - -volatile int daemon_shutdown = 0; +volatile sig_atomic_t daemon_shutdown = 0; /* Called by signal handler for controlled daemon shutdown. */ static void @@ -180,16 +155,19 @@ main(int argc, char **argv) if (net_init()) return 1; - /* Drop privileges. */ - privdrop(); - if (!cfgstate.debug) if (daemon(1, 0)) { perror("daemon()"); exit(1); } - /* Main loop. */ + if (monitor_init()) { + /* Parent, with privileges. */ + monitor_loop(); + exit(0); + } + + /* Child, no privileges left. Run main loop. */ sasyncd_run(); /* Shutdown. */ diff --git a/usr.sbin/sasyncd/sasyncd.h b/usr.sbin/sasyncd/sasyncd.h index 3eb107e33c8..b5f21f01d1f 100644 --- a/usr.sbin/sasyncd/sasyncd.h +++ b/usr.sbin/sasyncd/sasyncd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sasyncd.h,v 1.4 2005/05/22 20:35:48 ho Exp $ */ +/* $OpenBSD: sasyncd.h,v 1.5 2005/05/24 02:35:39 ho Exp $ */ /* * Copyright (c) 2005 Håkan Olsson. All rights reserved. @@ -91,7 +91,14 @@ void log_init(char *); void log_msg(int, const char *, ...); void log_err(const char *, ...); +/* monitor.c */ +pid_t monitor_init(void); +void monitor_loop(void); +int monitor_get_pfkey_snap(u_int8_t **, u_int32_t *, u_int8_t **, + u_int32_t *); + /* net.c */ +void dump_buf(int, u_int8_t *, u_int32_t, char *); void net_ctl_update_state(void); int net_init(void); void net_handle_messages(fd_set *); |