"Fossies" - the Fresh Open Source Software Archive

Member "freeradius-server-3.0.23/src/modules/rlm_eap/rlm_eap.c" (10 Jun 2021, 21442 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. For more information about "rlm_eap.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.22_vs_3.0.23.

    1 /*
    2  *   This program is is free software; you can redistribute it and/or modify
    3  *   it under the terms of the GNU General Public License as published by
    4  *   the Free Software Foundation; either version 2 of the License, or (at
    5  *   your option) any later version.
    6  *
    7  *   This program is distributed in the hope that it will be useful,
    8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  *   GNU General Public License for more details.
   11  *
   12  *   You should have received a copy of the GNU General Public License
   13  *   along with this program; if not, write to the Free Software
   14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   15  */
   16 
   17 /**
   18  * $Id: 49819d9ce7a86fb61de289c0ee103432e750f76f $
   19  * @file rlm_eap.c
   20  * @brief Implements the EAP framework.
   21  *
   22  * @copyright 2000-2003,2006  The FreeRADIUS server project
   23  * @copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
   24  * @copyright 2003  Alan DeKok <aland@freeradius.org>
   25  */
   26 RCSID("$Id: 49819d9ce7a86fb61de289c0ee103432e750f76f $")
   27 
   28 #include <freeradius-devel/radiusd.h>
   29 #include <freeradius-devel/modules.h>
   30 
   31 #include "rlm_eap.h"
   32 
   33 #include <sys/stat.h>
   34 
   35 static const CONF_PARSER module_config[] = {
   36     { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_t, default_method_name), "md5" },
   37     { "timer_expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, timer_limit), "60" },
   38     { "ignore_unknown_eap_types", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, ignore_unknown_types), "no" },
   39     { "cisco_accounting_username_bug", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, mod_accounting_username_bug), "no" },
   40     { "max_sessions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, max_sessions), "2048" },
   41     CONF_PARSER_TERMINATOR
   42 };
   43 
   44 /*
   45  * delete all the allocated space by eap module
   46  */
   47 static int mod_detach(void *instance)
   48 {
   49     rlm_eap_t *inst;
   50 
   51     inst = (rlm_eap_t *)instance;
   52 
   53 #ifdef HAVE_PTHREAD_H
   54     pthread_mutex_destroy(&(inst->session_mutex));
   55 #endif
   56 
   57     rbtree_free(inst->session_tree);
   58     inst->session_tree = NULL;
   59     eaplist_free(inst);
   60 
   61     return 0;
   62 }
   63 
   64 
   65 /*
   66  *  Compare two handlers.
   67  */
   68 static int eap_handler_cmp(void const *a, void const *b)
   69 {
   70     int rcode;
   71     eap_handler_t const *one = a;
   72     eap_handler_t const *two = b;
   73 
   74     if (one->eap_id < two->eap_id) return -1;
   75     if (one->eap_id > two->eap_id) return +1;
   76 
   77     rcode = memcmp(one->state, two->state, sizeof(one->state));
   78     if (rcode != 0) return rcode;
   79 
   80     /*
   81      *  As of 2.1.8, we don't key off of source IP.  This
   82      *  a NAS to send packets load-balanced (or fail-over)
   83      *  across multiple intermediate proxies, and still have
   84      *  EAP work.
   85      */
   86     if (fr_ipaddr_cmp(&one->src_ipaddr, &two->src_ipaddr) != 0) {
   87         char src1[64], src2[64];
   88 
   89         fr_ntop(src1, sizeof(src1), &one->src_ipaddr);
   90         fr_ntop(src2, sizeof(src2), &two->src_ipaddr);
   91         
   92         RATE_LIMIT(WARN("EAP packets for one session are arriving from two different upstream"
   93                 "servers (%s and %s).  Has there been a proxy fail-over?",
   94                 src1, src2));
   95     }
   96 
   97     return 0;
   98 }
   99 
  100 
  101 /*
  102  * read the config section and load all the eap authentication types present.
  103  */
  104 static int mod_instantiate(CONF_SECTION *cs, void *instance)
  105 {
  106     int     i, ret;
  107     eap_type_t  method;
  108     int     num_methods;
  109     CONF_SECTION    *scs;
  110     rlm_eap_t   *inst = instance;
  111 
  112     /*
  113      *  Create our own random pool.
  114      */
  115     for (i = 0; i < 256; i++) {
  116         inst->rand_pool.randrsl[i] = fr_rand();
  117     }
  118     fr_randinit(&inst->rand_pool, 1);
  119     inst->rand_pool.randcnt = 0;
  120 
  121     inst->xlat_name = cf_section_name2(cs);
  122     if (!inst->xlat_name) inst->xlat_name = "EAP";
  123 
  124     if (!dict_valbyname(PW_AUTH_TYPE, 0, inst->xlat_name)) {
  125         cf_log_err_cs(cs, "Failed to find 'Auth-Type %s' section.  Cannot authenticate users.",
  126                   inst->xlat_name);
  127         return -1;
  128     }
  129 
  130     /* Load all the configured EAP-Types */
  131     num_methods = 0;
  132     for(scs = cf_subsection_find_next(cs, NULL, NULL);
  133         scs != NULL;
  134         scs = cf_subsection_find_next(cs, scs, NULL)) {
  135         char const *name;
  136 
  137         name = cf_section_name1(scs);
  138         if (!name)  continue;
  139 
  140         if (!strcmp(name, TLS_CONFIG_SECTION))  continue;
  141 
  142         /*
  143          *  Don't break configurations for lazy people who still have LEAP enabled.
  144          */
  145         if (!strcmp(name, "leap")) {
  146             WARN("rlm_eap (%s): Ignoring EAP method 'leap', because it is no longer supported",
  147                  inst->xlat_name, name);
  148             continue;
  149         }
  150 
  151         /*
  152          *  Easier sometimes than commenting out blocks,
  153          *  or deleting blocks.
  154          */
  155         if (!strcmp(name, "disable")) continue;
  156 
  157         method = eap_name2type(name);
  158         if (method == PW_EAP_INVALID) {
  159             cf_log_err_cs(cs, "No dictionary definition for EAP method %s", name);
  160             return -1;
  161         }
  162 
  163         if ((method < PW_EAP_MD5) || (method >= PW_EAP_MAX_TYPES)) {
  164             cf_log_err_cs(cs, "Invalid EAP method %s (unsupported)", name);
  165             return -1;
  166         }
  167 
  168 #if !defined(HAVE_OPENSSL_SSL_H) || !defined(HAVE_LIBSSL)
  169         /*
  170          *  This allows the default configuration to be
  171          *  shipped with EAP-TLS, etc. enabled.  If the
  172          *  system doesn't have OpenSSL, they will be
  173          *  ignored.
  174          *
  175          *  If the system does have OpenSSL, then this
  176          *  code will not be used.  The administrator will
  177          *  then have to delete the tls,
  178          *  etc. configurations from eap.conf in order to
  179          *  have EAP without the TLS types.
  180          */
  181         switch (method) {
  182         case PW_EAP_FAST:
  183         case PW_EAP_TLS:
  184         case PW_EAP_TTLS:
  185         case PW_EAP_PEAP:
  186         case PW_EAP_PWD:
  187             WARN("rlm_eap (%s): Ignoring EAP method %s because we don't have OpenSSL support",
  188                  inst->xlat_name, name);
  189             continue;
  190 
  191         default:
  192             break;
  193         }
  194 #endif
  195 
  196         /*
  197          *  Load the type.
  198          */
  199         ret = eap_module_instantiate(inst, &inst->methods[method], method, scs);
  200 
  201         (void) talloc_get_type_abort(inst->methods[method], eap_module_t);
  202 
  203         if (ret < 0) {
  204             (void) talloc_steal(inst, inst->methods[method]);
  205             return -1;
  206         }
  207 
  208         (void) talloc_steal(inst, inst->methods[method]);
  209         num_methods++;  /* successfully loaded one more methods */
  210     }
  211 
  212     if (num_methods == 0) {
  213         cf_log_err_cs(cs, "No EAP method configured, module cannot do anything");
  214         return -1;
  215     }
  216 
  217     /*
  218      *  Ensure that the default EAP type is loaded.
  219      */
  220     method = eap_name2type(inst->default_method_name);
  221     if (method == PW_EAP_INVALID) {
  222         cf_log_err_cs(cs, "No dictionary definition for default EAP method '%s'",
  223                inst->default_method_name);
  224         return -1;
  225     }
  226 
  227     if (!inst->methods[method]) {
  228         cf_log_err_cs(cs, "No such sub-type for default EAP method %s",
  229                inst->default_method_name);
  230         return -1;
  231     }
  232     inst->default_method = method; /* save the numerical method */
  233 
  234     /*
  235      *  List of sessions are set to NULL by the memset
  236      *  of 'inst', above.
  237      */
  238 
  239     /*
  240      *  Lookup sessions in the tree.  We don't free them in
  241      *  the tree, as that's taken care of elsewhere...
  242      */
  243     inst->session_tree = rbtree_create(NULL, eap_handler_cmp, NULL, 0);
  244     if (!inst->session_tree) {
  245         ERROR("rlm_eap (%s): Cannot initialize tree", inst->xlat_name);
  246         return -1;
  247     }
  248     fr_link_talloc_ctx_free(inst, inst->session_tree);
  249 
  250 #ifdef HAVE_PTHREAD_H
  251     if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
  252         ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
  253         return -1;
  254     }
  255 #endif
  256 
  257     return 0;
  258 }
  259 
  260 
  261 /*
  262  *  For backwards compatibility.
  263  */
  264 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
  265 {
  266     rlm_eap_t       *inst;
  267     eap_handler_t       *handler;
  268     eap_packet_raw_t    *eap_packet;
  269     eap_rcode_t     status;
  270     rlm_rcode_t     rcode;
  271 
  272     inst = (rlm_eap_t *) instance;
  273 
  274     if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
  275         REDEBUG("You set 'Auth-Type = %s' for a request that does "
  276             "not contain an EAP-Message attribute!", inst->xlat_name);
  277         return RLM_MODULE_INVALID;
  278     }
  279 
  280     /*
  281      *  Get the eap packet  to start with
  282      */
  283     eap_packet = eap_vp2packet(request, request->packet->vps);
  284     if (!eap_packet) {
  285         RERROR("Malformed EAP Message: %s", fr_strerror());
  286         return RLM_MODULE_FAIL;
  287     }
  288 
  289     /*
  290      *  Create the eap handler.  The eap_packet will end up being
  291      *  "swallowed" into the handler, so we can't access it after
  292      *  this call.
  293      */
  294     handler = eap_handler(inst, &eap_packet, request);
  295     if (!handler) {
  296         RDEBUG2("Failed in handler");
  297         return RLM_MODULE_INVALID;
  298     }
  299 
  300     /*
  301      *  Select the appropriate method or default to the
  302      *  configured one
  303      */
  304     status = eap_method_select(inst, handler);
  305 
  306     /*
  307      *  If it failed, die.
  308      */
  309     if (status == EAP_INVALID) {
  310         eap_fail(handler);
  311         talloc_free(handler);
  312         RDEBUG2("Failed in EAP select");
  313         return RLM_MODULE_INVALID;
  314     }
  315 
  316 #ifdef WITH_PROXY
  317     /*
  318      *  If we're doing horrible tunneling work, remember it.
  319      */
  320     if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
  321         RDEBUG2("No EAP proxy set.  Not composing EAP");
  322         /*
  323          *  Add the handle to the proxied list, so that we
  324          *  can retrieve it in the post-proxy stage, and
  325          *  send a response.
  326          */
  327         handler->inst_holder = inst;
  328         status = request_data_add(request, inst, REQUEST_DATA_EAP_HANDLER, handler, true);
  329 
  330         rad_assert(status == 0);
  331         return RLM_MODULE_HANDLED;
  332     }
  333 #endif
  334 
  335 #ifdef WITH_PROXY
  336     /*
  337      *  Maybe the request was marked to be proxied.  If so,
  338      *  proxy it.
  339      */
  340     if (request->proxy != NULL) {
  341         VALUE_PAIR *vp = NULL;
  342 
  343         rad_assert(!request->proxy_reply);
  344 
  345         /*
  346          *  Add the handle to the proxied list, so that we
  347          *  can retrieve it in the post-proxy stage, and
  348          *  send a response.
  349          */
  350         handler->inst_holder = inst;
  351 
  352         status = request_data_add(request, inst, REQUEST_DATA_EAP_HANDLER, handler, true);
  353 
  354         rad_assert(status == 0);
  355 
  356         /*
  357          *  Some simple sanity checks.  These should really
  358          *  be handled by the radius library...
  359          */
  360         vp = fr_pair_find_by_num(request->proxy->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
  361         if (vp) {
  362             vp = fr_pair_find_by_num(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
  363             if (!vp) {
  364                 fr_pair_make(request->proxy,
  365                      &request->proxy->vps,
  366                      "Message-Authenticator",
  367                      NULL, T_OP_EQ);
  368             }
  369         }
  370 
  371         /*
  372          *  Delete the "proxied to" attribute, as it's
  373          *  set to 127.0.0.1 for tunneled requests, and
  374          *  we don't want to tell the world that...
  375          */
  376         fr_pair_delete_by_num(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
  377 
  378         RWDEBUG2("Tunneled session will be proxied.  Not doing EAP");
  379         return RLM_MODULE_HANDLED;
  380     }
  381 #endif
  382 
  383     /*
  384      *  We are done, wrap the EAP-request in RADIUS to send
  385      *  with all other required radius attributes
  386      */
  387     rcode = eap_compose(handler);
  388 
  389     /*
  390      *  Add to the list only if it is EAP-Request.
  391      */
  392     if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
  393         (handler->eap_ds->request->type.num >= PW_EAP_MD5)) {
  394         /*
  395          *  Return FAIL if we can't remember the handler.
  396          *  This is actually disallowed by the
  397          *  specification, as unexpected FAILs could have
  398          *  been forged.  However, we want to signal to
  399          *  everyone else involved that we are
  400          *  intentionally failing the session, as opposed
  401          *  to accidentally failing it.
  402          */
  403         if (!eaplist_add(inst, handler)) {
  404             RDEBUG("Failed adding handler to the list");
  405             eap_fail(handler);
  406             talloc_free(handler);
  407             return RLM_MODULE_FAIL;
  408         }
  409 
  410     } else {
  411         /*
  412          *  Enable the cached entry on success.
  413          */
  414         if (handler->eap_ds->request->code == PW_EAP_SUCCESS) {
  415             VALUE_PAIR *vp;
  416 
  417             vp = fr_pair_find_by_num(request->state, PW_TLS_CACHE_FILENAME, 0, TAG_ANY);
  418             if (vp) (void) chmod(vp->vp_strvalue, S_IRUSR | S_IWUSR);
  419         }
  420 
  421         /*
  422          *  Disable the cached entry on failure.
  423          */
  424         if (handler->eap_ds->request->code == PW_EAP_FAILURE) {
  425             VALUE_PAIR *vp;
  426 
  427             vp = fr_pair_find_by_num(request->state, PW_TLS_CACHE_FILENAME, 0, TAG_ANY);
  428             if (vp) (void) unlink(vp->vp_strvalue);
  429         }
  430 
  431         RDEBUG2("Freeing handler");
  432         /* handler is not required any more, free it now */
  433         talloc_free(handler);
  434     }
  435 
  436     /*
  437      *  If it's an Access-Accept, RFC 2869, Section 2.3.1
  438      *  says that we MUST include a User-Name attribute in the
  439      *  Access-Accept.
  440      */
  441     if ((request->reply->code == PW_CODE_ACCESS_ACCEPT) &&
  442         request->username) {
  443         VALUE_PAIR *vp;
  444 
  445         /*
  446          *  Doesn't exist, add it in.
  447          */
  448         vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
  449         if (!vp) {
  450             vp = request->username;
  451             if (vp->da->attr != PW_USER_NAME) {
  452                 vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
  453             }
  454             if (vp) {
  455                 vp = fr_pair_copy(request->reply, vp);
  456                 fr_pair_add(&request->reply->vps, vp);
  457             }
  458         }
  459 
  460         /*
  461          *  Cisco AP1230 has a bug and needs a zero
  462          *  terminated string in Access-Accept.  This
  463          *  means it requires 2 trailing zeros.  One to
  464          *  send in the RADIUS packet, and the other to
  465          *  convince the rest of the server that
  466          *  vp->vp_strvalue is still a NUL-terminated C
  467          *  string.
  468          */
  469         if (vp && inst->mod_accounting_username_bug) {
  470             char const *old = vp->vp_strvalue;
  471             char *new;
  472 
  473             vp->vp_length++; /* account for an additional zero */
  474 
  475             new = talloc_array(vp, char, vp->vp_length + 1);
  476 
  477             memcpy(new, old, vp->vp_length);
  478             new[vp->length] = '\0';
  479             vp->vp_strvalue = new;
  480 
  481             rad_const_free(old);
  482             VERIFY_VP(vp);
  483         }
  484     }
  485 
  486     return rcode;
  487 }
  488 
  489 /*
  490  * EAP authorization DEPENDS on other rlm authorizations,
  491  * to check for user existence & get their configured values.
  492  * It Handles EAP-START Messages, User-Name initilization.
  493  */
  494 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
  495 {
  496     rlm_eap_t   *inst;
  497     int     status;
  498     VALUE_PAIR  *vp;
  499 
  500     inst = (rlm_eap_t *)instance;
  501 
  502 #ifdef WITH_PROXY
  503     /*
  504      *  We don't do authorization again, once we've seen the
  505      *  proxy reply (or the proxied packet)
  506      */
  507     if (request->proxy != NULL)
  508         return RLM_MODULE_NOOP;
  509 #endif
  510 
  511     /*
  512      *  For EAP_START, send Access-Challenge with EAP Identity
  513      *  request.  even when we have to proxy this request
  514      *
  515      *  RFC 2869, Section 2.3.1 notes that the "domain" of the
  516      *  user, (i.e. where to proxy him) comes from the EAP-Identity,
  517      *  so we CANNOT proxy the user, until we know his identity.
  518      *
  519      *  We therefore send an EAP Identity request.
  520      */
  521     status = eap_start(inst, request);
  522     switch (status) {
  523     case EAP_NOOP:
  524         return RLM_MODULE_NOOP;
  525     case EAP_FAIL:
  526         return RLM_MODULE_FAIL;
  527     case EAP_FOUND:
  528         return RLM_MODULE_HANDLED;
  529     case EAP_OK:
  530     case EAP_NOTFOUND:
  531     default:
  532         break;
  533     }
  534 
  535     /*
  536      *  RFC 2869, Section 2.3.1.  If a NAS sends an EAP-Identity,
  537      *  it MUST copy the identity into the User-Name attribute.
  538      *
  539      *  But we don't worry about that too much.  We depend on
  540      *  each EAP sub-module to look for handler->request->username,
  541      *  and to get excited if it doesn't appear.
  542      */
  543     vp = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
  544     if ((!vp) || (vp->vp_integer != PW_AUTH_TYPE_REJECT)) {
  545         vp = pair_make_config("Auth-Type", inst->xlat_name, T_OP_EQ);
  546         if (!vp) {
  547             RDEBUG2("Failed to create Auth-Type %s: %s\n",
  548                 inst->xlat_name, fr_strerror());
  549             return RLM_MODULE_FAIL;
  550         }
  551     } else {
  552         RWDEBUG2("Auth-Type already set.  Not setting to EAP");
  553     }
  554 
  555     if (status == EAP_OK) return RLM_MODULE_OK;
  556 
  557     return RLM_MODULE_UPDATED;
  558 }
  559 
  560 
  561 #ifdef WITH_PROXY
  562 /*
  563  *  If we're proxying EAP, then there may be magic we need
  564  *  to do.
  565  */
  566 static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
  567 {
  568     size_t      i;
  569     size_t      len;
  570     ssize_t     ret;
  571     char        *p;
  572     VALUE_PAIR  *vp;
  573     eap_handler_t   *handler;
  574     vp_cursor_t cursor;
  575 
  576     /*
  577      *  If there was a handler associated with this request,
  578      *  then it's a tunneled request which was proxied...
  579      */
  580     handler = request_data_get(request, inst, REQUEST_DATA_EAP_HANDLER);
  581     if (handler != NULL) {
  582         rlm_rcode_t rcode;
  583         eap_tunnel_data_t *data;
  584 
  585         /*
  586          *  Grab the tunnel callbacks from the request.
  587          */
  588         data = (eap_tunnel_data_t *) request_data_get(request,
  589                                   request->proxy,
  590                                   REQUEST_DATA_EAP_TUNNEL_CALLBACK);
  591         if (!data) {
  592             RERROR("Failed to retrieve callback for tunneled session!");
  593             talloc_free(handler);
  594             return RLM_MODULE_FAIL;
  595         }
  596 
  597         /*
  598          *  Do the callback...
  599          */
  600         RDEBUG2("Doing post-proxy callback");
  601         rcode = data->callback(handler, data->tls_session);
  602         talloc_free(data);
  603         if (rcode == 0) {
  604             RDEBUG2("Failed in post-proxy callback");
  605             eap_fail(handler);
  606             talloc_free(handler);
  607             return RLM_MODULE_REJECT;
  608         }
  609 
  610         /*
  611          *  We are done, wrap the EAP-request in RADIUS to send
  612          *  with all other required radius attributes
  613          */
  614         eap_compose(handler);
  615 
  616         /*
  617          *  Add to the list only if it is EAP-Request.
  618          */
  619         if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
  620             (handler->eap_ds->request->type.num >= PW_EAP_MD5)) {
  621             if (!eaplist_add(inst, handler)) {
  622                 eap_fail(handler);
  623                 talloc_free(handler);
  624                 return RLM_MODULE_FAIL;
  625             }
  626 
  627         } else {
  628             RDEBUG2("Freeing handler");
  629             /* handler is not required any more, free it now */
  630             talloc_free(handler);
  631         }
  632 
  633         /*
  634          *  If it's an Access-Accept, RFC 2869, Section 2.3.1
  635          *  says that we MUST include a User-Name attribute in the
  636          *  Access-Accept.
  637          */
  638         if ((request->reply->code == PW_CODE_ACCESS_ACCEPT) &&
  639             request->username) {
  640             /*
  641              *  Doesn't exist, add it in.
  642              */
  643             vp = fr_pair_find_by_num(request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
  644             if (!vp) {
  645                 pair_make_reply("User-Name",
  646                            request->username->vp_strvalue,
  647                            T_OP_EQ);
  648             }
  649         }
  650 
  651         return RLM_MODULE_OK;
  652     } else {
  653         RDEBUG2("No pre-existing handler found");
  654     }
  655 
  656     /*
  657      *  This is allowed.
  658      */
  659     if (!request->proxy_reply) return RLM_MODULE_NOOP;
  660 
  661     /*
  662      *  There may be more than one Cisco-AVPair.
  663      *  Ensure we find the one with the LEAP attribute.
  664      */
  665     fr_cursor_init(&cursor, &request->proxy_reply->vps);
  666     for (;;) {
  667         /*
  668          *  Hmm... there's got to be a better way to
  669          *  discover codes for vendor attributes.
  670          *
  671          *  This is vendor Cisco (9), Cisco-AVPair
  672          *  attribute (1)
  673          */
  674         vp = fr_cursor_next_by_num(&cursor, 1, 9, TAG_ANY);
  675         if (!vp) {
  676             return RLM_MODULE_NOOP;
  677         }
  678 
  679         /*
  680          *  If it's "leap:session-key", then stop.
  681          *
  682          *  The format is VERY specific!
  683          */
  684         if (strncasecmp(vp->vp_strvalue, "leap:session-key=", 17) == 0) {
  685             break;
  686         }
  687     }
  688 
  689     /*
  690      *  The format is very specific.
  691      */
  692     if (vp->vp_length != (17 + 34)) {
  693         RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %zu: Expected %d",
  694                vp->vp_length, 17 + 34);
  695         return RLM_MODULE_NOOP;
  696     }
  697 
  698     /*
  699      *  Decrypt the session key, using the proxy data.
  700      *
  701      *  Note that the session key is *binary*, and therefore
  702      *  may contain embedded zeros.  So we have to use memdup.
  703      *  However, Cisco-AVPair is a "string", so the rest of the
  704      *  code assumes that it's terminated by a trailing '\0'.
  705      *
  706      *  So... be sure to (a) use memdup, and (b) include the last
  707      *  zero byte.
  708      */
  709     i = 34;
  710     p = talloc_memdup(vp, vp->vp_strvalue, vp->vp_length + 1);
  711     talloc_set_type(p, uint8_t);
  712     ret = rad_tunnel_pwdecode((uint8_t *)p + 17, &i, request->home_server->secret, request->proxy->vector);
  713     if (ret < 0) {
  714         REDEBUG("Decoding leap:session-key failed");
  715         talloc_free(p);
  716         return RLM_MODULE_FAIL;
  717     }
  718     len = i;
  719 
  720     if (i != 16) {
  721         REDEBUG("Decoded key length is incorrect, must be 16 bytes");
  722         talloc_free(p);
  723         return RLM_MODULE_FAIL;
  724     }
  725 
  726     /*
  727      *  Encrypt the session key again, using the request data.
  728      */
  729     ret = rad_tunnel_pwencode(p + 17, &len, request->client->secret, request->packet->vector);
  730     if (ret < 0) {
  731         REDEBUG("Decoding leap:session-key failed");
  732         talloc_free(p);
  733         return RLM_MODULE_FAIL;
  734     }
  735 
  736     fr_pair_value_strsteal(vp, p);
  737 
  738     return RLM_MODULE_UPDATED;
  739 }
  740 #endif
  741 
  742 static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
  743 {
  744     rlm_eap_t   *inst = instance;
  745     VALUE_PAIR  *vp;
  746     eap_handler_t   *handler;
  747     eap_packet_raw_t    *eap_packet;
  748 
  749     /*
  750      * Only build a failure message if something previously rejected the request
  751      */
  752     vp = fr_pair_find_by_num(request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
  753 
  754     if (!vp || (vp->vp_integer != PW_POST_AUTH_TYPE_REJECT)) return RLM_MODULE_NOOP;
  755 
  756     if (!fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
  757         RDEBUG3("Request didn't contain an EAP-Message, not inserting EAP-Failure");
  758         return RLM_MODULE_NOOP;
  759     }
  760 
  761     if (fr_pair_find_by_num(request->reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY)) {
  762         RDEBUG3("Reply already contained an EAP-Message, not inserting EAP-Failure");
  763         return RLM_MODULE_NOOP;
  764     }
  765 
  766     eap_packet = eap_vp2packet(request, request->packet->vps);
  767     if (!eap_packet) {
  768         RERROR("Malformed EAP Message: %s", fr_strerror());
  769         return RLM_MODULE_FAIL;
  770     }
  771 
  772     handler = eap_handler(inst, &eap_packet, request);
  773     if (!handler) {
  774         RDEBUG2("Failed to get handler, probably already removed, not inserting EAP-Failure");
  775         return RLM_MODULE_NOOP;
  776     }
  777 
  778     RDEBUG2("Request was previously rejected, inserting EAP-Failure");
  779     eap_fail(handler);
  780     talloc_free(handler);
  781 
  782     /*
  783      * Make sure there's a message authenticator attribute in the response
  784      * RADIUS protocol code will calculate the correct value later...
  785      */
  786     vp = fr_pair_find_by_num(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
  787     if (!vp) {
  788         pair_make_reply("Message-Authenticator", "0x00", T_OP_EQ);
  789     }
  790 
  791     return RLM_MODULE_UPDATED;
  792 }
  793 
  794 /*
  795  *  The module name should be the only globally exported symbol.
  796  *  That is, everything else should be 'static'.
  797  */
  798 extern module_t rlm_eap;
  799 module_t rlm_eap = {
  800     .magic      = RLM_MODULE_INIT,
  801     .name       = "eap",
  802     .inst_size  = sizeof(rlm_eap_t),
  803     .config     = module_config,
  804     .instantiate    = mod_instantiate,
  805     .detach     = mod_detach,
  806     .methods = {
  807         [MOD_AUTHENTICATE]  = mod_authenticate,
  808         [MOD_AUTHORIZE]     = mod_authorize,
  809 #ifdef WITH_PROXY
  810         [MOD_POST_PROXY]    = mod_post_proxy,
  811 #endif
  812         [MOD_POST_AUTH]     = mod_post_auth
  813     },
  814 };