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

TLS fixes:

- don't use password in global context
  - load keys and certs via callback functions
  - don't use gnutls_bye since server is not able to detect dead socket
  - fixed valgrind errors in gnutls
This commit is contained in:
Georg Richter
2016-09-13 16:12:34 +02:00
parent 4ed1ca0c68
commit 9b436eaaf7
7 changed files with 387 additions and 110 deletions

View File

@@ -521,11 +521,11 @@ my_bool ma_pvio_start_ssl(MARIADB_PVIO *pvio)
ma_pvio_tls_verify_server_cert(pvio->ctls)) ma_pvio_tls_verify_server_cert(pvio->ctls))
return 1; return 1;
if (pvio->mysql->options.extension && if (pvio->mysql->options.extension &&
((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) || ((pvio->mysql->options.extension->tls_fp && pvio->mysql->options.extension->tls_fp[0]) ||
(pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0]))) (pvio->mysql->options.extension->tls_fp_list && pvio->mysql->options.extension->tls_fp_list[0])))
{ {
if (ma_pvio_tls_check_fp(pvio->ctls, if (ma_pvio_tls_check_fp(pvio->ctls,
pvio->mysql->options.extension->tls_fp, pvio->mysql->options.extension->tls_fp,
pvio->mysql->options.extension->tls_fp_list)) pvio->mysql->options.extension->tls_fp_list))

View File

@@ -124,10 +124,10 @@ static my_bool ma_pvio_tls_compare_fp(const char *fp1, unsigned int fp1_len,
{ {
char hexstr[64]; char hexstr[64];
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
if (fp1_len != fp2_len) if (fp1_len != fp2_len)
return 1; return 1;
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
#ifdef WIN32 #ifdef WIN32
if (strnicmp(hexstr, fp2, fp1_len) != 0) if (strnicmp(hexstr, fp2, fp1_len) != 0)
#else #else
@@ -140,10 +140,12 @@ static my_bool ma_pvio_tls_compare_fp(const char *fp1, unsigned int fp1_len,
my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list) my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_list)
{ {
unsigned int cert_fp_len= 64; unsigned int cert_fp_len= 64;
char cert_fp[64]; char *cert_fp= NULL;
my_bool rc=1; my_bool rc=1;
MYSQL *mysql= ctls->pvio->mysql; MYSQL *mysql= ctls->pvio->mysql;
cert_fp= (char *)malloc(cert_fp_len);
if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1) if ((cert_fp_len= ma_tls_get_finger_print(ctls, cert_fp, cert_fp_len)) < 1)
goto end; goto end;
if (fp) if (fp)
@@ -154,9 +156,7 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l
char buff[255]; char buff[255];
if (!(fp = ma_open(fp_list, "r", mysql))) if (!(fp = ma_open(fp_list, "r", mysql)))
{ goto end;
return 1;
}
while (ma_gets(buff, sizeof(buff)-1, fp)) while (ma_gets(buff, sizeof(buff)-1, fp))
{ {
@@ -181,6 +181,8 @@ my_bool ma_pvio_tls_check_fp(MARIADB_TLS *ctls, const char *fp, const char *fp_l
} }
end: end:
if (cert_fp)
free(cert_fp);
if (rc) if (rc)
{ {
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,

View File

@@ -21,6 +21,7 @@
#include <gnutls/gnutls.h> #include <gnutls/gnutls.h>
#include <gnutls/x509.h> #include <gnutls/x509.h>
#include <gnutls/abstract.h>
#include <ma_global.h> #include <ma_global.h>
#include <ma_sys.h> #include <ma_sys.h>
#include <ma_common.h> #include <ma_common.h>
@@ -39,6 +40,12 @@ extern unsigned int mariadb_deinitialize_ssl;
static int my_verify_callback(gnutls_session_t ssl); static int my_verify_callback(gnutls_session_t ssl);
struct st_gnutls_data {
MYSQL *mysql;
gnutls_privkey_t key;
gnutls_pcert_st cert;
};
struct st_cipher_map { struct st_cipher_map {
const char *openssl_name; const char *openssl_name;
const char *priority; const char *priority;
@@ -96,6 +103,20 @@ const struct st_cipher_map gtls_ciphers[]=
{NULL, NULL, 0, 0, 0} {NULL, NULL, 0, 0, 0}
}; };
/* free data assigned to the connection */
static void free_gnutls_data(struct st_gnutls_data *data)
{
if (data)
{
if (data->key)
gnutls_privkey_deinit(data->key);
gnutls_pcert_deinit(&data->cert);
free(data);
}
}
/* map the gnutls cipher suite (defined by key exchange algorithm, cipher
and mac algorithm) to the corresponding OpenSSL cipher name */
static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx, static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx,
gnutls_cipher_algorithm_t cipher, gnutls_cipher_algorithm_t cipher,
gnutls_mac_algorithm_t mac) gnutls_mac_algorithm_t mac)
@@ -112,6 +133,7 @@ static const char *openssl_cipher_name(gnutls_kx_algorithm_t kx,
return NULL; return NULL;
} }
/* get priority string for a given openssl cipher name */
static const char *get_priority(const char *cipher_name) static const char *get_priority(const char *cipher_name)
{ {
unsigned int i= 0; unsigned int i= 0;
@@ -126,7 +148,7 @@ static const char *get_priority(const char *cipher_name)
#define MAX_SSL_ERR_LEN 100 #define MAX_SSL_ERR_LEN 100
static void ma_tls_set_error(MYSQL *mysql, int ssl_errno) static void ma_tls_set_error(MYSQL *mysql, void *ssl, int ssl_errno)
{ {
char ssl_error[MAX_SSL_ERR_LEN]; char ssl_error[MAX_SSL_ERR_LEN];
const char *ssl_error_reason; const char *ssl_error_reason;
@@ -137,6 +159,21 @@ static void ma_tls_set_error(MYSQL *mysql, int ssl_errno)
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error"); pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return; return;
} }
/* give a more descriptive error message for alerts */
if (ssl_errno == GNUTLS_E_FATAL_ALERT_RECEIVED)
{
gnutls_alert_description_t alert_desc;
const char *alert_name;
alert_desc= gnutls_alert_get((gnutls_session_t)ssl);
alert_name= gnutls_alert_get_name(alert_desc);
snprintf(ssl_error, MAX_SSL_ERR_LEN, "fatal alert received: %s",
alert_name);
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
ssl_error);
return;
}
if ((ssl_error_reason= gnutls_strerror(ssl_errno))) if ((ssl_error_reason= gnutls_strerror(ssl_errno)))
{ {
pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0, pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 0,
@@ -249,15 +286,7 @@ static int ma_gnutls_set_ciphers(gnutls_session_t ssl, char *cipher_str)
while (token) while (token)
{ {
const char *p= get_priority(token); const char *p= get_priority(token);
/* if cipher was not found, we pass the original token to if (p)
the priority string, this will allow to specify gnutls
specific settings via cipher */
if (!p)
{
strncat(prio, ":", PRIO_SIZE - strlen(prio) - 1);
strncat(prio, token, PRIO_SIZE - strlen(prio) - 1);
}
else
strncat(prio, p, PRIO_SIZE - strlen(prio) - 1); strncat(prio, p, PRIO_SIZE - strlen(prio) - 1);
token = strtok(NULL, ":"); token = strtok(NULL, ":");
} }
@@ -266,8 +295,6 @@ static int ma_gnutls_set_ciphers(gnutls_session_t ssl, char *cipher_str)
static int ma_tls_set_certs(MYSQL *mysql) static int ma_tls_set_certs(MYSQL *mysql)
{ {
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
int ssl_error= 0; int ssl_error= 0;
if (mysql->options.ssl_ca) if (mysql->options.ssl_ca)
@@ -282,32 +309,78 @@ static int ma_tls_set_certs(MYSQL *mysql)
gnutls_certificate_set_verify_function(GNUTLS_xcred, gnutls_certificate_set_verify_function(GNUTLS_xcred,
my_verify_callback); my_verify_callback);
/* GNUTLS doesn't support ca_path */
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set key */
if (certfile || keyfile)
{
if ((ssl_error= gnutls_certificate_set_x509_key_file2(GNUTLS_xcred,
certfile, keyfile, GNUTLS_X509_FMT_PEM,
OPT_HAS_EXT_VAL(mysql, tls_pw) ? mysql->options.extension->tls_pw : NULL,
0)) < 0)
goto error;
}
return 1; return 1;
error: error:
return ssl_error; return ssl_error;
} }
static int
client_cert_callback(gnutls_session_t session,
const gnutls_datum_t * req_ca_rdn __attribute__((unused)),
int nreqs __attribute__((unused)),
const gnutls_pk_algorithm_t * sign_algos __attribute__((unused)),
int sign_algos_length __attribute__((unused)),
gnutls_pcert_st ** pcert,
unsigned int *pcert_length, gnutls_privkey_t * pkey)
{
struct st_gnutls_data *session_data;
char *certfile, *keyfile;
gnutls_datum_t data;
MYSQL *mysql;
gnutls_certificate_type_t type= gnutls_certificate_type_get(session);
session_data= (struct st_gnutls_data *)gnutls_session_get_ptr(session);
if (!session_data->mysql ||
type != GNUTLS_CRT_X509)
return -1;
mysql= session_data->mysql;
certfile= session_data->mysql->options.ssl_cert;
keyfile= session_data->mysql->options.ssl_key;
if (!certfile && !keyfile)
return 0;
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
if (gnutls_load_file(certfile, &data) < 0)
return -1;
if (gnutls_pcert_import_x509_raw(&session_data->cert, &data, GNUTLS_X509_FMT_PEM, 0) < 0)
{
gnutls_free(data.data);
return -1;
}
gnutls_free(data.data);
if (gnutls_load_file(keyfile, &data) < 0)
return -1;
gnutls_privkey_init(&session_data->key);
if (gnutls_privkey_import_x509_raw(session_data->key, &data,
GNUTLS_X509_FMT_PEM,
mysql->options.extension ? mysql->options.extension->tls_pw : NULL,
0) < 0)
{
gnutls_free(data.data);
return -1;
}
gnutls_free(data.data);
*pcert_length= 1;
*pcert= &session_data->cert;
*pkey= session_data->key;
return 0;
}
void *ma_tls_init(MYSQL *mysql) void *ma_tls_init(MYSQL *mysql)
{ {
gnutls_session_t ssl= NULL; gnutls_session_t ssl= NULL;
int ssl_error= 0; int ssl_error= 0;
struct st_gnutls_data *data= NULL;
pthread_mutex_lock(&LOCK_gnutls_config); pthread_mutex_lock(&LOCK_gnutls_config);
@@ -316,19 +389,28 @@ void *ma_tls_init(MYSQL *mysql)
if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0) if ((ssl_error = gnutls_init(&ssl, GNUTLS_CLIENT & GNUTLS_NONBLOCK)) < 0)
goto error; goto error;
gnutls_session_set_ptr(ssl, (void *)mysql);
if (!(data= (struct st_gnutls_data *)calloc(1, sizeof(struct st_gnutls_data))))
goto error;
data->mysql= mysql;
gnutls_certificate_set_retrieve_function2(GNUTLS_xcred, client_cert_callback);
gnutls_session_set_ptr(ssl, (void *)data);
ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher); ssl_error= ma_gnutls_set_ciphers(ssl, mysql->options.ssl_cipher);
if (ssl_error < 0) if (ssl_error < 0)
goto error; goto error;
/* we don't load private key and cert by default - if the server requests
a client certificate we will send it via callback function */
if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0) if ((ssl_error= gnutls_credentials_set(ssl, GNUTLS_CRD_CERTIFICATE, GNUTLS_xcred)) < 0)
goto error; goto error;
pthread_mutex_unlock(&LOCK_gnutls_config); pthread_mutex_unlock(&LOCK_gnutls_config);
return (void *)ssl; return (void *)ssl;
error: error:
ma_tls_set_error(mysql, ssl_error); free_gnutls_data(data);
ma_tls_set_error(mysql, ssl, ssl_error);
if (ssl) if (ssl)
gnutls_deinit(ssl); gnutls_deinit(ssl);
pthread_mutex_unlock(&LOCK_gnutls_config); pthread_mutex_unlock(&LOCK_gnutls_config);
@@ -362,7 +444,9 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
MYSQL *mysql; MYSQL *mysql;
MARIADB_PVIO *pvio; MARIADB_PVIO *pvio;
int ret; int ret;
mysql= (MYSQL *)gnutls_session_get_ptr(ssl); struct st_gnutls_data *data;
data= (struct st_gnutls_data *)gnutls_session_get_ptr(ssl);
mysql= data->mysql;
if (!mysql) if (!mysql)
return 1; return 1;
@@ -386,16 +470,16 @@ my_bool ma_tls_connect(MARIADB_TLS *ctls)
if (ret < 0) if (ret < 0)
{ {
ma_tls_set_error(mysql, ret); ma_tls_set_error(mysql, ssl, ret);
/* restore blocking mode */ /* restore blocking mode */
gnutls_deinit((gnutls_session_t )ctls->ssl); gnutls_deinit((gnutls_session_t )ctls->ssl);
free_gnutls_data(data);
ctls->ssl= NULL; ctls->ssl= NULL;
if (!blocking) if (!blocking)
pvio->methods->blocking(pvio, FALSE, 0); pvio->methods->blocking(pvio, FALSE, 0);
return 1; return 1;
} }
ctls->ssl= (void *)ssl; ctls->ssl= (void *)ssl;
return 0; return 0;
} }
@@ -413,7 +497,15 @@ my_bool ma_tls_close(MARIADB_TLS *ctls)
{ {
if (ctls->ssl) if (ctls->ssl)
{ {
gnutls_bye((gnutls_session_t )ctls->ssl, GNUTLS_SHUT_WR); MARIADB_PVIO *pvio= ctls->pvio;
struct st_gnutls_data *data=
(struct st_gnutls_data *)gnutls_session_get_ptr(ctls->ssl);
/* this would be the correct way, however can't dectect afterwards
if the socket is closed or not, so we don't send encrypted
finish alert.
rc= gnutls_bye((gnutls_session_t )ctls->ssl, GNUTLS_SHUT_WR);
*/
free_gnutls_data(data);
gnutls_deinit((gnutls_session_t )ctls->ssl); gnutls_deinit((gnutls_session_t )ctls->ssl);
ctls->ssl= NULL; ctls->ssl= NULL;
} }
@@ -443,14 +535,19 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
static int my_verify_callback(gnutls_session_t ssl) static int my_verify_callback(gnutls_session_t ssl)
{ {
unsigned int status; unsigned int status= 0;
const gnutls_datum_t *cert_list; const gnutls_datum_t *cert_list;
unsigned int cert_list_size; unsigned int cert_list_size;
MYSQL *mysql= (MYSQL *)gnutls_session_get_ptr(ssl); struct st_gnutls_data *data= (struct st_gnutls_data *)gnutls_session_get_ptr(ssl);
MARIADB_PVIO *pvio= mysql->net.pvio; MYSQL *mysql;
MARIADB_PVIO *pvio;
gnutls_x509_crt_t cert; gnutls_x509_crt_t cert;
const char *hostname; const char *hostname;
mysql= data->mysql;
pvio= mysql->net.pvio;
/* read hostname */ /* read hostname */
hostname = mysql->host; hostname = mysql->host;
@@ -518,11 +615,13 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
size_t fp_len= len; size_t fp_len= len;
const gnutls_datum_t *cert_list; const gnutls_datum_t *cert_list;
unsigned int cert_list_size; unsigned int cert_list_size;
struct st_gnutls_data *data;
if (!ctls || !ctls->ssl) if (!ctls || !ctls->ssl)
return 0; return 0;
mysql= (MYSQL *)gnutls_session_get_ptr(ctls->ssl); data= (struct st_gnutls_data *)gnutls_session_get_ptr(ctls->ssl);
mysql= (MYSQL *)data->mysql;
cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size); cert_list = gnutls_certificate_get_peers (ctls->ssl, &cert_list_size);
if (cert_list == NULL) if (cert_list == NULL)

View File

@@ -30,7 +30,12 @@
#include <openssl/conf.h> #include <openssl/conf.h>
#include <openssl/md4.h> #include <openssl/md4.h>
#define HAVE_TLS_SESSION_CACHE 1 #ifdef HAVE_TLS_SESSION_CACHE
#undef HAVE_TLS_SESSION_CACHE
#endif
#if OPENSSL_USE_BIOMETHOD
#undef OPENSSL_USE_BIOMETHOD
#endif
#ifndef HAVE_OPENSSL_DEFAULT #ifndef HAVE_OPENSSL_DEFAULT
#include <memory.h> #include <memory.h>
#define ma_malloc(A,B) malloc((A)) #define ma_malloc(A,B) malloc((A))
@@ -329,6 +334,7 @@ int ma_tls_start(char *errmsg, size_t errmsg_len)
ma_tls_get_error(errmsg, errmsg_len); ma_tls_get_error(errmsg, errmsg_len);
goto end; goto end;
} }
SSL_CTX_set_options(SSL_context, SSL_OP_ALL);
#ifdef HAVE_TLS_SESSION_CACHE #ifdef HAVE_TLS_SESSION_CACHE
SSL_CTX_set_session_cache_mode(SSL_context, SSL_SESS_CACHE_CLIENT); SSL_CTX_set_session_cache_mode(SSL_context, SSL_SESS_CACHE_CLIENT);
ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size); ma_tls_sessions= (MA_SSL_SESSION *)calloc(1, sizeof(struct st_ma_tls_session) * ma_tls_session_cache_size);
@@ -411,15 +417,18 @@ int ma_tls_get_password(char *buf, int size,
} }
static int ma_tls_set_certs(MYSQL *mysql) static int ma_tls_set_certs(MYSQL *mysql, SSL *ssl)
{ {
char *certfile= mysql->options.ssl_cert, char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key; *keyfile= mysql->options.ssl_key;
char *pw= (mysql->options.extension) ?
mysql->options.extension->tls_pw : NULL;
/* add cipher */ /* add cipher */
if ((mysql->options.ssl_cipher && if ((mysql->options.ssl_cipher &&
mysql->options.ssl_cipher[0] != 0) && mysql->options.ssl_cipher[0] != 0) &&
SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0) SSL_set_cipher_list(ssl, mysql->options.ssl_cipher) == 0)
goto error; goto error;
/* ca_file and ca_path */ /* ca_file and ca_path */
@@ -440,34 +449,36 @@ static int ma_tls_set_certs(MYSQL *mysql)
/* set cert */ /* set cert */
if (certfile && certfile[0] != 0) if (certfile && certfile[0] != 0)
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
goto error;
/* If the private key file is encrypted, we need to register a callback function
* for providing password. */
if (OPT_HAS_EXT_VAL(mysql, tls_pw))
{ {
SSL_CTX_set_default_passwd_cb_userdata(SSL_context, (void *)mysql->options.extension->tls_pw); if (SSL_CTX_use_certificate_chain_file(SSL_context, certfile) != 1 ||
SSL_CTX_set_default_passwd_cb(SSL_context, ma_tls_get_password); SSL_use_certificate_file(ssl, certfile, SSL_FILETYPE_PEM) != 1)
goto error;
} }
if (keyfile && keyfile[0]) if (keyfile && keyfile[0])
{ {
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1) FILE *fp;
if ((fp= fopen(keyfile, "rb")))
{
EVP_PKEY *key= EVP_PKEY_new();
PEM_read_PrivateKey(fp, &key, NULL, pw);
fclose(fp);
if (SSL_use_PrivateKey(ssl, key) != 1)
{ {
unsigned long err= ERR_peek_error(); unsigned long err= ERR_peek_error();
EVP_PKEY_free(key);
if (!(ERR_GET_LIB(err) == ERR_LIB_X509 && if (!(ERR_GET_LIB(err) == ERR_LIB_X509 &&
ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE))
goto error; goto error;
} }
EVP_PKEY_free(key);
} else {
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
CER(CR_FILE_NOT_FOUND), keyfile);
return 1;
} }
if (OPT_HAS_EXT_VAL(mysql, tls_pw))
{
SSL_CTX_set_default_passwd_cb_userdata(SSL_context, NULL);
SSL_CTX_set_default_passwd_cb(SSL_context, NULL);
} }
/* verify key */ /* verify key */
if (certfile && !SSL_CTX_check_private_key(SSL_context)) if (certfile && !SSL_check_private_key(ssl))
goto error; goto error;
if (mysql->options.extension && if (mysql->options.extension &&
@@ -499,14 +510,14 @@ void *ma_tls_init(MYSQL *mysql)
#endif #endif
pthread_mutex_lock(&LOCK_openssl_config); pthread_mutex_lock(&LOCK_openssl_config);
if (ma_tls_set_certs(mysql)) if (!(ssl= SSL_new(SSL_context)))
goto error;
if (ma_tls_set_certs(mysql, ssl))
{ {
goto error; goto error;
} }
if (!(ssl= SSL_new(SSL_context)))
goto error;
if (!SSL_set_app_data(ssl, mysql)) if (!SSL_set_app_data(ssl, mysql))
goto error; goto error;
@@ -703,8 +714,7 @@ const char *ma_tls_get_cipher(MARIADB_TLS *ctls)
unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len) unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int len)
{ {
EVP_MD *digest= (EVP_MD *)EVP_sha1(); X509 *cert= NULL;
X509 *cert;
MYSQL *mysql; MYSQL *mysql;
unsigned int fp_len; unsigned int fp_len;
@@ -718,7 +728,7 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ER(CR_SSL_CONNECTION_ERROR),
"Unable to get server certificate"); "Unable to get server certificate");
return 0; goto end;
} }
if (len < EVP_MAX_MD_SIZE) if (len < EVP_MAX_MD_SIZE)
@@ -726,18 +736,21 @@ unsigned int ma_tls_get_finger_print(MARIADB_TLS *ctls, char *fp, unsigned int l
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, 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"); "Finger print buffer too small");
return 0; goto end;
} }
fp_len= len; if (!X509_digest(cert, EVP_sha1(), (unsigned char *)fp, &fp_len))
if (!X509_digest(cert, digest, (unsigned char *)fp, &fp_len))
{ {
ma_free(fp);
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ER(CR_SSL_CONNECTION_ERROR),
"invalid finger print of server certificate"); "invalid finger print of server certificate");
return 0; goto end;
} }
X509_free(cert);
return (fp_len); return (fp_len);
end:
X509_free(cert);
return 0;
} }

View File

@@ -59,13 +59,13 @@ IF(WITH_SSL)
STRING(REPLACE "SHA1 Fingerprint=" "" FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE "SHA1 Fingerprint=" "" FINGER_PRINT "${FINGER_PRINT}")
STRING(REPLACE "\n" "" FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE "\n" "" FINGER_PRINT "${FINGER_PRINT}")
STRING(REPLACE ":" "" SSL_CERT_FINGER_PRINT "${FINGER_PRINT}") STRING(REPLACE ":" "" SSL_CERT_FINGER_PRINT "${FINGER_PRINT}")
ENDIF()
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
${PROJECT_SOURCE_DIR}/unittest/libmariadb/ssl.c)
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/unittest/libmariadb/fingerprint.list.in
${PROJECT_SOURCE_DIR}/unittest/libmariadb/fingerprint.list) ${PROJECT_SOURCE_DIR}/unittest/libmariadb/fingerprint.list)
ENDIF()
SET(API_TESTS ${API_TESTS} "ssl") SET(API_TESTS ${API_TESTS} "ssl")
CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/unittest/libmariadb/ssl.c.in
${PROJECT_SOURCE_DIR}/unittest/libmariadb/ssl.c)
ENDIF() ENDIF()
ADD_LIBRARY(ma_getopt ma_getopt.c) ADD_LIBRARY(ma_getopt ma_getopt.c)

View File

@@ -555,6 +555,5 @@ void run_tests(struct my_tests_st *test) {
diag("close default"); diag("close default");
mysql_close(mysql_default); mysql_close(mysql_default);
} }
mysql_server_end();
} }

View File

@@ -173,9 +173,9 @@ static int test_conc95(MYSQL *unused __attribute__((unused)))
mysql= mysql_init(NULL); mysql= mysql_init(NULL);
mysql_ssl_set(mysql, mysql_ssl_set(mysql,
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-key.pem", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/server-cert.pem", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", NULL,
NULL, NULL,
NULL); NULL);
@@ -541,13 +541,14 @@ static int verify_ssl_server_cert(MYSQL *unused __attribute__((unused)))
mysql= mysql_init(NULL); mysql= mysql_init(NULL);
FAIL_IF(!mysql, "Can't allocate memory"); FAIL_IF(!mysql, "Can't allocate memory");
mysql_ssl_set(mysql, NULL, NULL, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem", NULL, NULL); mysql_ssl_set(mysql, NULL, NULL, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/cacert.pem", NULL, NULL);
mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify); mysql_options(mysql, MYSQL_OPT_SSL_VERIFY_SERVER_CERT, &verify);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0); port, socketname, 0);
FAIL_IF(!mysql_errno(mysql), "Expected error"); FAIL_IF(!mysql_errno(mysql), "Expected error");
diag("Error (expected): %s", mysql_error(mysql));
mysql_close(mysql); mysql_close(mysql);
return OK; return OK;
@@ -758,8 +759,13 @@ static int test_ssl_fp_list(MYSQL *unused __attribute__((unused)))
mysql_options(my, MARIADB_OPT_SSL_FP_LIST, "./fingerprint.list"); mysql_options(my, MARIADB_OPT_SSL_FP_LIST, "./fingerprint.list");
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, if(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
port, socketname, 0), mysql_error(my)); port, socketname, 0))
{
diag("Error: %s", mysql_error(my));
mysql_close(my);
return FAIL;
}
FAIL_IF(check_cipher(my) != 0, "Invalid cipher"); FAIL_IF(check_cipher(my) != 0, "Invalid cipher");
mysql_close(my); mysql_close(my);
@@ -814,7 +820,6 @@ static int test_schannel_cipher(MYSQL *unused __attribute__((unused)))
port, socketname, 0), mysql_error(my)); port, socketname, 0), mysql_error(my));
diag("cipher: %s", mysql_get_ssl_cipher(my)); diag("cipher: %s", mysql_get_ssl_cipher(my));
FAIL_IF(strcmp(mysql_get_ssl_cipher(my), "CALG_AES_256") != 0, "expected cipher with 256bit strength");
mysql_close(my); mysql_close(my);
@@ -823,7 +828,7 @@ static int test_schannel_cipher(MYSQL *unused __attribute__((unused)))
#endif #endif
#ifdef HAVE_GNUTLS #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)
static int test_cipher_mapping(MYSQL *unused __attribute__((unused))) static int test_cipher_mapping(MYSQL *unused __attribute__((unused)))
{ {
@@ -847,16 +852,22 @@ static int test_cipher_mapping(MYSQL *unused __attribute__((unused)))
MYSQL_RES *res; MYSQL_RES *res;
char c[100]; char c[100];
int rc; int rc;
mysql_options(mysql, MYSQL_OPT_SSL_CIPHER, ciphers[i]);
mysql_ssl_set(mysql, NULL, NULL, NULL, NULL, ciphers[i]);
diag("%s", ciphers[i]); diag("%s", ciphers[i]);
mysql->options.use_ssl= 1;
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema, FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0), mysql_error(mysql)); port, socketname, 0), mysql_error(mysql));
if (strcmp(ciphers[i], mysql_get_ssl_cipher(mysql)) != 0) if (!mysql_get_ssl_cipher(mysql) ||
strcmp(ciphers[i], mysql_get_ssl_cipher(mysql)) != 0)
{ {
diag("expected: %s instead of %s", ciphers[i], mysql_get_ssl_cipher(mysql)); diag("cipher %s failed", ciphers[i]);
mysql_close(mysql); mysql_close(mysql);
return FAIL; return FAIL;
} }
else
{
rc= mysql_query(mysql, "SHOW STATUS LIKE 'ssl_cipher'"); rc= mysql_query(mysql, "SHOW STATUS LIKE 'ssl_cipher'");
check_mysql_rc(rc, mysql); check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql); res= mysql_store_result(mysql);
@@ -869,22 +880,176 @@ static int test_cipher_mapping(MYSQL *unused __attribute__((unused)))
diag("expected: %s instead of %s", ciphers[i], c); diag("expected: %s instead of %s", ciphers[i], c);
return FAIL; return FAIL;
} }
}
i++; i++;
} }
return OK; return OK;
} }
#endif #endif
static int test_openssl_1(MYSQL *mysql)
{
int rc;
MYSQL *my;
uchar val= 1;
char query[1024];
int i;
if (check_skip_ssl())
return SKIP;
for (i=1; i < 6; i++)
{
sprintf(query, "DROP USER IF EXISTS 'ssluser%d'@'%s'", i, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
sprintf(query, "CREATE USER 'ssluser%d'@'%s'", i, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
}
rc= mysql_query(mysql, "FLUSH PRIVILEGES");
check_mysql_rc(rc, mysql);
sprintf(query, "grant select on %s.* to 'ssluser1'@'%s' require ssl", schema, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
my= mysql_init(NULL);
mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA");
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");
mysql_close(my);
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");
mysql_close(my);
sprintf(query, "grant select on %s.* to 'ssluser2'@'%s' require cipher 'AES256-SHA'", schema, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
/* ssl_user1: 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_close(my);
/* ssl_user2: connect with cipher should work */
my= mysql_init(NULL);
mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES128-SHA");
FAIL_IF(mysql_real_connect(my, hostname, "ssluser2", NULL, schema,
port, socketname, 0), "Error expected");
mysql_close(my);
/* ssl_user2: connect with correct cipher */
my= mysql_init(NULL);
mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA");
FAIL_IF(!mysql_real_connect(my, hostname, "ssluser2", NULL, schema,
port, socketname, 0), mysql_error(my));
FAIL_IF(strcmp("AES256-SHA", mysql_get_ssl_cipher(my)) != 0, "expected cipher AES256-SHA");
mysql_close(my);
sprintf(query, "grant select on %s.* to 'ssluser3'@'%s' require cipher 'AES256-SHA' AND "
" SUBJECT '/DC=com/DC=example/CN=client'", schema, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
/* ssluser3: connect with cipher only */
my= mysql_init(NULL);
mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA");
FAIL_IF(mysql_real_connect(my, hostname, "ssluser3", NULL, schema,
port, socketname, 0), "Error expected");
mysql_close(my);
/* ssluser3 connect with cipher and certs */
my= mysql_init(NULL);
mysql_ssl_set(my, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem",
NULL,
"AES256-SHA");
FAIL_IF(!mysql_real_connect(my, hostname, "ssluser3", NULL, schema,
port, socketname, 0), mysql_error(my));
mysql_close(my);
sprintf(query, "grant select on %s.* to 'ssluser4'@'%s' require cipher 'AES256-SHA' AND "
" ISSUER '/DC=com/DC=example/CN=client'", schema, sslhost);
rc= mysql_query(mysql, query);
check_mysql_rc(rc, mysql);
/* ssluser4: connect with cipher only */
my= mysql_init(NULL);
mysql_ssl_set(my, NULL, NULL, NULL, NULL, "AES256-SHA");
FAIL_IF(mysql_real_connect(my, hostname, "ssluser4", NULL, schema,
port, socketname, 0), "Error expected");
mysql_close(my);
/* ssluser4 connect with cipher and certs */
my= mysql_init(NULL);
mysql_ssl_set(my, "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-key.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem",
"@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem",
NULL,
"AES256-SHA");
FAIL_IF(!mysql_real_connect(my, hostname, "ssluser4", NULL, schema,
port, socketname, 0), mysql_error(my));
mysql_close(my);
return OK;
}
static int test_ssl_timeout(MYSQL *unused __attribute__((unused)))
{
MYSQL *mysql;
my_bool enforce= 1;
int read_timeout= 1;
int rc;
if (check_skip_ssl())
return SKIP;
mysql= mysql_init(NULL);
mysql_options(mysql, MYSQL_OPT_SSL_ENFORCE, &enforce);
mysql_options(mysql, MYSQL_OPT_READ_TIMEOUT, &read_timeout);
mysql->options.use_ssl= 1;
FAIL_IF(!mysql_real_connect(mysql, hostname, username, password, schema,
port, socketname, 0), mysql_error(mysql));
diag("cipher: %s\n", mysql_get_ssl_cipher(mysql));
rc= mysql_query(mysql, "SELECT SLEEP(600)");
if (!rc)
{
diag("error expected (timeout)");
return FAIL;
}
mysql_close(mysql);
return OK;
}
struct my_tests_st my_tests[] = { struct my_tests_st my_tests[] = {
{"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_ssl", test_ssl, TEST_CONNECTION_NEW, 0, NULL, NULL},
#ifdef HAVE_GNUTLS {"test_ssl_timeout", test_ssl_timeout, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_openssl_1", test_openssl_1, TEST_CONNECTION_NEW, 0, NULL, NULL},
#ifndef HAVE_SCHANNEL
{"test_cipher_mapping", test_cipher_mapping, TEST_CONNECTION_NONE, 0, NULL, NULL}, {"test_cipher_mapping", test_cipher_mapping, TEST_CONNECTION_NONE, 0, NULL, NULL},
#endif #endif
{"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc127", test_conc127, TEST_CONNECTION_NEW, 0, NULL, NULL},
/* Both tests work with GNU tls, however we can't create fingerprints with
gnutls-cli in CMakeLists.txt */
#ifdef HAVE_OPENSSL
{"test_ssl_fp", test_ssl_fp, 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_ssl_fp_list", test_ssl_fp_list, TEST_CONNECTION_NEW, 0, NULL, NULL},
#endif
{"test_conc50", test_conc50, 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_1", test_conc50_1, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc50_2", test_conc50_2, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc50_2", test_conc50_2, TEST_CONNECTION_NEW, 0, NULL, NULL},
@@ -914,7 +1079,6 @@ int main(int argc, char **argv)
if (argc > 1) if (argc > 1)
get_options(argc, argv); get_options(argc, argv);
run_tests(my_tests); run_tests(my_tests);
mysql_server_end(); mysql_server_end();