"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/main/state.c" between
freeradius-server-3.0.22.tar.bz2 and freeradius-server-3.0.23.tar.bz2

About: FreeRADIUS Server Project - a high performance and highly configurable RADIUS server.

state.c  (freeradius-server-3.0.22.tar.bz2):state.c  (freeradius-server-3.0.23.tar.bz2)
skipping to change at line 18 skipping to change at line 18
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/ */
/** /**
* $Id: 58b3f49c8e3b56a10af06a4bd6b2b3651d04884f $ * $Id: 2cd1dee07751decc167816f4c09a1da02bd8da19 $
* *
* @brief Multi-packet state handling * @brief Multi-packet state handling
* @file main/state.c * @file main/state.c
* *
* @ingroup AVP * @ingroup AVP
* *
* @copyright 2014 The FreeRADIUS server project * @copyright 2014 The FreeRADIUS server project
*/ */
RCSID("$Id: 58b3f49c8e3b56a10af06a4bd6b2b3651d04884f $") RCSID("$Id: 2cd1dee07751decc167816f4c09a1da02bd8da19 $")
#include <freeradius-devel/radiusd.h> #include <freeradius-devel/radiusd.h>
#include <freeradius-devel/state.h> #include <freeradius-devel/state.h>
#include <freeradius-devel/md5.h> #include <freeradius-devel/md5.h>
#include <freeradius-devel/rad_assert.h> #include <freeradius-devel/rad_assert.h>
#include <freeradius-devel/process.h>
typedef struct state_entry_t { typedef struct state_entry_t {
uint8_t state[AUTH_VECTOR_LEN]; uint8_t state[AUTH_VECTOR_LEN];
time_t cleanup; time_t cleanup;
struct state_entry_t *prev; struct state_entry_t *prev;
struct state_entry_t *next; struct state_entry_t *next;
int tries; int tries;
TALLOC_CTX *ctx; TALLOC_CTX *ctx;
VALUE_PAIR *vps; VALUE_PAIR *vps;
char *server;
unsigned int request_number;
RADCLIENT *request_client;
main_config_t *request_root;
void *opaque; void *opaque;
void (*free_opaque)(void *opaque); void (*free_opaque)(void *opaque);
} state_entry_t; } state_entry_t;
struct fr_state_t { struct fr_state_t {
rbtree_t *tree; rbtree_t *tree;
state_entry_t *head, *tail; state_entry_t *head, *tail;
#ifdef HAVE_PTHREAD_H #ifdef HAVE_PTHREAD_H
skipping to change at line 87 skipping to change at line 93
* rbtree callback. * rbtree callback.
*/ */
static int state_entry_cmp(void const *one, void const *two) static int state_entry_cmp(void const *one, void const *two)
{ {
state_entry_t const *a = one; state_entry_t const *a = one;
state_entry_t const *b = two; state_entry_t const *b = two;
return memcmp(a->state, b->state, sizeof(a->state)); return memcmp(a->state, b->state, sizeof(a->state));
} }
/* static bool state_entry_link(fr_state_t *state, state_entry_t *entry)
* When an entry is free'd, it's removed from the linked list of
* cleanup times.
*
* Note that
*/
static void state_entry_free(fr_state_t *state, state_entry_t *entry)
{ {
state_entry_t *prev, *next; if (!rbtree_insert(state->tree, entry)) {
return false;
}
/* /*
* If we're deleting the whole tree, don't bother doing * Link it to the end of the list, which is implicitely
* all of the fixups. * ordered by cleanup time.
*/ */
if (!state || !state->tree) return; if (!state->head) {
entry->prev = entry->next = NULL;
state->head = state->tail = entry;
} else {
rad_assert(state->tail != NULL);
entry->prev = state->tail;
state->tail->next = entry;
entry->next = NULL;
state->tail = entry;
}
return true;
}
static void state_entry_unlink(fr_state_t *state, state_entry_t *entry)
{
state_entry_t *prev, *next;
prev = entry->prev; prev = entry->prev;
next = entry->next; next = entry->next;
if (prev) { if (prev) {
rad_assert(state->head != entry); rad_assert(state->head != entry);
prev->next = next; prev->next = next;
} else if (state->head) { } else if (state->head) {
rad_assert(state->head == entry); rad_assert(state->head == entry);
state->head = next; state->head = next;
} }
if (next) { if (next) {
rad_assert(state->tail != entry); rad_assert(state->tail != entry);
next->prev = prev; next->prev = prev;
} else if (state->tail) { } else if (state->tail) {
rad_assert(state->tail == entry); rad_assert(state->tail == entry);
state->tail = prev; state->tail = prev;
} }
rbtree_deletebydata(state->tree, entry);
}
/*
* When an entry is free'd, it's removed from the linked list of
* cleanup timers.
*/
static void state_entry_free(fr_state_t *state, state_entry_t *entry)
{
/*
* If we're deleting the whole tree, don't bother doing
* all of the fixups.
*/
if (!state || !state->tree) return;
state_entry_unlink(state, entry);
if (entry->opaque) { if (entry->opaque) {
entry->free_opaque(entry->opaque); entry->free_opaque(entry->opaque);
} }
#ifdef WITH_VERIFY_PTR #ifdef WITH_VERIFY_PTR
(void) talloc_get_type_abort(entry, state_entry_t); (void) talloc_get_type_abort(entry, state_entry_t);
#endif #endif
rbtree_deletebydata(state->tree, entry);
if (entry->ctx) talloc_free(entry->ctx); if (entry->ctx) talloc_free(entry->ctx);
talloc_free(entry); talloc_free(entry);
} }
fr_state_t *fr_state_init(TALLOC_CTX *ctx) fr_state_t *fr_state_init(TALLOC_CTX *ctx)
{ {
fr_state_t *state; fr_state_t *state;
if (!ctx) { if (!ctx) {
skipping to change at line 186 skipping to change at line 221
my_tree = state->tree; my_tree = state->tree;
state->tree = NULL; state->tree = NULL;
rbtree_free(my_tree); rbtree_free(my_tree);
PTHREAD_MUTEX_UNLOCK(&state->mutex); PTHREAD_MUTEX_UNLOCK(&state->mutex);
if (state != &global_state) talloc_free(state); if (state != &global_state) talloc_free(state);
} }
/* /*
* Create a new entry. Called with the mutex held. * Create a fake request, based on what we know about the
* session that has expired, and inject it into the server to
* allow final logging or cleaning up.
*/
static REQUEST *fr_state_cleanup_request(state_entry_t *entry)
{
REQUEST *request;
RADIUS_PACKET *packet = NULL;
RADIUS_PACKET *reply_packet = NULL;
VALUE_PAIR *vp;
/*
* Allocate a new fake request with enough to keep
* the rest of the server happy.
*/
request = request_alloc(NULL);
if (unlikely(!request)) return NULL;
packet = rad_alloc(request, false);
if (unlikely(!packet)) {
error:
TALLOC_FREE(reply_packet);
TALLOC_FREE(packet);
TALLOC_FREE(request);
return NULL;
}
reply_packet = rad_alloc(request, false);
if (unlikely(!reply_packet)) goto error;
/*
* Move the server from the state entry over to the
* request. Clearing it in the state means this
* function will never be called again.
*/
request->server = talloc_steal(request, entry->server);
entry->server = NULL;
/*
* Build the fake request with the limited
* information we have from the state.
*/
request->packet = packet;
request->reply = reply_packet;
request->number = entry->request_number;
request->client = entry->request_client;
request->root = entry->request_root;
request->handle = rad_postauth;
/*
* Move session-state VPS over
*/
request->state_ctx = entry->ctx;
request->state = entry->vps;
entry->ctx = NULL;
entry->vps = NULL;
/*
* Set correct Post-Auth-Type section
*/
fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
vp = pair_make_config("Post-Auth-Type", "Client-Lost", T_OP_SET);
if (unlikely(!vp)) goto error;
VERIFY_REQUEST(request);
return request;
}
/*
* Check state for old entries that need to be cleaned up. If
* they are old enough then move them from the global state
* list to a list of entries to clean up (outside the mutex).
* Called with the mutex held.
*/ */
static state_entry_t *fr_state_create(fr_state_t *state, const char *server, RAD IUS_PACKET *packet, state_entry_t *old) static state_entry_t *fr_state_cleanup_find(fr_state_t *state)
{ {
size_t i;
uint32_t x;
time_t now = time(NULL); time_t now = time(NULL);
VALUE_PAIR *vp;
state_entry_t *entry, *next; state_entry_t *entry, *next;
state_entry_t *head = NULL, **tail = &head;
/*
* Clean up old entries.
*/
for (entry = state->head; entry != NULL; entry = next) { for (entry = state->head; entry != NULL; entry = next) {
next = entry->next; next = entry->next;
if (entry == old) continue;
/* /*
* Too old, we can delete it. * Unused. We can delete it, even if now isn't
* the time to clean it up.
*/ */
if (entry->cleanup < now) { if (!entry->ctx && !entry->opaque) {
state_entry_free(state, entry); state_entry_free(state, entry);
continue; continue;
} }
/* /*
* Unused. We can delete it, even if now isn't * Old enough that the request has been removed.
* the time to clean it up. * We can add it to the cleanup list.
*/ */
if (!entry->ctx && !entry->opaque) { if (entry->cleanup < now) {
state_entry_free(state, entry); (*tail) = entry;
continue; state_entry_unlink(state, entry);
tail = &entry->next;
}
}
return head;
}
/*
* Inject all requests in cleanup list for cleanup post-auth
*/
static void fr_state_cleanup(state_entry_t *head)
{
state_entry_t *entry, *next;
if (!head) return;
for (entry = head; entry != NULL; entry = next) {
next = entry->next;
if (main_config.postauth_client_lost) {
REQUEST *request;
request = fr_state_cleanup_request(entry);
if (request) {
RDEBUG2("No response from client, cleaning up exp
ired state");
RDEBUG2("Restoring &session-state");
/*
* @todo - print out message
* saying where the handler was
* in the process? i.e. "sent
* server cert", etc. This will
* require updating the EAP code
* to put a new attribute into
* the session state list.
*/
rdebug_pair_list(L_DBG_LVL_2, request, request->s
tate, "&session-state:");
request_inject(request);
}
} }
break; talloc_free(entry);
} }
}
/*
* Create a new entry. Called with the mutex held.
*/
static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
RADIUS_PACKET *packet, state_entry_t *old)
{
size_t i;
uint32_t x;
time_t now = time(NULL);
VALUE_PAIR *vp;
state_entry_t *entry;
/* /*
* Limit the size of the cache based on how many requests * Limit the size of the cache based on how many requests
* we can handle at the same time. * we can handle at the same time.
*/ */
if (rbtree_num_elements(state->tree) >= main_config.max_requests * 2) { if (rbtree_num_elements(state->tree) >= main_config.max_requests * 2) {
return NULL; return NULL;
} }
/* /*
skipping to change at line 244 skipping to change at line 399
*/ */
entry = talloc_zero(state->tree, state_entry_t); entry = talloc_zero(state->tree, state_entry_t);
if (!entry) return NULL; if (!entry) return NULL;
/* /*
* Limit the lifetime of this entry based on how long the * Limit the lifetime of this entry based on how long the
* server takes to process a request. Doing it this way * server takes to process a request. Doing it this way
* isn't perfect, but it's reasonable, and it's one less * isn't perfect, but it's reasonable, and it's one less
* thing for an administrator to configure. * thing for an administrator to configure.
*/ */
entry->cleanup = now + main_config.max_request_time * 10; entry->cleanup = now + main_config.max_request_time * 2;
/* /*
* Hacks for EAP, until we convert EAP to using the state API. * Hacks for EAP, until we convert EAP to using the state API.
* *
* The EAP module creates it's own State attribute, so we * The EAP module creates it's own State attribute, so we
* want to use that one in preference to one we create. * want to use that one in preference to one we create.
*/ */
vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY); vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY);
/* /*
skipping to change at line 322 skipping to change at line 477
memset(&entry->state[vp->vp_length], 0, sizeof(entry->sta te) - vp->vp_length); memset(&entry->state[vp->vp_length], 0, sizeof(entry->sta te) - vp->vp_length);
} }
} else { } else {
vp = fr_pair_afrom_num(packet, PW_STATE, 0); vp = fr_pair_afrom_num(packet, PW_STATE, 0);
fr_pair_value_memcpy(vp, entry->state, sizeof(entry->state)); fr_pair_value_memcpy(vp, entry->state, sizeof(entry->state));
fr_pair_add(&packet->vps, vp); fr_pair_add(&packet->vps, vp);
} }
/* Make unique for different virtual servers handling same request /* Make unique for different virtual servers handling same request
*/ */
if (server) *((uint32_t *)(&entry->state[4])) ^= fr_hash_string(server); if (request->server) {
/*
* Make unique for different virtual servers handling same r
equest
*/
*((uint32_t *)(&entry->state[4])) ^= fr_hash_string(request->serv
er);
if (!rbtree_insert(state->tree, entry)) { /*
talloc_free(entry); * Copy server to state in case it's needed for cleanup
return NULL; */
entry->server = talloc_strdup(entry, request->server);
entry->request_number = request->number;
entry->request_client = request->client;
entry->request_root = request->root;
} }
/* if (!state_entry_link(state, entry)) {
* Link it to the end of the list, which is implicitely talloc_free(entry);
* ordered by cleanup time. return NULL;
*/
if (!state->head) {
entry->prev = entry->next = NULL;
state->head = state->tail = entry;
} else {
rad_assert(state->tail != NULL);
entry->prev = state->tail;
state->tail->next = entry;
entry->next = NULL;
state->tail = entry;
} }
return entry; return entry;
} }
/* /*
* Find the entry, based on the State attribute. * Find the entry, based on the State attribute.
*/ */
static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIU S_PACKET *packet) static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIU S_PACKET *packet)
{ {
skipping to change at line 396 skipping to change at line 547
entry = rbtree_finddata(state->tree, &my_entry); entry = rbtree_finddata(state->tree, &my_entry);
#ifdef WITH_VERIFY_PTR #ifdef WITH_VERIFY_PTR
if (entry) (void) talloc_get_type_abort(entry, state_entry_t); if (entry) (void) talloc_get_type_abort(entry, state_entry_t);
#endif #endif
return entry; return entry;
} }
/* /*
* Called when sending Access-Reject, so that all State is * Called when sending Access-Accept or Access-Reject, so
* discarded. * that all State is discarded.
*/ */
void fr_state_discard(REQUEST *request, RADIUS_PACKET *original) void fr_state_discard(REQUEST *request, RADIUS_PACKET *original)
{ {
state_entry_t *entry; state_entry_t *entry;
fr_state_t *state = &global_state; fr_state_t *state = &global_state;
fr_pair_list_free(&request->state); fr_pair_list_free(&request->state);
request->state = NULL; request->state = NULL;
PTHREAD_MUTEX_LOCK(&state->mutex); PTHREAD_MUTEX_LOCK(&state->mutex);
skipping to change at line 428 skipping to change at line 579
/* /*
* Get the VPS from the state. * Get the VPS from the state.
*/ */
void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet) void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
{ {
state_entry_t *entry; state_entry_t *entry;
fr_state_t *state = &global_state; fr_state_t *state = &global_state;
TALLOC_CTX *old_ctx = NULL; TALLOC_CTX *old_ctx = NULL;
rad_assert(request->state == NULL);
/* /*
* No State, don't do anything. * No State, don't do anything.
*/ */
if (!fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY)) { if (!fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
RDEBUG3("session-state: No State attribute"); RDEBUG3("session-state: No State attribute");
return; return;
} }
rad_assert(request->state == NULL);
PTHREAD_MUTEX_LOCK(&state->mutex); PTHREAD_MUTEX_LOCK(&state->mutex);
entry = fr_state_find(state, request->server, packet); entry = fr_state_find(state, request->server, packet);
/* /*
* This has to be done in a mutex lock, because talloc * This has to be done in a mutex lock, because talloc
* isn't thread-safe. * isn't thread-safe.
*/ */
if (entry) { if (entry) {
RDEBUG2("Restoring &session-state"); RDEBUG2("Restoring &session-state");
skipping to change at line 482 skipping to change at line 633
/* /*
* Put request->state into the State attribute. Put the State * Put request->state into the State attribute. Put the State
* attribute into the vps list. Delete the original entry, if it * attribute into the vps list. Delete the original entry, if it
* exists. * exists.
*/ */
bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET * packet) bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET * packet)
{ {
state_entry_t *entry, *old; state_entry_t *entry, *old;
fr_state_t *state = &global_state; fr_state_t *state = &global_state;
state_entry_t *cleanup_list;
if (!request->state) { if (!request->state) {
size_t i; size_t i;
uint32_t x; uint32_t x;
VALUE_PAIR *vp; VALUE_PAIR *vp;
uint8_t buffer[16]; uint8_t buffer[16];
RDEBUG3("session-state: Nothing to cache"); RDEBUG3("session-state: Nothing to cache");
if (packet->code != PW_CODE_ACCESS_CHALLENGE) return true; if (packet->code != PW_CODE_ACCESS_CHALLENGE) return true;
skipping to change at line 516 skipping to change at line 668
fr_pair_add(&packet->vps, vp); fr_pair_add(&packet->vps, vp);
return true; return true;
} }
RDEBUG2("session-state: Saving cached attributes"); RDEBUG2("session-state: Saving cached attributes");
rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL); rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL);
PTHREAD_MUTEX_LOCK(&state->mutex); PTHREAD_MUTEX_LOCK(&state->mutex);
cleanup_list = fr_state_cleanup_find(state);
if (original) { if (original) {
old = fr_state_find(state, request->server, original); old = fr_state_find(state, request->server, original);
} else { } else {
old = NULL; old = NULL;
} }
entry = fr_state_create(state, request->server, packet, old); /*
* Create a new entry and add it to the list.
*/
entry = fr_state_entry_create(state, request, packet, old);
if (!entry) { if (!entry) {
PTHREAD_MUTEX_UNLOCK(&state->mutex); PTHREAD_MUTEX_UNLOCK(&state->mutex);
fr_state_cleanup(cleanup_list);
return false; return false;
} }
rad_assert(entry->ctx == NULL); rad_assert(entry->ctx == NULL);
entry->ctx = request->state_ctx; entry->ctx = request->state_ctx;
entry->vps = request->state; entry->vps = request->state;
request->state_ctx = NULL; request->state_ctx = NULL;
request->state = NULL; request->state = NULL;
PTHREAD_MUTEX_UNLOCK(&state->mutex); PTHREAD_MUTEX_UNLOCK(&state->mutex);
fr_state_cleanup(cleanup_list);
VERIFY_REQUEST(request); VERIFY_REQUEST(request);
return true; return true;
} }
/*
* Find the opaque data associated with a State attribute.
* Leave the data in the entry.
*/
void *fr_state_find_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *pac
ket)
{
void *data;
state_entry_t *entry;
if (!state) return false;
PTHREAD_MUTEX_LOCK(&state->mutex);
entry = fr_state_find(state, request->server, packet);
if (!entry) {
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return NULL;
}
data = entry->opaque;
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return data;
}
/*
* Get the opaque data associated with a State attribute.
* and remove the data from the entry.
*/
void *fr_state_get_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *pack
et)
{
void *data;
state_entry_t *entry;
if (!state) return NULL;
PTHREAD_MUTEX_LOCK(&state->mutex);
entry = fr_state_find(state, request->server, packet);
if (!entry) {
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return NULL;
}
data = entry->opaque;
entry->opaque = NULL;
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return data;
}
/*
* Get the opaque data associated with a State attribute.
* and remove the data from the entry.
*/
bool fr_state_put_data(fr_state_t *state, REQUEST *request, RADIUS_PACKET *origi
nal, RADIUS_PACKET *packet,
void *data, void (*free_data)(void *))
{
state_entry_t *entry, *old;
if (!state) return false;
PTHREAD_MUTEX_LOCK(&state->mutex);
if (original) {
old = fr_state_find(state, request->server, original);
} else {
old = NULL;
}
entry = fr_state_create(state, request->server, packet, old);
if (!entry) {
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return false;
}
/*
* If we're moving the data, ensure that we delete it
* from the old state.
*/
if (old && (old->opaque == data)) {
old->opaque = NULL;
}
entry->opaque = data;
entry->free_opaque = free_data;
PTHREAD_MUTEX_UNLOCK(&state->mutex);
return true;
}
 End of changes. 36 change blocks. 
58 lines changed or deleted 222 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)