--- /dev/null
+/*
+ * 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) */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}