-
Notifications
You must be signed in to change notification settings - Fork 4k
Extend TLS/SSL functionality with SNI and using system roots #358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3352,9 +3352,12 @@ const char *STDCALL mysql_get_ssl_cipher(MYSQL *mysql MY_ATTRIBUTE((unused))) { | |
return nullptr; | ||
} | ||
|
||
#if OPENSSL_VERSION_NUMBER < 0x10002000L | ||
/* | ||
Check the server's (subject) Common Name against the | ||
hostname we connected to | ||
hostname we connected to. This function is only used | ||
when OpenSSL is older than 1.0.2 since versions since | ||
1.0.2 provide an implementation for hostname validation. | ||
|
||
SYNOPSIS | ||
ssl_verify_server_cert() | ||
|
@@ -3375,13 +3378,11 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, | |
X509 *server_cert = nullptr; | ||
int ret_validation = 1; | ||
|
||
#if !(OPENSSL_VERSION_NUMBER >= 0x10002000L) | ||
int cn_loc = -1; | ||
char *cn = NULL; | ||
ASN1_STRING *cn_asn1 = NULL; | ||
X509_NAME_ENTRY *cn_entry = NULL; | ||
X509_NAME *subject = NULL; | ||
#endif | ||
|
||
DBUG_TRACE; | ||
DBUG_PRINT("enter", ("server_hostname: %s", server_hostname)); | ||
|
@@ -3411,16 +3412,6 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, | |
are what we expect. | ||
*/ | ||
|
||
/* Use OpenSSL certificate matching functions instead of our own if we | ||
have OpenSSL. The X509_check_* functions return 1 on success. | ||
*/ | ||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L | ||
/* | ||
For OpenSSL 1.0.2 and up we already set certificate verification | ||
parameters in the new_VioSSLFd() to perform automatic checks. | ||
*/ | ||
ret_validation = 0; | ||
#else /* OPENSSL_VERSION_NUMBER < 0x10002000L */ | ||
/* | ||
OpenSSL prior to 1.0.2 do not support X509_check_host() function. | ||
Use deprecated X509_get_subject_name() instead. | ||
|
@@ -3460,14 +3451,14 @@ static int ssl_verify_server_cert(Vio *vio, const char *server_hostname, | |
/* Success */ | ||
ret_validation = 0; | ||
} | ||
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */ | ||
|
||
*errptr = "SSL certificate validation success"; | ||
|
||
error: | ||
if (server_cert != nullptr) X509_free(server_cert); | ||
return ret_validation; | ||
} | ||
#endif /* OPENSSL_VERSION_NUMBER < 0x10002000L */ | ||
|
||
/* | ||
Note that the mysql argument must be initialized with mysql_init() | ||
|
@@ -4161,20 +4152,6 @@ static int cli_establish_ssl(MYSQL *mysql) { | |
goto error; | ||
} | ||
|
||
/* | ||
If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure that the | ||
connection doesn't succeed without providing the CA certificate. | ||
*/ | ||
if (mysql->options.extension && | ||
mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED && | ||
!(mysql->options.ssl_ca || mysql->options.ssl_capath)) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), | ||
"CA certificate is required if ssl-mode " | ||
"is VERIFY_CA or VERIFY_IDENTITY"); | ||
goto error; | ||
} | ||
|
||
/* | ||
Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and the | ||
server supports SSL. Fallback on unencrypted connection otherwise. | ||
|
@@ -4186,11 +4163,21 @@ static int cli_establish_ssl(MYSQL *mysql) { | |
struct st_mysql_options *options = &mysql->options; | ||
struct st_VioSSLFd *ssl_fd; | ||
enum enum_ssl_init_error ssl_init_error = SSL_INITERR_NOERROR; | ||
#if OPENSSL_VERSION_NUMBER < 0x10002000L | ||
const char *cert_error; | ||
#endif | ||
unsigned long ssl_error; | ||
char buff[33], *end; | ||
const bool verify_identity = | ||
mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT; | ||
int verify; | ||
bool verify_identity; | ||
|
||
if (options->extension && options->extension->ssl_mode >= SSL_MODE_VERIFY_CA) { | ||
verify = SSL_VERIFY_PEER; | ||
verify_identity = options->extension->ssl_mode == SSL_MODE_VERIFY_IDENTITY; | ||
} else { | ||
verify = SSL_VERIFY_NONE; | ||
verify_identity = false; | ||
} | ||
|
||
/* check if server supports compression else turn off client capability */ | ||
if (!(mysql->server_capabilities & CLIENT_ZSTD_COMPRESSION_ALGORITHM)) | ||
|
@@ -4227,7 +4214,7 @@ static int cli_establish_ssl(MYSQL *mysql) { | |
options->extension ? options->extension->ssl_crl : nullptr, | ||
options->extension ? options->extension->ssl_crlpath : nullptr, | ||
options->extension ? options->extension->ssl_ctx_flags : 0, | ||
verify_identity ? mysql->host : nullptr))) { | ||
mysql->host, verify, verify_identity))) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), | ||
sslGetErrString(ssl_init_error)); | ||
|
@@ -4249,13 +4236,19 @@ static int cli_establish_ssl(MYSQL *mysql) { | |
} | ||
DBUG_PRINT("info", ("IO layer change done!")); | ||
|
||
/* OpenSSL 1.0.2 and up will use their own verification function. | ||
* This is set up in new_VioSSLConnectorFd and adding here it here | ||
* again is superfluous. | ||
*/ | ||
#if OPENSSL_VERSION_NUMBER < 0x10002000L | ||
/* Verify server cert */ | ||
if (verify_identity && | ||
ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error); | ||
goto error; | ||
} | ||
#endif | ||
|
||
MYSQL_TRACE(SSL_CONNECTED, mysql, ()); | ||
MYSQL_TRACE_STAGE(mysql, AUTHENTICATE); | ||
|
@@ -4305,21 +4298,6 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { | |
goto error; | ||
} | ||
|
||
/* | ||
If the ssl_mode is VERIFY_CA or VERIFY_IDENTITY, make sure | ||
that the connection doesn't succeed without providing the | ||
CA certificate. | ||
*/ | ||
if (mysql->options.extension && | ||
mysql->options.extension->ssl_mode > SSL_MODE_REQUIRED && | ||
!(mysql->options.ssl_ca || mysql->options.ssl_capath)) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), | ||
"CA certificate is required if ssl-mode " | ||
"is VERIFY_CA or VERIFY_IDENTITY"); | ||
goto error; | ||
} | ||
|
||
/* | ||
Attempt SSL connection if ssl_mode != SSL_MODE_DISABLED and | ||
the server supports SSL. Fallback on unencrypted | ||
|
@@ -4366,11 +4344,21 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { | |
struct st_mysql_options *options = &mysql->options; | ||
struct st_VioSSLFd *ssl_fd; | ||
enum enum_ssl_init_error ssl_init_error; | ||
#if OPENSSL_VERSION_NUMBER < 0x10002000L | ||
const char *cert_error; | ||
#endif | ||
unsigned long ssl_error; | ||
size_t ret; | ||
const bool verify_identity = | ||
mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT; | ||
int verify; | ||
bool verify_identity; | ||
|
||
if (options->extension && options->extension->ssl_mode >= SSL_MODE_VERIFY_CA) { | ||
verify = SSL_VERIFY_PEER; | ||
verify_identity = options->extension->ssl_mode == SSL_MODE_VERIFY_IDENTITY; | ||
} else { | ||
verify = SSL_VERIFY_NONE; | ||
verify_identity = false; | ||
} | ||
|
||
MYSQL_TRACE_STAGE(mysql, SSL_NEGOTIATION); | ||
|
||
|
@@ -4385,7 +4373,7 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { | |
options->extension ? options->extension->ssl_crl : nullptr, | ||
options->extension ? options->extension->ssl_crlpath : nullptr, | ||
options->extension ? options->extension->ssl_ctx_flags : 0, | ||
verify_identity ? mysql->host : nullptr))) { | ||
mysql->host, verify, verify_identity))) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, | ||
unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), | ||
|
@@ -4427,14 +4415,19 @@ static net_async_status cli_establish_ssl_nonblocking(MYSQL *mysql, int *res) { | |
/* sslconnect creates a new vio, so update it. */ | ||
vio_set_blocking_flag(net->vio, !ctx->non_blocking); | ||
|
||
/* OpenSSL 1.0.2 and up will use their own verification function. | ||
* This is set up in new_VioSSLConnectorFd and adding here it here | ||
* again is superfluous. | ||
*/ | ||
#if OPENSSL_VERSION_NUMBER < 0x10002000L | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On OpenSSL 1.0.2 and newer, the additional identity check here is not needed since we already set it up inside If anything, it makes it more brittle because there could be implementation differences between these validation functions (such as honoring DNS SANs, using the deprecated CN or not etc). |
||
/* Verify server cert */ | ||
if (verify_identity && | ||
ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) { | ||
set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, | ||
ER_CLIENT(CR_SSL_CONNECTION_ERROR), cert_error); | ||
goto error; | ||
} | ||
|
||
#endif | ||
MYSQL_TRACE(SSL_CONNECTED, mysql, ()); | ||
MYSQL_TRACE_STAGE(mysql, AUTHENTICATE); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -580,7 +580,8 @@ static struct st_VioSSLFd *new_VioSSLFd( | |
const char *ca_path, const char *cipher, | ||
const char *ciphersuites MY_ATTRIBUTE((unused)), bool is_client, | ||
enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, | ||
const long ssl_ctx_flags, const char *server_host MY_ATTRIBUTE((unused))) { | ||
const long ssl_ctx_flags, const char *server_host, | ||
bool verify_identity MY_ATTRIBUTE((unused))) { | ||
DH *dh; | ||
struct st_VioSSLFd *ssl_fd; | ||
long ssl_ctx_options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; | ||
|
@@ -633,6 +634,10 @@ static struct st_VioSSLFd *new_VioSSLFd( | |
return nullptr; | ||
} | ||
|
||
if (server_host) { | ||
ssl_fd->hostname = my_strdup(PSI_NOT_INSTRUMENTED, server_host, MYF(0)); | ||
} | ||
|
||
#ifdef HAVE_TLSv13 | ||
/* | ||
Set OpenSSL TLS v1.3 ciphersuites. | ||
|
@@ -758,7 +763,7 @@ static struct st_VioSSLFd *new_VioSSLFd( | |
If server_host parameter is set it contains either IP address or | ||
server's hostname. Pass it to the lib to perform automatic checks. | ||
*/ | ||
if (server_host) { | ||
if (verify_identity && server_host) { | ||
X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ssl_fd->ssl_context); | ||
assert(is_client); | ||
/* | ||
|
@@ -784,6 +789,7 @@ static struct st_VioSSLFd *new_VioSSLFd( | |
error: | ||
DBUG_PRINT("error", ("%s", sslGetErrString(*error))); | ||
report_errors(); | ||
my_free(const_cast<char *>(ssl_fd->hostname)); | ||
SSL_CTX_free(ssl_fd->ssl_context); | ||
my_free(ssl_fd); | ||
return nullptr; | ||
|
@@ -795,19 +801,12 @@ struct st_VioSSLFd *new_VioSSLConnectorFd( | |
const char *key_file, const char *cert_file, const char *ca_file, | ||
const char *ca_path, const char *cipher, const char *ciphersuites, | ||
enum enum_ssl_init_error *error, const char *crl_file, const char *crl_path, | ||
const long ssl_ctx_flags, const char *server_host) { | ||
const long ssl_ctx_flags, const char *server_host, int verify, bool verify_identity) { | ||
struct st_VioSSLFd *ssl_fd; | ||
int verify = SSL_VERIFY_PEER; | ||
|
||
/* | ||
Turn off verification of servers certificate if both | ||
ca_file and ca_path is set to NULL | ||
*/ | ||
if (ca_file == nullptr && ca_path == nullptr) verify = SSL_VERIFY_NONE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We no longer infer this value, but it gets passed in explicitly by the caller based on the SSL mode that is selected. This means the SSL mode flag is leading and we fall back already automatically to the system roots. |
||
|
||
if (!(ssl_fd = new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, | ||
ciphersuites, true, error, crl_file, crl_path, | ||
ssl_ctx_flags, server_host))) { | ||
ssl_ctx_flags, server_host, verify_identity))) { | ||
return nullptr; | ||
} | ||
|
||
|
@@ -828,7 +827,7 @@ struct st_VioSSLFd *new_VioSSLAcceptorFd( | |
int verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; | ||
if (!(ssl_fd = new_VioSSLFd(key_file, cert_file, ca_file, ca_path, cipher, | ||
ciphersuites, false, error, crl_file, crl_path, | ||
ssl_ctx_flags, nullptr))) { | ||
ssl_ctx_flags, nullptr, false))) { | ||
return nullptr; | ||
} | ||
/* Init the the VioSSLFd as a "acceptor" ie. the server side */ | ||
|
@@ -849,6 +848,7 @@ struct st_VioSSLFd *new_VioSSLAcceptorFd( | |
} | ||
|
||
void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd) { | ||
my_free(const_cast<char *>(fd->hostname)); | ||
SSL_CTX_free(fd->ssl_context); | ||
my_free(fd); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code itself already supports fallback to the system roots if nothing is given. So this check can be removed on its own. The reason for all the other changes is that providing the ca path or file was also used to infer the SSL verification mode, which is now made all explicit.
The fallback logic exists here:
mysql-server/vio/viosslfactories.cc
Lines 696 to 700 in beb865a