From: Andreas Steffen Date: Sat, 31 May 2014 18:19:12 +0000 (+0200) Subject: Detect RADIUS packet retransmissions X-Git-Tag: 5.2.0dr5~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=59db6660946a92b1919753572b7c6643d3dbe6c4;p=thirdparty%2Fstrongswan.git Detect RADIUS packet retransmissions --- diff --git a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c index 89237f564e..109c216d5a 100644 --- a/src/libcharon/plugins/tnc_pdp/tnc_pdp.c +++ b/src/libcharon/plugins/tnc_pdp/tnc_pdp.c @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -37,7 +38,7 @@ #include typedef struct private_tnc_pdp_t private_tnc_pdp_t; - +typedef struct client_entry_t client_entry_t; /** * Default RADIUS port, when not configured */ @@ -48,6 +49,8 @@ typedef struct private_tnc_pdp_t private_tnc_pdp_t; */ #define MAX_PACKET 4096 +#define RADIUS_RETRANSMIT_TIMEOUT 30 /* seconds */ + /** * private data of tnc_pdp_t */ @@ -98,6 +101,11 @@ struct private_tnc_pdp_t { */ chunk_t secret; + /** + * RADIUS clients + */ + linked_list_t *clients; + /** * MD5 hasher */ @@ -120,6 +128,33 @@ struct private_tnc_pdp_t { }; +/** + * Client entry helping to detect RADIUS packet retransmissions + */ +struct client_entry_t { + + /** + * IP host address and port of client + */ + host_t *host; + + /** + * Time of last RADIUS Access-Request received from client + */ + time_t last_time; + + /** + * Identifier of last RADIUS Access-Request received from client + */ + uint8_t last_id; +}; + +static void free_client_entry(client_entry_t *this) +{ + this->host->destroy(this->host); + free(this); +} + /** * Open IPv4 or IPv6 UDP socket */ @@ -663,16 +698,24 @@ static bool radius_receive(private_tnc_pdp_t *this, int fd, watcher_event_t even { radius_message_t *request; char buffer[MAX_PACKET]; + client_entry_t *client; + bool retransmission = FALSE, found = FALSE, stale; + enumerator_t *enumerator; int bytes_read = 0; host_t *source; + uint8_t id; + time_t now; + union { struct sockaddr_in in4; struct sockaddr_in6 in6; } src; + struct iovec iov = { .iov_base = buffer, .iov_len = MAX_PACKET, }; + struct msghdr msg = { .msg_name = &src, .msg_namelen = sizeof(src), @@ -704,7 +747,46 @@ static bool radius_receive(private_tnc_pdp_t *this, int fd, watcher_event_t even if (request->verify(request, NULL, this->secret, this->hasher, this->signer)) { - process_eap(this, request, source); + id = request->get_identifier(request); + now = time(NULL); + + enumerator = this->clients->create_enumerator(this->clients); + while (enumerator->enumerate(enumerator, &client)) + { + stale = client->last_time < now - RADIUS_RETRANSMIT_TIMEOUT; + + if (source->equals(source, client->host)) + { + retransmission = !stale && client->last_id == id; + client->last_id = id; + client->last_time = now; + found = TRUE; + } + else if (stale) + { + this->clients->remove_at(this->clients, enumerator); + free_client_entry(client); + } + } + enumerator->destroy(enumerator); + + if (!found) + { + client = malloc_thing(client_entry_t); + client->host = source->clone(source); + client->last_id = id; + client->last_time = now; + this->clients->insert_last(this->clients, client); + } + if (retransmission) + { + DBG1(DBG_CFG, "ignoring RADIUS Access-Request 0x%02x, " + "already processing", id); + } + else + { + process_eap(this, request, source); + } } request->destroy(request); } @@ -739,6 +821,10 @@ METHOD(tnc_pdp_t, destroy, void, lib->watcher->remove(lib->watcher, this->radius_ipv6); close(this->radius_ipv6); } + if (this->clients) + { + this->clients->destroy_function(this->clients, (void*)free_client_entry); + } DESTROY_IF(this->server); DESTROY_IF(this->signer); DESTROY_IF(this->hasher); @@ -843,6 +929,7 @@ tnc_pdp_t *tnc_pdp_create(void) this->radius_ipv4 = open_udp_socket(AF_INET, radius_port); this->radius_ipv6 = open_udp_socket(AF_INET6, radius_port); this->secret = chunk_from_str(secret); + this->clients = linked_list_create(); this->type = eap_type_from_string(eap_type_str); this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5); this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);