From 50b37d4a27d3295a29afca2286f1a5a086142cec Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:49:46 +0200 Subject: Adding upstream version 3.2.1+dfsg. Signed-off-by: Daniel Baumann --- src/modules/rlm_eap/mem.c | 503 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 src/modules/rlm_eap/mem.c (limited to 'src/modules/rlm_eap/mem.c') diff --git a/src/modules/rlm_eap/mem.c b/src/modules/rlm_eap/mem.c new file mode 100644 index 0000000..6be8ca4 --- /dev/null +++ b/src/modules/rlm_eap/mem.c @@ -0,0 +1,503 @@ +/* + * mem.c Memory allocation, deallocation stuff. + * + * Version: $Id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Copyright 2000,2001,2006 The FreeRADIUS server project + * Copyright 2001 hereUare Communications, Inc. + */ + +RCSID("$Id$") + +#include +#include "rlm_eap.h" + +#ifdef WITH_TLS +#include +#endif + +#ifdef HAVE_PTHREAD_H +#define PTHREAD_MUTEX_LOCK pthread_mutex_lock +#define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock +#else +#define PTHREAD_MUTEX_LOCK(_x) +#define PTHREAD_MUTEX_UNLOCK(_x) +#endif + +/* + * Allocate a new eap_packet_t + */ +EAP_DS *eap_ds_alloc(eap_handler_t *handler) +{ + EAP_DS *eap_ds; + + eap_ds = talloc_zero(handler, EAP_DS); + eap_ds->response = talloc_zero(eap_ds, eap_packet_t); + if (!eap_ds->response) { + eap_ds_free(&eap_ds); + return NULL; + } + eap_ds->request = talloc_zero(eap_ds, eap_packet_t); + if (!eap_ds->response) { + eap_ds_free(&eap_ds); + return NULL; + } + + return eap_ds; +} + +void eap_ds_free(EAP_DS **eap_ds_p) +{ + EAP_DS *eap_ds; + + if (!eap_ds_p) return; + + eap_ds = *eap_ds_p; + if (!eap_ds) return; + + if (eap_ds->response) talloc_free(eap_ds->response); + if (eap_ds->request) talloc_free(eap_ds->request); + + talloc_free(eap_ds); + *eap_ds_p = NULL; +} + +static int _eap_handler_free(eap_handler_t *handler) +{ + if (handler->identity) { + talloc_free(handler->identity); + handler->identity = NULL; + } + + if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds)); + if (handler->eap_ds) eap_ds_free(&(handler->eap_ds)); + + if ((handler->opaque) && (handler->free_opaque)) { + handler->free_opaque(handler->opaque); + handler->opaque = NULL; + } + + handler->opaque = NULL; + handler->free_opaque = NULL; + + /* + * Give helpful debug messages if: + * + * we're debugging TLS sessions, which don't finish, + * and which aren't deleted early due to a likely RADIUS + * retransmit which nukes our ID, and therefore our stare. + */ + if (fr_debug_lvl && handler->tls && !handler->finished && + (time(NULL) > (handler->timestamp + 3))) { + WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x did not finish! !!", + handler->state[0], handler->state[1], + handler->state[2], handler->state[3], + handler->state[4], handler->state[5], + handler->state[6], handler->state[7], + handler->state[8], handler->state[9], + handler->state[10], handler->state[11], + handler->state[12], handler->state[13], + handler->state[14], handler->state[15]); + + WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility !!"); + WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } + + return 0; +} + +/* + * Allocate a new eap_handler_t + */ +eap_handler_t *eap_handler_alloc(rlm_eap_t *inst) +{ + eap_handler_t *handler; + + handler = talloc_zero(NULL, eap_handler_t); + if (!handler) { + ERROR("Failed allocating handler"); + return NULL; + } + handler->inst_holder = inst; + + /* Doesn't need to be inside the critical region */ + talloc_set_destructor(handler, _eap_handler_free); + + return handler; +} + + +void eaplist_free(rlm_eap_t *inst) +{ + eap_handler_t *node, *next; + + for (node = inst->session_head; node != NULL; node = next) { + next = node->next; + talloc_free(node); + } + + inst->session_head = inst->session_tail = NULL; +} + +/* + * Return a 32-bit random number. + */ +static uint32_t eap_rand(fr_randctx *ctx) +{ + uint32_t num; + + num = ctx->randrsl[ctx->randcnt++]; + if (ctx->randcnt >= 256) { + ctx->randcnt = 0; + fr_isaac(ctx); + } + + return num; +} + + +static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request, + eap_handler_t *handler) +{ + rbnode_t *node; + + node = rbtree_find(inst->session_tree, handler); + if (!node) return NULL; + + handler = rbtree_node2data(inst->session_tree, node); + + RDEBUG("Finished EAP session with state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + handler->state[0], handler->state[1], + handler->state[2], handler->state[3], + handler->state[4], handler->state[5], + handler->state[6], handler->state[7]); + /* + * Delete old handler from the tree. + */ + rbtree_delete(inst->session_tree, node); + + /* + * And unsplice it from the linked list. + */ + if (handler->prev) { + handler->prev->next = handler->next; + } else { + inst->session_head = handler->next; + } + if (handler->next) { + handler->next->prev = handler->prev; + } else { + inst->session_tail = handler->prev; + } + handler->prev = handler->next = NULL; + + return handler; +} + + +static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp) +{ + int i; + eap_handler_t *handler; + + /* + * Check the first few handlers in the list, and delete + * them if they're too old. We don't need to check them + * all, as incoming requests will quickly cause older + * handlers to be deleted. + * + */ + for (i = 0; i < 3; i++) { + handler = inst->session_head; + if (!handler) break; + + RDEBUG("Expiring EAP session with state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + handler->state[0], handler->state[1], + handler->state[2], handler->state[3], + handler->state[4], handler->state[5], + handler->state[6], handler->state[7]); + + /* + * Expire entries from the start of the list. + * They should be the oldest ones. + */ + if ((timestamp - handler->timestamp) > (int)inst->timer_limit) { + rbnode_t *node; + node = rbtree_find(inst->session_tree, handler); + rad_assert(node != NULL); + rbtree_delete(inst->session_tree, node); + + /* + * handler == inst->session_head + */ + inst->session_head = handler->next; + if (handler->next) { + handler->next->prev = NULL; + } else { + inst->session_head = NULL; + inst->session_tail = NULL; + } + +#ifdef WITH_TLS + /* + * Remove expired TLS sessions. + */ + switch (handler->type) { + case PW_EAP_TLS: + case PW_EAP_TTLS: + case PW_EAP_PEAP: + case PW_EAP_FAST: + tls_fail(handler->opaque); /* MUST be a tls_session! */ + break; + + default: + break; + } +#endif + + talloc_free(handler); + } else { + break; + } + } +} + +/* + * Add a handler to the set of active sessions. + * + * Since we're adding it to the list, we guess that this means + * the packet needs a State attribute. So add one. + */ +int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler) +{ + int status = 0; + VALUE_PAIR *state; + REQUEST *request = handler->request; + + /* + * Generate State, since we've been asked to add it to + * the list. + */ + state = pair_make_reply("State", NULL, T_OP_EQ); + if (!state) return 0; + + /* + * The time at which this request was made was the time + * at which it was received by the RADIUS server. + */ + handler->timestamp = request->timestamp; + handler->status = 1; + + handler->src_ipaddr = request->packet->src_ipaddr; + handler->eap_id = handler->eap_ds->request->id; + + /* + * Playing with a data structure shared among threads + * means that we need a lock, to avoid conflict. + */ + PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); + + /* + * If we have a DoS attack, discard new sessions. + */ + if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) { + status = -1; + eaplist_expire(inst, request, handler->timestamp); + goto done; + } + + /* + * Create a unique content for the State variable. + * It will be modified slightly per round trip, but less so + * than in 1.x. + */ + if (handler->trips == 0) { + int i; + + for (i = 0; i < 4; i++) { + uint32_t lvalue; + + lvalue = eap_rand(&inst->rand_pool); + + memcpy(handler->state + i * 4, &lvalue, + sizeof(lvalue)); + } + } + + /* + * Add some more data to distinguish the sessions. + */ + handler->state[4] = handler->trips ^ handler->state[0]; + handler->state[5] = handler->eap_id ^ handler->state[1]; + handler->state[6] = handler->type ^ handler->state[2]; + handler->state[12] = handler->state[2] ^ (RADIUSD_VERSION & 0xff); + + fr_pair_value_memcpy(state, handler->state, sizeof(handler->state)); + + /* + * Big-time failure. + */ + status = rbtree_insert(inst->session_tree, handler); + + if (status) { + eap_handler_t *prev; + + prev = inst->session_tail; + if (prev) { + prev->next = handler; + handler->prev = prev; + handler->next = NULL; + inst->session_tail = handler; + } else { + inst->session_head = inst->session_tail = handler; + handler->next = handler->prev = NULL; + } + } + + /* + * Now that we've finished mucking with the list, + * unlock it. + */ + done: + + /* + * We don't need this any more. + */ + if (status > 0) handler->request = NULL; + + PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); + + if (status <= 0) { + fr_pair_delete_by_num(&request->reply->vps, PW_STATE, 0, TAG_ANY); + + if (status < 0) { + static time_t last_logged = 0; + + if (last_logged < handler->timestamp) { + last_logged = handler->timestamp; + ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" " + "in the EAP module configuration", inst->xlat_name); + } + } else { + ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name); + } + return 0; + } + + RDEBUG("EAP session adding &reply:State = 0x%02x%02x%02x%02x%02x%02x%02x%02x", + state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3], + state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]); + + return 1; +} + +/* + * Find a a previous EAP-Request sent by us, which matches + * the current EAP-Response. + * + * Then, release the handle from the list, and return it to + * the caller. + * + * Also since we fill the eap_ds with the present EAP-Response we + * got to free the prev_eapds & move the eap_ds to prev_eapds + */ +eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request, + eap_packet_raw_t *eap_packet) +{ + VALUE_PAIR *state; + eap_handler_t *handler, myHandler; + + /* + * We key the sessions off of the 'state' attribute, so it + * must exist. + */ + state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY); + if (!state) { + REDEBUG("EAP requires the State attribute to work, but no State exists in the Access-Request packet."); + REDEBUG("The RADIUS client is broken. No amount of changing FreeRADIUS will fix the RADIUS client."); + return NULL; + } + + if (state->vp_length != EAP_STATE_LEN) { + REDEBUG("The RADIUS client has mangled the State attribute, OR you are forcing EAP in the wrong situation"); + return NULL; + } + + myHandler.src_ipaddr = request->packet->src_ipaddr; + myHandler.eap_id = eap_packet->id; + memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state)); + + /* + * Playing with a data structure shared among threads + * means that we need a lock, to avoid conflict. + */ + PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); + + eaplist_expire(inst, request, request->timestamp); + + handler = eaplist_delete(inst, request, &myHandler); + PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); + + /* + * Might not have been there. + */ + if (!handler) { + RERROR("rlm_eap (%s): No EAP session matching state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + inst->xlat_name, + state->vp_octets[0], state->vp_octets[1], + state->vp_octets[2], state->vp_octets[3], + state->vp_octets[4], state->vp_octets[5], + state->vp_octets[6], state->vp_octets[7]); + return NULL; + } + + if (handler->trips >= 50) { + RERROR("rlm_eap (%s): Aborting! More than 50 roundtrips " + "made in session with state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + inst->xlat_name, + state->vp_octets[0], state->vp_octets[1], + state->vp_octets[2], state->vp_octets[3], + state->vp_octets[4], state->vp_octets[5], + state->vp_octets[6], state->vp_octets[7]); + + + talloc_free(handler); + return NULL; + } + handler->trips++; + + RDEBUG("Previous EAP request found for state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list", + state->vp_octets[0], state->vp_octets[1], + state->vp_octets[2], state->vp_octets[3], + state->vp_octets[4], state->vp_octets[5], + state->vp_octets[6], state->vp_octets[7]); + + /* + * Remember what the previous request was. + */ + eap_ds_free(&(handler->prev_eapds)); + handler->prev_eapds = handler->eap_ds; + handler->eap_ds = NULL; + + return handler; +} -- cgit v1.2.3