=== modified file 'client/mysql.cc' *** client/mysql.cc 2010-03-15 17:35:32 +0000 --- client/mysql.cc 2010-03-26 11:32:46 +0000 *************** *** 4273,4296 **** const char *prompt, char *buf, int buf_len) { ! int ch; ! char *s=buf, *end=buf+buf_len-1; ! fputs("[mysql] ", stdout); fputs(prompt, stdout); fputs(" ", stdout); if (type == 2) /* password */ { s= get_tty_password(""); ! strncpy(buf, s, buf_len); my_free(s, MYF(0)); } else { ! for (ch= fgetc(stdin); s < end && ch != '\n' && ch != EOF; ch= fgetc(stdin)) ! *s++= ch; ! *s=0; } return buf; --- 4273,4296 ---- const char *prompt, char *buf, int buf_len) { ! char *s=buf; ! fputs("[mariadb] ", stdout); fputs(prompt, stdout); fputs(" ", stdout); if (type == 2) /* password */ { s= get_tty_password(""); ! strnmov(buf, s, buf_len); ! buf[buf_len-1]= 0; my_free(s, MYF(0)); } else { ! fgets(buf, buf_len-1, stdin); ! if (buf[0] && (s= strend(buf))[-1] == '\n') ! s[-1]= 0; } return buf; === modified file 'libmysqld/lib_sql.cc' *** libmysqld/lib_sql.cc 2010-03-15 17:35:32 +0000 --- libmysqld/lib_sql.cc 2010-03-26 11:32:46 +0000 *************** *** 415,421 **** int emb_read_change_user_result(MYSQL *mysql) { mysql->net.read_pos= (uchar*)""; // fake an OK packet ! return mysql_errno(mysql) ? packet_error : 1; } MYSQL_METHODS embedded_methods= --- 415,421 ---- int emb_read_change_user_result(MYSQL *mysql) { mysql->net.read_pos= (uchar*)""; // fake an OK packet ! return mysql_errno(mysql) ? packet_error : 1 /* length of the OK packet */; } MYSQL_METHODS embedded_methods= === modified file 'plugin/auth/auth_socket.c' *** plugin/auth/auth_socket.c 2010-03-11 12:33:22 +0000 --- plugin/auth/auth_socket.c 2010-03-26 11:32:46 +0000 *************** *** 1,5 **** /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by --- 1,5 ---- /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by === modified file 'plugin/auth/dialog.c' *** plugin/auth/dialog.c 2010-03-11 12:33:22 +0000 --- plugin/auth/dialog.c 2010-03-26 11:32:46 +0000 *************** *** 1,5 **** /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by --- 1,5 ---- /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by *************** *** 236,242 **** We send the "password", assuming the client knows what its doing. (in other words, the dialog plugin should be only set as a default authentication plugin on the client if the first question ! asks for a password - which will be sent in cleat text, by the way) */ reply= mysql->passwd; } --- 236,242 ---- We send the "password", assuming the client knows what its doing. (in other words, the dialog plugin should be only set as a default authentication plugin on the client if the first question ! asks for a password - which will be sent in clear text, by the way) */ reply= mysql->passwd; } === modified file 'sql-common/client.c' *** sql-common/client.c 2010-03-11 12:33:22 +0000 --- sql-common/client.c 2010-03-26 11:32:46 +0000 *************** *** 109,114 **** #include #include uint mysql_port=0; char *mysql_unix_port= 0; const char *unknown_sqlstate= "HY000"; --- 109,117 ---- #include #include + #define native_password_plugin_name "mysql_native_password" + #define old_password_plugin_name "mysql_old_password" + uint mysql_port=0; char *mysql_unix_port= 0; const char *unknown_sqlstate= "HY000"; *************** *** 1047,1059 **** return 0; } ! #define extension_free(OPTS, X) \ if ((OPTS)->extension) \ my_free((OPTS)->extension->X, MYF(MY_ALLOW_ZERO_PTR)); \ else \ (OPTS)->extension= (struct st_mysql_options_extention *) \ my_malloc(sizeof(struct st_mysql_options_extention), \ ! MYF(MY_WME | MY_ZEROFILL)); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group) --- 1050,1063 ---- return 0; } ! #define extension_set_string(OPTS, X, STR) \ if ((OPTS)->extension) \ my_free((OPTS)->extension->X, MYF(MY_ALLOW_ZERO_PTR)); \ else \ (OPTS)->extension= (struct st_mysql_options_extention *) \ my_malloc(sizeof(struct st_mysql_options_extention), \ ! MYF(MY_WME | MY_ZEROFILL)); \ ! (OPTS)->extension->X= my_strdup((STR), MYF(MY_WME)); void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group) *************** *** 1243,1254 **** options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; break; case OPT_plugin_dir: ! extension_free(options, plugin_dir); ! options->extension->plugin_dir= my_strdup(opt_arg, MYF(MY_WME)); break; case OPT_default_auth: ! extension_free(options, default_auth); ! options->extension->default_auth= my_strdup(opt_arg, MYF(MY_WME)); break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); --- 1247,1256 ---- options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; break; case OPT_plugin_dir: ! extension_set_string(options, plugin_dir, opt_arg); break; case OPT_default_auth: ! extension_set_string(options, default_auth, opt_arg); break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); *************** *** 1892,1897 **** /*********** client side authentication support **************************/ typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; /* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */ typedef struct { --- 1894,1937 ---- /*********** client side authentication support **************************/ 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 old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); + + static auth_plugin_t native_password_client_plugin= + { + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + native_password_plugin_name, + "R.J.Silk, Sergei Golubchik", + "Native MySQL authentication", + {1, 0, 0}, + NULL, + NULL, + native_password_auth_client + }; + + static auth_plugin_t old_password_client_plugin= + { + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + old_password_plugin_name, + "R.J.Silk, Sergei Golubchik", + "Old MySQL-3.23 authentication", + {1, 0, 0}, + NULL, + NULL, + old_password_auth_client + }; + + struct st_mysql_client_plugin *mysql_client_builtins[]= + { + (struct st_mysql_client_plugin *)&native_password_client_plugin, + (struct st_mysql_client_plugin *)&old_password_client_plugin, + 0 + }; + + /* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */ typedef struct { *************** *** 1906,1918 **** uchar *pkt; /**< pointer into NET::buff */ uint pkt_len; } cached_server_reply; ! int packets_read, packets_written; /**< counters for send/received packets */ ! int mysql_change_user; /**< if it's mysql_change_user() */ int last_read_packet_len; /**< the length of the last *read* packet */ } MCPVIO_EXT; - - #define native_password_plugin_name "mysql_native_password" - #define old_password_plugin_name "mysql_old_password" /** sends a COM_CHANGE_USER command with a caller provided payload --- 1946,1955 ---- uchar *pkt; /**< pointer into NET::buff */ uint pkt_len; } cached_server_reply; ! uint packets_read, packets_written; /**< counters for send/received packets */ ! my_bool mysql_change_user; /**< if it's mysql_change_user() */ int last_read_packet_len; /**< the length of the last *read* packet */ } MCPVIO_EXT; /** sends a COM_CHANGE_USER command with a caller provided payload *************** *** 2124,2130 **** goto error; } } ! #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu", mysql->server_version, mysql->server_capabilities, --- 2161,2167 ---- goto error; } } ! #endif /* HAVE_OPENSSL */ DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu", mysql->server_version, mysql->server_capabilities, *************** *** 2187,2194 **** } - static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int); - /** vio->read_packet() callback method for client authentication plugins --- 2224,2229 ---- } /** vio->read_packet() callback method for client authentication plugins *************** *** 2202,2208 **** MYSQL *mysql= mpvio->mysql; ulong pkt_len; ! if (mpvio->packets_read == 0 && !mpvio->cached_server_reply.pkt) { /* the server handshake packet came from the wrong plugin, --- 2237,2252 ---- MYSQL *mysql= mpvio->mysql; ulong pkt_len; ! /* there are cached data left, feed it to a plugin */ ! if (mpvio->cached_server_reply.pkt) ! { ! *buf= mpvio->cached_server_reply.pkt; ! mpvio->cached_server_reply.pkt= 0; ! mpvio->packets_read++; ! return mpvio->cached_server_reply.pkt_len; ! } ! ! if (mpvio->packets_read == 0) { /* the server handshake packet came from the wrong plugin, *************** *** 2214,2228 **** return (int)packet_error; } - /* there are cached data left, feed it to a plugin */ - if (mpvio->cached_server_reply.pkt) - { - *buf= mpvio->cached_server_reply.pkt; - mpvio->cached_server_reply.pkt= 0; - mpvio->packets_read++; - return mpvio->cached_server_reply.pkt_len; - } - /* otherwise read the data */ pkt_len= (*mysql->methods->read_change_user_result)(mysql); mpvio->last_read_packet_len= pkt_len; --- 2258,2263 ---- return (int)packet_error; } /* otherwise read the data */ pkt_len= (*mysql->methods->read_change_user_result)(mysql); mpvio->last_read_packet_len= pkt_len; *************** *** 2236,2241 **** the server sends \1\255 or \1\254 instead of just \255 or \254 - for us to not confuse it with an error or "change plugin" packets. We remove this escaping \1 here. */ if (pkt_len && **buf == 1) { --- 2271,2278 ---- the server sends \1\255 or \1\254 instead of just \255 or \254 - for us to not confuse it with an error or "change plugin" packets. We remove this escaping \1 here. + + See also server_mpvio_write_packet() where the escaping is done. */ if (pkt_len && **buf == 1) { *************** *** 2368,2381 **** /* determine the default/initial plugin to use */ if (mysql->options.extension && mysql->options.extension->default_auth && mysql->server_capabilities & CLIENT_PLUGIN_AUTH) auth_plugin_name= mysql->options.extension->default_auth; - else - auth_plugin_name= mysql->server_capabilities & CLIENT_PROTOCOL_41 ? - native_password_plugin_name : old_password_plugin_name; - if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) return 1; /* oops, not found */ mysql->net.last_errno= 0; /* just in case */ --- 2405,2422 ---- /* determine the default/initial plugin to use */ if (mysql->options.extension && mysql->options.extension->default_auth && mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + { auth_plugin_name= mysql->options.extension->default_auth; if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql, auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN))) return 1; /* oops, not found */ + } + else + { + auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ? + &native_password_client_plugin : &old_password_client_plugin; + auth_plugin_name= auth_plugin->name; + } mysql->net.last_errno= 0; /* just in case */ *************** *** 2436,2451 **** { /* The server asked to use a different authentication plugin */ if (pkt_length == 1) ! { /* old "use short scramble" packet */ auth_plugin_name= old_password_plugin_name; mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble; mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; } else ! { /* new "use different plugin" packet */ uint len; auth_plugin_name= (char*)mysql->net.read_pos + 1; ! len= strlen(auth_plugin_name); mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; } --- 2477,2494 ---- { /* The server asked to use a different authentication plugin */ if (pkt_length == 1) ! { ! /* old "use short scramble" packet */ auth_plugin_name= old_password_plugin_name; mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble; mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1; } else ! { ! /* new "use different plugin" packet */ uint len; auth_plugin_name= (char*)mysql->net.read_pos + 1; ! len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */ mpvio.cached_server_reply.pkt_len= pkt_length - len - 2; mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2; } *************** *** 2496,2503 **** { char buff[NAME_LEN+USERNAME_LENGTH+100]; int scramble_data_len, pkt_scramble_len; ! char *end,*host_info, *server_version_end, *pkt_end, *scramble_data; ! char *scramble_plugin; my_socket sock; in_addr_t ip_addr; struct sockaddr_in sock_addr; --- 2539,2546 ---- { char buff[NAME_LEN+USERNAME_LENGTH+100]; int scramble_data_len, pkt_scramble_len; ! char *end, *host_info=0, *server_version_end, *pkt_end; ! char *scramble_data, *scramble_plugin; my_socket sock; in_addr_t ip_addr; struct sockaddr_in sock_addr; *************** *** 2898,2903 **** { scramble_data_len= pkt_scramble_len; scramble_plugin= scramble_data + scramble_data_len; } else { --- 2941,2948 ---- { scramble_data_len= pkt_scramble_len; scramble_plugin= scramble_data + scramble_data_len; + if (scramble_data + scramble_data_len > pkt_end) + scramble_data_len= pkt_end - scramble_data; } else { *************** *** 3665,3676 **** mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT; break; case MYSQL_PLUGIN_DIR: ! extension_free(&mysql->options, plugin_dir); ! mysql->options.extension->plugin_dir= my_strdup(arg, MYF(MY_WME)); break; case MYSQL_DEFAULT_AUTH: ! extension_free(&mysql->options, default_auth); ! mysql->options.extension->default_auth= my_strdup(arg, MYF(MY_WME)); break; default: DBUG_RETURN(1); --- 3710,3719 ---- mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT; break; case MYSQL_PLUGIN_DIR: ! extension_set_string(&mysql->options, plugin_dir, arg); break; case MYSQL_DEFAULT_AUTH: ! extension_set_string(&mysql->options, default_auth, arg); break; default: DBUG_RETURN(1); *************** *** 3876,3911 **** return CR_OK; } - static auth_plugin_t native_password_client_plugin= - { - MYSQL_CLIENT_AUTHENTICATION_PLUGIN, - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - native_password_plugin_name, - "R.J.Silk, Sergei Golubchik", - "Native MySQL authentication", - {1, 0, 0}, - NULL, - NULL, - native_password_auth_client - }; - - static auth_plugin_t old_password_client_plugin= - { - MYSQL_CLIENT_AUTHENTICATION_PLUGIN, - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, - old_password_plugin_name, - "R.J.Silk, Sergei Golubchik", - "Old MySQL-3.23 authentication", - {1, 0, 0}, - NULL, - NULL, - old_password_auth_client - }; - - struct st_mysql_client_plugin *mysql_client_builtins[]= - { - (struct st_mysql_client_plugin *)&native_password_client_plugin, - (struct st_mysql_client_plugin *)&old_password_client_plugin, - 0 - }; - --- 3919,3921 ---- return CR_OK; } === modified file 'sql-common/client_plugin.c' *** sql-common/client_plugin.c 2010-03-11 12:33:22 +0000 --- sql-common/client_plugin.c 2010-03-26 11:32:46 +0000 *************** *** 1,5 **** /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by --- 1,5 ---- /* Copyright (C) 2010 Monty Program Ab ! /* Copyright (C) 2010 Sergei Golubchik and Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by *************** *** 209,226 **** char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS"); /* no plugins to load */ ! if(!s) return; free_env= plugs= my_strdup(s, MYF(MY_WME)); - s= NULL; do { if ((s= strchr(plugs, ';'))) *s= '\0'; mysql_load_plugin(mysql, plugs, -1, 0); ! if(s) ! plugs= ++s; } while (s); my_free(free_env, MYF(0)); --- 209,224 ---- char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS"); /* no plugins to load */ ! if (!s) return; free_env= plugs= my_strdup(s, MYF(MY_WME)); do { if ((s= strchr(plugs, ';'))) *s= '\0'; mysql_load_plugin(mysql, plugs, -1, 0); ! plugs= s + 1; } while (s); my_free(free_env, MYF(0)); === modified file 'sql/mysqld.cc' *** sql/mysqld.cc 2010-03-15 17:35:32 +0000 --- sql/mysqld.cc 2010-03-26 11:32:46 +0000 *************** *** 3491,3497 **** if (init_errmessage()) /* Read error messages from file */ return 1; init_client_errs(); ! mysql_library_init(un,us,ed); /* for replication */ lex_init(); if (item_create_init()) return 1; --- 3491,3497 ---- if (init_errmessage()) /* Read error messages from file */ return 1; init_client_errs(); ! mysql_library_init(never,never,never); /* for replication */ lex_init(); if (item_create_init()) return 1; === modified file 'sql/sql_acl.cc' *** sql/sql_acl.cc 2010-03-22 18:09:47 +0000 --- sql/sql_acl.cc 2010-03-26 11:32:46 +0000 *************** *** 664,671 **** user.user ? user.user : "", user.host.hostname ? user.host.hostname : ""); } user.plugin.str= tmpstr; user.plugin.length= strlen(tmpstr); user.auth_string.str= get_field(&mem, table->field[next_field++]); if (!user.auth_string.str) user.auth_string.str= const_cast(""); --- 664,681 ---- user.user ? user.user : "", user.host.hostname ? user.host.hostname : ""); } + if (my_strcasecmp(system_charset_info, tmpstr, + native_password_plugin_name.str) == 0) + user.plugin= native_password_plugin_name; + else + if (my_strcasecmp(system_charset_info, tmpstr, + old_password_plugin_name.str) == 0) + user.plugin= old_password_plugin_name; + else + { user.plugin.str= tmpstr; user.plugin.length= strlen(tmpstr); + } user.auth_string.str= get_field(&mem, table->field[next_field++]); if (!user.auth_string.str) user.auth_string.str= const_cast(""); *************** *** 6828,6839 **** /* few defines to have less ifdef's in the code below */ #ifdef EMBEDDED_LIBRARY #ifdef NO_EMBEDDED_ACCESS_CHECKS #define initialized 0 #define decrease_user_connections(X) /* nothing */ #define check_for_max_user_connections(X,Y) 0 #endif - #undef HAVE_OPENSSL #endif #ifndef HAVE_OPENSSL #define ssl_acceptor_fd 0 --- 6838,6849 ---- /* few defines to have less ifdef's in the code below */ #ifdef EMBEDDED_LIBRARY + #undef HAVE_OPENSSL #ifdef NO_EMBEDDED_ACCESS_CHECKS #define initialized 0 #define decrease_user_connections(X) /* nothing */ #define check_for_max_user_connections(X,Y) 0 #endif #endif #ifndef HAVE_OPENSSL #define ssl_acceptor_fd 0 *************** *** 6929,6934 **** const char *data, uint data_len) { DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE); THD *thd= mpvio->thd; char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64); --- 6939,6945 ---- const char *data, uint data_len) { DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE); + DBUG_ASSERT(data_len <= 255); THD *thd= mpvio->thd; char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64); *************** *** 7018,7025 **** return res; } ! static uchar switch_plugin_request_buf[]= { 254 }; /** sends a "change plugin" packet, requesting a client to restart authentication --- 7029,7059 ---- return res; } + static bool secure_auth(THD *thd) + { + if (!opt_secure_auth) + return 0; ! /* ! If the server is running in secure auth mode, short scrambles are ! forbidden. Extra juggling to report the same error as the old code. ! */ ! if (thd->client_capabilities & CLIENT_PROTOCOL_41) ! { ! my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0), ! thd->security_ctx->user, ! thd->security_ctx->host_or_ip); ! general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), ! thd->security_ctx->user, ! thd->security_ctx->host_or_ip); ! } ! else ! { ! my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); ! general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); ! } ! return 1; ! } /** sends a "change plugin" packet, requesting a client to restart authentication *************** *** 7049,7054 **** DBUG_ASSERT(mpvio->packets_written == 1); DBUG_ASSERT(mpvio->packets_read == 1); NET *net= &mpvio->thd->net; mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART --- 7083,7090 ---- DBUG_ASSERT(mpvio->packets_written == 1); DBUG_ASSERT(mpvio->packets_read == 1); NET *net= &mpvio->thd->net; + static uchar switch_plugin_request_buf[]= { 254 }; + mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART *************** *** 7061,7074 **** we send an old "short 4.0 scramble request", if we need to request a client to use 4.0 auth plugin (short scramble) and the scramble was already sent to the client */ bool switch_from_long_to_short_scramble= ! my_strcasecmp(system_charset_info, native_password_plugin_name.str, ! mpvio->cached_client_reply.plugin) == 0 && client_auth_plugin == old_password_plugin_name.str; if (switch_from_long_to_short_scramble) ! return my_net_write(net, switch_plugin_request_buf, 1) || net_flush(net); /* --- 7097,7114 ---- we send an old "short 4.0 scramble request", if we need to request a client to use 4.0 auth plugin (short scramble) and the scramble was already sent to the client + + below, cached_client_reply.plugin is the plugin name that client has used, + client_auth_plugin is derived from mysql.user table, for the given + user account, it's the plugin that the client need to use to login. */ bool switch_from_long_to_short_scramble= ! native_password_plugin_name.str == mpvio->cached_client_reply.plugin && client_auth_plugin == old_password_plugin_name.str; if (switch_from_long_to_short_scramble) ! return secure_auth(mpvio->thd) || ! my_net_write(net, switch_plugin_request_buf, 1) || net_flush(net); /* *************** *** 7077,7084 **** ask an old 4.0 client to use the new 4.1 authentication protocol. */ bool switch_from_short_to_long_scramble= ! my_strcasecmp(system_charset_info, old_password_plugin_name.str, ! mpvio->cached_client_reply.plugin) == 0 && client_auth_plugin == native_password_plugin_name.str; if (switch_from_short_to_long_scramble) --- 7117,7123 ---- ask an old 4.0 client to use the new 4.1 authentication protocol. */ bool switch_from_short_to_long_scramble= ! old_password_plugin_name.str == mpvio->cached_client_reply.plugin && client_auth_plugin == native_password_plugin_name.str; if (switch_from_short_to_long_scramble) *************** *** 7111,7122 **** static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx) { pthread_mutex_lock(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); ! if (!acl_user_tmp->user || (!strcmp(sctx->user, acl_user_tmp->user) && ! compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))) { mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); break; --- 7150,7163 ---- static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx) { + DBUG_ASSERT(mpvio->acl_user == 0); + pthread_mutex_lock(&acl_cache->lock); for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); ! if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) && ! compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) { mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); break; *************** *** 7131,7142 **** } /* user account requires non-default plugin and the client is too old */ ! if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, ! native_password_plugin_name.str) && ! my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, ! old_password_plugin_name.str) && !(mpvio->thd->client_capabilities & CLIENT_PLUGIN_AUTH)) { my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); return 1; --- 7172,7185 ---- } /* user account requires non-default plugin and the client is too old */ ! if (mpvio->acl_user->plugin.str != native_password_plugin_name.str && ! mpvio->acl_user->plugin.str != old_password_plugin_name.str && !(mpvio->thd->client_capabilities & CLIENT_PLUGIN_AUTH)) { + DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, + native_password_plugin_name.str)); + DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, + old_password_plugin_name.str)); my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); return 1; *************** *** 7203,7219 **** char *ptr= db + db_len + 1; - /* Convert database and user names to utf8 */ - db_buff[db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, db, db_len, - thd->charset(), &dummy_errors)]= 0; - db= db_buff; - - user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, - system_charset_info, user, user_len, - thd->charset(), &dummy_errors)]= '\0'; - user= user_buff; - if (ptr+1 < end) { uint cs_number= uint2korr(ptr); --- 7246,7251 ---- char *ptr= db + db_len + 1; if (ptr+1 < end) { uint cs_number= uint2korr(ptr); *************** *** 7221,7234 **** thd->update_charset(); } ! if (!(sctx->user= my_strdup(user, MYF(MY_WME)))) return 1; /* Clear variables that are allocated */ thd->user_connect= 0; strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH); ! if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0) return 1; /* The error is set by make_lex_string(). */ /* --- 7253,7274 ---- thd->update_charset(); } ! /* Convert database and user names to utf8 */ ! db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, ! db, db_len, thd->charset(), &dummy_errors); ! ! user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, ! system_charset_info, user, user_len, ! thd->charset(), &dummy_errors); ! ! if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME)))) return 1; /* Clear variables that are allocated */ thd->user_connect= 0; strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH); ! if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0) return 1; /* The error is set by make_lex_string(). */ /* *************** *** 7260,7265 **** } } else if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) client_plugin= native_password_plugin_name.str; else --- 7300,7306 ---- } } else + { if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) client_plugin= native_password_plugin_name.str; else *************** *** 7274,7279 **** if (mpvio->acl_user->auth_string.length == 0) mpvio->acl_user->plugin= old_password_plugin_name; } /* remember the data part of the packet, to present it to plugin in read_packet() */ mpvio->cached_client_reply.pkt= passwd; --- 7315,7321 ---- if (mpvio->acl_user->auth_string.length == 0) mpvio->acl_user->plugin= old_password_plugin_name; } + } /* remember the data part of the packet, to present it to plugin in read_packet() */ mpvio->cached_client_reply.pkt= passwd; *************** *** 7362,7368 **** char *user= end; char *passwd= strend(user)+1; ! uint user_len= passwd - user - 1; char *db= passwd; char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 --- 7404,7410 ---- char *user= end; char *passwd= strend(user)+1; ! uint user_len= passwd - user - 1, db_len; char *db= passwd; char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 *************** *** 7380,7389 **** */ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? (uchar)(*passwd++) : strlen(passwd); ! db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? ! db + passwd_len + 1 : 0; /* strlen() can't be easily deleted without changing protocol */ ! uint db_len= db ? strlen(db) : 0; if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) return packet_error; --- 7422,7439 ---- */ uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? (uchar)(*passwd++) : strlen(passwd); ! ! if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) ! { ! db= db + passwd_len + 1; /* strlen() can't be easily deleted without changing protocol */ ! db_len= strlen(db); ! } ! else ! { ! db= 0; ! db_len= 0; ! } if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) return packet_error; *************** *** 7393,7414 **** /* Since 4.1 all database names are stored in utf8 */ if (db) { ! db_buff[db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, ! system_charset_info, ! db, db_len, ! thd->charset(), &dummy_errors)]= 0; db= db_buff; } ! user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, system_charset_info, user, user_len, ! thd->charset(), &dummy_errors)]= '\0'; user= user_buff; /* If username starts and ends in "'", chop them off */ if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') { - user[user_len-1]= 0; user++; user_len-= 2; } --- 7443,7461 ---- /* Since 4.1 all database names are stored in utf8 */ if (db) { ! db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, ! db, db_len, thd->charset(), &dummy_errors); db= db_buff; } ! user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, system_charset_info, user, user_len, ! thd->charset(), &dummy_errors); user= user_buff; /* If username starts and ends in "'", chop them off */ if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') { user++; user_len-= 2; } *************** *** 7419,7425 **** return packet_error; /* The error is set by make_lex_string(). */ if (sctx->user) x_free(sctx->user); ! if (!(sctx->user= my_strdup(user, MYF(MY_WME)))) return packet_error; /* The error is set by my_strdup(). */ /* --- 7466,7472 ---- return packet_error; /* The error is set by make_lex_string(). */ if (sctx->user) x_free(sctx->user); ! if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME)))) return packet_error; /* The error is set by my_strdup(). */ /* *************** *** 7446,7451 **** return packet_error; } else if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) client_plugin= native_password_plugin_name.str; else --- 7493,7499 ---- return packet_error; } else + { if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) client_plugin= native_password_plugin_name.str; else *************** *** 7460,7465 **** if (mpvio->acl_user->auth_string.length == 0) mpvio->acl_user->plugin= old_password_plugin_name; } /* if the acl_user needs a different plugin to authenticate --- 7508,7514 ---- if (mpvio->acl_user->auth_string.length == 0) mpvio->acl_user->plugin= old_password_plugin_name; } + } /* if the acl_user needs a different plugin to authenticate *************** *** 7574,7580 **** { /* plugin wants to read the data without sending anything first. ! send an empty packet, to start a handshake */ if (server_mpvio_write_packet(mpvio, 0, 0)) pkt_len= packet_error; --- 7624,7630 ---- { /* plugin wants to read the data without sending anything first. ! send an empty packet to force a server handshake packet to be sent */ if (server_mpvio_write_packet(mpvio, 0, 0)) pkt_len= packet_error; *************** *** 7661,7667 **** } ! static int acl_check_ssl(THD *thd, ACL_USER *acl_user) { #if defined(HAVE_OPENSSL) Vio *vio=thd->net.vio; --- 7711,7717 ---- } ! static bool acl_check_ssl(THD *thd, ACL_USER *acl_user) { #if defined(HAVE_OPENSSL) Vio *vio=thd->net.vio; *************** *** 7766,7771 **** return 1; } /** Perform the handshake, authorize the client and update thd sctx variables. --- 7816,7871 ---- return 1; } + static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name, + MPVIO_EXT *mpvio) + { + int res= CR_OK, old_status= MPVIO_EXT::FAILURE; + bool unlock_plugin= false; + plugin_ref plugin; + + if (auth_plugin_name->str == native_password_plugin_name.str) + plugin= native_password_plugin; + else + #ifndef EMBEDDED_LIBRARY + if (auth_plugin_name->str == old_password_plugin_name.str) + plugin= old_password_plugin; + else + if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name, + MYSQL_AUTHENTICATION_PLUGIN))) + unlock_plugin= true; + #endif + + mpvio->plugin= plugin; + old_status= mpvio->status; + + if (plugin) + { + st_mysql_auth *auth= (st_mysql_auth*)plugin_decl(plugin)->info; + res= auth->authenticate_user(mpvio, &mpvio->auth_info); + + if (unlock_plugin) + plugin_unlock(thd, plugin); + } + else + { + /* Server cannot load the required plugin. */ + my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str); + res= CR_ERROR; + } + + /* + If the status was MPVIO_EXT::RESTART before the authenticate_user() call + it can never be MPVIO_EXT::RESTART after the call, because any call + to write_packet() or read_packet() will reset the status. + + But (!) if a plugin never called a read_packet() or write_packet(), the + status will stay unchanged. We'll fix it, by resetting the status here. + */ + if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART) + mpvio->status= MPVIO_EXT::FAILURE; // reset to the default + + return res; + } /** Perform the handshake, authorize the client and update thd sctx variables. *************** *** 7782,7798 **** @retval 1 error */ ! int acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) { ! DBUG_ENTER("acl_authenticate"); ! int res= CR_OK, old_status= MPVIO_EXT::FAILURE; ! plugin_ref plugin; MPVIO_EXT mpvio; - st_mysql_auth *auth; LEX_STRING *auth_plugin_name= default_auth_plugin_name; - bool unlock_plugin; enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER : COM_CONNECT; compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH); --- 7882,7895 ---- @retval 1 error */ ! bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) { ! int res= CR_OK; MPVIO_EXT mpvio; LEX_STRING *auth_plugin_name= default_auth_plugin_name; enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER : COM_CONNECT; + DBUG_ENTER("acl_authenticate"); compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH); *************** *** 7804,7812 **** mpvio.connect_errors= connect_errors; mpvio.status= MPVIO_EXT::FAILURE; ! if (com_change_user_pkt_len == 0) ! thd->scramble[SCRAMBLE_LENGTH]= 1; // it means - there is no scramble yet ! else { mpvio.packets_written++; // pretend that a server handshake packet was sent mpvio.packets_read++; // take COM_CHANGE_USER packet into account --- 7901,7907 ---- mpvio.connect_errors= connect_errors; mpvio.status= MPVIO_EXT::FAILURE; ! if (command == COM_CHANGE_USER) { mpvio.packets_written++; // pretend that a server handshake packet was sent mpvio.packets_read++; // take COM_CHANGE_USER packet into account *************** *** 7816,7882 **** DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART || mpvio.status == MPVIO_EXT::SUCCESS); - /* - we skip the first step of the authentication - - the one that starts a default plugin, sends the handshake packet with the - scramble and reads the packet with the user name. - in COM_CHANGE_USER the client already has the scramble and we already - know the user name - */ - goto skip_first; } - - retry: - unlock_plugin= false; - if (auth_plugin_name->str == native_password_plugin_name.str) - plugin= native_password_plugin; else - #ifndef EMBEDDED_LIBRARY - if (auth_plugin_name->str == old_password_plugin_name.str) - plugin= old_password_plugin; - else - if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name, - MYSQL_AUTHENTICATION_PLUGIN))) - unlock_plugin= true; - else - #endif { ! /* Server cannot load required plugin. */ ! my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str); ! DBUG_RETURN(1); ! } - mpvio.plugin= plugin; - auth= (st_mysql_auth*)plugin_decl(plugin)->info; - - old_status= mpvio.status; - res= auth->authenticate_user(&mpvio, &mpvio.auth_info); - - if (unlock_plugin) - plugin_unlock(thd, plugin); - - skip_first: /* ! restart the authentication, if needed. ! Note, that we only trust RESTART status if it wasn't set before ! the authentication. If it was - it can never be set after the call, as ! any read_packet() or write_packet() would've reset it. So, if it's ! was set before authenticate_user() and we see it here, it simply means ! that there was a restart, but a plugin never called read_packet() or ! write_packet() and what we see is the old status from before the ! authenticate_user() call. */ if (mpvio.status == MPVIO_EXT::RESTART) { - if (old_status == MPVIO_EXT::RESTART) - mpvio.status= MPVIO_EXT::FAILURE; // reset to the default - else - { DBUG_ASSERT(mpvio.acl_user); auth_plugin_name= &mpvio.acl_user->plugin; ! goto retry; ! } } Security_context *sctx= thd->security_ctx; --- 7911,7943 ---- DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART || mpvio.status == MPVIO_EXT::SUCCESS); } else { ! /* mark the thd as having no scramble yet */ ! thd->scramble[SCRAMBLE_LENGTH]= 1; /* ! perform the first authentication attempt, with the default plugin. ! This sends the server handshake packet, reads the client reply ! with a user name, and performs the authentication if everyone has used ! the correct plugin. ! */ ! res= do_auth_once(thd, auth_plugin_name, &mpvio); ! } ! /* ! retry the authentication, if - after receiving the user name - ! we found that we need to switch to a non-default plugin */ if (mpvio.status == MPVIO_EXT::RESTART) { DBUG_ASSERT(mpvio.acl_user); + DBUG_ASSERT(command == COM_CHANGE_USER || + my_strcasecmp(system_charset_info, auth_plugin_name->str, + mpvio.acl_user->plugin.str)); auth_plugin_name= &mpvio.acl_user->plugin; ! res= do_auth_once(thd, auth_plugin_name, &mpvio); } Security_context *sctx= thd->security_ctx; *************** *** 7995,8002 **** /* mysql_change_db() has pushed the error message. */ if (thd->user_connect) { - decrease_user_connections(thd->user_connect); status_var_increment(thd->status_var.access_denied_errors); thd->user_connect= 0; } DBUG_RETURN(1); --- 8056,8063 ---- /* mysql_change_db() has pushed the error message. */ if (thd->user_connect) { status_var_increment(thd->status_var.access_denied_errors); + decrease_user_connections(thd->user_connect); thd->user_connect= 0; } DBUG_RETURN(1); *************** *** 8036,8059 **** MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; THD *thd=mpvio->thd; ! /* ! let's check if the client has got the scramble already. ! That could've happened if another plugin has started the authentication, ! has sent the scramble, but after reading the user name the server has found ! that this particular user should be handled by this particular plugin. ! */ ! if (thd->scramble[SCRAMBLE_LENGTH]) ! { ! /* no scramble was sent to the client yet, do it now */ ! create_random_string(thd->scramble, ! pkt_len= SCRAMBLE_LENGTH, &thd->rand); ! pkt= (uchar*)thd->scramble; ! if (mpvio->write_packet(mpvio, pkt, pkt_len + 1)) return CR_ERROR; - } ! /* ok, the client has got the scramble. read the reply and authenticate */ /* --- 8097,8111 ---- MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; THD *thd=mpvio->thd; ! /* generate the scramble, or reuse the old one */ if (thd->scramble[SCRAMBLE_LENGTH]) ! create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); ! ! /* send it to the client */ ! if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1)) return CR_ERROR; ! /* reply and authenticate */ /* *************** *** 8085,8091 **** plugins are involved. Anyway, it still looks simple from a plugin point of view: ! "the client has got the scramble, read the reply and authenticate". All the magic is transparently handled by the server. */ --- 8137,8143 ---- plugins are involved. Anyway, it still looks simple from a plugin point of view: ! "send the scramble, read the reply and authenticate". All the magic is transparently handled by the server. */ *************** *** 8120,8136 **** MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; THD *thd=mpvio->thd; if (thd->scramble[SCRAMBLE_LENGTH]) ! { ! /* no scramble was sent to the client yet, do it now */ ! create_random_string(thd->scramble, ! pkt_len= SCRAMBLE_LENGTH, &thd->rand); ! pkt= (uchar*)thd->scramble; ! if (mpvio->write_packet(mpvio, pkt, pkt_len)) return CR_ERROR; - } ! /* ok, the client has got the scramble. read the reply and authenticate */ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0) return CR_ERROR; --- 8172,8186 ---- MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; THD *thd=mpvio->thd; + /* generate the scramble, or reuse the old one */ if (thd->scramble[SCRAMBLE_LENGTH]) ! create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); ! ! /* send it to the client */ ! if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1)) return CR_ERROR; ! /* read the reply and authenticate */ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0) return CR_ERROR; *************** *** 8149,8176 **** if (pkt_len == 0) /* no password */ return info->auth_string[0] ? CR_ERROR : CR_OK; ! /* ! If the server is running in secure auth mode, short scrambles are ! forbidden. Extra juggling to report the same error as the old code. ! */ ! if (opt_secure_auth) ! { ! if (thd->client_capabilities & CLIENT_PROTOCOL_41) ! { ! my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0), ! thd->security_ctx->user, ! thd->security_ctx->host_or_ip); ! general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), ! thd->security_ctx->user, ! thd->security_ctx->host_or_ip); ! } ! else ! { ! my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); ! general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); ! } return CR_ERROR; - } info->password_used = 1; --- 8199,8206 ---- if (pkt_len == 0) /* no password */ return info->auth_string[0] ? CR_ERROR : CR_OK; ! if (secure_auth(thd)) return CR_ERROR; info->password_used = 1; === modified file 'sql/sql_acl.h' *** sql/sql_acl.h 2010-02-19 08:18:09 +0000 --- sql/sql_acl.h 2010-03-26 11:32:46 +0000 *************** *** 169,175 **** void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); ! int acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len); bool acl_getroot(Security_context *sctx, char *user, char *host, char *ip, char *db); bool acl_check_host(const char *host, const char *ip); --- 169,175 ---- void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); ! bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len); bool acl_getroot(Security_context *sctx, char *user, char *host, char *ip, char *db); bool acl_check_host(const char *host, const char *ip); === modified file 'sql/sql_parse.cc' *** sql/sql_parse.cc 2010-03-19 20:29:42 +0000 --- sql/sql_parse.cc 2010-03-26 11:32:46 +0000 *************** *** 1115,1122 **** x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; ! thd->db= save_db; ! thd->db_length= save_db_length; thd->variables.character_set_client= save_character_set_client; thd->variables.collation_connection= save_collation_connection; thd->variables.character_set_results= save_character_set_results; --- 1115,1121 ---- x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; ! thd->reset_db(save_db, save_db_length); thd->variables.character_set_client= save_character_set_client; thd->variables.collation_connection= save_collation_connection; thd->variables.character_set_results= save_character_set_results;