2018-10-16 00:37:07 +00:00
/*-
* SSLsplit - transparent SSL / TLS interception
* https : //www.roe.ch/SSLsplit
*
* Copyright ( c ) 2009 - 2018 , Daniel Roethlisberger < daniel @ roe . ch > .
* All rights reserved .
*
* Redistribution and use 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 .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER 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 HOLDER 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 "protossl.h"
2018-10-17 13:28:21 +00:00
# include "prototcp.h"
2018-10-16 00:37:07 +00:00
# include "protopassthrough.h"
# include "pxysslshut.h"
# include "cachemgr.h"
# include <string.h>
2018-10-17 13:28:21 +00:00
# include <sys/param.h>
2018-10-16 00:37:07 +00:00
# include <event2/bufferevent_ssl.h>
/*
* Context used for all server sessions .
*/
# ifdef USE_SSL_SESSION_ID_CONTEXT
static unsigned long ssl_session_context = 0x31415926 ;
# endif /* USE_SSL_SESSION_ID_CONTEXT */
void
protossl_log_ssl_error ( struct bufferevent * bev , UNUSED pxy_conn_ctx_t * ctx )
{
unsigned long sslerr ;
/* Can happen for socket errs, ssl errs;
* may happen for unclean ssl socket shutdowns . */
sslerr = bufferevent_get_openssl_error ( bev ) ;
if ( ! errno & & ! sslerr ) {
# if LIBEVENT_VERSION_NUMBER >= 0x02010000
/* We have disabled notification for unclean shutdowns
* so this should not happen ; log a warning . */
log_err_level_printf ( LOG_WARNING , " Spurious error from bufferevent (errno=0,sslerr=0) \n " ) ;
# else /* LIBEVENT_VERSION_NUMBER < 0x02010000 */
/* Older versions of libevent will report these. */
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Unclean SSL shutdown, fd=%d \n " , ctx - > fd ) ;
}
# endif /* LIBEVENT_VERSION_NUMBER < 0x02010000 */
} else if ( ERR_GET_REASON ( sslerr ) = = SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE ) {
/* these can happen due to client cert auth,
* only log error if debugging is activated */
log_dbg_printf ( " Error from bufferevent: %i:%s %lu:%i:%s:%i:%s:%i:%s \n " ,
errno , errno ? strerror ( errno ) : " - " , sslerr ,
ERR_GET_REASON ( sslerr ) , sslerr ? ERR_reason_error_string ( sslerr ) : " - " ,
ERR_GET_LIB ( sslerr ) , sslerr ? ERR_lib_error_string ( sslerr ) : " - " ,
ERR_GET_FUNC ( sslerr ) , sslerr ? ERR_func_error_string ( sslerr ) : " - " ) ;
while ( ( sslerr = bufferevent_get_openssl_error ( bev ) ) ) {
log_dbg_printf ( " Additional SSL error: %lu:%i:%s:%i:%s:%i:%s \n " ,
sslerr ,
ERR_GET_REASON ( sslerr ) , ERR_reason_error_string ( sslerr ) ,
ERR_GET_LIB ( sslerr ) , ERR_lib_error_string ( sslerr ) ,
ERR_GET_FUNC ( sslerr ) , ERR_func_error_string ( sslerr ) ) ;
}
} else {
/* real errors */
log_err_printf ( " Error from bufferevent: %i:%s %lu:%i:%s:%i:%s:%i:%s \n " ,
errno , errno ? strerror ( errno ) : " - " ,
sslerr ,
ERR_GET_REASON ( sslerr ) , sslerr ? ERR_reason_error_string ( sslerr ) : " - " ,
ERR_GET_LIB ( sslerr ) , sslerr ? ERR_lib_error_string ( sslerr ) : " - " ,
ERR_GET_FUNC ( sslerr ) , sslerr ? ERR_func_error_string ( sslerr ) : " - " ) ;
while ( ( sslerr = bufferevent_get_openssl_error ( bev ) ) ) {
log_err_printf ( " Additional SSL error: %lu:%i:%s:%i:%s:%i:%s \n " ,
sslerr ,
ERR_GET_REASON ( sslerr ) , ERR_reason_error_string ( sslerr ) ,
ERR_GET_LIB ( sslerr ) , ERR_lib_error_string ( sslerr ) ,
ERR_GET_FUNC ( sslerr ) , ERR_func_error_string ( sslerr ) ) ;
}
}
}
int
protossl_log_masterkey ( pxy_conn_ctx_t * ctx , pxy_conn_desc_t * this )
{
2018-10-20 12:29:51 +00:00
// XXX: Remove ssl check? But the caller function is called by non-ssl protos.
2018-10-16 00:37:07 +00:00
if ( this - > ssl ) {
/* log master key */
if ( ctx - > opts - > masterkeylog ) {
char * keystr ;
keystr = ssl_ssl_masterkey_to_str ( this - > ssl ) ;
if ( ( keystr = = NULL ) | |
( log_masterkey_print_free ( keystr ) = = - 1 ) ) {
if ( errno = = ENOMEM )
ctx - > enomem = 1 ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
}
}
return 0 ;
}
/* forward declaration of OpenSSL callbacks */
# ifndef OPENSSL_NO_TLSEXT
static int protossl_ossl_servername_cb ( SSL * ssl , int * al , void * arg ) ;
# endif /* !OPENSSL_NO_TLSEXT */
static int protossl_ossl_sessnew_cb ( SSL * , SSL_SESSION * ) ;
static void protossl_ossl_sessremove_cb ( SSL_CTX * , SSL_SESSION * ) ;
# if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
static SSL_SESSION * protossl_ossl_sessget_cb ( SSL * , unsigned char * , int , int * ) ;
# else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
static SSL_SESSION * protossl_ossl_sessget_cb ( SSL * , const unsigned char * , int , int * ) ;
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
/*
* Dump information on a certificate to the debug log .
*/
static void
protossl_debug_crt ( X509 * crt )
{
char * sj = ssl_x509_subject ( crt ) ;
if ( sj ) {
log_dbg_printf ( " Subject DN: %s \n " , sj ) ;
free ( sj ) ;
}
char * names = ssl_x509_names_to_str ( crt ) ;
if ( names ) {
log_dbg_printf ( " Common Names: %s \n " , names ) ;
free ( names ) ;
}
char * fpr ;
if ( ! ( fpr = ssl_x509_fingerprint ( crt , 1 ) ) ) {
log_err_level_printf ( LOG_WARNING , " Error generating X509 fingerprint \n " ) ;
} else {
log_dbg_printf ( " Fingerprint: %s \n " , fpr ) ;
free ( fpr ) ;
}
# ifdef DEBUG_CERTIFICATE
/* dump certificate */
log_dbg_print_free ( ssl_x509_to_str ( crt ) ) ;
log_dbg_print_free ( ssl_x509_to_pem ( crt ) ) ;
# endif /* DEBUG_CERTIFICATE */
}
/*
* Called by OpenSSL when a new src SSL session is created .
* OpenSSL increments the refcount before calling the callback and will
* decrement it again if we return 0. Returning 1 will make OpenSSL skip
* the refcount decrementing . In other words , return 0 if we did not
* keep a pointer to the object ( which we never do here ) .
*/
# ifdef HAVE_SSLV2
# define MAYBE_UNUSED
# else /* !HAVE_SSLV2 */
# define MAYBE_UNUSED UNUSED
# endif /* !HAVE_SSLV2 */
static int
protossl_ossl_sessnew_cb ( MAYBE_UNUSED SSL * ssl , SSL_SESSION * sess )
# undef MAYBE_UNUSED
{
# ifdef DEBUG_SESSION_CACHE
log_dbg_printf ( " ===> OpenSSL new session callback: \n " ) ;
if ( sess ) {
log_dbg_print_free ( ssl_session_to_str ( sess ) ) ;
} else {
log_dbg_printf ( " (null) \n " ) ;
}
# endif /* DEBUG_SESSION_CACHE */
# ifdef HAVE_SSLV2
/* Session resumption seems to fail for SSLv2 with protocol
* parsing errors , so we disable caching for SSLv2 . */
if ( SSL_version ( ssl ) = = SSL2_VERSION ) {
log_err_level_printf ( LOG_WARNING , " Session resumption denied to SSLv2 "
" client. \n " ) ;
return 0 ;
}
# endif /* HAVE_SSLV2 */
if ( sess ) {
cachemgr_ssess_set ( sess ) ;
}
return 0 ;
}
/*
* Called by OpenSSL when a src SSL session should be removed .
* OpenSSL calls SSL_SESSION_free ( ) after calling the callback ;
* we do not need to free the reference here .
*/
static void
protossl_ossl_sessremove_cb ( UNUSED SSL_CTX * sslctx , SSL_SESSION * sess )
{
# ifdef DEBUG_SESSION_CACHE
log_dbg_printf ( " ===> OpenSSL remove session callback: \n " ) ;
if ( sess ) {
log_dbg_print_free ( ssl_session_to_str ( sess ) ) ;
} else {
log_dbg_printf ( " (null) \n " ) ;
}
# endif /* DEBUG_SESSION_CACHE */
if ( sess ) {
cachemgr_ssess_del ( sess ) ;
}
}
/*
* Called by OpenSSL when a src SSL session is requested by the client .
*/
static SSL_SESSION *
# if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
protossl_ossl_sessget_cb ( UNUSED SSL * ssl , unsigned char * id , int idlen , int * copy )
# else /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
protossl_ossl_sessget_cb ( UNUSED SSL * ssl , const unsigned char * id , int idlen , int * copy )
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
{
SSL_SESSION * sess ;
# ifdef DEBUG_SESSION_CACHE
log_dbg_printf ( " ===> OpenSSL get session callback: \n " ) ;
# endif /* DEBUG_SESSION_CACHE */
* copy = 0 ; /* SSL should not increment reference count of session */
sess = cachemgr_ssess_get ( id , idlen ) ;
# ifdef DEBUG_SESSION_CACHE
if ( sess ) {
log_dbg_print_free ( ssl_session_to_str ( sess ) ) ;
}
# endif /* DEBUG_SESSION_CACHE */
log_dbg_printf ( " SSL session cache: %s \n " , sess ? " HIT " : " MISS " ) ;
return sess ;
}
/*
* Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX .
*/
static void
protossl_sslctx_setoptions ( SSL_CTX * sslctx , pxy_conn_ctx_t * ctx )
{
SSL_CTX_set_options ( sslctx , SSL_OP_ALL ) ;
# ifdef SSL_OP_TLS_ROLLBACK_BUG
SSL_CTX_set_options ( sslctx , SSL_OP_TLS_ROLLBACK_BUG ) ;
# endif /* SSL_OP_TLS_ROLLBACK_BUG */
# ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
SSL_CTX_set_options ( sslctx , SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION ) ;
# endif /* SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION */
# ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options ( sslctx , SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS ) ;
# endif /* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */
# ifdef SSL_OP_NO_TICKET
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TICKET ) ;
# endif /* SSL_OP_NO_TICKET */
# ifdef SSL_OP_NO_SSLv2
# ifdef HAVE_SSLV2
if ( ctx - > opts - > no_ssl2 ) {
# endif /* HAVE_SSLV2 */
SSL_CTX_set_options ( sslctx , SSL_OP_NO_SSLv2 ) ;
# ifdef HAVE_SSLV2
}
# endif /* HAVE_SSLV2 */
# endif /* !SSL_OP_NO_SSLv2 */
# ifdef HAVE_SSLV3
if ( ctx - > opts - > no_ssl3 ) {
SSL_CTX_set_options ( sslctx , SSL_OP_NO_SSLv3 ) ;
}
# endif /* HAVE_SSLV3 */
# ifdef HAVE_TLSV10
if ( ctx - > opts - > no_tls10 ) {
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1 ) ;
}
# endif /* HAVE_TLSV10 */
# ifdef HAVE_TLSV11
if ( ctx - > opts - > no_tls11 ) {
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1_1 ) ;
}
# endif /* HAVE_TLSV11 */
# ifdef HAVE_TLSV12
if ( ctx - > opts - > no_tls12 ) {
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1_2 ) ;
}
# endif /* HAVE_TLSV12 */
# ifdef SSL_OP_NO_COMPRESSION
if ( ! ctx - > opts - > sslcomp ) {
SSL_CTX_set_options ( sslctx , SSL_OP_NO_COMPRESSION ) ;
}
# endif /* SSL_OP_NO_COMPRESSION */
SSL_CTX_set_cipher_list ( sslctx , ctx - > opts - > ciphers ) ;
}
/*
* Create and set up a new SSL_CTX instance for terminating SSL .
* Set up all the necessary callbacks , the certificate , the cert chain and key .
*/
static SSL_CTX *
protossl_srcsslctx_create ( pxy_conn_ctx_t * ctx , X509 * crt , STACK_OF ( X509 ) * chain ,
EVP_PKEY * key )
{
SSL_CTX * sslctx = SSL_CTX_new ( ctx - > opts - > sslmethod ( ) ) ;
if ( ! sslctx )
return NULL ;
protossl_sslctx_setoptions ( sslctx , ctx ) ;
# if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
if ( ctx - > opts - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , ctx - > opts - > sslversion ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , ctx - > opts - > sslversion ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
return NULL ;
}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
SSL_CTX_sess_set_new_cb ( sslctx , protossl_ossl_sessnew_cb ) ;
SSL_CTX_sess_set_remove_cb ( sslctx , protossl_ossl_sessremove_cb ) ;
SSL_CTX_sess_set_get_cb ( sslctx , protossl_ossl_sessget_cb ) ;
SSL_CTX_set_session_cache_mode ( sslctx , SSL_SESS_CACHE_SERVER |
SSL_SESS_CACHE_NO_INTERNAL ) ;
# ifdef USE_SSL_SESSION_ID_CONTEXT
SSL_CTX_set_session_id_context ( sslctx , ( void * ) ( & ssl_session_context ) ,
sizeof ( ssl_session_context ) ) ;
# endif /* USE_SSL_SESSION_ID_CONTEXT */
# ifndef OPENSSL_NO_TLSEXT
SSL_CTX_set_tlsext_servername_callback ( sslctx , protossl_ossl_servername_cb ) ;
SSL_CTX_set_tlsext_servername_arg ( sslctx , ctx ) ;
# endif /* !OPENSSL_NO_TLSEXT */
# ifndef OPENSSL_NO_DH
if ( ctx - > opts - > dh ) {
SSL_CTX_set_tmp_dh ( sslctx , ctx - > opts - > dh ) ;
} else {
SSL_CTX_set_tmp_dh_callback ( sslctx , ssl_tmp_dh_callback ) ;
}
# endif /* !OPENSSL_NO_DH */
# ifndef OPENSSL_NO_ECDH
if ( ctx - > opts - > ecdhcurve ) {
EC_KEY * ecdh = ssl_ec_by_name ( ctx - > opts - > ecdhcurve ) ;
SSL_CTX_set_tmp_ecdh ( sslctx , ecdh ) ;
EC_KEY_free ( ecdh ) ;
} else {
EC_KEY * ecdh = ssl_ec_by_name ( NULL ) ;
SSL_CTX_set_tmp_ecdh ( sslctx , ecdh ) ;
EC_KEY_free ( ecdh ) ;
}
# endif /* !OPENSSL_NO_ECDH */
SSL_CTX_use_certificate ( sslctx , crt ) ;
SSL_CTX_use_PrivateKey ( sslctx , key ) ;
for ( int i = 0 ; i < sk_X509_num ( chain ) ; i + + ) {
X509 * c = sk_X509_value ( chain , i ) ;
ssl_x509_refcount_inc ( c ) ; /* next call consumes a reference */
SSL_CTX_add_extra_chain_cert ( sslctx , c ) ;
}
# ifdef DEBUG_SESSION_CACHE
if ( OPTS_DEBUG ( ctx - > opts ) ) {
int mode = SSL_CTX_get_session_cache_mode ( sslctx ) ;
log_dbg_printf ( " SSL session cache mode: %08x \n " , mode ) ;
if ( mode = = SSL_SESS_CACHE_OFF )
log_dbg_printf ( " SSL_SESS_CACHE_OFF \n " ) ;
if ( mode & SSL_SESS_CACHE_CLIENT )
log_dbg_printf ( " SSL_SESS_CACHE_CLIENT \n " ) ;
if ( mode & SSL_SESS_CACHE_SERVER )
log_dbg_printf ( " SSL_SESS_CACHE_SERVER \n " ) ;
if ( mode & SSL_SESS_CACHE_NO_AUTO_CLEAR )
log_dbg_printf ( " SSL_SESS_CACHE_NO_AUTO_CLEAR \n " ) ;
if ( mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP )
log_dbg_printf ( " SSL_SESS_CACHE_NO_INTERNAL_LOOKUP \n " ) ;
if ( mode & SSL_SESS_CACHE_NO_INTERNAL_STORE )
log_dbg_printf ( " SSL_SESS_CACHE_NO_INTERNAL_STORE \n " ) ;
}
# endif /* DEBUG_SESSION_CACHE */
return sslctx ;
}
static int
protossl_srccert_write_to_gendir ( pxy_conn_ctx_t * ctx , X509 * crt , int is_orig )
{
char * fn ;
int rv ;
if ( ! ctx - > sslctx - > origcrtfpr )
return - 1 ;
if ( is_orig ) {
rv = asprintf ( & fn , " %s/%s.crt " , ctx - > opts - > certgendir ,
ctx - > sslctx - > origcrtfpr ) ;
} else {
if ( ! ctx - > sslctx - > usedcrtfpr )
return - 1 ;
rv = asprintf ( & fn , " %s/%s-%s.crt " , ctx - > opts - > certgendir ,
ctx - > sslctx - > origcrtfpr , ctx - > sslctx - > usedcrtfpr ) ;
}
if ( rv = = - 1 ) {
ctx - > enomem = 1 ;
return - 1 ;
}
rv = log_cert_submit ( fn , crt ) ;
free ( fn ) ;
return rv ;
}
void
protossl_srccert_write ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > opts - > certgen_writeall | | ctx - > sslctx - > generated_cert ) {
if ( protossl_srccert_write_to_gendir ( ctx ,
SSL_get_certificate ( ctx - > src . ssl ) , 0 ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Failed to write used certificate \n " ) ;
}
}
if ( ctx - > opts - > certgen_writeall ) {
if ( protossl_srccert_write_to_gendir ( ctx , ctx - > sslctx - > origcrt , 1 ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " Failed to write orig certificate \n " ) ;
}
}
}
static cert_t *
protossl_srccert_create ( pxy_conn_ctx_t * ctx )
{
cert_t * cert = NULL ;
if ( ctx - > opts - > tgcrtdir ) {
if ( ctx - > sslctx - > sni ) {
cert = cachemgr_tgcrt_get ( ctx - > sslctx - > sni ) ;
if ( ! cert ) {
char * wildcarded ;
wildcarded = ssl_wildcardify ( ctx - > sslctx - > sni ) ;
if ( ! wildcarded ) {
ctx - > enomem = 1 ;
return NULL ;
}
cert = cachemgr_tgcrt_get ( wildcarded ) ;
free ( wildcarded ) ;
}
if ( cert & & OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Target cert by SNI \n " ) ;
}
} else if ( ctx - > sslctx - > origcrt ) {
char * * names = ssl_x509_names ( ctx - > sslctx - > origcrt ) ;
for ( char * * p = names ; * p ; p + + ) {
if ( ! cert ) {
cert = cachemgr_tgcrt_get ( * p ) ;
}
if ( ! cert ) {
char * wildcarded ;
wildcarded = ssl_wildcardify ( * p ) ;
if ( ! wildcarded ) {
ctx - > enomem = 1 ;
} else {
cert = cachemgr_tgcrt_get (
wildcarded ) ;
free ( wildcarded ) ;
}
}
free ( * p ) ;
}
free ( names ) ;
if ( ctx - > enomem ) {
return NULL ;
}
if ( cert & & OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Target cert by origcrt \n " ) ;
}
}
if ( cert ) {
ctx - > sslctx - > immutable_cert = 1 ;
}
}
if ( ! cert & & ctx - > sslctx - > origcrt & & ctx - > opts - > key ) {
cert = cert_new ( ) ;
cert - > crt = cachemgr_fkcrt_get ( ctx - > sslctx - > origcrt ) ;
if ( cert - > crt ) {
if ( OPTS_DEBUG ( ctx - > opts ) )
log_dbg_printf ( " Certificate cache: HIT \n " ) ;
} else {
if ( OPTS_DEBUG ( ctx - > opts ) )
log_dbg_printf ( " Certificate cache: MISS \n " ) ;
cert - > crt = ssl_x509_forge ( ctx - > opts - > cacrt ,
ctx - > opts - > cakey ,
ctx - > sslctx - > origcrt ,
ctx - > opts - > key ,
NULL ,
ctx - > opts - > crlurl ) ;
cachemgr_fkcrt_set ( ctx - > sslctx - > origcrt , cert - > crt ) ;
}
cert_set_key ( cert , ctx - > opts - > key ) ;
cert_set_chain ( cert , ctx - > opts - > chain ) ;
ctx - > sslctx - > generated_cert = 1 ;
}
if ( ( WANT_CONNECT_LOG ( ctx ) | | ctx - > opts - > certgendir ) & & ctx - > sslctx - > origcrt ) {
ctx - > sslctx - > origcrtfpr = ssl_x509_fingerprint ( ctx - > sslctx - > origcrt , 0 ) ;
if ( ! ctx - > sslctx - > origcrtfpr )
ctx - > enomem = 1 ;
}
if ( ( WANT_CONNECT_LOG ( ctx ) | | ctx - > opts - > certgen_writeall ) & &
cert & & cert - > crt ) {
ctx - > sslctx - > usedcrtfpr = ssl_x509_fingerprint ( cert - > crt , 0 ) ;
if ( ! ctx - > sslctx - > usedcrtfpr )
ctx - > enomem = 1 ;
}
return cert ;
}
/*
* Create new SSL context for the incoming connection , based on the original
* destination SSL certificate .
* Returns NULL if no suitable certificate could be found .
*/
static SSL *
protossl_srcssl_create ( pxy_conn_ctx_t * ctx , SSL * origssl )
{
cert_t * cert ;
cachemgr_dsess_set ( ( struct sockaddr * ) & ctx - > addr ,
ctx - > addrlen , ctx - > sslctx - > sni ,
SSL_get0_session ( origssl ) ) ;
ctx - > sslctx - > origcrt = SSL_get_peer_certificate ( origssl ) ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
if ( ctx - > sslctx - > origcrt ) {
log_dbg_printf ( " ===> Original server certificate: \n " ) ;
protossl_debug_crt ( ctx - > sslctx - > origcrt ) ;
} else {
log_dbg_printf ( " ===> Original server has no cert! \n " ) ;
}
}
cert = protossl_srccert_create ( ctx ) ;
if ( ! cert )
return NULL ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " ===> Forged server certificate: \n " ) ;
protossl_debug_crt ( cert - > crt ) ;
}
if ( WANT_CONNECT_LOG ( ctx ) ) {
ctx - > sslctx - > ssl_names = ssl_x509_names_to_str ( ctx - > sslctx - > origcrt ?
ctx - > sslctx - > origcrt :
cert - > crt ) ;
if ( ! ctx - > sslctx - > ssl_names )
ctx - > enomem = 1 ;
}
SSL_CTX * sslctx = protossl_srcsslctx_create ( ctx , cert - > crt , cert - > chain ,
cert - > key ) ;
cert_free ( cert ) ;
if ( ! sslctx ) {
ctx - > enomem = 1 ;
return NULL ;
}
SSL * ssl = SSL_new ( sslctx ) ;
SSL_CTX_free ( sslctx ) ; /* SSL_new() increments refcount */
if ( ! ssl ) {
ctx - > enomem = 1 ;
return NULL ;
}
# ifdef SSL_MODE_RELEASE_BUFFERS
/* lower memory footprint for idle connections */
SSL_set_mode ( ssl , SSL_get_mode ( ssl ) | SSL_MODE_RELEASE_BUFFERS ) ;
# endif /* SSL_MODE_RELEASE_BUFFERS */
return ssl ;
}
# ifndef OPENSSL_NO_TLSEXT
/*
* OpenSSL servername callback , called when OpenSSL receives a servername
* TLS extension in the clientHello . Must switch to a new SSL_CTX with
* a different certificate if we want to replace the server cert here .
* We generate a new certificate if the current one does not match the
* supplied servername . This should only happen if the original destination
* server supplies a certificate which does not match the server name we
* indicate to it .
*/
static int
protossl_ossl_servername_cb ( SSL * ssl , UNUSED int * al , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
const char * sn ;
X509 * sslcrt ;
if ( ! ( sn = SSL_get_servername ( ssl , TLSEXT_NAMETYPE_host_name ) ) )
return SSL_TLSEXT_ERR_NOACK ;
if ( ! ctx - > sslctx - > sni ) {
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Warning: SNI parser yielded no "
" hostname, copying OpenSSL one: "
" [NULL] != [%s] \n " , sn ) ;
}
ctx - > sslctx - > sni = strdup ( sn ) ;
if ( ! ctx - > sslctx - > sni ) {
ctx - > enomem = 1 ;
return SSL_TLSEXT_ERR_NOACK ;
}
}
if ( OPTS_DEBUG ( ctx - > opts ) ) {
if ( ! ! strcmp ( sn , ctx - > sslctx - > sni ) ) {
/*
* This may happen if the client resumes a session , but
* uses a different SNI hostname when resuming than it
* used when the session was created . OpenSSL
* correctly ignores the SNI in the ClientHello in this
* case , but since we have already sent the SNI onwards
* to the original destination , there is no way back .
* We log an error and hope this never happens .
*/
log_dbg_printf ( " Warning: SNI parser yielded different "
" hostname than OpenSSL callback for "
" the same ClientHello message: "
" [%s] != [%s] \n " , ctx - > sslctx - > sni , sn ) ;
}
}
/* generate a new certificate with sn as additional altSubjectName
* and replace it both in the current SSL ctx and in the cert cache */
if ( ctx - > opts - > allow_wrong_host & & ! ctx - > sslctx - > immutable_cert & &
! ssl_x509_names_match ( ( sslcrt = SSL_get_certificate ( ssl ) ) , sn ) ) {
X509 * newcrt ;
SSL_CTX * newsslctx ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Certificate cache: UPDATE "
" (SNI mismatch) \n " ) ;
}
newcrt = ssl_x509_forge ( ctx - > opts - > cacrt , ctx - > opts - > cakey ,
sslcrt , ctx - > opts - > key ,
sn , ctx - > opts - > crlurl ) ;
if ( ! newcrt ) {
ctx - > enomem = 1 ;
return SSL_TLSEXT_ERR_NOACK ;
}
cachemgr_fkcrt_set ( ctx - > sslctx - > origcrt , newcrt ) ;
ctx - > sslctx - > generated_cert = 1 ;
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " ===> Updated forged server "
" certificate: \n " ) ;
protossl_debug_crt ( newcrt ) ;
}
if ( WANT_CONNECT_LOG ( ctx ) ) {
if ( ctx - > sslctx - > ssl_names ) {
free ( ctx - > sslctx - > ssl_names ) ;
}
ctx - > sslctx - > ssl_names = ssl_x509_names_to_str ( newcrt ) ;
if ( ! ctx - > sslctx - > ssl_names ) {
ctx - > enomem = 1 ;
}
}
if ( WANT_CONNECT_LOG ( ctx ) | | ctx - > opts - > certgendir ) {
if ( ctx - > sslctx - > usedcrtfpr ) {
free ( ctx - > sslctx - > usedcrtfpr ) ;
}
ctx - > sslctx - > usedcrtfpr = ssl_x509_fingerprint ( newcrt , 0 ) ;
if ( ! ctx - > sslctx - > usedcrtfpr ) {
ctx - > enomem = 1 ;
}
}
newsslctx = protossl_srcsslctx_create ( ctx , newcrt , ctx - > opts - > chain ,
ctx - > opts - > key ) ;
if ( ! newsslctx ) {
X509_free ( newcrt ) ;
ctx - > enomem = 1 ;
return SSL_TLSEXT_ERR_NOACK ;
}
SSL_set_SSL_CTX ( ssl , newsslctx ) ; /* decr's old incr new refc */
SSL_CTX_free ( newsslctx ) ;
X509_free ( newcrt ) ;
} else if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Certificate cache: KEEP (SNI match or "
" target mode) \n " ) ;
}
return SSL_TLSEXT_ERR_OK ;
}
# endif /* !OPENSSL_NO_TLSEXT */
/*
* Create new SSL context for outgoing connections to the original destination .
* If hostname sni is provided , use it for Server Name Indication .
*/
SSL *
protossl_dstssl_create ( pxy_conn_ctx_t * ctx )
{
SSL_CTX * sslctx ;
SSL * ssl ;
SSL_SESSION * sess ;
sslctx = SSL_CTX_new ( ctx - > opts - > sslmethod ( ) ) ;
if ( ! sslctx ) {
ctx - > enomem = 1 ;
return NULL ;
}
protossl_sslctx_setoptions ( sslctx , ctx ) ;
# if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
if ( ctx - > opts - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , ctx - > opts - > sslversion ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , ctx - > opts - > sslversion ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
ctx - > enomem = 1 ;
return NULL ;
}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
if ( ctx - > opts - > verify_peer ) {
SSL_CTX_set_verify ( sslctx , SSL_VERIFY_PEER , NULL ) ;
SSL_CTX_set_default_verify_paths ( sslctx ) ;
} else {
SSL_CTX_set_verify ( sslctx , SSL_VERIFY_NONE , NULL ) ;
}
if ( ctx - > opts - > clientcrt ) {
if ( ! SSL_CTX_use_certificate ( sslctx , ctx - > opts - > clientcrt ) )
log_dbg_printf ( " loading client certificate failed " ) ;
}
if ( ctx - > opts - > clientkey ) {
if ( ! SSL_CTX_use_PrivateKey ( sslctx , ctx - > opts - > clientkey ) )
log_dbg_printf ( " loading client key failed " ) ;
}
ssl = SSL_new ( sslctx ) ;
SSL_CTX_free ( sslctx ) ; /* SSL_new() increments refcount */
if ( ! ssl ) {
ctx - > enomem = 1 ;
return NULL ;
}
# ifndef OPENSSL_NO_TLSEXT
if ( ctx - > sslctx - > sni ) {
SSL_set_tlsext_host_name ( ssl , ctx - > sslctx - > sni ) ;
}
# endif /* !OPENSSL_NO_TLSEXT */
# ifdef SSL_MODE_RELEASE_BUFFERS
/* lower memory footprint for idle connections */
SSL_set_mode ( ssl , SSL_get_mode ( ssl ) | SSL_MODE_RELEASE_BUFFERS ) ;
# endif /* SSL_MODE_RELEASE_BUFFERS */
/* session resuming based on remote endpoint address and port */
sess = cachemgr_dsess_get ( ( struct sockaddr * ) & ctx - > addr ,
ctx - > addrlen , ctx - > sslctx - > sni ) ; /* new sess inst */
if ( sess ) {
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " Attempt reuse dst SSL session \n " ) ;
}
SSL_set_session ( ssl , sess ) ; /* increments sess refcount */
SSL_SESSION_free ( sess ) ;
}
return ssl ;
}
2018-10-17 13:28:21 +00:00
/*
* Set up a bufferevent structure for either a dst or src connection ,
* optionally with or without SSL . Sets all callbacks , enables read
* and write events , but does not call bufferevent_socket_connect ( ) .
*
* For dst connections , pass - 1 as fd . Pass a pointer to an initialized
* SSL struct as ssl if the connection should use SSL .
*
* Returns pointer to initialized bufferevent structure , as returned
* by bufferevent_socket_new ( ) or bufferevent_openssl_socket_new ( ) .
*/
static struct bufferevent * NONNULL ( 1 , 3 )
protossl_bufferevent_setup ( pxy_conn_ctx_t * ctx , evutil_socket_t fd , SSL * ssl )
2018-10-16 00:37:07 +00:00
{
2018-10-17 13:28:21 +00:00
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bufferevent_setup: ENTER, fd=%d \n " , fd ) ;
# endif /* DEBUG_PROXY */
2018-10-16 00:37:07 +00:00
2018-10-17 13:28:21 +00:00
struct bufferevent * bev = bufferevent_openssl_socket_new ( ctx - > evbase , fd , ssl ,
( ( fd = = - 1 ) ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING ) , BEV_OPT_DEFER_CALLBACKS ) ;
if ( ! bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating bufferevent socket \n " ) ;
return NULL ;
2018-10-16 00:37:07 +00:00
}
2018-10-17 13:28:21 +00:00
# if LIBEVENT_VERSION_NUMBER >= 0x02010000
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bufferevent_setup: bufferevent_openssl_set_allow_dirty_shutdown \n " ) ;
# endif /* DEBUG_PROXY */
2018-10-19 23:03:21 +00:00
2018-10-17 13:28:21 +00:00
/* Prevent unclean (dirty) shutdowns to cause error
* events on the SSL socket bufferevent . */
bufferevent_openssl_set_allow_dirty_shutdown ( bev , 1 ) ;
# endif /* LIBEVENT_VERSION_NUMBER >= 0x02010000 */
// @attention Do not set callbacks here, srv_dst does not set r cb
//bufferevent_setcb(bev, pxy_bev_readcb, pxy_bev_writecb, pxy_bev_eventcb, ctx);
// @todo Should we enable events here?
//bufferevent_enable(bev, EV_READ|EV_WRITE);
return bev ;
2018-10-16 00:37:07 +00:00
}
2018-10-17 23:00:49 +00:00
static struct bufferevent * NONNULL ( 1 , 3 )
2018-10-17 13:28:21 +00:00
protossl_bufferevent_setup_child ( pxy_conn_child_ctx_t * ctx , evutil_socket_t fd , SSL * ssl )
2018-10-16 00:37:07 +00:00
{
# ifdef DEBUG_PROXY
2018-10-17 13:28:21 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bufferevent_setup_child: ENTER, fd=%d \n " , fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
2018-10-17 13:28:21 +00:00
struct bufferevent * bev = bufferevent_openssl_socket_new ( ctx - > conn - > evbase , fd , ssl ,
( ( fd = = - 1 ) ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING ) , BEV_OPT_DEFER_CALLBACKS ) ;
if ( ! bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating bufferevent socket \n " ) ;
return NULL ;
2018-10-16 00:37:07 +00:00
}
2018-10-17 13:28:21 +00:00
# if LIBEVENT_VERSION_NUMBER >= 0x02010000
2018-10-16 00:37:07 +00:00
# ifdef DEBUG_PROXY
2018-10-17 13:28:21 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bufferevent_setup_child: bufferevent_openssl_set_allow_dirty_shutdown \n " ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
2018-10-19 23:03:21 +00:00
2018-10-17 13:28:21 +00:00
/* Prevent unclean (dirty) shutdowns to cause error
* events on the SSL socket bufferevent . */
bufferevent_openssl_set_allow_dirty_shutdown ( bev , 1 ) ;
# endif /* LIBEVENT_VERSION_NUMBER >= 0x02010000 */
2018-10-16 00:37:07 +00:00
2018-10-17 13:28:21 +00:00
bufferevent_setcb ( bev , pxy_bev_readcb_child , pxy_bev_writecb_child , pxy_bev_eventcb_child , ctx ) ;
2018-10-16 00:37:07 +00:00
2018-10-17 13:28:21 +00:00
// @attention We cannot enable events here, because src events will be deferred until after dst is connected
//bufferevent_enable(bev, EV_READ|EV_WRITE);
return bev ;
2018-10-16 00:37:07 +00:00
}
2018-10-19 12:03:36 +00:00
/*
* Free bufferenvent and close underlying socket properly .
* For OpenSSL bufferevents , this will shutdown the SSL connection .
*/
void
protossl_bufferevent_free_and_close_fd ( struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
evutil_socket_t fd = bufferevent_getfd ( bev ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " protossl_bufferevent_free_and_close_fd: in=%zu, out=%zu, fd=%d \n " ,
2018-10-19 12:03:36 +00:00
evbuffer_get_length ( bufferevent_get_input ( bev ) ) , evbuffer_get_length ( bufferevent_get_output ( bev ) ) , fd ) ;
# endif /* DEBUG_PROXY */
SSL * ssl = bufferevent_openssl_get_ssl ( bev ) ; /* does not inc refc */
// @todo Check if we need to NULL all cbs?
// @see https://stackoverflow.com/questions/31688709/knowing-all-callbacks-have-run-with-libevent-and-bufferevent-free
//bufferevent_setcb(bev, NULL, NULL, NULL, NULL);
bufferevent_free ( bev ) ; /* does not free SSL unless the option BEV_OPT_CLOSE_ON_FREE was set */
pxy_ssl_shutdown ( ctx - > opts , ctx - > evbase , ssl , fd ) ;
}
void
protossl_free ( pxy_conn_ctx_t * ctx )
{
if ( ctx - > sslctx - > ssl_names ) {
free ( ctx - > sslctx - > ssl_names ) ;
}
if ( ctx - > sslctx - > origcrtfpr ) {
free ( ctx - > sslctx - > origcrtfpr ) ;
}
if ( ctx - > sslctx - > usedcrtfpr ) {
free ( ctx - > sslctx - > usedcrtfpr ) ;
}
if ( ctx - > sslctx - > origcrt ) {
X509_free ( ctx - > sslctx - > origcrt ) ;
}
if ( ctx - > sslctx - > sni ) {
free ( ctx - > sslctx - > sni ) ;
}
if ( ctx - > sslctx - > srv_dst_ssl_version ) {
free ( ctx - > sslctx - > srv_dst_ssl_version ) ;
}
if ( ctx - > sslctx - > srv_dst_ssl_cipher ) {
free ( ctx - > sslctx - > srv_dst_ssl_cipher ) ;
}
free ( ctx - > sslctx ) ;
}
2018-10-16 00:37:07 +00:00
# ifndef OPENSSL_NO_TLSEXT
/*
* The SNI hostname has been resolved . Fill the first resolved address into
* the context and continue connecting .
*/
static void
protossl_sni_resolve_cb ( int errcode , struct evutil_addrinfo * ai , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
# ifdef DEBUG_PROXY
2018-10-17 13:28:21 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_sni_resolve_cb: ENTER, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
if ( errcode ) {
log_err_printf ( " Cannot resolve SNI hostname '%s': %s \n " , ctx - > sslctx - > sni , evutil_gai_strerror ( errcode ) ) ;
evutil_closesocket ( ctx - > fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
memcpy ( & ctx - > addr , ai - > ai_addr , ai - > ai_addrlen ) ;
ctx - > addrlen = ai - > ai_addrlen ;
evutil_freeaddrinfo ( ai ) ;
pxy_conn_connect ( ctx ) ;
}
# endif /* !OPENSSL_NO_TLSEXT */
# ifndef OPENSSL_NO_TLSEXT
# define MAYBE_UNUSED
# else /* OPENSSL_NO_TLSEXT */
# define MAYBE_UNUSED UNUSED
# endif /* OPENSSL_NO_TLSEXT */
void
protossl_fd_readcb ( MAYBE_UNUSED evutil_socket_t fd , UNUSED short what , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
# ifdef DEBUG_PROXY
2018-10-17 13:28:21 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_fd_readcb: ENTER, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
# ifndef OPENSSL_NO_TLSEXT
// Child connections will use the sni info obtained by the parent conn
/* for SSL, peek ClientHello and parse SNI from it */
unsigned char buf [ 1024 ] ;
ssize_t n ;
const unsigned char * chello ;
int rv ;
n = recv ( fd , buf , sizeof ( buf ) , MSG_PEEK ) ;
if ( n = = - 1 ) {
log_err_printf ( " Error peeking on fd, aborting connection \n " ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " ERROR: Error peeking on fd, aborting connection, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
evutil_closesocket ( fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
if ( n = = 0 ) {
/* socket got closed while we were waiting */
log_err_printf ( " Socket got closed while waiting \n " ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " ERROR: Socket got closed while waiting, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
evutil_closesocket ( fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
rv = ssl_tls_clienthello_parse ( buf , n , 0 , & chello , & ctx - > sslctx - > sni ) ;
if ( ( rv = = 1 ) & & ! chello ) {
log_err_printf ( " Peeking did not yield a (truncated) ClientHello message, aborting connection \n " ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " ERROR: Peeking did not yield a (truncated) ClientHello message, aborting connection, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
evutil_closesocket ( fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
if ( OPTS_DEBUG ( ctx - > opts ) ) {
log_dbg_printf ( " SNI peek: [%s] [%s], fd=%d \n " , ctx - > sslctx - > sni ? ctx - > sslctx - > sni : " n/a " ,
( ( rv = = 1 ) & & chello ) ? " incomplete " : " complete " , ctx - > fd ) ;
}
if ( ( rv = = 1 ) & & chello & & ( ctx - > sslctx - > sni_peek_retries + + < 50 ) ) {
/* ssl_tls_clienthello_parse indicates that we
* should retry later when we have more data , and we
* haven ' t reached the maximum retry count yet .
* Reschedule this event as timeout - only event in
* order to prevent busy looping over the read event .
* Because we only peeked at the pending bytes and
* never actually read them , fd is still ready for
* reading now . We use 25 * 0.2 s = 5 s timeout . */
struct timeval retry_delay = { 0 , 100 } ;
event_free ( ctx - > ev ) ;
ctx - > ev = event_new ( ctx - > evbase , fd , 0 , ctx - > protoctx - > fd_readcb , ctx ) ;
if ( ! ctx - > ev ) {
log_err_level_printf ( LOG_CRIT , " Error creating retry event, aborting connection \n " ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " ERROR: Error creating retry event, aborting connection, fd=%d \n " , ctx - > fd ) ;
2018-10-16 00:37:07 +00:00
# endif /* DEBUG_PROXY */
evutil_closesocket ( fd ) ;
pxy_conn_ctx_free ( ctx , 1 ) ;
return ;
}
event_add ( ctx - > ev , & retry_delay ) ;
return ;
}
event_free ( ctx - > ev ) ;
ctx - > ev = NULL ;
if ( ctx - > sslctx - > sni & & ! ctx - > addrlen & & ctx - > spec - > sni_port ) {
char sniport [ 6 ] ;
struct evutil_addrinfo hints ;
memset ( & hints , 0 , sizeof ( hints ) ) ;
hints . ai_family = ctx - > af ;
hints . ai_flags = EVUTIL_AI_ADDRCONFIG ;
hints . ai_socktype = SOCK_STREAM ;
hints . ai_protocol = IPPROTO_TCP ;
snprintf ( sniport , sizeof ( sniport ) , " %i " , ctx - > spec - > sni_port ) ;
evdns_getaddrinfo ( ctx - > dnsbase , ctx - > sslctx - > sni , sniport , & hints , protossl_sni_resolve_cb , ctx ) ;
return ;
}
# endif /* !OPENSSL_NO_TLSEXT */
pxy_conn_connect ( ctx ) ;
}
2018-10-19 12:03:36 +00:00
int
2018-10-17 13:28:21 +00:00
protossl_setup_srv_dst_ssl ( pxy_conn_ctx_t * ctx )
{
ctx - > srv_dst . ssl = protossl_dstssl_create ( ctx ) ;
if ( ! ctx - > srv_dst . ssl ) {
log_err_level_printf ( LOG_CRIT , " Error creating SSL for srv_dst \n " ) ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-17 23:00:49 +00:00
static int NONNULL ( 1 )
2018-10-17 13:28:21 +00:00
protossl_setup_srv_dst ( pxy_conn_ctx_t * ctx )
{
if ( protossl_setup_srv_dst_ssl ( ctx ) = = - 1 ) {
return - 1 ;
}
ctx - > srv_dst . bev = protossl_bufferevent_setup ( ctx , - 1 , ctx - > srv_dst . ssl ) ;
if ( ! ctx - > srv_dst . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating srv_dst \n " ) ;
SSL_free ( ctx - > srv_dst . ssl ) ;
ctx - > srv_dst . ssl = NULL ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-19 12:03:36 +00:00
int
2018-10-22 12:30:18 +00:00
protossl_setup_srv_dst_new_bev_ssl_connecting ( pxy_conn_ctx_t * ctx )
2018-10-19 12:03:36 +00:00
{
ctx - > srv_dst . bev = bufferevent_openssl_filter_new ( ctx - > evbase , ctx - > srv_dst . bev , ctx - > srv_dst . ssl ,
2018-10-21 19:01:46 +00:00
BUFFEREVENT_SSL_CONNECTING , BEV_OPT_DEFER_CALLBACKS ) ;
2018-10-19 12:03:36 +00:00
if ( ! ctx - > srv_dst . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating srv_dst bufferevent \n " ) ;
SSL_free ( ctx - > srv_dst . ssl ) ;
ctx - > srv_dst . ssl = NULL ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-17 13:28:21 +00:00
void
protossl_conn_connect ( pxy_conn_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_conn_connect: ENTER, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
if ( prototcp_setup_dst ( ctx ) = = - 1 ) {
return ;
}
bufferevent_setcb ( ctx - > dst . bev , pxy_bev_readcb , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
bufferevent_enable ( ctx - > dst . bev , EV_READ | EV_WRITE ) ;
/* create server-side socket and eventbuffer */
if ( protossl_setup_srv_dst ( ctx ) = = - 1 ) {
return ;
}
// @attention Sometimes dst write cb fires but not event cb, especially if this listener cb is not finished yet, so the conn stalls.
// @todo Why does event cb not fire sometimes?
// @attention BEV_OPT_DEFER_CALLBACKS seems responsible for the issue with srv_dst, libevent acts as if we call event connect() ourselves.
// @see Launching connections on socket-based bufferevents at http://www.wangafu.net/~nickm/libevent-book/Ref6_bufferevent.html
// Disable and NULL r cb, we do nothing for srv_dst in r cb
bufferevent_setcb ( ctx - > srv_dst . bev , NULL , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
bufferevent_enable ( ctx - > srv_dst . bev , EV_WRITE ) ;
/* initiate connection */
if ( bufferevent_socket_connect ( ctx - > srv_dst . bev , ( struct sockaddr * ) & ctx - > addr , ctx - > addrlen ) = = - 1 ) {
log_err_level_printf ( LOG_CRIT , " protossl_conn_connect: bufferevent_socket_connect for srv_dst failed \n " ) ;
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " protossl_conn_connect: bufferevent_socket_connect for srv_dst failed, fd=%d \n " , ctx - > fd ) ;
2018-10-17 13:28:21 +00:00
# endif /* DEBUG_PROXY */
2018-10-19 23:03:21 +00:00
2018-10-17 13:28:21 +00:00
// @attention Do not try to close the conn here, otherwise both pxy_conn_connect() and eventcb try to free the conn using pxy_conn_free(),
// they are running on different threads, causing multithreading issues, e.g. signal 10.
// @todo Should we use thrmgr->mutex? Can we?
}
}
int
protossl_setup_dst_ssl_child ( pxy_conn_child_ctx_t * ctx )
{
// Children rely on the findings of parent
ctx - > dst . ssl = protossl_dstssl_create ( ctx - > conn ) ;
if ( ! ctx - > dst . ssl ) {
log_err_level_printf ( LOG_CRIT , " Error creating SSL \n " ) ;
// pxy_conn_free()>pxy_conn_free_child() will close the fd, since we have a non-NULL src.bev now
pxy_conn_free ( ctx - > conn , 1 ) ;
return - 1 ;
}
return 0 ;
}
int
protossl_setup_dst_child ( pxy_conn_child_ctx_t * ctx )
{
if ( protossl_setup_dst_ssl_child ( ctx ) = = - 1 ) {
return - 1 ;
}
ctx - > dst . bev = protossl_bufferevent_setup_child ( ctx , - 1 , ctx - > dst . ssl ) ;
if ( ! ctx - > dst . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating dst bufferevent \n " ) ;
SSL_free ( ctx - > dst . ssl ) ;
ctx - > dst . ssl = NULL ;
pxy_conn_free ( ctx - > conn , 1 ) ;
return - 1 ;
}
return 0 ;
}
void
protossl_connect_child ( pxy_conn_child_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_connect_child: ENTER, child fd=%d, fd=%d \n " , ctx - > fd , ctx - > conn - > fd ) ;
2018-10-17 13:28:21 +00:00
# endif /* DEBUG_PROXY */
/* create server-side socket and eventbuffer */
protossl_setup_dst_child ( ctx ) ;
}
2018-10-19 12:03:36 +00:00
int
protossl_setup_src_ssl ( pxy_conn_ctx_t * ctx )
{
// @todo Make srv_dst.ssl the origssl param
ctx - > src . ssl = protossl_srcssl_create ( ctx , ctx - > srv_dst . ssl ) ;
if ( ! ctx - > src . ssl ) {
if ( ctx - > opts - > passthrough & & ! ctx - > enomem ) {
log_err_level_printf ( LOG_WARNING , " No cert found; falling back to passthrough, fd=%d \n " , ctx - > fd ) ;
protopassthrough_engage ( ctx ) ;
// report protocol change by returning 1
return 1 ;
}
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-17 23:00:49 +00:00
static int NONNULL ( 1 )
2018-10-17 13:28:21 +00:00
protossl_setup_src ( pxy_conn_ctx_t * ctx )
{
int rv ;
if ( ( rv = protossl_setup_src_ssl ( ctx ) ) ! = 0 ) {
return rv ;
}
ctx - > src . bev = protossl_bufferevent_setup ( ctx , ctx - > fd , ctx - > src . ssl ) ;
if ( ! ctx - > src . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating src bufferevent \n " ) ;
SSL_free ( ctx - > src . ssl ) ;
ctx - > src . ssl = NULL ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-19 12:03:36 +00:00
int
2018-10-22 12:30:18 +00:00
protossl_setup_src_new_bev_ssl_accepting ( pxy_conn_ctx_t * ctx )
2018-10-19 12:03:36 +00:00
{
ctx - > src . bev = bufferevent_openssl_filter_new ( ctx - > evbase , ctx - > src . bev , ctx - > src . ssl ,
BUFFEREVENT_SSL_ACCEPTING , BEV_OPT_DEFER_CALLBACKS ) ;
if ( ! ctx - > src . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating src bufferevent \n " ) ;
SSL_free ( ctx - > src . ssl ) ;
ctx - > src . ssl = NULL ;
pxy_conn_free ( ctx , 1 ) ;
return - 1 ;
}
return 0 ;
}
int
2018-10-22 12:30:18 +00:00
protossl_setup_dst_new_bev_ssl_connecting_child ( pxy_conn_child_ctx_t * ctx )
2018-10-19 12:03:36 +00:00
{
ctx - > dst . bev = bufferevent_openssl_filter_new ( ctx - > conn - > evbase , ctx - > dst . bev , ctx - > dst . ssl ,
2018-10-21 19:01:46 +00:00
BUFFEREVENT_SSL_CONNECTING , BEV_OPT_DEFER_CALLBACKS ) ;
2018-10-19 12:03:36 +00:00
if ( ! ctx - > dst . bev ) {
log_err_level_printf ( LOG_CRIT , " Error creating dst bufferevent \n " ) ;
SSL_free ( ctx - > dst . ssl ) ;
ctx - > dst . ssl = NULL ;
pxy_conn_free ( ctx - > conn , 1 ) ;
return - 1 ;
}
return 0 ;
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 )
protossl_close_srv_dst ( pxy_conn_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " protossl_close_srv_dst: Closing srv_dst, fd=%d, srv_dst fd=%d \n " , ctx - > fd , bufferevent_getfd ( ctx - > srv_dst . bev ) ) ;
# endif /* DEBUG_PROXY */
2018-10-19 12:03:36 +00:00
// @attention Free the srv_dst of the conn asap, we don't need it anymore, but we need its fd
2018-10-17 23:00:49 +00:00
// So save its ssl info for logging
ctx - > sslctx - > srv_dst_ssl_version = strdup ( SSL_get_version ( ctx - > srv_dst . ssl ) ) ;
ctx - > sslctx - > srv_dst_ssl_cipher = strdup ( SSL_get_cipher ( ctx - > srv_dst . ssl ) ) ;
// @attention When both eventcb and writecb for srv_dst are enabled, either eventcb or writecb may get a NULL srv_dst bev, causing a crash with signal 10.
// So, from this point on, we should check if srv_dst is NULL or not.
protossl_bufferevent_free_and_close_fd ( ctx - > srv_dst . bev , ctx ) ;
ctx - > srv_dst . bev = NULL ;
ctx - > srv_dst . closed = 1 ;
}
static int NONNULL ( 1 )
2018-10-17 13:28:21 +00:00
protossl_enable_src ( pxy_conn_ctx_t * ctx )
{
int rv ;
if ( ( rv = protossl_setup_src ( ctx ) ) ! = 0 ) {
// Might have switched to passthrough mode
return rv ;
}
bufferevent_setcb ( ctx - > src . bev , pxy_bev_readcb , pxy_bev_writecb , pxy_bev_eventcb , ctx ) ;
protossl_close_srv_dst ( ctx ) ;
if ( pxy_setup_child_listener ( ctx ) = = - 1 ) {
return - 1 ;
}
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINER , " protossl_enable_src: Enabling src, %s, fd=%d, child_fd=%d \n " , ctx - > sslproxy_header , ctx - > fd , ctx - > child_fd ) ;
2018-10-17 13:28:21 +00:00
# endif /* DEBUG_PROXY */
2018-10-19 23:03:21 +00:00
2018-10-17 13:28:21 +00:00
// Now open the gates
bufferevent_enable ( ctx - > src . bev , EV_READ | EV_WRITE ) ;
return 0 ;
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 , 2 )
2018-10-17 13:28:21 +00:00
protossl_bev_eventcb_connected_dst ( UNUSED struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bev_eventcb_connected_dst: ENTER, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
ctx - > dst_connected = 1 ;
if ( ctx - > srv_dst_connected & & ctx - > dst_connected & & ! ctx - > connected ) {
2018-10-19 23:03:21 +00:00
ctx - > connected = 1 ;
2018-10-17 13:28:21 +00:00
if ( protossl_enable_src ( ctx ) = = - 1 ) {
return ;
}
}
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 , 2 )
2018-10-17 13:28:21 +00:00
protossl_bev_eventcb_connected_srv_dst ( UNUSED struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINEST , " protossl_bev_eventcb_connected_srv_dst: ENTER, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
ctx - > srv_dst_connected = 1 ;
2018-10-18 22:31:10 +00:00
2018-10-17 13:28:21 +00:00
// @attention Create and enable dst.bev before, but connect here, because we check if dst.bev is NULL elsewhere
if ( bufferevent_socket_connect ( ctx - > dst . bev , ( struct sockaddr * ) & ctx - > spec - > conn_dst_addr , ctx - > spec - > conn_dst_addrlen ) = = - 1 ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " protossl_bev_eventcb_connected_srv_dst: FAILED bufferevent_socket_connect for dst, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
2018-10-18 22:31:10 +00:00
2018-10-17 13:28:21 +00:00
pxy_conn_free ( ctx , 1 ) ;
return ;
}
2018-10-18 22:31:10 +00:00
2018-10-17 13:28:21 +00:00
if ( ctx - > srv_dst_connected & & ctx - > dst_connected & & ! ctx - > connected ) {
2018-10-19 23:03:21 +00:00
ctx - > connected = 1 ;
2018-10-17 13:28:21 +00:00
if ( protossl_enable_src ( ctx ) = = - 1 ) {
return ;
}
}
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 , 2 )
2018-10-17 13:28:21 +00:00
protossl_bev_eventcb_error_srv_dst ( struct bufferevent * bev , pxy_conn_ctx_t * ctx )
{
# ifdef DEBUG_PROXY
2018-10-20 21:25:01 +00:00
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " protossl_bev_eventcb_error_srv_dst: BEV_EVENT_ERROR, fd=%d \n " , ctx - > fd ) ;
2018-10-17 13:28:21 +00:00
# endif /* DEBUG_PROXY */
if ( ! ctx - > connected ) {
# ifdef DEBUG_PROXY
log_dbg_level_printf ( LOG_DBG_MODE_FINE , " protossl_bev_eventcb_error_srv_dst: ERROR !ctx->connected, fd=%d \n " , ctx - > fd ) ;
# endif /* DEBUG_PROXY */
2018-10-19 23:03:21 +00:00
2018-10-17 13:28:21 +00:00
/* the callout to the original destination failed,
* e . g . because it asked for client cert auth , so
* close the accepted socket and clean up */
if ( ctx - > opts - > passthrough & & bufferevent_get_openssl_error ( bev ) ) {
/* ssl callout failed, fall back to plain TCP passthrough of SSL connection */
log_err_level_printf ( LOG_WARNING , " SSL srv_dst connection failed; falling back to passthrough, fd=%d \n " , ctx - > fd ) ;
protopassthrough_engage ( ctx ) ;
return ;
}
pxy_conn_free ( ctx , 0 ) ;
}
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 )
2018-10-17 13:28:21 +00:00
protossl_bev_eventcb_dst ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
if ( events & BEV_EVENT_CONNECTED ) {
protossl_bev_eventcb_connected_dst ( bev , ctx ) ;
} else if ( events & BEV_EVENT_EOF ) {
prototcp_bev_eventcb_eof_dst ( bev , ctx ) ;
} else if ( events & BEV_EVENT_ERROR ) {
prototcp_bev_eventcb_error_dst ( bev , ctx ) ;
}
}
2018-10-17 23:00:49 +00:00
static void NONNULL ( 1 )
2018-10-17 13:28:21 +00:00
protossl_bev_eventcb_srv_dst ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
if ( events & BEV_EVENT_CONNECTED ) {
protossl_bev_eventcb_connected_srv_dst ( bev , ctx ) ;
} else if ( events & BEV_EVENT_EOF ) {
prototcp_bev_eventcb_eof_srv_dst ( bev , ctx ) ;
} else if ( events & BEV_EVENT_ERROR ) {
protossl_bev_eventcb_error_srv_dst ( bev , ctx ) ;
}
}
void
protossl_bev_eventcb ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_ctx_t * ctx = arg ;
if ( events & BEV_EVENT_ERROR ) {
protossl_log_ssl_error ( bev , ctx ) ;
}
if ( bev = = ctx - > src . bev ) {
prototcp_bev_eventcb_src ( bev , events , arg ) ;
} else if ( bev = = ctx - > dst . bev ) {
protossl_bev_eventcb_dst ( bev , events , arg ) ;
} else if ( bev = = ctx - > srv_dst . bev ) {
protossl_bev_eventcb_srv_dst ( bev , events , arg ) ;
} else {
log_err_printf ( " protossl_bev_eventcb: UNKWN conn end \n " ) ;
}
}
void
protossl_bev_eventcb_child ( struct bufferevent * bev , short events , void * arg )
{
pxy_conn_child_ctx_t * ctx = arg ;
if ( events & BEV_EVENT_ERROR ) {
protossl_log_ssl_error ( bev , ctx - > conn ) ;
}
if ( bev = = ctx - > src . bev ) {
prototcp_bev_eventcb_src_child ( bev , events , arg ) ;
} else if ( bev = = ctx - > dst . bev ) {
prototcp_bev_eventcb_dst_child ( bev , events , arg ) ;
} else {
log_err_printf ( " protossl_bev_eventcb_child: UNKWN conn end \n " ) ;
}
}
2018-10-17 23:00:49 +00:00
protocol_t
2018-10-16 00:37:07 +00:00
protossl_setup ( pxy_conn_ctx_t * ctx )
{
ctx - > protoctx - > proto = PROTO_SSL ;
2018-10-17 13:28:21 +00:00
ctx - > protoctx - > connectcb = protossl_conn_connect ;
2018-10-16 00:37:07 +00:00
ctx - > protoctx - > fd_readcb = protossl_fd_readcb ;
2018-10-17 13:28:21 +00:00
ctx - > protoctx - > bev_eventcb = protossl_bev_eventcb ;
2018-10-16 00:37:07 +00:00
ctx - > protoctx - > bufferevent_free_and_close_fd = protossl_bufferevent_free_and_close_fd ;
ctx - > protoctx - > proto_free = protossl_free ;
ctx - > sslctx = malloc ( sizeof ( ssl_ctx_t ) ) ;
if ( ! ctx - > sslctx ) {
free ( ctx - > protoctx - > arg ) ;
free ( ctx - > protoctx ) ;
return PROTO_ERROR ;
}
memset ( ctx - > sslctx , 0 , sizeof ( ssl_ctx_t ) ) ;
return PROTO_SSL ;
}
2018-10-17 23:00:49 +00:00
protocol_t
2018-10-16 00:37:07 +00:00
protossl_setup_child ( pxy_conn_child_ctx_t * ctx )
{
ctx - > protoctx - > proto = PROTO_SSL ;
ctx - > protoctx - > connectcb = protossl_connect_child ;
2018-10-17 13:28:21 +00:00
ctx - > protoctx - > bev_eventcb = protossl_bev_eventcb_child ;
2018-10-16 00:37:07 +00:00
ctx - > protoctx - > bufferevent_free_and_close_fd = protossl_bufferevent_free_and_close_fd ;
return PROTO_SSL ;
}
/* vim: set noet ft=c: */