diff --git a/include/mysql.h b/include/mysql.h index 486f57fbcd9..3f59fa3edd2 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -289,8 +289,9 @@ typedef struct st_mysql /* session-wide random string */ char scramble[SCRAMBLE_LENGTH+1]; my_bool auto_local_infile; - void *unused2, *unused3, *unused4; + void *unused2, *unused3; MYSQL_FIELD *fields; + const char *tls_self_signed_error; LIST *stmts; /* list of all statements */ const struct st_mysql_methods *methods; diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index 990cecc9773..404c3f1eafc 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -56,7 +56,7 @@ #define MYSQL_CLIENT_reserved2 1 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 -#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0101 #define MYSQL_CLIENT_MAX_PLUGINS 3 @@ -96,6 +96,7 @@ struct st_mysql_client_plugin_AUTHENTICATION { MYSQL_CLIENT_PLUGIN_HEADER int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); + int (*hash_password_bin)(struct st_mysql *mysql, unsigned char *hash, size_t *hash_length); }; #include diff --git a/include/mysql/client_plugin.h.pp b/include/mysql/client_plugin.h.pp index b6ba9cf08ad..ff35364bd44 100644 --- a/include/mysql/client_plugin.h.pp +++ b/include/mysql/client_plugin.h.pp @@ -22,6 +22,7 @@ struct st_mysql_client_plugin_AUTHENTICATION { int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; const char *license; void *mysql_api; int (*init)(char *, size_t, int, va_list); int (*deinit)(); int (*options)(const char *option, const void *); int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); + int (*hash_password_bin)(struct st_mysql *mysql, unsigned char *hash, size_t *hash_length); }; struct st_mysql; typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql, diff --git a/mysql-test/suite/mariabackup/backup_ssl.result b/mysql-test/suite/mariabackup/backup_ssl.result index 099b8d42a85..387689c1215 100644 --- a/mysql-test/suite/mariabackup/backup_ssl.result +++ b/mysql-test/suite/mariabackup/backup_ssl.result @@ -7,3 +7,7 @@ FLUSH PRIVILEGES; # xtrabackup move back # restart DROP USER backup_user; +# +# MDEV-31855 validate ssl certificates using client password in the internal client +# +# tcp ssl ssl-verify-server-cert diff --git a/mysql-test/suite/mariabackup/backup_ssl.test b/mysql-test/suite/mariabackup/backup_ssl.test index e858c834d29..55c233b6a78 100644 --- a/mysql-test/suite/mariabackup/backup_ssl.test +++ b/mysql-test/suite/mariabackup/backup_ssl.test @@ -14,3 +14,10 @@ exec $XTRABACKUP --prepare --target-dir=$targetdir; DROP USER backup_user; rmdir $targetdir; +echo #; +echo # MDEV-31855 validate ssl certificates using client password in the internal client; +echo #; +# fails to connect, passwordless root +echo # tcp ssl ssl-verify-server-cert; +error 1; +exec $XTRABACKUP --protocol=tcp --user=root --port=$MASTER_MYPORT --backup --target-dir=$targetdir; diff --git a/mysql-test/suite/mariabackup/backup_ssl_not_win.result b/mysql-test/suite/mariabackup/backup_ssl_not_win.result new file mode 100644 index 00000000000..42376fce5eb --- /dev/null +++ b/mysql-test/suite/mariabackup/backup_ssl_not_win.result @@ -0,0 +1,4 @@ +# +# MDEV-31855 validate ssl certificates using client password in the internal client +# +# socket ssl ssl-verify-server-cert diff --git a/mysql-test/suite/mariabackup/backup_ssl_not_win.test b/mysql-test/suite/mariabackup/backup_ssl_not_win.test new file mode 100644 index 00000000000..933d434f296 --- /dev/null +++ b/mysql-test/suite/mariabackup/backup_ssl_not_win.test @@ -0,0 +1,10 @@ +source include/not_windows.inc; +let $targetdir=$MYSQLTEST_VARDIR/tmp/backup; + +echo #; +echo # MDEV-31855 validate ssl certificates using client password in the internal client; +echo #; +# connects fine, unix socket is a secure transport +echo # socket ssl ssl-verify-server-cert; +exec $XTRABACKUP --protocol=socket --user=root --socket=$MASTER_MYSOCK --backup --target-dir=$targetdir; +rmdir $targetdir; diff --git a/mysql-test/suite/rpl/r/rpl_ssl1.result b/mysql-test/suite/rpl/r/rpl_ssl1.result index afdfee83ddd..a480f51053a 100644 --- a/mysql-test/suite/rpl/r/rpl_ssl1.result +++ b/mysql-test/suite/rpl/r/rpl_ssl1.result @@ -75,14 +75,42 @@ Master_SSL_Cert = 'MYSQL_TEST_DIR/std_data/client-cert.pem' Master_SSL_Key = 'MYSQL_TEST_DIR/std_data/client-key.pem' include/check_slave_is_running.inc connection master; +create user replssl@127.0.0.1 identified by "sslrepl"; +grant replication slave on *.* to replssl@127.0.0.1 require ssl; +connection slave; +stop slave; +include/wait_for_slave_to_stop.inc +change master to +master_host="127.0.0.1", +master_user='replssl', +master_password="sslrepl", +master_ssl=1, +master_ssl_verify_server_cert=1, +master_ssl_ca ='', +master_ssl_cert='', +master_ssl_key=''; +start slave; +include/wait_for_slave_to_start.inc +show tables; +Tables_in_test +t1 +connection master; drop table t1; connection slave; +show tables; +Tables_in_test include/stop_slave.inc CHANGE MASTER TO master_host="127.0.0.1", +master_user='root', +master_password='', master_ssl_ca ='', master_ssl_cert='', master_ssl_key='', master_ssl_verify_server_cert=0, master_ssl=0; +connection master; +drop user replssl@127.0.0.1; +connection slave; +drop user replssl@127.0.0.1; include/rpl_end.inc diff --git a/mysql-test/suite/rpl/t/rpl_ssl1.test b/mysql-test/suite/rpl/t/rpl_ssl1.test index 0629e074217..8734299f42f 100644 --- a/mysql-test/suite/rpl/t/rpl_ssl1.test +++ b/mysql-test/suite/rpl/t/rpl_ssl1.test @@ -95,19 +95,47 @@ select * from t1; source include/show_slave_status.inc; --source include/check_slave_is_running.inc -# ==== Clean up ==== +# MDEV-31855 validate with master_password +connection master; +create user replssl@127.0.0.1 identified by "sslrepl"; +grant replication slave on *.* to replssl@127.0.0.1 require ssl; +connection slave; +stop slave; +--source include/wait_for_slave_to_stop.inc +eval change master to + master_host="127.0.0.1", + master_user='replssl', + master_password="sslrepl", + master_ssl=1, + master_ssl_verify_server_cert=1, + master_ssl_ca ='', + master_ssl_cert='', + master_ssl_key=''; +start slave; +--source include/wait_for_slave_to_start.inc + +show tables; connection master; drop table t1; sync_slave_with_master; +show tables; + +# ==== Clean up ==== --source include/stop_slave.inc CHANGE MASTER TO master_host="127.0.0.1", + master_user='root', + master_password='', master_ssl_ca ='', master_ssl_cert='', master_ssl_key='', master_ssl_verify_server_cert=0, master_ssl=0; +connection master; +drop user replssl@127.0.0.1; +connection slave; +drop user replssl@127.0.0.1; --let $rpl_only_running_threads= 1 --source include/rpl_end.inc diff --git a/plugin/auth_ed25519/client_ed25519.c b/plugin/auth_ed25519/client_ed25519.c index 5222da8c7e8..cc2dda92b4f 100644 --- a/plugin/auth_ed25519/client_ed25519.c +++ b/plugin/auth_ed25519/client_ed25519.c @@ -13,7 +13,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ - +#error see libmariadb/plugins/auth/ed25519.c instead /************************** CLIENT *************************************/ #include diff --git a/plugin/auth_examples/qa_auth_client.c b/plugin/auth_examples/qa_auth_client.c index a915306e60b..af05c9b12e8 100644 --- a/plugin/auth_examples/qa_auth_client.c +++ b/plugin/auth_examples/qa_auth_client.c @@ -113,5 +113,6 @@ mysql_declare_client_plugin(AUTHENTICATION) NULL, NULL, NULL, - test_plugin_client + test_plugin_client, + NULL mysql_end_client_plugin; diff --git a/plugin/auth_examples/qa_auth_interface.c b/plugin/auth_examples/qa_auth_interface.c index ad1f6fff1b6..3903f3d21b9 100644 --- a/plugin/auth_examples/qa_auth_interface.c +++ b/plugin/auth_examples/qa_auth_interface.c @@ -250,5 +250,6 @@ mysql_declare_client_plugin(AUTHENTICATION) NULL, NULL, NULL, - test_plugin_client + test_plugin_client, + NULL mysql_end_client_plugin; diff --git a/plugin/auth_examples/test_plugin.c b/plugin/auth_examples/test_plugin.c index 04405a1ccb6..f5ef08dd3ff 100644 --- a/plugin/auth_examples/test_plugin.c +++ b/plugin/auth_examples/test_plugin.c @@ -230,5 +230,6 @@ mysql_declare_client_plugin(AUTHENTICATION) NULL, NULL, NULL, - test_plugin_client + test_plugin_client, + NULL mysql_end_client_plugin; diff --git a/sql-common/client.c b/sql-common/client.c index d86c160e807..94ae6eadc85 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; @@ -1571,8 +1572,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 @@ -1586,22 +1586,22 @@ mysql_get_ssl_cipher(MYSQL *mysql __attribute__((unused))) #include -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; 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; @@ -1613,21 +1613,29 @@ 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; + break; } - /* - 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. - */ - - ret_validation= X509_check_host(server_cert, server_hostname, - strlen(server_hostname), 0, 0) != 1 && - X509_check_ip_asc(server_cert, server_hostname, 0) != 1; - *errptr= "SSL certificate validation failure"; error: X509_free(server_cert); @@ -1759,6 +1767,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= @@ -1768,13 +1777,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= @@ -1790,7 +1800,8 @@ static auth_plugin_t old_password_client_plugin= NULL, NULL, NULL, - old_password_auth_client + old_password_auth_client, + NULL }; @@ -2018,6 +2029,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= @@ -2052,7 +2064,7 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, if (mpvio->db) mysql->client_flag|= CLIENT_CONNECT_WITH_DB; - if (mysql->net.vio->type == VIO_TYPE_NAMEDPIPE) + if (vio_type == VIO_TYPE_NAMEDPIPE) { mysql->server_capabilities&= ~CLIENT_SSL; mysql->options.use_ssl= 0; @@ -2141,7 +2153,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...")); @@ -2160,12 +2172,32 @@ static int send_client_reply_packet(MCPVIO_EXT *mpvio, /* Verify server cert */ if ((mysql->options.extension && mysql->options.extension->tls_verify_server_cert) && - ssl_verify_server_cert(net->vio, mysql->host, &cert_error)) + 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 */ @@ -2520,6 +2552,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); @@ -2541,7 +2581,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, @@ -2556,7 +2596,43 @@ 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->tls_verify_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); } @@ -2647,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) @@ -3401,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; @@ -3421,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 */ { @@ -4123,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 diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c index 0a2e39f7daa..38444648a52 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;