1
0
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:
Georg Richter
2015-06-25 09:19:11 +02:00
9 changed files with 252 additions and 8 deletions

View File

@@ -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 */
};

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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))

View File

@@ -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")

View File

@@ -0,0 +1,4 @@
86DD6D764C0CA47C5014E1E7802674BFDB674ED3
@SSL_CERT_FINGER_PRINT@
73F1FEC1FE041473563BFF2D624B88F6CFCFE626

View File

@@ -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},