diff --git a/include/ma_secure.h b/include/ma_secure.h index f30e5d6a..148d65bd 100644 --- a/include/ma_secure.h +++ b/include/ma_secure.h @@ -35,6 +35,7 @@ int my_ssl_close(Vio *vio); size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size); SSL *my_ssl_init(MYSQL *mysql); int my_ssl_connect(SSL *ssl); +int my_ssl_verify_server_cert(SSL *ssl); int my_ssl_start(MYSQL *mysql); void my_ssl_end(void); diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 2d3e22d6..108456e4 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -2771,6 +2771,12 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) } } break; + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + if (*(uint *)arg) + mysql->options.client_flag |= CLIENT_SSL_VERIFY_SERVER_CERT; + else + mysql->options.client_flag &= ~CLIENT_SSL_VERIFY_SERVER_CERT; + break; default: DBUG_RETURN(-1); } diff --git a/libmariadb/ma_secure.c b/libmariadb/ma_secure.c index 453aec23..ce0f9616 100644 --- a/libmariadb/ma_secure.c +++ b/libmariadb/ma_secure.c @@ -209,7 +209,7 @@ static int my_ssl_set_certs(SSL *ssl) int have_cert= 0; MYSQL *mysql; - DBUG_ENTER("my_ssl_connect"); + DBUG_ENTER("my_ssl_set_certs"); /* Make sure that ssl was allocated and ssl_system was initialized */ @@ -377,7 +377,6 @@ int my_ssl_connect(SSL *ssl) if (SSL_connect(ssl) != 1) { - printf("connect failed\n"); my_SSL_error(mysql); /* restore blocking mode */ if (!blocking) @@ -390,6 +389,62 @@ int my_ssl_connect(SSL *ssl) DBUG_RETURN(0); } +/* + verify server certificate + + SYNOPSIS + my_ssl_verify_server_cert() + MYSQL mysql + + RETURN VALUES + 1 Error + 0 OK +*/ + +int my_ssl_verify_server_cert(SSL *ssl) +{ + X509 *cert; + MYSQL *mysql; + char *p1, *p2, buf[256]; + + DBUG_ENTER("my_ssl_verify_server_cert"); + + mysql= (MYSQL *)SSL_get_app_data(ssl); + + if (!mysql->host) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Invalid (empty) hostname"); + DBUG_RETURN(1); + } + + if (!(cert= SSL_get_peer_certificate(ssl))) + { + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Unable to get server certificate"); + DBUG_RETURN(1); + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, 256); + X509_free(cert); + + /* Extract the server name from buffer: + Format: ....CN=/hostname/.... */ + if ((p1= strstr(buf, "/CN="))) + { + p1+= 4; + if ((p2= strchr(p1, '/'))) + *p2= 0; + if (!strcmp(mysql->host, p1)) + DBUG_RETURN(0); + } + my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, + ER(CR_SSL_CONNECTION_ERROR), + "Validation of SSL server certificate failed"); + DBUG_RETURN(1); +} /* write to ssl socket diff --git a/libmariadb/my_auth.c b/libmariadb/my_auth.c index 0569def9..494969fe 100644 --- a/libmariadb/my_auth.c +++ b/libmariadb/my_auth.c @@ -352,7 +352,10 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, SSL_free(ssl); goto error; } - /* todo: server certification verification */ + + if (mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT && + my_ssl_verify_server_cert(ssl)) + goto error; } #endif /* HAVE_OPENSSL */ diff --git a/unittest/libmariadb/ssl.c b/unittest/libmariadb/ssl.c index e71ebc6b..6c2668c4 100644 --- a/unittest/libmariadb/ssl.c +++ b/unittest/libmariadb/ssl.c @@ -249,6 +249,36 @@ static int test_conc50(MYSQL *my) return OK; } +static int verify_ssl_server_cert(MYSQL *my) +{ + MYSQL *mysql; + uint verify= 1; + + if (check_skip_ssl()) + return SKIP; + + mysql= mysql_init(NULL); + FAIL_IF(!mysql, "Can't allocate memory"); + + mysql_ssl_set(mysql, NULL, NULL, "./certs/ca-cert.pem", NULL, NULL); + mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); + + mysql_real_connect(mysql, hostname, username, password, schema, + port, socketname, 0); + + if (!strcmp(mysql->host, "localhost")) + { + FAIL_IF(mysql_errno(mysql), "No error expected"); + } + else + { + FAIL_IF(mysql_errno(mysql) != 2026, "Expected errno 2026"); + } + mysql_close(mysql); + + return OK; +} + static int test_bug62743(MYSQL *my) { MYSQL *mysql; @@ -272,6 +302,7 @@ static int test_bug62743(MYSQL *my) struct my_tests_st my_tests[] = { {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"verify_ssl_server_cert", verify_ssl_server_cert, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_bug62743", test_bug62743, TEST_CONNECTION_NEW, 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},