Update the GPL boilerplate with the new address of the FSF.
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_tls / rlm_eap_tls.c
index 22b852a..3d6216b 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 2001  hereUare Communications, Inc. <raghud@hereuare.com>
+ * Copyright 2003  Alan DeKok <aland@freeradius.org>
  */
 
-#include "autoconf.h"
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <freeradius-devel/autoconf.h>
 #include "eap_tls.h"
 
+#ifdef HAVE_OPENSSL_RAND_H
+#include <openssl/rand.h>
+#endif
+
 static CONF_PARSER module_config[] = {
-       { "rsa_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" },
-       { "dh_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" },
-       { "rsa_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" },
-       { "dh_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" },
-       { "verify_depth", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" },
-       { "CA_path", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, ca_path), NULL, NULL },
-       { "pem_file_type", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, file_type), NULL, "yes" },
-       { "private_key_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL },
-       { "CA_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL },
-       { "dh_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, dh_file), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, random_file), NULL, NULL },
-       { "fragment_size", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" },
-       { "include_length", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, include_length), NULL, "yes" },
+       { "rsa_key_exchange", PW_TYPE_BOOLEAN,
+         offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" },
+       { "dh_key_exchange", PW_TYPE_BOOLEAN,
+         offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" },
+       { "rsa_key_length", PW_TYPE_INTEGER,
+         offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" },
+       { "dh_key_length", PW_TYPE_INTEGER,
+         offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" },
+       { "verify_depth", PW_TYPE_INTEGER,
+         offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" },
+       { "CA_path", PW_TYPE_FILENAME,
+         offsetof(EAP_TLS_CONF, ca_path), NULL, NULL },
+       { "pem_file_type", PW_TYPE_BOOLEAN,
+         offsetof(EAP_TLS_CONF, file_type), NULL, "yes" },
+       { "private_key_file", PW_TYPE_FILENAME,
+         offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL },
+       { "certificate_file", PW_TYPE_FILENAME,
+         offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL },
+       { "CA_file", PW_TYPE_FILENAME,
+         offsetof(EAP_TLS_CONF, ca_file), NULL, NULL },
+       { "private_key_password", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL },
+       { "dh_file", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, dh_file), NULL, NULL },
+       { "random_file", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, random_file), NULL, NULL },
+       { "fragment_size", PW_TYPE_INTEGER,
+         offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" },
+       { "include_length", PW_TYPE_BOOLEAN,
+         offsetof(EAP_TLS_CONF, include_length), NULL, "yes" },
+       { "check_crl", PW_TYPE_BOOLEAN,
+         offsetof(EAP_TLS_CONF, check_crl), NULL, "no"},
+       { "check_cert_cn", PW_TYPE_STRING_PTR,
+         offsetof(EAP_TLS_CONF, check_cert_cn), NULL, NULL},
 
        { NULL, -1, 0, NULL, NULL }           /* end the list */
 };
 
 
-static int eaptls_attach(CONF_SECTION *cs, void **arg)
+/*
+ *     TODO: Check for the type of key exchange * like conf->dh_key
+ */
+static int load_dh_params(SSL_CTX *ctx, char *file)
 {
-       SSL_CTX          *ctx;
-       EAP_TLS_CONF     *conf;
-       eap_tls_t        **eaptls;
+       DH *dh = NULL;
+       BIO *bio;
 
-       eaptls = (eap_tls_t **)arg;
-
-       /* Parse the config file & get all the configured values */
-       conf = (EAP_TLS_CONF *)malloc(sizeof(EAP_TLS_CONF));
-       if (conf == NULL) {
-               radlog(L_ERR, "rlm_eap_tls: out of memory");
+       if ((bio = BIO_new_file(file, "r")) == NULL) {
+               radlog(L_ERR, "rlm_eap_tls: Unable to open DH file - %s", file);
                return -1;
        }
-       if (cf_section_parse(cs, conf, module_config) < 0) {
-               free(conf);
+
+       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+       BIO_free(bio);
+       if (SSL_CTX_set_tmp_dh(ctx, dh) < 0) {
+               radlog(L_ERR, "rlm_eap_tls: Unable to set DH parameters");
+               DH_free(dh);
                return -1;
        }
 
+       DH_free(dh);
+       return 0;
+}
 
-       /* Initialize TLS */
-       ctx = init_tls_ctx(conf);
-       if (ctx == NULL) return -1;
-
-       if (load_dh_params(ctx, conf->dh_file) < 0) return -1;
-       if (generate_eph_rsa_key(ctx) < 0) return -1;
+/*
+ *     Generte ephemeral RSA keys.
+ */
+static int generate_eph_rsa_key(SSL_CTX *ctx)
+{
+       RSA *rsa;
 
-       /* Store all these values in the data structure for later references */
-       *eaptls = (eap_tls_t *)malloc(sizeof(eap_tls_t));
-       if (*eaptls == NULL) {
-               radlog(L_ERR, "rlm_eap_tls: out of memory");
+       rsa = RSA_generate_key(512, RSA_F4, NULL, NULL);
 
-               free(conf->dh_file);
-               free(conf->certificate_file);
-               free(conf->private_key_file);
-               free(conf->private_key_password);
-               free(conf);
+       if (!SSL_CTX_set_tmp_rsa(ctx, rsa)) {
+               radlog(L_ERR, "rlm_eap_tls: Couldn't set RSA key");
                return -1;
        }
 
-       radlog(L_ERR, "rlm_eap_tls: conf N ctx stored ");
-       (*eaptls)->conf = conf;
-       (*eaptls)->ctx = ctx;
-
+       RSA_free(rsa);
        return 0;
 }
 
 
 /*
- * send an initial eap-tls request
- * ie access challenge to the user/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
+ *     Create Global context SSL and use it in every new session
+ *
+ *     - Load the trusted CAs
+ *     - Load the Private key & the certificate
+ *     - Set the Context options & Verify options
  */
-static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler)
+static SSL_CTX *init_tls_ctx(EAP_TLS_CONF *conf)
 {
-       int status;
-       tls_session_t *ssn;
-       eap_tls_t    *eaptls;
-       int index;
+       SSL_METHOD *meth;
+       SSL_CTX *ctx;
+       X509_STORE *certstore;
+       int verify_mode = 0;
+       int ctx_options = 0;
+       int type;
+
+       /*
+        *      Add all the default ciphers and message digests
+        *      Create our context.
+        */
+       SSL_library_init();
+       SSL_load_error_strings();
+
+       meth = TLSv1_method();
+       ctx = SSL_CTX_new(meth);
 
-       eaptls = (eap_tls_t *)type_arg;
+       /*
+        * Identify the type of certificates that needs to be loaded
+        */
+       if (conf->file_type) {
+               type = SSL_FILETYPE_PEM;
+       } else {
+               type = SSL_FILETYPE_ASN1;
+       }
 
        /*
-        * 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
+        * Set the password to load private key
         */
-       ssn = new_tls_session(eaptls);
+       if (conf->private_key_password) {
+               SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->private_key_password);
+               SSL_CTX_set_default_passwd_cb(ctx, cbtls_password);
+       }
 
        /*
-        * Create a structure for all the items required to 
-        * be verified for each client and set that
-        * as opaque data structure.
+        *      Load our keys and certificates
         *
-        * NOTE: If we want to set each item sepearately then
-        * this index should be global.
+        *      If certificates are of type PEM then we can make use
+        *      of cert chain authentication using openssl api call
+        *      SSL_CTX_use_certificate_chain_file.  Please see how
+        *      the cert chain needs to be given in PEM from
+        *      openSSL.org
         */
-       index = SSL_get_ex_new_index(0, "index", NULL, NULL, NULL);
-       SSL_set_ex_data(ssn->ssl, index, (void *)handler->identity);
+       if (type == SSL_FILETYPE_PEM) {
+               radlog(L_INFO, "rlm_eap_tls: Loading the certificate file as a chain");
+               if (!(SSL_CTX_use_certificate_chain_file(ctx, conf->certificate_file))) {
+                       ERR_print_errors_fp(stderr);
+                       radlog(L_ERR, "rlm_eap_tls: Error reading certificate file");
+                       return NULL;
+               }
+
+       } else if (!(SSL_CTX_use_certificate_file(ctx, conf->certificate_file, type))) {
+               ERR_print_errors_fp(stderr);
+               radlog(L_ERR, "rlm_eap_tls: Error reading certificate file");
+               return NULL;
+       }
 
-       ssn->offset = eaptls->conf->fragment_size;
-       ssn->length_flag = eaptls->conf->include_length;
 
-       handler->opaque = ((void *)ssn);
-       handler->free_opaque = session_free;
+       /* Load the CAs we trust */
+       if (!SSL_CTX_load_verify_locations(ctx, conf->ca_file, conf->ca_path)) {
+               ERR_print_errors_fp(stderr);
+               radlog(L_ERR, "rlm_eap_tls: Error reading Trusted root CA list");
+               return NULL;
+       }
+       SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(conf->ca_file));
+
+       if (!(SSL_CTX_use_PrivateKey_file(ctx, conf->private_key_file, type))) {
+               ERR_print_errors_fp(stderr);
+               radlog(L_ERR, "rlm_eap_tls: Error reading private key file");
+               return NULL;
+       }
 
        /*
-        * TLS session initialization is over
-        * Now handle TLS related hanshaking or Data
+        * Check if the loaded private key is the right one
         */
-       status = eaptls_start(handler->eap_ds);
-       if (status == 0)
-               return 0;
+       if (!SSL_CTX_check_private_key(ctx)) {
+               radlog(L_ERR, "rlm_eap_tls: Private key does not match the certificate public key");
+               return NULL;
+       }
 
-       return 1;
+       /*
+        *      Set ctx_options
+        */
+       ctx_options |= SSL_OP_NO_SSLv2;
+       ctx_options |= SSL_OP_NO_SSLv3;
+
+       /*
+        *      SSL_OP_SINGLE_DH_USE must be used in order to prevent
+        *      small subgroup attacks and forward secrecy. Always
+        *      using
+        *
+        *      SSL_OP_SINGLE_DH_USE has an impact on the computer
+        *      time needed during negotiation, but it is not very
+        *      large.
+        */
+       ctx_options |= SSL_OP_SINGLE_DH_USE;
+       SSL_CTX_set_options(ctx, ctx_options);
+
+       /*
+        *      TODO: Set the RSA & DH
+        *      SSL_CTX_set_tmp_rsa_callback(ctx, cbtls_rsa);
+        *      SSL_CTX_set_tmp_dh_callback(ctx, cbtls_dh);
+        */
+
+       /*
+        *      set the message callback to identify the type of
+        *      message.  For every new session, there can be a
+        *      different callback argument.
+        *
+        *      SSL_CTX_set_msg_callback(ctx, cbtls_msg);
+        */
+
+       /* Set Info callback */
+       SSL_CTX_set_info_callback(ctx, cbtls_info);
+
+       /*
+        *      Check the certificates for revocation.
+        */
+#ifdef X509_V_FLAG_CRL_CHECK
+       if (conf->check_crl) {
+         certstore = SSL_CTX_get_cert_store(ctx);
+         if (certstore == NULL) {
+           ERR_print_errors_fp(stderr);
+           radlog(L_ERR, "rlm_eap_tls: Error reading Certificate Store");
+           return NULL;
+         }
+         X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK);
+       }
+#endif
+
+       /*
+        *      Set verify modes
+        *      Always verify the peer certificate
+        */
+       verify_mode |= SSL_VERIFY_PEER;
+       verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+       verify_mode |= SSL_VERIFY_CLIENT_ONCE;
+       SSL_CTX_set_verify(ctx, verify_mode, cbtls_verify);
+
+       if (conf->verify_depth) {
+               SSL_CTX_set_verify_depth(ctx, conf->verify_depth);
+       }
+
+       /* Load randomness */
+       if (!(RAND_load_file(conf->random_file, 1024*1024))) {
+               ERR_print_errors_fp(stderr);
+               radlog(L_ERR, "rlm_eap_tls: Error loading randomness");
+               return NULL;
+       }
+
+       return ctx;
 }
 
+
 /*
- * In the actual authentication first verify the packet and then create the data structure
+ *     Detach the EAP-TLS module.
  */
+static int eaptls_detach(void *arg)
+{
+       EAP_TLS_CONF     *conf;
+       eap_tls_t        *inst;
+
+       inst = (eap_tls_t *) arg;
+       conf = inst->conf;
+
+       if (conf) {
+               if (conf->dh_file) free(conf->dh_file);
+               conf->dh_file = NULL;
+               if (conf->certificate_file) free(conf->certificate_file);
+               conf->certificate_file = NULL;
+               if (conf->private_key_file) free(conf->private_key_file);
+               conf->private_key_file = NULL;
+               if (conf->private_key_password) free(conf->private_key_password);
+               conf->private_key_password = NULL;
+               if (conf->random_file) free(conf->random_file);
+               conf->random_file = NULL;
+
+               free(inst->conf);
+               inst->conf = NULL;
+       }
+
+       if (inst->ctx) SSL_CTX_free(inst->ctx);
+       inst->ctx = NULL;
+
+       free(inst);
+
+       return 0;
+}
+
+
 /*
- * To process the TLS,
- *  INCOMING DATA:
- *     1. EAP-TLS should get the compelete TLS data from the peer.
- *     2. Store that data in a data structure with any other required info
- *     3. Hand this data structure to the TLS module.
- *     4. TLS module will perform its operations on the data and hands back to EAP-TLS
- *  OUTGOING DATA:
- *     1. EAP-TLS if necessary will fragment it and send it to the destination.
+ *     Attach the EAP-TLS module.
+ */
+static int eaptls_attach(CONF_SECTION *cs, void **instance)
+{
+       EAP_TLS_CONF     *conf;
+       eap_tls_t        *inst;
+
+       /* Store all these values in the data structure for later references */
+       inst = (eap_tls_t *)malloc(sizeof(*inst));
+       if (!inst) {
+               radlog(L_ERR, "rlm_eap_tls: out of memory");
+               return -1;
+       }
+       memset(inst, 0, sizeof(*inst));
+
+       /*
+        *      Parse the config file & get all the configured values
+        */
+       conf = (EAP_TLS_CONF *)malloc(sizeof(*conf));
+       if (conf == NULL) {
+               radlog(L_ERR, "rlm_eap_tls: out of memory");
+               return -1;
+       }
+       memset(conf, 0, sizeof(*conf));
+
+       inst->conf = conf;
+       if (cf_section_parse(cs, conf, module_config) < 0) {
+               eaptls_detach(inst);
+               return -1;
+       }
+
+
+       /*
+        *      Initialize TLS
+        */
+       inst->ctx = init_tls_ctx(conf);
+       if (inst->ctx == NULL) {
+               eaptls_detach(inst);
+               return -1;
+       }
+
+       if (load_dh_params(inst->ctx, conf->dh_file) < 0) {
+               eaptls_detach(inst);
+               return -1;
+       }
+       if (generate_eph_rsa_key(inst->ctx) < 0) {
+               eaptls_detach(inst);
+               return -1;
+       }
+
+       *instance = inst;
+
+       return 0;
+}
+
+
+/*
+ *     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.
  *
- * During EAP-TLS initialization, TLS Context object will be initialized and stored.
- * For every new authentication requests, TLS will open a new session object and that
- * session object should be *maintained* even after the session is completed, for
- * session resumption. (Probably later as a feature, as we donot know who maintains these
- * session objects ie, SSL_CTX (internally) or TLS module(explicitly). If TLS module, then
- * how to let SSL API know about these sessions.)
+ *     Fragment length is Framed-MTU - 4.
+ *
+ *     http://mail.frascone.com/pipermail/public/eap/2003-July/001426.html
  */
-static int eaptls_authenticate(void *arg, EAP_HANDLER *handler)
+static int eaptls_initiate(void *type_arg, EAP_HANDLER *handler)
 {
-       //tls_session_t *tls_session;
-       EAPTLS_PACKET   *tlspacket;
-       eaptls_status_t status;
+       int             status;
+       tls_session_t   *ssn;
+       eap_tls_t       *inst;
+       VALUE_PAIR      *vp;
+       int             client_cert = TRUE;
 
-       /* This case is when SSL generates Alert then we 
-        * send that alert to the client and then send the EAP-Failure
+       inst = (eap_tls_t *)type_arg;
+
+       /*
+        *      If we're TTLS or PEAP, then do NOT require a client
+        *      certificate.
+        *
+        *      FIXME: This should be more configurable.
+        */
+       if (handler->eap_type != PW_EAP_TLS) {
+               vp = pairfind(handler->request->config_items,
+                             PW_EAP_TLS_REQUIRE_CLIENT_CERT);
+               if (!vp) {
+                       client_cert = FALSE;
+               } else {
+                       client_cert = vp->lvalue;
+               }
+       }
+
+       /*
+        *      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
         */
-       status = eaptls_verify(handler->eap_ds, handler->prev_eapds);
-       if (status == EAPTLS_INVALID)
+       ssn = eaptls_new_session(inst->ctx, client_cert);
+       if (!ssn) {
                return 0;
+       }
+
+       /*
+        *      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, 0, (void *)handler);
+       SSL_set_ex_data(ssn->ssl, 1, (void *)inst->conf);
 
-       /* In case of EAPTLS_ACK, we need to send the next fragment
-        * or send success or failure
+       ssn->length_flag = inst->conf->include_length;
+
+       /*
+        *      We set a default fragment size, unless the Framed-MTU
+        *      tells us it's too big.
+        */
+       ssn->offset = inst->conf->fragment_size;
+       vp = pairfind(handler->request->packet->vps, PW_FRAMED_MTU);
+       if (vp && ((vp->lvalue - 4) < ssn->offset)) {
+               ssn->offset = vp->lvalue - 4;
+       }
+
+       handler->opaque = ((void *)ssn);
+       handler->free_opaque = session_free;
+
+       DEBUG2("  rlm_eap_tls: Initiate");
+
+       /*
+        *      PEAP-specific breakage.
         */
-       if (status == EAPTLS_ACK) {
-               if (eaptls_ack_handler(handler) != EAPTLS_NOOP)
-                       return 1;
-               else
-                       return 0;
+       if (handler->eap_type == PW_EAP_PEAP) {
+               /*
+                *      As it is a poorly designed protocol, PEAP uses
+                *      bits in the TLS header to indicate PEAP
+                *      version numbers.  For now, we only support
+                *      PEAP version 0, so it doesn't matter too much.
+                *      However, if we support later versions of PEAP,
+                *      we will need this flag to indicate which
+                *      version we're currently dealing with.
+                */
+               ssn->peap_flag = 0x00;
+
+               /*
+                *      PEAP version 0 requires 'include_length = no',
+                *      so rather than hoping the user figures it out,
+                *      we force it here.
+                */
+               ssn->length_flag = 0;
        }
 
-       if ((tlspacket = eaptls_extract(handler->eap_ds, status)) == NULL)
+       /*
+        *      TLS session initialization is over.  Now handle TLS
+        *      related handshaking or application data.
+        */
+       status = eaptls_start(handler->eap_ds, ssn->peap_flag);
+       DEBUG2("  rlm_eap_tls: Start returned %d", status);
+       if (status == 0)
                return 0;
 
-       eaptls_operation(tlspacket, status, handler);
+       /*
+        *      The next stage to process the packet.
+        */
+       handler->stage = AUTHENTICATE;
 
        return 1;
 }
 
-static int eaptls_detach(void **arg)
+/*
+ *     Do authentication, by letting EAP-TLS do most of the work.
+ */
+static int eaptls_authenticate(void *arg UNUSED, EAP_HANDLER *handler)
 {
-       EAP_TLS_CONF     *conf;
-       eap_tls_t        **eaptls;
-
-       eaptls = (eap_tls_t **)arg;
-       conf = (*eaptls)->conf;
-
-       free(conf->dh_file);
-               conf->dh_file = NULL;
-       free(conf->certificate_file);
-               conf->certificate_file = NULL;
-       free(conf->private_key_file);
-               conf->private_key_file = NULL;
-       free(conf->private_key_password);
-               conf->private_key_password = NULL;
-       free(conf->random_file);
-               conf->random_file = NULL;
-
-       free((*eaptls)->conf);
-       (*eaptls)->conf = NULL;
+       eaptls_status_t status;
+       tls_session_t *tls_session = (tls_session_t *) handler->opaque;
+
+       DEBUG2("  rlm_eap_tls: Authenticate");
+
+       status = eaptls_process(handler);
+       DEBUG2("  eaptls_process returned %d\n", status);
+       switch (status) {
+               /*
+                *      EAP-TLS handshake was successful, return an
+                *      EAP-TLS-Success packet here.
+                */
+       case EAPTLS_SUCCESS:
+               break;
+
+               /*
+                *      The TLS code is still working on the TLS
+                *      exchange, and it's a valid TLS request.
+                *      do nothing.
+                */
+       case EAPTLS_HANDLED:
+               return 1;
+
+               /*
+                *      Handshake is done, proceed with decoding tunneled
+                *      data.
+                */
+       case EAPTLS_OK:
+               DEBUG2("  rlm_eap_tls: Received unexpected tunneled data after successful handshake.");
+#ifndef NDEBUG
+               if (debug_flag > 2) {
+                       unsigned int i;
+                       unsigned int data_len;
+                       unsigned char buffer[1024];
+
+                       data_len = (tls_session->record_minus)(&tls_session->dirty_in,
+                                               buffer, sizeof(buffer));
+                       log_debug("  Tunneled data (%u bytes)\n", data_len);
+                       for (i = 0; i < data_len; i++) {
+                               if ((i & 0x0f) == 0x00) printf("  %x: ", i);
+                               if ((i & 0x0f) == 0x0f) printf("\n");
+
+                               printf("%02x ", buffer[i]);
+                       }
+                       printf("\n");
+               }
+#endif
+
+               eaptls_fail(handler->eap_ds, 0);
+               return 0;
+               break;
 
-       SSL_CTX_free((*eaptls)->ctx);
-       (*eaptls)->ctx = NULL;
-       *eaptls = NULL;
+               /*
+                *      Anything else: fail.
+                */
+       default:
+               return 0;
+       }
 
-       return 0;
+       /*
+        *      Success: Return MPPE keys.
+        */
+       eaptls_success(handler->eap_ds, 0);
+       eaptls_gen_mppe_keys(&handler->request->reply->vps,
+                            tls_session->ssl,
+                            "client EAP encryption");
+       return 1;
 }
 
 /*
@@ -241,7 +574,8 @@ static int eaptls_detach(void **arg)
 EAP_TYPE rlm_eap_tls = {
        "eap_tls",
        eaptls_attach,                  /* attach */
-       eaptls_initiate,                        /* Start the initial request, after Identity */
+       eaptls_initiate,                /* Start the initial request */
+       NULL,                           /* authorization */
        eaptls_authenticate,            /* authentication */
        eaptls_detach                   /* detach */
 };