All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
mem.c
Go to the documentation of this file.
1 /*
2  * mem.c Session handling, mostly taken from src/modules/rlm_eap/mem.c
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * Copyright 2012 The FreeRADIUS server project
19  * Copyright 2012 Alan DeKok <aland@networkradius.com>
20  */
21 
22 #include <stdio.h>
23 #include "rlm_securid.h"
24 
25 static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *request, time_t timestamp);
26 
28  SECURID_SESSION *session);
29 
31 {
32  SECURID_SESSION *session;
33 
34  session = rad_malloc(sizeof(SECURID_SESSION));
35  memset(session, 0, sizeof(SECURID_SESSION));
36 
37  session->sdiHandle = SDI_HANDLE_NONE;
38 
39  return session;
40 }
41 
43  SECURID_SESSION *session)
44 {
45  if (!session)
46  return;
47 
48  RDEBUG2("Freeing session id=%d identity='%s' state='%s'",
49  session->session_id,SAFE_STR(session->identity),session->state);
50 
51  if (session->identity) {
52  free(session->identity);
53  session->identity = NULL;
54  }
55  if (session->pin) {
56  free(session->pin);
57  session->pin = NULL;
58  }
59 
60  if (session->sdiHandle != SDI_HANDLE_NONE) {
61  SD_Close(session->sdiHandle);
62  session->sdiHandle = SDI_HANDLE_NONE;
63  }
64 
65  free(session);
66 }
67 
68 
70 {
71  SECURID_SESSION *node, *next;
72 
74 
75  for (node = inst->session_head; node != NULL; node = next) {
76  next = node->next;
77  securid_session_free(inst,request,node);
78  }
79 
80  inst->session_head = inst->session_tail = NULL;
81 
83 }
84 
85 
86 
87 /*
88  * Add a session to the set of active sessions.
89  *
90  * Since we're adding it to the list, we guess that this means
91  * the packet needs a State attribute. So add one.
92  */
94 {
95  int status = 0;
97 
98  /*
99  * The time at which this request was made was the time
100  * at which it was received by the RADIUS server.
101  */
102  session->timestamp = request->timestamp;
103 
104  session->src_ipaddr = request->packet->src_ipaddr;
105 
106  /*
107  * Playing with a data structure shared among threads
108  * means that we need a lock, to avoid conflict.
109  */
111 
112  /*
113  * If we have a DoS attack, discard new sessions.
114  */
115  if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
116  securid_sessionlist_clean_expired(inst, request, session->timestamp);
117  goto done;
118  }
119 
120  if (session->session_id == 0) {
121  /* this is a NEW session (we are not inserting an updated session) */
122  inst->last_session_id++;
123  session->session_id = inst->last_session_id;
124  RDEBUG2("Creating a new session with id=%d\n",session->session_id);
125  }
126  snprintf(session->state,sizeof(session->state)-1,"FRR-CH %d|%d",session->session_id,session->trips+1);
127  RDEBUG2("Inserting session id=%d identity='%s' state='%s' to the session list",
128  session->session_id,SAFE_STR(session->identity),session->state);
129 
130 
131  /*
132  * Generate State, since we've been asked to add it to
133  * the list.
134  */
135  state = pair_make_reply("State", session->state, T_OP_EQ);
136  if (!state) return -1;
137  state->vp_length = SECURID_STATE_LEN;
138 
139  status = rbtree_insert(inst->session_tree, session);
140  if (status) {
141  /* tree insert SUCCESS */
142  /* insert the session to the linked list of sessions */
143  SECURID_SESSION *prev;
144 
145  prev = inst->session_tail;
146  if (prev) {
147  /* insert to the tail of the list */
148  prev->next = session;
149  session->prev = prev;
150  session->next = NULL;
151  inst->session_tail = session;
152  } else {
153  /* 1st time */
154  inst->session_head = inst->session_tail = session;
155  session->next = session->prev = NULL;
156  }
157  }
158 
159  /*
160  * Now that we've finished mucking with the list,
161  * unlock it.
162  */
163  done:
165 
166  if (!status) {
167  fr_pair_list_free(&state);
168  ERROR("rlm_securid: Failed to store session");
169  return -1;
170  }
171 
172  return 0;
173 }
174 
175 /*
176  * Find existing session if any which matches the State variable in current AccessRequest
177  * Then, release the session from the list, and return it to
178  * the caller.
179  *
180  */
182 {
183  VALUE_PAIR *state;
184  SECURID_SESSION* session;
185  SECURID_SESSION mySession;
186 
187  /* clean expired sessions if any */
189  securid_sessionlist_clean_expired(inst, request, request->timestamp);
191 
192  /*
193  * We key the sessions off of the 'state' attribute
194  */
195  state = fr_pair_find_by_num(request->packet->vps, 0, PW_STATE, TAG_ANY);
196  if (!state) {
197  return NULL;
198  }
199 
200  if (state->vp_length != SECURID_STATE_LEN) {
201  ERROR("rlm_securid: Invalid State variable. length=%d", (int) state->vp_length);
202  return NULL;
203  }
204 
205  memset(&mySession,0,sizeof(mySession));
206  mySession.src_ipaddr = request->packet->src_ipaddr;
207  memcpy(mySession.state, state->vp_strvalue, sizeof(mySession.state));
208 
209  /*
210  * Playing with a data structure shared among threads
211  * means that we need a lock, to avoid conflict.
212  */
214  session = securid_sessionlist_delete(inst, &mySession);
216 
217  /*
218  * Might not have been there.
219  */
220  if (!session) {
221  ERROR("rlm_securid: No SECURID session matching the State variable");
222  return NULL;
223  }
224 
225  RDEBUG2("Session found identity='%s' state='%s', released from the list",
226  SAFE_STR(session->identity),session->state);
227  if (session->trips >= inst->max_trips_per_session) {
228  RDEBUG2("More than %d authentication packets for this SECURID session. Aborted.",inst->max_trips_per_session);
229  securid_session_free(inst,request,session);
230  return NULL;
231  }
232  session->trips++;
233 
234  return session;
235 }
236 
237 
238 /************ private functions *************/
240 {
241  rbnode_t *node;
242 
243  node = rbtree_find(inst->session_tree, session);
244  if (!node) return NULL;
245 
246  session = rbtree_node2data(inst->session_tree, node);
247 
248  /*
249  * Delete old session from the tree.
250  */
251  rbtree_delete(inst->session_tree, node);
252 
253  /*
254  * And unsplice it from the linked list.
255  */
256  if (session->prev) {
257  session->prev->next = session->next;
258  } else {
259  inst->session_head = session->next;
260  }
261  if (session->next) {
262  session->next->prev = session->prev;
263  } else {
264  inst->session_tail = session->prev;
265  }
266  session->prev = session->next = NULL;
267 
268  return session;
269 }
270 
271 
272 static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *request, time_t timestamp)
273 {
274  int num_sessions;
275  SECURID_SESSION *session;
276 
277  num_sessions = rbtree_num_elements(inst->session_tree);
278  RDEBUG2("There are %d sessions in the tree\n",num_sessions);
279 
280  /*
281  * Delete old sessions from the list
282  *
283  */
284  while((session = inst->session_head)) {
285  if ((timestamp - session->timestamp) > inst->timer_limit) {
286  rbnode_t *node;
287  node = rbtree_find(inst->session_tree, session);
288  rad_assert(node != NULL);
289  rbtree_delete(inst->session_tree, node);
290 
291  /*
292  * session == inst->session_head
293  */
294  inst->session_head = session->next;
295  if (session->next) {
296  session->next->prev = NULL;
297  } else {
298  inst->session_head = NULL;
299  inst->session_tail = NULL;
300  }
301 
302  RDEBUG2("Cleaning expired session: identity='%s' state='%s'\n",
303  SAFE_STR(session->identity),session->state);
304  securid_session_free(inst,request,session);
305  } else {
306  /* no need to check all sessions since they are sorted by age */
307  break;
308  }
309  }
310 }
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
int securid_sessionlist_add(rlm_securid_t *inst, REQUEST *request, SECURID_SESSION *session)
Definition: mem.c:93
rbnode_t * rbtree_find(rbtree_t *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rbtree.c:511
void * rad_malloc(size_t size)
Definition: util.c:411
SECURID_SESSION * securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
Definition: mem.c:181
static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *request, time_t timestamp)
Definition: mem.c:272
fr_ipaddr_t src_ipaddr
Src IP address of packet.
Definition: libradius.h:149
SECURID_SESSION * securid_session_alloc(void)
Definition: mem.c:30
rbtree_t * session_tree
Definition: rlm_securid.h:68
fr_ipaddr_t src_ipaddr
Definition: rlm_securid.h:46
#define UNUSED
Definition: libradius.h:134
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
#define inst
Definition: token.h:46
#define rad_assert(expr)
Definition: rad_assert.h:38
SDI_HANDLE sdiHandle
Definition: rlm_securid.h:41
#define pthread_mutex_unlock(_x)
Definition: rlm_eap.h:78
void securid_sessionlist_free(rlm_securid_t *inst, REQUEST *request)
Definition: mem.c:69
struct _securid_session_t * next
Definition: rlm_securid.h:40
static bool done
Definition: radclient.c:53
void rbtree_delete(rbtree_t *tree, rbnode_t *z)
Definition: rbtree.c:488
unsigned int session_id
Definition: rlm_securid.h:48
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
SECURID_SESSION * session_tail
Definition: rlm_securid.h:69
#define SAFE_STR(s)
Definition: rlm_securid.h:10
unsigned int last_session_id
Definition: rlm_securid.h:71
#define RDEBUG2(fmt,...)
Definition: log.h:244
unsigned int state
Definition: proto_bfd.c:200
uint32_t max_sessions
Definition: rlm_securid.h:77
#define TAG_ANY
Definition: pair.h:191
uint32_t max_trips_per_session
Definition: rlm_securid.h:78
struct timeval timestamp
When we started processing the request.
Definition: radiusd.h:214
static SECURID_SESSION * securid_sessionlist_delete(rlm_securid_t *inst, SECURID_SESSION *session)
Definition: mem.c:239
struct _securid_session_t * prev
Definition: rlm_securid.h:40
#define pair_make_reply(_a, _b, _c)
Definition: radiusd.h:546
#define SECURID_STATE_LEN
Definition: rlm_securid.h:38
bool rbtree_insert(rbtree_t *tree, void *data)
Definition: rbtree.c:329
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
SECURID_SESSION * session_head
Definition: rlm_securid.h:69
uint32_t timer_limit
Definition: rlm_securid.h:76
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
#define pthread_mutex_lock(_x)
Definition: rlm_eap.h:77
char state[SECURID_STATE_LEN]
Definition: rlm_securid.h:44
void securid_session_free(UNUSED rlm_securid_t *inst, REQUEST *request, SECURID_SESSION *session)
Definition: mem.c:42
void * rbtree_node2data(rbtree_t *tree, rbnode_t *node)
#define ERROR(fmt,...)
Definition: log.h:145
pthread_mutex_t session_mutex
Definition: rlm_securid.h:67
uint32_t rbtree_num_elements(rbtree_t *tree)
Definition: rbtree.c:727