diff options
Diffstat (limited to 'keyexchange/isakmpd-20041012/message.c')
-rw-r--r-- | keyexchange/isakmpd-20041012/message.c | 2530 |
1 files changed, 0 insertions, 2530 deletions
diff --git a/keyexchange/isakmpd-20041012/message.c b/keyexchange/isakmpd-20041012/message.c deleted file mode 100644 index 9259e2d..0000000 --- a/keyexchange/isakmpd-20041012/message.c +++ /dev/null @@ -1,2530 +0,0 @@ -/* $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; -} |