From: Alan T. DeKok Date: Thu, 22 Jan 2026 12:47:44 +0000 (-0500) Subject: move state data to a config structure X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fe643e39ed0830b49bd7d9a931ce6db15901c84b;p=thirdparty%2Ffreeradius-server.git move state data to a config structure --- diff --git a/src/lib/server/state.c b/src/lib/server/state.c index e7857366cf4..996e0c783f0 100644 --- a/src/lib/server/state.c +++ b/src/lib/server/state.c @@ -56,6 +56,16 @@ RCSID("$Id$") #include #include +const conf_parser_t state_session_config[] = { + { FR_CONF_OFFSET("timeout", fr_state_config_t, timeout), .dflt = "15" }, + { FR_CONF_OFFSET("max", fr_state_config_t, max_sessions), .dflt = "4096" }, + { FR_CONF_OFFSET("max_rounds", fr_state_config_t, max_rounds), .dflt = "50" }, + { FR_CONF_OFFSET("state_server_id", fr_state_config_t, server_id) }, + + CONF_PARSER_TERMINATOR +}; + + /** Holds a state value, and associated fr_pair_ts and data * */ @@ -110,7 +120,7 @@ typedef struct { fr_dlist_t free_entry; //!< Entry in the list of things to free. }; - int tries; + unsigned int tries; fr_pair_t *ctx; //!< for all session specific data. @@ -144,25 +154,19 @@ struct fr_state_tree_s { uint64_t id; //!< Next ID to assign. uint64_t timed_out; //!< Number of states that were cleaned up due to //!< timeout. - uint32_t max_sessions; //!< Maximum number of sessions we track. + fr_state_config_t config; //!< a local copy + uint32_t used_sessions; //!< How many sessions are currently in progress. fr_rb_tree_t *tree; //!< rbtree used to lookup state value. fr_dlist_head_t to_expire; //!< Linked list of entries to free. - fr_time_delta_t timeout; //!< How long to wait before cleaning up state entries. - - bool thread_safe; //!< Whether we lock the tree whilst modifying it. pthread_mutex_t mutex; //!< Synchronisation mutex. - uint8_t server_id; //!< ID to use for load balancing. - uint32_t context_id; //!< ID binding state values to a context such - ///< as a virtual server. - - fr_dict_attr_t const *da; //!< State attribute used. + fr_dict_attr_t const *da; //!< Attribute where the state is stored. }; -#define PTHREAD_MUTEX_LOCK if (state->thread_safe) pthread_mutex_lock -#define PTHREAD_MUTEX_UNLOCK if (state->thread_safe) pthread_mutex_unlock +#define PTHREAD_MUTEX_LOCK if (state->config.thread_safe) pthread_mutex_lock +#define PTHREAD_MUTEX_UNLOCK if (state->config.thread_safe) pthread_mutex_unlock static void state_entry_unlink(fr_state_tree_t *state, fr_state_entry_t *entry); @@ -185,7 +189,7 @@ static int _state_tree_free(fr_state_tree_t *state) { fr_state_entry_t *entry; - if (state->thread_safe) pthread_mutex_destroy(&state->mutex); + if (state->config.thread_safe) pthread_mutex_destroy(&state->mutex); DEBUG4("Freeing state tree %p", state); @@ -207,27 +211,20 @@ static int _state_tree_free(fr_state_tree_t *state) * * @param[in] ctx to link the lifecycle of the state tree to. * @param[in] da Attribute used to store and retrieve state from. - * @param[in] thread_safe Whether we should mutex protect the state tree. - * @param[in] max_sessions we track state for. - * @param[in] timeout How long to wait before cleaning up entries. - * @param[in] server_id ID byte to use in load-balancing operations. - * @param[in] context_id Specifies a unique ctx id to prevent states being - * used in contexts for which they weren't intended. + * @param[in] config the configuration data * @return * - A new state tree. * - NULL on failure. */ -fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, bool thread_safe, - uint32_t max_sessions, fr_time_delta_t timeout, - uint8_t server_id, uint32_t context_id) +fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_state_config_t const *config) { fr_state_tree_t *state; state = talloc_zero(NULL, fr_state_tree_t); if (!state) return 0; - state->max_sessions = max_sessions; - state->timeout = timeout; + state->config = *config; + state->da = da; /* Remember which attribute we use to load/store state */ /* * Create a break in the contexts. @@ -238,7 +235,7 @@ fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, b */ talloc_link_ctx(ctx, state); - if (thread_safe && (pthread_mutex_init(&state->mutex, NULL) != 0)) { + if (state->config.thread_safe && (pthread_mutex_init(&state->mutex, NULL) != 0)) { talloc_free(state); return NULL; } @@ -258,11 +255,6 @@ fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, b } talloc_set_destructor(state, _state_tree_free); - state->da = da; /* Remember which attribute we use to load/store state */ - state->server_id = server_id; - state->context_id = context_id; - state->thread_safe = thread_safe; - return state; } @@ -378,7 +370,7 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r state->timed_out += timed_out; if (!old) { - too_many = (state->used_sessions == (uint32_t) state->max_sessions); + too_many = (state->used_sessions == state->config.max_sessions); if (!too_many) state->used_sessions++; /* preemptively increment whilst we hold the mutex */ memset(old_state, 0, sizeof(old_state)); } else { @@ -412,7 +404,7 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r */ if (too_many) { RERROR("Failed inserting state entry - At maximum ongoing session limit (%u)", - state->max_sessions); + state->config.max_sessions); PTHREAD_MUTEX_LOCK(&state->mutex); /* Caller expects this to be locked */ return NULL; } @@ -448,7 +440,7 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r * isn't perfect, but it's reasonable, and it's one less * thing for an administrator to configure. */ - entry->cleanup = fr_time_add(now, state->timeout); + entry->cleanup = fr_time_add(now, state->config.timeout); /* * Some modules create their own magic @@ -491,6 +483,13 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r if (old) { memcpy(entry->state, old_state, sizeof(entry->state)); entry->tries = old_tries + 1; + + if (entry->tries > state->config.max_rounds) { + RERROR("Failed tracking state entry - too many rounds (%u)", entry->tries); + goto fail; + } + + /* * 16 octets of randomness should be enough to * have a globally unique state. @@ -519,7 +518,7 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r * Allow a portion of the State attribute to be set, * this is useful for debugging purposes. */ - entry->state_comp.server_id = state->server_id; + entry->state_comp.server_id = state->config.server_id; MEM(vp = fr_pair_afrom_da(request->reply_ctx, state->da)); fr_pair_value_memdup(vp, entry->state, sizeof(entry->state), false); @@ -538,10 +537,11 @@ static fr_state_entry_t *state_entry_create(fr_state_tree_t *state, request_t *r * only succeed in the virtual server that created the state * value. */ - *((uint32_t *)(&entry->state_comp.context_id)) ^= state->context_id; + *((uint32_t *)(&entry->state_comp.context_id)) ^= state->config.context_id; if (!fr_rb_insert(state->tree, entry)) { RERROR("Failed inserting state entry - Insertion into state tree failed"); + fail: fr_pair_delete_by_da(reply_list, state->da); talloc_free(entry); return NULL; @@ -588,7 +588,7 @@ static fr_state_entry_t *state_entry_find_and_unlink(fr_state_tree_t *state, fr_ /* * Make it unique for different virtual servers handling the same request */ - my_entry.state_comp.context_id ^= state->context_id; + my_entry.state_comp.context_id ^= state->config.context_id; entry = fr_rb_remove(state->tree, &my_entry); if (entry) { diff --git a/src/lib/server/state.h b/src/lib/server/state.h index 367b70ec104..95d54edebf1 100644 --- a/src/lib/server/state.h +++ b/src/lib/server/state.h @@ -32,12 +32,22 @@ extern "C" { #include #include +#include typedef struct fr_state_tree_s fr_state_tree_t; -fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, bool thread_safe, - uint32_t max_sessions, fr_time_delta_t timeout, - uint8_t server_id, uint32_t context_id); +typedef struct { + uint32_t max_sessions; //!< maximum number of sessions + uint32_t max_rounds; //!< maximum number of rounds before we give up + uint32_t context_id; //!< internal number to help keep state trees separate + fr_time_delta_t timeout; //!< idle timeout + uint8_t server_id; //!< for mangling State + bool thread_safe; +} fr_state_config_t; + +extern const conf_parser_t state_session_config[]; + +fr_state_tree_t *fr_state_tree_init(TALLOC_CTX *ctx, fr_dict_attr_t const *da, fr_state_config_t const *config); void fr_state_discard(fr_state_tree_t *state, request_t *request); diff --git a/src/process/radius/base.c b/src/process/radius/base.c index b99a1033270..0570e719f7f 100644 --- a/src/process/radius/base.c +++ b/src/process/radius/base.c @@ -133,13 +133,7 @@ typedef struct { } process_radius_sections_t; typedef struct { - fr_time_delta_t session_timeout;//!< Maximum time between the last response and next request. - uint32_t max_session; //!< Maximum ongoing session allowed. - - uint8_t state_server_id;//!< Sets a specific byte in the state to allow the - //!< authenticating server to be identified in packet - //! -static const conf_parser_t session_config[] = { - { FR_CONF_OFFSET("timeout", process_radius_auth_t, session_timeout), .dflt = "15" }, - { FR_CONF_OFFSET("max", process_radius_auth_t, max_session), .dflt = "4096" }, - { FR_CONF_OFFSET("state_server_id", process_radius_auth_t, state_server_id) }, - - CONF_PARSER_TERMINATOR -}; - static const conf_parser_t auth_config[] = { - { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) session_config }, + { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) state_session_config }, CONF_PARSER_TERMINATOR }; @@ -877,9 +863,13 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf)); - inst->auth.state_tree = fr_state_tree_init(inst, attr_state, main_config->spawn_workers, inst->auth.max_session, - inst->auth.session_timeout, inst->auth.state_server_id, - fr_hash_string(cf_section_name2(inst->server_cs))); + FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, >=, 32); + FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, <=, 100); + + inst->auth.session.thread_safe = main_config->spawn_workers; + inst->auth.session.context_id = fr_hash_string(cf_section_name2(inst->server_cs)); + + inst->auth.state_tree = fr_state_tree_init(inst, attr_state, &inst->auth.session); return 0; } diff --git a/src/process/tacacs/base.c b/src/process/tacacs/base.c index 949d289fd6d..a28dffef5c7 100644 --- a/src/process/tacacs/base.c +++ b/src/process/tacacs/base.c @@ -161,16 +161,8 @@ typedef struct { } process_tacacs_sections_t; typedef struct { - fr_time_delta_t session_timeout; //!< Maximum time between the last response and next request. - uint32_t max_session; //!< Maximum ongoing session allowed. - - uint32_t max_rounds; //!< maximum number of authentication rounds allowed - - uint8_t state_server_id; //!< Sets a specific byte in the state to allow the - //!< authenticating server to be identified in packet - //! -static const conf_parser_t session_config[] = { - { FR_CONF_OFFSET("timeout", process_tacacs_auth_t, session_timeout), .dflt = "15" }, - { FR_CONF_OFFSET("max", process_tacacs_auth_t, max_session), .dflt = "4096" }, - { FR_CONF_OFFSET("max_rounds", process_tacacs_auth_t, max_rounds), .dflt = "4" }, - { FR_CONF_OFFSET("state_server_id", process_tacacs_auth_t, state_server_id) }, - - CONF_PARSER_TERMINATOR -}; - static const conf_parser_t auth_config[] = { - { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) session_config }, + { FR_CONF_POINTER("session", 0, CONF_FLAG_SUBSECTION, NULL), .subcs = (void const *) state_session_config }, CONF_PARSER_TERMINATOR }; @@ -723,13 +705,6 @@ RESUME(auth_get) REXDENT(); } else { - session->rounds++; - - if (session->rounds > inst->auth.max_rounds) { - REDEBUG("Too many rounds of authentication - failing the session"); - return CALL_SEND_TYPE(FR_TACACS_CODE_AUTH_FAIL); - } - /* * It is possible that the user name or password are added on subsequent Authentication-Continue * packets following replies with Authentication-GetUser or Authentication-GetPass. @@ -1071,15 +1046,16 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf)); - FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, >=, 1); - FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.max_rounds, <=, 8); + FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, >=, 1); + FR_INTEGER_BOUND_CHECK("session.max_rounds", inst->auth.session.max_rounds, <=, 8); + + FR_INTEGER_BOUND_CHECK("session.max", inst->auth.session.max_sessions, >=, 64); + FR_INTEGER_BOUND_CHECK("session.max", inst->auth.session.max_sessions, <=, (1 << 18)); - FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, >=, 64); - FR_INTEGER_BOUND_CHECK("session.max", inst->auth.max_session, <=, (1 << 18)); + inst->auth.session.thread_safe = main_config->spawn_workers; + inst->auth.session.context_id = fr_hash_string(cf_section_name2(inst->server_cs)); - inst->auth.state_tree = fr_state_tree_init(inst, attr_tacacs_state, main_config->spawn_workers, inst->auth.max_session, - inst->auth.session_timeout, inst->auth.state_server_id, - fr_hash_string(cf_section_name2(inst->server_cs))); + inst->auth.state_tree = fr_state_tree_init(inst, attr_tacacs_state, &inst->auth.session); return 0; } diff --git a/src/process/ttls/base.c b/src/process/ttls/base.c index a2a619f903c..95ab527838e 100644 --- a/src/process/ttls/base.c +++ b/src/process/ttls/base.c @@ -124,16 +124,7 @@ typedef struct { } process_ttls_sections_t; typedef struct { - fr_time_delta_t timeout; //!< Maximum time between the last response and next request. - uint32_t max; //!< Maximum ongoing session allowed. - - uint8_t state_server_id; //!< Sets a specific byte in the state to allow the - //!< authenticating server to be identified in packet - //! -static const conf_parser_t session_config[] = { - { FR_CONF_OFFSET("timeout", process_ttls_session_t, timeout), .dflt = "15" }, - { FR_CONF_OFFSET("max", process_ttls_session_t, max), .dflt = "4096" }, - { FR_CONF_OFFSET("state_server_id", process_ttls_session_t, state_server_id) }, - - CONF_PARSER_TERMINATOR -}; - static const conf_parser_t auth_config[] = { - { FR_CONF_OFFSET_SUBSECTION("session", 0, process_ttls_auth_t, session, session_config )}, + { FR_CONF_OFFSET_SUBSECTION("session", 0, process_ttls_auth_t, session, state_session_config )}, CONF_PARSER_TERMINATOR }; @@ -510,9 +493,10 @@ static int mod_instantiate(module_inst_ctx_t const *mctx) inst->server_cs = cf_item_to_section(cf_parent(mctx->mi->conf)); - inst->auth.state_tree = fr_state_tree_init(inst, attr_state, main_config->spawn_workers, inst->auth.session.max, - inst->auth.session.timeout, inst->auth.session.state_server_id, - fr_hash_string(cf_section_name2(inst->server_cs))); + inst->auth.session.thread_safe = main_config->spawn_workers; + inst->auth.session.context_id = fr_hash_string(cf_section_name2(inst->server_cs)); + + inst->auth.state_tree = fr_state_tree_init(inst, attr_state, &inst->auth.session); return 0; }