#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
*
*/
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.
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);
{
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);
*
* @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.
*/
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;
}
}
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;
}
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 {
*/
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;
}
* 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
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.
* 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);
* 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;
/*
* 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) {
#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);
} 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;
#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
};
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;
}
} 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 {
} 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
#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
};
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.
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;
}
} 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;
#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
};
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;
}