Added LEAP sub-module, which works in simple tests from packet
authoraland <aland>
Tue, 4 Mar 2003 17:42:20 +0000 (17:42 +0000)
committeraland <aland>
Tue, 4 Mar 2003 17:42:20 +0000 (17:42 +0000)
traces.

Untested with real-world samples, though.

src/modules/rlm_eap/types/rlm_eap_leap/Makefile [new file with mode: 0644]
src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.c [new file with mode: 0644]
src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.h [new file with mode: 0644]
src/modules/rlm_eap/types/rlm_eap_leap/rlm_eap_leap.c [new file with mode: 0644]

diff --git a/src/modules/rlm_eap/types/rlm_eap_leap/Makefile b/src/modules/rlm_eap/types/rlm_eap_leap/Makefile
new file mode 100644 (file)
index 0000000..ab9b3ce
--- /dev/null
@@ -0,0 +1,13 @@
+# Generated automatically from Makefile.in by configure.
+TARGET      = rlm_eap_leap
+SRCS        = rlm_eap_leap.c eap_leap.c
+RLM_CFLAGS  = $(INCLTDL) -I../..
+HEADERS     = des.h eap_leap.h
+RLM_INSTALL = 
+
+$(STATIC_OBJS): $(HEADERS)
+
+$(DYNAMIC_OBJS): $(HEADERS)
+
+RLM_DIR=../../
+include ${RLM_DIR}../rules.mak
diff --git a/src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.c b/src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.c
new file mode 100644 (file)
index 0000000..830625c
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * eap_leap.c  EAP LEAP functionality.
+ *
+ * Version:     $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   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
+ *
+ * Copyright 2003 Alan DeKok <aland@freeradius.org>
+ */
+
+/*
+ *
+ *  LEAP Packet Format in EAP Type-Data
+ *  --- ------ ------ -- --- --------- 
+ *    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
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |     Type 0x11 |  Version 0x01 | Unused 0x00   | Count 0x08    |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |               Peer Challenge                                |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |               Peer Challenge                                |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |   User Name .....
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-
+ *
+ *  Count is 8 octets since the Peer challenge is 8 bytes.
+ *  Count is 24 for EAP response, with MSCHAP response.
+ *  Length is the total number of octets in the EAP-Message.
+ *
+ *  The LEAP type (0x11) is *not* included in the type data...
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "eap.h"
+#include "eap_leap.h"
+
+/*
+ *      Allocate a new LEAP_PACKET
+ */
+LEAP_PACKET *eapleap_alloc(void)
+{
+       LEAP_PACKET   *rp;
+
+       if ((rp = malloc(sizeof(LEAP_PACKET))) == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               return NULL;
+       }
+       memset(rp, 0, sizeof(LEAP_PACKET));
+       return rp;
+}
+
+/*
+ *      Free LEAP_PACKET
+ */
+void eapleap_free(LEAP_PACKET **leap_packet_ptr)
+{
+       LEAP_PACKET *leap_packet;
+
+       if (!leap_packet_ptr) return;
+       leap_packet = *leap_packet_ptr;
+       if (leap_packet == NULL) return;
+
+       if (leap_packet->challenge) free(leap_packet->challenge);
+       if (leap_packet->name) free(leap_packet->name);
+
+       free(leap_packet);
+
+       *leap_packet_ptr = NULL;
+}
+
+/* 
+ *   Extract the data from the LEAP packet.
+ */ 
+LEAP_PACKET *eapleap_extract(EAP_DS *eap_ds)
+{
+       leap_packet_t   *data;
+       LEAP_PACKET     *packet;
+       int             name_len;
+
+       /*
+        *      LEAP can have EAP-Response or EAP-Request (step 5)
+        *      messages sent to it.
+        */
+       if (!eap_ds || 
+           !eap_ds->response || 
+           ((eap_ds->response->code != PW_EAP_RESPONSE) &&
+            (eap_ds->response->code != PW_EAP_REQUEST)) ||
+           eap_ds->response->type.type != PW_EAP_LEAP ||
+           !eap_ds->response->type.data ||
+           (eap_ds->response->length < LEAP_HEADER_LEN) ||
+           (eap_ds->response->type.data[0] != 0x01)) { /* version 1 */
+               radlog(L_ERR, "rlm_eap_leap: corrupted data");
+               return NULL;
+       }
+
+       /*
+        *      Hmm... this cast isn't the best thing to do.
+        */
+       data = (leap_packet_t *)eap_ds->response->type.data;
+
+       /*
+        *      Some simple sanity checks on the incoming packet.
+        *
+        *      See 'leap.txt' in this directory for a description
+        *      of the stages.
+        */
+       switch (eap_ds->response->code) {
+       case PW_EAP_RESPONSE:
+               if (data->count != 24) {
+                       radlog(L_ERR, "rlm_eap_leap: Bad NTChallengeResponse in LEAP stage 3");
+                       return NULL;
+               }
+               break;
+
+       case PW_EAP_REQUEST:
+               if (data->count != 8) {
+                       radlog(L_ERR, "rlm_eap_leap: Bad AP Challenge in LEAP stage 5");
+                       return NULL;
+               }
+               break;
+
+       default:
+               radlog(L_ERR, "rlm_eap_leap: Invalid EAP code %d",
+                      eap_ds->response->code);
+               return NULL;
+               break;
+       }
+
+       packet = eapleap_alloc();
+       if (!packet) return NULL;
+
+       /*
+        *      Remember code, length, and id.
+        */
+       packet->code = eap_ds->response->code;
+       packet->id = eap_ds->response->id;
+
+       /*
+        *      The size of the LEAP portion of the packet, not
+        *      counting the EAP header and the type.
+        */
+       packet->length = eap_ds->response->length - EAP_HEADER_LEN - 1;
+
+       /*
+        *      Remember the length of the challenge.
+        */
+       packet->count = data->count;
+
+       packet->challenge = malloc(packet->count);
+       if (packet->challenge == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               eapleap_free(&packet);
+               return NULL;
+       }
+       memcpy(packet->challenge, data->challenge, packet->count);
+
+       /*
+        *      The User-Name comes after the challenge.
+        */
+       name_len = eap_ds->response->length - 3 - packet->count;
+       if (name_len > 0) {
+               packet->name = malloc(name_len + 1);
+               if (!packet->name) {
+                       radlog(L_ERR, "rlm_eap_leap: out of memory");
+                       eapleap_free(&packet);
+                       return NULL;
+               }
+               memcpy(packet->name, &data->challenge[packet->count],
+                      name_len);
+               packet->name[name_len] = '\0';
+               packet->name_len = name_len;
+       }
+
+       return packet;
+}
+
+/* 
+ *     Verify the MS-CHAP response from the user.
+ */
+int eapleap_stage4(LEAP_PACKET *packet, VALUE_PAIR* password, 
+                  leap_session_t *session)
+{
+       int i;
+       unsigned char unicode[512];
+       unsigned char ntpwdhash[16];
+       unsigned char response[24];
+       
+       
+       /*
+        *      No password or previous packet.  Die.
+        */
+       if ((password == NULL) || (session == NULL)) {
+               return 0;
+       }
+
+       /*
+        *      Convert the password to NT's weird Unicode format.
+        */
+       memset(unicode, 0, sizeof(unicode));
+       for (i = 0; i < password->length; i++) {
+               /*
+                *  Yes, the *even* bytes have the values,
+                *  and the *odd* bytes are zero.
+                */
+               unicode[(i << 1)] = password->strvalue[i];
+       }
+
+       /*
+        *  Get the NT Password hash.
+        */
+       md4_calc(ntpwdhash, unicode, password->length * 2);
+
+       /*
+        *      Calculate and verify the CHAP challenge.
+        */
+       lrad_mschap(ntpwdhash, session->peer_challenge, response);
+       if (memcmp(response, packet->challenge, 24) == 0) {
+               DEBUG2("  rlm_eap_leap: NtChallengeResponse from AP is valid");
+               memcpy(session->peer_response, response, sizeof(response));
+               return 1;
+       }
+
+       DEBUG2("  rlm_eap_leap: FAILED incorrect NtChallengeResponse from AP");
+       return 0;
+}
+
+/* 
+ *     Verify ourselves to the AP
+ */
+LEAP_PACKET *eapleap_stage6(LEAP_PACKET *packet, REQUEST *request,
+                           VALUE_PAIR *user_name, VALUE_PAIR* password, 
+                           leap_session_t *session, VALUE_PAIR **reply_vps)
+{
+       int i;
+       unsigned char unicode[512];
+       unsigned char ntpwdhash[16];
+       LEAP_PACKET *reply;
+       char *p;
+       VALUE_PAIR *vp;
+       
+       /*
+        *      No password or previous packet.  Die.
+        */
+       if ((password == NULL) || (session == NULL)) {
+               return NULL;
+       }
+
+       /*
+        *      Convert the password to NT's weird Unicode format.
+        */
+       memset(unicode, 0, sizeof(unicode));
+       for (i = 0; i < password->length; i++) {
+               /*
+                *  Yes, the *even* bytes have the values,
+                *  and the *odd* bytes are zero.
+                */
+               unicode[(i << 1)] = password->strvalue[i];
+       }
+
+       reply = eapleap_alloc();
+       if (!reply) return NULL;
+
+       reply->code = PW_EAP_RESPONSE;
+       reply->length = LEAP_HEADER_LEN + 24 + user_name->length;
+       reply->count = 24;
+
+       reply->challenge = malloc(reply->count);
+       if (reply->challenge == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               eapleap_free(&reply);
+               return NULL;
+       }
+
+       /*
+        *      The LEAP packet also contains the user name.
+        */
+       reply->name = malloc(user_name->length + 1);
+       if (reply->name == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               eapleap_free(&reply);
+               return NULL;
+       }
+       
+       /*
+        *      Copy the name over, and ensure it's NUL terminated.
+        */
+       memcpy(reply->name, user_name->strvalue, user_name->length);
+       reply->name[user_name->length] = '\0';
+       reply->name_len = user_name->length;
+
+       /*
+        *  MPPE hash = ntpwdhash(ntpwdhash(unicode(pw)))
+        */
+       md4_calc(ntpwdhash, unicode, password->length * 2);
+       memcpy(unicode, ntpwdhash, 16);
+       md4_calc(ntpwdhash, unicode, 16);
+
+       /*
+        *      Calculate our response, to authenticate ourselves
+        *      to the AP.
+        */
+       lrad_mschap(ntpwdhash, packet->challenge, reply->challenge);
+
+       /*
+        *  Calculate the leap:session-key attribute
+        */
+       vp = pairmake("Cisco-AVPair", "leap:session-key=", T_OP_ADD);
+       if (!vp) {
+               radlog(L_ERR, "rlm_eap_leap: Failed to create Cisco-AVPair attribute.  LEAP cancelled.");
+               eapleap_free(&reply);
+               return NULL;
+       }
+
+       /*
+        *      And calculate the MPPE session key.
+        */
+       p = unicode;
+       memcpy(p, ntpwdhash, 16); /* MPPEHASH */
+       p += 16;
+       memcpy(p, packet->challenge, 8); /* APC */
+       p += 8;
+       memcpy(p, reply->challenge, 24); /* APR */
+       p += 24;
+       memcpy(p, session->peer_challenge, 8); /* PC */
+       p += 8;
+       memcpy(p, session->peer_response, 24); /* PR */
+       p += 24;
+
+       /*
+        *      These 16 bytes are the session key to use.
+        */
+       librad_md5_calc(ntpwdhash, unicode, 16 + 8 + 24 + 8 + 24);
+
+       memcpy(vp->strvalue + vp->length, ntpwdhash, 16);
+       memset(vp->strvalue + vp->length + 16, 0,
+              sizeof(vp->strvalue) - (vp->length + 16));
+
+       i = 16;
+       rad_tunnel_pwencode(vp->strvalue + vp->length, &i,
+                           request->secret, request->packet->vector);
+       vp->length += i;
+       pairadd(reply_vps, vp);
+
+       return reply;
+}
+
+/*
+ *     If an EAP LEAP request needs to be initiated then
+ *     create such a packet.
+ */
+LEAP_PACKET *eapleap_initiate(EAP_DS *eap_ds, VALUE_PAIR *user_name)
+{
+       int i;
+       LEAP_PACKET     *reply;
+
+       reply = eapleap_alloc();
+       if (reply == NULL)  {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               return NULL;
+       }
+
+       reply->code = PW_EAP_REQUEST;
+       reply->length = LEAP_HEADER_LEN + 8 + user_name->length;
+       reply->count = 8;       /* random challenge */
+
+       reply->challenge = malloc(reply->count);
+       if (reply->challenge == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               eapleap_free(&reply);
+               return NULL;
+       }
+
+       /*
+        *      Fill the challenge with random bytes.
+        */
+       for (i = 0; i < len; i++) {
+               value[i] = lrad_rand();
+       }
+
+       DEBUG2("  rlm_eap_leap: Issuing AP Challenge");
+
+       /*
+        *      The LEAP packet also contains the user name.
+        */
+       reply->name = malloc(user_name->length + 1);
+       if (reply->name == NULL) {
+               radlog(L_ERR, "rlm_eap_leap: out of memory");
+               eapleap_free(&reply);
+               return NULL;
+       }
+       
+       /*
+        *      Copy the name over, and ensure it's NUL terminated.
+        */
+       memcpy(reply->name, user_name->strvalue, user_name->length);
+       reply->name[user_name->length] = '\0';
+       reply->name_len = user_name->length;
+
+       return reply;
+}
+
+/* 
+ * compose the LEAP reply packet in the EAP reply typedata
+ */
+int eapleap_compose(EAP_DS *eap_ds, LEAP_PACKET *reply)
+{
+       leap_packet_t *data;
+
+       /*
+        *  We need the name and the challenge.
+        */
+       switch (reply->code) {
+       case PW_EAP_REQUEST:
+       case PW_EAP_RESPONSE:
+               eap_ds->request->type.type = PW_EAP_LEAP;
+               eap_ds->request->type.length = reply->length;
+               
+               eap_ds->request->type.data = malloc(reply->length);
+               if (eap_ds->request->type.data == NULL) {
+                       radlog(L_ERR, "rlm_eap_leap: out of memory");
+                       return 0;
+               }
+               data = (leap_packet_t *) eap_ds->request->type.data;
+               data->version = 0x01;
+               data->unused = 0;
+               data->count = reply->count;
+
+               /*
+                *      N bytes of the challenge, followed by the user name.
+                */
+               memcpy(&data->challenge[0], reply->challenge, reply->count);
+               memcpy(&data->challenge[reply->count],
+                      reply->name, reply->name_len);
+               break;
+
+               /*
+                *      EAP-Success packets don't contain any data
+                *      other than the header.
+                */
+       case PW_EAP_SUCCESS:
+               eap_ds->request->type.length = 0;
+               break;
+
+       default:
+               radlog(L_ERR, "rlm_eap_leap: Internal sanity check failed");
+               return 0;
+               break;
+       }
+
+       /*
+        *      Set the EAP code.
+        */
+       eap_ds->request->code = reply->code;
+
+       return 1;
+}
diff --git a/src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.h b/src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.h
new file mode 100644 (file)
index 0000000..1412b83
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef _EAP_LEAP_H
+#define _EAP_LEAP_H
+
+#include "eap.h"
+
+#define PW_LEAP_CHALLENGE      1
+#define PW_LEAP_RESPONSE       2
+#define PW_LEAP_SUCCESS                3
+#define PW_LEAP_FAILURE                4
+#define PW_LEAP_MAX_CODES      4
+
+/*
+ *  Version + unused + count
+ */
+#define LEAP_HEADER_LEN        3
+
+/*
+ ****
+ * EAP - LEAP doesnot specify code, id & length but chap specifies them,
+ *     for generalization purpose, complete header should be sent
+ *     and not just value_size, value and name.
+ *     future implementation.
+ */
+
+/* eap packet structure */
+typedef struct leap_packet_t {
+       /*
+        *  EAP header, followed by type comes before this.
+        */
+       uint8_t version;
+       uint8_t unused;
+       uint8_t count;
+       uint8_t challenge[1];   /* 8 or 24, followed by user name */
+} leap_packet_t;
+
+/*
+ *     Which is decoded into this.
+ */
+typedef struct leap_packet {
+       unsigned char   code;
+       unsigned char   id;
+       int             length;
+       int             count;
+       unsigned char   *challenge;
+       int             name_len;
+       char            *name;
+} LEAP_PACKET;
+
+/*
+ *     The information which must be kept around
+ *     during the LEAP session.
+ */
+typedef struct leap_session_t {
+       int             stage;
+       uint8_t         peer_challenge[8];
+       uint8_t         peer_response[24];
+} leap_session_t;
+
+/* function declarations here */
+
+LEAP_PACKET    *eapleap_alloc(void);
+void           eapleap_free(LEAP_PACKET **leap_packet_ptr);
+
+int            eapleap_compose(EAP_DS *auth, LEAP_PACKET *reply);
+LEAP_PACKET    *eapleap_extract(EAP_DS *auth);
+LEAP_PACKET    *eapleap_initiate(EAP_DS *eap_ds, VALUE_PAIR *user_name);
+int            eapleap_stage4(LEAP_PACKET *packet, VALUE_PAIR* password, 
+                              leap_session_t *session);
+LEAP_PACKET    *eapleap_stage6(LEAP_PACKET *packet, REQUEST *request,
+                               VALUE_PAIR *user_name, VALUE_PAIR* password, 
+                               leap_session_t *session,
+                               VALUE_PAIR **reply_vps);
+#endif /*_EAP_LEAP_H*/
diff --git a/src/modules/rlm_eap/types/rlm_eap_leap/rlm_eap_leap.c b/src/modules/rlm_eap/types/rlm_eap_leap/rlm_eap_leap.c
new file mode 100644 (file)
index 0000000..7002bb9
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * rlm_eap_leap.c    Handles that are called from eap
+ *
+ * Version:     $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   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
+ *
+ * Copyright 2003 Alan DeKok <aland@freeradius.org>
+ */
+
+#include "autoconf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "eap_leap.h"
+
+
+static int leap_attach(CONF_SECTION *conf, void **instance)
+{
+       return 0;
+}
+
+static void leap_session_free(void **opaque)
+{
+       leap_session_t *session;
+
+       if (!opaque) return;
+
+       session = *(leap_session_t **) opaque;
+
+       free(session);
+       *opaque = NULL;
+}
+
+/*
+ * send an initial eap-leap request
+ * ie access challenge to the user/peer.
+
+ * Frame eap reply packet.
+ * len = header + type + leap_typedata
+ * leap_typedata = value_size + value
+ */
+static int leap_initiate(void *instance, EAP_HANDLER *handler)
+{
+       leap_session_t  *session;
+       LEAP_PACKET     *reply;
+
+       DEBUG2("  rlm_eap_leap: Stage 2");
+       
+       reply = eapleap_initiate(handler->eap_ds, handler->username);
+       if (reply == NULL)
+               return 0;
+
+       eapleap_compose(handler->eap_ds, reply);
+
+       handler->opaque = malloc(sizeof(leap_session_t));
+       if (!handler->opaque) {
+         radlog(L_ERR, "rlm_eap_leap: Out of memory");
+         eapleap_free(&reply);
+         return 0;
+       }
+
+       /*
+        *      Remember which stage we're in, and which challenge
+        *      we sent to the AP.  The later stages will take care
+        *      of filling in the peer response.
+        */
+       session = (leap_session_t *) handler->opaque;
+       handler->free_opaque = leap_session_free;
+
+       session->stage = 4;     /* the next stage we're in */
+       memcpy(session->peer_challenge, reply->challenge, reply->count);
+
+       DEBUG2("  rlm_eap_leap: Successfully initiated");
+
+       eapleap_free(&reply);
+       return 1;
+}
+
+static int leap_authenticate(void *instance, EAP_HANDLER *handler)
+{
+       int             rcode;
+       leap_session_t  *session;
+       LEAP_PACKET     *packet;
+       LEAP_PACKET     *reply;
+       leap_packet_t   *request;
+       char*           username;
+       VALUE_PAIR      *password;
+       EAP_DS          *temp;
+
+       if (!handler->opaque) {
+               radlog(L_ERR, "rlm_eap_leap: Cannot authenticate without LEAP history");
+               return 0;
+       }
+       session = (leap_session_t *) handler->opaque;
+       reply = NULL;
+
+       /*
+        *      Extract the LEAP packet.
+        */
+       if (!(packet = eapleap_extract(handler->eap_ds)))
+               return 0;
+
+       username = (char *)handler->username->strvalue;
+
+       /*
+        *      The password is never sent over the wire.
+        *      Always get the configured password, for each user.
+        */
+       password = pairfind(handler->configured, PW_PASSWORD);
+       if (password == NULL) {
+               radlog(L_INFO, "rlm_eap_leap: No password configured for this user");
+               eapleap_free(&packet);
+               return 0;
+       }
+
+       /*
+        *      See if there was a previous stage...
+        */
+       temp = (EAP_DS *)handler->prev_eapds;
+       request = temp ? (leap_packet_t *)(temp->request->type.data): NULL;
+
+       /*
+        *      We've already sent the AP challenge.  This packet
+        *      should contain the NtChallengeResponse
+        */
+       switch (session->stage) {
+       case 4:                 /* Verify NtChallengeResponse */
+               DEBUG2("  rlm_eap_leap: Stage 4");
+               rcode = eapleap_stage4(packet, password, session);
+               session->stage = 6;
+
+               /*
+                *      We send EAP-Success or EAP-Fail, and not
+                *      any LEAP packet.  So we return here.
+                */
+               if (!rcode) {
+                       handler->eap_ds->request->code = PW_EAP_FAILURE;
+                       eapleap_free(&packet);
+                       return 0;
+               }
+
+               handler->eap_ds->request->code = PW_EAP_SUCCESS;
+               return 1;
+               break;
+
+       case 6:                 /* Issue session key */
+               DEBUG2("  rlm_eap_leap: Stage 6");
+               reply = eapleap_stage6(packet, handler->request,
+                                      handler->username, password,
+                                      session, handler->reply_vps);
+               break;
+
+               /*
+                *      Stages 1, 3, and 5 are requests from the AP.
+                *      Stage 2 is handled by initiate()
+                */
+       default:
+               radlog(L_ERR, "  rlm_eap_leap: Internal sanity check failed on stage");
+               break;
+       }
+
+       eapleap_free(&packet);
+
+       /*
+        *      Process the packet.  We don't care about any previous
+        *      EAP packets, as 
+        */
+       if (!reply) {
+               return 0;
+       }
+
+       eapleap_compose(handler->eap_ds, reply);
+
+       eapleap_free(&reply);
+       return 1;
+}
+
+static int leap_detach(void **instance)
+{
+       return 0;
+}
+
+/*
+ *     The module name should be the only globally exported symbol.
+ *     That is, everything else should be 'static'.
+ */
+EAP_TYPE rlm_eap_leap = {
+       "eap_leap",
+       leap_attach,                    /* attach */
+       leap_initiate,                  /* Start the initial request, after Identity */
+       leap_authenticate,              /* authentication */
+       leap_detach                     /* detach */
+};