@@ -4,6 +4,8 @@
# include <string.h>
# include <errno.h>
# include <assert.h>
# include <stdint.h>
# include <sys/socket.h>
# include <event2/event.h>
# include <event2/listener.h>
@@ -26,7 +28,33 @@
# include <stream.h>
# include <cert.h>
# include <ssl.h>
# include <MESA_htable.h>
# define SSL_EX_DATA_IDX_SSLMGR 0
struct ssl_mgr
{
uint8_t sslcomp ;
uint8_t no_ssl2 ;
uint8_t no_ssl3 ;
uint8_t no_tls10 ;
uint8_t no_tls11 ;
uint8_t no_tls12 ;
CONST_SSL_METHOD * ( * sslmethod ) ( void ) ;
int sslversion ;
char ssl_session_context [ 8 ] ;
MESA_htable_handle down_sess_cache ;
MESA_htable_handle up_sess_cache ;
char * default_ciphers ;
DH * dh ;
char * ecdhcurve ;
char * crlurl ;
uint8_t SSL_MODE_RELEASE_BUFFERS ;
} ;
struct ssl_stream
{
SSL * ssl ;
struct ssl_mgr * ref_mgr ;
char * sni ;
} ;
struct peek_client_hello_ctx
{
@@ -35,6 +63,71 @@ struct peek_client_hello_ctx
struct event_base * evbase ;
} ;
struct ssl_connect_origin_ctx
{
struct bufferevent * bev ;
struct ssl_stream * ssl ;
struct sockaddr addr ;
socklen_t addrlen ;
} ;
struct ssl_mgr * init_ssl_manager ( const char * ini_profile , const char * section )
{
struct ssl_mgr * mgr = ALLOC ( struct ssl_mgr * , 1 ) ;
memcpy ( mgr - > ssl_session_context , " mesa-tfe " , sizeof ( mgr - > ssl_session_context ) ) ;
return mgr ;
}
void destroy_ssl_manager ( struct ssl_mgr * mgr )
{
free ( mgr ) ;
}
SSL_SESSION * up_session_get ( MESA_htable_handle * cache , struct sockaddr * addr , socklen_t addr_len , const char * sni )
{
}
void up_session_set ( MESA_htable_handle * cache , struct sockaddr * addr , socklen_t addr_len , const char * sni , SSL_SESSION * value )
{
}
SSL_SESSION * down_session_get ( MESA_htable_handle * cache , unsigned char * id , int idlen )
{
dynbuf_t * valbuf = ( dynbuf_t * ) val ;
MESA_htable_search_cb ( const MESA_htable_handle table , const uchar * key , uint size , hash_cb_fun_t * cb , void * arg , long * cb_ret ) ;
SSL_SESSION * sess ;
const unsigned char * p ;
p = ( const unsigned char * ) valbuf - > buf ;
sess = d2i_SSL_SESSION ( NULL , & p , valbuf - > sz ) ; /* increments p */
if ( ! sess )
return NULL ;
if ( ! ssl_session_is_valid ( sess ) ) {
SSL_SESSION_free ( sess ) ;
return NULL ;
}
if ( copy )
return sess ;
SSL_SESSION_free ( sess ) ;
return ( ( void * ) - 1 ) ;
}
void down_session_set ( MESA_htable_handle * cache , SSL_SESSION * sess )
{
unsigned int len = 0 ;
const unsigned char * id = SSL_SESSION_get_id ( sess , & len ) ;
unsigned char * p = NULL ;
size_t asn1sz = 0 ;
asn1sz = i2d_SSL_SESSION ( sess , NULL ) ;
p = ALLOC ( char , asn1sz ) ;
i2d_SSL_SESSION ( sess , & p ) ; /* updates p */
int ret = MESA_htable_add ( cache , id , len , p ) ;
return ;
}
void down_session_del ( MESA_htable_handle * cache , SSL_SESSION * sess )
{
}
void peek_client_hello_ctx_free ( void * ctx )
{
struct peek_client_hello_ctx * _ctx = ( struct peek_client_hello_ctx * ) ctx ;
@@ -45,10 +138,10 @@ void peek_client_hello_ctx_free(void* ctx)
free ( _ctx ) ;
return ;
}
struct ssl_client_ hello * ssl_get_ peek_result ( future_result_t * result )
struct ssl_chello * ssl_peek_result_release_chello ( future_result_t * result )
{
struct ssl_client_ hello * p = ( struct ssl_client_ hello * ) result , * copy = NULL ;
copy = ALLOC ( struct ssl_client_ hello * , 1 ) ;
struct ssl_chello * p = ( struct ssl_chello * ) result , * copy = NULL ;
copy = ALLOC ( struct ssl_chello * , 1 ) ;
if ( p ! = NULL )
{
copy - > sni = tfe_strdup ( p - > sni ) ;
@@ -57,7 +150,7 @@ struct ssl_client_hello* ssl_get_peek_result(future_result_t* result)
}
return copy ;
}
void ssl_free_peek_result ( struct ssl_client_ hello * p )
void ssl_free_chello ( struct ssl_chello * p )
{
if ( p = = NULL )
{
@@ -75,8 +168,11 @@ void ssl_free_peek_result(struct ssl_client_hello* p)
static void peek_client_hello_cb ( evutil_socket_t fd , short what , void * arg )
{
struct promise * promise = ( struct promise * ) arg ;
struct peek_client_hello_ctx * ctx = ( struct peek_client_hello_ctx * ) promise- > ctx ;
struct peek_client_hello_ctx * ctx = ( struct peek_client_hello_ctx * ) promise_get_ctx ( promise ) ;
struct ssl_chello * result = NULL ;
const char * reason_too_many_retries = " too many tries " ;
const char * reason_see_no_client_hello = " see no client hello " ;
const char * reason = NULL ;
memset ( & result , 0 , sizeof ( result ) ) ;
char * sni = NULL ;
@@ -97,44 +193,50 @@ static void peek_client_hello_cb(evutil_socket_t fd, short what, void * arg)
goto failed ;
}
result = ssl_get_peek_result ( NULL ) ;
//todo: parse version and cipher suites.
//or we should use sni proxy instead? https://github.com/dlundquist/sniproxy/blob/master/src/tls.c
rv = ssl_tls_clienthello_parse ( buf , n , 0 , & chello , & result - > sni ) ;
rv = ssl_tls_clienthello_parse ( buf , n , 0 , & chello , & result - > sni ) ;
if ( rv = = 0 )
{
TFE_LOG_ERROR ( " Peeking did not yield a (truncated) ClientHello message, aborting connection \n " ) ;
{
promise_dettach_ctx ( promise ) ;
promise_success ( promise , result ) ;
ssl_free_chello ( result ) ;
peek_client_hello_ctx_free ( ctx ) ;
}
}
else
{
if ( ! chello )
{
TFE_LOG_ERROR ( " Peeking did not yield a (truncated) ClientHello message, aborting connection \n " ) ;
reason = " see no client hello " ;
goto failed ;
}
if ( ctx - > sni_peek_retries + + > 50 )
{
TFE_LOG_ERROR ( " Peek failed due to too many retries \n " ) ;
reason = " too many peek retries " ;
goto failed ;
}
/* 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
/* 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 } ;
struct timeval retry_delay = { 0 , 100 } ;
event_free ( ctx - > ev ) ;
ctx - > ev = event_new ( ctx - > evbase , fd , 0 , peek_client_hello_cb , promise ) ;
assert ( ctx - > ev ! = NULL ) ;
event_add ( ctx - > ev , & retry_delay ) ;
event_add ( ctx - > ev , & retry_delay ) ;
}
promise_dettach_ctx ( promise ) ;
promise_success ( promise , result ) ;
ssl_free_peek_result ( result ) ;
}
return ;
failed :
promise_failed ( promise , FUTURE_ERROR_EXCEPTION , " too many tries " ) ;
failed :
promise_dettach_ctx ( promise ) ;
ssl_free_peek_result ( result ) ;
promise_dettach_ctx ( promise ) ;
promise_failed ( promise , FUTURE_ERROR_EXCEPTION , reason ) ;
peek_client_hello_ctx_free ( ctx ) ;
ssl_free_chello ( result ) ;
return ;
}
@@ -151,21 +253,101 @@ void ssl_async_peek_client_hello(struct future* future, evutil_socket_t fd, str
promise_set_ctx ( p , ctx , peek_client_hello_ctx_free ) ;
return ;
}
/*
* Create new SSL context for outgoing connections to the original destination.
* If hostname sni is provided, use it for Server Name Indication.
*/
static SSL * upstream_ssl_create ( struct ssl_mgr * mgr , const struct ssl_chello * chello , struct sockaddr * addr , socklen_t addrlen )
{
struct bufferevent * bev ;
{
SSL_CTX * sslctx = NULL ;
SSL * ssl = NULL ;
SSL_SESSION * sess = NULL ;
sslctx = SSL_CTX_new ( mgr - > sslmethod ( ) ) ;
pxy_sslctx_setoptions ( sslctx , mgr ) ;
# if OPENSSL_VERSION_NUMBER >= 0x10100000L
if ( mgr - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , chello - > version ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , chello - > version ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
return NULL ;
}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
SSL_CTX_set_verify ( sslctx , SSL_VERIFY_NONE , NULL ) ;
ssl = SSL_new ( sslctx ) ;
SSL_CTX_free ( sslctx ) ; /* SSL_new() increments refcount */
if ( ! ssl )
{
return NULL ;
}
# ifndef OPENSSL_NO_TLSEXT
if ( chello - > sni )
{
SSL_set_tlsext_host_name ( ssl , 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 */
//Todo: Refactor below session resumption.
/* session resuming based on remote endpoint address and port */
sess = up_session_get ( mgr - > up_sess_cache , addr ,
addrlen , chello - > sni ) ; /* new sess inst */
if ( sess )
{
SSL_set_session ( ssl , sess ) ; /* increments sess refcount */
SSL_SESSION_free ( sess ) ;
}
return ssl ;
}
void ssl_stream_shutdown ( struct bufferevent * bev , struct ssl_stream * ssl )
{
//TODO:
}
void ssl_connect_origin_ctx_free ( struct ssl_connect_origin_ctx * ctx )
{
ctx - > ssl = NULL ;
bufferevent_free ( ctx - > bev ) ;
ctx - > bev = NULL ;
//Do not free bev and ssl, reserved for user.
{
if ( ctx - > ssl ! =NULL )
{
ssl_stream_shutdown ( ctx - > bev , ctx - > ssl ) ;
free ( ctx - > ssl ) ;
}
if ( ctx - > bev ! = NULL )
{
bufferevent_free ( ctx - > bev ) ;
ctx - > bev = NULL ;
}
free ( ctx ) ;
return ;
}
struct ssl_stream * ssl_conn_origin_result_release_stream ( future_result_t * result )
{
struct ssl_connect_origin_ctx * ctx = ( struct ssl_connect_origin_ctx * ) result ;
struct ssl_stream * ret = ctx - > ssl ;
ctx - > ssl = NULL ; //giveup ownership
return ret ;
}
struct bufferevent * ssl_conn_origin_result_release_bev ( future_result_t * result )
{
struct ssl_connect_origin_ctx * ctx = ( struct ssl_connect_origin_ctx * ) result ;
struct bufferevent * ret = ctx - > bev ;
ctx - > bev = NULL ; //giveup ownership
return ret ;
}
/*
* Callback for meta events on the up- and downstream connection bufferevents.
@@ -173,22 +355,25 @@ void ssl_connect_origin_ctx_free(struct ssl_connect_origin_ctx* ctx)
*/
static void ssl_connect_origin_eventcb ( struct bufferevent * bev , short events , void * arg )
{
struct promise * promise = ( struct promise * ) arg ;
struct ssl_connect_origin_ctx * ctx = ( struct ssl_connect_origin_ctx * ) promise_d ettach _ctx ( promise ) ;
struct promise * promise = ( struct promise * ) arg ;
struct ssl_connect_origin_ctx * ctx = ( struct ssl_connect_origin_ctx * ) promise_g et_ctx ( promise ) ;
SSL_SESSION * to_cached = NULL ;
struct tfe_stream_private * _stream = ( struct tfe_stream_private * ) arg ;
if ( events & BEV_EVENT_ERROR )
{
{
promise_failed ( promise , FUTURE_ERROR_EXCEPTION , " ssl connect failed " ) ;
}
else
{
if ( events & BEV_EVENT_CONNECTED )
{
if ( events & BEV_EVENT_CONNECTED )
{
bufferevent_setcb ( ctx - > bev , NULL , NULL , NULL , NULL ) ; //leave a clean bev for on_success
to_cached = SSL_get0_session ( ctx - > ssl - > ssl ) ;
up_session_set ( ctx - > ssl - > ref_mgr - > up_sess_cache , & ( ctx - > addr ) , ctx - > addrlen , ctx - > ssl - > sni , to_cached ) ;
promise_dettach_ctx ( promise ) ;
promise_success ( promise , ctx ) ;
ctx - > bev = NULL ;
}
}
else
{
@@ -198,11 +383,19 @@ static void ssl_connect_origin_eventcb(struct bufferevent * bev, short events, v
ssl_connect_origin_ctx_free ( ctx ) ;
return
}
void ssl_async_connect_origin ( struct future * future , struct ssl_mgr * mgr , const struct ssl_chello * chello , evutil_socket_t fd , struct event_base * evbase )
{
struct promise * p = future_to_promise ( future ) ;
struct ssl_connect_origin_ctx * ctx = ALLOC ( struct ssl_connect_origin_ctx , 1 ) ;
struct ssl_connect_origin_ctx * ctx = ALLOC ( struct ssl_connect_origin_ctx , 1 ) ;
int ret = 0 ;
struct sockaddr addr ;
socklen_t addrlen ;
ret = getpeername ( fd , & ( ctx - > addr ) , & ( ctx - > addrlen ) ) ;
assert ( ret = = 0 ) ;
ctx - > ssl = ALLOC ( struct ssl_stream , 1 ) ;
ctx - > ssl - > ref_mgr = mgr ;
ctx - > ssl - > sni = tfe_strdup ( chello - > sni ) ;
ctx - > ssl - > ssl = upstream_ssl_create ( mgr , chello , & ( ctx - > addr ) , ctx - > addrlen ) ;
ctx - > bev = bufferevent_openssl_socket_new ( evbase , fd , ctx - > ssl , BUFFEREVENT_SSL_CONNECTING , BEV_OPT_DEFER_CALLBACKS ) ;
promise_set_ctx ( p , ctx , ssl_connect_origin_ctx_free ) ;
@@ -211,7 +404,7 @@ void ssl_async_connect_origin(struct future* future, const struct ssl_client_hel
bufferevent_enable ( ctx - > bev , EV_READ | EV_WRITE ) ;
}
/*
/*
* Dump information on a cert to the debug log.
*/
static void pxy_debug_crt ( X509 * crt )
@@ -241,7 +434,7 @@ static void pxy_debug_crt(X509 * crt)
free ( fpr ) ;
}
# ifdef DEBUG_CERTIFICATE
# ifdef DEBUG_CERTIFICATE
/* dump cert */
log_dbg_print_free ( ssl_x509_to_str ( crt ) ) ;
log_dbg_print_free ( ssl_x509_to_pem ( crt ) ) ;
@@ -406,35 +599,22 @@ out:
* 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
*/
static int
pxy_ossl_sessnew_cb ( MAYBE_UNUSED SSL * ssl , SSL_SESSION * sess )
static int
pxy_ossl_sessnew_cb ( SSL * ssl , SSL_SESSION * sess )
{
# 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 " ) ;
}
{
struct ssl_mgr * mgr = ( struct ssl_mgr * ) SSL_get_ex_data ( ssl , SSL_EX_DATA_IDX_SSLMGR , mgr ) ;
# 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_printf ( " Warning: Session resumption denied to SSLv2 "
if ( SSL_version ( ssl ) = = SSL2_VERSION ) {
return 0 ;
}
# endif /* HAVE_SSLV2 */
if ( sess )
{
{
down_session_set ( mgr - > down_sess_cache , sess ) ;
}
return 0 ;
@@ -447,46 +627,26 @@ pxy_ossl_sessnew_cb(MAYBE_UNUSED SSL * ssl, SSL_SESSION * sess)
*/
static void
pxy_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 " ) ;
}
{
struct ssl_mgr * mgr = ( struct ssl_mgr * ) SSL_get_ex_data ( ssl , SSL_EX_DATA_IDX_SSLMGR , mgr ) ;
if ( sess )
{
{
down_session_del ( mgr - > down_sess_cache , sess ) ;
}
return ;
}
/*
* Called by OpenSSL when a src SSL session is requested by the client.
OPENSSL_VERSION_NUMBER >= 0x10100000L required.
*/
static SSL_SESSION *
# if OPENSSL_VERSION_NUMBER < 0x10100000L
pxy_ossl_sessget_cb ( UNUSED SSL * ssl , unsigned char * id , int idlen , int * copy )
static SSL_SESSION *
pxy_ossl_sessget_cb ( UNUSED SSL * ssl , const unsigned char * id , int idlen , int * copy )
pxy_ossl_sessget_cb ( UNUSED SSL * ssl , const unsigned char * id , int idlen , int * copy )
{
struct ssl_mgr * mgr = ( struct ssl_mgr * ) SSL_get_ex_data ( ssl , SSL_EX_DATA_IDX_SSLMGR , mgr ) ;
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 = ( SSL_SESSION * ) cachemgr_ssess_get ( id , idlen ) ;
# ifdef DEBUG_SESSION_CACHE
if ( sess ) {
log_dbg_print_free ( ssl_session_to_str ( sess ) ) ;
}
# endif /* DEBUG_SESSION_CACHE */
* copy = 0 ; /* SSL should not increment reference count of session */
sess = ( SSL_SESSION * ) down_session_get ( mgr - > down_sess_cache , id , idlen ) ;
return sess ;
}
@@ -494,7 +654,7 @@ pxy_ossl_sessget_cb(UNUSED SSL *ssl, const unsigned char *id, int idlen, int *co
/*
* Set SSL_CTX options that are the same for incoming and outgoing SSL_CTX.
*/
static void
static void
pxy_sslctx_setoptions ( SSL_CTX * sslctx , struct ssl_mgr * mgr )
{
SSL_CTX_set_options ( sslctx , SSL_OP_ALL ) ;
@@ -512,7 +672,7 @@ pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts)
# endif /* SSL_OP_NO_TICKET */
# ifdef SSL_OP_NO_SSLv2
# ifdef HAVE_SSLV2
# ifdef HAVE_SSLV2
if ( mgr - > no_ssl2 ) {
# endif /* HAVE_SSLV2 */
SSL_CTX_set_options ( sslctx , SSL_OP_NO_SSLv2 ) ;
@@ -520,131 +680,40 @@ pxy_sslctx_setoptions(SSL_CTX * sslctx, tfe_config opts)
}
# endif /* HAVE_SSLV2 */
# endif /* !SSL_OP_NO_SSLv2 */
# ifdef HAVE_SSLV3
# ifdef HAVE_SSLV3
if ( mgr - > no_ssl3 )
{
SSL_CTX_set_options ( sslctx , SSL_OP_NO_SSLv3 ) ;
}
# endif /* HAVE_SSLV3 */
# ifdef HAVE_TLSV10
# ifdef HAVE_TLSV10
if ( mgr - > no_tls10 )
{
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1 ) ;
}
# endif /* HAVE_TLSV10 */
# ifdef HAVE_TLSV11
# ifdef HAVE_TLSV11
if ( mgr - > no_tls11 )
{
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1_1 ) ;
}
# endif /* HAVE_TLSV11 */
# ifdef HAVE_TLSV12
# ifdef HAVE_TLSV12
if ( mgr - > no_tls12 )
{
SSL_CTX_set_options ( sslctx , SSL_OP_NO_TLSv1_2 ) ;
}
# endif /* HAVE_TLSV12 */
# ifdef SSL_OP_NO_COMPRESSION
# ifdef SSL_OP_NO_COMPRESSION
if ( ! mgr - > sslcomp )
{
SSL_CTX_set_options ( sslctx , SSL_OP_NO_COMPRESSION ) ;
}
# endif /* SSL_OP_NO_COMPRESSION */
SSL_CTX_set_cipher_list ( sslctx , mgr - > default_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 * downstream_sslctx_create (
pxy_conn_ctx_t * ctx , X509 * crt , STACK_OF ( X509 ) * chain ,
EVP_PKEY * key , struct tfe_config * opts , void * cb_arg )
{
SSL_CTX * sslctx = SSL_CTX_new ( opts - > sslmethod ( ) ) ;
if ( ! sslctx )
return NULL ;
pxy_sslctx_setoptions ( sslctx , opts ) ;
# if OPENSSL_VERSION_NUMBER >= 0x10100000L
if ( opts - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , opts - > sslversion ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , opts - > sslversion ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
return NULL ;
}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
SSL_CTX_sess_set_new_cb ( sslctx , pxy_ossl_sessnew_cb ) ;
SSL_CTX_sess_set_remove_cb ( sslctx , pxy_ossl_sessremove_cb ) ;
SSL_CTX_sess_set_get_cb ( sslctx , pxy_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 , pxy_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
@@ -683,22 +752,22 @@ static void pxy_srccert_write(pxy_conn_ctx_t * ctx)
{
if ( pxy_srccert_write_to_gendir ( ctx ,
SSL_get_certificate ( ctx - > src . ssl ) , 0 ) = = - 1 )
{
{
log_err_printf ( " Failed to write used cert \n " ) ;
}
}
if ( ctx - > opts - > certgen_writeall )
{
if ( pxy_srccert_write_to_gendir ( ctx , ctx - > origcrt , 1 ) = = - 1 )
{
{
log_err_printf ( " Failed to write orig cert \n " ) ;
}
}
}
/*
* Create new SSL context for the incoming connection, based on the original
* destination SSL certificate .
* Create new SSL context for the incoming connection, based on the original
* destination SSL cert.
* Returns NULL if no suitable cert could be found.
*/
@@ -729,10 +798,10 @@ static SSL * downstream_ssl_create(cert_t * cert, struct tfe_config* opts)
/*
* 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.
* TLS extension in the clientHello. Must switch to a new SSL_CTX with
* a different cert if we want to replace the server cert here.
* We generate a new cert if the current one does not match the
* supplied servername. This should only happen if the original destination
* supplied servername. This should only happen if the original destination
* server supplies a cert which does not match the server name we
* indicate to it.
*/
@@ -779,7 +848,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
" [%s] != [%s] \n " , ctx - > sni , sn ) ;
}
}
/* generate a new cert with sn as additional altSubjectName
* and replace it both in the current SSL ctx and in the cert cache */
if ( ! ctx - > immutable_cert & &
@@ -805,7 +874,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
ctx - > generated_cert = 1 ;
if ( OPTS_DEBUG ( ctx - > opts ) )
{
log_dbg_printf ( " ===> Updated forged server "
log_dbg_printf ( " ===> Updated forged server "
" cert: \n " ) ;
pxy_debug_crt ( newcrt ) ;
}
@@ -854,81 +923,7 @@ static int pxy_ossl_servername_cb(SSL * ssl, UNUSED int * al, void * arg)
return SSL_TLSEXT_ERR_OK ;
}
/*
* Create new SSL context for outgoing connections to the original destination.
* If hostname sni is provided, use it for Server Name Indication.
static SSL * upstream_ssl_create ( const struct ssl_client_hello * client_hello , int sslversion , const char * sni , MESA_htable_handle * dsess_cache )
{
SSL_CTX * sslctx = NULL ;
SSL * ssl = NULL ;
SSL_SESSION * sess = NULL ;
sslctx = SSL_CTX_new ( opts - > sslmethod ( ) ) ;
pxy_sslctx_setoptions ( sslctx , opts ) ;
# if OPENSSL_VERSION_NUMBER >= 0x10100000L
if ( opts - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , opts - > sslversion ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , opts - > sslversion ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
return NULL ;
}
}
# endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */
SSL_CTX_set_verify ( sslctx , SSL_VERIFY_NONE , NULL ) ;
ssl = SSL_new ( sslctx ) ;
SSL_CTX_free ( sslctx ) ; /* SSL_new() increments refcount */
if ( ! ssl )
{
return NULL ;
}
# ifndef OPENSSL_NO_TLSEXT
if ( sni )
{
SSL_set_tlsext_host_name ( ssl , 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 */
//Todo: Refactor below session resumption.
/* session resuming based on remote endpoint address and port */
sess = ( SSL_SESSION * ) cachemgr_dsess_get ( ( struct sockaddr * ) & ctx - > addr ,
ctx - > addrlen , ctx - > sni ) ; /* new sess inst */
if ( sess )
{
if ( OPTS_DEBUG ( opts ) )
{
log_dbg_printf ( " Attempt reuse dst SSL session \n " ) ;
}
SSL_set_session ( ssl , sess ) ; /* increments sess refcount */
SSL_SESSION_free ( sess ) ;
}
return ssl ;
}
/*
struct ssl_upstream* ssl_upstream_create(tfe_config*opts, const char* sni)
{
struct ssl_upstream* upstream=NULL;
SSL* ssl=NULL;
ssl=upstream_ssl_create(opts,sni);
if(ssl==NULL)
{
return NULL;
}
upstream=ALLOC(struct ssl_upstream,1);
upstream->ssl=ssl;
return upstream;
void ssl_upstream_free ( struct ssl_upstream * p )
@@ -936,16 +931,120 @@ void ssl_upstream_free(struct ssl_upstream* p)
X509_free ( p - > orig_cert ) ;
//todo close ssl with a callback.
//SSL_free(ctx->ssl);
}
}
/*
* Create and set up a new SSL_CTX instance for terminating SSL.
* Set up all the necessary callbacks, the cert, the cert chain and key.
*/
static SSL_CTX * down_sslctx_create ( struct ssl_mgr * mgr , struct cert * crt )
{
struct ssl_downstream * p = NULL ;
p = ALLOC ( struct ssl_downstream , 1 ) ;
{
SSL_CTX * sslctx = SSL_CTX_new ( mgr - > sslmethod ( ) ) ;
if ( ! sslctx )
return NULL ;
pxy_sslctx_setoptions ( sslctx , mgr ) ;
//TFE's OPENSSL_VERSION_NUMBER >= 0x10100000L
if ( mgr - > sslversion ) {
if ( SSL_CTX_set_min_proto_version ( sslctx , mgr - > sslversion ) = = 0 | |
SSL_CTX_set_max_proto_version ( sslctx , mgr - > sslversion ) = = 0 ) {
SSL_CTX_free ( sslctx ) ;
return NULL ;
}
}
SSL_CTX_sess_set_new_cb ( sslctx , pxy_ossl_sessnew_cb ) ;
SSL_CTX_sess_set_remove_cb ( sslctx , pxy_ossl_sessremove_cb ) ;
SSL_CTX_sess_set_get_cb ( sslctx , pxy_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 * ) ( mgr - > ssl_session_context ) ,
sizeof ( mgr - > ssl_session_context ) ) ;
# endif /* USE_SSL_SESSION_ID_CONTEXT */
# ifndef OPENSSL_NO_DH
if ( mgr - > dh )
{
SSL_CTX_set_tmp_dh ( sslctx , mgr - > dh ) ;
}
else
{
SSL_CTX_set_tmp_dh_callback ( sslctx , ssl_tmp_dh_callback ) ;
}
# endif /* !OPENSSL_NO_DH */
# ifndef OPENSSL_NO_ECDH
if ( mgr - > ecdhcurve )
{
EC_KEY * ecdh = ssl_ec_by_name ( mgr - > 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 - > crt ) ;
SSL_CTX_use_PrivateKey ( sslctx , crt - > key ) ;
for ( int i = 0 ; i < sk_X509_num ( crt - > chain ) ; i + + )
{
X509 * c = sk_X509_value ( crt - > chain , i ) ;
ssl_x509_refcount_inc ( c ) ; /* next call consumes a reference */
SSL_CTX_add_extra_chain_cert ( sslctx , c ) ;
}
return sslctx ;
}
}
/*
* Create new SSL context for the incoming connection, based on the original
* destination SSL cert.
* Returns NULL if no suitable cert could be found.
*/
struct ssl_stream * ssl_downstream_create ( struct ssl_mgr * mgr , struct ssl_chello * hello , struct cert * crt )
{
free ( p - > sni ) ;
{
struct ssl_stream * p = NULL ;
if ( crt = = NULL )
{
goto error_out ;
}
p = ALLOC ( struct ssl_stream , 1 ) ;
p - > ref_mgr = mgr ;
//could be optimized by store the sslctx.
SSL_CTX * sslctx = down_sslctx_create ( mgr , crt ) ;
cert_mgr_free_cert ( crt ) ;
if ( ! sslctx )
{
goto error_out ;
}
p - > ssl = SSL_new ( sslctx ) ;
int ret = SSL_set_ex_data ( p - > ssl , SSL_EX_DATA_IDX_SSLMGR , mgr ) ;
assert ( ret = = 1 ) ;
SSL_CTX_free ( sslctx ) ; /* SSL_new() increments refcount */
if ( ! p - > ssl )
{
goto error_out ;
}
if ( mgr - > SSL_MODE_RELEASE_BUFFERS = = 1 )
{
/* lower memory footprint for idle connections */
SSL_set_mode ( p - > ssl , SSL_get_mode ( p - > ssl ) | SSL_MODE_RELEASE_BUFFERS ) ;
}
p - > sni = tfe_strdup ( hello - > sni ) ;
return p ;
error_out :
free ( p ) ;
return NULL ;
}
void ssl_stream_free_and_close_fd ( struct ssl_stream * stream , struct event_base * evbase , evutil_socket_t fd )
{
free ( stream - > sni ) ;
stream - > sni = NULL ;
// X509_free(p->fake_cert);
}