check before dereference. Found by PVS-Studio
[freeradius.git] / src / modules / rlm_eap / libeap / eap_tls.c
index 731838b..a08cd50 100644 (file)
  *
  */
 
-#include <freeradius-devel/ident.h>
 RCSID("$Id$")
+USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
 
 #include <assert.h>
-#include "eap_tls.h"
-
-/*
- *      Allocate a new TLS_PACKET
- */
-EAPTLS_PACKET *eaptls_alloc(void)
-{
-       EAPTLS_PACKET   *rp;
-
-       if ((rp = malloc(sizeof(EAPTLS_PACKET))) == NULL) {
-               radlog(L_ERR, "rlm_eap_tls: out of memory");
-               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;
-       }
-
-       free(eaptls_packet);
-       *eaptls_packet_ptr = NULL;
-}
 
+#include "eap_tls.h"
 /*
  *     Send an initial eap-tls request to the peer.
  *
@@ -96,14 +61,12 @@ void eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr)
  *
  *     Fragment length is Framed-MTU - 4.
  */
-tls_session_t *eaptls_session(fr_tls_server_conf_t *tls_conf, eap_handler_t *handler, int client_cert)
+tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_conf, bool client_cert)
 {
        tls_session_t   *ssn;
-       int             verify_mode = 0;
        REQUEST         *request = handler->request;
 
-       handler->tls = TRUE;
-       handler->finished = FALSE;
+       handler->tls = true;
 
        /*
         *      Every new session is started only from EAP-TLS-START.
@@ -112,23 +75,12 @@ tls_session_t *eaptls_session(fr_tls_server_conf_t *tls_conf, eap_handler_t *han
         *      in Opaque.  So that we can use these data structures
         *      when we get the response
         */
-       ssn = tls_new_session(tls_conf, request, client_cert);
+       ssn = tls_new_session(handler, tls_conf, request, client_cert);
        if (!ssn) {
                return NULL;
        }
 
        /*
-        *      Verify the peer certificate, if asked.
-        */
-       if (client_cert) {
-               RDEBUG2("Requiring client certificate");
-               verify_mode = SSL_VERIFY_PEER;
-               verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
-               verify_mode |= SSL_VERIFY_CLIENT_ONCE;
-       }
-       SSL_set_verify(ssn->ssl, verify_mode, cbtls_verify);
-
-       /*
         *      Create a structure for all the items required to be
         *      verified for each client and set that as opaque data
         *      structure.
@@ -138,14 +90,15 @@ tls_session_t *eaptls_session(fr_tls_server_conf_t *tls_conf, eap_handler_t *han
         */
        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_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);
 
-       return ssn;
+       return talloc_steal(handler, ssn); /* ssn */
 }
 
 /*
@@ -176,7 +129,7 @@ int eaptls_success(eap_handler_t *handler, int peap_flag)
        REQUEST *request = handler->request;
        tls_session_t *tls_session = handler->opaque;
 
-       handler->finished = TRUE;
+       handler->finished = true;
        reply.code = FR_TLS_SUCCESS;
        reply.length = TLS_HEADER_LEN;
        reply.flags = peap_flag;
@@ -197,11 +150,11 @@ int eaptls_success(eap_handler_t *handler, int peap_flag)
                eaptls_gen_mppe_keys(handler->request,
                                     tls_session->ssl, tls_session->prf_label);
        } else {
-               RDEBUGW("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, &handler->request->reply->vps);
+                          handler->type);
        return 1;
 }
 
@@ -210,7 +163,7 @@ int eaptls_fail(eap_handler_t *handler, int peap_flag)
        EAPTLS_PACKET   reply;
        tls_session_t *tls_session = handler->opaque;
 
-       handler->finished = TRUE;
+       handler->finished = true;
        reply.code = FR_TLS_FAIL;
        reply.length = TLS_HEADER_LEN;
        reply.flags = peap_flag;
@@ -247,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.
@@ -270,8 +223,8 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
        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) {
@@ -286,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);
@@ -295,7 +250,7 @@ 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;
@@ -317,17 +272,19 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
  *     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;
 
+       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;
 }
@@ -343,10 +300,12 @@ static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
  */
 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
@@ -366,6 +325,16 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
        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
         *
@@ -374,19 +343,14 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *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 (prev_eap_ds &&
-                   (prev_eap_ds->request->id == eap_ds->response->id)) {
-                       /*
-                        *      Run the ACK handler directly from here.
-                        */
-                       RDEBUG2("Received TLS ACK");
+               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");
+                       REDEBUG("Received Invalid TLS ACK");
                        return FR_TLS_INVALID;
                }
        }
@@ -395,11 +359,17 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler)
         *      We send TLS_START, but do not receive it.
         */
        if (TLS_START(eaptls_packet->flags)) {
-               RDEBUG("Received unexpected EAP-TLS Start message");
+               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
@@ -413,58 +383,119 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *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 (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 == NULL) ||
-                           (eaptls_prev == NULL) ||
+                       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;
 
-                               RDEBUG2("Received EAP-TLS First Fragment of the message");
                                return FR_TLS_FIRST_FRAGMENT;
-                       } else {
+                       }
 
-                               RDEBUG2("More Fragments with length included");
-                               return FR_TLS_MORE_FRAGMENTS_WITH_LENGTH;
+                       RDEBUG2("Got additional TLS record fragment with length (%zu bytes).  "
+                               "Peer indicated more fragments to follow", frag_len);
+
+                       /*
+                        *      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 FR_TLS_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");
+               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 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
@@ -493,8 +524,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
        uint32_t        len = 0;
        uint8_t         *data = NULL;
 
-       if (status  == FR_TLS_INVALID)
-               return NULL;
+       if (status == FR_TLS_INVALID) return NULL;
 
        /*
         *      The main EAP code & eaptls_verify() take care of
@@ -511,8 +541,8 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_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
@@ -532,8 +562,9 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_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;
        }
 
@@ -543,28 +574,17 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_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) {
@@ -581,8 +601,8 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
        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;
                }
 
@@ -614,17 +634,17 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
                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);
@@ -657,12 +677,10 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
  *     SSL_CTX (internally) or TLS module(explicitly). If TLS module,
  *     then how to let SSL API know about these sessions.)
  */
-static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
-                                       eap_handler_t *handler)
+static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *handler)
 {
-       tls_session_t *tls_session;
-
-       tls_session = (tls_session_t *)handler->opaque;
+       REQUEST         *request = handler->request;
+       tls_session_t   *tls_session = handler->opaque;
 
        if ((status == FR_TLS_MORE_FRAGMENTS) ||
            (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
@@ -670,7 +688,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
                /*
                 *      Send the ACK.
                 */
-               eaptls_send_ack(handler->eap_ds, tls_session->peap_flag);
+               eaptls_send_ack(handler, tls_session->peap_flag);
                return FR_TLS_HANDLED;
 
        }
@@ -687,8 +705,8 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
         *      is required then send another request.
         */
        if (!tls_handshake_recv(handler->request, tls_session)) {
-               DEBUG2("TLS receive handshake failed during operation");
-               eaptls_fail(handler, tls_session->peap_flag);
+               REDEBUG("TLS receive handshake failed during operation");
+               tls_fail(tls_session);
                return FR_TLS_FAIL;
        }
 
@@ -701,14 +719,14 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
                eaptls_request(handler->eap_ds, tls_session);
                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
@@ -717,11 +735,11 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
                tls_session->info.content_type = application_data;
                return FR_TLS_SUCCESS;
        }
-       
+
        /*
         *      Who knows what happened...
         */
-       DEBUG2("TLS failed during operation");
+       REDEBUG("TLS failed during operation");
        return FR_TLS_FAIL;
 }
 
@@ -762,51 +780,57 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
 
        if (!request) return FR_TLS_FAIL;
 
-       RDEBUG2("processing EAP-TLS");
+       RDEBUG2("Continuing EAP-TLS");
+
        SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request);
 
-       if (handler->certs) pairadd(&request->packet->vps,
-                                   paircopy(request->packet, handler->certs));
+       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
+       /*
+        *      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 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.
-                */
+       /*
+        *      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.
-                */
+       /*
+        *      Normal TLS request, continue with the "get rest
+        *      of fragments" phase.
+        */
        case FR_TLS_REQUEST:
                eaptls_request(handler->eap_ds, tls_session);
                status = FR_TLS_HANDLED;
                goto done;
 
-               /*
-                *      The handshake is done, and we're in the "tunnel
-                *      data" phase.
-                */
+       /*
+        *      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.
-                */
+       /*
+        *      Get the rest of the fragments.
+        */
        case FR_TLS_FIRST_FRAGMENT:
        case FR_TLS_MORE_FRAGMENTS:
        case FR_TLS_LENGTH_INCLUDED:
@@ -834,16 +858,16 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
         */
        if (tlspacket->dlen !=
            (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
-               eaptls_free(&tlspacket);
-               RDEBUG("Exceeded maximum record size");
-               status =FR_TLS_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.
@@ -856,15 +880,14 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
                 *      there more fragments coming, then send ACK,
                 *      and get the caller to continue the
                 *      conversation.
-                */     
+                */
                if ((status == FR_TLS_MORE_FRAGMENTS) ||
                    (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
-                   (status == FR_TLS_FIRST_FRAGMENT)) {
+                   (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");
                        status = FR_TLS_HANDLED;
                        goto done;
@@ -878,6 +901,59 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
         *      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];
+
+               /*
+                *      Restore the cached VPs before processing the
+                *      application data.
+                */
+               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)) {
+                               /*
+                                *      TLS-* attrs get added back to
+                                *      the request list.
+                                */
+                               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));
+                               }
+                       }
+                       REXDENT();
+               }
+       }
 
  done:
        SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL);
@@ -913,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;
@@ -933,15 +1007,18 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
        case FR_TLS_REQUEST:
                eap_ds->request->code = PW_EAP_REQUEST;
                break;
+
        case FR_TLS_SUCCESS:
                eap_ds->request->code = PW_EAP_SUCCESS;
                break;
+
        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;
        }
 
@@ -956,9 +1033,9 @@ int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply)
  *     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, const char *attr)
+fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
 {
-       const char              *tls_conf_name;
+       char const              *tls_conf_name;
        CONF_PAIR               *cp;
        CONF_SECTION            *parent;
        CONF_SECTION            *tls_cs;
@@ -969,7 +1046,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, const char *attr)
 
        rad_assert(attr != NULL);
 
-       parent = cf_item_parent(cf_sectiontoitem(cs));
+       parent = cf_item_parent(cf_section_to_item(cs));
 
        cp = cf_pair_find(cs, attr);
        if (cp) {
@@ -978,7 +1055,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, const char *attr)
                tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name);
 
                if (!tls_cs) {
-                       radlog(L_ERR, "error: cannot find tls config '%s'", tls_conf_name);
+                       ERROR("Cannot find tls config \"%s\"", tls_conf_name);
                        return NULL;
                }
        } else {
@@ -990,7 +1067,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, const char *attr)
                 *      We don't fall back if the 'attr' is specified, but we can't
                 *      find the section - that is just a config error.
                 */
-               radlog(L_INFO, "debug: '%s' option missing, trying to use legacy configuration", attr);
+               INFO("TLS section \"%s\" missing, trying to use legacy configuration", attr);
                tls_cs = cf_section_sub_find(parent, "tls");
        }
 
@@ -1006,7 +1083,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, const char *attr)
         *      The EAP RFC's say 1020, but we're less picky.
         */
        if (tls_conf->fragment_size < 100) {
-               radlog(L_ERR, "error: Fragment size is too small.");
+               ERROR("Configured fragment size is too small, must be >= 100");
                return NULL;
        }
 
@@ -1017,7 +1094,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, const char *attr)
         *      that can be devoted *solely* to EAP.
         */
        if (tls_conf->fragment_size > 4000) {
-               radlog(L_ERR, "error: Fragment size is too large.");
+               ERROR("Configured fragment size is too large, must be <= 4000");
                return NULL;
        }