Detect RADIUS packet retransmissions
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <radius_message.h>
|
#include <radius_message.h>
|
||||||
#include <radius_mppe.h>
|
#include <radius_mppe.h>
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
#include <sa/eap/eap_method.h>
|
#include <sa/eap/eap_method.h>
|
||||||
|
|
||||||
typedef struct private_tnc_pdp_t private_tnc_pdp_t;
|
typedef struct private_tnc_pdp_t private_tnc_pdp_t;
|
||||||
|
typedef struct client_entry_t client_entry_t;
|
||||||
/**
|
/**
|
||||||
* Default RADIUS port, when not configured
|
* 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 MAX_PACKET 4096
|
||||||
|
|
||||||
|
#define RADIUS_RETRANSMIT_TIMEOUT 30 /* seconds */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* private data of tnc_pdp_t
|
* private data of tnc_pdp_t
|
||||||
*/
|
*/
|
||||||
@@ -98,6 +101,11 @@ struct private_tnc_pdp_t {
|
|||||||
*/
|
*/
|
||||||
chunk_t secret;
|
chunk_t secret;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RADIUS clients
|
||||||
|
*/
|
||||||
|
linked_list_t *clients;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MD5 hasher
|
* 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
|
* 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;
|
radius_message_t *request;
|
||||||
char buffer[MAX_PACKET];
|
char buffer[MAX_PACKET];
|
||||||
|
client_entry_t *client;
|
||||||
|
bool retransmission = FALSE, found = FALSE, stale;
|
||||||
|
enumerator_t *enumerator;
|
||||||
int bytes_read = 0;
|
int bytes_read = 0;
|
||||||
host_t *source;
|
host_t *source;
|
||||||
|
uint8_t id;
|
||||||
|
time_t now;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct sockaddr_in in4;
|
struct sockaddr_in in4;
|
||||||
struct sockaddr_in6 in6;
|
struct sockaddr_in6 in6;
|
||||||
} src;
|
} src;
|
||||||
|
|
||||||
struct iovec iov = {
|
struct iovec iov = {
|
||||||
.iov_base = buffer,
|
.iov_base = buffer,
|
||||||
.iov_len = MAX_PACKET,
|
.iov_len = MAX_PACKET,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct msghdr msg = {
|
struct msghdr msg = {
|
||||||
.msg_name = &src,
|
.msg_name = &src,
|
||||||
.msg_namelen = sizeof(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,
|
if (request->verify(request, NULL, this->secret, this->hasher,
|
||||||
this->signer))
|
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);
|
request->destroy(request);
|
||||||
}
|
}
|
||||||
@@ -739,6 +821,10 @@ METHOD(tnc_pdp_t, destroy, void,
|
|||||||
lib->watcher->remove(lib->watcher, this->radius_ipv6);
|
lib->watcher->remove(lib->watcher, this->radius_ipv6);
|
||||||
close(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->server);
|
||||||
DESTROY_IF(this->signer);
|
DESTROY_IF(this->signer);
|
||||||
DESTROY_IF(this->hasher);
|
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_ipv4 = open_udp_socket(AF_INET, radius_port);
|
||||||
this->radius_ipv6 = open_udp_socket(AF_INET6, radius_port);
|
this->radius_ipv6 = open_udp_socket(AF_INET6, radius_port);
|
||||||
this->secret = chunk_from_str(secret);
|
this->secret = chunk_from_str(secret);
|
||||||
|
this->clients = linked_list_create();
|
||||||
this->type = eap_type_from_string(eap_type_str);
|
this->type = eap_type_from_string(eap_type_str);
|
||||||
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
|
this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_MD5);
|
||||||
this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
|
this->signer = lib->crypto->create_signer(lib->crypto, AUTH_HMAC_MD5_128);
|
||||||
|
|||||||
Reference in New Issue
Block a user