X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=src%2Fmodules%2Frlm_eap%2Fmem.c;h=56bf35c04021f3b51e75b5396696842c70c1e0be;hb=33862cbe5c186cf3133ccdc79bcab840bfd7192a;hp=b49cd763906214d7070f075cf33469348effa01d;hpb=1c4fbb8d7d5beb5319193cbbc73b79b91caf065d;p=freeradius.git diff --git a/src/modules/rlm_eap/mem.c b/src/modules/rlm_eap/mem.c index b49cd76..56bf35c 100644 --- a/src/modules/rlm_eap/mem.c +++ b/src/modules/rlm_eap/mem.c @@ -27,6 +27,14 @@ RCSID("$Id$") #include #include "rlm_eap.h" +#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 */ @@ -52,10 +60,13 @@ void eap_packet_free(EAP_PACKET **eap_packet_ptr) if (eap_packet->type.data) { /* - * This is just a pointer in the packet - * so we do not free it but we NULL it - free(eap_packet->type.data); - */ + * There's no packet, OR the type data isn't + * pointing inside of the packet: free it. + */ + if ((eap_packet->packet == NULL) || + (eap_packet->type.data != (eap_packet->packet + 5))) { + free(eap_packet->type.data); + } eap_packet->type.data = NULL; } @@ -109,20 +120,33 @@ void eap_ds_free(EAP_DS **eap_ds_p) /* * Allocate a new EAP_HANDLER */ -EAP_HANDLER *eap_handler_alloc(void) +EAP_HANDLER *eap_handler_alloc(rlm_eap_t *inst) { EAP_HANDLER *handler; handler = rad_malloc(sizeof(EAP_HANDLER)); memset(handler, 0, sizeof(EAP_HANDLER)); + + if (fr_debug_flag && inst->handler_tree) { + PTHREAD_MUTEX_LOCK(&(inst->handler_mutex)); + rbtree_insert(inst->handler_tree, handler); + PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex)); + + } return handler; } -void eap_handler_free(EAP_HANDLER *handler) +void eap_handler_free(rlm_eap_t *inst, EAP_HANDLER *handler) { if (!handler) return; + if (inst->handler_tree) { + PTHREAD_MUTEX_LOCK(&(inst->handler_mutex)); + rbtree_deletebydata(inst->handler_tree, handler); + PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex)); + } + if (handler->identity) { free(handler->identity); handler->identity = NULL; @@ -141,14 +165,72 @@ void eap_handler_free(EAP_HANDLER *handler) handler->opaque = NULL; handler->free_opaque = NULL; + if (handler->certs) pairfree(&handler->certs); + free(handler); } + +typedef struct check_handler_t { + rlm_eap_t *inst; + EAP_HANDLER *handler; + int trips; +} check_handler_t; + +static void check_handler(void *data) +{ + int do_warning = FALSE; + uint8_t state[8]; + check_handler_t *check = data; + + if (!check) return; + + if (!check->inst || !check->handler) { + free(check); + return; + } + + PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex)); + if (!rbtree_finddata(check->inst->handler_tree, check->handler)) { + goto done; + } + + /* + * The session has continued *after* this packet. + * Don't do a warning. + */ + if (check->handler->trips > check->trips) { + goto done; + } + + if (check->handler->tls && !check->handler->finished) { + do_warning = TRUE; + memcpy(state, check->handler->state, sizeof(state)); + } + +done: + PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex)); + free(check); + + if (do_warning) { + DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + DEBUG("WARNING: !! EAP session for state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!", + state[0], state[1], + state[2], state[3], + state[4], state[5], + state[6], state[7]); + + DEBUG("WARNING: !! Please read http://wiki.freeradius.org/Certificate_Compatibility"); + DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } +} + void eaptype_free(EAP_TYPES *i) { if (i->type->detach) (i->type->detach)(i->type_data); i->type_data = NULL; if (i->handle) lt_dlclose(i->handle); + free(i); } @@ -158,13 +240,104 @@ void eaplist_free(rlm_eap_t *inst) for (node = inst->session_head; node != NULL; node = next) { next = node->next; - eap_handler_free(node); + eap_handler_free(inst, 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 *eaplist_delete(rlm_eap_t *inst, EAP_HANDLER *handler) +{ + rbnode_t *node; + + node = rbtree_find(inst->session_tree, handler); + if (!node) return NULL; + + handler = rbtree_node2data(inst->session_tree, node); + + /* + * 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, time_t timestamp) +{ + int i; + EAP_HANDLER *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; + + /* + * Expire entries from the start of the list. + * They should be the oldest ones. + */ + if ((timestamp - handler->timestamp) > 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; + } + eap_handler_free(inst, handler); + } + } +} + +/* * Add a handler to the set of active sessions. * * Since we're adding it to the list, we guess that this means @@ -172,52 +345,95 @@ void eaplist_free(rlm_eap_t *inst) */ int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler) { - int status; + int status = 0; VALUE_PAIR *state; - + REQUEST *request = handler->request; + rad_assert(handler != NULL); - rad_assert(handler->request != NULL); + rad_assert(request != NULL); /* * Generate State, since we've been asked to add it to * the list. */ - state = generate_state(handler->request->timestamp); - pairadd(&(handler->request->reply->vps), state); - - /* - * Create a unique 'key' for the handler, based - * on State, Client-IP-Address, and EAP ID. - */ - rad_assert(state->length == EAP_STATE_LEN); + state = pairmake("State", "0x00", 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 = handler->request->timestamp; + handler->timestamp = request->timestamp; handler->status = 1; - memcpy(handler->state, state->vp_strvalue, sizeof(handler->state)); - handler->src_ipaddr = handler->request->packet->src_ipaddr; + handler->src_ipaddr = request->packet->src_ipaddr; handler->eap_id = handler->eap_ds->request->id; /* - * We don't need this any more. + * Playing with a data structure shared among threads + * means that we need a lock, to avoid conflict. */ - handler->request = NULL; + PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); /* - * Playing with a data structure shared among threads - * means that we need a lock, to avoid conflict. + * If we have a DoS attack, discard new sessions. + */ + if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) { + status = -1; + eaplist_expire(inst, 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)); + } + } + + memcpy(state->vp_octets, handler->state, sizeof(handler->state)); + state->length = EAP_STATE_LEN; + + /* + * Add some more data to distinguish the sessions. + */ + state->vp_octets[4] = handler->trips ^ handler->state[0]; + state->vp_octets[5] = handler->eap_id ^ handler->state[1]; + state->vp_octets[6] = handler->eap_type ^ handler->state[2]; + + /* + * and copy the state back again. */ - pthread_mutex_lock(&(inst->session_mutex)); + memcpy(handler->state, state->vp_octets, sizeof(handler->state)); /* * Big-time failure. */ status = rbtree_insert(inst->session_tree, handler); + /* + * Catch Access-Challenge without response. + */ + if (fr_debug_flag) { + check_handler_t *check = rad_malloc(sizeof(*check)); + + check->inst = inst; + check->handler = handler; + check->trips = handler->trips; + request_data_add(request, inst, 0, check, check_handler); + } + if (status) { EAP_HANDLER *prev; @@ -237,14 +453,33 @@ int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler) * Now that we've finished mucking with the list, * unlock it. */ - pthread_mutex_unlock(&(inst->session_mutex)); + done: + + /* + * We don't need this any more. + */ + if (status > 0) handler->request = NULL; + + PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); + + if (status <= 0) { + pairfree(&state); - if (!status) { - radlog(L_ERR, "rlm_eap: Failed to remember handler!"); - eap_handler_free(handler); + if (status < 0) { + static time_t last_logged = 0; + + if (last_logged < handler->timestamp) { + last_logged = handler->timestamp; + radlog(L_ERR, "rlm_eap: Too many open sessions. Try increasing \"max_sessions\" in the EAP module configuration"); + } + } else { + radlog(L_ERR, "rlm_eap: Internal error: failed to store handler"); + } return 0; } + pairadd(&(request->reply->vps), state); + return 1; } @@ -261,16 +496,14 @@ int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler) EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, eap_packet_t *eap_packet) { - int i; VALUE_PAIR *state; - rbnode_t *node; EAP_HANDLER *handler, myHandler; /* * We key the sessions off of the 'state' attribute, so it * must exist. */ - state = pairfind(request->packet->vps, PW_STATE); + state = pairfind(request->packet->vps, PW_STATE, 0); if (!state || (state->length != EAP_STATE_LEN)) { return NULL; @@ -284,88 +517,29 @@ EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, * Playing with a data structure shared among threads * means that we need a lock, to avoid conflict. */ - pthread_mutex_lock(&(inst->session_mutex)); - - /* - * 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 < 2; i++) { - handler = inst->session_head; - if (handler && - ((request->timestamp - handler->timestamp) > inst->timer_limit)) { - node = rbtree_find(inst->session_tree, handler); - rad_assert(node != NULL); - rbtree_delete(inst->session_tree, node); - - inst->session_head = handler->next; - if (handler->next) handler->next->prev = NULL; - eap_handler_free(handler); - } - } - - handler = NULL; - node = rbtree_find(inst->session_tree, &myHandler); - if (node) { - handler = rbtree_node2data(inst->session_tree, node); - - /* - * Check against replays. The client can re-play - * a State attribute verbatim, so we wish to - * ensure that the attribute falls within the - * valid time window, which is the second at - * which it was sent out. - * - * Hmm... I'm not sure that this step is - * necessary, or even that it does anything. - */ - if (verify_state(state, handler->timestamp) != 0) { - handler = NULL; - } else { - /* - * It's OK, delete it from the tree. - */ - rbtree_delete(inst->session_tree, node); + PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); - /* - * 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; - } - } + eaplist_expire(inst, request->timestamp); - pthread_mutex_unlock(&(inst->session_mutex)); + handler = eaplist_delete(inst, &myHandler); + PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); /* - * Not found. + * Might not have been there. */ - if (!node) { - DEBUG2(" rlm_eap: Request not found in the list"); + if (!handler) { + radlog(L_ERR, "rlm_eap: No EAP session matching the State variable."); return NULL; } - /* - * Found, but state verification failed. - */ - if (!handler) { - radlog(L_ERR, "rlm_eap: State verification failed."); + if (handler->trips >= 50) { + RDEBUG2("More than 50 authentication packets for this EAP session. Aborted."); + eap_handler_free(inst, handler); return NULL; } + handler->trips++; - DEBUG2(" rlm_eap: Request found, released from the list"); + RDEBUG2("Request found, released from the list"); /* * Remember what the previous request was. @@ -373,6 +547,6 @@ EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, eap_ds_free(&(handler->prev_eapds)); handler->prev_eapds = handler->eap_ds; handler->eap_ds = NULL; - + return handler; }