/* $OpenBSD: pfkey.c,v 1.6 2005/05/24 03:15:11 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 #include #include #include #include #include #include #include #include #include #include #include #include #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 *); 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) { static char uk[20]; if (msg->sadb_msg_type < sizeof msgtypes / sizeof msgtypes[0]) return msgtypes[msg->sadb_msg_type]; else { snprintf(uk, sizeof uk, "", msg->sadb_msg_type); return uk; } } 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 && msg->sadb_msg_pid == (u_int32_t)getpid()) { errno = msg->sadb_msg_errno; log_msg(1, "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; } /* Move next msg to start of the buffer. */ 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; } 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; 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: /* Pass the the rest along to our peers. */ memmove(m, msg, msg->sadb_msg_len * CHUNK); /* for realloc */ return net_queue(NULL, MSG_PFKEYDATA, (u_int8_t *)m, m->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 (cfgstate.runstate == MASTER) pfkey_set_promisc(); if (reinit) 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, "pfkey_queue_message: 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 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 (!p) return; if (monitor_get_pfkey_snap(&sadb, &sadbsz, &spd, &spdsz)) { log_msg(0, "pfkey_snapshot: failed to get pfkey snapshot"); return; } /* 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; if (m->sadb_msg_len == 0) break; /* Tweak and send this SA to the peer. */ m->sadb_msg_type = SADB_ADD; /* 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); } } /* 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; if (ip->ipo_flags & IPSP_POLICY_SOCKET) continue; } /* Cleanup. */ memset(sadb, 0, sadbsz); free(sadb); memset(spd, 0, spdsz); free(spd); return; }