1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +03:00

MDEV-14101: tls-version

Client part of MDEV-14101: Add support for tls-version, via
mysql_options(mysql, MARIADB_OPT_TLS_VERSION, value)
Accepted values are "TLSv1.1", "TLSv1.2" and "TLSv1.3".

Fixed testcase openssl_1 for schannel
This commit is contained in:
Georg Richter
2017-10-23 11:04:14 +02:00
parent a09510671e
commit b241f8995f
10 changed files with 187 additions and 78 deletions

View File

@@ -10,6 +10,14 @@ enum enum_pvio_tls_type {
SSL_TYPE_GNUTLS
};
#define PROTOCOL_SSLV3 0
#define PROTOCOL_TLS_1_0 1
#define PROTOCOL_TLS_1_1 2
#define PROTOCOL_TLS_1_2 3
#define PROTOCOL_TLS_1_3 4
#define PROTOCOL_UNKNOWN 5
#define PROTOCOL_MAX PROTOCOL_TLS_1_3
#define TLS_VERSION_LENGTH 64
extern char tls_library_version[TLS_VERSION_LENGTH];
@@ -19,11 +27,6 @@ typedef struct st_ma_pvio_tls {
void *ssl;
} MARIADB_TLS;
struct st_ssl_version {
unsigned int iversion;
char *cversion;
};
/* Function prototypes */
/* ma_tls_start
@@ -133,15 +136,15 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ssl);
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int fp_len);
/* ma_tls_get_protocol_version
returns protocol version in use
returns protocol version number in use
Parameter:
MARIADB_TLS MariaDB SSL container
version pointer to ssl version info
Returns:
0 success
1 error
protocol number
*/
my_bool ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version);
int ma_tls_get_protocol_version(MARIADB_TLS *ctls);
const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls);
int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls);
/* Function prototypes */
MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql);
@@ -153,7 +156,6 @@ int ma_pvio_tls_verify_server_cert(MARIADB_TLS *ctls);
const char *ma_pvio_tls_cipher(MARIADB_TLS *ctls);
my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list);
my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio);
my_bool ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version);
void ma_pvio_tls_end();
#endif /* _ma_tls_h_ */

View File

@@ -204,6 +204,7 @@ extern const char *SQLSTATE_UNKNOWN;
MYSQL_OPT_SSL_ENFORCE,
MYSQL_OPT_MAX_ALLOWED_PACKET,
MYSQL_OPT_NET_BUFFER_LENGTH,
MYSQL_OPT_TLS_VERSION,
/* MariaDB specific */
MYSQL_PROGRESS_CALLBACK=5999,

View File

@@ -51,7 +51,8 @@
my_bool ma_tls_initialized= FALSE;
unsigned int mariadb_deinitialize_ssl= 1;
const char *ssl_protocol_version[5]= {"TLS1.0", "TLS1.1", "TLS1.2"};
const char *tls_protocol_version[]=
{"SSLv3", "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3", "Unknown"};
MARIADB_TLS *ma_pvio_tls_init(MYSQL *mysql)
{
@@ -114,9 +115,19 @@ void ma_pvio_tls_end()
ma_tls_end();
}
my_bool ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version)
int ma_pvio_tls_get_protocol_version_id(MARIADB_TLS *ctls)
{
return ma_tls_get_protocol_version(ctls, version);
return ma_tls_get_protocol_version(ctls);
}
const char *ma_pvio_tls_get_protocol_version(MARIADB_TLS *ctls)
{
int version;
version= ma_tls_get_protocol_version(ctls);
if (version < 0 || version > PROTOCOL_MAX)
return tls_protocol_version[PROTOCOL_UNKNOWN];
return tls_protocol_version[version];
}
static char ma_hex2int(char c)

View File

@@ -135,7 +135,7 @@ struct st_mariadb_methods MARIADB_DEFAULT_METHODS;
#define native_password_plugin_name "mysql_native_password"
#define IS_CONNHDLR_ACTIVE(mysql)\
(((mysql)->extension->conn_hdlr))
((mysql)->extension && (mysql)->extension->conn_hdlr)
static void end_server(MYSQL *mysql);
static void mysql_close_memory(MYSQL *mysql);
@@ -644,7 +644,8 @@ struct st_default_options mariadb_defaults[] =
{MARIADB_OPT_SSL_FP, MARIADB_OPTION_STR, "ssl-fp"},
{MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fp-list"},
{MARIADB_OPT_SSL_FP_LIST, MARIADB_OPTION_STR, "ssl-fplist"},
{MARIADB_OPT_TLS_PASSPHRASE, MARIADB_OPTION_STR, "ssl_passphrase"},
{MARIADB_OPT_TLS_PASSPHRASE, MARIADB_OPTION_STR, "ssl-passphrase"},
{MARIADB_OPT_TLS_VERSION, MARIADB_OPTION_STR, "tls_version"},
{MYSQL_OPT_BIND, MARIADB_OPTION_STR, "bind-address"},
{0, 0, NULL}
};
@@ -3003,6 +3004,10 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
OPT_SET_EXTENDED_VALUE(&mysql->options, proxy_header_len, arg2);
}
break;
case MARIADB_OPT_TLS_VERSION:
case MYSQL_OPT_TLS_VERSION:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, tls_version, (char *)arg1);
break;
default:
va_end(ap);
return(-1);
@@ -3685,11 +3690,7 @@ my_bool STDCALL mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *
case MARIADB_CONNECTION_TLS_VERSION:
#ifdef HAVE_TLS
if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
{
struct st_ssl_version version;
if (!ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls, &version))
*((char **)arg)= version.cversion;
}
*((char **)arg)= (char *)ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls);
else
#endif
goto error;
@@ -3697,11 +3698,7 @@ my_bool STDCALL mariadb_get_infov(MYSQL *mysql, enum mariadb_value value, void *
case MARIADB_CONNECTION_TLS_VERSION_ID:
#ifdef HAVE_TLS
if (mysql && mysql->net.pvio && mysql->net.pvio->ctls)
{
struct st_ssl_version version;
if (!ma_pvio_tls_get_protocol_version(mysql->net.pvio->ctls, &version))
*((unsigned int *)arg)= version.iversion;
}
*((unsigned int *)arg)= ma_pvio_tls_get_protocol_version_id(mysql->net.pvio->ctls);
else
#endif
goto error;

View File

@@ -1006,17 +1006,46 @@ void ma_tls_end()
return;
}
static int ma_gnutls_set_ciphers(gnutls_session_t ssl, char *cipher_str)
static size_t ma_gnutls_get_protocol_version(const char *tls_version_option,
char *priority_string,
size_t prio_len)
{
char tls_versions[128];
tls_versions[0]= 0;
if (!tls_version_option || !tls_version_option[0])
goto end;
if (strstr(tls_version_option, "TLSv1.0"))
strcat(tls_versions, ":+VERS-TLS1.0");
if (strstr(tls_version_option, "TLSv1.1"))
strcat(tls_versions, ":+VERS-TLS1.1");
if (strstr(tls_version_option, "TLSv1.2"))
strcat(tls_versions, ":+VERS-TLS1.2");
end:
if (tls_versions[0])
snprintf(priority_string, prio_len - 1, "NORMAL:-VERS-TLS-ALL%s", tls_versions);
else
strncpy(priority_string, "NORMAL", prio_len - 1);
return strlen(priority_string);
}
static int ma_gnutls_set_ciphers(gnutls_session_t ssl,
const char *cipher_str,
const char *tls_version)
{
const char *err;
char *token;
#define PRIO_SIZE 1024
#define PRIO_SIZE 1024
char prio[PRIO_SIZE];
if (!cipher_str)
return gnutls_priority_set_direct(ssl, "NORMAL", &err);
ma_gnutls_get_protocol_version(tls_version, prio, PRIO_SIZE);
token= strtok(cipher_str, ":");
if (!cipher_str)
return gnutls_priority_set_direct(ssl, prio, &err);
token= strtok((char *)cipher_str, ":");
strcpy(prio, "NONE:+VERS-TLS-ALL:+SIGN-ALL:+COMP-NULL");
@@ -1180,7 +1209,7 @@ void *ma_tls_init(MYSQL *mysql)
/*
gnutls_certificate_set_retrieve_function2(GNUTLS_xcred, client_cert_callback);
*/
ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher);
ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher, mysql->options.extension ? mysql->options.extension->tls_version : NULL);
if (ssl_error < 0)
goto error;
@@ -1427,19 +1456,17 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
else
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
ER(CR_SSL_CONNECTION_ERROR),
"Finger print buffer too small");
return 0;
}
}
my_bool ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version)
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
{
if (!ctls || !ctls->ssl)
return 1;
version->iversion= gnutls_protocol_get_version(ctls->ssl);
version->cversion= (char *)gnutls_protocol_get_name(version->iversion);
return 0;
return gnutls_protocol_get_version(ctls->ssl) - 1;
}
#endif /* HAVE_GNUTLS */

View File

@@ -851,7 +851,7 @@ my_bool ma_schannel_verify_certs(MARIADB_TLS *ctls)
if (crl_file && !(crl_ctx= (CRL_CONTEXT *)ma_schannel_create_crl_context(pvio, mysql->options.extension->ssl_crl)))
goto end;
if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
{
ma_schannel_set_sec_error(pvio, sRet);
@@ -978,8 +978,8 @@ ssize_t ma_schannel_write_encrypt(MARIADB_PVIO *pvio,
extern char *ssl_protocol_version[5];
/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version) */
my_bool ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version)
/* {{{ ma_tls_get_protocol_version(MARIADB_TLS *ctls) */
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
{
SC_CTX *sctx;
SecPkgContext_ConnectionInfo ConnectionInfo;
@@ -989,27 +989,20 @@ my_bool ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *ve
sctx= (SC_CTX *)ctls->ssl;
if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, &ConnectionInfo) != SEC_E_OK)
return 1;
return -1;
switch(ConnectionInfo.dwProtocol)
{
case SP_PROT_SSL3_CLIENT:
version->iversion= 1;
break;
return PROTOCOL_SSLV3;
case SP_PROT_TLS1_CLIENT:
version->iversion= 2;
break;
return PROTOCOL_TLS_1_0;
case SP_PROT_TLS1_1_CLIENT:
version->iversion= 3;
break;
return PROTOCOL_TLS_1_1;
case SP_PROT_TLS1_2_CLIENT:
version->iversion= 4;
break;
return PROTOCOL_TLS_1_2;
default:
version->iversion= 0;
break;
return -1;
}
version->cversion= ssl_protocol_version[version->iversion];
return 0;
}
/* }}} */

View File

@@ -72,6 +72,30 @@ static int ma_bio_write(BIO *h, const char *buf, int size);
static BIO_METHOD ma_BIO_method;
#endif
static long ma_tls_version_options(const char *version)
{
long protocol_options,
disable_all_protocols;
protocol_options= disable_all_protocols=
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
if (!version)
return 0;
if (strstr(version, "TLSv1.0"))
protocol_options&= ~SSL_OP_NO_TLSv1;
if (strstr(version, "TLSv1.1"))
protocol_options&= ~SSL_OP_NO_TLSv1_1;
if (strstr(version, "TLSv1.2"))
protocol_options&= ~SSL_OP_NO_TLSv1_2;
if (protocol_options != disable_all_protocols)
return protocol_options;
return 0;
}
static void ma_tls_set_error(MYSQL *mysql)
{
ulong ssl_errno= ERR_get_error();
@@ -488,6 +512,7 @@ void *ma_tls_init(MYSQL *mysql)
{
SSL *ssl= NULL;
SSL_CTX *ctx= NULL;
long options= SSL_OP_ALL;
#ifdef HAVE_TLS_SESSION_CACHE
MA_SSL_SESSION *session= ma_tls_get_session(mysql);
#endif
@@ -499,7 +524,9 @@ void *ma_tls_init(MYSQL *mysql)
if (!(ctx= SSL_CTX_new(SSLv23_client_method())))
#endif
goto error;
SSL_CTX_set_options(ctx, SSL_OP_ALL);
if (mysql->options.extension)
options|= ma_tls_version_options(mysql->options.extension->tls_version);
SSL_CTX_set_options(ctx, options);
#ifdef HAVE_TLS_SESSION_CACHE
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_CLIENT);
ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size);
@@ -762,18 +789,11 @@ end:
}
extern char *ssl_protocol_version[5];
my_bool ma_tls_get_protocol_version(MARIADB_TLS *ctls, struct st_ssl_version *version)
int ma_tls_get_protocol_version(MARIADB_TLS *ctls)
{
SSL *ssl;
if (!ctls || !ctls->ssl)
return 1;
return -1;
ssl = (SSL *)ctls->ssl;
version->iversion= SSL_version(ssl) - TLS1_VERSION;
version->cversion= ssl_protocol_version[version->iversion];
return 0;
return SSL_version(ctls->ssl) & 0xFF;
}

View File

@@ -372,11 +372,11 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
}
if (mysql->options.extension && mysql->options.extension->tls_version)
{
if (strstr("TLSv1.0", mysql->options.extension->tls_version))
if (strstr(mysql->options.extension->tls_version, "TLSv1.0"))
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_0_CLIENT;
if (strstr("TLSv1.1", mysql->options.extension->tls_version))
if (strstr(mysql->options.extension->tls_version, "TLSv1.1"))
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_1_CLIENT;
if (strstr("TLSv1.2", mysql->options.extension->tls_version))
if (strstr(mysql->options.extension->tls_version, "TLSv1.2"))
Cred.grbitEnabledProtocols|= SP_PROT_TLS1_2_CLIENT;
}
if (!Cred.grbitEnabledProtocols)

View File

@@ -41,6 +41,9 @@ IF(WITH_SSL)
FILE(READ ${CERT_PATH}/server-cert.sha1 CERT_FINGER_PRINT)
STRING(REPLACE "\n" "" CERT_FINGER_PRINT "${CERT_FINGER_PRINT}")
SET(API_TESTS ${API_TESTS} "ssl")
IF(WIN32)
STRING(REPLACE "\\" "\\\\" CERT_PATH ${CERT_PATH})
ENDIF()
CONFIGURE_FILE(${CC_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
${CC_BINARY_DIR}/unittest/libmariadb/ssl.c)
ADD_EXECUTABLE(ssl ${CC_BINARY_DIR}/unittest/libmariadb/ssl.c)

View File

@@ -112,7 +112,7 @@ static int test_ssl(MYSQL *mysql)
MYSQL_ROW row;
char *tls_library;
rc= mysql_query(mysql, "SELECT @@have_ssl UNION SELECT @@have_openssl");
rc= mysql_query(mysql, "SELECT @@have_ssl, @@have_openssl");
check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql);
@@ -422,7 +422,7 @@ static int test_conc50(MYSQL *unused __attribute__((unused)))
mysql= mysql_init(NULL);
FAIL_IF(!mysql, "Can't allocate memory");
mysql_ssl_set(mysql, NULL, NULL, "/home/georg/work/mariadb/bb-10.2-georg/unittest/libmariadb/certs/my_cert.pem", NULL, NULL);
mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting_cert.pem", NULL, NULL);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0);
@@ -471,7 +471,7 @@ static int test_conc50_2(MYSQL *unused __attribute__((unused)))
mysql= mysql_init(NULL);
FAIL_IF(!mysql, "Can't allocate memory");
mysql_ssl_set(mysql, NULL, NULL, "/home/georg/work/mariadb/bb-10.2-georg/unittest/libmariadb/certs/not-found.pem", NULL, NULL);
mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting_cert.pem", NULL, NULL);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0);
@@ -494,7 +494,7 @@ static int test_conc127(MYSQL *unused __attribute__((unused)))
mysql= mysql_init(NULL);
FAIL_IF(!mysql, "Can't allocate memory");
mysql_ssl_set(mysql, NULL, NULL, "/home/georg/work/mariadb/bb-10.2-georg/unittest/libmariadb/certs/dummy.pem", NULL, NULL);
mysql_ssl_set(mysql, NULL, NULL, "./non_exisiting.pem", NULL, NULL);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0);
@@ -649,9 +649,9 @@ DWORD WINAPI thread_conc102(void)
mysql_thread_init();
mysql= mysql_init(NULL);
mysql_ssl_set(mysql, "/home/georg/work/mariadb/bb-10.2-georg/unitt/libmariadb/certs/client-key.pem",
sslcert,
sslca,
mysql_ssl_set(mysql, sslkey,
sslcert,
sslca,
NULL, NULL);
mysql_ssl_set(mysql,0, 0, sslca, 0, 0);
@@ -975,7 +975,6 @@ static int test_openssl_1(MYSQL *mysql)
my= mysql_init(NULL);
mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val);
my->options.use_ssl= 1;
FAIL_IF(!mysql_real_connect(my, hostname, "ssluser1", NULL, schema,
port, socketname, 0), mysql_error(my));
FAIL_IF(!mysql_get_ssl_cipher(my), "No TLS connection");
@@ -985,11 +984,18 @@ static int test_openssl_1(MYSQL *mysql)
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
/* ssl_user1: connect with enforce should work */
/* ssl_user2: connect with enforce should work */
my= mysql_init(NULL);
mysql_options(my, MYSQL_OPT_SSL_ENFORCE, &val);
FAIL_IF(mysql_real_connect(my, hostname, "ssluser2", NULL, schema,
port, socketname, 0), "Error expected");
mysql_real_connect(my, hostname, "ssluser2", NULL, schema,
port, socketname, 0);
if (!mysql_error(my) &&
strcmp(mysql_get_ssl_cipher(my), "AES256-SHA"))
{
diag("Expected error or correct cipher");
return FAIL;
}
mysql_close(my);
/* ssl_user2: connect with cipher should work */
@@ -1158,8 +1164,57 @@ static int test_mdev14027(MYSQL *mysql __attribute__((unused)))
return OK;
}
static int test_mdev14101(MYSQL *my __attribute__((unused)))
{
struct {
bool do_yassl;
const char *opt_tls_version;
const char *expected;
} combinations[]= {
{1, "TLSv1.0", "TLSv1.0"},
{1, "TLSv1.1", "TLSv1.1"},
{1, "TLSv1,TLSv1.1", "TLSv1.1"},
{0, "TLSv1.2", "TLSv1.2"},
{0, NULL, "TLSv1.2"},
{0, "TLSv1.0,TLSv1.1,TLSv1.2", "TLSv1.2"},
{1, NULL, NULL}
};
int i;
#ifdef HAVE_SCHANNEL
bool skip_tlsv12= 1;
#else
bool skip_tlsv12= !have_openssl;
#endif
diag("%d %d", skip_tlsv12, have_openssl);
for (i=0; combinations[i].expected; i++)
{
MYSQL *mysql;
bool val=1;
char *tls_version;
if (!combinations[i].do_yassl && skip_tlsv12)
break;
mysql= mysql_init(NULL);
mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &val);
mysql_options(mysql, MARIADB_OPT_TLS_VERSION, combinations[i].opt_tls_version);
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0), mysql_error(mysql));
mariadb_get_infov(mysql, MARIADB_CONNECTION_TLS_VERSION, &tls_version);
diag("options: %s", combinations[i].opt_tls_version);
diag("protocol: %s", tls_version);
FAIL_IF(strcmp(combinations[i].expected, tls_version), "Wrong tls_version");
mysql_close(mysql);
}
return OK;
}
struct my_tests_st my_tests[] = {
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_mdev14101", test_mdev14101, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_mdev14027", test_mdev14027, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc286", test_conc286, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_ssl_timeout", test_ssl_timeout, TEST_CONNECTION_NEW, 0, NULL, NULL},