diff --git a/libmariadb/ma_pvio.c b/libmariadb/ma_pvio.c index 8f9afc7b..12569b03 100644 --- a/libmariadb/ma_pvio.c +++ b/libmariadb/ma_pvio.c @@ -540,8 +540,7 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio) 2. verify CN (requires option ssl_verify_check) 3. verrify finger print */ - if ((pvio->mysql->options.ssl_ca || pvio->mysql->options.ssl_capath) && - (pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + if ((pvio->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && ma_pvio_tls_verify_server_cert(pvio->ctls)) return 1; diff --git a/libmariadb/secure/gnutls.c b/libmariadb/secure/gnutls.c index ad29f1c4..5f495ff0 100644 --- a/libmariadb/secure/gnutls.c +++ b/libmariadb/secure/gnutls.c @@ -1080,6 +1080,23 @@ static int ma_tls_set_certs(MYSQL *mysql, if (ssl_error < 0) goto error; } + + if (mysql->options.ssl_capath) + { + ssl_error= gnutls_certificate_set_x509_trust_dir(ctx, + mysql->options.ssl_capath, + GNUTLS_X509_FMT_PEM); + if (ssl_error < 0) + goto error; + } + + if (!mysql->options.ssl_ca && !mysql->options.ssl_capath) + { + ssl_error= gnutls_certificate_set_x509_system_trust(ctx); + if (ssl_error < 0) + goto error; + } + gnutls_certificate_set_verify_function(ctx, my_verify_callback); @@ -1211,7 +1228,11 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) if (ret < 0) { - ma_tls_set_error(mysql, ssl, ret); + /* If error message was not set while calling certification callback function, + use default error message (which is not very descriptive */ + if (!mysql_errno(mysql)) + ma_tls_set_error(mysql, ssl, ret); + /* restore blocking mode */ gnutls_deinit((gnutls_session_t )ctls->ssl); free_gnutls_data(data); @@ -1330,7 +1351,7 @@ my_bool ma_tls_close(MARIADB_TLS *ctls) int ma_tls_verify_server_cert(MARIADB_TLS *ctls __attribute__((unused))) { - /* server verification is already handled before */ + /* server verification is already handled before during handshake */ return 0; } @@ -1352,77 +1373,44 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls) static int my_verify_callback(gnutls_session_t ssl) { unsigned int status= 0; - const gnutls_datum_t *cert_list; - unsigned int cert_list_size; struct st_gnutls_data *data= (struct st_gnutls_data *)gnutls_session_get_ptr(ssl); MYSQL *mysql; - MARIADB_PVIO *pvio; - - gnutls_x509_crt_t cert; - const char *hostname; mysql= data->mysql; - pvio= mysql->net.pvio; - - /* read hostname */ - hostname = mysql->host; - - /* This verification function uses the trusted CAs in the credentials - * structure. So you must have installed one or more CA certificates. - */ - if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && - gnutls_certificate_verify_peers2 (ssl, &status) < 0) - { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CA verification failed"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - if (status & GNUTLS_CERT_INVALID) - { - char errbuf[100]; - snprintf(errbuf, 99, "CA Verification failed (Status: %d)", status); - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, errbuf); - return GNUTLS_E_CERTIFICATE_ERROR; - } - /* Up to here the process is the same for X.509 certificates and - * OpenPGP keys. From now on X.509 certificates are assumed. This can - * be easily extended to work with openpgp keys as well. - */ - if (gnutls_certificate_type_get (ssl) != GNUTLS_CRT_X509) - { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Expected X509 certificate"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - if (gnutls_x509_crt_init (&cert) < 0) - { - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Error during certificate initialization"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - cert_list = gnutls_certificate_get_peers (ssl, &cert_list_size); - if (cert_list == NULL) - { - gnutls_x509_crt_deinit (cert); - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "No certificate found"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) - { - gnutls_x509_crt_deinit (cert); - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - - if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && - !gnutls_x509_crt_check_hostname (cert, hostname)) - { - gnutls_x509_crt_deinit (cert); - pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Hostname in certificate doesn't match"); - return GNUTLS_E_CERTIFICATE_ERROR; - } - gnutls_x509_crt_deinit (cert); - /* notify gnutls to continue handshake normally */ CLEAR_CLIENT_ERROR(mysql); + + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) + { + const char *hostname= mysql->host; + + if (gnutls_certificate_verify_peers3 (ssl, hostname, &status) < 0) + return GNUTLS_E_CERTIFICATE_ERROR; + } else { + if (gnutls_certificate_verify_peers2 (ssl, &status) < 0) + return GNUTLS_E_CERTIFICATE_ERROR; + } + if (status & GNUTLS_CERT_INVALID) + { + gnutls_datum_t out; + int type; + /* accept self signed certificates if we don't have to verify server cert */ + if (!(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + (status & GNUTLS_CERT_SIGNER_NOT_FOUND)) + return 0; + + /* gnutls default error mesage "certificate validation failed" isn't very + descriptive, so we provide more information about the error here */ + type= gnutls_certificate_type_get(ssl); + gnutls_certificate_verification_status_print(status, type, &out, 0); + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), out.data); + gnutls_free(out.data); + + return GNUTLS_E_CERTIFICATE_ERROR; + } + + return 0; } diff --git a/libmariadb/secure/openssl.c b/libmariadb/secure/openssl.c index 95c23bcb..a9037b86 100644 --- a/libmariadb/secure/openssl.c +++ b/libmariadb/secure/openssl.c @@ -530,9 +530,7 @@ static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl) X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } } - SSL_CTX_set_verify(ctx, (mysql->options.ssl_ca || mysql->options.ssl_capath) || - (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ? - SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); return 0; error: @@ -648,7 +646,8 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls) pvio->methods->blocking(pvio, FALSE, 0); return 1; } - if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT)) + if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) || + (mysql->options.ssl_ca || mysql->options.ssl_capath)) { rc= SSL_get_verify_result(ssl); if (rc != X509_V_OK) diff --git a/unittest/libmariadb/ssl.c.in b/unittest/libmariadb/ssl.c.in index 4124c366..bd4760cd 100644 --- a/unittest/libmariadb/ssl.c.in +++ b/unittest/libmariadb/ssl.c.in @@ -1329,7 +1329,7 @@ static int test_conc386(MYSQL *mysql) return OK; } -#ifdef HAVE_OPENSSL +#ifndef HAVE_SCHANNEL static int test_ssl_verify(MYSQL *my __attribute__((unused))) { MYSQL *mysql; @@ -1355,13 +1355,20 @@ static int test_ssl_verify(MYSQL *my __attribute__((unused))) port, socketname, 0), mysql_error(mysql)); mysql_close(mysql); + mysql= mysql_init(NULL); + mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce); + FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0), mysql_error(mysql)); + + diag("cipher: %s", mysql_get_ssl_cipher(mysql)); + mysql_close(mysql); return OK; } #endif struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, -#ifdef HAVE_OPENSSL +#ifndef HAVE_SCHANNEL {"test_ssl_verify", test_ssl_verify, TEST_CONNECTION_NEW, 0, NULL, NULL}, #endif {"test_mdev14101", test_mdev14101, TEST_CONNECTION_NEW, 0, NULL, NULL},