/* ****************************************************************************** * 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 ****************************************************************************** */ /* **************************************************************************** * 1 DESCRIPTION **************************************************************************** */ /* **************************************************************************** * 2 CONFIGURATION **************************************************************************** */ #include "ipeap_config.h" /* **************************************************************************** * 3 INCLUDE FILES **************************************************************************** */ #include "ipeap_common_tls.h" #include #include #include "ipeap.h" #include "ipeap_h.h" #include /* **************************************************************************** * 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 **************************************************************************** */