1
0
This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
2024-07-22 01:58:46 -03:00

665 lines
17 KiB
C
Executable File

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "rtdot1x.h"
#include "radius.h"
#include "radius_client.h"
#include "eloop.h"
/* Defaults for RADIUS retransmit values (exponential backoff) */
#define RADIUS_CLIENT_FIRST_WAIT 1 /* seconds */
#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */
#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts
* before entry is removed from retransmit list */
#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit
* list (oldest will be removed, if this limit is exceeded) */
#ifdef TCSUPPORT_WLAN_8021X_EXT
#define RADIUS_CLIENT_NUM_FAILOVER 6
#else
#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this
* many failed retry attempts */
#endif
#ifdef TCSUPPORT_WLAN_8021X_EXT
#define RADIUS_CLIENT_DEFAULT_WAIT 10 /* seconds */
#endif
static int
Radius_change_server(rtapd *rtapd, struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv, int sock, int auth);
static void Radius_client_msg_free(struct radius_msg_list *req)
{
Radius_msg_free(req->msg);
free(req->msg);
free(req);
}
int Radius_client_register(rtapd *apd, RadiusType msg_type,
RadiusRxResult (*handler)(rtapd *apd, struct radius_msg *msg, struct radius_msg *req,
u8 *shared_secret, size_t shared_secret_len, void *data), void *data)
{
struct radius_rx_handler **handlers, *newh;
size_t *num;
handlers = &apd->radius->auth_handlers;
num = &apd->radius->num_auth_handlers;
newh = (struct radius_rx_handler *)
realloc(*handlers, (*num + 1) * sizeof(struct radius_rx_handler));
if (newh == NULL)
return -1;
newh[*num].handler = handler;
newh[*num].data = data;
(*num)++;
*handlers = newh;
return 0;
}
static int Radius_client_retransmit(rtapd *rtapd, struct radius_msg_list *entry, time_t now)
{
int s;
#if MULTIPLE_RADIUS
s = rtapd->radius->mbss_auth_serv_sock[entry->ApIdx];
#else
s = rtapd->radius->auth_serv_sock;
#endif
/* retransmit; remove entry if too many attempts */
entry->attempts++;
if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0)
perror("send[RADIUS]");
entry->next_try = now + entry->next_wait;
#ifdef RT_8021X_EXT
entry->next_wait = RADIUS_CLIENT_DEFAULT_WAIT;
#else
entry->next_wait *= 2;
#endif
if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES)
{
DBGPRINT(RT_DEBUG_ERROR,"Removing un-ACKed RADIUS message due to too many failed retransmit attempts\n");
return 1;
}
return 0;
}
static void Radius_client_timer(void *eloop_ctx, void *timeout_ctx)
{
rtapd *rtapd = eloop_ctx;
time_t now, first;
struct radius_msg_list *entry, *prev, *tmp;
#if MULTIPLE_RADIUS
int i;
int mbss_auth_failover[MAX_MBSSID_NUM];
#else
int auth_failover = 0;
#endif
#if MULTIPLE_RADIUS
for (i = 0; i < MAX_MBSSID_NUM; i++)
mbss_auth_failover[i] = 0;
#endif
entry = rtapd->radius->msgs;
if (!entry)
return;
time(&now);
first = 0;
prev = NULL;
while (entry)
{
if (now >= entry->next_try && Radius_client_retransmit(rtapd, entry, now))
{
if (prev)
prev->next = entry->next;
else
rtapd->radius->msgs = entry->next;
tmp = entry;
entry = entry->next;
Radius_client_msg_free(tmp);
rtapd->radius->num_msgs--;
continue;
}
if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER)
{
if (entry->msg_type == RADIUS_AUTH)
{
#if MULTIPLE_RADIUS
mbss_auth_failover[entry->ApIdx]++;
#else
auth_failover++;
#endif
DBGPRINT(RT_DEBUG_WARN, "Radius_client_timer : Failed retry attempts(%d) \n", RADIUS_CLIENT_NUM_FAILOVER);
}
}
if (first == 0 || entry->next_try < first)
first = entry->next_try;
prev = entry;
entry = entry->next;
}
if (rtapd->radius->msgs)
{
if (first < now)
first = now;
eloop_register_timeout(first - now, 0, Radius_client_timer, rtapd, NULL);
}
#if MULTIPLE_RADIUS
for (i = 0; i < rtapd->conf->SsidNum; i++)
{
if (mbss_auth_failover[i] && rtapd->conf->mbss_num_auth_servers[i] > 1)
{
struct hostapd_radius_server *next, *old;
old = rtapd->conf->mbss_auth_server[i];
next = old + 1;
if (next > &(rtapd->conf->mbss_auth_servers[i][rtapd->conf->mbss_num_auth_servers[i]- 1]))
next = rtapd->conf->mbss_auth_servers[i];
rtapd->conf->mbss_auth_server[i] = next;
Radius_change_server(rtapd, next, old, rtapd->radius->mbss_auth_serv_sock[i], 1);
DBGPRINT(RT_DEBUG_WARN, "Radius_client_timer : ready to change RADIUS server for %s%d\n", rtapd->prefix_wlan_name, i);
}
}
#else
if (auth_failover && rtapd->conf->num_auth_servers > 1)
{
struct hostapd_radius_server *next, *old;
old = rtapd->conf->auth_server;
next = old + 1;
if (next > &(rtapd->conf->auth_servers[rtapd->conf->num_auth_servers - 1]))
next = rtapd->conf->auth_servers;
rtapd->conf->auth_server = next;
Radius_change_server(rtapd, next, old, rtapd->radius->auth_serv_sock, 1);
DBGPRINT(RT_DEBUG_WARN, "==> Radius_client_timer : ready to change RADIUS server \n");
}
#endif
}
static void Radius_client_list_add(rtapd *rtapd, struct radius_msg *msg,
RadiusType msg_type, u8 *shared_secret, size_t shared_secret_len, u8 ApIdx)
{
struct radius_msg_list *entry, *prev;
if (eloop_terminated())
{
/* No point in adding entries to retransmit queue since event
* loop has already been terminated. */
DBGPRINT(RT_DEBUG_TRACE,"eloop_terminate \n");
Radius_msg_free(msg);
free(msg);
return;
}
entry = malloc(sizeof(*entry));
if (entry == NULL)
{
DBGPRINT(RT_DEBUG_TRACE,"Failed to add RADIUS packet into retransmit list\n");
Radius_msg_free(msg);
free(msg);
return;
}
memset(entry, 0, sizeof(*entry));
entry->msg = msg;
entry->msg_type = msg_type;
entry->shared_secret = shared_secret;
entry->shared_secret_len = shared_secret_len;
entry->ApIdx = ApIdx;
time(&entry->first_try);
#ifdef RT_8021X_EXT
entry->next_try = entry->first_try +RADIUS_CLIENT_DEFAULT_WAIT;
#else
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
#endif
entry->attempts = 1;
#ifdef RT_8021X_EXT
entry->next_wait = RADIUS_CLIENT_DEFAULT_WAIT;
#else
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
#endif
if (!rtapd->radius->msgs)
{
eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, Radius_client_timer, rtapd, NULL);
}
entry->next = rtapd->radius->msgs;
rtapd->radius->msgs = entry;
if (rtapd->radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES)
{
DBGPRINT(RT_DEBUG_TRACE,"Removing the oldest un-ACKed RADIUS packet due to retransmit list limits.\n");
prev = NULL;
while (entry->next)
{
prev = entry;
entry = entry->next;
}
if (prev)
{
prev->next = NULL;
Radius_client_msg_free(entry);
}
} else
rtapd->radius->num_msgs++;
}
int Radius_client_send(rtapd *rtapd, struct radius_msg *msg, RadiusType msg_type, u8 ApIdx)
{
u8 *shared_secret;
size_t shared_secret_len;
char *name;
int s, res = 0;
#if MULTIPLE_RADIUS
shared_secret = rtapd->conf->mbss_auth_server[ApIdx]->shared_secret;
shared_secret_len = rtapd->conf->mbss_auth_server[ApIdx]->shared_secret_len;
s = rtapd->radius->mbss_auth_serv_sock[ApIdx];
DBGPRINT(RT_DEBUG_TRACE, "Send packet to server (%s)\n",
inet_ntoa(rtapd->conf->mbss_auth_server[ApIdx]->addr));
#else
shared_secret = rtapd->conf->auth_server->shared_secret;
shared_secret_len = rtapd->conf->auth_server->shared_secret_len;
s = rtapd->radius->auth_serv_sock;
#endif
Radius_msg_finish(msg, shared_secret, shared_secret_len);
name = "authentication";
res = send(s, msg->buf, msg->buf_used, 0);
if (res < 0)
perror("send[RADIUS]");
Radius_client_list_add(rtapd, msg, msg_type, shared_secret, shared_secret_len, ApIdx);
return res;
}
static void Radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
{
rtapd *rtapd = eloop_ctx;
RadiusType msg_type = (RadiusType) sock_ctx;
int len, i,len_80211hdr=24;
unsigned char buf[3000];
struct radius_msg *msg;
struct radius_rx_handler *handlers;
size_t num_handlers;
struct radius_msg_list *req, *prev_req;
DBGPRINT(RT_DEBUG_TRACE, "RADIUS_CLIENT_RECEIVE : msg_type= %d \n", msg_type);
len = recv(sock, buf, sizeof(buf), 0);
if (len < 0)
{
perror("recv[RADIUS]");
return;
}
if (len == sizeof(buf))
{
DBGPRINT(RT_DEBUG_ERROR,"Possibly too long UDP frame for our buffer - dropping it\n");
return;
}
if(buf[0]!=0xff)
{
int i;
DBGPRINT(RT_DEBUG_INFO," r_dump %d bytes:",len);
for (i = 0; i < len_80211hdr; i++)
DBGPRINT(RT_DEBUG_INFO," %02x", buf[i]);
DBGPRINT(RT_DEBUG_INFO,"\n");
}
msg = Radius_msg_parse(buf, len);
if (msg == NULL)
{
DBGPRINT(RT_DEBUG_ERROR,"Parsing incoming RADIUS frame failed\n");
return;
}
handlers = rtapd->radius->auth_handlers;
num_handlers = rtapd->radius->num_auth_handlers;
prev_req = NULL;
req = rtapd->radius->msgs;
while (req)
{
/* TODO: also match by src addr:port of the packet when using
* alternative RADIUS servers (?) */
if (req->msg_type == msg_type && req->msg->hdr->identifier == msg->hdr->identifier)
break;
prev_req = req;
req = req->next;
}
if (req == NULL)
{
goto fail;
}
/* Remove ACKed RADIUS packet from retransmit list */
if (prev_req)
prev_req->next = req->next;
else
rtapd->radius->msgs = req->next;
rtapd->radius->num_msgs--;
for (i = 0; i < num_handlers; i++)
{
RadiusRxResult res;
res = handlers[i].handler(rtapd, msg, req->msg, req->shared_secret, req->shared_secret_len, handlers[i].data);
switch (res)
{
case RADIUS_RX_PROCESSED:
Radius_msg_free(msg);
free(msg);
/* continue */
case RADIUS_RX_QUEUED:
Radius_client_msg_free(req);
return;
case RADIUS_RX_UNKNOWN:
/* continue with next handler */
break;
}
}
DBGPRINT(RT_DEBUG_ERROR,"No RADIUS RX handler found (type=%d code=%d id=%d) - dropping "
"packet\n", msg_type, msg->hdr->code, msg->hdr->identifier);
Radius_client_msg_free(req);
fail:
Radius_msg_free(msg);
free(msg);
}
u8 Radius_client_get_id(rtapd *rtapd)
{
struct radius_msg_list *entry, *prev, *remove;
u8 id = rtapd->radius->next_radius_identifier++;
/* remove entries with matching id from retransmit list to avoid
* using new reply from the RADIUS server with an old request */
entry = rtapd->radius->msgs;
prev = NULL;
while (entry)
{
if (entry->msg->hdr->identifier == id)
{
if (prev)
prev->next = entry->next;
else
rtapd->radius->msgs = entry->next;
remove = entry;
} else
remove = NULL;
prev = entry;
entry = entry->next;
if (remove)
Radius_client_msg_free(remove);
}
return id;
}
void Radius_client_flush(rtapd *rtapd)
{
struct radius_msg_list *entry, *prev;
if (!rtapd->radius)
return;
eloop_cancel_timeout(Radius_client_timer, rtapd, NULL);
entry = rtapd->radius->msgs;
rtapd->radius->msgs = NULL;
rtapd->radius->num_msgs = 0;
while (entry)
{
prev = entry;
entry = entry->next;
Radius_client_msg_free(prev);
}
}
static int
Radius_change_server(rtapd *rtapd, struct hostapd_radius_server *nserv,
struct hostapd_radius_server *oserv, int sock, int auth)
{
struct sockaddr_in serv;
if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
memcmp(nserv->shared_secret, oserv->shared_secret, nserv->shared_secret_len) != 0)
{
/* Pending RADIUS packets used different shared
* secret, so they would need to be modified. Could
* update all message authenticators and
* User-Passwords, etc. and retry with new server. For
* now, just drop all pending packets. */
Radius_client_flush(rtapd);
}
else
{
/* Reset retry counters for the new server */
struct radius_msg_list *entry;
entry = rtapd->radius->msgs;
while (entry)
{
#ifdef RT_8021X_EXT
entry->next_try = entry->first_try + RADIUS_CLIENT_DEFAULT_WAIT;
#else
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
#endif
entry->attempts = 0;
#ifdef RT_8021X_EXT
entry->next_wait = RADIUS_CLIENT_DEFAULT_WAIT;
#else
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
#endif
entry = entry->next;
}
if (rtapd->radius->msgs)
{
eloop_cancel_timeout(Radius_client_timer, rtapd, NULL);
eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, Radius_client_timer, rtapd, NULL);
}
}
// bind before connect to assign local port
/*Comment by rory
memset(&serv, 0, sizeof(serv));
port = 2048;
serv.sin_family = AF_INET;
//serv.sin_addr.s_addr = inet_addr("192.168.1.138");
serv.sin_addr.s_addr = rtapd->conf->own_ip_addr.s_addr;
serv.sin_port = htons(port);
if (bind(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0)
{
perror("bind");
return -1;
}*/
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = nserv->addr.s_addr;
serv.sin_port = htons(nserv->port);
if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0)
{
perror("connect[radius]");
return -1;
}
DBGPRINT(RT_DEBUG_TRACE, "Radius_change_server :: Connect to Radius Server(%s)\n", inet_ntoa(nserv->addr));
return 0;
}
static void Radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
{
rtapd *rtapd = eloop_ctx;
struct hostapd_radius_server *oserv;
DBGPRINT(RT_DEBUG_TRACE, "RUN Radius_retry_primary_timer.....\n");
#if MULTIPLE_RADIUS
int i;
for (i = 0; i < rtapd->conf->SsidNum; i++)
{
if (rtapd->radius->mbss_auth_serv_sock[i] >= 0 && rtapd->conf->mbss_auth_servers[i] &&
rtapd->conf->mbss_auth_server[i] != rtapd->conf->mbss_auth_servers[i])
{
oserv = rtapd->conf->mbss_auth_server[i];
rtapd->conf->mbss_auth_server[i] = rtapd->conf->mbss_auth_servers[i];
Radius_change_server(rtapd, rtapd->conf->mbss_auth_server[i], oserv, rtapd->radius->mbss_auth_serv_sock[i], 1);
}
}
#else
if (rtapd->radius->auth_serv_sock >= 0 && rtapd->conf->auth_servers &&
rtapd->conf->auth_server != rtapd->conf->auth_servers)
{
oserv = rtapd->conf->auth_server;
rtapd->conf->auth_server = rtapd->conf->auth_servers;
Radius_change_server(rtapd, rtapd->conf->auth_server, oserv, rtapd->radius->auth_serv_sock, 1);
}
#endif
if (rtapd->conf->radius_retry_primary_interval)
eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
}
#if MULTIPLE_RADIUS
static int Radius_client_init_auth(rtapd *rtapd, int apidx)
{
if (rtapd->conf->mbss_auth_server[apidx]->addr.s_addr == 0)
{
DBGPRINT(RT_DEBUG_WARN, "Radius_client_init_auth: can't create auth RADIUS socket for %s%d (it's invalid IP)\n", rtapd->prefix_wlan_name, apidx);
return -1;
}
rtapd->radius->mbss_auth_serv_sock[apidx] = socket(PF_INET, SOCK_DGRAM, 0);
if (rtapd->radius->mbss_auth_serv_sock[apidx] < 0)
{
perror("socket[PF_INET,SOCK_DGRAM]");
return -1;
}
if (Radius_change_server(rtapd, rtapd->conf->mbss_auth_server[apidx], NULL, rtapd->radius->mbss_auth_serv_sock[apidx], 1))
return -1;
if (eloop_register_read_sock(rtapd->radius->mbss_auth_serv_sock[apidx], Radius_client_receive, rtapd, (void *) RADIUS_AUTH))
{
DBGPRINT(RT_DEBUG_ERROR,"Could not register read socket for authentication server - iface-%d\n", apidx);
return -1;
}
return 0;
}
#else
static int Radius_client_init_auth(rtapd *rtapd)
{
rtapd->radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
if (rtapd->radius->auth_serv_sock < 0)
{
perror("socket[PF_INET,SOCK_DGRAM]");
return -1;
}
Radius_change_server(rtapd, rtapd->conf->auth_server, NULL, rtapd->radius->auth_serv_sock, 1);
if (eloop_register_read_sock(rtapd->radius->auth_serv_sock, Radius_client_receive, rtapd, (void *) RADIUS_AUTH))
{
DBGPRINT(RT_DEBUG_ERROR,"Could not register read socket for authentication server\n");
return -1;
}
return 0;
}
#endif
int Radius_client_init(rtapd *rtapd)
{
#if MULTIPLE_RADIUS
int i, ready_sock_count = 0, bReInit = 1;
#endif
if (rtapd->radius == NULL)
{
rtapd->radius = malloc(sizeof(struct radius_client_data));
if (rtapd->radius == NULL)
return -1;
memset(rtapd->radius, 0, sizeof(struct radius_client_data));
#if MULTIPLE_RADIUS
for (i = 0; i < MAX_MBSSID_NUM; i++)
rtapd->radius->mbss_auth_serv_sock[i] = -1;
bReInit= 0;
#else
rtapd->radius->auth_serv_sock = -1;
#endif
}
#if MULTIPLE_RADIUS
// Create socket for auth RADIUS
for (i = 0; i < rtapd->conf->SsidNum; i++)
{
if (rtapd->radius->mbss_auth_serv_sock[i] < 0)
{
if (rtapd->conf->mbss_auth_server[i] && (!Radius_client_init_auth(rtapd, i)))
ready_sock_count++;
}
else
ready_sock_count++;
}
if (ready_sock_count == 0)
{
DBGPRINT(RT_DEBUG_ERROR, "Radius_client_init : no any auth RADIUS socket ready \n");
return -1;
}
else
DBGPRINT(RT_DEBUG_TRACE, "Radius_client_init : ready_sock_count %d \n", ready_sock_count);
if (rtapd->conf->radius_retry_primary_interval && !bReInit && ready_sock_count > 0)
eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
#else
if( rtapd->radius->auth_serv_sock < 0)
{
if (rtapd->conf->auth_server && Radius_client_init_auth(rtapd))
return -1;
if (rtapd->conf->radius_retry_primary_interval)
eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
}
#endif
return 0;
}
void Radius_client_deinit(rtapd *rtapd)
{
if (!rtapd->radius)
return;
eloop_cancel_timeout(Radius_retry_primary_timer, rtapd, NULL);
Radius_client_flush(rtapd);
free(rtapd->radius->auth_handlers);
free(rtapd->radius);
rtapd->radius = NULL;
}