@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/strings.h"
# include "asterisk/options.h"
# include "asterisk/manager.h"
# include "asterisk/astobj2.h"
/*! \brief
* replacement read / write functions for SSL support .
@ -117,9 +118,110 @@ static void session_instance_destructor(void *obj)
ast_mutex_destroy ( & i - > lock ) ;
}
/*! \brief
* creates a FILE * from the fd passed by the accept thread .
* This operation is potentially expensive ( certificate verification ) ,
* so we do it in the child thread context .
*/
static void * handle_tls_connection ( void * data )
{
struct ast_tcptls_session_instance * ser = data ;
# ifdef DO_SSL
int ( * ssl_setup ) ( SSL * ) = ( ser - > client ) ? SSL_connect : SSL_accept ;
int ret ;
char err [ 256 ] ;
# endif
/*
* open a FILE * as appropriate .
*/
if ( ! ser - > parent - > tls_cfg )
ser - > f = fdopen ( ser - > fd , " w+ " ) ;
# ifdef DO_SSL
else if ( ( ser - > ssl = SSL_new ( ser - > parent - > tls_cfg - > ssl_ctx ) ) ) {
SSL_set_fd ( ser - > ssl , ser - > fd ) ;
if ( ( ret = ssl_setup ( ser - > ssl ) ) < = 0 ) {
ast_verb ( 2 , " Problem setting up ssl connection: %s \n " , ERR_error_string ( ERR_get_error ( ) , err ) ) ;
} else {
# if defined(HAVE_FUNOPEN) /* the BSD interface */
ser - > f = funopen ( ser - > ssl , ssl_read , ssl_write , NULL , ssl_close ) ;
# elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
static const cookie_io_functions_t cookie_funcs = {
ssl_read , ssl_write , NULL , ssl_close
} ;
ser - > f = fopencookie ( ser - > ssl , " w+ " , cookie_funcs ) ;
# else
/* could add other methods here */
ast_debug ( 2 , " no ser->f methods attempted! " ) ;
# endif
if ( ( ser - > client & & ! ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_DONT_VERIFY_SERVER ) )
| | ( ! ser - > client & & ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_VERIFY_CLIENT ) ) ) {
X509 * peer ;
long res ;
peer = SSL_get_peer_certificate ( ser - > ssl ) ;
if ( ! peer )
ast_log ( LOG_WARNING , " No peer SSL certificate \n " ) ;
res = SSL_get_verify_result ( ser - > ssl ) ;
if ( res ! = X509_V_OK )
ast_log ( LOG_ERROR , " Certificate did not verify: %s \n " , X509_verify_cert_error_string ( res ) ) ;
if ( ! ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_IGNORE_COMMON_NAME ) ) {
ASN1_STRING * str ;
unsigned char * str2 ;
X509_NAME * name = X509_get_subject_name ( peer ) ;
int pos = - 1 ;
int found = 0 ;
for ( ; ; ) {
/* Walk the certificate to check all available "Common Name" */
/* XXX Probably should do a gethostbyname on the hostname and compare that as well */
pos = X509_NAME_get_index_by_NID ( name , NID_commonName , pos ) ;
if ( pos < 0 )
break ;
str = X509_NAME_ENTRY_get_data ( X509_NAME_get_entry ( name , pos ) ) ;
ASN1_STRING_to_UTF8 ( & str2 , str ) ;
if ( str2 ) {
if ( ! strcasecmp ( ser - > parent - > hostname , ( char * ) str2 ) )
found = 1 ;
ast_debug ( 3 , " SSL Common Name compare s1='%s' s2='%s' \n " , ser - > parent - > hostname , str2 ) ;
OPENSSL_free ( str2 ) ;
}
if ( found )
break ;
}
if ( ! found ) {
ast_log ( LOG_ERROR , " Certificate common name did not match (%s) \n " , ser - > parent - > hostname ) ;
if ( peer )
X509_free ( peer ) ;
fclose ( ser - > f ) ;
return NULL ;
}
}
if ( peer )
X509_free ( peer ) ;
}
}
if ( ! ser - > f ) /* no success opening descriptor stacking */
SSL_free ( ser - > ssl ) ;
}
# endif /* DO_SSL */
if ( ! ser - > f ) {
close ( ser - > fd ) ;
ast_log ( LOG_WARNING , " FILE * open failed! \n " ) ;
ao2_ref ( ser , - 1 ) ;
return NULL ;
}
if ( ser & & ser - > parent - > worker_fn )
return ser - > parent - > worker_fn ( ser ) ;
else
return ser ;
}
void * ast_tcptls_server_root ( void * data )
{
struct server_args * desc = data ;
struct ast_tcptls_session _args * desc = data ;
int fd ;
struct sockaddr_in sin ;
socklen_t sinlen ;
@ -135,7 +237,7 @@ void *ast_tcptls_server_root(void *data)
if ( i < = 0 )
continue ;
sinlen = sizeof ( sin ) ;
fd = accept ( desc - > accept_fd , ( struct sockaddr * ) & sin , & sinlen ) ;
fd = accept ( desc - > accept_fd , ( struct sockaddr * ) & sin , & sinlen ) ;
if ( fd < 0 ) {
if ( ( errno ! = EAGAIN ) & & ( errno ! = EINTR ) )
ast_log ( LOG_WARNING , " Accept failed: %s \n " , strerror ( errno ) ) ;
@ -154,11 +256,11 @@ void *ast_tcptls_server_root(void *data)
fcntl ( fd , F_SETFL , flags & ~ O_NONBLOCK ) ;
ser - > fd = fd ;
ser - > parent = desc ;
memcpy ( & ser - > re questor , & sin , sizeof ( ser - > re questor ) ) ;
memcpy ( & ser - > re mote_address , & sin , sizeof ( ser - > re mote_address ) ) ;
ser - > client = 0 ;
if ( ast_pthread_create_detached_background ( & launched , NULL , ast_make_file_from_fd , ser ) ) {
if ( ast_pthread_create_detached_background ( & launched , NULL , handle_tls_connection , ser ) ) {
ast_log ( LOG_WARNING , " Unable to launch helper thread: %s \n " , strerror ( errno ) ) ;
close ( ser - > fd ) ;
ao2_ref ( ser , - 1 ) ;
@ -225,18 +327,19 @@ int ast_ssl_setup(struct ast_tls_config *cfg)
/*! \brief A generic client routine for a TCP client
* and starts a thread for handling accept ( )
*/
struct ast_tcptls_session_instance * ast_tcptls_client_start ( struct server _args * desc )
struct ast_tcptls_session_instance * ast_tcptls_client_start ( struct ast_tcptls_session _args * desc )
{
int flags ;
int x = 1 ;
struct ast_tcptls_session_instance * ser = NULL ;
/* Do nothing if nothing has changed */
if ( ! memcmp ( & desc - > old sin, & desc - > sin , sizeof ( desc - > oldsin ) ) ) {
if ( ! memcmp ( & desc - > old _local_address, & desc - > local_address , sizeof ( desc - > old_local_address ) ) ) {
ast_debug ( 1 , " Nothing changed in %s \n " , desc - > name ) ;
return NULL ;
}
desc - > old sin = desc - > sin ;
desc - > old _local_address = desc - > local_address ;
if ( desc - > accept_fd ! = - 1 )
close ( desc - > accept_fd ) ;
@ -248,10 +351,23 @@ struct ast_tcptls_session_instance *ast_tcptls_client_start(struct server_args *
return NULL ;
}
if ( connect ( desc - > accept_fd , ( const struct sockaddr * ) & desc - > sin , sizeof ( desc - > sin ) ) ) {
/* if a local address was specified, bind to it so the connection will
originate from the desired address */
if ( desc - > local_address . sin_family ! = 0 ) {
setsockopt ( desc - > accept_fd , SOL_SOCKET , SO_REUSEADDR , & x , sizeof ( x ) ) ;
if ( bind ( desc - > accept_fd , ( struct sockaddr * ) & desc - > local_address , sizeof ( desc - > local_address ) ) ) {
ast_log ( LOG_ERROR , " Unable to bind %s to %s:%d: %s \n " ,
desc - > name ,
ast_inet_ntoa ( desc - > local_address . sin_addr ) , ntohs ( desc - > local_address . sin_port ) ,
strerror ( errno ) ) ;
goto error ;
}
}
if ( connect ( desc - > accept_fd , ( const struct sockaddr * ) & desc - > remote_address , sizeof ( desc - > remote_address ) ) ) {
ast_log ( LOG_ERROR , " Unable to connect %s to %s:%d: %s \n " ,
desc - > name ,
ast_inet_ntoa ( desc - > sin . sin_addr ) , ntohs ( desc - > sin . sin_port ) ,
ast_inet_ntoa ( desc - > remote_address . sin_addr ) , ntohs ( desc - > remote_address . sin_port ) ,
strerror ( errno ) ) ;
goto error ;
}
@ -267,7 +383,7 @@ struct ast_tcptls_session_instance *ast_tcptls_client_start(struct server_args *
ser - > fd = desc - > accept_fd ;
ser - > parent = desc ;
ser - > parent - > worker_fn = NULL ;
memcpy ( & ser - > re questor, & desc - > sin , sizeof ( ser - > requestor ) ) ;
memcpy ( & ser - > re mote_address, & desc - > local_address , sizeof ( ser - > remote_address ) ) ;
ser - > client = 1 ;
@ -277,7 +393,7 @@ struct ast_tcptls_session_instance *ast_tcptls_client_start(struct server_args *
}
ao2_ref ( ser , + 1 ) ;
if ( ! ast_make_file_from_fd ( ser ) )
if ( ! handle_tls_connection ( ser ) )
goto error ;
return ser ;
@ -295,18 +411,18 @@ error:
* which does the socket / bind / listen and starts a thread for handling
* accept ( ) .
*/
void ast_tcptls_server_start ( struct server _args * desc )
void ast_tcptls_server_start ( struct ast_tcptls_session _args * desc )
{
int flags ;
int x = 1 ;
/* Do nothing if nothing has changed */
if ( ! memcmp ( & desc - > old sin, & desc - > sin , sizeof ( desc - > oldsin ) ) ) {
if ( ! memcmp ( & desc - > old _local_address, & desc - > local_address , sizeof ( desc - > old_local_address ) ) ) {
ast_debug ( 1 , " Nothing changed in %s \n " , desc - > name ) ;
return ;
}
desc - > old sin = desc - > sin ;
desc - > old _local_address = desc - > local_address ;
/* Shutdown a running server if there is one */
if ( desc - > master ! = AST_PTHREADT_NULL ) {
@ -319,8 +435,9 @@ void ast_tcptls_server_start(struct server_args *desc)
close ( desc - > accept_fd ) ;
/* If there's no new server, stop here */
if ( desc - > sin . sin_family = = 0 )
if ( desc - > local_address . sin_family = = 0 ) {
return ;
}
desc - > accept_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( desc - > accept_fd < 0 ) {
@ -330,10 +447,10 @@ void ast_tcptls_server_start(struct server_args *desc)
}
setsockopt ( desc - > accept_fd , SOL_SOCKET , SO_REUSEADDR , & x , sizeof ( x ) ) ;
if ( bind ( desc - > accept_fd , ( struct sockaddr * ) & desc - > sin , sizeof ( desc - > sin ) ) ) {
if ( bind ( desc - > accept_fd , ( struct sockaddr * ) & desc - > local_address , sizeof ( desc - > local_address ) ) ) {
ast_log ( LOG_ERROR , " Unable to bind %s to %s:%d: %s \n " ,
desc - > name ,
ast_inet_ntoa ( desc - > sin . sin_addr ) , ntohs ( desc - > sin . sin_port ) ,
ast_inet_ntoa ( desc - > local_address . sin_addr ) , ntohs ( desc - > local_address . sin_port ) ,
strerror ( errno ) ) ;
goto error ;
}
@ -346,7 +463,7 @@ void ast_tcptls_server_start(struct server_args *desc)
if ( ast_pthread_create_background ( & desc - > master , NULL , desc - > accept_fn , desc ) ) {
ast_log ( LOG_ERROR , " Unable to launch thread for %s on %s:%d: %s \n " ,
desc - > name ,
ast_inet_ntoa ( desc - > sin . sin_addr ) , ntohs ( desc - > sin . sin_port ) ,
ast_inet_ntoa ( desc - > local_address . sin_addr ) , ntohs ( desc - > local_address . sin_port ) ,
strerror ( errno ) ) ;
goto error ;
}
@ -358,7 +475,7 @@ error:
}
/*! \brief Shutdown a running server if there is one */
void ast_tcptls_server_stop ( struct server _args * desc )
void ast_tcptls_server_stop ( struct ast_tcptls_session _args * desc )
{
if ( desc - > master ! = AST_PTHREADT_NULL ) {
pthread_cancel ( desc - > master ) ;
@ -369,105 +486,3 @@ void ast_tcptls_server_stop(struct server_args *desc)
close ( desc - > accept_fd ) ;
desc - > accept_fd = - 1 ;
}
/*! \brief
* creates a FILE * from the fd passed by the accept thread .
* This operation is potentially expensive ( certificate verification ) ,
* so we do it in the child thread context .
*/
void * ast_make_file_from_fd ( void * data )
{
struct ast_tcptls_session_instance * ser = data ;
# ifdef DO_SSL
int ( * ssl_setup ) ( SSL * ) = ( ser - > client ) ? SSL_connect : SSL_accept ;
int ret ;
char err [ 256 ] ;
# endif
/*
* open a FILE * as appropriate .
*/
if ( ! ser - > parent - > tls_cfg )
ser - > f = fdopen ( ser - > fd , " w+ " ) ;
# ifdef DO_SSL
else if ( ( ser - > ssl = SSL_new ( ser - > parent - > tls_cfg - > ssl_ctx ) ) ) {
SSL_set_fd ( ser - > ssl , ser - > fd ) ;
if ( ( ret = ssl_setup ( ser - > ssl ) ) < = 0 ) {
ast_verb ( 2 , " Problem setting up ssl connection: %s \n " , ERR_error_string ( ERR_get_error ( ) , err ) ) ;
} else {
# if defined(HAVE_FUNOPEN) /* the BSD interface */
ser - > f = funopen ( ser - > ssl , ssl_read , ssl_write , NULL , ssl_close ) ;
# elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */
static const cookie_io_functions_t cookie_funcs = {
ssl_read , ssl_write , NULL , ssl_close
} ;
ser - > f = fopencookie ( ser - > ssl , " w+ " , cookie_funcs ) ;
# else
/* could add other methods here */
ast_debug ( 2 , " no ser->f methods attempted! " ) ;
# endif
if ( ( ser - > client & & ! ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_DONT_VERIFY_SERVER ) )
| | ( ! ser - > client & & ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_VERIFY_CLIENT ) ) ) {
X509 * peer ;
long res ;
peer = SSL_get_peer_certificate ( ser - > ssl ) ;
if ( ! peer )
ast_log ( LOG_WARNING , " No peer SSL certificate \n " ) ;
res = SSL_get_verify_result ( ser - > ssl ) ;
if ( res ! = X509_V_OK )
ast_log ( LOG_ERROR , " Certificate did not verify: %s \n " , X509_verify_cert_error_string ( res ) ) ;
if ( ! ast_test_flag ( & ser - > parent - > tls_cfg - > flags , AST_SSL_IGNORE_COMMON_NAME ) ) {
ASN1_STRING * str ;
unsigned char * str2 ;
X509_NAME * name = X509_get_subject_name ( peer ) ;
int pos = - 1 ;
int found = 0 ;
for ( ; ; ) {
/* Walk the certificate to check all available "Common Name" */
/* XXX Probably should do a gethostbyname on the hostname and compare that as well */
pos = X509_NAME_get_index_by_NID ( name , NID_commonName , pos ) ;
if ( pos < 0 )
break ;
str = X509_NAME_ENTRY_get_data ( X509_NAME_get_entry ( name , pos ) ) ;
ASN1_STRING_to_UTF8 ( & str2 , str ) ;
if ( str2 ) {
if ( ! strcasecmp ( ser - > parent - > hostname , ( char * ) str2 ) )
found = 1 ;
ast_debug ( 3 , " SSL Common Name compare s1='%s' s2='%s' \n " , ser - > parent - > hostname , str2 ) ;
OPENSSL_free ( str2 ) ;
}
if ( found )
break ;
}
if ( ! found ) {
ast_log ( LOG_ERROR , " Certificate common name did not match (%s) \n " , ser - > parent - > hostname ) ;
if ( peer )
X509_free ( peer ) ;
fclose ( ser - > f ) ;
return NULL ;
}
}
if ( peer )
X509_free ( peer ) ;
}
}
if ( ! ser - > f ) /* no success opening descriptor stacking */
SSL_free ( ser - > ssl ) ;
}
# endif /* DO_SSL */
if ( ! ser - > f ) {
close ( ser - > fd ) ;
ast_log ( LOG_WARNING , " FILE * open failed! \n " ) ;
ao2_ref ( ser , - 1 ) ;
return NULL ;
}
if ( ser & & ser - > parent - > worker_fn )
return ser - > parent - > worker_fn ( ser ) ;
else
return ser ;
}