mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
merge commit, hope that none of bar and dlenev changes were
lost. BitKeeper/etc/logging_ok: auto-union include/mysql.h: Auto merged include/mysql_com.h: Auto merged libmysql/libmysql.c: Auto merged scripts/mysql_fix_privilege_tables.sql: Auto merged sql/item_create.cc: Auto merged sql/item_create.h: Auto merged sql/item_strfunc.cc: Auto merged sql/item_strfunc.h: Auto merged sql/lex.h: Auto merged sql/mysqld.cc: Auto merged sql/protocol.h: Auto merged sql/set_var.cc: Auto merged sql/sql_acl.h: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_yacc.yy: Auto merged include/mysqld_error.h: merge commit: new error message added sql-common/client.c: merge commit sql/mysql_priv.h: merge commit: autoremerge sql/set_var.h: merge commit sql/share/czech/errmsg.txt: merge commit sql/share/danish/errmsg.txt: merge commit sql/share/dutch/errmsg.txt: merge commit sql/share/english/errmsg.txt: merge commit sql/share/estonian/errmsg.txt: merge commit sql/share/french/errmsg.txt: merge commit sql/share/german/errmsg.txt: merge commit sql/share/greek/errmsg.txt: merge commit sql/share/hungarian/errmsg.txt: merge commit sql/share/italian/errmsg.txt: merge commit sql/share/japanese/errmsg.txt: merge commit sql/share/korean/errmsg.txt: merge commit sql/share/norwegian-ny/errmsg.txt: merge commit sql/share/norwegian/errmsg.txt: merge commit sql/share/polish/errmsg.txt: merge commit sql/share/portuguese/errmsg.txt: merge commit sql/share/romanian/errmsg.txt: merge commit sql/share/russian/errmsg.txt: merge commit sql/share/serbian/errmsg.txt: merge commit sql/share/slovak/errmsg.txt: merge commit sql/share/spanish/errmsg.txt: merge commit sql/share/swedish/errmsg.txt: merge commit sql/share/ukrainian/errmsg.txt: merge commit sql/slave.cc: merge commit sql/slave.h: merge commit sql/sql_acl.cc: merge commit sql/sql_parse.cc: merge commit sql/sql_repl.h: merge commit
This commit is contained in:
614
sql/sql_parse.cc
614
sql/sql_parse.cc
@ -178,153 +178,179 @@ end:
|
||||
|
||||
|
||||
/*
|
||||
Check if user is ok
|
||||
|
||||
Check if user exist and password supplied is correct.
|
||||
SYNOPSIS
|
||||
check_user()
|
||||
thd Thread handle
|
||||
command Command for connection (for log)
|
||||
user Name of user trying to connect
|
||||
passwd Scrambled password sent from client
|
||||
db Database to connect to
|
||||
check_count If set to 1, don't allow too many connection
|
||||
simple_connect If 1 then client is of old type and we should connect
|
||||
using the old method (no challange)
|
||||
do_send_error Set to 1 if we should send error to user
|
||||
prepared_scramble Buffer to store hash password of new connection
|
||||
had_password Set to 1 if the user gave a password
|
||||
cur_priv_version Check flag to know if someone flushed the privileges
|
||||
since last code
|
||||
hint_user Pointer used by acl_getroot() to remmeber user for
|
||||
next call
|
||||
thd thread handle, thd->{host,user,ip} are used
|
||||
command originator of the check: now check_user is called
|
||||
during connect and change user procedures; used for
|
||||
logging.
|
||||
passwd scrambled password recieved from client
|
||||
passwd_len length of scrambled password
|
||||
db database name to connect to, may be NULL
|
||||
check_count dont know exactly
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
thd->user, thd->master_access, thd->priv_user, thd->db and
|
||||
thd->db_access are updated
|
||||
1 Access denied; Error sent to client
|
||||
-1 If do_send_error == 1: Failed connect, error sent to client
|
||||
If do_send_error == 0: Prepare for stage of connect
|
||||
Note, that host, user and passwd may point to communication buffer.
|
||||
Current implementation does not depened on that, but future changes
|
||||
should be done with this in mind; 'thd' is INOUT, all other params
|
||||
are 'IN'.
|
||||
|
||||
RETURN VALUE
|
||||
0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and
|
||||
thd->db_access are updated; OK is sent to client;
|
||||
-1 access denied or handshake error; error is sent to client;
|
||||
>0 error, not sent to client
|
||||
*/
|
||||
|
||||
static int check_user(THD *thd,enum_server_command command, const char *user,
|
||||
const char *passwd, const char *db, bool check_count,
|
||||
bool simple_connect, bool do_send_error,
|
||||
char *prepared_scramble, bool had_password,
|
||||
uint *cur_priv_version, ACL_USER** hint_user)
|
||||
static int check_user(THD *thd, enum enum_server_command command,
|
||||
const char *passwd, uint passwd_len, const char *db,
|
||||
bool check_count)
|
||||
{
|
||||
thd->db=0;
|
||||
thd->db_length=0;
|
||||
USER_RESOURCES ur;
|
||||
char tmp_passwd[SCRAMBLE41_LENGTH+1];
|
||||
DBUG_ENTER("check_user");
|
||||
|
||||
my_bool opt_secure_auth_local;
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
opt_secure_auth_local= opt_secure_auth;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
|
||||
/*
|
||||
Move password to temporary buffer as it may be stored in communication
|
||||
buffer
|
||||
If the server is running in secure auth mode, short scrambles are
|
||||
forbidden.
|
||||
*/
|
||||
strmake(tmp_passwd, passwd, sizeof(tmp_passwd));
|
||||
passwd= tmp_passwd; // Use local copy
|
||||
|
||||
/* We shall avoid dupplicate user allocations here */
|
||||
if (!thd->user && !(thd->user = my_strdup(user, MYF(0))))
|
||||
if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
|
||||
{
|
||||
send_error(thd,ER_OUT_OF_RESOURCES);
|
||||
DBUG_RETURN(1);
|
||||
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
||||
mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
thd->master_access=acl_getroot(thd, thd->host, thd->ip, thd->user,
|
||||
passwd, thd->scramble,
|
||||
&thd->priv_user, thd->priv_host,
|
||||
(protocol_version == 9 ||
|
||||
!(thd->client_capabilities &
|
||||
CLIENT_LONG_PASSWORD)),
|
||||
&ur,prepared_scramble,
|
||||
cur_priv_version,hint_user);
|
||||
|
||||
DBUG_PRINT("info",
|
||||
("Capabilities: %d packet_length: %ld Host: '%s' Login user: '%s' Priv_user: '%s' Using password: %s Access: %u db: '%s'",
|
||||
thd->client_capabilities, thd->max_client_packet_length,
|
||||
thd->host_or_ip, thd->user, thd->priv_user,
|
||||
had_password ? "yes": "no",
|
||||
thd->master_access, thd->db ? thd->db : "*none*"));
|
||||
if (passwd_len != 0 &&
|
||||
passwd_len != SCRAMBLE_LENGTH &&
|
||||
passwd_len != SCRAMBLE_LENGTH_323)
|
||||
DBUG_RETURN(ER_HANDSHAKE_ERROR);
|
||||
|
||||
/*
|
||||
In case we're going to retry we should not send error message at this
|
||||
point
|
||||
Clear thd->db as it points to something, that will be freed when
|
||||
connection is closed. We don't want to accidently free a wrong pointer
|
||||
if connect failed. Also in case of 'CHANGE USER' failure, current
|
||||
database will be switched to 'no database selected'.
|
||||
*/
|
||||
if (thd->master_access & NO_ACCESS)
|
||||
thd->db= 0;
|
||||
thd->db_length= 0;
|
||||
|
||||
USER_RESOURCES ur;
|
||||
int res= acl_getroot(thd, &ur, passwd, passwd_len);
|
||||
if (res == -1)
|
||||
{
|
||||
if (do_send_error || !had_password || !*hint_user)
|
||||
/*
|
||||
This happens when client (new) sends password scrambled with
|
||||
scramble(), but database holds old value (scrambled with
|
||||
scramble_323()). Here we please client to send scrambled_password
|
||||
in old format.
|
||||
*/
|
||||
NET *net= &thd->net;
|
||||
if (opt_secure_auth_local)
|
||||
{
|
||||
DBUG_PRINT("info",("Access denied"));
|
||||
/*
|
||||
Old client should get nicer error message if password version is
|
||||
not supported
|
||||
*/
|
||||
if (simple_connect && *hint_user && (*hint_user)->pversion)
|
||||
net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE,
|
||||
thd->user, thd->host_or_ip);
|
||||
mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
|
||||
thd->user, thd->host_or_ip);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
if (send_old_password_request(thd) ||
|
||||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
|
||||
{ // specific packet size
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
DBUG_RETURN(ER_HANDSHAKE_ERROR);
|
||||
}
|
||||
/* Final attempt to check the user based on reply */
|
||||
/* So as passwd is short, errcode is always >= 0 */
|
||||
res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
|
||||
}
|
||||
/* here res is always >= 0 */
|
||||
if (res == 0)
|
||||
{
|
||||
if (!(thd->master_access & NO_ACCESS)) // authentification is OK
|
||||
{
|
||||
DBUG_PRINT("info",
|
||||
("Capabilities: %d packet_length: %ld Host: '%s' "
|
||||
"Login user: '%s' Priv_user: '%s' Using password: %s "
|
||||
"Access: %u db: '%s'",
|
||||
thd->client_capabilities, thd->max_client_packet_length,
|
||||
thd->host_or_ip, thd->user, thd->priv_user,
|
||||
passwd_len ? "yes": "no",
|
||||
thd->master_access, thd->db ? thd->db : "*none*"));
|
||||
|
||||
if (check_count)
|
||||
{
|
||||
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
||||
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||
bool count_ok= thread_count < max_connections + delayed_insert_threads
|
||||
|| (thd->master_access & SUPER_ACL);
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
if (!count_ok)
|
||||
{ // too many connections
|
||||
send_error(thd, ER_CON_COUNT_ERROR);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Why logging is performed before all checks've passed? */
|
||||
mysql_log.write(thd,command,
|
||||
(thd->priv_user == thd->user ?
|
||||
(char*) "%s@%s on %s" :
|
||||
(char*) "%s@%s as anonymous on %s"),
|
||||
thd->user, thd->host_or_ip,
|
||||
db ? db : (char*) "");
|
||||
|
||||
/*
|
||||
This is the default access rights for the current database. It's
|
||||
set to 0 here because we don't have an active database yet (and we
|
||||
may not have an active database to set.
|
||||
*/
|
||||
thd->db_access=0;
|
||||
|
||||
/* Don't allow user to connect if he has done too many queries */
|
||||
if ((ur.questions || ur.updates ||
|
||||
ur.connections || max_user_connections) &&
|
||||
get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
|
||||
DBUG_RETURN(1);
|
||||
if (thd->user_connect && thd->user_connect->user_resources.connections &&
|
||||
check_for_max_user_connections(thd, thd->user_connect))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/* Change database if necessary: OK or FAIL is sent in mysql_change_db */
|
||||
if (db && db[0])
|
||||
{
|
||||
if (mysql_change_db(thd, db))
|
||||
{
|
||||
if (thd->user_connect)
|
||||
decrease_user_connections(thd->user_connect);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
net_printf(thd, ER_ACCESS_DENIED_ERROR,
|
||||
thd->user,
|
||||
thd->host_or_ip,
|
||||
had_password ? ER(ER_YES) : ER(ER_NO));
|
||||
mysql_log.write(thd,COM_CONNECT,ER(ER_ACCESS_DENIED_ERROR),
|
||||
thd->user,
|
||||
thd->host_or_ip,
|
||||
had_password ? ER(ER_YES) : ER(ER_NO));
|
||||
}
|
||||
DBUG_RETURN(1); // Error already given
|
||||
}
|
||||
DBUG_PRINT("info",("Prepare for second part of handshake"));
|
||||
DBUG_RETURN(-1); // no report error in special handshake
|
||||
}
|
||||
|
||||
if (check_count)
|
||||
{
|
||||
VOID(pthread_mutex_lock(&LOCK_thread_count));
|
||||
bool tmp=(thread_count - delayed_insert_threads >= max_connections &&
|
||||
!(thd->master_access & SUPER_ACL));
|
||||
VOID(pthread_mutex_unlock(&LOCK_thread_count));
|
||||
if (tmp)
|
||||
{ // Too many connections
|
||||
send_error(thd, ER_CON_COUNT_ERROR);
|
||||
DBUG_RETURN(1);
|
||||
send_ok(thd);
|
||||
thd->password= test(passwd_len); // remember for error messages
|
||||
/* Ready to handle queries */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
}
|
||||
mysql_log.write(thd,command,
|
||||
(thd->priv_user == thd->user ?
|
||||
(char*) "%s@%s on %s" :
|
||||
(char*) "%s@%s as anonymous on %s"),
|
||||
user,
|
||||
thd->host_or_ip,
|
||||
db ? db : (char*) "");
|
||||
thd->db_access=0;
|
||||
/* Don't allow user to connect if he has done too many queries */
|
||||
if ((ur.questions || ur.updates || ur.connections || max_user_connections) &&
|
||||
get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
|
||||
DBUG_RETURN(1);
|
||||
if (thd->user_connect && ((thd->user_connect->user_resources.connections) ||
|
||||
max_user_connections) &&
|
||||
check_for_max_user_connections(thd, thd->user_connect))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (db && db[0])
|
||||
else if (res == 2) // client gave short hash, server has long hash
|
||||
{
|
||||
int error= test(mysql_change_db(thd,db));
|
||||
if (error && thd->user_connect)
|
||||
decrease_user_connections(thd->user_connect);
|
||||
DBUG_RETURN(error);
|
||||
net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE);
|
||||
mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
send_ok(thd); // Ready to handle questions
|
||||
thd->password= test(passwd[0]); // Remember for error messages
|
||||
DBUG_RETURN(0); // ok
|
||||
net_printf(thd, ER_ACCESS_DENIED_ERROR,
|
||||
thd->user,
|
||||
thd->host_or_ip,
|
||||
passwd_len ? ER(ER_YES) : ER(ER_NO));
|
||||
mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
|
||||
thd->user,
|
||||
thd->host_or_ip,
|
||||
passwd_len ? ER(ER_YES) : ER(ER_NO));
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check for maximum allowable user connections, if the mysqld server is
|
||||
started with corresponding variable that is greater then 0.
|
||||
@ -526,45 +552,36 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
|
||||
|
||||
|
||||
/*
|
||||
Check connnectionn and get priviliges
|
||||
|
||||
Perform handshake, authorize client and update thd ACL variables.
|
||||
SYNOPSIS
|
||||
check_connections
|
||||
thd Thread handle
|
||||
check_connection()
|
||||
thd thread handle
|
||||
|
||||
RETURN
|
||||
0 ok
|
||||
-1 Error, which is sent to user
|
||||
> 0 Error code (not sent to user)
|
||||
0 success, OK is sent to user, thd is updated.
|
||||
-1 error, which is sent to user
|
||||
> 0 error code (not sent to user)
|
||||
*/
|
||||
|
||||
#ifndef EMBEDDED_LIBRARY
|
||||
static int
|
||||
check_connections(THD *thd)
|
||||
check_connection(THD *thd)
|
||||
{
|
||||
int res;
|
||||
uint connect_errors=0;
|
||||
uint cur_priv_version;
|
||||
bool using_password;
|
||||
uint connect_errors= 0;
|
||||
NET *net= &thd->net;
|
||||
char *end, *user, *passwd, *db;
|
||||
char prepared_scramble[SCRAMBLE41_LENGTH+4]; /* Buffer for scramble&hash */
|
||||
char db_buff[NAME_LEN+1];
|
||||
ACL_USER* cached_user=NULL; /* Initialise to NULL for first stage */
|
||||
DBUG_PRINT("info",("New connection received on %s",
|
||||
vio_description(net->vio)));
|
||||
|
||||
/* Remove warning from valgrind. TODO: Fix it in password.c */
|
||||
bzero((char*) &prepared_scramble[0], sizeof(prepared_scramble));
|
||||
DBUG_PRINT("info",
|
||||
("New connection received on %s", vio_description(net->vio)));
|
||||
|
||||
if (!thd->host) // If TCP/IP connection
|
||||
{
|
||||
char ip[30];
|
||||
|
||||
if (vio_peer_addr(net->vio, ip, &thd->peer_port))
|
||||
return (ER_BAD_HOST_ERROR);
|
||||
if (!(thd->ip = my_strdup(ip,MYF(0))))
|
||||
if (!(thd->ip= my_strdup(ip,MYF(0))))
|
||||
return (ER_OUT_OF_RESOURCES);
|
||||
thd->host_or_ip=thd->ip;
|
||||
thd->host_or_ip= thd->ip;
|
||||
#if !defined(HAVE_SYS_UN_H) || defined(HAVE_mit_thread)
|
||||
/* Fast local hostname resolve for Win32 */
|
||||
if (!strcmp(thd->ip,"127.0.0.1"))
|
||||
@ -600,15 +617,16 @@ check_connections(THD *thd)
|
||||
DBUG_PRINT("info",("Host: %s",thd->host));
|
||||
thd->host_or_ip= thd->host;
|
||||
thd->ip= 0;
|
||||
bzero((char*) &thd->remote,sizeof(struct sockaddr));
|
||||
bzero((char*) &thd->remote, sizeof(struct sockaddr));
|
||||
}
|
||||
/* Ensure that wrong hostnames doesn't cause buffer overflows */
|
||||
vio_keepalive(net->vio, TRUE);
|
||||
|
||||
ulong pkt_len=0;
|
||||
ulong pkt_len= 0;
|
||||
char *end;
|
||||
{
|
||||
/* buff[] needs to big enough to hold the server_version variable */
|
||||
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH+64];
|
||||
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64];
|
||||
ulong client_flags = (CLIENT_LONG_FLAG | CLIENT_CONNECT_WITH_DB |
|
||||
CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION);
|
||||
|
||||
@ -622,19 +640,34 @@ check_connections(THD *thd)
|
||||
client_flags |= CLIENT_SSL; /* Wow, SSL is avalaible! */
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
end=strnmov(buff,server_version,SERVER_VERSION_LENGTH)+1;
|
||||
int4store((uchar*) end,thd->thread_id);
|
||||
end+=4;
|
||||
memcpy(end,thd->scramble,SCRAMBLE_LENGTH+1);
|
||||
end+=SCRAMBLE_LENGTH +1;
|
||||
int2store(end,client_flags);
|
||||
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
|
||||
int4store((uchar*) end, thd->thread_id);
|
||||
end+= 4;
|
||||
/*
|
||||
So as check_connection is the only entry point to authorization
|
||||
procedure, scramble is set here. This gives us new scramble for
|
||||
each handshake.
|
||||
*/
|
||||
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
|
||||
/*
|
||||
Old clients does not understand long scrambles, but can ignore packet
|
||||
tail: that's why first part of the scramble is placed here, and second
|
||||
part at the end of packet.
|
||||
*/
|
||||
end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
|
||||
|
||||
int2store(end, client_flags);
|
||||
/* write server characteristics: up to 16 bytes allowed */
|
||||
end[2]=(char) default_charset_info->number;
|
||||
int2store(end+3,thd->server_status);
|
||||
bzero(end+5,13);
|
||||
end+=18;
|
||||
int2store(end+3, thd->server_status);
|
||||
bzero(end+5, 13);
|
||||
end+= 18;
|
||||
/* write scramble tail */
|
||||
end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
|
||||
SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
|
||||
|
||||
// At this point we write connection message and read reply
|
||||
if (net_write_command(net,(uchar) protocol_version, "", 0, buff,
|
||||
/* At this point we write connection message and read reply */
|
||||
if (net_write_command(net, (uchar) protocol_version, "", 0, buff,
|
||||
(uint) (end-buff)) ||
|
||||
(pkt_len= my_net_read(net)) == packet_error ||
|
||||
pkt_len < MIN_HANDSHAKE_SIZE)
|
||||
@ -723,7 +756,7 @@ check_connections(THD *thd)
|
||||
return(ER_HANDSHAKE_ERROR);
|
||||
}
|
||||
DBUG_PRINT("info", ("Reading user information over SSL layer"));
|
||||
if ((pkt_len=my_net_read(net)) == packet_error ||
|
||||
if ((pkt_len= my_net_read(net)) == packet_error ||
|
||||
pkt_len < NORMAL_HANDSHAKE_SIZE)
|
||||
{
|
||||
DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
|
||||
@ -740,13 +773,30 @@ check_connections(THD *thd)
|
||||
return(ER_HANDSHAKE_ERROR);
|
||||
}
|
||||
|
||||
user= end;
|
||||
passwd= strend(user)+1;
|
||||
db=0;
|
||||
using_password= test(passwd[0]);
|
||||
if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB)
|
||||
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
||||
opt_using_transactions)
|
||||
net->return_status= &thd->server_status;
|
||||
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
||||
|
||||
char *user= end;
|
||||
char *passwd= strend(user)+1;
|
||||
char *db= passwd;
|
||||
char db_buff[NAME_LEN+1]; // buffer to store db in utf8
|
||||
/*
|
||||
Old clients send null-terminated string as password; new clients send
|
||||
the size (1 byte) + string (not null-terminated). Hence in case of empty
|
||||
password both send '\0'.
|
||||
*/
|
||||
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
|
||||
*passwd++ : strlen(passwd);
|
||||
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
|
||||
db + passwd_len + 1 : 0;
|
||||
|
||||
/* Since 4.1 all database names are stored in utf8 */
|
||||
if (db)
|
||||
{
|
||||
db= strend(passwd)+1;
|
||||
uint32 length= copy_and_convert(db_buff, sizeof(db_buff)-1,
|
||||
system_charset_info,
|
||||
db, strlen(db),
|
||||
@ -755,71 +805,11 @@ check_connections(THD *thd)
|
||||
db= db_buff;
|
||||
}
|
||||
|
||||
/* We can get only old hash at this point */
|
||||
if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
|
||||
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
|
||||
opt_using_transactions)
|
||||
net->return_status= &thd->server_status;
|
||||
net->read_timeout=(uint) thd->variables.net_read_timeout;
|
||||
|
||||
/* Simple connect only for old clients. New clients always use secure auth */
|
||||
bool simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
||||
|
||||
/* Check user permissions. If password failure we'll get scramble back */
|
||||
if ((res=check_user(thd, COM_CONNECT, user, passwd, db, 1, simple_connect,
|
||||
simple_connect, prepared_scramble, using_password,
|
||||
&cur_priv_version,
|
||||
&cached_user)) < 0)
|
||||
{
|
||||
/* Store current used and database as they are erased with next packet */
|
||||
char tmp_user[USERNAME_LENGTH+1];
|
||||
char tmp_db[NAME_LEN+1];
|
||||
|
||||
/* If the client is old we just have to return error */
|
||||
if (simple_connect)
|
||||
return -1;
|
||||
|
||||
DBUG_PRINT("info",("password challenge"));
|
||||
|
||||
tmp_user[0]= tmp_db[0]= 0;
|
||||
if (user)
|
||||
strmake(tmp_user,user,USERNAME_LENGTH);
|
||||
if (db)
|
||||
strmake(tmp_db,db,NAME_LEN);
|
||||
|
||||
/* Write hash and encrypted scramble to client */
|
||||
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
|
||||
net_flush(net))
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* Reading packet back */
|
||||
if ((pkt_len= my_net_read(net)) == packet_error)
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* We have to get very specific packet size */
|
||||
if (pkt_len != SCRAMBLE41_LENGTH)
|
||||
{
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* Final attempt to check the user based on reply */
|
||||
if (check_user(thd,COM_CONNECT, tmp_user, (char*)net->read_pos,
|
||||
tmp_db, 1, 0, 1, prepared_scramble, using_password,
|
||||
&cur_priv_version,
|
||||
&cached_user))
|
||||
return -1;
|
||||
}
|
||||
else if (res)
|
||||
return -1; // Error sent from check_user()
|
||||
return 0;
|
||||
if (thd->user)
|
||||
x_free(thd->user);
|
||||
if (!(thd->user= my_strdup(user, MYF(0))))
|
||||
return (ER_OUT_OF_RESOURCES);
|
||||
return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true);
|
||||
}
|
||||
|
||||
|
||||
@ -876,7 +866,7 @@ pthread_handler_decl(handle_one_connection,arg)
|
||||
NET *net= &thd->net;
|
||||
thd->thread_stack= (char*) &thd;
|
||||
|
||||
if ((error=check_connections(thd)))
|
||||
if ((error=check_connection(thd)))
|
||||
{ // Wrong permissions
|
||||
if (error > 0)
|
||||
net_printf(thd,error,thd->host_or_ip);
|
||||
@ -1188,116 +1178,74 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
|
||||
case COM_CHANGE_USER:
|
||||
{
|
||||
thd->change_user();
|
||||
thd->clear_error(); // If errors from rollback
|
||||
thd->clear_error(); // if errors from rollback
|
||||
|
||||
statistic_increment(com_other,&LOCK_status);
|
||||
char *user= (char*) packet;
|
||||
statistic_increment(com_other, &LOCK_status);
|
||||
char *user= (char*) packet;
|
||||
char *passwd= strend(user)+1;
|
||||
char *db= strend(passwd)+1;
|
||||
/*
|
||||
Old clients send null-terminated string ('\0' for empty string) for
|
||||
password. New clients send the size (1 byte) + string (not null
|
||||
terminated, so also '\0' for empty string).
|
||||
*/
|
||||
char *db= passwd;
|
||||
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
|
||||
*passwd++ : strlen(passwd);
|
||||
db+= passwd_len + 1;
|
||||
/* Convert database name to utf8 */
|
||||
String convdb;
|
||||
convdb.copy(db, strlen(db), thd->variables.character_set_client,
|
||||
system_charset_info);
|
||||
db= convdb.c_ptr();
|
||||
|
||||
|
||||
|
||||
/* Small check for incomming packet */
|
||||
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
||||
{
|
||||
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Save user and privileges */
|
||||
uint save_master_access=thd->master_access;
|
||||
uint save_db_access= thd->db_access;
|
||||
uint save_db_length= thd->db_length;
|
||||
char *save_user= thd->user;
|
||||
thd->user=NULL; /* Needed for check_user to allocate new user */
|
||||
char *save_priv_user= thd->priv_user;
|
||||
char *save_db= thd->db;
|
||||
USER_CONN *save_uc= thd->user_connect;
|
||||
bool simple_connect;
|
||||
bool using_password;
|
||||
char prepared_scramble[SCRAMBLE41_LENGTH+4];/* Buffer for scramble,hash */
|
||||
char tmp_user[USERNAME_LENGTH+1];
|
||||
char tmp_db[NAME_LEN+1];
|
||||
ACL_USER* cached_user ; /* Cached user */
|
||||
uint cur_priv_version; /* Cached grant version */
|
||||
int res;
|
||||
ulong pkt_len= 0; /* Length of reply packet */
|
||||
|
||||
bzero((char*) prepared_scramble, sizeof(prepared_scramble));
|
||||
/* Small check for incomming packet */
|
||||
|
||||
if ((uint) ((uchar*) db - net->read_pos) > packet_length)
|
||||
goto restore_user_err;
|
||||
|
||||
/* Now we shall basically perform authentication again */
|
||||
|
||||
/* We can get only old hash at this point */
|
||||
if (passwd[0] && strlen(passwd)!=SCRAMBLE_LENGTH)
|
||||
goto restore_user_err;
|
||||
|
||||
cached_user= NULL;
|
||||
|
||||
/* Simple connect only for old clients. New clients always use sec. auth*/
|
||||
simple_connect=(!(thd->client_capabilities & CLIENT_SECURE_CONNECTION));
|
||||
|
||||
/* Store information if we used password. passwd will be dammaged */
|
||||
using_password=test(passwd[0]);
|
||||
|
||||
if (simple_connect) /* Restore scramble for old clients */
|
||||
memcpy(thd->scramble,thd->old_scramble,9);
|
||||
|
||||
/*
|
||||
Check user permissions. If password failure we'll get scramble back
|
||||
Do not retry if we already have sent error (result>0)
|
||||
*/
|
||||
if ((res=check_user(thd,COM_CHANGE_USER, user, passwd, db, 0,
|
||||
simple_connect, simple_connect, prepared_scramble,
|
||||
using_password, &cur_priv_version, &cached_user)) < 0)
|
||||
uint save_master_access= thd->master_access;
|
||||
uint save_db_access= thd->db_access;
|
||||
uint save_db_length= thd->db_length;
|
||||
char *save_user= thd->user;
|
||||
char *save_priv_user= thd->priv_user;
|
||||
char *save_db= thd->db;
|
||||
USER_CONN *save_uc= thd->user_connect;
|
||||
thd->user= my_strdup(user, MYF(0));
|
||||
if (!thd->user)
|
||||
{
|
||||
/* If the client is old we just have to have auth failure */
|
||||
if (simple_connect)
|
||||
goto restore_user; /* Error is already reported */
|
||||
|
||||
/* Store current used and database as they are erased with next packet */
|
||||
tmp_user[0]= tmp_db[0]= 0;
|
||||
if (user)
|
||||
strmake(tmp_user,user,USERNAME_LENGTH);
|
||||
if (db)
|
||||
strmake(tmp_db,db,NAME_LEN);
|
||||
|
||||
/* Write hash and encrypted scramble to client */
|
||||
if (my_net_write(net,prepared_scramble,SCRAMBLE41_LENGTH+4) ||
|
||||
net_flush(net))
|
||||
goto restore_user_err;
|
||||
|
||||
/* Reading packet back */
|
||||
if ((pkt_len=my_net_read(net)) == packet_error)
|
||||
goto restore_user_err;
|
||||
|
||||
/* We have to get very specific packet size */
|
||||
if (pkt_len != SCRAMBLE41_LENGTH)
|
||||
goto restore_user;
|
||||
|
||||
/* Final attempt to check the user based on reply */
|
||||
if (check_user(thd,COM_CHANGE_USER, tmp_user, (char*) net->read_pos,
|
||||
tmp_db, 0, 0, 1, prepared_scramble, using_password,
|
||||
&cur_priv_version, &cached_user))
|
||||
goto restore_user;
|
||||
thd->user= save_user;
|
||||
send_error(thd, ER_OUT_OF_RESOURCES);
|
||||
break;
|
||||
}
|
||||
else if (res)
|
||||
goto restore_user;
|
||||
|
||||
/* Finally we've authenticated new user */
|
||||
if (max_connections && save_uc)
|
||||
decrease_user_connections(save_uc);
|
||||
x_free((gptr) save_db);
|
||||
x_free((gptr) save_user);
|
||||
thd->password=using_password;
|
||||
break;
|
||||
int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false);
|
||||
|
||||
/* Bad luck we shall restore old user */
|
||||
restore_user_err:
|
||||
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
||||
|
||||
restore_user:
|
||||
x_free(thd->user);
|
||||
thd->master_access=save_master_access;
|
||||
thd->db_access=save_db_access;
|
||||
thd->db=save_db;
|
||||
thd->db_length=save_db_length;
|
||||
thd->user=save_user;
|
||||
thd->priv_user=save_priv_user;
|
||||
if (res)
|
||||
{
|
||||
/* authentification failure, we shall restore old user */
|
||||
if (res > 0)
|
||||
send_error(thd, ER_UNKNOWN_COM_ERROR);
|
||||
x_free(thd->user);
|
||||
thd->user= save_user;
|
||||
thd->priv_user= save_priv_user;
|
||||
thd->master_access= save_master_access;
|
||||
thd->db_access= save_db_access;
|
||||
thd->db= save_db;
|
||||
thd->db_length= save_db_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* we've authenticated new user */
|
||||
if (max_connections && save_uc)
|
||||
decrease_user_connections(save_uc);
|
||||
x_free((gptr) save_db);
|
||||
x_free((gptr) save_user);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif /* EMBEDDED_LIBRARY */
|
||||
@ -3247,7 +3195,7 @@ error:
|
||||
Check grants for commands which work only with one table and all other
|
||||
tables belong to subselects.
|
||||
|
||||
SYNOPSYS
|
||||
SYNOPSIS
|
||||
single_table_command_access()
|
||||
thd - Thread handler
|
||||
privilege - asked privelage
|
||||
|
Reference in New Issue
Block a user