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:
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,