Replaced INFO level logging with debug logging. 99.9% of people
authoraland <aland>
Tue, 7 Feb 2006 18:58:06 +0000 (18:58 +0000)
committeraland <aland>
Tue, 7 Feb 2006 18:58:06 +0000 (18:58 +0000)
don't care to see "ack received" in radius.log

src/modules/rlm_eap/libeap/cb.c [new file with mode: 0644]
src/modules/rlm_eap/libeap/eap_tls.c [new file with mode: 0644]
src/modules/rlm_eap/libeap/tls.c [new file with mode: 0644]

diff --git a/src/modules/rlm_eap/libeap/cb.c b/src/modules/rlm_eap/libeap/cb.c
new file mode 100644 (file)
index 0000000..7b2f1b9
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * cb.c
+ *
+ * Version:     $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
+ */
+#include "eap_tls.h"
+
+#ifndef NO_OPENSSL
+
+void cbtls_info(const SSL *s, int where, int ret)
+{
+       const char *str, *state;
+       int w;
+
+       w = where & ~SSL_ST_MASK;
+       if (w & SSL_ST_CONNECT) str="    TLS_connect";
+       else if (w & SSL_ST_ACCEPT) str="    TLS_accept";
+       else str="    (other)";
+
+       state = SSL_state_string_long(s);
+       state = state ? state : "NULL";
+
+       if (where & SSL_CB_LOOP) {
+               DEBUG2("%s: %s\n", str, state);
+       } else if (where & SSL_CB_HANDSHAKE_START) {
+               DEBUG2("%s: %s\n", str, state);
+       } else if (where & SSL_CB_HANDSHAKE_DONE) {
+               DEBUG2("%s: %s\n", str, state);
+       } else if (where & SSL_CB_ALERT) {
+               str=(where & SSL_CB_READ)?"read":"write";
+               radlog(L_ERR,"TLS Alert %s:%s:%s\n", str,
+                       SSL_alert_type_string_long(ret),
+                       SSL_alert_desc_string_long(ret));
+       } else if (where & SSL_CB_EXIT) {
+               if (ret == 0)
+                       radlog(L_ERR, "%s:failed in %s\n", str, state);
+               else if (ret < 0)
+                       radlog(L_ERR, "%s:error in %s\n", str, state);
+       }
+}
+
+/*
+ *     Before trusting a certificate, you must make sure that the
+ *     certificate is 'valid'. There are several steps that your
+ *     application can take in determining if a certificate is
+ *     valid. Commonly used steps are:
+ *
+ *     1.Verifying the certificate's signature, and verifying that
+ *     the certificate has been issued by a trusted Certificate
+ *     Authority.
+ *
+ *     2.Verifying that the certificate is valid for the present date
+ *     (i.e. it is being presented within its validity dates).
+ *
+ *     3.Verifying that the certificate has not been revoked by its
+ *     issuing Certificate Authority, by checking with respect to a
+ *     Certificate Revocation List (CRL).
+ *
+ *     4.Verifying that the credentials presented by the certificate
+ *     fulfill additional requirements specific to the application,
+ *     such as with respect to access control lists or with respect
+ *     to OCSP (Online Certificate Status Processing).
+ *
+ *     NOTE: This callback will be called multiple times based on the
+ *     depth of the root certificate chain
+ */
+int cbtls_verify(int ok, X509_STORE_CTX *ctx)
+{
+       char subject[256]; /* Used for the subject name */
+       char issuer[256]; /* Used for the issuer name */
+       char buf[256];
+       char cn_str[256];
+       EAP_HANDLER *handler = NULL;
+       X509 *client_cert;
+       SSL *ssl;
+       int err, depth;
+       EAP_TLS_CONF *conf;
+       int my_ok = ok;
+
+       client_cert = X509_STORE_CTX_get_current_cert(ctx);
+       err = X509_STORE_CTX_get_error(ctx);
+       depth = X509_STORE_CTX_get_error_depth(ctx);
+
+       if (!my_ok) {
+               radlog(L_ERR,"--> verify error:num=%d:%s\n",err,
+                       X509_verify_cert_error_string(err));
+               return my_ok;
+       }
+       /*
+        *      Catch too long Certificate chains
+        */
+
+       /*
+        * Retrieve the pointer to the SSL of the connection currently treated
+        * and the application specific data stored into the SSL object.
+        */
+       ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+       handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0);
+       conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1);
+
+       /*
+        *      Get the Subject & Issuer
+        */
+       subject[0] = issuer[0] = '\0';
+       X509_NAME_oneline(X509_get_subject_name(client_cert), subject, 256);
+       X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, 256);
+
+       /*
+        *      Get the Common Name
+        */
+       X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
+             NID_commonName, buf, 256);
+
+       switch (ctx->error) {
+
+       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+               radlog(L_ERR, "issuer= %s\n", issuer);
+               break;
+       case X509_V_ERR_CERT_NOT_YET_VALID:
+       case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+               radlog(L_ERR, "notBefore=");
+#if 0
+               ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert));
+#endif
+               break;
+       case X509_V_ERR_CERT_HAS_EXPIRED:
+       case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+               radlog(L_ERR, "notAfter=");
+#if 0
+               ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert));
+#endif
+               break;
+       }
+
+       /*
+        *      If we're at the actual client cert and the conf tells
+        *      us to, check the CN in the cert against the xlat'ed
+        *      value
+        */
+       if (depth == 0 && conf->check_cert_cn != NULL) {
+               if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) {
+                       radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.",
+                               conf->check_cert_cn);
+                       /* if this fails, fail the verification */
+                       my_ok = 0;
+               }
+               DEBUG2("    rlm_eap_tls: checking certificate CN (%s) with xlat'ed value (%s)", buf, cn_str);
+               if (strncmp(cn_str, buf, sizeof(buf)) != 0) {
+                       my_ok = 0;
+                       radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", buf, cn_str);
+               }
+       }
+
+       if (debug_flag > 0) {
+               radlog(L_INFO, "chain-depth=%d, ", depth);
+               /*
+                 if (depth > 0) {
+                 return ok;
+                 }
+               */
+               radlog(L_INFO, "error=%d", err);
+
+               radlog(L_INFO, "--> User-Name = %s", handler->identity);
+               radlog(L_INFO, "--> BUF-Name = %s", buf);
+               radlog(L_INFO, "--> subject = %s", subject);
+               radlog(L_INFO, "--> issuer  = %s", issuer);
+               radlog(L_INFO, "--> verify return:%d", my_ok);
+       }
+       return my_ok;
+}
+
+
+/*
+ *     Fill in our 'info' with TLS data.
+ */
+void cbtls_msg(int write_p, int msg_version, int content_type,
+              const void *buf, size_t len,
+              SSL *ssl UNUSED, void *arg)
+{
+       tls_session_t *state = (tls_session_t *)arg;
+
+       state->info.origin = (unsigned char)write_p;
+       state->info.content_type = (unsigned char)content_type;
+       state->info.record_len = len;
+       state->info.version = msg_version;
+       state->info.initialized = 1;
+
+       if (content_type == SSL3_RT_ALERT) {
+               state->info.alert_level = ((const unsigned char*)buf)[0];
+               state->info.alert_description = ((const unsigned char*)buf)[1];
+               state->info.handshake_type = 0x00;
+
+       } else if (content_type == SSL3_RT_HANDSHAKE) {
+               state->info.handshake_type = ((const unsigned char*)buf)[0];
+               state->info.alert_level = 0x00;
+               state->info.alert_description = 0x00;
+       }
+       tls_session_information(state);
+}
+
+int cbtls_password(char *buf,
+                  int num UNUSED,
+                  int rwflag UNUSED,
+                  void *userdata)
+{
+       strcpy(buf, (char *)userdata);
+       return(strlen((char *)userdata));
+}
+
+RSA *cbtls_rsa(SSL *s UNUSED, int is_export UNUSED, int keylength)
+{
+       static RSA *rsa_tmp=NULL;
+
+       if (rsa_tmp == NULL) {
+               DEBUG2("Generating temp (%d bit) RSA key...", keylength);
+               rsa_tmp=RSA_generate_key(keylength, RSA_F4, NULL, NULL);
+       }
+       return(rsa_tmp);
+}
+
+#endif /* !defined(NO_OPENSSL) */
diff --git a/src/modules/rlm_eap/libeap/eap_tls.c b/src/modules/rlm_eap/libeap/eap_tls.c
new file mode 100644 (file)
index 0000000..c7708e9
--- /dev/null
@@ -0,0 +1,834 @@
+/*
+ * eap_tls.c
+ *
+ * Version:     $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
+ * Copyright 2003  Alan DeKok <aland@freeradius.org>
+ */
+
+/*
+ *
+ *  TLS Packet Format in EAP
+ *  --- ------ ------ -- ---
+ * 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             |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |     Type      |     Flags     |      TLS Message Length
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |     TLS Message Length        |       TLS Data...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+#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;
+}
+
+/*
+   The S flag is set only within the EAP-TLS start message
+   sent from the EAP server to the peer.
+*/
+int eaptls_start(EAP_DS *eap_ds, int peap_flag)
+{
+       EAPTLS_PACKET   reply;
+
+       reply.code = EAPTLS_START;
+       reply.length = TLS_HEADER_LEN + 1/*flags*/;
+
+       reply.flags = peap_flag;
+       reply.flags = SET_START(reply.flags);
+
+       reply.data = NULL;
+       reply.dlen = 0;
+
+       eaptls_compose(eap_ds, &reply);
+
+       return 1;
+}
+
+int eaptls_success(EAP_DS *eap_ds, int peap_flag)
+{
+       EAPTLS_PACKET   reply;
+
+       reply.code = EAPTLS_SUCCESS;
+       reply.length = TLS_HEADER_LEN;
+       reply.flags = peap_flag;
+       reply.data = NULL;
+       reply.dlen = 0;
+
+       eaptls_compose(eap_ds, &reply);
+
+       return 1;
+}
+
+int eaptls_fail(EAP_DS *eap_ds, int peap_flag)
+{
+       EAPTLS_PACKET   reply;
+
+       reply.code = EAPTLS_FAIL;
+       reply.length = TLS_HEADER_LEN;
+       reply.flags = peap_flag;
+       reply.data = NULL;
+       reply.dlen = 0;
+
+       eaptls_compose(eap_ds, &reply);
+
+       return 1;
+}
+
+/*
+   A single TLS record may be up to 16384 octets in length, but a TLS
+   message may span multiple TLS records, and a TLS certificate message
+   may in principle be as long as 16MB.
+*/
+
+/*
+ *     Frame the Dirty data that needs to be send to the client in an
+ *     EAP-Request.  We always embed the TLS-length in all EAP-TLS
+ *     packets that we send, for easy reference purpose.  Handle
+ *     fragmentation and sending the next fragment etc.
+ */
+int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn)
+{
+       EAPTLS_PACKET   reply;
+       unsigned int    size;
+       unsigned int    nlen;
+       unsigned int    lbit = 0;
+
+       /* This value determines whether we set (L)ength flag for
+               EVERY packet we send and add corresponding
+               "TLS Message Length" field.
+
+       length_flag = TRUE;
+               This means we include L flag and "TLS Msg Len" in EVERY
+               packet we send out.
+
+       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.
+
+               Having L flag in every packet is prefered.
+
+       */
+       if (ssn->length_flag) {
+               lbit = 4;
+       }
+       if (ssn->fragment == 0) {
+               ssn->tls_msg_len = ssn->dirty_out.used;
+       }
+
+       reply.code = EAPTLS_REQUEST;
+       reply.flags = ssn->peap_flag;
+
+       /* Send data, NOT more than the FRAGMENT size */
+       if (ssn->dirty_out.used > ssn->offset) {
+               size = ssn->offset;
+               reply.flags = SET_MORE_FRAGMENTS(reply.flags);
+               /* Length MUST be included if it is the First Fragment */
+               if (ssn->fragment == 0) {
+                       lbit = 4;
+               }
+               ssn->fragment = 1;
+       } else {
+               size = ssn->dirty_out.used;
+               ssn->fragment = 0;
+       }
+
+       reply.dlen = lbit + size;
+       reply.length = TLS_HEADER_LEN + 1/*flags*/ + reply.dlen;
+
+       reply.data = malloc(reply.dlen);
+       if (lbit) {
+               nlen = htonl(ssn->tls_msg_len);
+               memcpy(reply.data, &nlen, lbit);
+               reply.flags = SET_LENGTH_INCLUDED(reply.flags);
+       }
+       (ssn->record_minus)(&ssn->dirty_out, reply.data + lbit, size);
+
+       eaptls_compose(eap_ds, &reply);
+       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;
+
+       tls_session = (tls_session_t *)handler->opaque;
+       if (tls_session == NULL){
+               radlog(L_ERR, "rlm_eap_tls: Unexpected ACK received");
+               return EAPTLS_FAIL;
+       }
+       if (tls_session->info.initialized == 0) {
+               DEBUG("  rlm_eap_tls: No SSL info available. Waiting for more SSL data.");
+               return EAPTLS_REQUEST;
+       }
+       if (tls_session->info.origin == 0) {
+               radlog(L_ERR, "rlm_eap_tls: Unexpected ACK received");
+               return EAPTLS_FAIL;
+       }
+
+       switch (tls_session->info.content_type) {
+       case alert:
+               DEBUG2("  rlm_eap_tls: ack alert");
+               eaptls_fail(handler->eap_ds, tls_session->peap_flag);
+               return EAPTLS_FAIL;
+
+       case handshake:
+               if (tls_session->info.handshake_type == finished) {
+                       DEBUG2("  rlm_eap_tls: ack handshake is finished");
+                       return EAPTLS_SUCCESS;
+               }
+
+               DEBUG2("  rlm_eap_tls: ack handshake fragment handler");
+               /* Fragmentation handler, send next fragment */
+               return EAPTLS_REQUEST;
+
+               /*
+                *      For the rest of the conditions, switch over
+                *      to the default section below.
+                */
+       default:
+               DEBUG2("  rlm_eap_tls: ack default");
+               radlog(L_ERR, "rlm_eap_tls: Invalid ACK received: %d",
+                      tls_session->info.content_type);
+               return EAPTLS_FAIL;
+       }
+}
+
+/*
+ *     Similarly, when the EAP server receives an EAP-Response with
+ *     the M bit set, it MUST respond with an EAP-Request with
+ *     EAP-Type=EAP-TLS and no data. This serves as a fragment ACK.
+ *
+ *     In order to prevent errors in the processing of fragments, the
+ *     EAP server MUST use increment the Identifier value for each
+ *     fragment ACK contained within an EAP-Request, and the peer
+ *     MUST include this Identifier value in the subsequent fragment
+ *     contained within an EAP- Reponse.
+ *
+ *     EAP server sends an ACK when it determines there are More
+ *     fragments to receive to make the complete
+ *     TLS-record/TLS-Message
+ */
+static int eaptls_send_ack(EAP_DS *eap_ds, int peap_flag)
+{
+       EAPTLS_PACKET   reply;
+
+       reply.code = EAPTLS_ACK;
+       reply.length = TLS_HEADER_LEN + 1/*flags*/;
+       reply.flags = peap_flag;
+       reply.data = NULL;
+       reply.dlen = 0;
+
+       eaptls_compose(eap_ds, &reply);
+
+       return 1;
+}
+
+/*
+ *     The S flag is set only within the EAP-TLS start message sent
+ *     from the EAP server to the peer.
+ *
+ *     Similarly, when the EAP server receives an EAP-Response with
+ *     the M bit set, it MUST respond with an EAP-Request with
+ *     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)
+{
+       EAP_DS *eap_ds = handler->eap_ds;
+       EAP_DS *prev_eap_ds = handler->prev_eapds;
+       eaptls_packet_t *eaptls_packet, *eaptls_prev = NULL;
+
+       /*
+        *      We don't check ANY of the input parameters.  It's all
+        *      code which works together, so if something is wrong,
+        *      we SHOULD core dump.
+        *
+        *      e.g. if eap_ds is NULL, of if eap_ds->response is
+        *      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
+        *      appropriate type, so we don't need to check
+        *      eap_ds->response->type.type == 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;
+
+       /*
+        *      check for ACK
+        *
+        *      If there's no TLS data, or there's 1 byte of TLS data,
+        *      with the flags set to zero, then it's an ACK.
+        *
+        *      Find if this is a reply to the previous request sent
+        */
+       if ((eaptls_packet == NULL) ||
+           ((eap_ds->response->length == EAP_HEADER_LEN + 2) &&
+            ((eaptls_packet->flags & 0xc0) == 0x00))) {
+
+               if (prev_eap_ds->request->id == eap_ds->response->id) {
+                       /*
+                        *      Run the ACK handler directly from here.
+                        */
+                       DEBUG2("rlm_eap_tls: Received EAP-TLS ACK message");
+                       return eaptls_ack_handler(handler);
+               } else {
+                       radlog(L_ERR, "rlm_eap_tls: Received Invalid EAP-TLS ACK message");
+                       return EAPTLS_INVALID;
+               }
+       }
+
+       /*
+        *      We send TLS_START, but do not receive it.
+        */
+       if (TLS_START(eaptls_packet->flags)) {
+               radlog(L_ERR, "rlm_eap_tls:  Received unexpected EAP-TLS Start message");
+               return EAPTLS_INVALID;
+       }
+
+       /*
+        *      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
+        *      TLS message or set of messages.
+        *
+        *      The M bit (more fragments) is set on all but the last
+        *      fragment.
+        *
+        *      The S bit (EAP-TLS start) is set in an EAP-TLS Start
+        *      message. This differentiates the EAP-TLS Start message
+        *      from a fragment acknowledgement.
+        */
+       if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) {
+               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)
+                        */
+                       if ((prev_eap_ds->response == NULL) ||
+                           (eaptls_prev == NULL) ||
+                           !TLS_MORE_FRAGMENTS(eaptls_prev->flags)) {
+
+                               DEBUG2("rlm_eap_tls:  Received EAP-TLS First Fragment of the message");
+                               return EAPTLS_FIRST_FRAGMENT;
+                       } else {
+
+                               DEBUG2("rlm_eap_tls:  More Fragments with length included");
+                               return EAPTLS_MORE_FRAGMENTS_WITH_LENGTH;
+                       }
+               } else {
+
+                       DEBUG2("rlm_eap_tls:  Length Included");
+                       return EAPTLS_LENGTH_INCLUDED;
+               }
+       }
+
+       if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
+               DEBUG2("rlm_eap_tls:  More fragments to follow");
+               return EAPTLS_MORE_FRAGMENTS;
+       }
+
+       /*
+        *      None of the flags are set, but it's still a valid
+        *      EAPTLS packet.
+        */
+       return EAPTLS_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)
+ *
+ * Points to consider during EAP-TLS data extraction
+ * 1. In the received packet, No data will be present incase of ACK-NAK
+ * 2. Incase if more fragments need to be received then ACK after retreiving this fragment.
+ *
+ *  RFC 2716 Section 4.2.  PPP EAP TLS Request Packet
+ *
+ *  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             |
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |     Type      |     Flags     |      TLS Message Length
+ *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *  |     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(EAP_DS *eap_ds, eaptls_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;
+
+       /*
+        *      The main EAP code & eaptls_verify() take care of
+        *      ensuring that the packet is OK, and that we can
+        *      extract the various fields we want.
+        *
+        *      e.g. a TLS packet with zero data is allowed as an ACK,
+        *      but we will never see it here, as we will simply
+        *      send another fragment, instead of trying to extract
+        *      the data.
+        *
+        *      MUST have TLS type octet, followed by flags, followed
+        *      by data.
+        */
+       rad_assert(eap_ds->response->length > 2);
+
+       tlspacket = eaptls_alloc();
+       if (tlspacket == NULL) return NULL;
+
+       /*
+        *      Code & id for EAPTLS & EAP are same
+        *      but eaptls_length = eap_length - 1(EAP-Type = 1 octet)
+        *
+        *      length = code + id + length + type + tlsdata
+        *             =  1   +  1 +   2    +  1    +  X
+        */
+       tlspacket->code = eap_ds->response->code;
+       tlspacket->id = eap_ds->response->id;
+       tlspacket->length = eap_ds->response->length - 1; /* EAP type */
+       tlspacket->flags = eap_ds->response->type.data[0];
+
+       /*
+        *      A quick sanity check of the flags.  If we've been told
+        *      that there's a length, and there isn't one, then stop.
+        */
+       if (TLS_LENGTH_INCLUDED(tlspacket->flags) &&
+           (tlspacket->length < 5)) { /* flags + TLS message length */
+               radlog(L_ERR, "rlm_eap_tls: Invalid EAP-TLS packet received.  (Length bit is set, but no length was found.)");
+               eaptls_free(&tlspacket);
+               return NULL;
+       }
+
+       /*
+        *      If the final TLS packet is larger than we can handle, die
+        *      now.
+        *
+        *      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) {
+                       radlog(L_ERR, "rlm_eap_tls: 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) {
+                       radlog(L_ERR, "rlm_eap_tls: EAP-TLS packet claims to be smaller than the encapsulating EAP packet.");
+                       eaptls_free(&tlspacket);
+                       return NULL;
+               }
+#endif
+       }
+
+       switch (status) {
+       /*
+        *      The TLS Message Length field is four octets, and
+        *      provides the total length of the TLS message or set of
+        *      messages that is being fragmented; this simplifies
+        *      buffer allocation.
+        *
+        *      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:
+               if (tlspacket->length < 5) { /* flags + TLS message length */
+                       radlog(L_ERR, "rlm_eap_tls: Invalid EAP-TLS packet received.  (Expected length, got none.)");
+                       eaptls_free(&tlspacket);
+                       return NULL;
+               }
+
+               /*
+                *      Extract all the TLS fragments from the
+                *      previous eap_ds Start appending this
+                *      fragment to the above ds
+                */
+               memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t));
+               data_len = ntohl(data_len);
+               data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/);
+               len = eap_ds->response->type.length - 5/*flags+TLS-Length*/;
+
+               /*
+                *      Hmm... this should be an error, too.
+                */
+               if (data_len > len) {
+                       data_len = len;
+               }
+               break;
+
+               /*
+                *      Data length is implicit, from the EAP header.
+                */
+       case EAPTLS_MORE_FRAGMENTS:
+       case EAPTLS_OK:
+               data_len = eap_ds->response->type.length - 1/*flags*/;
+               data = eap_ds->response->type.data + 1/*flags*/;
+               break;
+
+       default:
+               radlog(L_ERR, "rlm_eap_tls: Invalid EAP-TLS packet received");
+               eaptls_free(&tlspacket);
+               return NULL;
+       }
+
+       tlspacket->dlen = data_len;
+       if (data_len) {
+               tlspacket->data = (unsigned char *)malloc(data_len);
+               if (tlspacket->data == NULL) {
+                       radlog(L_ERR, "rlm_eap_tls: out of memory");
+                       eaptls_free(&tlspacket);
+                       return NULL;
+               }
+               memcpy(tlspacket->data, data, data_len);
+       }
+
+       return tlspacket;
+}
+
+
+
+/*
+ * 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. Handle that data structure to the TLS module.
+ *     4. TLS module will perform its operations on the data and
+ *     handle back to EAP-TLS
+ *
+ *  OUTGOING DATA:
+ *     1. EAP-TLS if necessary will fragment it and send it to the
+ *     destination.
+ *
+ *     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.)
+ */
+static void eaptls_operation(EAPTLS_PACKET *eaptls_packet UNUSED,
+                            eaptls_status_t status, EAP_HANDLER *handler)
+{
+       tls_session_t *tls_session;
+
+       tls_session = (tls_session_t *)handler->opaque;
+
+       if ((status == EAPTLS_MORE_FRAGMENTS) ||
+           (status == EAPTLS_MORE_FRAGMENTS_WITH_LENGTH) ||
+           (status == EAPTLS_FIRST_FRAGMENT)) {
+               /*
+                * Send the ACK.
+                */
+               eaptls_send_ack(handler->eap_ds, tls_session->peap_flag);
+       } else {
+               /*
+                *      We have the complete TLS-data or TLS-message.
+                *
+                *      Clean the dirty message.
+                *
+                *      Authenticate the user and send
+                *      Success/Failure.
+                *
+                *      If more info
+                *      is required then send another request.  */
+               if (tls_handshake_recv(tls_session)) {
+                       /*
+                        *      FIXME: return success/fail.
+                        *
+                        *      TLS proper can decide what to do, then.
+                        */
+                       eaptls_request(handler->eap_ds, tls_session);
+               } else {
+                       eaptls_fail(handler->eap_ds, tls_session->peap_flag);
+               }
+       }
+       return;
+}
+
+
+/*
+ * In the actual authentication first verify the packet and then create the data structure
+ */
+/*
+ * 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.
+ *
+ *     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 do not 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.)
+ */
+
+/*
+ *     Process an EAP request
+ */
+eaptls_status_t eaptls_process(EAP_HANDLER *handler)
+{
+       tls_session_t *tls_session = (tls_session_t *) handler->opaque;
+       EAPTLS_PACKET   *tlspacket;
+       eaptls_status_t status;
+
+       DEBUG2("  rlm_eap_tls: processing TLS");
+
+       /* 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);
+       DEBUG2("  eaptls_verify returned %d\n", status);
+
+       switch (status) {
+       default:
+       case EAPTLS_INVALID:
+       case EAPTLS_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;
+
+               /*
+                *      Normal TLS request, continue with the "get rest
+                *      of fragments" phase.
+                */
+       case EAPTLS_REQUEST:
+               eaptls_request(handler->eap_ds, tls_session);
+               return EAPTLS_HANDLED;
+               break;
+
+               /*
+                *      The handshake is done, and we're in the "tunnel
+                *      data" phase.
+                */
+       case EAPTLS_OK:
+               DEBUG2("  rlm_eap_tls: 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:
+               break;
+       }
+
+       /*
+        *      Extract the TLS packet from the buffer.
+        */
+       if ((tlspacket = eaptls_extract(handler->eap_ds, status)) == NULL)
+               return EAPTLS_FAIL;
+
+       /*
+        *      Get the session struct from the handler
+        *
+        *      update the dirty_in buffer
+        *
+        *      NOTE: This buffer will contain partial data when M bit is set.
+        *
+        *      CAUTION while reinitializing this buffer, it should be
+        *      reinitialized only when this M bit is NOT set.
+        */
+       if (tlspacket->dlen !=
+           (tls_session->record_plus)(&tls_session->dirty_in, tlspacket->data, tlspacket->dlen)) {
+               eaptls_free(&tlspacket);
+               radlog(L_ERR, "rlm_eap_tls: Exceeded maximum record size");
+               return EAPTLS_FAIL;
+       }
+
+       /*
+        *      SSL initalization is done.  Return.
+        *
+        *      The TLS data will be in the tls_session structure.
+        */
+       if (SSL_is_init_finished(tls_session->ssl)) {
+               eaptls_free(&tlspacket);
+               return EAPTLS_OK;
+       }
+
+       /*
+        *      Continue the handshake.
+        */
+       eaptls_operation(tlspacket, status, handler);
+
+       eaptls_free(&tlspacket);
+       return EAPTLS_HANDLED;
+}
+
+
+/*
+ *     compose the TLS reply packet in the EAP reply typedata
+ */
+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
+        *      handler will do that for us.  This allows the TLS
+        *      module to be called from TTLS & PEAP.
+        */
+
+       /*
+        *      When the EAP server receives an EAP-Response with the
+        *      M bit set, it MUST respond with an EAP-Request with
+        *      EAP-Type=EAP-TLS and no data. This serves as a
+        *      fragment ACK. The EAP peer MUST wait until it receives
+        *      the EAP-Request before sending another fragment.
+        *
+        *      In order to prevent errors in the processing of
+        *      fragments, the EAP server MUST use increment the
+        *      Identifier value for each fragment ACK contained
+        *      within an EAP-Request, and the peer MUST include this
+        *      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, "rlm_eap_tls: out of memory");
+               return 0;
+       }
+
+       /* EAPTLS Header length is excluded while computing EAP typelen */
+       eap_ds->request->type.length = reply->length - TLS_HEADER_LEN;
+
+       ptr = eap_ds->request->type.data;
+       *ptr++ = (uint8_t)(reply->flags & 0xFF);
+
+       if (reply->dlen) memcpy(ptr, reply->data, reply->dlen);
+
+       switch (reply->code) {
+       case EAPTLS_ACK:
+       case EAPTLS_START:
+       case EAPTLS_REQUEST:
+               eap_ds->request->code = PW_EAP_REQUEST;
+               break;
+       case EAPTLS_SUCCESS:
+               eap_ds->request->code = PW_EAP_SUCCESS;
+               break;
+       case EAPTLS_FAIL:
+               eap_ds->request->code = PW_EAP_FAILURE;
+               break;
+       default:
+               /* Should never enter here */
+               eap_ds->request->code = PW_EAP_FAILURE;
+               break;
+       }
+
+       return 1;
+}
diff --git a/src/modules/rlm_eap/libeap/tls.c b/src/modules/rlm_eap/libeap/tls.c
new file mode 100644 (file)
index 0000000..e772287
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * tls.c
+ *
+ * Version:     $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
+ * Copyright 2003  Alan DeKok <aland@freeradius.org>
+ */
+#include "eap_tls.h"
+
+/* record */
+static void            record_init(record_t *buf);
+static void            record_close(record_t *buf);
+static unsigned int    record_plus(record_t *buf, const void *ptr,
+                                   unsigned int size);
+static unsigned int    record_minus(record_t *buf, void *ptr,
+                                    unsigned int size);
+
+tls_session_t *eaptls_new_session(SSL_CTX *ssl_ctx, int client_cert)
+{
+       tls_session_t *state = NULL;
+       SSL *new_tls = NULL;
+       int verify_mode = SSL_VERIFY_NONE;
+
+       if ((new_tls = SSL_new(ssl_ctx)) == NULL) {
+               radlog(L_ERR, "rlm_eap_tls: Error creating new SSL");
+               ERR_print_errors_fp(stderr);
+               return NULL;
+       }
+
+       /* We use the SSL's "app_data" to indicate a call-back */
+       SSL_set_app_data(new_tls, NULL);
+
+       state = (tls_session_t *)malloc(sizeof(*state));
+       memset(state, 0, sizeof(*state));
+       session_init(state);
+       state->ssl = new_tls;
+
+       /*
+        *      Initialize callbacks
+        */
+       state->record_init = record_init;
+       state->record_close = record_close;
+       state->record_plus = record_plus;
+       state->record_minus = record_minus;
+
+       /*
+        *      Create & hook the BIOs to handle the dirty side of the
+        *      SSL.  This is *very important* as we want to handle
+        *      the transmission part.  Now the only IO interface
+        *      that SSL is aware of, is our defined BIO buffers.
+        *
+        *      This means that all SSL IO is done to/from memory,
+        *      and we can update those BIOs from the EAP packets we've
+        *      received.
+        */
+       state->into_ssl = BIO_new(BIO_s_mem());
+       state->from_ssl = BIO_new(BIO_s_mem());
+       SSL_set_bio(state->ssl, state->into_ssl, state->from_ssl);
+
+       /*
+        *      Add the message callback to identify what type of
+        *      message/handshake is passed
+        */
+       SSL_set_msg_callback(new_tls, cbtls_msg);
+       SSL_set_msg_callback_arg(new_tls, state);
+       SSL_set_info_callback(new_tls, cbtls_info);
+
+       /*
+        *      Verify the peer certificate, if asked.
+        */
+       if (client_cert) {
+               DEBUG2(" rlm_eap_tls: 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(state->ssl, verify_mode, cbtls_verify);
+
+       /*
+        *      In Server mode we only accept.
+        */
+       SSL_set_accept_state(state->ssl);
+
+       return state;
+}
+
+/*
+ *     Print out some text describing the error.
+ */
+static void int_ssl_check(SSL *s, int ret, const char *text)
+{
+       int e;
+
+       ERR_print_errors_fp(stderr);
+       e = SSL_get_error(s, ret);
+
+       switch(e) {
+               /*
+                *      These seem to be harmless and already "dealt
+                *      with" by our non-blocking environment. NB:
+                *      "ZERO_RETURN" is the clean "error"
+                *      indicating a successfully closed SSL
+                *      tunnel. We let this happen because our IO
+                *      loop should not appear to have broken on
+                *      this condition - and outside the IO loop, the
+                *      "shutdown" state is checked.
+                *
+                *      Don't print anything if we ignore the error.
+                */
+       case SSL_ERROR_NONE:
+       case SSL_ERROR_WANT_READ:
+       case SSL_ERROR_WANT_WRITE:
+       case SSL_ERROR_WANT_X509_LOOKUP:
+       case SSL_ERROR_ZERO_RETURN:
+               return;
+
+               /*
+                *      These seem to be indications of a genuine
+                *      error that should result in the SSL tunnel
+                *      being regarded as "dead".
+                */
+       case SSL_ERROR_SYSCALL:
+               radlog(L_ERR, "rlm_eap_tls: %s failed in a system call (%d), TLS session fails.",
+                      text, ret);
+               SSL_set_app_data(s, (char *)1);
+               return;
+
+       case SSL_ERROR_SSL:
+               radlog(L_ERR, "rlm_eap_tls: %s failed inside of TLS (%d), TLS session fails.",
+                      text, ret);
+               SSL_set_app_data(s, (char *)1);
+               return;
+
+       default:
+               /*
+                *      For any other errors that (a) exist, and (b)
+                *      crop up - we need to interpret what to do with
+                *      them - so "politely inform" the caller that
+                *      the code needs updating here.
+                */
+               radlog(L_ERR, "rlm_eap_tls: FATAL SSL error ..... %d\n", e);
+               break;
+       }
+}
+
+/*
+ * We are the server, we always get the dirty data
+ * (Handshake data is also considered as dirty data)
+ * During handshake, since SSL API handles itself,
+ * After clean-up, dirty_out will be filled with
+ * the data required for handshaking. So we check
+ * if dirty_out is empty then we simply send it back.
+ * As of now, if handshake is successful, then it is EAP-Success
+ * or else EAP-failure should be sent
+ *
+ * Fill the Bio with the dirty data to clean it
+ * Get the cleaned data from SSL, if it is not Handshake data
+ */
+int tls_handshake_recv(tls_session_t *ssn)
+{
+       int err;
+
+       BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
+       err = SSL_read(ssn->ssl, ssn->clean_out.data,
+                      sizeof(ssn->clean_out.data));
+       if (err > 0) {
+               ssn->clean_out.used = err;
+       } else {
+               int_ssl_check(ssn->ssl, err, "SSL_read");
+       }
+
+       /* Some Extra STATE information for easy debugging */
+       if (SSL_is_init_finished(ssn->ssl)) {
+               DEBUG2("SSL Connection Established\n");
+       }
+       if (SSL_in_init(ssn->ssl)) {
+               DEBUG2("In SSL Handshake Phase\n");
+       }
+       if (SSL_in_before(ssn->ssl)) {
+               DEBUG2("Before SSL Handshake Phase\n");
+       }
+       if (SSL_in_accept_init(ssn->ssl)) {
+               DEBUG2("In SSL Accept mode \n");
+       }
+       if (SSL_in_connect_init(ssn->ssl)) {
+               DEBUG2("In SSL Connect mode \n");
+       }
+
+       if (ssn->info.content_type != application_data) {
+               err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,
+                              sizeof(ssn->dirty_out.data));
+               if (err > 0) {
+                       ssn->dirty_out.used = err;
+               } else {
+                       int_ssl_check(ssn->ssl, err, "BIO_read");
+                       record_init(&ssn->dirty_in);
+                       return 0;
+               }
+       } else {
+               DEBUG2("rlm_eap_tls: Application Data");
+               /* Its clean application data, do whatever we want */
+               record_init(&ssn->clean_out);
+       }
+
+       /* We are done with dirty_in, reinitialize it */
+       record_init(&ssn->dirty_in);
+       return 1;
+}
+
+/*
+ *     Take clear-text user data, and encrypt it into the output buffer,
+ *     to send to the client at the other end of the SSL connection.
+ */
+int tls_handshake_send(tls_session_t *ssn)
+{
+       int err;
+
+       /*
+        *      If there's un-encrypted data in 'clean_in', then write
+        *      that data to the SSL session, and then call the BIO function
+        *      to get that encrypted data from the SSL session, into
+        *      a buffer which we can then package into an EAP packet.
+        *
+        *      Based on Server's logic this clean_in is expected to
+        *      contain the data to send to the client.
+        */
+       if (ssn->clean_in.used > 0) {
+               SSL_write(ssn->ssl, ssn->clean_in.data, ssn->clean_in.used);
+
+               /* Get the dirty data from Bio to send it */
+               err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,
+                              sizeof(ssn->dirty_out.data));
+               if (err > 0) {
+                       ssn->dirty_out.used = err;
+               } else {
+                       int_ssl_check(ssn->ssl, err, "handshake_send");
+               }
+       }
+
+       record_init(&ssn->clean_in);
+       return 1;
+}
+
+void session_init(tls_session_t *ssn)
+{
+       ssn->ssl = NULL;
+       ssn->into_ssl = ssn->from_ssl = NULL;
+       record_init(&ssn->clean_in);
+       record_init(&ssn->clean_out);
+       record_init(&ssn->dirty_in);
+       record_init(&ssn->dirty_out);
+
+       memset(&ssn->info, 0, sizeof(ssn->info));
+
+       ssn->offset = 0;
+       ssn->fragment = 0;
+       ssn->tls_msg_len = 0;
+       ssn->length_flag = 0;
+       ssn->opaque = NULL;
+       ssn->free_opaque = NULL;
+}
+
+void session_close(tls_session_t *ssn)
+{
+       if(ssn->ssl)
+               SSL_free(ssn->ssl);
+#if 0
+/*
+ * WARNING: SSL_free seems to decrement the reference counts already,
+ *     so doing this might crash the application.
+ */
+       if(ssn->into_ssl)
+               BIO_free(ssn->into_ssl);
+       if(ssn->from_ssl)
+               BIO_free(ssn->from_ssl);
+#endif
+       record_close(&ssn->clean_in);
+       record_close(&ssn->clean_out);
+       record_close(&ssn->dirty_in);
+       record_close(&ssn->dirty_out);
+       session_init(ssn);
+}
+
+void session_free(void *ssn)
+{
+       tls_session_t *sess = (tls_session_t *)ssn;
+
+       if (!ssn) return;
+
+       /*
+        *      Free any opaque TTLS or PEAP data.
+        */
+       if ((sess->opaque) && (sess->free_opaque)) {
+               sess->free_opaque(sess->opaque);
+               sess->opaque = NULL;
+       }
+
+       session_close(sess);
+
+       free(sess);
+}
+
+static void record_init(record_t *rec)
+{
+       rec->used = 0;
+}
+
+static void record_close(record_t *rec)
+{
+       rec->used = 0;
+}
+
+
+/*
+ *     Copy data to the intermediate buffer, before we send
+ *     it somewhere.
+ */
+static unsigned int record_plus(record_t *rec, const void *ptr,
+                               unsigned int size)
+{
+       unsigned int added = MAX_RECORD_SIZE - rec->used;
+
+       if(added > size)
+               added = size;
+       if(added == 0)
+               return 0;
+       memcpy(rec->data + rec->used, ptr, added);
+       rec->used += added;
+       return added;
+}
+
+/*
+ *     Take data from the buffer, and give it to the caller.
+ */
+static unsigned int record_minus(record_t *rec, void *ptr,
+                                unsigned int size)
+{
+       unsigned int taken = rec->used;
+
+       if(taken > size)
+               taken = size;
+       if(taken == 0)
+               return 0;
+       if(ptr)
+               memcpy(ptr, rec->data, taken);
+       rec->used -= taken;
+
+       /*
+        *      This is pretty bad...
+        */
+       if(rec->used > 0)
+               memmove(rec->data, rec->data + taken, rec->used);
+       return taken;
+}
+
+void tls_session_information(tls_session_t *tls_session)
+{
+       const char *str_write_p, *str_version, *str_content_type = "";
+       const char *str_details1 = "", *str_details2= "";
+
+       /*
+        *      Don't print this out in the normal course of
+        *      operations.
+        */
+       if (debug_flag == 0) {
+               return;
+       }
+
+       str_write_p = tls_session->info.origin ? ">>>" : "<<<";
+
+       switch (tls_session->info.version)
+       {
+       case SSL2_VERSION:
+               str_version = "SSL 2.0";
+               break;
+       case SSL3_VERSION:
+               str_version = "SSL 3.0 ";
+               break;
+       case TLS1_VERSION:
+               str_version = "TLS 1.0 ";
+               break;
+       default:
+               str_version = "Unknown TLS version";
+               break;
+       }
+
+       if (tls_session->info.version == SSL3_VERSION ||
+           tls_session->info.version == TLS1_VERSION) {
+               switch (tls_session->info.content_type) {
+               case SSL3_RT_CHANGE_CIPHER_SPEC:
+                       str_content_type = "ChangeCipherSpec";
+                       break;
+               case SSL3_RT_ALERT:
+                       str_content_type = "Alert";
+                       break;
+               case SSL3_RT_HANDSHAKE:
+                       str_content_type = "Handshake";
+                       break;
+               case SSL3_RT_APPLICATION_DATA:
+                       str_content_type = "ApplicationData";
+                       break;
+               default:
+                       str_content_type = "UnknownContentType";
+                       break;
+               }
+
+               if (tls_session->info.content_type == SSL3_RT_ALERT) {
+                       str_details1 = ", ???";
+
+                       if (tls_session->info.record_len == 2) {
+
+                               switch (tls_session->info.alert_level) {
+                               case SSL3_AL_WARNING:
+                                       str_details1 = ", warning";
+                                       break;
+                               case SSL3_AL_FATAL:
+                                       str_details1 = ", fatal";
+                                       break;
+                               }
+
+                               str_details2 = " ???";
+                               switch (tls_session->info.alert_description) {
+                               case SSL3_AD_CLOSE_NOTIFY:
+                                       str_details2 = " close_notify";
+                                       break;
+                               case SSL3_AD_UNEXPECTED_MESSAGE:
+                                       str_details2 = " unexpected_message";
+                                       break;
+                               case SSL3_AD_BAD_RECORD_MAC:
+                                       str_details2 = " bad_record_mac";
+                                       break;
+                               case TLS1_AD_DECRYPTION_FAILED:
+                                       str_details2 = " decryption_failed";
+                                       break;
+                               case TLS1_AD_RECORD_OVERFLOW:
+                                       str_details2 = " record_overflow";
+                                       break;
+                               case SSL3_AD_DECOMPRESSION_FAILURE:
+                                       str_details2 = " decompression_failure";
+                                       break;
+                               case SSL3_AD_HANDSHAKE_FAILURE:
+                                       str_details2 = " handshake_failure";
+                                       break;
+                               case SSL3_AD_BAD_CERTIFICATE:
+                                       str_details2 = " bad_certificate";
+                                       break;
+                               case SSL3_AD_UNSUPPORTED_CERTIFICATE:
+                                       str_details2 = " unsupported_certificate";
+                                       break;
+                               case SSL3_AD_CERTIFICATE_REVOKED:
+                                       str_details2 = " certificate_revoked";
+                                       break;
+                               case SSL3_AD_CERTIFICATE_EXPIRED:
+                                       str_details2 = " certificate_expired";
+                                       break;
+                               case SSL3_AD_CERTIFICATE_UNKNOWN:
+                                       str_details2 = " certificate_unknown";
+                                       break;
+                               case SSL3_AD_ILLEGAL_PARAMETER:
+                                       str_details2 = " illegal_parameter";
+                                       break;
+                               case TLS1_AD_UNKNOWN_CA:
+                                       str_details2 = " unknown_ca";
+                                       break;
+                               case TLS1_AD_ACCESS_DENIED:
+                                       str_details2 = " access_denied";
+                                       break;
+                               case TLS1_AD_DECODE_ERROR:
+                                       str_details2 = " decode_error";
+                                       break;
+                               case TLS1_AD_DECRYPT_ERROR:
+                                       str_details2 = " decrypt_error";
+                                       break;
+                               case TLS1_AD_EXPORT_RESTRICTION:
+                                       str_details2 = " export_restriction";
+                                       break;
+                               case TLS1_AD_PROTOCOL_VERSION:
+                                       str_details2 = " protocol_version";
+                                       break;
+                               case TLS1_AD_INSUFFICIENT_SECURITY:
+                                       str_details2 = " insufficient_security";
+                                       break;
+                               case TLS1_AD_INTERNAL_ERROR:
+                                       str_details2 = " internal_error";
+                                       break;
+                               case TLS1_AD_USER_CANCELLED:
+                                       str_details2 = " user_canceled";
+                                       break;
+                               case TLS1_AD_NO_RENEGOTIATION:
+                                       str_details2 = " no_renegotiation";
+                                       break;
+                               }
+                       }
+               }
+
+               if (tls_session->info.content_type == SSL3_RT_HANDSHAKE) {
+                       str_details1 = "???";
+
+                       if (tls_session->info.record_len > 0)
+                       switch (tls_session->info.handshake_type)
+                       {
+                       case SSL3_MT_HELLO_REQUEST:
+                               str_details1 = ", HelloRequest";
+                               break;
+                       case SSL3_MT_CLIENT_HELLO:
+                               str_details1 = ", ClientHello";
+                               break;
+                       case SSL3_MT_SERVER_HELLO:
+                               str_details1 = ", ServerHello";
+                               break;
+                       case SSL3_MT_CERTIFICATE:
+                               str_details1 = ", Certificate";
+                               break;
+                       case SSL3_MT_SERVER_KEY_EXCHANGE:
+                               str_details1 = ", ServerKeyExchange";
+                               break;
+                       case SSL3_MT_CERTIFICATE_REQUEST:
+                               str_details1 = ", CertificateRequest";
+                               break;
+                       case SSL3_MT_SERVER_DONE:
+                               str_details1 = ", ServerHelloDone";
+                               break;
+                       case SSL3_MT_CERTIFICATE_VERIFY:
+                               str_details1 = ", CertificateVerify";
+                               break;
+                       case SSL3_MT_CLIENT_KEY_EXCHANGE:
+                               str_details1 = ", ClientKeyExchange";
+                               break;
+                       case SSL3_MT_FINISHED:
+                               str_details1 = ", Finished";
+                               break;
+                       }
+               }
+       }
+
+       sprintf(tls_session->info.info_description, "%s %s%s [length %04lx]%s%s\n",
+               str_write_p, str_version, str_content_type,
+               (unsigned long)tls_session->info.record_len, str_details1, str_details2);
+       DEBUG2("  rlm_eap_tls: %s\n", tls_session->info.info_description);
+}