/*
* mem.c Memory allocation, deallocation stuff.
*
* Version: $Id: mem.c,v 1.14.4.3 2007/02/09 15:42:03 aland Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright 2000,2001 The FreeRADIUS server project
* Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
*/
#include <stdio.h>
#include "rlm_eap.h"
static const char rcsid[] = "$Id: mem.c,v 1.14.4.3 2007/02/09 15:42:03 aland Exp $";
/*
* Allocate a new EAP_PACKET
*/
EAP_PACKET *eap_packet_alloc(void)
{
EAP_PACKET *rp;
rp = rad_malloc(sizeof(EAP_PACKET));
memset(rp, 0, sizeof(EAP_PACKET));
return rp;
}
/*
* Free EAP_PACKET
*/
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) {
/*
* This is just a pointer in the packet
* so we do not free it but we NULL it
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;
}
/*
* Allocate a new EAP_PACKET
*/
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;
}
/*
* Allocate a new EAP_HANDLER
*/
EAP_HANDLER *eap_handler_alloc(void)
{
EAP_HANDLER *handler;
handler = rad_malloc(sizeof(EAP_HANDLER));
memset(handler, 0, sizeof(EAP_HANDLER));
return handler;
}
void eap_handler_free(EAP_HANDLER *handler)
{
if (!handler)
return;
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;
free(handler);
}
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(node);
}
inst->session_head = inst->session_tail = NULL;
}
/*
* Add a handler to the set of active sessions.
*
* Since we're adding it to the list, we guess that this means
* the packet needs a State attribute. So add one.
*/
int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
{
int status;
VALUE_PAIR *state;
rad_assert(handler != NULL);
rad_assert(handler->request != NULL);
/*
* Generate State, since we've been asked to add it to
* the list.
*/
state = generate_state(handler->request->timestamp);
pairadd(&(handler->request->reply->vps), state);
/*
* Create a unique 'key' for the handler, based
* on State, Client-IP-Address, and EAP ID.
*/
rad_assert(state->length == EAP_STATE_LEN);
/*
* The time at which this request was made was the time
* at which it was received by the RADIUS server.
*/
handler->timestamp = handler->request->timestamp;
handler->status = 1;
memcpy(handler->state, state->strvalue, sizeof(handler->state));
handler->src_ipaddr = handler->request->packet->src_ipaddr;
handler->eap_id = handler->eap_ds->request->id;
/*
* We don't need this any more.
*/
handler->request = NULL;
/*
* Playing with a data structure shared among threads
* means that we need a lock, to avoid conflict.
*/
pthread_mutex_lock(&(inst->session_mutex));
/*
* Big-time failure.
*/
status = rbtree_insert(inst->session_tree, 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;
}
}
/*
* Now that we've finished mucking with the list,
* unlock it.
*/
pthread_mutex_unlock(&(inst->session_mutex));
if (!status) {
radlog(L_ERR, "rlm_eap: Failed to remember handler!");
eap_handler_free(handler);
return 0;
}
return 1;
}
/*
* Find a a previous EAP-Request sent by us, which matches
* the current EAP-Response.
*
* Then, release the handle from the list, and return it to
* the caller.
*
* Also since we fill the eap_ds with the present EAP-Response we
* got to free the prev_eapds & move the eap_ds to prev_eapds
*/
EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
eap_packet_t *eap_packet)
{
int i;
VALUE_PAIR *state;
rbnode_t *node;
EAP_HANDLER *handler, myHandler;
/*
* We key the sessions off of the 'state' attribute, so it
* must exist.
*/
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->strvalue, sizeof(myHandler.state));
/*
* Playing with a data structure shared among threads
* means that we need a lock, to avoid conflict.
*/
pthread_mutex_lock(&(inst->session_mutex));
/*
* Check the first few handlers in the list, and delete
* them if they're too old. We don't need to check them
* all, as incoming requests will quickly cause older
* handlers to be deleted.
*
*/
for (i = 0; i < 2; i++) {
handler = inst->session_head;
if (handler &&
((request->timestamp - handler->timestamp) > inst->timer_limit)) {
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;
eap_handler_free(handler);
}
}
handler = NULL;
node = rbtree_find(inst->session_tree, &myHandler);
if (node) {
handler = rbtree_node2data(inst->session_tree, node);
/*
* Check against replays. The client can re-play
* a State attribute verbatim, so we wish to
* ensure that the attribute falls within the
* valid time window, which is the second at
* which it was sent out.
*
* Hmm... I'm not sure that this step is
* necessary, or even that it does anything.
*/
if (verify_state(state, handler->timestamp) != 0) {
handler = NULL;
} else {
/*
* It's OK, delete it from the tree.
*/
rbtree_delete(inst->session_tree, node);
/*
* And unsplice it from the linked list.
*/
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;
}
}
pthread_mutex_unlock(&(inst->session_mutex));
/*
* Not found.
*/
if (!node) {
DEBUG2(" rlm_eap: Request not found in the list");
return NULL;
}
/*
* Found, but state verification failed.
*/
if (!handler) {
radlog(L_ERR, "rlm_eap: State verification failed.");
return NULL;
}
DEBUG2(" rlm_eap: Request found, released from the list");
/*
* Remember what the previous request was.
*/
eap_ds_free(&(handler->prev_eapds));
handler->prev_eapds = handler->eap_ds;
handler->eap_ds = NULL;
return handler;
}
syntax highlighted by Code2HTML, v. 0.9.1