#include <freeradius-devel/ident.h>
#include <stdio.h>
#include "rlm_securid.h"
static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *request, time_t timestamp);
static SECURID_SESSION* securid_sessionlist_delete(rlm_securid_t *inst,
SECURID_SESSION *session);
SECURID_SESSION* securid_session_alloc(void)
{
SECURID_SESSION *session;
session = rad_malloc(sizeof(SECURID_SESSION));
memset(session, 0, sizeof(SECURID_SESSION));
session->sdiHandle = SDI_HANDLE_NONE;
return session;
}
void securid_session_free(rlm_securid_t *inst,REQUEST *request,
SECURID_SESSION *session)
{
if (!session)
return;
RDEBUG2("Freeing session id=%d identity='%s' state='%s'",
session->session_id,SAFE_STR(session->identity),session->state);
if (session->identity) {
free(session->identity);
session->identity = NULL;
}
if (session->pin) {
free(session->pin);
session->pin = NULL;
}
if (session->sdiHandle != SDI_HANDLE_NONE) {
SD_Close(session->sdiHandle);
session->sdiHandle = SDI_HANDLE_NONE;
}
free(session);
}
void securid_sessionlist_free(rlm_securid_t *inst,REQUEST *request)
{
SECURID_SESSION *node, *next;
pthread_mutex_lock(&(inst->session_mutex));
for (node = inst->session_head; node != NULL; node = next) {
next = node->next;
securid_session_free(inst,request,node);
}
inst->session_head = inst->session_tail = NULL;
pthread_mutex_unlock(&(inst->session_mutex));
}
int securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request,
SECURID_SESSION *session)
{
int status = 0;
VALUE_PAIR *state;
rad_assert(session != NULL);
rad_assert(request != NULL);
session->timestamp = request->timestamp;
session->src_ipaddr = request->packet->src_ipaddr;
pthread_mutex_lock(&(inst->session_mutex));
if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
securid_sessionlist_clean_expired(inst, request, session->timestamp);
goto done;
}
if (session->session_id == 0) {
inst->last_session_id++;
session->session_id = inst->last_session_id;
RDEBUG2("Creating a new session with id=%d\n",session->session_id);
}
snprintf(session->state,sizeof(session->state)-1,"FRR-CH %d|%d",session->session_id,session->trips+1);
RDEBUG2("Inserting session id=%d identity='%s' state='%s' to the session list",
session->session_id,SAFE_STR(session->identity),session->state);
state = pairmake("State", session->state, T_OP_EQ);
if (!state) return -1;
state->length = SECURID_STATE_LEN;
status = rbtree_insert(inst->session_tree, session);
if (status) {
SECURID_SESSION *prev;
prev = inst->session_tail;
if (prev) {
prev->next = session;
session->prev = prev;
session->next = NULL;
inst->session_tail = session;
} else {
inst->session_head = inst->session_tail = session;
session->next = session->prev = NULL;
}
}
done:
pthread_mutex_unlock(&(inst->session_mutex));
if (!status) {
pairfree(&state);
radlog(L_ERR, "rlm_securid: Failed to store session");
return -1;
}
pairadd(&(request->reply->vps), state);
return 0;
}
SECURID_SESSION *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
{
VALUE_PAIR *state;
SECURID_SESSION* session;
SECURID_SESSION mySession;
pthread_mutex_lock(&(inst->session_mutex));
securid_sessionlist_clean_expired(inst, request, request->timestamp);
pthread_mutex_unlock(&(inst->session_mutex));
state = pairfind(request->packet->vps, PW_STATE);
if (!state) {
return NULL;
}
if (state->length != SECURID_STATE_LEN) {
radlog(L_ERR,"rlm_securid: Invalid State variable. length=%d",state->length);
return NULL;
}
memset(&mySession,0,sizeof(mySession));
mySession.src_ipaddr = request->packet->src_ipaddr;
memcpy(mySession.state, state->vp_strvalue, sizeof(mySession.state));
pthread_mutex_lock(&(inst->session_mutex));
session = securid_sessionlist_delete(inst, &mySession);
pthread_mutex_unlock(&(inst->session_mutex));
if (!session) {
radlog(L_ERR,"rlm_securid: No SECURID session matching the State variable.");
return NULL;
}
RDEBUG2("Session found identity='%s' state='%s', released from the list",
SAFE_STR(session->identity),session->state);
if (session->trips >= inst->max_trips_per_session) {
RDEBUG2("More than %d authentication packets for this SECURID session. Aborted.",inst->max_trips_per_session);
securid_session_free(inst,request,session);
return NULL;
}
session->trips++;
return session;
}
static SECURID_SESSION *securid_sessionlist_delete(rlm_securid_t *inst, SECURID_SESSION *session)
{
rbnode_t *node;
node = rbtree_find(inst->session_tree, session);
if (!node) return NULL;
session = rbtree_node2data(inst->session_tree, node);
rbtree_delete(inst->session_tree, node);
if (session->prev) {
session->prev->next = session->next;
} else {
inst->session_head = session->next;
}
if (session->next) {
session->next->prev = session->prev;
} else {
inst->session_tail = session->prev;
}
session->prev = session->next = NULL;
return session;
}
static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *request, time_t timestamp)
{
int num_sessions;
SECURID_SESSION *session;
num_sessions = rbtree_num_elements(inst->session_tree);
RDEBUG2("There are %d sessions in the tree\n",num_sessions);
while((session = inst->session_head)) {
if ((timestamp - session->timestamp) > inst->timer_limit) {
rbnode_t *node;
node = rbtree_find(inst->session_tree, session);
rad_assert(node != NULL);
rbtree_delete(inst->session_tree, node);
inst->session_head = session->next;
if (session->next) {
session->next->prev = NULL;
} else {
inst->session_head = NULL;
inst->session_tail = NULL;
}
RDEBUG2("Cleaning expired session: identity='%s' state='%s'\n",
SAFE_STR(session->identity),session->state);
securid_session_free(inst,request,session);
} else {
break;
}
}
}