Initial commit
Change-Id: I6a4444e3c193dae437cd7929f4c39aba7b749efa
diff --git a/extensions/app_diameap/diameap_tls.c b/extensions/app_diameap/diameap_tls.c
new file mode 100644
index 0000000..e77a65c
--- /dev/null
+++ b/extensions/app_diameap/diameap_tls.c
@@ -0,0 +1,492 @@
+/*****************************************************************************************************
+ * 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 "diameap_tls.h"
+
+//GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+int diameap_tls_init(struct tls_config * tls_conf)
+{
+ int ret;
+ ret = gnutls_global_init();
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ }
+
+ gnutls_global_set_log_function(diameap_tls_log);
+ //gnutls_global_set_log_level(9);
+
+
+ if(tls_conf->cafile ==NULL){
+ fprintf(stderr,"[DiamEAP extension] [EAP TLS] Missing certification authority (CA) certificates. Please provide CA configuration directive.\n");
+ return EINVAL;
+ }
+ if( !tls_conf->certfile || !tls_conf->keyfile){
+ fprintf(stderr,"[DiamEAP extension] [EAP TLS] Missing private Key. Please provide Cred configuration directive.\n");
+ return EINVAL;
+ }
+
+ ret = gnutls_certificate_allocate_credentials(&tls_conf->cert_cred);
+
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ return ret;
+ }
+
+ ret = gnutls_certificate_set_x509_trust_file(tls_conf->cert_cred,
+ tls_conf->cafile, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ return ret;
+ }
+ if (tls_conf->crlfile)
+ {
+ ret = gnutls_certificate_set_x509_crl_file(tls_conf->cert_cred,
+ tls_conf->crlfile, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ return ret;
+ }
+ }
+
+ ret = gnutls_certificate_set_x509_key_file(tls_conf->cert_cred,
+ tls_conf->certfile, tls_conf->keyfile, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ return ret;
+ }
+ return 0;
+}
+
+void diameap_tls_log(int lev, const char * text)
+{
+ u8 * msg;
+ if (text == NULL)
+ return;
+ msg = (u8 *) strdup(text);
+ int i;
+ for (i = 0; (G8(text+i) != '\n') && (G8(text+i) != '\0'); i++)
+ {
+ }
+ P8((msg+i),'\0');
+ fprintf(stderr, "[DiamEAP extension] [EAP TLS] GNUTLS log[%d] : %s\n", lev, msg);
+ free(msg);
+}
+
+int diameap_tls_init_session(struct tls_config * tls_conf,
+ struct tls_data * data)
+{
+ int ret;
+ ret = gnutls_init(&data->session, GNUTLS_SERVER);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ }
+ ret = gnutls_set_default_priority(data->session);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ }
+
+ ret = gnutls_credentials_set(data->session, GNUTLS_CRD_CERTIFICATE,
+ tls_conf->cert_cred);
+ if (ret < 0)
+ {
+ gnutls_perror(ret);
+ }
+
+ /* request client certificate if any.
+ */
+ gnutls_certificate_server_set_request(data->session, GNUTLS_CERT_REQUIRE); //GNUTLS_CERT_REQUEST);
+
+ gnutls_transport_set_pull_function(data->session, diameap_tls_receive);
+ gnutls_transport_set_push_function(data->session, diameap_tls_send);
+ gnutls_transport_set_ptr(data->session, (gnutls_transport_ptr_t) data);
+
+ /* starting version 2.12, this call is not needed */
+ //gnutls_transport_set_lowat(data->session, 0);
+
+ return ret;
+}
+
+ssize_t diameap_tls_receive(gnutls_transport_ptr_t ptr, void *buffer,
+ size_t length)
+{
+ struct tls_data * data = (struct tls_data *) ptr;
+ if (data->p_length == 0)
+ {
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (length > data->p_length)
+ {
+ length = data->p_length;
+ }
+ memcpy(buffer, data->tlsResp.data + (data->tlsResp.datalength
+ - data->p_length), length);
+ data->p_length -= length;
+
+ return length;
+}
+
+ssize_t diameap_tls_send(gnutls_transport_ptr_t ptr, const void *buffer,
+ size_t length)
+{
+ struct tls_data * data = (struct tls_data *) ptr;
+ data->tlsReq.data = realloc(data->tlsReq.data, data->tlsReq.datalength
+ + length);
+ U8COPY(data->tlsReq.data,data->tlsReq.datalength,length,(u8*)buffer);
+ data->tlsReq.datalength += length;
+
+ return length;
+}
+
+int diameap_tls_new(struct tls_msg * tlsmsg)
+{
+ if (!tlsmsg)
+ {
+ return EINVAL;
+ }
+ tlsmsg->flags = 0x00;
+ tlsmsg->length = 0;
+ tlsmsg->data = NULL;
+ tlsmsg->datalength = 0;
+ return 0;
+}
+
+int diameap_tls_get_flags(struct tls_msg tlsmsg, u8 * flags)
+{
+ *flags = tlsmsg.flags;
+ return 0;
+}
+
+int diameap_tls_set_flags(struct tls_msg * tlsmsg, u8 flags)
+{
+ if (!tlsmsg)
+ {
+ return EINVAL;
+ }
+ if ((flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ flags = flags ^ TLS_FLAG_LENGTH;
+ }
+ tlsmsg->flags = tlsmsg->flags | flags;
+ return 0;
+}
+
+int diameap_tls_get_message_length(struct tls_msg tlsmsg, u32 * length)
+{
+ if ((tlsmsg.flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ *length = tlsmsg.length;
+ }
+ else
+ {
+ *length = 0;
+ }
+ return 0;
+}
+
+int diameap_tls_set_message_length(struct tls_msg * tlsmsg, u32 length)
+{
+ if (!tlsmsg)
+ {
+ return EINVAL;
+ }
+ if (length > 0)
+ {
+ tlsmsg->length = length;
+ tlsmsg->flags = tlsmsg->flags | TLS_FLAG_LENGTH;
+ }
+ else
+ {
+ tlsmsg->length = 0;
+ if ((tlsmsg->flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ tlsmsg->flags = tlsmsg->flags ^ TLS_FLAG_LENGTH;
+ }
+ }
+ return 0;
+}
+
+int diameap_tls_get_data(struct tls_msg tlsmsg, u8** tls_data,
+ u32 * data_length)
+{
+ if (tlsmsg.datalength > 0)
+ {
+ *tls_data = malloc(sizeof(u8) * tlsmsg.datalength);
+ U8COPY(*tls_data,0,tlsmsg.datalength,tlsmsg.data);
+ *data_length = tlsmsg.datalength;
+ }
+ else
+ {
+ *tls_data = NULL;
+ *data_length = 0;
+ }
+ return 0;
+}
+
+int diameap_tls_set_data(struct tls_msg * tlsmsg, u8* tls_data, int data_length)
+{
+ if (!tlsmsg)
+ {
+ return EINVAL;
+ }
+ tlsmsg->data = malloc(sizeof(u8) * data_length);
+ U8COPY(tlsmsg->data,0,data_length,tls_data);
+ tlsmsg->datalength = data_length;
+ return 0;
+}
+
+/*
+ * data : returned data
+ * eaptls_data : the TLS_Data field
+ * length : the length of eaptls_data
+ * flags : combination of flags et set
+ */
+int diameap_tls_new_tls_packet(u8** data, int * len, struct tls_msg tlsmsg)
+{
+ int buflen, pos = 0;
+
+ buflen = 1;
+
+ if ((tlsmsg.flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ buflen += 4;
+ }
+
+ if (tlsmsg.datalength > 0)
+ {
+ buflen += tlsmsg.datalength;
+ }
+ *data = malloc(sizeof(u8) * buflen);
+ memset(*data, 0, sizeof(u8) * buflen);
+ P8(*data,tlsmsg.flags);
+ pos++;
+
+ if ((tlsmsg.flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ P32BIGE(*data+pos,tlsmsg.length);
+ pos += 4;
+ }
+ if (tlsmsg.data)
+ {
+ U8COPY(*data,pos,tlsmsg.datalength,tlsmsg.data);
+ }
+ *len = buflen;
+ return 0;
+}
+
+int diameap_set_tls(struct tls_msg * tlsmsg, u8 flags, u32 length,
+ u8 *tls_data, int data_length)
+{
+ diameap_tls_new(tlsmsg);
+ diameap_tls_set_flags(tlsmsg, flags);
+ diameap_tls_set_message_length(tlsmsg, length);
+ diameap_tls_set_data(tlsmsg, tls_data, data_length);
+ return 0;
+}
+
+int diameap_tls_parse(u8* data, int len, struct tls_msg * tlsmsg)
+{
+
+ if (data == NULL)
+ return EINVAL;
+ int pos = 0;
+ diameap_tls_new(tlsmsg);
+ tlsmsg->flags = G8(data);
+ pos++;
+ if ((tlsmsg->flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ tlsmsg->length = G32BIGE(data+1);
+ pos = pos + 4;
+ }
+
+ if (len > pos)
+ {
+ tlsmsg->data = malloc(sizeof(u8) * (len - pos));
+ U8COPY(tlsmsg->data,0,(len-pos),data+pos);
+ tlsmsg->datalength = len - pos;
+ }
+ else
+ {
+ tlsmsg->data = NULL;
+ tlsmsg->datalength = 0;
+ }
+ return 0;
+}
+
+void diameap_tls_dump(struct tls_msg tlsmsg)
+{
+ u8 * data = NULL;
+ u32 len;
+ diameap_tls_get_data(tlsmsg, &data, &len);
+
+ fprintf(stderr, "-------------Dump EAP-TLS msg-------------\n");
+ u8 flags;
+ diameap_tls_get_flags(tlsmsg, &flags);
+ fprintf(stderr, "\t -flags : %02x ", flags);
+ if (flags & TLS_FLAG_LENGTH)
+ fprintf(stderr, " TLS_FLAG_LENGTH ");
+ if (flags & TLS_FLAG_MORE)
+ fprintf(stderr, " TLS_FLAG_MORE ");
+ if (flags & TLS_FLAG_START)
+ fprintf(stderr, " TLS_FLAG_START ");
+ fprintf(stderr, "\n");
+ if ((tlsmsg.flags & TLS_FLAG_LENGTH) == TLS_FLAG_LENGTH)
+ {
+ u32 length;
+ diameap_tls_get_message_length(tlsmsg, &length);
+ fprintf(stderr, "\t -TLS msg length : %u (0x%02x%02x%02x%02x)\n",
+ length, (length >> 24) & 0xffU, (length >> 16) & 0xffU, (length
+ >> 8) & 0xffU, length & 0xffU);
+ }
+ fprintf(stderr, "\t -data length : %d \n", len);
+ /*
+ if (len > 0)
+ {
+ int i;
+ fprintf(stderr, "\t -Data : ");
+ for (i = 0; i < len; i++)
+ {
+ fprintf(stderr, "%02x ", G8(data + i));
+ }
+ fprintf(stderr, "\n");
+ }
+ */
+ fprintf(stderr, "-------------End Dump EAP-TLS msg-------------\n");
+
+ free(data);
+}
+
+int diameap_tls_initialize(struct tls_data * data)
+{
+ if (!data)
+ {
+ return EINVAL;
+ }
+ data->state = START;
+ data->more_tosend_length = 0;
+ data->more_toreceive = FALSE;
+ data->handshake = FALSE;
+ return 0;
+}
+
+int diameap_tls_reassemble(struct tls_msg * to, struct tls_msg from)
+{
+ u8 from_flag;
+ diameap_tls_get_flags(from, &from_flag);
+
+ if (from_flag & TLS_FLAG_LENGTH)
+ {
+ diameap_tls_new(to);
+ u32 length;
+ diameap_tls_get_message_length(from, &length);
+ diameap_tls_set_message_length(to, length);
+ }
+ diameap_tls_set_flags(to, from_flag);
+
+ u8 * tlsRespData;
+ u32 tlsRespDataLength;
+ diameap_tls_get_data(from, &tlsRespData, &tlsRespDataLength);
+ to->data = realloc(to->data, to->datalength + tlsRespDataLength);
+ U8COPY(to->data,to->datalength,tlsRespDataLength,tlsRespData);
+ to->datalength += tlsRespDataLength;
+ free(tlsRespData);
+ return 0;
+}
+
+int diameap_tls_copy(struct tls_msg * to, struct tls_msg from)
+{
+ u8 flag;
+ u32 length;
+ u8 * data;
+ diameap_tls_new(to);
+ diameap_tls_get_flags(from, &flag);
+ diameap_tls_set_flags(to, flag);
+ diameap_tls_get_message_length(from, &length);
+ diameap_tls_set_message_length(to, length);
+ length = 0;
+ diameap_tls_get_data(from, &data, &length);
+ diameap_tls_set_data(to, data, length);
+ return 0;
+}
+
+int diameap_tls_process_receive(struct tls_data * data)
+{
+ int ret;
+
+ data->p_length = data->tlsResp.datalength;
+
+ ret = gnutls_handshake(data->session);
+
+ if (ret < 0)
+ {
+ switch (ret)
+ {
+ case GNUTLS_E_AGAIN:
+ break;
+ case GNUTLS_E_INTERRUPTED:
+ fprintf(stderr, "[DiamEAP extension] [EAP TLS] gnutls handshake : GNUTLS_E_INTERRUPTED");
+ break;
+ case GNUTLS_E_GOT_APPLICATION_DATA:
+ fprintf(stderr,
+ "[DiamEAP extension] [EAP TLS] gnutls handshake : GNUTLS_E_GOT_APPLICATION_DATA");
+ break;
+ case GNUTLS_E_WARNING_ALERT_RECEIVED:
+ fprintf(stderr,
+ "[DiamEAP extension] [EAP TLS] gnutls handshake : GNUTLS_E_WARNING_ALERT_RECEIVED");
+ break;
+ }
+ if (ret != GNUTLS_E_AGAIN)
+ {
+ gnutls_perror(ret);
+ }
+ }
+ if (ret == GNUTLS_E_SUCCESS)
+ {
+ data->handshake = TRUE;
+ }
+ return 0;
+}
+