blob: 6b812dde04fae4df99e30b16836aab08cc841dac [file] [log] [blame]
/*****************************************************************************************************
* Software License Agreement (BSD License)
* Author : Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>
*
* Copyright (c) 2009-2010, Souheil Ben Ayed, Teraoka Laboratory of Keio University, and the WIDE Project
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Souheil Ben Ayed <souheil@tera.ics.keio.ac.jp>.
*
* 4. Neither the name of Souheil Ben Ayed, Teraoka Laboratory of Keio University or the WIDE Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************************************/
#include "eap_tls.h"
int eap_tls_configure(char * configfile);
int eap_tls_init(struct eap_state_machine *smd);
int eap_tls_initPickUp(struct eap_state_machine *smd);
int eap_tls_buildReq(struct eap_state_machine *smd, u8 eap_md5,
struct eap_packet * eapPacket);
int eap_tls_getTimeout(struct eap_state_machine *smd, int * timeout);
boolean eap_tls_check(struct eap_state_machine *smd,
struct eap_packet *eapRespData);
int eap_tls_process(struct eap_state_machine *smd,
struct eap_packet *eapRespData);
boolean eap_tls_isDone(struct eap_state_machine *smd);
int eap_tls_getKey(struct eap_state_machine *smd, u8** msk, int * msklen, u8** emsk, int * emsklen);
void eap_tls_unregister(void);
void eap_tls_free(void * data);
REGISTER_METHOD("eap_tls", "eap_tls_configure", "eap_tls_init", "eap_tls_initPickUp", "eap_tls_buildReq", "eap_tls_getTimeout", "eap_tls_check", "eap_tls_process", "eap_tls_isDone", "eap_tls_getKey", "eap_tls_unregister", "eap_tls_free")
;
int eap_tls_configure(char * configfile)
{
int ret;
extern FILE * eaptlsin;
if (configfile)
{
tls_global_conf.conffile = configfile;
}
tls_global_conf.certfile = NULL;
tls_global_conf.keyfile = NULL;
tls_global_conf.cafile = NULL;
tls_global_conf.crlfile = NULL;
tls_global_conf.check_cert_cn_username = FALSE;
/*Parse EAP TLS configuration file */
eaptlsin = fopen(tls_global_conf.conffile, "r");
if (!eaptlsin)
{
TRACE_DEBUG(INFO,"%s[EAP TLS plugin] Unable to open configuration file %s for reading: %s",DIAMEAP_EXTENSION, tls_global_conf.conffile, strerror(errno));
return errno;
}
/* call yacc parser */
CHECK_FCT(eaptlsparse(&tls_global_conf));
tls_global_conf.max_size = 64*1024 /* As per RFC 5216 recommendation */;
/* Initializing GnuTLS library */
ret = diameap_tls_init(&tls_global_conf);
return ret;
}
int eap_tls_init(struct eap_state_machine *smd)
{
int ret;
struct tls_data *data = NULL;
CHECK_MALLOC(data = malloc(sizeof(struct tls_data)));
memset(data, 0, sizeof(struct tls_data));
CHECK_FCT(diameap_tls_initialize(data));
ret = diameap_tls_init_session(&tls_global_conf, data);
smd->methodData = (struct tls_data*) data;
if (ret < 0)
{
return ret;
}
return 0;
}
int eap_tls_initPickUp(struct eap_state_machine *smd)
{
return 0;
}
int eap_tls_buildReq(struct eap_state_machine *smd, u8 id,
struct eap_packet * eapPacket)
{
struct tls_data * data;
data = (struct tls_data *) smd->methodData;
if (data->more_toreceive == TRUE)
{
CHECK_FCT(diameap_eap_tls_buildReq_ack(id,eapPacket));
return 0;
}
if (data->state == START)
{
CHECK_FCT(diameap_eap_tls_buildReq_start(id,eapPacket));
return 0;
}
if (data->state == CONTINUE)
{
diameap_eap_tls_buildReq_data(data, id, eapPacket);
smd->methodData = (struct tls_data*) data;
return 0;
}
return 0;
}
int eap_tls_getTimeout(struct eap_state_machine *smd, int * timeout)
{
return 0;
}
boolean eap_tls_check(struct eap_state_machine *smd,
struct eap_packet *eapRespData)
{
eap_type type;
if(diameap_eap_get_type(eapRespData,&type)!=0){
goto cf;
}
if (type == TYPE_EAP_TLS)
{
return TRUE;
}
cf:
TRACE_DEBUG(INFO,"%s[EAP TLS plugin] EAP-TLS check failed: Received EAP packet with different EAP-Type (Type = %d)",DIAMEAP_EXTENSION, type);
return FALSE;
}
int eap_tls_process(struct eap_state_machine *smd,
struct eap_packet *eapRespData)
{
struct tls_data * data;
data = (struct tls_data *) smd->methodData;
struct tls_msg tlsmsg;
CHECK_FCT(diameap_eap_tls_parse(&tlsmsg,eapRespData));
if ((tlsmsg.datalength == 0))
{
if (data->more_tosend_length > 0)
{
//ACK and more to send
return 0;
}
else
{
//Success
if (data->handshake == TRUE)
{
data->state = SUCCESS;
smd->user.success = TRUE;
if(tls_global_conf.check_cert_cn_username == TRUE){
unsigned int list_size;
const gnutls_datum_t * list = gnutls_certificate_get_peers (data->session, &list_size);
if(list_size<1){
goto failure;
}
gnutls_x509_crt_t cert;
CHECK_GNUTLS_DO(gnutls_x509_crt_init(&cert),{
TRACE_DEBUG(NONE,"%s[EAP TLS plugin] [GnuTLS] error in initialization crt init",DIAMEAP_EXTENSION);
goto failure;});
CHECK_GNUTLS_DO(gnutls_x509_crt_import(cert, &list[0], GNUTLS_X509_FMT_DER), {
TRACE_DEBUG(NONE,"%s[EAP TLS plugin] [GnuTLS] error parsing certificate",DIAMEAP_EXTENSION);
goto failure;});
void * buff;
size_t size_buffer;
int ret;
ret = gnutls_x509_crt_get_dn_by_oid(cert,GNUTLS_OID_X520_COMMON_NAME,0,0,NULL,&size_buffer);
if( ret != GNUTLS_E_SHORT_MEMORY_BUFFER){
CHECK_GNUTLS_DO(ret,{
TRACE_DEBUG(NONE,"%s[EAP TLS plugin] [GnuTLS] error get dn by oid",DIAMEAP_EXTENSION);
goto failure;});
}
CHECK_MALLOC_DO(buff=malloc(size_buffer), goto failure);
CHECK_GNUTLS_DO(gnutls_x509_crt_get_dn_by_oid(cert,GNUTLS_OID_X520_COMMON_NAME,0,0,buff,&size_buffer),{
TRACE_DEBUG(NONE,"%s[EAP TLS plugin] [GnuTLS] error get dn by oid",DIAMEAP_EXTENSION);
goto failure;});
if(strncmp((char *)smd->user.userid,buff,smd->user.useridLength)!=0){
goto failure;
}
gnutls_x509_crt_deinit(cert);
goto next;
failure:
TRACE_DEBUG(NONE,"%s[EAP TLS plugin] Checking failed. certificate's CN does not match User_Name AVP value.",DIAMEAP_EXTENSION);
data->state = FAILURE;
smd->user.success = FALSE;
gnutls_x509_crt_deinit(cert);
}
next:
smd->methodData = (struct tls_data*) data;
return 0;
}
return 0;
}
}
if (data->more_toreceive == TRUE)
{
//reassemble received fragment to TLS Response
CHECK_FCT(diameap_tls_reassemble(&data->tlsResp,tlsmsg));
}
else
{
//receive the first fragment or a complete TLS message
CHECK_FCT(diameap_tls_copy(&data->tlsResp,tlsmsg));
}
if (tlsmsg.flags & TLS_FLAG_MORE)
{
data->more_toreceive = TRUE;
smd->methodData = (struct tls_data*) data;
return 0;
}
else
{
//last fragment received
data->more_toreceive = FALSE;
}
data->state = CONTINUE;
diameap_tls_process_receive(data);
if (data->state == SUCCESS)
{
smd->user.success = TRUE;
}
smd->methodData = (struct tls_data*) data;
return 0;
}
boolean eap_tls_isDone(struct eap_state_machine *smd)
{
struct tls_data * data;
data = (struct tls_data *) smd->methodData;
if (data->state == CONTINUE || data->state == START)
{
return FALSE;
}
return TRUE;
}
int eap_tls_getKey(struct eap_state_machine *smd, u8 ** msk, int *msklen, u8 ** emsk, int *emsklen)
{
struct tls_data * data;
int len = emsk ? 128 : 64;
data = (struct tls_data *) smd->methodData;
*msk = malloc(len);
if (gnutls_prf(data->session, strlen("client EAP encryption"),
"client EAP encryption", 0, 0, NULL, len, (char *) *msk)
!= GNUTLS_E_SUCCESS)
{
free(*msk);
*msk = NULL;
*msklen = 0;
return 1;
}
else
{
*msklen = 64;
}
if (emsk) {
*emsk = malloc(64);
memcpy(*emsk, (*msk)+64, 64);
memset((*msk)+64, 0, 64);
*emsklen = 64;
}
return 0;
}
void eap_tls_unregister(void)
{
//
}
void eap_tls_free(void * mdata)
{
struct tls_data *data;
data = (struct tls_data*) mdata;
gnutls_deinit(data->session);
if(data->tlsReq.data){
free(data->tlsReq.data);
data->tlsReq.data=NULL;
}
if(data->tlsResp.data){
free(data->tlsResp.data);
data->tlsResp.data=NULL;
}
free(data);
data=NULL;
}
//send TLS ACK Request (empty TLS msg)
int diameap_eap_tls_buildReq_ack(u8 id, struct eap_packet * eapPacket)
{
u8* payload;
struct tls_msg tlsmsg;
int len;
CHECK_FCT(diameap_tls_new(&tlsmsg));
CHECK_FCT(diameap_tls_new_tls_packet(&payload,&len,tlsmsg));
CHECK_FCT(diameap_eap_new(EAP_REQUEST,id,TYPE_EAP_TLS,payload,len,eapPacket));
return 0;
}
// parse EAP TLS msg
int diameap_eap_tls_parse(struct tls_msg * tlsmsg, struct eap_packet *eapPacket)
{
u8 *datatls;
int len;
//initialize a new empty EAP TLS msg
diameap_tls_new(tlsmsg);
//retrieve the data field from EAP Packet
diameap_eap_get_data(eapPacket, &datatls, &len);
//parse EAP TLS msg
diameap_tls_parse(datatls, len, tlsmsg);
return 0;
}
int diameap_eap_tls_buildReq_start(u8 id, struct eap_packet * eapPacket)
{
u8* payload;
struct tls_msg tlsmsg;
int len;
CHECK_FCT(diameap_tls_new(&tlsmsg));
CHECK_FCT(diameap_tls_set_flags(&tlsmsg,TLS_FLAG_START));
CHECK_FCT(diameap_tls_new_tls_packet(&payload,&len,tlsmsg));
CHECK_FCT(diameap_eap_new(EAP_REQUEST,id,TYPE_EAP_TLS,payload,len,eapPacket));
return 0;
}
int diameap_eap_tls_buildReq_data(struct tls_data * data, int id,
struct eap_packet * eapPacket)
{
struct tls_msg tlsmsg;
u8* datatosend;
u8 * eaptls_data;
int length = 0;
diameap_tls_new(&tlsmsg);
if (data->more_tosend_length == 0)
{
//First fragment of message or the only fragment of message
data->more_tosend_length = data->tlsReq.datalength;
}
if (data->more_tosend_length > tls_global_conf.max_size)
{
//New fragment of message. Is not the last fragment.
length = tls_global_conf.max_size;
CHECK_FCT(diameap_tls_set_flags(&tlsmsg,TLS_FLAG_MORE));
if (data->more_tosend_length == data->tlsReq.datalength)
{
//The first fragment of message
CHECK_FCT(diameap_tls_set_message_length(&tlsmsg,data->tlsReq.datalength));//set L flag and length value
}
}
else
{
//The last fragment or the only fragment.
length = data->more_tosend_length;
}
datatosend = malloc(sizeof(u8) * length);
U8COPY(datatosend,0,length,data->tlsReq.data+(data->tlsReq.datalength-data->more_tosend_length));
data->more_tosend_length -= length;
CHECK_FCT(diameap_tls_set_data(&tlsmsg,datatosend,length));
CHECK_FCT(diameap_tls_new_tls_packet(&eaptls_data,&length,tlsmsg));
CHECK_FCT(diameap_eap_new(EAP_REQUEST,id,TYPE_EAP_TLS,eaptls_data,length,eapPacket));
if (data->more_tosend_length == 0)
{
diameap_tls_new(&data->tlsReq);
}
return 0;
}