diff options
author | Hakan Olsson <ho@cvs.openbsd.org> | 2005-03-30 18:44:50 +0000 |
---|---|---|
committer | Hakan Olsson <ho@cvs.openbsd.org> | 2005-03-30 18:44:50 +0000 |
commit | 1e42720c7d8b17ab1489a1fac2f86ba12229ccf1 (patch) | |
tree | a6dc00bfa0b4344cecdbd733263eee02a88b53ec /usr.sbin/sasyncd | |
parent | c2c1ef90c70e5f9accfa8a65b07648fae651be03 (diff) |
Move sasyncd(8), for IPsec SA synchronization, in-tree. Work in progress.
deraadt@ ok.
Diffstat (limited to 'usr.sbin/sasyncd')
-rw-r--r-- | usr.sbin/sasyncd/Makefile | 19 | ||||
-rw-r--r-- | usr.sbin/sasyncd/carp.c | 176 | ||||
-rw-r--r-- | usr.sbin/sasyncd/conf.c | 275 | ||||
-rw-r--r-- | usr.sbin/sasyncd/log.c | 98 | ||||
-rw-r--r-- | usr.sbin/sasyncd/net.c | 529 | ||||
-rw-r--r-- | usr.sbin/sasyncd/net.h | 69 | ||||
-rw-r--r-- | usr.sbin/sasyncd/net_ctl.c | 197 | ||||
-rw-r--r-- | usr.sbin/sasyncd/net_ssl.c | 239 | ||||
-rw-r--r-- | usr.sbin/sasyncd/pfkey.c | 359 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.8 | 124 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.c | 208 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.conf.5 | 104 | ||||
-rw-r--r-- | usr.sbin/sasyncd/sasyncd.h | 142 | ||||
-rw-r--r-- | usr.sbin/sasyncd/timer.c | 143 |
14 files changed, 2682 insertions, 0 deletions
diff --git a/usr.sbin/sasyncd/Makefile b/usr.sbin/sasyncd/Makefile new file mode 100644 index 00000000000..617d4c14740 --- /dev/null +++ b/usr.sbin/sasyncd/Makefile @@ -0,0 +1,19 @@ +# $Id: Makefile,v 1.1 2005/03/30 18:44:49 ho Exp $ + +PROG= sasyncd +SRCS= sasyncd.c carp.c conf.c log.c net.c net_ctl.c net_ssl.c \ + pfkey.c timer.c +MAN= sasyncd.8 sasyncd.conf.5 + +# ElectricFence +#LDADD+= -L/usr/local/lib -lefence + +# Boehms GC +#LDADD+= -L/usr/local/lib -lgc +#CFLAGS+= -DGC_DEBUG + +CFLAGS+= -Wall -Wstrict-prototypes -Wmissing-prototypes \ + -Wmissing-declarations +LDADD+= -lcrypto -lssl + +.include <bsd.prog.mk> diff --git a/usr.sbin/sasyncd/carp.c b/usr.sbin/sasyncd/carp.c new file mode 100644 index 00000000000..06391e52499 --- /dev/null +++ b/usr.sbin/sasyncd/carp.c @@ -0,0 +1,176 @@ +/* $OpenBSD: carp.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/ip_carp.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "sasyncd.h" + +/* For some reason, ip_carp.h does not define this. */ +#define CARP_INIT 0 +#define CARP_BACKUP 1 +#define CARP_MASTER 2 + +/* Returns 1 for the CARP MASTER, 0 for BACKUP/INIT, -1 on error. */ +static enum RUNSTATE +carp_get_state(char *ifname) +{ + struct ifreq ifr; + struct carpreq carp; + int s, saved_errno; + char *state; + + if (!ifname || !*ifname) { + errno = ENOENT; + return FAIL; + } + + memset(&ifr, 0, sizeof ifr); + strlcpy(ifr.ifr_name, ifname, sizeof ifr.ifr_name); + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return FAIL; + + ifr.ifr_data = (caddr_t)&carp; + if (ioctl(s, SIOCGVH, (caddr_t)&ifr) == -1) { + saved_errno = errno; + close(s); + errno = saved_errno; + return FAIL; + } + close(s); + + switch (carp.carpr_state) { + case CARP_INIT: + state = "INIT"; + break; + + case CARP_BACKUP: + state = "BACKUP"; + break; + + case CARP_MASTER: + state = "MASTER"; + break; + + default: + state = "<unknown>"; + break; + } + + log_msg(4, "carp_get_state: %s vhid %u state %s(%d)", ifname, + carp.carpr_vhid, state, carp.carpr_state); + + if (carp.carpr_vhid > 0) + return carp.carpr_state == CARP_MASTER ? MASTER : SLAVE; + else + return FAIL; +} + +void +carp_check_state(void) +{ + enum RUNSTATE current_state = carp_get_state(cfgstate.carp_ifname); + static char *carpstate[] = CARPSTATES; + + if (current_state < 0 || current_state > FAIL) { + log_err("carp_state_tracker: invalid result on interface " + "%s, abort", cfgstate.carp_ifname); + cfgstate.runstate = FAIL; + return; + } + + if (current_state != cfgstate.runstate) { + log_msg(1, "carp_state_tracker: switching state to %s", + carpstate[current_state]); + cfgstate.runstate = current_state; + if (current_state == MASTER) + pfkey_set_promisc(); + net_ctl_update_state(); + } +} + +static void +carp_state_tracker(void *v_arg) +{ + static int failures = 0; + u_int32_t next_check; + + carp_check_state(); + + if (cfgstate.runstate == FAIL) + if (++failures < 3) + log_err("carp_state_tracker"); + + if (failures > 5) + next_check = 600; + else + next_check = cfgstate.carp_check_interval + failures * 10; + + if (timer_add("carp_state_tracker", next_check, carp_state_tracker, + NULL)) + log_msg(0, "carp_state_tracker: failed to renew event"); + return; +} + +/* Initialize the CARP state tracker. */ +int +carp_init(void) +{ + enum RUNSTATE initial_state; + + if (cfgstate.lockedstate != INIT) { + cfgstate.runstate = cfgstate.lockedstate; + log_msg(1, "carp_init: locking runstate to %s", + cfgstate.runstate == MASTER ? "MASTER" : "SLAVE"); + return 0; + } + + initial_state = carp_get_state(cfgstate.carp_ifname); + if (initial_state == FAIL) { + fprintf(stderr, "Failed to check interface \"%s\".\n", + cfgstate.carp_ifname); + fprintf(stderr, "Correct or manually select runstate.\n"); + return -1; + } + + return timer_add("carp_state_tracker", 0, carp_state_tracker, NULL); +} diff --git a/usr.sbin/sasyncd/conf.c b/usr.sbin/sasyncd/conf.c new file mode 100644 index 00000000000..80a7a8c8723 --- /dev/null +++ b/usr.sbin/sasyncd/conf.c @@ -0,0 +1,275 @@ +/* $OpenBSD: conf.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sasyncd.h" +#include "net.h" + +/* Global configuration context. */ +struct cfgstate cfgstate; + +static int +conf_parse_file(char *cfgfile) +{ + struct syncpeer *peer; + FILE *fp = fopen(cfgfile, "r"); + int lineno = 0, dup; + char linebuf[1024], name[1024], *p; + + if (!fp) { + log_err("failed to open \"%s\"", cfgfile); + return 1; + } + + while(fgets(linebuf, sizeof linebuf, fp)) { + lineno++; + + /* Strip comments and remove \n. */ + for (p = linebuf; *p; p++) + if (*p == '\n' || *p == '#') { + *p = 0; + break; + } + + if (*linebuf == 0) + continue; + + if (sscanf(linebuf, "listen on %1023s", name) == 1) { + if (cfgstate.listen_on) + free(cfgstate.listen_on); + cfgstate.listen_on = strdup(name); + if (!cfgstate.listen_on) { + log_err("config: strdup() failed"); + return 1; + } + log_msg(2, "config(line %02d): listen on %s", lineno, + cfgstate.listen_on); + continue; + } + + if (sscanf(linebuf, "listen port %1023s", name) == 1) { + cfgstate.listen_port = atoi(name); + if (cfgstate.listen_port < 1 || + cfgstate.listen_port > 65534) { + cfgstate.listen_port = SASYNCD_DEFAULT_PORT; + log_msg(0, "config: bad value line %d, " + "listen-port reset to %u", lineno, + SASYNCD_DEFAULT_PORT); + } else + log_msg(2, "config(line %02d): listen port %u", + cfgstate.listen_port); + continue; + } + + if (sscanf(linebuf, "peer %1023s", name) == 1) { + dup = 0; + for (peer = LIST_FIRST(&cfgstate.peerlist); peer; + peer = LIST_NEXT(peer, link)) + if (strcmp(name, peer->name) == 0) { + dup++; + break; + } + if (dup) + continue; + peer = (struct syncpeer *)calloc(1, sizeof *peer); + if (!peer) { + log_err("config: calloc(1, %lu) failed", + sizeof *peer); + return 1; + } + peer->name = strdup(name); + if (!peer->name) { + log_err("config: strdup() failed"); + return 1; + } + LIST_INSERT_HEAD(&cfgstate.peerlist, peer, link); + log_msg(2, "config(line %02d): add peer %s", lineno, + peer->name); + continue; + } + + if (sscanf(linebuf, "carp interface %1023s", name) == 1) { + if (cfgstate.carp_ifname) + free(cfgstate.carp_ifname); + cfgstate.carp_ifname = strdup(name); + if (!cfgstate.carp_ifname) { + log_err("config: strdup failed"); + return 1; + } + log_msg(2, "config(line %02d): carp interface %s", + lineno, cfgstate.carp_ifname); + continue; + } + + if (sscanf(linebuf, "carp interval %1023s", name) == 1) { + cfgstate.carp_check_interval = atoi(name); + if (cfgstate.carp_check_interval < 1) { + cfgstate.carp_check_interval = + CARP_DEFAULT_INTERVAL; + log_msg(0, "config: bad value line %d, " + "carp-interval reset to %d", lineno, + CARP_DEFAULT_INTERVAL); + } else + log_msg(2, "config(line %02d): carp interval " + "%d", lineno, + cfgstate.carp_check_interval); + continue; + } + + if (sscanf(linebuf, "CAcertificate file %1023s", name) == 1) { + if (cfgstate.cafile) + free(cfgstate.cafile); + cfgstate.cafile = strdup(name); + if (!cfgstate.cafile) { + log_err("config: strdup failed"); + return 1; + } + log_msg(2, "config(line %02d): CAcertificate file $s", + lineno, cfgstate.cafile); + continue; + } + + if (sscanf(linebuf, "certificate file %1023s", name) == 1) { + if (cfgstate.certfile) + free(cfgstate.certfile); + cfgstate.certfile = strdup(name); + if (!cfgstate.certfile) { + log_err("config: strdup failed"); + return 1; + } + log_msg(2, "config(line %02d): certificate file $s", + lineno, cfgstate.certfile); + continue; + } + + if (sscanf(linebuf, "private key file %1023s", name) == 1) { + if (cfgstate.privkeyfile) + free(cfgstate.privkeyfile); + cfgstate.privkeyfile = strdup(name); + if (!cfgstate.privkeyfile) { + log_err("config: strdup failed"); + return 1; + } + log_msg(2, "config(line %02d): private key file $s", + lineno, cfgstate.privkeyfile); + continue; + } + + if (sscanf(linebuf, "run as %1023s", name) == 1) { + if (strcasecmp(name, "SLAVE") == 0) + cfgstate.lockedstate = SLAVE; + else if (strcasecmp(name, "MASTER") == 0) + cfgstate.lockedstate = MASTER; + else { + log_msg(0, "config(line %02d): unknown state " + "%s", lineno, name); + return 1; + } + log_msg(2, "config(line %02d): runstate locked to %s", + lineno, cfgstate.lockedstate == MASTER ? "MASTER" : + "SLAVE"); + continue; + } + } + + /* Sanity checks. */ + if (LIST_EMPTY(&cfgstate.peerlist)) { + log_msg(0, "config: no peers defined"); + return 1; + } + + if (!cfgstate.carp_ifname && cfgstate.lockedstate == INIT) { + log_msg(0, "config: no carp interface or runstate defined"); + return 1; + } + + /* Success. */ + return 0; +} + +int +conf_init(int argc, char **argv) +{ + char *cfgfile = 0; + int ch; + + memset(&cfgstate, 0, sizeof cfgstate); + cfgstate.runstate = INIT; + LIST_INIT(&cfgstate.peerlist); + + cfgstate.carp_check_interval = CARP_DEFAULT_INTERVAL; + cfgstate.listen_port = SASYNCD_DEFAULT_PORT; + + while ((ch = getopt(argc, argv, "c:dv")) != -1) { + switch (ch) { + case 'c': + if (cfgfile) + return 2; + cfgfile = optarg; + break; + case 'd': + cfgstate.debug++; + break; + case 'v': + cfgstate.verboselevel++; + break; + default: + return 2; + } + } + argc -= optind; + argv += optind; + + if (argc > 0) + return 2; + + if (!cfgfile) + cfgfile = SASYNCD_CFGFILE; + + if (conf_parse_file(cfgfile) == 0) { + if (!cfgstate.certfile) + cfgstate.certfile = SASYNCD_CERTFILE; + if (!cfgstate.privkeyfile) + cfgstate.privkeyfile = SASYNCD_PRIVKEY; + if (!cfgstate.cafile) + cfgstate.cafile = SASYNCD_CAFILE; + return 0; + } else + return 1; +} diff --git a/usr.sbin/sasyncd/log.c b/usr.sbin/sasyncd/log.c new file mode 100644 index 00000000000..239f764b008 --- /dev/null +++ b/usr.sbin/sasyncd/log.c @@ -0,0 +1,98 @@ +/* $OpenBSD: log.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <syslog.h> +#include <errno.h> +#include <time.h> + +#include "sasyncd.h" + +static char logbuf[2048]; + +void +log_init(char *pname) +{ + tzset(); + openlog(pname, LOG_CONS, LOG_DAEMON); +} + +static void +log_output(char *msg) +{ + if (cfgstate.debug) + fprintf(stderr, "%s\n", msg); + else + syslog(LOG_CRIT, msg); +} + +void +log_err(const char *fmt, ...) +{ + va_list ap; + int off = 0; + + if (cfgstate.debug) { + snprintf(logbuf, sizeof logbuf, "%s: ", __progname); + off = strlen(logbuf); + } + + va_start(ap, fmt); + (void)vsnprintf(logbuf + off, sizeof logbuf - off, fmt, ap); + va_end(ap); + + strlcat(logbuf, ": ", sizeof logbuf); + strlcat(logbuf, strerror (errno), sizeof logbuf); + + log_output(logbuf); + return; +} + +void +log_msg (int minlevel, const char *fmt, ...) +{ + va_list ap; + + if (cfgstate.verboselevel < minlevel) + return; + + va_start(ap, fmt); + (void)vsnprintf(logbuf, sizeof logbuf, fmt, ap); + va_end(ap); + + log_output(logbuf); + return; +} diff --git a/usr.sbin/sasyncd/net.c b/usr.sbin/sasyncd/net.c new file mode 100644 index 00000000000..8cb3dddc0e7 --- /dev/null +++ b/usr.sbin/sasyncd/net.c @@ -0,0 +1,529 @@ +/* $OpenBSD: net.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "sasyncd.h" +#include "net.h" + +struct msg { + u_int8_t *buf; + u_int8_t *obuf; /* Original buf w/o offset. */ + u_int32_t len; + u_int32_t type; + int refcnt; +}; + +struct qmsg { + SIMPLEQ_ENTRY(qmsg) next; + struct msg *msg; +}; + +int listen_socket; + +/* Local prototypes. */ +static u_int8_t *net_read(struct syncpeer *, u_int32_t *, u_int32_t *); +static int net_set_sa(struct sockaddr *, char *, in_port_t); +static void net_check_peers(void *); + +int +net_init(void) +{ + struct sockaddr_storage sa_storage; + struct sockaddr *sa = (struct sockaddr *)&sa_storage; + struct syncpeer *p; + int r; + + if (net_SSL_init()) + return -1; + + /* Setup listening socket. */ + memset(&sa_storage, 0, sizeof sa_storage); + if (net_set_sa(sa, cfgstate.listen_on, cfgstate.listen_port)) { + perror("inet_pton"); + return -1; + } + listen_socket = socket(sa->sa_family, SOCK_STREAM, 0); + if (listen_socket < 0) { + perror("socket()"); + close(listen_socket); + return -1; + } + r = 1; + if (setsockopt(listen_socket, SOL_SOCKET, + cfgstate.listen_on ? SO_REUSEADDR : SO_REUSEPORT, (void *)&r, + sizeof r)) { + perror("setsockopt()"); + close(listen_socket); + return -1; + } + if (bind(listen_socket, sa, sizeof(struct sockaddr_in))) { + perror("bind()"); + close(listen_socket); + return -1; + } + if (listen(listen_socket, 10)) { + perror("listen()"); + close(listen_socket); + return -1; + } + log_msg(2, "listening on port %u fd %d", cfgstate.listen_port, + listen_socket); + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + p->socket = -1; + SIMPLEQ_INIT(&p->msgs); + } + + net_check_peers(0); + return 0; +} + +static void +net_enqueue(struct syncpeer *p, struct msg *m) +{ + struct qmsg *qm; + + if (p->socket < 0) + return; + + if (!p->ssl) + if (net_SSL_connect(p)) + return; + + qm = (struct qmsg *)malloc(sizeof *qm); + if (!qm) { + log_err("malloc()"); + return; + } + + memset(qm, 0, sizeof *qm); + qm->msg = m; + m->refcnt++; + + SIMPLEQ_INSERT_TAIL(&p->msgs, qm, next); + return; +} + +/* + * Queue a message for transmission to a particular peer, + * or to all peers if no peer is specified. + */ +int +net_queue(struct syncpeer *p0, u_int32_t msgtype, u_int8_t *buf, + u_int32_t offset, u_int32_t len) +{ + struct syncpeer *p = p0; + struct msg *m; + + m = (struct msg *)malloc(sizeof *m); + if (!m) { + log_err("malloc()"); + free(buf); + return -1; + } + memset(m, 0, sizeof *m); + m->obuf = buf; + m->buf = buf + offset; + m->len = len; + m->type = msgtype; + + if (p) + net_enqueue(p, m); + else + for (p = LIST_FIRST(&cfgstate.peerlist); p; + p = LIST_NEXT(p, link)) + net_enqueue(p, m); + + if (!m->refcnt) { + free(m->obuf); + free(m); + } + + return 0; +} + +/* Set all write pending filedescriptors. */ +int +net_set_pending_wfds(fd_set *fds) +{ + struct syncpeer *p; + int max_fd = -1; + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) + if (p->socket > -1 && SIMPLEQ_FIRST(&p->msgs)) { + FD_SET(p->socket, fds); + if (p->socket > max_fd) + max_fd = p->socket; + } + return max_fd + 1; +} + +/* + * Set readable filedescriptors. They are basically the same as for write, + * plus the listening socket. + */ +int +net_set_rfds(fd_set *fds) +{ + struct syncpeer *p; + int max_fd = -1; + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + if (p->socket > -1) + FD_SET(p->socket, fds); + if (p->socket > max_fd) + max_fd = p->socket; + } + FD_SET(listen_socket, fds); + if (listen_socket > max_fd) + max_fd = listen_socket; + return max_fd + 1; +} + +void +net_handle_messages(fd_set *fds) +{ + struct sockaddr_storage sa_storage, sa_storage2; + struct sockaddr *sa = (struct sockaddr *)&sa_storage; + struct sockaddr *sa2 = (struct sockaddr *)&sa_storage2; + socklen_t socklen; + struct syncpeer *p; + u_int8_t *msg; + u_int32_t msgtype, msglen; + int newsock, found; + + if (FD_ISSET(listen_socket, fds)) { + /* Accept a new incoming connection */ + socklen = sizeof sa_storage; + newsock = accept(listen_socket, sa, &socklen); + if (newsock > -1) { + /* Setup the syncpeer structure */ + found = 0; + for (p = LIST_FIRST(&cfgstate.peerlist); p && !found; + p = LIST_NEXT(p, link)) { + struct sockaddr_in *sin, *sin2; + struct sockaddr_in6 *sin6, *sin62; + + /* Match? */ + if (net_set_sa(sa2, p->name, 0)) + continue; + if (sa->sa_family != sa2->sa_family) + continue; + if (sa->sa_family == AF_INET) { + sin = (struct sockaddr_in *)sa; + sin2 = (struct sockaddr_in *)sa2; + if (memcmp(&sin->sin_addr, + &sin2->sin_addr, + sizeof(struct in_addr))) + continue; + } else { + sin6 = (struct sockaddr_in6 *)sa; + sin62 = (struct sockaddr_in6 *)sa2; + if (memcmp(&sin6->sin6_addr, + &sin62->sin6_addr, + sizeof(struct in6_addr))) + continue; + } + /* Match! */ + found++; + p->socket = newsock; + p->ssl = NULL; + log_msg(1, "peer \"%s\" connected", p->name); + } + if (!found) { + log_msg(1, "Found no matching peer for " + "accepted socket, closing."); + close(newsock); + } + } else + log_err("accept()"); + } + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + if (p->socket < 0 || !FD_ISSET(p->socket, fds)) + continue; + msg = net_read(p, &msgtype, &msglen); + if (!msg) + continue; + + /* XXX check message validity. */ + + log_msg(4, "net_handle_messages: got msg type %u len %u from " + "peer %s", msgtype, msglen, p->name); + + switch (msgtype) { + case MSG_SYNCCTL: + net_ctl_handle_msg(p, msg, msglen); + free(msg); + break; + + case MSG_PFKEYDATA: + if (p->runstate != MASTER || + cfgstate.runstate == MASTER) { + log_msg(0, "got PFKEY message from non-MASTER " + "peer"); + free(msg); + if (cfgstate.runstate == MASTER) + net_ctl_send_state(p); + else + net_ctl_send_error(p, 0); + } else if (pfkey_queue_message(msg, msglen)) + free(msg); + break; + + default: + log_msg(0, "Got unknown message type %u len %u from " + "peer %s", msgtype, msglen, p->name); + free(msg); + net_ctl_send_error(p, 0); + } + } +} + +void +net_send_messages(fd_set *fds) +{ + struct syncpeer *p; + struct qmsg *qm; + struct msg *m; + u_int32_t v; + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + if (p->socket < 0 || !FD_ISSET(p->socket, fds)) + continue; + + qm = SIMPLEQ_FIRST(&p->msgs); + if (!qm) { + /* XXX Log */ + continue; + } + m = qm->msg; + + log_msg(4, "sending msg %p (qm %p ref %d) to peer %s", m, qm, + m->refcnt, p->name); + + /* Send the message. */ + v = htonl(m->type); + if (net_SSL_write(p, &v, sizeof v)) + continue; + + v = htonl(m->len); + if (net_SSL_write(p, &v, sizeof v)) + continue; + + (void)net_SSL_write(p, m->buf, m->len); + + /* Cleanup. */ + SIMPLEQ_REMOVE_HEAD(&p->msgs, next); + free(qm); + + if (--m->refcnt < 1) { + log_msg(4, "freeing msg %p", m); + free(m->obuf); + free(m); + } + } + return; +} + +void +net_disconnect_peer(struct syncpeer *p) +{ + net_SSL_disconnect(p); + if (p->socket > -1) + close(p->socket); + p->socket = -1; +} + +void +net_shutdown(void) +{ + struct syncpeer *p; + struct qmsg *qm; + struct msg *m; + + while ((p = LIST_FIRST(&cfgstate.peerlist))) { + while ((qm = SIMPLEQ_FIRST(&p->msgs))) { + SIMPLEQ_REMOVE_HEAD(&p->msgs, next); + m = qm->msg; + if (--m->refcnt < 1) { + free(m->obuf); + free(m); + } + free(qm); + } + net_disconnect_peer(p); + if (p->name) + free(p->name); + LIST_REMOVE(p, link); + free(p); + } + + if (listen_socket > -1) + close(listen_socket); + net_SSL_shutdown(); +} + +/* + * Helper functions (local) below here. + */ + +static u_int8_t * +net_read(struct syncpeer *p, u_int32_t *msgtype, u_int32_t *msglen) +{ + u_int8_t *msg; + u_int32_t v; + + if (net_SSL_read(p, &v, sizeof v)) + return NULL; + *msgtype = ntohl(v); + + if (*msgtype > MSG_MAXTYPE) + return NULL; + + if (net_SSL_read(p, &v, sizeof v)) + return NULL; + *msglen = ntohl(v); + + /* XXX msglen sanity */ + + msg = (u_int8_t *)malloc(*msglen); + memset(msg, 0, *msglen); + if (net_SSL_read(p, msg, *msglen)) { + free(msg); + return NULL; + } + + return msg; +} + +static int +net_set_sa(struct sockaddr *sa, char *name, in_port_t port) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sa; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + if (name) { + if (inet_pton(AF_INET, name, &sin->sin_addr) == 1) { + sa->sa_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_len = sizeof *sin; + return 0; + } + + if (inet_pton(AF_INET6, name, &sin6->sin6_addr) == 1) { + sa->sa_family = AF_INET6; + sin6->sin6_port = htons(port); + sin6->sin6_len = sizeof *sin6; + return 0; + } + } else { + /* XXX Assume IPv4 */ + sa->sa_family = AF_INET; + sin->sin_port = htons(port); + sin->sin_len = sizeof *sin; + return 0; + } + + return 1; +} + +static void +got_sigalrm(int s) +{ + return; +} + +void +net_connect_peers(void) +{ + struct sockaddr_storage sa_storage; + struct itimerval iv; + struct sockaddr *sa = (struct sockaddr *)&sa_storage; + struct syncpeer *p; + + signal(SIGALRM, got_sigalrm); + memset(&iv, 0, sizeof iv); + iv.it_value.tv_sec = 5; + iv.it_interval.tv_sec = 5; + setitimer(ITIMER_REAL, &iv, NULL); + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + if (p->ssl || p->socket > -1) + continue; + + memset(sa, 0, sizeof sa_storage); + if (net_set_sa(sa, p->name, cfgstate.listen_port)) + continue; + p->socket = socket(sa->sa_family, SOCK_STREAM, 0); + if (p->socket < 0) { + log_err("peer \"%s\": socket()", p->name); + continue; + } + if (connect(p->socket, sa, sa->sa_len)) { + log_msg(1, "peer \"%s\" not ready yet", p->name); + net_disconnect_peer(p); + continue; + } + if (net_ctl_send_state(p)) { + log_msg(0, "peer \"%s\" failed", p->name); + net_disconnect_peer(p); + continue; + } + log_msg(1, "peer \"%s\" connected", p->name); + } + + timerclear(&iv.it_value); + timerclear(&iv.it_interval); + setitimer(ITIMER_REAL, &iv, NULL); + signal(SIGALRM, SIG_IGN); + + return; +} + +static void +net_check_peers(void *arg) +{ + net_connect_peers(); + + (void)timer_add("peer recheck", 600, net_check_peers, 0); +} + diff --git a/usr.sbin/sasyncd/net.h b/usr.sbin/sasyncd/net.h new file mode 100644 index 00000000000..b3a5569a187 --- /dev/null +++ b/usr.sbin/sasyncd/net.h @@ -0,0 +1,69 @@ +/* $OpenBSD: net.h,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <openssl/ssl.h> + +struct qmsg; +struct syncpeer { + LIST_ENTRY(syncpeer) link; + + char *name; /* FQDN or an IP, from conf */ + SSL *ssl; + int socket; + enum RUNSTATE runstate; + + SIMPLEQ_HEAD(, qmsg) msgs; +}; + +/* Control message types. */ +enum CTLTYPE { RESERVED = 0, CTL_STATE, CTL_ERROR, CTL_ACK, CTL_UNKNOWN }; +#define CTLTYPES { \ + "RESERVED", "CTL_STATE", "CTL_ERROR", "CTL_ACK", "CTL_UNKNOWN" \ +}; + +/* net.c */ +void net_connect_peers(void); +void net_disconnect_peer(struct syncpeer *); + +/* net_ctl.c */ +void net_ctl_handle_msg(struct syncpeer *, u_int8_t *, u_int32_t); +int net_ctl_send_ack(struct syncpeer *, enum CTLTYPE, u_int32_t); +int net_ctl_send_error(struct syncpeer *, enum CTLTYPE); +int net_ctl_send_state(struct syncpeer *); + +/* net_ssl.c */ +int net_SSL_init(void); +int net_SSL_connect(struct syncpeer *); +void net_SSL_disconnect(struct syncpeer *); +int net_SSL_read(struct syncpeer *, void *, u_int32_t); +int net_SSL_write(struct syncpeer *, void *, u_int32_t); +void net_SSL_shutdown(void); diff --git a/usr.sbin/sasyncd/net_ctl.c b/usr.sbin/sasyncd/net_ctl.c new file mode 100644 index 00000000000..97cabfa97f4 --- /dev/null +++ b/usr.sbin/sasyncd/net_ctl.c @@ -0,0 +1,197 @@ +/* $OpenBSD: net_ctl.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "sasyncd.h" +#include "net.h" + +struct ctlmsg { + u_int32_t type; + u_int32_t data; + u_int32_t data2; +}; + +int listen_socket; + +static int +net_ctl_check_state(struct syncpeer *p, enum RUNSTATE nstate) +{ + static char *runstate[] = CARPSTATES; + + if (nstate < INIT || nstate > FAIL) { + log_msg(0, "got bad state %d from peer \"%s\"", nstate, + p->name); + net_ctl_send_error(p, CTL_STATE); + return -1; + } + if (cfgstate.runstate == MASTER && + nstate == MASTER) { + log_msg(0, "got bad state MASTER from peer \"%s\"", + p->name); + net_ctl_send_error(p, CTL_STATE); + return -1; + } + if (p->runstate != nstate) { + p->runstate = nstate; + log_msg(1, "peer \"%s\" state change to %s", p->name, + runstate[nstate]); + } + return 0; +} + +void +net_ctl_handle_msg(struct syncpeer *p, u_int8_t *msg, u_int32_t msglen) +{ + struct ctlmsg *ctl = (struct ctlmsg *)msg; + enum RUNSTATE nstate; + enum CTLTYPE ctype; + static char *ct, *ctltype[] = CTLTYPES; + + if (msglen < sizeof *ctl) { + log_msg(0, "got invalid control message from peer \"%s\"", + p->name); + net_ctl_send_error(p, CTL_UNKNOWN); + return; + } + + switch (ntohl(ctl->type)) { + case CTL_STATE: + log_msg(3, "got CTL_STATE from peer \"%s\"", p->name); + nstate = (enum RUNSTATE)ntohl(ctl->data); + if (net_ctl_check_state(p, nstate) == 0) + net_ctl_send_ack(p, CTL_STATE, cfgstate.runstate); + break; + + case CTL_ERROR: + log_msg(1, "got ERROR from peer \"%s\"", p->name); + + switch (ntohl(ctl->data)) { + case RESERVED: /* PFKEY -- nothing to do here for now */ + break; + + case CTL_STATE: + nstate = cfgstate.runstate; + carp_check_state(); + if (nstate != cfgstate.runstate) + net_ctl_send_state(p); + break; + + case CTL_UNKNOWN: + default: + break; + } + break; + + case CTL_ACK: + ctype = (enum CTLTYPE)ntohl(ctl->data); + if (ctype < RESERVED || ctype > CTL_UNKNOWN) + ct = "<unknown>"; + else + ct = ctltype[ctype]; + log_msg(3, "got %s ACK from peer \"%s\"", ct, p->name); + if (ctype == CTL_STATE) { + nstate = (enum RUNSTATE)ntohl(ctl->data2); + net_ctl_check_state(p, nstate); + } + break; + + case CTL_UNKNOWN: + default: + log_msg(1, "got unknown msg type %u from peer \"%s\"", + ntohl(ctl->type), p->name); + break; + } +} + +static int +net_ctl_send(struct syncpeer *p, u_int32_t type, u_int32_t d, u_int32_t d2) +{ + struct ctlmsg *m = (struct ctlmsg *)malloc(sizeof *m); + + if (!m) { + log_err("malloc(%u)", sizeof *m); + return -1; + } + + memset(m, 0, sizeof *m); + m->type = htonl(type); + m->data = htonl(d); + m->data2 = htonl(d2); + + return net_queue(p, MSG_SYNCCTL, (u_int8_t *)m, 0, sizeof *m); +} + +int +net_ctl_send_ack(struct syncpeer *p, enum CTLTYPE prevtype, u_int32_t code) +{ + return net_ctl_send(p, CTL_ACK, (u_int32_t)prevtype, code); +} + +int +net_ctl_send_state(struct syncpeer *p) +{ + return net_ctl_send(p, CTL_STATE, (u_int32_t)cfgstate.runstate, 0); +} + +int +net_ctl_send_error(struct syncpeer *p, enum CTLTYPE prevtype) +{ + return net_ctl_send(p, CTL_ERROR, (u_int32_t)prevtype, 0); +} + +/* After a CARP tracker state change, send an state ctl msg to all peers. */ +void +net_ctl_update_state(void) +{ + struct syncpeer *p; + static char *carpstate[] = CARPSTATES; + + /* We may have new peers available. */ + net_connect_peers(); + + for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { + if (p->socket == -1) + continue; + log_msg(2, "sending my state %s to peer \"%s\"", + carpstate[cfgstate.runstate], p->name); + net_ctl_send_state(p); + } +} diff --git a/usr.sbin/sasyncd/net_ssl.c b/usr.sbin/sasyncd/net_ssl.c new file mode 100644 index 00000000000..7cef23bf112 --- /dev/null +++ b/usr.sbin/sasyncd/net_ssl.c @@ -0,0 +1,239 @@ +/* $OpenBSD: net_ssl.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> + +#include <openssl/ssl.h> +#include <openssl/tls1.h> +#include <openssl/err.h> + +#include <string.h> + +#include "sasyncd.h" +#include "net.h" + +/* Global SSL context. */ +SSL_CTX *ctx; + +static void net_SSL_dump_stack(int); +static void net_SSL_print_error(int, int); + +int +net_SSL_init(void) +{ + SSL_library_init(); + SSL_load_error_strings(); + + ctx = SSL_CTX_new(TLSv1_method()); + if (!ctx) + return -1; + + (void)SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2); + + /* Load CA cert. */ + if (!SSL_CTX_load_verify_locations(ctx, cfgstate.cafile, NULL)) { + net_SSL_dump_stack(0); + fprintf(stderr, "cannot read \"%s\": %s\n", cfgstate.cafile, + strerror(errno)); + return -1; + } + + /* Load our certificate. */ + if (!SSL_CTX_use_certificate_chain_file(ctx, cfgstate.certfile)) { + net_SSL_dump_stack(0); + fprintf(stderr, "cannot read \"%s\": %s\n", cfgstate.certfile, + strerror(errno)); + return -1; + } + + /* Load and check private key. */ + if (!SSL_CTX_use_PrivateKey_file(ctx, cfgstate.privkeyfile, + SSL_FILETYPE_PEM)) { + net_SSL_dump_stack(0); + if (ERR_GET_REASON(ERR_peek_error() == EVP_R_BAD_DECRYPT)) { + fprintf(stderr, "bad pass phrase\n"); + return -1; + } else { + fprintf(stderr, "cannot read \"%s\": %s\n", + cfgstate.privkeyfile, strerror(errno)); + return -1; + } + } + if (!SSL_CTX_check_private_key(ctx)) { + net_SSL_dump_stack(0); + fprintf(stderr, "Private key does not match certificate\n"); + return -1; + } + return 0; +} + +int +net_SSL_connect(struct syncpeer *p) +{ + int r, err; + + p->ssl = SSL_new(ctx); + if (!p->ssl) + return -1; + SSL_set_fd(p->ssl, p->socket); + r = SSL_connect(p->ssl); + if (r != 1) { + err = SSL_get_error(p->ssl, r); + net_SSL_print_error(err, r); + return -1; + } + log_msg(2, "TLS connection established with peer " + "\"%s\"", p->name); + return 0; +} + +void +net_SSL_disconnect(struct syncpeer *p) +{ + if (p->ssl) { + SSL_shutdown(p->ssl); + SSL_free(p->ssl); + } + p->ssl = NULL; +} + +static void +net_SSL_dump_stack(int level) +{ + int err; + + while ((err = ERR_get_error()) != 0) + log_msg(level, "%s", ERR_error_string(err, NULL)); +} + +static void +net_SSL_print_error(int r, int prev) +{ + char *msg; + + switch (r) { + case SSL_ERROR_NONE: + msg = "SSL_ERROR_NONE"; + break; + case SSL_ERROR_ZERO_RETURN: + msg = "SSL_ERROR_ZERO_RETURN"; + break; + case SSL_ERROR_WANT_READ: + msg = "SSL_ERROR_WANT_READ"; + break; + case SSL_ERROR_WANT_WRITE: + msg = "SSL_ERROR_WANT_WRITE"; + break; + case SSL_ERROR_WANT_CONNECT: + msg = "SSL_ERROR_WANT_CONNECT"; + break; + case SSL_ERROR_WANT_ACCEPT: + msg = "SSL_ERROR_WANT_ACCEPT"; + break; + case SSL_ERROR_WANT_X509_LOOKUP: + msg = "SSL_ERROR_WANT_X509_LOOKUP"; + break; + case SSL_ERROR_SYSCALL: + msg = "SSL_ERROR_SYSCALL"; + break; + case SSL_ERROR_SSL: + msg = "SSL_ERROR_SSL"; + break; + default: + msg = "<unknown error>"; + break; + } + + log_msg(3, "SSL: \"%s\" original code = %d", msg, prev); + + net_SSL_dump_stack(3); +} + +static int +net_SSL_io(struct syncpeer *p, void *buf, u_int32_t len, int writeflag) +{ + int ret, e; + + retry: + if (writeflag) + ret = SSL_write(p->ssl, buf, len); + else + ret = SSL_read(p->ssl, buf, len); + if (ret == (int)len) + return 0; + + e = SSL_get_error(p->ssl, ret); + net_SSL_print_error(e, ret); + + if (e == SSL_ERROR_WANT_READ || e == SSL_ERROR_WANT_WRITE) + goto retry; /* Enough to just retry here? XXX */ + + log_msg(1, "peer \"%s\" disconnected", p->name); + net_disconnect_peer(p); + return 1; +} + +int +net_SSL_read(struct syncpeer *p, void *buf, u_int32_t len) +{ + int r, err; + + if (!p->ssl) { + p->ssl = SSL_new(ctx); + if (!p->ssl) { + log_msg(0, "SSL_new() failed"); + return NULL; + } + SSL_set_fd(p->ssl, p->socket); + r = SSL_accept(p->ssl); + if (r != 1) { + err = SSL_get_error(p->ssl, r); + net_SSL_print_error(err, r); + return NULL; + } + } + + return net_SSL_io(p, buf, len, 0); +} + +int +net_SSL_write(struct syncpeer *p, void *buf, u_int32_t len) +{ + return net_SSL_io(p, buf, len, 1); +} + +void +net_SSL_shutdown(void) +{ + ERR_free_strings(); + SSL_CTX_free(ctx); +} diff --git a/usr.sbin/sasyncd/pfkey.c b/usr.sbin/sasyncd/pfkey.c new file mode 100644 index 00000000000..6ec09d5c327 --- /dev/null +++ b/usr.sbin/sasyncd/pfkey.c @@ -0,0 +1,359 @@ +/* $OpenBSD: pfkey.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/queue.h> +#include <sys/sysctl.h> +#include <net/pfkeyv2.h> + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "sasyncd.h" + +struct pfkey_msg +{ + SIMPLEQ_ENTRY(pfkey_msg) next; + + u_int8_t *buf; + u_int32_t len; +}; + +SIMPLEQ_HEAD(, pfkey_msg) pfkey_msglist; + +static const char *msgtypes[] = { + "RESERVED", "GETSPI", "UPDATE", "ADD", "DELETE", "GET", "ACQUIRE", + "REGISTER", "EXPIRE", "FLUSH", "DUMP", "X_PROMISC", "X_ADDFLOW", + "X_DELFLOW", "X_GRPSPIS", "X_ASKPOLICY" +}; + +#define CHUNK sizeof(u_int64_t) + +static const char *pfkey_print_type(struct sadb_msg *msg); + +static int +pfkey_write(u_int8_t *buf, ssize_t len) +{ + struct sadb_msg *msg = (struct sadb_msg *)buf; + + if (cfgstate.pfkey_socket == -1) + return 0; + + if (write(cfgstate.pfkey_socket, buf, len) != len) { + log_err("pfkey: msg %s write() failed", + pfkey_print_type(msg), cfgstate.pfkey_socket); + return -1; + } + + return 0; +} + +int +pfkey_set_promisc(void) +{ + struct sadb_msg msg; + static u_int32_t seq = 1; + + memset(&msg, 0, sizeof msg); + msg.sadb_msg_version = PF_KEY_V2; + msg.sadb_msg_seq = seq++; + msg.sadb_msg_satype = 1; /* Special; 1 to enable, 0 to disable */ + msg.sadb_msg_type = SADB_X_PROMISC; + msg.sadb_msg_pid = getpid(); + msg.sadb_msg_len = sizeof msg / CHUNK; + + return pfkey_write((u_int8_t *)&msg, sizeof msg); +} + +static const char * +pfkey_print_type(struct sadb_msg *msg) +{ + if (msg->sadb_msg_type < sizeof msgtypes / sizeof msgtypes[0]) + return msgtypes[msg->sadb_msg_type]; + else + return "<unknown>"; +} + +static int +pfkey_handle_message(struct sadb_msg *m) +{ + struct sadb_msg *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) { + errno = msg->sadb_msg_errno; + log_err("pfkey error (%s)", pfkey_print_type(msg)); + } + + /* We only want promiscuous messages here, skip all others. */ + if (msg->sadb_msg_type != SADB_X_PROMISC || + (msg->sadb_msg_len * CHUNK) <= 2 * sizeof *msg) { + free(m); + return 0; + } + msg++; + + /* + * We should not listen to PFKEY messages when we are not running + * as MASTER, or the pid is our own. + */ + if (cfgstate.runstate != MASTER || + msg->sadb_msg_pid == (u_int32_t)getpid()) { + free(m); + return 0; + } + + log_msg(3, "pfkey: 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: + /* Some messages should not be synced. */ + free(m); + break; + + case SADB_UPDATE: + /* + * Tweak -- the peers do not have a larval SA to update, so + * instead we ADD it here. + */ + msg->sadb_msg_type = SADB_ADD; + /* FALLTHROUGH */ + + default: + /* The rest should just be passed along to our peers. */ + return net_queue(NULL, MSG_PFKEYDATA, (u_int8_t *)m, sizeof *m, + msg->sadb_msg_len * CHUNK); + } + + return 0; +} + +static int +pfkey_read(void) +{ + struct sadb_msg hdr, *msg; + u_int8_t *data; + ssize_t datalen; + int fd = cfgstate.pfkey_socket; + + if (recv(fd, &hdr, sizeof hdr, MSG_PEEK) != sizeof hdr) { + log_err("pfkey_read: recv() failed"); + return -1; + } + datalen = hdr.sadb_msg_len * CHUNK; + data = (u_int8_t *)malloc(datalen); + if (!data) { + log_err("pfkey_read: malloc(%lu) failed", datalen); + return -1; + } + msg = (struct sadb_msg *)data; + + if (read(fd, data, datalen) != datalen) { + log_err("pfkey_read: read() failed, %lu bytes", datalen); + free(data); + return -1; + } + + return pfkey_handle_message(msg); +} + +int +pfkey_init(int reinit) +{ + int fd; + + fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (fd == -1) { + perror("failed to open PF_KEY socket"); + return -1; + } + cfgstate.pfkey_socket = fd; + + if (reinit) { + if (cfgstate.runstate == MASTER) + pfkey_set_promisc(); + return (fd > -1 ? 0 : -1); + } + + SIMPLEQ_INIT(&pfkey_msglist); + return 0; +} + +void +pfkey_set_rfd(fd_set *fds) +{ + if (cfgstate.pfkey_socket != -1) + FD_SET(cfgstate.pfkey_socket, fds); +} + +void +pfkey_set_pending_wfd(fd_set *fds) +{ + if (cfgstate.pfkey_socket != -1 && SIMPLEQ_FIRST(&pfkey_msglist)) + FD_SET(cfgstate.pfkey_socket, fds); +} + +void +pfkey_read_message(fd_set *fds) +{ + if (cfgstate.pfkey_socket != -1) + if (FD_ISSET(cfgstate.pfkey_socket, fds)) + (void)pfkey_read(); +} + +void +pfkey_send_message(fd_set *fds) +{ + struct pfkey_msg *pmsg = SIMPLEQ_FIRST(&pfkey_msglist); + + if (!pmsg || !FD_ISSET(cfgstate.pfkey_socket, fds)) + return; + + if (cfgstate.pfkey_socket == -1) + if (pfkey_init(1)) /* Reinit socket */ + return; + + (void)pfkey_write(pmsg->buf, pmsg->len); + + SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next); + free(pmsg->buf); + free(pmsg); + return; +} + +int +pfkey_queue_message(u_int8_t *data, u_int32_t datalen) +{ + struct pfkey_msg *pmsg; + struct sadb_msg *sadb = (struct sadb_msg *)data; + static u_int32_t seq = 1; + + pmsg = (struct pfkey_msg *)malloc(sizeof *pmsg); + if (!pmsg) { + log_err("malloc()"); + return -1; + } + memset(pmsg, 0, sizeof *pmsg); + + pmsg->buf = data; + pmsg->len = datalen; + + sadb->sadb_msg_pid = getpid(); + sadb->sadb_msg_seq = seq++; + log_msg(3, "sync: pfkey %s len %d seq %d", pfkey_print_type(sadb), + sadb->sadb_msg_len * CHUNK, sadb->sadb_msg_seq); + + SIMPLEQ_INSERT_TAIL(&pfkey_msglist, pmsg, next); + return 0; +} + +void +pfkey_shutdown(void) +{ + struct pfkey_msg *p = SIMPLEQ_FIRST(&pfkey_msglist); + + while ((p = SIMPLEQ_FIRST(&pfkey_msglist))) { + SIMPLEQ_REMOVE_HEAD(&pfkey_msglist, next); + free(p->buf); + free(p); + } + + if (cfgstate.pfkey_socket > -1) + close(cfgstate.pfkey_socket); +} + +/* ------------------------------------------------------------------------- */ + +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"); + + if (sysctl(mib, sizeof mib / sizeof mib[0], NULL, &sz, NULL, 0) == -1 + || sz == 0) + 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"); + 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, "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); + } + /* ... */ + m = (struct sadb_msg *)((u_int8_t *)m + mlen); + } + memset(buf, 0, sz); + free(buf); + return; +} diff --git a/usr.sbin/sasyncd/sasyncd.8 b/usr.sbin/sasyncd/sasyncd.8 new file mode 100644 index 00000000000..415eed160d5 --- /dev/null +++ b/usr.sbin/sasyncd/sasyncd.8 @@ -0,0 +1,124 @@ +.\" $OpenBSD: sasyncd.8,v 1.1 2005/03/30 18:44:49 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. +.\" +.\" This code was written under funding by Multicom Security AB. +.\" +.\" Manual page for sasyncd +.\" +.Dd August 07, 2004 +.Dt SASYNCD 8 +.Os +.Sh NAME +.Nm sasyncd +.Nd IPSec SA synchronization daemon for failover gateways +.Sh SYNOPSIS +.Nm +.Op Fl dv +.Op Fl c Ar config-file +.Sh DESCRIPTION +The +.Nm +daemon synchronizes IPSec SA information between a number of failover +IPsec gateways. +The most typical scenario is to run +.Nm +on hosts also running +.Xr isakmpd 8 +and sharing a common IP-address using +.Xr carp 4 . +.Pp +The daemon runs either in master or slave mode, in which the master +tracks all local IPsec SA changes and sends this information along to +all slaves so they will have the same data. +.Ss Failover +.Nm +does not itself do any failover processing; the normal mode of +operation is to track state changes on a specified +.Xr carp 4 +interface. +Whenever it changes, +.Nm +will follow suit. +It is possible to +.Qq lock +the daemon to a particular state; see +.Xr sasyncd.conf 5 . +.Ss Host to host communication +All +.Nm +host to host communication is protected by +.Xr ssl 8 , +so the daemon requires certificates configured for each host. +Normally +.Pa /etc/ssl/sasyncd.crt +and +.Pa /etc/ssl/private/sasyncd.key +are used, although alternate locations may be specified in the +configuration file. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl c Ar config-file +If given, the +.Fl c +option specifies an alternate configuration file instead of +.Pa /etc/sasyncd.conf . +.It Fl d +The +.Fl d +option causes the daemon to run in the foreground, logging to stderr. +.It Fl v +The +.Fl v +option increases the verbosity level of the daemon. +This option can be specified several times. +.El +.Sh FILES +.Bl -tag -width /etc/ssl/private/sasyncd.key -compact +.It Pa /etc/sasyncd.conf +The default +.Nm +configuration file. +.It Pa /etc/ssl/sascynd.crt +The default certificate. +.It Pa /etc/ssl/private/sasyncd.key +The default private key. +.It Pa /etc/ssl/ca.crt +The default CA certificate. +.El +.Sh SEE ALSO +.Xr openssl 1 , +.Xr carp 4 , +.Xr ipsec 4 , +.Xr sasyncd.conf 5 , +.Xr isakmpd 8 , +.Xr ssl 8 +.Sh HISTORY +The +.Nm +daemon first appeared in +.Ox 3.7 . +It was written in 2004 by Hakan Olsson, in part sponsored by +Multicom Security AB, Sweden. diff --git a/usr.sbin/sasyncd/sasyncd.c b/usr.sbin/sasyncd/sasyncd.c new file mode 100644 index 00000000000..b59fc818025 --- /dev/null +++ b/usr.sbin/sasyncd/sasyncd.c @@ -0,0 +1,208 @@ +/* $OpenBSD: sasyncd.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "sasyncd.h" + +static int +privdrop(void) +{ + struct passwd *pw = getpwnam(SASYNCD_USER); + + if (!pw) { + log_err("%s: getpwnam(\"%s\") failed", __progname, + SASYNCD_USER); + return -1; + } + + if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) { + log_err("%s: chroot failed", __progname); + return -1; + } + + if (setgroups(1, &pw->pw_gid) || setegid(pw->pw_gid) || + setgid(pw->pw_gid) || seteuid(pw->pw_uid) || setuid(pw->pw_uid)) { + log_err("%s: failed to drop privileges", __progname); + return -1; + } + + return 0; +} + +volatile int daemon_shutdown = 0; + +/* Called by signal handler for controlled daemon shutdown. */ +static void +sasyncd_stop(int s) +{ + daemon_shutdown++; +} + +static int +sasyncd_run(void) +{ + struct timeval *timeout, tv; + fd_set *rfds, *wfds; + size_t fdsetsize; + int maxfd, n; + + n = getdtablesize(); + fdsetsize = howmany(n, NFDBITS) * sizeof(fd_mask); + + rfds = (fd_set *)malloc(fdsetsize); + if (!rfds) { + log_err("malloc(%lu) failed", (unsigned long)fdsetsize); + return -1; + } + + wfds = (fd_set *)malloc(fdsetsize); + if (!wfds) { + log_err("malloc(%lu) failed", (unsigned long)fdsetsize); + return -1; + } + + signal(SIGINT, sasyncd_stop); + signal(SIGTERM, sasyncd_stop); + signal(SIGHUP, sasyncd_stop); + + while (!daemon_shutdown) { + memset(rfds, 0, fdsetsize); + memset(wfds, 0, fdsetsize); + maxfd = net_set_rfds(rfds); + n = net_set_pending_wfds(wfds); + if (n > maxfd) + maxfd = n; + + pfkey_set_rfd(rfds); + pfkey_set_pending_wfd(wfds); + if (cfgstate.pfkey_socket + 1 > maxfd) + maxfd = cfgstate.pfkey_socket + 1; + + timeout = &tv; + timer_next_event(&tv); + + n = select(maxfd, rfds, wfds, 0, timeout); + if (n == -1) { + if (errno != EINTR) { + log_err("select()"); + sleep(1); + } + } else if (n) { + net_handle_messages(rfds); + net_send_messages(wfds); + pfkey_read_message(rfds); + pfkey_send_message(wfds); + } + timer_run(); + } + free(rfds); + free(wfds); + return 0; +} + +int +main(int argc, char **argv) +{ + int r; + + /* Init. */ + closefrom(STDERR_FILENO + 1); + for (r = 0; r <= 2; r++) + if (fcntl(r, F_GETFL, 0) == -1 && errno == EBADF) + (void)open("/dev/null", r ? O_WRONLY : O_RDONLY, 0); + + for (r = 1; r < _NSIG; r++) + signal(r, SIG_DFL); + + log_init(__progname); + timer_init(); + + pfkey_snapshot(0); + exit(1); + + r = conf_init(argc, argv); + if (r > 1) { + fprintf(stderr, "Usage: %s [-c config-file] [-d] [-v[v]]\n", + __progname); + fprintf(stderr, "Default configuration file is %s\n", + SASYNCD_CFGFILE); + } + if (r) + return 1; + + if (carp_init()) + return 1; + if (pfkey_init(0)) + return 1; + if (net_init()) + return 1; + + /* Drop privileges. */ + privdrop(); + + if (!cfgstate.debug) + if (daemon(1, 0)) { + perror("daemon()"); + exit(1); + } + + /* Main loop. */ + sasyncd_run(); + + /* Shutdown. */ + log_msg(0, "shutting down..."); + + net_shutdown(); + pfkey_shutdown(); + return 0; +} + +/* Special for compiling with Boehms GC. See Makefile and sasyncd.h */ +#if defined (GC_DEBUG) +char * +gc_strdup(const char *x) +{ + char *strcpy(char *,const char *); + char *y = malloc(strlen(x) + 1); + return strcpy(y,x); +} +#endif diff --git a/usr.sbin/sasyncd/sasyncd.conf.5 b/usr.sbin/sasyncd/sasyncd.conf.5 new file mode 100644 index 00000000000..4b70f469645 --- /dev/null +++ b/usr.sbin/sasyncd/sasyncd.conf.5 @@ -0,0 +1,104 @@ +.\" $OpenBSD: sasyncd.conf.5,v 1.1 2005/03/30 18:44:49 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. +.\" +.\" This code was written under funding by Multicom Security AB. +.\" +.\" Manual page for sasyncd.conf +.\" +.Dd August 07, 2004 +.Dt SASYNCD.CONF 5 +.Os +.Sh NAME +.Nm sasyncd.conf +.Nd configuration file for sasyncd +.Sh DESCRIPTION +.Nm +is the configuration file for the +.Xr sasyncd 8 +daemon. +.Pp +Comments can be put anywhere in the file using a hash mark +.Pq Sq # , +and extends to the end of the current line. +.Pp +The following configuration settings are understood: +.Bl -tag -width Ds +.It Ic CAcertificate file Ar filename +Specify a file containing the CA certificate. +The default is +.Pa /etc/ssl/ca.crt . +.It Ic carp interface Ar interface +Specify which +.Xr carp 4 +interface +.Nm sasyncd +should track master/slave state on. +.It Ic carp interval Ar seconds +Specify how often the daemon should check the above interface for +state changes. +Defaults to once every 10 seconds. +.It Ic certificate file Ar filename +Specify a file containing our certificate. +The default is +.Pa /etc/ssl/sasyncd.crt . +.It Ic listen on Ar address +Specify a local IP address, hostname, or interface the +.Xr sasyncd 8 +daemon should listen on. +The default is to listen on all local addresses. +.It Ic listen port Ar port +Specify a local port the +.Xr sasyncd 8 +daemon should listen on. +The default is to listen on port 501. +.It Ic peer Ar address +Specify a +.Xr sasyncd 8 +peer IP address or hostname. +May be specified multiple times. +For example: +.Bd -literal -offset indent +peer 10.0.0.2 +peer 10.0.0.3 +peer 10.0.0.4 +.Ed +.It Ic private key file Ar filename +Specify a file containing the private key matching the certificate +specified using +.Ar ceritificate file . +The default is +.Pa /etc/ssl/private/sasyncd.key . +.It Ic run as master | slave +Force the daemon to run as master or slave. +Normally only intended for debugging use. +.El +.Sh SEE ALSO +.Xr carp 4 , +.Xr sasyncd 8 +.Sh HISTORY +The +.Nm +file format first appeared in +.Ox 3.7 . diff --git a/usr.sbin/sasyncd/sasyncd.h b/usr.sbin/sasyncd/sasyncd.h new file mode 100644 index 00000000000..aa37ff3c161 --- /dev/null +++ b/usr.sbin/sasyncd/sasyncd.h @@ -0,0 +1,142 @@ +/* $OpenBSD: sasyncd.h,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/queue.h> + +enum RUNSTATE { INIT = 0, SLAVE, MASTER, FAIL }; +#define CARPSTATES { "INIT", "SLAVE", "MASTER", "FAIL" } + +struct syncpeer; +struct timeval; + +struct cfgstate { + enum RUNSTATE runstate; + enum RUNSTATE lockedstate; + int debug; + int verboselevel; + + char *carp_ifname; + int carp_check_interval; + + char *cafile; + char *certfile; + char *privkeyfile; + + int pfkey_socket; + + char *listen_on; + in_port_t listen_port; + + LIST_HEAD(, syncpeer) peerlist; +}; + +extern struct cfgstate cfgstate; +extern char *__progname; + +#define SASYNCD_USER "_isakmpd" +#define SASYNCD_CFGFILE "/etc/sasyncd.conf" + +#define CARP_DEFAULT_INTERVAL 10 + +#define SASYNCD_DEFAULT_PORT 501 +#define SASYNCD_CAFILE "/etc/ssl/ca.crt" +#define SASYNCD_CERTFILE "/etc/ssl/sasyncd.crt" +#define SASYNCD_PRIVKEY "/etc/ssl/private/sasyncd.key" + +/* + * sasyncd "protocol" definition + * + * Message format: + * u_int32_t type + * u_int32_t len + * raw data + */ + +/* sasyncd protocol message types */ +#define MSG_SYNCCTL 0 +#define MSG_PFKEYDATA 1 +#define MSG_MAXTYPE 1 /* Increase when new types are added. */ + +/* conf.c */ +int conf_init(int, char **); + +/* carp.c */ +void carp_check_state(void); +int carp_init(void); + +/* log.c */ +void log_init(char *); +void log_msg(int, const char *, ...); +void log_err(const char *, ...); + +/* net.c */ +void net_ctl_update_state(void); +int net_init(void); +void net_handle_messages(fd_set *); +int net_queue(struct syncpeer *, u_int32_t, u_int8_t *, u_int32_t, + u_int32_t); +void net_send_messages(fd_set *); +int net_set_rfds(fd_set *); +int net_set_pending_wfds(fd_set *); +void net_shutdown(void); + +/* pfkey.c */ +int pfkey_init(int); +int pfkey_queue_message(u_int8_t *, u_int32_t); +void pfkey_read_message(fd_set *); +void pfkey_send_message(fd_set *); +void pfkey_set_rfd(fd_set *); +void pfkey_set_pending_wfd(fd_set *); +int pfkey_set_promisc(void); +void pfkey_shutdown(void); +void pfkey_snapshot(void *); + +/* timer.c */ +void timer_init(void); +void timer_next_event(struct timeval *); +void timer_run(void); +int timer_add(char *, u_int32_t, void (*)(void *), void *); + +#if defined (GC_DEBUG) +/* Boehms GC */ +void *GC_debug_malloc(size_t, char *, int); +void *GC_debug_realloc(void *, size_t, char *, int); +void GC_debug_free(void *); +char *gc_strdup(const char *); + +#define malloc(x) GC_debug_malloc ((x), __FILE__, __LINE__) +#define realloc(x,y) GC_debug_realloc ((x), (y), __FILE__, __LINE__) +#define free(x) GC_debug_free (x) +#define calloc(x,y) malloc((x) * (y)) +#define strdup(x) gc_strdup((x)) + +#endif /* WITH_BOEHM_GC */ diff --git a/usr.sbin/sasyncd/timer.c b/usr.sbin/sasyncd/timer.c new file mode 100644 index 00000000000..5582d6918ce --- /dev/null +++ b/usr.sbin/sasyncd/timer.c @@ -0,0 +1,143 @@ +/* $OpenBSD: timer.c,v 1.1 2005/03/30 18:44:49 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. + */ + +/* + * This code was written under funding by Multicom Security AB. + */ + + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/time.h> + +#include <stdlib.h> +#include <string.h> + +#include "sasyncd.h" + +/* + * All events have a name, an expiration time and a function to be called + * at this time. The queue is always sorted so the next event to happen is + * first in the queue. + */ +struct event { + TAILQ_ENTRY (event) next; + struct timeval expire; + char *name; + void (*fun) (void *); + void *arg; +}; + +static TAILQ_HEAD (event_head, event) events; + +/* Initialize timer event queue. */ +void +timer_init(void) +{ + TAILQ_INIT(&events); +} + +/* + * Return the number of seconds until the next event happens. Used for + * the select() call in the main loop. + */ +void +timer_next_event(struct timeval *tv) +{ + struct timeval now; + struct event *e = TAILQ_FIRST(&events); + + if (e) { + gettimeofday(&now, 0); + if (timercmp(&now, &e->expire, >=)) + timerclear(tv); + else + timersub(&e->expire, &now, tv); + } else { + tv->tv_sec = 60; /* "Best guess". */ + tv->tv_usec = 0; + } +} + +/* + * Whenever select() times out, we have an event that should happen and this + * routine gets called. Handle and remove all pending events. + */ +void +timer_run(void) +{ + struct timeval now; + struct event *e; + + gettimeofday(&now, 0); + for (e = TAILQ_FIRST(&events); e && timercmp(&now, &e->expire, >=); + e = TAILQ_FIRST(&events)) { + TAILQ_REMOVE(&events, e, next); + log_msg(4, "timer_run: event \"%s\"", + e->name ? e->name : "<unknown>"); + (*e->fun)(e->arg); + if (e->name) + free(e->name); + free(e); + } +} + +/* Add a new event. */ +int +timer_add(char *name, u_int32_t when, void (*function)(void *), void *arg) +{ + struct timeval now, tmp; + struct event *e, *new; + + new = (struct event *)calloc(1, sizeof *new); + if (!new) { + log_err("timer_add: calloc (1, %u) failed", sizeof *new); + return -1; + } + + new->name = strdup(name); /* We handle failures here. */ + new->fun = function; + new->arg = arg; + + memset(&tmp, 0, sizeof tmp); + tmp.tv_sec = when; + gettimeofday(&now, 0); + timeradd(&now, &tmp, &new->expire); + + log_msg(4, "timer_add: new event \"%s\" (expiring in %us)", + name ? name : "<unknown>", when); + + /* Insert the new event in the queue so it's always sorted. */ + for (e = TAILQ_FIRST(&events); e; e = TAILQ_NEXT(e, next)) { + if (timercmp(&new->expire, &e->expire, >=)) + continue; + TAILQ_INSERT_BEFORE(e, new, next); + return 0; + } + TAILQ_INSERT_TAIL(&events, new, next); + return 0; +} |