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 * Allocate a new EAP_PACKET
33 EAP_PACKET *eap_packet_alloc(void)
37 rp = rad_malloc(sizeof(EAP_PACKET));
38 memset(rp, 0, sizeof(EAP_PACKET));
45 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
47 EAP_PACKET *eap_packet;
49 if (!eap_packet_ptr) return;
50 eap_packet = *eap_packet_ptr;
51 if (!eap_packet) return;
53 if (eap_packet->type.data) {
55 * There's no packet, OR the type data isn't
56 * pointing inside of the packet: free it.
58 if ((eap_packet->packet == NULL) ||
59 (eap_packet->type.data != (eap_packet->packet + 5))) {
60 free(eap_packet->type.data);
62 eap_packet->type.data = NULL;
65 if (eap_packet->packet) {
66 free(eap_packet->packet);
67 eap_packet->packet = NULL;
72 *eap_packet_ptr = NULL;
76 * Allocate a new EAP_PACKET
78 EAP_DS *eap_ds_alloc(void)
82 eap_ds = rad_malloc(sizeof(EAP_DS));
83 memset(eap_ds, 0, sizeof(EAP_DS));
84 if ((eap_ds->response = eap_packet_alloc()) == NULL) {
88 if ((eap_ds->request = eap_packet_alloc()) == NULL) {
96 void eap_ds_free(EAP_DS **eap_ds_p)
100 if (!eap_ds_p) return;
105 if (eap_ds->response) eap_packet_free(&(eap_ds->response));
106 if (eap_ds->request) eap_packet_free(&(eap_ds->request));
113 * Allocate a new EAP_HANDLER
115 EAP_HANDLER *eap_handler_alloc(void)
117 EAP_HANDLER *handler;
119 handler = rad_malloc(sizeof(EAP_HANDLER));
120 memset(handler, 0, sizeof(EAP_HANDLER));
124 void eap_handler_free(EAP_HANDLER *handler)
129 if (handler->identity) {
130 free(handler->identity);
131 handler->identity = NULL;
134 if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
135 if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
137 if ((handler->opaque) && (handler->free_opaque)) {
138 handler->free_opaque(handler->opaque);
139 handler->opaque = NULL;
141 else if ((handler->opaque) && (handler->free_opaque == NULL))
142 radlog(L_ERR, "Possible memory leak ...");
144 handler->opaque = NULL;
145 handler->free_opaque = NULL;
150 void eaptype_free(EAP_TYPES *i)
152 if (i->type->detach) (i->type->detach)(i->type_data);
154 if (i->handle) lt_dlclose(i->handle);
159 void eaplist_free(rlm_eap_t *inst)
161 EAP_HANDLER *node, *next;
163 for (node = inst->session_head; node != NULL; node = next) {
165 eap_handler_free(node);
168 inst->session_head = inst->session_tail = NULL;
172 * Return a 32-bit random number.
174 static uint32_t eap_rand(fr_randctx *ctx)
178 num = ctx->randrsl[ctx->randcnt++];
179 if (ctx->randcnt >= 256) {
187 static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, EAP_HANDLER *handler)
191 node = rbtree_find(inst->session_tree, handler);
192 if (!node) return NULL;
194 handler = rbtree_node2data(inst->session_tree, node);
197 * Delete old handler from the tree.
199 rbtree_delete(inst->session_tree, node);
202 * And unsplice it from the linked list.
205 handler->prev->next = handler->next;
207 inst->session_head = handler->next;
210 handler->next->prev = handler->prev;
212 inst->session_tail = handler->prev;
214 handler->prev = handler->next = NULL;
220 static void eaplist_expire(rlm_eap_t *inst, time_t timestamp)
223 EAP_HANDLER *handler;
226 * Check the first few handlers in the list, and delete
227 * them if they're too old. We don't need to check them
228 * all, as incoming requests will quickly cause older
229 * handlers to be deleted.
232 for (i = 0; i < 2; i++) {
233 handler = inst->session_head;
235 ((timestamp - handler->timestamp) > inst->timer_limit)) {
237 node = rbtree_find(inst->session_tree, handler);
238 rad_assert(node != NULL);
239 rbtree_delete(inst->session_tree, node);
242 * handler == inst->session_head
244 inst->session_head = handler->next;
246 handler->next->prev = NULL;
248 inst->session_head = NULL;
250 eap_handler_free(handler);
256 * Add a handler to the set of active sessions.
258 * Since we're adding it to the list, we guess that this means
259 * the packet needs a State attribute. So add one.
261 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
265 REQUEST *request = handler->request;
267 rad_assert(handler != NULL);
268 rad_assert(request != NULL);
271 * Generate State, since we've been asked to add it to
274 state = pairmake("State", "0x00", T_OP_EQ);
275 if (!state) return 0;
278 * The time at which this request was made was the time
279 * at which it was received by the RADIUS server.
281 handler->timestamp = request->timestamp;
284 handler->src_ipaddr = request->packet->src_ipaddr;
285 handler->eap_id = handler->eap_ds->request->id;
288 * Playing with a data structure shared among threads
289 * means that we need a lock, to avoid conflict.
291 pthread_mutex_lock(&(inst->session_mutex));
294 * If we have a DoS attack, discard new sessions.
296 if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
297 eaplist_expire(inst, handler->timestamp);
302 * Create a unique content for the State variable.
303 * It will be modified slightly per round trip, but less so
306 if (handler->trips == 0) {
309 for (i = 0; i < 4; i++) {
312 lvalue = eap_rand(&inst->rand_pool);
314 memcpy(handler->state + i * 4, &lvalue,
319 memcpy(state->vp_octets, handler->state, sizeof(handler->state));
320 state->length = EAP_STATE_LEN;
323 * Add some more data to distinguish the sessions.
325 state->vp_octets[4] = handler->trips ^ handler->state[0];
326 state->vp_octets[5] = handler->eap_id ^ handler->state[1];
327 state->vp_octets[6] = handler->eap_type ^ handler->state[2];
330 * and copy the state back again.
332 memcpy(handler->state, state->vp_octets, sizeof(handler->state));
337 status = rbtree_insert(inst->session_tree, handler);
342 prev = inst->session_tail;
344 prev->next = handler;
345 handler->prev = prev;
346 handler->next = NULL;
347 inst->session_tail = handler;
349 inst->session_head = inst->session_tail = handler;
350 handler->next = handler->prev = NULL;
355 * Now that we've finished mucking with the list,
361 * We don't need this any more.
363 if (status) handler->request = NULL;
365 pthread_mutex_unlock(&(inst->session_mutex));
369 radlog(L_ERR, "rlm_eap: Failed to store handler");
373 pairadd(&(request->reply->vps), state);
379 * Find a a previous EAP-Request sent by us, which matches
380 * the current EAP-Response.
382 * Then, release the handle from the list, and return it to
385 * Also since we fill the eap_ds with the present EAP-Response we
386 * got to free the prev_eapds & move the eap_ds to prev_eapds
388 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
389 eap_packet_t *eap_packet)
392 EAP_HANDLER *handler, myHandler;
395 * We key the sessions off of the 'state' attribute, so it
398 state = pairfind(request->packet->vps, PW_STATE);
400 (state->length != EAP_STATE_LEN)) {
404 myHandler.src_ipaddr = request->packet->src_ipaddr;
405 myHandler.eap_id = eap_packet->id;
406 memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
409 * Playing with a data structure shared among threads
410 * means that we need a lock, to avoid conflict.
412 pthread_mutex_lock(&(inst->session_mutex));
414 eaplist_expire(inst, request->timestamp);
416 handler = eaplist_delete(inst, &myHandler);
417 pthread_mutex_unlock(&(inst->session_mutex));
420 * Might not have been there.
423 radlog(L_ERR, "rlm_eap: No EAP session matching the State variable.");
427 if (handler->trips >= 50) {
428 RDEBUG2("More than 50 authentication packets for this EAP session. Aborted.");
429 eap_handler_free(handler);
434 RDEBUG2("Request found, released from the list");
437 * Remember what the previous request was.
439 eap_ds_free(&(handler->prev_eapds));
440 handler->prev_eapds = handler->eap_ds;
441 handler->eap_ds = NULL;