Port "use_tunneled_reply" fix for MS-CHAP from branch_1_1
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_ttls / ttls.c
index 2a2c7aa..bd5d270 100644 (file)
  *
  *   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 2003 Alan DeKok <aland@freeradius.org>
+ *   Copyright 2006 The FreeRADIUS server project
  */
-#include "eap_tls.h"
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
 #include "eap_ttls.h"
 
 /*
- *    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 
- *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
- *   |                           AVP Code                            | 
- *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
- *   |V M r r r r r r|                  AVP Length                   | 
- *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
- *   |                        Vendor-ID (opt)                        | 
- *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 
- *   |    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
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                           AVP Code                            |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |V M r r r r r r|                  AVP Length                   |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |                        Vendor-ID (opt)                        |
+ *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *   |    Data ...
+ *   +-+-+-+-+-+-+-+-+
  */
 
 /*
  *     Verify that the diameter packet is valid.
  */
-static int diameter_verify(const uint8_t *data, int data_len)
+static int diameter_verify(const uint8_t *data, unsigned int data_len)
 {
        uint32_t attr;
        uint32_t length;
        unsigned int offset;
+       unsigned int data_left = data_len;
+
+       while (data_left > 0) {
+               if (data_len < 12) {
+                       DEBUG2("  rlm_eap_ttls:  Diameter attribute is too small to contain a Diameter header");
+                       return 0;
+               }
 
-       while (data_len > 0) {
+               rad_assert(data_left <= data_len);
                memcpy(&attr, data, sizeof(attr));
                data += 4;
                attr = ntohl(attr);
@@ -79,7 +90,7 @@ static int diameter_verify(const uint8_t *data, int data_len)
                        attribute = (vendor << 16) | attr;
 
                        da = dict_attrbyvalue(attribute);
-                       
+
                        /*
                         *      SHOULD check ((length & (1 << 30)) != 0)
                         *      for the mandatory bit.
@@ -106,12 +117,20 @@ static int diameter_verify(const uint8_t *data, int data_len)
                /*
                 *      Too short or too long is bad.
                 *
-                *      FIXME: EAP-Message 
+                *      FIXME: EAP-Message
                 */
-               if ((length < offset) ||
-                   (length > (MAX_STRING_LEN + 8)) ||
-                   (length > data_len)) {
-                       DEBUG2("  rlm_eap_ttls: Tunneled attribute %d has invalid length %d", attr, length);
+               if (length < offset) {
+                       DEBUG2("  rlm_eap_ttls: Tunneled attribute %d is too short (%d)to contain anything useful.", attr, length);
+                       return 0;
+               }
+
+               if (length > (MAX_STRING_LEN + 8)) {
+                       DEBUG2("  rlm_eap_ttls: Tunneled attribute %d is too long (%d) to pack into a RADIUS attribute.", attr, length);
+                       return 0;
+               }
+                   
+               if (length > data_left) {
+                       DEBUG2("  rlm_eap_ttls: Tunneled attribute %d is longer than room left in the packet (%d > %d).", attr, length, data_left);
                        return 0;
                }
 
@@ -119,7 +138,7 @@ static int diameter_verify(const uint8_t *data, int data_len)
                 *      Check for broken implementations, which don't
                 *      pad the AVP to a 4-octet boundary.
                 */
-               if (data_len == length) break;
+               if (data_left == length) break;
 
                /*
                 *      The length does NOT include the padding, so
@@ -136,17 +155,22 @@ static int diameter_verify(const uint8_t *data, int data_len)
                 *      Otherwise, if the attribute over-flows the end
                 *      of the packet, die.
                 */
-               if (data_len > length) {
-                       data_len -= length;
-                       data += length - offset;
-
-               } else if (data_len < length) {
+               if (data_left < length) {
                        DEBUG2("  rlm_eap_ttls: ERROR! Diameter attribute overflows packet!");
                        return 0;
-
-               } else {        /* equal, end of packet... */
-                       break;
                }
+       
+               /*
+                *      Check again for equality, now that we're padded
+                *      length to a multiple of 4 octets.
+                */
+               if (data_left == length) break;
+
+               /*
+                *      data_left > length, continue.
+                */
+               data_left -= length;
+               data += length - offset;
        }
 
        /*
@@ -164,13 +188,15 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
 {
        uint32_t        attr;
        uint32_t        length;
-       int             offset;
+       unsigned int    offset;
        int             size;
+       unsigned int    data_left = data_len;
        VALUE_PAIR      *first = NULL;
        VALUE_PAIR      **last = &first;
        VALUE_PAIR      *vp;
 
-       while (data_len > 0) {
+       while (data_left > 0) {
+               rad_assert(data_left <= data_len);
                memcpy(&attr, data, sizeof(attr));
                data += 4;
                attr = ntohl(attr);
@@ -226,7 +252,7 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                        pairfree(&first);
                        return NULL;
                }
-               
+
                /*
                 *      If it's a type from our dictionary, then
                 *      we need to put the data in a relevant place.
@@ -234,27 +260,27 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                switch (vp->type) {
                case PW_TYPE_INTEGER:
                case PW_TYPE_DATE:
-                 if (size != vp->length) {
-                   DEBUG2("  rlm_eap_ttls: Invalid length attribute %d",
-                          attr);
-                       pairfree(&first);
-                       return NULL;
-                 }
-                 memcpy(&vp->lvalue, data, vp->length);
-
-                 /*
-                  *    Stored in host byte order: change it.
-                  */
-                 vp->lvalue = ntohl(vp->lvalue);
-                 break;
-
+                       if (size != vp->length) {
+                               DEBUG2("  rlm_eap_ttls: Invalid length attribute %d",
+                                      attr);
+                               pairfree(&first);
+                               return NULL;
+                       }
+                       memcpy(&vp->lvalue, data, vp->length);
+                       
+                       /*
+                        *      Stored in host byte order: change it.
+                        */
+                       vp->lvalue = ntohl(vp->lvalue);
+                       break;
+                       
                case PW_TYPE_IPADDR:
-                 if (size != vp->length) {
-                   DEBUG2("  rlm_eap_ttls: Invalid length attribute %d",
-                          attr);
-                   pairfree(&first);
-                   return NULL;
-                 }
+                       if (size != vp->length) {
+                               DEBUG2("  rlm_eap_ttls: Invalid length attribute %d",
+                                      attr);
+                               pairfree(&first);
+                               return NULL;
+                       }
                  memcpy(&vp->lvalue, data, vp->length);
                  
                  /*
@@ -265,11 +291,14 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                  /*
                   *    String, octet, etc.  Copy the data from the
                   *    value field over verbatim.
+                  *
+                  *    FIXME: Ipv6 attributes ?
+                  *
                   */
                default:
-                 vp->length = size;
-                 memcpy(vp->strvalue, data, vp->length);
-                 break;
+                       vp->length = size;
+                       memcpy(vp->vp_strvalue, data, vp->length);
+                       break;
                }
 
                /*
@@ -282,16 +311,14 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                 */
                switch (vp->attribute) {
                case PW_USER_PASSWORD:
-                       {
-                               int i;
-                               
-                               for (i = 0; i < vp->length; i++) {
-                                       if (vp->strvalue[i] == 0) {
-                                               vp->length = i;
-                                               break;
-                                       }
-                               }
-                       }
+                       rad_assert(vp->length <= 128); /* RFC requirements */
+
+                       /*
+                        *      If the password is exactly 16 octets,
+                        *      it won't be zero-terminated.
+                        */
+                       vp->vp_strvalue[vp->length] = '\0';
+                       vp->length = strlen(vp->vp_strvalue);
                        break;
 
                        /*
@@ -332,7 +359,7 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                                                      sizeof(challenge));
 
                                for (i = 0; i < vp->length; i++) {
-                                       if (challenge[i] != vp->strvalue[i]) {
+                                       if (challenge[i] != vp->vp_strvalue[i]) {
                                                DEBUG2("  TTLS: Tunneled challenge is incorrect");
                                                pairfree(&first);
                                                return NULL;
@@ -352,6 +379,11 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                last = &(vp->next);
 
                /*
+                *      Catch non-aligned attributes.
+                */
+               if (data_left == length) break;
+
+               /*
                 *      The length does NOT include the padding, so
                 *      we've got to account for it here by rounding up
                 *      to the nearest 4-byte boundary.
@@ -359,7 +391,8 @@ static VALUE_PAIR *diameter2vp(SSL *ssl,
                length += 0x03;
                length &= ~0x03;
 
-               data_len -= length;
+               rad_assert(data_left >= length);
+               data_left -= length;
                data += length - offset; /* already updated */
        }
 
@@ -471,7 +504,7 @@ static int vp2diameter(tls_session_t *tls_session, VALUE_PAIR *first)
                case PW_TYPE_STRING:
                case PW_TYPE_OCTETS:
                default:
-                       memcpy(p, vp->strvalue, vp->length);
+                       memcpy(p, vp->vp_strvalue, vp->length);
                        length = vp->length;
                        break;
                }
@@ -486,9 +519,9 @@ static int vp2diameter(tls_session_t *tls_session, VALUE_PAIR *first)
                 *      Align the data to a multiple of 4 bytes.
                 */
                if ((total & 0x03) != 0) {
-                       int i;
+                       unsigned int i;
 
-                       length = total & 0x03;
+                       length = 4 - (total & 0x03);
                        for (i = 0; i < length; i++) {
                                *p = '\0';
                                p++;
@@ -500,24 +533,23 @@ static int vp2diameter(tls_session_t *tls_session, VALUE_PAIR *first)
        /*
         *      Write the data in the buffer to the SSL session.
         */
-
        if (total > 0) {
 #ifndef NDEBUG
-               int i;
-               
+               unsigned int i;
+
                if (debug_flag > 2) {
                        for (i = 0; i < total; i++) {
                                if ((i & 0x0f) == 0) printf("  TTLS tunnel data out %04x: ", i);
-                               
+
                                printf("%02x ", buffer[i]);
-                               
+
                                if ((i & 0x0f) == 0x0f) printf("\n");
                        }
                        if ((total & 0x0f) != 0) printf("\n");
                }
 #endif
 
-               record_plus(&tls_session->clean_in, buffer, total);
+               (tls_session->record_plus)(&tls_session->clean_in, buffer, total);
 
                /*
                 *      FIXME: Check the return code.
@@ -531,13 +563,319 @@ static int vp2diameter(tls_session_t *tls_session, VALUE_PAIR *first)
        return 1;
 }
 
+/*
+ *     Use a reply packet to determine what to do.
+ */
+static int process_reply(EAP_HANDLER *handler, tls_session_t *tls_session,
+                        REQUEST *request, RADIUS_PACKET *reply)
+{
+       int rcode = RLM_MODULE_REJECT;
+       VALUE_PAIR *vp;
+       ttls_tunnel_t *t = tls_session->opaque;
+
+       handler = handler;      /* -Wunused */
+
+       /*
+        *      If the response packet was Access-Accept, then
+        *      we're OK.  If not, die horribly.
+        *
+        *      FIXME: Take MS-CHAP2-Success attribute, and
+        *      tunnel it back to the client, to authenticate
+        *      ourselves to the client.
+        *
+        *      FIXME: If we have an Access-Challenge, then
+        *      the Reply-Message is tunneled back to the client.
+        *
+        *      FIXME: If we have an EAP-Message, then that message
+        *      must be tunneled back to the client.
+        *
+        *      FIXME: If we have an Access-Challenge with a State
+        *      attribute, then do we tunnel that to the client, or
+        *      keep track of it ourselves?
+        *
+        *      FIXME: EAP-Messages can only start with 'identity',
+        *      NOT 'eap start', so we should check for that....
+        */
+       switch (reply->code) {
+       case PW_AUTHENTICATION_ACK:
+               DEBUG2("  TTLS: Got tunneled Access-Accept");
+
+               rcode = RLM_MODULE_OK;
+
+               /*
+                *      MS-CHAP2-Success means that we do NOT return
+                *      an Access-Accept, but instead tunnel that
+                *      attribute to the client, and keep going with
+                *      the TTLS session.  Once the client accepts
+                *      our identity, it will respond with an empty
+                *      packet, and we will send EAP-Success.
+                */
+               vp = NULL;
+               pairmove2(&vp, &reply->vps, PW_MSCHAP2_SUCCESS);
+               if (vp) {
+                       DEBUG2("  TTLS: Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
+                       rcode = RLM_MODULE_HANDLED;
+                       t->authenticated = TRUE;
+                       
+                       /*
+                        *      Delete MPPE keys & encryption policy.  We don't
+                        *      want these here.
+                        */
+                       pairdelete(&reply->vps, ((311 << 16) | 7));
+                       pairdelete(&reply->vps, ((311 << 16) | 8));
+                       pairdelete(&reply->vps, ((311 << 16) | 16));
+                       pairdelete(&reply->vps, ((311 << 16) | 17));
+                       
+                       /*
+                        *      Use the tunneled reply, but not now.
+                        */
+                       if (t->use_tunneled_reply) {
+                               t->reply = reply->vps;
+                               reply->vps = NULL;
+                       }
+
+               } else { /* no MS-CHAP2-Success */
+                       /*
+                        *      Can only have EAP-Message if there's
+                        *      no MS-CHAP2-Success.  (FIXME: EAP-MSCHAP?)
+                        *
+                        *      We also do NOT tunnel the EAP-Success
+                        *      attribute back to the client, as the client
+                        *      can figure it out, from the non-tunneled
+                        *      EAP-Success packet.
+                        */
+                       pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+                       pairfree(&vp);
+               }
+
+               /*
+                *      Handle the ACK, by tunneling any necessary reply
+                *      VP's back to the client.
+                */
+               if (vp) {
+                       vp2diameter(tls_session, vp);
+                       pairfree(&vp);
+               }
+
+               /*
+                *      If we've been told to use the attributes from
+                *      the reply, then do so.
+                *
+                *      WARNING: This may leak information about the
+                *      tunneled user!
+                */
+               if (t->use_tunneled_reply) {
+                       pairdelete(&reply->vps, PW_PROXY_STATE);
+                       pairadd(&request->reply->vps, reply->vps);
+                       reply->vps = NULL;
+               }
+               break;
+
+
+       case PW_AUTHENTICATION_REJECT:
+               DEBUG2("  TTLS: Got tunneled Access-Reject");
+               rcode = RLM_MODULE_REJECT;
+               break;
+
+               /*
+                *      Handle Access-Challenge, but only if we
+                *      send tunneled reply data.  This is because
+                *      an Access-Challenge means that we MUST tunnel
+                *      a Reply-Message to the client.
+                */
+       case PW_ACCESS_CHALLENGE:
+               DEBUG2("  TTLS: Got tunneled Access-Challenge");
+
+               /*
+                *      Keep the State attribute, if necessary.
+                *
+                *      Get rid of the old State, too.
+                */
+               pairfree(&t->state);
+               pairmove2(&t->state, &reply->vps, PW_STATE);
+
+               /*
+                *      We should really be a bit smarter about this,
+                *      and move over only those attributes which
+                *      are relevant to the authentication request,
+                *      but that's a lot more work, and this "dumb"
+                *      method works in 99.9% of the situations.
+                */
+               vp = NULL;
+               pairmove2(&vp, &reply->vps, PW_EAP_MESSAGE);
+
+               /*
+                *      There MUST be a Reply-Message in the challenge,
+                *      which we tunnel back to the client.
+                *
+                *      If there isn't one in the reply VP's, then
+                *      we MUST create one, with an empty string as
+                *      it's value.
+                */
+               pairmove2(&vp, &reply->vps, PW_REPLY_MESSAGE);
+
+               /*
+                *      Handle the ACK, by tunneling any necessary reply
+                *      VP's back to the client.
+                */
+               if (vp) {
+                       vp2diameter(tls_session, vp);
+                       pairfree(&vp);
+               }
+               rcode = RLM_MODULE_HANDLED;
+               break;
+
+       default:
+               DEBUG2("  TTLS: Unknown RADIUS packet type %d: rejecting tunneled user", reply->code);
+               rcode = RLM_MODULE_INVALID;
+               break;
+       }
+
+       return rcode;
+}
+
+
+/*
+ *     Do post-proxy processing,
+ */
+static int eapttls_postproxy(EAP_HANDLER *handler, void *data)
+{
+       int rcode;
+       tls_session_t *tls_session = (tls_session_t *) data;
+       REQUEST *fake;
+
+       DEBUG2("  TTLS: Passing reply from proxy back into the tunnel.");
+
+       /*
+        *      If there was a fake request associated with the proxied
+        *      request, do more processing of it.
+        */
+       fake = (REQUEST *) request_data_get(handler->request,
+                                           handler->request->proxy,
+                                           REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK);
+       
+       /*
+        *      Do the callback, if it exists, and if it was a success.
+        */
+       if (fake && (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) {
+               VALUE_PAIR *vp;
+               REQUEST *request = handler->request;
+
+               /*
+                *      Terrible hacks.
+                */
+               rad_assert(fake->packet == NULL);
+               fake->packet = request->proxy;
+               request->proxy = NULL;
+
+               rad_assert(fake->reply == NULL);
+               fake->reply = request->proxy_reply;
+               request->proxy_reply = NULL;
+
+               /*
+                *      Perform a post-auth stage for the tunneled
+                *      session.
+                */
+               fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+               rcode = rad_postauth(fake);
+               DEBUG2("  POST-AUTH %d", rcode);
+
+#ifndef NDEBUG
+               if (debug_flag > 0) {
+                       printf("  TTLS: Final reply from tunneled session code %d\n",
+                              fake->reply->code);
+                       
+                       for (vp = fake->reply->vps; vp != NULL; vp = vp->next) {
+                               putchar('\t');vp_print(stdout, vp);putchar('\n');
+                       }
+               }
+#endif
+
+               /*
+                *      Terrible hacks.
+                */
+               request->proxy = fake->packet;
+               fake->packet = NULL;
+               request->proxy_reply = fake->reply;
+               fake->reply = NULL;
+
+               /*
+                *      And we're done with this request.
+                */
+
+               switch (rcode) {
+                case RLM_MODULE_FAIL:
+                       request_free(&fake);
+                       eaptls_fail(handler->eap_ds, 0);
+                       return 0;
+                       break;
+                       
+                default:  /* Don't Do Anything */
+                       DEBUG2(" TTLS: Got reply %d",
+                              request->proxy_reply->code);
+                       break;
+               }       
+       }
+       request_free(&fake);    /* robust if fake == NULL */
+
+       /*
+        *      Process the reply from the home server.
+        */
+       rcode = process_reply(handler, tls_session, handler->request,
+                             handler->request->proxy_reply);
+
+       /*
+        *      The proxy code uses the reply from the home server as
+        *      the basis for the reply to the NAS.  We don't want that,
+        *      so we toss it, after we've had our way with it.
+        */
+       pairfree(&handler->request->proxy_reply->vps);
+
+       switch (rcode) {
+       case RLM_MODULE_REJECT:
+               DEBUG2("  TTLS: Reply was rejected");
+               break;
+
+       case RLM_MODULE_HANDLED:
+               DEBUG2("  TTLS: Reply was handled");
+               eaptls_request(handler->eap_ds, tls_session);
+               return 1;
+
+       case RLM_MODULE_OK:
+               DEBUG2("  TTLS: Reply was OK");
+               eaptls_success(handler->eap_ds, 0);
+               eaptls_gen_mppe_keys(&handler->request->reply->vps,
+                                    tls_session->ssl,
+                                    "ttls keying material");
+               return 1;
+
+       default:
+               DEBUG2("  TTLS: Reply was unknown.");
+               break;
+       }
+
+       eaptls_fail(handler->eap_ds, 0);
+       return 0;
+}
+
+
+/*
+ *     Free a request.
+ */
+static void my_request_free(void *data)
+{
+       REQUEST *request = (REQUEST *)data;
+
+       request_free(&request);
+}
+
 
 /*
  *     Process the "diameter" contents of the tunneled data.
  */
-int eapttls_process(REQUEST *request, tls_session_t *tls_session)
+int eapttls_process(EAP_HANDLER *handler, tls_session_t *tls_session)
 {
-       int i, err;
+       int err;
        int rcode = PW_AUTHENTICATION_REJECT;
        REQUEST *fake;
        VALUE_PAIR *vp;
@@ -545,13 +883,14 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
        const uint8_t *data;
        unsigned int data_len;
        char buffer[1024];
+       REQUEST *request = handler->request;
 
        /*
         *      Grab the dirty data, and copy it to our buffer.
         *
         *      I *really* don't like these 'record_t' things...
         */
-       data_len = record_minus(&tls_session->dirty_in, buffer, sizeof(buffer));
+       data_len = (tls_session->record_minus)(&tls_session->dirty_in, buffer, sizeof(buffer));
        data = buffer;
 
        /*
@@ -566,7 +905,7 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
         *      go there, too...
         */
        BIO_write(tls_session->into_ssl, buffer, data_len);
-       record_init(&tls_session->clean_out);
+       (tls_session->record_init)(&tls_session->clean_out);
 
        /*
         *      Read (and decrypt) the tunneled data from the SSL session,
@@ -588,7 +927,7 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
        /*
         *      If there's no data, maybe this is an ACK to an
         *      MS-CHAP2-Success.
-        */     
+        */
        if (err == 0) {
                if (t->authenticated) {
                        DEBUG2("  TTLS: Got ACK, and the user was already authenticated.");
@@ -608,18 +947,20 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
 
 #ifndef NDEBUG
        if (debug_flag > 2) {
+               unsigned int i;
+
                for (i = 0; i < data_len; i++) {
                        if ((i & 0x0f) == 0) printf("  TTLS tunnel data in %04x: ", i);
-                       
+
                        printf("%02x ", data[i]);
-                       
+
                        if ((i & 0x0f) == 0x0f) printf("\n");
                }
                if ((data_len & 0x0f) != 0) printf("\n");
        }
 #endif
 
-       if (!diameter_verify(data, (int) data_len)) {
+       if (!diameter_verify(data, data_len)) {
                return PW_AUTHENTICATION_REJECT;
        }
 
@@ -648,11 +989,11 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
 
 #ifndef NDEBUG
        if (debug_flag > 0) {
-         printf("  TTLS: Got tunneled request\n");
-
-         for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
-           putchar('\t');vp_print(stdout, vp);putchar('\n');
-         }
+               printf("  TTLS: Got tunneled request\n");
+               
+               for (vp = fake->packet->vps; vp != NULL; vp = vp->next) {
+                       putchar('\t');vp_print(stdout, vp);putchar('\n');
+               }
        }
 #endif
 
@@ -660,7 +1001,7 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
         *      Update other items in the REQUEST data structure.
         */
        fake->username = pairfind(fake->packet->vps, PW_USER_NAME);
-       fake->password = pairfind(fake->packet->vps, PW_PASSWORD);
+       fake->password = pairfind(fake->packet->vps, PW_USER_PASSWORD);
 
        /*
         *      No User-Name, try to create one from stored data.
@@ -674,22 +1015,22 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
                        vp = pairfind(fake->packet->vps, PW_EAP_MESSAGE);
                        if (vp &&
                            (vp->length >= EAP_HEADER_LEN + 2) &&
-                           (vp->strvalue[0] == PW_EAP_RESPONSE) &&
-                           (vp->strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
-                           (vp->strvalue[EAP_HEADER_LEN + 1] != 0)) {
+                           (vp->vp_strvalue[0] == PW_EAP_RESPONSE) &&
+                           (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) &&
+                           (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) {
                                /*
                                 *      Create & remember a User-Name
                                 */
                                t->username = pairmake("User-Name", "", T_OP_EQ);
                                rad_assert(t->username != NULL);
-                               
-                               memcpy(t->username->strvalue, vp->strvalue + 5,
+
+                               memcpy(t->username->vp_strvalue, vp->vp_strvalue + 5,
                                       vp->length - 5);
                                t->username->length = vp->length - 5;
-                               t->username->strvalue[t->username->length] = 0;
-                               
+                               t->username->vp_strvalue[t->username->length] = 0;
+
                                DEBUG2("  TTLS: Got tunneled identity of %s",
-                                      t->username->strvalue);
+                                      t->username->vp_strvalue);
 
                                /*
                                 *      If there's a default EAP type,
@@ -726,7 +1067,7 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
         */
        if (t->state) {
                DEBUG2("  TTLS: Adding old state with %02x %02x",
-                      t->state->strvalue[0], t->state->strvalue[1]);
+                      t->state->vp_strvalue[0], t->state->vp_strvalue[1]);
                vp = paircopy(t->state);
                if (vp) pairadd(&fake->packet->vps, vp);
        }
@@ -830,145 +1171,108 @@ int eapttls_process(REQUEST *request, tls_session_t *tls_session)
          }
        }
 #endif
+
        /*
-        *      If the response packet was Access-Accept, then
-        *      we're OK.  If not, die horribly.
-        *
-        *      FIXME: Take MS-CHAP2-Success attribute, and
-        *      tunnel it back to the client, to authenticate
-        *      ourselves to the client.
-        *
-        *      FIXME: If we have an Access-Challenge, then
-        *      the Reply-Message is tunneled back to the client.
-        *
-        *      FIXME: If we have an EAP-Message, then that message
-        *      must be tunneled back to the client.
-        *
-        *      FIXME: If we have an Access-Challenge with a State
-        *      attribute, then do we tunnel that to the client, or
-        *      keep track of it ourselves?
-        *
-        *      FIXME: EAP-Messages can only start with 'identity',
-        *      NOT 'eap start', so we should check for that....
+        *      Decide what to do with the reply.
         */
-       rcode = 0;
-       if (fake->reply->code == PW_AUTHENTICATION_ACK) {
-               DEBUG2("  TTLS: Got tunneled Access-Accept");
-
-               rcode = fake->reply->code;
-
-               /*
-                *      MS-CHAP2-Success means that we do NOT return
-                *      an Access-Accept, but instead tunnel that
-                *      attribute to the client, and keep going with
-                *      the TTLS session.  Once the client accepts
-                *      our identity, it will respond with an empty
-                *      packet, and we will send EAP-Success.
-                */
-               vp = NULL;
-               pairmove2(&vp, &fake->reply->vps, PW_MSCHAP2_SUCCESS);
+       switch (fake->reply->code) {
+       case 0:                 /* No reply code, must be proxied... */
+               vp = pairfind(fake->config_items, PW_PROXY_TO_REALM);
                if (vp) {
-#if 1
+                       eap_tunnel_data_t *tunnel;
+                       DEBUG2("  TTLS: Tunneled authentication will be proxied to %s", vp->vp_strvalue);
+
                        /*
-                        *      FIXME: Tunneling MS-CHAP2-Success causes
-                        *      the only client we have access to, to die.
-                        *
-                        *      We don't want that...
+                        *      Tell the original request that it's going
+                        *      to be proxied.
                         */
-                       pairfree(&vp);
-#else
-                       DEBUG2("  TTLS: Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
-                       rcode = PW_ACCESS_CHALLENGE;
-                       t->authenticated = TRUE;
-#endif
-               } else { /* no MS-CHAP2-Success */
+                       pairmove2(&(request->config_items),
+                                 &(fake->config_items),
+                                 PW_PROXY_TO_REALM);
+
                        /*
-                        *      Can only have EAP-Message if there's
-                        *      no MS-CHAP2-Success.  (FIXME: EAP-MSCHAP?)
-                        *
-                        *      We also do NOT tunnel the EAP-Success
-                        *      attribute back to the client, as the client
-                        *      can figure it out, from the non-tunneled
-                        *      EAP-Success packet.
+                        *      Seed the proxy packet with the
+                        *      tunneled request.
                         */
-                       pairmove2(&vp, &fake->reply->vps, PW_EAP_MESSAGE);
-                       pairfree(&vp);
+                       rad_assert(request->proxy == NULL);
+                       request->proxy = fake->packet;
+                       fake->packet = NULL;
+                       rad_free(&fake->reply);
+                       fake->reply = NULL;
 
                        /*
-                        *      If we've been told to use the attributes from
-                        *      the reply, then do so.
-                        *
-                        *      WARNING: This may leak information about the
-                        *      tunneled user!
+                        *      Set up the callbacks for the tunnel
                         */
-                       if (t->use_tunneled_reply) {
-                               pairadd(&request->reply->vps, fake->reply->vps);
-                               fake->reply->vps = NULL;
-                       }
-               }
+                       tunnel = rad_malloc(sizeof(*tunnel));
+                       memset(tunnel, 0, sizeof(*tunnel));
 
-               /*
-                *      Handle the ACK, by tunneling any necessary reply
-                *      VP's back to the client.
-                */
-               if (vp) {
-                       vp2diameter(tls_session, vp);
-                       pairfree(&vp);
-               }
-               
-               /*
-                *      Handle Access-Challenge, but only if we
-                *      send tunneled reply data.  This is because
-                *      an Access-Challenge means that we MUST tunnel
-                *      a Reply-Message to the client.
-                */
-       } else if (fake->reply->code == PW_ACCESS_CHALLENGE) {
-               DEBUG2("  TTLS: Got tunneled Access-Challenge");
+                       tunnel->tls_session = tls_session;
+                       tunnel->callback = eapttls_postproxy;
 
-               /*
-                *      Keep the State attribute, if necessary.
-                *
-                *      Get rid of the old State, too.
-                */
-               pairfree(&t->state);
-               pairmove2(&t->state, &fake->reply->vps, PW_STATE);
+                       /*
+                        *      Associate the callback with the request.
+                        */
+                       rcode = request_data_add(request,
+                                                request->proxy,
+                                                REQUEST_DATA_EAP_TUNNEL_CALLBACK,
+                                                tunnel, free);
+                       rad_assert(rcode == 0);
+                       
+                       /*
+                        *      rlm_eap.c has taken care of associating
+                        *      the handler with the fake request.
+                        *
+                        *      So we associate the fake request with
+                        *      this request.
+                        */
+                       rcode = request_data_add(request,
+                                                request->proxy,
+                                                REQUEST_DATA_EAP_MSCHAP_TUNNEL_CALLBACK,
+                                                fake, my_request_free);
+                       rad_assert(rcode == 0);
+                       fake = NULL;
 
-               /*
-                *      We should really be a bit smarter about this,
-                *      and move over only those attributes which
-                *      are relevant to the authentication request,
-                *      but that's a lot more work, and this "dumb"
-                *      method works in 99.9% of the situations.
-                */
-               vp = NULL;
-               pairmove2(&vp, &fake->reply->vps, PW_EAP_MESSAGE);
+                       /*
+                        *      Didn't authenticate the packet, but
+                        *      we're proxying it.
+                        */
+                       rcode = PW_STATUS_CLIENT;
 
-               /*
-                *      There MUST be a Reply-Message in the challenge,
-                *      which we tunnel back to the client.
-                *
-                *      If there isn't one in the reply VP's, then
-                *      we MUST create one, with an empty string as
-                *      it's value.
-                */
-               pairmove2(&vp, &fake->reply->vps, PW_REPLY_MESSAGE);
+               } else {
+                       DEBUG2("  TTLS: No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.",
+                              request->number);
+                       rcode = PW_AUTHENTICATION_REJECT;
+               }
+               break;
 
+       default:
                /*
-                *      Handle the ACK, by tunneling any necessary reply
-                *      VP's back to the client.
+                *      Returns RLM_MODULE_FOO, and we want to return
+                *      PW_FOO
                 */
-               if (vp) {
-                       vp2diameter(tls_session, vp);
-                       pairfree(&vp);
+               rcode = process_reply(handler, tls_session, request,
+                                     fake->reply);
+               switch (rcode) {
+               case RLM_MODULE_REJECT:
+                       rcode = PW_AUTHENTICATION_REJECT;
+                       break;
+                       
+               case RLM_MODULE_HANDLED:
+                       rcode = PW_ACCESS_CHALLENGE;
+                       break;
+                       
+               case RLM_MODULE_OK:
+                       rcode = PW_AUTHENTICATION_ACK;
+                       break;
+                       
+               default:
+                       rcode = PW_AUTHENTICATION_REJECT;
+                       break;
                }
-               rcode = fake->reply->code;
-
-       } else {
-               DEBUG2("  TTLS: Rejecting tunneled user");
+               break;
        }
-       
+
        request_free(&fake);
 
        return rcode;
 }
-