mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-14 11:47:15 +01:00
701 lines
17 KiB
C
701 lines
17 KiB
C
|
/************************************************************************************
|
||
|
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>
|
||
|
#include <string.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 < 0x10100000
|
||
|
#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;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
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 OPENSSL_VERSION_NUMBER < 0x10100000
|
||
|
if (ssl_crypto_init())
|
||
|
goto end;
|
||
|
#endif
|
||
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||
|
OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
|
||
|
#else
|
||
|
SSL_library_init();
|
||
|
#if SSLEAY_VERSION_NUMBER >= 0x00907000L
|
||
|
OPENSSL_config(NULL);
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/* load errors */
|
||
|
SSL_load_error_strings();
|
||
|
/* digests and ciphers */
|
||
|
OpenSSL_add_all_algorithms();
|
||
|
|
||
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||
|
if (!(SSL_context= SSL_CTX_new(TLS_client_method())))
|
||
|
#else
|
||
|
if (!(SSL_context= SSL_CTX_new(SSLv23_client_method())))
|
||
|
#endif
|
||
|
{
|
||
|
my_SSL_error(mysql);
|
||
|
rc= 1;
|
||
|
goto end;
|
||
|
}
|
||
|
/* use server preferences instead of client preferences:
|
||
|
client sends lowest tls version (=1.0) first, and will
|
||
|
update to the version the server sent in client hellp
|
||
|
response packet */
|
||
|
SSL_CTX_set_options(SSL_context, SSL_OP_ALL);
|
||
|
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)
|
||
|
{
|
||
|
#if OPENSSL_VERSION_NUMBER < 0x10100000
|
||
|
ERR_remove_state(0);
|
||
|
#endif
|
||
|
EVP_cleanup();
|
||
|
CRYPTO_cleanup_all_ex_data();
|
||
|
ERR_free_strings();
|
||
|
CONF_modules_free();
|
||
|
CONF_modules_unload(1);
|
||
|
}
|
||
|
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.extension->ssl_crl,
|
||
|
mysql->options.extension->ssl_crlpath) == 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)
|
||
|
{
|
||
|
/* SHA1 fingerprint (160 bit) / 8 * 2 + 1 */
|
||
|
char hexstr[41];
|
||
|
|
||
|
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)
|
||
|
#endif
|
||
|
return 1;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
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)
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
my_bool try_connect= 1;
|
||
|
|
||
|
DBUG_ENTER("my_ssl_connect");
|
||
|
|
||
|
DBUG_ASSERT(ssl != NULL);
|
||
|
|
||
|
mysql= (MYSQL *)SSL_get_app_data(ssl);
|
||
|
CLEAR_CLIENT_ERROR(mysql);
|
||
|
|
||
|
/* Set socket to non blocking */
|
||
|
if (!(blocking= vio_is_blocking(mysql->net.vio)))
|
||
|
vio_blocking(mysql->net.vio, FALSE, 0);
|
||
|
|
||
|
SSL_clear(ssl);
|
||
|
SSL_SESSION_set_timeout(SSL_get_session(ssl),
|
||
|
mysql->options.connect_timeout);
|
||
|
SSL_set_fd(ssl, mysql->net.vio->sd);
|
||
|
|
||
|
while (try_connect && (rc= SSL_connect(ssl)) == -1)
|
||
|
{
|
||
|
switch(SSL_get_error(ssl, rc)) {
|
||
|
case SSL_ERROR_WANT_READ:
|
||
|
if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1)
|
||
|
try_connect= 0;
|
||
|
break;
|
||
|
case SSL_ERROR_WANT_WRITE:
|
||
|
if (vio_wait_or_timeout(mysql->net.vio, TRUE, mysql->options.connect_timeout) < 1)
|
||
|
try_connect= 0;
|
||
|
break;
|
||
|
default:
|
||
|
try_connect= 0;
|
||
|
}
|
||
|
}
|
||
|
if (rc != 1)
|
||
|
{
|
||
|
my_SSL_error(mysql);
|
||
|
DBUG_RETURN(1);
|
||
|
}
|
||
|
|
||
|
if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT))
|
||
|
{
|
||
|
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;
|
||
|
X509_NAME *x509sn;
|
||
|
int cn_pos;
|
||
|
X509_NAME_ENTRY *cn_entry;
|
||
|
ASN1_STRING *cn_asn1;
|
||
|
const char *cn_str;
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
x509sn= X509_get_subject_name(cert);
|
||
|
|
||
|
if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0)
|
||
|
goto error;
|
||
|
|
||
|
if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos)))
|
||
|
goto error;
|
||
|
|
||
|
if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry)))
|
||
|
goto error;
|
||
|
|
||
|
cn_str = (char *)ASN1_STRING_data(cn_asn1);
|
||
|
|
||
|
/* Make sure there is no embedded \0 in the CN */
|
||
|
if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str))
|
||
|
goto error;
|
||
|
|
||
|
if (strcmp(cn_str, mysql->host))
|
||
|
goto error;
|
||
|
|
||
|
X509_free(cert);
|
||
|
|
||
|
DBUG_RETURN(0);
|
||
|
|
||
|
error:
|
||
|
X509_free(cert);
|
||
|
|
||
|
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 */
|