summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_securid/mem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/rlm_securid/mem.c')
-rw-r--r--src/modules/rlm_securid/mem.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/src/modules/rlm_securid/mem.c b/src/modules/rlm_securid/mem.c
new file mode 100644
index 0000000..6e262e8
--- /dev/null
+++ b/src/modules/rlm_securid/mem.c
@@ -0,0 +1,313 @@
+/*
+ * mem.c Session handling, mostly taken from src/modules/rlm_eap/mem.c
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2012 The FreeRADIUS server project
+ * Copyright 2012 Alan DeKok <aland@networkradius.com>
+ */
+
+#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(UNUSED 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));
+}
+
+
+
+/*
+ * Add a session 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 securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request, SECURID_SESSION *session)
+{
+ int status = 0;
+ VALUE_PAIR *state;
+
+ /*
+ * The time at which this request was made was the time
+ * at which it was received by the RADIUS server.
+ */
+ session->timestamp = request->timestamp;
+
+ session->src_ipaddr = request->packet->src_ipaddr;
+
+ /*
+ * Playing with a data structure shared among threads
+ * means that we need a lock, to avoid conflict.
+ */
+ pthread_mutex_lock(&(inst->session_mutex));
+
+ /*
+ * If we have a DoS attack, discard new sessions.
+ */
+ 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) {
+ /* this is a NEW session (we are not inserting an updated session) */
+ inst->last_session_id++;
+ session->session_id = inst->last_session_id;
+ RDEBUG2("Creating a new session with id=%d\n",session->session_id);
+ }
+
+ memset(session->state, 0, sizeof(session->state));
+ 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);
+
+
+ /*
+ * Generate State, since we've been asked to add it to
+ * the list.
+ */
+ state = fr_pair_make(request->reply, &request->reply->vps, "State", NULL, T_OP_EQ);
+ if (!state) return -1;
+
+ fr_pair_value_memcpy(state, session->state, sizeof(session->state));
+
+ status = rbtree_insert(inst->session_tree, session);
+ if (status) {
+ /* tree insert SUCCESS */
+ /* insert the session to the linked list of sessions */
+ SECURID_SESSION *prev;
+
+ prev = inst->session_tail;
+ if (prev) {
+ /* insert to the tail of the list */
+ prev->next = session;
+ session->prev = prev;
+ session->next = NULL;
+ inst->session_tail = session;
+ } else {
+ /* 1st time */
+ inst->session_head = inst->session_tail = session;
+ session->next = session->prev = NULL;
+ }
+ }
+
+ /*
+ * Now that we've finished mucking with the list,
+ * unlock it.
+ */
+ done:
+ pthread_mutex_unlock(&(inst->session_mutex));
+
+ if (!status) {
+ fr_pair_list_free(&state);
+ ERROR("rlm_securid: Failed to store session");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Find existing session if any which matches the State variable in current AccessRequest
+ * Then, release the session from the list, and return it to
+ * the caller.
+ *
+ */
+SECURID_SESSION *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
+{
+ VALUE_PAIR *state;
+ SECURID_SESSION* session;
+ SECURID_SESSION mySession;
+
+ /* clean expired sessions if any */
+ pthread_mutex_lock(&(inst->session_mutex));
+ securid_sessionlist_clean_expired(inst, request, request->timestamp);
+ pthread_mutex_unlock(&(inst->session_mutex));
+
+ /*
+ * We key the sessions off of the 'state' attribute
+ */
+ state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
+ if (!state) {
+ return NULL;
+ }
+
+ if (state->vp_length != SECURID_STATE_LEN) {
+ ERROR("rlm_securid: Invalid State variable. length=%d", (int) state->vp_length);
+ return NULL;
+ }
+
+ memset(&mySession,0,sizeof(mySession));
+ mySession.src_ipaddr = request->packet->src_ipaddr;
+ memcpy(mySession.state, state->vp_strvalue, sizeof(mySession.state));
+
+ /*
+ * Playing with a data structure shared among threads
+ * means that we need a lock, to avoid conflict.
+ */
+ pthread_mutex_lock(&(inst->session_mutex));
+ session = securid_sessionlist_delete(inst, &mySession);
+ pthread_mutex_unlock(&(inst->session_mutex));
+
+ /*
+ * Might not have been there.
+ */
+ if (!session) {
+ ERROR("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;
+}
+
+
+/************ private functions *************/
+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);
+
+ /*
+ * Delete old session from the tree.
+ */
+ rbtree_delete(inst->session_tree, node);
+
+ /*
+ * And unsplice it from the linked list.
+ */
+ 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);
+
+ /*
+ * Delete old sessions from the list
+ *
+ */
+ 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);
+
+ /*
+ * session == inst->session_head
+ */
+ 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 {
+ /* no need to check all sessions since they are sorted by age */
+ break;
+ }
+ }
+}