#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <stdio.h>
#include "rlm_eap.h"
EAP_PACKET *eap_packet_alloc(void)
{
EAP_PACKET *rp;
rp = rad_malloc(sizeof(EAP_PACKET));
memset(rp, 0, sizeof(EAP_PACKET));
return rp;
}
void eap_packet_free(EAP_PACKET **eap_packet_ptr)
{
EAP_PACKET *eap_packet;
if (!eap_packet_ptr) return;
eap_packet = *eap_packet_ptr;
if (!eap_packet) return;
if (eap_packet->type.data) {
if ((eap_packet->packet == NULL) ||
(eap_packet->type.data != (eap_packet->packet + 5))) {
free(eap_packet->type.data);
}
eap_packet->type.data = NULL;
}
if (eap_packet->packet) {
free(eap_packet->packet);
eap_packet->packet = NULL;
}
free(eap_packet);
*eap_packet_ptr = NULL;
}
EAP_DS *eap_ds_alloc(void)
{
EAP_DS *eap_ds;
eap_ds = rad_malloc(sizeof(EAP_DS));
memset(eap_ds, 0, sizeof(EAP_DS));
if ((eap_ds->response = eap_packet_alloc()) == NULL) {
eap_ds_free(&eap_ds);
return NULL;
}
if ((eap_ds->request = eap_packet_alloc()) == NULL) {
eap_ds_free(&eap_ds);
return NULL;
}
return eap_ds;
}
void eap_ds_free(EAP_DS **eap_ds_p)
{
EAP_DS *eap_ds;
if (!eap_ds_p) return;
eap_ds = *eap_ds_p;
if (!eap_ds) return;
if (eap_ds->response) eap_packet_free(&(eap_ds->response));
if (eap_ds->request) eap_packet_free(&(eap_ds->request));
free(eap_ds);
*eap_ds_p = NULL;
}
EAP_HANDLER *eap_handler_alloc(rlm_eap_t *inst)
{
EAP_HANDLER *handler;
handler = rad_malloc(sizeof(EAP_HANDLER));
memset(handler, 0, sizeof(EAP_HANDLER));
if (fr_debug_flag && inst->handler_tree) {
pthread_mutex_lock(&(inst->handler_mutex));
rbtree_insert(inst->handler_tree, handler);
pthread_mutex_unlock(&(inst->handler_mutex));
}
return handler;
}
void eap_handler_free(rlm_eap_t *inst, EAP_HANDLER *handler)
{
if (!handler)
return;
if (inst->handler_tree) {
pthread_mutex_lock(&(inst->handler_mutex));
rbtree_deletebydata(inst->handler_tree, handler);
pthread_mutex_unlock(&(inst->handler_mutex));
}
if (handler->identity) {
free(handler->identity);
handler->identity = NULL;
}
if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
if ((handler->opaque) && (handler->free_opaque)) {
handler->free_opaque(handler->opaque);
handler->opaque = NULL;
}
else if ((handler->opaque) && (handler->free_opaque == NULL))
radlog(L_ERR, "Possible memory leak ...");
handler->opaque = NULL;
handler->free_opaque = NULL;
if (handler->certs) pairfree(&handler->certs);
free(handler);
}
typedef struct check_handler_t {
rlm_eap_t *inst;
EAP_HANDLER *handler;
int trips;
} check_handler_t;
static void check_handler(void *data)
{
check_handler_t *check = data;
if (!check) return;
if (!check->inst || !check->handler) {
free(check);
return;
}
pthread_mutex_lock(&(check->inst->session_mutex));
if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
goto done;
}
if (check->handler->trips > check->trips) {
goto done;
}
if (check->handler->tls && !check->handler->finished) {
DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
DEBUG("WARNING: !! EAP session for state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!",
check->handler->state[0], check->handler->state[1],
check->handler->state[2], check->handler->state[3],
check->handler->state[4], check->handler->state[5],
check->handler->state[6], check->handler->state[7]);
DEBUG("WARNING: !! Please read http://wiki.freeradius.org/Certificate_Compatibility");
DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
}
done:
pthread_mutex_unlock(&(check->inst->session_mutex));
free(check);
}
void eaptype_free(EAP_TYPES *i)
{
if (i->type->detach) (i->type->detach)(i->type_data);
i->type_data = NULL;
if (i->handle) lt_dlclose(i->handle);
free(i);
}
void eaplist_free(rlm_eap_t *inst)
{
EAP_HANDLER *node, *next;
for (node = inst->session_head; node != NULL; node = next) {
next = node->next;
eap_handler_free(inst, node);
}
inst->session_head = inst->session_tail = NULL;
}
static uint32_t eap_rand(fr_randctx *ctx)
{
uint32_t num;
num = ctx->randrsl[ctx->randcnt++];
if (ctx->randcnt >= 256) {
ctx->randcnt = 0;
fr_isaac(ctx);
}
return num;
}
static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, EAP_HANDLER *handler)
{
rbnode_t *node;
node = rbtree_find(inst->session_tree, handler);
if (!node) return NULL;
handler = rbtree_node2data(inst->session_tree, node);
rbtree_delete(inst->session_tree, node);
if (handler->prev) {
handler->prev->next = handler->next;
} else {
inst->session_head = handler->next;
}
if (handler->next) {
handler->next->prev = handler->prev;
} else {
inst->session_tail = handler->prev;
}
handler->prev = handler->next = NULL;
return handler;
}
static void eaplist_expire(rlm_eap_t *inst, time_t timestamp)
{
int i;
EAP_HANDLER *handler;
for (i = 0; i < 3; i++) {
handler = inst->session_head;
if (!handler) break;
if ((timestamp - handler->timestamp) > inst->timer_limit) {
rbnode_t *node;
node = rbtree_find(inst->session_tree, handler);
rad_assert(node != NULL);
rbtree_delete(inst->session_tree, node);
inst->session_head = handler->next;
if (handler->next) {
handler->next->prev = NULL;
} else {
inst->session_head = NULL;
inst->session_tail = NULL;
}
eap_handler_free(inst, handler);
}
}
}
int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
{
int status = 0;
VALUE_PAIR *state;
REQUEST *request = handler->request;
rad_assert(handler != NULL);
rad_assert(request != NULL);
state = pairmake("State", "0x00", T_OP_EQ);
if (!state) return 0;
handler->timestamp = request->timestamp;
handler->status = 1;
handler->src_ipaddr = request->packet->src_ipaddr;
handler->eap_id = handler->eap_ds->request->id;
pthread_mutex_lock(&(inst->session_mutex));
if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
status = -1;
eaplist_expire(inst, handler->timestamp);
goto done;
}
if (handler->trips == 0) {
int i;
for (i = 0; i < 4; i++) {
uint32_t lvalue;
lvalue = eap_rand(&inst->rand_pool);
memcpy(handler->state + i * 4, &lvalue,
sizeof(lvalue));
}
}
memcpy(state->vp_octets, handler->state, sizeof(handler->state));
state->length = EAP_STATE_LEN;
state->vp_octets[4] = handler->trips ^ handler->state[0];
state->vp_octets[5] = handler->eap_id ^ handler->state[1];
state->vp_octets[6] = handler->eap_type ^ handler->state[2];
memcpy(handler->state, state->vp_octets, sizeof(handler->state));
status = rbtree_insert(inst->session_tree, handler);
if (fr_debug_flag) {
check_handler_t *check = rad_malloc(sizeof(*check));
check->inst = inst;
check->handler = handler;
check->trips = handler->trips;
request_data_add(request, inst, 0, check, check_handler);
}
if (status) {
EAP_HANDLER *prev;
prev = inst->session_tail;
if (prev) {
prev->next = handler;
handler->prev = prev;
handler->next = NULL;
inst->session_tail = handler;
} else {
inst->session_head = inst->session_tail = handler;
handler->next = handler->prev = NULL;
}
}
done:
if (status > 0) handler->request = NULL;
pthread_mutex_unlock(&(inst->session_mutex));
if (status <= 0) {
pairfree(&state);
if (status < 0) {
static time_t last_logged = 0;
if (last_logged < handler->timestamp) {
last_logged = handler->timestamp;
radlog(L_ERR, "rlm_eap: Too many open sessions. Try increasing \"max_sessions\" in the EAP module configuration");
}
} else {
radlog(L_ERR, "rlm_eap: Internal error: failed to store handler");
}
return 0;
}
pairadd(&(request->reply->vps), state);
return 1;
}
EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
eap_packet_t *eap_packet)
{
VALUE_PAIR *state;
EAP_HANDLER *handler, myHandler;
state = pairfind(request->packet->vps, PW_STATE);
if (!state ||
(state->length != EAP_STATE_LEN)) {
return NULL;
}
myHandler.src_ipaddr = request->packet->src_ipaddr;
myHandler.eap_id = eap_packet->id;
memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
pthread_mutex_lock(&(inst->session_mutex));
eaplist_expire(inst, request->timestamp);
handler = eaplist_delete(inst, &myHandler);
pthread_mutex_unlock(&(inst->session_mutex));
if (!handler) {
radlog(L_ERR, "rlm_eap: No EAP session matching the State variable.");
return NULL;
}
if (handler->trips >= 50) {
RDEBUG2("More than 50 authentication packets for this EAP session. Aborted.");
eap_handler_free(inst, handler);
return NULL;
}
handler->trips++;
RDEBUG2("Request found, released from the list");
eap_ds_free(&(handler->prev_eapds));
handler->prev_eapds = handler->eap_ds;
handler->eap_ds = NULL;
return handler;
}