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>
24 #include <freeradius-devel/ident.h>
31 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
32 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
34 #define PTHREAD_MUTEX_LOCK(_x)
35 #define PTHREAD_MUTEX_UNLOCK(_x)
39 * Allocate a new EAP_PACKET
41 EAP_PACKET *eap_packet_alloc(void)
45 rp = rad_malloc(sizeof(EAP_PACKET));
46 memset(rp, 0, sizeof(EAP_PACKET));
53 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
55 EAP_PACKET *eap_packet;
57 if (!eap_packet_ptr) return;
58 eap_packet = *eap_packet_ptr;
59 if (!eap_packet) return;
61 if (eap_packet->type.data) {
63 * There's no packet, OR the type data isn't
64 * pointing inside of the packet: free it.
66 if ((eap_packet->packet == NULL) ||
67 (eap_packet->type.data != (eap_packet->packet + 5))) {
68 free(eap_packet->type.data);
70 eap_packet->type.data = NULL;
73 if (eap_packet->packet) {
74 free(eap_packet->packet);
75 eap_packet->packet = NULL;
80 *eap_packet_ptr = NULL;
84 * Allocate a new EAP_PACKET
86 EAP_DS *eap_ds_alloc(void)
90 eap_ds = rad_malloc(sizeof(EAP_DS));
91 memset(eap_ds, 0, sizeof(EAP_DS));
92 if ((eap_ds->response = eap_packet_alloc()) == NULL) {
96 if ((eap_ds->request = eap_packet_alloc()) == NULL) {
104 void eap_ds_free(EAP_DS **eap_ds_p)
108 if (!eap_ds_p) return;
113 if (eap_ds->response) eap_packet_free(&(eap_ds->response));
114 if (eap_ds->request) eap_packet_free(&(eap_ds->request));
121 * Allocate a new EAP_HANDLER
123 EAP_HANDLER *eap_handler_alloc(rlm_eap_t *inst)
125 EAP_HANDLER *handler;
127 handler = rad_malloc(sizeof(EAP_HANDLER));
128 memset(handler, 0, sizeof(EAP_HANDLER));
130 if (inst->handler_tree) {
131 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
132 rbtree_insert(inst->handler_tree, handler);
133 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
139 void eap_opaque_free(EAP_HANDLER *handler)
144 eap_handler_free(handler->inst_holder, handler);
147 void eap_handler_free(rlm_eap_t *inst, EAP_HANDLER *handler)
152 if (inst->handler_tree) {
153 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
154 rbtree_deletebydata(inst->handler_tree, handler);
155 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
158 if (handler->identity) {
159 free(handler->identity);
160 handler->identity = NULL;
163 if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
164 if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
166 if ((handler->opaque) && (handler->free_opaque)) {
167 handler->free_opaque(handler->opaque);
168 handler->opaque = NULL;
170 else if ((handler->opaque) && (handler->free_opaque == NULL))
171 radlog(L_ERR, "rlm_eap (%s): Possible memory leak ...",
174 handler->opaque = NULL;
175 handler->free_opaque = NULL;
177 if (handler->certs) pairfree(&handler->certs);
183 typedef struct check_handler_t {
185 EAP_HANDLER *handler;
189 static void check_handler(void *data)
191 int do_warning = FALSE;
193 check_handler_t *check = data;
197 if (!check->inst || !check->handler) {
202 if (!check->inst->handler_tree) goto done;
204 PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex));
205 if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
210 * The session has continued *after* this packet.
211 * Don't do a warning.
213 if (check->handler->trips > check->trips) {
218 * No TLS means no warnings.
220 if (!check->handler->tls) goto done;
223 * If we're being deleted early, it's likely because we
224 * received a transmit from the client that re-uses the
225 * same RADIUS Id, which forces the current packet to be
226 * deleted. In that case, ignore the error.
228 if (time(NULL) < (check->handler->timestamp + 3)) goto done;
230 if (!check->handler->finished) {
232 memcpy(state, check->handler->state, sizeof(state));
236 PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
240 DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
241 DEBUGW("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish! !!",
247 DEBUGW("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility !!");
248 DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
252 void eaptype_free(EAP_TYPES *i)
254 cf_section_parse_free(i->cs, i->type_data);
256 if (i->type->detach) (i->type->detach)(i->type_data);
258 if (i->handle) lt_dlclose(i->handle);
263 void eaplist_free(rlm_eap_t *inst)
265 EAP_HANDLER *node, *next;
267 for (node = inst->session_head; node != NULL; node = next) {
269 eap_handler_free(inst, node);
272 inst->session_head = inst->session_tail = NULL;
276 * Return a 32-bit random number.
278 static uint32_t eap_rand(fr_randctx *ctx)
282 num = ctx->randrsl[ctx->randcnt++];
283 if (ctx->randcnt >= 256) {
292 static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
293 EAP_HANDLER *handler)
297 node = rbtree_find(inst->session_tree, handler);
298 if (!node) return NULL;
300 handler = rbtree_node2data(inst->session_tree, node);
302 RDEBUG("Finished EAP session with state "
303 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
304 handler->state[0], handler->state[1],
305 handler->state[2], handler->state[3],
306 handler->state[4], handler->state[5],
307 handler->state[6], handler->state[7]);
309 * Delete old handler from the tree.
311 rbtree_delete(inst->session_tree, node);
314 * And unsplice it from the linked list.
317 handler->prev->next = handler->next;
319 inst->session_head = handler->next;
322 handler->next->prev = handler->prev;
324 inst->session_tail = handler->prev;
326 handler->prev = handler->next = NULL;
332 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
335 EAP_HANDLER *handler;
338 * Check the first few handlers in the list, and delete
339 * them if they're too old. We don't need to check them
340 * all, as incoming requests will quickly cause older
341 * handlers to be deleted.
344 for (i = 0; i < 3; i++) {
345 handler = inst->session_head;
348 RDEBUG("Expiring EAP session with state "
349 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
350 handler->state[0], handler->state[1],
351 handler->state[2], handler->state[3],
352 handler->state[4], handler->state[5],
353 handler->state[6], handler->state[7]);
356 * Expire entries from the start of the list.
357 * They should be the oldest ones.
359 if ((timestamp - handler->timestamp) > inst->timer_limit) {
361 node = rbtree_find(inst->session_tree, handler);
362 rad_assert(node != NULL);
363 rbtree_delete(inst->session_tree, node);
366 * handler == inst->session_head
368 inst->session_head = handler->next;
370 handler->next->prev = NULL;
372 inst->session_head = NULL;
373 inst->session_tail = NULL;
375 eap_handler_free(inst, handler);
381 * Add a handler to the set of active sessions.
383 * Since we're adding it to the list, we guess that this means
384 * the packet needs a State attribute. So add one.
386 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
390 REQUEST *request = handler->request;
392 rad_assert(handler != NULL);
393 rad_assert(request != NULL);
396 * Generate State, since we've been asked to add it to
399 state = pairmake("State", "0x00", T_OP_EQ);
400 if (!state) return 0;
403 * The time at which this request was made was the time
404 * at which it was received by the RADIUS server.
406 handler->timestamp = request->timestamp;
409 handler->src_ipaddr = request->packet->src_ipaddr;
410 handler->eap_id = handler->eap_ds->request->id;
413 * Playing with a data structure shared among threads
414 * means that we need a lock, to avoid conflict.
416 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
419 * If we have a DoS attack, discard new sessions.
421 if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
423 eaplist_expire(inst, request, handler->timestamp);
428 * Create a unique content for the State variable.
429 * It will be modified slightly per round trip, but less so
432 if (handler->trips == 0) {
435 for (i = 0; i < 4; i++) {
438 lvalue = eap_rand(&inst->rand_pool);
440 memcpy(handler->state + i * 4, &lvalue,
445 memcpy(state->vp_octets, handler->state, sizeof(handler->state));
446 state->length = EAP_STATE_LEN;
449 * Add some more data to distinguish the sessions.
451 state->vp_octets[4] = handler->trips ^ handler->state[0];
452 state->vp_octets[5] = handler->eap_id ^ handler->state[1];
453 state->vp_octets[6] = handler->eap_type ^ handler->state[2];
456 * and copy the state back again.
458 memcpy(handler->state, state->vp_octets, sizeof(handler->state));
463 status = rbtree_insert(inst->session_tree, handler);
466 * Catch Access-Challenge without response.
468 if (inst->handler_tree) {
469 check_handler_t *check = rad_malloc(sizeof(*check));
472 check->handler = handler;
473 check->trips = handler->trips;
474 request_data_add(request, inst, 0, check, check_handler);
480 prev = inst->session_tail;
482 prev->next = handler;
483 handler->prev = prev;
484 handler->next = NULL;
485 inst->session_tail = handler;
487 inst->session_head = inst->session_tail = handler;
488 handler->next = handler->prev = NULL;
493 * Now that we've finished mucking with the list,
499 * We don't need this any more.
501 if (status > 0) handler->request = NULL;
503 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
509 static time_t last_logged = 0;
511 if (last_logged < handler->timestamp) {
512 last_logged = handler->timestamp;
513 radlog(L_ERR, "rlm_eap (%s): Too many open "
514 "sessions. Try increasing "
515 "\"max_sessions\" in the EAP module "
516 "configuration", inst->xlat_name);
519 radlog(L_ERR, "rlm_eap (%s): Internal error: "
520 "failed to store handler", inst->xlat_name);
525 RDEBUG("New EAP session, adding 'State' attribute to reply "
526 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
527 state->vp_octets[0], state->vp_octets[1],
528 state->vp_octets[2], state->vp_octets[3],
529 state->vp_octets[4], state->vp_octets[5],
530 state->vp_octets[6], state->vp_octets[7]);
532 pairadd(&(request->reply->vps), state);
538 * Find a a previous EAP-Request sent by us, which matches
539 * the current EAP-Response.
541 * Then, release the handle from the list, and return it to
544 * Also since we fill the eap_ds with the present EAP-Response we
545 * got to free the prev_eapds & move the eap_ds to prev_eapds
547 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
548 eap_packet_t *eap_packet)
551 EAP_HANDLER *handler, myHandler;
554 * We key the sessions off of the 'state' attribute, so it
557 state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
559 (state->length != EAP_STATE_LEN)) {
563 myHandler.src_ipaddr = request->packet->src_ipaddr;
564 myHandler.eap_id = eap_packet->id;
565 memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
568 * Playing with a data structure shared among threads
569 * means that we need a lock, to avoid conflict.
571 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
573 eaplist_expire(inst, request, request->timestamp);
575 handler = eaplist_delete(inst, request, &myHandler);
576 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
579 * Might not have been there.
582 radlog(L_ERR, "rlm_eap (%s): No EAP session matching state "
583 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
585 state->vp_octets[0], state->vp_octets[1],
586 state->vp_octets[2], state->vp_octets[3],
587 state->vp_octets[4], state->vp_octets[5],
588 state->vp_octets[6], state->vp_octets[7]);
592 if (handler->trips >= 50) {
593 radlog(L_ERR, "rlm_eap (%s): Aborting! More than 50 roundtrips "
594 "made in session with state "
595 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
597 state->vp_octets[0], state->vp_octets[1],
598 state->vp_octets[2], state->vp_octets[3],
599 state->vp_octets[4], state->vp_octets[5],
600 state->vp_octets[6], state->vp_octets[7]);
603 eap_handler_free(inst, handler);
608 RDEBUG("Previous EAP request found for state "
609 "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
610 state->vp_octets[0], state->vp_octets[1],
611 state->vp_octets[2], state->vp_octets[3],
612 state->vp_octets[4], state->vp_octets[5],
613 state->vp_octets[6], state->vp_octets[7]);
616 * Remember what the previous request was.
618 eap_ds_free(&(handler->prev_eapds));
619 handler->prev_eapds = handler->eap_ds;
620 handler->eap_ds = NULL;