Move TLS code from rlm_eap_tls to libeap.
+++ /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) {
- if (debug_flag) radlog(L_INFO, "%s: %s\n", str, state);
- } else if (where & SSL_CB_HANDSHAKE_START) {
- if (debug_flag) radlog(L_INFO, "%s: %s\n", str, state);
- } else if (where & SSL_CB_HANDSHAKE_DONE) {
- radlog(L_INFO, "%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));
- /*
- * 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) {
- radlog(L_INFO, "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);
- }
- 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.
- */
- radlog(L_INFO, "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)) {
-
- radlog(L_INFO, "rlm_eap_tls: Received EAP-TLS First Fragment of the message");
- return EAPTLS_FIRST_FRAGMENT;
- } else {
-
- radlog(L_INFO, "rlm_eap_tls: More Fragments with length included");
- return EAPTLS_MORE_FRAGMENTS_WITH_LENGTH;
- }
- } else {
-
- radlog(L_INFO, "rlm_eap_tls: Length Included");
- return EAPTLS_LENGTH_INCLUDED;
- }
- }
-
- if (TLS_MORE_FRAGMENTS(eaptls_packet->flags)) {
- radlog(L_INFO, "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 !=
- 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
-/*
- * eap_tls.h
- *
- * 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>
- */
-#ifndef _EAP_TLS_H
-#define _EAP_TLS_H
-
-#include "rlm_eap_tls.h"
-
-#define BUFFER_SIZE 1024
-
-#define EAP_TLS_START 1
-#define EAP_TLS_ACK 2
-#define EAP_TLS_SUCCESS 3
-#define EAP_TLS_FAIL 4
-#define EAP_TLS_ALERT 9
-
-#define TLS_HEADER_LEN 4
-
-/*
- * RFC 2716, Section 4.2:
- *
- * Flags
- *
- * 0 1 2 3 4 5 6 7 8
- * +-+-+-+-+-+-+-+-+
- * |L M S R R R R R|
- * +-+-+-+-+-+-+-+-+
- *
- * L = Length included
- * M = More fragments
- * S = EAP-TLS start
- * R = Reserved
- */
-#define TLS_START(x) (((x) & 0x20) != 0)
-#define TLS_MORE_FRAGMENTS(x) (((x) & 0x40) != 0)
-#define TLS_LENGTH_INCLUDED(x) (((x) & 0x80) != 0)
-
-#define TLS_CHANGE_CIPHER_SPEC(x) (((x) & 0x0014) == 0x0014)
-#define TLS_ALERT(x) (((x) & 0x0015) == 0x0015)
-#define TLS_HANDSHAKE(x) (((x) & 0x0016) == 0x0016)
-
-#define SET_START(x) ((x) | (0x20))
-#define SET_MORE_FRAGMENTS(x) ((x) | (0x40))
-#define SET_LENGTH_INCLUDED(x) ((x) | (0x80))
-
-
-/*
- * Following enums from rfc2246
- *
- * Hmm... since we dpeend on OpenSSL, it would be smarter to
- * use the OpenSSL names for these.
- */
-enum ContentType {
- change_cipher_spec = 20,
- alert = 21,
- handshake = 22,
- application_data = 23
-};
-
-enum AlertLevel {
- warning = 1,
- fatal = 2
-};
-
-enum AlertDescription {
- close_notify = 0,
- unexpected_message = 10,
- bad_record_mac = 20,
- decryption_failed = 21,
- record_overflow = 22,
- decompression_failure = 30,
- handshake_failure = 40,
- bad_certificate = 42,
- unsupported_certificate = 43,
- certificate_revoked = 44,
- certificate_expired = 45,
- certificate_unknown = 46,
- illegal_parameter = 47,
- unknown_ca = 48,
- access_denied = 49,
- decode_error = 50,
- decrypt_error = 51,
- export_restriction = 60,
- protocol_version = 70,
- insufficient_security = 71,
- internal_error = 80,
- user_canceled = 90,
- no_renegotiation = 100
-};
-
-enum HandshakeType {
- hello_request = 0,
- client_hello = 1,
- server_hello = 2,
- certificate = 11,
- server_key_exchange = 12,
- certificate_request = 13,
- server_hello_done = 14,
- certificate_verify = 15,
- client_key_exchange = 16,
- finished = 20
-};
-
-
-/*
- * From rfc
- Flags
-
- 0 1 2 3 4 5 6 7 8
- +-+-+-+-+-+-+-+-+
- |L M S R R R R R|
- +-+-+-+-+-+-+-+-+
-
- L = Length included
- M = More fragments
- S = EAP-TLS start
- R = Reserved
-
- 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.
-
- TLS Message Length
-
- The TLS Message Length field is four octets, and is present only
- if the L bit is set. This field provides the total length of the
- TLS message or set of messages that is being fragmented.
-
- TLS data
-
- The TLS data consists of the encapsulated TLS packet in TLS record
- format.
- *
- * The data structures present here
- * maps only to the typedata in the EAP packet
- *
- * Based on the L bit flag, first 4 bytes of data indicate the length
- */
-typedef struct tls_packet_t {
- uint8_t flags;
- uint8_t data[1];
-} eaptls_packet_t;
-
-typedef struct tls_packet {
- uint8_t code;
- uint8_t id;
- uint32_t length;
- uint8_t flags;
- uint8_t *data;
- uint32_t dlen;
-
- //uint8_t *packet; /* Wired EAP-TLS packet as found in typdedata of EAP_PACKET */
-} EAPTLS_PACKET;
-
-
-/* configured values goes right here */
-typedef struct eap_tls_conf {
- char *private_key_password;
- char *private_key_file;
- char *certificate_file;
- char *random_file;
- char *ca_path;
- char *ca_file;
- char *dh_file;
- char *rsa_file;
- int rsa_key;
- int dh_key;
- int rsa_key_length;
- int dh_key_length;
- int verify_depth;
- int file_type;
- int include_length;
-
- /*
- * Always < 4096 (due to radius limit), 0 by default = 2048
- */
- int fragment_size;
- int check_crl;
- char *check_cert_cn;
-} EAP_TLS_CONF;
-
-
-/* This structure gets stored in arg */
-typedef struct _eap_tls_t {
- EAP_TLS_CONF *conf;
- SSL_CTX *ctx;
-} eap_tls_t;
-
-
-/* EAP-TLS framework */
-EAPTLS_PACKET *eaptls_alloc(void);
-void eaptls_free(EAPTLS_PACKET **eaptls_packet_ptr);
-int eaptls_start(EAP_DS *eap_ds, int peap);
-int eaptls_compose(EAP_DS *eap_ds, EAPTLS_PACKET *reply);
-
-/* Callbacks */
-int cbtls_password(char *buf, int num, int rwflag, void *userdata);
-void cbtls_info(const SSL *s, int where, int ret);
-int cbtls_verify(int ok, X509_STORE_CTX *ctx);
-void cbtls_msg(int write_p, int msg_version, int content_type,
- const void *buf, size_t len, SSL *ssl, void *arg);
-RSA *cbtls_rsa(SSL *s, int is_export, int keylength);
-
-/* TLS */
-tls_session_t *eaptls_new_session(SSL_CTX *ssl_ctx, int client_cert);
-int tls_handshake_recv(tls_session_t *ssn);
-int tls_handshake_send(tls_session_t *ssn);
-void tls_session_information(tls_session_t *tls_session);
-
-/* Session */
-void session_free(void *ssn);
-void session_close(tls_session_t *ssn);
-void session_init(tls_session_t *ssn);
-
-/* record */
-void record_init(record_t *buf);
-void record_close(record_t *buf);
-unsigned int record_plus(record_t *buf, const unsigned char *ptr,
- unsigned int size);
-unsigned int record_minus(record_t *buf, unsigned char *ptr,
- unsigned int size);
-#endif /*_EAP_TLS_H*/
#ifndef _RLM_EAP_TLS_H
#define _RLM_EAP_TLS_H
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <netdb.h>
-#include <fcntl.h>
-#include <signal.h>
-
-#include <ctype.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
-#include "config.h"
-
-#ifndef NO_OPENSSL
-/*
- * For RH 9, which apparently needs this.
- */
-#ifndef OPENSSL_NO_KRB5
-#define OPENSSL_NO_KRB5
-#endif
-#include <openssl/err.h>
-#ifdef HAVE_OPENSSL_ENGINE_H
-#include <openssl/engine.h>
-#endif
-#include <openssl/ssl.h>
-#endif /* !defined(NO_OPENSSL) */
-
-#include "eap.h"
-
-typedef enum {
- EAPTLS_INVALID = 0, /* invalid, don't reply */
- EAPTLS_REQUEST, /* request, ok to send, invalid to receive */
- EAPTLS_RESPONSE, /* response, ok to receive, invalid to send */
- EAPTLS_SUCCESS, /* success, send success */
- EAPTLS_FAIL, /* fail, send fail */
- EAPTLS_NOOP, /* noop, continue */
-
- EAPTLS_START, /* start, ok to send, invalid to receive */
- EAPTLS_OK, /* ok, continue */
- EAPTLS_ACK, /* acknowledge, continue */
- EAPTLS_FIRST_FRAGMENT, /* first fragment */
- EAPTLS_MORE_FRAGMENTS, /* more fragments, to send/receive */
- EAPTLS_LENGTH_INCLUDED, /* length included */
- EAPTLS_MORE_FRAGMENTS_WITH_LENGTH, /* more fragments with length */
- EAPTLS_HANDLED /* tls code has handled it */
-} eaptls_status_t;
-
-#define MAX_RECORD_SIZE 16384
-
-/*
- * 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.
- *
- * However, note that in order to protect against reassembly
- * lockup and denial of service attacks, it may be desirable for
- * an implementation to set a maximum size for one such group of
- * TLS messages.
- *
- * 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.
- */
-
-/*
- * FIXME: Dynamic allocation of buffer to overcome MAX_RECORD_SIZE overflows.
- * or configure TLS not to exceed MAX_RECORD_SIZE.
- */
-typedef struct _record_t {
- unsigned char data[MAX_RECORD_SIZE];
- unsigned int used;
-} record_t;
-
-typedef struct _tls_info_t {
- unsigned char origin;
- unsigned char content_type;
- unsigned char handshake_type;
- unsigned char alert_level;
- unsigned char alert_description;
- char info_description[256];
- size_t record_len;
- int version;
- char initialized;
-} tls_info_t;
-
-/*
- * tls_session_t Structure gets stored as opaque in EAP_HANDLER
- * This contains EAP-REQUEST specific data
- * (ie EAPTLS_DATA(fragment), EAPTLS-ALERT, EAPTLS-REQUEST ...)
- *
- * clean_in - data that needs to be sent but only after it is soiled.
- * dirty_in - data EAP server receives.
- * clean_out - data that is cleaned after receiving.
- * dirty_out - data EAP server sends.
- * offset - current fragment size transmitted
- * fragment - Flag, In fragment mode or not.
- * tls_msg_len - Actual/Total TLS message length.
- * length_flag - A flag to include length in every TLS Data/Alert packet
- * if set to no then only the first fragment contains length
- */
-typedef struct _tls_session_t {
- SSL *ssl;
- tls_info_t info;
-
- BIO *into_ssl;
- BIO *from_ssl;
- record_t clean_in;
- record_t clean_out;
- record_t dirty_in;
- record_t dirty_out;
-
- /*
- * Framed-MTU attribute in RADIUS,
- * if present, can also be used to set this
- */
- unsigned int offset;
- unsigned int tls_msg_len;
- int fragment;
- int length_flag;
- int peap_flag;
-
- /*
- * Used by TTLS & PEAP to keep track of other per-session
- * data.
- */
- void *opaque;
- void (*free_opaque)(void *opaque);
-} tls_session_t;
-
-
-/*
- * Externally exported TLS functions.
- */
-eaptls_status_t eaptls_process(EAP_HANDLER *handler);
-
-int eaptls_success(EAP_DS *eap_ds, int peap_flag);
-int eaptls_fail(EAP_DS *eap_ds, int peap_flag);
-int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn);
-
-
-/* MPPE key generation */
-void eaptls_gen_mppe_keys(VALUE_PAIR **reply_vps, SSL *s,
- const char *prf_label);
-void eapttls_gen_challenge(SSL *s, char *buffer, int size);
+#include "eap_tls.h"
#endif /* _RLM_EAP_TLS_H */
+++ /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"
-
-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;
-
- /*
- * 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 {
- radlog(L_INFO, "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");
- }
- }
-
- 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);
-}
-
-void record_init(record_t *rec)
-{
- rec->used = 0;
-}
-
-void record_close(record_t *rec)
-{
- rec->used = 0;
-}
-
-
-/*
- * Copy data to the intermediate buffer, before we send
- * it somewhere.
- */
-unsigned int record_plus(record_t *rec, const unsigned char *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.
- */
-unsigned int record_minus(record_t *rec, unsigned char *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);
-}