1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-08 14:02:17 +03:00

Fix for CONC-102:

Since we use one SSL context per library instance (which might be shared by several threads) we need to protect
certification loading by a mutex.
This commit is contained in:
Georg Richter
2014-10-12 05:35:43 +02:00
parent a292115104
commit d12429bf99
3 changed files with 153 additions and 43 deletions

View File

@@ -32,7 +32,7 @@ static SSL_CTX *SSL_context= NULL;
#define MAX_SSL_ERR_LEN 100 #define MAX_SSL_ERR_LEN 100
extern pthread_mutex_t LOCK_ssl_config; extern pthread_mutex_t LOCK_ssl_config;
static pthread_mutex_t *LOCK_crypto; static pthread_mutex_t *LOCK_crypto= NULL;
/* /*
SSL error handling SSL error handling
@@ -70,14 +70,12 @@ static void my_SSL_error(MYSQL *mysql)
Crypto call back functions will be Crypto call back functions will be
set during ssl_initialization set during ssl_initialization
*/ */
static unsigned long my_cb_threadid(void) static void my_cb_threadid(CRYPTO_THREADID *id)
{ {
/* cast pthread_t to unsigned long */ CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
return (unsigned long) pthread_self();
} }
static void static void my_cb_locking(int mode, int n, const char *file, int line)
my_cb_locking(int mode, int n, const char *file, int line)
{ {
if (mode & CRYPTO_LOCK) if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&LOCK_crypto[n]); pthread_mutex_lock(&LOCK_crypto[n]);
@@ -85,6 +83,28 @@ my_cb_locking(int mode, int n, const char *file, int line)
pthread_mutex_unlock(&LOCK_crypto[n]); pthread_mutex_unlock(&LOCK_crypto[n]);
} }
static int ssl_thread_init()
{
int i, max= CRYPTO_num_locks();
if (LOCK_crypto == NULL)
{
if (!(LOCK_crypto=
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
return 1;
for (i=0; i < max; i++)
pthread_mutex_init(&LOCK_crypto[i], NULL);
}
CRYPTO_THREADID_set_callback(my_cb_threadid);
CRYPTO_set_locking_callback(my_cb_locking);
return 0;
}
/* /*
Initializes SSL and allocate global Initializes SSL and allocate global
context SSL_context context SSL_context
@@ -103,30 +123,15 @@ int my_ssl_start(MYSQL *mysql)
DBUG_ENTER("my_ssl_start"); DBUG_ENTER("my_ssl_start");
/* lock mutex to prevent multiple initialization */ /* lock mutex to prevent multiple initialization */
pthread_mutex_lock(&LOCK_ssl_config); pthread_mutex_lock(&LOCK_ssl_config);
if (!my_ssl_initialized) if (!my_ssl_initialized)
{ {
if (!(LOCK_crypto= if (ssl_thread_init())
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) *
CRYPTO_num_locks(), MYF(0))))
{
rc= 1;
goto end; goto end;
} else SSL_library_init();
{
int i;
for (i=0; i < CRYPTO_num_locks(); i++)
pthread_mutex_init(&LOCK_crypto[i], NULL);
CRYPTO_set_id_callback(my_cb_threadid);
CRYPTO_set_locking_callback(my_cb_locking);
}
#if SSLEAY_VERSION_NUMBER >= 0x00907000L #if SSLEAY_VERSION_NUMBER >= 0x00907000L
OPENSSL_config(NULL); OPENSSL_config(NULL);
#endif #endif
/* always returns 1, so we can discard return code */
SSL_library_init();
/* load errors */ /* load errors */
SSL_load_error_strings(); SSL_load_error_strings();
/* digests and ciphers */ /* digests and ciphers */
@@ -171,6 +176,8 @@ void my_ssl_end()
pthread_mutex_destroy(&LOCK_crypto[i]); pthread_mutex_destroy(&LOCK_crypto[i]);
my_free((gptr)LOCK_crypto, MYF(0)); my_free((gptr)LOCK_crypto, MYF(0));
LOCK_crypto= NULL;
if (SSL_context) if (SSL_context)
{ {
SSL_CTX_free(SSL_context); SSL_CTX_free(SSL_context);
@@ -196,7 +203,9 @@ void my_ssl_end()
*/ */
static int my_ssl_set_certs(MYSQL *mysql) static int my_ssl_set_certs(MYSQL *mysql)
{ {
char *key_file= mysql->options.ssl_key ? mysql->options.ssl_key : mysql->options.ssl_cert; char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
DBUG_ENTER("my_ssl_set_certs"); DBUG_ENTER("my_ssl_set_certs");
/* Make sure that ssl was allocated and /* Make sure that ssl was allocated and
@@ -220,21 +229,26 @@ static int my_ssl_set_certs(MYSQL *mysql)
goto error; goto error;
} }
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set cert */ /* set cert */
if (mysql->options.ssl_cert && mysql->options.ssl_cert[0] != 0) if (certfile && certfile[0] != 0)
if (SSL_CTX_use_certificate_chain_file(SSL_context, mysql->options.ssl_cert) <= 0) if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
goto error; goto error;
/* set key */ /* set key */
if (key_file) if (keyfile && keyfile[0])
{ {
if (SSL_CTX_use_PrivateKey_file(SSL_context, key_file, SSL_FILETYPE_PEM) <= 0) if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
goto error;
/* verify key */
if (!SSL_CTX_check_private_key(SSL_context))
goto error; goto error;
} }
/* verify key */
if (certfile && !SSL_CTX_check_private_key(SSL_context))
goto error;
if (mysql->options.extension && if (mysql->options.extension &&
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath)) (mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
{ {
@@ -318,6 +332,7 @@ SSL *my_ssl_init(MYSQL *mysql)
if (!my_ssl_initialized) if (!my_ssl_initialized)
my_ssl_start(mysql); my_ssl_start(mysql);
pthread_mutex_lock(&LOCK_ssl_config);
if (my_ssl_set_certs(mysql)) if (my_ssl_set_certs(mysql))
goto error; goto error;
@@ -333,8 +348,10 @@ SSL *my_ssl_init(MYSQL *mysql)
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback); SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
SSL_CTX_set_verify_depth(SSL_context, 1); SSL_CTX_set_verify_depth(SSL_context, 1);
pthread_mutex_unlock(&LOCK_ssl_config);
DBUG_RETURN(ssl); DBUG_RETURN(ssl);
error: error:
pthread_mutex_unlock(&LOCK_ssl_config);
if (ssl) if (ssl)
SSL_free(ssl); SSL_free(ssl);
DBUG_RETURN(NULL); DBUG_RETURN(NULL);
@@ -503,7 +520,10 @@ int my_ssl_close(Vio *vio)
int i, rc; int i, rc;
DBUG_ENTER("my_ssl_close"); DBUG_ENTER("my_ssl_close");
if (!vio || !vio->ssl)
DBUG_RETURN(1);
SSL_set_quiet_shutdown(vio->ssl, 1);
/* 2 x pending + 2 * data = 4 */ /* 2 x pending + 2 * data = 4 */
for (i=0; i < 4; i++) for (i=0; i < 4; i++)
if ((rc= SSL_shutdown(vio->ssl))) if ((rc= SSL_shutdown(vio->ssl)))

View File

@@ -13,3 +13,5 @@ openssl rsa -in client-key-enc.pem -out client-key.pem \
-passin pass:qwerty -passout pass: -passin pass:qwerty -passout pass:
cat server-cert.pem client-cert.pem > ca.pem cat server-cert.pem client-cert.pem > ca.pem
cat client-key.pem client-cert.pem ca.pem > combined.pem

View File

@@ -104,7 +104,6 @@ static int test_conc95(MYSQL *my)
return SKIP; return SKIP;
rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'"); rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'");
check_mysql_rc(rc, my);
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509"); rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509");
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
rc= mysql_query(my, "FLUSH PRIVILEGES"); rc= mysql_query(my, "FLUSH PRIVILEGES");
@@ -484,6 +483,96 @@ static int test_bug62743(MYSQL *my)
return OK; return OK;
} }
#ifndef _WIN32
int thread_conc102(void)
#else
DWORD WINAPI thread_conc102(void)
#endif
{
MYSQL *mysql;
int rc;
MYSQL_RES *res;
mysql_thread_init();
mysql= mysql_init(NULL);
mysql_ssl_set(mysql, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem", 0, 0);
if(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0))
{
diag(">Error: %s", mysql_error(mysql));
goto end;
}
if (!mysql_get_ssl_cipher(mysql))
{
diag("Error: No ssl connection");
goto end;
}
pthread_mutex_lock(&LOCK_test);
rc= mysql_query(mysql, "UPDATE t_conc102 SET a=a+1");
check_mysql_rc(rc, mysql);
pthread_mutex_unlock(&LOCK_test);
check_mysql_rc(rc, mysql);
if (res= mysql_store_result(mysql))
mysql_free_result(res);
end:
mysql_close(mysql);
mysql_thread_end();
return 0;
}
static int test_conc_102(MYSQL *mysql)
{
int rc;
int i;
MYSQL_ROW row;
MYSQL_RES *res;
#ifndef _WIN32
pthread_t threads[50];
#else
HANDLE hthreads[50];
DWORD threads[50];
#endif
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t_conc102");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "CREATE TABLE t_conc102 ( a int)");
check_mysql_rc(rc, mysql);
rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)");
check_mysql_rc(rc, mysql);
for (i=0; i < 50; i++)
{
#ifndef _WIN32
pthread_create(&threads[i], NULL, (void *)thread_conc102, NULL);
#else
hthreads[i]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_conc27, NULL, 0, &threads[i]);
if (hthreads[i]==NULL)
diag("error while starting thread");
#endif
}
for (i=0; i < 50; i++)
{
#ifndef _WIN32
pthread_join(threads[i], NULL);
#else
WaitForSingleObject(hthreads[i], INFINITE);
#endif
}
rc= mysql_query(mysql, "SELECT a FROM t_conc102");
check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql);
row= mysql_fetch_row(res);
diag("Found: %s", row[0]);
FAIL_IF(strcmp(row[0], "50") != 0, "Expected 50");
mysql_free_result(res);
return OK;
}
struct my_tests_st my_tests[] = { struct my_tests_st my_tests[] = {
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL},
@@ -497,9 +586,8 @@ struct my_tests_st my_tests[] = {
{"test_phpbug51647", test_phpbug51647, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_phpbug51647", test_phpbug51647, TEST_CONNECTION_NONE, 0, NULL, NULL},
{"test_ssl_cipher", test_ssl_cipher, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_ssl_cipher", test_ssl_cipher, TEST_CONNECTION_NONE, 0, NULL, NULL},
{"test_multi_ssl_connections", test_multi_ssl_connections, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_multi_ssl_connections", test_multi_ssl_connections, TEST_CONNECTION_NONE, 0, NULL, NULL},
#ifndef WIN32 {"test_conc_102", test_conc_102, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL},
#endif
{NULL, NULL, 0, 0, NULL, NULL} {NULL, NULL, 0, 0, NULL, NULL}
}; };