diff options
-rw-r--r-- | src/libcharon/plugins/eap_radius/eap_radius_dae.c | 147 |
1 files changed, 126 insertions, 21 deletions
diff --git a/src/libcharon/plugins/eap_radius/eap_radius_dae.c b/src/libcharon/plugins/eap_radius/eap_radius_dae.c index d1aac679b..3ea587309 100644 --- a/src/libcharon/plugins/eap_radius/eap_radius_dae.c +++ b/src/libcharon/plugins/eap_radius/eap_radius_dae.c @@ -71,9 +71,114 @@ struct private_eap_radius_dae_t { * HMAC MD5 signer, with secret set */ signer_t *signer; + + /** + * List of responses for retransmission, as entry_t + */ + linked_list_t *responses; }; /** + * Entry to store responses for retransmit + */ +typedef struct { + /** stored response */ + radius_message_t *response; + /** client that sent the request */ + host_t *client; +} entry_t; + +/** + * Clean up an entry + */ +static void entry_destroy(entry_t *entry) +{ + entry->response->destroy(entry->response); + entry->client->destroy(entry->client); + free(entry); +} + +/** + * Save/Replace response for retransmission + */ +static void save_retransmit(private_eap_radius_dae_t *this, + radius_message_t *response, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client)) + { + entry->response->destroy(entry->response); + entry->response = response; + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + if (!found) + { + INIT(entry, + .response = response, + .client = client->clone(client), + ); + this->responses->insert_first(this->responses, entry); + } +} + +/** + * Send a RADIUS message to client + */ +static void send_message(private_eap_radius_dae_t *this, + radius_message_t *message, host_t *client) +{ + chunk_t data; + + data = message->get_encoding(message); + if (sendto(this->fd, data.ptr, data.len, 0, client->get_sockaddr(client), + *client->get_sockaddr_len(client)) != data.len) + { + DBG1(DBG_CFG, "sending RADIUS DAE response failed: %s", strerror(errno)); + } +} + +/** + * Check if we request is a retransmit, retransmit stored response + */ +static bool send_retransmit(private_eap_radius_dae_t *this, + radius_message_t *request, host_t *client) +{ + enumerator_t *enumerator; + entry_t *entry; + bool found = FALSE; + + enumerator = this->responses->create_enumerator(this->responses); + while (enumerator->enumerate(enumerator, &entry)) + { + if (client->equals(client, entry->client) && + request->get_identifier(request) == + entry->response->get_identifier(entry->response)) + { + DBG1(DBG_CFG, "received retransmit of RADIUS %N, retransmitting %N " + "to %H", radius_message_code_names, request->get_code(request), + radius_message_code_names, + entry->response->get_code(entry->response), client); + send_message(this, entry->response, client); + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return found; +} + +/** * Send an ACK/NAK response for a request */ static void send_response(private_eap_radius_dae_t *this, @@ -81,20 +186,14 @@ static void send_response(private_eap_radius_dae_t *this, host_t *client) { radius_message_t *response; - chunk_t data; response = radius_message_create(code); response->set_identifier(response, request->get_identifier(request)); response->sign(response, request->get_authenticator(request), this->secret, this->hasher, this->signer, NULL); - data = response->get_encoding(response); - if (sendto(this->fd, data.ptr, data.len, 0, client->get_sockaddr(client), - *client->get_sockaddr_len(client)) != data.len) - { - DBG1(DBG_CFG, "sending RADIUS DAE response failed: %s", strerror(errno)); - } - response->destroy(response); + send_message(this, response, client); + save_retransmit(this, response, client); } /** @@ -303,22 +402,26 @@ static job_requeue_t receive(private_eap_radius_dae_t *this) client = host_create_from_sockaddr((struct sockaddr*)&addr); if (client) { - if (request->verify(request, NULL, this->secret, - this->hasher, this->signer)) + if (!send_retransmit(this, request, client)) { - switch (request->get_code(request)) + if (request->verify(request, NULL, this->secret, + this->hasher, this->signer)) { - case RMC_DISCONNECT_REQUEST: - process_disconnect(this, request, client); - break; - case RMC_COA_REQUEST: - process_coa(this, request, client); + switch (request->get_code(request)) + { + case RMC_DISCONNECT_REQUEST: + process_disconnect(this, request, client); + break; + case RMC_COA_REQUEST: + process_coa(this, request, client); + break; + default: + DBG1(DBG_CFG, "ignoring unsupported RADIUS DAE " + "%N message from %H", + radius_message_code_names, + request->get_code(request), client); break; - default: - DBG1(DBG_CFG, "ignoring unsupported RADIUS DAE %N " - "message from %H", radius_message_code_names, - request->get_code(request), client); - break; + } } } client->destroy(client); @@ -386,6 +489,7 @@ METHOD(eap_radius_dae_t, destroy, void, } DESTROY_IF(this->signer); DESTROY_IF(this->hasher); + this->responses->destroy_function(this->responses, (void*)entry_destroy); free(this); } @@ -408,6 +512,7 @@ eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting) }, .hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5), .signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128), + .responses = linked_list_create(), ); if (!this->hasher || !this->signer) |