]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
move state data to a config structure
authorAlan T. DeKok <aland@freeradius.org>
Thu, 22 Jan 2026 12:47:44 +0000 (07:47 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 22 Jan 2026 13:57:25 +0000 (08:57 -0500)
src/lib/server/state.c
src/lib/server/state.h
src/process/radius/base.c
src/process/tacacs/base.c
src/process/ttls/base.c

index e7857366cf41e10b8c1ba3baf093a85004440c3a..996e0c783f03e68b12e08b277a9466dc7017cf28 100644 (file)
@@ -56,6 +56,16 @@ RCSID("$Id$")
 #include <freeradius-devel/util/md5.h>
 #include <freeradius-devel/util/rand.h>
 
+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) {
index 367b70ec104f22fa472d67f4d1e2fd1affcd64fb..95d54edebf16e37f5c43f8caa9ad79c9b7a352bd 100644 (file)
@@ -32,12 +32,22 @@ extern "C" {
 
 #include <freeradius-devel/util/dict.h>
 #include <freeradius-devel/server/request.h>
+#include <freeradius-devel/server/cf_parse.h>
 
 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);
 
index b99a103327071b69596ad603b121952ad2338e75..0570e719f7fe5c0880b3acc3857cde95617e5718 100644 (file)
@@ -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
-                                                       //!<captures.
-
+       fr_state_config_t               session;        //!< track state session information.
        fr_state_tree_t                 *state_tree;    //!< State tree to link multiple requests/responses.
 } process_radius_auth_t;
 
@@ -169,16 +163,8 @@ typedef struct {
 #define PROCESS_CODE_DYNAMIC_CLIENT    FR_RADIUS_CODE_ACCESS_ACCEPT
 #include <freeradius-devel/server/process.h>
 
-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;
 }
index 949d289fd6dbbfe0c91cbb495c51e3795cb4857b..a28dffef5c7c09ec594b8556c47f24a5c0979c68 100644 (file)
@@ -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
-                                               //!<captures.
-
-       fr_state_tree_t *state_tree;            //!< State tree to link multiple requests/responses.
+       fr_state_config_t       session;        //!< track state session information.
+       fr_state_tree_t         *state_tree;    //!< State tree to link multiple requests/responses.
 } process_tacacs_auth_t;
 
 typedef struct {
@@ -187,7 +179,6 @@ typedef struct {
 } process_tacacs_t;
 
 typedef struct {
-       uint32_t        rounds;                 //!< how many rounds were taken
        uint32_t        reply;                  //!< for multiround state machine
        uint8_t         seq_no;                 //!< sequence number of last request.
        fr_pair_list_t  list;                   //!< copied from the request
@@ -203,17 +194,8 @@ typedef struct {
 
 #include <freeradius-devel/server/process.h>
 
-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;
 }
 
index a2a619f903c109444f83d5ae966198119d553710..95ab527838e3ba16b9c630caf2c0a29bff257dfc 100644 (file)
@@ -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
-                                               //!<captures.
-} process_ttls_session_t;
-
-typedef struct {
-       process_ttls_session_t          session;        //!< Session settings.
+       fr_state_config_t               session;        //!< Session settings.
 
        fr_state_tree_t                 *state_tree;    //!< State tree to link multiple requests/responses.
 } process_ttls_auth_t;
@@ -152,16 +143,8 @@ typedef struct {
 #define PROCESS_INST                   process_ttls_t
 #include <freeradius-devel/server/process.h>
 
-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;
 }