Detect RADIUS packet retransmissions
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <radius_message.h>
|
||||
#include <radius_mppe.h>
|
||||
@@ -37,7 +38,7 @@
|
||||
#include <sa/eap/eap_method.h>
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user