diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-07-01 18:15:00 +0000 |
commit | a2a2e32c02643a0cec111511220227703fda1cd5 (patch) | |
tree | 69cc2b631234c2a8e026b9cd4d72676c61c594df /sql-common | |
parent | Releasing progress-linux version 1:10.11.8-1~progress7.99u1. (diff) | |
download | mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.tar.xz mariadb-a2a2e32c02643a0cec111511220227703fda1cd5.zip |
Merging upstream version 1:11.4.2.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/client.c | 313 | ||||
-rw-r--r-- | sql-common/client_plugin.c | 4 |
2 files changed, 194 insertions, 123 deletions
diff --git a/sql-common/client.c b/sql-common/client.c index db52a42c..9b88855f 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -167,6 +167,7 @@ static void mysql_close_free_options(MYSQL *mysql); static void mysql_close_free(MYSQL *mysql); static void mysql_prune_stmt_list(MYSQL *mysql); static int cli_report_progress(MYSQL *mysql, char *packet, uint length); +static my_bool parse_ok_packet(MYSQL *mysql, ulong length); CHARSET_INFO *default_client_charset_info = &my_charset_latin1; @@ -723,6 +724,12 @@ void end_server(MYSQL *mysql) DBUG_ENTER("end_server"); if (mysql->net.vio != 0) { + struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; + if (ssl_fd) + SSL_CTX_free(ssl_fd->ssl_context); + my_free(ssl_fd); + mysql->connector_fd = 0; + DBUG_PRINT("info",("Net: %s", vio_description(mysql->net.vio))); #ifdef MYSQL_SERVER slave_io_thread_detach_vio(); @@ -1432,6 +1439,7 @@ mysql_init(MYSQL *mysql) bzero((char*) (mysql), sizeof(*(mysql))); mysql->options.connect_timeout= CONNECT_TIMEOUT; mysql->charset=default_client_charset_info; + mysql->options.use_ssl= 1; strmov(mysql->net.sqlstate, not_error_sqlstate); /* @@ -1509,7 +1517,6 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , static void mysql_ssl_free(MYSQL *mysql __attribute__((unused))) { - struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; DBUG_ENTER("mysql_ssl_free"); my_free(mysql->options.ssl_key); @@ -1522,9 +1529,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) my_free(mysql->options.extension->ssl_crl); my_free(mysql->options.extension->ssl_crlpath); } - if (ssl_fd) - SSL_CTX_free(ssl_fd->ssl_context); - my_free(mysql->connector_fd); mysql->options.ssl_key = 0; mysql->options.ssl_cert = 0; mysql->options.ssl_ca = 0; @@ -1536,7 +1540,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) mysql->options.extension->ssl_crlpath = 0; } mysql->options.use_ssl = FALSE; - mysql->connector_fd = 0; DBUG_VOID_RETURN; } @@ -1570,8 +1573,7 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) SYNOPSIS ssl_verify_server_cert() - vio pointer to a SSL connected vio - server_hostname name of the server that we connected to + MYSQL mysql errptr if we fail, we'll return (a pointer to a string describing) the reason here @@ -1583,33 +1585,24 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) #if defined(HAVE_OPENSSL) -#ifdef HAVE_X509_check_host #include <openssl/x509v3.h> -#endif -static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const char **errptr) +static int ssl_verify_server_cert(MYSQL *mysql, const char **errptr) { SSL *ssl; X509 *server_cert= NULL; -#ifndef HAVE_X509_check_host - char *cn= NULL; - int cn_loc= -1; - ASN1_STRING *cn_asn1= NULL; - X509_NAME_ENTRY *cn_entry= NULL; - X509_NAME *subject= NULL; -#endif int ret_validation= 1; DBUG_ENTER("ssl_verify_server_cert"); - DBUG_PRINT("enter", ("server_hostname: %s", server_hostname)); + DBUG_PRINT("enter", ("server_hostname: %s", mysql->host)); - if (!(ssl= (SSL*)vio->ssl_arg)) + if (!(ssl= (SSL*)mysql->net.vio->ssl_arg)) { *errptr= "No SSL pointer found"; goto error; } - if (!server_hostname) + if (!mysql->host) { *errptr= "No server hostname supplied"; goto error; @@ -1621,70 +1614,32 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname, const c goto error; } - if (X509_V_OK != SSL_get_verify_result(ssl)) + switch (SSL_get_verify_result(ssl)) { + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: /* OpenSSL */ + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: /* OpenSSL */ + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: /* wolfSSL */ + /* + If the caller have specified CA - it'll define whether the + cert is good. Otherwise we'll do more checks. + */ + ret_validation= (mysql->options.ssl_ca && mysql->options.ssl_ca[0]) || + (mysql->options.ssl_capath && mysql->options.ssl_capath[0]); + mysql->tls_self_signed_error= *errptr= "SSL certificate is self-signed"; + break; + case X509_V_OK: + ret_validation= X509_check_host(server_cert, mysql->host, + strlen(mysql->host), 0, 0) != 1 && + X509_check_ip_asc(server_cert, mysql->host, 0) != 1; + *errptr= "SSL certificate validation failure"; + break; + default: *errptr= "Failed to verify the server certificate"; - goto error; - } - /* - We already know that the certificate exchanged was valid; the SSL library - handled that. Now we need to verify that the contents of the certificate - are what we expect. - */ - -#ifdef HAVE_X509_check_host - ret_validation= - X509_check_host(server_cert, server_hostname, - strlen(server_hostname), 0, 0) != 1; -#ifndef HAVE_WOLFSSL - if (ret_validation) - { - ret_validation= - X509_check_ip_asc(server_cert, server_hostname, 0) != 1; - } -#endif -#else - subject= X509_get_subject_name(server_cert); - cn_loc= X509_NAME_get_index_by_NID(subject, NID_commonName, -1); - if (cn_loc < 0) - { - *errptr= "Failed to get CN location in the certificate subject"; - goto error; - } - cn_entry= X509_NAME_get_entry(subject, cn_loc); - if (cn_entry == NULL) - { - *errptr= "Failed to get CN entry using CN location"; - goto error; - } - - cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry); - if (cn_asn1 == NULL) - { - *errptr= "Failed to get CN from CN entry"; - goto error; - } - - cn= (char *) ASN1_STRING_get0_data(cn_asn1); - - if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn)) - { - *errptr= "NULL embedded in the certificate CN"; - goto error; + break; } - DBUG_PRINT("info", ("Server hostname in cert: %s", cn)); - if (!strcmp(cn, server_hostname)) - { - /* Success */ - ret_validation= 0; - } -#endif - *errptr= "SSL certificate validation failure"; - error: - if (server_cert != NULL) - X509_free (server_cert); + X509_free(server_cert); DBUG_RETURN(ret_validation); } @@ -1813,6 +1768,7 @@ C_MODE_END typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int); static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +static int native_password_auth_hash(MYSQL *mysql, uchar *out, size_t *outlen); static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); static auth_plugin_t native_password_client_plugin= @@ -1822,13 +1778,14 @@ static auth_plugin_t native_password_client_plugin= native_password_plugin_name, "R.J.Silk, Sergei Golubchik", "Native MySQL authentication", - {1, 0, 0}, + {1, 0, 1}, "GPL", NULL, NULL, NULL, NULL, - native_password_auth_client + native_password_auth_client, + native_password_auth_hash }; static auth_plugin_t old_password_client_plugin= @@ -1844,7 +1801,8 @@ static auth_plugin_t old_password_client_plugin= NULL, NULL, NULL, - old_password_auth_client + old_password_auth_client, + NULL }; @@ -2072,6 +2030,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, { MYSQL *mysql= mpvio->mysql; NET *net= &mysql->net; + enum enum_vio_type vio_type= net->vio->type; char *buff, *end; size_t buff_size; size_t connect_attrs_len= @@ -2106,6 +2065,12 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mpvio->db) mysql->client_flag|= CLIENT_CONNECT_WITH_DB; + if (vio_type == VIO_TYPE_NAMEDPIPE) + { + mysql->server_capabilities&= ~CLIENT_SSL; + mysql->options.use_ssl= 0; + } + /* Remove options that server doesn't support */ mysql->client_flag= mysql->client_flag & (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41) @@ -2136,9 +2101,9 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, certificate, a ssl connection is required. If the server does not support ssl, we abort the connection. */ - if (mysql->options.use_ssl && - (mysql->options.extension && mysql->options.extension->tls_verify_server_cert) && - !(mysql->server_capabilities & CLIENT_SSL)) + if (mysql->options.use_ssl && !(mysql->server_capabilities & CLIENT_SSL) && + (!mysql->options.extension || + !mysql->options.extension->tls_allow_invalid_server_cert)) { set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, ER(CR_SSL_CONNECTION_ERROR), @@ -2156,9 +2121,6 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, enum enum_ssl_init_error ssl_init_error; const char *cert_error; unsigned long ssl_error; -#ifdef EMBEDDED_LIBRARY - DBUG_ASSERT(0); // embedded should not do SSL connect -#endif /* Send mysql->client_flag, max_packet_size - unencrypted otherwise @@ -2189,7 +2151,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, ER(CR_SSL_CONNECTION_ERROR), sslGetErrString(ssl_init_error)); goto error; } - mysql->connector_fd= (unsigned char *) ssl_fd; + mysql->connector_fd= (uchar *) ssl_fd; /* Connect to the server */ DBUG_PRINT("info", ("IO layer change in progress...")); @@ -2207,13 +2169,34 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, DBUG_PRINT("info", ("IO layer change done!")); /* Verify server cert */ - if ((mysql->options.extension && mysql->options.extension->tls_verify_server_cert) && - ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) + if ((!mysql->options.extension || + !mysql->options.extension->tls_allow_invalid_server_cert) && + ssl_verify_server_cert(mysql, &cert_error)) { set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, ER(CR_SSL_CONNECTION_ERROR), cert_error); goto error; } + if (mysql->tls_self_signed_error) + { + /* + If the transport is secure (see opt_require_secure_transport) we + allow a self-signed cert as we know it came from the server. + + If no password or plugin uses insecure protocol - refuse the cert. + + Otherwise one last cert check after auth. + */ + if (vio_type == VIO_TYPE_SOCKET) + mysql->tls_self_signed_error= 0; + else if (!mysql->passwd || !mysql->passwd[0] || + !mpvio->plugin->hash_password_bin) + { + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), mysql->tls_self_signed_error); + goto error; + } + } } #endif /* HAVE_OPENSSL */ @@ -2568,6 +2551,14 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) DBUG_RETURN (1); + /* refuse insecure plugin if TLS is in doubt */ + if (mysql->tls_self_signed_error && !auth_plugin->hash_password_bin) + { + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), mysql->tls_self_signed_error); + DBUG_RETURN (1); + } + mpvio.plugin= auth_plugin; res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql); @@ -2589,7 +2580,7 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, if (res != CR_OK_HANDSHAKE_COMPLETE) { /* Read what server thinks about out new auth message report */ - if (cli_safe_read(mysql) == packet_error) + if ((pkt_length= cli_safe_read(mysql)) == packet_error) { if (mysql->net.last_errno == CR_SERVER_LOST) set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate, @@ -2604,7 +2595,44 @@ int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, net->read_pos[0] should always be 0 here if the server implements the protocol correctly */ - DBUG_RETURN (mysql->net.read_pos[0] != 0); + if (mysql->net.read_pos[0] != 0) + DBUG_RETURN(1); + if (!mysql->tls_self_signed_error) + DBUG_RETURN(0); + + /* Last attempt to validate the cert: compare cert info packet */ + DBUG_ASSERT(mysql->options.use_ssl); + DBUG_ASSERT(mysql->net.vio->ssl_arg); + DBUG_ASSERT(!mysql->options.extension || + !mysql->options.extension->tls_allow_invalid_server_cert); + DBUG_ASSERT(!mysql->options.ssl_ca || !mysql->options.ssl_ca[0]); + DBUG_ASSERT(!mysql->options.ssl_capath || !mysql->options.ssl_capath[0]); + DBUG_ASSERT(auth_plugin->hash_password_bin); + DBUG_ASSERT(mysql->passwd[0]); + + parse_ok_packet(mysql, pkt_length); /* set mysql->info */ + if (mysql->info && mysql->info[0] == '\1') + { + uchar fp[128], buf[1024], digest[256/8]; + size_t buflen= sizeof(buf); + uint fplen= sizeof(fp); + char *hexsig= mysql->info + 1, hexdigest[sizeof(digest)*2+1]; + X509 *cert= SSL_get_peer_certificate((SSL*)mysql->net.vio->ssl_arg); + X509_digest(cert, EVP_sha256(), fp, &fplen); + X509_free(cert); + auth_plugin->hash_password_bin(mysql, buf, &buflen); + my_sha256_multi(digest, buf, buflen, mysql->scramble, SCRAMBLE_LENGTH, + fp, fplen, NULL); + mysql->info= NULL; /* no need to confuse the client with binary info */ + + octet2hex(hexdigest, digest, sizeof(digest)); + if (strcmp(hexdigest, hexsig) == 0) + DBUG_RETURN(0); /* phew. self-signed certificate is validated! */ + } + + set_mysql_extended_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate, + ER(CR_SSL_CONNECTION_ERROR), mysql->tls_self_signed_error); + DBUG_RETURN(1); } @@ -2695,6 +2723,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->methods= &client_methods; mysql->client_flag=0; /* For handshake */ + mysql->tls_self_signed_error= 0; /* use default options */ if (mysql->options.my_cnf_file || mysql->options.my_cnf_group) @@ -3070,9 +3099,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->port=port; /* - remove the rpl hack from the version string, - see RPL_VERSION_HACK comment + remove the rpl hack from the version string, in case we're connecting + to a pre-11.0 server */ +#define RPL_VERSION_HACK "5.5.5-" if ((mysql->server_capabilities & CLIENT_PLUGIN_AUTH) && strncmp(mysql->server_version, RPL_VERSION_HACK, sizeof(RPL_VERSION_HACK) - 1) == 0) @@ -3448,6 +3478,29 @@ void STDCALL mysql_close(MYSQL *mysql) } +static my_bool parse_ok_packet(MYSQL *mysql, ulong length) +{ + uchar *pos= mysql->net.read_pos + 1; + DBUG_ASSERT(pos[-1] == 0); + + mysql->affected_rows= net_field_length_ll(&pos); + mysql->insert_id= net_field_length_ll(&pos); + if (protocol_41(mysql)) + { + mysql->server_status=uint2korr(pos); pos+=2; + mysql->warning_count=uint2korr(pos); pos+=2; + } + else if (mysql->server_capabilities & CLIENT_TRANSACTIONS) + { + mysql->server_status=uint2korr(pos); pos+=2; + mysql->warning_count= 0; + } + if (pos < mysql->net.read_pos + length && net_field_length(&pos)) + mysql->info=(char*) pos; + return 0; +} + + static my_bool cli_read_query_result(MYSQL *mysql) { uchar *pos; @@ -3468,31 +3521,10 @@ static my_bool cli_read_query_result(MYSQL *mysql) #ifdef MYSQL_CLIENT /* Avoid warn of unused labels*/ get_info: #endif - pos=(uchar*) mysql->net.read_pos; + pos= mysql->net.read_pos; if ((field_count= net_field_length(&pos)) == 0) - { - mysql->affected_rows= net_field_length_ll(&pos); - mysql->insert_id= net_field_length_ll(&pos); - DBUG_PRINT("info",("affected_rows: %lu insert_id: %lu", - (ulong) mysql->affected_rows, - (ulong) mysql->insert_id)); - if (protocol_41(mysql)) - { - mysql->server_status=uint2korr(pos); pos+=2; - mysql->warning_count=uint2korr(pos); pos+=2; - } - else if (mysql->server_capabilities & CLIENT_TRANSACTIONS) - { - /* MySQL 4.0 protocol */ - mysql->server_status=uint2korr(pos); pos+=2; - mysql->warning_count= 0; - } - DBUG_PRINT("info",("status: %u warning_count: %u", - mysql->server_status, mysql->warning_count)); - if (pos < mysql->net.read_pos+length && net_field_length(&pos)) - mysql->info=(char*) pos; - DBUG_RETURN(0); - } + DBUG_RETURN(parse_ok_packet(mysql, length)); + #ifdef MYSQL_CLIENT if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ { @@ -3817,7 +3849,7 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg) sizeof(struct st_mysql_options_extention), MYF(MY_WME | MY_ZEROFILL)); if (mysql->options.extension) - mysql->options.extension->tls_verify_server_cert= *(my_bool*) arg; + mysql->options.extension->tls_allow_invalid_server_cert= !*(my_bool*) arg; break; case MYSQL_PLUGIN_DIR: EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg); @@ -4170,6 +4202,22 @@ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) DBUG_RETURN(CR_OK); } + +static int native_password_auth_hash(MYSQL *mysql, uchar *out, size_t *out_length) +{ + uchar hash_stage1[MY_SHA1_HASH_SIZE]; + + if (*out_length < MY_SHA1_HASH_SIZE) + return 1; + *out_length= MY_SHA1_HASH_SIZE; + + my_sha1(hash_stage1, mysql->passwd, strlen(mysql->passwd)); + my_sha1(out, (char*)hash_stage1, MY_SHA1_HASH_SIZE); + + return 0; +} + + /** client authentication plugin that does old MySQL authentication using an 8-byte (4.0-) scramble @@ -4235,3 +4283,28 @@ int STDCALL mysql_cancel(MYSQL *mysql) return vio_shutdown(mysql->net.vio, SHUT_RDWR); return -1; } + + +MYSQL_RES *STDCALL mysql_use_result(MYSQL *mysql) +{ + return (*mysql->methods->use_result)(mysql); +} + + +MYSQL_FIELD *STDCALL mysql_fetch_fields(MYSQL_RES *res) +{ + return (res)->fields; +} + + +ulong STDCALL +mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, + ulong length) +{ + my_bool overflow; + if (mysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES) + return (ulong) escape_quotes_for_mysql(mysql->charset, to, 0, from, length, + &overflow); + return (ulong) escape_string_for_mysql(mysql->charset, to, 0, from, length, + &overflow); +} diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c index 0a2e39f7..38444648 100644 --- a/sql-common/client_plugin.c +++ b/sql-common/client_plugin.c @@ -171,9 +171,7 @@ add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle, goto err1; } - if (plugin->interface_version < plugin_version[plugin->type] || - (plugin->interface_version >> 8) > - (plugin_version[plugin->type] >> 8)) + if (plugin->interface_version >> 8 != plugin_version[plugin->type] >> 8) { errmsg= "Incompatible client plugin interface"; goto err1; |