check before dereference. Found by PVS-Studio
[freeradius.git] / src / modules / rlm_eap / libeap / eap_tls.c
index cd95bec..a08cd50 100644 (file)
  *
  *  TLS Packet Format in EAP
  *  --- ------ ------ -- ---
- * 0                   1                   2                   3
+ * 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
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |     Code      |   Identifier  |            Length             |
+ * |     Code      |   Identifier  |       Length           |
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  * |     Type      |     Flags     |      TLS Message Length
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- * |     TLS Message Length        |       TLS Data...
+ * |     TLS Message Length    |       TLS Data...
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  */
 
-#include <freeradius-devel/ident.h>
 RCSID("$Id$")
+USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
 
-#include <freeradius-devel/autoconf.h>
 #include <assert.h>
-#include "eap_tls.h"
 
+#include "eap_tls.h"
 /*
- *      Allocate a new TLS_PACKET
+ *     Send an initial eap-tls request to the peer.
+ *
+ *     Frame eap reply packet.
+ *     len = header + type + tls_typedata
+ *     tls_typedata = flags(Start (S) bit set, and no data)
+ *
+ *     Once having received the peer's Identity, the EAP server MUST
+ *     respond with an EAP-TLS/Start packet, which is an
+ *     EAP-Request packet with EAP-Type=EAP-TLS, the Start (S) bit
+ *     set, and no data.  The EAP-TLS conversation will then begin,
+ *     with the peer sending an EAP-Response packet with
+ *     EAP-Type = EAP-TLS.  The data field of that packet will
+ *     be the TLS data.
+ *
+ *     Fragment length is Framed-MTU - 4.
  */
-EAPTLS_PACKET *eaptls_alloc(void)
+tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert)
 {
-       EAPTLS_PACKET   *rp;
+       tls_session_t   *ssn;
+       REQUEST         *request = handler->request;
+
+       handler->tls = true;
 
-       if ((rp = malloc(sizeof(EAPTLS_PACKET))) == NULL) {
-               radlog(L_ERR, "rlm_eap_tls: out of memory");
+       /*
+        *      Every new session is started only from EAP-TLS-START.
+        *      Before Sending EAP-TLS-START, open a new SSL session.
+        *      Create all the required data structures & store them
+        *      in Opaque.  So that we can use these data structures
+        *      when we get the response
+        */
+       ssn = tls_new_session(handler, tls_conf, request, client_cert);
+       if (!ssn) {
                return NULL;
        }
-       memset(rp, 0, sizeof(EAPTLS_PACKET));
-       return rp;
-}
-
-/*
- *      Free EAPTLS_PACKET
- */
-void eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr)
-{
-       EAPTLS_PACKET *eaptls_packet;
 
-       if (!eaptls_packet_ptr) return;
-       eaptls_packet = *eaptls_packet_ptr;
-       if (eaptls_packet == NULL) return;
-
-       if (eaptls_packet->data) {
-               free(eaptls_packet->data);
-               eaptls_packet->data = NULL;
-       }
+       /*
+        *      Create a structure for all the items required to be
+        *      verified for each client and set that as opaque data
+        *      structure.
+        *
+        *      NOTE: If we want to set each item sepearately then
+        *      this index should be global.
+        */
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_HANDLER, (void *)handler);
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CONF, (void *)tls_conf);
+       SSL_set_ex_data(ssn->ssl, fr_tls_ex_index_certs, (void *)&(handler->certs));
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_IDENTITY, (void *)&(handler->identity));
+#ifdef HAVE_OPENSSL_OCSP_H
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_STORE, (void *)tls_conf->ocsp_store);
+#endif
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_SSN, (void *)ssn);
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_TALLOC, NULL);
 
-       free(eaptls_packet);
-       *eaptls_packet_ptr = NULL;
+       return talloc_steal(handler, ssn); /* ssn */
 }
 
 /*
@@ -88,7 +109,7 @@ int eaptls_start(EAP_DS *eap_ds, int peap_flag)
 {
        EAPTLS_PACKET   reply;
 
-       reply.code = EAPTLS_START;
+       reply.code = FR_TLS_START;
        reply.length = TLS_HEADER_LEN + 1/*flags*/;
 
        reply.flags = peap_flag;
@@ -102,84 +123,20 @@ int eaptls_start(EAP_DS *eap_ds, int peap_flag)
        return 1;
 }
 
-int eaptls_success(EAP_HANDLER *handler, int peap_flag)
+int eaptls_success(eap_handler_t *handler, int peap_flag)
 {
        EAPTLS_PACKET   reply;
-       VALUE_PAIR *vp, *vps = NULL;
        REQUEST *request = handler->request;
        tls_session_t *tls_session = handler->opaque;
 
-       reply.code = EAPTLS_SUCCESS;
+       handler->finished = true;
+       reply.code = FR_TLS_SUCCESS;
        reply.length = TLS_HEADER_LEN;
        reply.flags = peap_flag;
        reply.data = NULL;
        reply.dlen = 0;
 
-       /*
-        *      If there's no session resumption, delete the entry
-        *      from the cache.  This means either it's disabled
-        *      globally for this SSL context, OR we were told to
-        *      disable it for this user.
-        *
-        *      This also means you can't turn it on just for one
-        *      user.
-        */
-       if ((!tls_session->allow_session_resumption) ||
-           (((vp = pairfind(request->config_items, 1127)) != NULL) &&
-            (vp->vp_integer == 0))) {
-               SSL_CTX_remove_session(tls_session->ctx,
-                                      tls_session->ssl->session);
-               tls_session->allow_session_resumption = 0;
-
-               /*
-                *      If we're in a resumed session and it's
-                *      not allowed, 
-                */
-               if (SSL_session_reused(tls_session->ssl)) {
-                       RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed.");
-                       return eaptls_fail(handler, peap_flag);
-               }
-               
-               /*
-                *      Else resumption IS allowed, so we store the
-                *      user data in the cache.
-                */
-       } else if (!SSL_session_reused(tls_session->ssl)) {
-               RDEBUG2("Saving response in the cache");
-               
-               vp = paircopy2(request->reply->vps, PW_USER_NAME);
-               pairadd(&vps, vp);
-               
-               vp = paircopy2(request->packet->vps, PW_STRIPPED_USER_NAME);
-               pairadd(&vps, vp);
-               
-               if (vps) {
-                       SSL_SESSION_set_ex_data(tls_session->ssl->session,
-                                               eaptls_session_idx, vps);
-               }
-
-               /*
-                *      Else the session WAS allowed.  Copy the cached
-                *      reply.
-                */
-       } else {
-              
-               vp = SSL_SESSION_get_ex_data(tls_session->ssl->session,
-                                            eaptls_session_idx);
-               if (!vp) {
-                       RDEBUG("WARNING: No information in cached session!");
-               } else {
-                       RDEBUG("Adding cached attributes to the reply:");
-                       debug_pair_list(vp);
-                       pairadd(&request->reply->vps, paircopy(vp));
-
-                       /*
-                        *      Mark the request as resumed.
-                        */
-                       vp = pairmake("EAP-Session-Resumed", "0", T_OP_SET);
-                       if (vp) pairadd(&request->packet->vps, vp);
-               }
-       }
+       tls_success(tls_session, request);
 
        /*
         *      Call compose AFTER checking for cached data.
@@ -190,30 +147,30 @@ int eaptls_success(EAP_HANDLER *handler, int peap_flag)
         *      Automatically generate MPPE keying material.
         */
        if (tls_session->prf_label) {
-               eaptls_gen_mppe_keys(&handler->request->reply->vps,
+               eaptls_gen_mppe_keys(handler->request,
                                     tls_session->ssl, tls_session->prf_label);
        } else {
-               RDEBUG("WARNING: Not adding MPPE keys because there is no PRF label");
+               RWDEBUG("Not adding MPPE keys because there is no PRF label");
        }
 
+       eaptls_gen_eap_key(handler->request->reply, tls_session->ssl,
+                          handler->type);
        return 1;
 }
 
-int eaptls_fail(EAP_HANDLER *handler, int peap_flag)
+int eaptls_fail(eap_handler_t *handler, int peap_flag)
 {
        EAPTLS_PACKET   reply;
        tls_session_t *tls_session = handler->opaque;
 
-       reply.code = EAPTLS_FAIL;
+       handler->finished = true;
+       reply.code = FR_TLS_FAIL;
        reply.length = TLS_HEADER_LEN;
        reply.flags = peap_flag;
        reply.data = NULL;
        reply.dlen = 0;
 
-       /*
-        *      Force the session to NOT be cached.
-        */
-       SSL_CTX_remove_session(tls_session->ctx, tls_session->ssl->session);
+       tls_fail(tls_session);
 
        eaptls_compose(handler->eap_ds, &reply);
 
@@ -243,11 +200,11 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
                EVERY packet we send and add corresponding
                "TLS Message Length" field.
 
-       length_flag = TRUE;
+       length_flag = true;
                This means we include L flag and "TLS Msg Len" in EVERY
                packet we send out.
 
-       length_flag = FALSE;
+       length_flag = false;
                This means we include L flag and "TLS Msg Len" **ONLY**
                in First packet of a fragment series. We do not use
                it anywhere else.
@@ -262,12 +219,12 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
                ssn->tls_msg_len = ssn->dirty_out.used;
        }
 
-       reply.code = EAPTLS_REQUEST;
+       reply.code = FR_TLS_REQUEST;
        reply.flags = ssn->peap_flag;
 
        /* Send data, NOT more than the FRAGMENT size */
-       if (ssn->dirty_out.used > ssn->offset) {
-               size = ssn->offset;
+       if (ssn->dirty_out.used > ssn->mtu) {
+               size = ssn->mtu;
                reply.flags = SET_MORE_FRAGMENTS(reply.flags);
                /* Length MUST be included if it is the First Fragment */
                if (ssn->fragment == 0) {
@@ -282,7 +239,9 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
        reply.dlen = lbit + size;
        reply.length = TLS_HEADER_LEN + 1/*flags*/ + reply.dlen;
 
-       reply.data = malloc(reply.dlen);
+       reply.data = talloc_array(eap_ds, uint8_t, reply.length);
+       if (!reply.data) return 0;
+
        if (lbit) {
                nlen = htonl(ssn->tls_msg_len);
                memcpy(reply.data, &nlen, lbit);
@@ -291,76 +250,12 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
        (ssn->record_minus)(&ssn->dirty_out, reply.data + lbit, size);
 
        eaptls_compose(eap_ds, &reply);
-       free(reply.data);
+       talloc_free(reply.data);
        reply.data = NULL;
 
        return 1;
 }
 
-/*
- * Acknowledge received is for one of the following messages sent earlier
- * 1. Handshake completed Message, so now send, EAP-Success
- * 2. Alert Message, now send, EAP-Failure
- * 3. Fragment Message, now send, next Fragment
- */
-static eaptls_status_t eaptls_ack_handler(EAP_HANDLER *handler)
-{
-       tls_session_t *tls_session;
-       REQUEST *request = handler->request;
-
-       tls_session = (tls_session_t *)handler->opaque;
-       if (tls_session == NULL){
-               radlog_request(L_ERR, 0, request, "FAIL: Unexpected ACK received.  Could not obtain session information.");
-               return EAPTLS_FAIL;
-       }
-       if (tls_session->info.initialized == 0) {
-               RDEBUG("No SSL info available. Waiting for more SSL data.");
-               return EAPTLS_REQUEST;
-       }
-       if ((tls_session->info.content_type == handshake) &&
-           (tls_session->info.origin == 0)) {
-               radlog_request(L_ERR, 0, request, "FAIL: ACK without earlier message.");
-               return EAPTLS_FAIL;
-       }
-
-       switch (tls_session->info.content_type) {
-       case alert:
-               RDEBUG2("ACK alert");
-               eaptls_fail(handler, tls_session->peap_flag);
-               return EAPTLS_FAIL;
-
-       case handshake:
-               if (tls_session->info.handshake_type == finished) {
-                       RDEBUG2("ACK handshake is finished");
-
-                       /* 
-                        *      From now on all the content is
-                        *      application data set it here as nobody else
-                        *      sets it.
-                        */
-                       tls_session->info.content_type = application_data;
-                       return EAPTLS_SUCCESS;
-               } /* else more data to send */
-
-               RDEBUG2("ACK handshake fragment handler");
-               /* Fragmentation handler, send next fragment */
-               return EAPTLS_REQUEST;
-
-       case application_data:
-               RDEBUG2("ACK handshake fragment handler in application data");
-               return EAPTLS_REQUEST;
-                                               
-               /*
-                *      For the rest of the conditions, switch over
-                *      to the default section below.
-                */
-       default:
-               RDEBUG2("ACK default");
-               radlog_request(L_ERR, 0, request, "Invalid ACK received: %d",
-                      tls_session->info.content_type);
-               return EAPTLS_FAIL;
-       }
-}
 
 /*
  *     Similarly, when the EAP server receives an EAP-Response with
@@ -377,17 +272,19 @@ static eaptls_status_t eaptls_ack_handler(EAP_HANDLER *handler)
  *     fragments to receive to make the complete
  *     TLS-record/TLS-Message
  */
-static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
+static int eaptls_send_ack(eap_handler_t *handler, int peap_flag)
 {
        EAPTLS_PACKET   reply;
+       REQUEST         *request = handler->request;
 
-       reply.code = EAPTLS_ACK;
+       RDEBUG2("ACKing Peer's TLS record fragment");
+       reply.code = FR_TLS_ACK;
        reply.length = TLS_HEADER_LEN + 1/*flags*/;
        reply.flags = peap_flag;
        reply.data = NULL;
        reply.dlen = 0;
 
-       eaptls_compose(eap_ds, &reply);
+       eaptls_compose(handler->eap_ds, &reply);
 
        return 1;
 }
@@ -401,12 +298,14 @@ static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
  *     EAP-Type=EAP-TLS and no data. This serves as a fragment
  *     ACK. The EAP peer MUST wait.
  */
-static eaptls_status_t eaptls_verify(EAP_HANDLER *handler)
+static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
 {
-       EAP_DS *eap_ds = handler->eap_ds;
-       EAP_DS *prev_eap_ds = handler->prev_eapds;
-       eaptls_packet_t *eaptls_packet, *eaptls_prev = NULL;
-       REQUEST *request = handler->request;
+       EAP_DS                  *eap_ds = handler->eap_ds;
+       tls_session_t           *tls_session = handler->opaque;
+       EAP_DS                  *prev_eap_ds = handler->prev_eapds;
+       eaptls_packet_t         *eaptls_packet, *eaptls_prev = NULL;
+       REQUEST                 *request = handler->request;
+       size_t                  frag_len;
 
        /*
         *      We don't check ANY of the input parameters.  It's all
@@ -417,15 +316,25 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler)
         *      NULL, of if it's NOT an EAP-Response, or if the packet
         *      is too short.  See eap_validation()., in ../../eap.c
         *
-        *      Also, eaptype_select() takes care of selecting the
+        *      Also, eap_method_select() takes care of selecting the
         *      appropriate type, so we don't need to check
-        *      eap_ds->response->type.type == PW_EAP_TLS, or anything
+        *      eap_ds->response->type.num == PW_EAP_TLS, or anything
         *      else.
         */
        eaptls_packet = (eaptls_packet_t *)eap_ds->response->type.data;
        if (prev_eap_ds && prev_eap_ds->response)
                eaptls_prev = (eaptls_packet_t *)prev_eap_ds->response->type.data;
 
+       if (eaptls_packet) {
+               /*
+                *      First output the flags (for debugging)
+                */
+               RDEBUG3("Peer sent flags %c%c%c",
+                       TLS_START(eaptls_packet->flags) ? 'S' : '-',
+                       TLS_MORE_FRAGMENTS(eaptls_packet->flags) ? 'M' : '-',
+                       TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 'L' : '-');
+       }
+
        /*
         *      check for ACK
         *
@@ -434,39 +343,33 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler)
         *
         *      Find if this is a reply to the previous request sent
         */
-       if ((eaptls_packet == NULL) ||
+       if ((!eaptls_packet) ||
            ((eap_ds->response->length == EAP_HEADER_LEN + 2) &&
             ((eaptls_packet->flags & 0xc0) == 0x00))) {
 
-#if 0
-               /*
-                *      Un-comment this for TLS inside of TTLS/PEAP
-                */
-               RDEBUG2("Received EAP-TLS ACK message");
-               return eaptls_ack_handler(handler);
-#else
-               if (prev_eap_ds->request->id == eap_ds->response->id) {
-                       /*
-                        *      Run the ACK handler directly from here.
-                        */
-                       RDEBUG2("Received TLS ACK");
-                       return eaptls_ack_handler(handler);
+               if (prev_eap_ds && (prev_eap_ds->request->id == eap_ds->response->id)) {
+                       return tls_ack_handler(handler->opaque, request);
                } else {
-                       radlog_request(L_ERR, 0, request, "Received Invalid TLS ACK");
-                       return EAPTLS_INVALID;
+                       REDEBUG("Received Invalid TLS ACK");
+                       return FR_TLS_INVALID;
                }
-#endif
        }
 
        /*
         *      We send TLS_START, but do not receive it.
         */
        if (TLS_START(eaptls_packet->flags)) {
-               RDEBUG("Received unexpected EAP-TLS Start message");
-               return EAPTLS_INVALID;
+               REDEBUG("Peer sent EAP-TLS Start message (only the server is allowed to do this)");
+               return FR_TLS_INVALID;
        }
 
        /*
+        *      Calculate this fragment's length
+        */
+       frag_len = eap_ds->response->length -
+                  (EAP_HEADER_LEN + (TLS_LENGTH_INCLUDED(eaptls_packet->flags) ? 6 : 2));
+
+       /*
         *      The L bit (length included) is set to indicate the
         *      presence of the four octet TLS Message Length field,
         *      and MUST be set for the first fragment of a fragmented
@@ -480,58 +383,119 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler)
         *      from a fragment acknowledgement.
         */
        if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) {
-               DEBUG2("  TLS Length %d",
-                      eaptls_packet->data[2] * 256 | eaptls_packet->data[3]);
+               size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3];
+
+               if (frag_len > total_len) {
+                       RWDEBUG("TLS fragment length (%zu bytes) greater than TLS record length (%zu bytes)", frag_len,
+                               total_len);
+               }
+
+               RDEBUG2("Peer indicated complete TLS record size will be %zu bytes", total_len);
                if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
                        /*
-                        * FIRST_FRAGMENT is identified
-                        * 1. If there is no previous EAP-response received.
-                        * 2. If EAP-response received, then its M bit not set.
-                        *      (It is because Last fragment will not have M bit set)
+                        *      The supplicant is free to send fragments of wildly varying
+                        *      lengths, but the vast majority won't.
+                        *
+                        *      In this calculation we take into account the fact that the future
+                        *      fragments are likely to be 4 bytes larger than the initial one
+                        *      as they won't contain the length field.
                         */
-                       if (!prev_eap_ds ||
-                           (prev_eap_ds->response == NULL) ||
-                           (eaptls_prev == NULL) ||
+                       if (frag_len + 4) {     /* check for wrap, else clang scan gets excited */
+                               RDEBUG2("Expecting %i TLS record fragments",
+                                       (int)((((total_len - frag_len) + ((frag_len + 4) - 1)) / (frag_len + 4)) + 1));
+                       }
+
+                       /*
+                        *      FIRST_FRAGMENT is identified
+                        *      1. If there is no previous EAP-response received.
+                        *      2. If EAP-response received, then its M bit not set.
+                        *         (It is because Last fragment will not have M bit set)
+                        */
+                       if (!prev_eap_ds || (!prev_eap_ds->response) || (!eaptls_prev) ||
                            !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) {
+                               RDEBUG2("Got first TLS record fragment (%zu bytes).  Peer indicated more fragments "
+                                       "to follow", frag_len);
+                               tls_session->tls_record_in_total_len = total_len;
+                               tls_session->tls_record_in_recvd_len = frag_len;
+
+                               return FR_TLS_FIRST_FRAGMENT;
+                       }
 
-                               RDEBUG2("Received EAP-TLS First Fragment of the message");
-                               return EAPTLS_FIRST_FRAGMENT;
-                       } else {
+                       RDEBUG2("Got additional TLS record fragment with length (%zu bytes).  "
+                               "Peer indicated more fragments to follow", frag_len);
 
-                               RDEBUG2("More Fragments with length included");
-                               return EAPTLS_MORE_FRAGMENTS_WITH_LENGTH;
+                       /*
+                        *      Check we've not exceeded the originally indicated TLS record size.
+                        */
+                       tls_session->tls_record_in_recvd_len += frag_len;
+                       if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) {
+                               RWDEBUG("Total received TLS record fragments (%zu bytes), exceeds "
+                                       "total TLS record length (%zu bytes)", frag_len, total_len);
                        }
-               } else {
-                       RDEBUG2("Length Included");
-                       return EAPTLS_LENGTH_INCLUDED;
+
+                       return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH;
+               }
+
+               /*
+                *      If it's a complete record, our fragment size should match the
+                *      value of the four octet TLS length field.
+                */
+               if (total_len != frag_len) {
+                       RWDEBUG("Peer indicated no more fragments, but TLS record length (%zu bytes) "
+                               "does not match EAP-TLS data length (%zu bytes)", total_len, frag_len);
+               }
+
+               tls_session->tls_record_in_total_len = total_len;
+               tls_session->tls_record_in_recvd_len = frag_len;
+               RDEBUG2("Got complete TLS record (%zu bytes)", frag_len);
+               return FR_TLS_LENGTH_INCLUDED;
+       }
+
+       /*
+        *      The previous packet had the M flags set, but this one doesn't,
+        *      this must be the final record fragment
+        */
+       if ((eaptls_prev && TLS_MORE_FRAGMENTS(eaptls_prev->flags)) && !TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
+               RDEBUG2("Got final TLS record fragment (%zu bytes)", frag_len);
+               tls_session->tls_record_in_recvd_len += frag_len;
+               if (tls_session->tls_record_in_recvd_len != tls_session->tls_record_in_total_len) {
+                       RWDEBUG("Total received TLS record fragments (%zu bytes), does not equal indicated "
+                               "TLS record length (%zu bytes)",
+                               tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
                }
        }
 
        if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
-               RDEBUG2("More fragments to follow");
-               return EAPTLS_MORE_FRAGMENTS;
+               RDEBUG2("Got additional TLS record fragment (%zu bytes).  Peer indicated more fragments to follow",
+                       frag_len);
+               tls_session->tls_record_in_recvd_len += frag_len;
+               if (tls_session->tls_record_in_recvd_len > tls_session->tls_record_in_total_len) {
+                       RWDEBUG("Total received TLS record fragments (%zu bytes), exceeds "
+                               "indicated TLS record length (%zu bytes)",
+                               tls_session->tls_record_in_recvd_len, tls_session->tls_record_in_total_len);
+               }
+               return FR_TLS_MORE_FRAGMENTS;
        }
 
        /*
-        *      None of the flags are set, but it's still a valid
-        *      EAPTLS packet.
+        *      None of the flags are set, but it's still a valid EAP-TLS packet.
         */
-       return EAPTLS_OK;
+       return FR_TLS_OK;
 }
 
 /*
  * EAPTLS_PACKET
- * code    EAP-code
- * id      EAP-id
- * length = code + id + length + flags + tlsdata
- *        =  1   +  1 +   2    +  1    +  X
- * length = EAP-length - 1(EAP-Type = 1 octet)
- * flags  = EAP-typedata[0] (1 octet)
- * dlen   = EAP-typedata[1-4] (4 octets), if L flag set
- *        = length - 5(code+id+length+flags), otherwise
- * data   = EAP-typedata[5-n], if L flag set
- *        = EAP-typedata[1-n], otherwise
- * packet = EAP-typedata (complete typedata)
+ * code    = EAP-code
+ * id      = EAP-id
+ * length  = code + id + length + flags + tlsdata
+ *        =  1   +  1 +   2    +  1    +  X
+ * length  = EAP-length - 1(EAP-Type = 1 octet)
+ * flags   = EAP-typedata[0] (1 octet)
+ * dlen    = EAP-typedata[1-4] (4 octets), if L flag set
+ *        = length - 5(code+id+length+flags), otherwise
+ * data    = EAP-typedata[5-n], if L flag set
+ *        = EAP-typedata[1-n], otherwise
+ * packet  = EAP-typedata (complete typedata)
  *
  * Points to consider during EAP-TLS data extraction
  * 1. In the received packet, No data will be present incase of ACK-NAK
@@ -539,29 +503,28 @@ static eaptls_status_t eaptls_verify(EAP_HANDLER *handler)
  *
  *  RFC 2716 Section 4.2.  PPP EAP TLS Request Packet
  *
- *  0                   1                   2                   3
+ *  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
  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *  |     Code      |   Identifier  |            Length             |
+ *  |     Code      |   Identifier  |      Length           |
  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *  |     Type      |     Flags     |      TLS Message Length
  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *  |     TLS Message Length        |       TLS Data...
+ *  |     TLS Message Length   |       TLS Data...
  *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *
  *  The Length field is two octets and indicates the length of the EAP
  *  packet including the Code, Identifir, Length, Type, and TLS data
  *  fields.
  */
-static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_status_t status)
+static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_status_t status)
 {
        EAPTLS_PACKET   *tlspacket;
        uint32_t        data_len = 0;
        uint32_t        len = 0;
        uint8_t         *data = NULL;
 
-       if (status  == EAPTLS_INVALID)
-               return NULL;
+       if (status == FR_TLS_INVALID) return NULL;
 
        /*
         *      The main EAP code & eaptls_verify() take care of
@@ -578,8 +541,8 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
         */
        assert(eap_ds->response->length > 2);
 
-       tlspacket = eaptls_alloc();
-       if (tlspacket == NULL) return NULL;
+       tlspacket = talloc(eap_ds, EAPTLS_PACKET);
+       if (!tlspacket) return NULL;
 
        /*
         *      Code & id for EAPTLS & EAP are same
@@ -599,8 +562,9 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
         */
        if (TLS_LENGTH_INCLUDED(tlspacket->flags) &&
            (tlspacket->length < 5)) { /* flags + TLS message length */
-               RDEBUG("Invalid EAP-TLS packet received.  (Length bit is set, but no length was found.)");
-               eaptls_free(&tlspacket);
+               REDEBUG("Invalid EAP-TLS packet received:  Length bit is set, "
+                       "but packet too short to contain length field");
+               talloc_free(tlspacket);
                return NULL;
        }
 
@@ -610,28 +574,17 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
         *
         *      Likewise, if the EAP packet says N bytes, and the TLS
         *      packet says there's fewer bytes, it's a problem.
-        *
-        *      FIXME: Try to ensure that the claimed length is
-        *      consistent across multiple TLS fragments.
         */
        if (TLS_LENGTH_INCLUDED(tlspacket->flags)) {
                memcpy(&data_len, &eap_ds->response->type.data[1], 4);
                data_len = ntohl(data_len);
                if (data_len > MAX_RECORD_SIZE) {
-                       RDEBUG("The EAP-TLS packet will contain more data than we can process.");
-                       eaptls_free(&tlspacket);
-                       return NULL;
-               }
-
-#if 0
-               DEBUG2(" TLS: %d %d\n", data_len, tlspacket->length);
-
-               if (data_len < tlspacket->length) {
-                       RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet.");
-                       eaptls_free(&tlspacket);
+                       REDEBUG("Reassembled TLS record will be %u bytes, "
+                               "greater than our maximum record size (" STRINGIFY(MAX_RECORD_SIZE) " bytes)",
+                               data_len);
+                       talloc_free(tlspacket);
                        return NULL;
                }
-#endif
        }
 
        switch (status) {
@@ -644,12 +597,12 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
         *      Dynamic allocation of buffers as & when we know the
         *      length should solve the problem.
         */
-       case EAPTLS_FIRST_FRAGMENT:
-       case EAPTLS_LENGTH_INCLUDED:
-       case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH:
+       case FR_TLS_FIRST_FRAGMENT:
+       case FR_TLS_LENGTH_INCLUDED:
+       case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH:
                if (tlspacket->length < 5) { /* flags + TLS message length */
-                       RDEBUG("Invalid EAP-TLS packet received.  (Expected length, got none.)");
-                       eaptls_free(&tlspacket);
+                       REDEBUG("Invalid EAP-TLS packet received: Expected length, got none");
+                       talloc_free(tlspacket);
                        return NULL;
                }
 
@@ -674,24 +627,24 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
                /*
                 *      Data length is implicit, from the EAP header.
                 */
-       case EAPTLS_MORE_FRAGMENTS:
-       case EAPTLS_OK:
+       case FR_TLS_MORE_FRAGMENTS:
+       case FR_TLS_OK:
                data_len = eap_ds->response->type.length - 1/*flags*/;
                data = eap_ds->response->type.data + 1/*flags*/;
                break;
 
        default:
-               RDEBUG("Invalid EAP-TLS packet received");
-               eaptls_free(&tlspacket);
+               REDEBUG("Invalid EAP-TLS packet received");
+               talloc_free(tlspacket);
                return NULL;
        }
 
        tlspacket->dlen = data_len;
        if (data_len) {
-               tlspacket->data = (unsigned char *)malloc(data_len);
-               if (tlspacket->data == NULL) {
-                       RDEBUG("out of memory");
-                       eaptls_free(&tlspacket);
+               tlspacket->data = talloc_array(tlspacket, uint8_t,
+                                              data_len);
+               if (!tlspacket->data) {
+                       talloc_free(tlspacket);
                        return NULL;
                }
                memcpy(tlspacket->data, data, data_len);
@@ -724,21 +677,19 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, eaptls_st
  *     SSL_CTX (internally) or TLS module(explicitly). If TLS module,
  *     then how to let SSL API know about these sessions.)
  */
-static eaptls_status_t eaptls_operation(eaptls_status_t status,
-                                       EAP_HANDLER *handler)
+static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *handler)
 {
-       tls_session_t *tls_session;
+       REQUEST         *request = handler->request;
+       tls_session_t   *tls_session = handler->opaque;
 
-       tls_session = (tls_session_t *)handler->opaque;
-
-       if ((status == EAPTLS_MORE_FRAGMENTS) ||
-           (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) ||
-           (status == EAPTLS_FIRST_FRAGMENT)) {
+       if ((status == FR_TLS_MORE_FRAGMENTS) ||
+           (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
+           (status == FR_TLS_FIRST_FRAGMENT)) {
                /*
                 *      Send the ACK.
                 */
-               eaptls_send_ack(handler->eap_ds, tls_session->peap_flag);
-               return EAPTLS_HANDLED;
+               eaptls_send_ack(handler, tls_session->peap_flag);
+               return FR_TLS_HANDLED;
 
        }
 
@@ -753,10 +704,10 @@ static eaptls_status_t eaptls_operation(eaptls_status_t status,
         *      If more info
         *      is required then send another request.
         */
-       if (!tls_handshake_recv(tls_session)) {
-               DEBUG2("TLS receive handshake failed during operation");
-               eaptls_fail(handler, tls_session->peap_flag);
-               return EAPTLS_FAIL;
+       if (!tls_handshake_recv(handler->request, tls_session)) {
+               REDEBUG("TLS receive handshake failed during operation");
+               tls_fail(tls_session);
+               return FR_TLS_FAIL;
        }
 
        /*
@@ -766,30 +717,30 @@ static eaptls_status_t eaptls_operation(eaptls_status_t status,
         */
        if (tls_session->dirty_out.used > 0) {
                eaptls_request(handler->eap_ds, tls_session);
-               return EAPTLS_HANDLED;
+               return FR_TLS_HANDLED;
        }
-               
-       /* 
+
+       /*
         *      If there is no data to send i.e
         *      dirty_out.used <=0 and if the SSL
         *      handshake is finished, then return a
         *      EPTLS_SUCCESS
         */
-       
+
        if (SSL_is_init_finished(tls_session->ssl)) {
                /*
                 *      Init is finished.  The rest is
                 *      application data.
                 */
-               tls_session->info.content_type = application_data; 
-               return EAPTLS_SUCCESS;
+               tls_session->info.content_type = application_data;
+               return FR_TLS_SUCCESS;
        }
-       
+
        /*
         *      Who knows what happened...
         */
-       DEBUG2("TLS failed during operation");
-       return EAPTLS_FAIL;
+       REDEBUG("TLS failed during operation");
+       return FR_TLS_FAIL;
 }
 
 
@@ -820,67 +771,80 @@ static eaptls_status_t eaptls_operation(eaptls_status_t status,
 /*
  *     Process an EAP request
  */
-eaptls_status_t eaptls_process(EAP_HANDLER *handler)
+fr_tls_status_t eaptls_process(eap_handler_t *handler)
 {
        tls_session_t *tls_session = (tls_session_t *) handler->opaque;
        EAPTLS_PACKET   *tlspacket;
-       eaptls_status_t status;
+       fr_tls_status_t status;
        REQUEST *request = handler->request;
 
-       RDEBUG2("processing EAP-TLS");
+       if (!request) return FR_TLS_FAIL;
+
+       RDEBUG2("Continuing EAP-TLS");
 
-       /* This case is when SSL generates Alert then we
-        * send that alert to the client and then send the EAP-Failure
+       SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request);
+
+       if (handler->certs) fr_pair_add(&request->packet->vps,
+                                   fr_pair_list_copy(request->packet, handler->certs));
+
+       /*
+        *      This case is when SSL generates Alert then we
+        *      send that alert to the client and then send the EAP-Failure
         */
        status = eaptls_verify(handler);
-       RDEBUG2("eaptls_verify returned %d\n", status);
+       if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) {
+               REDEBUG("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       } else {
+               RDEBUG2("[eaptls verify] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>"));
+       }
 
        switch (status) {
        default:
-       case EAPTLS_INVALID:
-       case EAPTLS_FAIL:
+       case FR_TLS_INVALID:
+       case FR_TLS_FAIL:
 
-               /*
-                *      Success means that we're done the initial
-                *      handshake.  For TTLS, this means send stuff
-                *      back to the client, and the client sends us
-                *      more tunneled data.
-                */
-       case EAPTLS_SUCCESS:
-               return status;
-               break;
+       /*
+        *      Success means that we're done the initial
+        *      handshake.  For TTLS, this means send stuff
+        *      back to the client, and the client sends us
+        *      more tunneled data.
+        */
+       case FR_TLS_SUCCESS:
+               goto done;
 
-               /*
-                *      Normal TLS request, continue with the "get rest
-                *      of fragments" phase.
-                */
-       case EAPTLS_REQUEST:
+       /*
+        *      Normal TLS request, continue with the "get rest
+        *      of fragments" phase.
+        */
+       case FR_TLS_REQUEST:
                eaptls_request(handler->eap_ds, tls_session);
-               return EAPTLS_HANDLED;
-               break;
+               status = FR_TLS_HANDLED;
+               goto done;
 
-               /*
-                *      The handshake is done, and we're in the "tunnel
-                *      data" phase.
-                */
-       case EAPTLS_OK:
+       /*
+        *      The handshake is done, and we're in the "tunnel
+        *      data" phase.
+        */
+       case FR_TLS_OK:
                RDEBUG2("Done initial handshake");
 
-               /*
-                *      Get the rest of the fragments.
-                */
-       case EAPTLS_FIRST_FRAGMENT:
-       case EAPTLS_MORE_FRAGMENTS:
-       case EAPTLS_LENGTH_INCLUDED:
-       case EAPTLS_MORE_FRAGMENTS_WITH_LENGTH:
+       /*
+        *      Get the rest of the fragments.
+        */
+       case FR_TLS_FIRST_FRAGMENT:
+       case FR_TLS_MORE_FRAGMENTS:
+       case FR_TLS_LENGTH_INCLUDED:
+       case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH:
                break;
        }
 
        /*
         *      Extract the TLS packet from the buffer.
         */
-       if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL)
-               return EAPTLS_FAIL;
+       if ((tlspacket = eaptls_extract(request, handler->eap_ds, status)) == NULL) {
+               status = FR_TLS_FAIL;
+               goto done;
+       }
 
        /*
         *      Get the session struct from the handler
@@ -894,15 +858,16 @@ eaptls_status_t eaptls_process(EAP_HANDLER *handler)
         */
        if (tlspacket->dlen !=
            (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
-               eaptls_free(&tlspacket);
-               RDEBUG("Exceeded maximum record size");
-               return EAPTLS_FAIL;
+               talloc_free(tlspacket);
+               REDEBUG("Exceeded maximum record size");
+               status = FR_TLS_FAIL;
+               goto done;
        }
 
        /*
         *      No longer needed.
         */
-       eaptls_free(&tlspacket);
+       talloc_free(tlspacket);
 
        /*
         *      SSL initalization is done.  Return.
@@ -910,80 +875,90 @@ eaptls_status_t eaptls_process(EAP_HANDLER *handler)
         *      The TLS data will be in the tls_session structure.
         */
        if (SSL_is_init_finished(tls_session->ssl)) {
-               int err;
-
                /*
                 *      The initialization may be finished, but if
                 *      there more fragments coming, then send ACK,
                 *      and get the caller to continue the
                 *      conversation.
-                */     
-               if ((status == EAPTLS_MORE_FRAGMENTS) ||
-                   (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) ||
-                   (status == EAPTLS_FIRST_FRAGMENT)) {
+                */
+               if ((status == FR_TLS_MORE_FRAGMENTS) ||
+                   (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
+                   (status == FR_TLS_FIRST_FRAGMENT)) {
                        /*
                         *      Send the ACK.
                         */
-                       eaptls_send_ack(handler->eap_ds,
-                                       tls_session->peap_flag);
+                       eaptls_send_ack(handler, tls_session->peap_flag);
                        RDEBUG2("Init is done, but tunneled data is fragmented");
-                       return EAPTLS_HANDLED;
+                       status = FR_TLS_HANDLED;
+                       goto done;
                }
 
-               /*      
-                *      Decrypt the complete record.
-                */
-               BIO_write(tls_session->into_ssl, tls_session->dirty_in.data,
-                         tls_session->dirty_in.used);
+               status = tls_application_data(tls_session, request);
+               goto done;
+       }
 
-               /*
-                *      Clear the dirty buffer now that we are done with it
-                *      and init the clean_out buffer to store decrypted data
-                */
-               (tls_session->record_init)(&tls_session->dirty_in);
-               (tls_session->record_init)(&tls_session->clean_out);
+       /*
+        *      Continue the handshake.
+        */
+       status = eaptls_operation(status, handler);
+       if (status == FR_TLS_SUCCESS) {
+#define MAX_SESSION_SIZE (256)
+               VALUE_PAIR *vps;
+               char buffer[2 * MAX_SESSION_SIZE + 1];
 
                /*
-                *      Read (and decrypt) the tunneled data from the
-                *      SSL session, and put it into the decrypted
-                *      data buffer.
+                *      Restore the cached VPs before processing the
+                *      application data.
                 */
-               err = SSL_read(tls_session->ssl, tls_session->clean_out.data,
-                              sizeof(tls_session->clean_out.data));
-
-               if (err < 0) {
-                       RDEBUG("SSL_read Error");
-
-                       switch (SSL_get_error(tls_session->ssl, err)) {
-                       case SSL_ERROR_WANT_READ:
-                       case SSL_ERROR_WANT_WRITE:
-                               RDEBUG("Error in fragmentation logic");
-                               break;
-                       default:
+               tls_session_id(tls_session->ssl_session, buffer, MAX_SESSION_SIZE);
+
+               vps = SSL_SESSION_get_ex_data(tls_session->ssl_session, fr_tls_ex_index_vps);
+               if (!vps) {
+                       RWDEBUG("No information in cached session %s", buffer);
+               } else {
+                       vp_cursor_t cursor;
+                       VALUE_PAIR *vp;
+
+                       RDEBUG("Adding cached attributes from session %s", buffer);
+
+                       /*
+                        *      The cbtls_get_session() function doesn't have
+                        *      access to sock->certs or handler->certs, which
+                        *      is where the certificates normally live.  So
+                        *      the certs are all in the VPS list here, and
+                        *      have to be manually extracted.
+                        */
+                       RINDENT();
+                       for (vp = fr_cursor_init(&cursor, &vps);
+                            vp;
+                            vp = fr_cursor_next(&cursor)) {
                                /*
-                                *      FIXME: Call int_ssl_check?
+                                *      TLS-* attrs get added back to
+                                *      the request list.
                                 */
-                               break;
+                               if ((vp->da->vendor == 0) &&
+                                   (vp->da->attr >= PW_TLS_CERT_SERIAL) &&
+                                   (vp->da->attr <= PW_TLS_CLIENT_CERT_SUBJECT_ALT_NAME_UPN)) {
+                                       /*
+                                        *      Certs already exist.  Don't re-add them.
+                                        */
+                                       if (!handler->certs) {
+                                               rdebug_pair(L_DBG_LVL_2, request, vp, "request:");
+                                               fr_pair_add(&request->packet->vps, fr_pair_copy(request->packet, vp));
+                                       }
+                               } else {
+                                       rdebug_pair(L_DBG_LVL_2, request, vp, "reply:");
+                                       fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
+                               }
                        }
-                       return EAPTLS_FAIL;
+                       REXDENT();
                }
-
-               if (err == 0) {
-                       RDEBUG("WARNING: No data inside of the tunnel.");
-               }
-       
-               /*
-                *      Passed all checks, successfully decrypted data
-                */
-               tls_session->clean_out.used = err;
-               
-               return EAPTLS_OK;
        }
 
-       /*
-        *      Continue the handshake.
-        */
-       return eaptls_operation(status, handler);
+ done:
+       SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL);
+
+       return status;
 }
 
 
@@ -995,7 +970,7 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
        uint8_t *ptr;
 
        /*
-        *      Don't set eap_ds->request->type.type, as the main EAP
+        *      Don't set eap_ds->request->type.num, as the main EAP
         *      handler will do that for us.  This allows the TLS
         *      module to be called from TTLS & PEAP.
         */
@@ -1014,11 +989,9 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
         *      Identifier value in the subsequent fragment contained
         *      within an EAP- Reponse.
         */
-       eap_ds->request->type.data = malloc(reply->length - TLS_HEADER_LEN + 1);
-       if (eap_ds->request->type.data == NULL) {
-               radlog(L_ERR, "out of memory");
-               return 0;
-       }
+       eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t,
+                                                 reply->length - TLS_HEADER_LEN + 1);
+       if (!eap_ds->request->type.data) return 0;
 
        /* EAPTLS Header length is excluded while computing EAP typelen */
        eap_ds->request->type.length = reply->length - TLS_HEADER_LEN;
@@ -1029,23 +1002,109 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
        if (reply->dlen) memcpy(ptr, reply->data, reply->dlen);
 
        switch (reply->code) {
-       case EAPTLS_ACK:
-       case EAPTLS_START:
-       case EAPTLS_REQUEST:
+       case FR_TLS_ACK:
+       case FR_TLS_START:
+       case FR_TLS_REQUEST:
                eap_ds->request->code = PW_EAP_REQUEST;
                break;
-       case EAPTLS_SUCCESS:
+
+       case FR_TLS_SUCCESS:
                eap_ds->request->code = PW_EAP_SUCCESS;
                break;
-       case EAPTLS_FAIL:
+
+       case FR_TLS_FAIL:
                eap_ds->request->code = PW_EAP_FAILURE;
                break;
+
        default:
                /* Should never enter here */
-               eap_ds->request->code = PW_EAP_FAILURE;
+               rad_assert(0);
                break;
        }
 
        return 1;
 }
 
+/*
+ *     Parse TLS configuration
+ *
+ *     If the option given by 'attr' is set, we find the config section
+ *     of that name and use that for the TLS configuration. If not, we
+ *     fall back to compatibility mode and read the TLS options from
+ *     the 'tls' section.
+ */
+fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
+{
+       char const              *tls_conf_name;
+       CONF_PAIR               *cp;
+       CONF_SECTION            *parent;
+       CONF_SECTION            *tls_cs;
+       fr_tls_server_conf_t    *tls_conf;
+
+       if (!cs)
+               return NULL;
+
+       rad_assert(attr != NULL);
+
+       parent = cf_item_parent(cf_section_to_item(cs));
+
+       cp = cf_pair_find(cs, attr);
+       if (cp) {
+               tls_conf_name = cf_pair_value(cp);
+
+               tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name);
+
+               if (!tls_cs) {
+                       ERROR("Cannot find tls config \"%s\"", tls_conf_name);
+                       return NULL;
+               }
+       } else {
+               /*
+                *      If we can't find the section given by the 'attr', we
+                *      fall-back to looking for the "tls" section, as in
+                *      previous versions.
+                *
+                *      We don't fall back if the 'attr' is specified, but we can't
+                *      find the section - that is just a config error.
+                */
+               INFO("TLS section \"%s\" missing, trying to use legacy configuration", attr);
+               tls_cs = cf_section_sub_find(parent, "tls");
+       }
+
+       if (!tls_cs)
+               return NULL;
+
+       tls_conf = tls_server_conf_parse(tls_cs);
+
+       if (!tls_conf)
+               return NULL;
+
+       /*
+        *      The EAP RFC's say 1020, but we're less picky.
+        */
+       if (tls_conf->fragment_size < 100) {
+               ERROR("Configured fragment size is too small, must be >= 100");
+               return NULL;
+       }
+
+       /*
+        *      The maximum size for a RADIUS packet is 4096,
+        *      minus the header (20), Message-Authenticator (18),
+        *      and State (18), etc. results in about 4000 bytes of data
+        *      that can be devoted *solely* to EAP.
+        */
+       if (tls_conf->fragment_size > 4000) {
+               ERROR("Configured fragment size is too large, must be <= 4000");
+               return NULL;
+       }
+
+       /*
+        *      Account for the EAP header (4), and the EAP-TLS header
+        *      (6), as per Section 4.2 of RFC 2716.  What's left is
+        *      the maximum amount of data we read from a TLS buffer.
+        */
+       tls_conf->fragment_size -= 10;
+
+       return tls_conf;
+}
+