mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
First version of new authentification procedure: now authentification is one-stage (instead of two-stage in 4.1)
For now following tasks have been done: - PASSWORD() function was rewritten. PASSWORD() now returns SHA1 hash_stage2; for new passwords user.password contains '*'hash_stage2; sql_yacc.yy also fixed; - password.c: new functions were implemented, old rolled back to 4.0 state - server code was rewritten to use new authorization algorithm (check_user(), change user, and other stuff in sql/sql_parse.cc) - client code was rewritten to use new authorization algorithm (mysql_real_connect, myslq_authenticate in sql-common/client.c) - now server barks on 45-byte-length 4.1.0 passwords and refuses 4.1.0-style authentification. Users with 4.1.0 passwords are blocked (sql/sql_acl.cc) - mysqladmin.c was fixed to work correctly with new passwords Tests for 4.0-4.1.1, 4.1.1-4.1.1 (with or without db/password) logons was performed; mysqladmin also was tested. Additional check are nevertheless necessary. BitKeeper/etc/ignore: Added start_mysqld.sh mysys/main.cc to the ignore list client/mysqladmin.c: fixed with new password api include/mysql.h: So as scramble_323 accepts only null-terminated message, two scramble buffs are necessary. gotta be fixed include/mysql_com.h: new constants and password.c api changes libmysql/libmysql.c: mysql_change_user rewritten to work with new password api scripts/mysql_create_system_tables.sh: fixed 'Password' column length to 41 scripts/mysql_fix_privilege_tables.sql: fixed 'Password' column length to 41 sql-common/client.c: mysql_real_connect rewritten to support new handshake procedure sql/item_strfunc.cc: Item_func_password and Item_func_old_password rewritten with new password api sql/item_strfunc.h: bit commented, numbers replaced with #defined constants sql/mysql_priv.h: removed unnecessary declaration as now all constants defined is in mysql_com.h sql/mysqld.cc: scramble initialization moved to sql_parce.cc:check_connection sql/password.c: All 4.1 functions were rolled back to 4.0 with attempt to save all possible 4.0-4.1 changes. Names for 4.0 functions were suffixed with '_323' Functions for new handshake were added. sql/slave.cc: Fixed to new constant; Bug #766 remains to be fixed sql/slave.h: fixed to new constant; Buf #766 remains to be fixed sql/sql_acl.cc: rewritten to support new passwords (41 byte-long) and password api sql/sql_acl.h: ditto sql/sql_class.cc: initialization for new members added sql/sql_class.h: same thing as in struct mysql - scramble is used for new family of functions, scramble_323 - for old sql/sql_parse.cc: check_connections was renamed to check_connection as this name reflects better what this function does authorization part of check_connection was rewritten check_user was rewritten with new password and acl api new function 'authenticate', which optionally re-request scramble from client was added fixed some typos COM_CHANGE_USER piece of dipsatch_command() was rewritten sql/sql_repl.h: HASH_PASSWORD_LENGTH replaced with SCRAMBLED_PASSWORD_CHAR_LENGTH bug #766 remains sql/sql_yacc.yy: Two-argument form of PASSWORD() was removed PASSWORD() function was fixed with new password api. BitKeeper/etc/logging_ok: Logging to logging@openlogging.org accepted
This commit is contained in:
parent
b871e549ee
commit
dbb088b034
23 changed files with 1186 additions and 1505 deletions
|
@ -622,3 +622,5 @@ vio/test-ssl
|
|||
vio/test-sslclient
|
||||
vio/test-sslserver
|
||||
vio/viotest-ssl
|
||||
start_mysqld.sh
|
||||
mysys/main.cc
|
||||
|
|
|
@ -51,6 +51,7 @@ jcole@sarvik.tfr.cafe.ee
|
|||
jcole@tetra.spaceapes.com
|
||||
jorge@linux.jorge.mysql.com
|
||||
kaj@work.mysql.com
|
||||
kostja@oak.local
|
||||
lenz@kallisto.mysql.com
|
||||
lenz@mysql.com
|
||||
miguel@hegel.(none)
|
||||
|
|
|
@ -769,9 +769,12 @@ static int execute_commands(MYSQL *mysql,int argc, char **argv)
|
|||
return 1;
|
||||
}
|
||||
if (argv[1][0])
|
||||
make_scrambled_password(crypted_pw,argv[1],
|
||||
(find_type(argv[0], &command_typelib, 2) ==
|
||||
ADMIN_OLD_PASSWORD), &rand_st);
|
||||
{
|
||||
if (find_type(argv[0], &command_typelib, 2) == ADMIN_OLD_PASSWORD)
|
||||
make_scrambled_password_323(crypted_pw, argv[1]);
|
||||
else
|
||||
make_scrambled_password(crypted_pw, argv[1]);
|
||||
}
|
||||
else
|
||||
crypted_pw[0]=0; /* No password */
|
||||
sprintf(buff,"set password='%s',sql_log_off=0",crypted_pw);
|
||||
|
|
|
@ -227,7 +227,9 @@ typedef struct st_mysql
|
|||
enum mysql_status status;
|
||||
my_bool free_me; /* If free in mysql_close */
|
||||
my_bool reconnect; /* set to 1 if automatic reconnect */
|
||||
char scramble_buff[21]; /* New protocol requires longer scramble*/
|
||||
|
||||
char scramble[SCRAMBLE_LENGTH+1]; /* for new servers */
|
||||
char scramble_323[SCRAMBLE_LENGTH_323+1]; /* for old servers */
|
||||
|
||||
/*
|
||||
Set if this is the original connection, not a master or a slave we have
|
||||
|
|
|
@ -48,8 +48,15 @@ enum enum_server_command
|
|||
};
|
||||
|
||||
|
||||
#define SCRAMBLE_LENGTH 8
|
||||
#define SCRAMBLE41_LENGTH 20
|
||||
/*
|
||||
Length of random string sent by server on handshake; this is also length of
|
||||
obfuscated password, recieved from client
|
||||
*/
|
||||
#define SCRAMBLE_LENGTH 20
|
||||
#define SCRAMBLE_LENGTH_323 8
|
||||
/* length of password stored in the db: new passwords are preceeded with '*' */
|
||||
#define SCRAMBLED_PASSWORD_CHAR_LENGTH (SCRAMBLE_LENGTH*2+1)
|
||||
#define SCRAMBLED_PASSWORD_CHAR_LENGTH_323 (SCRAMBLE_LENGTH_323*2)
|
||||
|
||||
|
||||
#define NOT_NULL_FLAG 1 /* Field can't be NULL */
|
||||
|
@ -300,31 +307,35 @@ extern "C" {
|
|||
extern unsigned long max_allowed_packet;
|
||||
extern unsigned long net_buffer_length;
|
||||
|
||||
void randominit(struct rand_struct *,unsigned long seed1,
|
||||
unsigned long seed2);
|
||||
/*
|
||||
These functions are used for authentication by client and server and
|
||||
implemented in sql/password.c
|
||||
*/
|
||||
|
||||
void randominit(struct rand_struct *, unsigned long seed1,
|
||||
unsigned long seed2);
|
||||
double my_rnd(struct rand_struct *);
|
||||
void make_scrambled_password(char *to,const char *password,
|
||||
my_bool force_old_scramble,struct rand_struct *rand_st);
|
||||
int get_password_length(my_bool force_old_scramble);
|
||||
char get_password_version(const char* password);
|
||||
void create_random_string(int length,struct rand_struct *rand_st,char* target);
|
||||
my_bool validate_password(const char* password, const char* message,
|
||||
unsigned long* salt);
|
||||
void password_hash_stage1(char *to, const char *password);
|
||||
void password_hash_stage2(char *to,const char *salt);
|
||||
void password_crypt(const char* from,char* to, const char* password,int length);
|
||||
void get_hash_and_password(unsigned long* salt, unsigned char pversion,char* hash,
|
||||
unsigned char* bin_password);
|
||||
void get_salt_from_password(unsigned long *res,const char *password);
|
||||
void create_key_from_old_password(const char* password,char* key);
|
||||
void make_password_from_salt(char *to, unsigned long *hash_res,
|
||||
unsigned char password_version);
|
||||
char *scramble(char *to,const char *message,const char *password,
|
||||
my_bool old_ver);
|
||||
my_bool check_scramble(const char *, const char *message,
|
||||
unsigned long *salt,my_bool old_ver);
|
||||
void create_random_string(char *to, uint length, struct rand_struct *rand_st);
|
||||
|
||||
void hash_password(ulong *to, const char *password);
|
||||
void make_scrambled_password_323(char *to, const char *password);
|
||||
char *scramble_323(char *to, const char *message, const char *password,
|
||||
my_bool old_ver);
|
||||
my_bool check_scramble_323(const char *, const char *message,
|
||||
unsigned long *salt, my_bool old_ver);
|
||||
void get_salt_from_password_323(unsigned long *res, const char *password);
|
||||
void make_password_from_salt_323(char *to, const unsigned long *salt);
|
||||
|
||||
void make_scrambled_password(char *to, const char *password);
|
||||
char *scramble(char *to, const char *message, const char *password);
|
||||
my_bool check_scramble(const char *reply, const char *message,
|
||||
const unsigned char *hash_stage2);
|
||||
void get_salt_from_password(unsigned char *res, const char *password);
|
||||
void make_password_from_salt(char *to, const unsigned char *hash_stage2);
|
||||
|
||||
/* end of password.c */
|
||||
|
||||
char *get_tty_password(char *opt_message);
|
||||
void hash_password(unsigned long *result, const char *password);
|
||||
const char *mysql_errno_to_sqlstate(unsigned int mysql_errno);
|
||||
|
||||
/* Some other useful functions */
|
||||
|
|
|
@ -616,41 +616,51 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
|
|||
/* Store user into the buffer */
|
||||
end=strmov(end,user)+1;
|
||||
|
||||
/*
|
||||
We always start with old type handshake the only difference is message sent
|
||||
If server handles secure connection type we'll not send the real scramble
|
||||
*/
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
/* write scrambled password according to server capabilities */
|
||||
if (passwd[0])
|
||||
{
|
||||
if (passwd[0])
|
||||
{
|
||||
/* Prepare false scramble */
|
||||
bfill(end, SCRAMBLE_LENGTH, 'x');
|
||||
end+=SCRAMBLE_LENGTH;
|
||||
*end=0;
|
||||
|
||||
}
|
||||
else /* For empty password */
|
||||
*end=0; /* zero length scramble */
|
||||
/* Write NULL-terminated scrambled password: */
|
||||
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
|
||||
scramble(end, mysql->scramble, passwd) :
|
||||
scramble_323(end, mysql->scramble_323, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Real scramble is only sent to old servers. This can be blocked
|
||||
by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
|
||||
*/
|
||||
end=scramble(end, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
*end= '\0'; // empty password
|
||||
/* Add database if needed */
|
||||
end=strmov(end+1,db ? db : "");
|
||||
|
||||
/* Write authentication package */
|
||||
simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1);
|
||||
|
||||
if (mysql_autenticate(mysql, passwd))
|
||||
NET *net= &mysql->net;
|
||||
ulong pkt_length= net_safe_read(mysql);
|
||||
|
||||
if (pkt_length == packet_error)
|
||||
goto error;
|
||||
|
||||
if (net->read_pos[0] == mysql->scramble_323[0] &&
|
||||
pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
|
||||
mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
/*
|
||||
By sending this very specific reply server asks us to send scrambled
|
||||
password in old format. The reply contains scramble_323.
|
||||
*/
|
||||
scramble_323(buff, mysql->scramble_323, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->sqlstate, unknown_sqlstate);
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
/* Read what server thinks about out new auth message report */
|
||||
if (net_safe_read(mysql) == packet_error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Free old connect information */
|
||||
my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR));
|
||||
my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR));
|
||||
|
|
|
@ -108,7 +108,7 @@ then
|
|||
c_u="$c_u CREATE TABLE user ("
|
||||
c_u="$c_u Host char(60) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u User char(16) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u Password char(45) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u Password char(41) binary DEFAULT '' NOT NULL,"
|
||||
c_u="$c_u Select_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
|
||||
c_u="$c_u Insert_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
|
||||
c_u="$c_u Update_priv enum('N','Y') DEFAULT 'N' NOT NULL,"
|
||||
|
|
|
@ -4,7 +4,7 @@ ALTER TABLE host type=MyISAM;
|
|||
ALTER TABLE func type=MyISAM;
|
||||
ALTER TABLE columns_priv type=MyISAM;
|
||||
ALTER TABLE tables_priv type=MyISAM;
|
||||
ALTER TABLE user change Password Password char(45) not null;
|
||||
ALTER TABLE user change Password Password char(41) not null;
|
||||
ALTER TABLE user add File_priv enum('N','Y') NOT NULL;
|
||||
CREATE TABLE IF NOT EXISTS func (
|
||||
name char(64) DEFAULT '' NOT NULL,
|
||||
|
|
|
@ -1334,76 +1334,6 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused)))
|
|||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
|
||||
/*
|
||||
Handle password authentication
|
||||
*/
|
||||
|
||||
my_bool mysql_autenticate(MYSQL *mysql, const char *passwd)
|
||||
{
|
||||
ulong pkt_length;
|
||||
NET *net= &mysql->net;
|
||||
char buff[SCRAMBLE41_LENGTH];
|
||||
char password_hash[SCRAMBLE41_LENGTH]; /* Used for storage of stage1 hash */
|
||||
|
||||
/* We shall only query server if it expect us to do so */
|
||||
if ((pkt_length=net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
/*
|
||||
This should always happen with new server unless empty password
|
||||
OK/Error packets have zero as the first char
|
||||
*/
|
||||
if (pkt_length == 24 && net->read_pos[0])
|
||||
{
|
||||
/* Old passwords will have '*' at the first byte of hash */
|
||||
if (net->read_pos[0] != '*')
|
||||
{
|
||||
/* Build full password hash as it is required to decode scramble */
|
||||
password_hash_stage1(buff, passwd);
|
||||
/* Store copy as we'll need it later */
|
||||
memcpy(password_hash,buff,SCRAMBLE41_LENGTH);
|
||||
/* Finally hash complete password using hash we got from server */
|
||||
password_hash_stage2(password_hash,(const char*) net->read_pos);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
|
||||
password_hash, SCRAMBLE41_LENGTH);
|
||||
mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
|
||||
/* Encode scramble with password. Recycle buffer */
|
||||
password_crypt(mysql->scramble_buff,buff,buff,SCRAMBLE41_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Create password to decode scramble */
|
||||
create_key_from_old_password(passwd,password_hash);
|
||||
/* Decypt and store scramble 4 = hash for stage2 */
|
||||
password_crypt((const char*) net->read_pos+4,mysql->scramble_buff,
|
||||
password_hash, SCRAMBLE41_LENGTH);
|
||||
mysql->scramble_buff[SCRAMBLE41_LENGTH]=0;
|
||||
/* Finally scramble decoded scramble with password */
|
||||
scramble(buff, mysql->scramble_buff, passwd,0);
|
||||
}
|
||||
/* Write second package of authentication */
|
||||
if (my_net_write(net,buff,SCRAMBLE41_LENGTH) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->sqlstate, unknown_sqlstate);
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
/* Read what server thinks about out new auth message report */
|
||||
if (net_safe_read(mysql) == packet_error)
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Note that the mysql argument must be initialized with mysql_init()
|
||||
before calling mysql_real_connect !
|
||||
|
@ -1481,7 +1411,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
|
||||
|
||||
/*
|
||||
Grab a socket and connect it to the server
|
||||
Part 0: Grab a socket and connect it to the server
|
||||
*/
|
||||
#if defined(HAVE_SMEM)
|
||||
if ((!mysql->options.protocol ||
|
||||
|
@ -1682,6 +1612,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
Part 1: Connection established, read and parse first packet
|
||||
*/
|
||||
|
||||
if ((pkt_length=net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
|
@ -1702,8 +1637,14 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
end=strend((char*) net->read_pos+1);
|
||||
mysql->thread_id=uint4korr(end+1);
|
||||
end+=5;
|
||||
strmake(mysql->scramble_buff,end,8);
|
||||
end+=9;
|
||||
/*
|
||||
Scramble is split into two parts because old clients does not understand
|
||||
long scrambles; here goes the first part.
|
||||
*/
|
||||
strmake(mysql->scramble_323, end, SCRAMBLE_LENGTH_323);
|
||||
end+= SCRAMBLE_LENGTH_323+1;
|
||||
memcpy(mysql->scramble, mysql->scramble_323, SCRAMBLE_LENGTH_323);
|
||||
|
||||
if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
|
||||
mysql->server_capabilities=uint2korr(end);
|
||||
if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
|
||||
|
@ -1712,6 +1653,13 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
mysql->server_language=end[2];
|
||||
mysql->server_status=uint2korr(end+3);
|
||||
}
|
||||
end+= 18;
|
||||
if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 -
|
||||
(char *) net->read_pos))
|
||||
strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
|
||||
SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
|
||||
else
|
||||
mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
|
||||
|
||||
/* Set character set */
|
||||
if ((charset_name=mysql->options.charset_name))
|
||||
|
@ -1783,9 +1731,12 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
mysql->unix_socket=0;
|
||||
strmov(mysql->server_version,(char*) net->read_pos+1);
|
||||
mysql->port=port;
|
||||
client_flag|=mysql->options.client_flag;
|
||||
|
||||
/* Send client information for access check */
|
||||
/*
|
||||
Part 2: format and send client info to the server for access check
|
||||
*/
|
||||
|
||||
client_flag|=mysql->options.client_flag;
|
||||
client_flag|=CLIENT_CAPABILITIES;
|
||||
if (client_flag & CLIENT_MULTI_QUERIES)
|
||||
client_flag|= CLIENT_MULTI_RESULTS;
|
||||
|
@ -1881,35 +1832,18 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
#include "_cust_libmysql.h"
|
||||
#endif
|
||||
DBUG_PRINT("info",("user: %s",end));
|
||||
/*
|
||||
We always start with old type handshake the only difference is message sent
|
||||
If server handles secure connection type we'll not send the real scramble
|
||||
*/
|
||||
if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
end= strend(end) + 1;
|
||||
if (passwd[0])
|
||||
{
|
||||
if (passwd[0])
|
||||
{
|
||||
/* Prepare false scramble */
|
||||
end=strend(end)+1;
|
||||
bfill(end, SCRAMBLE_LENGTH, 'x');
|
||||
end+=SCRAMBLE_LENGTH;
|
||||
*end=0;
|
||||
}
|
||||
else /* For empty password*/
|
||||
{
|
||||
end=strend(end)+1;
|
||||
*end=0; /* Store zero length scramble */
|
||||
}
|
||||
/* Write NULL-terminated scrambled password: */
|
||||
end= mysql->server_capabilities & CLIENT_SECURE_CONNECTION ?
|
||||
scramble(end, mysql->scramble, passwd) :
|
||||
scramble_323(end, mysql->scramble_323, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
Real scramble is only sent to old servers. This can be blocked
|
||||
by calling mysql_options(MYSQL *, MYSQL_SECURE_CONNECT, (char*) &1);
|
||||
*/
|
||||
end=scramble(strend(end)+1, mysql->scramble_buff, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
}
|
||||
*end= '\0'; /* empty password */
|
||||
|
||||
/* Add database if needed */
|
||||
if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
|
||||
{
|
||||
|
@ -1925,10 +1859,38 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
|
|||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
Part 3: Authorization data's been sent. Now server can reply with
|
||||
OK-packet, or re-request scrambled password.
|
||||
*/
|
||||
|
||||
if (mysql_autenticate(mysql, passwd))
|
||||
if ((pkt_length=net_safe_read(mysql)) == packet_error)
|
||||
goto error;
|
||||
|
||||
if (net->read_pos[0] == mysql->scramble_323[0] &&
|
||||
pkt_length == SCRAMBLE_LENGTH_323 + 1 &&
|
||||
mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
|
||||
{
|
||||
/*
|
||||
By sending this very specific reply server asks us to send scrambled
|
||||
password in old format. The reply contains scramble_323.
|
||||
*/
|
||||
scramble_323(buff, mysql->scramble_323, passwd,
|
||||
(my_bool) (mysql->protocol_version == 9));
|
||||
if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net))
|
||||
{
|
||||
net->last_errno= CR_SERVER_LOST;
|
||||
strmov(net->sqlstate, unknown_sqlstate);
|
||||
strmov(net->last_error,ER(net->last_errno));
|
||||
goto error;
|
||||
}
|
||||
/* Read what server thinks about out new auth message report */
|
||||
if (net_safe_read(mysql) == packet_error)
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
if (client_flag & CLIENT_COMPRESS) /* We will use compression */
|
||||
net->compress=1;
|
||||
|
||||
|
|
|
@ -1346,95 +1346,33 @@ void Item_func_trim::fix_length_and_dec()
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void Item_func_password::fix_length_and_dec()
|
||||
{
|
||||
/*
|
||||
If PASSWORD() was called with only one argument, it depends on a random
|
||||
number so we need to save this random number into the binary log.
|
||||
If called with two arguments, it is repeatable.
|
||||
*/
|
||||
if (arg_count == 1)
|
||||
{
|
||||
THD *thd= current_thd;
|
||||
thd->rand_used= 1;
|
||||
thd->rand_saved_seed1= thd->rand.seed1;
|
||||
thd->rand_saved_seed2= thd->rand.seed2;
|
||||
}
|
||||
max_length= get_password_length(use_old_passwords);
|
||||
}
|
||||
|
||||
/*
|
||||
Password() function has 2 arguments. Second argument can be used
|
||||
to make results repeatable
|
||||
*/
|
||||
/* Item_func_password */
|
||||
|
||||
String *Item_func_password::val_str(String *str)
|
||||
{
|
||||
struct rand_struct rand_st; // local structure for 2 param version
|
||||
ulong seed=0; // seed to initialise random generator to
|
||||
|
||||
String *res =args[0]->val_str(str);
|
||||
if ((null_value=args[0]->null_value))
|
||||
return 0;
|
||||
|
||||
if (arg_count == 1)
|
||||
{
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
|
||||
¤t_thd->rand);
|
||||
str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
|
||||
return str;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We'll need the buffer to get second parameter */
|
||||
char key_buff[80];
|
||||
String tmp_key_value(key_buff, sizeof(key_buff), system_charset_info);
|
||||
String *key =args[1]->val_str(&tmp_key_value);
|
||||
|
||||
/* Check second argument for NULL value. First one is already checked */
|
||||
if ((null_value=args[1]->null_value))
|
||||
return 0;
|
||||
|
||||
/* This shall be done after checking for null for proper results */
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
|
||||
/* Generate the seed first this allows to avoid double allocation */
|
||||
char* seed_ptr=key->c_ptr();
|
||||
while (*seed_ptr)
|
||||
{
|
||||
seed=(seed*211+*seed_ptr) & 0xffffffffL; /* Use simple hashing */
|
||||
seed_ptr++;
|
||||
}
|
||||
|
||||
/* Use constants which allow nice random values even with small seed */
|
||||
randominit(&rand_st,
|
||||
(ulong) ((ulonglong) seed*111111+33333333L) & (ulong) 0xffffffff,
|
||||
(ulong) ((ulonglong) seed*1111+55555555L) & (ulong) 0xffffffff);
|
||||
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),use_old_passwords,
|
||||
&rand_st);
|
||||
str->set(tmp_value,get_password_length(use_old_passwords),res->charset());
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
String *Item_func_old_password::val_str(String *str)
|
||||
{
|
||||
String *res =args[0]->val_str(str);
|
||||
String *res= args[0]->val_str(str);
|
||||
if ((null_value=args[0]->null_value))
|
||||
return 0;
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
make_scrambled_password(tmp_value,res->c_ptr(),1,¤t_thd->rand);
|
||||
str->set(tmp_value,16,res->charset());
|
||||
make_scrambled_password(tmp_value, res->c_ptr());
|
||||
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset());
|
||||
return str;
|
||||
}
|
||||
|
||||
/* Item_func_old_password */
|
||||
|
||||
String *Item_func_old_password::val_str(String *str)
|
||||
{
|
||||
String *res= args[0]->val_str(str);
|
||||
if ((null_value=args[0]->null_value))
|
||||
return 0;
|
||||
if (res->length() == 0)
|
||||
return &empty_string;
|
||||
make_scrambled_password_323(tmp_value, res->c_ptr());
|
||||
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset());
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
#define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
|
||||
|
|
|
@ -254,30 +254,44 @@ public:
|
|||
};
|
||||
|
||||
|
||||
/*
|
||||
Item_func_password -- new (4.1.1) PASSWORD() function implementation.
|
||||
Returns strcat('*', octet2hex(sha1(sha1(password)))). '*' stands for new
|
||||
password format, sha1(sha1(password) is so-called hash_stage2 value.
|
||||
Length of returned string is always 41 byte. To find out how entire
|
||||
authentification procedure works, see comments in password.c.
|
||||
*/
|
||||
|
||||
class Item_func_password :public Item_str_func
|
||||
{
|
||||
char tmp_value[64]; /* This should be enough for new password format */
|
||||
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
|
||||
public:
|
||||
Item_func_password(Item *a) :Item_str_func(a) {}
|
||||
Item_func_password(Item *a, Item *b) :Item_str_func(a,b) {}
|
||||
String *val_str(String *);
|
||||
void fix_length_and_dec();
|
||||
String *val_str(String *str);
|
||||
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }
|
||||
const char *func_name() const { return "password"; }
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
Item_func_old_password -- PASSWORD() implementation used in MySQL 3.21 - 4.0
|
||||
compatibility mode. This item is created in sql_yacc.yy when
|
||||
'use_old_passwords' session variable is set, and to handle OLD_PASSWORD()
|
||||
function.
|
||||
*/
|
||||
|
||||
class Item_func_old_password :public Item_str_func
|
||||
{
|
||||
char tmp_value[17]; /* old password length +1 */
|
||||
char tmp_value[SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1];
|
||||
public:
|
||||
Item_func_old_password(Item *a) :Item_str_func(a) {}
|
||||
String *val_str(String *);
|
||||
void fix_length_and_dec() { max_length = get_password_length(1); }
|
||||
String *val_str(String *str);
|
||||
void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; }
|
||||
const char *func_name() const { return "old_password"; }
|
||||
unsigned int size_of() { return sizeof(*this);}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Item_func_des_encrypt :public Item_str_func
|
||||
{
|
||||
String tmp_value;
|
||||
|
|
|
@ -74,9 +74,6 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
|
|||
****************************************************************************/
|
||||
|
||||
#define ACL_CACHE_SIZE 256
|
||||
/* Password lengh for 4.1 version previous versions had 16 bytes password hash */
|
||||
#define HASH_PASSWORD_LENGTH 45
|
||||
#define HASH_OLD_PASSWORD_LENGTH 16
|
||||
#define HOST_CACHE_SIZE 128
|
||||
#define MAX_ACCEPT_RETRY 10 // Test accept this many times
|
||||
#define MAX_FIELDS_BEFORE_HASH 32
|
||||
|
|
|
@ -2743,12 +2743,6 @@ static void create_new_thread(THD *thd)
|
|||
if (thread_count-delayed_insert_threads > max_used_connections)
|
||||
max_used_connections=thread_count-delayed_insert_threads;
|
||||
thd->thread_id=thread_id++;
|
||||
for (uint i=0; i < 8 ; i++) // Generate password teststring
|
||||
thd->scramble[i]= (char) (my_rnd(&sql_rand)*94+33);
|
||||
thd->scramble[8]=0;
|
||||
// Back it up as old clients may need it
|
||||
memcpy(thd->old_scramble,thd->scramble,9);
|
||||
|
||||
|
||||
thd->real_id=pthread_self(); // Keep purify happy
|
||||
|
||||
|
|
918
sql/password.c
918
sql/password.c
File diff suppressed because it is too large
Load diff
|
@ -1459,7 +1459,7 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
|
|||
if (master_user)
|
||||
strmake(mi->user, master_user, sizeof(mi->user) - 1);
|
||||
if (master_password)
|
||||
strmake(mi->password, master_password, HASH_PASSWORD_LENGTH);
|
||||
strmake(mi->password, master_password, SCRAMBLED_PASSWORD_CHAR_LENGTH);
|
||||
mi->port = master_port;
|
||||
mi->connect_retry = master_connect_retry;
|
||||
}
|
||||
|
@ -1483,8 +1483,8 @@ int init_master_info(MASTER_INFO* mi, const char* master_info_fname,
|
|||
master_host) ||
|
||||
init_strvar_from_file(mi->user, sizeof(mi->user), &mi->file,
|
||||
master_user) ||
|
||||
init_strvar_from_file(mi->password, HASH_PASSWORD_LENGTH+1, &mi->file,
|
||||
master_password) ||
|
||||
init_strvar_from_file(mi->password, SCRAMBLED_PASSWORD_CHAR_LENGTH+1,
|
||||
&mi->file, master_password) ||
|
||||
init_intvar_from_file(&port, &mi->file, master_port) ||
|
||||
init_intvar_from_file(&connect_retry, &mi->file,
|
||||
master_connect_retry))
|
||||
|
|
|
@ -292,7 +292,7 @@ typedef struct st_master_info
|
|||
/* the variables below are needed because we can change masters on the fly */
|
||||
char host[HOSTNAME_LENGTH+1];
|
||||
char user[USERNAME_LENGTH+1];
|
||||
char password[HASH_PASSWORD_LENGTH+1];
|
||||
char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
|
||||
pthread_mutex_t data_lock,run_lock;
|
||||
pthread_cond_t data_cond,start_cond,stop_cond;
|
||||
THD *io_thd;
|
||||
|
|
663
sql/sql_acl.cc
663
sql/sql_acl.cc
|
@ -68,11 +68,36 @@ static ulong get_sort(uint count,...);
|
|||
static void init_check_host(void);
|
||||
static ACL_USER *find_acl_user(const char *host, const char *user);
|
||||
static bool update_user_table(THD *thd, const char *host, const char *user,
|
||||
const char *new_password);
|
||||
const char *new_password, uint new_password_len);
|
||||
static void update_hostname(acl_host_and_ip *host, const char *hostname);
|
||||
static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
|
||||
const char *ip);
|
||||
|
||||
/*
|
||||
Convert scrambled password to binary form, according to scramble type,
|
||||
Binary form is stored in user.salt.
|
||||
*/
|
||||
|
||||
static
|
||||
void
|
||||
set_user_salt(ACL_USER *acl_user, const char *password, uint password_len)
|
||||
{
|
||||
if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH)
|
||||
{
|
||||
get_salt_from_password(acl_user->salt, password);
|
||||
acl_user->salt_len= SCRAMBLE_LENGTH;
|
||||
}
|
||||
else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323
|
||||
|| password_len == 8 && protocol_version == 9)
|
||||
{
|
||||
get_salt_from_password_323((ulong *) acl_user->salt, password);
|
||||
acl_user->salt_len= password_len/2;
|
||||
}
|
||||
else
|
||||
acl_user->salt_len= 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Read grant privileges from the privilege tables in the 'mysql' database.
|
||||
|
||||
|
@ -175,16 +200,19 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
|
|||
if (table->field[2]->field_length == 8 &&
|
||||
protocol_version == PROTOCOL_VERSION)
|
||||
{
|
||||
sql_print_error(
|
||||
"Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */
|
||||
sql_print_error("Old 'user' table. "
|
||||
"(Check README or the Reference manual). "
|
||||
"Continuing --old-protocol"); /* purecov: tested */
|
||||
protocol_version=9; /* purecov: tested */
|
||||
}
|
||||
|
||||
DBUG_PRINT("info",("user table fields: %d, password length: %d",
|
||||
table->fields, table->field[2]->field_length));
|
||||
if (table->field[2]->field_length < 45 && !use_old_passwords)
|
||||
if (table->field[2]->field_length < 41 && !use_old_passwords)
|
||||
{
|
||||
sql_print_error("mysql.user table is not updated to new password format; Disabling new password usage until mysql_fix_privilege_tables is run");
|
||||
sql_print_error("mysql.user table is not updated to new password format; "
|
||||
"Disabling new password usage until "
|
||||
"mysql_fix_privilege_tables is run");
|
||||
use_old_passwords= 1;
|
||||
}
|
||||
|
||||
|
@ -192,83 +220,88 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
|
|||
while (!(read_record_info.read_record(&read_record_info)))
|
||||
{
|
||||
ACL_USER user;
|
||||
uint length=0;
|
||||
update_hostname(&user.host,get_field(&mem, table->field[0]));
|
||||
user.user=get_field(&mem, table->field[1]);
|
||||
user.password=get_field(&mem, table->field[2]);
|
||||
if (user.password && (length=(uint) strlen(user.password)) == 8 &&
|
||||
protocol_version == PROTOCOL_VERSION)
|
||||
update_hostname(&user.host, get_field(&mem, table->field[0]));
|
||||
user.user= get_field(&mem, table->field[1]);
|
||||
const char *password= get_field(&mem, table->field[2]);
|
||||
uint password_len= password ? strlen(password) : 0;
|
||||
set_user_salt(&user, password, password_len);
|
||||
if (user.salt_len == 0 && password_len != 0)
|
||||
{
|
||||
sql_print_error(
|
||||
"Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)",
|
||||
user.user ? user.user : ""); /* purecov: tested */
|
||||
switch (password_len) {
|
||||
case 8: /* 3.20: to be removed */
|
||||
sql_print_error("Found old style password for user '%s'. "
|
||||
"Ignoring user. (You may want to restart mysqld "
|
||||
"using --old-protocol) ",
|
||||
user.user ? user.user : "");
|
||||
break;
|
||||
case 45: /* 4.1: to be removed */
|
||||
sql_print_error("Found 4.1 style password for user '%s'. "
|
||||
"Ignoring user. "
|
||||
"You should change password for this user.",
|
||||
user.user ? user.user : "");
|
||||
break;
|
||||
default:
|
||||
sql_print_error("Found invalid password for user: '%s@%s'; "
|
||||
"Ignoring user", user.user ? user.user : "",
|
||||
user.host.hostname ? user.host.hostname : "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /* non empty and not short passwords */
|
||||
else // password is correct
|
||||
{
|
||||
user.pversion=get_password_version(user.password);
|
||||
/* Only passwords of specific lengths depending on version are allowed */
|
||||
if ( (!user.pversion && length % 8) || (user.pversion && length!=45 ))
|
||||
user.access= get_access(table,3) & GLOBAL_ACLS;
|
||||
user.sort= get_sort(2,user.host.hostname,user.user);
|
||||
user.hostname_length= (user.host.hostname ?
|
||||
(uint) strlen(user.host.hostname) : 0);
|
||||
if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
|
||||
{
|
||||
sql_print_error(
|
||||
"Found invalid password for user: '%s@%s'; Ignoring user",
|
||||
user.user ? user.user : "",
|
||||
user.host.hostname ? user.host.hostname : ""); /* purecov: tested */
|
||||
continue; /* purecov: tested */
|
||||
char *ssl_type=get_field(&mem, table->field[24]);
|
||||
if (!ssl_type)
|
||||
user.ssl_type=SSL_TYPE_NONE;
|
||||
else if (!strcmp(ssl_type, "ANY"))
|
||||
user.ssl_type=SSL_TYPE_ANY;
|
||||
else if (!strcmp(ssl_type, "X509"))
|
||||
user.ssl_type=SSL_TYPE_X509;
|
||||
else /* !strcmp(ssl_type, "SPECIFIED") */
|
||||
user.ssl_type=SSL_TYPE_SPECIFIED;
|
||||
|
||||
user.ssl_cipher= get_field(&mem, table->field[25]);
|
||||
user.x509_issuer= get_field(&mem, table->field[26]);
|
||||
user.x509_subject= get_field(&mem, table->field[27]);
|
||||
|
||||
char *ptr = get_field(&mem, table->field[28]);
|
||||
user.user_resource.questions=atoi(ptr);
|
||||
ptr = get_field(&mem, table->field[29]);
|
||||
user.user_resource.updates=atoi(ptr);
|
||||
ptr = get_field(&mem, table->field[30]);
|
||||
user.user_resource.connections=atoi(ptr);
|
||||
if (user.user_resource.questions || user.user_resource.updates ||
|
||||
user.user_resource.connections)
|
||||
mqh_used=1;
|
||||
}
|
||||
}
|
||||
get_salt_from_password(user.salt,user.password);
|
||||
user.access=get_access(table,3) & GLOBAL_ACLS;
|
||||
user.sort=get_sort(2,user.host.hostname,user.user);
|
||||
user.hostname_length= (user.host.hostname ?
|
||||
(uint) strlen(user.host.hostname) : 0);
|
||||
if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */
|
||||
{
|
||||
char *ssl_type=get_field(&mem, table->field[24]);
|
||||
if (!ssl_type)
|
||||
user.ssl_type=SSL_TYPE_NONE;
|
||||
else if (!strcmp(ssl_type, "ANY"))
|
||||
user.ssl_type=SSL_TYPE_ANY;
|
||||
else if (!strcmp(ssl_type, "X509"))
|
||||
user.ssl_type=SSL_TYPE_X509;
|
||||
else /* !strcmp(ssl_type, "SPECIFIED") */
|
||||
user.ssl_type=SSL_TYPE_SPECIFIED;
|
||||
|
||||
user.ssl_cipher= get_field(&mem, table->field[25]);
|
||||
user.x509_issuer= get_field(&mem, table->field[26]);
|
||||
user.x509_subject= get_field(&mem, table->field[27]);
|
||||
|
||||
char *ptr = get_field(&mem, table->field[28]);
|
||||
user.user_resource.questions=atoi(ptr);
|
||||
ptr = get_field(&mem, table->field[29]);
|
||||
user.user_resource.updates=atoi(ptr);
|
||||
ptr = get_field(&mem, table->field[30]);
|
||||
user.user_resource.connections=atoi(ptr);
|
||||
if (user.user_resource.questions || user.user_resource.updates ||
|
||||
user.user_resource.connections)
|
||||
mqh_used=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.ssl_type=SSL_TYPE_NONE;
|
||||
bzero(&(user.user_resource),sizeof(user.user_resource));
|
||||
else
|
||||
{
|
||||
user.ssl_type=SSL_TYPE_NONE;
|
||||
bzero(&(user.user_resource),sizeof(user.user_resource));
|
||||
#ifndef TO_BE_REMOVED
|
||||
if (table->fields <= 13)
|
||||
{ // Without grant
|
||||
if (user.access & CREATE_ACL)
|
||||
user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
|
||||
}
|
||||
/* Convert old privileges */
|
||||
user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
|
||||
if (user.access & FILE_ACL)
|
||||
user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
|
||||
if (user.access & PROCESS_ACL)
|
||||
user.access|= SUPER_ACL | EXECUTE_ACL;
|
||||
if (table->fields <= 13)
|
||||
{ // Without grant
|
||||
if (user.access & CREATE_ACL)
|
||||
user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL;
|
||||
}
|
||||
/* Convert old privileges */
|
||||
user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL;
|
||||
if (user.access & FILE_ACL)
|
||||
user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL;
|
||||
if (user.access & PROCESS_ACL)
|
||||
user.access|= SUPER_ACL | EXECUTE_ACL;
|
||||
#endif
|
||||
}
|
||||
VOID(push_dynamic(&acl_users,(gptr) &user));
|
||||
if (!user.host.hostname || user.host.hostname[0] == wild_many &&
|
||||
!user.host.hostname[1])
|
||||
allow_all_hosts=1; // Anyone can connect
|
||||
}
|
||||
VOID(push_dynamic(&acl_users,(gptr) &user));
|
||||
if (!user.host.hostname || user.host.hostname[0] == wild_many &&
|
||||
!user.host.hostname[1])
|
||||
allow_all_hosts=1; // Anyone can connect
|
||||
}
|
||||
qsort((gptr) dynamic_element(&acl_users,0,ACL_USER*),acl_users.elements,
|
||||
sizeof(ACL_USER),(qsort_cmp) acl_compare);
|
||||
|
@ -462,247 +495,203 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
|
|||
|
||||
|
||||
/*
|
||||
Prepare crypted scramble to be sent to the client
|
||||
Seek ACL entry for a user, check password, SSL cypher, and if
|
||||
everything is OK, update THD user data and USER_RESOURCES struct.
|
||||
This function does not check if the user has any sensible privileges:
|
||||
only user's existence and validity is checked.
|
||||
Note, that entire operation is protected by acl_cache_lock.
|
||||
SYNOPSIS
|
||||
thd INOUT thread handle. If all checks are OK,
|
||||
thd->priv_user, thd->master_access are updated.
|
||||
thd->host, thd->ip, thd->user are used for checks.
|
||||
mqh OUT user resources; on success mqh is reset, else
|
||||
unchanged
|
||||
passwd IN scrambled & crypted password, recieved from client
|
||||
(to check): thd->scramble or thd->scramble_323 is
|
||||
used to decrypt passwd, so they must contain
|
||||
original random string,
|
||||
passwd_len IN length of passwd, must be one of 0, 8,
|
||||
SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
|
||||
old_version IN if old (3.20) protocol is used
|
||||
RETURN VALUE
|
||||
0 success: thread data and mqh are updated
|
||||
1 user not found or authentification failure
|
||||
-1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
|
||||
*/
|
||||
|
||||
void prepare_scramble(THD *thd, ACL_USER *acl_user,char* prepared_scramble)
|
||||
int
|
||||
acl_getroot(THD *thd, USER_RESOURCES *mqh,
|
||||
const char *passwd, uint passwd_len, bool old_version)
|
||||
{
|
||||
/* Binary password format to be used for generation*/
|
||||
char bin_password[SCRAMBLE41_LENGTH];
|
||||
/* Generate new long scramble for the thread */
|
||||
create_random_string(SCRAMBLE41_LENGTH,&thd->rand,thd->scramble);
|
||||
thd->scramble[SCRAMBLE41_LENGTH]=0;
|
||||
/* Get binary form, First 4 bytes of prepared scramble is salt */
|
||||
get_hash_and_password(acl_user->salt,acl_user->pversion,prepared_scramble,
|
||||
(unsigned char*) bin_password);
|
||||
/* Store "*" as identifier for old passwords */
|
||||
if (!acl_user->pversion)
|
||||
prepared_scramble[0]='*';
|
||||
/* Finally encrypt password to get prepared scramble */
|
||||
password_crypt(thd->scramble, prepared_scramble+4, bin_password,
|
||||
SCRAMBLE41_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get master privilges for user (priviliges for all tables).
|
||||
Required before connecting to MySQL
|
||||
|
||||
As we have 2 stage handshake now we cache user not to lookup
|
||||
it second time. At the second stage we do not lookup user in case
|
||||
we already know it;
|
||||
|
||||
*/
|
||||
|
||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||
const char *password,const char *message,char **priv_user,
|
||||
char *priv_host, bool old_ver, USER_RESOURCES *mqh,
|
||||
char *prepared_scramble, uint *cur_priv_version,
|
||||
ACL_USER **cached_user)
|
||||
{
|
||||
ulong user_access=NO_ACCESS;
|
||||
*priv_user= (char*) user;
|
||||
bool password_correct= 0;
|
||||
int stage= (*cached_user != NULL); /* NULL passed as first stage */
|
||||
ACL_USER *acl_user= NULL;
|
||||
DBUG_ENTER("acl_getroot");
|
||||
|
||||
bzero(mqh,sizeof(USER_RESOURCES));
|
||||
if (!initialized)
|
||||
{
|
||||
// If no data allow anything
|
||||
DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */
|
||||
if (!initialized) /* if no data allow anything */
|
||||
{
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
int res= 1;
|
||||
|
||||
VOID(pthread_mutex_lock(&acl_cache->lock));
|
||||
|
||||
/*
|
||||
Get possible access from user_list. This is or'ed to others not
|
||||
fully specified
|
||||
|
||||
If we have cached user use it, in other case look it up.
|
||||
Find acl entry in user database. Note, that find_acl_user is not the same,
|
||||
because it doesn't take into account the case when user is not empty,
|
||||
but acl_user->user is empty
|
||||
*/
|
||||
|
||||
if (stage && (*cur_priv_version == priv_version))
|
||||
acl_user= *cached_user;
|
||||
else
|
||||
ACL_USER *acl_user= 0;
|
||||
for (uint i=0 ; i < acl_users.elements ; i++)
|
||||
{
|
||||
for (uint i=0 ; i < acl_users.elements ; i++)
|
||||
ACL_USER *user_i = dynamic_element(&acl_users,i,ACL_USER*);
|
||||
if (!user_i->user || !strcmp(thd->user, user_i->user))
|
||||
{
|
||||
ACL_USER *acl_user_search=dynamic_element(&acl_users,i,ACL_USER*);
|
||||
if (!acl_user_search->user || !strcmp(user,acl_user_search->user))
|
||||
if (compare_hostname(&user_i->host, thd->host, thd->ip))
|
||||
{
|
||||
if (compare_hostname(&acl_user_search->host,host,ip))
|
||||
/* check password: it should be empty or valid */
|
||||
if (passwd_len == user_i->salt_len)
|
||||
{
|
||||
/* Found mathing user */
|
||||
acl_user= acl_user_search;
|
||||
/* Store it as a cache */
|
||||
*cached_user= acl_user;
|
||||
*cur_priv_version= priv_version;
|
||||
break;
|
||||
if (user_i->salt_len == 0 ||
|
||||
user_i->salt_len == SCRAMBLE_LENGTH &&
|
||||
check_scramble(passwd, thd->scramble, user_i->salt) == 0 ||
|
||||
check_scramble_323(passwd, thd->scramble_323,
|
||||
(ulong *) user_i->salt, old_version) == 0)
|
||||
{
|
||||
acl_user= user_i;
|
||||
res= 0;
|
||||
}
|
||||
}
|
||||
else if (passwd_len == SCRAMBLE_LENGTH &&
|
||||
user_i->salt_len == SCRAMBLE_LENGTH_323)
|
||||
res= -1;
|
||||
/* linear search complete: */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we have acl_user found and may start our checks */
|
||||
/*
|
||||
This was moved to separate tree because of heavy HAVE_OPENSSL case.
|
||||
If acl_user is not null, res is 0.
|
||||
*/
|
||||
|
||||
if (acl_user)
|
||||
{
|
||||
/* Password should present for both or absend for both */
|
||||
if (!acl_user->password && !*password)
|
||||
password_correct=1;
|
||||
else if (!acl_user->password || !*password)
|
||||
{
|
||||
*cached_user= 0; // Impossible to connect
|
||||
}
|
||||
else
|
||||
{
|
||||
/* New version password is checked differently */
|
||||
if (acl_user->pversion)
|
||||
{
|
||||
if (stage) /* We check password only on the second stage */
|
||||
{
|
||||
if (!validate_password(password,message,acl_user->salt))
|
||||
password_correct=1;
|
||||
}
|
||||
else /* First stage - just prepare scramble */
|
||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||
}
|
||||
/* Old way to check password */
|
||||
else
|
||||
{
|
||||
/* Checking the scramble at any stage. First - old clients */
|
||||
if (!check_scramble(password,message,acl_user->salt,
|
||||
(my_bool) old_ver))
|
||||
password_correct=1;
|
||||
else if (!stage) /* Here if password incorrect */
|
||||
{
|
||||
/* At the first stage - prepare scramble */
|
||||
prepare_scramble(thd,acl_user,prepared_scramble);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If user not found password_correct will also be zero */
|
||||
if (!password_correct)
|
||||
goto unlock_and_exit;
|
||||
|
||||
/* OK. User found and password checked continue validation */
|
||||
|
||||
/* OK. User found and password checked continue validation */
|
||||
#ifdef HAVE_OPENSSL
|
||||
{
|
||||
Vio *vio=thd->net.vio;
|
||||
/*
|
||||
At this point we know that user is allowed to connect
|
||||
from given host by given username/password pair. Now
|
||||
we check if SSL is required, if user is using SSL and
|
||||
if X509 certificate attributes are OK
|
||||
*/
|
||||
switch (acl_user->ssl_type) {
|
||||
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
||||
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
||||
if (vio_type(vio) == VIO_TYPE_SSL)
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
||||
{
|
||||
ulong user_access= NO_ACCESS;
|
||||
Vio *vio=thd->net.vio;
|
||||
/*
|
||||
Connections with non-valid certificates are dropped already
|
||||
in sslaccept() anyway, so we do not check validity here.
|
||||
|
||||
We need to check for absence of SSL because without SSL
|
||||
we should reject connection.
|
||||
At this point we know that user is allowed to connect
|
||||
from given host by given username/password pair. Now
|
||||
we check if SSL is required, if user is using SSL and
|
||||
if X509 certificate attributes are OK
|
||||
*/
|
||||
if (vio_type(vio) == VIO_TYPE_SSL && SSL_get_peer_certificate(vio->ssl_))
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
||||
/*
|
||||
We do not check for absence of SSL because without SSL it does
|
||||
not pass all checks here anyway.
|
||||
If cipher name is specified, we compare it to actual cipher in
|
||||
use.
|
||||
*/
|
||||
if (vio_type(vio) != VIO_TYPE_SSL)
|
||||
break;
|
||||
if (acl_user->ssl_cipher)
|
||||
{
|
||||
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
||||
acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
|
||||
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
||||
user_access=acl_user->access;
|
||||
else
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'",
|
||||
acl_user->ssl_cipher,
|
||||
SSL_get_cipher(vio->ssl_));
|
||||
user_access=NO_ACCESS;
|
||||
break;
|
||||
}
|
||||
switch (acl_user->ssl_type) {
|
||||
case SSL_TYPE_NOT_SPECIFIED: // Impossible
|
||||
case SSL_TYPE_NONE: /* SSL is not required to connect */
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_ANY: /* Any kind of SSL is good enough */
|
||||
if (vio_type(vio) == VIO_TYPE_SSL)
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_X509: /* Client should have any valid certificate. */
|
||||
/*
|
||||
Connections with non-valid certificates are dropped already
|
||||
in sslaccept() anyway, so we do not check validity here.
|
||||
|
||||
We need to check for absence of SSL because without SSL
|
||||
we should reject connection.
|
||||
*/
|
||||
if (vio_type(vio) == VIO_TYPE_SSL &&
|
||||
SSL_get_peer_certificate(vio->ssl_))
|
||||
user_access=acl_user->access;
|
||||
break;
|
||||
case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
|
||||
/*
|
||||
We do not check for absence of SSL because without SSL it does
|
||||
not pass all checks here anyway.
|
||||
If cipher name is specified, we compare it to actual cipher in
|
||||
use.
|
||||
*/
|
||||
if (vio_type(vio) != VIO_TYPE_SSL)
|
||||
break;
|
||||
if (acl_user->ssl_cipher)
|
||||
{
|
||||
DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
|
||||
acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)));
|
||||
if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(vio->ssl_)))
|
||||
user_access=acl_user->access;
|
||||
else
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 ciphers mismatch: should be '%s'"
|
||||
"but is '%s'", acl_user->ssl_cipher,
|
||||
SSL_get_cipher(vio->ssl_));
|
||||
user_access=NO_ACCESS;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Prepare certificate (if exists) */
|
||||
DBUG_PRINT("info",("checkpoint 1"));
|
||||
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
||||
DBUG_PRINT("info",("checkpoint 2"));
|
||||
/* If X509 issuer is speified, we check it... */
|
||||
if (acl_user->x509_issuer)
|
||||
{
|
||||
DBUG_PRINT("info",("checkpoint 3"));
|
||||
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
||||
acl_user->x509_issuer, ptr));
|
||||
if (strcmp(acl_user->x509_issuer, ptr))
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 issuer mismatch: should be '%s' "
|
||||
"but is '%s'", acl_user->x509_issuer, ptr);
|
||||
user_access=NO_ACCESS;
|
||||
free(ptr);
|
||||
break;
|
||||
}
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
DBUG_PRINT("info",("checkpoint 4"));
|
||||
/* X509 subject is specified, we check it .. */
|
||||
if (acl_user->x509_subject)
|
||||
{
|
||||
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
||||
acl_user->x509_subject, ptr));
|
||||
if (strcmp(acl_user->x509_subject,ptr))
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 subject mismatch: '%s' vs '%s'",
|
||||
acl_user->x509_subject, ptr);
|
||||
user_access=NO_ACCESS;
|
||||
}
|
||||
else
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Prepare certificate (if exists) */
|
||||
DBUG_PRINT("info",("checkpoint 1"));
|
||||
X509* cert=SSL_get_peer_certificate(vio->ssl_);
|
||||
DBUG_PRINT("info",("checkpoint 2"));
|
||||
/* If X509 issuer is speified, we check it... */
|
||||
if (acl_user->x509_issuer)
|
||||
{
|
||||
DBUG_PRINT("info",("checkpoint 3"));
|
||||
char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
|
||||
acl_user->x509_issuer, ptr));
|
||||
if (strcmp(acl_user->x509_issuer, ptr))
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'",
|
||||
acl_user->x509_issuer, ptr);
|
||||
user_access=NO_ACCESS;
|
||||
free(ptr);
|
||||
break;
|
||||
}
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
DBUG_PRINT("info",("checkpoint 4"));
|
||||
/* X509 subject is specified, we check it .. */
|
||||
if (acl_user->x509_subject)
|
||||
{
|
||||
char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
|
||||
DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
|
||||
acl_user->x509_subject, ptr));
|
||||
if (strcmp(acl_user->x509_subject,ptr))
|
||||
{
|
||||
if (global_system_variables.log_warnings)
|
||||
sql_print_error("X509 subject mismatch: '%s' vs '%s'",
|
||||
acl_user->x509_subject, ptr);
|
||||
user_access=NO_ACCESS;
|
||||
}
|
||||
else
|
||||
user_access=acl_user->access;
|
||||
free(ptr);
|
||||
}
|
||||
break;
|
||||
/* end of SSL stuff: assign result */
|
||||
thd->master_access= user_access;
|
||||
}
|
||||
}
|
||||
#else /* HAVE_OPENSSL */
|
||||
user_access=acl_user->access;
|
||||
thd->master_access= acl_user->access;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
*mqh=acl_user->user_resource;
|
||||
if (!acl_user->user)
|
||||
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
|
||||
thd->priv_user= acl_user->user ? thd->user : (char *) "";
|
||||
*mqh= acl_user->user_resource;
|
||||
|
||||
if (acl_user->host.hostname)
|
||||
strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME);
|
||||
else
|
||||
*priv_host= 0;
|
||||
|
||||
unlock_and_exit:
|
||||
if (acl_user->host.hostname)
|
||||
strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
|
||||
else
|
||||
*thd->priv_host= 0;
|
||||
}
|
||||
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
||||
DBUG_RETURN(user_access);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
|
@ -713,8 +702,9 @@ static byte* check_get_key(ACL_USER *buff,uint *length,
|
|||
return (byte*) buff->host.hostname;
|
||||
}
|
||||
|
||||
|
||||
static void acl_update_user(const char *user, const char *host,
|
||||
const char *password,
|
||||
const char *password, uint password_len,
|
||||
enum SSL_type ssl_type,
|
||||
const char *ssl_cipher,
|
||||
const char *x509_issuer,
|
||||
|
@ -750,20 +740,9 @@ static void acl_update_user(const char *user, const char *host,
|
|||
acl_user->x509_subject= (x509_subject ?
|
||||
strdup_root(&mem,x509_subject) : 0);
|
||||
}
|
||||
if (password)
|
||||
{
|
||||
if (!password[0]) /* If password is empty set it to null */
|
||||
{
|
||||
acl_user->password=0;
|
||||
acl_user->pversion=0; // just initialize
|
||||
}
|
||||
else
|
||||
{
|
||||
acl_user->password=(char*) ""; // Just point at something
|
||||
get_salt_from_password(acl_user->salt,password);
|
||||
acl_user->pversion=get_password_version(acl_user->password);
|
||||
}
|
||||
}
|
||||
|
||||
set_user_salt(acl_user, password, password_len);
|
||||
/* search complete: */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -772,7 +751,7 @@ static void acl_update_user(const char *user, const char *host,
|
|||
|
||||
|
||||
static void acl_insert_user(const char *user, const char *host,
|
||||
const char *password,
|
||||
const char *password, uint password_len,
|
||||
enum SSL_type ssl_type,
|
||||
const char *ssl_cipher,
|
||||
const char *x509_issuer,
|
||||
|
@ -783,7 +762,6 @@ static void acl_insert_user(const char *user, const char *host,
|
|||
ACL_USER acl_user;
|
||||
acl_user.user=strdup_root(&mem,user);
|
||||
update_hostname(&acl_user.host,strdup_root(&mem,host));
|
||||
acl_user.password=0;
|
||||
acl_user.access=privileges;
|
||||
acl_user.user_resource = *mqh;
|
||||
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
|
||||
|
@ -793,12 +771,8 @@ static void acl_insert_user(const char *user, const char *host,
|
|||
acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0;
|
||||
acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0;
|
||||
acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0;
|
||||
if (password)
|
||||
{
|
||||
acl_user.password=(char*) ""; // Just point at something
|
||||
get_salt_from_password(acl_user.salt,password);
|
||||
acl_user.pversion=get_password_version(password);
|
||||
}
|
||||
|
||||
set_user_salt(&acl_user, password, password_len);
|
||||
|
||||
VOID(push_dynamic(&acl_users,(gptr) &acl_user));
|
||||
if (!acl_user.host.hostname || acl_user.host.hostname[0] == wild_many
|
||||
|
@ -1135,7 +1109,6 @@ bool check_change_password(THD *thd, const char *host, const char *user)
|
|||
bool change_password(THD *thd, const char *host, const char *user,
|
||||
char *new_password)
|
||||
{
|
||||
uint length=0;
|
||||
DBUG_ENTER("change_password");
|
||||
DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'",
|
||||
host,user,new_password));
|
||||
|
@ -1144,37 +1117,27 @@ bool change_password(THD *thd, const char *host, const char *user,
|
|||
if (check_change_password(thd, host, user))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
/*
|
||||
password should always be 0,16 or 45 chars;
|
||||
Simple hack to avoid cracking
|
||||
*/
|
||||
length=(uint) strlen(new_password);
|
||||
if (length != 45)
|
||||
new_password[length & 16]=0;
|
||||
|
||||
VOID(pthread_mutex_lock(&acl_cache->lock));
|
||||
ACL_USER *acl_user;
|
||||
if (!(acl_user= find_acl_user(host,user)))
|
||||
if (!(acl_user= find_acl_user(host, user)))
|
||||
{
|
||||
send_error(thd, ER_PASSWORD_NO_MATCH);
|
||||
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
||||
send_error(thd, ER_PASSWORD_NO_MATCH);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
/* update loaded acl entry: */
|
||||
uint new_password_len= new_password ? strlen(new_password) : 0;
|
||||
set_user_salt(acl_user, new_password, new_password_len);
|
||||
|
||||
if (update_user_table(thd,
|
||||
acl_user->host.hostname ? acl_user->host.hostname : "",
|
||||
acl_user->user ? acl_user->user : "",
|
||||
new_password))
|
||||
new_password, new_password_len))
|
||||
{
|
||||
VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */
|
||||
send_error(thd,0); /* purecov: deadcode */
|
||||
DBUG_RETURN(1); /* purecov: deadcode */
|
||||
}
|
||||
get_salt_from_password(acl_user->salt,new_password);
|
||||
acl_user->pversion=get_password_version(new_password);
|
||||
if (!new_password[0])
|
||||
acl_user->password=0;
|
||||
else
|
||||
acl_user->password=(char*) ""; // Point at something
|
||||
|
||||
acl_cache->clear(1); // Clear locked hostname cache
|
||||
VOID(pthread_mutex_unlock(&acl_cache->lock));
|
||||
|
@ -1210,7 +1173,7 @@ find_acl_user(const char *host, const char *user)
|
|||
if (!acl_user->user && !user[0] ||
|
||||
acl_user->user && !strcmp(user,acl_user->user))
|
||||
{
|
||||
if (compare_hostname(&(acl_user->host),host,host))
|
||||
if (compare_hostname(&acl_user->host,host,host))
|
||||
{
|
||||
DBUG_RETURN(acl_user);
|
||||
}
|
||||
|
@ -1280,7 +1243,7 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
|
|||
****************************************************************************/
|
||||
|
||||
static bool update_user_table(THD *thd, const char *host, const char *user,
|
||||
const char *new_password)
|
||||
const char *new_password, uint new_password_len)
|
||||
{
|
||||
TABLE_LIST tables;
|
||||
TABLE *table;
|
||||
|
@ -1304,7 +1267,7 @@ static bool update_user_table(THD *thd, const char *host, const char *user,
|
|||
DBUG_RETURN(1); /* purecov: deadcode */
|
||||
}
|
||||
store_record(table,record[1]);
|
||||
table->field[2]->store(new_password,(uint) strlen(new_password), &my_charset_latin1);
|
||||
table->field[2]->store(new_password, new_password_len, &my_charset_latin1);
|
||||
if ((error=table->file->update_row(table->record[1],table->record[0])))
|
||||
{
|
||||
table->file->print_error(error,MYF(0)); /* purecov: deadcode */
|
||||
|
@ -1352,24 +1315,24 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
|
|||
{
|
||||
int error = -1;
|
||||
bool old_row_exists=0;
|
||||
char *password,empty_string[1];
|
||||
char empty_string[]= { '\0' };
|
||||
char *password= empty_string;
|
||||
uint password_len= 0;
|
||||
char what= (revoke_grant) ? 'N' : 'Y';
|
||||
DBUG_ENTER("replace_user_table");
|
||||
safe_mutex_assert_owner(&acl_cache->lock);
|
||||
|
||||
password=empty_string;
|
||||
empty_string[0]=0;
|
||||
|
||||
if (combo.password.str && combo.password.str[0])
|
||||
{
|
||||
if ((combo.password.length != HASH_PASSWORD_LENGTH)
|
||||
&& combo.password.length != HASH_OLD_PASSWORD_LENGTH)
|
||||
if (combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH &&
|
||||
combo.password.length != SCRAMBLED_PASSWORD_CHAR_LENGTH_323)
|
||||
{
|
||||
my_printf_error(ER_PASSWORD_NO_MATCH,
|
||||
"Password hash should be a %d-digit hexadecimal number",
|
||||
MYF(0),HASH_PASSWORD_LENGTH);
|
||||
MYF(0), SCRAMBLED_PASSWORD_CHAR_LENGTH);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
password_len= combo.password.length;
|
||||
password=combo.password.str;
|
||||
}
|
||||
|
||||
|
@ -1394,17 +1357,20 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
|
|||
goto end;
|
||||
}
|
||||
old_row_exists = 0;
|
||||
restore_record(table,default_values); // cp empty row from default_values
|
||||
table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1);
|
||||
table->field[1]->store(combo.user.str,combo.user.length, &my_charset_latin1);
|
||||
table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
|
||||
restore_record(table,default_values); // cp empty row from default_values
|
||||
table->field[0]->store(combo.host.str,combo.host.length,
|
||||
&my_charset_latin1);
|
||||
table->field[1]->store(combo.user.str,combo.user.length,
|
||||
&my_charset_latin1);
|
||||
table->field[2]->store(password, password_len,
|
||||
&my_charset_latin1);
|
||||
}
|
||||
else
|
||||
{
|
||||
old_row_exists = 1;
|
||||
store_record(table,record[1]); // Save copy for update
|
||||
if (combo.password.str) // If password given
|
||||
table->field[2]->store(password,(uint) strlen(password), &my_charset_latin1);
|
||||
table->field[2]->store(password, password_len, &my_charset_latin1);
|
||||
}
|
||||
|
||||
/* Update table columns with new privileges */
|
||||
|
@ -1501,10 +1467,8 @@ end:
|
|||
if (!error)
|
||||
{
|
||||
acl_cache->clear(1); // Clear privilege cache
|
||||
if (!combo.password.str)
|
||||
password=0; // No password given on command
|
||||
if (old_row_exists)
|
||||
acl_update_user(combo.user.str,combo.host.str,password,
|
||||
acl_update_user(combo.user.str, combo.host.str, password, password_len,
|
||||
thd->lex.ssl_type,
|
||||
thd->lex.ssl_cipher,
|
||||
thd->lex.x509_issuer,
|
||||
|
@ -1512,7 +1476,7 @@ end:
|
|||
&thd->lex.mqh,
|
||||
rights);
|
||||
else
|
||||
acl_insert_user(combo.user.str,combo.host.str,password,
|
||||
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
|
||||
thd->lex.ssl_type,
|
||||
thd->lex.ssl_cipher,
|
||||
thd->lex.x509_issuer,
|
||||
|
@ -2915,12 +2879,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
|
|||
global.append ("'@'",3);
|
||||
global.append(lex_user->host.str,lex_user->host.length);
|
||||
global.append ('\'');
|
||||
if (acl_user->password)
|
||||
if (acl_user->salt_len)
|
||||
{
|
||||
char passd_buff[HASH_PASSWORD_LENGTH+1];
|
||||
make_password_from_salt(passd_buff,acl_user->salt,acl_user->pversion);
|
||||
char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
|
||||
if (acl_user->salt_len == SCRAMBLE_LENGTH)
|
||||
make_password_from_salt(passwd_buff, acl_user->salt);
|
||||
else
|
||||
make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt);
|
||||
global.append(" IDENTIFIED BY PASSWORD '",25);
|
||||
global.append(passd_buff);
|
||||
global.append(passwd_buff);
|
||||
global.append('\'');
|
||||
}
|
||||
/* "show grants" SSL related stuff */
|
||||
|
|
|
@ -111,9 +111,9 @@ public:
|
|||
acl_host_and_ip host;
|
||||
uint hostname_length;
|
||||
USER_RESOURCES user_resource;
|
||||
char *user,*password;
|
||||
ulong salt[6]; // New password has longer length
|
||||
uint8 pversion; // password version
|
||||
char *user;
|
||||
uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
|
||||
uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1
|
||||
enum SSL_type ssl_type;
|
||||
const char *ssl_cipher, *x509_issuer, *x509_subject;
|
||||
};
|
||||
|
@ -135,11 +135,8 @@ void acl_reload(THD *thd);
|
|||
void acl_free(bool end=0);
|
||||
ulong acl_get(const char *host, const char *ip, const char *bin_ip,
|
||||
const char *user, const char *db);
|
||||
ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
|
||||
const char *password,const char *scramble,
|
||||
char **priv_user, char *priv_host,
|
||||
bool old_ver, USER_RESOURCES *max,char* prepared_scramble,
|
||||
uint *cur_priv_version, ACL_USER **cached_user);
|
||||
int acl_getroot(THD *thd, USER_RESOURCES *mqh,
|
||||
const char *passwd, uint passwd_len, bool old_ver);
|
||||
bool acl_check_host(const char *host, const char *ip);
|
||||
bool check_change_password(THD *thd, const char *host, const char *user);
|
||||
bool change_password(THD *thd, const char *host, const char *user,
|
||||
|
|
|
@ -139,6 +139,7 @@ THD::THD():user_time(0), is_fatal_error(0),
|
|||
set_query_id=1;
|
||||
db_access=NO_ACCESS;
|
||||
version=refresh_version; // For boot
|
||||
*scramble= *scramble_323= '\0';
|
||||
|
||||
init();
|
||||
/* Initialize sub structures */
|
||||
|
|
|
@ -529,10 +529,16 @@ public:
|
|||
enum_tx_isolation session_tx_isolation;
|
||||
/* for user variables replication*/
|
||||
DYNAMIC_ARRAY user_var_events;
|
||||
// extend scramble to handle new auth
|
||||
char scramble[SCRAMBLE41_LENGTH+1];
|
||||
// old scramble is needed to handle old clients
|
||||
char old_scramble[SCRAMBLE_LENGTH+1];
|
||||
|
||||
/* scramble - random string sent to client on handshake */
|
||||
char scramble[SCRAMBLE_LENGTH+1];
|
||||
/*
|
||||
The same as scramble but for old password checking routines. It always
|
||||
contains first N bytes of scramble.
|
||||
See check_connection() at sql_parse.cc for authentification details.
|
||||
*/
|
||||
char scramble_323[SCRAMBLE_LENGTH_323+1];
|
||||
|
||||
uint8 query_cache_type; // type of query cache processing
|
||||
bool slave_thread;
|
||||
bool set_query_id,locked,count_cuted_fields,some_tables_deleted;
|
||||
|
|
582
sql/sql_parse.cc
582
sql/sql_parse.cc
|
@ -178,152 +178,119 @@ 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
|
||||
|
||||
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
|
||||
thd INOUT thread handle, thd->{host,user,ip} are used
|
||||
command IN originator of the check: now check_user is called
|
||||
during connect and change user procedures; used for
|
||||
logging.
|
||||
passwd IN scrambled password recieved from client
|
||||
passwd_len IN length of scrambled password
|
||||
db IN database name to connect to, may be NULL
|
||||
check_count IN dont know exactly
|
||||
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.
|
||||
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 internal error; error is sent to client
|
||||
Note, that this return semantics differs from check_connection,
|
||||
which returns -1 if message was already sent.
|
||||
-1 acl entry for this user contains old scramble, but passwd contains
|
||||
new one, error is 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];
|
||||
DBUG_ENTER("check_user");
|
||||
|
||||
/*
|
||||
Move password to temporary buffer as it may be stored in communication
|
||||
buffer
|
||||
*/
|
||||
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))))
|
||||
{
|
||||
send_error(thd,ER_OUT_OF_RESOURCES);
|
||||
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*"));
|
||||
|
||||
/*
|
||||
In case we're going to retry we should not send error message at this
|
||||
point
|
||||
Why this is set here? - probably to reset current DB to 'no database
|
||||
selected' in case of 'change user' failure.
|
||||
*/
|
||||
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,
|
||||
protocol_version == 9 ||
|
||||
!(thd->client_capabilities & CLIENT_LONG_PASSWORD));
|
||||
if (res == 0 && !(thd->master_access & NO_ACCESS)) // authentification is OK
|
||||
{
|
||||
if (do_send_error || !had_password || !*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,
|
||||
passwd_len ? "yes": "no",
|
||||
thd->master_access, thd->db ? thd->db : "*none*"));
|
||||
|
||||
if (check_count)
|
||||
{
|
||||
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_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);
|
||||
}
|
||||
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);
|
||||
/* 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*) "");
|
||||
|
||||
/* Why is it set here? */
|
||||
thd->db_access=0;
|
||||
|
||||
/* Don't allow user to connect if he has done too many queries */
|
||||
if ((ur.questions || ur.updates || ur.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
|
||||
send_ok(thd);
|
||||
thd->password= test(passwd_len); // remember for error messages
|
||||
/* Ready to handle queries */
|
||||
}
|
||||
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) &&
|
||||
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 &&
|
||||
check_for_max_user_connections(thd, thd->user_connect))
|
||||
DBUG_RETURN(1);
|
||||
|
||||
if (db && db[0])
|
||||
else if (res != -1) // authentication failure
|
||||
{
|
||||
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_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));
|
||||
}
|
||||
send_ok(thd); // Ready to handle questions
|
||||
thd->password= test(passwd[0]); // Remember for error messages
|
||||
DBUG_RETURN(0); // ok
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check for maximum allowable user connections, if the mysqld server is
|
||||
started with corresponding variable that is greater then 0.
|
||||
|
@ -525,48 +492,93 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0)
|
|||
|
||||
|
||||
/*
|
||||
Check connnectionn and get priviliges
|
||||
|
||||
Perform check for scrambled password, re-request scrambled password
|
||||
from client if necessary. See also help for check_user.
|
||||
SYNOPSIS
|
||||
check_connections
|
||||
thd Thread handle
|
||||
authenticate()
|
||||
RETURN VALUE
|
||||
0 success, OK sent to client
|
||||
-1 error, sent to client
|
||||
> 0 error, not sent to client
|
||||
*/
|
||||
|
||||
static
|
||||
int
|
||||
authenticate(THD *thd, enum enum_server_command command,
|
||||
const char *passwd, uint passwd_len, const char *db,
|
||||
bool check_count)
|
||||
{
|
||||
if (passwd_len != 0 &&
|
||||
passwd_len != SCRAMBLE_LENGTH &&
|
||||
passwd_len != SCRAMBLE_LENGTH_323)
|
||||
return 1;
|
||||
int res= check_user(thd, COM_CONNECT, passwd, passwd_len, db, check_count);
|
||||
if (res < 0)
|
||||
{
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
char buff[NAME_LEN + 1];
|
||||
/* save db because network buffer is to hold new packet */
|
||||
if (db)
|
||||
{
|
||||
strmake(buff, db, NAME_LEN);
|
||||
db= buff;
|
||||
}
|
||||
NET *net= &thd->net;
|
||||
if (my_net_write(net, thd->scramble_323, SCRAMBLE_LENGTH_323 + 1) ||
|
||||
net_flush(net) ||
|
||||
my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very
|
||||
{ // specific packet size
|
||||
inc_host_errors(&thd->remote.sin_addr);
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
}
|
||||
/* Final attempt to check the user based on reply */
|
||||
/* So as passwd is short, errcode is always sent to user and res >= 0 */
|
||||
res= check_user(thd, COM_CONNECT, (char *) net->read_pos,
|
||||
SCRAMBLE_LENGTH_323, db, check_count);
|
||||
}
|
||||
return res > 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Perform handshake, authorize client and update thd ACL variables.
|
||||
SYNOPSIS
|
||||
check_connection()
|
||||
thd INOUT 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
|
||||
-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 */
|
||||
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"))
|
||||
thd->host=(char*) localhost;
|
||||
thd->host= (char *) localhost;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
|
@ -595,15 +607,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);
|
||||
|
||||
|
@ -617,19 +630,36 @@ 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[2]=(char) default_charset_info->number;
|
||||
int2store(end+3,thd->server_status);
|
||||
bzero(end+5,13);
|
||||
end+=18;
|
||||
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);
|
||||
strmake(thd->scramble_323, thd->scramble, SCRAMBLE_LENGTH_323);
|
||||
|
||||
// At this point we write connection message and read reply
|
||||
if (net_write_command(net,(uchar) protocol_version, "", 0, buff,
|
||||
/*
|
||||
Old clients does not understand long scrambles, but can ignore packet
|
||||
tail: that's why first part of scramble is placed here, and second
|
||||
part at the end of packet.
|
||||
*/
|
||||
end= strmake(end, thd->scramble_323, 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;
|
||||
/* 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,
|
||||
(uint) (end-buff)) ||
|
||||
(pkt_len= my_net_read(net)) == packet_error ||
|
||||
pkt_len < MIN_HANDSHAKE_SIZE)
|
||||
|
@ -702,7 +732,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)",
|
||||
|
@ -719,16 +749,7 @@ 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)
|
||||
db=strend(passwd)+1;
|
||||
|
||||
/* We can get only old hash at this point */
|
||||
if (using_password && strlen(passwd) != SCRAMBLE_LENGTH)
|
||||
return ER_HANDSHAKE_ERROR;
|
||||
/* why has it been put here? */
|
||||
|
||||
if (thd->client_capabilities & CLIENT_INTERACTIVE)
|
||||
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
|
||||
|
@ -737,60 +758,19 @@ check_connections(THD *thd)
|
|||
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));
|
||||
char *user= end;
|
||||
char *passwd= strend(user)+1;
|
||||
uint passwd_len= strlen(passwd);
|
||||
|
||||
/* 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];
|
||||
char *db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
|
||||
passwd+passwd_len+1 : 0;
|
||||
|
||||
/* 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);
|
||||
thd->user= my_strdup(user, MYF(0));
|
||||
if (!thd->user)
|
||||
return(ER_OUT_OF_RESOURCES);
|
||||
return authenticate(thd, COM_CONNECT, passwd, passwd_len, db, true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -847,7 +827,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);
|
||||
|
@ -1152,116 +1132,60 @@ 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;
|
||||
uint passwd_len= strlen(passwd);
|
||||
char *db= passwd + passwd_len + 1;
|
||||
|
||||
/* 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= authenticate(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 */
|
||||
|
@ -3158,7 +3082,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
|
||||
|
|
|
@ -7,7 +7,7 @@ typedef struct st_slave_info
|
|||
uint32 rpl_recovery_rank, master_id;
|
||||
char host[HOSTNAME_LENGTH+1];
|
||||
char user[USERNAME_LENGTH+1];
|
||||
char password[HASH_PASSWORD_LENGTH+1];
|
||||
char password[SCRAMBLED_PASSWORD_CHAR_LENGTH+1];
|
||||
uint16 port;
|
||||
THD* thd;
|
||||
} SLAVE_INFO;
|
||||
|
|
|
@ -2515,9 +2515,10 @@ simple_expr:
|
|||
| NOW_SYM '(' expr ')'
|
||||
{ $$= new Item_func_now($3); Lex->safe_to_cache_query=0;}
|
||||
| PASSWORD '(' expr ')'
|
||||
{ $$= new Item_func_password($3); }
|
||||
| PASSWORD '(' expr ',' expr ')'
|
||||
{ $$= new Item_func_password($3,$5); }
|
||||
{
|
||||
$$= use_old_passwords ? (Item *) new Item_func_old_password($3) :
|
||||
(Item *) new Item_func_password($3);
|
||||
}
|
||||
| POINT_SYM '(' expr ',' expr ')'
|
||||
{ $$= new Item_func_point($3,$5); }
|
||||
| POINTFROMTEXT '(' expr ')'
|
||||
|
@ -4604,13 +4605,22 @@ text_or_password:
|
|||
{
|
||||
if (!$3.length)
|
||||
$$=$3.str;
|
||||
else
|
||||
else if (use_old_passwords)
|
||||
{
|
||||
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
|
||||
make_scrambled_password(buff,$3.str,use_old_passwords,
|
||||
&YYTHD->rand);
|
||||
char *buff= (char *)
|
||||
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
|
||||
if (buff)
|
||||
make_scrambled_password_323(buff, $3.str);
|
||||
$$=buff;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *buff= (char *)
|
||||
YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
|
||||
if (buff)
|
||||
make_scrambled_password(buff, $3.str);
|
||||
$$=buff;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -4918,14 +4928,24 @@ grant_user:
|
|||
$$=$1; $1->password=$4;
|
||||
if ($4.length)
|
||||
{
|
||||
char *buff=(char*) YYTHD->alloc(HASH_PASSWORD_LENGTH+1);
|
||||
if (buff)
|
||||
{
|
||||
make_scrambled_password(buff,$4.str,use_old_passwords,
|
||||
&YYTHD->rand);
|
||||
$1->password.str=buff;
|
||||
$1->password.length=HASH_PASSWORD_LENGTH;
|
||||
}
|
||||
if (use_old_passwords)
|
||||
{
|
||||
char *buff=
|
||||
(char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1);
|
||||
if (buff)
|
||||
make_scrambled_password_323(buff, $4.str);
|
||||
$1->password.str= buff;
|
||||
$1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
|
||||
}
|
||||
else
|
||||
{
|
||||
char *buff=
|
||||
(char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1);
|
||||
if (buff)
|
||||
make_scrambled_password(buff, $4.str);
|
||||
$1->password.str= buff;
|
||||
$1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
|
||||
|
|
Loading…
Reference in a new issue