@ -80,12 +80,7 @@ static int my_SSL_set_fd(PGconn *conn, int fd);
static bool pq_init_ssl_lib = true ;
static bool pq_init_crypto_lib = true ;
/*
* SSL_context is currently shared between threads and therefore we need to be
* careful to lock around any usage of it when providing thread safety .
* ssl_config_mutex is the mutex that we use to protect it .
*/
static SSL_CTX * SSL_context = NULL ;
static bool ssl_lib_initialized = false ;
# ifdef ENABLE_THREAD_SAFETY
static long ssl_open_connections = 0 ;
@ -133,44 +128,9 @@ pgtls_open_client(PGconn *conn)
/* First time through? */
if ( conn - > ssl = = NULL )
{
# ifdef ENABLE_THREAD_SAFETY
int rc ;
# endif
# ifdef ENABLE_THREAD_SAFETY
if ( ( rc = pthread_mutex_lock ( & ssl_config_mutex ) ) )
{
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not acquire mutex: %s \n " ) , strerror ( rc ) ) ;
return PGRES_POLLING_FAILED ;
}
# endif
/* Create a connection-specific SSL object */
if ( ! ( conn - > ssl = SSL_new ( SSL_context ) ) | |
! SSL_set_app_data ( conn - > ssl , conn ) | |
! my_SSL_set_fd ( conn , conn - > sock ) )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not establish SSL connection: %s \n " ) ,
err ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
pgtls_close ( conn ) ;
return PGRES_POLLING_FAILED ;
}
conn - > ssl_in_use = true ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
/*
* Load client certificate , private key , and trusted CA certs .
* Create a connection - specific SSL object , and load client certificate ,
* private key , and trusted CA certs .
*/
if ( initialize_SSL ( conn ) ! = 0 )
{
@ -771,8 +731,7 @@ pq_lockingcallback(int mode, int n, const char *file, int line)
# endif /* ENABLE_THREAD_SAFETY && HAVE_CRYPTO_LOCK */
/*
* Initialize SSL system , in particular creating the SSL_context object
* that will be shared by all SSL - using connections in this process .
* Initialize SSL library .
*
* In threadsafe mode , this includes setting up libcrypto callback functions
* to do thread locking .
@ -851,7 +810,7 @@ pgtls_init(PGconn *conn)
# endif /* HAVE_CRYPTO_LOCK */
# endif /* ENABLE_THREAD_SAFETY */
if ( ! SSL_context )
if ( ! ssl_lib_initialized )
{
if ( pq_init_ssl_lib )
{
@ -863,36 +822,7 @@ pgtls_init(PGconn *conn)
SSL_load_error_strings ( ) ;
# endif
}
/*
* We use SSLv23_method ( ) because it can negotiate use of the highest
* mutually supported protocol version , while alternatives like
* TLSv1_2_method ( ) permit only one specific version . Note that we
* don ' t actually allow SSL v2 or v3 , only TLS protocols ( see below ) .
*/
SSL_context = SSL_CTX_new ( SSLv23_method ( ) ) ;
if ( ! SSL_context )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not create SSL context: %s \n " ) ,
err ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
return - 1 ;
}
/* Disable old protocol versions */
SSL_CTX_set_options ( SSL_context , SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 ) ;
/*
* Disable OpenSSL ' s moving - write - buffer sanity check , because it
* causes unnecessary failures in nonblocking send cases .
*/
SSL_CTX_set_mode ( SSL_context , SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER ) ;
ssl_lib_initialized = true ;
}
# ifdef ENABLE_THREAD_SAFETY
@ -936,9 +866,8 @@ destroy_ssl_system(void)
CRYPTO_set_id_callback ( NULL ) ;
/*
* We don ' t free the lock array or the SSL_context . If we get another
* connection in this process , we will just re - use them with the
* existing mutexes .
* We don ' t free the lock array . If we get another connection in
* this process , we will just re - use them with the existing mutexes .
*
* This means we leak a little memory on repeated load / unload of the
* library .
@ -950,26 +879,22 @@ destroy_ssl_system(void)
}
/*
* Initialize ( potentially ) per - connection SSL data , namely the
* client certificate , private key , and trusted CA certs .
*
* conn - > ssl must already be created . It receives the connection ' s client
* certificate and private key . Note however that certificates also get
* loaded into the SSL_context object , and are therefore accessible to all
* connections in this process . This should be OK as long as there aren ' t
* any hash collisions among the certs .
* Create per - connection SSL object , and load the client certificate ,
* private key , and trusted CA certs .
*
* Returns 0 if OK , - 1 on failure ( with a message in conn - > errorMessage ) .
*/
static int
initialize_SSL ( PGconn * conn )
{
SSL_CTX * SSL_context ;
struct stat buf ;
char homedir [ MAXPGPATH ] ;
char fnbuf [ MAXPGPATH ] ;
char sebuf [ 256 ] ;
bool have_homedir ;
bool have_cert ;
bool have_rootcert ;
EVP_PKEY * pkey = NULL ;
/*
@ -985,6 +910,123 @@ initialize_SSL(PGconn *conn)
else /* won't need it */
have_homedir = false ;
/*
* Create a new SSL_CTX object .
*
* We used to share a single SSL_CTX between all connections , but it was
* complicated if connections used different certificates . So now we create
* a separate context for each connection , and accept the overhead .
*/
SSL_context = SSL_CTX_new ( SSLv23_method ( ) ) ;
if ( ! SSL_context )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not create SSL context: %s \n " ) ,
err ) ;
SSLerrfree ( err ) ;
return - 1 ;
}
/* Disable old protocol versions */
SSL_CTX_set_options ( SSL_context , SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 ) ;
/*
* Disable OpenSSL ' s moving - write - buffer sanity check , because it
* causes unnecessary failures in nonblocking send cases .
*/
SSL_CTX_set_mode ( SSL_context , SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER ) ;
/*
* If the root cert file exists , load it so we can perform certificate
* verification . If sslmode is " verify-full " we will also do further
* verification after the connection has been completed .
*/
if ( conn - > sslrootcert & & strlen ( conn - > sslrootcert ) > 0 )
strlcpy ( fnbuf , conn - > sslrootcert , sizeof ( fnbuf ) ) ;
else if ( have_homedir )
snprintf ( fnbuf , sizeof ( fnbuf ) , " %s/%s " , homedir , ROOT_CERT_FILE ) ;
else
fnbuf [ 0 ] = ' \0 ' ;
if ( fnbuf [ 0 ] ! = ' \0 ' & &
stat ( fnbuf , & buf ) = = 0 )
{
X509_STORE * cvstore ;
if ( SSL_CTX_load_verify_locations ( SSL_context , fnbuf , NULL ) ! = 1 )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not read root certificate file \" %s \" : %s \n " ) ,
fnbuf , err ) ;
SSLerrfree ( err ) ;
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
}
if ( ( cvstore = SSL_CTX_get_cert_store ( SSL_context ) ) ! = NULL )
{
if ( conn - > sslcrl & & strlen ( conn - > sslcrl ) > 0 )
strlcpy ( fnbuf , conn - > sslcrl , sizeof ( fnbuf ) ) ;
else if ( have_homedir )
snprintf ( fnbuf , sizeof ( fnbuf ) , " %s/%s " , homedir , ROOT_CRL_FILE ) ;
else
fnbuf [ 0 ] = ' \0 ' ;
/* Set the flags to check against the complete CRL chain */
if ( fnbuf [ 0 ] ! = ' \0 ' & &
X509_STORE_load_locations ( cvstore , fnbuf , NULL ) = = 1 )
{
/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
# ifdef X509_V_FLAG_CRL_CHECK
X509_STORE_set_flags ( cvstore ,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ) ;
# else
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " SSL library does not support CRL certificates (file \" %s \" ) \n " ) ,
fnbuf ) ;
SSLerrfree ( err ) ;
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
# endif
}
/* if not found, silently ignore; we do not require CRL */
}
have_rootcert = true ;
}
else
{
/*
* stat ( ) failed ; assume root file doesn ' t exist . If sslmode is
* verify - ca or verify - full , this is an error . Otherwise , continue
* without performing any server cert verification .
*/
if ( conn - > sslmode [ 0 ] = = ' v ' ) /* "verify-ca" or "verify-full" */
{
/*
* The only way to reach here with an empty filename is if
* pqGetHomeDirectory failed . That ' s a sufficiently unusual case
* that it seems worth having a specialized error message for it .
*/
if ( fnbuf [ 0 ] = = ' \0 ' )
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not get home directory to locate root certificate file \n "
" Either provide the file or change sslmode to disable server certificate verification. \n " ) ) ;
else
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " root certificate file \" %s \" does not exist \n "
" Either provide the file or change sslmode to disable server certificate verification. \n " ) , fnbuf ) ;
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
}
have_rootcert = false ;
}
/* Read the client certificate file */
if ( conn - > sslcert & & strlen ( conn - > sslcert ) > 0 )
strlcpy ( fnbuf , conn - > sslcert , sizeof ( fnbuf ) ) ;
@ -1010,6 +1052,7 @@ initialize_SSL(PGconn *conn)
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not open certificate file \" %s \" : %s \n " ) ,
fnbuf , pqStrerror ( errno , sebuf , sizeof ( sebuf ) ) ) ;
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
}
have_cert = false ;
@ -1017,31 +1060,10 @@ initialize_SSL(PGconn *conn)
else
{
/*
* Cert file exists , so load it . Since OpenSSL doesn ' t provide the
* equivalent of " SSL_use_certificate_chain_file " , we actually have to
* load the file twice . The first call loads any extra certs after
* the first one into chain - cert storage associated with the
* SSL_context . The second call loads the first cert ( only ) into the
* SSL object , where it will be correctly paired with the private key
* we load below . We do it this way so that each connection
* understands which subject cert to present , in case different
* sslcert settings are used for different connections in the same
* process .
*
* NOTE : This function may also modify our SSL_context and therefore
* we have to lock around this call and any places where we use the
* SSL_context struct .
* Cert file exists , so load it . Since OpenSSL doesn ' t provide the
* equivalent of " SSL_use_certificate_chain_file " , we have to load
* it into the SSL context , rather than the SSL object .
*/
# ifdef ENABLE_THREAD_SAFETY
int rc ;
if ( ( rc = pthread_mutex_lock ( & ssl_config_mutex ) ) )
{
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not acquire mutex: %s \n " ) , strerror ( rc ) ) ;
return - 1 ;
}
# endif
if ( SSL_CTX_use_certificate_chain_file ( SSL_context , fnbuf ) ! = 1 )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
@ -1050,34 +1072,42 @@ initialize_SSL(PGconn *conn)
libpq_gettext ( " could not read certificate file \" %s \" : %s \n " ) ,
fnbuf , err ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
return - 1 ;
}
if ( SSL_use_certificate_file ( conn - > ssl , fnbuf , SSL_FILETYPE_PEM ) ! = 1 )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not read certificate file \" %s \" : %s \n " ) ,
fnbuf , err ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
}
/* need to load the associated private key, too */
have_cert = true ;
}
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
/*
* The SSL context is now loaded with the correct root and client certificates .
* Create a connection - specific SSL object . The private key is loaded directly
* into the SSL object . ( We could load the private key into the context , too , but
* we have done it this way historically , and it doesn ' t really matter . )
*/
if ( ! ( conn - > ssl = SSL_new ( SSL_context ) ) | |
! SSL_set_app_data ( conn - > ssl , conn ) | |
! my_SSL_set_fd ( conn , conn - > sock ) )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not establish SSL connection: %s \n " ) ,
err ) ;
SSLerrfree ( err ) ;
SSL_CTX_free ( SSL_context ) ;
return - 1 ;
}
conn - > ssl_in_use = true ;
/*
* SSL contexts are reference counted by OpenSSL . We can free it as soon as we
* have created the SSL object , and it will stick around for as long as it ' s
* actually needed .
*/
SSL_CTX_free ( SSL_context ) ;
SSL_context = NULL ;
/*
* Read the SSL key . If a key is specified , treat it as an engine : key
@ -1236,109 +1266,10 @@ initialize_SSL(PGconn *conn)
}
/*
* If the root cert file exists , load it so we can perform certificate
* verification . If sslmode is " verify-full " we will also do further
* verification after the connection has been completed .
* If a root cert was loaded , also set our certificate verification callback .
*/
if ( conn - > sslrootcert & & strlen ( conn - > sslrootcert ) > 0 )
strlcpy ( fnbuf , conn - > sslrootcert , sizeof ( fnbuf ) ) ;
else if ( have_homedir )
snprintf ( fnbuf , sizeof ( fnbuf ) , " %s/%s " , homedir , ROOT_CERT_FILE ) ;
else
fnbuf [ 0 ] = ' \0 ' ;
if ( fnbuf [ 0 ] ! = ' \0 ' & &
stat ( fnbuf , & buf ) = = 0 )
{
X509_STORE * cvstore ;
# ifdef ENABLE_THREAD_SAFETY
int rc ;
if ( ( rc = pthread_mutex_lock ( & ssl_config_mutex ) ) )
{
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not acquire mutex: %s \n " ) , strerror ( rc ) ) ;
return - 1 ;
}
# endif
if ( SSL_CTX_load_verify_locations ( SSL_context , fnbuf , NULL ) ! = 1 )
{
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not read root certificate file \" %s \" : %s \n " ) ,
fnbuf , err ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
return - 1 ;
}
if ( ( cvstore = SSL_CTX_get_cert_store ( SSL_context ) ) ! = NULL )
{
if ( conn - > sslcrl & & strlen ( conn - > sslcrl ) > 0 )
strlcpy ( fnbuf , conn - > sslcrl , sizeof ( fnbuf ) ) ;
else if ( have_homedir )
snprintf ( fnbuf , sizeof ( fnbuf ) , " %s/%s " , homedir , ROOT_CRL_FILE ) ;
else
fnbuf [ 0 ] = ' \0 ' ;
/* Set the flags to check against the complete CRL chain */
if ( fnbuf [ 0 ] ! = ' \0 ' & &
X509_STORE_load_locations ( cvstore , fnbuf , NULL ) = = 1 )
{
/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
# ifdef X509_V_FLAG_CRL_CHECK
X509_STORE_set_flags ( cvstore ,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ) ;
# else
char * err = SSLerrmessage ( ERR_get_error ( ) ) ;
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " SSL library does not support CRL certificates (file \" %s \" ) \n " ) ,
fnbuf ) ;
SSLerrfree ( err ) ;
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
return - 1 ;
# endif
}
/* if not found, silently ignore; we do not require CRL */
}
# ifdef ENABLE_THREAD_SAFETY
pthread_mutex_unlock ( & ssl_config_mutex ) ;
# endif
if ( have_rootcert )
SSL_set_verify ( conn - > ssl , SSL_VERIFY_PEER , verify_cb ) ;
}
else
{
/*
* stat ( ) failed ; assume root file doesn ' t exist . If sslmode is
* verify - ca or verify - full , this is an error . Otherwise , continue
* without performing any server cert verification .
*/
if ( conn - > sslmode [ 0 ] = = ' v ' ) /* "verify-ca" or "verify-full" */
{
/*
* The only way to reach here with an empty filename is if
* pqGetHomeDirectory failed . That ' s a sufficiently unusual case
* that it seems worth having a specialized error message for it .
*/
if ( fnbuf [ 0 ] = = ' \0 ' )
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " could not get home directory to locate root certificate file \n "
" Either provide the file or change sslmode to disable server certificate verification. \n " ) ) ;
else
printfPQExpBuffer ( & conn - > errorMessage ,
libpq_gettext ( " root certificate file \" %s \" does not exist \n "
" Either provide the file or change sslmode to disable server certificate verification. \n " ) , fnbuf ) ;
return - 1 ;
}
}
/*
* If the OpenSSL version used supports it ( from 1.0 .0 on ) and the user