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 rlm_eap_t *inst = handler->inst_holder;
79 if (handler->identity) {
80 talloc_free(handler->identity);
81 handler->identity = NULL;
84 if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
85 if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
87 if ((handler->opaque) && (handler->free_opaque)) {
88 handler->free_opaque(handler->opaque);
89 handler->opaque = NULL;
92 handler->opaque = NULL;
93 handler->free_opaque = NULL;
95 if (handler->certs) pairfree(&handler->certs);
97 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
98 if (inst->handler_tree) {
99 rbtree_deletebydata(inst->handler_tree, handler);
102 * Free operations need to be synchronised too.
104 talloc_free(handler);
105 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
111 * Allocate a new eap_handler_t
113 eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
115 eap_handler_t *handler;
117 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
118 handler = talloc_zero(NULL, eap_handler_t);
119 if (inst->handler_tree) {
120 if (!rbtree_insert(inst->handler_tree, handler)) {
121 ERROR("Failed inserting EAP handler into handler tree");
122 talloc_free(handler);
126 handler->inst_holder = inst;
127 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
129 /* Doesn't need to be inside the critical region */
130 talloc_set_destructor(handler, _eap_handler_free);
135 typedef struct check_handler_t {
137 eap_handler_t *handler;
141 static int _check_opaque_free(check_handler_t *check)
143 bool do_warning = false;
146 if (!check->inst || !check->handler) {
150 if (!check->inst->handler_tree) goto done;
152 PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex));
153 if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
158 * The session has continued *after* this packet.
159 * Don't do a warning.
161 if (check->handler->trips > check->trips) {
166 * No TLS means no warnings.
168 if (!check->handler->tls) goto done;
171 * If we're being deleted early, it's likely because we
172 * received a transmit from the client that re-uses the
173 * same RADIUS Id, which forces the current packet to be
174 * deleted. In that case, ignore the error.
176 if (time(NULL) < (check->handler->timestamp + 3)) goto done;
178 if (!check->handler->finished) {
180 memcpy(state, check->handler->state, sizeof(state));
184 PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
187 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
188 WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish! !!",
194 WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility !!");
195 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
201 void eaplist_free(rlm_eap_t *inst)
203 eap_handler_t *node, *next;
205 for (node = inst->session_head; node != NULL; node = next) {
210 inst->session_head = inst->session_tail = NULL;
214 * Return a 32-bit random number.
216 static uint32_t eap_rand(fr_randctx *ctx)
220 num = ctx->randrsl[ctx->randcnt++];
221 if (ctx->randcnt >= 256) {
230 static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
231 eap_handler_t *handler)
235 node = rbtree_find(inst->session_tree, handler);
236 if (!node) return NULL;
238 handler = rbtree_node2data(inst->session_tree, node);
240 RDEBUG("Finished EAP session with state "
241 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
242 handler->state[0], handler->state[1],
243 handler->state[2], handler->state[3],
244 handler->state[4], handler->state[5],
245 handler->state[6], handler->state[7]);
247 * Delete old handler from the tree.
249 rbtree_delete(inst->session_tree, node);
252 * And unsplice it from the linked list.
255 handler->prev->next = handler->next;
257 inst->session_head = handler->next;
260 handler->next->prev = handler->prev;
262 inst->session_tail = handler->prev;
264 handler->prev = handler->next = NULL;
270 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
273 eap_handler_t *handler;
276 * Check the first few handlers in the list, and delete
277 * them if they're too old. We don't need to check them
278 * all, as incoming requests will quickly cause older
279 * handlers to be deleted.
282 for (i = 0; i < 3; i++) {
283 handler = inst->session_head;
286 RDEBUG("Expiring EAP session with state "
287 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
288 handler->state[0], handler->state[1],
289 handler->state[2], handler->state[3],
290 handler->state[4], handler->state[5],
291 handler->state[6], handler->state[7]);
294 * Expire entries from the start of the list.
295 * They should be the oldest ones.
297 if ((timestamp - handler->timestamp) > (int)inst->timer_limit) {
299 node = rbtree_find(inst->session_tree, handler);
300 rad_assert(node != NULL);
301 rbtree_delete(inst->session_tree, node);
304 * handler == inst->session_head
306 inst->session_head = handler->next;
308 handler->next->prev = NULL;
310 inst->session_head = NULL;
311 inst->session_tail = NULL;
313 talloc_free(handler);
321 * Add a handler to the set of active sessions.
323 * Since we're adding it to the list, we guess that this means
324 * the packet needs a State attribute. So add one.
326 int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
330 REQUEST *request = handler->request;
333 * Generate State, since we've been asked to add it to
336 state = pairmake_reply("State", NULL, T_OP_EQ);
337 if (!state) return 0;
340 * The time at which this request was made was the time
341 * at which it was received by the RADIUS server.
343 handler->timestamp = request->timestamp;
346 handler->src_ipaddr = request->packet->src_ipaddr;
347 handler->eap_id = handler->eap_ds->request->id;
350 * Playing with a data structure shared among threads
351 * means that we need a lock, to avoid conflict.
353 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
356 * If we have a DoS attack, discard new sessions.
358 if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
360 eaplist_expire(inst, request, handler->timestamp);
365 * Create a unique content for the State variable.
366 * It will be modified slightly per round trip, but less so
369 if (handler->trips == 0) {
372 for (i = 0; i < 4; i++) {
375 lvalue = eap_rand(&inst->rand_pool);
377 memcpy(handler->state + i * 4, &lvalue,
383 * Add some more data to distinguish the sessions.
385 handler->state[4] = handler->trips ^ handler->state[0];
386 handler->state[5] = handler->eap_id ^ handler->state[1];
387 handler->state[6] = handler->type ^ handler->state[2];
389 pairmemcpy(state, handler->state, sizeof(handler->state));
394 status = rbtree_insert(inst->session_tree, handler);
397 * Catch Access-Challenge without response.
399 if (inst->handler_tree) {
400 check_handler_t *check = talloc(handler, check_handler_t);
403 check->handler = handler;
404 check->trips = handler->trips;
406 talloc_set_destructor(check, _check_opaque_free);
407 request_data_add(request, inst, 0, check, true);
413 prev = inst->session_tail;
415 prev->next = handler;
416 handler->prev = prev;
417 handler->next = NULL;
418 inst->session_tail = handler;
420 inst->session_head = inst->session_tail = handler;
421 handler->next = handler->prev = NULL;
426 * Now that we've finished mucking with the list,
432 * We don't need this any more.
434 if (status > 0) handler->request = NULL;
436 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
442 static time_t last_logged = 0;
444 if (last_logged < handler->timestamp) {
445 last_logged = handler->timestamp;
446 ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" "
447 "in the EAP module configuration", inst->xlat_name);
450 ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name);
455 RDEBUG("New EAP session, adding 'State' attribute to reply 0x%02x%02x%02x%02x%02x%02x%02x%02x",
456 state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3],
457 state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]);
463 * Find a a previous EAP-Request sent by us, which matches
464 * the current EAP-Response.
466 * Then, release the handle from the list, and return it to
469 * Also since we fill the eap_ds with the present EAP-Response we
470 * got to free the prev_eapds & move the eap_ds to prev_eapds
472 eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request,
473 eap_packet_raw_t *eap_packet)
476 eap_handler_t *handler, myHandler;
479 * We key the sessions off of the 'state' attribute, so it
482 state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
484 (state->length != EAP_STATE_LEN)) {
488 myHandler.src_ipaddr = request->packet->src_ipaddr;
489 myHandler.eap_id = eap_packet->id;
490 memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
493 * Playing with a data structure shared among threads
494 * means that we need a lock, to avoid conflict.
496 PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
498 eaplist_expire(inst, request, request->timestamp);
500 handler = eaplist_delete(inst, request, &myHandler);
501 PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
504 * Might not have been there.
507 ERROR("rlm_eap (%s): No EAP session matching state "
508 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
510 state->vp_octets[0], state->vp_octets[1],
511 state->vp_octets[2], state->vp_octets[3],
512 state->vp_octets[4], state->vp_octets[5],
513 state->vp_octets[6], state->vp_octets[7]);
517 if (handler->trips >= 50) {
518 ERROR("rlm_eap (%s): Aborting! More than 50 roundtrips "
519 "made in session with state "
520 "0x%02x%02x%02x%02x%02x%02x%02x%02x",
522 state->vp_octets[0], state->vp_octets[1],
523 state->vp_octets[2], state->vp_octets[3],
524 state->vp_octets[4], state->vp_octets[5],
525 state->vp_octets[6], state->vp_octets[7]);
528 talloc_free(handler);
533 RDEBUG("Previous EAP request found for state "
534 "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
535 state->vp_octets[0], state->vp_octets[1],
536 state->vp_octets[2], state->vp_octets[3],
537 state->vp_octets[4], state->vp_octets[5],
538 state->vp_octets[6], state->vp_octets[7]);
541 * Remember what the previous request was.
543 eap_ds_free(&(handler->prev_eapds));
544 handler->prev_eapds = handler->eap_ds;
545 handler->eap_ds = NULL;