1 | /************************************************************************************ |
2 | Copyright (C) 2012 Monty Program AB |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2 of the License, or (at your option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Library General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public |
15 | License along with this library; if not see <http://www.gnu.org/licenses> |
16 | or write to the Free Software Foundation, Inc., |
17 | 51 Franklin St., Fifth Floor, Boston, MA 02110, USA |
18 | |
19 | *************************************************************************************/ |
20 | #include <ma_global.h> |
21 | #include <ma_sys.h> |
22 | #include <ma_common.h> |
23 | #include <ma_pvio.h> |
24 | #include <errmsg.h> |
25 | #include <string.h> |
26 | #include <mysql/client_plugin.h> |
27 | #include <string.h> |
28 | #include <openssl/ssl.h> /* SSL and SSL_CTX */ |
29 | #include <openssl/err.h> /* error reporting */ |
30 | #include <openssl/conf.h> |
31 | #include <openssl/md4.h> |
32 | |
33 | #if defined(_WIN32) && !defined(_OPENSSL_Applink) && defined(HAVE_OPENSSL_APPLINK_C) |
34 | #include <openssl/applink.c> |
35 | #endif |
36 | |
37 | #if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER) |
38 | #include <openssl/x509v3.h> |
39 | #define HAVE_OPENSSL_CHECK_HOST 1 |
40 | #endif |
41 | |
42 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER) |
43 | #define HAVE_OPENSSL_1_1_API |
44 | #endif |
45 | |
46 | #if OPENSSL_VERSION_NUMBER < 0x10000000L |
47 | #define SSL_OP_NO_TLSv1_1 0L |
48 | #define SSL_OP_NO_TLSv1_2 0L |
49 | #define CRYPTO_THREADID_set_callback CRYPTO_set_id_callback |
50 | #define CRYPTO_THREADID_get_callback CRYPTO_get_id_callback |
51 | #endif |
52 | |
53 | #ifdef HAVE_TLS_SESSION_CACHE |
54 | #undef HAVE_TLS_SESSION_CACHE |
55 | #endif |
56 | #if defined(OPENSSL_USE_BIOMETHOD) |
57 | #undef OPENSSL_USE_BIOMETHOD |
58 | #endif |
59 | #ifndef HAVE_OPENSSL_DEFAULT |
60 | #include <memory.h> |
61 | #define ma_malloc(A,B) malloc((A)) |
62 | #undef ma_free |
63 | #define ma_free(A) free((A)) |
64 | #define ma_snprintf snprintf |
65 | #define ma_vsnprintf vsnprintf |
66 | #undef SAFE_MUTEX |
67 | #endif |
68 | #include <ma_pthread.h> |
69 | |
70 | #include <mariadb_async.h> |
71 | #include <ma_context.h> |
72 | |
73 | extern my_bool ma_tls_initialized; |
74 | extern unsigned int mariadb_deinitialize_ssl; |
75 | |
76 | #define MAX_SSL_ERR_LEN 100 |
77 | char tls_library_version[TLS_VERSION_LENGTH]; |
78 | |
79 | static pthread_mutex_t LOCK_openssl_config; |
80 | #ifndef HAVE_OPENSSL_1_1_API |
81 | static pthread_mutex_t *LOCK_crypto= NULL; |
82 | #endif |
83 | #if defined(OPENSSL_USE_BIOMETHOD) |
84 | static int ma_bio_read(BIO *h, char *buf, int size); |
85 | static int ma_bio_write(BIO *h, const char *buf, int size); |
86 | static BIO_METHOD ma_BIO_method; |
87 | #endif |
88 | |
89 | |
90 | static long ma_tls_version_options(const char *version) |
91 | { |
92 | long protocol_options, |
93 | disable_all_protocols; |
94 | |
95 | protocol_options= disable_all_protocols= |
96 | SSL_OP_NO_SSLv2 | |
97 | SSL_OP_NO_SSLv3 | |
98 | SSL_OP_NO_TLSv1 | |
99 | SSL_OP_NO_TLSv1_1 | |
100 | SSL_OP_NO_TLSv1_2 |
101 | #ifdef TLS1_3_VERSION |
102 | | SSL_OP_NO_TLSv1_3 |
103 | #endif |
104 | ; |
105 | |
106 | if (!version) |
107 | return 0; |
108 | |
109 | if (strstr(version, "TLSv1.0" )) |
110 | protocol_options&= ~SSL_OP_NO_TLSv1; |
111 | if (strstr(version, "TLSv1.1" )) |
112 | protocol_options&= ~SSL_OP_NO_TLSv1_1; |
113 | if (strstr(version, "TLSv1.2" )) |
114 | protocol_options&= ~SSL_OP_NO_TLSv1_2; |
115 | #ifdef TLS1_3_VERSION |
116 | if (strstr(version, "TLSv1.3" )) |
117 | protocol_options&= ~SSL_OP_NO_TLSv1_3; |
118 | #endif |
119 | |
120 | if (protocol_options != disable_all_protocols) |
121 | return protocol_options; |
122 | return 0; |
123 | } |
124 | |
125 | static void ma_tls_set_error(MYSQL *mysql) |
126 | { |
127 | ulong ssl_errno= ERR_get_error(); |
128 | char ssl_error[MAX_SSL_ERR_LEN]; |
129 | const char *ssl_error_reason; |
130 | MARIADB_PVIO *pvio= mysql->net.pvio; |
131 | |
132 | if (!ssl_errno) |
133 | { |
134 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error" ); |
135 | return; |
136 | } |
137 | if ((ssl_error_reason= ERR_reason_error_string(ssl_errno))) |
138 | { |
139 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
140 | 0, ssl_error_reason); |
141 | return; |
142 | } |
143 | snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu" , ssl_errno); |
144 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, ssl_error); |
145 | return; |
146 | } |
147 | |
148 | #ifndef HAVE_OPENSSL_1_1_API |
149 | /* |
150 | thread safe callbacks for OpenSSL |
151 | Crypto call back functions will be |
152 | set during ssl_initialization |
153 | */ |
154 | #if OPENSSL_VERSION_NUMBER < 0x10000000L |
155 | static unsigned long my_cb_threadid(void) |
156 | { |
157 | /* cast pthread_t to unsigned long */ |
158 | return (unsigned long) pthread_self(); |
159 | } |
160 | #else |
161 | static void my_cb_threadid(CRYPTO_THREADID *id) |
162 | { |
163 | CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self()); |
164 | } |
165 | #endif |
166 | #endif |
167 | |
168 | #ifdef HAVE_TLS_SESSION_CACHE |
169 | typedef struct st_ma_tls_session { |
170 | char md4_hash[17]; |
171 | SSL_SESSION *session; |
172 | } MA_SSL_SESSION; |
173 | |
174 | MA_SSL_SESSION *ma_tls_sessions= NULL; |
175 | int ma_tls_session_cache_size= 128; |
176 | |
177 | static char *ma_md4_hash(const char *host, const char *user, unsigned int port, char *md4) |
178 | { |
179 | char buffer[195]; /* MAX_USERNAME_LEN + MAX_HOST_NAME_LEN + 2 + 5 */ |
180 | snprintf(buffer, 194, "%s@%s:%d" , user ? user : "" , host, port); |
181 | buffer[194]= 0; |
182 | MD4((unsigned char *)buffer, strlen(buffer), (unsigned char *)md4); |
183 | return md4; |
184 | } |
185 | |
186 | MA_SSL_SESSION *ma_tls_get_session(MYSQL *mysql) |
187 | { |
188 | char md4[17]; |
189 | int i; |
190 | |
191 | if (!ma_tls_sessions) |
192 | return NULL; |
193 | |
194 | memset(md4, 0, 16); |
195 | ma_md4_hash(mysql->host, mysql->user, mysql->port, md4); |
196 | for (i=0; i < ma_tls_session_cache_size; i++) |
197 | { |
198 | if (ma_tls_sessions[i].session && |
199 | !strncmp(ma_tls_sessions[i].md4_hash, md4, 16)) |
200 | { |
201 | return &ma_tls_sessions[i]; |
202 | } |
203 | } |
204 | return NULL; |
205 | } |
206 | |
207 | |
208 | #if OPENSSL_USE_BIOMETHOD |
209 | static int ma_bio_read(BIO *bio, char *buf, int size) |
210 | { |
211 | MARIADB_PVIO *pvio= (MARIADB_PVIO *)bio->ptr; |
212 | size_t rc; |
213 | |
214 | rc= pvio->methods->read(pvio, buf, (size_t)size); |
215 | BIO_clear_retry_flags(bio); |
216 | return (int)rc; |
217 | } |
218 | static int ma_bio_write(BIO *bio, const char *buf, int size) |
219 | { |
220 | MARIADB_PVIO *pvio= (MARIADB_PVIO *)bio->ptr; |
221 | size_t rc; |
222 | |
223 | rc= pvio->methods->write(pvio, buf, (size_t)size); |
224 | BIO_clear_retry_flags(bio); |
225 | return (int)rc; |
226 | } |
227 | #endif |
228 | |
229 | static int ma_tls_session_cb(SSL *ssl, SSL_SESSION *session) |
230 | { |
231 | MYSQL *mysql; |
232 | MA_SSL_SESSION *stored_session; |
233 | int i; |
234 | |
235 | mysql= (MYSQL *)SSL_get_app_data(ssl); |
236 | |
237 | /* check if we already stored session key */ |
238 | if ((stored_session= ma_tls_get_session(mysql))) |
239 | { |
240 | SSL_SESSION_free(stored_session->session); |
241 | stored_session->session= session; |
242 | return 1; |
243 | } |
244 | |
245 | for (i=0; i < ma_tls_session_cache_size; i++) |
246 | { |
247 | if (!ma_tls_sessions[i].session) |
248 | { |
249 | ma_md4_hash(mysql->host, mysql->user, mysql->port, ma_tls_sessions[i].md4_hash); |
250 | ma_tls_sessions[i].session= session; |
251 | } |
252 | return 1; |
253 | } |
254 | return 0; |
255 | } |
256 | |
257 | static void ma_tls_remove_session_cb(SSL_CTX* ctx __attribute__((unused)), |
258 | SSL_SESSION* session) |
259 | { |
260 | int i; |
261 | for (i=0; i < ma_tls_session_cache_size; i++) |
262 | if (session == ma_tls_sessions[i].session) |
263 | { |
264 | ma_tls_sessions[i].md4_hash[0]= 0; |
265 | SSL_SESSION_free(ma_tls_sessions[i].session); |
266 | ma_tls_sessions[i].session= NULL; |
267 | } |
268 | } |
269 | #endif |
270 | |
271 | #ifndef HAVE_OPENSSL_1_1_API |
272 | static void my_cb_locking(int mode, int n, |
273 | const char *file __attribute__((unused)), |
274 | int line __attribute__((unused))) |
275 | { |
276 | if (mode & CRYPTO_LOCK) |
277 | pthread_mutex_lock(&LOCK_crypto[n]); |
278 | else |
279 | pthread_mutex_unlock(&LOCK_crypto[n]); |
280 | } |
281 | |
282 | static int ssl_thread_init() |
283 | { |
284 | if (!CRYPTO_THREADID_get_callback() |
285 | #ifndef OPENSSL_NO_DEPRECATED |
286 | && !CRYPTO_get_id_callback() |
287 | #endif |
288 | ) |
289 | { |
290 | int i, max= CRYPTO_num_locks(); |
291 | |
292 | if (LOCK_crypto == NULL) |
293 | { |
294 | if (!(LOCK_crypto= |
295 | (pthread_mutex_t *)ma_malloc(sizeof(pthread_mutex_t) * max, MYF(0)))) |
296 | return 1; |
297 | |
298 | for (i=0; i < max; i++) |
299 | pthread_mutex_init(&LOCK_crypto[i], NULL); |
300 | } |
301 | CRYPTO_set_locking_callback(my_cb_locking); |
302 | CRYPTO_THREADID_set_callback(my_cb_threadid); |
303 | } |
304 | return 0; |
305 | } |
306 | #endif |
307 | |
308 | #if defined(_WIN32) || !defined(DISABLE_SIGPIPE) |
309 | #define disable_sigpipe() |
310 | #else |
311 | #include <signal.h> |
312 | static void ma_sigpipe_handler() |
313 | { |
314 | } |
315 | |
316 | static void disable_sigpipe() |
317 | { |
318 | struct sigaction old_handler, new_handler={NULL}; |
319 | if (!sigaction (SIGPIPE, NULL, &old_handler) && |
320 | !old_handler.sa_handler) |
321 | { |
322 | new_handler.sa_handler= ma_sigpipe_handler; |
323 | new_handler.sa_flags= 0; |
324 | if (!sigemptyset(&new_handler.sa_mask)) |
325 | sigaction(SIGPIPE, &new_handler, NULL); |
326 | } |
327 | } |
328 | #endif |
329 | |
330 | /* |
331 | Initializes SSL |
332 | |
333 | SYNOPSIS |
334 | my_ssl_start |
335 | mysql connection handle |
336 | |
337 | RETURN VALUES |
338 | 0 success |
339 | 1 error |
340 | */ |
341 | int ma_tls_start(char *errmsg __attribute__((unused)), size_t errmsg_len __attribute__((unused))) |
342 | { |
343 | int rc= 1; |
344 | char *p; |
345 | if (ma_tls_initialized) |
346 | return 0; |
347 | |
348 | /* lock mutex to prevent multiple initialization */ |
349 | pthread_mutex_init(&LOCK_openssl_config, NULL); |
350 | pthread_mutex_lock(&LOCK_openssl_config); |
351 | #ifdef HAVE_OPENSSL_1_1_API |
352 | if (!OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL)) |
353 | goto end; |
354 | #else |
355 | if (ssl_thread_init()) |
356 | { |
357 | strncpy(errmsg, "Not enough memory" , errmsg_len); |
358 | goto end; |
359 | } |
360 | SSL_library_init(); |
361 | #if SSLEAY_VERSION_NUMBER >= 0x00907000L |
362 | OPENSSL_config(NULL); |
363 | #endif |
364 | #endif |
365 | #ifndef HAVE_OPENSSL_1_1_API |
366 | /* load errors */ |
367 | SSL_load_error_strings(); |
368 | /* digests and ciphers */ |
369 | OpenSSL_add_all_algorithms(); |
370 | #endif |
371 | disable_sigpipe(); |
372 | #ifdef OPENSSL_USE_BIOMETHOD |
373 | memcpy(&ma_BIO_method, BIO_s_socket(), sizeof(BIO_METHOD)); |
374 | ma_BIO_method.bread= ma_bio_read; |
375 | ma_BIO_method.bwrite= ma_bio_write; |
376 | #endif |
377 | snprintf(tls_library_version, TLS_VERSION_LENGTH - 1, "%s" , |
378 | #if defined(LIBRESSL_VERSION_NUMBER) || !defined(HAVE_OPENSSL_1_1_API) |
379 | SSLeay_version(SSLEAY_VERSION)); |
380 | #else |
381 | OpenSSL_version(OPENSSL_VERSION)); |
382 | #endif |
383 | /* remove date from version */ |
384 | if ((p= strstr(tls_library_version, " " ))) |
385 | *p= 0; |
386 | rc= 0; |
387 | ma_tls_initialized= TRUE; |
388 | end: |
389 | pthread_mutex_unlock(&LOCK_openssl_config); |
390 | return rc; |
391 | } |
392 | |
393 | /* |
394 | Release SSL and free resources |
395 | Will be automatically executed by |
396 | mysql_server_end() function |
397 | |
398 | SYNOPSIS |
399 | my_ssl_end() |
400 | void |
401 | |
402 | RETURN VALUES |
403 | void |
404 | */ |
405 | void ma_tls_end() |
406 | { |
407 | if (ma_tls_initialized) |
408 | { |
409 | pthread_mutex_lock(&LOCK_openssl_config); |
410 | #ifndef HAVE_OPENSSL_1_1_API |
411 | if (LOCK_crypto) |
412 | { |
413 | int i; |
414 | CRYPTO_set_locking_callback(NULL); |
415 | CRYPTO_THREADID_set_callback(NULL); |
416 | |
417 | for (i=0; i < CRYPTO_num_locks(); i++) |
418 | pthread_mutex_destroy(&LOCK_crypto[i]); |
419 | ma_free((gptr)LOCK_crypto); |
420 | LOCK_crypto= NULL; |
421 | } |
422 | #endif |
423 | if (mariadb_deinitialize_ssl) |
424 | { |
425 | #ifndef HAVE_OPENSSL_1_1_API |
426 | ERR_remove_thread_state(NULL); |
427 | EVP_cleanup(); |
428 | CRYPTO_cleanup_all_ex_data(); |
429 | ERR_free_strings(); |
430 | CONF_modules_free(); |
431 | CONF_modules_unload(1); |
432 | #endif |
433 | } |
434 | ma_tls_initialized= FALSE; |
435 | pthread_mutex_unlock(&LOCK_openssl_config); |
436 | pthread_mutex_destroy(&LOCK_openssl_config); |
437 | } |
438 | return; |
439 | } |
440 | |
441 | int ma_tls_get_password(char *buf, int size, |
442 | int rwflag __attribute__((unused)), |
443 | void *userdata) |
444 | { |
445 | memset(buf, 0, size); |
446 | if (userdata) |
447 | strncpy(buf, (char *)userdata, size); |
448 | return (int)strlen(buf); |
449 | } |
450 | |
451 | |
452 | static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl) |
453 | { |
454 | char *certfile= mysql->options.ssl_cert, |
455 | *keyfile= mysql->options.ssl_key; |
456 | char *pw= (mysql->options.extension) ? |
457 | mysql->options.extension->tls_pw : NULL; |
458 | SSL_CTX *ctx= SSL_get_SSL_CTX(ssl); |
459 | |
460 | |
461 | /* add cipher */ |
462 | if ((mysql->options.ssl_cipher && |
463 | mysql->options.ssl_cipher[0] != 0)) |
464 | { |
465 | if(SSL_set_cipher_list(ssl, mysql->options.ssl_cipher) == 0) |
466 | goto error; |
467 | } |
468 | |
469 | /* ca_file and ca_path */ |
470 | if (!SSL_CTX_load_verify_locations(ctx, |
471 | mysql->options.ssl_ca, |
472 | mysql->options.ssl_capath)) |
473 | { |
474 | if (mysql->options.ssl_ca || mysql->options.ssl_capath) |
475 | goto error; |
476 | if (SSL_CTX_set_default_verify_paths(ctx) == 0) |
477 | goto error; |
478 | } |
479 | |
480 | if (keyfile && !certfile) |
481 | certfile= keyfile; |
482 | if (certfile && !keyfile) |
483 | keyfile= certfile; |
484 | |
485 | /* set cert */ |
486 | if (certfile && certfile[0] != 0) |
487 | { |
488 | if (SSL_CTX_use_certificate_chain_file(ctx, certfile) != 1 || |
489 | SSL_use_certificate_file(ssl, certfile, SSL_FILETYPE_PEM) != 1) |
490 | goto error; |
491 | } |
492 | if (keyfile && keyfile[0]) |
493 | { |
494 | FILE *fp; |
495 | if ((fp= fopen(keyfile, "rb" ))) |
496 | { |
497 | EVP_PKEY *key= EVP_PKEY_new(); |
498 | PEM_read_PrivateKey(fp, &key, NULL, pw); |
499 | fclose(fp); |
500 | if (SSL_use_PrivateKey(ssl, key) != 1) |
501 | { |
502 | unsigned long err= ERR_peek_error(); |
503 | EVP_PKEY_free(key); |
504 | if (!(ERR_GET_LIB(err) == ERR_LIB_X509 && |
505 | ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) |
506 | goto error; |
507 | } |
508 | EVP_PKEY_free(key); |
509 | } else { |
510 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
511 | CER(CR_FILE_NOT_FOUND), keyfile); |
512 | return 1; |
513 | } |
514 | } |
515 | /* verify key */ |
516 | if (certfile && !SSL_check_private_key(ssl)) |
517 | goto error; |
518 | |
519 | if (mysql->options.extension && |
520 | (mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath)) |
521 | { |
522 | X509_STORE *certstore; |
523 | |
524 | if ((certstore= SSL_CTX_get_cert_store(ctx))) |
525 | { |
526 | if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl, |
527 | mysql->options.extension->ssl_crlpath) == 0) |
528 | goto error; |
529 | |
530 | X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); |
531 | } |
532 | } |
533 | SSL_CTX_set_verify(ctx, (mysql->options.ssl_ca || mysql->options.ssl_capath)? |
534 | SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); |
535 | return 0; |
536 | |
537 | error: |
538 | ma_tls_set_error(mysql); |
539 | return 1; |
540 | } |
541 | |
542 | void *ma_tls_init(MYSQL *mysql) |
543 | { |
544 | SSL *ssl= NULL; |
545 | SSL_CTX *ctx= NULL; |
546 | long options= SSL_OP_ALL | |
547 | SSL_OP_NO_SSLv2 | |
548 | SSL_OP_NO_SSLv3; |
549 | #ifdef HAVE_TLS_SESSION_CACHE |
550 | MA_SSL_SESSION *session= ma_tls_get_session(mysql); |
551 | #endif |
552 | pthread_mutex_lock(&LOCK_openssl_config); |
553 | |
554 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
555 | if (!(ctx= SSL_CTX_new(TLS_client_method()))) |
556 | #else |
557 | if (!(ctx= SSL_CTX_new(SSLv23_client_method()))) |
558 | #endif |
559 | goto error; |
560 | if (mysql->options.extension) |
561 | options|= ma_tls_version_options(mysql->options.extension->tls_version); |
562 | SSL_CTX_set_options(ctx, options); |
563 | #ifdef HAVE_TLS_SESSION_CACHE |
564 | SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT); |
565 | ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size); |
566 | SSL_CTX_sess_set_new_cb(ctx, ma_tls_session_cb); |
567 | SSL_CTX_sess_set_remove_cb(ctx, ma_tls_remove_session_cb); |
568 | #endif |
569 | |
570 | if (!(ssl= SSL_new(ctx))) |
571 | goto error; |
572 | |
573 | if (ma_tls_set_certs(mysql, ssl)) |
574 | { |
575 | goto error; |
576 | } |
577 | |
578 | if (!SSL_set_app_data(ssl, mysql)) |
579 | goto error; |
580 | |
581 | #ifdef HAVE_TLS_SESSION_CACHE |
582 | if (session) |
583 | SSL_set_session(ssl, session->session); |
584 | #endif |
585 | |
586 | pthread_mutex_unlock(&LOCK_openssl_config); |
587 | return (void *)ssl; |
588 | error: |
589 | pthread_mutex_unlock(&LOCK_openssl_config); |
590 | if (ctx) |
591 | SSL_CTX_free(ctx); |
592 | if (ssl) |
593 | SSL_free(ssl); |
594 | return NULL; |
595 | } |
596 | |
597 | my_bool ma_tls_connect(MARIADB_TLS *ctls) |
598 | { |
599 | SSL *ssl = (SSL *)ctls->ssl; |
600 | my_bool blocking, try_connect= 1; |
601 | MYSQL *mysql; |
602 | MARIADB_PVIO *pvio; |
603 | int rc; |
604 | #ifdef OPENSSL_USE_BIOMETHOD |
605 | BIO_METHOD *bio_method= NULL; |
606 | BIO *bio; |
607 | #endif |
608 | |
609 | mysql= (MYSQL *)SSL_get_app_data(ssl); |
610 | pvio= mysql->net.pvio; |
611 | |
612 | /* Set socket to non blocking if not already set */ |
613 | if (!(blocking= pvio->methods->is_blocking(pvio))) |
614 | pvio->methods->blocking(pvio, FALSE, 0); |
615 | |
616 | SSL_clear(ssl); |
617 | |
618 | #ifdef OPENSSL_USE_BIOMETHOD |
619 | bio= BIO_new(&ma_BIO_method); |
620 | bio->ptr= pvio; |
621 | SSL_set_bio(ssl, bio, bio); |
622 | BIO_set_fd(bio, mysql_get_socket(mysql), BIO_NOCLOSE); |
623 | #else |
624 | SSL_set_fd(ssl, (int)mysql_get_socket(mysql)); |
625 | #endif |
626 | |
627 | while (try_connect && (rc= SSL_connect(ssl)) == -1) |
628 | { |
629 | switch(SSL_get_error(ssl, rc)) { |
630 | case SSL_ERROR_WANT_READ: |
631 | if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1) |
632 | try_connect= 0; |
633 | break; |
634 | case SSL_ERROR_WANT_WRITE: |
635 | if (pvio->methods->wait_io_or_timeout(pvio, TRUE, mysql->options.connect_timeout) < 1) |
636 | try_connect= 0; |
637 | break; |
638 | default: |
639 | try_connect= 0; |
640 | } |
641 | } |
642 | if (rc != 1) |
643 | { |
644 | ma_tls_set_error(mysql); |
645 | /* restore blocking mode */ |
646 | if (!blocking) |
647 | pvio->methods->blocking(pvio, FALSE, 0); |
648 | return 1; |
649 | } |
650 | if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) |
651 | { |
652 | rc= SSL_get_verify_result(ssl); |
653 | if (rc != X509_V_OK) |
654 | { |
655 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
656 | ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc)); |
657 | /* restore blocking mode */ |
658 | if (!blocking) |
659 | pvio->methods->blocking(pvio, FALSE, 0); |
660 | |
661 | return 1; |
662 | } |
663 | } |
664 | pvio->ctls->ssl= ctls->ssl= (void *)ssl; |
665 | |
666 | return 0; |
667 | } |
668 | |
669 | static my_bool |
670 | ma_tls_async_check_result(int res, struct mysql_async_context *b, SSL *ssl) |
671 | { |
672 | int ssl_err; |
673 | b->events_to_wait_for= 0; |
674 | if (res >= 0) |
675 | return 1; |
676 | ssl_err= SSL_get_error(ssl, res); |
677 | if (ssl_err == SSL_ERROR_WANT_READ) |
678 | b->events_to_wait_for|= MYSQL_WAIT_READ; |
679 | else if (ssl_err == SSL_ERROR_WANT_WRITE) |
680 | b->events_to_wait_for|= MYSQL_WAIT_WRITE; |
681 | else |
682 | return 1; |
683 | if (b->suspend_resume_hook) |
684 | (*b->suspend_resume_hook)(TRUE, b->suspend_resume_hook_user_data); |
685 | my_context_yield(&b->async_context); |
686 | if (b->suspend_resume_hook) |
687 | (*b->suspend_resume_hook)(FALSE, b->suspend_resume_hook_user_data); |
688 | return 0; |
689 | } |
690 | |
691 | ssize_t ma_tls_read_async(MARIADB_PVIO *pvio, |
692 | const unsigned char *buffer, |
693 | size_t length) |
694 | { |
695 | int res; |
696 | struct mysql_async_context *b= pvio->mysql->options.extension->async_context; |
697 | MARIADB_TLS *ctls= pvio->ctls; |
698 | |
699 | for (;;) |
700 | { |
701 | res= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length); |
702 | if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl)) |
703 | return res; |
704 | } |
705 | } |
706 | |
707 | ssize_t ma_tls_write_async(MARIADB_PVIO *pvio, |
708 | const unsigned char *buffer, |
709 | size_t length) |
710 | { |
711 | int res; |
712 | struct mysql_async_context *b= pvio->mysql->options.extension->async_context; |
713 | MARIADB_TLS *ctls= pvio->ctls; |
714 | |
715 | for (;;) |
716 | { |
717 | res= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length); |
718 | if (ma_tls_async_check_result(res, b, (SSL *)ctls->ssl)) |
719 | return res; |
720 | } |
721 | } |
722 | |
723 | |
724 | ssize_t ma_tls_read(MARIADB_TLS *ctls, const uchar* buffer, size_t length) |
725 | { |
726 | int rc; |
727 | MARIADB_PVIO *pvio= ctls->pvio; |
728 | |
729 | while ((rc= SSL_read((SSL *)ctls->ssl, (void *)buffer, (int)length)) < 0) |
730 | { |
731 | int error= SSL_get_error((SSL *)ctls->ssl, rc); |
732 | if (error != SSL_ERROR_WANT_READ) |
733 | return rc; |
734 | if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.read_timeout) < 1) |
735 | return rc; |
736 | } |
737 | return rc; |
738 | } |
739 | |
740 | ssize_t ma_tls_write(MARIADB_TLS *ctls, const uchar* buffer, size_t length) |
741 | { |
742 | int rc; |
743 | MARIADB_PVIO *pvio= ctls->pvio; |
744 | |
745 | while ((rc= SSL_write((SSL *)ctls->ssl, (void *)buffer, (int)length)) <= 0) |
746 | { |
747 | int error= SSL_get_error((SSL *)ctls->ssl, rc); |
748 | if (error != SSL_ERROR_WANT_WRITE) |
749 | return rc; |
750 | if (pvio->methods->wait_io_or_timeout(pvio, TRUE, pvio->mysql->options.write_timeout) < 1) |
751 | return rc; |
752 | } |
753 | return rc; |
754 | } |
755 | |
756 | my_bool ma_tls_close(MARIADB_TLS *ctls) |
757 | { |
758 | int i, rc; |
759 | SSL *ssl; |
760 | SSL_CTX *ctx= NULL; |
761 | |
762 | if (!ctls || !ctls->ssl) |
763 | return 1; |
764 | ssl= (SSL *)ctls->ssl; |
765 | ctx= SSL_get_SSL_CTX(ssl); |
766 | if (ctx) |
767 | SSL_CTX_free(ctx); |
768 | |
769 | SSL_set_quiet_shutdown(ssl, 1); |
770 | /* 2 x pending + 2 * data = 4 */ |
771 | for (i=0; i < 4; i++) |
772 | if ((rc= SSL_shutdown(ssl))) |
773 | break; |
774 | |
775 | /* Since we transferred ownership of BIO to ssl, BIO will |
776 | automatically freed - no need for an explicit BIO_free_all */ |
777 | |
778 | SSL_free(ssl); |
779 | ctls->ssl= NULL; |
780 | |
781 | return rc; |
782 | } |
783 | |
784 | int ma_tls_verify_server_cert(MARIADB_TLS *ctls) |
785 | { |
786 | X509 *cert; |
787 | MYSQL *mysql; |
788 | SSL *ssl; |
789 | MARIADB_PVIO *pvio; |
790 | #if !defined(HAVE_OPENSSL_CHECK_HOST) |
791 | X509_NAME *x509sn; |
792 | int cn_pos; |
793 | X509_NAME_ENTRY *cn_entry; |
794 | ASN1_STRING *cn_asn1; |
795 | const char *cn_str; |
796 | #endif |
797 | |
798 | if (!ctls || !ctls->ssl) |
799 | return 1; |
800 | ssl= (SSL *)ctls->ssl; |
801 | |
802 | mysql= (MYSQL *)SSL_get_app_data(ssl); |
803 | pvio= mysql->net.pvio; |
804 | |
805 | if (!mysql->host) |
806 | { |
807 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
808 | ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname" ); |
809 | return 1; |
810 | } |
811 | |
812 | if (!(cert= SSL_get_peer_certificate(ssl))) |
813 | { |
814 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
815 | ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate" ); |
816 | return 1; |
817 | } |
818 | #ifdef HAVE_OPENSSL_CHECK_HOST |
819 | if (X509_check_host(cert, mysql->host, 0, 0, 0) != 1 |
820 | && X509_check_ip_asc(cert, mysql->host, 0) != 1) |
821 | goto error; |
822 | #else |
823 | x509sn= X509_get_subject_name(cert); |
824 | |
825 | if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0) |
826 | goto error; |
827 | |
828 | if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos))) |
829 | goto error; |
830 | |
831 | if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry))) |
832 | goto error; |
833 | |
834 | cn_str = (char *)ASN1_STRING_data(cn_asn1); |
835 | |
836 | /* Make sure there is no embedded \0 in the CN */ |
837 | if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str)) |
838 | goto error; |
839 | |
840 | if (strcmp(cn_str, mysql->host)) |
841 | goto error; |
842 | #endif |
843 | X509_free(cert); |
844 | |
845 | return 0; |
846 | error: |
847 | X509_free(cert); |
848 | |
849 | pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
850 | ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed" ); |
851 | return 1; |
852 | } |
853 | |
854 | const char *ma_tls_get_cipher(MARIADB_TLS *ctls) |
855 | { |
856 | if (!ctls || !ctls->ssl) |
857 | return NULL; |
858 | return SSL_get_cipher_name(ctls->ssl); |
859 | } |
860 | |
861 | unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len) |
862 | { |
863 | X509 *cert= NULL; |
864 | MYSQL *mysql; |
865 | unsigned int fp_len; |
866 | |
867 | if (!ctls || !ctls->ssl) |
868 | return 0; |
869 | |
870 | mysql= SSL_get_app_data(ctls->ssl); |
871 | |
872 | if (!(cert= SSL_get_peer_certificate(ctls->ssl))) |
873 | { |
874 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
875 | ER(CR_SSL_CONNECTION_ERROR), |
876 | "Unable to get server certificate" ); |
877 | goto end; |
878 | } |
879 | |
880 | if (len < EVP_MAX_MD_SIZE) |
881 | { |
882 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
883 | ER(CR_SSL_CONNECTION_ERROR), |
884 | "Finger print buffer too small" ); |
885 | goto end; |
886 | } |
887 | if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len)) |
888 | { |
889 | my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, |
890 | ER(CR_SSL_CONNECTION_ERROR), |
891 | "invalid finger print of server certificate" ); |
892 | goto end; |
893 | } |
894 | |
895 | X509_free(cert); |
896 | return (fp_len); |
897 | end: |
898 | X509_free(cert); |
899 | return 0; |
900 | } |
901 | |
902 | |
903 | int ma_tls_get_protocol_version(MARIADB_TLS *ctls) |
904 | { |
905 | if (!ctls || !ctls->ssl) |
906 | return -1; |
907 | |
908 | return SSL_version(ctls->ssl) & 0xFF; |
909 | } |
910 | |
911 | |