You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-08 14:02:17 +03:00
Merge remote-tracking branch 'origin/connector_c_2.2'
This commit is contained in:
@@ -49,6 +49,8 @@ struct st_mysql_options_extension {
|
||||
const char *proc_info,
|
||||
unsigned int proc_info_length);
|
||||
MARIADB_DB_DRIVER *db_driver;
|
||||
char *ssl_fp; /* finger print of server certificate */
|
||||
char *ssl_fp_list; /* white list of finger prints */
|
||||
};
|
||||
|
||||
|
||||
|
@@ -36,6 +36,7 @@ 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 ma_ssl_verify_fingerprint(SSL *ssl);
|
||||
|
||||
int my_ssl_start(MYSQL *mysql);
|
||||
void my_ssl_end(void);
|
||||
|
@@ -209,7 +209,10 @@ extern unsigned int mariadb_deinitialize_ssl;
|
||||
/* MariaDB specific */
|
||||
MYSQL_PROGRESS_CALLBACK=5999,
|
||||
MYSQL_OPT_NONBLOCK,
|
||||
MYSQL_DATABASE_DRIVER=7000
|
||||
/* MariaDB Connector/C specific */
|
||||
MYSQL_DATABASE_DRIVER=7000,
|
||||
MARIADB_OPT_SSL_FP, /* single finger print for server certificate verification */
|
||||
MARIADB_OPT_SSL_FP_LIST /* finger print white list for server certificate verification */
|
||||
};
|
||||
|
||||
enum mysql_status { MYSQL_STATUS_READY,
|
||||
|
@@ -845,6 +845,7 @@ static const char *default_options[]=
|
||||
"ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
|
||||
"multi-results", "multi-statements", "multi-queries", "secure-auth",
|
||||
"report-data-truncation", "plugin-dir", "default-auth", "database-type",
|
||||
"ssl-fp", "ssl-fp-list",
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -858,7 +859,8 @@ enum option_val
|
||||
OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile,
|
||||
OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
|
||||
OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth,
|
||||
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type
|
||||
OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth, OPT_db_type,
|
||||
OPT_ssl_fp, OPT_ssl_fp_list
|
||||
};
|
||||
|
||||
#define CHECK_OPT_EXTENSION_SET(OPTS)\
|
||||
@@ -991,7 +993,7 @@ static void mysql_read_default_options(struct st_mysql_options *options,
|
||||
case OPT_return_found_rows:
|
||||
options->client_flag|=CLIENT_FOUND_ROWS;
|
||||
break;
|
||||
#ifdef HAVE_OPENSSL
|
||||
#ifdef HAVE_OPENSSL
|
||||
case OPT_ssl_key:
|
||||
my_free(options->ssl_key);
|
||||
options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
|
||||
@@ -1010,12 +1012,20 @@ static void mysql_read_default_options(struct st_mysql_options *options,
|
||||
break;
|
||||
case OPT_ssl_cipher:
|
||||
break;
|
||||
case OPT_ssl_fp:
|
||||
OPT_SET_EXTENDED_VALUE(options, ssl_fp, opt_arg, 1);
|
||||
break;
|
||||
case OPT_ssl_fp_list:
|
||||
OPT_SET_EXTENDED_VALUE(options, ssl_fp_list, opt_arg, 1);
|
||||
break;
|
||||
#else
|
||||
case OPT_ssl_key:
|
||||
case OPT_ssl_cert:
|
||||
case OPT_ssl_ca:
|
||||
case OPT_ssl_capath:
|
||||
case OPT_ssl_cipher:
|
||||
case OPT_ssl_fp:
|
||||
case OPT_ssl_fp_list:
|
||||
break;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
case OPT_charset_dir:
|
||||
@@ -2191,6 +2201,8 @@ static void mysql_close_options(MYSQL *mysql)
|
||||
my_free(mysql->options.extension->db_driver);
|
||||
my_free(mysql->options.extension->ssl_crl);
|
||||
my_free(mysql->options.extension->ssl_crlpath);
|
||||
my_free(mysql->options.extension->ssl_fp);
|
||||
my_free(mysql->options.extension->ssl_fp_list);
|
||||
if(hash_inited(&mysql->options.extension->connect_attrs))
|
||||
hash_free(&mysql->options.extension->connect_attrs);
|
||||
}
|
||||
@@ -3036,6 +3048,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
|
||||
case MYSQL_OPT_SSL_CRLPATH:
|
||||
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_crlpath, (char *)arg1, 1);
|
||||
break;
|
||||
case MARIADB_OPT_SSL_FP:
|
||||
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp, (char *)arg1, 1);
|
||||
break;
|
||||
case MARIADB_OPT_SSL_FP_LIST:
|
||||
OPT_SET_EXTENDED_VALUE(&mysql->options, ssl_fp_list, (char *)arg1, 1);
|
||||
break;
|
||||
case MYSQL_OPT_CONNECT_ATTR_DELETE:
|
||||
{
|
||||
uchar *p;
|
||||
@@ -3493,8 +3511,8 @@ ulong STDCALL mysql_hex_string(char *to, const char *from,
|
||||
|
||||
while (len--)
|
||||
{
|
||||
*to++= hexdigits[*from >> 4];
|
||||
*to++= hexdigits[*from & 0x0F];
|
||||
*to++= hexdigits[((unsigned char)*from) >> 4];
|
||||
*to++= hexdigits[((unsigned char)*from) & 0x0F];
|
||||
from++;
|
||||
}
|
||||
*to= 0;
|
||||
|
@@ -298,7 +298,28 @@ error:
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
static int my_verify_callback(int ok, X509_STORE_CTX *ctx)
|
||||
static unsigned int ma_get_cert_fingerprint(X509 *cert, EVP_MD *digest,
|
||||
unsigned char *fingerprint, unsigned int *fp_length)
|
||||
{
|
||||
if (*fp_length < EVP_MD_size(digest))
|
||||
return 0;
|
||||
if (!X509_digest(cert, digest, fingerprint, fp_length))
|
||||
return 0;
|
||||
return *fp_length;
|
||||
}
|
||||
|
||||
static my_bool ma_check_fingerprint(char *fp1, unsigned int fp1_len,
|
||||
char *fp2, unsigned int fp2_len)
|
||||
{
|
||||
char hexstr[fp1_len * 2 + 1];
|
||||
|
||||
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
|
||||
if (strncasecmp(hexstr, fp2, fp1_len) != 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int my_verify_callback(int ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
X509 *check_cert;
|
||||
SSL *ssl;
|
||||
@@ -400,7 +421,7 @@ int my_ssl_connect(SSL *ssl)
|
||||
{
|
||||
my_bool blocking;
|
||||
MYSQL *mysql;
|
||||
int rc;
|
||||
long rc;
|
||||
|
||||
DBUG_ENTER("my_ssl_connect");
|
||||
|
||||
@@ -444,6 +465,89 @@ int my_ssl_connect(SSL *ssl)
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
int ma_ssl_verify_fingerprint(SSL *ssl)
|
||||
{
|
||||
X509 *cert= SSL_get_peer_certificate(ssl);
|
||||
MYSQL *mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||||
unsigned char fingerprint[EVP_MAX_MD_SIZE];
|
||||
EVP_MD *digest;
|
||||
unsigned int fp_length;
|
||||
|
||||
DBUG_ENTER("ma_ssl_verify_fingerprint");
|
||||
|
||||
if (!cert)
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Unable to get server certificate");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
digest= (EVP_MD *)EVP_sha1();
|
||||
fp_length= sizeof(fingerprint);
|
||||
|
||||
if (!ma_get_cert_fingerprint(cert, digest, fingerprint, &fp_length))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Unable to get finger print of server certificate");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
/* single finger print was specified */
|
||||
if (mysql->options.extension->ssl_fp)
|
||||
{
|
||||
if (ma_check_fingerprint(fingerprint, fp_length, mysql->options.extension->ssl_fp,
|
||||
strlen(mysql->options.extension->ssl_fp)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"invalid finger print of server certificate");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* white list of finger prints was specified */
|
||||
if (mysql->options.extension->ssl_fp_list)
|
||||
{
|
||||
FILE *fp;
|
||||
char buff[255];
|
||||
|
||||
if (!(fp = my_fopen(mysql->options.extension->ssl_fp_list ,O_RDONLY, MYF(0))))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Can't open finger print list");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
while (fgets(buff, sizeof(buff)-1, fp))
|
||||
{
|
||||
/* remove trailing new line character */
|
||||
char *pos= strchr(buff, '\r');
|
||||
if (!pos)
|
||||
pos= strchr(buff, '\n');
|
||||
if (pos)
|
||||
*pos= '\0';
|
||||
|
||||
if (!ma_check_fingerprint(fingerprint, fp_length, buff, strlen(buff)))
|
||||
{
|
||||
/* finger print is valid: close file and exit */
|
||||
my_fclose(fp, MYF(0));
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* No finger print matched - close file and return error */
|
||||
my_fclose(fp, MYF(0));
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"invalid finger print of server certificate");
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
/*
|
||||
verify server certificate
|
||||
|
||||
|
@@ -1,3 +1,26 @@
|
||||
/************************************************************************************
|
||||
Copyright (C) 2012-2015 Monty Program AB, MariaDB Corporation AB,
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not see <http://www.gnu.org/licenses>
|
||||
or write to the Free Software Foundation, Inc.,
|
||||
51 Franklin St., Fifth Floor, Boston, MA 02110, USA
|
||||
|
||||
Part of this code includes code from the PHP project which
|
||||
is freely available from http://www.php.net
|
||||
|
||||
Originally written by Sergei Golubchik
|
||||
*************************************************************************************/
|
||||
#include <my_global.h>
|
||||
#include <my_sys.h>
|
||||
#include <m_string.h>
|
||||
@@ -238,6 +261,22 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
mysql->options.use_ssl= 1;
|
||||
if (mysql->options.use_ssl)
|
||||
mysql->client_flag|= CLIENT_SSL;
|
||||
|
||||
/* if server doesn't support SSL and verification of server certificate
|
||||
was set to mandator, we need to return an error */
|
||||
if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL))
|
||||
{
|
||||
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) ||
|
||||
(mysql->options.extension && (mysql->options.extension->ssl_fp ||
|
||||
mysql->options.extension->ssl_fp_list)))
|
||||
{
|
||||
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
|
||||
ER(CR_SSL_CONNECTION_ERROR),
|
||||
"Server doesn't support SSL");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
|
||||
if (mpvio->db)
|
||||
mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
|
||||
@@ -279,6 +318,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
#endif
|
||||
)
|
||||
mysql->options.use_ssl= 1;
|
||||
|
||||
if (mysql->options.use_ssl &&
|
||||
(mysql->client_flag & CLIENT_SSL))
|
||||
{
|
||||
@@ -307,6 +347,13 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio,
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (mysql->options.extension &&
|
||||
(mysql->options.extension->ssl_fp || mysql->options.extension->ssl_fp_list))
|
||||
{
|
||||
if (ma_ssl_verify_fingerprint(ssl))
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((mysql->options.ssl_ca || mysql->options.ssl_capath) &&
|
||||
(mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
|
||||
my_ssl_verify_server_cert(ssl))
|
||||
|
@@ -21,8 +21,19 @@ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include
|
||||
${CMAKE_SOURCE_DIR}/unittest/mytap)
|
||||
ADD_DEFINITIONS(-DLIBMARIADB)
|
||||
|
||||
# Get finger print from server certificate
|
||||
EXECUTE_PROCESS(COMMAND openssl x509 -in server-cert.pem -sha1 -fingerprint -noout
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/unittest/libmariadb/certs
|
||||
OUTPUT_VARIABLE FINGER_PRINT)
|
||||
STRING(REPLACE "SHA1 Fingerprint=" "" FINGER_PRINT ${FINGER_PRINT})
|
||||
STRING(REPLACE "\n" "" FINGER_PRINT ${FINGER_PRINT})
|
||||
STRING(REPLACE ":" "" SSL_CERT_FINGER_PRINT ${FINGER_PRINT})
|
||||
MESSAGE(STATUS "FINGER_PRINT ${SSL_CERT_FINGER_PRINT}")
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
|
||||
${CMAKE_SOURCE_DIR}/unittest/libmariadb/ssl.c)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in
|
||||
${CMAKE_SOURCE_DIR}/unittest/libmariadb/fingerprint.list)
|
||||
|
||||
SET(API_TESTS "async" "basic-t" "fetch" "charset" "logs" "cursor" "errors" "view" "ps" "ps_bugs"
|
||||
"sp" "result" "connection" "misc" "ssl" "ps_new" "sqlite3" "thread" "dyncol")
|
||||
|
4
unittest/libmariadb/fingerprint.list.in
Normal file
4
unittest/libmariadb/fingerprint.list.in
Normal file
@@ -0,0 +1,4 @@
|
||||
86DD6D764C0CA47C5014E1E7802674BFDB674ED3
|
||||
@SSL_CERT_FINGER_PRINT@
|
||||
73F1FEC1FE041473563BFF2D624B88F6CFCFE626
|
||||
|
@@ -380,6 +380,7 @@ static int test_conc127(MYSQL *my)
|
||||
|
||||
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
|
||||
port, socketname, 0);
|
||||
diag("Error: %s", mysql_error(mysql));
|
||||
FAIL_IF(mysql_errno(mysql) == 0, "Error expected (invalid certificate)");
|
||||
mysql_close(mysql);
|
||||
|
||||
@@ -615,10 +616,63 @@ static int test_conc_102(MYSQL *mysql)
|
||||
return OK;
|
||||
}
|
||||
|
||||
const char *ssl_cert_finger_print= "@SSL_CERT_FINGER_PRINT@";
|
||||
|
||||
static int test_ssl_fp(MYSQL *unused)
|
||||
{
|
||||
MYSQL *my;
|
||||
char *cipher;
|
||||
|
||||
if (check_skip_ssl())
|
||||
return SKIP;
|
||||
|
||||
my= mysql_init(NULL);
|
||||
FAIL_IF(!my, "mysql_init() failed");
|
||||
|
||||
mysql_ssl_set(my,0, 0, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0);
|
||||
|
||||
mysql_options(my, MARIADB_OPT_SSL_FP, ssl_cert_finger_print);
|
||||
|
||||
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
|
||||
port, socketname, 0), mysql_error(my));
|
||||
|
||||
cipher= (char *)mysql_get_ssl_cipher(my);
|
||||
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
|
||||
mysql_close(my);
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int test_ssl_fp_list(MYSQL *unused)
|
||||
{
|
||||
MYSQL *my;
|
||||
char *cipher;
|
||||
|
||||
if (check_skip_ssl())
|
||||
return SKIP;
|
||||
|
||||
my= mysql_init(NULL);
|
||||
FAIL_IF(!my, "mysql_init() failed");
|
||||
|
||||
mysql_ssl_set(my,0, 0, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", 0, 0);
|
||||
|
||||
mysql_options(my, MARIADB_OPT_SSL_FP_LIST, "./fingerprint.list");
|
||||
|
||||
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
|
||||
port, socketname, 0), mysql_error(my));
|
||||
|
||||
cipher= (char *)mysql_get_ssl_cipher(my);
|
||||
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
|
||||
mysql_close(my);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct my_tests_st my_tests[] = {
|
||||
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_ssl_fp", test_ssl_fp, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_conc50", test_conc50, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_conc50_1", test_conc50_1, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
{"test_conc50_2", test_conc50_2, TEST_CONNECTION_NEW, 0, NULL, NULL},
|
||||
|
Reference in New Issue
Block a user