summaryrefslogtreecommitdiff
path: root/keyexchange/isakmpd-20041012/message.c
diff options
context:
space:
mode:
Diffstat (limited to 'keyexchange/isakmpd-20041012/message.c')
-rw-r--r--keyexchange/isakmpd-20041012/message.c2530
1 files changed, 2530 insertions, 0 deletions
diff --git a/keyexchange/isakmpd-20041012/message.c b/keyexchange/isakmpd-20041012/message.c
new file mode 100644
index 0000000..9259e2d
--- /dev/null
+++ b/keyexchange/isakmpd-20041012/message.c
@@ -0,0 +1,2530 @@
+/* $OpenBSD: message.c,v 1.89 2004/09/17 13:45:02 ho Exp $ */
+/* $EOM: message.c,v 1.156 2000/10/10 12:36:39 provos Exp $ */
+
+/*
+ * Copyright (c) 1998, 1999, 2000, 2001 Niklas Hallqvist. All rights reserved.
+ * Copyright (c) 1999 Angelos D. Keromytis. All rights reserved.
+ * Copyright (c) 1999, 2000, 2001, 2004 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 Ericsson Radio Systems.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sysdep.h"
+
+#include "attribute.h"
+#include "cert.h"
+#include "constants.h"
+#include "crypto.h"
+#include "doi.h"
+#ifdef USE_DPD
+#include "dpd.h"
+#endif
+#include "exchange.h"
+#include "field.h"
+#include "hash.h"
+#include "ipsec.h"
+#include "ipsec_num.h"
+#include "isakmp.h"
+#include "log.h"
+#include "message.h"
+#if defined (USE_NAT_TRAVERSAL)
+#include "nat_traversal.h"
+#endif
+#include "prf.h"
+#include "sa.h"
+#include "timer.h"
+#include "transport.h"
+#include "util.h"
+#include "virtual.h"
+
+#ifdef __GNUC__
+#define INLINE __inline
+#else
+#define INLINE
+#endif
+
+/* A local set datatype, coincidentally fd_set suits our purpose fine. */
+typedef fd_set set;
+#define ISSET FD_ISSET
+#define SET FD_SET
+#define ZERO FD_ZERO
+
+static int message_check_duplicate(struct message *);
+static int message_encrypt(struct message *);
+static int message_index_payload(struct message *, struct payload *,
+ u_int8_t ,u_int8_t *);
+static int message_parse_transform(struct message *, struct payload *,
+ u_int8_t, u_int8_t *);
+static u_int16_t message_payload_sz(u_int8_t);
+static int message_validate_attribute(struct message *, struct payload *);
+static int message_validate_cert(struct message *, struct payload *);
+static int message_validate_cert_req(struct message *, struct payload *);
+static int message_validate_delete(struct message *, struct payload *);
+static int message_validate_hash(struct message *, struct payload *);
+static int message_validate_id(struct message *, struct payload *);
+static int message_validate_key_exch(struct message *, struct payload *);
+static int message_validate_nat_d(struct message *, struct payload *);
+static int message_validate_nat_oa(struct message *, struct payload *);
+static int message_validate_nonce(struct message *, struct payload *);
+static int message_validate_notify(struct message *, struct payload *);
+static int message_validate_proposal(struct message *, struct payload *);
+static int message_validate_sa(struct message *, struct payload *);
+static int message_validate_sig(struct message *, struct payload *);
+static int message_validate_transform(struct message *, struct payload *);
+static int message_validate_vendor(struct message *, struct payload *);
+
+static void message_packet_log(struct message *);
+
+static int (*message_validate_payload[])(struct message *, struct payload *) =
+{
+ message_validate_sa, message_validate_proposal,
+ message_validate_transform, message_validate_key_exch,
+ message_validate_id, message_validate_cert, message_validate_cert_req,
+ message_validate_hash, message_validate_sig, message_validate_nonce,
+ message_validate_notify, message_validate_delete,
+ message_validate_vendor, message_validate_attribute,
+ message_validate_nat_d, message_validate_nat_oa,
+ message_validate_nat_d, message_validate_nat_oa
+};
+
+static struct field *fields[] = {
+ isakmp_sa_fld, isakmp_prop_fld, isakmp_transform_fld, isakmp_ke_fld,
+ isakmp_id_fld, isakmp_cert_fld, isakmp_certreq_fld, isakmp_hash_fld,
+ isakmp_sig_fld, isakmp_nonce_fld, isakmp_notify_fld, isakmp_delete_fld,
+ isakmp_vendor_fld, isakmp_attribute_fld, isakmp_nat_d_fld,
+ isakmp_nat_oa_fld, isakmp_nat_d_fld, isakmp_nat_oa_fld
+};
+
+/*
+ * These maps are used for indexing the payloads in msg->payloads[i].
+ * payload_revmap should be updated if the payloads in isakmp_num.cst change.
+ * payload_map is populated during startup by message_init().
+ */
+static u_int8_t payload_revmap[] = {
+ ISAKMP_PAYLOAD_NONE, ISAKMP_PAYLOAD_SA, ISAKMP_PAYLOAD_PROPOSAL,
+ ISAKMP_PAYLOAD_TRANSFORM, ISAKMP_PAYLOAD_KEY_EXCH, ISAKMP_PAYLOAD_ID,
+ ISAKMP_PAYLOAD_CERT, ISAKMP_PAYLOAD_CERT_REQ, ISAKMP_PAYLOAD_HASH,
+ ISAKMP_PAYLOAD_SIG, ISAKMP_PAYLOAD_NONCE, ISAKMP_PAYLOAD_NOTIFY,
+ ISAKMP_PAYLOAD_DELETE, ISAKMP_PAYLOAD_VENDOR, ISAKMP_PAYLOAD_ATTRIBUTE,
+#ifdef notyet
+ ISAKMP_PAYLOAD_SAK, ISAKMP_PAYLOAD_SAT, ISAKMP_PAYLOAD_KD,
+ ISAKMP_PAYLOAD_SEQ, ISAKMP_PAYLOAD_POP
+#endif
+ ISAKMP_PAYLOAD_NAT_D, ISAKMP_PAYLOAD_NAT_OA,
+ ISAKMP_PAYLOAD_NAT_D_DRAFT, ISAKMP_PAYLOAD_NAT_OA_DRAFT
+};
+
+static u_int8_t payload_map[256];
+u_int8_t payload_index_max;
+
+/*
+ * Fields used for checking monotonic increasing of proposal and transform
+ * numbers.
+ */
+static u_int8_t *last_sa = 0;
+static u_int32_t last_prop_no;
+static u_int8_t *last_prop = 0;
+static u_int32_t last_xf_no;
+
+/*
+ * Allocate a message structure bound to transport T, and with a first
+ * segment buffer sized SZ, copied from BUF if given.
+ */
+struct message *
+message_alloc(struct transport *t, u_int8_t *buf, size_t sz)
+{
+ struct message *msg;
+ int i;
+
+ /*
+ * We use calloc(3) because it zeroes the structure which we rely on in
+ * message_free when determining what sub-allocations to free.
+ */
+ msg = (struct message *)calloc(1, sizeof *msg);
+ if (!msg)
+ return 0;
+ msg->iov = calloc(1, sizeof *msg->iov);
+ if (!msg->iov) {
+ message_free(msg);
+ return 0;
+ }
+ msg->iov[0].iov_len = sz;
+ msg->iov[0].iov_base = malloc(sz);
+ if (!msg->iov[0].iov_base) {
+ message_free(msg);
+ return 0;
+ }
+ msg->iovlen = 1;
+ if (buf)
+ memcpy(msg->iov[0].iov_base, buf, sz);
+ msg->nextp = (u_int8_t *)msg->iov[0].iov_base +
+ ISAKMP_HDR_NEXT_PAYLOAD_OFF;
+ msg->transport = t;
+ transport_reference(t);
+ msg->payload = (struct payload_head *)calloc(payload_index_max,
+ sizeof *msg->payload);
+ if (!msg->payload) {
+ message_free(msg);
+ return 0;
+ }
+ for (i = 0; i < payload_index_max; i++)
+ TAILQ_INIT(&msg->payload[i]);
+ TAILQ_INIT(&msg->post_send);
+ LOG_DBG((LOG_MESSAGE, 90, "message_alloc: allocated %p", msg));
+ return msg;
+}
+
+/*
+ * Allocate a message suitable for a reply to MSG. Just allocate an empty
+ * ISAKMP header as the first segment.
+ */
+struct message *
+message_alloc_reply(struct message *msg)
+{
+ struct message *reply;
+
+ reply = message_alloc(msg->transport, 0, ISAKMP_HDR_SZ);
+ reply->exchange = msg->exchange;
+ reply->isakmp_sa = msg->isakmp_sa;
+ if (msg->isakmp_sa)
+ sa_reference(msg->isakmp_sa);
+ return reply;
+}
+
+/* Free up all resources used by the MSG message. */
+void
+message_free(struct message *msg)
+{
+ u_int32_t i;
+ struct payload *payload, *next;
+
+ LOG_DBG((LOG_MESSAGE, 20, "message_free: freeing %p", msg));
+ if (!msg)
+ return;
+ if (msg->orig && msg->orig != (u_int8_t *) msg->iov[0].iov_base)
+ free(msg->orig);
+ if (msg->iov) {
+ for (i = 0; i < msg->iovlen; i++)
+ if (msg->iov[i].iov_base)
+ free(msg->iov[i].iov_base);
+ free(msg->iov);
+ }
+ if (msg->retrans)
+ timer_remove_event(msg->retrans);
+ if (msg->payload) {
+ for (i = 0; i < payload_index_max; i++)
+ for (payload = payload_first(msg, i); payload;
+ payload = next) {
+ next = TAILQ_NEXT(payload, link);
+ free(payload);
+ }
+ free(msg->payload);
+ }
+ while (TAILQ_FIRST(&msg->post_send) != 0)
+ TAILQ_REMOVE(&msg->post_send, TAILQ_FIRST(&msg->post_send),
+ link);
+
+ /* If we are on the send queue, remove us from there. */
+ if (msg->flags & MSG_IN_TRANSIT)
+ TAILQ_REMOVE(msg->transport->vtbl->get_queue(msg), msg, link);
+
+ if (msg->transport)
+ transport_release(msg->transport);
+
+ if (msg->isakmp_sa)
+ sa_release(msg->isakmp_sa);
+
+ free(msg);
+}
+
+/*
+ * Generic ISAKMP parser.
+ * MSG is the ISAKMP message to be parsed. NEXT is the type of the first
+ * payload to be parsed, and it's pointed to by BUF. ACCEPTED_PAYLOADS
+ * tells what payloads are accepted and FUNC is a pointer to a function
+ * to be called for each payload found. Returns the total length of the
+ * parsed payloads.
+ */
+static int
+message_parse_payloads(struct message *msg, struct payload *p, u_int8_t next,
+ u_int8_t *buf, set *accepted_payloads, int (*func)(struct message *,
+ struct payload *, u_int8_t, u_int8_t *))
+{
+ u_int8_t payload;
+ u_int16_t len;
+ int sz = 0;
+
+ do {
+ LOG_DBG((LOG_MESSAGE, 50,
+ "message_parse_payloads: offset %ld payload %s",
+ (long)(buf - (u_int8_t *) msg->iov[0].iov_base),
+ constant_name(isakmp_payload_cst, next)));
+
+ /* Does this payload's header fit? */
+ if (buf + ISAKMP_GEN_SZ > (u_int8_t *)msg->iov[0].iov_base +
+ msg->iov[0].iov_len) {
+ log_print("message_parse_payloads: short message");
+ message_drop(msg,
+ ISAKMP_NOTIFY_UNEQUAL_PAYLOAD_LENGTHS, 0, 1, 1);
+ return -1;
+ }
+ /* Ponder on the payload that is at BUF... */
+ payload = next;
+
+ /* Look at the next payload's type. */
+ next = GET_ISAKMP_GEN_NEXT_PAYLOAD(buf);
+ if (next >= ISAKMP_PAYLOAD_RESERVED_MIN &&
+ next <= ISAKMP_PAYLOAD_RESERVED_MAX) {
+ log_print("message_parse_payloads: invalid next "
+ "payload type %s in payload of type %d",
+ constant_name(isakmp_payload_cst, next), payload);
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE,
+ 0, 1, 1);
+ return -1;
+ }
+ /* Reserved fields in ISAKMP messages should be zero. */
+ if (GET_ISAKMP_GEN_RESERVED(buf) != 0) {
+ log_print("message_parse_payloads: reserved field "
+ "non-zero: %x", GET_ISAKMP_GEN_RESERVED(buf));
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
+ 0, 1, 1);
+ return -1;
+ }
+ /*
+ * Decode and validate the payload length field.
+ */
+ len = GET_ISAKMP_GEN_LENGTH(buf);
+
+ if (message_payload_sz(payload) == 0) {
+ log_print("message_parse_payloads: unknown minimum "
+ "payload size for payload type %s",
+ constant_name(isakmp_payload_cst, payload));
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
+ 0, 1, 1);
+ return -1;
+ }
+ if (len < message_payload_sz(payload)) {
+ log_print("message_parse_payloads: payload too "
+ "short: %u", len);
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
+ 0, 1, 1);
+ return -1;
+ }
+ if (buf + len > (u_int8_t *)msg->iov[0].iov_base +
+ msg->iov[0].iov_len) {
+ log_print("message_parse_payloads: payload too "
+ "long: %u", len);
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED,
+ 0, 1, 1);
+ return -1;
+ }
+ /* Ignore most private payloads. */
+ if (next >= ISAKMP_PAYLOAD_PRIVATE_MIN &&
+ next != ISAKMP_PAYLOAD_NAT_D_DRAFT &&
+ next != ISAKMP_PAYLOAD_NAT_OA_DRAFT) {
+ LOG_DBG((LOG_MESSAGE, 30, "message_parse_payloads: "
+ "private next payload type %s in payload of "
+ "type %d ignored",
+ constant_name(isakmp_payload_cst, next), payload));
+ goto next_payload;
+ }
+ /*
+ * Check if the current payload is one of the accepted ones at
+ * this stage.
+ */
+ if (!ISSET(payload, accepted_payloads)) {
+ log_print("message_parse_payloads: payload type %s "
+ "unexpected", constant_name(isakmp_payload_cst,
+ payload));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE,
+ 0, 1, 1);
+ return -1;
+ }
+ /* Call the payload handler specified by the caller. */
+ if (func(msg, p, payload, buf))
+ return -1;
+
+next_payload:
+ /* Advance to next payload. */
+ buf += len;
+ sz += len;
+ }
+ while (next != ISAKMP_PAYLOAD_NONE);
+ return sz;
+}
+
+/*
+ * Parse a proposal payload found in message MSG. PAYLOAD is always
+ * ISAKMP_PAYLOAD_PROPOSAL and ignored in here. It's needed as the API for
+ * message_parse_payloads requires it. BUF points to the proposal's
+ * generic payload header.
+ */
+static int
+message_parse_proposal(struct message *msg, struct payload *p,
+ u_int8_t payload, u_int8_t *buf)
+{
+ set payload_set;
+
+ /* Put the proposal into the proposal bucket. */
+ message_index_payload(msg, p, payload, buf);
+
+ ZERO(&payload_set);
+ SET(payload_revmap[ISAKMP_PAYLOAD_TRANSFORM], &payload_set);
+ if (message_parse_payloads(msg,
+ payload_last(msg, ISAKMP_PAYLOAD_PROPOSAL),
+ ISAKMP_PAYLOAD_TRANSFORM, buf + ISAKMP_PROP_SPI_OFF +
+ GET_ISAKMP_PROP_SPI_SZ(buf), &payload_set, message_parse_transform)
+ == -1)
+ return -1;
+
+ return 0;
+}
+
+static int
+message_parse_transform(struct message *msg, struct payload *p,
+ u_int8_t payload, u_int8_t *buf)
+{
+ /* Put the transform into the transform bucket. */
+ message_index_payload(msg, p, payload, buf);
+
+ LOG_DBG((LOG_MESSAGE, 50, "Transform %d's attributes",
+ GET_ISAKMP_TRANSFORM_NO(buf)));
+#ifdef USE_DEBUG
+ attribute_map(buf + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ GET_ISAKMP_GEN_LENGTH(buf) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ msg->exchange->doi->debug_attribute, msg);
+#endif
+
+ return 0;
+}
+
+/* Check payloads for their required minimum size. */
+static u_int16_t
+message_payload_sz(u_int8_t payload)
+{
+ switch (payload) {
+ case ISAKMP_PAYLOAD_SA:
+ return ISAKMP_SA_SZ;
+ case ISAKMP_PAYLOAD_PROPOSAL:
+ return ISAKMP_PROP_SZ;
+ case ISAKMP_PAYLOAD_TRANSFORM:
+ return ISAKMP_TRANSFORM_SZ;
+ case ISAKMP_PAYLOAD_KEY_EXCH:
+ return ISAKMP_KE_SZ;
+ case ISAKMP_PAYLOAD_ID:
+ return ISAKMP_ID_SZ;
+ case ISAKMP_PAYLOAD_CERT:
+ return ISAKMP_CERT_SZ;
+ case ISAKMP_PAYLOAD_CERT_REQ:
+ return ISAKMP_CERTREQ_SZ;
+ case ISAKMP_PAYLOAD_HASH:
+ return ISAKMP_HASH_SZ;
+ case ISAKMP_PAYLOAD_SIG:
+ return ISAKMP_SIG_SZ;
+ case ISAKMP_PAYLOAD_NONCE:
+ return ISAKMP_NONCE_SZ;
+ case ISAKMP_PAYLOAD_NOTIFY:
+ return ISAKMP_NOTIFY_SZ;
+ case ISAKMP_PAYLOAD_DELETE:
+ return ISAKMP_DELETE_SZ;
+ case ISAKMP_PAYLOAD_VENDOR:
+ return ISAKMP_VENDOR_SZ;
+ case ISAKMP_PAYLOAD_ATTRIBUTE:
+ return ISAKMP_ATTRIBUTE_SZ;
+#if defined (USE_NAT_TRAVERSAL)
+ case ISAKMP_PAYLOAD_NAT_D:
+ case ISAKMP_PAYLOAD_NAT_D_DRAFT:
+ return ISAKMP_NAT_D_SZ;
+ case ISAKMP_PAYLOAD_NAT_OA:
+ case ISAKMP_PAYLOAD_NAT_OA_DRAFT:
+ return ISAKMP_NAT_OA_SZ;
+#endif
+ /* Not yet supported and any other unknown payloads. */
+ case ISAKMP_PAYLOAD_SAK:
+ case ISAKMP_PAYLOAD_SAT:
+ case ISAKMP_PAYLOAD_KD:
+ case ISAKMP_PAYLOAD_SEQ:
+ case ISAKMP_PAYLOAD_POP:
+ default:
+ return 0;
+ }
+}
+
+/* Validate the attribute payload P in message MSG. */
+static int
+message_validate_attribute(struct message *msg, struct payload *p)
+{
+#ifdef USE_ISAKMP_CFG
+ /* If we don't have an exchange yet, create one. */
+ if (!msg->exchange) {
+ if (zero_test((u_int8_t *) msg->iov[0].iov_base +
+ ISAKMP_HDR_MESSAGE_ID_OFF, ISAKMP_HDR_MESSAGE_ID_LEN))
+ msg->exchange = exchange_setup_p1(msg,
+ IPSEC_DOI_IPSEC);
+ else
+ msg->exchange = exchange_setup_p2(msg,
+ IPSEC_DOI_IPSEC);
+ if (!msg->exchange) {
+ log_print("message_validate_attribute: can not "
+ "create exchange");
+ message_free(msg);
+ return -1;
+ }
+ }
+#endif
+ return 0;
+}
+
+/* Validate the certificate payload P in message MSG. */
+static int
+message_validate_cert(struct message *msg, struct payload *p)
+{
+ if (GET_ISAKMP_CERT_ENCODING(p->p) >= ISAKMP_CERTENC_RESERVED_MIN) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_CERT_ENCODING, 0, 1,
+ 1);
+ return -1;
+ }
+ return 0;
+}
+
+/* Validate the certificate request payload P in message MSG. */
+static int
+message_validate_cert_req(struct message *msg, struct payload *p)
+{
+ struct cert_handler *cert;
+ size_t len =
+ GET_ISAKMP_GEN_LENGTH(p->p) - ISAKMP_CERTREQ_AUTHORITY_OFF;
+
+ if (GET_ISAKMP_CERTREQ_TYPE(p->p) >= ISAKMP_CERTENC_RESERVED_MIN) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_CERT_ENCODING, 0, 1,
+ 1);
+ return -1;
+ }
+ /*
+ * Check the certificate types we support and if an acceptable
+ * authority is included in the payload check if it can be decoded
+ */
+ cert = cert_get(GET_ISAKMP_CERTREQ_TYPE(p->p));
+ if (!cert || (len && !cert->certreq_validate(p->p +
+ ISAKMP_CERTREQ_AUTHORITY_OFF, len))) {
+ message_drop(msg, ISAKMP_NOTIFY_CERT_TYPE_UNSUPPORTED, 0, 1,
+ 1);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Validate the delete payload P in message MSG. As a side-effect, create
+ * an exchange if we do not have one already.
+ *
+ * Note: DELETEs are only accepted as part of an INFORMATIONAL exchange.
+ * exchange_validate() makes sure a HASH payload is present. Due to the order
+ * of message validation functions in message_validate_payload[] we can be
+ * sure that the HASH payload has been successfully validated at this point.
+ */
+static int
+message_validate_delete(struct message *msg, struct payload *p)
+{
+ u_int8_t proto = GET_ISAKMP_DELETE_PROTO(p->p);
+ struct doi *doi;
+ struct sa *sa, *isakmp_sa;
+ struct sockaddr *dst, *dst_isa;
+ u_int32_t nspis = GET_ISAKMP_DELETE_NSPIS(p->p);
+ u_int8_t *spis = (u_int8_t *)p->p + ISAKMP_DELETE_SPI_OFF;
+ u_int32_t i;
+ char *addr;
+
+ /* Only accept authenticated DELETEs. */
+ if ((msg->flags & MSG_AUTHENTICATED) == 0) {
+ log_print("message_validate_delete: "
+ "got unauthenticated DELETE");
+ message_free(msg);
+ return -1;
+ }
+
+ doi = doi_lookup(GET_ISAKMP_DELETE_DOI(p->p));
+ if (!doi) {
+ log_print("message_validate_delete: DOI not supported");
+ message_free(msg);
+ return -1;
+ }
+ /* If we don't have an exchange yet, create one. */
+ if (!msg->exchange) {
+ if (zero_test((u_int8_t *) msg->iov[0].iov_base
+ + ISAKMP_HDR_MESSAGE_ID_OFF, ISAKMP_HDR_MESSAGE_ID_LEN))
+ msg->exchange = exchange_setup_p1(msg, doi->id);
+ else
+ msg->exchange = exchange_setup_p2(msg, doi->id);
+ if (!msg->exchange) {
+ log_print("message_validate_delete: can not create "
+ "exchange");
+ message_free(msg);
+ return -1;
+ }
+ }
+ /* Only accept DELETE as part of an INFORMATIONAL exchange. */
+ if (msg->exchange->type != ISAKMP_EXCH_INFO) {
+ log_print("message_validate_delete: delete in exchange other "
+ "than INFO: %s", constant_name(isakmp_exch_cst,
+ msg->exchange->type));
+ message_free(msg);
+ return -1;
+ }
+ if (proto != ISAKMP_PROTO_ISAKMP && doi->validate_proto(proto)) {
+ log_print("message_validate_delete: protocol not supported");
+ message_free(msg);
+ return -1;
+ }
+ /* Validate the SPIs. */
+ for (i = 0; i < nspis; i++) {
+ /* Get ISAKMP SA protecting this message. */
+ isakmp_sa = msg->isakmp_sa;
+ if (!isakmp_sa) {
+ /* XXX should not happen? */
+ log_print("message_validate_delete: invalid spi (no "
+ "valid ISAKMP SA found)");
+ message_free(msg);
+ return -1;
+ }
+ isakmp_sa->transport->vtbl->get_dst(isakmp_sa->transport,
+ &dst_isa);
+
+ /* Get SA to be deleted. */
+ msg->transport->vtbl->get_dst(msg->transport, &dst);
+ if (proto == ISAKMP_PROTO_ISAKMP)
+ sa = sa_lookup_isakmp_sa(dst, spis + i
+ * ISAKMP_HDR_COOKIES_LEN);
+ else
+ sa = ipsec_sa_lookup(dst, ((u_int32_t *) spis)[i],
+ proto);
+ if (!sa) {
+ LOG_DBG((LOG_MESSAGE, 50, "message_validate_delete: "
+ "invalid spi (no valid SA found)"));
+ message_free(msg);
+ return -1;
+ }
+ sa->transport->vtbl->get_dst(sa->transport, &dst);
+
+ /* Destination addresses must match. */
+ if (dst->sa_family != dst_isa->sa_family ||
+ memcmp(sockaddr_addrdata(dst_isa), sockaddr_addrdata(dst),
+ sockaddr_addrlen(dst))) {
+ sockaddr2text(dst_isa, &addr, 0);
+
+ log_print("message_validate_delete: invalid spi "
+ "(illegal delete request from %s)", addr);
+ free(addr);
+ message_free(msg);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Validate the hash payload P in message MSG.
+ * XXX Currently hash payloads are processed by the particular exchanges,
+ * except INFORMATIONAL. This should be actually done here.
+ */
+static int
+message_validate_hash(struct message *msg, struct payload *p)
+{
+ struct sa *isakmp_sa = msg->isakmp_sa;
+ struct ipsec_sa *isa;
+ struct hash *hash;
+ struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH);
+ struct prf *prf;
+ u_int8_t *comp_hash, *rest;
+ u_int8_t message_id[ISAKMP_HDR_MESSAGE_ID_LEN];
+ size_t rest_len;
+
+ /* active exchanges other than INFORMATIONAL validates hash payload. */
+ if (msg->exchange && (msg->exchange->type != ISAKMP_EXCH_INFO))
+ return 0;
+
+ if (isakmp_sa == NULL) {
+ log_print("message_validate_hash: invalid hash information");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
+ 0, 1, 1);
+ return -1;
+ }
+ isa = isakmp_sa->data;
+ hash = hash_get(isa->hash);
+
+ if (hash == NULL) {
+ log_print("message_validate_hash: invalid hash information");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
+ 0, 1, 1);
+ return -1;
+ }
+ /* If no SKEYID_a, we can not do anything (should not happen). */
+ if (!isa->skeyid_a) {
+ log_print("message_validate_hash: invalid hash information");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
+ 0, 1, 1);
+ return -1;
+ }
+ /* Allocate the prf and start calculating our HASH(1). */
+ LOG_DBG_BUF((LOG_MISC, 90, "message_validate_hash: SKEYID_a",
+ isa->skeyid_a, isa->skeyid_len));
+ prf = prf_alloc(isa->prf_type, hash->type, isa->skeyid_a,
+ isa->skeyid_len);
+ if (!prf) {
+ message_free(msg);
+ return -1;
+ }
+ comp_hash = (u_int8_t *)malloc(hash->hashsize);
+ if (!comp_hash) {
+ log_error("message_validate_hash: malloc (%lu) failed",
+ (unsigned long)hash->hashsize);
+ prf_free(prf);
+ message_free(msg);
+ return -1;
+ }
+ /* This is not an active exchange. */
+ GET_ISAKMP_HDR_MESSAGE_ID(msg->iov[0].iov_base, message_id);
+
+ prf->Init(prf->prfctx);
+ LOG_DBG_BUF((LOG_MISC, 90, "message_validate_hash: message_id",
+ message_id, ISAKMP_HDR_MESSAGE_ID_LEN));
+ prf->Update(prf->prfctx, message_id, ISAKMP_HDR_MESSAGE_ID_LEN);
+ rest = hashp->p + GET_ISAKMP_GEN_LENGTH(hashp->p);
+ rest_len = (GET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base) - (rest -
+ (u_int8_t *)msg->iov[0].iov_base));
+ LOG_DBG_BUF((LOG_MISC, 90,
+ "message_validate_hash: payloads after HASH(1)", rest, rest_len));
+ prf->Update(prf->prfctx, rest, rest_len);
+ prf->Final(comp_hash, prf->prfctx);
+ prf_free(prf);
+
+ if (memcmp(hashp->p + ISAKMP_HASH_DATA_OFF, comp_hash,
+ hash->hashsize)) {
+ log_print("message_validate_hash: invalid hash value for %s "
+ "payload", payload_first(msg, ISAKMP_PAYLOAD_DELETE) ?
+ "DELETE" : "NOTIFY");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION,
+ 0, 1, 1);
+ free(comp_hash);
+ return -1;
+ }
+ free(comp_hash);
+
+ /* Mark the HASH as handled. */
+ hashp->flags |= PL_MARK;
+
+ /* Mark message as authenticated. */
+ msg->flags |= MSG_AUTHENTICATED;
+
+ return 0;
+}
+
+/* Validate the identification payload P in message MSG. */
+static int
+message_validate_id(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+ size_t len = GET_ISAKMP_GEN_LENGTH(p->p);
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_id: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ if (exchange->doi
+ && exchange->doi->validate_id_information(GET_ISAKMP_ID_TYPE(p->p),
+ p->p + ISAKMP_ID_DOI_DATA_OFF, p->p + ISAKMP_ID_DATA_OFF, len -
+ ISAKMP_ID_DATA_OFF, exchange)) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1,
+ 1);
+ return -1;
+ }
+ return 0;
+}
+
+/* Validate the key exchange payload P in message MSG. */
+static int
+message_validate_key_exch(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+ size_t len = GET_ISAKMP_GEN_LENGTH(p->p);
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_key_exch: "
+ "payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ if (exchange->doi && exchange->doi->validate_key_information(p->p +
+ ISAKMP_KE_DATA_OFF, len - ISAKMP_KE_DATA_OFF)) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_KEY_INFORMATION,
+ 0, 1, 1);
+ return -1;
+ }
+ return 0;
+}
+
+/* Validate the NAT-D payload P in message MSG. */
+static int
+message_validate_nat_d(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_nat_d: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+ if (exchange->phase != 1) {
+ log_print("message_validate_nat_d: "
+ "NAT-D payload must be in phase 1");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+ /* Mark as handled. */
+ p->flags |= PL_MARK;
+
+ return 0;
+}
+
+/* Validate the NAT-OA payload P in message MSG. */
+static int
+message_validate_nat_oa(struct message *msg, struct payload *p)
+{
+ struct exchange *exchange = msg->exchange;
+
+ if (!exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_nat_d: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+
+#ifdef notyet /* XXX Probably never, due to patent issues. */
+ /* Mark as handled. */
+ p->flags |= PL_MARK;
+#endif
+
+ return 0;
+}
+
+/* Validate the nonce payload P in message MSG. */
+static int
+message_validate_nonce(struct message *msg, struct payload *p)
+{
+ if (!msg->exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_nonce: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ /* Nonces require no specific validation. */
+ return 0;
+}
+
+/*
+ * Validate the notify payload P in message MSG. As a side-effect, create
+ * an exchange if we do not have one already.
+ */
+static int
+message_validate_notify(struct message *msg, struct payload *p)
+{
+ u_int8_t proto = GET_ISAKMP_NOTIFY_PROTO(p->p);
+ u_int16_t type = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p);
+ struct doi *doi;
+
+ doi = doi_lookup(GET_ISAKMP_NOTIFY_DOI(p->p));
+ if (!doi) {
+ log_print("message_validate_notify: DOI not supported");
+ message_free(msg);
+ return -1;
+ }
+ /* If we don't have an exchange yet, create one. */
+ if (!msg->exchange) {
+ if (zero_test((u_int8_t *) msg->iov[0].iov_base +
+ ISAKMP_HDR_MESSAGE_ID_OFF, ISAKMP_HDR_MESSAGE_ID_LEN))
+ msg->exchange = exchange_setup_p1(msg, doi->id);
+ else
+ msg->exchange = exchange_setup_p2(msg, doi->id);
+ if (!msg->exchange) {
+ log_print("message_validate_notify: can not create "
+ "exchange");
+ message_free(msg);
+ return -1;
+ }
+ }
+ if (proto != ISAKMP_PROTO_ISAKMP && doi->validate_proto(proto)) {
+ log_print("message_validate_notify: protocol not supported");
+ message_free(msg);
+ return -1;
+ }
+
+ /* Validate the SPI. XXX Just ISAKMP for now. */
+ if (proto == ISAKMP_PROTO_ISAKMP &&
+ GET_ISAKMP_NOTIFY_SPI_SZ(p->p) == ISAKMP_HDR_COOKIES_LEN &&
+ msg->isakmp_sa &&
+ memcmp(p->p + ISAKMP_NOTIFY_SPI_OFF, msg->isakmp_sa->cookies,
+ ISAKMP_HDR_COOKIES_LEN) != 0) {
+ log_print("message_validate_notify: bad cookies");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_SPI, 0, 1, 0);
+ return -1;
+ }
+
+ if (type < ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE
+ || (type >= ISAKMP_NOTIFY_RESERVED_MIN
+ && type < ISAKMP_NOTIFY_PRIVATE_MIN)
+ || (type >= ISAKMP_NOTIFY_STATUS_RESERVED1_MIN
+ && type <= ISAKMP_NOTIFY_STATUS_RESERVED1_MAX)
+ || (type >= ISAKMP_NOTIFY_STATUS_DOI_MIN
+ && type <= ISAKMP_NOTIFY_STATUS_DOI_MAX
+ && doi->validate_notification(type))
+ || type >= ISAKMP_NOTIFY_STATUS_RESERVED2_MIN) {
+ log_print("message_validate_notify: "
+ "message type not supported");
+ message_free(msg);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Validate the proposal payload P in message MSG. */
+static int
+message_validate_proposal(struct message *msg, struct payload *p)
+{
+ u_int8_t proto = GET_ISAKMP_PROP_PROTO(p->p);
+ u_int8_t *sa = p->context->p;
+
+ if (!msg->exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_proposal: "
+ "payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ if (proto != ISAKMP_PROTO_ISAKMP
+ && msg->exchange->doi->validate_proto(proto)) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_PROTOCOL_ID, 0, 1, 1);
+ return -1;
+ }
+ /* Check that we get monotonically increasing proposal IDs per SA. */
+ if (sa != last_sa)
+ last_sa = sa;
+ else if (GET_ISAKMP_PROP_NO(p->p) < last_prop_no) {
+ message_drop(msg, ISAKMP_NOTIFY_BAD_PROPOSAL_SYNTAX, 0, 1, 1);
+ return -1;
+ }
+ last_prop_no = GET_ISAKMP_PROP_NO(p->p);
+
+ /* XXX Validate the SPI, and other syntactic things. */
+
+ return 0;
+}
+
+/*
+ * Validate the SA payload P in message MSG.
+ * Aside from normal validation, note what DOI is in use for other
+ * validation routines to look at. Also index the proposal payloads
+ * on the fly.
+ * XXX This assumes PAYLOAD_SA is always the first payload
+ * to be validated, which is true for IKE, except for quick mode where
+ * a PAYLOAD_HASH comes first, but in that specific case it does not matter.
+ * XXX Make sure the above comment is relevant, isn't SA always checked
+ * first due to the IANA assigned payload number?
+ */
+static int
+message_validate_sa(struct message *msg, struct payload *p)
+{
+ set payload_set;
+ size_t len;
+ u_int32_t doi_id;
+ struct exchange *exchange = msg->exchange;
+ u_int8_t *pkt = msg->iov[0].iov_base;
+
+ doi_id = GET_ISAKMP_SA_DOI(p->p);
+ if (!doi_lookup(doi_id)) {
+ log_print("message_validate_sa: DOI not supported");
+ message_drop(msg, ISAKMP_NOTIFY_DOI_NOT_SUPPORTED, 0, 1, 1);
+ return -1;
+ }
+ /*
+ * It's time to figure out what SA this message is about. If it is
+ * already set, then we are creating a new phase 1 SA. Otherwise,
+ * lookup the SA using the cookies and the message ID. If we cannot
+ * find it, and the phase 1 SA is ready, setup a phase 2 SA.
+ */
+ if (!exchange) {
+ if (zero_test(pkt + ISAKMP_HDR_RCOOKIE_OFF,
+ ISAKMP_HDR_RCOOKIE_LEN))
+ exchange = exchange_setup_p1(msg, doi_id);
+ else if (msg->isakmp_sa->flags & SA_FLAG_READY)
+ exchange = exchange_setup_p2(msg, doi_id);
+ else {
+ /* XXX What to do here? */
+ message_free(msg);
+ return -1;
+ }
+ if (!exchange) {
+ /* XXX Log? */
+ message_free(msg);
+ return -1;
+ }
+ }
+ msg->exchange = exchange;
+
+ /*
+ * Create a struct sa for each SA payload handed to us unless we are
+ * the initiator where we only will count them.
+ */
+ if (exchange->initiator) {
+ /* XXX Count SA payloads. */
+ } else if (sa_create(exchange, msg->transport)) {
+ /* XXX Remove exchange if we just created it? */
+ message_free(msg);
+ return -1;
+ }
+ if (exchange->phase == 1) {
+ msg->isakmp_sa = TAILQ_FIRST(&exchange->sa_list);
+ if (msg->isakmp_sa)
+ sa_reference(msg->isakmp_sa);
+ }
+ /*
+ * Let the DOI validate the situation, at the same time it tells us
+ * what the length of the situation field is.
+ */
+ if (exchange->doi->validate_situation(p->p + ISAKMP_SA_SIT_OFF, &len,
+ GET_ISAKMP_GEN_LENGTH(p->p) - ISAKMP_SA_SIT_OFF)) {
+ log_print("message_validate_sa: situation not supported");
+ message_drop(msg, ISAKMP_NOTIFY_SITUATION_NOT_SUPPORTED,
+ 0, 1, 1);
+ return -1;
+ }
+ /*
+ * Reset the fields we base our proposal & transform number checks
+ * on.
+ */
+ last_sa = last_prop = 0;
+ last_prop_no = last_xf_no = 0;
+
+ /* Go through the PROPOSAL payloads. */
+ ZERO(&payload_set);
+ SET(payload_revmap[ISAKMP_PAYLOAD_PROPOSAL], &payload_set);
+ if (message_parse_payloads(msg, p, ISAKMP_PAYLOAD_PROPOSAL,
+ p->p + ISAKMP_SA_SIT_OFF + len, &payload_set,
+ message_parse_proposal) == -1)
+ return -1;
+
+ return 0;
+}
+
+/* Validate the signature payload P in message MSG. */
+static int
+message_validate_sig(struct message *msg, struct payload *p)
+{
+ if (!msg->exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_sig: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ /* XXX Not implemented yet. */
+ return 0;
+}
+
+/* Validate the transform payload P in message MSG. */
+static int
+message_validate_transform(struct message *msg, struct payload *p)
+{
+ u_int8_t proto = GET_ISAKMP_PROP_PROTO(p->context->p);
+ u_int8_t *prop = p->context->p;
+
+ if (!msg->exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_transform: "
+ "payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ if (msg->exchange->doi
+ ->validate_transform_id(proto, GET_ISAKMP_TRANSFORM_ID(p->p))) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_TRANSFORM_ID, 0, 1, 1);
+ return -1;
+ }
+ /* Check that the reserved field is zero. */
+ if (!zero_test(p->p + ISAKMP_TRANSFORM_RESERVED_OFF,
+ ISAKMP_TRANSFORM_RESERVED_LEN)) {
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ /*
+ * Check that we get monotonically increasing transform numbers per
+ * proposal.
+ */
+ if (prop != last_prop)
+ last_prop = prop;
+ else if (GET_ISAKMP_TRANSFORM_NO(p->p) <= last_xf_no) {
+ message_drop(msg, ISAKMP_NOTIFY_BAD_PROPOSAL_SYNTAX, 0, 1, 1);
+ return -1;
+ }
+ last_xf_no = GET_ISAKMP_TRANSFORM_NO(p->p);
+
+ /* Validate the attributes. */
+ if (attribute_map(p->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ GET_ISAKMP_GEN_LENGTH(p->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ msg->exchange->doi->validate_attribute, msg)) {
+ message_drop(msg, ISAKMP_NOTIFY_ATTRIBUTES_NOT_SUPPORTED,
+ 0, 1, 1);
+ return -1;
+ }
+ return 0;
+}
+
+/* Validate the vendor payload P in message MSG. */
+static int
+message_validate_vendor(struct message *msg, struct payload *p)
+{
+ if (!msg->exchange) {
+ /* We should have an exchange at this point. */
+ log_print("message_validate_vendor: payload out of sequence");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ return -1;
+ }
+ /* Vendor IDs are only allowed in phase 1. */
+ if (msg->exchange->phase != 1) {
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 1, 1);
+ return -1;
+ }
+#if defined (USE_DPD)
+ dpd_check_vendor_payload(msg, p);
+#endif
+#if defined (USE_NAT_TRAVERSAL)
+ nat_t_check_vendor_payload(msg, p);
+#endif
+ if (!(p->flags & PL_MARK))
+ LOG_DBG((LOG_MESSAGE, 40, "message_validate_vendor: "
+ "vendor ID seen"));
+ return 0;
+}
+
+/*
+ * Add an index-record pointing to the payload at BUF in message MSG
+ * to the PAYLOAD bucket of payloads. This allows us to quickly reference
+ * payloads by type. Also stash the parent payload P link into the new
+ * node so we can go from transforms -> payloads -> SAs.
+ */
+static int
+message_index_payload(struct message *msg, struct payload *p, u_int8_t payload,
+ u_int8_t *buf)
+{
+ struct payload *payload_node;
+
+ /* Put the payload pointer into the right bucket. */
+ payload_node = malloc(sizeof *payload_node);
+ if (!payload_node)
+ return -1;
+ payload_node->p = buf;
+ payload_node->context = p;
+ payload_node->flags = 0;
+ TAILQ_INSERT_TAIL(&msg->payload[payload_map[payload]], payload_node,
+ link);
+ return 0;
+}
+
+/*
+ * Group each payload found in MSG by type for easy reference later.
+ * While doing this, validate the generic parts of the message structure too.
+ * NEXT is the 1st payload's type. This routine will also register the
+ * computed message length (i.e. without padding) in msg->iov[0].iov_len.
+ */
+static int
+message_sort_payloads(struct message *msg, u_int8_t next)
+{
+ set payload_set;
+ int i, sz;
+
+ ZERO(&payload_set);
+ for (i = ISAKMP_PAYLOAD_SA; i < payload_index_max; i++)
+ if (i != ISAKMP_PAYLOAD_PROPOSAL && i !=
+ ISAKMP_PAYLOAD_TRANSFORM)
+ SET(payload_revmap[i], &payload_set);
+ sz = message_parse_payloads(msg, 0, next,
+ (u_int8_t *)msg->iov[0].iov_base + ISAKMP_HDR_SZ, &payload_set,
+ message_index_payload);
+ if (sz == -1)
+ return -1;
+ msg->iov[0].iov_len = ISAKMP_HDR_SZ + sz;
+ SET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base, ISAKMP_HDR_SZ + sz);
+ return 0;
+}
+
+/* Run all the generic payload tests that the drafts specify. */
+static int
+message_validate_payloads(struct message *msg)
+{
+ int i;
+ struct payload *p;
+
+ for (i = ISAKMP_PAYLOAD_SA; i < payload_index_max; i++)
+ for (p = payload_first(msg, i); p; p = TAILQ_NEXT(p, link)) {
+ LOG_DBG((LOG_MESSAGE, 60, "message_validate_payloads: "
+ "payload %s at %p of message %p",
+ constant_name(isakmp_payload_cst, i), p->p, msg));
+ field_dump_payload(fields[i - ISAKMP_PAYLOAD_SA],
+ p->p);
+ if (message_validate_payload[i - ISAKMP_PAYLOAD_SA]
+ (msg, p))
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * All incoming messages go through here. We do generic validity checks
+ * and try to find or establish SAs. Last but not least we try to find
+ * the exchange this message, MSG, is part of, and feed it there.
+ */
+int
+message_recv(struct message *msg)
+{
+ u_int8_t *buf = msg->iov[0].iov_base;
+ size_t sz = msg->iov[0].iov_len;
+ u_int8_t exch_type;
+ int setup_isakmp_sa, msgid_is_zero;
+ u_int8_t flags;
+ struct keystate *ks = 0;
+ struct proto tmp_proto;
+ struct sa tmp_sa;
+
+ /* Messages shorter than an ISAKMP header are bad. */
+ if (sz < ISAKMP_HDR_SZ || sz != GET_ISAKMP_HDR_LENGTH(buf)) {
+ log_print("message_recv: bad message length");
+ message_drop(msg, ISAKMP_NOTIFY_UNEQUAL_PAYLOAD_LENGTHS,
+ 0, 1, 1);
+ return -1;
+ }
+#ifdef USE_DEBUG
+ /* Possibly dump a raw hex image of the message to the log channel. */
+ message_dump_raw("message_recv", msg, LOG_MESSAGE);
+#endif
+
+ /*
+ * If the responder cookie is zero, this is a request to setup an
+ * ISAKMP SA. Otherwise the cookies should refer to an existing
+ * ISAKMP SA.
+ *
+ * XXX This is getting ugly, please reread later to see if it can be
+ * made nicer.
+ */
+ setup_isakmp_sa = zero_test(buf + ISAKMP_HDR_RCOOKIE_OFF,
+ ISAKMP_HDR_RCOOKIE_LEN);
+ if (setup_isakmp_sa) {
+ /*
+ * This might be a retransmission of a former ISAKMP SA setup
+ * message. If so, just drop it.
+ * XXX Must we really look in both the SA and exchange pools?
+ */
+ if (exchange_lookup_from_icookie(buf + ISAKMP_HDR_ICOOKIE_OFF)
+ || sa_lookup_from_icookie(buf + ISAKMP_HDR_ICOOKIE_OFF)) {
+ /*
+ * XXX Later we should differentiate between
+ * retransmissions and potential replay attacks.
+ */
+ LOG_DBG((LOG_MESSAGE, 90,
+ "message_recv: dropping setup for existing SA"));
+ message_free(msg);
+ return -1;
+ }
+ } else {
+ msg->isakmp_sa = sa_lookup_by_header(buf, 0);
+ if (msg->isakmp_sa)
+ sa_reference(msg->isakmp_sa);
+
+ /*
+ * If we cannot find an ISAKMP SA out of the cookies, this is
+ * either a responder's first reply, and we need to upgrade
+ * our exchange, or it's just plain invalid cookies.
+ */
+ if (!msg->isakmp_sa) {
+ msg->exchange = exchange_lookup_from_icookie(buf +
+ ISAKMP_HDR_ICOOKIE_OFF);
+ if (msg->exchange && msg->exchange->phase == 1
+ && zero_test(msg->exchange->cookies +
+ ISAKMP_HDR_RCOOKIE_OFF, ISAKMP_HDR_RCOOKIE_LEN))
+ exchange_upgrade_p1(msg);
+ else {
+ log_print("message_recv: invalid cookie(s) "
+ "%08x%08x %08x%08x",
+ decode_32(buf + ISAKMP_HDR_ICOOKIE_OFF),
+ decode_32(buf + ISAKMP_HDR_ICOOKIE_OFF + 4),
+ decode_32(buf + ISAKMP_HDR_RCOOKIE_OFF),
+ decode_32(buf + ISAKMP_HDR_RCOOKIE_OFF + 4));
+ tmp_proto.sa = &tmp_sa;
+ tmp_sa.doi = doi_lookup(ISAKMP_DOI_ISAKMP);
+ tmp_proto.proto = ISAKMP_PROTO_ISAKMP;
+ tmp_proto.spi_sz[1] = ISAKMP_HDR_COOKIES_LEN;
+ tmp_proto.spi[1] =
+ buf + ISAKMP_HDR_COOKIES_OFF;
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_COOKIE,
+ &tmp_proto, 1, 1);
+ return -1;
+ }
+#if 0
+ msg->isakmp_sa = sa_lookup_from_icookie(buf +
+ ISAKMP_HDR_ICOOKIE_OFF);
+ if (msg->isakmp_sa)
+ sa_isakmp_upgrade(msg);
+#endif
+ }
+ msg->exchange = exchange_lookup(buf, 1);
+ }
+
+ if (message_check_duplicate(msg))
+ return -1;
+
+ if (GET_ISAKMP_HDR_NEXT_PAYLOAD(buf) >= ISAKMP_PAYLOAD_RESERVED_MIN) {
+ log_print("message_recv: invalid payload type %d in ISAKMP "
+ "header (check passphrases, if applicable and in Phase 1)",
+ GET_ISAKMP_HDR_NEXT_PAYLOAD(buf));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_PAYLOAD_TYPE, 0, 1, 1);
+ return -1;
+ }
+ /* Validate that the message is of version 1.0. */
+ if (ISAKMP_VERSION_MAJOR(GET_ISAKMP_HDR_VERSION(buf)) != 1) {
+ log_print("message_recv: invalid version major %d",
+ ISAKMP_VERSION_MAJOR(GET_ISAKMP_HDR_VERSION(buf)));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_MAJOR_VERSION, 0, 1,
+ 1);
+ return -1;
+ }
+ if (ISAKMP_VERSION_MINOR(GET_ISAKMP_HDR_VERSION(buf)) != 0) {
+ log_print("message_recv: invalid version minor %d",
+ ISAKMP_VERSION_MINOR(GET_ISAKMP_HDR_VERSION(buf)));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_MINOR_VERSION, 0, 1,
+ 1);
+ return -1;
+ }
+ /*
+ * Validate the exchange type. If it's a DOI-specified exchange wait
+ * until after all payloads have been seen for the validation as the
+ * SA payload might not yet have been parsed, thus the DOI might be
+ * unknown.
+ */
+ exch_type = GET_ISAKMP_HDR_EXCH_TYPE(buf);
+ if (exch_type == ISAKMP_EXCH_NONE
+ || (exch_type >= ISAKMP_EXCH_FUTURE_MIN &&
+ exch_type <= ISAKMP_EXCH_FUTURE_MAX)
+ || (setup_isakmp_sa && exch_type >= ISAKMP_EXCH_DOI_MIN)) {
+ log_print("message_recv: invalid exchange type %s",
+ constant_name(isakmp_exch_cst, exch_type));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 1,
+ 1);
+ return -1;
+ }
+ /*
+ * Check for unrecognized flags, or the encryption flag when we don't
+ * have an ISAKMP SA to decrypt with.
+ */
+ flags = GET_ISAKMP_HDR_FLAGS(buf);
+ if (flags & ~(ISAKMP_FLAGS_ENC | ISAKMP_FLAGS_COMMIT |
+ ISAKMP_FLAGS_AUTH_ONLY)) {
+ log_print("message_recv: invalid flags 0x%x",
+ GET_ISAKMP_HDR_FLAGS(buf));
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_FLAGS, 0, 1, 1);
+ return -1;
+ }
+ /*
+ * If we are about to setup an ISAKMP SA, the message ID must be
+ * zero.
+ */
+ msgid_is_zero = zero_test(buf + ISAKMP_HDR_MESSAGE_ID_OFF,
+ ISAKMP_HDR_MESSAGE_ID_LEN);
+ if (setup_isakmp_sa && !msgid_is_zero) {
+ log_print("message_recv: invalid message id");
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_MESSAGE_ID, 0, 1, 1);
+ return -1;
+ }
+ if (!setup_isakmp_sa && msgid_is_zero) {
+ /*
+ * XXX Very likely redundant, look at the else clause of the
+ * if (setup_isakmp_sa) statement above.
+ */
+ msg->exchange = exchange_lookup(buf, 0);
+ if (!msg->exchange) {
+ log_print("message_recv: phase 1 message after "
+ "ISAKMP SA is ready");
+ message_free(msg);
+ return -1;
+ } else if (msg->exchange->last_sent) {
+ LOG_DBG((LOG_MESSAGE, 80, "message_recv: resending "
+ "last message from phase 1"));
+ message_send(msg->exchange->last_sent);
+ }
+ }
+ if (flags & ISAKMP_FLAGS_ENC) {
+ if (!msg->isakmp_sa) {
+ LOG_DBG((LOG_MISC, 10, "message_recv: no isakmp_sa "
+ "for encrypted message"));
+ message_free(msg);
+ return -1;
+ }
+ /* Decrypt rest of message using a DOI-specified IV. */
+ ks = msg->isakmp_sa->doi->get_keystate(msg);
+ if (!ks) {
+ message_free(msg);
+ return -1;
+ }
+ msg->orig = malloc(sz);
+ if (!msg->orig) {
+ message_free(msg);
+ free(ks);
+ return -1;
+ }
+ memcpy(msg->orig, buf, sz);
+ crypto_decrypt(ks, buf + ISAKMP_HDR_SZ, sz - ISAKMP_HDR_SZ);
+ } else
+ msg->orig = buf;
+ msg->orig_sz = sz;
+
+ /* IKE packet capture */
+ message_packet_log(msg);
+
+ /*
+ * Check the overall payload structure at the same time as indexing
+ * them by type.
+ */
+ if (GET_ISAKMP_HDR_NEXT_PAYLOAD(buf) != ISAKMP_PAYLOAD_NONE
+ && message_sort_payloads(msg, GET_ISAKMP_HDR_NEXT_PAYLOAD(buf))) {
+ if (ks)
+ free(ks);
+ return -1;
+ }
+ /*
+ * Run generic payload tests now. If anything fails these checks, the
+ * message needs either to be retained for later duplicate checks or
+ * freed entirely.
+ * XXX Should SAs and even transports be cleaned up then too?
+ */
+ if (message_validate_payloads(msg)) {
+ if (ks)
+ free(ks);
+ return -1;
+ }
+ /*
+ * If we have not found an exchange by now something is definitely
+ * wrong.
+ */
+ if (!msg->exchange) {
+ log_print("message_recv: no exchange");
+ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 1);
+ if (ks)
+ free(ks);
+ return -1;
+ }
+ /*
+ * Now we can validate DOI-specific exchange types. If we have no SA
+ * DOI-specific exchange types are definitely wrong.
+ */
+ if (exch_type >= ISAKMP_EXCH_DOI_MIN
+ && exch_type <= ISAKMP_EXCH_DOI_MAX
+ && msg->exchange->doi->validate_exchange(exch_type)) {
+ log_print("message_recv: invalid DOI exchange type %d",
+ exch_type);
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_EXCHANGE_TYPE, 0, 1,
+ 1);
+ if (ks)
+ free(ks);
+ return -1;
+ }
+ /* Make sure the IV we used gets saved in the proper SA. */
+ if (ks) {
+ if (!msg->exchange->keystate) {
+ msg->exchange->keystate = ks;
+ msg->exchange->crypto = ks->xf;
+ } else
+ free(ks);
+ }
+ /* Handle the flags. */
+ if (flags & ISAKMP_FLAGS_ENC)
+ msg->exchange->flags |= EXCHANGE_FLAG_ENCRYPT;
+ if ((msg->exchange->flags & EXCHANGE_FLAG_COMMITTED) == 0
+ && (flags & ISAKMP_FLAGS_COMMIT))
+ msg->exchange->flags |= EXCHANGE_FLAG_HE_COMMITTED;
+
+ /*
+ * Except for the 3rd Aggressive Mode message, require encryption
+ * as soon as we have the keystate for it.
+ */
+ if ((flags & ISAKMP_FLAGS_ENC) == 0 &&
+ (msg->exchange->phase == 2 ||
+ (msg->exchange->keystate &&
+ msg->exchange->type != ISAKMP_EXCH_AGGRESSIVE))) {
+ log_print("message_recv: cleartext phase %d message",
+ msg->exchange->phase);
+ message_drop(msg, ISAKMP_NOTIFY_INVALID_FLAGS, 0, 1, 1);
+ return -1;
+ }
+
+ /* OK let the exchange logic do the rest. */
+ exchange_run(msg);
+
+ return 0;
+}
+
+void
+message_send_expire(struct message *msg)
+{
+ msg->retrans = 0;
+
+ message_send(msg);
+}
+
+/* Queue up message MSG for transmittal. */
+void
+message_send(struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ struct message *m;
+ struct msg_head *q;
+
+ /* Remove retransmissions on this message */
+ if (msg->retrans) {
+ timer_remove_event(msg->retrans);
+ msg->retrans = 0;
+ }
+ /* IKE packet capture */
+ message_packet_log(msg);
+
+ /*
+ * If the ISAKMP SA has set up encryption, encrypt the message.
+ * However, in a retransmit, it is already encrypted.
+ */
+ if ((msg->flags & MSG_ENCRYPTED) == 0
+ && exchange->flags & EXCHANGE_FLAG_ENCRYPT) {
+ if (!exchange->keystate) {
+ exchange->keystate = exchange->doi->get_keystate(msg);
+ if (!exchange->keystate)
+ return;
+ exchange->crypto = exchange->keystate->xf;
+ exchange->flags |= EXCHANGE_FLAG_ENCRYPT;
+ }
+ if (message_encrypt(msg)) {
+ /* XXX Log. */
+ return;
+ }
+ }
+ /* Keep the COMMIT bit on. */
+ if (exchange->flags & EXCHANGE_FLAG_COMMITTED)
+ SET_ISAKMP_HDR_FLAGS(msg->iov[0].iov_base,
+ GET_ISAKMP_HDR_FLAGS(msg->iov[0].iov_base)
+ | ISAKMP_FLAGS_COMMIT);
+
+#ifdef USE_DEBUG
+ message_dump_raw("message_send", msg, LOG_MESSAGE);
+#endif
+ msg->flags |= MSG_IN_TRANSIT;
+ exchange->in_transit = msg;
+
+ /*
+ * If we get a retransmission of a message before our response
+ * has left the queue, don't queue it again, as it will result
+ * in a circular list.
+ */
+ q = msg->transport->vtbl->get_queue(msg);
+ for (m = TAILQ_FIRST(q); m; m = TAILQ_NEXT(m, link))
+ if (m == msg) {
+ LOG_DBG((LOG_MESSAGE, 60,
+ "message_send: msg %p already on sendq %p", m, q));
+ return;
+ }
+ TAILQ_INSERT_TAIL(q, msg, link);
+}
+
+/*
+ * Setup the ISAKMP message header for message MSG. EXCHANGE is the exchange
+ * type, FLAGS are the ISAKMP header flags and MSG_ID is message ID
+ * identifying the exchange.
+ */
+void
+message_setup_header(struct message *msg, u_int8_t exchange, u_int8_t flags,
+ u_int8_t *msg_id)
+{
+ u_int8_t *buf = msg->iov[0].iov_base;
+
+ SET_ISAKMP_HDR_ICOOKIE(buf, msg->exchange->cookies);
+ SET_ISAKMP_HDR_RCOOKIE(buf, msg->exchange->cookies +
+ ISAKMP_HDR_ICOOKIE_LEN);
+ SET_ISAKMP_HDR_NEXT_PAYLOAD(buf, ISAKMP_PAYLOAD_NONE);
+ SET_ISAKMP_HDR_VERSION(buf, ISAKMP_VERSION_MAKE(1, 0));
+ SET_ISAKMP_HDR_EXCH_TYPE(buf, exchange);
+ SET_ISAKMP_HDR_FLAGS(buf, flags);
+ SET_ISAKMP_HDR_MESSAGE_ID(buf, msg_id);
+ SET_ISAKMP_HDR_LENGTH(buf, msg->iov[0].iov_len);
+}
+
+/*
+ * Add the payload of type PAYLOAD in BUF sized SZ to the MSG message.
+ * The caller thereby is released from the responsibility of freeing BUF,
+ * unless we return a failure of course. If LINK is set the former
+ * payload's "next payload" field to PAYLOAD.
+ *
+ * XXX We might want to resize the iov array several slots at a time.
+ */
+int
+message_add_payload(struct message *msg, u_int8_t payload, u_int8_t *buf,
+ size_t sz, int link)
+{
+ struct iovec *new_iov;
+ struct payload *payload_node;
+
+ payload_node = calloc(1, sizeof *payload_node);
+ if (!payload_node) {
+ log_error("message_add_payload: calloc (1, %lu) failed",
+ (unsigned long)sizeof *payload_node);
+ return -1;
+ }
+ new_iov = (struct iovec *) realloc(msg->iov, (msg->iovlen + 1) *
+ sizeof *msg->iov);
+ if (!new_iov) {
+ log_error("message_add_payload: realloc (%p, %lu) failed",
+ msg->iov, (msg->iovlen + 1) *
+ (unsigned long)sizeof *msg->iov);
+ free(payload_node);
+ return -1;
+ }
+ msg->iov = new_iov;
+ new_iov[msg->iovlen].iov_base = buf;
+ new_iov[msg->iovlen].iov_len = sz;
+ msg->iovlen++;
+ if (link)
+ *msg->nextp = payload;
+ msg->nextp = buf + ISAKMP_GEN_NEXT_PAYLOAD_OFF;
+ *msg->nextp = ISAKMP_PAYLOAD_NONE;
+ SET_ISAKMP_GEN_RESERVED(buf, 0);
+ SET_ISAKMP_GEN_LENGTH(buf, sz);
+ SET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base,
+ GET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base) + sz);
+
+ /*
+ * For the sake of exchange_validate we index the payloads even in
+ * outgoing messages, however context and flags are uninteresting in
+ * this situation.
+ */
+ payload_node->p = buf;
+ TAILQ_INSERT_TAIL(&msg->payload[payload_map[payload]], payload_node,
+ link);
+ return 0;
+}
+
+/* XXX Move up when ready. */
+struct info_args {
+ char discr;
+ u_int32_t doi;
+ u_int8_t proto;
+ u_int16_t spi_sz;
+ union {
+ struct {
+ u_int16_t msg_type;
+ u_int8_t *spi;
+ } n;
+ struct {
+ u_int16_t nspis;
+ u_int8_t *spis;
+ } d;
+#if defined (USE_DPD)
+ struct {
+ u_int16_t msg_type;
+ u_int8_t *spi;
+ u_int32_t seq;
+ } dpd;
+#endif
+ } u;
+};
+
+/*
+ * As a reaction to the incoming message MSG create an informational exchange
+ * protected by ISAKMP_SA and send a notify payload of type NOTIFY, with
+ * fields initialized from SA. INCOMING is true if the SPI field should be
+ * filled with the incoming SPI and false if it is to be filled with the
+ * outgoing one.
+ *
+ * XXX Should we handle sending multiple notify payloads? The draft allows
+ * it, but do we need it? Furthermore, should we not return a success
+ * status value?
+ */
+void
+message_send_notification(struct message *msg, struct sa *isakmp_sa,
+ u_int16_t notify, struct proto *proto, int incoming)
+{
+ struct info_args args;
+ struct sa *doi_sa = proto ? proto->sa : isakmp_sa;
+
+ args.discr = 'N';
+ args.doi = doi_sa ? doi_sa->doi->id : ISAKMP_DOI_ISAKMP;
+ args.proto = proto ? proto->proto : ISAKMP_PROTO_ISAKMP;
+ args.spi_sz = proto ? proto->spi_sz[incoming] : 0;
+ args.u.n.msg_type = notify;
+ args.u.n.spi = proto ? proto->spi[incoming] : 0;
+ if (isakmp_sa && (isakmp_sa->flags & SA_FLAG_READY))
+ exchange_establish_p2(isakmp_sa, ISAKMP_EXCH_INFO, 0, &args,
+ 0, 0);
+ else
+ exchange_establish_p1(msg->transport, ISAKMP_EXCH_INFO,
+ msg->exchange ? msg->exchange->doi->id : ISAKMP_DOI_ISAKMP,
+ 0, &args, 0, 0);
+}
+
+/* Send a DELETE inside an informational exchange for each protocol in SA. */
+void
+message_send_delete(struct sa *sa)
+{
+ struct info_args args;
+ struct proto *proto;
+ struct sa *isakmp_sa;
+ struct sockaddr *dst;
+
+ sa->transport->vtbl->get_dst(sa->transport, &dst);
+ isakmp_sa = sa_isakmp_lookup_by_peer(dst, sysdep_sa_len(dst));
+ if (!isakmp_sa) {
+ /*
+ * XXX We ought to setup an ISAKMP SA with our peer here and
+ * send the DELETE over that one.
+ */
+ return;
+ }
+ args.discr = 'D';
+ args.doi = sa->doi->id;
+ args.u.d.nspis = 1;
+ for (proto = TAILQ_FIRST(&sa->protos); proto;
+ proto = TAILQ_NEXT(proto, link)) {
+ args.proto = proto->proto;
+ args.spi_sz = proto->spi_sz[1];
+ args.u.d.spis = proto->spi[1];
+ exchange_establish_p2(isakmp_sa, ISAKMP_EXCH_INFO, 0, &args,
+ 0, 0);
+ }
+}
+
+#if defined (USE_DPD)
+void
+message_send_dpd_notify(struct sa* isakmp_sa, u_int16_t notify, u_int32_t seq)
+{
+ struct info_args args;
+
+ args.discr = 'P';
+ args.doi = IPSEC_DOI_IPSEC;
+ args.proto = ISAKMP_PROTO_ISAKMP;
+ args.spi_sz = ISAKMP_HDR_COOKIES_LEN;
+ args.u.dpd.msg_type = notify;
+ args.u.dpd.spi = isakmp_sa->cookies;
+ args.u.dpd.seq = htonl(seq);
+
+ exchange_establish_p2(isakmp_sa, ISAKMP_EXCH_INFO, 0, &args, 0, 0);
+}
+#endif
+
+/* Build the informational message into MSG. */
+int
+message_send_info(struct message *msg)
+{
+ u_int8_t *buf;
+ size_t sz = 0;
+ struct info_args *args = msg->extra;
+ u_int8_t payload;
+
+ /* Let the DOI get the first hand on the message. */
+ if (msg->exchange->doi->informational_pre_hook)
+ if (msg->exchange->doi->informational_pre_hook(msg))
+ return -1;
+
+ switch (args->discr) {
+#if defined (USE_DPD)
+ case 'P':
+ sz = sizeof args->u.dpd.seq;
+ /* FALLTHROUGH */
+#endif
+ case 'N':
+ sz += ISAKMP_NOTIFY_SPI_OFF + args->spi_sz;
+ break;
+ case 'D':
+ default: /* Silence gcc */
+ sz = ISAKMP_DELETE_SPI_OFF + args->u.d.nspis * args->spi_sz;
+ break;
+ }
+
+ buf = calloc(1, sz);
+ if (!buf) {
+ log_error("message_send_info: calloc (1, %lu) failed",
+ (unsigned long)sz);
+ message_free(msg);
+ return -1;
+ }
+ switch (args->discr) {
+#if defined (USE_DPD)
+ case 'P':
+ memcpy(buf + ISAKMP_NOTIFY_SPI_OFF + args->spi_sz,
+ &args->u.dpd.seq, sizeof args->u.dpd.seq);
+ /* FALLTHROUGH */
+#endif
+ case 'N':
+ /* Build the NOTIFY payload. */
+ payload = ISAKMP_PAYLOAD_NOTIFY;
+ SET_ISAKMP_NOTIFY_DOI(buf, args->doi);
+ SET_ISAKMP_NOTIFY_PROTO(buf, args->proto);
+ SET_ISAKMP_NOTIFY_SPI_SZ(buf, args->spi_sz);
+ SET_ISAKMP_NOTIFY_MSG_TYPE(buf, args->u.n.msg_type);
+ memcpy(buf + ISAKMP_NOTIFY_SPI_OFF, args->u.n.spi,
+ args->spi_sz);
+ break;
+
+ case 'D':
+ default: /* Silence GCC. */
+ /* Build the DELETE payload. */
+ payload = ISAKMP_PAYLOAD_DELETE;
+ SET_ISAKMP_DELETE_DOI(buf, args->doi);
+ SET_ISAKMP_DELETE_PROTO(buf, args->proto);
+ SET_ISAKMP_DELETE_SPI_SZ(buf, args->spi_sz);
+ SET_ISAKMP_DELETE_NSPIS(buf, args->u.d.nspis);
+ memcpy(buf + ISAKMP_DELETE_SPI_OFF, args->u.d.spis,
+ args->u.d.nspis * args->spi_sz);
+ msg->flags |= MSG_PRIORITIZED;
+ break;
+ }
+
+ if (message_add_payload(msg, payload, buf, sz, 1)) {
+ free(buf);
+ message_free(msg);
+ return -1;
+ }
+ /* Let the DOI get the last hand on the message. */
+ if (msg->exchange->doi->informational_post_hook)
+ if (msg->exchange->doi->informational_post_hook(msg)) {
+ message_free(msg);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Drop the MSG message due to reason given in NOTIFY. If NOTIFY is set
+ * send out a notification to the originator. Fill this notification with
+ * values from PROTO. INCOMING decides which SPI to include. If CLEAN is
+ * set, free the message when ready with it.
+ */
+void
+message_drop(struct message *msg, int notify, struct proto *proto,
+ int incoming, int clean)
+{
+ struct transport *t = msg->transport;
+ struct sockaddr *dst;
+ char *address;
+ short port = 0;
+
+ t->vtbl->get_dst(t, &dst);
+ if (sockaddr2text(dst, &address, 0)) {
+ log_error("message_drop: sockaddr2text () failed");
+ address = 0;
+ }
+ switch (dst->sa_family) {
+ case AF_INET:
+ port = ((struct sockaddr_in *)dst)->sin_port;
+ break;
+ case AF_INET6:
+ port = ((struct sockaddr_in6 *)dst)->sin6_port;
+ break;
+ default:
+ log_print("message_drop: unknown protocol family %d",
+ dst->sa_family);
+ }
+
+ log_print("dropped message from %s port %d due to notification type "
+ "%s", address ? address : "<unknown>", htons(port),
+ constant_name(isakmp_notify_cst, notify));
+
+ if (address)
+ free(address);
+
+ /* If specified, return a notification. */
+ if (notify)
+ message_send_notification(msg, msg->isakmp_sa, notify, proto,
+ incoming);
+ if (clean)
+ message_free(msg);
+}
+
+/*
+ * If the user demands debug printouts, printout MSG with as much detail
+ * as we can without resorting to per-payload handling.
+ */
+void
+message_dump_raw(char *header, struct message *msg, int class)
+{
+ u_int32_t i, j, k = 0;
+ char buf[80], *p = buf;
+
+ LOG_DBG((class, 70, "%s: message %p", header, msg));
+ field_dump_payload(isakmp_hdr_fld, msg->iov[0].iov_base);
+ for (i = 0; i < msg->iovlen; i++)
+ for (j = 0; j < msg->iov[i].iov_len; j++) {
+ snprintf(p, sizeof buf - (int) (p - buf), "%02x",
+ ((u_int8_t *) msg->iov[i].iov_base)[j]);
+ p += 2;
+ if (++k % 32 == 0) {
+ *p = '\0';
+ LOG_DBG((class, 70, "%s: %s", header, buf));
+ p = buf;
+ } else if (k % 4 == 0)
+ *p++ = ' ';
+ }
+ *p = '\0';
+ if (p != buf)
+ LOG_DBG((class, 70, "%s: %s", header, buf));
+}
+
+static void
+message_packet_log(struct message *msg)
+{
+#if defined (USE_DEBUG)
+ struct sockaddr *src, *dst;
+ struct transport *t = msg->transport;
+
+ /* Don't log retransmissions. Redundant for incoming packets... */
+ if (msg->xmits > 0)
+ return;
+
+#if defined (USE_NAT_TRAVERSAL)
+ if (msg->exchange && msg->exchange->flags & EXCHANGE_FLAG_NAT_T_ENABLE)
+ t = ((struct virtual_transport *)msg->transport)->encap;
+#endif
+
+ /* Figure out direction. */
+ if (msg->exchange &&
+ msg->exchange->initiator ^ (msg->exchange->step % 2)) {
+ t->vtbl->get_src(t, &src);
+ t->vtbl->get_dst(t, &dst);
+ } else {
+ t->vtbl->get_src(t, &dst);
+ t->vtbl->get_dst(t, &src);
+ }
+
+ log_packet_iov(src, dst, msg->iov, msg->iovlen);
+#endif /* USE_DEBUG */
+}
+
+/*
+ * Encrypt an outgoing message MSG. As outgoing messages are represented
+ * with an iovec with one segment per payload, we need to coalesce them
+ * into just une buffer containing all payloads and some padding before
+ * we encrypt.
+ */
+static int
+message_encrypt(struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ size_t i, sz = 0;
+ u_int8_t *buf;
+
+ /* If no payloads, nothing to do. */
+ if (msg->iovlen == 1)
+ return 0;
+
+ /*
+ * For encryption we need to put all payloads together in a single
+ * buffer. This buffer should be padded to the current crypto
+ * transform's blocksize.
+ */
+ for (i = 1; i < msg->iovlen; i++)
+ sz += msg->iov[i].iov_len;
+ sz = ((sz + exchange->crypto->blocksize - 1) /
+ exchange->crypto->blocksize) * exchange->crypto->blocksize;
+ buf = realloc(msg->iov[1].iov_base, sz);
+ if (!buf) {
+ log_error("message_encrypt: realloc (%p, %lu) failed",
+ msg->iov[1].iov_base, (unsigned long) sz);
+ return -1;
+ }
+ msg->iov[1].iov_base = buf;
+ for (i = 2; i < msg->iovlen; i++) {
+ memcpy(buf + msg->iov[1].iov_len, msg->iov[i].iov_base,
+ msg->iov[i].iov_len);
+ msg->iov[1].iov_len += msg->iov[i].iov_len;
+ free(msg->iov[i].iov_base);
+ }
+
+ /* Pad with zeroes. */
+ memset(buf + msg->iov[1].iov_len, '\0', sz - msg->iov[1].iov_len);
+ msg->iov[1].iov_len = sz;
+ msg->iovlen = 2;
+
+ SET_ISAKMP_HDR_FLAGS(msg->iov[0].iov_base,
+ GET_ISAKMP_HDR_FLAGS(msg->iov[0].iov_base) | ISAKMP_FLAGS_ENC);
+ SET_ISAKMP_HDR_LENGTH(msg->iov[0].iov_base, ISAKMP_HDR_SZ + sz);
+ crypto_encrypt(exchange->keystate, buf, msg->iov[1].iov_len);
+ msg->flags |= MSG_ENCRYPTED;
+
+ /* Update the IV so we can decrypt the next incoming message. */
+ crypto_update_iv(exchange->keystate);
+
+ return 0;
+}
+
+/*
+ * Check whether the message MSG is a duplicate of the last one negotiating
+ * this specific SA.
+ */
+static int
+message_check_duplicate(struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ size_t sz = msg->iov[0].iov_len;
+ u_int8_t *pkt = msg->iov[0].iov_base;
+
+ /* If no SA has been found, we cannot test, thus it's good. */
+ if (!exchange)
+ return 0;
+
+ LOG_DBG((LOG_MESSAGE, 90, "message_check_duplicate: last_received %p",
+ exchange->last_received));
+ if (exchange->last_received) {
+ LOG_DBG_BUF((LOG_MESSAGE, 95,
+ "message_check_duplicate: last_received",
+ exchange->last_received->orig,
+ exchange->last_received->orig_sz));
+ /* Is it a duplicate, lose the new one. */
+ if (sz == exchange->last_received->orig_sz
+ && memcmp(pkt, exchange->last_received->orig, sz) == 0) {
+ LOG_DBG((LOG_MESSAGE, 80,
+ "message_check_duplicate: dropping dup"));
+
+ /*
+ * Retransmit if the previos sent message was the last
+ * of an exchange, otherwise just wait for the
+ * ordinary retransmission.
+ */
+ if (exchange->last_sent && (exchange->last_sent->flags
+ & MSG_LAST))
+ message_send(exchange->last_sent);
+ message_free(msg);
+ return -1;
+ }
+ }
+ /*
+ * As this new message is an indication that state is moving forward
+ * at the peer, remove the retransmit timer on our last message.
+ */
+ if (exchange->last_sent) {
+ if (exchange->last_sent == exchange->in_transit) {
+ struct message *m = exchange->in_transit;
+ TAILQ_REMOVE(m->transport->vtbl->get_queue(m), m,
+ link);
+ exchange->in_transit = 0;
+ }
+ message_free(exchange->last_sent);
+ exchange->last_sent = 0;
+ }
+ return 0;
+}
+
+/* Helper to message_negotiate_sa. */
+static INLINE struct payload *
+step_transform(struct payload *tp, struct payload **propp,
+ struct payload **sap)
+{
+ tp = TAILQ_NEXT(tp, link);
+ if (tp) {
+ *propp = tp->context;
+ *sap = (*propp)->context;
+ }
+ return tp;
+}
+
+/*
+ * Pick out the first transforms out of MSG (which should contain at least one
+ * SA payload) we accept as a full protection suite.
+ */
+int
+message_negotiate_sa(struct message *msg, int (*validate)(struct exchange *,
+ struct sa *, struct sa *))
+{
+ struct payload *tp, *propp, *sap, *next_tp = 0, *next_propp, *next_sap;
+ struct payload *saved_tp = 0, *saved_propp = 0, *saved_sap = 0;
+ struct sa *sa;
+ struct proto *proto;
+ int suite_ok_so_far = 0;
+ struct exchange *exchange = msg->exchange;
+
+ /*
+ * This algorithm is a weird bottom-up thing... mostly due to the
+ * payload links pointing upwards.
+ *
+ * The algorithm goes something like this:
+ * Foreach transform
+ * If transform is compatible
+ * Remember that this protocol can work
+ * Skip to last transform of this protocol
+ * If next transform belongs to a new protocol inside the same suite
+ * If no transform was found for the current protocol
+ * Forget all earlier transforms for protocols in this suite
+ * Skip to last transform of this suite
+ * If next transform belongs to a new suite
+ * If the current protocol had an OK transform
+ * Skip to the last transform of this SA
+ * If the next transform belongs to a new SA
+ * If no transforms have been chosen
+ * Issue a NO_PROPOSAL_CHOSEN notification
+ */
+
+ sa = TAILQ_FIRST(&exchange->sa_list);
+ for (tp = payload_first(msg, ISAKMP_PAYLOAD_TRANSFORM); tp;
+ tp = next_tp) {
+ propp = tp->context;
+ sap = propp->context;
+ sap->flags |= PL_MARK;
+ next_tp = step_transform(tp, &next_propp, &next_sap);
+
+ /* For each transform, see if it is compatible. */
+ if (!attribute_map(tp->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ GET_ISAKMP_GEN_LENGTH(tp->p) -
+ ISAKMP_TRANSFORM_SA_ATTRS_OFF,
+ exchange->doi->is_attribute_incompatible, msg)) {
+ LOG_DBG((LOG_NEGOTIATION, 30, "message_negotiate_sa: "
+ "transform %d proto %d proposal %d ok",
+ GET_ISAKMP_TRANSFORM_NO(tp->p),
+ GET_ISAKMP_PROP_PROTO(propp->p),
+ GET_ISAKMP_PROP_NO(propp->p)));
+ if (sa_add_transform(sa, tp, exchange->initiator,
+ &proto))
+ goto cleanup;
+ suite_ok_so_far = 1;
+
+ saved_tp = next_tp;
+ saved_propp = next_propp;
+ saved_sap = next_sap;
+ /* Skip to last transform of this protocol proposal. */
+ while ((next_tp = step_transform(tp, &next_propp,
+ &next_sap)) && next_propp == propp)
+ tp = next_tp;
+ }
+retry_transform:
+ /*
+ * Figure out if we will be looking at a new protocol proposal
+ * inside the current protection suite.
+ */
+ if (next_tp && propp != next_propp && sap == next_sap
+ && (GET_ISAKMP_PROP_NO(propp->p)
+ == GET_ISAKMP_PROP_NO(next_propp->p))) {
+ if (!suite_ok_so_far) {
+ LOG_DBG((LOG_NEGOTIATION, 30,
+ "message_negotiate_sa: proto %d proposal "
+ "%d failed",
+ GET_ISAKMP_PROP_PROTO(propp->p),
+ GET_ISAKMP_PROP_NO(propp->p)));
+ /*
+ * Remove potentially succeeded choices from
+ * the SA.
+ */
+ while (TAILQ_FIRST(&sa->protos))
+ TAILQ_REMOVE(&sa->protos,
+ TAILQ_FIRST(&sa->protos), link);
+
+ /*
+ * Skip to the last transform of this
+ * protection suite.
+ */
+ while ((next_tp = step_transform(tp,
+ &next_propp, &next_sap))
+ && (GET_ISAKMP_PROP_NO(next_propp->p)
+ == GET_ISAKMP_PROP_NO(propp->p))
+ && next_sap == sap)
+ tp = next_tp;
+ }
+ suite_ok_so_far = 0;
+ }
+ /*
+ * Figure out if we will be looking at a new protection
+ * suite.
+ */
+ if (!next_tp
+ || (propp != next_propp && (GET_ISAKMP_PROP_NO(propp->p)
+ != GET_ISAKMP_PROP_NO(next_propp->p)))
+ || sap != next_sap) {
+ /*
+ * Check if the suite we just considered was OK, if so
+ * we check it against the accepted ones.
+ */
+ if (suite_ok_so_far) {
+ if (!validate || validate(exchange, sa,
+ msg->isakmp_sa)) {
+ LOG_DBG((LOG_NEGOTIATION, 30,
+ "message_negotiate_sa: proposal "
+ "%d succeeded",
+ GET_ISAKMP_PROP_NO(propp->p)));
+
+ /*
+ * Skip to the last transform of this
+ * SA.
+ */
+ while ((next_tp = step_transform(tp,
+ &next_propp, &next_sap))
+ && next_sap == sap)
+ tp = next_tp;
+ } else {
+ /* Backtrack. */
+ LOG_DBG((LOG_NEGOTIATION, 30,
+ "message_negotiate_sa: proposal "
+ "%d failed",
+ GET_ISAKMP_PROP_NO(propp->p)));
+ next_tp = saved_tp;
+ next_propp = saved_propp;
+ next_sap = saved_sap;
+ suite_ok_so_far = 0;
+
+ /*
+ * Remove potentially succeeded
+ * choices from the SA.
+ */
+ while (TAILQ_FIRST(&sa->protos))
+ TAILQ_REMOVE(&sa->protos,
+ TAILQ_FIRST(&sa->protos),
+ link);
+ goto retry_transform;
+ }
+ }
+ }
+ /* Have we walked all the proposals of an SA? */
+ if (!next_tp || sap != next_sap) {
+ if (!suite_ok_so_far) {
+ /*
+ * XXX We cannot possibly call this a drop...
+ * seeing we just turn down one of the offers,
+ * can we? I suggest renaming message_drop to
+ * something else.
+ */
+ log_print("message_negotiate_sa: no "
+ "compatible proposal found");
+ message_drop(msg,
+ ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0);
+ }
+ sa = TAILQ_NEXT(sa, next);
+ }
+ }
+ return 0;
+
+cleanup:
+ /*
+ * Remove potentially succeeded choices from the SA.
+ * XXX Do we leak struct protos and related data here?
+ */
+ while (TAILQ_FIRST(&sa->protos))
+ TAILQ_REMOVE(&sa->protos, TAILQ_FIRST(&sa->protos), link);
+ return -1;
+}
+
+/*
+ * Add SA, proposal and transform payload(s) to MSG out of information
+ * found in the exchange MSG is part of..
+ */
+int
+message_add_sa_payload(struct message *msg)
+{
+ struct exchange *exchange = msg->exchange;
+ u_int8_t *sa_buf, *saved_nextp_sa, *saved_nextp_prop;
+ size_t sa_len, extra_sa_len;
+ int i, nprotos = 0;
+ struct proto *proto;
+ u_int8_t **transforms = 0, **proposals = 0;
+ size_t *transform_lens = 0, *proposal_lens = 0;
+ struct sa *sa;
+ struct doi *doi = exchange->doi;
+ u_int8_t *spi = 0;
+ size_t spi_sz;
+
+ /*
+ * Generate SA payloads.
+ */
+ for (sa = TAILQ_FIRST(&exchange->sa_list); sa;
+ sa = TAILQ_NEXT(sa, next)) {
+ /* Setup a SA payload. */
+ sa_len = ISAKMP_SA_SIT_OFF + doi->situation_size();
+ extra_sa_len = 0;
+ sa_buf = malloc(sa_len);
+ if (!sa_buf) {
+ log_error("message_add_sa_payload: "
+ "malloc (%lu) failed", (unsigned long)sa_len);
+ goto cleanup;
+ }
+ SET_ISAKMP_SA_DOI(sa_buf, doi->id);
+ doi->setup_situation(sa_buf);
+
+ /* Count transforms. */
+ nprotos = 0;
+ for (proto = TAILQ_FIRST(&sa->protos); proto;
+ proto = TAILQ_NEXT(proto, link))
+ nprotos++;
+
+ /*
+ * Allocate transient transform and proposal payload/size
+ * vectors.
+ */
+ transforms = calloc(nprotos, sizeof *transforms);
+ if (!transforms) {
+ log_error("message_add_sa_payload: calloc (%d, %lu) "
+ "failed", nprotos,
+ (unsigned long)sizeof *transforms);
+ goto cleanup;
+ }
+ transform_lens = calloc(nprotos, sizeof *transform_lens);
+ if (!transform_lens) {
+ log_error("message_add_sa_payload: calloc (%d, %lu) "
+ "failed", nprotos,
+ (unsigned long) sizeof *transform_lens);
+ goto cleanup;
+ }
+ proposals = calloc(nprotos, sizeof *proposals);
+ if (!proposals) {
+ log_error("message_add_sa_payload: calloc (%d, %lu) "
+ "failed", nprotos,
+ (unsigned long)sizeof *proposals);
+ goto cleanup;
+ }
+ proposal_lens = calloc(nprotos, sizeof *proposal_lens);
+ if (!proposal_lens) {
+ log_error("message_add_sa_payload: calloc (%d, %lu) "
+ "failed", nprotos,
+ (unsigned long)sizeof *proposal_lens);
+ goto cleanup;
+ }
+ /* Pick out the chosen transforms. */
+ for (proto = TAILQ_FIRST(&sa->protos), i = 0; proto;
+ proto = TAILQ_NEXT(proto, link), i++) {
+ transform_lens[i] =
+ GET_ISAKMP_GEN_LENGTH(proto->chosen->p);
+ transforms[i] = malloc(transform_lens[i]);
+ if (!transforms[i]) {
+ log_error("message_add_sa_payload: malloc "
+ "(%lu) failed",
+ (unsigned long)transform_lens[i]);
+ goto cleanup;
+ }
+ /* Get incoming SPI from application. */
+ if (doi->get_spi) {
+ spi = doi->get_spi(&spi_sz,
+ GET_ISAKMP_PROP_PROTO(proto->chosen->context->p),
+ msg);
+ if (spi_sz && !spi)
+ goto cleanup;
+ proto->spi[1] = spi;
+ proto->spi_sz[1] = spi_sz;
+ } else
+ spi_sz = 0;
+
+ proposal_lens[i] = ISAKMP_PROP_SPI_OFF + spi_sz;
+ proposals[i] = malloc(proposal_lens[i]);
+ if (!proposals[i]) {
+ log_error("message_add_sa_payload: malloc "
+ "(%lu) failed",
+ (unsigned long)proposal_lens[i]);
+ goto cleanup;
+ }
+ memcpy(transforms[i], proto->chosen->p,
+ transform_lens[i]);
+ memcpy(proposals[i], proto->chosen->context->p,
+ ISAKMP_PROP_SPI_OFF);
+ SET_ISAKMP_PROP_NTRANSFORMS(proposals[i], 1);
+ SET_ISAKMP_PROP_SPI_SZ(proposals[i], spi_sz);
+ if (spi_sz)
+ memcpy(proposals[i] + ISAKMP_PROP_SPI_OFF, spi,
+ spi_sz);
+ extra_sa_len += proposal_lens[i] + transform_lens[i];
+ }
+
+ /*
+ * Add the payloads. As this is a SA, we need to recompute the
+ * lengths of the payloads containing others. We also need to
+ * reset these payload's "next payload type" field.
+ */
+ if (message_add_payload(msg, ISAKMP_PAYLOAD_SA, sa_buf,
+ sa_len, 1))
+ goto cleanup;
+ SET_ISAKMP_GEN_LENGTH(sa_buf, sa_len + extra_sa_len);
+ sa_buf = 0;
+
+ saved_nextp_sa = msg->nextp;
+ for (proto = TAILQ_FIRST(&sa->protos), i = 0; proto;
+ proto = TAILQ_NEXT(proto, link), i++) {
+ if (message_add_payload(msg, ISAKMP_PAYLOAD_PROPOSAL,
+ proposals[i], proposal_lens[i], i > 1))
+ goto cleanup;
+ SET_ISAKMP_GEN_LENGTH(proposals[i], proposal_lens[i] +
+ transform_lens[i]);
+ proposals[i] = 0;
+
+ saved_nextp_prop = msg->nextp;
+ if (message_add_payload(msg, ISAKMP_PAYLOAD_TRANSFORM,
+ transforms[i], transform_lens[i], 0))
+ goto cleanup;
+ msg->nextp = saved_nextp_prop;
+ transforms[i] = 0;
+ }
+ msg->nextp = saved_nextp_sa;
+
+ /* Free the temporary allocations made above. */
+ free(transforms);
+ free(transform_lens);
+ free(proposals);
+ free(proposal_lens);
+ }
+ return 0;
+
+cleanup:
+ if (sa_buf)
+ free(sa_buf);
+ for (i = 0; i < nprotos; i++) {
+ if (transforms[i])
+ free(transforms[i]);
+ if (proposals[i])
+ free(proposals[i]);
+ }
+ if (transforms)
+ free(transforms);
+ if (transform_lens)
+ free(transform_lens);
+ if (proposals)
+ free(proposals);
+ if (proposal_lens)
+ free(proposal_lens);
+ return -1;
+}
+
+/*
+ * Return a copy of MSG's constants starting from OFFSET and stash the size
+ * in SZP. It is the callers responsibility to free this up.
+ */
+u_int8_t *
+message_copy(struct message *msg, size_t offset, size_t *szp)
+{
+ int skip = 0;
+ size_t i, sz = 0;
+ ssize_t start = -1;
+ u_int8_t *buf, *p;
+
+ /* Calculate size of message and where we want to start to copy. */
+ for (i = 1; i < msg->iovlen; i++) {
+ sz += msg->iov[i].iov_len;
+ if (sz <= offset)
+ skip = i;
+ else if (start < 0)
+ start = offset - (sz - msg->iov[i].iov_len);
+ }
+
+ /* Allocate and copy. */
+ *szp = sz - offset;
+ buf = malloc(*szp);
+ if (!buf)
+ return 0;
+ p = buf;
+ for (i = skip + 1; i < msg->iovlen; i++) {
+ memcpy(p, (u_int8_t *) msg->iov[i].iov_base + start,
+ msg->iov[i].iov_len - start);
+ p += msg->iov[i].iov_len - start;
+ start = 0;
+ }
+ return buf;
+}
+
+/* Register a post-send function POST_SEND with message MSG. */
+int
+message_register_post_send(struct message *msg,
+ void (*post_send)(struct message *))
+{
+ struct post_send *node;
+
+ node = malloc(sizeof *node);
+ if (!node)
+ return -1;
+ node->func = post_send;
+ TAILQ_INSERT_TAIL(&msg->post_send, node, link);
+ return 0;
+}
+
+/* Run the post-send functions of message MSG. */
+void
+message_post_send(struct message *msg)
+{
+ struct post_send *node;
+
+ while ((node = TAILQ_FIRST(&msg->post_send)) != 0) {
+ TAILQ_REMOVE(&msg->post_send, node, link);
+ node->func(msg);
+ free(node);
+ }
+}
+
+/* Initialize and populate payload_map[]. */
+void
+message_init(void)
+{
+ u_int8_t i;
+
+ memset(&payload_map, 0, sizeof payload_map);
+
+ payload_index_max = sizeof payload_revmap / sizeof payload_revmap[0];
+ for (i = 0; i < payload_index_max; i++) {
+ payload_map[payload_revmap[i]] = i;
+ LOG_DBG((LOG_MESSAGE, 95, "message_init: payload_map[%d] = %d",
+ payload_revmap[i], i));
+ }
+}
+
+struct payload *
+payload_first(struct message *msg, u_int8_t payload)
+{
+ if (payload_map[payload])
+ return TAILQ_FIRST(&msg->payload[payload_map[payload]]);
+ else
+ return 0;
+}
+
+struct payload *
+payload_last(struct message *msg, u_int8_t payload)
+{
+ if (payload_map[payload])
+ return TAILQ_LAST(&msg->payload[payload_map[payload]],
+ payload_head);
+ else
+ return 0;
+}