"Fossies" - the Fresh Open Source Software Archive

Member "freeradius-server-3.0.23/src/modules/rlm_eap/mem.c" (10 Jun 2021, 13034 Bytes) of package /linux/misc/freeradius-server-3.0.23.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*
    2  * mem.c  Memory allocation, deallocation stuff.
    3  *
    4  * Version:     $Id: 6be8ca4af78f039850e5eb24bc572b9dc512b288 $
    5  *
    6  *   This program is free software; you can redistribute it and/or modify
    7  *   it under the terms of the GNU General Public License as published by
    8  *   the Free Software Foundation; either version 2 of the License, or
    9  *   (at your option) any later version.
   10  *
   11  *   This program is distributed in the hope that it will be useful,
   12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14  *   GNU General Public License for more details.
   15  *
   16  *   You should have received a copy of the GNU General Public License
   17  *   along with this program; if not, write to the Free Software
   18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   19  *
   20  * Copyright 2000,2001,2006  The FreeRADIUS server project
   21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
   22  */
   23 
   24 RCSID("$Id: 6be8ca4af78f039850e5eb24bc572b9dc512b288 $")
   25 
   26 #include <stdio.h>
   27 #include "rlm_eap.h"
   28 
   29 #ifdef WITH_TLS
   30 #include <freeradius-devel/tls.h>
   31 #endif
   32 
   33 #ifdef HAVE_PTHREAD_H
   34 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
   35 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
   36 #else
   37 #define PTHREAD_MUTEX_LOCK(_x)
   38 #define PTHREAD_MUTEX_UNLOCK(_x)
   39 #endif
   40 
   41 /*
   42  * Allocate a new eap_packet_t
   43  */
   44 EAP_DS *eap_ds_alloc(eap_handler_t *handler)
   45 {
   46     EAP_DS  *eap_ds;
   47 
   48     eap_ds = talloc_zero(handler, EAP_DS);
   49     eap_ds->response = talloc_zero(eap_ds, eap_packet_t);
   50     if (!eap_ds->response) {
   51         eap_ds_free(&eap_ds);
   52         return NULL;
   53     }
   54     eap_ds->request = talloc_zero(eap_ds, eap_packet_t);
   55     if (!eap_ds->response) {
   56         eap_ds_free(&eap_ds);
   57         return NULL;
   58     }
   59 
   60     return eap_ds;
   61 }
   62 
   63 void eap_ds_free(EAP_DS **eap_ds_p)
   64 {
   65     EAP_DS *eap_ds;
   66 
   67     if (!eap_ds_p) return;
   68 
   69     eap_ds = *eap_ds_p;
   70     if (!eap_ds) return;
   71 
   72     if (eap_ds->response) talloc_free(eap_ds->response);
   73     if (eap_ds->request) talloc_free(eap_ds->request);
   74 
   75     talloc_free(eap_ds);
   76     *eap_ds_p = NULL;
   77 }
   78 
   79 static int _eap_handler_free(eap_handler_t *handler)
   80 {
   81     if (handler->identity) {
   82         talloc_free(handler->identity);
   83         handler->identity = NULL;
   84     }
   85 
   86     if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
   87     if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
   88 
   89     if ((handler->opaque) && (handler->free_opaque)) {
   90         handler->free_opaque(handler->opaque);
   91         handler->opaque = NULL;
   92     }
   93 
   94     handler->opaque = NULL;
   95     handler->free_opaque = NULL;
   96 
   97     /*
   98      *  Give helpful debug messages if:
   99      *
  100      *  we're debugging TLS sessions, which don't finish,
  101      *  and which aren't deleted early due to a likely RADIUS
  102      *  retransmit which nukes our ID, and therefore our stare.
  103      */
  104     if (fr_debug_lvl && handler->tls && !handler->finished &&
  105         (time(NULL) > (handler->timestamp + 3))) {
  106         WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  107         WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!                  !!",
  108              handler->state[0], handler->state[1],
  109              handler->state[2], handler->state[3],
  110              handler->state[4], handler->state[5],
  111              handler->state[6], handler->state[7],
  112              handler->state[8], handler->state[9],
  113              handler->state[10], handler->state[11],
  114              handler->state[12], handler->state[13],
  115              handler->state[14], handler->state[15]);
  116 
  117         WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
  118         WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
  119     }
  120     
  121     return 0;
  122 }
  123 
  124 /*
  125  * Allocate a new eap_handler_t
  126  */
  127 eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
  128 {
  129     eap_handler_t   *handler;
  130 
  131     handler = talloc_zero(NULL, eap_handler_t);
  132     if (!handler) {
  133         ERROR("Failed allocating handler");
  134         return NULL;
  135     }
  136     handler->inst_holder = inst;
  137 
  138     /* Doesn't need to be inside the critical region */
  139     talloc_set_destructor(handler, _eap_handler_free);
  140 
  141     return handler;
  142 }
  143 
  144 
  145 void eaplist_free(rlm_eap_t *inst)
  146 {
  147     eap_handler_t *node, *next;
  148 
  149     for (node = inst->session_head; node != NULL; node = next) {
  150         next = node->next;
  151         talloc_free(node);
  152     }
  153 
  154     inst->session_head = inst->session_tail = NULL;
  155 }
  156 
  157 /*
  158  *  Return a 32-bit random number.
  159  */
  160 static uint32_t eap_rand(fr_randctx *ctx)
  161 {
  162     uint32_t num;
  163 
  164     num = ctx->randrsl[ctx->randcnt++];
  165     if (ctx->randcnt >= 256) {
  166         ctx->randcnt = 0;
  167         fr_isaac(ctx);
  168     }
  169 
  170     return num;
  171 }
  172 
  173 
  174 static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
  175                    eap_handler_t *handler)
  176 {
  177     rbnode_t *node;
  178 
  179     node = rbtree_find(inst->session_tree, handler);
  180     if (!node) return NULL;
  181 
  182     handler = rbtree_node2data(inst->session_tree, node);
  183 
  184     RDEBUG("Finished EAP session with state "
  185            "0x%02x%02x%02x%02x%02x%02x%02x%02x",
  186            handler->state[0], handler->state[1],
  187            handler->state[2], handler->state[3],
  188            handler->state[4], handler->state[5],
  189            handler->state[6], handler->state[7]);
  190     /*
  191      *  Delete old handler from the tree.
  192      */
  193     rbtree_delete(inst->session_tree, node);
  194 
  195     /*
  196      *  And unsplice it from the linked list.
  197      */
  198     if (handler->prev) {
  199         handler->prev->next = handler->next;
  200     } else {
  201         inst->session_head = handler->next;
  202     }
  203     if (handler->next) {
  204         handler->next->prev = handler->prev;
  205     } else {
  206         inst->session_tail = handler->prev;
  207     }
  208     handler->prev = handler->next = NULL;
  209 
  210     return handler;
  211 }
  212 
  213 
  214 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
  215 {
  216     int i;
  217     eap_handler_t *handler;
  218 
  219     /*
  220      *  Check the first few handlers in the list, and delete
  221      *  them if they're too old.  We don't need to check them
  222      *  all, as incoming requests will quickly cause older
  223      *  handlers to be deleted.
  224      *
  225      */
  226     for (i = 0; i < 3; i++) {
  227         handler = inst->session_head;
  228         if (!handler) break;
  229 
  230         RDEBUG("Expiring EAP session with state "
  231                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
  232                handler->state[0], handler->state[1],
  233                handler->state[2], handler->state[3],
  234                handler->state[4], handler->state[5],
  235                handler->state[6], handler->state[7]);
  236 
  237         /*
  238          *  Expire entries from the start of the list.
  239          *  They should be the oldest ones.
  240          */
  241         if ((timestamp - handler->timestamp) > (int)inst->timer_limit) {
  242             rbnode_t *node;
  243             node = rbtree_find(inst->session_tree, handler);
  244             rad_assert(node != NULL);
  245             rbtree_delete(inst->session_tree, node);
  246 
  247             /*
  248              *  handler == inst->session_head
  249              */
  250             inst->session_head = handler->next;
  251             if (handler->next) {
  252                 handler->next->prev = NULL;
  253             } else {
  254                 inst->session_head = NULL;
  255                 inst->session_tail = NULL;
  256             }
  257 
  258 #ifdef WITH_TLS
  259             /*
  260              *  Remove expired TLS sessions.
  261              */
  262             switch (handler->type) {
  263             case PW_EAP_TLS:
  264             case PW_EAP_TTLS:
  265             case PW_EAP_PEAP:
  266             case PW_EAP_FAST:
  267                 tls_fail(handler->opaque); /* MUST be a tls_session! */
  268                 break;
  269 
  270             default:
  271                 break;
  272             }
  273 #endif
  274 
  275             talloc_free(handler);
  276         } else {
  277             break;
  278         }
  279     }
  280 }
  281 
  282 /*
  283  *  Add a handler to the set of active sessions.
  284  *
  285  *  Since we're adding it to the list, we guess that this means
  286  *  the packet needs a State attribute.  So add one.
  287  */
  288 int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
  289 {
  290     int     status = 0;
  291     VALUE_PAIR  *state;
  292     REQUEST     *request = handler->request;
  293 
  294     /*
  295      *  Generate State, since we've been asked to add it to
  296      *  the list.
  297      */
  298     state = pair_make_reply("State", NULL, T_OP_EQ);
  299     if (!state) return 0;
  300 
  301     /*
  302      *  The time at which this request was made was the time
  303      *  at which it was received by the RADIUS server.
  304      */
  305     handler->timestamp = request->timestamp;
  306     handler->status = 1;
  307 
  308     handler->src_ipaddr = request->packet->src_ipaddr;
  309     handler->eap_id = handler->eap_ds->request->id;
  310 
  311     /*
  312      *  Playing with a data structure shared among threads
  313      *  means that we need a lock, to avoid conflict.
  314      */
  315     PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
  316 
  317     /*
  318      *  If we have a DoS attack, discard new sessions.
  319      */
  320     if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
  321         status = -1;
  322         eaplist_expire(inst, request, handler->timestamp);
  323         goto done;
  324     }
  325 
  326     /*
  327      *  Create a unique content for the State variable.
  328      *  It will be modified slightly per round trip, but less so
  329      *  than in 1.x.
  330      */
  331     if (handler->trips == 0) {
  332         int i;
  333 
  334         for (i = 0; i < 4; i++) {
  335             uint32_t lvalue;
  336 
  337             lvalue = eap_rand(&inst->rand_pool);
  338 
  339             memcpy(handler->state + i * 4, &lvalue,
  340                    sizeof(lvalue));
  341         }
  342     }
  343 
  344     /*
  345      *  Add some more data to distinguish the sessions.
  346      */
  347     handler->state[4] = handler->trips ^ handler->state[0];
  348     handler->state[5] = handler->eap_id ^ handler->state[1];
  349     handler->state[6] = handler->type ^ handler->state[2];
  350     handler->state[12] = handler->state[2] ^ (RADIUSD_VERSION & 0xff);
  351 
  352     fr_pair_value_memcpy(state, handler->state, sizeof(handler->state));
  353 
  354     /*
  355      *  Big-time failure.
  356      */
  357     status = rbtree_insert(inst->session_tree, handler);
  358 
  359     if (status) {
  360         eap_handler_t *prev;
  361 
  362         prev = inst->session_tail;
  363         if (prev) {
  364             prev->next = handler;
  365             handler->prev = prev;
  366             handler->next = NULL;
  367             inst->session_tail = handler;
  368         } else {
  369             inst->session_head = inst->session_tail = handler;
  370             handler->next = handler->prev = NULL;
  371         }
  372     }
  373 
  374     /*
  375      *  Now that we've finished mucking with the list,
  376      *  unlock it.
  377      */
  378  done:
  379 
  380     /*
  381      *  We don't need this any more.
  382      */
  383     if (status > 0) handler->request = NULL;
  384 
  385     PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
  386 
  387     if (status <= 0) {
  388         fr_pair_delete_by_num(&request->reply->vps, PW_STATE, 0, TAG_ANY);
  389 
  390         if (status < 0) {
  391             static time_t last_logged = 0;
  392 
  393             if (last_logged < handler->timestamp) {
  394                 last_logged = handler->timestamp;
  395                 ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" "
  396                       "in the EAP module configuration", inst->xlat_name);
  397             }
  398         } else {
  399             ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name);
  400         }
  401         return 0;
  402     }
  403 
  404     RDEBUG("EAP session adding &reply:State = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
  405            state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3],
  406            state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]);
  407 
  408     return 1;
  409 }
  410 
  411 /*
  412  *  Find a a previous EAP-Request sent by us, which matches
  413  *  the current EAP-Response.
  414  *
  415  *  Then, release the handle from the list, and return it to
  416  *  the caller.
  417  *
  418  *  Also since we fill the eap_ds with the present EAP-Response we
  419  *  got to free the prev_eapds & move the eap_ds to prev_eapds
  420  */
  421 eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request,
  422               eap_packet_raw_t *eap_packet)
  423 {
  424     VALUE_PAIR  *state;
  425     eap_handler_t   *handler, myHandler;
  426 
  427     /*
  428      *  We key the sessions off of the 'state' attribute, so it
  429      *  must exist.
  430      */
  431     state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
  432     if (!state) {
  433         REDEBUG("EAP requires the State attribute to work, but no State exists in the Access-Request packet.");
  434         REDEBUG("The RADIUS client is broken.  No amount of changing FreeRADIUS will fix the RADIUS client.");
  435         return NULL;
  436     }
  437 
  438     if (state->vp_length != EAP_STATE_LEN) {
  439         REDEBUG("The RADIUS client has mangled the State attribute, OR you are forcing EAP in the wrong situation");
  440         return NULL;
  441     }
  442 
  443     myHandler.src_ipaddr = request->packet->src_ipaddr;
  444     myHandler.eap_id = eap_packet->id;
  445     memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
  446 
  447     /*
  448      *  Playing with a data structure shared among threads
  449      *  means that we need a lock, to avoid conflict.
  450      */
  451     PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
  452 
  453     eaplist_expire(inst, request, request->timestamp);
  454 
  455     handler = eaplist_delete(inst, request, &myHandler);
  456     PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
  457 
  458     /*
  459      *  Might not have been there.
  460      */
  461     if (!handler) {
  462         RERROR("rlm_eap (%s): No EAP session matching state "
  463                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
  464                inst->xlat_name,
  465                state->vp_octets[0], state->vp_octets[1],
  466                state->vp_octets[2], state->vp_octets[3],
  467                state->vp_octets[4], state->vp_octets[5],
  468                state->vp_octets[6], state->vp_octets[7]);
  469         return NULL;
  470     }
  471 
  472     if (handler->trips >= 50) {
  473         RERROR("rlm_eap (%s): Aborting! More than 50 roundtrips "
  474                "made in session with state "
  475                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
  476                inst->xlat_name,
  477                state->vp_octets[0], state->vp_octets[1],
  478                state->vp_octets[2], state->vp_octets[3],
  479                state->vp_octets[4], state->vp_octets[5],
  480                state->vp_octets[6], state->vp_octets[7]);
  481 
  482 
  483         talloc_free(handler);
  484         return NULL;
  485     }
  486     handler->trips++;
  487 
  488     RDEBUG("Previous EAP request found for state "
  489            "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
  490         state->vp_octets[0], state->vp_octets[1],
  491         state->vp_octets[2], state->vp_octets[3],
  492         state->vp_octets[4], state->vp_octets[5],
  493         state->vp_octets[6], state->vp_octets[7]);
  494 
  495     /*
  496      *  Remember what the previous request was.
  497      */
  498     eap_ds_free(&(handler->prev_eapds));
  499     handler->prev_eapds = handler->eap_ds;
  500     handler->eap_ds = NULL;
  501 
  502     return handler;
  503 }