const unsigned char *data,
SilcUInt32 data_len);
+/*
+ * Notify the owner of the ske that we failed. Ensures that we don't make the
+ * same callout twice, as the notification callback routines are not designed
+ * to handle that case.
+ */
+static void silc_ske_notify_failure(SilcSKE ske)
+{
+ SILC_LOG_DEBUG(("Notifying SKE %p owner of failure (failure_notified = %d)",
+ ske, ske->failure_notified));
+
+ /*
+ * First, check if we have already made a failure callout. If so, then we
+ * will stop here.
+ */
+ if (ske->failure_notified)
+ return;
+
+ /*
+ * Mark ourselves as having already sent the failure notification here and
+ * now.
+ */
+ ske->failure_notified = TRUE;
+
+ SILC_LOG_DEBUG(("Deliver failure notification for SKE %p (%s)",
+ ske, ske->responder ? "responder" : "initiator"));
+
+ /*
+ * Finally, make the call to the owner's registered failure callback.
+ */
+ if (ske->responder)
+ silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
+ else
+ silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+}
+
/* Packet callback */
static SilcBool silc_ske_packet_receive(SilcPacketEngine engine,
}
/* See if received failure from remote */
- if (packet->type == SILC_PACKET_FAILURE) {
- if (ske->responder)
- silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
- else
- silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
- }
+ if (packet->type == SILC_PACKET_FAILURE)
+ silc_ske_notify_failure(ske);
/* Handle rekey and SUCCESS packets synchronously. After SUCCESS packets
they keys are taken into use immediately, hence the synchronous
static SilcSKEStatus silc_ske_check_version(SilcSKE ske)
{
- SilcUInt32 r_software_version = 0;
- char *r_software_string = NULL;
-
if (!ske->remote_version || !ske->version)
return SILC_SKE_STATUS_BAD_VERSION;
if (!silc_parse_version_string(ske->remote_version, NULL, NULL,
- &r_software_version,
- &r_software_string, NULL))
+ NULL, NULL, NULL))
return SILC_SKE_STATUS_BAD_VERSION;
return SILC_SKE_STATUS_OK;
silc_free(ske->retrans.data);
ske->retrans.data = NULL;
ske->status = SILC_SKE_STATUS_TIMEOUT;
- if (ske->responder)
- silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
- else
- silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+ silc_ske_notify_failure(ske);
silc_fsm_continue_sync(&ske->fsm);
return;
}
static void silc_ske_completion(SilcSKE ske)
{
/* Call the completion callback */
- if (!ske->freed && !ske->aborted && ske->callbacks->completed) {
+ if (!ske->aborted && ske->callbacks->completed) {
if (ske->status != SILC_SKE_STATUS_OK)
ske->callbacks->completed(ske, ske->status, NULL, NULL, NULL,
ske->callbacks->context);
void *destructor_context)
{
SilcSKE ske = fsm_context;
- ske->running = FALSE;
- if (ske->freed)
- silc_ske_free(ske);
+ silc_ske_free(ske);
}
/* Key exchange timeout task callback */
ske->packet = NULL;
ske->status = SILC_SKE_STATUS_TIMEOUT;
- if (ske->responder)
- silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
- else
- silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+ silc_ske_notify_failure(ske);
silc_fsm_continue_sync(&ske->fsm);
}
void silc_ske_free(SilcSKE ske)
{
- SILC_LOG_DEBUG(("Freeing Key Exchange object"));
-
if (!ske)
return;
- if (ske->running) {
- ske->freed = TRUE;
+ SILC_LOG_DEBUG(("Freeing Key Exchange object %p: aborted=%u refcount=%hu",
+ ske, ske->aborted, ske->refcnt));
- if (ske->aborted) {
- /* If already aborted, destroy the session immediately */
- ske->packet = NULL;
- ske->status = SILC_SKE_STATUS_ERROR;
- if (ske->responder)
- silc_fsm_next(&ske->fsm, silc_ske_st_responder_failure);
- else
- silc_fsm_next(&ske->fsm, silc_ske_st_initiator_failure);
+ if (ske->aborted) {
+ /*
+ * If already aborted, destroy the session immediately. Only do the
+ * notification work if we have not already though, as doing so twice
+ * results in memory corruption. We may have silc_ske_free called
+ * twice, once when the abort is requested, and then again when the
+ * FSM finish routine is called. We have to be prepared to handle
+ * that case.
+ */
+ ske->packet = NULL;
+ ske->status = SILC_SKE_STATUS_ERROR;
+
+ silc_ske_notify_failure(ske);
+
+ if (silc_fsm_is_started(&ske->fsm))
silc_fsm_continue_sync(&ske->fsm);
- }
- return;
+ else
+ SILC_LOG_DEBUG(("Not continuing FSM as it's finished for SKE %p", ske));
}
ske->refcnt--;
silc_free(ske->hash);
silc_free(ske->callbacks);
- memset(ske, 'F', sizeof(*ske));
+ memset(ske, 0xdd, sizeof(*ske));
silc_free(ske);
}
if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) {
SILC_GET32_MSB(error, ske->packet->buffer.data);
- ske->status = error;
silc_packet_free(ske->packet);
ske->packet = NULL;
}
+ ske->status = error;
SILC_LOG_DEBUG(("Error %s (%d) received during key exchange",
silc_ske_map_status(ske->status), ske->status));
SilcSKEParams params,
SilcSKEStartPayload start_payload)
{
- SILC_LOG_DEBUG(("Start SKE as initiator"));
+ SILC_LOG_DEBUG(("Start SKE %p as initiator; stream=%p; params=%p; "
+ "start_payload=%p", ske, stream, params, start_payload));
if (!ske || !stream || !params || !params->version)
return NULL;
ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
ske->start_payload = start_payload;
ske->version = params->version;
- ske->running = TRUE;
+ ++ ske->refcnt;
/* Link to packet stream to get key exchange packets */
ske->stream = stream;
if (ske->packet && silc_buffer_len(&ske->packet->buffer) == 4) {
SILC_GET32_MSB(error, ske->packet->buffer.data);
- ske->status = error;
silc_packet_free(ske->packet);
ske->packet = NULL;
}
+ ske->status = error;
silc_packet_stream_unlink(ske->stream, &silc_ske_stream_cbs, ske);
silc_schedule_task_del_by_context(ske->schedule, ske);
ske->timeout = params->timeout_secs ? params->timeout_secs : 30;
if (ske->flags & SILC_SKE_SP_FLAG_IV_INCLUDED)
ske->session_port = params->session_port;
- ske->version = strdup(params->version);
+ ske->version = params->version;
if (!ske->version)
return NULL;
- ske->running = TRUE;
+ ++ ske->refcnt;
/* Link to packet stream to get key exchange packets */
ske->stream = stream;
ske->rekey = rekey;
ske->responder = FALSE;
- ske->running = TRUE;
ske->rekeying = TRUE;
+ ++ ske->refcnt;
/* Link to packet stream to get key exchange packets */
ske->stream = stream;
ske->rekey = rekey;
ske->responder = TRUE;
- ske->running = TRUE;
ske->rekeying = TRUE;
ske->packet = packet;
+ ++ ske->refcnt;
/* Link to packet stream to get key exchange packets */
ske->stream = stream;