From 2117af4583be79d1f76f96a65cb52d9c3e3735bd Mon Sep 17 00:00:00 2001 From: tobhe Date: Sat, 2 Mar 2024 16:16:07 +0000 Subject: [PATCH] Trigger retransmission only for fragment 1/x, otherwise each received fragment can trigger retransmission of the full fragment queue. From RFC7383, 2.6.1: "[...] that even MUST only trigger a retransmission of the response message (fragmented or no) if the Fragment Number field in the received fragments is set to 1; otherwise, it MUST be ignored." from markus --- sbin/iked/iked.h | 7 ++-- sbin/iked/ikev2.c | 6 ++-- sbin/iked/ikev2_msg.c | 22 ++++++++---- sbin/iked/ikev2_pld.c | 78 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 12 deletions(-) diff --git a/sbin/iked/iked.h b/sbin/iked/iked.h index 5b65a16bc95..41720d39787 100644 --- a/sbin/iked/iked.h +++ b/sbin/iked/iked.h @@ -1,4 +1,4 @@ -/* $OpenBSD: iked.h,v 1.229 2024/02/15 20:10:45 tobhe Exp $ */ +/* $OpenBSD: iked.h,v 1.230 2024/03/02 16:16:07 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -656,6 +656,7 @@ struct iked_message { struct iked_addr *msg_cp_addr; /* requested address */ struct iked_addr *msg_cp_addr6; /* requested address */ struct iked_addr *msg_cp_dns; /* requested dns */ + uint16_t msg_frag_num; /* MOBIKE */ int msg_update_sa_addresses; @@ -1130,7 +1131,7 @@ struct iked_socket * int ikev2_msg_enqueue(struct iked *, struct iked_msgqueue *, struct iked_message *, int); int ikev2_msg_retransmit_response(struct iked *, struct iked_sa *, - struct iked_message *, uint8_t); + struct iked_message *, struct ike_header *); void ikev2_msg_prevail(struct iked *, struct iked_msgqueue *, struct iked_message *); void ikev2_msg_dispose(struct iked *, struct iked_msgqueue *, @@ -1143,6 +1144,8 @@ struct iked_msg_retransmit * /* ikev2_pld.c */ int ikev2_pld_parse(struct iked *, struct ike_header *, struct iked_message *, size_t); +int ikev2_pld_parse_quick(struct iked *, struct ike_header *, + struct iked_message *, size_t); /* eap.c */ int eap_parse(struct iked *, const struct iked_sa *, struct iked_message*, diff --git a/sbin/iked/ikev2.c b/sbin/iked/ikev2.c index 8bbbeb34b1d..d5cc0ed52bd 100644 --- a/sbin/iked/ikev2.c +++ b/sbin/iked/ikev2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2.c,v 1.384 2024/02/15 19:11:00 tobhe Exp $ */ +/* $OpenBSD: ikev2.c,v 1.385 2024/03/02 16:16:07 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -756,8 +756,8 @@ ikev2_recv(struct iked *env, struct iked_message *msg) * See if we have responded to this request before * For return values 0 and -1 we have. */ - if ((r = ikev2_msg_retransmit_response(env, sa, msg, - hdr->ike_exchange)) != -2) { + if ((r = ikev2_msg_retransmit_response(env, sa, msg, hdr)) + != -2) { if (r == -1) { log_warn("%s: failed to retransmit a " "response", __func__); diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c index 8e0f7488997..15ef2825197 100644 --- a/sbin/iked/ikev2_msg.c +++ b/sbin/iked/ikev2_msg.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_msg.c,v 1.100 2023/08/04 19:06:25 claudio Exp $ */ +/* $OpenBSD: ikev2_msg.c,v 1.101 2024/03/02 16:16:07 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -1275,15 +1275,25 @@ ikev2_msg_lookup(struct iked *env, struct iked_msgqueue *queue, int ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa, - struct iked_message *msg, uint8_t exchange) + struct iked_message *msg, struct ike_header *hdr) { struct iked_msg_retransmit *mr = NULL; - struct iked_message *m = NULL; + struct iked_message *m = NULL; - if ((mr = ikev2_msg_lookup(env, &sa->sa_responses, msg, exchange)) - == NULL) + if ((mr = ikev2_msg_lookup(env, &sa->sa_responses, msg, + hdr->ike_exchange)) == NULL) return (-2); /* not found */ + if (hdr->ike_nextpayload == IKEV2_PAYLOAD_SKF) { + /* only retransmit for fragment number one */ + if (ikev2_pld_parse_quick(env, hdr, msg, + msg->msg_offset) != 0 || msg->msg_frag_num != 1) { + log_debug("%s: ignoring fragment", SPI_SA(sa, __func__)); + return (0); + } + log_debug("%s: first fragment", SPI_SA(sa, __func__)); + } + TAILQ_FOREACH(m, &mr->mrt_frags, msg_entry) { if (sendtofrom(m->msg_fd, ibuf_data(m->msg_data), ibuf_size(m->msg_data), 0, @@ -1295,7 +1305,7 @@ ikev2_msg_retransmit_response(struct iked *env, struct iked_sa *sa, } log_info("%sretransmit %s res %u local %s peer %s", SPI_SA(sa, NULL), - print_map(exchange, ikev2_exchange_map), + print_map(hdr->ike_exchange, ikev2_exchange_map), m->msg_msgid, print_addr(&m->msg_local), print_addr(&m->msg_peer)); diff --git a/sbin/iked/ikev2_pld.c b/sbin/iked/ikev2_pld.c index f207fbfc348..1fb5f305499 100644 --- a/sbin/iked/ikev2_pld.c +++ b/sbin/iked/ikev2_pld.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ikev2_pld.c,v 1.133 2023/09/02 18:36:30 tobhe Exp $ */ +/* $OpenBSD: ikev2_pld.c,v 1.134 2024/03/02 16:16:07 tobhe Exp $ */ /* * Copyright (c) 2019 Tobias Heider @@ -2103,3 +2103,79 @@ ikev2_pld_eap(struct iked *env, struct ikev2_payload *pld, return (0); } + +/* parser for the initial IKE_AUTH payload, does not require msg_sa */ +int +ikev2_pld_parse_quick(struct iked *env, struct ike_header *hdr, + struct iked_message *msg, size_t offset) +{ + struct ikev2_payload pld; + struct ikev2_frag_payload frag; + uint8_t *msgbuf = ibuf_data(msg->msg_data); + uint8_t *buf; + size_t len, total, left; + size_t length; + unsigned int payload; + + log_debug("%s: header ispi %s rspi %s" + " nextpayload %s version 0x%02x exchange %s flags 0x%02x" + " msgid %d length %u response %d", __func__, + print_spi(betoh64(hdr->ike_ispi), 8), + print_spi(betoh64(hdr->ike_rspi), 8), + print_map(hdr->ike_nextpayload, ikev2_payload_map), + hdr->ike_version, + print_map(hdr->ike_exchange, ikev2_exchange_map), + hdr->ike_flags, + betoh32(hdr->ike_msgid), + betoh32(hdr->ike_length), + msg->msg_response); + + length = betoh32(hdr->ike_length); + + if (ibuf_size(msg->msg_data) < length) { + log_debug("%s: short message", __func__); + return (-1); + } + + offset += sizeof(*hdr); + + /* Bytes left in datagram. */ + total = length - offset; + + payload = hdr->ike_nextpayload; + + while (payload != 0 && offset < length) { + if (ikev2_validate_pld(msg, offset, total, &pld)) + return (-1); + + log_debug("%s: %spayload %s" + " nextpayload %s critical 0x%02x length %d", + __func__, msg->msg_e ? "decrypted " : "", + print_map(payload, ikev2_payload_map), + print_map(pld.pld_nextpayload, ikev2_payload_map), + pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, + betoh16(pld.pld_length)); + + /* Skip over generic payload header. */ + offset += sizeof(pld); + total -= sizeof(pld); + left = betoh16(pld.pld_length) - sizeof(pld); + + switch (payload) { + case IKEV2_PAYLOAD_SKF: + len = left; + buf = msgbuf + offset; + if (len < sizeof(frag)) + return (-1); + memcpy(&frag, buf, sizeof(frag)); + msg->msg_frag_num = betoh16(frag.frag_num); + break; + } + + payload = pld.pld_nextpayload; + offset += left; + total -= left; + } + + return (0); +} -- 2.20.1