Skip to content

Commit d12429b

Browse files
committed
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.
1 parent a292115 commit d12429b

File tree

3 files changed

+153
-43
lines changed

3 files changed

+153
-43
lines changed

libmariadb/ma_secure.c

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ static SSL_CTX *SSL_context= NULL;
3232
#define MAX_SSL_ERR_LEN 100
3333

3434
extern pthread_mutex_t LOCK_ssl_config;
35-
static pthread_mutex_t *LOCK_crypto;
35+
static pthread_mutex_t *LOCK_crypto= NULL;
3636

37-
/*
37+
/*
3838
SSL error handling
3939
*/
4040
static void my_SSL_error(MYSQL *mysql)
@@ -70,21 +70,41 @@ static void my_SSL_error(MYSQL *mysql)
7070
Crypto call back functions will be
7171
set during ssl_initialization
7272
*/
73-
static unsigned long my_cb_threadid(void)
73+
static void my_cb_threadid(CRYPTO_THREADID *id)
7474
{
75-
/* cast pthread_t to unsigned long */
76-
return (unsigned long) pthread_self();
75+
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
7776
}
7877

79-
static void
80-
my_cb_locking(int mode, int n, const char *file, int line)
78+
static void my_cb_locking(int mode, int n, const char *file, int line)
8179
{
82-
if (mode & CRYPTO_LOCK)
83-
pthread_mutex_lock(&LOCK_crypto[n]);
84-
else
85-
pthread_mutex_unlock(&LOCK_crypto[n]);
80+
if (mode & CRYPTO_LOCK)
81+
pthread_mutex_lock(&LOCK_crypto[n]);
82+
else
83+
pthread_mutex_unlock(&LOCK_crypto[n]);
84+
}
85+
86+
87+
static int ssl_thread_init()
88+
{
89+
int i, max= CRYPTO_num_locks();
90+
91+
if (LOCK_crypto == NULL)
92+
{
93+
if (!(LOCK_crypto=
94+
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
95+
return 1;
96+
97+
for (i=0; i < max; i++)
98+
pthread_mutex_init(&LOCK_crypto[i], NULL);
99+
}
100+
101+
CRYPTO_THREADID_set_callback(my_cb_threadid);
102+
CRYPTO_set_locking_callback(my_cb_locking);
103+
104+
return 0;
86105
}
87106

107+
88108
/*
89109
Initializes SSL and allocate global
90110
context SSL_context
@@ -103,30 +123,15 @@ int my_ssl_start(MYSQL *mysql)
103123
DBUG_ENTER("my_ssl_start");
104124
/* lock mutex to prevent multiple initialization */
105125
pthread_mutex_lock(&LOCK_ssl_config);
106-
107126
if (!my_ssl_initialized)
108127
{
109-
if (!(LOCK_crypto=
110-
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) *
111-
CRYPTO_num_locks(), MYF(0))))
112-
{
113-
rc= 1;
128+
if (ssl_thread_init())
114129
goto end;
115-
} else
116-
{
117-
int i;
130+
SSL_library_init();
118131

119-
for (i=0; i < CRYPTO_num_locks(); i++)
120-
pthread_mutex_init(&LOCK_crypto[i], NULL);
121-
CRYPTO_set_id_callback(my_cb_threadid);
122-
CRYPTO_set_locking_callback(my_cb_locking);
123-
}
124132
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
125133
OPENSSL_config(NULL);
126134
#endif
127-
128-
/* always returns 1, so we can discard return code */
129-
SSL_library_init();
130135
/* load errors */
131136
SSL_load_error_strings();
132137
/* digests and ciphers */
@@ -165,12 +170,14 @@ void my_ssl_end()
165170
{
166171
int i;
167172
CRYPTO_set_locking_callback(NULL);
168-
CRYPTO_set_id_callback(NULL);
173+
CRYPTO_set_id_callback(NULL);
169174

170175
for (i=0; i < CRYPTO_num_locks(); i++)
171176
pthread_mutex_destroy(&LOCK_crypto[i]);
172177

173178
my_free((gptr)LOCK_crypto, MYF(0));
179+
LOCK_crypto= NULL;
180+
174181
if (SSL_context)
175182
{
176183
SSL_CTX_free(SSL_context);
@@ -196,7 +203,9 @@ void my_ssl_end()
196203
*/
197204
static int my_ssl_set_certs(MYSQL *mysql)
198205
{
199-
char *key_file= mysql->options.ssl_key ? mysql->options.ssl_key : mysql->options.ssl_cert;
206+
char *certfile= mysql->options.ssl_cert,
207+
*keyfile= mysql->options.ssl_key;
208+
200209
DBUG_ENTER("my_ssl_set_certs");
201210

202211
/* Make sure that ssl was allocated and
@@ -220,21 +229,26 @@ static int my_ssl_set_certs(MYSQL *mysql)
220229
goto error;
221230
}
222231

232+
if (keyfile && !certfile)
233+
certfile= keyfile;
234+
if (certfile && !keyfile)
235+
keyfile= certfile;
236+
223237
/* set cert */
224-
if (mysql->options.ssl_cert && mysql->options.ssl_cert[0] != 0)
225-
if (SSL_CTX_use_certificate_chain_file(SSL_context, mysql->options.ssl_cert) <= 0)
238+
if (certfile && certfile[0] != 0)
239+
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
226240
goto error;
227241

228-
/* set key */
229-
if (key_file)
242+
/* set key */
243+
if (keyfile && keyfile[0])
230244
{
231-
if (SSL_CTX_use_PrivateKey_file(SSL_context, key_file, SSL_FILETYPE_PEM) <= 0)
232-
goto error;
233-
234-
/* verify key */
235-
if (!SSL_CTX_check_private_key(SSL_context))
245+
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
236246
goto error;
237247
}
248+
/* verify key */
249+
if (certfile && !SSL_CTX_check_private_key(SSL_context))
250+
goto error;
251+
238252
if (mysql->options.extension &&
239253
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
240254
{
@@ -318,6 +332,7 @@ SSL *my_ssl_init(MYSQL *mysql)
318332
if (!my_ssl_initialized)
319333
my_ssl_start(mysql);
320334

335+
pthread_mutex_lock(&LOCK_ssl_config);
321336
if (my_ssl_set_certs(mysql))
322337
goto error;
323338

@@ -333,8 +348,10 @@ SSL *my_ssl_init(MYSQL *mysql)
333348
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
334349
SSL_CTX_set_verify_depth(SSL_context, 1);
335350

351+
pthread_mutex_unlock(&LOCK_ssl_config);
336352
DBUG_RETURN(ssl);
337353
error:
354+
pthread_mutex_unlock(&LOCK_ssl_config);
338355
if (ssl)
339356
SSL_free(ssl);
340357
DBUG_RETURN(NULL);
@@ -503,7 +520,10 @@ int my_ssl_close(Vio *vio)
503520
int i, rc;
504521
DBUG_ENTER("my_ssl_close");
505522

506-
523+
if (!vio || !vio->ssl)
524+
DBUG_RETURN(1);
525+
526+
SSL_set_quiet_shutdown(vio->ssl, 1);
507527
/* 2 x pending + 2 * data = 4 */
508528
for (i=0; i < 4; i++)
509529
if ((rc= SSL_shutdown(vio->ssl)))

unittest/libmariadb/certs/create_certs.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ openssl rsa -in client-key-enc.pem -out client-key.pem \
1313
-passin pass:qwerty -passout pass:
1414

1515
cat server-cert.pem client-cert.pem > ca.pem
16+
17+
cat client-key.pem client-cert.pem ca.pem > combined.pem

unittest/libmariadb/ssl.c.in

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ static int test_conc95(MYSQL *my)
104104
return SKIP;
105105

106106
rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'");
107-
check_mysql_rc(rc, my);
108107
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509");
109108
check_mysql_rc(rc, my);
110109
rc= mysql_query(my, "FLUSH PRIVILEGES");
@@ -484,6 +483,96 @@ static int test_bug62743(MYSQL *my)
484483
return OK;
485484
}
486485

486+
#ifndef _WIN32
487+
int thread_conc102(void)
488+
#else
489+
DWORD WINAPI thread_conc102(void)
490+
#endif
491+
{
492+
MYSQL *mysql;
493+
int rc;
494+
MYSQL_RES *res;
495+
mysql_thread_init();
496+
mysql= mysql_init(NULL);
497+
498+
mysql_ssl_set(mysql, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem",
499+
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem",
500+
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/combined.pem", 0, 0);
501+
502+
if(!mysql_real_connect(mysql, hostname, username, password, schema,
503+
port, socketname, 0))
504+
{
505+
diag(">Error: %s", mysql_error(mysql));
506+
goto end;
507+
}
508+
if (!mysql_get_ssl_cipher(mysql))
509+
{
510+
diag("Error: No ssl connection");
511+
goto end;
512+
}
513+
pthread_mutex_lock(&LOCK_test);
514+
rc= mysql_query(mysql, "UPDATE t_conc102 SET a=a+1");
515+
check_mysql_rc(rc, mysql);
516+
pthread_mutex_unlock(&LOCK_test);
517+
check_mysql_rc(rc, mysql);
518+
if (res= mysql_store_result(mysql))
519+
mysql_free_result(res);
520+
end:
521+
mysql_close(mysql);
522+
mysql_thread_end();
523+
return 0;
524+
}
525+
526+
static int test_conc_102(MYSQL *mysql)
527+
{
528+
529+
int rc;
530+
int i;
531+
MYSQL_ROW row;
532+
MYSQL_RES *res;
533+
#ifndef _WIN32
534+
pthread_t threads[50];
535+
#else
536+
HANDLE hthreads[50];
537+
DWORD threads[50];
538+
#endif
539+
540+
rc= mysql_query(mysql, "DROP TABLE IF EXISTS t_conc102");
541+
check_mysql_rc(rc, mysql);
542+
rc= mysql_query(mysql, "CREATE TABLE t_conc102 ( a int)");
543+
check_mysql_rc(rc, mysql);
544+
rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)");
545+
check_mysql_rc(rc, mysql);
546+
547+
for (i=0; i < 50; i++)
548+
{
549+
#ifndef _WIN32
550+
pthread_create(&threads[i], NULL, (void *)thread_conc102, NULL);
551+
#else
552+
hthreads[i]= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_conc27, NULL, 0, &threads[i]);
553+
if (hthreads[i]==NULL)
554+
diag("error while starting thread");
555+
#endif
556+
}
557+
for (i=0; i < 50; i++)
558+
{
559+
#ifndef _WIN32
560+
pthread_join(threads[i], NULL);
561+
#else
562+
WaitForSingleObject(hthreads[i], INFINITE);
563+
#endif
564+
}
565+
rc= mysql_query(mysql, "SELECT a FROM t_conc102");
566+
check_mysql_rc(rc, mysql);
567+
res= mysql_store_result(mysql);
568+
row= mysql_fetch_row(res);
569+
diag("Found: %s", row[0]);
570+
FAIL_IF(strcmp(row[0], "50") != 0, "Expected 50");
571+
mysql_free_result(res);
572+
return OK;
573+
}
574+
575+
487576
struct my_tests_st my_tests[] = {
488577
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
489578
{"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL},
@@ -497,9 +586,8 @@ struct my_tests_st my_tests[] = {
497586
{"test_phpbug51647", test_phpbug51647, TEST_CONNECTION_NONE, 0, NULL, NULL},
498587
{"test_ssl_cipher", test_ssl_cipher, TEST_CONNECTION_NONE, 0, NULL, NULL},
499588
{"test_multi_ssl_connections", test_multi_ssl_connections, TEST_CONNECTION_NONE, 0, NULL, NULL},
500-
#ifndef WIN32
589+
{"test_conc_102", test_conc_102, TEST_CONNECTION_NEW, 0, NULL, NULL},
501590
{"test_ssl_threads", test_ssl_threads, TEST_CONNECTION_NEW, 0, NULL, NULL},
502-
#endif
503591

504592
{NULL, NULL, 0, 0, NULL, NULL}
505593
};

0 commit comments

Comments
 (0)