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

Various ssl and schannel fixes

This commit is contained in:
Georg Richter
2015-09-10 17:16:21 +02:00
parent 0cc8a36454
commit 4597cd6a80
20 changed files with 731 additions and 2373 deletions

View File

@@ -1,45 +0,0 @@
/************************************************************************************
Copyright (C) 2012 Monty Program 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
*************************************************************************************/
#ifndef _ma_secure_h_
#define _ma_secure_h_
#ifdef HAVE_OPENSSL
#include <mysql.h>
#include <openssl/ssl.h> /* SSL and SSL_CTX */
#include <openssl/err.h> /* error reporting */
#include <openssl/conf.h>
struct MYSQL;
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size);
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 ma_ssl_verify_fingerprint(SSL *ssl);
int my_ssl_start(MYSQL *mysql);
void my_ssl_end(void);
#endif /* HAVE_OPENSSL */
#endif /* _ma_secure_h_ */

View File

@@ -133,5 +133,6 @@ my_bool ma_cio_ssl_close(MARIADB_SSL *cssl);
int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl); int ma_cio_ssl_verify_server_cert(MARIADB_SSL *cssl);
const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl); const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl);
my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list); my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_list);
my_bool ma_cio_start_ssl(MARIADB_CIO *cio);
#endif /* _ma_ssl_h_ */ #endif /* _ma_ssl_h_ */

View File

@@ -1145,13 +1145,15 @@ int STDCALL
mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert, mysql_ssl_set(MYSQL *mysql, const char *key, const char *cert,
const char *ca, const char *capath, const char *cipher) const char *ca, const char *capath, const char *cipher)
{ {
mysql->options.ssl_key = key==0 ? 0 : my_strdup(key,MYF(0)); #ifdef HAVE_SSL
mysql->options.ssl_cert = cert==0 ? 0 : my_strdup(cert,MYF(0)); return (mysql_optionsv(mysql, MYSQL_OPT_SSL_KEY, key) |
mysql->options.ssl_ca = ca==0 ? 0 : my_strdup(ca,MYF(0)); mysql_optionsv(mysql, MYSQL_OPT_SSL_CERT, cert) |
mysql->options.ssl_capath = capath==0 ? 0 : my_strdup(capath,MYF(0)); mysql_optionsv(mysql, MYSQL_OPT_SSL_CA, ca) |
mysql->options.ssl_cipher = cipher==0 ? 0 : my_strdup(cipher,MYF(0)); mysql_optionsv(mysql, MYSQL_OPT_SSL_CAPATH, capath) |
/* todo: add crl stuff */ mysql_optionsv(mysql, MYSQL_OPT_SSL_CIPHER, cipher)) ? 1 : 0;
#else
return 0; return 0;
#endif
} }
/************************************************************************** /**************************************************************************
@@ -2674,23 +2676,23 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
break; break;
case MYSQL_OPT_SSL_KEY: case MYSQL_OPT_SSL_KEY:
my_free(mysql->options.ssl_key); my_free(mysql->options.ssl_key);
mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME)); mysql->options.ssl_key=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break; break;
case MYSQL_OPT_SSL_CERT: case MYSQL_OPT_SSL_CERT:
my_free(mysql->options.ssl_cert); my_free(mysql->options.ssl_cert);
mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME)); mysql->options.ssl_cert=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break; break;
case MYSQL_OPT_SSL_CA: case MYSQL_OPT_SSL_CA:
my_free(mysql->options.ssl_ca); my_free(mysql->options.ssl_ca);
mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME)); mysql->options.ssl_ca=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break; break;
case MYSQL_OPT_SSL_CAPATH: case MYSQL_OPT_SSL_CAPATH:
my_free(mysql->options.ssl_capath); my_free(mysql->options.ssl_capath);
mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME)); mysql->options.ssl_capath=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break; break;
case MYSQL_OPT_SSL_CIPHER: case MYSQL_OPT_SSL_CIPHER:
my_free(mysql->options.ssl_cipher); my_free(mysql->options.ssl_cipher);
mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME)); mysql->options.ssl_cipher=my_strdup((char *)arg1,MYF(MY_WME | MY_ALLOW_ZERO_PTR));
break; break;
case MYSQL_OPT_SSL_CRL: case MYSQL_OPT_SSL_CRL:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1); OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_crl, (char *)arg1);
@@ -2789,6 +2791,12 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
my_free(mysql->options.bind_address); my_free(mysql->options.bind_address);
mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME)); mysql->options.bind_address= my_strdup(arg1, MYF(MY_WME));
break; break;
case MARIADB_OPT_SSL_FP:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp, (char *)arg1);
break;
case MARIADB_OPT_SSL_FP_LIST:
OPT_SET_EXTENDED_VALUE_STR(&mysql->options, ssl_fp_list, (char *)arg1);
break;
default: default:
va_end(ap); va_end(ap);
DBUG_RETURN(-1); DBUG_RETURN(-1);

View File

@@ -283,7 +283,7 @@ size_t ma_cio_cache_read(MARIADB_CIO *cio, uchar *buffer, size_t length)
} }
memcpy(buffer, cio->cache, r); memcpy(buffer, cio->cache, r);
} }
} }
return r; return r;
} }
/* }}} */ /* }}} */

View File

@@ -1,688 +0,0 @@
/************************************************************************************
Copyright (C) 2012 Monty Program 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
*************************************************************************************/
unsigned int mariadb_deinitialize_ssl= 1;
#ifdef HAVE_OPENSSL
#include <my_global.h>
#include <my_sys.h>
#include <ma_common.h>
#include <ma_secure.h>
#include <errmsg.h>
#include <violite.h>
#include <mysql_async.h>
#include <my_context.h>
static my_bool my_ssl_initialized= FALSE;
static SSL_CTX *SSL_context= NULL;
#define MAX_SSL_ERR_LEN 100
extern pthread_mutex_t LOCK_ssl_config;
static pthread_mutex_t *LOCK_crypto= NULL;
/*
SSL error handling
*/
static void my_SSL_error(MYSQL *mysql)
{
ulong ssl_errno= ERR_get_error();
char ssl_error[MAX_SSL_ERR_LEN];
const char *ssl_error_reason;
DBUG_ENTER("my_SSL_error");
if (mysql_errno(mysql))
DBUG_VOID_RETURN;
if (!ssl_errno)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
DBUG_VOID_RETURN;
}
if ((ssl_error_reason= ERR_reason_error_string(ssl_errno)))
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ssl_error_reason);
DBUG_VOID_RETURN;
}
my_snprintf(ssl_error, MAX_SSL_ERR_LEN, "SSL errno=%lu", ssl_errno, mysql->charset);
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), ssl_error);
DBUG_VOID_RETURN;
}
/*
thread safe callbacks for OpenSSL
Crypto call back functions will be
set during ssl_initialization
*/
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
static unsigned long my_cb_threadid(void)
{
/* cast pthread_t to unsigned long */
return (unsigned long) pthread_self();
}
#else
static void my_cb_threadid(CRYPTO_THREADID *id)
{
CRYPTO_THREADID_set_numeric(id, (unsigned long)pthread_self());
}
#endif
static void my_cb_locking(int mode, int n, const char *file, int line)
{
if (mode & CRYPTO_LOCK)
pthread_mutex_lock(&LOCK_crypto[n]);
else
pthread_mutex_unlock(&LOCK_crypto[n]);
}
static int ssl_crypto_init()
{
int i, rc= 1, max= CRYPTO_num_locks();
#if (OPENSSL_VERSION_NUMBER < 0x10000000)
CRYPTO_set_id_callback(my_cb_threadid);
#else
rc= CRYPTO_THREADID_set_callback(my_cb_threadid);
#endif
/* if someone else already set callbacks
* there is nothing do */
if (!rc)
return 0;
if (LOCK_crypto == NULL)
{
if (!(LOCK_crypto=
(pthread_mutex_t *)my_malloc(sizeof(pthread_mutex_t) * max, MYF(0))))
return 1;
for (i=0; i < max; i++)
pthread_mutex_init(&LOCK_crypto[i], NULL);
}
CRYPTO_set_locking_callback(my_cb_locking);
return 0;
}
/*
Initializes SSL and allocate global
context SSL_context
SYNOPSIS
my_ssl_start
mysql connection handle
RETURN VALUES
0 success
1 error
*/
int my_ssl_start(MYSQL *mysql)
{
int rc= 0;
DBUG_ENTER("my_ssl_start");
/* lock mutex to prevent multiple initialization */
pthread_mutex_lock(&LOCK_ssl_config);
if (!my_ssl_initialized)
{
if (ssl_crypto_init())
goto end;
SSL_library_init();
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
OPENSSL_config(NULL);
#endif
/* load errors */
SSL_load_error_strings();
/* digests and ciphers */
OpenSSL_add_all_algorithms();
if (!(SSL_context= SSL_CTX_new(TLSv1_client_method())))
{
my_SSL_error(mysql);
rc= 1;
goto end;
}
my_ssl_initialized= TRUE;
}
end:
pthread_mutex_unlock(&LOCK_ssl_config);
DBUG_RETURN(rc);
}
/*
Release SSL and free resources
Will be automatically executed by
mysql_server_end() function
SYNOPSIS
my_ssl_end()
void
RETURN VALUES
void
*/
void my_ssl_end()
{
DBUG_ENTER("my_ssl_end");
pthread_mutex_lock(&LOCK_ssl_config);
if (my_ssl_initialized)
{
int i;
if (LOCK_crypto)
{
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_id_callback(NULL);
for (i=0; i < CRYPTO_num_locks(); i++)
pthread_mutex_destroy(&LOCK_crypto[i]);
my_free(LOCK_crypto);
LOCK_crypto= NULL;
}
if (SSL_context)
{
SSL_CTX_free(SSL_context);
SSL_context= FALSE;
}
if (mariadb_deinitialize_ssl)
{
ERR_remove_state(0);
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
CONF_modules_free();
CONF_modules_unload(1);
sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
}
my_ssl_initialized= FALSE;
}
pthread_mutex_unlock(&LOCK_ssl_config);
pthread_mutex_destroy(&LOCK_ssl_config);
DBUG_VOID_RETURN;
}
/*
Set certification stuff.
*/
static int my_ssl_set_certs(MYSQL *mysql)
{
char *certfile= mysql->options.ssl_cert,
*keyfile= mysql->options.ssl_key;
DBUG_ENTER("my_ssl_set_certs");
/* Make sure that ssl was allocated and
ssl_system was initialized */
DBUG_ASSERT(my_ssl_initialized == TRUE);
/* add cipher */
if ((mysql->options.ssl_cipher &&
mysql->options.ssl_cipher[0] != 0) &&
SSL_CTX_set_cipher_list(SSL_context, mysql->options.ssl_cipher) == 0)
goto error;
/* ca_file and ca_path */
if (SSL_CTX_load_verify_locations(SSL_context,
mysql->options.ssl_ca,
mysql->options.ssl_capath) == 0)
{
if (mysql->options.ssl_ca || mysql->options.ssl_capath)
goto error;
if (SSL_CTX_set_default_verify_paths(SSL_context) == 0)
goto error;
}
if (keyfile && !certfile)
certfile= keyfile;
if (certfile && !keyfile)
keyfile= certfile;
/* set cert */
if (certfile && certfile[0] != 0)
if (SSL_CTX_use_certificate_file(SSL_context, certfile, SSL_FILETYPE_PEM) != 1)
goto error;
/* set key */
if (keyfile && keyfile[0])
{
if (SSL_CTX_use_PrivateKey_file(SSL_context, keyfile, SSL_FILETYPE_PEM) != 1)
goto error;
}
/* verify key */
if (certfile && !SSL_CTX_check_private_key(SSL_context))
goto error;
if (mysql->options.extension &&
(mysql->options.extension->ssl_crl || mysql->options.extension->ssl_crlpath))
{
X509_STORE *certstore;
if ((certstore= SSL_CTX_get_cert_store(SSL_context)))
{
if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca,
mysql->options.ssl_capath) == 0 ||
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK |
X509_V_FLAG_CRL_CHECK_ALL) == 0)
goto error;
}
}
DBUG_RETURN(0);
error:
my_SSL_error(mysql);
DBUG_RETURN(1);
}
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;
MYSQL *mysql;
DBUG_ENTER("my_verify_callback");
ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
DBUG_ASSERT(ssl != NULL);
mysql= (MYSQL *)SSL_get_app_data(ssl);
DBUG_ASSERT(mysql != NULL);
/* skip verification if no ca_file/path was specified */
if (!mysql->options.ssl_ca && !mysql->options.ssl_capath)
{
ok= 1;
DBUG_RETURN(1);
}
if (!ok)
{
uint depth;
if (!(check_cert= X509_STORE_CTX_get_current_cert(ctx)))
DBUG_RETURN(0);
depth= X509_STORE_CTX_get_error_depth(ctx);
if (depth == 0)
ok= 1;
}
/*
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR),
X509_verify_cert_error_string(ctx->error));
*/
DBUG_RETURN(ok);
}
/*
allocates a new ssl object
SYNOPSIS
my_ssl_init
mysql connection object
RETURN VALUES
NULL on error
SSL new SSL object
*/
SSL *my_ssl_init(MYSQL *mysql)
{
int verify;
SSL *ssl= NULL;
DBUG_ENTER("my_ssl_init");
DBUG_ASSERT(mysql->net.vio->ssl == NULL);
if (!my_ssl_initialized)
my_ssl_start(mysql);
pthread_mutex_lock(&LOCK_ssl_config);
if (my_ssl_set_certs(mysql))
goto error;
if (!(ssl= SSL_new(SSL_context)))
goto error;
if (!SSL_set_app_data(ssl, mysql))
goto error;
verify= (!mysql->options.ssl_ca && !mysql->options.ssl_capath) ?
SSL_VERIFY_NONE : SSL_VERIFY_PEER;
SSL_CTX_set_verify(SSL_context, verify, my_verify_callback);
SSL_CTX_set_verify_depth(SSL_context, 1);
pthread_mutex_unlock(&LOCK_ssl_config);
DBUG_RETURN(ssl);
error:
pthread_mutex_unlock(&LOCK_ssl_config);
if (ssl)
SSL_free(ssl);
DBUG_RETURN(NULL);
}
/*
establish SSL connection between client
and server
SYNOPSIS
my_ssl_connect
ssl ssl object
RETURN VALUES
0 success
1 error
*/
int my_ssl_connect(SSL *ssl)
{
my_bool blocking;
MYSQL *mysql;
long rc;
DBUG_ENTER("my_ssl_connect");
DBUG_ASSERT(ssl != NULL);
mysql= (MYSQL *)SSL_get_app_data(ssl);
CLEAR_CLIENT_ERROR(mysql);
/* Set socket to blocking if not already set */
if (!(blocking= vio_is_blocking(mysql->net.vio)))
vio_blocking(mysql->net.vio, TRUE, 0);
SSL_clear(ssl);
SSL_SESSION_set_timeout(SSL_get_session(ssl),
mysql->options.connect_timeout);
SSL_set_fd(ssl, mysql->net.vio->sd);
if (SSL_connect(ssl) != 1)
{
my_SSL_error(mysql);
/* restore blocking mode */
if (!blocking)
vio_blocking(mysql->net.vio, FALSE, 0);
DBUG_RETURN(1);
}
rc= SSL_get_verify_result(ssl);
if (rc != X509_V_OK)
{
my_set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
ER(CR_SSL_CONNECTION_ERROR), X509_verify_cert_error_string(rc));
/* restore blocking mode */
if (!blocking)
vio_blocking(mysql->net.vio, FALSE, 0);
DBUG_RETURN(1);
}
vio_reset(mysql->net.vio, VIO_TYPE_SSL, mysql->net.vio->sd, 0, 0);
mysql->net.vio->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
SYNOPSIS
my_ssl_verify_server_cert()
MYSQL mysql
mybool verify_server_cert;
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
SYNOPSIS
my_ssl_write()
vio vio
buf write buffer
size size of buffer
RETURN VALUES
bytes written
*/
size_t my_ssl_write(Vio *vio, const uchar* buf, size_t size)
{
size_t written;
DBUG_ENTER("my_ssl_write");
if (vio->async_context && vio->async_context->active)
written= my_ssl_write_async(vio->async_context, (SSL *)vio->ssl, buf,
size);
else
written= SSL_write((SSL*) vio->ssl, buf, size);
DBUG_RETURN(written);
}
/*
read from ssl socket
SYNOPSIS
my_ssl_read()
vio vio
buf read buffer
size_t max number of bytes to read
RETURN VALUES
number of bytes read
*/
size_t my_ssl_read(Vio *vio, uchar* buf, size_t size)
{
size_t read;
DBUG_ENTER("my_ssl_read");
if (vio->async_context && vio->async_context->active)
read= my_ssl_read_async(vio->async_context, (SSL *)vio->ssl, buf, size);
else
read= SSL_read((SSL*) vio->ssl, buf, size);
DBUG_RETURN(read);
}
/*
close ssl connection and free
ssl object
SYNOPSIS
my_ssl_close()
vio vio
RETURN VALUES
1 ok
0 or -1 on error
*/
int my_ssl_close(Vio *vio)
{
int i, rc;
DBUG_ENTER("my_ssl_close");
if (!vio || !vio->ssl)
DBUG_RETURN(1);
SSL_set_quiet_shutdown(vio->ssl, 1);
/* 2 x pending + 2 * data = 4 */
for (i=0; i < 4; i++)
if ((rc= SSL_shutdown(vio->ssl)))
break;
SSL_free(vio->ssl);
vio->ssl= NULL;
DBUG_RETURN(rc);
}
#endif /* HAVE_OPENSSL */

View File

@@ -49,6 +49,7 @@
/* Errors should be handled via cio callback function */ /* Errors should be handled via cio callback function */
my_bool ma_ssl_initialized= FALSE; my_bool ma_ssl_initialized= FALSE;
unsigned int mariadb_deinitialize_ssl= 1;
MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql) MARIADB_SSL *ma_cio_ssl_init(MYSQL *mysql)
{ {
@@ -106,10 +107,14 @@ const char *ma_cio_ssl_cipher(MARIADB_SSL *cssl)
static my_bool ma_cio_ssl_compare_fp(char *fp1, unsigned int fp1_len, static my_bool ma_cio_ssl_compare_fp(char *fp1, unsigned int fp1_len,
char *fp2, unsigned int fp2_len) char *fp2, unsigned int fp2_len)
{ {
char hexstr[fp1_len * 2 + 1]; char hexstr[64];
fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len); fp1_len= (unsigned int)mysql_hex_string(hexstr, fp1, fp1_len);
#ifdef WIN32
if (strnicmp(hexstr, fp2, fp1_len) != 0)
#else
if (strncasecmp(hexstr, fp2, fp1_len) != 0) if (strncasecmp(hexstr, fp2, fp1_len) != 0)
#endif
return 1; return 1;
return 0; return 0;
} }
@@ -121,9 +126,8 @@ my_bool ma_cio_ssl_check_fp(MARIADB_SSL *cssl, const char *fp, const char *fp_li
MYSQL *mysql; MYSQL *mysql;
my_bool rc=1; my_bool rc=1;
if (ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len) < 1) if ((cert_fp_len= ma_ssl_get_finger_print(cssl, cert_fp, cert_fp_len)) < 1)
goto end; goto end;
if (fp) if (fp)
rc= ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, fp, strlen(fp)); rc= ma_cio_ssl_compare_fp(cert_fp, cert_fp_len, fp, strlen(fp));
else if (fp_list) else if (fp_list)

View File

@@ -77,7 +77,14 @@ gptr my_memdup(const unsigned char *from, size_t length, myf MyFlags)
my_string my_strdup(const char *from, myf MyFlags) my_string my_strdup(const char *from, myf MyFlags)
{ {
gptr ptr; gptr ptr;
uint length=(uint) strlen(from)+1; uint length;
if (MyFlags & MY_ALLOW_ZERO_PTR)
if (!from)
return NULL;
length=(uint) strlen(from)+1;
if ((ptr=my_malloc(length,MyFlags)) != 0) if ((ptr=my_malloc(length,MyFlags)) != 0)
memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length); memcpy((unsigned char*) ptr, (unsigned char*) from,(size_t) length);
return((my_string) ptr); return((my_string) ptr);

View File

@@ -386,7 +386,7 @@ unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsig
return 0; return 0;
} }
if (gnutls_fingerprint(GNUTLS_DIG_MD5, &cert_list[0], fp, &fp_len) > 0) if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &cert_list[0], fp, &fp_len) == 0)
return fp_len; return fp_len;
else else
{ {

View File

@@ -20,8 +20,75 @@
*************************************************************************************/ *************************************************************************************/
#include "ma_schannel.h" #include "ma_schannel.h"
#include <assert.h>
#define SC_IO_BUFFER_SIZE 0x4000 #define SC_IO_BUFFER_SIZE 0x4000
#define MAX_SSL_ERR_LEN 100
#define SCHANNEL_PAYLOAD(A) (A).cbMaximumMessage - (A).cbHeader - (A).cbTrailer
/* {{{ void ma_schannel_set_sec_error */
void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo)
{
MYSQL *mysql= cio->mysql;
switch(ErrorNo) {
case SEC_E_UNTRUSTED_ROOT:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Untrusted root certificate");
break;
case SEC_E_BUFFER_TOO_SMALL:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Buffer too small");
break;
case SEC_E_CRYPTO_SYSTEM_INVALID:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Cipher is not supported");
break;
case SEC_E_INSUFFICIENT_MEMORY:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Out of memory");
break;
case SEC_E_OUT_OF_SEQUENCE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid message sequence");
break;
case SEC_E_DECRYPT_FAILURE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during decrypting data");
break;
case SEC_I_INCOMPLETE_CREDENTIALS:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Incomplete credentials");
break;
case SEC_E_ENCRYPT_FAILURE:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "An error occured during encrypting data");
break;
case SEC_I_CONTEXT_EXPIRED:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Context expired: ");
case SEC_E_OK:
break;
default:
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error (%d)", ErrorNo);
}
}
/* }}} */
/* {{{ void ma_schnnel_set_win_error */
void ma_schannel_set_win_error(MARIADB_CIO *cio)
{
ulong ssl_errno= GetLastError();
char ssl_error[MAX_SSL_ERR_LEN];
char *ssl_error_reason= NULL;
if (!ssl_errno)
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return;
}
/* todo: obtain error messge */
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &ssl_error_reason, 0, NULL );
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason);
if (ssl_error_reason)
LocalFree(ssl_error_reason);
return;
}
/* }}} */
/* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */ /* {{{ LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) */
/* /*
@@ -44,7 +111,7 @@
LPBYTE * a pointer to a binary der object LPBYTE * a pointer to a binary der object
buffer_len will contain the length of binary der object buffer_len will contain the length of binary der object
*/ */
static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len) static LPBYTE ma_schannel_load_pem(MARIADB_CIO *cio, const char *PemFileName, DWORD *buffer_len)
{ {
HANDLE hfile; HANDLE hfile;
char *buffer= NULL; char *buffer= NULL;
@@ -57,32 +124,53 @@ static LPBYTE ma_schannel_load_pem(const char *PemFileName, DWORD *buffer_len)
return NULL; return NULL;
if ((hfile= CreateFile(PemFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, if ((hfile= CreateFile(PemFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE) FILE_ATTRIBUTE_NORMAL, NULL )) == INVALID_HANDLE_VALUE)
{
ma_schannel_set_win_error(cio);
return NULL; return NULL;
}
if (!(*buffer_len = GetFileSize(hfile, NULL))) if (!(*buffer_len = GetFileSize(hfile, NULL)))
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Invalid pem format");
goto end; goto end;
}
if (!(buffer= LocalAlloc(0, *buffer_len + 1))) if (!(buffer= LocalAlloc(0, *buffer_len + 1)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end; goto end;
}
if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL)) if (!ReadFile(hfile, buffer, *buffer_len, &dwBytesRead, NULL))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
CloseHandle(hfile); CloseHandle(hfile);
/* calculate the length of DER binary */ /* calculate the length of DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER, if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
NULL, &der_buffer_length, NULL, NULL)) NULL, &der_buffer_length, NULL, NULL))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
/* allocate DER binary buffer */ /* allocate DER binary buffer */
if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length))) if (!(der_buffer= (LPBYTE)LocalAlloc(0, der_buffer_length)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end; goto end;
}
/* convert to DER binary */ /* convert to DER binary */
if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER, if (!CryptStringToBinaryA(buffer, *buffer_len, CRYPT_STRING_BASE64HEADER,
der_buffer, &der_buffer_length, NULL, NULL)) der_buffer, &der_buffer_length, NULL, NULL))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
*buffer_len= der_buffer_length; *buffer_len= der_buffer_length;
LocalFree(buffer); LocalFree(buffer);
@@ -101,12 +189,13 @@ end:
} }
/* }}} */ /* }}} */
/* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) */ /* {{{ CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file) */
/* /*
Create a certification context from ca or cert file Create a certification context from ca or cert file
SYNOPSIS SYNOPSIS
ma_schannel_create_cert_context() ma_schannel_create_cert_context()
cio cio object
pem_file name of certificate or ca file pem_file name of certificate or ca file
DESCRIPTION DESCRIPTION
@@ -119,7 +208,7 @@ end:
NULL If loading of the file or creating context failed NULL If loading of the file or creating context failed
CERT_CONTEXT * A pointer to a certification context structure CERT_CONTEXT * A pointer to a certification context structure
*/ */
CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file) CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file)
{ {
DWORD der_buffer_length; DWORD der_buffer_length;
LPBYTE der_buffer= NULL; LPBYTE der_buffer= NULL;
@@ -127,10 +216,12 @@ CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file)
CERT_CONTEXT *ctx= NULL; CERT_CONTEXT *ctx= NULL;
/* create DER binary object from ca/certification file */ /* create DER binary object from ca/certification file */
if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length)))
goto end; goto end;
ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, if (!(ctx= CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
der_buffer, der_buffer_length); der_buffer, der_buffer_length)))
ma_schannel_set_win_error(cio);
end: end:
if (der_buffer) if (der_buffer)
LocalFree(der_buffer); LocalFree(der_buffer);
@@ -138,7 +229,7 @@ end:
} }
/* }}} */ /* }}} */
/* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) */ /* {{{ PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file) */
/* /*
Create a crl context from crlfile Create a crl context from crlfile
@@ -156,7 +247,7 @@ end:
NULL If loading of the file or creating context failed NULL If loading of the file or creating context failed
PCCRL_CONTEXT A pointer to a certification context structure PCCRL_CONTEXT A pointer to a certification context structure
*/ */
PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file) PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file)
{ {
DWORD der_buffer_length; DWORD der_buffer_length;
LPBYTE der_buffer= NULL; LPBYTE der_buffer= NULL;
@@ -164,10 +255,11 @@ PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file)
PCCRL_CONTEXT ctx= NULL; PCCRL_CONTEXT ctx= NULL;
/* load ca pem file into memory */ /* load ca pem file into memory */
if (!(der_buffer= ma_schannel_load_pem(pem_file, (DWORD *)&der_buffer_length))) if (!(der_buffer= ma_schannel_load_pem(cio, pem_file, (DWORD *)&der_buffer_length)))
goto end; goto end;
ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, if (!(ctx= CertCreateCRLContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
der_buffer, der_buffer_length); der_buffer, der_buffer_length)))
ma_schannel_set_win_error(cio);
end: end:
if (der_buffer) if (der_buffer)
LocalFree(der_buffer); LocalFree(der_buffer);
@@ -175,7 +267,7 @@ end:
} }
/* }}} */ /* }}} */
/* {{{ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) */ /* {{{ my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file) */
/* /*
Load privte key into context Load privte key into context
@@ -195,7 +287,7 @@ end:
PCCRL_CONTEXT A pointer to a certification context structure PCCRL_CONTEXT A pointer to a certification context structure
*/ */
my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file) my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file)
{ {
DWORD der_buffer_len= 0; DWORD der_buffer_len= 0;
LPBYTE der_buffer= NULL; LPBYTE der_buffer= NULL;
@@ -203,11 +295,11 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file)
LPBYTE priv_key= NULL; LPBYTE priv_key= NULL;
HCRYPTPROV crypt_prov= NULL; HCRYPTPROV crypt_prov= NULL;
HCRYPTKEY crypt_key= NULL; HCRYPTKEY crypt_key= NULL;
CRYPT_KEY_PROV_INFO kpi; CERT_KEY_CONTEXT kpi;
my_bool rc= 0; my_bool rc= 0;
/* load private key into der binary object */ /* load private key into der binary object */
if (!(der_buffer= ma_schannel_load_pem(key_file, &der_buffer_len))) if (!(der_buffer= ma_schannel_load_pem(cio, key_file, &der_buffer_len)))
return 0; return 0;
/* determine required buffer size for decoded private key */ /* determine required buffer size for decoded private key */
@@ -216,11 +308,17 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file)
der_buffer, der_buffer_len, der_buffer, der_buffer_len,
0, NULL, 0, NULL,
NULL, &priv_key_len)) NULL, &priv_key_len))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
/* allocate buffer for decoded private key */ /* allocate buffer for decoded private key */
if (!(priv_key= LocalAlloc(0, priv_key_len))) if (!(priv_key= LocalAlloc(0, priv_key_len)))
{
cio->set_error(cio->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
goto end; goto end;
}
/* decode */ /* decode */
if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, if (!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
@@ -228,24 +326,37 @@ my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file)
der_buffer, der_buffer_len, der_buffer, der_buffer_len,
0, NULL, 0, NULL,
priv_key, &priv_key_len)) priv_key, &priv_key_len))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
/* Acquire context */ /* Acquire context:
If cio_schannel context doesn't exist, create a new one */
if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0)) if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, 0))
if (!CryptAcquireContext(&crypt_prov, "cio_schannel", MS_ENHANCED_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
/* ... and import the private key */ /* ... and import the private key */
if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key)) if (!CryptImportKey(crypt_prov, priv_key, priv_key_len, NULL, 0, &crypt_key))
{
ma_schannel_set_win_error(cio);
goto end; goto end;
}
SecureZeroMemory(&kpi, sizeof(kpi)); SecureZeroMemory(&kpi, sizeof(kpi));
kpi.pwszContainerName = "cio-schanel"; kpi.hCryptProv= crypt_prov;
kpi.dwKeySpec = AT_KEYEXCHANGE; kpi.dwKeySpec = AT_KEYEXCHANGE;
kpi.dwFlags = CRYPT_MACHINE_KEYSET; kpi.cbSize= sizeof(kpi);
/* assign private key to certificate context */ /* assign private key to certificate context */
if (CertSetCertificateContextProperty(ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, &kpi)) if (CertSetCertificateContextProperty(ctx, CERT_KEY_CONTEXT_PROP_ID, 0, &kpi))
rc= 1; rc= 1;
else
ma_schannel_set_win_error(cio);
end: end:
if (der_buffer) if (der_buffer)
LocalFree(der_buffer); LocalFree(der_buffer);
@@ -285,7 +396,7 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead
PUCHAR IoBuffer; PUCHAR IoBuffer;
BOOL fDoRead; BOOL fDoRead;
MARIADB_SSL *cssl= cio->cssl; MARIADB_SSL *cssl= cio->cssl;
SC_CTX *sctx= (SC_CTX *)cssl->data; SC_CTX *sctx= (SC_CTX *)cssl->ssl;
dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT |
@@ -416,6 +527,7 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead
pExtraData->cbBuffer= 0; pExtraData->cbBuffer= 0;
} }
break; break;
case SEC_I_INCOMPLETE_CREDENTIALS: case SEC_I_INCOMPLETE_CREDENTIALS:
/* Provided credentials didn't contain a valid client certificate. /* Provided credentials didn't contain a valid client certificate.
We will try to connect anonymously, using current credentials */ We will try to connect anonymously, using current credentials */
@@ -425,7 +537,10 @@ SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead
break; break;
default: default:
if (FAILED(rc)) if (FAILED(rc))
{
ma_schannel_set_sec_error(cio, rc);
goto loopend; goto loopend;
}
break; break;
} }
@@ -472,22 +587,24 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl)
SecBuffer ExtraData; SecBuffer ExtraData;
DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | DWORD SFlags= ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR | ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
ISC_REQ_USE_SUPPLIED_CREDS |
ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM; ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM;
SecBufferDesc BufferIn, BufferOut; SecBufferDesc BufferIn, BufferOut;
SecBuffer BuffersOut[1], BuffersIn[2]; SecBuffer BuffersOut[1], BuffersIn[2];
if (!cssl || !cssl->cio || !cssl->data) if (!cssl || !cssl->cio)
return 1; return 1;
cio= cssl->cio; cio= cssl->cio;
sctx= (SC_CTX *)cssl->data; sctx= (SC_CTX *)cssl->ssl;
/* Initialie securifty context */ /* Initialie securifty context */
BuffersOut[0].BufferType= SECBUFFER_TOKEN; BuffersOut[0].BufferType= SECBUFFER_TOKEN;
BuffersOut[0].cbBuffer= 0; BuffersOut[0].cbBuffer= 0;
BuffersOut[0].pvBuffer= NULL; BuffersOut[0].pvBuffer= NULL;
BufferOut.cBuffers= 1; BufferOut.cBuffers= 1;
BufferOut.pBuffers= BuffersOut; BufferOut.pBuffers= BuffersOut;
BufferOut.ulVersion= SECBUFFER_VERSION; BufferOut.ulVersion= SECBUFFER_VERSION;
@@ -506,7 +623,18 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl)
NULL); NULL);
if(sRet != SEC_I_CONTINUE_NEEDED) if(sRet != SEC_I_CONTINUE_NEEDED)
{
ma_schannel_set_sec_error(cio, sRet);
return sRet; return sRet;
}
/* Allocate IO-Buffer */
sctx->IoBufferSize= 2 * net_buffer_length;
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(LMEM_ZEROINIT, sctx->IoBufferSize)))
{
sRet= SEC_E_INSUFFICIENT_MEMORY;
goto end;
}
/* send client hello packaet */ /* send client hello packaet */
if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL) if(BuffersOut[0].cbBuffer != 0 && BuffersOut[0].pvBuffer != NULL)
@@ -518,51 +646,26 @@ SECURITY_STATUS ma_schannel_client_handshake(MARIADB_SSL *cssl)
goto end; goto end;
} }
} }
return ma_schannel_handshake_loop(cio, TRUE, &ExtraData); sRet= ma_schannel_handshake_loop(cio, TRUE, &ExtraData);
/* Reallocate IO-Buffer for write operations: After handshake
was successfull, we are able now to calculate payload */
QueryContextAttributes( &sctx->ctxt, SECPKG_ATTR_STREAM_SIZES, &sctx->Sizes );
sctx->IoBufferSize= SCHANNEL_PAYLOAD(sctx->Sizes);
sctx->IoBuffer= LocalReAlloc(sctx->IoBuffer, sctx->IoBufferSize, LMEM_ZEROINIT);
return sRet;
end: end:
LocalFree(sctx->IoBuffer);
sctx->IoBufferSize= 0;
FreeContextBuffer(BuffersOut[0].pvBuffer); FreeContextBuffer(BuffersOut[0].pvBuffer);
DeleteSecurityContext(&sctx->ctxt); DeleteSecurityContext(&sctx->ctxt);
return sRet; return sRet;
} }
/* }}} */ /* }}} */
/* {{{ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength) */ /* {{{ SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext,
/* DWORD DecryptLength, uchar *ReadBuffer, DWORD ReadBufferSize) */
alloates an IO Buffer for ssl communication
SYNOPSUS
ma_schannel_alloc_iobuffer()
Context SChannel context handle
BufferLength a pointer for the buffer length
DESCRIPTION
calculates the required buffer length and allocates a memory buffer for en- and
decryption. The calculated buffer length will be returned in BufferLength pointer.
The allocated buffer needs to be freed at end of the connection.
RETURN
NULL if an error occured
PUCHAR an IO Buffer
*/
static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLength)
{
SecPkgContext_StreamSizes StreamSizes;
PUCHAR Buffer= NULL;
if (!BufferLength || QueryContextAttributes(Context, SECPKG_ATTR_STREAM_SIZES, &StreamSizes) != SEC_E_OK)
return NULL;
/* Calculate BufferLength */
*BufferLength= StreamSizes.cbHeader + StreamSizes.cbTrailer + StreamSizes.cbMaximumMessage;
Buffer= LocalAlloc(LMEM_FIXED, *BufferLength);
return Buffer;
}
/* }}} */
/* {{{ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */
/* /*
Reads encrypted data from a SSL stream and decrypts it. Reads encrypted data from a SSL stream and decrypts it.
@@ -583,44 +686,34 @@ static PUCHAR ma_schannel_alloc_iobuffer(CtxtHandle *Context, DWORD *BufferLengt
SEC_E_* if an error occured SEC_E_* if an error occured
*/ */
SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio, SECURITY_STATUS ma_schannel_read_decrypt(MARIADB_CIO *cio,
PCredHandle phCreds, PCredHandle phCreds,
CtxtHandle * phContext, CtxtHandle * phContext,
DWORD *DecryptLength, DWORD *DecryptLength,
uchar *ReadBuffer, uchar *ReadBuffer,
DWORD ReadBufferSize) DWORD ReadBufferSize)
{ {
DWORD dwBytesRead= 0; DWORD dwBytesRead= 0;
DWORD dwOffset= 0; DWORD dwOffset= 0;
SC_CTX *sctx; SC_CTX *sctx;
SECURITY_STATUS sRet= 0; SECURITY_STATUS sRet= 0;
SecBuffersDesc Msg; SecBufferDesc Msg;
SecBuffer Buffers[4], SecBuffer Buffers[4],
ExtraBuffer, ExtraBuffer,
*pData, *pExtra; *pData, *pExtra;
int i; int i;
if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !cio->cssl->data | !DecryptLength) if (!cio || !cio->methods || !cio->methods->read || !cio->cssl || !DecryptLength)
return SEC_E_INTERNAL_ERROR; return SEC_E_INTERNAL_ERROR;
sctx= (SC_CTX *)cio->cssl->data; sctx= (SC_CTX *)cio->cssl->ssl;
*DecryptLength= 0; *DecryptLength= 0;
/* Allocate IoBuffer */
if (!sctx->IoBuffer)
{
if (!(sctx->IoBuffer= ma_schannel_alloc_iobuffer(&sctx->ctxt, &sctx->IoBufferSize)))
{
/* todo: error */
return NULL;
}
}
while (1) while (1)
{ {
if (!dwOffset || sRet == SEC_E_INCOMPLETE_MESSAGE) if (!dwBytesRead || sRet == SEC_E_INCOMPLETE_MESSAGE)
{ {
dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, cbIoBufferLength - dwOffset); dwBytesRead= cio->methods->read(cio, sctx->IoBuffer + dwOffset, sctx->IoBufferSize - dwOffset);
if (dwBytesRead == 0) if (dwBytesRead == 0)
{ {
/* server closed connection */ /* server closed connection */
@@ -633,8 +726,9 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio,
printf("Socket error\n"); printf("Socket error\n");
return NULL; return NULL;
} }
dwOffset+= dwBytesRead;
} }
ZeroMem(Buffers, sizeof(SecBuffer) * 4); ZeroMemory(Buffers, sizeof(SecBuffer) * 4);
Buffers[0].pvBuffer= sctx->IoBuffer; Buffers[0].pvBuffer= sctx->IoBuffer;
Buffers[0].cbBuffer= dwOffset; Buffers[0].cbBuffer= dwOffset;
@@ -652,9 +746,9 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio,
/* Check for possible errors: we continue in case context has /* Check for possible errors: we continue in case context has
expired or renogitiation is required */ expired or renogitiation is required */
if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED && if (sRet != SEC_E_OK && sRet != SEC_I_CONTEXT_EXPIRED &&
sRet != SEC_I_RENEGOTIARE) sRet != SEC_I_RENEGOTIATE && sRet != SEC_E_INCOMPLETE_MESSAGE)
{ {
// set error ma_schannel_set_sec_error(cio, sRet);
return sRet; return sRet;
} }
@@ -668,11 +762,12 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio,
if (pData && pExtra) if (pData && pExtra)
break; break;
} }
if (pData && pData->cbBuffer) if (pData && pData->cbBuffer)
{ {
memcpy(ReadBuffer + *DecrypthLength, pData->pvBuffer, pData->cbBuffer); memcpy(ReadBuffer + *DecryptLength, pData->pvBuffer, pData->cbBuffer);
*DecryptLength+= pData->cbBuffer; *DecryptLength+= pData->cbBuffer;
return sRet;
} }
if (pExtra) if (pExtra)
@@ -684,4 +779,129 @@ SECURITY_STATUS ma_schannel_read(MARIADB_CIO *cio,
dwOffset= 0; dwOffset= 0;
} }
} }
my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags)
{
SECURITY_STATUS sRet;
DWORD flags;
MARIADB_CIO *cio= sctx->mysql->net.cio;
PCCERT_CONTEXT pServerCert= NULL;
if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
return 0;
}
flags= CERT_STORE_SIGNATURE_FLAG |
CERT_STORE_TIME_VALIDITY_FLAG;
if (sctx->client_ca_ctx)
{
if (!(sRet= CertVerifySubjectCertificateContext(pServerCert,
sctx->client_ca_ctx,
&flags)))
{
ma_schannel_set_win_error(cio);
return 0;
}
if (flags)
{
if ((flags & CERT_STORE_SIGNATURE_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate signature check failed");
else if ((flags & CERT_STORE_REVOCATION_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate was revoked");
else if ((flags & CERT_STORE_TIME_VALIDITY_FLAG) != 0)
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Client certificate has expired");
else
cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown error during client certificate validation");
return 0;
}
}
/* Check if none of the certificates in the certificate chain have been revoked. */
if (sctx->client_crl_ctx)
{
PCRL_INFO Info[1];
Info[0]= sctx->client_crl_ctx->pCrlInfo;
if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pServerCert->pCertInfo,
1, Info)) )
{
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "CRL Revocation failed");
return 0;
}
}
return 1;
}
/* {{{ size_t ma_schannel_write_encrypt(MARIADB_CIO *cio, PCredHandle phCreds, CtxtHandle * phContext) */
/*
Decrypts data and write to SSL stream
SYNOPSIS
ma_schannel_write_decrypt
cio pointer to Communication IO structure
phContext a context handle
DecryptLength size of decrypted buffer
ReadBuffer Buffer for decrypted data
ReadBufferSize size of ReadBuffer
DESCRIPTION
Write encrypted data to SSL stream.
RETURN
SEC_E_OK on success
SEC_E_* if an error occured
*/
size_t ma_schannel_write_encrypt(MARIADB_CIO *cio,
uchar *WriteBuffer,
size_t WriteBufferSize)
{
SECURITY_STATUS scRet;
SecBufferDesc Message;
SecBuffer Buffers[4];
DWORD cbMessage, cbData;
PBYTE pbMessage;
SC_CTX *sctx= (SC_CTX *)cio->cssl->ssl;
size_t payload;
payload= MIN(WriteBufferSize, sctx->IoBufferSize);
memcpy(&sctx->IoBuffer[sctx->Sizes.cbHeader], WriteBuffer, payload);
pbMessage = sctx->IoBuffer + sctx->Sizes.cbHeader;
cbMessage = payload;
Buffers[0].pvBuffer = sctx->IoBuffer;
Buffers[0].cbBuffer = sctx->Sizes.cbHeader;
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; // Type of the buffer
Buffers[1].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader];
Buffers[1].cbBuffer = payload;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[2].pvBuffer = &sctx->IoBuffer[sctx->Sizes.cbHeader] + payload;
Buffers[2].cbBuffer = sctx->Sizes.cbTrailer;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[3].pvBuffer = SECBUFFER_EMPTY; // Pointer to buffer 4
Buffers[3].cbBuffer = SECBUFFER_EMPTY; // length of buffer 4
Buffers[3].BufferType = SECBUFFER_EMPTY; // Type of the buffer 4
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
if ((scRet = EncryptMessage(&sctx->ctxt, 0, &Message, 0))!= SEC_E_OK)
return -1;
if (cio->methods->write(cio, sctx->IoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer))
return payload;
}
/* }}} */ /* }}} */

View File

@@ -28,23 +28,23 @@
#include <ma_common.h> #include <ma_common.h>
#include <ma_cio.h> #include <ma_cio.h>
#include <errmsg.h> #include <errmsg.h>
#include <mysql/client_plugin.h>
typedef void VOID; typedef void VOID;
#include <wincrypt.h> #include <wincrypt.h>
#define SECURITY_WIN32 #include <wintrust.h>
#include <security.h> #include <security.h>
#include <schnlsp.h> #include <schnlsp.h>
#undef SECURITY_WIN32 #undef SECURITY_WIN32
#include <Windows.h> #include <Windows.h>
#include <sspi.h>
#define SC_IO_BUFFER_SIZE 0x4000 #define SC_IO_BUFFER_SIZE 0x4000
CERT_CONTEXT *ma_schannel_create_cert_context(const char *pem_file);
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData);
my_bool ma_schannel_load_private_key(CERT_CONTEXT *ctx, char *key_file);
PCCRL_CONTEXT ma_schannel_create_crl_context(const char *pem_file);
#ifndef HAVE_SCHANNEL_DEFAULT #ifndef HAVE_SCHANNEL_DEFAULT
#define my_snprintf snprintf #define my_snprintf snprintf
@@ -61,12 +61,35 @@ struct st_schannel {
CredHandle CredHdl; CredHandle CredHdl;
PUCHAR IoBuffer; PUCHAR IoBuffer;
DWORD IoBufferSize; DWORD IoBufferSize;
/* PUCHAR EncryptBuffer;
DWORD EncryptBufferSize;
DWORD EncryptBufferLength;
PUCHAR DecryptBuffer; PUCHAR DecryptBuffer;
DWORD DecryptBufferSize; DWORD DecryptBufferSize;
DWORD DecryptBufferLength; DWORD DecryptBufferLength;
uchar thumbprint[21]; */
SecPkgContext_StreamSizes Sizes;
CtxtHandle ctxt; CtxtHandle ctxt;
MYSQL *mysql;
}; };
typedef struct st_schannel SC_CTX; typedef struct st_schannel SC_CTX;
CERT_CONTEXT *ma_schannel_create_cert_context(MARIADB_CIO *cio, const char *pem_file);
SECURITY_STATUS ma_schannel_handshake_loop(MARIADB_CIO *cio, my_bool InitialRead, SecBuffer *pExtraData);
my_bool ma_schannel_load_private_key(MARIADB_CIO *cio, CERT_CONTEXT *ctx, char *key_file);
PCCRL_CONTEXT ma_schannel_create_crl_context(MARIADB_CIO *cio, const char *pem_file);
my_bool ma_schannel_verify_certs(SC_CTX *sctx, DWORD dwCertFlags);
size_t ma_schannel_write_encrypt(MARIADB_CIO *cio,
uchar *WriteBuffer,
size_t WriteBufferSize);
size_t ma_schannel_read_decrypt(MARIADB_CIO *cio,
PCredHandle phCreds,
CtxtHandle * phContext,
DWORD *DecryptLength,
uchar *ReadBuffer,
DWORD ReadBufferSize);
#endif /* _ma_schannel_h_ */ #endif /* _ma_schannel_h_ */

View File

@@ -287,10 +287,9 @@ static int ma_ssl_set_certs(MYSQL *mysql)
if ((certstore= SSL_CTX_get_cert_store(SSL_context))) if ((certstore= SSL_CTX_get_cert_store(SSL_context)))
{ {
if (X509_STORE_load_locations(certstore, mysql->options.ssl_ca, if (X509_STORE_load_locations(certstore, mysql->options.extension->ssl_crl,
mysql->options.ssl_capath) == 0 || mysql->options.extension->ssl_crlpath) == 0 ||
X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_STORE_set_flags(certstore, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL) == 0)
X509_V_FLAG_CRL_CHECK_ALL) == 0)
goto error; goto error;
} }
} }

View File

@@ -24,77 +24,72 @@
#define VOID void #define VOID void
static my_bool my_schannel_initialized= FALSE; extern my_bool ma_ssl_initialized;
#define MAX_SSL_ERR_LEN 100
static pthread_mutex_t LOCK_schannel_config; static pthread_mutex_t LOCK_schannel_config;
static pthread_mutex_t *LOCK_crypto= NULL; static pthread_mutex_t *LOCK_crypto= NULL;
int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list); struct st_cipher_suite {
int cio_schannel_end(); DWORD aid;
void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql); CHAR *cipher;
my_bool cio_schannel_connect(MARIADB_SSL *cssl);
size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length);
my_bool cio_schannel_close(MARIADB_SSL *cssl);
int cio_schannel_verify_server_cert(MARIADB_SSL *cssl);
const char *cio_schannel_cipher(MARIADB_SSL *cssl);
struct st_ma_cio_ssl_methods cio_schannel_methods= {
cio_schannel_init,
cio_schannel_connect,
cio_schannel_read,
cio_schannel_write,
cio_schannel_close,
cio_schannel_verify_server_cert,
cio_schannel_cipher
}; };
#ifndef HAVE_SCHANNEL_DEFAULT void ma_schannel_set_sec_error(MARIADB_CIO *cio, DWORD ErrorNo);
MARIADB_CIO_PLUGIN _mysql_client_plugin_declaration_= void ma_schannel_set_win_error(MYSQL *mysql);
#else
MARIADB_CIO_PLUGIN cio_schannel_plugin= const struct st_cipher_suite sc_ciphers[]=
#endif
{ {
MYSQL_CLIENT_CIO_PLUGIN, {CALG_3DES, "CALG_3DES"},
MYSQL_CLIENT_CIO_PLUGIN_INTERFACE_VERSION, {CALG_3DES_112, "CALG_3DES_112"},
"cio_schannel", {CALG_AES, "CALG_AES"},
"Georg Richter", {CALG_AES_128, "CALG_AES_128"},
"MariaDB communication IO plugin for Windows SSL/SChannel communication", {CALG_AES_192, "CALG_AES_192"},
{1, 0, 0}, {CALG_AES_256, "CALG_AES_256"},
"LGPL", {CALG_AGREEDKEY_ANY, "CALG_AGREEDKEY_ANY"},
cio_schannel_start, {CALG_CYLINK_MEK, "CALG_CYLINK_MEK"},
cio_schannel_end, {CALG_DES, "CALG_DES"},
NULL, {CALG_DESX, "CALG_DESX"},
&cio_schannel_methods, {CALG_DH_EPHEM, "CALG_DH_EPHEM"},
NULL {CALG_DH_SF, "CALG_DH_SF"},
{CALG_DSS_SIGN, "CALG_DSS_SIGN"},
{CALG_ECDH, "CALG_ECDH"},
{CALG_ECDSA, "CALG_ECDSA"},
{CALG_ECMQV, "CALG_ECMQV"},
{CALG_HASH_REPLACE_OWF, "CALG_HASH_REPLACE_OWF"},
{CALG_HUGHES_MD5, "CALG_HUGHES_MD5"},
{CALG_HMAC, "CALG_HMAC"},
{CALG_KEA_KEYX, "CALG_KEA_KEYX"},
{CALG_MAC, "CALG_MAC"},
{CALG_MD2, "CALG_MD2"},
{CALG_MD4, "CALG_MD4"},
{CALG_MD4, "CALG_MD5"},
{CALG_NO_SIGN, "CALG_NO_SIGN"},
{CALG_OID_INFO_CNG_ONLY, "CALG_OID_INFO_CNG_ONLY"},
{CALG_OID_INFO_PARAMETERS, "CALG_OID_INFO_PARAMETERS"},
{CALG_PCT1_MASTER, "CALG_PCT1_MASTER"},
{CALG_RC2, "CALG_RC2"},
{CALG_RC4, "CALG_RC4"},
{CALG_RC5, "CALG_RC5"},
{CALG_RSA_KEYX, "CALG_RSA_KEYX"},
{CALG_RSA_SIGN, "CALG_RSA_SIGN"},
{CALG_SCHANNEL_MAC_KEY, "CALG_SCHANNEL_MAC_KEY"},
{CALG_SCHANNEL_MASTER_HASH, "CALG_SCHANNEL_MASTER_HASH"},
{CALG_SEAL, "CALG_SEAL"},
{CALG_SHA, "CALG_SHA"},
{CALG_SHA1, "CALG_SHA1"},
{CALG_SHA_256, "CALG_SHA_256"},
{CALG_SHA_384, "CALG_SHA_384"},
{CALG_SHA_512, "CALG_SHA_512"},
{CALG_SKIPJACK, "CALG_SKIPJACK"},
{CALG_SSL2_MASTER, "CALG_SSL2_MASTER"},
{CALG_SSL3_MASTER, "CALG_SSL3_MASTER"},
{CALG_SSL3_SHAMD5, "CALG_SSL3_SHAMD5"},
{CALG_TEK, "CALG_TEK"},
{CALG_TLS1_MASTER, "CALG_TLS1_MASTER"},
{CALG_TLS1PRF, "CALG_TLS1PRF"},
{0, NULL}
}; };
static void cio_schannel_set_error(MYSQL *mysql)
{
ulong ssl_errno= GetLastError();
char ssl_error[MAX_SSL_ERR_LEN];
char *ssl_error_reason= NULL;
MARIADB_CIO *cio= mysql->net.cio;
if (!ssl_errno)
{
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Unknown SSL error");
return;
}
/* todo: obtain error messge */
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, ssl_errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &ssl_error_reason, 0, NULL );
cio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ssl_error_reason);
if (ssl_error_reason)
LocalFree(ssl_error_reason);
return;
}
static int ssl_thread_init() static int ssl_thread_init()
{ {
return 0; return 0;
@@ -106,22 +101,19 @@ static int ssl_thread_init()
context SSL_context context SSL_context
SYNOPSIS SYNOPSIS
cio_schannel_start ma_ssl_start
RETURN VALUES RETURN VALUES
0 success 0 success
1 error 1 error
*/ */
int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list) int ma_ssl_start(char *errmsg, size_t errmsg_len, int count, va_list list)
{ {
if (!my_schannel_initialized) if (!ma_ssl_initialized)
{ {
pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST); pthread_mutex_init(&LOCK_schannel_config,MY_MUTEX_INIT_FAST);
pthread_mutex_lock(&LOCK_schannel_config); pthread_mutex_lock(&LOCK_schannel_config);
ma_ssl_initialized= TRUE;
// SecureZeroMemory(&SC_CTX, sizeof(struct st_schannel_global));
my_schannel_initialized= TRUE;
} }
pthread_mutex_unlock(&LOCK_schannel_config); pthread_mutex_unlock(&LOCK_schannel_config);
return 0; return 0;
@@ -133,27 +125,27 @@ int cio_schannel_start(char *errmsg, size_t errmsg_len, int count, va_list list)
mysql_server_end() function mysql_server_end() function
SYNOPSIS SYNOPSIS
cio_schannel_end() ma_ssl_end()
void void
RETURN VALUES RETURN VALUES
void void
*/ */
int cio_schannel_end() void ma_ssl_end()
{ {
pthread_mutex_lock(&LOCK_schannel_config); pthread_mutex_lock(&LOCK_schannel_config);
if (my_schannel_initialized) if (ma_ssl_initialized)
{ {
my_schannel_initialized= FALSE; ma_ssl_initialized= FALSE;
} }
pthread_mutex_unlock(&LOCK_schannel_config); pthread_mutex_unlock(&LOCK_schannel_config);
pthread_mutex_destroy(&LOCK_schannel_config); pthread_mutex_destroy(&LOCK_schannel_config);
return 0; return;
} }
/* {{{ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) */ /* {{{ static int ma_ssl_set_client_certs(MARIADB_SSL *cssl) */
static int cio_schannel_set_client_certs(MARIADB_SSL *cssl) static int ma_ssl_set_client_certs(MARIADB_SSL *cssl)
{ {
MYSQL *mysql= cssl->cio->mysql; MYSQL *mysql= cssl->cio->mysql;
char *certfile= mysql->options.ssl_cert, char *certfile= mysql->options.ssl_cert,
@@ -161,24 +153,50 @@ static int cio_schannel_set_client_certs(MARIADB_SSL *cssl)
*cafile= mysql->options.ssl_ca; *cafile= mysql->options.ssl_ca;
SC_CTX *sctx= (SC_CTX *)cssl->ssl; SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= cssl->cio;
if (cafile) if (cafile)
if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cafile))) {
HCERTSTORE myCS= NULL;
char szName[64];
if (!(sctx->client_ca_ctx = ma_schannel_create_cert_context(cio, cafile)))
goto end; goto end;
if (certfile) /* For X509 authentication we need to add ca certificate to local MY store.
{ Schannel doesn't provide a callback to send ca to server during handshake */
if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(certfile))) if ((myCS= CertOpenStore(CERT_STORE_PROV_SYSTEM,
0, //X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_SYSTEM_STORE_CURRENT_USER,
L"CA")))
{
CertAddCertificateContextToStore(myCS, sctx->client_ca_ctx, CERT_STORE_ADD_NEWER, NULL);
CertCloseStore(myCS, 0);
}
else {
ma_schannel_set_win_error(sctx->mysql);
goto end; goto end;
if (keyfile) }
if (!ma_schannel_load_private_key(sctx->client_cert_ctx, keyfile))
goto end;
} }
if (!certfile && keyfile)
certfile= keyfile;
if (!keyfile && certfile)
keyfile= certfile;
if (certfile && certfile[0])
if (!(sctx->client_cert_ctx = ma_schannel_create_cert_context(cssl->cio, certfile)))
goto end;
if (sctx->client_cert_ctx && keyfile[0])
if (!ma_schannel_load_private_key(cio, sctx->client_cert_ctx, keyfile))
goto end;
if (mysql->options.extension && mysql->options.extension->ssl_crl) if (mysql->options.extension && mysql->options.extension->ssl_crl)
{ {
sctx->client_crl_ctx= ma_schannel_create_crl_context(mysql->options.extension->ssl_crl); if (!(sctx->client_crl_ctx= ma_schannel_create_crl_context(cio, mysql->options.extension->ssl_crl)))
goto end;
} }
return 0; return 0;
@@ -189,95 +207,207 @@ end:
CertFreeCertificateContext(sctx->client_cert_ctx); CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx) if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx); CertFreeCRLContext(sctx->client_crl_ctx);
sctx->client_ca_ctx= sctx->client_cert_ctx= NULL;
cio_schannel_set_error(mysql); sctx->client_crl_ctx= NULL;
return 1; return 1;
} }
/* }}} */ /* }}} */
/* {{{ void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) */ /* {{{ void *ma_ssl_init(MARIADB_SSL *cssl, MYSQL *mysql) */
void *cio_schannel_init(MARIADB_SSL *cssl, MYSQL *mysql) void *ma_ssl_init(MYSQL *mysql)
{ {
int verify; int verify;
SC_CTX *sctx; SC_CTX *sctx= NULL;
if (!(sctx= LocalAlloc(0, sizeof(SC_CTX))))
return NULL;
ZeroMemory(sctx, sizeof(SC_CTX));
cssl->data= (void *)sctx;
pthread_mutex_lock(&LOCK_schannel_config); pthread_mutex_lock(&LOCK_schannel_config);
return (void *)sctx;
error: if ((sctx= LocalAlloc(0, sizeof(SC_CTX))))
{
ZeroMemory(sctx, sizeof(SC_CTX));
sctx->mysql= mysql;
}
pthread_mutex_unlock(&LOCK_schannel_config); pthread_mutex_unlock(&LOCK_schannel_config);
return NULL; return sctx;
} }
/* }}} */ /* }}} */
my_bool ma_ssl_connect(MARIADB_SSL *cssl)
static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert, PSTR pszServerName, DWORD dwCertFlags )
{ {
my_bool blocking;
MYSQL *mysql;
SCHANNEL_CRED Cred;
MARIADB_CIO *cio;
SC_CTX *sctx;
SECURITY_STATUS sRet; SECURITY_STATUS sRet;
DWORD flags; PCCERT_CONTEXT pRemoteCertContext = NULL,
char *szName= NULL; pLocalCertContext= NULL;
int rc= 0; ALG_ID AlgId[2]= {0, 0};
if (!cssl || !cssl->cio)
return 1;;
cio= cssl->cio;
sctx= (SC_CTX *)cssl->ssl;
/* We perform a manually validation, as described at /* Set socket to blocking if not already set */
http://msdn.microsoft.com/en-us/library/windows/desktop/aa378740%28v=vs.85%29.aspx if (!(blocking= cio->methods->is_blocking(cio)))
*/ cio->methods->blocking(cio, TRUE, 0);
/* Check if mysql= cio->mysql;
- The certificate chain is complete and the root is a certificate from a trusted certification authority (CA).
- The current time is not beyond the begin and end dates for each of the certificates in the certificate chain. if (ma_ssl_set_client_certs(cssl))
*/ goto end;
flags= CERT_STORE_SIGNATURE_FLAG |
CERT_STORE_TIME_VALIDITY_FLAG; /* Set cipher */
if (mysql->options.ssl_cipher)
if (!(sRet= CertVerifySubjectCertificateContext(pServerCert,
sctx->client_ca_ctx,
&flags)))
{ {
/* todo: error handling */ DWORD i= 0;
return 0; while (sc_ciphers[i].cipher) {
} if (!strcmp(sc_ciphers[i].cipher, mysql->options.ssl_cipher))
{
/* Check if none of the certificates in the certificate chain have been revoked. */ AlgId[0]= sc_ciphers[i].aid;
if (sctx->client_crl_ctx) break;
{ }
PCRL_INFO Info[1];
Info[0]= sctx->client_crl_ctx->pCrlInfo;
if (!(CertVerifyCRLRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pServerCert->pCertInfo,
1, Info)) )
{
/* todo: error handling */
return 0;
} }
Cred.palgSupportedAlgs= &AlgId;
} }
ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));
Cred.dwVersion= SCHANNEL_CRED_VERSION;
Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK | SCH_SEND_ROOT_CERT |
SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION;
if (sctx->client_cert_ctx)
{
Cred.cCreds = 1;
Cred.paCred = &sctx->client_cert_ctx;
}
Cred.grbitEnabledProtocols= SP_PROT_TLS1;
if ((sRet= AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
goto end;
}
if (ma_schannel_client_handshake(cssl))
goto end;
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
if (sRet != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
goto end;
}
if (!ma_schannel_verify_certs(sctx, 0))
goto end;
return 0;
end:
/* todo: cleanup */
if (sctx->IoBufferSize)
LocalFree(sctx->IoBuffer);
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx);
return 1;
}
size_t ma_ssl_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= sctx->mysql->net.cio;
size_t dlength= -1;
ma_schannel_read_decrypt(cio, &sctx->CredHdl, &sctx->ctxt, &dlength, buffer, length);
return dlength;
}
size_t ma_ssl_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= sctx->mysql->net.cio;
size_t rc, wlength= 0;
size_t remain= length;
while (remain)
{
if ((rc= ma_schannel_write_encrypt(cio, (uchar *)buffer + wlength, remain)) <= 0)
return rc;
wlength+= rc;
remain-= rc;
}
return length;
}
/* {{{ void ma_ssl_close(MARIADB_CIO *cio) */
void ma_ssl_close(MARIADB_CIO *cio)
{
SC_CTX *sctx;
if (!cio || !cio->cssl)
return;
if ((sctx= (SC_CTX *)cio->cssl))
{
if (sctx->IoBufferSize)
LocalFree(sctx->IoBuffer);
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
if (sctx->client_crl_ctx)
CertFreeCRLContext(sctx->client_crl_ctx);
FreeCredentialHandle(&sctx->CredHdl);
DeleteSecurityContext(&sctx->ctxt);
}
LocalFree(sctx);
}
/* }}} */
int ma_ssl_verify_server_cert(MARIADB_SSL *cssl)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
MARIADB_CIO *cio= cssl->cio;
int rc= 1;
char *szName= NULL;
char *pszServerName= cio->mysql->host;
/* check server name */ /* check server name */
if (pszServerName) if (pszServerName && (sctx->mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
{ {
PCCERT_CONTEXT pServerCert;
DWORD NameSize= 0; DWORD NameSize= 0;
char *p1, *p2; char *p1, *p2;
SECURITY_STATUS sRet;
if ((sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pServerCert)) != SEC_E_OK)
{
ma_schannel_set_sec_error(cio, sRet);
return 1;
}
if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType, if (!(NameSize= CertNameToStr(pServerCert->dwCertEncodingType,
&pServerCert->pCertInfo->Subject, &pServerCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
NULL, 0))) NULL, 0)))
{ {
/* todo: error handling */ cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
return 0; return 1;
} }
if (!(szName= LocalAlloc(0, NameSize + 1))) if (!(szName= LocalAlloc(0, NameSize + 1)))
{ {
/* error handling */ cio->set_error(sctx->mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, NULL);
return 0; goto end;
} }
if (!CertNameToStr(pServerCert->dwCertEncodingType, if (!CertNameToStr(pServerCert->dwCertEncodingType,
@@ -285,7 +415,7 @@ static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG, CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, NameSize)) szName, NameSize))
{ {
/* error handling */ cio->set_error(sctx->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, "Can't retrieve name of server certificate");
goto end; goto end;
} }
if ((p1 = strstr(szName, "CN="))) if ((p1 = strstr(szName, "CN=")))
@@ -295,116 +425,50 @@ static my_bool VerifyServerCertificate(SC_CTX *sctx, PCCERT_CONTEXT pServerCert,
*p2= 0; *p2= 0;
if (!strcmp(pszServerName, p1)) if (!strcmp(pszServerName, p1))
{ {
rc= 1; rc= 0;
goto end; goto end;
} }
cio->set_error(cio->mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN,
"Name of server certificate didn't match");
} }
} }
end: end:
if (szName) if (szName)
LocalFree(szName); LocalFree(szName);
return rc;
}
my_bool cio_schannel_connect(MARIADB_SSL *cssl)
{
my_bool blocking;
MYSQL *mysql;
SCHANNEL_CRED Cred;
MARIADB_CIO *cio;
SC_CTX *sctx;
SECURITY_STATUS sRet;
PCCERT_CONTEXT pRemoteCertContext = NULL;
if (!cssl || !cssl->cio || !cssl->data)
return 1;;
cio= cssl->cio;
sctx= (SC_CTX *)cssl->data;
/* Set socket to blocking if not already set */
if (!(blocking= cio->methods->is_blocking(cio)))
cio->methods->blocking(cio, TRUE, 0);
mysql= cio->mysql;
if (cio_schannel_set_client_certs(cssl))
{
cio_schannel_set_error(mysql);
goto end;
}
ZeroMemory(&Cred, sizeof(SCHANNEL_CRED));
Cred.dwVersion= SCHANNEL_CRED_VERSION;
Cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK |
SCH_CRED_NO_DEFAULT_CREDS |
SCH_CRED_MANUAL_CRED_VALIDATION;
if (sctx->client_cert_ctx)
{
Cred.cCreds = 1;
Cred.paCred = &sctx->client_cert_ctx;
}
Cred.grbitEnabledProtocols= SP_PROT_TLS1;
/* We allocate 2 x net_buffer_length */
if (!(sctx->IoBuffer= (PUCHAR)LocalAlloc(0, 0x4000)))
goto end;
if (AcquireCredentialsHandleA(NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND,
NULL, &Cred, NULL, NULL, &sctx->CredHdl, NULL) != SEC_E_OK)
goto end;
if (ma_schannel_client_handshake(cssl))
goto end;
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext);
if (sRet != SEC_E_OK)
goto end;
if (!VerifyServerCertificate(sctx,
pRemoteCertContext,
mysql->host,
0 ))
goto end;
return 0;
end:
/* todo: cleanup */
if (sctx->IoBuffer)
LocalFree(sctx->IoBuffer);
if (sctx->client_ca_ctx)
CertFreeCertificateContext(sctx->client_ca_ctx);
if (sctx->client_cert_ctx)
CertFreeCertificateContext(sctx->client_cert_ctx);
return 1;
}
size_t cio_schannel_read(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
}
size_t cio_schannel_write(MARIADB_SSL *cssl, const uchar* buffer, size_t length)
{
}
my_bool cio_schannel_close(MARIADB_SSL *cssl)
{
int i, rc;
return rc; return rc;
} }
int cio_schannel_verify_server_cert(MARIADB_SSL *cssl) const char *ma_ssl_get_cipher(MARIADB_SSL *cssl)
{ {
} SecPkgContext_ConnectionInfo cinfo;
SECURITY_STATUS sRet;
SC_CTX *sctx;
DWORD i= 0;
const char *cio_schannel_cipher(MARIADB_SSL *cssl)
{
if (!cssl || !cssl->ssl) if (!cssl || !cssl->ssl)
return NULL; return NULL;
sctx= (SC_CTX *)cssl->ssl;
sRet= QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_CONNECTION_INFO, (PVOID)&cinfo);
if (sRet != SEC_E_OK)
return NULL;
while (sc_ciphers[i].cipher)
{
if (sc_ciphers[i].aid == cinfo.aiCipher)
return sc_ciphers[i].cipher;
i++;
}
return NULL;
} }
unsigned int ma_ssl_get_finger_print(MARIADB_SSL *cssl, unsigned char *fp, unsigned int len)
{
SC_CTX *sctx= (SC_CTX *)cssl->ssl;
PCCERT_CONTEXT pRemoteCertContext = NULL;
if (QueryContextAttributes(&sctx->ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&pRemoteCertContext) != SEC_E_OK)
return NULL;
CertGetCertificateContextProperty(pRemoteCertContext, CERT_HASH_PROP_ID, fp, &len);
return len;
}

View File

@@ -436,6 +436,7 @@ int cio_socket_wait_io_or_timeout(MARIADB_CIO *cio, my_bool is_read, int timeout
csock= (struct st_cio_socket *)cio->data; csock= (struct st_cio_socket *)cio->data;
{ {
#ifndef _WIN32 #ifndef _WIN32
memset(&p_fd, 0, sizeof(p_fd));
p_fd.fd= csock->socket; p_fd.fd= csock->socket;
p_fd.events= (is_read) ? POLLIN : POLLOUT; p_fd.events= (is_read) ? POLLIN : POLLOUT;

File diff suppressed because it is too large Load Diff

View File

@@ -52,7 +52,6 @@ mysql_declare_client_plugin(TRACE)
"LGPL", "LGPL",
&trace_init, &trace_init,
&trace_deinit, &trace_deinit,
NULL
mysql_end_client_plugin; mysql_end_client_plugin;
static char *commands[]= { static char *commands[]= {

Binary file not shown.

View File

@@ -57,7 +57,7 @@ static int test_conc66(MYSQL *my)
diag("Error: %s", mysql_error(mysql)); diag("Error: %s", mysql_error(mysql));
return FAIL; return FAIL;
} }
rc= mysql_query(my, "DROP USER conc66@localhost"); rc= mysql_query(my, "DROP USER conc66@%");
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
mysql_close(mysql); mysql_close(mysql);

View File

@@ -143,7 +143,6 @@ static int create_dyncol_num(MYSQL *mysql)
rc= mariadb_dyncol_unpack(&dyncol, &my_count, &my_keys, &my_vals); rc= mariadb_dyncol_unpack(&dyncol, &my_count, &my_keys, &my_vals);
diag("unpack: %d %d", rc, my_count); diag("unpack: %d %d", rc, my_count);
diag("---------------__");
for(i=0; i < 5; i++) for(i=0; i < 5; i++)
{ {
diag("%s %d", my_keys[i].str, my_keys[i].length); diag("%s %d", my_keys[i].str, my_keys[i].length);
@@ -200,7 +199,7 @@ static int mdev_x1(MYSQL *mysql)
for (i=0; i < unpack_columns; i++) for (i=0; i < unpack_columns; i++)
if (memcmp(unpack_vals[i].x.string.value.str, vals[i].x.string.value.str, vals[i].x.string.value.length)) if (memcmp(unpack_vals[i].x.string.value.str, vals[i].x.string.value.str, vals[i].x.string.value.length))
printf("Error1: key: %1s val: %s %s\n", unpack_keys[i].str, unpack_vals[i].x.string.value.str, vals[i].x.string.value.str); diag("Error1: key: %1s val: %s %s", unpack_keys[i].str, unpack_vals[i].x.string.value.str, vals[i].x.string.value.str);
free(unpack_keys); free(unpack_keys);
free(unpack_vals); free(unpack_vals);

View File

@@ -978,6 +978,7 @@ static int test_remote1(MYSQL *mysql)
{ {
int rc; int rc;
void *myplugin= (void *)mysql_client_find_plugin(mysql, "trace_example", MYSQL_CLIENT_TRACE_PLUGIN);
remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MYSQL_CLIENT_REMOTEIO_PLUGIN); remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MYSQL_CLIENT_REMOTEIO_PLUGIN);
if (!remote_plugin) if (!remote_plugin)
{ {

View File

@@ -67,7 +67,8 @@ static int test_ssl(MYSQL *mysql)
if (!skip_ssl) if (!skip_ssl)
{ {
rc= mysql_query(mysql, "DROP USER 'ssluser'@'localhost'"); rc= mysql_query(mysql, "DROP USER 'ssluser'@'%'");
rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'%' IDENTIFIED BY 'sslpw' REQUIRE SSL");
rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE SSL"); rc= mysql_query(mysql, "GRANT ALL ON test.* TO 'ssluser'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE SSL");
rc= mysql_query(mysql, "FLUSH PRVILEGES"); rc= mysql_query(mysql, "FLUSH PRVILEGES");
} }
@@ -92,11 +93,7 @@ static int test_ssl_cipher(MYSQL *unused)
port, socketname, 0), mysql_error(my)); port, socketname, 0), mysql_error(my));
cipher= (char *)mysql_get_ssl_cipher(my); cipher= (char *)mysql_get_ssl_cipher(my);
#ifdef HAVE_OPENSSL FAIL_IF(cipher == NULL, "used cipher is NULL");
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
#elif defined(HAVE_HNUTLS)
FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC");
#endif
mysql_close(my); mysql_close(my);
return OK; return OK;
} }
@@ -109,7 +106,9 @@ static int test_conc95(MYSQL *my)
if (check_skip_ssl()) if (check_skip_ssl())
return SKIP; return SKIP;
rc= mysql_query(my, "DROP USER 'ssluser1'@'%'");
rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'"); rc= mysql_query(my, "DROP USER 'ssluser1'@'localhost'");
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'%' IDENTIFIED BY 'sslpw' REQUIRE X509");
rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509"); rc= mysql_query(my, "GRANT ALL ON test.* TO 'ssluser1'@'localhost' IDENTIFIED BY 'sslpw' REQUIRE X509");
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
rc= mysql_query(my, "FLUSH PRIVILEGES"); rc= mysql_query(my, "FLUSH PRIVILEGES");
@@ -117,8 +116,8 @@ static int test_conc95(MYSQL *my)
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", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/ca-cert.pem",
NULL, NULL,
NULL); NULL);
@@ -126,6 +125,7 @@ static int test_conc95(MYSQL *my)
if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema, if (!mysql_real_connect(mysql, hostname, "ssluser1", sslpw, schema,
port, socketname, 0)) port, socketname, 0))
{ {
diag("Error: %s", mysql_error(mysql));
mysql_close(mysql); mysql_close(mysql);
diag("could not establish x509 connection"); diag("could not establish x509 connection");
return FAIL; return FAIL;
@@ -178,11 +178,7 @@ static int test_multi_ssl_connections(MYSQL *unused)
} }
cipher= (char *)mysql_get_ssl_cipher(mysql[i]); cipher= (char *)mysql_get_ssl_cipher(mysql[i]);
#ifdef HAVE_OPENSSL FAIL_IF(cipher == NULL, "used cipher is NULL");
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
#elif defined(HAVE_HNUTLS)
FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC");
#endif
} }
for (i=0; i < 50; i++) for (i=0; i < 50; i++)
mysql_close(mysql[i]); mysql_close(mysql[i]);
@@ -404,8 +400,11 @@ static int test_conc50_3(MYSQL *my)
if (check_skip_ssl()) if (check_skip_ssl())
return SKIP; return SKIP;
mysql_query(my, "DROP USER 'ssltest'@'localhost'"); mysql_query(my, "DROP USER 'ssltest'@'%'");
sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'%' REQUIRE SSL", schema ? schema : "*");
rc= mysql_query(my, query);
check_mysql_rc(rc, my);
sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'localhost' REQUIRE SSL", schema ? schema : "*"); sprintf(query, "GRANT ALL ON %s.* TO 'ssltest'@'localhost' REQUIRE SSL", schema ? schema : "*");
rc= mysql_query(my, query); rc= mysql_query(my, query);
check_mysql_rc(rc, my); check_mysql_rc(rc, my);
@@ -489,7 +488,7 @@ static int test_bug62743(MYSQL *my)
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, "dummykey", NULL, NULL, NULL, NULL); mysql_ssl_set(mysql, "dummykey", "@CMAKE_SOURCE_DIR@/unittest/libmariadb/certs/client-cert.pem", NULL, NULL, NULL);
mysql_real_connect(mysql, hostname, ssluser, sslpw, schema, mysql_real_connect(mysql, hostname, ssluser, sslpw, schema,
port, socketname, 0); port, socketname, 0);
@@ -596,6 +595,8 @@ static int test_conc_102(MYSQL *mysql)
rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)"); rc= mysql_query(mysql, "INSERT INTO t_conc102 VALUES (0)");
check_mysql_rc(rc, mysql); check_mysql_rc(rc, mysql);
pthread_mutex_init(&LOCK_test, 0);
for (i=0; i < 50; i++) for (i=0; i < 50; i++)
{ {
#ifndef WIN32 #ifndef WIN32
@@ -613,7 +614,8 @@ static int test_conc_102(MYSQL *mysql)
#else #else
WaitForSingleObject(hthreads[i], INFINITE); WaitForSingleObject(hthreads[i], INFINITE);
#endif #endif
} }
pthread_mutex_destroy(&LOCK_test);
rc= mysql_query(mysql, "SELECT a FROM t_conc102"); rc= mysql_query(mysql, "SELECT a FROM t_conc102");
check_mysql_rc(rc, mysql); check_mysql_rc(rc, mysql);
res= mysql_store_result(mysql); res= mysql_store_result(mysql);
@@ -645,11 +647,7 @@ static int test_ssl_fp(MYSQL *unused)
port, socketname, 0), mysql_error(my)); port, socketname, 0), mysql_error(my));
cipher= (char *)mysql_get_ssl_cipher(my); cipher= (char *)mysql_get_ssl_cipher(my);
#ifdef HAVE_OPENSSL FAIL_IF(cipher == NULL, "used cipher is NULL");
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA");
#elif defined(HAVE_HNUTLS)
FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC");
#endif
mysql_close(my); mysql_close(my);
return OK; return OK;
} }
@@ -672,19 +670,43 @@ static int test_ssl_fp_list(MYSQL *unused)
FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema, FAIL_IF(!mysql_real_connect(my, hostname, ssluser, sslpw, schema,
port, socketname, 0), mysql_error(my)); port, socketname, 0), mysql_error(my));
#ifdef HAVE_OPENSSL cipher= mysql_get_ssl_cipher(my);
FAIL_IF(strcmp(cipher, "DHE-RSA-AES256-SHA") != 0, "Cipher != DHE-RSA-AES256-SHA"); FAIL_IF(cipher == NULL, "used cipher is NULL");
#elif defined(HAVE_HNUTLS)
FAIL_IF(strcmp(cipher, "AES-256-CBC") != 0, "Cipher != AES-256-CBC");
#endif
mysql_close(my); mysql_close(my);
return OK; return OK;
} }
static int test_ssl_long_msg(MYSQL *unused)
{
MYSQL *my;
char buffer[20000];
int rc;
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));
memset(buffer, 0, 20000);
strcpy(buffer, "SET @a:=");
memset(buffer + strlen(buffer), '0', 19000);
rc= mysql_query(my, buffer);
check_mysql_rc(rc, my);
mysql_close(my);
}
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},
{"test_ssl_long_msg", test_ssl_long_msg, TEST_CONNECTION_NEW, 0, NULL, NULL},
{"test_conc127", test_conc127, 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", 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},