gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

commit 58241d1f517fcf47830eb4d9047bae22c9f55209
parent 0114f63e404068129cd8329a0155757727ca56a8
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Wed, 29 Jul 2026 07:40:11 +0200

core: properly handle retransmissioned done messages

Diffstat:
Msrc/service/core/gnunet-service-core_kx.c | 93++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 73 insertions(+), 20 deletions(-)

diff --git a/src/service/core/gnunet-service-core_kx.c b/src/service/core/gnunet-service-core_kx.c @@ -1740,12 +1740,18 @@ handle_responder_hello_cont (void *cls, const struct GNUNET_ShortHashCode *ss_I) &responder_finished, sizeof (struct GNUNET_HashCode))) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Could not verify \"responder finished\"\n"); + /* A peer that answers our InitiatorHello with a ResponderHello whose + finished field does not verify must not be able to abort us; this + used to be a GNUNET_assert (0). */ + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Could not verify \"responder finished\" from `%s'\n", + GNUNET_i2s (&kx->peer)); GNUNET_free (rh_ctx->rhp); GNUNET_CRYPTO_hash_context_abort (rh_ctx->hc); GNUNET_free (rh_ctx); - GNUNET_assert (0); + GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); + restart_kx (kx); return; } @@ -1912,6 +1918,31 @@ handle_responder_hello (void *cls, const struct ResponderHello *rhm_e) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received ResponderHello: %d %d\n", kx-> role, kx->status); + if (ROLE_RESPONDER == kx->role) + { + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "I am the responder! Ignoring.\n"); + GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); + return; + } + if (GNUNET_CORE_KX_STATE_INITIATOR_HELLO_SENT != kx->status) + { + /* Outside of that state there is no handshake this message could + belong to. In particular @e transcript_hash_ctx is then NULL, and + #GNUNET_CRYPTO_hash_context_copy() dereferences its argument -- so + a peer could crash us by sending a ResponderHello at any other + time. Note that @e resend_task and @e resend_env below belong to + the exchange we *are* in the middle of and must not be cleared + before this point either. */ + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Unexpected ResponderHello in state %d, ignoring\n", + kx->status); + GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); + return; + } + GNUNET_assert (NULL != kx->transcript_hash_ctx); hc = GNUNET_CRYPTO_hash_context_copy (kx->transcript_hash_ctx); if (NULL != kx->resend_task) { @@ -1925,15 +1956,6 @@ handle_responder_hello (void *cls, const struct ResponderHello *rhm_e) } /* Forward the transcript hash context */ - if (ROLE_RESPONDER == kx->role) - { - GNUNET_break_op (0); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "I am the responder! Ignoring.\n"); - GNUNET_CRYPTO_hash_context_abort (hc); - GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); - return; - } GNUNET_CRYPTO_hash_context_read (hc, rhm_e, sizeof (struct ResponderHello)); @@ -2100,24 +2122,55 @@ handle_initiator_done (void *cls, const struct InitiatorDone *idm_e) GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Received InitiatorDone: %d %d\n", kx-> role, kx->status); - if (NULL != kx->resend_task) + if (ROLE_INITIATOR == kx->role) { - GNUNET_SCHEDULER_cancel (kx->resend_task); - kx->resend_task = NULL; + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "I am the initiator! Tearing down...\n"); + GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); + return; } - if (NULL != kx->resend_env) + if (GNUNET_CORE_KX_STATE_RESPONDER_CONNECTED == kx->status) { - GNUNET_MQ_discard (kx->resend_env); - kx->resend_env = NULL; + /* The initiator did not see our ConfirmationAck and is resending (it + tries #RESEND_MAX_TRIES times). Our handshake secrets are gone -- + #cleanup_handshake_secrets() zeroed @e ihts -- so verifying this + message again is not possible and would only look like a decryption + failure. Send what the initiator is actually missing instead. */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "InitiatorDone repeated by `%s', resending our Ack\n", + GNUNET_i2s (&kx->peer)); + ack_r.header.type = htons (GNUNET_MESSAGE_TYPE_CORE_ACK); + ack_r.header.size = htons (sizeof ack_r); + GSC_KX_encrypt_and_transmit (kx, + &ack_r, + sizeof ack_r); + GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); + return; } - if (ROLE_INITIATOR == kx->role) + if (GNUNET_CORE_KX_STATE_RESPONDER_HELLO_SENT != kx->status) { + /* We have no handshake state this message could be checked against. + Note that @e resend_task and @e resend_env below belong to whatever + exchange we *are* in the middle of, so they must not be cleared + before this point. */ GNUNET_break_op (0); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "I am the initiator! Tearing down...\n"); + "Unexpected InitiatorDone in state %d, ignoring\n", + kx->status); GNUNET_TRANSPORT_core_receive_continue (transport, &kx->peer); return; } + if (NULL != kx->resend_task) + { + GNUNET_SCHEDULER_cancel (kx->resend_task); + kx->resend_task = NULL; + } + if (NULL != kx->resend_env) + { + GNUNET_MQ_discard (kx->resend_env); + kx->resend_env = NULL; + } derive_per_message_secrets (&kx->ihts, 0, enc_key,