Use local EAP, even if the realm doesn't exist
[freeradius.git] / src / modules / rlm_eap / eap.c
index a6e0d5b..4eb9c04 100644 (file)
@@ -15,9 +15,9 @@
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000-2003  The FreeRADIUS server project
+ * Copyright 2000-2003,2006  The FreeRADIUS server project
  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
  * Copyright 2003  Alan DeKok <aland@freeradius.org>
  */
@@ -33,7 +33,7 @@
  * +-+-+-+-+
  *
  *
- * EAP Request and Response Packet Format 
+ * EAP Request and Response Packet Format
  * --- ------- --- -------- ------ ------
  *  0                   1                   2                   3
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
@@ -44,7 +44,7 @@
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
  *
  *
- * EAP Success and Failure Packet Format 
+ * EAP Success and Failure Packet Format
  * --- ------- --- ------- ------ ------
  *  0                   1                   2                   3
  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  *
  */
 
-#include "rlm_eap.h"
+#include <freeradius-devel/ident.h>
+#include <freeradius-devel/modpriv.h>
+RCSID("$Id$")
 
-static const char rcsid[] = "$Id$";
+#include "rlm_eap.h"
 
 static const char *eap_codes[] = {
   "",                          /* 0 is invalid */
@@ -68,23 +70,17 @@ static const char *eap_codes[] = {
 
 /*
  * Load all the required eap authentication types.
- * Get all the supported EAP-types from config file. 
+ * Get all the supported EAP-types from config file.
  */
 int eaptype_load(EAP_TYPES **type, int eap_type, CONF_SECTION *cs)
 {
        char            buffer[64];
-       lt_dlhandle     handle;
+       char            namebuf[64];
+       const char      *eaptype_name;
        EAP_TYPES       *node;
 
-       snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eaptype_type2name(eap_type));
-
-       /* Link the loaded EAP-Type */
-       handle = lt_dlopenext(buffer);
-       if (handle == NULL) {
-               radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s", 
-                               eaptype_type2name(eap_type), lt_dlerror());
-               return -1;
-       }
+       eaptype_name = eaptype_type2name(eap_type, namebuf, sizeof(namebuf));
+       snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eaptype_name);
 
        /* Make room for the EAP-Type */
        node = (EAP_TYPES *)malloc(sizeof(EAP_TYPES));
@@ -95,30 +91,64 @@ int eaptype_load(EAP_TYPES **type, int eap_type, CONF_SECTION *cs)
        memset(node, 0, sizeof(*node));
 
        /* fill in the structure */
-       node->handle = handle;
        node->cs = cs;
-       node->typename = eaptype_type2name(eap_type);
+
+       /*
+        *      In general, this is a terrible idea.  It works here
+        *      solely because the eap_type2name function returns a
+        *      'static const char *' pointer sometimes, and we can
+        *      ONLY link to module which are named in that static
+        *      array.
+        */
+       node->typename = eaptype_name;
        node->type_data = NULL;
-       
+
+#ifdef WITHOUT_LIBLTDL
+#ifdef WITH_DLOPEN
+#include <dlfcn.h>
+
+#ifdef RTLD_SELF
+       node->type = (EAP_TYPE *)lt_dlsym(RTLD_SELF, buffer);
+       if (node->type) goto open_self;
+#endif
+#endif
+#endif
+
+       /* Link the loaded EAP-Type */
+       node->handle = lt_dlopenext(buffer);
+       if (node->handle == NULL) {
+               free(node);
+               radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s",
+                      eaptype_name, lt_dlerror());
+               return -1;
+       }
+
        node->type = (EAP_TYPE *)lt_dlsym(node->handle, buffer);
        if (!node->type) {
                radlog(L_ERR, "rlm_eap: Failed linking to %s structure in %s: %s",
-                               buffer, eaptype_type2name(eap_type), lt_dlerror());
+                               buffer, eaptype_name, lt_dlerror());
                lt_dlclose(node->handle);       /* ignore any errors */
                free(node);
                return -1;
        }
-       if ((node->type->attach) && 
-               ((node->type->attach)(node->cs, &(node->type_data)) < 0)) {
+
+#if defined(WITHOUT_LIBLTDL) && defined (WITH_DLOPEN) && defined(RTLD_SELF)
+open_self:
+#endif
+       cf_log_module(cs, "Linked to sub-module %s", buffer);
+
+       cf_log_module(cs, "Instantiating eap-%s", eaptype_name);
+
+       if ((node->type->attach) &&
+           ((node->type->attach)(node->cs, &(node->type_data)) < 0)) {
 
                radlog(L_ERR, "rlm_eap: Failed to initialize type %s",
-                      eaptype_type2name(eap_type));
+                      eaptype_name);
                lt_dlclose(node->handle);
                free(node);
                return -1;
        }
 
-       DEBUG("rlm_eap: Loaded and initialized type %s", eaptype_type2name(eap_type));
        *type = node;
        return 0;
 }
@@ -129,8 +159,11 @@ int eaptype_load(EAP_TYPES **type, int eap_type, CONF_SECTION *cs)
 static int eaptype_call(EAP_TYPES *atype, EAP_HANDLER *handler)
 {
        int rcode = 1;
+       REQUEST *request = handler->request;
+       const char *module = request->module;
 
-       DEBUG2("  rlm_eap: processing type %s", atype->typename);
+       RDEBUG2("processing type %s", atype->typename);
+       request->module = atype->typename;
 
        rad_assert(atype != NULL);
 
@@ -160,24 +193,29 @@ static int eaptype_call(EAP_TYPES *atype, EAP_HANDLER *handler)
 
        default:
                /* Should never enter here */
-               radlog(L_DBG, "rlm_eap: Invalid operation on eap_type");
+               RDEBUG("Internal sanity check failed on eap_type");
                rcode = 0;
                break;
        }
 
+       request->module = module;
        return rcode;
 }
 
 /*
  * Based on TYPE, call the appropriate EAP-type handler
- * Default to the configured EAP-Type 
- * for all Unsupported EAP-Types 
+ * Default to the configured EAP-Type
+ * for all Unsupported EAP-Types
  */
 int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
 {
-       int             default_eap_type = inst->default_eap_type;
+       size_t          i;
+       unsigned int    default_eap_type = inst->default_eap_type;
        eaptype_t       *eaptype;
        VALUE_PAIR      *vp;
+       char            namebuf[64];
+       const char      *eaptype_name;
+       REQUEST         *request = handler->request;
 
        eaptype = &handler->eap_ds->response->type;
 
@@ -186,7 +224,15 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
         */
        if ((eaptype->type == 0) ||
            (eaptype->type > PW_EAP_MAX_TYPES)) {
-               DEBUG2(" rlm_eap: Asked to select bad type");
+               RDEBUG2("Asked to select bad type");
+               return EAP_INVALID;
+       }
+
+       /*
+        *      Multiple levels of nesting are invalid.
+        */
+       if (handler->request->parent && handler->request->parent->parent) {
+               RDEBUG2("Multiple levels of TLS nesting is invalid.");
                return EAP_INVALID;
        }
 
@@ -195,15 +241,15 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
         */
        switch(eaptype->type) {
        case PW_EAP_IDENTITY:
-               DEBUG2("  rlm_eap: EAP Identity");
-               
+               RDEBUG2("EAP Identity");
+
                /*
                 *      Allow per-user configuration of EAP types.
                 */
                vp = pairfind(handler->request->config_items,
-                             PW_EAP_TYPE);
-               if (vp) default_eap_type = vp->lvalue;
-               
+                             PW_EAP_TYPE, 0);
+               if (vp) default_eap_type = vp->vp_integer;
+
        do_initiate:
                /*
                 *      Ensure it's valid.
@@ -211,14 +257,15 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
                if ((default_eap_type < PW_EAP_MD5) ||
                    (default_eap_type > PW_EAP_MAX_TYPES) ||
                    (inst->types[default_eap_type] == NULL)) {
-                       DEBUG2(" rlm_eap: No such EAP type %d",
-                              default_eap_type);
+                       RDEBUG2("No such EAP type %s",
+                              eaptype_type2name(default_eap_type,
+                                                namebuf, sizeof(namebuf)));
                        return EAP_INVALID;
                }
-               
+
                handler->stage = INITIATE;
                handler->eap_type = default_eap_type;
-               
+
                /*
                 *      Wild & crazy stuff!  For TTLS & PEAP, we
                 *      initiate a TLS session, and then pass that
@@ -231,31 +278,32 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
                    (default_eap_type == PW_EAP_PEAP)) {
                        default_eap_type = PW_EAP_TLS;
                }
-               
-               
-               /*
-                *      We don't do TLS inside of TLS, as it's a bad
-                *      idea...
-                */
-               if (((handler->request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) &&
-                   (default_eap_type == PW_EAP_TLS)) {
-                       DEBUG2(" rlm_eap: Unable to tunnel TLS inside of TLS");
+
+               if ((default_eap_type == PW_EAP_TNC) &&
+                   !handler->request->parent) {
+                       RDEBUG2("ERROR: EAP-TNC must be run inside of a TLS method.");
                        return EAP_INVALID;
                }
-               
+
                if (eaptype_call(inst->types[default_eap_type],
                                 handler) == 0) {
-                       DEBUG2(" rlm_eap: Default EAP type %s failed in initiate", eaptype_type2name(default_eap_type));
+                       RDEBUG2("Default EAP type %s failed in initiate",
+                              eaptype_type2name(default_eap_type,
+                                                namebuf, sizeof(namebuf)));
                        return EAP_INVALID;
                }
                break;
 
        case PW_EAP_NAK:
                /*
-                *      The one byte of NAK data is the preferred EAP type
-                *      of the client.
+                *      The NAK data is the preferred EAP type(s) of
+                *      the client.
+                *
+                *      RFC 3748 says to list one or more proposed
+                *      alternative types, one per octet, or to use
+                *      0 for no alternative.
                 */
-               DEBUG2("  rlm_eap: EAP NAK");
+               RDEBUG2("EAP NAK");
 
                /*
                 *      Delete old data, if necessary.
@@ -266,47 +314,90 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
                        handler->opaque = NULL;
                }
 
-               /*
-                *      It is invalid to request identity,
-                *      notification & nak in nak
-                */
                if (eaptype->data == NULL) {
-                       DEBUG2(" rlm_eap: Empty NAK packet, cannot decide what EAP type the client wants.");
-                       return EAP_INVALID;
-               }
-
-               if ((eaptype->data[0] < PW_EAP_MD5) ||
-                   (eaptype->data[0] > PW_EAP_MAX_TYPES)) {
-                       DEBUG2(" rlm_eap: NAK asked for bad type %d",
-                              eaptype->data[0]);
+                       RDEBUG2("Empty NAK packet, cannot decide what EAP type the client wants.");
                        return EAP_INVALID;
                }
 
-               default_eap_type = eaptype->data[0];
-               DEBUG2(" rlm_eap: EAP-NAK asked for EAP-Type/%s",
-                      eaptype_type2name(default_eap_type));
-
                /*
-                *      Prevent a firestorm if the client is confused.
+                *      Pick one type out of the one they asked for,
+                *      as they may have asked for many.
                 */
-               if (handler->eap_type == default_eap_type) {
-                       DEBUG2(" rlm_eap: ERROR! Our request for %s was NAK'd with a request for %s, what is the client thinking?",
-                              eaptype_type2name(default_eap_type),
-                              eaptype_type2name(default_eap_type));
-                       return EAP_INVALID;
+               default_eap_type = 0;
+               vp = pairfind(handler->request->config_items,
+                             PW_EAP_TYPE, 0);
+               for (i = 0; i < eaptype->length; i++) {
+                       /*
+                        *      It is invalid to request identity,
+                        *      notification & nak in nak.
+                        *
+                        *      Type 0 is valid, and means there are no
+                        *      common choices.
+                        */
+                       if (eaptype->data[i] < PW_EAP_MD5) {
+                               RDEBUG2("NAK asked for bad type %d",
+                                      eaptype->data[i]);
+                               return EAP_INVALID;
+                       }
+
+                       if ((eaptype->data[i] > PW_EAP_MAX_TYPES) ||
+                           !inst->types[eaptype->data[i]]) {
+                               DICT_VALUE *dv;
+
+                               dv = dict_valbyattr(PW_EAP_TYPE, 0, eaptype->data[i]);
+                               if (dv) {
+                                       RDEBUG2("NAK asked for unsupported type %s",
+                                               dv->name);
+                               } else {
+                                       RDEBUG2("NAK asked for unsupported type %d",
+                                               eaptype->data[i]);
+                               }
+                               continue;
+                       }
+
+                       eaptype_name = eaptype_type2name(eaptype->data[i],
+                                                        namebuf,
+                                                        sizeof(namebuf));
+                       
+                       /*
+                        *      Prevent a firestorm if the client is confused.
+                        */
+                       if (handler->eap_type == eaptype->data[i]) {
+                               RDEBUG2("ERROR! Our request for %s was NAK'd with a request for %s.  Skipping the requested type.",
+                                      eaptype_name, eaptype_name);
+                               continue;
+                       }
+
+                       /*
+                        *      Enforce per-user configuration of EAP
+                        *      types.
+                        */
+                       if (vp && (vp->vp_integer != eaptype->data[i])) {
+                               char    mynamebuf[64];
+                               RDEBUG2("Client wants %s, while we require %s.  Skipping the requested type.",
+                                      eaptype_name,
+                                      eaptype_type2name(vp->vp_integer,
+                                                        mynamebuf,
+                                                        sizeof(mynamebuf)));
+                               continue;
+                       }
+
+                       default_eap_type = eaptype->data[i];
+                       break;
                }
 
                /*
-                *      Enforce per-user configuration of EAP types.
+                *      We probably want to return 'fail' here...
                 */
-               vp = pairfind(handler->request->config_items,
-                             PW_EAP_TYPE);
-               if (vp && (vp->lvalue != default_eap_type)) {
-                       DEBUG2("  rlm_eap: Client wants %s, while we require %s, rejecting the user.",
-                              eaptype_type2name(default_eap_type),
-                              eaptype_type2name(vp->lvalue));
+               if (!default_eap_type) {
+                       RDEBUG2("No common EAP types found.");
                        return EAP_INVALID;
                }
+               eaptype_name = eaptype_type2name(default_eap_type,
+                                                namebuf, sizeof(namebuf));
+               RDEBUG2("EAP-NAK asked for EAP-Type/%s",
+                      eaptype_name);
+
                goto do_initiate;
                break;
 
@@ -314,24 +405,26 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
                 *      Key off of the configured sub-modules.
                 */
                default:
-                       DEBUG2("  rlm_eap: EAP/%s",
-                              eaptype_type2name(eaptype->type));
-                       
+                       eaptype_name = eaptype_type2name(eaptype->type,
+                                                        namebuf,
+                                                        sizeof(namebuf));
+                       RDEBUG2("EAP/%s", eaptype_name);
+
                        /*
                         *      We haven't configured it, it doesn't exit.
                         */
                        if (!inst->types[eaptype->type]) {
-                               DEBUG2(" rlm_eap: EAP type %d is unsupported",
+                               RDEBUG2("EAP type %d is unsupported",
                                       eaptype->type);
                                return EAP_INVALID;
                        }
-                       
+
                        rad_assert(handler->stage == AUTHENTICATE);
                        handler->eap_type = eaptype->type;
                        if (eaptype_call(inst->types[eaptype->type],
                                         handler) == 0) {
-                               DEBUG2(" rlm_eap: Handler failed in EAP/%s",
-                                      eaptype_type2name(eaptype->type));
+                               RDEBUG2("Handler failed in EAP/%s",
+                                      eaptype_name);
                                return EAP_INVALID;
                        }
                break;
@@ -342,64 +435,6 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
 
 
 /*
- *     EAP packet format to be sent over the wire
- *
- *     i.e. code+id+length+data where data = null/type+typedata
- *     based on code.
- */
-static int eap_wireformat(EAP_PACKET *reply)
-{
-       eap_packet_t    *hdr;
-       uint16_t total_length = 0;
-
-       if (reply == NULL) return EAP_INVALID;
-
-       total_length = EAP_HEADER_LEN;
-       if (reply->code < 3) {
-               total_length += 1/*EAPtype*/;
-               if (reply->type.data && reply->type.length > 0) {
-                       total_length += reply->type.length;
-               }
-       }
-
-       reply->packet = (unsigned char *)malloc(total_length);
-       hdr = (eap_packet_t *)reply->packet;
-       if (!hdr) {
-               radlog(L_ERR, "rlm_eap: out of memory");
-               return EAP_INVALID;
-       }
-
-       hdr->code = (reply->code & 0xFF);
-       hdr->id = (reply->id & 0xFF);
-       total_length = htons(total_length);
-       memcpy(hdr->length, &total_length, sizeof(uint16_t));
-
-       /*
-        *      Request and Response packets are special.
-        */
-       if ((reply->code == PW_EAP_REQUEST) ||
-           (reply->code == PW_EAP_RESPONSE)) {
-               hdr->data[0] = (reply->type.type & 0xFF);
-
-               /*
-                * Here since we cannot know the typedata format and length
-                *
-                * Type_data is expected to be wired by each EAP-Type
-                *
-                * Zero length/No typedata is supported as long as
-                * type is defined
-                */
-               if (reply->type.data && reply->type.length > 0) {
-                       memcpy(&hdr->data[1], reply->type.data, reply->type.length);
-                       free(reply->type.data);
-                       reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
-               }
-       }
-
-       return EAP_VALID;
-}
-
-/*
  *     compose EAP reply packet in EAP-Message attr of RADIUS.  If
  *     EAP exceeds 253, frame it in multiple EAP-Message attrs.
  *
@@ -408,11 +443,8 @@ static int eap_wireformat(EAP_PACKET *reply)
  */
 int eap_compose(EAP_HANDLER *handler)
 {
-       uint16_t eap_len, len;
-       VALUE_PAIR *eap_msg;
        VALUE_PAIR *vp;
        eap_packet_t *eap_packet;
-       unsigned char   *ptr;
        REQUEST *request = handler->request;
        EAP_DS *eap_ds = handler->eap_ds;
        EAP_PACKET *reply = eap_ds->request;
@@ -439,16 +471,23 @@ int eap_compose(EAP_HANDLER *handler)
                 *      mentioned restriction.
                 */
                reply->id = handler->eap_ds->response->id;
-               
+
                switch (reply->code) {
                        /*
                         *      The Id is a simple "ack" for success
                         *      and failure.
+                        *
+                        *      RFC 3748 section 4.2 says
+                        *
+                        *      ... The Identifier field MUST match
+                        *      the Identifier field of the Response
+                        *      packet that it is sent in response
+                        *      to.
                         */
                case PW_EAP_SUCCESS:
                case PW_EAP_FAILURE:
                        break;
-                       
+
                        /*
                         *      We've sent a response to their
                         *      request, the Id is incremented.
@@ -457,7 +496,7 @@ int eap_compose(EAP_HANDLER *handler)
                        ++reply->id;
                }
        } else {
-               DEBUG2("  rlm_eap: Underlying EAP-Type set EAP ID to %d",
+               RDEBUG2("Underlying EAP-Type set EAP ID to %d",
                       reply->id);
        }
 
@@ -477,37 +516,20 @@ int eap_compose(EAP_HANDLER *handler)
 
                eap_ds->request->type.type = handler->eap_type;
        }
-         
 
+       /*
+        *      FIXME: We malloc memory for the eap packet, and then
+        *      immediately copy that data into VALUE_PAIRs.  This
+        *      could be done more efficiently...
+        */
        if (eap_wireformat(reply) == EAP_INVALID) {
                return RLM_MODULE_INVALID;
        }
        eap_packet = (eap_packet_t *)reply->packet;
 
-       memcpy(&eap_len, &(eap_packet->length), sizeof(uint16_t));
-       len = eap_len = ntohs(eap_len);
-       ptr = (unsigned char *)eap_packet;
-
-       do {
-               if (eap_len > 253) {
-                       len = 253;
-                       eap_len -= 253;
-               } else {
-                       len = eap_len;
-                       eap_len = 0;
-               }
-
-               /* 
-                * create a value pair & append it to the request reply list
-                * This memory gets freed up when request is freed up
-                */
-               eap_msg = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
-               memcpy(eap_msg->strvalue, ptr, len);
-               eap_msg->length = len;
-               pairadd(&(request->reply->vps), eap_msg);
-               ptr += len;
-               eap_msg = NULL;
-       } while (eap_len);
+       vp = eap_packet2vp(eap_packet);
+       if (!vp) return RLM_MODULE_INVALID;
+       pairadd(&(request->reply->vps), vp);
 
        /*
         *      EAP-Message is always associated with
@@ -516,10 +538,10 @@ int eap_compose(EAP_HANDLER *handler)
         *      Don't add a Message-Authenticator if it's already
         *      there.
         */
-       vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR);
+       vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0);
        if (!vp) {
-               vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
-               memset(vp->strvalue, 0, AUTH_VECTOR_LEN);
+               vp = paircreate(PW_MESSAGE_AUTHENTICATOR, 0, PW_TYPE_OCTETS);
+               memset(vp->vp_octets, 0, AUTH_VECTOR_LEN);
                vp->length = AUTH_VECTOR_LEN;
                pairadd(&(request->reply->vps), vp);
        }
@@ -544,9 +566,19 @@ int eap_compose(EAP_HANDLER *handler)
                rcode = RLM_MODULE_HANDLED;
                break;
        default:
+               /*
+                *      When we're pulling MS-CHAPv2 out of EAP-MS-CHAPv2,
+                *      we do so WITHOUT setting a reply code, as the
+                *      request is being proxied.
+                */
+               if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
+                       return RLM_MODULE_HANDLED;
+               }
+
                /* Should never enter here */
                radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
                request->reply->code = PW_AUTHENTICATION_REJECT;
+               reply->code = PW_EAP_FAILURE;
                rcode = RLM_MODULE_REJECT;
                break;
        }
@@ -560,14 +592,12 @@ int eap_compose(EAP_HANDLER *handler)
  */
 int eap_start(rlm_eap_t *inst, REQUEST *request)
 {
-       VALUE_PAIR *vp;
+       VALUE_PAIR *vp, *proxy;
        VALUE_PAIR *eap_msg;
-       EAP_DS *eap_ds;
-       EAP_HANDLER handler;
 
-       eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE);
+       eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE, 0);
        if (eap_msg == NULL) {
-               DEBUG2("  rlm_eap: No EAP-Message, not doing EAP");
+               RDEBUG2("No EAP-Message, not doing EAP");
                return EAP_NOOP;
        }
 
@@ -575,159 +605,210 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         *      Look for EAP-Type = None (FreeRADIUS specific attribute)
         *      this allows you to NOT do EAP for some users.
         */
-       vp = pairfind(request->packet->vps, PW_EAP_TYPE);
-       if (vp && vp->lvalue == 0) {
-               DEBUG2("  rlm_eap: Found EAP-Message, but EAP-Type = None, so we're not doing EAP.");
+       vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0);
+       if (vp && vp->vp_integer == 0) {
+               RDEBUG2("Found EAP-Message, but EAP-Type = None, so we're not doing EAP.");
                return EAP_NOOP;
        }
 
        /*
         *      http://www.freeradius.org/rfc/rfc2869.html#EAP-Message
         *
-        *      This is handled by rad_recv().
+        *      Checks for Message-Authenticator are handled by rad_recv().
         */
 
        /*
-        *      We're allowed only a few codes.  Request, Response,
-        *      Success, or Failure.
+        *      Check for a Proxy-To-Realm.  Don't get excited over LOCAL
+        *      realms (sigh).
         */
-       if ((eap_msg->strvalue[0] == 0) ||
-           (eap_msg->strvalue[0] > PW_EAP_MAX_CODES)) {
-               DEBUG2("  rlm_eap: Unknown EAP packet");
-       } else {
-               DEBUG2("  rlm_eap: EAP packet type %s id %d length %d",
-                      eap_codes[eap_msg->strvalue[0]],
-                      eap_msg->strvalue[1],
-                      eap_msg->length);
-       }
+       proxy = pairfind(request->config_items, PW_PROXY_TO_REALM, 0);
+       if (proxy) {
+               REALM *realm;
 
-       /*
-        *      If we've been configured to proxy, do nothing.
-        *
-        *      Note that we don't check if the realm is local.
-        *      We figure that anyone bright enough to add
-        *      Proxy-To-Realm is bright enough to NOT do so
-        *      when it's a local realm.
-        */
-       if (pairfind(request->config_items, PW_PROXY_TO_REALM) != NULL) {
-               DEBUG2("  rlm_eap: Request is supposed to be proxied.  Not doing EAP.");
-               return EAP_NOOP;
+               /*
+                *      If it's a LOCAL realm, then we're not proxying
+                *      to it.
+                */
+               realm = realm_find(proxy->vp_strvalue);
+               if (!realm || (realm && (realm->auth_pool == NULL))) {
+                       proxy = NULL;
+               }
        }
 
        /*
-        *      Not a start message.  Don't start anything.
+        *      Check the length before de-referencing the contents.
+        *
+        *      Lengths of zero are required by the RFC for EAP-Start,
+        *      but we've never seen them in practice.
         *
-        *      Later EAP messages are longer than the 'start' message,
-        *      so this function returns 'no start found', so that
-        *      the rest of the EAP code can use the State attribute
-        *      to match this EAP-Message to an ongoing conversation.
+        *      Lengths of two are what we see in practice as
+        *      EAP-Starts.
         */
-       if (eap_msg->length != EAP_START) {
-               DEBUG2("  rlm_eap: No EAP Start, assuming it's an on-going EAP conversation");
+       if ((eap_msg->length == 0) || (eap_msg->length == 2)) {
+               EAP_DS *eap_ds;
+               EAP_HANDLER handler;
+
+               /*
+                *      It's a valid EAP-Start, but the request
+                *      was marked as being proxied.  So we don't
+                *      do EAP, as the home server will do it.
+                */
+               if (proxy) {
+               do_proxy:
+                       RDEBUG2("Request is supposed to be proxied to Realm %s.  Not doing EAP.", proxy->vp_strvalue);
+                       return EAP_NOOP;
+               }
+
+               RDEBUG2("Got EAP_START message");
+               if ((eap_ds = eap_ds_alloc()) == NULL) {
+                       RDEBUG2("EAP Start failed in allocation");
+                       return EAP_FAIL;
+               }
 
                /*
-                *      Add the 'EAP-Type' attribute to the request,
-                *      if it's part of an EAP conversation, and the
-                *      EAP sub-type is in the EAP packet.
+                *      It's an EAP-Start packet.  Tell them to stop wasting
+                *      our time, and give us an EAP-Identity packet.
                 *
-                *      Store the EAP type in the request, so modules
-                *      outside of EAP can check & use it.
+                *      Hmm... we should probably check the contents of the
+                *      EAP-Start packet for something...
                 */
-               if (((eap_msg->strvalue[0] == PW_EAP_REQUEST) ||
-                    (eap_msg->strvalue[0] == PW_EAP_RESPONSE)) &&
-                   (eap_msg->length >= (EAP_HEADER_LEN + 1))) {
-                       /*
-                        *      Create an EAP-Type of the type which
-                        *      was NAK'd, or of the type in the packet.
-                        */
-                       vp = paircreate(PW_EAP_TYPE, PW_TYPE_INTEGER);
-                       if (vp) {
-                               vp->lvalue = eap_msg->strvalue[4];
-                               pairadd(&(request->packet->vps), vp);
-                       }
-                       
-                       /*
-                        *      We've been told to ignore unknown EAP
-                        *      types, AND it's an unknown type.
-                        *      Return "NOOP", which will cause the
-                        *      eap_authorize() to return NOOP.
-                        *
-                        *      EAP-Identity, Notification, and NAK
-                        *      are all handled internally, so they
-                        *      never have handlers.
-                        */
-                       if ((eap_msg->strvalue[4] >= PW_EAP_MD5) &&
-                           inst->ignore_unknown_eap_types &&
-                           ((eap_msg->strvalue[4] == 0) ||
-                            (eap_msg->strvalue[4] > PW_EAP_MAX_TYPES) ||
-                            (inst->types[eap_msg->strvalue[4]] == NULL))) {
-                               DEBUG2("  rlm_eap:  Ignoring Unknown EAP type");
-                               return EAP_NOOP;
-                       }
+               eap_ds->request->code = PW_EAP_REQUEST;
+               eap_ds->request->type.type = PW_EAP_IDENTITY;
 
-                       /*
-                        *      They're NAKing the EAP type we wanted
-                        *      to use, and asking for one which we don't
-                        *      support.
-                        *
-                        *      NAK is code + id + length1 + length + NAK
-                        *             + requested EAP type.
-                        *
-                        *      We know at this point that we can't
-                        *      handle the request.  We could either
-                        *      return an EAP-Fail here, but it's not
-                        *      too critical.
-                        *
-                        *      By returning "noop", we can ensure
-                        *      that authorize() returns NOOP, and
-                        *      another module may choose to proxy
-                        *      the request.
-                        */
-                       if ((eap_msg->strvalue[4] == PW_EAP_NAK) &&
-                           (eap_msg->length >= (EAP_HEADER_LEN + 2)) &&
-                           inst->ignore_unknown_eap_types &&
-                           ((eap_msg->strvalue[5] == 0) ||
-                            (eap_msg->strvalue[5] > PW_EAP_MAX_TYPES) ||
-                            (inst->types[eap_msg->strvalue[5]] == NULL))) {
-                               DEBUG2("  rlm_eap: Ignoring NAK with request for unknown EAP type");
-                               return EAP_NOOP;
-                       }
-               } /* else it's not an EAP-Request or EAP-Response */
                /*
-                *      No EAP-Start found.
+                *      We don't have a handler, but eap_compose needs one,
+                *      (for various reasons), so we fake it out here.
                 */
-               return EAP_NOTFOUND;
+               memset(&handler, 0, sizeof(handler));
+               handler.request = request;
+               handler.eap_ds = eap_ds;
+
+               eap_compose(&handler);
+
+               eap_ds_free(&eap_ds);
+               return EAP_FOUND;
+       } /* end of handling EAP-Start */
+
+       /*
+        *      The EAP packet header is 4 bytes, plus one byte of
+        *      EAP sub-type.  Short packets are discarded, unless
+        *      we're proxying.
+        */
+       if (eap_msg->length < (EAP_HEADER_LEN + 1)) {
+               if (proxy) goto do_proxy;
+
+               RDEBUG2("Ignoring EAP-Message which is too short to be meaningful.");
+               return EAP_FAIL;
        }
 
-       DEBUG2("  rlm_eap: Got EAP_START message");
-       if ((eap_ds = eap_ds_alloc()) == NULL) {
-               DEBUG2("  rlm_eap: EAP Start failed in allocation");
+       /*
+        *      Create an EAP-Type containing the EAP-type
+        *      from the packet.
+        */
+       vp = paircreate(PW_EAP_TYPE, 0, PW_TYPE_INTEGER);
+       if (vp) {
+               vp->vp_integer = eap_msg->vp_octets[4];
+               pairadd(&(request->packet->vps), vp);
+       }
+
+       /*
+        *      If the request was marked to be proxied, do it now.
+        *      This is done after checking for a valid length
+        *      (which may not be good), and after adding the EAP-Type
+        *      attribute.  This lets other modules selectively cancel
+        *      proxying based on EAP-Type.
+        */
+       if (proxy) goto do_proxy;
+
+       /*
+        *      From now on, we're supposed to be handling the
+        *      EAP packet.  We better understand it...
+        */
+
+       /*
+        *      We're allowed only a few codes.  Request, Response,
+        *      Success, or Failure.
+        */
+       if ((eap_msg->vp_octets[0] == 0) ||
+           (eap_msg->vp_octets[0] > PW_EAP_MAX_CODES)) {
+               RDEBUG2("Unknown EAP packet");
+       } else {
+               RDEBUG2("EAP packet type %s id %d length %d",
+                      eap_codes[eap_msg->vp_octets[0]],
+                      eap_msg->vp_octets[1],
+                      eap_msg->length);
+       }
+
+       /*
+        *      We handle request and responses.  The only other defined
+        *      codes are success and fail.  The client SHOULD NOT be
+        *      sending success/fail packets to us, as it doesn't make
+        *      sense.
+        */
+       if ((eap_msg->vp_octets[0] != PW_EAP_REQUEST) &&
+           (eap_msg->vp_octets[0] != PW_EAP_RESPONSE)) {
+               RDEBUG2("Ignoring EAP packet which we don't know how to handle.");
                return EAP_FAIL;
        }
 
        /*
-        *      It's an EAP-Start packet.  Tell them to stop wasting
-        *      our time, and give us an EAP-Identity packet.
+        *      We've been told to ignore unknown EAP types, AND it's
+        *      an unknown type.  Return "NOOP", which will cause the
+        *      eap_authorize() to return NOOP.
         *
-        *      Hmm... we should probably check the contents of the
-        *      EAP-Start packet for something...
+        *      EAP-Identity, Notification, and NAK are all handled
+        *      internally, so they never have handlers.
         */
-       eap_ds->request->code = PW_EAP_REQUEST;
-       eap_ds->request->type.type = PW_EAP_IDENTITY;
+       if ((eap_msg->vp_octets[4] >= PW_EAP_MD5) &&
+           inst->ignore_unknown_eap_types &&
+           ((eap_msg->vp_octets[4] == 0) ||
+            (eap_msg->vp_octets[4] > PW_EAP_MAX_TYPES) ||
+            (inst->types[eap_msg->vp_octets[4]] == NULL))) {
+               RDEBUG2(" Ignoring Unknown EAP type");
+               return EAP_NOOP;
+       }
 
        /*
-        *      We don't have a handler, but eap_compose needs one,
-        *      (for various reasons), so we fake it out here.
+        *      They're NAKing the EAP type we wanted to use, and
+        *      asking for one which we don't support.
+        *
+        *      NAK is code + id + length1 + length + NAK
+        *             + requested EAP type(s).
+        *
+        *      We know at this point that we can't handle the
+        *      request.  We could either return an EAP-Fail here, but
+        *      it's not too critical.
+        *
+        *      By returning "noop", we can ensure that authorize()
+        *      returns NOOP, and another module may choose to proxy
+        *      the request.
         */
-       memset(&handler, 0, sizeof(handler));
-       handler.request = request;
-       handler.eap_ds = eap_ds;
+       if ((eap_msg->vp_octets[4] == PW_EAP_NAK) &&
+           (eap_msg->length >= (EAP_HEADER_LEN + 2)) &&
+           inst->ignore_unknown_eap_types &&
+           ((eap_msg->vp_octets[5] == 0) ||
+            (eap_msg->vp_octets[5] > PW_EAP_MAX_TYPES) ||
+            (inst->types[eap_msg->vp_octets[5]] == NULL))) {
+               RDEBUG2("Ignoring NAK with request for unknown EAP type");
+               return EAP_NOOP;
+       }
 
-       eap_compose(&handler);
+       if ((eap_msg->vp_octets[4] == PW_EAP_TTLS) ||
+           (eap_msg->vp_octets[4] == PW_EAP_PEAP)) {
+               RDEBUG2("Continuing tunnel setup.");
+               return EAP_OK;
+       }
+
+       /*
+        *      Later EAP messages are longer than the 'start'
+        *      message, so if everything is OK, this function returns
+        *      'no start found', so that the rest of the EAP code can
+        *      use the State attribute to match this EAP-Message to
+        *      an ongoing conversation.
+        */
+       RDEBUG2("No EAP Start, assuming it's an on-going EAP conversation");
 
-       eap_ds_free(&eap_ds);
-       return EAP_FOUND;
+       return EAP_NOTFOUND;
 }
 
 /*
@@ -735,6 +816,15 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
  */
 void eap_fail(EAP_HANDLER *handler)
 {
+       /*
+        *      Delete any previous replies.
+        */
+       pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE, 0);
+       pairdelete(&handler->request->reply->vps, PW_STATE, 0);
+
+       eap_packet_free(&handler->eap_ds->request);
+       handler->eap_ds->request = eap_packet_alloc();
+
        handler->eap_ds->request->code = PW_EAP_FAILURE;
        eap_compose(handler);
 }
@@ -751,7 +841,7 @@ void eap_success(EAP_HANDLER *handler)
 /*
  * Basic EAP packet verfications & validations
  */
-static int eap_validation(eap_packet_t *eap_packet)
+static int eap_validation(REQUEST *request, eap_packet_t *eap_packet)
 {
        uint16_t len;
 
@@ -767,15 +857,15 @@ static int eap_validation(eap_packet_t *eap_packet)
            (eap_packet->data[0] <= 0) ||
            (eap_packet->data[0] > PW_EAP_MAX_TYPES)) {
 
-               radlog(L_AUTH, "rlm_eap: Incorrect EAP Message, "
-                               "Ignoring the packet");
+               radlog_request(L_AUTH, 0, request, 
+                              "Badly formatted EAP Message: Ignoring the packet");
                return EAP_INVALID;
        }
 
        /* we don't expect notification, but we send it */
        if (eap_packet->data[0] == PW_EAP_NOTIFICATION) {
-               radlog(L_AUTH, "rlm_eap: Got NOTIFICATION, "
-                               "Ignoring the packet");
+               radlog_request(L_AUTH, 0, request, "Got NOTIFICATION, "
+                              "Ignoring the packet");
                return EAP_INVALID;
        }
 
@@ -786,7 +876,7 @@ static int eap_validation(eap_packet_t *eap_packet)
 /*
  *  Get the user Identity only from EAP-Identity packets
  */
-static char *eap_identity(eap_packet_t *eap_packet)
+static char *eap_identity(REQUEST *request, eap_packet_t *eap_packet)
 {
        int size;
        uint16_t len;
@@ -802,16 +892,12 @@ static char *eap_identity(eap_packet_t *eap_packet)
        len = ntohs(len);
 
        if ((len <= 5) || (eap_packet->data[1] == 0x00)) {
-               radlog(L_ERR, "rlm_eap: UserIdentity Unknown ");
+               RDEBUG("UserIdentity Unknown ");
                return NULL;
        }
 
        size = len - 5;
-       identity = (char *)malloc(size + 1);
-       if (identity == NULL) {
-               radlog(L_ERR, "rlm_eap: out of memory");
-               return NULL;
-       }
+       identity = rad_malloc(size + 1);
        memcpy(identity, &eap_packet->data[1], size);
        identity[size] = '\0';
 
@@ -889,7 +975,7 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
        /*
         *      Ensure it's a valid EAP-Request, or EAP-Response.
         */
-       if (eap_validation(eap_packet) == EAP_INVALID) {
+       if (eap_validation(request, eap_packet) == EAP_INVALID) {
                free(*eap_packet_p);
                *eap_packet_p = NULL;
                return NULL;
@@ -903,8 +989,8 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                handler = eaplist_find(inst, request, eap_packet);
                if (handler == NULL) {
                        /* Either send EAP_Identity or EAP-Fail */
-                       radlog(L_ERR, "rlm_eap: Either EAP-request timed out OR"
-                               " EAP-response to an unknown EAP-request");
+                       RDEBUG("Either EAP-request timed out OR"
+                              " EAP-response to an unknown EAP-request");
                        free(*eap_packet_p);
                        *eap_packet_p = NULL;
                        return NULL;
@@ -920,13 +1006,13 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                 */
                if ((eap_packet->data[0] != PW_EAP_NAK) &&
                    (eap_packet->data[0] != handler->eap_type)) {
-                       radlog(L_ERR, "rlm_eap: Response appears to match, but EAP type is wrong.");
+                       RDEBUG("Response appears to match, but EAP type is wrong.");
                        free(*eap_packet_p);
                        *eap_packet_p = NULL;
                        return NULL;
                }
 
-               vp = pairfind(request->packet->vps, PW_USER_NAME);
+               vp = pairfind(request->packet->vps, PW_USER_NAME, 0);
                if (!vp) {
                        /*
                         *      NAS did not set the User-Name
@@ -935,10 +1021,10 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                         *      request vps so that autz's work
                         *      correctly
                        */
-                       radlog(L_INFO, "rlm_eap: Broken NAS did not set User-Name, setting from EAP Identity");
+                      RDEBUG2("Broken NAS did not set User-Name, setting from EAP Identity");
                        vp = pairmake("User-Name", handler->identity, T_OP_EQ);
                        if (vp == NULL) {
-                               radlog(L_ERR, "rlm_eap: out of memory");
+                              RDEBUG("Out of memory");
                                free(*eap_packet_p);
                                *eap_packet_p = NULL;
                                return NULL;
@@ -956,18 +1042,18 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                         *      request as the NAS is doing something
                         *      funny.
                        */
-                       if (strncmp(handler->identity, vp->strvalue,
+                       if (strncmp(handler->identity, vp->vp_strvalue,
                                   MAX_STRING_LEN) != 0) {
-                               radlog(L_ERR, "rlm_eap: Identity does not match User-Name.  Authentication failed.");
+                               RDEBUG("Identity does not match User-Name.  Authentication failed.");
                                free(*eap_packet_p);
                                *eap_packet_p = NULL;
                                return NULL;
                        }
               }
        } else {                /* packet was EAP identity */
-               handler = eap_handler_alloc();
+               handler = eap_handler_alloc(inst);
                if (handler == NULL) {
-                       radlog(L_ERR, "rlm_eap: out of memory");
+                       RDEBUG("Out of memory.");
                        free(*eap_packet_p);
                        *eap_packet_p = NULL;
                        return NULL;
@@ -976,16 +1062,16 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                /*
                 *      All fields in the handler are set to zero.
                 */
-               handler->identity = eap_identity(eap_packet);
+               handler->identity = eap_identity(request, eap_packet);
                if (handler->identity == NULL) {
-                       radlog(L_ERR, "rlm_eap: Identity Unknown, authentication failed");
+                       RDEBUG("Identity Unknown, authentication failed");
                        free(*eap_packet_p);
                        *eap_packet_p = NULL;
-                       eap_handler_free(&handler);
+                       eap_handler_free(inst, handler);
                        return NULL;
                }
 
-               vp = pairfind(request->packet->vps, PW_USER_NAME);
+               vp = pairfind(request->packet->vps, PW_USER_NAME, 0);
                if (!vp) {
                        /*
                         *      NAS did not set the User-Name
@@ -994,12 +1080,13 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                         *      request vps so that autz's work
                         *      correctly
                        */
-                       radlog(L_INFO, "rlm_eap: WARNING NAS did not set User-Name.  Setting it locally from EAP Identity");
+                      RDEBUG2("WARNING NAS did not set User-Name.  Setting it locally from EAP Identity");
                        vp = pairmake("User-Name", handler->identity, T_OP_EQ);
                        if (vp == NULL) {
-                               radlog(L_ERR, "rlm_eap: out of memory");
+                               RDEBUG("Out of memory");
                                free(*eap_packet_p);
                                *eap_packet_p = NULL;
+                              eap_handler_free(inst, handler);
                                return NULL;
                        }
                        vp->next = request->packet->vps;
@@ -1011,12 +1098,12 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
                         *      identity, the NAS is doing something
                         *      funny, so reject the request.
                        */
-                       if (strncmp(handler->identity, vp->strvalue,
+                       if (strncmp(handler->identity, vp->vp_strvalue,
                                   MAX_STRING_LEN) != 0) {
-                               radlog(L_ERR, "rlm_eap: Identity does not match User-Name, setting from EAP Identity.");
+                               RDEBUG("Identity does not match User-Name, setting from EAP Identity.");
                                free(*eap_packet_p);
                                *eap_packet_p = NULL;
-                               eap_handler_free(&handler);
+                               eap_handler_free(inst, handler);
                                return NULL;
                        }
               }
@@ -1026,11 +1113,11 @@ EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
        if (handler->eap_ds == NULL) {
                free(*eap_packet_p);
                *eap_packet_p = NULL;
-               eap_handler_free(&handler);
+               eap_handler_free(inst, handler);
                return NULL;
        }
 
-       handler->timestamp = time(NULL);
+       handler->timestamp = request->timestamp;
        handler->request = request; /* LEAP needs this */
        return handler;
 }