📄 radius_client.c
字号:
/*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
Module Name:
radius_client.c
Revision History:
Who When What
-------- ---------- ----------------------------------------------
Jan, Lee Dec --2003 modified
*/
#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 "rt61apd.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) */
#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this
* many failed retry attempts */
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;
s = rtapd->radius->auth_serv_sock;
/* 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;
entry->next_wait *= 2;
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;
int auth_failover = 0;
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)
auth_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 (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);
}
}
static void Radius_client_list_add(rtapd *rtapd, struct radius_msg *msg,
RadiusType msg_type, u8 *shared_secret, size_t shared_secret_len)
{
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;
time(&entry->first_try);
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
entry->attempts = 1;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
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 *shared_secret;
size_t shared_secret_len;
char *name;
int s, res;
shared_secret = rtapd->conf->auth_server->shared_secret;
shared_secret_len = rtapd->conf->auth_server->shared_secret_len;
Radius_msg_finish(msg, shared_secret, shared_secret_len);
name = "authentication";
s = rtapd->radius->auth_serv_sock;
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);
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_INFO,"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)
{
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
entry->attempts = 0;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
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;
}
return 0;
}
static void Radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
{
rtapd *rtapd = eloop_ctx;
struct hostapd_radius_server *oserv;
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);
}
if (rtapd->conf->radius_retry_primary_interval)
eloop_register_timeout(rtapd->conf->radius_retry_primary_interval, 0, Radius_retry_primary_timer, rtapd, NULL);
}
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;
}
int Radius_client_init(rtapd *rtapd)
{
if (rtapd->radius == NULL)
{
rtapd->radius = malloc(sizeof(struct radius_client_data));
memset(rtapd->radius, 0, sizeof(struct radius_client_data));
rtapd->radius->auth_serv_sock = -1;
}
if (rtapd->radius == NULL)
return -1;
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);
}
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;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -