=== modified file 'include/sql_common.h' --- include/sql_common.h 2010-02-19 08:18:09 +0000 +++ include/sql_common.h 2010-02-23 18:49:25 +0000 @@ -95,7 +95,8 @@ void set_mysql_extended_error(MYSQL *mys /* client side of the pluggable authentication */ struct st_plugin_vio_info; void mpvio_info(Vio *vio, struct st_plugin_vio_info *info); -int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *db); +int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + char *data_plugin, const char *db); int mysql_client_plugin_init(); void mysql_client_plugin_deinit(); struct st_mysql_client_plugin; === modified file 'libmysql/libmysql.c' --- libmysql/libmysql.c 2010-02-19 08:18:09 +0000 +++ libmysql/libmysql.c 2010-02-23 18:49:25 +0000 @@ -692,7 +692,7 @@ my_bool STDCALL mysql_change_user(MYSQL mysql->passwd= (char*)(passwd ? passwd : ""); mysql->db= 0; - rc= run_plugin_auth(mysql, 0, 0, db); + rc= run_plugin_auth(mysql, 0, 0, 0, db); /* The server will close all statements no matter was the attempt === modified file 'plugin/auth/dialog.c' --- plugin/auth/dialog.c 2010-02-19 08:18:09 +0000 +++ plugin/auth/dialog.c 2010-02-23 12:02:00 +0000 @@ -48,8 +48,8 @@ */ #define ORDINARY_QUESTION "\2" #define LAST_QUESTION "\3" -#define LAST_PASSWORD "\4" -#define PASSWORD_QUESTION "\5" +#define PASSWORD_QUESTION "\4" +#define LAST_PASSWORD "\5" /********************* SERVER SIDE ****************************************/ === modified file 'sql-common/client.c' --- sql-common/client.c 2010-02-19 08:18:09 +0000 +++ sql-common/client.c 2010-02-23 18:49:25 +0000 @@ -2183,6 +2183,7 @@ error: return 1; } +static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int); /** vio->read_packet() callback method for client authentication plugins @@ -2195,8 +2196,20 @@ static int client_mpvio_read_packet(stru 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, + or it's mysql_change_user(). Either way, there is no data + for a plugin to read. send a dummy packet to the server + to initiate a dialog. + */ + if (client_mpvio_write_packet(mpv, 0, 0)) + return (int)packet_error; + } + /* there are cached data left, feed it to a plugin */ - if (mpvio->packets_read == 0 || mpvio->cached_server_reply.pkt) + if (mpvio->cached_server_reply.pkt) { *buf= mpvio->cached_server_reply.pkt; mpvio->cached_server_reply.pkt= 0; @@ -2206,10 +2219,16 @@ static int client_mpvio_read_packet(stru /* otherwise read the data */ pkt_len= (*mysql->methods->read_change_user_result)(mysql); + mpvio->last_read_packet_len= pkt_len; *buf= mysql->net.read_pos; + + /* was it a request to change plugins ? */ + if (**buf == 254) + return (int)packet_error; /* if yes, this plugin shan't continue */ + /* - the server sends \1\255 instead of just \255 - otherwise - cli_safe_read would took it as an error packet. + 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) @@ -2218,7 +2237,7 @@ static int client_mpvio_read_packet(stru pkt_len--; } mpvio->packets_read++; - return mpvio->last_read_packet_len= pkt_len; + return pkt_len; } /** @@ -2238,7 +2257,6 @@ static int client_mpvio_write_packet(str if (mpvio->packets_written == 0) { - DBUG_ASSERT(mpvio->packets_read == 1); if (mpvio->mysql_change_user) res= send_change_user_packet(mpvio, pkt, pkt_len); else @@ -2314,16 +2332,19 @@ static void client_mpvio_info(MYSQL_PLUG @note this is used by both the mysql_real_connect and mysql_change_user - @param mysql mysql - @param data pointer to the plugin auth data (scramble) in the handshake - packet or 0 if it's mysql_change_user() - @param data_len the length of the data - @param db initial db to use, can be 0 + @param mysql mysql + @param data pointer to the plugin auth data (scramble) in the + handshake packet + @param data_len the length of the data + @param data_plugin a plugin that data were prepared for + or 0 if it's mysql_change_user() + @param db initial db to use, can be 0 @retval 0 ok @retval 1 error */ -int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, const char *db) +int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + char *data_plugin, const char *db) { const char *auth_plugin_name; auth_plugin_t *auth_plugin; @@ -2332,7 +2353,8 @@ int run_plugin_auth(MYSQL *mysql, char * int res; /* determine the default/initial plugin to use */ - if (mysql->options.extension && mysql->options.extension->default_auth) + 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 ? @@ -2344,9 +2366,16 @@ int run_plugin_auth(MYSQL *mysql, char * mysql->net.last_errno= 0; /* just in case */ + if (data_plugin && strcmp(data_plugin, auth_plugin_name)) + { + /* data was prepared for a different plugin, don't show it to this one */ + data= 0; + data_len= 0; + } + + mpvio.mysql_change_user= data_plugin == 0; mpvio.cached_server_reply.pkt= (uchar*)data; mpvio.cached_server_reply.pkt_len= data_len; - mpvio.mysql_change_user= data == 0; mpvio.read_packet= client_mpvio_read_packet; mpvio.write_packet= client_mpvio_write_packet; mpvio.info= client_mpvio_info; @@ -2359,7 +2388,7 @@ int run_plugin_auth(MYSQL *mysql, char * compile_time_assert(CR_OK == -1); compile_time_assert(CR_ERROR == 0); - if (res > CR_OK) + if (res > CR_OK && mysql->net.read_pos[0] != 254) { /* the plugin returned an error. write it down in mysql, @@ -2452,8 +2481,9 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons uint port, const char *unix_socket,ulong client_flag) { char buff[NAME_LEN+USERNAME_LENGTH+100]; - int scramble_data_len; + 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; @@ -2791,6 +2821,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons */ scramble_data= end; scramble_data_len= SCRAMBLE_LENGTH_323 + 1; + scramble_plugin= old_password_plugin_name; end+= scramble_data_len; if (pkt_end >= end + 1) @@ -2801,6 +2832,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons mysql->server_language=end[2]; mysql->server_status=uint2korr(end+3); mysql->server_capabilities|= uint2korr(end+5) << 16; + pkt_scramble_len= end[7]; } end+= 18; @@ -2848,7 +2880,16 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons memmove(end - SCRAMBLE_LENGTH_323, scramble_data, SCRAMBLE_LENGTH_323); scramble_data= end - SCRAMBLE_LENGTH_323; - scramble_data_len= pkt_end - scramble_data; + if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH) + { + scramble_data_len= pkt_scramble_len; + scramble_plugin= scramble_data + scramble_data_len; + } + else + { + scramble_data_len= pkt_end - scramble_data; + scramble_plugin= native_password_plugin_name; + } } else mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION; @@ -2859,7 +2900,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,cons Part 2: invoke the plugin to send the authentication data to the server */ - if (run_plugin_auth(mysql, scramble_data, scramble_data_len, db)) + if (run_plugin_auth(mysql, scramble_data, scramble_data_len, + scramble_plugin, db)) goto error; /* @@ -3729,27 +3771,27 @@ int STDCALL mysql_set_character_set(MYSQ */ static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { - /* read the scramble */ + int pkt_len; uchar *pkt; - int pkt_len= vio->read_packet(vio, &pkt); - - if (pkt_len < 0) - return CR_ERROR; - if (pkt == 0) + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* - in mysql_change_user() the client sends the first packet, so - the first vio->read_packet() does nothing (pkt == 0). + in mysql_change_user() the client sends the first packet. we use the old scramble. */ - pkt= mysql->scramble; + pkt= (uchar*)mysql->scramble; pkt_len= SCRAMBLE_LENGTH + 1; } else { + /* read the scramble */ + if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) + return CR_ERROR; + if (pkt_len != SCRAMBLE_LENGTH + 1) return CR_SERVER_HANDSHAKE_ERR; + /* save it in MYSQL */ memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH); mysql->scramble[SCRAMBLE_LENGTH] = 0; @@ -3775,25 +3817,24 @@ static int native_password_auth_client(M */ static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) { - /* read the scramble */ uchar *pkt; - int pkt_len= vio->read_packet(vio, &pkt); - - if (pkt_len < 0) - return CR_ERROR; + int pkt_len; - if (pkt == 0) + if (((MCPVIO_EXT *)vio)->mysql_change_user) { /* - in mysql_change_user() the client sends the first packet, so - the first vio->read_packet() does nothing (pkt == 0). + in mysql_change_user() the client sends the first packet. we use the old scramble. */ - pkt= mysql->scramble; + pkt= (uchar*)mysql->scramble; pkt_len= SCRAMBLE_LENGTH_323 + 1; } else { + /* read the scramble */ + if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) + return CR_ERROR; + if (pkt_len != SCRAMBLE_LENGTH_323 + 1 && pkt_len != SCRAMBLE_LENGTH + 1) return CR_SERVER_HANDSHAKE_ERR; === modified file 'sql/sql_acl.cc' --- sql/sql_acl.cc 2010-02-19 08:18:09 +0000 +++ sql/sql_acl.cc 2010-02-23 18:49:25 +0000 @@ -6838,7 +6838,8 @@ static void login_failed_error(THD *thd, 1 server character set 2 server status 2 server capabilities (two upper bytes) - 11 reserved, always 0 + 1 length of the scramble + 10 reserved, always 0 n rest of the plugin provided data (at least 12 bytes) 1 \0 byte, terminating the second part of a scramble @@ -6913,18 +6914,25 @@ static bool send_server_handshake_packet tail: that's why first part of the scramble is placed here, and second part at the end of packet. */ - end= strmake(end, data, SCRAMBLE_LENGTH_323) + 1; + end= (char*)memcpy(end, data, SCRAMBLE_LENGTH_323); + end+= SCRAMBLE_LENGTH_323; + *end++= 0; int2store(end, thd->client_capabilities); /* write server characteristics: up to 16 bytes allowed */ end[2]=(char) default_charset_info->number; int2store(end+3, mpvio->thd->server_status); int2store(end+5, thd->client_capabilities >> 16); - bzero(end+7, 11); + end[7]= data_len; + bzero(end+8, 10); end+= 18; /* write scramble tail */ - end= strmake(end, data + SCRAMBLE_LENGTH_323, - data_len - SCRAMBLE_LENGTH_323) + 1; + end= (char*)memcpy(end, data + SCRAMBLE_LENGTH_323, + data_len - SCRAMBLE_LENGTH_323); + end+= data_len - SCRAMBLE_LENGTH_323; + end= strmake(end, plugin_name(mpvio->plugin)->str, + plugin_name(mpvio->plugin)->length); + int res= my_net_write(&mpvio->thd->net, (uchar*) buff, (size_t) (end-buff)) || net_flush(&mpvio->thd->net); my_afree(buff); @@ -7438,12 +7446,12 @@ static int server_mpvio_write_packet(MYS res= send_server_handshake_packet(mpvio, (char*)packet, packet_len); else if (mpvio->status == MPVIO_EXT::RESTART) res= send_plugin_request_packet(mpvio, packet, packet_len); - else if (packet_len > 0 && (*packet == 1 || *packet == 255)) + else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254)) { /* - we cannot allow plugin data packet to start from 255 - - as the client will treat it as an error packet and abort. - We'll escape \255 byte with \1 byte. Consequently, we + we cannot allow plugin data packet to start from 255 or 254 - + as the client will treat it as an error or "change plugi" packet. + We'll escape these bytes with \1. Consequently, we have to escape \1 byte too. */ res= net_write_command(&mpvio->thd->net, 1, (uchar*)"", 0,