The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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 #define LOG_PREFIX "securid"
23 
24 #include <stdio.h>
25 #include "rlm_securid.h"
26 
27 static void securid_sessionlist_clean_expired(rlm_securid_t *inst, request_t *request, time_t timestamp);
28 
30  SECURID_SESSION *session);
31 
33 {
34  SECURID_SESSION *session;
35 
36  session = talloc_zero(NULL, SECURID_SESSION);
37  session->sdiHandle = SDI_HANDLE_NONE;
38 
39  return session;
40 }
41 
43  SECURID_SESSION *session)
44 {
45  if (!session) return;
46 
47  RDEBUG2("Freeing session id=%d identity='%s' state='%s'", session->session_id,
48  SAFE_STR(session->identity), session->state);
49 
50  if (session->sdiHandle != SDI_HANDLE_NONE) {
51  SD_Close(session->sdiHandle);
52  session->sdiHandle = SDI_HANDLE_NONE;
53  }
54 
55  talloc_free(session);
56 }
57 
58 
60 {
61  SECURID_SESSION *node, *next;
62 
63  pthread_mutex_lock(&(inst->session_mutex));
64 
65  for (node = inst->session_head; node != NULL; node = next) {
66  next = node->next;
67  securid_session_free(inst,request,node);
68  }
69 
70  inst->session_head = inst->session_tail = NULL;
71 
72  pthread_mutex_unlock(&(inst->session_mutex));
73 }
74 
75 
76 
77 /*
78  * Add a session to the set of active sessions.
79  *
80  * Since we're adding it to the list, we guess that this means
81  * the packet needs a State attribute. So add one.
82  */
84 {
85  int status = 0;
86  fr_pair_t *state;
87 
88  /*
89  * The time at which this request was made was the time
90  * at which it was received by the RADIUS server.
91  */
92  session->timestamp = fr_time_to_sec(request->packet->timestamp);
93 
94  session->src_ipaddr = request->packet->src_ipaddr;
95 
96  /*
97  * Playing with a data structure shared among threads
98  * means that we need a lock, to avoid conflict.
99  */
100  pthread_mutex_lock(&(inst->session_mutex));
101 
102  /*
103  * If we have a DoS attack, discard new sessions.
104  */
105  if (fr_rb_num_elements(inst->session_tree) >= inst->max_sessions) {
107  goto done;
108  }
109 
110  if (session->session_id == 0) {
111  /* this is a NEW session (we are not inserting an updated session) */
112  inst->last_session_id++;
113  session->session_id = inst->last_session_id;
114  RDEBUG2("Creating a new session with id=%d\n",session->session_id);
115  }
116 
117  memset(session->state, 0, sizeof(session->state));
118  snprintf(session->state,sizeof(session->state)-1,"FRR-CH %d|%d",session->session_id,session->trips+1);
119  RDEBUG2("Inserting session id=%d identity='%s' state='%s' to the session list",
120  session->session_id,SAFE_STR(session->identity),session->state);
121 
122 
123  /*
124  * Generate State, since we've been asked to add it to
125  * the list.
126  */
127  MEM(pair_update_reply(&state, attr_state) >= 0);
128  fr_pair_value_memdup(state, session->state, sizeof(session->state), true);
129 
130  status = fr_rb_insert(inst->session_tree, session);
131  if (status) {
132  /* tree insert SUCCESS */
133  /* insert the session to the linked list of sessions */
134  SECURID_SESSION *prev;
135 
136  prev = inst->session_tail;
137  if (prev) {
138  /* insert to the tail of the list */
139  prev->next = session;
140  session->prev = prev;
141  session->next = NULL;
142  inst->session_tail = session;
143  } else {
144  /* 1st time */
145  inst->session_head = inst->session_tail = session;
146  session->next = session->prev = NULL;
147  }
148  }
149 
150  /*
151  * Now that we've finished mucking with the list,
152  * unlock it.
153  */
154  done:
155  pthread_mutex_unlock(&(inst->session_mutex));
156 
157  if (!status) {
158  fr_pair_list_free(&state);
159  ERROR("Failed to store session");
160  return -1;
161  }
162 
163  return 0;
164 }
165 
166 /*
167  * Find existing session if any which matches the State variable in current AccessRequest
168  * Then, release the session from the list, and return it to
169  * the caller.
170  *
171  */
173 {
174  fr_pair_t *state;
175  SECURID_SESSION* session;
176  SECURID_SESSION mySession;
177 
178  /* clean expired sessions if any */
179  pthread_mutex_lock(&(inst->session_mutex));
180  securid_sessionlist_clean_expired(inst, request, fr_time_to_sec(request->packet->timestamp));
181  pthread_mutex_unlock(&(inst->session_mutex));
182 
183  /*
184  * We key the sessions off of the 'state' attribute
185  */
186  state = fr_pair_find_by_da(&request->request_pairs, NULL, attr_state);
187  if (!state) {
188  return NULL;
189  }
190 
191  if (state->vp_length != SECURID_STATE_LEN) {
192  ERROR("Invalid State variable. length=%d", (int) state->vp_length);
193  return NULL;
194  }
195 
196  memset(&mySession,0,sizeof(mySession));
197  mySession.src_ipaddr = request->packet->src_ipaddr;
198  memcpy(mySession.state, state->vp_strvalue, sizeof(mySession.state));
199 
200  /*
201  * Playing with a data structure shared among threads
202  * means that we need a lock, to avoid conflict.
203  */
204  pthread_mutex_lock(&(inst->session_mutex));
205  session = securid_sessionlist_delete(inst, &mySession);
206  pthread_mutex_unlock(&(inst->session_mutex));
207 
208  /*
209  * Might not have been there.
210  */
211  if (!session) {
212  ERROR("No SECURID session matching the State variable");
213  return NULL;
214  }
215 
216  RDEBUG2("Session found identity='%s' state='%s', released from the list",
217  SAFE_STR(session->identity),session->state);
218  if (session->trips >= inst->max_trips_per_session) {
219  RDEBUG2("More than %d authentication packets for this SECURID session. Aborted.",inst->max_trips_per_session);
220  securid_session_free(inst,request,session);
221  return NULL;
222  }
223  session->trips++;
224 
225  return session;
226 }
227 
228 
229 /************ private functions *************/
231 {
232  fr_assert(fr_rb_find(inst->session_tree, session) == session);
233 
234  /*
235  * Delete old session from the tree.
236  */
237  fr_rb_delete(inst->session_tree, node);
238 
239  /*
240  * And unsplice it from the linked list.
241  */
242  if (session->prev) {
243  session->prev->next = session->next;
244  } else {
245  inst->session_head = session->next;
246  }
247  if (session->next) {
248  session->next->prev = session->prev;
249  } else {
250  inst->session_tail = session->prev;
251  }
252  session->prev = session->next = NULL;
253 
254  return session;
255 }
256 
257 
258 static void securid_sessionlist_clean_expired(rlm_securid_t *inst, request_t *request, time_t timestamp)
259 {
260  uint64_t num_sessions;
261  SECURID_SESSION *session;
262 
263  num_sessions = fr_rb_num_elements(inst->session_tree);
264  RDEBUG2("There are %d sessions in the tree\n",num_sessions);
265 
266  /*
267  * Delete old sessions from the list
268  *
269  */
270  while((session = inst->session_head)) {
271  if ((timestamp - session->timestamp) > inst->timer_limit) {
272  fr_rb_delete(inst->session_tree, session);
273 
274  /*
275  * session == inst->session_head
276  */
277  inst->session_head = session->next;
278  if (session->next) {
279  session->next->prev = NULL;
280  } else {
281  inst->session_head = NULL;
282  inst->session_tail = NULL;
283  }
284 
285  RDEBUG2("Cleaning expired session: identity='%s' state='%s'\n",
286  SAFE_STR(session->identity),session->state);
287  securid_session_free(inst,request,session);
288  } else {
289  /* no need to check all sessions since they are sorted by age */
290  break;
291  }
292  }
293 }
#define UNUSED
Definition: build.h:313
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
HIDDEN fr_dict_attr_t const * attr_state
Definition: base.c:96
talloc_free(reap)
static void securid_sessionlist_clean_expired(rlm_securid_t *inst, request_t *request, time_t timestamp)
Definition: mem.c:258
int securid_sessionlist_add(rlm_securid_t *inst, request_t *request, SECURID_SESSION *session)
Definition: mem.c:83
static SECURID_SESSION * securid_sessionlist_delete(rlm_securid_t *inst, SECURID_SESSION *session)
Definition: mem.c:230
SECURID_SESSION * securid_session_alloc(void)
Definition: mem.c:32
void securid_session_free(UNUSED rlm_securid_t *inst, request_t *request, SECURID_SESSION *session)
Definition: mem.c:42
SECURID_SESSION * securid_sessionlist_find(rlm_securid_t *inst, request_t *request)
Definition: mem.c:172
void securid_sessionlist_free(rlm_securid_t *inst, request_t *request)
Definition: mem.c:59
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition: pair.c:2978
static bool done
Definition: radclient.c:80
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
uint32_t fr_rb_num_elements(fr_rb_tree_t *tree)
Return how many nodes there are in a tree.
Definition: rb.c:775
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
bool fr_rb_delete(fr_rb_tree_t *tree, void const *data)
Remove node and free data (if a free function was specified)
Definition: rb.c:736
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
unsigned int session_id
Definition: rlm_securid.h:47
uint32_t trips
Definition: rlm_securid.h:48
fr_ipaddr_t src_ipaddr
Definition: rlm_securid.h:45
SDI_HANDLE sdiHandle
Definition: rlm_securid.h:40
struct _securid_session_t * next
Definition: rlm_securid.h:38
struct _securid_session_t * prev
Definition: rlm_securid.h:38
#define SECURID_STATE_LEN
Definition: rlm_securid.h:36
time_t timestamp
Definition: rlm_securid.h:46
char state[SECURID_STATE_LEN]
Definition: rlm_securid.h:43
#define SAFE_STR(s)
Definition: rlm_securid.h:8
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition: pair.h:129
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition: time.h:729
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
Definition: pair_inline.c:113