2 * mem.c Memory allocation, deallocation stuff.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2001,2006 The FreeRADIUS server project
21 * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
30 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
31 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
33 #define PTHREAD_MUTEX_LOCK(_x)
34 #define PTHREAD_MUTEX_UNLOCK(_x)
38 * Allocate a new eap_packet_t
40 EAP_DS *eap_ds_alloc(eap_handler_t *handler)
44 eap_ds = talloc_zero(handler, EAP_DS);
45 eap_ds->response = talloc_zero(eap_ds, eap_packet_t);
46 if (!eap_ds->response) {
50 eap_ds->request = talloc_zero(eap_ds, eap_packet_t);
51 if (!eap_ds->response) {
59 void eap_ds_free(EAP_DS **eap_ds_p)
63 if (!eap_ds_p) return;
68 if (eap_ds->response) talloc_free(eap_ds->response);
69 if (eap_ds->request) talloc_free(eap_ds->request);
75 static int _eap_handler_free(eap_handler_t *handler)
77 if (handler->identity) {
78 talloc_free(handler->identity);
79 handler->identity = NULL;
82 if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
83 if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
85 if ((handler->opaque) && (handler->free_opaque)) {
86 handler->free_opaque(handler->opaque);
87 handler->opaque = NULL;
90 handler->opaque = NULL;
91 handler->free_opaque = NULL;
93 if (handler->certs) fr_pair_list_free(&handler->certs);
96 * Give helpful debug messages if:
98 * we're debugging TLS sessions, which don't finish,
99 * and which aren't deleted early due to a likely RADIUS
100 * retransmit which nukes our ID, and therefore our stare.
102 if (fr_debug_lvl && handler->tls && !handler->finished &&
103 (time(NULL) > (handler->timestamp + 3))) {
104 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
105 WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish! !!",
106 handler->state[0], handler->state[1],
107 handler->state[2], handler->state[3],
108 handler->state[4], handler->state[5],
109 handler->state[6], handler->state[7]);
111 WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility !!");
112 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
115 talloc_free(handler);
121 * Allocate a new eap_handler_t
123 eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
125 eap_handler_t *handler;
127 handler = talloc_zero(NULL, eap_handler_t);
129 ERROR("Failed allocating handler");
132 handler->inst_holder = inst;
134 /* Doesn't need to be inside the critical region */
135 talloc_set_destructor(handler, _eap_handler_free);
141 void eaplist_free(rlm_eap_t *inst)
143 eap_handler_t *node, *next;
145 for (node = inst->session_head; node != NULL; node = next) {
150 inst->session_head = inst->session_tail = NULL;
154 * Return a 32-bit random number.
156 static uint32_t eap_rand(fr_randctx *ctx)
160 num = ctx->randrsl[ctx->randcnt++];
161 if (ctx->randcnt >= 256) {
170 static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
171 eap_handler_t *handler)
175 node = rbtree_find(inst->session_tree, handler);
176 if (!node) return NULL;
178 handler = rbtree_node2data(inst->session_tree, node);
180 RDEBUG("Finished EAP session with state "
181 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
182 handler->state[0], handler->state[1],
183 handler->state[2], handler->state[3],
184 handler->state[4], handler->state[5],
185 handler->state[6], handler->state[7]);
187 * Delete old handler from the tree.
189 rbtree_delete(inst->session_tree, node);
192 * And unsplice it from the linked list.
195 handler->prev->next = handler->next;
197 inst->session_head = handler->next;
200 handler->next->prev = handler->prev;
202 inst->session_tail = handler->prev;
204 handler->prev = handler->next = NULL;
210 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
213 eap_handler_t *handler;
216 * Check the first few handlers in the list, and delete
217 * them if they're too old. We don't need to check them
218 * all, as incoming requests will quickly cause older
219 * handlers to be deleted.
222 for (i = 0; i < 3; i++) {
223 handler = inst->session_head;
226 RDEBUG("Expiring EAP session with state "
227 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
228 handler->state[0], handler->state[1],
229 handler->state[2], handler->state[3],
230 handler->state[4], handler->state[5],
231 handler->state[6], handler->state[7]);
234 * Expire entries from the start of the list.
235 * They should be the oldest ones.
237 if ((timestamp - handler->timestamp) > (int)inst->timer_limit) {
239 node = rbtree_find(inst->session_tree, handler);
240 rad_assert(node != NULL);
241 rbtree_delete(inst->session_tree, node);
244 * handler == inst->session_head
246 inst->session_head = handler->next;
248 handler->next->prev = NULL;
250 inst->session_head = NULL;
251 inst->session_tail = NULL;
253 talloc_free(handler);
261 * Add a handler to the set of active sessions.
263 * Since we're adding it to the list, we guess that this means
264 * the packet needs a State attribute. So add one.
266 int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
270 REQUEST *request = handler->request;
273 * Generate State, since we've been asked to add it to
276 state = pair_make_reply("State", NULL, T_OP_EQ);
277 if (!state) return 0;
280 * The time at which this request was made was the time
281 * at which it was received by the RADIUS server.
283 handler->timestamp = request->timestamp;
286 handler->src_ipaddr = request->packet->src_ipaddr;
287 handler->eap_id = handler->eap_ds->request->id;
290 * Playing with a data structure shared among threads
291 * means that we need a lock, to avoid conflict.
293 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
296 * If we have a DoS attack, discard new sessions.
298 if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
300 eaplist_expire(inst, request, handler->timestamp);
305 * Create a unique content for the State variable.
306 * It will be modified slightly per round trip, but less so
309 if (handler->trips == 0) {
312 for (i = 0; i < 4; i++) {
315 lvalue = eap_rand(&inst->rand_pool);
317 memcpy(handler->state + i * 4, &lvalue,
323 * Add some more data to distinguish the sessions.
325 handler->state[4] = handler->trips ^ handler->state[0];
326 handler->state[5] = handler->eap_id ^ handler->state[1];
327 handler->state[6] = handler->type ^ handler->state[2];
329 fr_pair_value_memcpy(state, handler->state, sizeof(handler->state));
334 status = rbtree_insert(inst->session_tree, handler);
339 prev = inst->session_tail;
341 prev->next = handler;
342 handler->prev = prev;
343 handler->next = NULL;
344 inst->session_tail = handler;
346 inst->session_head = inst->session_tail = handler;
347 handler->next = handler->prev = NULL;
352 * Now that we've finished mucking with the list,
358 * We don't need this any more.
360 if (status > 0) handler->request = NULL;
362 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
365 fr_pair_delete_by_num(&request->reply->vps, PW_STATE, 0, TAG_ANY);
368 static time_t last_logged = 0;
370 if (last_logged < handler->timestamp) {
371 last_logged = handler->timestamp;
372 ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" "
373 "in the EAP module configuration", inst->xlat_name);
376 ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name);
381 RDEBUG("EAP session adding &reply:State = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
382 state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3],
383 state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]);
389 * Find a a previous EAP-Request sent by us, which matches
390 * the current EAP-Response.
392 * Then, release the handle from the list, and return it to
395 * Also since we fill the eap_ds with the present EAP-Response we
396 * got to free the prev_eapds & move the eap_ds to prev_eapds
398 eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request,
399 eap_packet_raw_t *eap_packet)
402 eap_handler_t *handler, myHandler;
405 * We key the sessions off of the 'state' attribute, so it
408 state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
410 REDEBUG("EAP requires the State attribute to work, but no State exists in the Access-Request packet.");
411 REDEBUG("The RADIUS client is broken. No amount of changing FreeRADIUS will fix the RADIUS client.");
415 if (state->vp_length != EAP_STATE_LEN) {
416 REDEBUG("The RADIUS client has mangled the State attribute, OR you are forcing EAP in the wrong situation");
420 myHandler.src_ipaddr = request->packet->src_ipaddr;
421 myHandler.eap_id = eap_packet->id;
422 memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
425 * Playing with a data structure shared among threads
426 * means that we need a lock, to avoid conflict.
428 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
430 eaplist_expire(inst, request, request->timestamp);
432 handler = eaplist_delete(inst, request, &myHandler);
433 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
436 * Might not have been there.
439 RERROR("rlm_eap (%s): No EAP session matching state "
440 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
442 state->vp_octets[0], state->vp_octets[1],
443 state->vp_octets[2], state->vp_octets[3],
444 state->vp_octets[4], state->vp_octets[5],
445 state->vp_octets[6], state->vp_octets[7]);
449 if (handler->trips >= 50) {
450 RERROR("rlm_eap (%s): Aborting! More than 50 roundtrips "
451 "made in session with state "
452 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
454 state->vp_octets[0], state->vp_octets[1],
455 state->vp_octets[2], state->vp_octets[3],
456 state->vp_octets[4], state->vp_octets[5],
457 state->vp_octets[6], state->vp_octets[7]);
460 talloc_free(handler);
465 RDEBUG("Previous EAP request found for state "
466 "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
467 state->vp_octets[0], state->vp_octets[1],
468 state->vp_octets[2], state->vp_octets[3],
469 state->vp_octets[4], state->vp_octets[5],
470 state->vp_octets[6], state->vp_octets[7]);
473 * Remember what the previous request was.
475 eap_ds_free(&(handler->prev_eapds));
476 handler->prev_eapds = handler->eap_ds;
477 handler->eap_ds = NULL;