556 lines
17 KiB
C
556 lines
17 KiB
C
/*
|
|
******************************************************************************
|
|
* INTERPEAK SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipeap_common_tls.c,v $ $Revision: 1.6.10.2 $
|
|
* $Source: /home/interpeak/CVSRoot/ipeap/src/ipeap_common_tls.c,v $
|
|
* $Author: jli7 $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Roger Boden <roger@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#include "ipeap_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
#include "ipeap_common_tls.h"
|
|
#include <ipcom_type.h>
|
|
#include <ipcom_cstyle.h>
|
|
|
|
#include "ipeap.h"
|
|
#include "ipeap_h.h"
|
|
#include <openssl/err.h>
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 STATIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_tls_suppl_ctx_free
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC void
|
|
ipeap_tls_suppl_ctx_free(Ipeap_tls_suppl_ctx *ctx)
|
|
{
|
|
if(!ctx)
|
|
return;
|
|
|
|
if(ctx->ssl_ctx)
|
|
SSL_CTX_free(ctx->ssl_ctx);
|
|
|
|
if(ctx->ssl)
|
|
SSL_free(ctx->ssl);
|
|
|
|
ipcom_memset(ctx, 0, sizeof(Ipeap_tls_suppl_ctx));
|
|
|
|
ipcom_free(ctx);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_tls_suppl_setup_ssl
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_STATIC Ip_err
|
|
ipeap_tls_suppl_setup_ssl(Ipeap_tls_suppl_ctx *ctx)
|
|
{
|
|
ctx->ssl = SSL_new(ctx->ssl_ctx);
|
|
if(!ctx->ssl)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_setup_ssl :: SSL_new() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
SSL_set_connect_state(ctx->ssl);
|
|
(void)SSL_set_options(ctx->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE);
|
|
|
|
ctx->in_bio = BIO_new(BIO_s_mem());
|
|
ctx->out_bio = BIO_new(BIO_s_mem());
|
|
if(!ctx->in_bio || !ctx->out_bio)
|
|
{
|
|
if(ctx->in_bio)
|
|
(void)BIO_free(ctx->in_bio);
|
|
if(ctx->out_bio)
|
|
(void)BIO_free(ctx->out_bio);
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_setup_ssl :: BIO_s_mem() failed");
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
SSL_set_bio(ctx->ssl, ctx->in_bio, ctx->out_bio);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 GLOBAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_common_tls_ctx_new
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ipeap_tls_suppl_ctx*
|
|
ipeap_common_tls_ctx_new(void)
|
|
{
|
|
Ipeap_tls_suppl_ctx *tls_suppl_ctx = IP_NULL;
|
|
EVP_PKEY *tls_key = IP_NULL;
|
|
X509 *tls_cert = IP_NULL;
|
|
|
|
tls_suppl_ctx = ipcom_malloc(sizeof(Ipeap_tls_suppl_ctx));
|
|
if(!tls_suppl_ctx)
|
|
return IP_NULL;
|
|
ipcom_memset(tls_suppl_ctx, 0, sizeof(Ipeap_tls_suppl_ctx));
|
|
|
|
tls_suppl_ctx->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
|
|
if(!tls_suppl_ctx->ssl_ctx)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_init :: SSL_CTX_new() failed");
|
|
goto error;
|
|
}
|
|
|
|
/* Load server key */
|
|
tls_key = ipeap_load_tls_key();
|
|
if(tls_key)
|
|
{
|
|
/* load server cert */
|
|
tls_cert = ipeap_load_tls_cert();
|
|
if(tls_cert)
|
|
{
|
|
/* Check that key and cert match */
|
|
if(X509_check_private_key(tls_cert, tls_key) != 1)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_init :: X509_check_private_key() failed");
|
|
goto error;
|
|
}
|
|
if(!SSL_CTX_use_PrivateKey(tls_suppl_ctx->ssl_ctx, tls_key))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_init :: SSL_CTX_use_PrivateKey() failed");
|
|
goto error;
|
|
}
|
|
if(!SSL_CTX_use_certificate(tls_suppl_ctx->ssl_ctx, tls_cert))
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_init :: SSL_CTX_use_certificate() failed");
|
|
goto error;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG0(WARNING, "No TLS X509 certificate loaded by ipwebs_load_tls_cert()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IPCOM_LOG0(WARNING, "No TLS key loaded by ipeap_load_tls_key()");
|
|
}
|
|
if(tls_key)
|
|
EVP_PKEY_free(tls_key);
|
|
if(tls_cert)
|
|
X509_free(tls_cert);
|
|
|
|
/* Load CA certificates */
|
|
if(ipeap_load_ca_certs(tls_suppl_ctx->ssl_ctx->cert_store) == IPCOM_SUCCESS)
|
|
SSL_CTX_set_verify(tls_suppl_ctx->ssl_ctx, SSL_VERIFY_PEER, ipeap_tls_cert_verify);
|
|
else
|
|
{
|
|
IPCOM_LOG0(WARNING, "No TLS trusted certs loaded by ipeap_load_ca_certs(). Peer certificate validation turned off");
|
|
}
|
|
|
|
if(ipeap_tls_suppl_setup_ssl(tls_suppl_ctx) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG0(ERR, "ipeap_tls_suppl_init :: ipeap_tls_suppl_setup_ssl() failed");
|
|
goto error;
|
|
}
|
|
|
|
return tls_suppl_ctx;
|
|
|
|
error:
|
|
ipeap_tls_suppl_ctx_free(tls_suppl_ctx);
|
|
if(tls_key)
|
|
EVP_PKEY_free(tls_key);
|
|
if(tls_cert)
|
|
X509_free(tls_cert);
|
|
|
|
return IP_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_common_tls_handle_frag_and_handshake
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipeap_common_tls_handle_frag_and_handshake(Ipeap_ctx *ctx, Ipeap_tls_suppl_ctx *tls_suppl_ctx, Ipeap_pkt *in_pkt, Ipeap_pkt *out_pkt)
|
|
{
|
|
Ipeap_tls_msg *msg;
|
|
int msg_len;
|
|
int rc;
|
|
int ssl_rc;
|
|
|
|
msg = IPEAP_PKT2TLSMSG(in_pkt);
|
|
ip_assert(msg->code == IPEAP_MSG_REQUEST);
|
|
|
|
/* Are we sending a fragmented packet? */
|
|
if(tls_suppl_ctx->frag_tot_len > 0)
|
|
return ipeap_common_tls_make_out_frag_pkt(tls_suppl_ctx, out_pkt, IPEAP_MSG_RESPONSE, msg->id, ctx->meth_id);
|
|
|
|
/* Write received data to SSL's input BIO */
|
|
if(msg->flags & IPEAP_TLS_FLAG_LEN_INCL)
|
|
msg_len = IP_GET_NTOHS(&msg->length) - 10; /* Remove header length*/
|
|
else
|
|
msg_len = IP_GET_NTOHS(&msg->length) - 6; /* Remove header length*/
|
|
if(msg_len > 0)
|
|
{
|
|
if(msg->flags & IPEAP_TLS_FLAG_LEN_INCL)
|
|
rc = BIO_write(tls_suppl_ctx->in_bio, msg->u.len_present.data, msg_len);
|
|
else
|
|
rc = BIO_write(tls_suppl_ctx->in_bio, msg->u.data, msg_len);
|
|
|
|
if(rc != msg_len)
|
|
{
|
|
IPCOM_LOG3(ERR, "%sFailed to write EAP fragment to SSL BIO, rc: %d, msg_len: %d",
|
|
ctx->syslog_prefix, rc, msg_len);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
/* If this is a fragmented packet, wait for remaining fragments to arrive */
|
|
if(msg->flags & IPEAP_TLS_FLAG_MORE_FRAG)
|
|
{
|
|
rc = ipeap_tls_pkt_set_header(out_pkt, IPEAP_PKT_RESPONSE, msg->id, 6, ctx->meth_id, 0);
|
|
out_pkt->end += 6;
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
return IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
|
|
/* TODO: It should be possible to reset existing SSL context */
|
|
if(SSL_get_state(tls_suppl_ctx->ssl) == SSL_ST_OK)
|
|
{
|
|
if(msg->flags & IPEAP_TLS_FLAG_START)
|
|
{
|
|
IPCOM_LOG1(INFO, "%sRenegotiating SSL", ctx->syslog_prefix);
|
|
SSL_free(tls_suppl_ctx->ssl);
|
|
if(ipeap_tls_suppl_setup_ssl(tls_suppl_ctx) != IPCOM_SUCCESS)
|
|
{
|
|
IPCOM_LOG1(ERR, "%sipeap_tls_suppl_setup_ssl() failed", ctx->syslog_prefix);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
ctx->state = IPEAP_STATE_HANDSHAKE;
|
|
}
|
|
else
|
|
/* TLS handshake completed => we have received application data over the TLS connection */
|
|
return IPEAP_TLS_DATA_AVAILABLE;
|
|
}
|
|
|
|
ssl_rc = SSL_do_handshake(tls_suppl_ctx->ssl);
|
|
if(ssl_rc == 1)
|
|
{
|
|
IPCOM_LOG1(INFO, "%sTLS handshake successfully completed", ctx->syslog_prefix);
|
|
return IPEAP_TLS_HANDSHAKE_COMPLETE;
|
|
}
|
|
if(ssl_rc <= 0 && SSL_get_error(tls_suppl_ctx->ssl, ssl_rc) == SSL_ERROR_WANT_READ)
|
|
rc = ipeap_common_tls_make_out_frag_pkt(tls_suppl_ctx, out_pkt, IPEAP_MSG_RESPONSE, msg->id, ctx->meth_id);
|
|
else
|
|
{
|
|
IPCOM_LOG3(ERR, "%sTLS handshake failed, SSL_get_error(): %d, %s",
|
|
ctx->syslog_prefix, SSL_get_error(tls_suppl_ctx->ssl, ssl_rc),
|
|
ERR_error_string(ERR_get_error(), IP_NULL));
|
|
if(BIO_pending(tls_suppl_ctx->out_bio) > 0)
|
|
/* There is data in the out_bio, most likely an SSL alert. Send it to peer */
|
|
rc = ipeap_common_tls_make_out_frag_pkt(tls_suppl_ctx, out_pkt, IPEAP_MSG_RESPONSE, msg->id, ctx->meth_id);
|
|
else
|
|
{
|
|
/* No SSL data to send, make an empty ack packet */
|
|
rc = ipeap_tls_pkt_set_header(out_pkt, IPEAP_PKT_RESPONSE, msg->id, 6, ctx->meth_id, 0);
|
|
out_pkt->end += 6;
|
|
rc = IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
}
|
|
|
|
ERR_clear_error();
|
|
ERR_remove_state(0);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_tls_pkt_set_header
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipeap_tls_pkt_set_header(Ipeap_pkt *pkt, Ip_u8 code, Ip_u8 id, Ip_u16 len, Ip_u8 type, Ip_u8 flags)
|
|
{
|
|
Ip_err rc;
|
|
Ipeap_tls_msg* msg;
|
|
|
|
rc = ipeap_pkt_set_header(pkt, code, id, len, type);
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
|
|
msg = IPEAP_PKT2TLSMSG(pkt);
|
|
msg->flags = flags;
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_common_tls_make_out_frag_pkt
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipeap_common_tls_make_out_frag_pkt(Ipeap_tls_suppl_ctx *ctx, Ipeap_pkt *pkt, Ip_u8 code, Ip_u8 id, Ip_u8 meth_id)
|
|
{
|
|
Ipeap_tls_msg* msg;
|
|
int bytes_left_to_send;
|
|
int tls_data_len;
|
|
Ip_u8 *out_buf;
|
|
Ip_err rc;
|
|
Ip_u8 flags = 0;
|
|
int header_size = 6;
|
|
|
|
msg = IPEAP_PKT2TLSMSG(pkt);
|
|
|
|
bytes_left_to_send = (int)BIO_ctrl_pending(ctx->out_bio);
|
|
if(bytes_left_to_send > IPEAP_MTU && ctx->frag_tot_len == 0)
|
|
{
|
|
/* This message must be fragmented and this is the first fragment */
|
|
ctx->frag_tot_len = bytes_left_to_send;
|
|
}
|
|
|
|
if(bytes_left_to_send > IPEAP_MTU)
|
|
flags |= IPEAP_TLS_FLAG_MORE_FRAG;
|
|
if(ctx->frag_tot_len != 0)
|
|
{
|
|
flags |= IPEAP_TLS_FLAG_LEN_INCL;
|
|
header_size = 10;
|
|
IP_SET_HTONL(msg->u.len_present.len, ctx->frag_tot_len);
|
|
out_buf = msg->u.len_present.data;
|
|
}
|
|
else
|
|
out_buf = msg->u.data;
|
|
|
|
tls_data_len = IP_MIN(bytes_left_to_send, IPEAP_MTU-header_size);
|
|
tls_data_len = IP_MIN(tls_data_len, IPEAP_PKT_GET_SPACE_LEFT(pkt));
|
|
if(BIO_read(ctx->out_bio, out_buf, tls_data_len) != tls_data_len)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipeap_common_tls_make_out_frag_pkt :: failed to read %d bytes from out_bio", tls_data_len);
|
|
return IPCOM_ERR_FAILED;
|
|
}
|
|
|
|
rc = ipeap_tls_pkt_set_header(pkt, code, id, (Ip_u16)(tls_data_len+header_size), meth_id, flags);
|
|
if(rc != IPCOM_SUCCESS)
|
|
return rc;
|
|
if(flags & IPEAP_TLS_FLAG_LEN_INCL)
|
|
IP_SET_HTONL(msg->u.len_present.len, ctx->frag_tot_len);
|
|
|
|
if(BIO_ctrl_pending(ctx->out_bio) == 0)
|
|
ctx->frag_tot_len = 0; /* This is the last fragment, reset frag_tot_len */
|
|
|
|
pkt->end += header_size + tls_data_len; /* Add header and payload to length */
|
|
|
|
return IPEAP_PENDING_SEND_OUT_PKT;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_common_tls_ctx_free
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL void
|
|
ipeap_common_tls_ctx_free(Ipeap_tls_suppl_ctx *tls_suppl_ctx)
|
|
{
|
|
if(tls_suppl_ctx->ssl)
|
|
SSL_free(tls_suppl_ctx->ssl);
|
|
if(tls_suppl_ctx->ssl_ctx)
|
|
SSL_CTX_free(tls_suppl_ctx->ssl_ctx);
|
|
|
|
ipcom_free(tls_suppl_ctx);
|
|
|
|
ERR_clear_error();
|
|
ERR_remove_state(0);
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* ipeap_tls_common_generate_key
|
|
*===========================================================================
|
|
* Description:
|
|
* Parameters:
|
|
* Returns:
|
|
*
|
|
*/
|
|
IP_GLOBAL Ip_err
|
|
ipeap_tls_common_generate_key(Ipeap_tls_suppl_ctx *tls_suppl_ctx, const char* label, Ip_u8* key, int key_len)
|
|
{
|
|
Ip_u8 tmp_buf[2*SSL3_RANDOM_SIZE+40];
|
|
Ip_u8* tmp_out;
|
|
ip_assert(tls_suppl_ctx);
|
|
|
|
ip_assert(ipcom_strlen(label) < 40);
|
|
|
|
tmp_out = ipcom_malloc((Ip_size_t)key_len);
|
|
if(!tmp_out)
|
|
return IPCOM_ERR_FAILED;
|
|
|
|
ipcom_memcpy(tmp_buf, label, ipcom_strlen(label));
|
|
ipcom_memcpy(tmp_buf+ipcom_strlen(label), tls_suppl_ctx->ssl->s3->client_random, SSL3_RANDOM_SIZE);
|
|
ipcom_memcpy(tmp_buf+ipcom_strlen(label)+SSL3_RANDOM_SIZE, tls_suppl_ctx->ssl->s3->server_random, SSL3_RANDOM_SIZE);
|
|
|
|
#ifdef _WRS_CONFIG_COMPONENT_IPCRYPTO_OPENSSL_1_0_2
|
|
ipssl_tls1_PRF(TLS1_PRF,
|
|
tmp_buf, (int)ipcom_strlen(label)+2*SSL3_RANDOM_SIZE,
|
|
NULL, 0, NULL, 0, NULL, 0, NULL, 0,
|
|
tls_suppl_ctx->ssl->session->master_key, tls_suppl_ctx->ssl->session->master_key_length,
|
|
key, tmp_out, key_len);
|
|
#else
|
|
ipssl_tls1_PRF(EVP_md5(), EVP_sha1(),
|
|
tmp_buf, (int)ipcom_strlen(label)+2*SSL3_RANDOM_SIZE,
|
|
tls_suppl_ctx->ssl->session->master_key, tls_suppl_ctx->ssl->session->master_key_length,
|
|
key, tmp_out, key_len);
|
|
#endif
|
|
|
|
ipcom_free(tmp_out);
|
|
|
|
return IPCOM_SUCCESS;
|
|
}
|
|
|
|
|
|
IP_GLOBAL Ip_err
|
|
ipeap_tls_common_generate_session_id(Ipeap_tls_suppl_ctx *tls_suppl_ctx,Ip_u8 eap_type,Ip_u8 *session_id,Ip_u32 *session_id_len)
|
|
{
|
|
|
|
|
|
|
|
if (*session_id_len < IPEAP_TLS_SESSION_ID_LEN)
|
|
{
|
|
IPCOM_LOG3(ERR, "ipeap_tls_common_generate_session_id - not enough buffer is available to hold "
|
|
"the Session_Id for 0x%x , (%d wanted), (%d given )",eap_type,IPEAP_TLS_SESSION_ID_LEN , *session_id_len );
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
|
|
if(eap_type!=IPEAP_TYPE_TLS && eap_type!= IPEAP_TYPE_TTLS && eap_type!=IPEAP_TYPE_PEAP)
|
|
{
|
|
IPCOM_LOG1(ERR, "ipeap_tls_common_generate_session_id - not a valid EAP TYPE 0x%x",eap_type);
|
|
return IPCOM_ERR_INVALID_ARG;
|
|
}
|
|
|
|
|
|
ipcom_memset(session_id, 0,*session_id_len);
|
|
session_id[0] = eap_type;
|
|
session_id += 1;
|
|
ipcom_memcpy(session_id, tls_suppl_ctx->ssl->s3->client_random, SSL3_RANDOM_SIZE);
|
|
ipcom_memcpy(session_id+SSL3_RANDOM_SIZE, tls_suppl_ctx->ssl->s3->server_random, SSL3_RANDOM_SIZE);
|
|
|
|
*session_id_len = IPEAP_TLS_SESSION_ID_LEN;
|
|
|
|
return IPCOM_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 11 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|