From 7df0475847103581798ddacf75dbf634e8f98d0a Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Tue, 1 Jul 2003 23:40:59 +0400 Subject: [PATCH 01/30] 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. --- .bzrignore | 2 + BitKeeper/etc/logging_ok | 1 + client/mysqladmin.c | 9 +- include/mysql.h | 4 +- include/mysql_com.h | 61 +- libmysql/libmysql.c | 58 +- scripts/mysql_create_system_tables.sh | 2 +- scripts/mysql_fix_privilege_tables.sql | 2 +- sql-common/client.c | 166 ++--- sql/item_strfunc.cc | 96 +-- sql/item_strfunc.h | 30 +- sql/mysql_priv.h | 3 - sql/mysqld.cc | 6 - sql/password.c | 918 ++++++++++--------------- sql/slave.cc | 6 +- sql/slave.h | 2 +- sql/sql_acl.cc | 663 +++++++++--------- sql/sql_acl.h | 13 +- sql/sql_class.cc | 1 + sql/sql_class.h | 14 +- sql/sql_parse.cc | 582 +++++++--------- sql/sql_repl.h | 2 +- sql/sql_yacc.yy | 50 +- 23 files changed, 1186 insertions(+), 1505 deletions(-) diff --git a/.bzrignore b/.bzrignore index 4e9ce4e41ab..a23384d4170 100644 --- a/.bzrignore +++ b/.bzrignore @@ -622,3 +622,5 @@ vio/test-ssl vio/test-sslclient vio/test-sslserver vio/viotest-ssl +start_mysqld.sh +mysys/main.cc diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok index 794edcdd968..ac862cc9770 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -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) diff --git a/client/mysqladmin.c b/client/mysqladmin.c index 018bcbc1963..f263d321a7b 100644 --- a/client/mysqladmin.c +++ b/client/mysqladmin.c @@ -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); diff --git a/include/mysql.h b/include/mysql.h index bd63a10ba45..91ef481e7f7 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -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 diff --git a/include/mysql_com.h b/include/mysql_com.h index e87001ff27d..c1f18160667 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -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 */ diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 771278c1dbb..6bc38abb060 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -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)); diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 2739c45e750..c4cdc7b52d7 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -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," diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index 43dc6d89481..8c5b29a49ff 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -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, diff --git a/sql-common/client.c b/sql-common/client.c index 721164c8301..35dea62edc3 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -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; diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index ae63ac85d4d..f8488565b75 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -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)+'.') diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 6cc6d730627..11f5a66b3d1 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -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; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index aca84f1bcb3..13ff168e553 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -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 diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 72ee3e30c63..2677973ff0e 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -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 diff --git a/sql/password.c b/sql/password.c index 257547671e5..be6514d89c6 100644 --- a/sql/password.c +++ b/sql/password.c @@ -29,28 +29,33 @@ The password is saved (in user.password) by using the PASSWORD() function in mysql. + This is .c file because it's used in libmysqlclient, which is entirely in C. + (we need it to be portable to a variety of systems). Example: update user set password=PASSWORD("hello") where user="test" This saves a hashed number as a string in the password field. + The new autentication is performed in following manner: - New in MySQL 4.1 authentication works even more secure way. - At the first step client sends user name to the sever, and password if - it is empty. So in case of empty password authentication is as fast as before. - At the second stap servers sends scramble to client, which is encoded with - password stage2 hash stored in the password database as well as salt, needed - for client to build stage2 password to decrypt scramble. - Client decrypts the scramble and encrypts it once again with stage1 password. - This information is sent to server. - Server decrypts the scramble to get stage1 password and hashes it to get - stage2 hash. This hash is when compared to hash stored in the database. + SERVER: public_seed=create_random_string() + send(public_seed) - This authentication needs 2 packet round trips instead of one but it is much - stronger. Now if one will steal mysql database content he will not be able - to break into MySQL. + CLIENT: recv(public_seed) + hash_stage1=sha1("password") + hash_stage2=sha1(hash_stage1) + reply=xor(hash_stage1, sha1(public_seed,hash_stage2) - New Password handling functions by Peter Zaitsev + // this three steps are done in scramble() + send(reply) + + + SERVER: recv(reply) + hash_stage1=xor(reply, sha1(public_seed,hash_stage2)) + candidate_hash2=sha1(hash_stage1) + check(candidate_hash2==hash_stage2) + + // this three steps are done in check_scramble() *****************************************************************************/ @@ -60,31 +65,21 @@ #include #include "mysql.h" - - -/* Character to use as version identifier for version 4.1 */ -#define PVERSION41_CHAR '*' - -/* Scramble length for new password version */ - +/************ MySQL 3.23-4.0 authentification routines: untouched ***********/ /* New (MySQL 3.21+) random generation structure initialization - SYNOPSIS randominit() rand_st OUT Structure to initialize seed1 IN First initialization parameter seed2 IN Second initialization parameter - - RETURN - none */ -void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) -{ /* For mysql 3.21.# */ +void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2) +{ /* For mysql 3.21.# */ #ifdef HAVE_purify - bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ + bzero((char*) rand_st,sizeof(*rand_st)); /* Avoid UMC varnings */ #endif rand_st->max_value= 0x3FFFFFFFL; rand_st->max_value_dbl=(double) rand_st->max_value; @@ -95,18 +90,15 @@ void randominit(struct rand_struct *rand_st,ulong seed1, ulong seed2) /* Old (MySQL 3.20) random generation structure initialization - + XXX: is to be deleted very soon! SYNOPSIS old_randominit() rand_st OUT Structure to initialize seed1 IN First initialization parameter - - RETURN - none */ -static void old_randominit(struct rand_struct *rand_st,ulong seed1) -{ /* For mysql 3.20.# */ +static void old_randominit(struct rand_struct *rand_st, ulong seed1) +{ /* For mysql 3.20.# */ rand_st->max_value= 0x01FFFFFFL; rand_st->max_value_dbl=(double) rand_st->max_value; seed1%=rand_st->max_value; @@ -115,14 +107,12 @@ static void old_randominit(struct rand_struct *rand_st,ulong seed1) /* - Generate Random number - + Generate random number. SYNOPSIS my_rnd() rand_st INOUT Structure used for number generation - - RETURN - Generated pseudo random number + RETURN VALUE + generated pseudo random number */ double my_rnd(struct rand_struct *rand_st) @@ -134,63 +124,12 @@ double my_rnd(struct rand_struct *rand_st) /* - Generate String of printable random characters of requested length - String will not be zero terminated. - + Generate binary hash from raw text string + Used for Pre-4.1 password handling SYNOPSIS - create_random_string() - length IN Lenght of - rand_st INOUT Structure used for number generation - target OUT Buffer for generation - - RETURN - none -*/ - -void create_random_string(int length,struct rand_struct *rand_st,char *target) -{ - char *end=target+length; - /* Use pointer arithmetics as it is faster way to do so. */ - for (; target0 -*/ - -int get_password_length(my_bool force_old_scramble) -{ - return (force_old_scramble) ? 16 : SHA1_HASH_SIZE*2+4+1; -} - - -/* - Get version of the password based on mysql.user password string - - SYNOPSIS - get_password_version() - password IN Password string as stored in mysql.user - - RETURN - 0 for pre 4.1 passwords - !0 password version char for newer passwords -*/ - -char get_password_version(const char *password) -{ - if (password==NULL) return 0; - if (password[0]==PVERSION41_CHAR) return PVERSION41_CHAR; - return 0; -} - - -/* - Get integer value of Hex character - - SYNOPSIS - char_val() - X IN Character to find value for - - RETURN - Appropriate integer value -*/ - - - -static inline unsigned int char_val(char X) -{ - return (uint) (X >= '0' && X <= '9' ? X-'0' : - X >= 'A' && X <= 'Z' ? X-'A'+10 : - X-'a'+10); -} - - -/* - Get Binary salt from password as in mysql.user format - - SYNOPSIS - get_salt_from_password() - res OUT Store binary salt here - password IN Password string as stored in mysql.user - - RETURN - none - - NOTE - This function does not have length check for passwords. It will just crash - Password hashes in old format must have length divisible by 8 -*/ - -void get_salt_from_password(ulong *res,const char *password) -{ - if (password) /* zero salt corresponds to empty password */ - { - if (password[0]==PVERSION41_CHAR) /* if new password */ - { - uint val=0; - uint i; - password++; /* skip version identifier */ - - /*get hashing salt from password and store in in the start of array */ - for (i=0 ; i < 4 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } - /* We process old passwords the same way as new ones in other case */ -#ifdef EXTRA_DEBUG - if (strlen(password)%8!=0) - fprintf(stderr,"Warning: Incorrect password length for salting: %d\n", - strlen(password)); -#endif - while (*password) - { - ulong val=0; - uint i; - for (i=0 ; i < 8 ; i++) - val=(val << 4)+char_val(*password++); - *res++=val; - } - } - return; -} - - -/* - Get string version as stored in mysql.user from salt form - - SYNOPSIS - make_password_from_salt() - to OUT Store resulting string password here - hash_res IN Password in salt format - password_version - IN According to which version salt should be treated - - RETURN - none -*/ - -void make_password_from_salt(char *to, ulong *hash_res,uint8 password_version) -{ - if (!password_version) /* Handling of old passwords. */ - sprintf(to,"%08lx%08lx",hash_res[0],hash_res[1]); - else - if (password_version==PVERSION41_CHAR) - sprintf(to,"%c%04x%08lx%08lx%08lx%08lx%08lx",PVERSION41_CHAR,(unsigned short)hash_res[0],hash_res[1], - hash_res[2],hash_res[3],hash_res[4],hash_res[5]); - else /* Just use empty password if we can't handle it. This should not happen */ - to[0]='\0'; -} - - -/* - Convert password in salted form to binary string password and hash-salt - For old password this involes one more hashing - - SYNOPSIS - get_hash_and_password() - salt IN Salt to convert from - pversion IN Password version to use - hash OUT Store zero ended hash here - bin_password OUT Store binary password here (no zero at the end) - - RETURN - 0 for pre 4.1 passwords - !0 password version char for newer passwords -*/ - -void get_hash_and_password(ulong *salt, uint8 pversion, char *hash, - unsigned char *bin_password) -{ - int t; - ulong* salt_end; - ulong val; - SHA1_CONTEXT context; - - if (pversion) /* New password version assumed */ - { - salt_end=salt+5; - sprintf(hash,"%04x",(unsigned short)salt[0]); - while (salt=0; t--) - { - bin_password[t]= (char) (val & 255); - val>>=8; /* Scroll 8 bits to get next part*/ - } - bin_password+=4; /* Get to next 4 chars*/ - } - } - else - { - unsigned char *bp= bin_password; /* Binary password loop pointer */ - - /* Use zero starting hash as an indication of old password */ - hash[0]=0; - salt_end=salt+2; - /* Encode salt using SHA1 here */ - sha1_reset(&context); - while (salt=0;t--) - { - bp[t]= (uchar) (val & 255); - val>>=8; /* Scroll 8 bits to get next part*/ - } - bp+= 4; /* Get to next 4 chars*/ - salt++; - } - /* Use 8 bytes of binary password for hash */ - sha1_input(&context,(uint8*)bin_password,8); - sha1_result(&context,(uint8*)bin_password); - } -} - - -/* - Create key from old password to decode scramble - Used in 4.1 authentication with passwords stored old way - - SYNOPSIS - create_key_from_old_password() - passwd IN Password used for key generation - key OUT Created 20 bytes key - - RETURN - None -*/ - - -void create_key_from_old_password(const char *passwd, char *key) -{ - char buffer[SCRAMBLE41_LENGTH]; /* Buffer for various needs */ - ulong salt[6]; /* Salt (large for safety) */ - /* At first hash password to the string stored in password */ - make_scrambled_password(buffer,passwd,1,(struct rand_struct *)NULL); - /* Now convert it to the salt form */ - get_salt_from_password(salt,buffer); - /* Finally get hash and bin password from salt */ - get_hash_and_password(salt,0,buffer,(unsigned char*) key); -} - - -/* - Scramble string with password - Used at pre 4.1 authentication phase. - - SYNOPSIS - scramble() - to OUT Store scrambled message here - message IN Message to scramble - password IN Password to use while scrambling - old_ver IN Forse old version random number generator - + scramble_323() + to OUT Store scrambled message here. Buffer must be at least + SCRAMBLE_LENGTH_323+1 bytes long + message IN Message to scramble. Message must be exactly + SRAMBLE_LENGTH_323 long and NULL terminated. + password IN Password to use while scrambling + old_ver IN Force old version random number generator RETURN End of scrambled string */ -char *scramble(char *to,const char *message,const char *password, - my_bool old_ver) +char *scramble_323(char *to, const char *message, const char *password, + my_bool old_ver) { struct rand_struct rand_st; - ulong hash_pass[2],hash_message[2]; - char message_buffer[9]; /* Real message buffer */ - char *msg=message_buffer; - - /* We use special message buffer now as new server can provide longer hash */ - - memcpy(message_buffer,message,8); - message_buffer[8]=0; + ulong hash_pass[2], hash_message[2]; if (password && password[0]) { char *to_start=to; hash_password(hash_pass,password); - hash_password(hash_message,message_buffer); + hash_password(hash_message, message); if (old_ver) old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); else randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); - while (*msg++) + hash_pass[1] ^ hash_message[1]); + while (*message++) *to++= (char) (floor(my_rnd(&rand_st)*31)+64); if (!old_ver) - { /* Make it harder to break */ + { /* Make it harder to break */ char extra=(char) (floor(my_rnd(&rand_st)*31)); while (to_start != to) - *(to_start++)^=extra; + *(to_start++)^=extra; } } *to=0; @@ -666,49 +214,40 @@ char *scramble(char *to,const char *message,const char *password, /* - Check scrambled message - Used for pre 4.1 password handling - + Check scrambled message + Used in pre 4.1 password handling SYNOPSIS - scramble() - scrambled IN Scrambled message to check - message IN Original message which was scramble - hash_pass IN Password which should be used for scrambling - old_ver IN Forse old version random number generator - - RETURN - 0 Password correct - !0 Password invalid + check_scramble_323() + scrambled IN scrambled message to check. + message IN original random message which was used for scrambling; must + be exactly SCRAMBLED_LENGTH_323 bytes long and + NULL-terminated. + hash_pass IN password which should be used for scrambling + old_ver IN force old (3.20) version random number generator + RETURN VALUE + 0 - password correct + !0 - password invalid */ -my_bool check_scramble(const char *scrambled, const char *message, - ulong *hash_pass, my_bool old_ver) +my_bool +check_scramble_323(const char *scrambled, const char *message, + ulong *hash_pass, my_bool old_ver) { struct rand_struct rand_st; ulong hash_message[2]; - char buff[16],*to,extra; /* Big enough for check */ + char buff[16],*to,extra; /* Big enough for check */ const char *pos; - char message_buffer[SCRAMBLE_LENGTH+1]; /* Copy of message */ - - /* We need to copy the message as this function can be called for MySQL 4.1 - scramble which is not zero ended and can have zeroes inside - We could just write zero to proper place in original message but - this would make it harder to understand code for next generations - */ - - memcpy(message_buffer,message,SCRAMBLE_LENGTH); /* Ignore the rest */ - message_buffer[SCRAMBLE_LENGTH]=0; /* Check if this exactly N bytes. Overwise this is something fishy */ - if (strlen(message_buffer)!=SCRAMBLE_LENGTH) - return 1; /* Wrong password */ + if (strlen(message) != SCRAMBLE_LENGTH_323) + return 1; /* Wrong password */ - hash_password(hash_message,message_buffer); + hash_password(hash_message,message); if (old_ver) old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); else randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); + hash_pass[1] ^ hash_message[1]); to=buff; for (pos=scrambled ; *pos ; pos++) *to++=(char) (floor(my_rnd(&rand_st)*31)+64); @@ -720,7 +259,300 @@ my_bool check_scramble(const char *scrambled, const char *message, while (*scrambled) { if (*scrambled++ != (char) (*to++ ^ extra)) - return 1; /* Wrong password */ + return 1; /* Wrong password */ } return 0; } + +static uint8 char_val(uint8 X) +{ + return (uint) (X >= '0' && X <= '9' ? X-'0' : + X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10); +} + + +/* + Convert password from hex string (as stored in mysql.user) to binary form. + SYNOPSIS + get_salt_from_password_323() + res OUT store salt here + password IN password string as stored in mysql.user + NOTE + This function does not have length check for passwords. It will just crash + Password hashes in old format must have length divisible by 8 +*/ + +void get_salt_from_password_323(ulong *res, const char *password) +{ + res[0]= res[1]= 0; + if (password) + { + while (*password) + { + ulong val=0; + uint i; + for (i=0 ; i < 8 ; i++) + val=(val << 4)+char_val(*password++); + *res++=val; + } + } +} + + +/* + Convert scrambled password from binary form to asciiz hex string. + SYNOPSIS + make_password_from_salt_323() + to OUT store resulting string password here, at least 17 bytes + salt IN password in salt format, 2 ulongs +*/ + +void make_password_from_salt_323(char *to, const ulong *salt) +{ + sprintf(to,"%08lx%08lx", salt[0], salt[1]); +} + + +/******************* MySQL 4.1.1 authentification routines ******************/ +/* + Generate string of printable random characters of requested length + SYNOPSIS + create_random_string() + to OUT buffer for generation; must be at least length+1 bytes + long; result string is always null-terminated + length IN how many random characters to put in buffer + rand_st INOUT structure used for number generation +*/ + +void create_random_string(char *to, uint length, struct rand_struct *rand_st) +{ + char *end= to + length; + /* Use pointer arithmetics as it is faster way to do so. */ + for (; to < end; to++) + *to= (char) (my_rnd(rand_st)*94+33); + *to= '\0'; +} + + +/* Character to use as version identifier for version 4.1 */ + +#define PVERSION41_CHAR '*' + + +/* + Convert given octet sequence to asciiz string of hex characters; + str..str+len and 'to' may not overlap. + SYNOPSIS + octet2hex() + buf OUT output buffer. Must be at least 2*len+1 bytes + str, len IN the beginning and the length of the input string +*/ + +static +void +octet2hex(char *to, const uint8 *str, uint len) +{ + static const char alphabet[] = { '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + const uint8 *str_end= str + len; + for (; str != str_end; ++str) + { + *to++= alphabet[(*str & 0xF0) >> 4]; + *to++= alphabet[*str & 0x0F]; + } + *to++= '\0'; +} + + +/* + Convert given asciiz string of hex (0..9 a..f) characters to octet + sequence. + SYNOPSIS + hex2octet() + to OUT buffer to place result; must be at least len/2 bytes + str, len IN begin, length for character string; str and to may not + overlap; len % 2 == 0 +*/ + +static +void +hex2octet(uint8 *to, const char *str, uint len) +{ + const char *str_end= str + len; + while (str < str_end) + { + *to= char_val(*str++) << 4; + *to++|= char_val(*str++); + } +} + + +/* + Encrypt/Decrypt function used for password encryption in authentication. + Simple XOR is used here but it is OK as we crypt random strings. Note, + that XOR(s1, XOR(s1, s2)) == s2, XOR(s1, s2) == XOR(s2, s1) + SYNOPSIS + my_crypt() + to OUT buffer to hold crypted string; must be at least len bytes + long; to and s1 (or s2) may be the same. + s1, s2 IN input strings (of equal length) + len IN length of s1 and s2 +*/ + +static +void +my_crypt(char *to, const uint8 *s1, const uint8 *s2, uint len) +{ + const uint8 *s1_end= s1 + len; + while (s1 < s1_end) + *to++= *s1++ ^ *s2++; +} + + +/* + MySQL 4.1.1 password hashing: SHA conversion (see RFC 2289, 3174) twice + applied to the password string, and then produced octet sequence is + converted to hex string. + The result of this function is used as return value from PASSWORD() and + is stored in the database. + SYNOPSIS + make_scrambled_password() + buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string + password IN NULL-terminated password string +*/ + +void +make_scrambled_password(char *to, const char *password) +{ + SHA1_CONTEXT sha1_context; + uint8 hash_stage2[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* stage 1: hash password */ + sha1_input(&sha1_context, (uint8 *) password, strlen(password)); + sha1_result(&sha1_context, (uint8 *) to); + /* stage 2: hash stage1 output */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, (uint8 *) to, SHA1_HASH_SIZE); + /* separate buffer is used to pass 'to' in octet2hex */ + sha1_result(&sha1_context, hash_stage2); + /* convert hash_stage2 to hex string */ + *to++= PVERSION41_CHAR; + octet2hex(to, hash_stage2, SHA1_HASH_SIZE); +} + + +/* + Produce an obscure octet sequence from password and random + string, recieved from the server. This sequence corresponds to the + password, but password can not be easily restored from it. The sequence + is then sent to the server for validation. Trailing zero is stored in + the buf. + This function is used by client to create authenticated reply to the + server's greeting. + SYNOPSIS + scramble() + buf OUT store scrambled string here. The buf must be at least + SHA1_HASH_SIZE+1 bytes long. + message IN random message, must be exactly SCRAMBLE_LENGTH long and + NULL-terminated. + password IN users' password + RETURN VALUE + end of scrambled string +*/ + +char * +scramble(char *to, const char *message, const char *password) +{ + SHA1_CONTEXT sha1_context; + uint8 hash_stage1[SHA1_HASH_SIZE]; + uint8 hash_stage2[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* stage 1: hash password */ + sha1_input(&sha1_context, (uint8 *) password, strlen(password)); + sha1_result(&sha1_context, hash_stage1); + /* stage 2: hash stage 1; note that hash_stage2 is stored in the database */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, hash_stage1, SHA1_HASH_SIZE); + sha1_result(&sha1_context, hash_stage2); + /* create crypt string as sha1(message, hash_stage2) */; + sha1_reset(&sha1_context); + sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH); + sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); + /* xor allows 'from' and 'to' overlap: lets take advantage of it */ + sha1_result(&sha1_context, (uint8 *) to); + my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH); + to[SHA1_HASH_SIZE]= '\0'; + return to + SHA1_HASH_SIZE; +} + + +/* + Check that scrambled message corresponds to the password; the function + is used by server to check that recieved reply is authentic. + This function does not check lengths of given strings: message must be + null-terminated, reply and hash_stage2 must be at least SHA1_HASH_SIZE + long (if not, something fishy is going on). + SYNOPSIS + check_scramble() + scramble IN clients' reply, presumably produced by scramble() + message IN original random string, previously sent to client + (presumably second argument of scramble()), must be + exactly SCRAMBLE_LENGTH long and NULL-terminated. + hash_stage2 IN hex2octet-decoded database entry + RETURN VALUE + 0 password is correct + !0 password is invalid +*/ + +my_bool +check_scramble(const char *scramble, const char *message, + const uint8 *hash_stage2) +{ + SHA1_CONTEXT sha1_context; + uint8 buf[SHA1_HASH_SIZE]; + uint8 hash_stage2_reassured[SHA1_HASH_SIZE]; + + sha1_reset(&sha1_context); + /* create key to encrypt scramble */ + sha1_input(&sha1_context, (const uint8 *) message, SCRAMBLE_LENGTH); + sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); + sha1_result(&sha1_context, buf); + /* encrypt scramble */ + my_crypt((char *) buf, buf, (const uint8 *) scramble, SCRAMBLE_LENGTH); + /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ + sha1_reset(&sha1_context); + sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); + sha1_result(&sha1_context, hash_stage2_reassured); + return memcmp(hash_stage2, hash_stage2_reassured, SHA1_HASH_SIZE); +} + + +/* + Convert scrambled password from asciiz hex string to binary form. + SYNOPSIS + get_salt_from_password() + res OUT buf to hold password. Must be at least SHA1_HASH_SIZE + bytes long. + password IN 4.1.1 version value of user.password +*/ + +void get_salt_from_password(uint8 *hash_stage2, const char *password) +{ + hex2octet(hash_stage2, password+1 /* skip '*' */, SHA1_HASH_SIZE * 2); +} + +/* + Convert scrambled password from binary form to asciiz hex string. + SYNOPSIS + make_password_from_salt() + to OUT store resulting string here, 2*SHA1_HASH_SIZE+2 bytes + salt IN password in salt format +*/ + +void make_password_from_salt(char *to, const uint8 *hash_stage2) +{ + *to++= PVERSION41_CHAR; + octet2hex(to, hash_stage2, SHA1_HASH_SIZE); +} diff --git a/sql/slave.cc b/sql/slave.cc index c45c11f8bef..91376df5590 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -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)) diff --git a/sql/slave.h b/sql/slave.h index 429456eb0bb..d3565565ded 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -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; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 1bdca7167e8..bbc6b74c3a9 100644 --- a/sql/sql_acl.cc +++ b/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 */ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index e6c6771253c..3370797820a 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -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, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index c233ffd422a..ebb3e819ddc 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -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 */ diff --git a/sql/sql_class.h b/sql/sql_class.h index ccfe2555518..d962cc8086e 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -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; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 5070466007e..384ec2bd4dd 100644 --- a/sql/sql_parse.cc +++ b/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 diff --git a/sql/sql_repl.h b/sql/sql_repl.h index e3d600b9798..b53551845bc 100644 --- a/sql/sql_repl.h +++ b/sql/sql_repl.h @@ -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; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1d605abe8a3..c8c9eb97a6a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -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 From 1d20b23247e19aa6aa5e309fc47d5c5c3bbfe433 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Fri, 4 Jul 2003 20:52:04 +0400 Subject: [PATCH 02/30] Bug fixes for authentication OLD_PASSWORD made a keyword to allow set password=old_password('abc') constructions. --- .bzrignore | 5 + include/mysql_com.h | 2 +- libmysql/libmysql.c | 18 ++-- sql-common/client.c | 24 +++-- sql/item_create.cc | 12 --- sql/item_create.h | 2 - sql/item_strfunc.cc | 16 +++ sql/item_strfunc.h | 3 +- sql/lex.h | 2 +- sql/password.c | 12 +-- sql/sql_acl.cc | 9 +- sql/sql_parse.cc | 256 ++++++++++++++++++++++---------------------- sql/sql_yacc.yy | 37 +++---- 13 files changed, 204 insertions(+), 194 deletions(-) diff --git a/.bzrignore b/.bzrignore index a23384d4170..7c0a871a951 100644 --- a/.bzrignore +++ b/.bzrignore @@ -624,3 +624,8 @@ vio/test-sslserver vio/viotest-ssl start_mysqld.sh mysys/main.cc +BitKeeper/post-commit +BitKeeper/post-commit-manual +build_tags.sh +tests/connect_test +BUILD/compile-pentium-maintainer diff --git a/include/mysql_com.h b/include/mysql_com.h index c1f18160667..784a7782855 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -327,7 +327,7 @@ 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); +void 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); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 6bc38abb060..8b83343df8f 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -619,16 +619,20 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* write scrambled password according to server capabilities */ if (passwd[0]) { - /* 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)); + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + *end++= SCRAMBLE_LENGTH; + scramble(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH; + } + else + end= scramble_323(end, mysql->scramble_323, passwd, + (my_bool) (mysql->protocol_version == 9)) + 1; } else - *end= '\0'; // empty password + *end++= '\0'; // empty password /* Add database if needed */ - end=strmov(end+1,db ? db : ""); + end= strmov(end, db ? db : "") + 1; /* Write authentication package */ simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); diff --git a/sql-common/client.c b/sql-common/client.c index 35dea62edc3..efb71021f8d 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1823,7 +1823,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, mysql->server_status, client_flag)); /* This needs to be changed as it's not useful with big packets */ if (user && user[0]) - strmake(end,user,32); /* Max user name */ + strmake(end,user,USERNAME_LENGTH); /* Max user name */ else read_user_name((char*) end); @@ -1835,21 +1835,25 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, end= strend(end) + 1; if (passwd[0]) { - /* 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)); + if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) + { + *end++= SCRAMBLE_LENGTH; + scramble(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH; + } + else + end= scramble_323(end, mysql->scramble_323, passwd, + (my_bool) (mysql->protocol_version == 9)) + 1; } else - *end= '\0'; /* empty password */ + *end++= '\0'; /* empty password */ /* Add database if needed */ if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB)) { - end=strmake(end+1,db,NAME_LEN); - mysql->db=my_strdup(db,MYF(MY_WME)); - db=0; + end= strmake(end, db, NAME_LEN) + 1; + mysql->db= my_strdup(db,MYF(MY_WME)); + db= 0; } /* Write authentication package */ if (my_net_write(net,buff,(ulong) (end-buff)) || net_flush(net)) diff --git a/sql/item_create.cc b/sql/item_create.cc index 90f42cee959..fbb26e83dfd 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -52,13 +52,6 @@ Item *create_func_ord(Item* a) return new Item_func_ord(a); } -Item *create_func_old_password(Item* a) -{ - return new Item_func_old_password(a); -} - - - Item *create_func_asin(Item* a) { return new Item_func_asin(a); @@ -332,11 +325,6 @@ Item *create_func_quarter(Item* a) return new Item_func_quarter(a); } -Item *create_func_password(Item* a) -{ - return new Item_func_password(a); -} - Item *create_func_radians(Item *a) { return new Item_func_units((char*) "radians",a,M_PI/180,0.0); diff --git a/sql/item_create.h b/sql/item_create.h index 4151f59a87f..2872451c034 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -69,14 +69,12 @@ Item *create_func_monthname(Item* a); Item *create_func_nullif(Item* a, Item *b); Item *create_func_oct(Item *); Item *create_func_ord(Item* a); -Item *create_func_old_password(Item* a); Item *create_func_period_add(Item* a, Item *b); Item *create_func_period_diff(Item* a, Item *b); Item *create_func_pi(void); Item *create_func_pow(Item* a, Item *b); Item *create_func_current_user(void); Item *create_func_quarter(Item* a); -Item *create_func_password(Item* a); Item *create_func_radians(Item *a); Item *create_func_release_lock(Item* a); Item *create_func_repeat(Item* a, Item *b); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index f8488565b75..def465363fe 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1360,6 +1360,14 @@ String *Item_func_password::val_str(String *str) return str; } +char *Item_func_password::alloc(THD *thd, const char *password) +{ + char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); + if (buff) + make_scrambled_password(buff, password); + return buff; +} + /* Item_func_old_password */ String *Item_func_old_password::val_str(String *str) @@ -1374,6 +1382,14 @@ String *Item_func_old_password::val_str(String *str) return str; } +char *Item_func_old_password::alloc(THD *thd, const char *password) +{ + char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); + if (buff) + make_scrambled_password_323(buff, password); + return buff; +} + #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.') diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 11f5a66b3d1..3e0239cf76a 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -270,6 +270,7 @@ public: String *val_str(String *str); void fix_length_and_dec() { max_length= SCRAMBLED_PASSWORD_CHAR_LENGTH; } const char *func_name() const { return "password"; } + static char *alloc(THD *thd, const char *password); }; @@ -288,7 +289,7 @@ public: 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);} + static char *alloc(THD *thd, const char *password); }; diff --git a/sql/lex.h b/sql/lex.h index bb6e7a81ab4..f105fd4d9c8 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -284,6 +284,7 @@ static SYMBOL symbols[] = { { "NULL", SYM(NULL_SYM),0,0}, { "NUMERIC", SYM(NUMERIC_SYM),0,0}, { "OFFSET", SYM(OFFSET_SYM),0,0}, + { "OLD_PASSWORD", SYM(OLD_PASSWORD),0,0}, { "ON", SYM(ON),0,0}, { "OPEN", SYM(OPEN_SYM),0,0}, { "OPTIMIZE", SYM(OPTIMIZE),0,0}, @@ -577,7 +578,6 @@ static SYMBOL sql_functions[] = { { "NUMPOINTS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_numpoints)}, { "OCTET_LENGTH", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_length)}, { "OCT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_oct)}, - { "OLD_PASSWORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_old_password)}, { "ORD", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_ord)}, { "OVERLAPS", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_overlaps)}, { "PERIOD_ADD", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_period_add)}, diff --git a/sql/password.c b/sql/password.c index be6514d89c6..bfdb453af01 100644 --- a/sql/password.c +++ b/sql/password.c @@ -446,22 +446,20 @@ make_scrambled_password(char *to, const char *password) Produce an obscure octet sequence from password and random string, recieved from the server. This sequence corresponds to the password, but password can not be easily restored from it. The sequence - is then sent to the server for validation. Trailing zero is stored in - the buf. + is then sent to the server for validation. Trailing zero is not stored + in the buf as it is not needed. This function is used by client to create authenticated reply to the server's greeting. SYNOPSIS scramble() buf OUT store scrambled string here. The buf must be at least - SHA1_HASH_SIZE+1 bytes long. + SHA1_HASH_SIZE bytes long. message IN random message, must be exactly SCRAMBLE_LENGTH long and NULL-terminated. password IN users' password - RETURN VALUE - end of scrambled string */ -char * +void scramble(char *to, const char *message, const char *password) { SHA1_CONTEXT sha1_context; @@ -483,8 +481,6 @@ scramble(char *to, const char *message, const char *password) /* xor allows 'from' and 'to' overlap: lets take advantage of it */ sha1_result(&sha1_context, (uint8 *) to); my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH); - to[SHA1_HASH_SIZE]= '\0'; - return to + SHA1_HASH_SIZE; } diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index bbc6b74c3a9..f88799c2843 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -51,7 +51,7 @@ static byte* acl_entry_get_key(acl_entry *entry,uint *length, return (byte*) entry->key; } -#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+17) +#define ACL_KEY_LENGTH (sizeof(long)+NAME_LEN+USERNAME_LENGTH+1) static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; @@ -208,7 +208,8 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) DBUG_PRINT("info",("user table fields: %d, password length: %d", table->fields, table->field[2]->field_length)); - if (table->field[2]->field_length < 41 && !use_old_passwords) + if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH && + !use_old_passwords) { sql_print_error("mysql.user table is not updated to new password format; " "Disabling new password usage until " @@ -516,6 +517,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) RETURN VALUE 0 success: thread data and mqh are updated 1 user not found or authentification failure + 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format. -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format. */ @@ -564,6 +566,9 @@ acl_getroot(THD *thd, USER_RESOURCES *mqh, else if (passwd_len == SCRAMBLE_LENGTH && user_i->salt_len == SCRAMBLE_LENGTH_323) res= -1; + else if (passwd_len == SCRAMBLE_LENGTH_323 && + user_i->salt_len == SCRAMBLE_LENGTH) + res= 2; /* linear search complete: */ break; } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 384ec2bd4dd..a6d3121158c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -195,11 +195,8 @@ end: 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 + -1 access denied or handshake error; error is sent to client; + >0 error, not sent to client */ static int check_user(THD *thd, enum enum_server_command command, @@ -208,87 +205,129 @@ static int check_user(THD *thd, enum enum_server_command command, { DBUG_ENTER("check_user"); + if (passwd_len != 0 && + passwd_len != SCRAMBLE_LENGTH && + passwd_len != SCRAMBLE_LENGTH_323) + DBUG_RETURN(ER_HANDSHAKE_ERROR); + /* Why this is set here? - probably to reset current DB to 'no database selected' in case of 'change user' failure. */ thd->db= 0; thd->db_length= 0; - + + char buff[NAME_LEN + 1]; /* to conditionally save db */ + 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 (res == -1) { - 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) + /* + 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. + */ + /* save db because network buffer is to hold new packet */ + if (db) { - 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); - } + strmake(buff, db, NAME_LEN); + db= buff; } - - /* 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]) + 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); + DBUG_RETURN(ER_HANDSHAKE_ERROR); + } + /* Final attempt to check the user based on reply */ + /* So as passwd is short, errcode is always >= 0 */ + res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323, + false); + } + /* here res is always >= 0 */ + if (res == 0) + { + if (!(thd->master_access & NO_ACCESS)) // authentification is OK { - if (mysql_change_db(thd, db)) + 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) { - if (thd->user_connect) - decrease_user_connections(thd->user_connect); - DBUG_RETURN(1); + VOID(pthread_mutex_lock(&LOCK_thread_count)); + bool count_ok= thread_count < max_connections + delayed_insert_threads + || thd->master_access & SUPER_ACL; + VOID(pthread_mutex_unlock(&LOCK_thread_count)); + if (!count_ok) + { // too many connections + send_error(thd, ER_CON_COUNT_ERROR); + DBUG_RETURN(-1); + } } + + /* Why logging is performed before all checks've passed? */ + mysql_log.write(thd,command, + (thd->priv_user == thd->user ? + (char*) "%s@%s on %s" : + (char*) "%s@%s as anonymous on %s"), + thd->user, thd->host_or_ip, + db ? db : (char*) ""); + + /* 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 */ + DBUG_RETURN(0); } - else - send_ok(thd); - thd->password= test(passwd_len); // remember for error messages - /* Ready to handle queries */ } - else if (res != -1) // authentication failure + else if (res == 2) // client gave short hash, server has long hash { - 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)); + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd,COM_CONNECT,ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); } - DBUG_RETURN(res); + net_printf(thd, ER_ACCESS_DENIED_ERROR, + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + mysql_log.write(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), + thd->user, + thd->host_or_ip, + passwd_len ? ER(ER_YES) : ER(ER_NO)); + DBUG_RETURN(-1); } /* @@ -491,60 +530,6 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) } -/* - Perform check for scrambled password, re-request scrambled password - from client if necessary. See also help for check_user. - SYNOPSIS - 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 @@ -643,7 +628,7 @@ check_connection(THD *thd) /* Old clients does not understand long scrambles, but can ignore packet - tail: that's why first part of scramble is placed here, and second + tail: that's why first part of the scramble is placed here, and second part at the end of packet. */ end= strmake(end, thd->scramble_323, SCRAMBLE_LENGTH_323) + 1; @@ -760,17 +745,23 @@ check_connection(THD *thd) char *user= end; char *passwd= strend(user)+1; - uint passwd_len= strlen(passwd); - - char *db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? - passwd+passwd_len+1 : 0; + char *db= passwd; + /* + Old clients send null-terminated string as password; new clients send + the size (1 byte) + string (not null-terminated). Hence in case of empty + password both send '\0'. + */ + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd); + db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? + db + passwd_len + 1 : 0; 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); + return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); } @@ -1137,8 +1128,15 @@ bool dispatch_command(enum enum_server_command command, THD *thd, statistic_increment(com_other, &LOCK_status); char *user= (char*) packet; char *passwd= strend(user)+1; - uint passwd_len= strlen(passwd); - char *db= passwd + passwd_len + 1; + /* + Old clients send null-terminated string ('\0' for empty string) for + password. New clients send the size (1 byte) + string (not null + terminated, so also '\0' for empty string). + */ + char *db= passwd; + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + *passwd++ : strlen(passwd); + db+= passwd_len + 1; /* Small check for incomming packet */ if ((uint) ((uchar*) db - net->read_pos) > packet_length) @@ -1163,7 +1161,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } - int res= authenticate(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); + int res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, false); if (res) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c8c9eb97a6a..ddf4b71e891 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -493,6 +493,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MULTIPOINT %token MULTIPOLYGON %token NOW_SYM +%token OLD_PASSWORD %token PASSWORD %token POINTFROMTEXT %token POINT_SYM @@ -2516,9 +2517,11 @@ simple_expr: { $$= new Item_func_now($3); Lex->safe_to_cache_query=0;} | PASSWORD '(' expr ')' { - $$= use_old_passwords ? (Item *) new Item_func_old_password($3) : - (Item *) new Item_func_password($3); - } + $$= use_old_passwords ? (Item *) new Item_func_old_password($3) : + (Item *) new Item_func_password($3); + } + | OLD_PASSWORD '(' expr ')' + { $$= new Item_func_old_password($3); } | POINT_SYM '(' expr ',' expr ')' { $$= new Item_func_point($3,$5); } | POINTFROMTEXT '(' expr ')' @@ -4412,6 +4415,7 @@ keyword: | NO_SYM {} | NONE_SYM {} | OFFSET_SYM {} + | OLD_PASSWORD {} | OPEN_SYM {} | PACK_KEYS_SYM {} | PARTIAL {} @@ -4603,24 +4607,15 @@ text_or_password: TEXT_STRING { $$=$1.str;} | PASSWORD '(' TEXT_STRING ')' { - if (!$3.length) - $$=$3.str; - else if (use_old_passwords) - { - 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; - } + $$= $3.length ? use_old_passwords ? + Item_func_old_password::alloc(YYTHD, $3.str) : + Item_func_password::alloc(YYTHD, $3.str) : + $3.str; + } + | OLD_PASSWORD '(' TEXT_STRING ')' + { + $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : + $3.str; } ; From a232225b69cc34f18f87a761e19fc0e3d2790d97 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Tue, 8 Jul 2003 02:36:14 +0400 Subject: [PATCH 03/30] Preliminary support for options --secure-auth, --old-passwords Support for option --old-protocol was removed. Some test performed. Tests for SSL and replication are pending. More strict following to specification for --old-passwords is in the TODO. --- include/mysql_com.h | 5 +- include/mysqld_error.h | 3 +- libmysql/libmysql.c | 6 +-- mysql-test/r/connect.result | 1 + mysql-test/r/func_crypt.result | 88 ++++++++++++++++++++++++++----- mysql-test/t/connect.test | 5 +- mysql-test/t/func_crypt.test | 34 ++++++++++-- sql-common/client.c | 6 +-- sql/item_strfunc.h | 2 +- sql/mysql_priv.h | 2 +- sql/mysqld.cc | 25 +++++---- sql/password.c | 55 ++++--------------- sql/set_var.cc | 6 +++ sql/set_var.h | 2 + sql/share/czech/errmsg.txt | 1 + sql/share/danish/errmsg.txt | 1 + sql/share/dutch/errmsg.txt | 1 + sql/share/english/errmsg.txt | 1 + sql/share/estonian/errmsg.txt | 1 + sql/share/french/errmsg.txt | 1 + sql/share/german/errmsg.txt | 1 + sql/share/greek/errmsg.txt | 1 + sql/share/hungarian/errmsg.txt | 1 + sql/share/italian/errmsg.txt | 1 + sql/share/japanese/errmsg.txt | 1 + sql/share/korean/errmsg.txt | 1 + sql/share/norwegian-ny/errmsg.txt | 1 + sql/share/norwegian/errmsg.txt | 1 + sql/share/polish/errmsg.txt | 1 + sql/share/portuguese/errmsg.txt | 1 + sql/share/romanian/errmsg.txt | 1 + sql/share/russian/errmsg.txt | 1 + sql/share/serbian/errmsg.txt | 1 + sql/share/slovak/errmsg.txt | 1 + sql/share/spanish/errmsg.txt | 1 + sql/share/swedish/errmsg.txt | 1 + sql/share/ukrainian/errmsg.txt | 1 + sql/sql_acl.cc | 82 ++++++++++++++++++---------- sql/sql_acl.h | 4 +- sql/sql_class.h | 1 + sql/sql_parse.cc | 32 ++++++++--- sql/sql_yacc.yy | 7 +-- 42 files changed, 264 insertions(+), 125 deletions(-) diff --git a/include/mysql_com.h b/include/mysql_com.h index 784a7782855..1f9d996c457 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -319,10 +319,9 @@ 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); +char *scramble_323(char *to, const char *message, const char *password); my_bool check_scramble_323(const char *, const char *message, - unsigned long *salt, my_bool old_ver); + unsigned long *salt); void get_salt_from_password_323(unsigned long *res, const char *password); void make_password_from_salt_323(char *to, const unsigned long *salt); diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 565c2812c50..341e0144ca3 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -286,4 +286,5 @@ #define ER_REVOKE_GRANTS 1267 #define ER_CANT_AGGREGATE_3COLLATIONS 1268 #define ER_CANT_AGGREGATE_NCOLLATIONS 1269 -#define ER_ERROR_MESSAGES 270 +#define ER_SERVER_IS_IN_SECURE_AUTH_MODE 1270 +#define ER_ERROR_MESSAGES 271 diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 8b83343df8f..1cf4880db24 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -626,8 +626,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, end+= SCRAMBLE_LENGTH; } else - end= scramble_323(end, mysql->scramble_323, passwd, - (my_bool) (mysql->protocol_version == 9)) + 1; + end= scramble_323(end, mysql->scramble_323, passwd); } else *end++= '\0'; // empty password @@ -651,8 +650,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, 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)); + scramble_323(buff, mysql->scramble_323, passwd); if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) { net->last_errno= CR_SERVER_LOST; diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 9c848c3434f..c0608af0de2 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -30,6 +30,7 @@ show tables; Tables_in_test update mysql.user set password=old_password("gambling2") where user="test"; flush privileges; +set password=old_password('gambling3'); show tables; Tables_in_mysql columns_priv diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index 461ae1e7e09..bd4c6d41d39 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -1,15 +1,79 @@ select length(encrypt('foo', 'ff')) <> 0; length(encrypt('foo', 'ff')) <> 0 1 -select password("a",""), password("a",NULL), password("","a"), password(NULL,"a"); -password("a","") password("a",NULL) password("","a") password(NULL,"a") -*2517f7235d68d4ba2e5019c93420523101157a792c01 NULL NULL -select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa"); -password("aaaaaaaaaaaaaaaa","a") password("a","aaaaaaaaaaaaaaaa") -*2cd3b9a44e9a9994789a30f935c92f45a96c5472f381 *37c7c5c794ff144819f2531bf03c57772cd84e40db09 -select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa'); -old_password('test') length(password("1")) length(encrypt('test')) encrypt('test','aa') -378b243e220ca493 45 13 aaqPiZY5xR5l. -select old_password(""), old_password(NULL), password(""), password(NULL); -old_password("") old_password(NULL) password("") password(NULL) - NULL NULL +select password('abc'); +password('abc') +*0d3ced9bec10a777aec23ccc353a8c08a633045e +select password(''); +password('') + +select old_password('abc'); +old_password('abc') +7cd2b5942be28759 +select old_password(''); +old_password('') + +select password('gabbagabbahey'); +password('gabbagabbahey') +*b0f99d2963660dd7e16b751ec9ee2f17b6a68fa6 +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +select length(password('1')); +length(password('1')) +41 +select length(encrypt('test')); +length(encrypt('test')) +13 +select encrypt('test','aa'); +encrypt('test','aa') +aaqPiZY5xR5l. +select old_password(NULL); +old_password(NULL) +NULL +select password(NULL); +password(NULL) +NULL +set global old_passwords=on; +select password(''); +password('') + +select old_password(''); +old_password('') + +select password('idkfa'); +password('idkfa') +*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set old_passwords=on; +select password('idkfa'); +password('idkfa') +5c078dc54ca0fcca +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set global old_passwords=off; +select password('idkfa'); +password('idkfa') +5c078dc54ca0fcca +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +set old_passwords=off; +select password('idkfa '); +password('idkfa ') +*2dc31d90647b4c1abc9231563d2236e96c9a2db2 +select password('idkfa'); +password('idkfa') +*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +select password(' idkfa'); +password(' idkfa') +*12b099e56bb7fe8d43c78fd834a9d1d11178d045 +select old_password('idkfa'); +old_password('idkfa') +5c078dc54ca0fcca +select old_password(' i d k f a '); +old_password(' i d k f a ') +5c078dc54ca0fcca diff --git a/mysql-test/t/connect.test b/mysql-test/t/connect.test index e6ccc52f0d4..7585ff0f608 100644 --- a/mysql-test/t/connect.test +++ b/mysql-test/t/connect.test @@ -48,8 +48,9 @@ flush privileges; #connect (con1,localhost,test,gambling2,""); #show tables; connect (con1,localhost,test,gambling2,mysql); +set password=old_password('gambling3'); show tables; -connect (con1,localhost,test,gambling2,test); +connect (con1,localhost,test,gambling3,test); show tables; # Re enable this one day if error handling on connect will take place @@ -63,7 +64,9 @@ show tables; #connect (con1,localhost,test,zorro,); #--error 1045 + # remove user 'test' so that other tests which may use 'test' # do not depend on this test. + delete from mysql.user where user="test"; flush privileges; diff --git a/mysql-test/t/func_crypt.test b/mysql-test/t/func_crypt.test index c72356bda1a..c1c7090cab3 100644 --- a/mysql-test/t/func_crypt.test +++ b/mysql-test/t/func_crypt.test @@ -4,7 +4,33 @@ select length(encrypt('foo', 'ff')) <> 0; --replace_result $1$aa$4OSUA5cjdx0RUQ08opV27/ aaqPiZY5xR5l. # Test new and old password handling functions -select password("a",""), password("a",NULL), password("","a"), password(NULL,"a"); -select password("aaaaaaaaaaaaaaaa","a"), password("a","aaaaaaaaaaaaaaaa"); -select old_password('test'), length(password("1")), length(encrypt('test')), encrypt('test','aa'); -select old_password(""), old_password(NULL), password(""), password(NULL); +select password('abc'); +select password(''); +select old_password('abc'); +select old_password(''); +select password('gabbagabbahey'); +select old_password('idkfa'); +select length(password('1')); +select length(encrypt('test')); +select encrypt('test','aa'); +select old_password(NULL); +select password(NULL); +set global old_passwords=on; +select password(''); +select old_password(''); +select password('idkfa'); +select old_password('idkfa'); +set old_passwords=on; +select password('idkfa'); +select old_password('idkfa'); +set global old_passwords=off; +select password('idkfa'); +select old_password('idkfa'); + +# this test shows that new scrambles honor spaces in passwords: +set old_passwords=off; +select password('idkfa '); +select password('idkfa'); +select password(' idkfa'); +select old_password('idkfa'); +select old_password(' i d k f a '); diff --git a/sql-common/client.c b/sql-common/client.c index efb71021f8d..9a0b7eb3fe2 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1842,8 +1842,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, end+= SCRAMBLE_LENGTH; } else - end= scramble_323(end, mysql->scramble_323, passwd, - (my_bool) (mysql->protocol_version == 9)) + 1; + end= scramble_323(end, mysql->scramble_323, passwd) + 1; } else *end++= '\0'; /* empty password */ @@ -1880,8 +1879,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, 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)); + scramble_323(buff, mysql->scramble_323, passwd); if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) { net->last_errno= CR_SERVER_LOST; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 3e0239cf76a..96e264fd8d2 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -277,7 +277,7 @@ public: /* 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() + 'old_passwords' session variable is set, and to handle OLD_PASSWORD() function. */ diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 13ff168e553..f8bf197249b 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -748,7 +748,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, lower_case_table_names; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_readonly; extern my_bool opt_enable_named_pipe; -extern my_bool opt_old_passwords, use_old_passwords; +extern my_bool opt_secure_auth; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2677973ff0e..c5f875bfcc8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -250,9 +250,10 @@ my_bool opt_local_infile, opt_external_locking, opt_slave_compressed_protocol; my_bool opt_safe_user_create = 0, opt_no_mix_types = 0; my_bool lower_case_table_names, opt_old_rpl_compat; my_bool opt_show_slave_auth_info, opt_sql_bin_update = 0; -my_bool opt_log_slave_updates= 0, opt_old_passwords=0, use_old_passwords=0; +my_bool opt_log_slave_updates= 0; my_bool opt_console= 0, opt_bdb, opt_innodb, opt_isam; my_bool opt_readonly, use_temp_pool, relay_log_purge; +my_bool opt_secure_auth= 0; volatile bool mqh_used = 0; uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; @@ -3452,7 +3453,8 @@ enum options OPT_EXPIRE_LOGS_DAYS, OPT_DEFAULT_WEEK_FORMAT, OPT_GROUP_CONCAT_MAX_LEN, - OPT_DEFAULT_COLLATION + OPT_DEFAULT_COLLATION, + OPT_SECURE_AUTH }; @@ -3753,9 +3755,10 @@ Does nothing yet.", (gptr*) &opt_no_mix_types, (gptr*) &opt_no_mix_types, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #endif - {"old-protocol", 'o', "Use the old (3.20) protocol client/server protocol.", - (gptr*) &protocol_version, (gptr*) &protocol_version, 0, GET_UINT, NO_ARG, - PROTOCOL_VERSION, 0, 0, 0, 0, 0}, + {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).", + (gptr*) &global_system_variables.old_passwords, + (gptr*) &max_system_variables.old_passwords, 0, GET_BOOL, NO_ARG, + 0, 0, 0, 0, 0, 0}, {"old-rpl-compat", OPT_OLD_RPL_COMPAT, "Use old LOAD DATA format in the binary log (don't save data in file).", (gptr*) &opt_old_rpl_compat, (gptr*) &opt_old_rpl_compat, 0, GET_BOOL, @@ -3824,8 +3827,6 @@ relay logs.", GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"safe-mode", OPT_SAFE, "Skip some optimize stages (for testing).", 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, - {"old-passwords", OPT_OLD_PASSWORDS, "Use old password encryption method (needed for 4.0 and older clients).", - (gptr*) &opt_old_passwords, (gptr*) &opt_old_passwords, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, #ifndef TO_BE_DELETED {"safe-show-database", OPT_SAFE_SHOW_DB, "Deprecated option; One should use GRANT SHOW DATABASES instead...", @@ -3835,6 +3836,9 @@ relay logs.", "Don't allow new user creation by the user who has no write privileges to the mysql.user table.", (gptr*) &opt_safe_user_create, (gptr*) &opt_safe_user_create, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"secure-auth", OPT_SECURE_AUTH, "Disallow authentication for accounts that have old (pre-4.1) passwords.", + (gptr*) &opt_secure_auth, (gptr*) &opt_secure_auth, 0, GET_BOOL, NO_ARG, + my_bool(0), 0, 0, 0, 0, 0}, {"server-id", OPT_SERVER_ID, "Uniquely identifies the server instance in the community of replication partners.", (gptr*) &server_id, (gptr*) &server_id, 0, GET_ULONG, REQUIRED_ARG, 0, 0, 0, @@ -4604,7 +4608,8 @@ static void mysql_init_variables(void) opt_log= opt_update_log= opt_bin_log= opt_slow_log= 0; opt_disable_networking= opt_skip_show_db=0; opt_logname= opt_update_logname= opt_binlog_index_name= opt_slow_logname=0; - opt_bootstrap= opt_myisam_log= use_old_passwords= 0; + opt_secure_auth= 0; + opt_bootstrap= opt_myisam_log= 0; mqh_used= 0; segfaulted= kill_in_progress= 0; cleanup_done= 0; @@ -4704,6 +4709,7 @@ static void mysql_init_variables(void) max_system_variables.select_limit= (ulonglong) HA_POS_ERROR; global_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; max_system_variables.max_join_size= (ulonglong) HA_POS_ERROR; + global_system_variables.old_passwords= 0; /* Variables that depends on compile options */ #ifndef DBUG_OFF @@ -4825,9 +4831,6 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), case 'L': strmake(language, argument, sizeof(language)-1); break; - case 'o': - protocol_version=PROTOCOL_VERSION-1; - break; #ifdef HAVE_REPLICATION case OPT_SLAVE_SKIP_ERRORS: init_slave_skip_errors(argument); diff --git a/sql/password.c b/sql/password.c index bfdb453af01..2e9139c12aa 100644 --- a/sql/password.c +++ b/sql/password.c @@ -88,24 +88,6 @@ void randominit(struct rand_struct *rand_st, ulong seed1, ulong seed2) } -/* - Old (MySQL 3.20) random generation structure initialization - XXX: is to be deleted very soon! - SYNOPSIS - old_randominit() - rand_st OUT Structure to initialize - seed1 IN First initialization parameter -*/ - -static void old_randominit(struct rand_struct *rand_st, ulong seed1) -{ /* For mysql 3.20.# */ - rand_st->max_value= 0x01FFFFFFL; - rand_st->max_value_dbl=(double) rand_st->max_value; - seed1%=rand_st->max_value; - rand_st->seed1=seed1 ; rand_st->seed2=seed1/2; -} - - /* Generate random number. SYNOPSIS @@ -178,13 +160,11 @@ void make_scrambled_password_323(char *to, const char *password) message IN Message to scramble. Message must be exactly SRAMBLE_LENGTH_323 long and NULL terminated. password IN Password to use while scrambling - old_ver IN Force old version random number generator RETURN End of scrambled string */ -char *scramble_323(char *to, const char *message, const char *password, - my_bool old_ver) +char *scramble_323(char *to, const char *message, const char *password) { struct rand_struct rand_st; ulong hash_pass[2], hash_message[2]; @@ -194,21 +174,15 @@ char *scramble_323(char *to, const char *message, const char *password, char *to_start=to; hash_password(hash_pass,password); hash_password(hash_message, message); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); while (*message++) *to++= (char) (floor(my_rnd(&rand_st)*31)+64); - if (!old_ver) - { /* Make it harder to break */ - char extra=(char) (floor(my_rnd(&rand_st)*31)); - while (to_start != to) - *(to_start++)^=extra; - } + char extra=(char) (floor(my_rnd(&rand_st)*31)); + while (to_start != to) + *(to_start++)^=extra; } - *to=0; + *to= 0; return to; } @@ -223,7 +197,6 @@ char *scramble_323(char *to, const char *message, const char *password, be exactly SCRAMBLED_LENGTH_323 bytes long and NULL-terminated. hash_pass IN password which should be used for scrambling - old_ver IN force old (3.20) version random number generator RETURN VALUE 0 - password correct !0 - password invalid @@ -231,7 +204,7 @@ char *scramble_323(char *to, const char *message, const char *password, my_bool check_scramble_323(const char *scrambled, const char *message, - ulong *hash_pass, my_bool old_ver) + ulong *hash_pass) { struct rand_struct rand_st; ulong hash_message[2]; @@ -243,18 +216,12 @@ check_scramble_323(const char *scrambled, const char *message, return 1; /* Wrong password */ hash_password(hash_message,message); - if (old_ver) - old_randominit(&rand_st,hash_pass[0] ^ hash_message[0]); - else - randominit(&rand_st,hash_pass[0] ^ hash_message[0], - hash_pass[1] ^ hash_message[1]); + randominit(&rand_st,hash_pass[0] ^ hash_message[0], + hash_pass[1] ^ hash_message[1]); to=buff; for (pos=scrambled ; *pos ; pos++) *to++=(char) (floor(my_rnd(&rand_st)*31)+64); - if (old_ver) - extra=0; - else - extra=(char) (floor(my_rnd(&rand_st)*31)); + extra=(char) (floor(my_rnd(&rand_st)*31)); to=buff; while (*scrambled) { diff --git a/sql/set_var.cc b/sql/set_var.cc index a281fac530a..a4ecf24d09f 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -210,6 +210,7 @@ sys_var_thd_ulong sys_net_retry_count("net_retry_count", &SV::net_retry_count, fix_net_retry_count); sys_var_thd_bool sys_new_mode("new", &SV::new_mode); +sys_var_thd_bool sys_old_passwords("old_passwords", &SV::old_passwords); sys_var_thd_ulong sys_preload_buff_size("preload_buffer_size", &SV::preload_buff_size); sys_var_thd_ulong sys_read_buff_size("read_buffer_size", @@ -236,6 +237,7 @@ sys_var_thd_enum sys_query_cache_type("query_cache_type", &SV::query_cache_type, &query_cache_type_typelib); #endif /* HAVE_QUERY_CACHE */ +sys_var_bool_ptr sys_secure_auth("secure_auth", &opt_secure_auth); sys_var_long_ptr sys_server_id("server_id",&server_id); sys_var_bool_ptr sys_slave_compressed_protocol("slave_compressed_protocol", &opt_slave_compressed_protocol); @@ -425,6 +427,7 @@ sys_var *sys_variables[]= &sys_net_wait_timeout, &sys_net_write_timeout, &sys_new_mode, + &sys_old_passwords, &sys_preload_buff_size, &sys_pseudo_thread_id, &sys_query_cache_size, @@ -443,6 +446,7 @@ sys_var *sys_variables[]= #endif &sys_rpl_recovery_rank, &sys_safe_updates, + &sys_secure_auth, &sys_select_limit, &sys_server_id, #ifdef HAVE_REPLICATION @@ -600,6 +604,7 @@ struct show_var_st init_vars[]= { {sys_net_retry_count.name, (char*) &sys_net_retry_count, SHOW_SYS}, {sys_net_write_timeout.name,(char*) &sys_net_write_timeout, SHOW_SYS}, {sys_new_mode.name, (char*) &sys_new_mode, SHOW_SYS}, + {sys_old_passwords.name, (char*) &sys_old_passwords, SHOW_SYS}, {"open_files_limit", (char*) &open_files_limit, SHOW_LONG}, {"pid_file", (char*) pidfile_name, SHOW_CHAR}, {"log_error", (char*) log_error_file, SHOW_CHAR}, @@ -620,6 +625,7 @@ struct show_var_st init_vars[]= { SHOW_SYS}, {sys_query_cache_size.name, (char*) &sys_query_cache_size, SHOW_SYS}, {sys_query_cache_type.name, (char*) &sys_query_cache_type, SHOW_SYS}, + {"secure_auth", (char*) &sys_secure_auth, SHOW_SYS}, #endif /* HAVE_QUERY_CACHE */ #ifdef HAVE_SMEM {"shared_memory", (char*) &opt_enable_shared_memory, SHOW_MY_BOOL}, diff --git a/sql/set_var.h b/sql/set_var.h index 5a0fbd21809..0622e504499 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -652,3 +652,5 @@ ulong fix_sql_mode(ulong sql_mode); extern sys_var_str sys_charset_system; CHARSET_INFO *get_old_charset_by_name(const char *old_name); + +extern sys_var_thd_bool sys_old_passwords; diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index 641b1384e9a..4f1836ef80a 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -275,3 +275,4 @@ v/* "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 574d26b7c1c..138c8c59a39 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -269,3 +269,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index c6c975cb141..f7a79dfa738 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -277,3 +277,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index f39c415fa55..c57527e2578 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -271,3 +271,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index de22d6fd111..e6ade1c7e3d 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -271,3 +271,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index 6c1187cd0e4..7ffd834fbcf 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 78d53034a71..2c6343eeeea 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -275,3 +275,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 347370f1ac8..228834f7937 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index 313275b3cb6..620234e2321 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -268,3 +268,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 8af7e3ba9f7..8091d3185ba 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 417a03978fb..962505423b1 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -268,3 +268,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 22395d0fb6a..aa0439fcd32 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index de6db62cdce..21dfad648b9 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -268,3 +268,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index 28db8caa8bc..e1d7501bca4 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -268,3 +268,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index fdf856c7e56..eaa2395b675 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -270,3 +270,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 397784dc7dd..89aded8afce 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 8f1cdb7b259..e76fd43e841 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -270,3 +270,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index ec41a6acb34..23d20c1b8fe 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -268,3 +268,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"óÅÒ×ÅÒ ÚÁÐÕÝÅÎ × ÒÅÖÉÍÅ --secure-auth (ÂÅÚÏÐÁÓÎÏÊ Á×ÔÏÒÉÚÁÃÉÉ), ÎÏ ÄÌÑ ÐÏÌØÚÏ×ÁÔÅÌÑ '%s@%s' ÐÁÒÏÌØ ÓÏÈÒÁÎ£Î × ÓÔÁÒÏÍ ÆÏÒÍÁÔÅ; ÎÅÏÂÈÏÄÉÍÏ ÏÂÎÏ×ÉÔØ ÆÏÒÍÁÔ ÐÁÒÏÌÑ" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 77d35be2fc9..e0ba1413f5e 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -262,3 +262,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 33cabdfc752..bc8949127fd 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -274,3 +274,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 35e26f35ff7..9a6dcd90a4d 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -267,3 +267,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 9cdcb20db35..3538ba3c47e 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -266,3 +266,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 5a614714de2..9bc07241856 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -271,3 +271,4 @@ "Can't revoke all privileges, grant for one or more of the requested users" "Illegal mix of collations (%s,%s), (%s,%s), (%s,%s) for operation '%s'", "Illegal mix of collations for operation '%s'", +"Server is running in --secure-auth mode, but '%s@%s' has a password in the old format; please change the password to the new format" diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index f88799c2843..ee544335a99 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -87,16 +87,33 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) 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) + else if (password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { get_salt_from_password_323((ulong *) acl_user->salt, password); - acl_user->salt_len= password_len/2; + acl_user->salt_len= SCRAMBLE_LENGTH_323; } else acl_user->salt_len= 0; } +/* + This after_update function is used when user.password is less than + SCRAMBLE_LENGTH bytes. +*/ + +static void restrict_update_of_old_passwords_var(THD *thd, + enum_var_type var_type) +{ + if (var_type == OPT_GLOBAL) + { + pthread_mutex_lock(&LOCK_global_system_variables); + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + } + else + thd->variables.old_passwords= 1; +} + /* Read grant privileges from the privilege tables in the 'mysql' database. @@ -139,8 +156,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ thd->store_globals(); - /* Use passwords according to command line option */ - use_old_passwords= opt_old_passwords; acl_cache->clear(1); // Clear locked hostname cache thd->db= my_strdup("mysql",MYF(0)); @@ -197,24 +212,43 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) init_read_record(&read_record_info,thd,table=tables[1].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100)); - if (table->field[2]->field_length == 8 && - protocol_version == PROTOCOL_VERSION) + if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) { - sql_print_error("Old 'user' table. " - "(Check README or the Reference manual). " - "Continuing --old-protocol"); /* purecov: tested */ - protocol_version=9; /* purecov: tested */ + sql_print_error("Fatal error: mysql.user table is damaged or in " + "unsupported 3.20 format."); + goto end; } DBUG_PRINT("info",("user table fields: %d, password length: %d", table->fields, table->field[2]->field_length)); - if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH && - !use_old_passwords) + + pthread_mutex_lock(&LOCK_global_system_variables); + if (table->field[2]->field_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) { - 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; + if (opt_secure_auth) + { + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("Fatal error: mysql.user table is in old format, " + "but server started with --secure-auth option."); + goto end; + } + sys_old_passwords.after_update= restrict_update_of_old_passwords_var; + if (global_system_variables.old_passwords) + pthread_mutex_unlock(&LOCK_global_system_variables); + else + { + global_system_variables.old_passwords= 1; + pthread_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("mysql.user table is not updated to new password format; " + "Disabling new password usage until " + "mysql_fix_privilege_tables is run"); + } + thd->variables.old_passwords= 1; + } + else + { + sys_old_passwords.after_update= 0; + pthread_mutex_unlock(&LOCK_global_system_variables); } allow_all_hosts=0; @@ -229,12 +263,6 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) if (user.salt_len == 0 && password_len != 0) { 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. " @@ -513,7 +541,6 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) 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 @@ -521,9 +548,8 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format. */ -int -acl_getroot(THD *thd, USER_RESOURCES *mqh, - const char *passwd, uint passwd_len, bool old_version) +int acl_getroot(THD *thd, USER_RESOURCES *mqh, + const char *passwd, uint passwd_len) { DBUG_ENTER("acl_getroot"); @@ -557,7 +583,7 @@ acl_getroot(THD *thd, USER_RESOURCES *mqh, 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) + (ulong *) user_i->salt) == 0) { acl_user= user_i; res= 0; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 3370797820a..b4ee1a9b15f 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -135,8 +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); -int acl_getroot(THD *thd, USER_RESOURCES *mqh, - const char *passwd, uint passwd_len, bool old_ver); +int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, + uint passwd_len); 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, diff --git a/sql/sql_class.h b/sql/sql_class.h index d962cc8086e..5e46f44634b 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -386,6 +386,7 @@ struct system_variables my_bool log_warnings; my_bool low_priority_updates; my_bool new_mode; + my_bool old_passwords; CHARSET_INFO *character_set_server; CHARSET_INFO *character_set_database; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a6d3121158c..4b7486c7b4f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -204,7 +204,22 @@ static int check_user(THD *thd, enum enum_server_command command, bool check_count) { DBUG_ENTER("check_user"); - + + my_bool opt_secure_auth_local; + pthread_mutex_lock(&LOCK_global_system_variables); + opt_secure_auth_local= opt_secure_auth; + pthread_mutex_unlock(&LOCK_global_system_variables); + + /* + If the server is running in secure auth mode, short scrambles are + forbidden. + */ + if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) + { + net_printf(thd, ER_NOT_SUPPORTED_AUTH_MODE); + mysql_log.write(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + DBUG_RETURN(-1); + } if (passwd_len != 0 && passwd_len != SCRAMBLE_LENGTH && passwd_len != SCRAMBLE_LENGTH_323) @@ -220,9 +235,7 @@ static int check_user(THD *thd, enum enum_server_command command, char buff[NAME_LEN + 1]; /* to conditionally save db */ USER_RESOURCES ur; - int res= acl_getroot(thd, &ur, passwd, passwd_len, - protocol_version == 9 || - !(thd->client_capabilities & CLIENT_LONG_PASSWORD)); + int res= acl_getroot(thd, &ur, passwd, passwd_len); if (res == -1) { /* @@ -231,6 +244,14 @@ static int check_user(THD *thd, enum enum_server_command command, scramble_323()). Here we please client to send scrambled_password in old format. */ + if (opt_secure_auth_local) + { + net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, + thd->user, thd->host_or_ip); + mysql_log.write(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), + thd->user, thd->host_or_ip); + DBUG_RETURN(-1); + } /* save db because network buffer is to hold new packet */ if (db) { @@ -247,8 +268,7 @@ static int check_user(THD *thd, enum enum_server_command command, } /* Final attempt to check the user based on reply */ /* So as passwd is short, errcode is always >= 0 */ - res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323, - false); + res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); } /* here res is always >= 0 */ if (res == 0) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index ddf4b71e891..e283991b496 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2517,7 +2517,8 @@ simple_expr: { $$= new Item_func_now($3); Lex->safe_to_cache_query=0;} | PASSWORD '(' expr ')' { - $$= use_old_passwords ? (Item *) new Item_func_old_password($3) : + $$= YYTHD->variables.old_passwords ? + (Item *) new Item_func_old_password($3) : (Item *) new Item_func_password($3); } | OLD_PASSWORD '(' expr ')' @@ -4607,7 +4608,7 @@ text_or_password: TEXT_STRING { $$=$1.str;} | PASSWORD '(' TEXT_STRING ')' { - $$= $3.length ? use_old_passwords ? + $$= $3.length ? YYTHD->variables.old_passwords ? Item_func_old_password::alloc(YYTHD, $3.str) : Item_func_password::alloc(YYTHD, $3.str) : $3.str; @@ -4923,7 +4924,7 @@ grant_user: $$=$1; $1->password=$4; if ($4.length) { - if (use_old_passwords) + if (YYTHD->variables.old_passwords) { char *buff= (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); From 09e53b016967a84126dc50474a54010712893bf1 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Fri, 18 Jul 2003 18:25:54 +0400 Subject: [PATCH 04/30] Style fixes, comments for 4.1.1 authorization Now special 1-byte packet is used for request of old password Fixed bug with --skip-grant-tables and acl_getroot --- include/mysql.h | 4 +- include/mysql_com.h | 4 +- libmysql/libmysql.c | 10 +++-- sql-common/client.c | 13 ++++--- sql/password.c | 93 ++++++++++++++++++++++----------------------- sql/protocol.cc | 19 +++++++++ sql/protocol.h | 1 + sql/sql_acl.cc | 54 ++++++++++++++++---------- sql/sql_class.h | 8 +--- sql/sql_crypt.cc | 2 +- sql/sql_parse.cc | 56 ++++++++++++++------------- 11 files changed, 148 insertions(+), 116 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index bf05f6e8e37..079808d9ba7 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -228,8 +228,8 @@ typedef struct st_mysql my_bool free_me; /* If free in mysql_close */ my_bool reconnect; /* set to 1 if automatic reconnect */ - char scramble[SCRAMBLE_LENGTH+1]; /* for new servers */ - char scramble_323[SCRAMBLE_LENGTH_323+1]; /* for old servers */ + /* session-wide random string */ + char scramble[max(SCRAMBLE_LENGTH,SCRAMBLE_LENGTH_323)+1]; /* Set if this is the original connection, not a master or a slave we have diff --git a/include/mysql_com.h b/include/mysql_com.h index e5782235934..87cedafb93d 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -318,9 +318,9 @@ void randominit(struct rand_struct *, unsigned long seed1, double my_rnd(struct rand_struct *); void create_random_string(char *to, uint length, struct rand_struct *rand_st); -void hash_password(ulong *to, const char *password); +void hash_password(ulong *to, const char *password, uint password_len); void make_scrambled_password_323(char *to, const char *password); -char *scramble_323(char *to, const char *message, const char *password); +void scramble_323(char *to, const char *message, const char *password); my_bool check_scramble_323(const char *, const char *message, unsigned long *salt); void get_salt_from_password_323(unsigned long *res, const char *password); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index ee7fd6a2576..cec9980bae7 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -626,7 +626,10 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, end+= SCRAMBLE_LENGTH; } else - end= scramble_323(end, mysql->scramble_323, passwd); + { + scramble_323(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH_323 + 1; + } } else *end++= '\0'; // empty password @@ -642,15 +645,14 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, if (pkt_length == packet_error) goto error; - if (net->read_pos[0] == mysql->scramble_323[0] && - pkt_length == SCRAMBLE_LENGTH_323 + 1 && + if (pkt_length == 1 && net->read_pos[0] == 254 && 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); + scramble_323(buff, mysql->scramble, passwd); if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) { net->last_errno= CR_SERVER_LOST; diff --git a/sql-common/client.c b/sql-common/client.c index 9a0b7eb3fe2..aac5be4b690 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1641,9 +1641,8 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, 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); + strmake(mysql->scramble, 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); @@ -1842,7 +1841,10 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, end+= SCRAMBLE_LENGTH; } else - end= scramble_323(end, mysql->scramble_323, passwd) + 1; + { + scramble_323(end, mysql->scramble, passwd); + end+= SCRAMBLE_LENGTH_323 + 1; + } } else *end++= '\0'; /* empty password */ @@ -1871,15 +1873,14 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, 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 && + if (pkt_length == 1 && net->read_pos[0] == 254 && 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); + scramble_323(buff, mysql->scramble, passwd); if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) { net->last_errno= CR_SERVER_LOST; diff --git a/sql/password.c b/sql/password.c index 2e9139c12aa..16227aab611 100644 --- a/sql/password.c +++ b/sql/password.c @@ -110,15 +110,17 @@ double my_rnd(struct rand_struct *rand_st) Used for Pre-4.1 password handling SYNOPSIS hash_password() - result OUT store hash in this location - password IN plain text password to build hash + result OUT store hash in this location + password IN plain text password to build hash + password_len IN password length (password may be not null-terminated) */ -void hash_password(ulong *result, const char *password) +void hash_password(ulong *result, const char *password, uint password_len) { register ulong nr=1345345333L, add=7, nr2=0x12345671L; ulong tmp; - for (; *password ; password++) + const char *password_end= password + password_len; + for (; password < password_end; password++) { if (*password == ' ' || *password == '\t') continue; /* skip space in password */ @@ -129,7 +131,6 @@ void hash_password(ulong *result, const char *password) } result[0]=nr & (((ulong) 1L << 31) -1L); /* Don't use sign bit (str2int) */; result[1]=nr2 & (((ulong) 1L << 31) -1L); - return; } @@ -145,7 +146,7 @@ void hash_password(ulong *result, const char *password) void make_scrambled_password_323(char *to, const char *password) { ulong hash_res[2]; - hash_password(hash_res, password); + hash_password(hash_res, password, strlen(password)); sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]); } @@ -157,14 +158,12 @@ void make_scrambled_password_323(char *to, const char *password) scramble_323() to OUT Store scrambled message here. Buffer must be at least SCRAMBLE_LENGTH_323+1 bytes long - message IN Message to scramble. Message must be exactly - SRAMBLE_LENGTH_323 long and NULL terminated. + message IN Message to scramble. Message must be at least + SRAMBLE_LENGTH_323 bytes long. password IN Password to use while scrambling - RETURN - End of scrambled string */ -char *scramble_323(char *to, const char *message, const char *password) +void scramble_323(char *to, const char *message, const char *password) { struct rand_struct rand_st; ulong hash_pass[2], hash_message[2]; @@ -172,18 +171,18 @@ char *scramble_323(char *to, const char *message, const char *password) if (password && password[0]) { char *to_start=to; - hash_password(hash_pass,password); - hash_password(hash_message, message); + hash_password(hash_pass,password, strlen(password)); + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); randominit(&rand_st,hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]); - while (*message++) + const char *message_end= message + SCRAMBLE_LENGTH_323; + for (; message < message_end; message++) *to++= (char) (floor(my_rnd(&rand_st)*31)+64); char extra=(char) (floor(my_rnd(&rand_st)*31)); while (to_start != to) *(to_start++)^=extra; } *to= 0; - return to; } @@ -192,11 +191,13 @@ char *scramble_323(char *to, const char *message, const char *password) Used in pre 4.1 password handling SYNOPSIS check_scramble_323() - scrambled IN scrambled message to check. - message IN original random message which was used for scrambling; must - be exactly SCRAMBLED_LENGTH_323 bytes long and - NULL-terminated. - hash_pass IN password which should be used for scrambling + scrambled scrambled message to check. + message original random message which was used for scrambling; must + be exactly SCRAMBLED_LENGTH_323 bytes long and + NULL-terminated. + hash_pass password which should be used for scrambling + All params are IN. + RETURN VALUE 0 - password correct !0 - password invalid @@ -211,11 +212,7 @@ check_scramble_323(const char *scrambled, const char *message, char buff[16],*to,extra; /* Big enough for check */ const char *pos; - /* Check if this exactly N bytes. Overwise this is something fishy */ - if (strlen(message) != SCRAMBLE_LENGTH_323) - return 1; /* Wrong password */ - - hash_password(hash_message,message); + hash_password(hash_message, message, SCRAMBLE_LENGTH_323); randominit(&rand_st,hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]); to=buff; @@ -231,7 +228,7 @@ check_scramble_323(const char *scrambled, const char *message, return 0; } -static uint8 char_val(uint8 X) +static inline uint8 char_val(uint8 X) { return (uint) (X >= '0' && X <= '9' ? X-'0' : X >= 'A' && X <= 'Z' ? X-'A'+10 : X-'a'+10); @@ -280,7 +277,10 @@ void make_password_from_salt_323(char *to, const ulong *salt) } -/******************* MySQL 4.1.1 authentification routines ******************/ +/* + **************** MySQL 4.1.1 authentification routines ************* +*/ + /* Generate string of printable random characters of requested length SYNOPSIS @@ -315,19 +315,16 @@ void create_random_string(char *to, uint length, struct rand_struct *rand_st) str, len IN the beginning and the length of the input string */ -static -void +static void octet2hex(char *to, const uint8 *str, uint len) { - static const char alphabet[] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; const uint8 *str_end= str + len; for (; str != str_end; ++str) { - *to++= alphabet[(*str & 0xF0) >> 4]; - *to++= alphabet[*str & 0x0F]; + *to++= _dig_vec[(*str & 0xF0) >> 4]; + *to++= _dig_vec[*str & 0x0F]; } - *to++= '\0'; + *to= '\0'; } @@ -341,15 +338,14 @@ octet2hex(char *to, const uint8 *str, uint len) overlap; len % 2 == 0 */ -static -void +static void hex2octet(uint8 *to, const char *str, uint len) { const char *str_end= str + len; while (str < str_end) { - *to= char_val(*str++) << 4; - *to++|= char_val(*str++); + register char tmp= char_val(*str++); + *to++= (tmp << 4) | char_val(*str++); } } @@ -366,9 +362,8 @@ hex2octet(uint8 *to, const char *str, uint len) len IN length of s1 and s2 */ -static -void -my_crypt(char *to, const uint8 *s1, const uint8 *s2, uint len) +static void +my_crypt(char *to, const uchar *s1, const uchar *s2, uint len) { const uint8 *s1_end= s1 + len; while (s1 < s1_end) @@ -447,7 +442,7 @@ scramble(char *to, const char *message, const char *password) sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); /* xor allows 'from' and 'to' overlap: lets take advantage of it */ sha1_result(&sha1_context, (uint8 *) to); - my_crypt(to, (const uint8 *) to, hash_stage1, SCRAMBLE_LENGTH); + my_crypt(to, (const uchar *) to, hash_stage1, SCRAMBLE_LENGTH); } @@ -459,11 +454,13 @@ scramble(char *to, const char *message, const char *password) long (if not, something fishy is going on). SYNOPSIS check_scramble() - scramble IN clients' reply, presumably produced by scramble() - message IN original random string, previously sent to client - (presumably second argument of scramble()), must be - exactly SCRAMBLE_LENGTH long and NULL-terminated. - hash_stage2 IN hex2octet-decoded database entry + scramble clients' reply, presumably produced by scramble() + message original random string, previously sent to client + (presumably second argument of scramble()), must be + exactly SCRAMBLE_LENGTH long and NULL-terminated. + hash_stage2 hex2octet-decoded database entry + All params are IN. + RETURN VALUE 0 password is correct !0 password is invalid @@ -483,7 +480,7 @@ check_scramble(const char *scramble, const char *message, sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); sha1_result(&sha1_context, buf); /* encrypt scramble */ - my_crypt((char *) buf, buf, (const uint8 *) scramble, SCRAMBLE_LENGTH); + my_crypt((char *) buf, buf, (const uchar *) scramble, SCRAMBLE_LENGTH); /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ sha1_reset(&sha1_context); sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); diff --git a/sql/protocol.cc b/sql/protocol.cc index 1b9256c7723..7e009aa6a17 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -348,6 +348,25 @@ send_eof(THD *thd, bool no_flush) } DBUG_VOID_RETURN; } + +/* + Please client to send scrambled_password in old format. + SYNOPSYS + send_old_password_request() + thd thread handle + + RETURN VALUE + 0 ok + !0 error +*/ + +bool send_old_password_request(THD *thd) +{ + static char buff[1]= { (char) 254 }; + NET *net= &thd->net; + return my_net_write(net, buff, 1) || net_flush(net); +} + #endif /* EMBEDDED_LIBRARY */ diff --git a/sql/protocol.h b/sql/protocol.h index ffd61b3e848..405d3d4aebe 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -164,6 +164,7 @@ void net_printf(THD *thd,uint sql_errno, ...); void send_ok(THD *thd, ha_rows affected_rows=0L, ulonglong id=0L, const char *info=0); void send_eof(THD *thd, bool no_flush=0); +bool send_old_password_request(THD *thd); char *net_store_length(char *packet,ulonglong length); char *net_store_length(char *packet,uint length); char *net_store_data(char *to,const char *from, uint length); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ee544335a99..fafe725201d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -264,10 +264,11 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) { switch (password_len) { case 45: /* 4.1: to be removed */ - sql_print_error("Found 4.1 style password for user '%s'. " + sql_print_error("Found 4.1 style password for user '%s@%s'. " "Ignoring user. " "You should change password for this user.", - user.user ? user.user : ""); + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); break; default: sql_print_error("Found invalid password for user: '%s@%s'; " @@ -526,23 +527,30 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) /* Seek ACL entry for a user, check password, SSL cypher, and if everything is OK, update THD user data and USER_RESOURCES struct. + + IMPLEMENTATION 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 + acl_getroot() + thd 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 user resources; on success mqh is reset, else + unchanged + passwd 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 length of passwd, must be one of 0, 8, + SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH + 'thd' and 'mqh' are updated on success; other params are IN. + RETURN VALUE - 0 success: thread data and mqh are updated + 0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are + updated 1 user not found or authentification failure 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format. -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format. @@ -553,9 +561,16 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, { DBUG_ENTER("acl_getroot"); - if (!initialized) /* if no data allow anything */ - { - DBUG_RETURN(1); + if (!initialized) + { + /* + here if mysqld's been started with --skip-grant-tables option. + */ + thd->priv_user= (char *) ""; // privileges for + *thd->priv_host= '\0'; // the user are unknown + thd->master_access= ~NO_ACCESS; // everything is allowed + bzero(mqh, sizeof(*mqh)); + DBUG_RETURN(0); } int res= 1; @@ -582,7 +597,7 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, 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, + check_scramble_323(passwd, thd->scramble, (ulong *) user_i->salt) == 0) { acl_user= user_i; @@ -1346,8 +1361,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, { int error = -1; bool old_row_exists=0; - char empty_string[]= { '\0' }; - char *password= empty_string; + const char *password= ""; uint password_len= 0; char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_user_table"); diff --git a/sql/sql_class.h b/sql/sql_class.h index e80b5d9c8d5..1557ede8305 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -547,13 +547,7 @@ public: DYNAMIC_ARRAY user_var_events; /* 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]; + char scramble[max(SCRAMBLE_LENGTH, SCRAMBLE_LENGTH_323)+1]; uint8 query_cache_type; // type of query cache processing bool slave_thread; diff --git a/sql/sql_crypt.cc b/sql/sql_crypt.cc index 930ecfffef7..b0b8050e311 100644 --- a/sql/sql_crypt.cc +++ b/sql/sql_crypt.cc @@ -32,7 +32,7 @@ SQL_CRYPT::SQL_CRYPT(const char *password) { ulong rand_nr[2]; - hash_password(rand_nr,password); + hash_password(rand_nr,password, strlen(password)); crypt_init(rand_nr); } diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7878f2c8a29..9d75029a997 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -181,17 +181,20 @@ end: Check if user exist and password supplied is correct. SYNOPSIS check_user() - 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 + thd thread handle, thd->{host,user,ip} are used + command originator of the check: now check_user is called + during connect and change user procedures; used for + logging. + passwd scrambled password recieved from client + passwd_len length of scrambled password + db database name to connect to, may be NULL + check_count dont know exactly + 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. + should be done with this in mind; 'thd' is INOUT, all other params + are 'IN'. + RETURN VALUE 0 OK; thd->user, thd->master_access, thd->priv_user, thd->db and thd->db_access are updated; OK is sent to client; @@ -226,8 +229,10 @@ static int check_user(THD *thd, enum enum_server_command command, DBUG_RETURN(ER_HANDSHAKE_ERROR); /* - Why this is set here? - probably to reset current DB to 'no database - selected' in case of 'change user' failure. + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidently free a wrong pointer + if connect failed. Also in case of 'CHANGE USER' failure, current + database will be switched to 'no database selected'. */ thd->db= 0; thd->db_length= 0; @@ -244,6 +249,7 @@ static int check_user(THD *thd, enum enum_server_command command, scramble_323()). Here we please client to send scrambled_password in old format. */ + NET *net= &thd->net; if (opt_secure_auth_local) { net_printf(thd, ER_SERVER_IS_IN_SECURE_AUTH_MODE, @@ -258,9 +264,7 @@ static int check_user(THD *thd, enum enum_server_command command, 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) || + if (send_old_password_request(thd) || my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) // We have to read very { // specific packet size inc_host_errors(&thd->remote.sin_addr); @@ -288,7 +292,7 @@ static int check_user(THD *thd, enum enum_server_command command, { VOID(pthread_mutex_lock(&LOCK_thread_count)); bool count_ok= thread_count < max_connections + delayed_insert_threads - || thd->master_access & SUPER_ACL; + || (thd->master_access & SUPER_ACL); VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (!count_ok) { // too many connections @@ -305,7 +309,11 @@ static int check_user(THD *thd, enum enum_server_command command, thd->user, thd->host_or_ip, db ? db : (char*) ""); - /* Why is it set here? */ + /* + This is the default access rights for the current database. It's + set to 0 here because we don't have an active database yet (and we + may not have an active database to set. + */ thd->db_access=0; /* Don't allow user to connect if he has done too many queries */ @@ -554,9 +562,10 @@ static void reset_mqh(THD *thd, LEX_USER *lu, bool get_them= 0) Perform handshake, authorize client and update thd ACL variables. SYNOPSIS check_connection() - thd INOUT thread handle + thd thread handle + RETURN - 0 success, OK is sent to user + 0 success, OK is sent to user, thd is updated. -1 error, which is sent to user > 0 error code (not sent to user) */ @@ -644,14 +653,12 @@ check_connection(THD *thd) each handshake. */ create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); - strmake(thd->scramble_323, thd->scramble, SCRAMBLE_LENGTH_323); - /* Old clients does not understand long scrambles, but can ignore packet tail: that's why first part of the scramble is placed here, and second part at the end of packet. */ - end= strmake(end, thd->scramble_323, SCRAMBLE_LENGTH_323) + 1; + end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; int2store(end, client_flags); /* write server characteristics: up to 16 bytes allowed */ @@ -754,8 +761,6 @@ check_connection(THD *thd) 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; if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && @@ -778,9 +783,8 @@ check_connection(THD *thd) if (thd->user) x_free(thd->user); - thd->user= my_strdup(user, MYF(0)); - if (!thd->user) - return(ER_OUT_OF_RESOURCES); + if (!(thd->user= my_strdup(user, MYF(0)))) + return (ER_OUT_OF_RESOURCES); return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); } From 715f8747d49bec27ae4948e74e18247dc4e9d52b Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Fri, 18 Jul 2003 18:57:21 +0400 Subject: [PATCH 05/30] few compile-time bugs fixed --- include/mysql.h | 2 +- sql/sql_class.cc | 2 +- sql/sql_class.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/mysql.h b/include/mysql.h index 079808d9ba7..10bacffb2b1 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -229,7 +229,7 @@ typedef struct st_mysql my_bool reconnect; /* set to 1 if automatic reconnect */ /* session-wide random string */ - char scramble[max(SCRAMBLE_LENGTH,SCRAMBLE_LENGTH_323)+1]; + char scramble[SCRAMBLE_LENGTH+1]; /* Set if this is the original connection, not a master or a slave we have diff --git a/sql/sql_class.cc b/sql/sql_class.cc index ebb3e819ddc..c7fb2ce3368 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -139,7 +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'; + *scramble= '\0'; init(); /* Initialize sub structures */ diff --git a/sql/sql_class.h b/sql/sql_class.h index 1557ede8305..4eb1d6057a9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -547,7 +547,7 @@ public: DYNAMIC_ARRAY user_var_events; /* scramble - random string sent to client on handshake */ - char scramble[max(SCRAMBLE_LENGTH, SCRAMBLE_LENGTH_323)+1]; + char scramble[SCRAMBLE_LENGTH+1]; uint8 query_cache_type; // type of query cache processing bool slave_thread; From 6ac0c55c103685c9e4bcd76080a5d5cb428c6130 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Mon, 28 Jul 2003 21:21:42 +0200 Subject: [PATCH 06/30] INSTALL-WIN-SOURCE: Change mode to -rw-rw-r-- --- INSTALL-WIN-SOURCE | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 INSTALL-WIN-SOURCE diff --git a/INSTALL-WIN-SOURCE b/INSTALL-WIN-SOURCE old mode 100755 new mode 100644 From ce691c7c61392a75313a129a83800c93b3f13f01 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Thu, 31 Jul 2003 15:47:00 +0400 Subject: [PATCH 07/30] merge commit --- sql-common/client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql-common/client.c b/sql-common/client.c index aac5be4b690..b43084855b9 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -1878,7 +1878,7 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, { /* By sending this very specific reply server asks us to send scrambled - password in old format. The reply contains scramble_323. + password in old format. */ scramble_323(buff, mysql->scramble, passwd); if (my_net_write(net, buff, SCRAMBLE_LENGTH_323 + 1) || net_flush(net)) From 79922d946ac7c7c9c77c10fd7f831f14f2a9892c Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Tue, 5 Aug 2003 21:14:15 +0200 Subject: [PATCH 08/30] table checksum background: my_checksum() mysys function NISAM checksum code moved from mysys to isam/ - it's obsolete MyISAM checksum code moved to mysys table's checksum accessible from sql layer SHOW TABLE STATUS shows checksum (WL#646) code cleanup --- include/my_sys.h | 6 ++-- include/myisam.h | 2 -- isam/isamchk.c | 2 +- isam/isamdef.h | 1 + isam/open.c | 19 ++++++++++++ isam/pack_isam.c | 2 +- myisam/mi_checksum.c | 19 +++++------- mysys/checksum.c | 21 ++++++------- sql/ha_myisam.cc | 8 +++++ sql/ha_myisam.h | 1 + sql/handler.h | 71 ++++++++++++++++++++++---------------------- sql/sql_lex.h | 20 ++++++------- sql/sql_select.cc | 2 +- sql/sql_show.cc | 6 ++++ sql/sql_yacc.yy | 24 ++++++++------- 15 files changed, 119 insertions(+), 85 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index e4125a2e7e6..6721d77a8af 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -213,7 +213,7 @@ extern uint get_charset_number(const char *cs_name); extern const char *get_charset_name(uint cs_number); extern CHARSET_INFO *get_charset(uint cs_number, myf flags); extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags); -extern CHARSET_INFO *get_charset_by_csname(const char *cs_name, +extern CHARSET_INFO *get_charset_by_csname(const char *cs_name, uint cs_flags, myf my_flags); extern void free_charsets(void); extern char *get_charsets_dir(char *buf); @@ -507,6 +507,8 @@ typedef struct st_keycache ulonglong size; } KEY_CACHE; +typedef uint32 ha_checksum; + #include /* Prototypes for mysys and my_func functions */ @@ -749,7 +751,7 @@ extern void print_defaults(const char *conf_file, const char **groups); extern my_bool my_compress(byte *, ulong *, ulong *); extern my_bool my_uncompress(byte *, ulong *, ulong *); extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen); -extern ulong checksum(const byte *mem, uint count); +extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count); extern uint my_bit_log2(ulong value); uint my_count_bits(ulonglong v); extern void my_sleep(ulong m_seconds); diff --git a/include/myisam.h b/include/myisam.h index e85d3057672..0ffcdae8567 100644 --- a/include/myisam.h +++ b/include/myisam.h @@ -53,8 +53,6 @@ extern "C" { #define mi_portable_sizeof_char_ptr 8 -typedef uint32 ha_checksum; - /* Param to/from mi_info */ typedef struct st_mi_isaminfo /* Struct from h_info */ diff --git a/isam/isamchk.c b/isam/isamchk.c index dc772290e13..939a4be732f 100644 --- a/isam/isamchk.c +++ b/isam/isamchk.c @@ -1328,7 +1328,7 @@ int extend; print_error("Found wrong record at %lu",(ulong) start_recpos); got_error=1; } - crc^=checksum(record,info->s->base.reclength); + crc^=_nisam_checksum(record,info->s->base.reclength); link_used+=info->s->pack.ref_length; used+=block_info.rec_len+info->s->pack.ref_length; } diff --git a/isam/isamdef.h b/isam/isamdef.h index 0884b18e997..54656b6842e 100644 --- a/isam/isamdef.h +++ b/isam/isamdef.h @@ -358,6 +358,7 @@ extern int _nisam_read_pack_record(N_INFO *info,ulong filepos,byte *buf); extern int _nisam_read_rnd_pack_record(N_INFO*, byte *,ulong, int); extern int _nisam_pack_rec_unpack(N_INFO *info,byte *to,byte *from, uint reclength); +extern ulong _nisam_checksum(const byte *mem, uint count); typedef struct st_sortinfo { uint key_length; diff --git a/isam/open.c b/isam/open.c index 48fab27cac1..824fbe804ee 100644 --- a/isam/open.c +++ b/isam/open.c @@ -453,3 +453,22 @@ static void setup_key_functions(register N_KEYDEF *keyinfo) } return; } + +/* + Calculate a long checksum for a memoryblock. Used to verify pack_isam + + SYNOPSIS + checksum() + mem Pointer to memory block + count Count of bytes +*/ + +ulong _nisam_checksum(const byte *mem, uint count) +{ + ulong crc; + for (crc= 0; count-- ; mem++) + crc= ((crc << 1) + *((uchar*) mem)) + + test(crc & ((ulong) 1L << (8*sizeof(ulong)-1))); + return crc; +} + diff --git a/isam/pack_isam.c b/isam/pack_isam.c index fd12aac1e09..9108070f918 100644 --- a/isam/pack_isam.c +++ b/isam/pack_isam.c @@ -738,7 +738,7 @@ static int get_statistic(MRG_INFO *mrg,HUFF_COUNTS *huff_counts) { if (! error) { - crc^=checksum(record,reclength); + crc^=_nisam_checksum(record,reclength); for (pos=record,count=huff_counts ; count < end_count ; count++, diff --git a/myisam/mi_checksum.c b/myisam/mi_checksum.c index a760b03a032..982f77ee81f 100644 --- a/myisam/mi_checksum.c +++ b/myisam/mi_checksum.c @@ -28,30 +28,29 @@ ha_checksum mi_checksum(MI_INFO *info, const byte *buf) { const byte *pos; const byte *end; + ulong length; switch (rec->type) { case FIELD_BLOB: { - ulong length=_mi_calc_blob_length(rec->length- + length=_mi_calc_blob_length(rec->length- mi_portable_sizeof_char_ptr, buf); memcpy((char*) &pos, buf+rec->length- mi_portable_sizeof_char_ptr, sizeof(char*)); - end=pos+length; break; } case FIELD_VARCHAR: { - uint length; length=uint2korr(buf); - pos=buf+2; end=pos+length; + pos=buf+2; break; } default: - pos=buf; end=buf+rec->length; + length=rec->length; + pos=buf; break; } - for ( ; pos != end ; pos++) - crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8)); + crc=my_checksum(crc, pos, length); } return crc; } @@ -59,9 +58,5 @@ ha_checksum mi_checksum(MI_INFO *info, const byte *buf) ha_checksum mi_static_checksum(MI_INFO *info, const byte *pos) { - ha_checksum crc; - const byte *end=pos+info->s->base.reclength; - for (crc=0; pos != end; pos++) - crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8)); - return crc; + return my_checksum(0, pos, info->s->base.reclength); } diff --git a/mysys/checksum.c b/mysys/checksum.c index 1dd135c7ad9..2ae139b81c3 100644 --- a/mysys/checksum.c +++ b/mysys/checksum.c @@ -19,19 +19,20 @@ #include "my_sys.h" /* - Calculate a long checksum for a memoryblock. Used to verify pack_isam - + Calculate a long checksum for a memoryblock. + SYNOPSIS - checksum() - mem Pointer to memory block - count Count of bytes + my_checksum() + crc start value for crc + pos pointer to memory block + length length of the block */ -ulong checksum(const byte *mem, uint count) +ha_checksum my_checksum(ha_checksum crc, const byte *pos, uint length) { - ulong crc; - for (crc= 0; count-- ; mem++) - crc= ((crc << 1) + *((uchar*) mem)) + - test(crc & ((ulong) 1L << (8*sizeof(ulong)-1))); + const byte *end=pos+length; + for ( ; pos != end ; pos++) + crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8)); return crc; } + diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc index 4f1021232a4..d2cbdec36f1 100644 --- a/sql/ha_myisam.cc +++ b/sql/ha_myisam.cc @@ -236,6 +236,8 @@ int ha_myisam::open(const char *name, int mode, uint test_if_locked) VOID(mi_extra(file, HA_EXTRA_WAIT_LOCK, 0)); if (!table->db_record_offset) int_table_flags|=HA_REC_NOT_IN_SEQ; + if (file->s->options & (HA_OPTION_CHECKSUM | HA_OPTION_COMPRESS_RECORD)) + int_table_flags|=HA_HAS_CHECKSUM; return (0); } @@ -1398,3 +1400,9 @@ int ha_myisam::ft_read(byte * buf) table->status=error ? STATUS_NOT_FOUND: 0; return error; } + +uint ha_myisam::checksum() const +{ + return (uint)file->s->state.checksum; +} + diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h index 8486e25556b..e4e3192af10 100644 --- a/sql/ha_myisam.h +++ b/sql/ha_myisam.h @@ -64,6 +64,7 @@ class ha_myisam: public handler uint max_keys() const { return MI_MAX_KEY; } uint max_key_parts() const { return MAX_REF_PARTS; } uint max_key_length() const { return MI_MAX_KEY_LENGTH; } + uint checksum() const; int open(const char *name, int mode, uint test_if_locked); int close(void); diff --git a/sql/handler.h b/sql/handler.h index 08a7c83d328..c26efa47ded 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,38 +42,39 @@ #define HA_ADMIN_INVALID -5 /* Bits in table_flags() to show what database can do */ -#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record - (To update with RND-read) */ -#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ -#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ -#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; - It returns a position to ha_r_rnd */ -#define HA_HAS_GEOMETRY 16 -#define HA_NO_INDEX 32 /* No index needed for next/prev */ -#define HA_KEY_READ_WRONG_STR 64 /* keyread returns converted strings */ -#define HA_NULL_KEY 128 /* One can have keys with NULL */ -#define HA_DUPP_POS 256 /* ha_position() gives dupp row */ -#define HA_NO_BLOBS 512 /* Doesn't support blobs */ -#define HA_BLOB_KEY (HA_NO_BLOBS*2) /* key on blob */ -#define HA_AUTO_PART_KEY (HA_BLOB_KEY*2) -#define HA_REQUIRE_PRIMARY_KEY (HA_AUTO_PART_KEY*2) -#define HA_NOT_EXACT_COUNT (HA_REQUIRE_PRIMARY_KEY*2) -#define HA_NO_WRITE_DELAYED (HA_NOT_EXACT_COUNT*2) -#define HA_PRIMARY_KEY_IN_READ_INDEX (HA_NO_WRITE_DELAYED*2) -#define HA_DROP_BEFORE_CREATE (HA_PRIMARY_KEY_IN_READ_INDEX*2) -#define HA_NOT_READ_AFTER_KEY (HA_DROP_BEFORE_CREATE*2) -#define HA_NOT_DELETE_WITH_CACHE (HA_NOT_READ_AFTER_KEY*2) -#define HA_NO_TEMP_TABLES (HA_NOT_DELETE_WITH_CACHE*2) -#define HA_NO_PREFIX_CHAR_KEYS (HA_NO_TEMP_TABLES*2) -#define HA_CAN_FULLTEXT (HA_NO_PREFIX_CHAR_KEYS*2) -#define HA_CAN_SQL_HANDLER (HA_CAN_FULLTEXT*2) -#define HA_NO_AUTO_INCREMENT (HA_CAN_SQL_HANDLER*2) +#define HA_READ_RND_SAME 1 /* Read RND-record to KEY-record + (To update with RND-read) */ +#define HA_KEYPOS_TO_RNDPOS 2 /* ha_info gives pos to record */ +#define HA_TABLE_SCAN_ON_INDEX 4 /* No separate data/index file */ +#define HA_REC_NOT_IN_SEQ 8 /* ha_info don't return recnumber; + It returns a position to ha_r_rnd */ +#define HA_HAS_GEOMETRY (1 << 4) +#define HA_NO_INDEX (1 << 5) /* No index needed for next/prev */ +#define HA_KEY_READ_WRONG_STR (1 << 6) /* keyread returns converted strings */ +#define HA_NULL_KEY (1 << 7) /* One can have keys with NULL */ +#define HA_DUPP_POS (1 << 8) /* ha_position() gives dupp row */ +#define HA_NO_BLOBS (1 << 9) /* Doesn't support blobs */ +#define HA_BLOB_KEY (1 << 10) /* key on blob */ +#define HA_AUTO_PART_KEY (1 << 11) +#define HA_REQUIRE_PRIMARY_KEY (1 << 12) +#define HA_NOT_EXACT_COUNT (1 << 13) +#define HA_NO_WRITE_DELAYED (1 << 14) +#define HA_PRIMARY_KEY_IN_READ_INDEX (1 << 15) +#define HA_DROP_BEFORE_CREATE (1 << 16) +#define HA_NOT_READ_AFTER_KEY (1 << 17) +#define HA_NOT_DELETE_WITH_CACHE (1 << 18) +#define HA_NO_TEMP_TABLES (1 << 19) +#define HA_NO_PREFIX_CHAR_KEYS (1 << 20) +#define HA_CAN_FULLTEXT (1 << 21) +#define HA_CAN_SQL_HANDLER (1 << 22) +#define HA_NO_AUTO_INCREMENT (1 << 23) +#define HA_HAS_CHECKSUM (1 << 24) /* Next record gives next record according last record read (even if database is updated after read). Not used at this point. */ -#define HA_LASTKEY_ORDER (HA_NO_AUTO_INCREMENT*2) +#define HA_LASTKEY_ORDER (1 << 25) /* bits in index_flags(index_number) for what you can do with index */ @@ -304,8 +305,8 @@ public: virtual bool check_and_repair(THD *thd) {return 1;} virtual int optimize(THD* thd,HA_CHECK_OPT* check_opt); virtual int analyze(THD* thd, HA_CHECK_OPT* check_opt); - virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); virtual int preload_keys(THD* thd, HA_CHECK_OPT* check_opt); + virtual int backup(THD* thd, HA_CHECK_OPT* check_opt); /* restore assumes .frm file must exist, and that generate_table() has been called; It will just copy the data file and run repair. @@ -323,8 +324,8 @@ public: virtual char* get_foreign_key_create_info() { return(NULL);} /* gets foreign key create string from InnoDB */ virtual void init_table_handle_for_HANDLER() - { return; } /* prepare InnoDB for HANDLER */ - virtual void free_foreign_key_create_info(char* str) {} + { return; } /* prepare InnoDB for HANDLER */ + virtual void free_foreign_key_create_info(char* str) {} /* The following can be called without an open handler */ virtual const char *table_type() const =0; virtual const char **bas_ext() const =0; @@ -340,6 +341,7 @@ public: virtual uint max_key_part_length() { return 255; } virtual uint min_record_length(uint options) const { return 1; } virtual bool low_byte_first() const { return 1; } + virtual uint checksum() const { return 0; } virtual bool is_crashed() const { return 0; } virtual bool auto_repair() const { return 0; } @@ -353,13 +355,12 @@ public: /* Type of table for caching query */ virtual uint8 table_cache_type() { return HA_CACHE_TBL_NONTRANSACT; } - /* - Is query with this cable cachable (have sense only for ASKTRANSACT + /* + Is query with this table cachable (have sense only for ASKTRANSACT tables) */ - static bool caching_allowed(THD* thd, char* table_key, + static bool caching_allowed(THD* thd, char* table_key, uint key_length, uint8 cahe_type); - }; /* Some extern variables used with handlers */ @@ -388,7 +389,7 @@ int ha_delete_table(enum db_type db_type, const char *path); void ha_drop_database(char* path); void ha_key_cache(void); void ha_resize_key_cache(void); -int ha_start_stmt(THD *thd); +int ha_start_stmt(THD *thd); int ha_report_binlog_offset_and_commit(THD *thd, char *log_file_name, my_off_t end_offset); int ha_commit_complete(THD *thd); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index faf7e16e54a..df73f12d32f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -447,15 +447,21 @@ typedef struct st_lex SELECT_LEX *all_selects_list; uchar *ptr,*tok_start,*tok_end,*end_of_query; char *length,*dec,*change,*name; + char *help_arg; char *backup_dir; /* For RESTORE/BACKUP */ char* to_log; /* For PURGE MASTER LOGS TO */ time_t purge_time; /* For PURGE MASTER LOGS BEFORE */ char* x509_subject,*x509_issuer,*ssl_cipher; char* found_colon; /* For multi queries - next query */ - enum SSL_type ssl_type; /* defined in violite.h */ String *wild; sql_exchange *exchange; select_result *result; + Item *default_value, *comment; + LEX_USER *grant_user; + gptr yacc_yyss,yacc_yyvs; + THD *thd; + CHARSET_INFO *charset; + SQL_LIST *gorder_list; List col_list; List ref_list; @@ -473,11 +479,6 @@ typedef struct st_lex SQL_LIST proc_list, auxilliary_table_list; TYPELIB *interval; create_field *last_field; - Item *default_value, *comment; - uint uint_geom_type; - LEX_USER *grant_user; - gptr yacc_yyss,yacc_yyvs; - THD *thd; udf_func udf; HA_CHECK_OPT check_opt; // check/repair options HA_CREATE_INFO create_info; @@ -486,6 +487,7 @@ typedef struct st_lex ulong thread_id,type; enum_sql_command sql_command; thr_lock_type lock_option; + enum SSL_type ssl_type; /* defined in violite.h */ enum my_lex_states next_state; enum enum_duplicates duplicates; enum enum_tx_isolation tx_isolation; @@ -493,17 +495,15 @@ typedef struct st_lex enum ha_rkey_function ha_rkey_mode; enum enum_enable_or_disable alter_keys_onoff; enum enum_var_type option_type; + uint uint_geom_type; uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; uint param_count; + uint slave_thd_opt; bool drop_primary, drop_if_exists, drop_temporary, local_file; bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; bool derived_tables, describe; bool safe_to_cache_query; - uint slave_thd_opt; - CHARSET_INFO *charset; - char *help_arg; - SQL_LIST *gorder_list; st_lex() {} inline void uncacheable() { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0aa7e67a12b..12d93dfc674 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -2349,7 +2349,7 @@ static void optimize_keyuse(JOIN *join, DYNAMIC_ARRAY *keyuse_array) Constant tables are ignored. To avoid bad matches, we don't make ref_table_rows less than 100. */ - keyuse->ref_table_rows= ~(table_map) 0; // If no ref + keyuse->ref_table_rows= ~(ha_rows) 0; // If no ref if (keyuse->used_tables & (map= (keyuse->used_tables & ~join->const_table_map & ~OUTER_REF_TABLE_BIT))) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 9084269f486..7dd2004b664 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -503,6 +503,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Charset",32)); item->maybe_null=1; + field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21)); + item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Create_options",255)); item->maybe_null=1; field_list.push_back(item=new Item_empty_string("Comment",80)); @@ -588,6 +590,10 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild) } str= (table->table_charset ? table->table_charset->name : "default"); protocol->store(str, system_charset_info); + if (file->table_flags() & HA_HAS_CHECKSUM) + protocol->store((ulonglong)file->checksum()); + else + protocol->store_null(); // Checksum { char option_buff[350],*ptr; ptr=option_buff; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index a5ac04dc775..2e8ed389a98 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -730,17 +730,19 @@ verb_clause: | describe | do | drop - | grant - | insert | flush + | grant + | handler + | help + | insert + | kill | load | lock - | kill | optimize | preload | purge | rename - | repair + | repair | replace | reset | restore @@ -748,15 +750,14 @@ verb_clause: | rollback | select | set + | show | slave | start - | show | truncate - | handler | unlock | update | use - | help; + ; /* help */ @@ -1817,7 +1818,7 @@ optimize: { LEX *lex=Lex; lex->sql_command = SQLCOM_OPTIMIZE; - lex->no_write_to_binlog= $2; + lex->no_write_to_binlog= $2; lex->check_opt.init(); } table_list opt_mi_check_type @@ -3327,7 +3328,7 @@ do: DO_SYM */ drop: - DROP opt_temporary TABLE_SYM if_exists table_list opt_restrict + DROP opt_temporary table_or_tables if_exists table_list opt_restrict { LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_TABLE; @@ -3363,7 +3364,7 @@ drop: LEX *lex=Lex; lex->sql_command = SQLCOM_DROP_USER; lex->users_list.empty(); - } + } user_list {} ; @@ -4851,7 +4852,7 @@ grant_privilege_list: | grant_privilege_list ',' grant_privilege; grant_privilege: - SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} + SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} | REFERENCES { Lex->which_columns = REFERENCES_ACL;} opt_column_list {} @@ -5228,3 +5229,4 @@ subselect_end: LEX *lex=Lex; lex->current_select = lex->current_select->return_after_parsing(); }; + From 08d7f298eee94368ea2f704a3897a0cc9054fd6a Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Thu, 21 Aug 2003 16:15:06 +0200 Subject: [PATCH 09/30] fixed a crash on COMPRESS() and other zlib-dependent functions when compiled w/o zlib moved them all from different places to item_strfunc.{h,cc} checksum table command Com_xxx status variables updated --- sql/item_create.cc | 21 +++------------ sql/item_func.cc | 36 ++----------------------- sql/item_func.h | 21 --------------- sql/item_geofunc.h | 24 ----------------- sql/item_strfunc.cc | 55 ++++++++++++++++++++++++++++++------- sql/item_strfunc.h | 49 ++++++++++++++++++++++++++++++++- sql/mysql_priv.h | 1 + sql/mysqld.cc | 11 +++++--- sql/sql_lex.h | 4 +-- sql/sql_parse.cc | 8 ++++++ sql/sql_table.cc | 66 ++++++++++++++++++++++++++++++++++++++++++++- sql/sql_yacc.yy | 15 +++++++++-- 12 files changed, 197 insertions(+), 114 deletions(-) diff --git a/sql/item_create.cc b/sql/item_create.cc index e18d1cfa189..b0704931535 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -107,14 +107,6 @@ Item *create_func_cot(Item* a) new Item_func_tan(a)); } - -#ifdef HAVE_COMPRESS -Item *create_func_crc32(Item* a) -{ - return new Item_func_crc32(a); -} -#endif - Item *create_func_date_format(Item* a,Item *b) { return new Item_func_date_format(a,b,0); @@ -666,13 +658,10 @@ Item *create_func_point(Item *a, Item *b) return new Item_func_point(a, b); } -#if !defined(HAVE_COMPRESS) - -Item *create_func_compress (Item*a __attribute__((unused))){return 0;} -Item *create_func_uncompress (Item*a __attribute__((unused))){return 0;} -Item *create_func_uncompressed_length(Item*a __attribute__((unused))){return 0;} - -#else +Item *create_func_crc32(Item* a) +{ + return new Item_func_crc32(a); +} Item *create_func_compress(Item* a) { @@ -689,8 +678,6 @@ Item *create_func_uncompressed_length(Item* a) return new Item_func_uncompressed_length(a); } -#endif - Item *create_func_datediff(Item *a, Item *b) { return new Item_func_minus(new Item_func_to_days(a), diff --git a/sql/item_func.cc b/sql/item_func.cc index a7c5b35b8db..6350d3874a1 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -27,19 +27,16 @@ #include #include #include -#ifdef HAVE_COMPRESS -#include -#endif static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, const char *fname) { my_error(ER_CANT_AGGREGATE_2COLLATIONS,MYF(0), - c1.collation->name,c1.derivation_name(), + c1.collation->name,c1.derivation_name(), c2.collation->name,c2.derivation_name(), fname); } -static void my_coll_agg_error(DTCollation &c1, +static void my_coll_agg_error(DTCollation &c1, DTCollation &c2, DTCollation &c3, const char *fname) @@ -1039,35 +1036,6 @@ longlong Item_func_min_max::val_int() return value; } - -#ifdef HAVE_COMPRESS -longlong Item_func_crc32::val_int() -{ - String *res=args[0]->val_str(&value); - if (!res) - { - null_value=1; - return 0; /* purecov: inspected */ - } - null_value=0; - return (longlong) crc32(0L, (Bytef*)res->ptr(), res->length()); -} - -longlong Item_func_uncompressed_length::val_int() -{ - String *res= args[0]->val_str(&value); - if (!res) - { - null_value=1; - return 0; /* purecov: inspected */ - } - null_value=0; - if (res->is_empty()) return 0; - return uint4korr(res->c_ptr()) & 0x3FFFFFFF; -} - -#endif /* HAVE_COMPRESS */ - longlong Item_func_length::val_int() { String *res=args[0]->val_str(&value); diff --git a/sql/item_func.h b/sql/item_func.h index 56ee0379cd5..b7e99bb37b7 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -553,27 +553,6 @@ public: }; -#ifdef HAVE_COMPRESS -class Item_func_crc32 :public Item_int_func -{ - String value; -public: - Item_func_crc32(Item *a) :Item_int_func(a) {} - longlong val_int(); - const char *func_name() const { return "crc32"; } - void fix_length_and_dec() { max_length=10; } -}; -class Item_func_uncompressed_length : public Item_int_func -{ - String value; -public: - Item_func_uncompressed_length(Item *a):Item_int_func(a){} - longlong val_int(); - const char *func_name() const{return "uncompressed_length";} - void fix_length_and_dec() { max_length=10; } -}; -#endif - class Item_func_length :public Item_int_func { String value; diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index d86914eb6c5..79e45cca26f 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -167,30 +167,6 @@ public: const char *func_name() const { return "multipoint"; } }; -#ifdef HAVE_COMPRESS - -class Item_func_compress: public Item_str_func -{ - String buffer; -public: - Item_func_compress(Item *a):Item_str_func(a){} - String *val_str(String *); - void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} - const char *func_name() const{return "compress";} -}; - -class Item_func_uncompress: public Item_str_func -{ - String buffer; -public: - Item_func_uncompress(Item *a): Item_str_func(a){} - String *val_str(String *); - void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} - const char *func_name() const{return "uncompress";} -}; - -#endif - /* Spatial relations */ diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index b92839e6c1e..7c6e6e0686c 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2548,9 +2548,47 @@ null: return 0; } +longlong Item_func_uncompressed_length::val_int() +{ + String *res= args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + if (res->is_empty()) return 0; + return uint4korr(res->c_ptr()) & 0x3FFFFFFF; +} + #ifdef HAVE_COMPRESS #include "zlib.h" +longlong Item_func_crc32::val_int() +{ + String *res=args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + return (longlong) crc32(0L, (Bytef*)res->ptr(), res->length()); +} + +longlong Item_func_uncompressed_length::val_int() +{ + String *res= args[0]->val_str(&value); + if (!res) + { + null_value=1; + return 0; /* purecov: inspected */ + } + null_value=0; + if (res->is_empty()) return 0; + return uint4korr(res->c_ptr()) & 0x3FFFFFFF; +} + String *Item_func_compress::val_str(String *str) { String *res= args[0]->val_str(str); @@ -2575,7 +2613,7 @@ String *Item_func_compress::val_str(String *str) buffer.realloc((uint32)new_size + 4 + 1); Byte *body= ((Byte*)buffer.c_ptr()) + 4; - + if ((err= compress(body, &new_size, (const Bytef*)res->c_ptr(), res->length())) != Z_OK) { @@ -2597,7 +2635,7 @@ String *Item_func_compress::val_str(String *str) } buffer.length((uint32)new_size + 4); - + return &buffer; } @@ -2609,7 +2647,7 @@ String *Item_func_uncompress::val_str(String *str) ulong new_size= uint4korr(res->c_ptr()) & 0x3FFFFFFF; int err= Z_OK; uint code; - + if (new_size > MAX_BLOB_WIDTH) { push_warning_printf(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR, @@ -2618,21 +2656,20 @@ String *Item_func_uncompress::val_str(String *str) null_value= 0; return 0; } - + buffer.realloc((uint32)new_size); - - if ((err= uncompress((Byte*)buffer.c_ptr(), &new_size, + + if ((err= uncompress((Byte*)buffer.c_ptr(), &new_size, ((const Bytef*)res->c_ptr())+4,res->length())) == Z_OK) { buffer.length((uint32)new_size); return &buffer; } - - code= err==Z_BUF_ERROR ? ER_ZLIB_Z_BUF_ERROR : + + code= err==Z_BUF_ERROR ? ER_ZLIB_Z_BUF_ERROR : err==Z_MEM_ERROR ? ER_ZLIB_Z_MEM_ERROR : ER_ZLIB_Z_DATA_ERROR; push_warning(current_thd,MYSQL_ERROR::WARN_LEVEL_ERROR,code,ER(code)); null_value= 1; return 0; } - #endif diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index dfcc22b3443..d7547d69aed 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -623,9 +623,56 @@ public: Item_func_collation(Item *a) :Item_str_func(a) {} String *val_str(String *); const char *func_name() const { return "collation"; } - void fix_length_and_dec() + void fix_length_and_dec() { max_length=40; // should be enough collation.set(system_charset_info); }; }; + +#ifdef HAVE_COMPRESS +#define ZLIB_DEPENDED_FUNCTION ; +#else +#define ZLIB_DEPENDED_FUNCTION { null_value=1; return 0; } +#endif + +class Item_func_compress: public Item_str_func +{ + String buffer; +public: + Item_func_compress(Item *a):Item_str_func(a){} + void fix_length_and_dec(){max_length= (args[0]->max_length*120)/100+12;} + const char *func_name() const{return "compress";} + String *val_str(String *) ZLIB_DEPENDED_FUNCTION +}; + +class Item_func_uncompress: public Item_str_func +{ + String buffer; +public: + Item_func_uncompress(Item *a): Item_str_func(a){} + void fix_length_and_dec(){max_length= MAX_BLOB_WIDTH;} + const char *func_name() const{return "uncompress";} + String *val_str(String *) ZLIB_DEPENDED_FUNCTION +}; + +class Item_func_crc32 :public Item_int_func +{ + String value; +public: + Item_func_crc32(Item *a) :Item_int_func(a) {} + const char *func_name() const { return "crc32"; } + void fix_length_and_dec() { max_length=10; } + longlong val_int() ZLIB_DEPENDED_FUNCTION +}; + +class Item_func_uncompressed_length : public Item_int_func +{ + String value; +public: + Item_func_uncompressed_length(Item *a):Item_int_func(a){} + const char *func_name() const{return "uncompressed_length";} + void fix_length_and_dec() { max_length=10; } + longlong val_int(); +}; + diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2c41d973f2e..9834d169ac4 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -398,6 +398,7 @@ bool check_global_access(THD *thd, ulong want_access); int mysql_backup_table(THD* thd, TABLE_LIST* table_list); int mysql_restore_table(THD* thd, TABLE_LIST* table_list); +int mysql_checksum_table(THD* thd, TABLE_LIST* table_list); int mysql_check_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); int mysql_repair_table(THD* thd, TABLE_LIST* table_list, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8ddcbdc572f..fedaf32a45c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4383,14 +4383,15 @@ struct show_var_st status_vars[]= { {"Bytes_received", (char*) &bytes_received, SHOW_LONG}, {"Bytes_sent", (char*) &bytes_sent, SHOW_LONG}, {"Com_admin_commands", (char*) &com_other, SHOW_LONG}, - {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, {"Com_alter_db", (char*) (com_stat+(uint) SQLCOM_ALTER_DB),SHOW_LONG}, + {"Com_alter_table", (char*) (com_stat+(uint) SQLCOM_ALTER_TABLE),SHOW_LONG}, {"Com_analyze", (char*) (com_stat+(uint) SQLCOM_ANALYZE),SHOW_LONG}, {"Com_backup_table", (char*) (com_stat+(uint) SQLCOM_BACKUP_TABLE),SHOW_LONG}, {"Com_begin", (char*) (com_stat+(uint) SQLCOM_BEGIN),SHOW_LONG}, {"Com_change_db", (char*) (com_stat+(uint) SQLCOM_CHANGE_DB),SHOW_LONG}, {"Com_change_master", (char*) (com_stat+(uint) SQLCOM_CHANGE_MASTER),SHOW_LONG}, {"Com_check", (char*) (com_stat+(uint) SQLCOM_CHECK),SHOW_LONG}, + {"Com_checksum", (char*) (com_stat+(uint) SQLCOM_CHECKSUM),SHOW_LONG}, {"Com_commit", (char*) (com_stat+(uint) SQLCOM_COMMIT),SHOW_LONG}, {"Com_create_db", (char*) (com_stat+(uint) SQLCOM_CREATE_DB),SHOW_LONG}, {"Com_create_function", (char*) (com_stat+(uint) SQLCOM_CREATE_FUNCTION),SHOW_LONG}, @@ -4403,6 +4404,7 @@ struct show_var_st status_vars[]= { {"Com_drop_function", (char*) (com_stat+(uint) SQLCOM_DROP_FUNCTION),SHOW_LONG}, {"Com_drop_index", (char*) (com_stat+(uint) SQLCOM_DROP_INDEX),SHOW_LONG}, {"Com_drop_table", (char*) (com_stat+(uint) SQLCOM_DROP_TABLE),SHOW_LONG}, + {"Com_drop_user", (char*) (com_stat+(uint) SQLCOM_DROP_USER),SHOW_LONG}, {"Com_flush", (char*) (com_stat+(uint) SQLCOM_FLUSH),SHOW_LONG}, {"Com_grant", (char*) (com_stat+(uint) SQLCOM_GRANT),SHOW_LONG}, {"Com_ha_close", (char*) (com_stat+(uint) SQLCOM_HA_CLOSE),SHOW_LONG}, @@ -4417,6 +4419,7 @@ struct show_var_st status_vars[]= { {"Com_load_master_table", (char*) (com_stat+(uint) SQLCOM_LOAD_MASTER_TABLE),SHOW_LONG}, {"Com_lock_tables", (char*) (com_stat+(uint) SQLCOM_LOCK_TABLES),SHOW_LONG}, {"Com_optimize", (char*) (com_stat+(uint) SQLCOM_OPTIMIZE),SHOW_LONG}, + {"Com_preload_keys", (char*) (com_stat+(uint) SQLCOM_PRELOAD_KEYS),SHOW_LONG}, {"Com_purge", (char*) (com_stat+(uint) SQLCOM_PURGE),SHOW_LONG}, {"Com_purge_before_date", (char*) (com_stat+(uint) SQLCOM_PURGE_BEFORE),SHOW_LONG}, {"Com_rename_table", (char*) (com_stat+(uint) SQLCOM_RENAME_TABLE),SHOW_LONG}, @@ -4426,12 +4429,14 @@ struct show_var_st status_vars[]= { {"Com_reset", (char*) (com_stat+(uint) SQLCOM_RESET),SHOW_LONG}, {"Com_restore_table", (char*) (com_stat+(uint) SQLCOM_RESTORE_TABLE),SHOW_LONG}, {"Com_revoke", (char*) (com_stat+(uint) SQLCOM_REVOKE),SHOW_LONG}, + {"Com_revoke_all", (char*) (com_stat+(uint) SQLCOM_REVOKE_ALL),SHOW_LONG}, {"Com_rollback", (char*) (com_stat+(uint) SQLCOM_ROLLBACK),SHOW_LONG}, {"Com_select", (char*) (com_stat+(uint) SQLCOM_SELECT),SHOW_LONG}, {"Com_set_option", (char*) (com_stat+(uint) SQLCOM_SET_OPTION),SHOW_LONG}, - {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_binlogs", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOGS),SHOW_LONG}, + {"Com_show_binlog_events", (char*) (com_stat+(uint) SQLCOM_SHOW_BINLOG_EVENTS),SHOW_LONG}, {"Com_show_charsets", (char*) (com_stat+(uint) SQLCOM_SHOW_CHARSETS),SHOW_LONG}, + {"Com_show_collations", (char*) (com_stat+(uint) SQLCOM_SHOW_COLLATIONS),SHOW_LONG}, {"Com_show_column_types", (char*) (com_stat+(uint) SQLCOM_SHOW_COLUMN_TYPES),SHOW_LONG}, {"Com_show_create_table", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE),SHOW_LONG}, {"Com_show_create_db", (char*) (com_stat+(uint) SQLCOM_SHOW_CREATE_DB),SHOW_LONG}, @@ -4439,6 +4444,7 @@ struct show_var_st status_vars[]= { {"Com_show_errors", (char*) (com_stat+(uint) SQLCOM_SHOW_ERRORS),SHOW_LONG}, {"Com_show_fields", (char*) (com_stat+(uint) SQLCOM_SHOW_FIELDS),SHOW_LONG}, {"Com_show_grants", (char*) (com_stat+(uint) SQLCOM_SHOW_GRANTS),SHOW_LONG}, + {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, {"Com_show_keys", (char*) (com_stat+(uint) SQLCOM_SHOW_KEYS),SHOW_LONG}, {"Com_show_logs", (char*) (com_stat+(uint) SQLCOM_SHOW_LOGS),SHOW_LONG}, {"Com_show_master_status", (char*) (com_stat+(uint) SQLCOM_SHOW_MASTER_STAT),SHOW_LONG}, @@ -4449,7 +4455,6 @@ struct show_var_st status_vars[]= { {"Com_show_slave_hosts", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_HOSTS),SHOW_LONG}, {"Com_show_slave_status", (char*) (com_stat+(uint) SQLCOM_SHOW_SLAVE_STAT),SHOW_LONG}, {"Com_show_status", (char*) (com_stat+(uint) SQLCOM_SHOW_STATUS),SHOW_LONG}, - {"Com_show_innodb_status", (char*) (com_stat+(uint) SQLCOM_SHOW_INNODB_STATUS),SHOW_LONG}, {"Com_show_tables", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLES),SHOW_LONG}, {"Com_show_table_types", (char*) (com_stat+(uint) SQLCOM_SHOW_TABLE_TYPES),SHOW_LONG}, {"Com_show_variables", (char*) (com_stat+(uint) SQLCOM_SHOW_VARIABLES),SHOW_LONG}, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index abf1a9e4ab3..fb2f3181c23 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -60,7 +60,7 @@ enum enum_sql_command { SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_PRELOAD_KEYS, - SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, + SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, SQLCOM_ROLLBACK, SQLCOM_COMMIT, SQLCOM_SLAVE_START, SQLCOM_SLAVE_STOP, SQLCOM_BEGIN, SQLCOM_LOAD_MASTER_TABLE, SQLCOM_CHANGE_MASTER, SQLCOM_RENAME_TABLE, SQLCOM_BACKUP_TABLE, SQLCOM_RESTORE_TABLE, @@ -71,7 +71,7 @@ enum enum_sql_command { SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_NEW_MASTER, SQLCOM_DO, SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES, - SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, + SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, /* This should be the last !!! */ SQLCOM_END diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ed68f487924..0e344756b18 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2264,6 +2264,14 @@ mysql_execute_command(THD *thd) break; } #endif + case SQLCOM_CHECKSUM: + { + if (check_db_used(thd,tables) || + check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) + goto error; /* purecov: inspected */ + res = mysql_checksum_table(thd, tables); + break; + } case SQLCOM_REPAIR: { if (check_db_used(thd,tables) || diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 7cb8dfaae0d..d63a15c13a8 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -2555,7 +2555,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, delete_count++; } else - found_count++; + found_count++; } end_read_record(&info); free_io_cache(from); @@ -2587,3 +2587,67 @@ copy_data_between_tables(TABLE *from,TABLE *to, *deleted=delete_count; DBUG_RETURN(error > 0 ? -1 : 0); } + +int mysql_checksum_table(THD* thd, TABLE_LIST* tables) +{ + TABLE_LIST *table; + List field_list; + Item *item; + Protocol *protocol= thd->protocol; + DBUG_ENTER("mysql_admin_table"); + + field_list.push_back(item = new Item_empty_string("Table", NAME_LEN*2)); + item->maybe_null= 1; + field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21)); + item->maybe_null= 1; + if (protocol->send_fields(&field_list, 1)) + DBUG_RETURN(-1); + + for (table = tables; table; table = table->next) + { + char table_name[NAME_LEN*2+2]; + char* db = (table->db) ? table->db : thd->db; + bool fatal_error=0; + strxmov(table_name,db ? db : "",".",table->real_name,NullS); + + table->table = open_ltable(thd, table, TL_READ_NO_INSERT); +#ifdef EMBEDDED_LIBRARY + thd->net.last_errno= 0; // these errors shouldn't get client +#endif + + protocol->prepare_for_resend(); + protocol->store(table_name, system_charset_info); + + if (!table->table) + { + protocol->store_null(); + thd->net.last_error[0]=0; + } + else + { + table->table->pos_in_table_list= table; + + if (table->table->file->table_flags() & HA_HAS_CHECKSUM) + protocol->store((ulonglong)table->table->file->checksum()); + else + protocol->store_null(); +#ifdef EMBEDDED_LIBRARY + thd->net.last_errno= 0; // these errors shouldn't get client +#endif + + close_thread_tables(thd); + table->table=0; // For query cache + } + if (protocol->write()) + goto err; + } + + send_eof(thd); + DBUG_RETURN(0); + err: + close_thread_tables(thd); // Shouldn't be needed + if (table) + table->table=0; + DBUG_RETURN(-1); +} + diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c1b37bdba65..9be2b21debc 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -669,7 +669,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); insert_values update delete truncate rename show describe load alter optimize preload flush reset purge begin commit rollback slave master_def master_defs - repair restore backup analyze check start + repair restore backup analyze check start checksum field_list field_list_item field_spec kill column_def key_def preload_list preload_keys select_item_list select_item values_list no_braces @@ -727,6 +727,7 @@ verb_clause: | begin | change | check + | checksum | commit | create | delete @@ -1753,6 +1754,16 @@ backup: Lex->backup_dir = $6.str; }; +checksum: + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + } + table_list + {} + ; + repair: REPAIR opt_no_write_to_binlog table_or_tables { @@ -5152,7 +5163,7 @@ union_opt: ; optional_order_or_limit: - /* Empty */ {} + /* Empty */ {} | { THD *thd= YYTHD; From 58bdc6f5e18671812942f4599d8bd99f4991f668 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Thu, 21 Aug 2003 18:30:43 +0200 Subject: [PATCH 10/30] use crc32() from bundled zlib if system zlib is unavailable --- include/my_sys.h | 3 ++- mysys/Makefile.am | 2 +- mysys/my_crc32.c | 36 ++++++++++++++++++++++++++++++++++++ sql/item_create.h | 2 -- sql/item_strfunc.cc | 19 +++---------------- sql/item_strfunc.h | 40 ++++++++++++++++++++-------------------- sql/lex.h | 2 -- 7 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 mysys/my_crc32.c diff --git a/include/my_sys.h b/include/my_sys.h index 6721d77a8af..a6a4d46e227 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -753,8 +753,9 @@ extern my_bool my_uncompress(byte *, ulong *, ulong *); extern byte *my_compress_alloc(const byte *packet, ulong *len, ulong *complen); extern ha_checksum my_checksum(ha_checksum crc, const byte *mem, uint count); extern uint my_bit_log2(ulong value); -uint my_count_bits(ulonglong v); +extern uint my_count_bits(ulonglong v); extern void my_sleep(ulong m_seconds); +extern ulong crc32(ulong crc, const uchar *buf, uint len); #ifdef __WIN__ extern my_bool have_tcpip; /* Is set if tcpip is used */ diff --git a/mysys/Makefile.am b/mysys/Makefile.am index 5b1c859cb2a..9e563755ebd 100644 --- a/mysys/Makefile.am +++ b/mysys/Makefile.am @@ -29,7 +29,7 @@ libmysys_a_SOURCES = my_init.c my_getwd.c mf_getdate.c\ mf_path.c mf_loadpath.c\ my_open.c my_create.c my_dup.c my_seek.c my_read.c \ my_pread.c my_write.c \ - mf_keycache.c \ + mf_keycache.c my_crc32.c \ mf_iocache.c mf_iocache2.c mf_cache.c mf_tempfile.c \ mf_tempdir.c my_lock.c mf_brkhant.c my_alarm.c \ my_malloc.c my_realloc.c my_once.c mulalloc.c \ diff --git a/mysys/my_crc32.c b/mysys/my_crc32.c new file mode 100644 index 00000000000..5514b01ede2 --- /dev/null +++ b/mysys/my_crc32.c @@ -0,0 +1,36 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "mysys_priv.h" + +#ifndef HAVE_COMPRESS + +/* minimal set of defines for using crc32() from zlib codebase */ +#define _ZLIB_H +#define ZEXPORT +#define Z_NULL 0 +#define OF(args) args +#undef DYNAMIC_CRC_TABLE +typedef uchar Byte; +typedef uchar Bytef; +typedef uint uInt; +typedef ulong uLong; +typedef ulong uLongf; + +#include "../zlib/crc32.c" + +#endif + diff --git a/sql/item_create.h b/sql/item_create.h index 1326077b096..e80272e21a5 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -33,9 +33,7 @@ Item *create_func_connection_id(void); Item *create_func_conv(Item* a, Item *b, Item *c); Item *create_func_cos(Item* a); Item *create_func_cot(Item* a); -#ifdef HAVE_COMPRESS Item *create_func_crc32(Item* a); -#endif Item *create_func_date_format(Item* a,Item *b); Item *create_func_dayname(Item* a); Item *create_func_dayofmonth(Item* a); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 7c6e6e0686c..cb24fb0f027 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2561,9 +2561,6 @@ longlong Item_func_uncompressed_length::val_int() return uint4korr(res->c_ptr()) & 0x3FFFFFFF; } -#ifdef HAVE_COMPRESS -#include "zlib.h" - longlong Item_func_crc32::val_int() { String *res=args[0]->val_str(&value); @@ -2573,21 +2570,11 @@ longlong Item_func_crc32::val_int() return 0; /* purecov: inspected */ } null_value=0; - return (longlong) crc32(0L, (Bytef*)res->ptr(), res->length()); + return (longlong) crc32(0L, (uchar*)res->ptr(), res->length()); } -longlong Item_func_uncompressed_length::val_int() -{ - String *res= args[0]->val_str(&value); - if (!res) - { - null_value=1; - return 0; /* purecov: inspected */ - } - null_value=0; - if (res->is_empty()) return 0; - return uint4korr(res->c_ptr()) & 0x3FFFFFFF; -} +#ifdef HAVE_COMPRESS +#include "zlib.h" String *Item_func_compress::val_str(String *str) { diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index d7547d69aed..a8725febe62 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -630,6 +630,26 @@ public: }; }; +class Item_func_crc32 :public Item_int_func +{ + String value; +public: + Item_func_crc32(Item *a) :Item_int_func(a) {} + const char *func_name() const { return "crc32"; } + void fix_length_and_dec() { max_length=10; } + longlong val_int(); +}; + +class Item_func_uncompressed_length : public Item_int_func +{ + String value; +public: + Item_func_uncompressed_length(Item *a):Item_int_func(a){} + const char *func_name() const{return "uncompressed_length";} + void fix_length_and_dec() { max_length=10; } + longlong val_int(); +}; + #ifdef HAVE_COMPRESS #define ZLIB_DEPENDED_FUNCTION ; #else @@ -656,23 +676,3 @@ public: String *val_str(String *) ZLIB_DEPENDED_FUNCTION }; -class Item_func_crc32 :public Item_int_func -{ - String value; -public: - Item_func_crc32(Item *a) :Item_int_func(a) {} - const char *func_name() const { return "crc32"; } - void fix_length_and_dec() { max_length=10; } - longlong val_int() ZLIB_DEPENDED_FUNCTION -}; - -class Item_func_uncompressed_length : public Item_int_func -{ - String value; -public: - Item_func_uncompressed_length(Item *a):Item_int_func(a){} - const char *func_name() const{return "uncompressed_length";} - void fix_length_and_dec() { max_length=10; } - longlong val_int(); -}; - diff --git a/sql/lex.h b/sql/lex.h index 35c4d990b32..7ac61c73ad8 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -472,9 +472,7 @@ static SYMBOL sql_functions[] = { { "COUNT", SYM(COUNT_SYM),0,0}, { "COS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)}, { "COT", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)}, -#ifdef HAVE_COMPRESS { "CRC32", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_crc32)}, -#endif { "CROSSES", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_crosses)}, { "CURDATE", SYM(CURDATE),0,0}, { "CURTIME", SYM(CURTIME),0,0}, From eb3912c05adc860937f8535d42f934966ff1de1b Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Fri, 22 Aug 2003 18:21:23 +0200 Subject: [PATCH 11/30] bug #1078. two innodb+delayed crashes --- mysql-test/r/innodb.result | 12 ++++++++++++ mysql-test/t/innodb.test | 14 ++++++++++++++ sql/sql_insert.cc | 4 ++++ 3 files changed, 30 insertions(+) diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 69268f1d5c5..20cc50dcc61 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1329,3 +1329,15 @@ id label 3524 Societe Test 3525 Fournisseur Test drop table t1,t2; +create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) type=innodb; +select * from t1; +c1 c2 stamp +replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +ERROR HY000: Table storage engine for 't1' doesn't have this option +select * from t1; +c1 c2 stamp +replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +ERROR HY000: Table storage engine for 't1' doesn't have this option +select * from t1; +c1 c2 stamp +drop table t1; diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index 635a15baa41..cd3a5693535 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -906,3 +906,17 @@ SELECT t2.id, t1.label FROM t2 INNER JOIN (SELECT t1.id_object as id_object FROM t1 WHERE t1.label LIKE '%test%') AS lbl ON (t2.id = lbl.id_object) INNER JOIN t1 ON (t2.id = t1.id_object); drop table t1,t2; + +# +# Bug #1078 +# +create table t1 (c1 char(5) unique not null, c2 int, stamp timestamp) type=innodb; +select * from t1; +--error 1031 +replace delayed into t1 (c1, c2) values ( "text1","11"),( "text2","12"); +select * from t1; +--error 1031 +replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text3","14", "a" ),( "text4","15", "b" ); +select * from t1; +drop table t1; + diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 947205949f1..6cc5d25c34e 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -172,7 +172,10 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list, else res= (table == 0); else + { + lock_type=TL_WRITE; res= open_and_lock_tables(thd, table_list); + } } else res= open_and_lock_tables(thd, table_list); @@ -627,6 +630,7 @@ public: thd.current_tablenr=0; thd.version=refresh_version; thd.command=COM_DELAYED_INSERT; + thd.lex.current_select= 0; /* for my_message_sql */ bzero((char*) &thd.net,sizeof(thd.net)); // Safety thd.system_thread=1; From 8424aec8db8297d07bc6f2fbf5123ca0fa6778ee Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Fri, 22 Aug 2003 23:41:56 +0200 Subject: [PATCH 12/30] added support for --libs_r --- scripts/mysql_config.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/mysql_config.sh b/scripts/mysql_config.sh index 3cc5b3a5016..e8cc9322eaf 100644 --- a/scripts/mysql_config.sh +++ b/scripts/mysql_config.sh @@ -86,6 +86,8 @@ client_libs='@CLIENT_LIBS@' libs="$ldflags -L'$pkglibdir' -lmysqlclient $client_libs" libs=`echo $libs | sed -e 's; +;;'` +libs_r="$ldflags -L'$pkglibdir' -lmysqlclient_r $client_libs" +libs_r=`echo $libs_r | sed -e 's; +;;'` cflags="-I'$pkgincludedir'" embedded_libs="$ldflags -L'$pkglibdir' -lmysqld @LIBS@ @innodb_system_libs@" @@ -95,6 +97,7 @@ Usage: $0 [OPTIONS] Options: --cflags [$cflags] --libs [$libs] + --libs_r [$libs_r] --socket [$socket] --port [$port] --version [$version] @@ -109,6 +112,7 @@ while test $# -gt 0; do case $1 in --cflags) echo "$cflags" ;; --libs) echo "$libs" ;; + --libs_r) echo "$libs_r" ;; --socket) echo "$socket" ;; --port) echo "$port" ;; --version) echo "$version" ;; From a39f98d63776ff2027f7369867d603a03993229f Mon Sep 17 00:00:00 2001 From: "wax@kishkin.ru" <> Date: Fri, 29 Aug 2003 02:28:32 +0600 Subject: [PATCH 13/30] BUG correct bugs #833 and #836 --- sql/item_sum.cc | 8 ++++---- sql/item_sum.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 4680be828c3..be2b419dcd4 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1466,7 +1466,7 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, for (uint i= 0; i < item->arg_count_field; i++) { Item *field_item= item->args[i]; - Field *field= field_item->tmp_table_field(); + Field *field= field_item->real_item()->tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1497,7 +1497,7 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) { ORDER *order_item= item->order[i]; Item *item= *order_item->item; - Field *field= item->tmp_table_field(); + Field *field= item->real_item()->tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1548,7 +1548,7 @@ int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), Item *show_item= group_concat_item->args[i]; if (!show_item->const_item()) { - Field *f= show_item->tmp_table_field(); + Field *f= show_item->real_item()->tmp_table_field(); char *sv= f->ptr; f->ptr= (char *)key + f->abs_offset; String *res= f->val_str(&tmp,&tmp2); @@ -1716,7 +1716,7 @@ bool Item_func_group_concat::add() Item *show_item= args[i]; if (!show_item->const_item()) { - Field *f= show_item->tmp_table_field(); + Field *f= show_item->real_item()->tmp_table_field(); if (!f->is_null()) { record_is_null= FALSE; diff --git a/sql/item_sum.h b/sql/item_sum.h index bd946609745..219ca98dc45 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -734,4 +734,5 @@ class Item_func_group_concat : public Item_sum } String* val_str(String* str); Item *copy_or_same(THD* thd); + void no_rows_in_result() {} }; From bd49884c1986235c93141c284d944e055dcb72d6 Mon Sep 17 00:00:00 2001 From: "dlenev@dlenev.mshome" <> Date: Wed, 3 Sep 2003 12:19:30 +0400 Subject: [PATCH 14/30] Added automatically created SSL certificates from mysql-test/std_data to ignore list. --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index 35db3c5d5ba..b28278f83a7 100644 --- a/.bzrignore +++ b/.bzrignore @@ -473,6 +473,7 @@ mysql-test/r/rpl_log.eval mysql-test/r/slave-running.eval mysql-test/r/slave-stopped.eval mysql-test/share/mysql +mysql-test/std_data/*.pem mysql-test/var/* mysql.kdevprj mysql.proj From 2fcab1c2ee99a73718e14a56cbe7871bca8bd967 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 3 Sep 2003 11:34:32 +0200 Subject: [PATCH 15/30] CHECKSUM TABLE table1, table2, ... [ QUICK | EXTENDED ] --- myisam/mi_checksum.c | 2 +- mysql-test/r/innodb.result | 25 +++++++++++++ mysql-test/r/myisam.result | 23 ++++++++++-- mysql-test/r/show_check.result | 2 +- mysql-test/t/innodb.test | 12 +++++++ mysql-test/t/myisam.test | 13 ++++++- mysys/checksum.c | 4 ++- sql/mysql_priv.h | 3 +- sql/sql_parse.cc | 2 +- sql/sql_table.cc | 66 ++++++++++++++++++++++++++-------- sql/sql_yacc.yy | 14 +++++--- 11 files changed, 138 insertions(+), 28 deletions(-) diff --git a/myisam/mi_checksum.c b/myisam/mi_checksum.c index 982f77ee81f..cdbb634c5ff 100644 --- a/myisam/mi_checksum.c +++ b/myisam/mi_checksum.c @@ -50,7 +50,7 @@ ha_checksum mi_checksum(MI_INFO *info, const byte *buf) pos=buf; break; } - crc=my_checksum(crc, pos, length); + crc=my_checksum(crc, pos ? pos : "", length); } return crc; } diff --git a/mysql-test/r/innodb.result b/mysql-test/r/innodb.result index 20cc50dcc61..e1a03b051ce 100644 --- a/mysql-test/r/innodb.result +++ b/mysql-test/r/innodb.result @@ -1341,3 +1341,28 @@ ERROR HY000: Table storage engine for 't1' doesn't have this option select * from t1; c1 c2 stamp drop table t1; +create table t1 (a int, b varchar(200), c text not null) checksum=1 type=myisam; +create table t2 (a int, b varchar(200), c text not null) checksum=0 type=innodb; +create table t3 (a int, b varchar(200), c text not null) checksum=1 type=innodb; +insert t1 values (1, "aaa", "bbb"), (NULL, "", "ccccc"), (0, NULL, ""); +insert t2 select * from t1; +insert t3 select * from t1; +checksum table t1, t2, t3, t4 quick; +Table Checksum +test.t1 968604391 +test.t2 NULL +test.t3 NULL +test.t4 NULL +checksum table t1, t2, t3, t4; +Table Checksum +test.t1 968604391 +test.t2 968604391 +test.t3 968604391 +test.t4 NULL +checksum table t1, t2, t3, t4 extended; +Table Checksum +test.t1 968604391 +test.t2 968604391 +test.t3 968604391 +test.t4 NULL +drop table t1,t2,t3; diff --git a/mysql-test/r/myisam.result b/mysql-test/r/myisam.result index fd9ff2c90cf..ece89ca282d 100644 --- a/mysql-test/r/myisam.result +++ b/mysql-test/r/myisam.result @@ -394,6 +394,23 @@ id select_type table type possible_keys key key_len ref rows Extra drop table t1,t2; CREATE TABLE t1 (`a` int(11) NOT NULL default '0', `b` int(11) NOT NULL default '0', UNIQUE KEY `a` USING RTREE (`a`,`b`)) TYPE=MyISAM; ERROR 42000: This version of MySQL doesn't yet support 'RTREE INDEX' -DROP TABLE IF EXISTS t1; -Warnings: -Note 1051 Unknown table 't1' +create table t1 (a int, b varchar(200), c text not null) checksum=1; +create table t2 (a int, b varchar(200), c text not null) checksum=0; +insert t1 values (1, "aaa", "bbb"), (NULL, "", "ccccc"), (0, NULL, ""); +insert t2 select * from t1; +checksum table t1, t2, t3 quick; +Table Checksum +test.t1 968604391 +test.t2 NULL +test.t3 NULL +checksum table t1, t2, t3; +Table Checksum +test.t1 968604391 +test.t2 968604391 +test.t3 NULL +checksum table t1, t2, t3 extended; +Table Checksum +test.t1 968604391 +test.t2 968604391 +test.t3 NULL +drop table t1,t2; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 201d1b541ae..682c48c9c55 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -43,7 +43,7 @@ wait_timeout 28800 show variables like "this_doesn't_exists%"; Variable_name Value show table status from test like "this_doesn't_exists%"; -Name Type Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Charset Create_options Comment +Name Type Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Charset Checksum Create_options Comment show databases; Database mysql diff --git a/mysql-test/t/innodb.test b/mysql-test/t/innodb.test index cd3a5693535..72a791b263c 100644 --- a/mysql-test/t/innodb.test +++ b/mysql-test/t/innodb.test @@ -920,3 +920,15 @@ replace delayed into t1 (c1, c2) values ( "text1","12"),( "text2","13"),( "text select * from t1; drop table t1; +create table t1 (a int, b varchar(200), c text not null) checksum=1 type=myisam; +create table t2 (a int, b varchar(200), c text not null) checksum=0 type=innodb; +create table t3 (a int, b varchar(200), c text not null) checksum=1 type=innodb; +insert t1 values (1, "aaa", "bbb"), (NULL, "", "ccccc"), (0, NULL, ""); +insert t2 select * from t1; +insert t3 select * from t1; +checksum table t1, t2, t3, t4 quick; +checksum table t1, t2, t3, t4; +checksum table t1, t2, t3, t4 extended; +#show table status; +drop table t1,t2,t3; + diff --git a/mysql-test/t/myisam.test b/mysql-test/t/myisam.test index 80d46d1ef0c..2bcc5dd7514 100644 --- a/mysql-test/t/myisam.test +++ b/mysql-test/t/myisam.test @@ -360,4 +360,15 @@ drop table t1,t2; CREATE TABLE t1 (`a` int(11) NOT NULL default '0', `b` int(11) NOT NULL default '0', UNIQUE KEY `a` USING RTREE (`a`,`b`)) TYPE=MyISAM; # INSERT INTO t1 VALUES (1,1),(1,1); # DELETE FROM rt WHERE a<1; -DROP TABLE IF EXISTS t1; +# DROP TABLE IF EXISTS t1; + +create table t1 (a int, b varchar(200), c text not null) checksum=1; +create table t2 (a int, b varchar(200), c text not null) checksum=0; +insert t1 values (1, "aaa", "bbb"), (NULL, "", "ccccc"), (0, NULL, ""); +insert t2 select * from t1; +checksum table t1, t2, t3 quick; +checksum table t1, t2, t3; +checksum table t1, t2, t3 extended; +#show table status; +drop table t1,t2; + diff --git a/mysys/checksum.c b/mysys/checksum.c index 2ae139b81c3..664e768ef4e 100644 --- a/mysys/checksum.c +++ b/mysys/checksum.c @@ -30,9 +30,11 @@ ha_checksum my_checksum(ha_checksum crc, const byte *pos, uint length) { - const byte *end=pos+length; +/* const byte *end=pos+length; for ( ; pos != end ; pos++) crc=((crc << 8) + *((uchar*) pos)) + (crc >> (8*sizeof(ha_checksum)-8)); return crc; +*/ + return (ha_checksum)crc32((uint)crc, (const uchar *)pos, length); } diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9834d169ac4..5913bee6d0a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -398,7 +398,8 @@ bool check_global_access(THD *thd, ulong want_access); int mysql_backup_table(THD* thd, TABLE_LIST* table_list); int mysql_restore_table(THD* thd, TABLE_LIST* table_list); -int mysql_checksum_table(THD* thd, TABLE_LIST* table_list); +int mysql_checksum_table(THD* thd, TABLE_LIST* table_list, + HA_CHECK_OPT* check_opt); int mysql_check_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); int mysql_repair_table(THD* thd, TABLE_LIST* table_list, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0e344756b18..fb74ca7dd71 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2269,7 +2269,7 @@ mysql_execute_command(THD *thd) if (check_db_used(thd,tables) || check_table_access(thd, SELECT_ACL | EXTRA_ACL , tables)) goto error; /* purecov: inspected */ - res = mysql_checksum_table(thd, tables); + res = mysql_checksum_table(thd, tables, &lex->check_opt); break; } case SQLCOM_REPAIR: diff --git a/sql/sql_table.cc b/sql/sql_table.cc index d63a15c13a8..48a99cfaad5 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -350,10 +350,10 @@ static int sort_keys(KEY *a, KEY *b) fields List of fields to create keys List of keys to create tmp_table Set to 1 if this is an internal temporary table - (From ALTER TABLE) + (From ALTER TABLE) no_log Don't log the query to binary log. - DESCRIPTION + DESCRIPTION If one creates a temporary table, this is automaticly opened no_log is needed for the case of CREATE ... SELECT, @@ -672,11 +672,11 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, /* Make SPATIAL to be RTREE by default SPATIAL only on BLOB or at least BINARY, this - actually should be replaced by special GEOM type + actually should be replaced by special GEOM type in near future when new frm file is ready checking for proper key parts number: */ - + if (key_info->flags == HA_SPATIAL) { if (key_info->key_parts != 1) @@ -699,7 +699,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, MYF(0), "RTREE INDEX"); DBUG_RETURN(-1); } - + List_iterator cols(key->columns); for (uint column_nr=0 ; (column=cols++) ; column_nr++) { @@ -745,9 +745,9 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { if (!column->length ) { - /* + /* BAR: 4 is: (Xmin,Xmax,Ymin,Ymax), this is for 2D case - Lately we'll extend this code to support more dimensions + Lately we'll extend this code to support more dimensions */ column->length=4*sizeof(double); } @@ -797,7 +797,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name, { } else if (column->length > length || - ((f_is_packed(sql_field->pack_flag) || + ((f_is_packed(sql_field->pack_flag) || ((file->table_flags() & HA_NO_PREFIX_CHAR_KEYS) && (key_info->flags & HA_NOSAME))) && column->length != length)) @@ -2588,7 +2588,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, DBUG_RETURN(error > 0 ? -1 : 0); } -int mysql_checksum_table(THD* thd, TABLE_LIST* tables) +int mysql_checksum_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT *check_opt) { TABLE_LIST *table; List field_list; @@ -2608,9 +2608,10 @@ int mysql_checksum_table(THD* thd, TABLE_LIST* tables) char table_name[NAME_LEN*2+2]; char* db = (table->db) ? table->db : thd->db; bool fatal_error=0; + TABLE *t; strxmov(table_name,db ? db : "",".",table->real_name,NullS); - table->table = open_ltable(thd, table, TL_READ_NO_INSERT); + t=table->table = open_ltable(thd, table, TL_READ_NO_INSERT); #ifdef EMBEDDED_LIBRARY thd->net.last_errno= 0; // these errors shouldn't get client #endif @@ -2618,19 +2619,54 @@ int mysql_checksum_table(THD* thd, TABLE_LIST* tables) protocol->prepare_for_resend(); protocol->store(table_name, system_charset_info); - if (!table->table) + if (!t) { protocol->store_null(); thd->net.last_error[0]=0; } else { - table->table->pos_in_table_list= table; + t->pos_in_table_list= table; - if (table->table->file->table_flags() & HA_HAS_CHECKSUM) - protocol->store((ulonglong)table->table->file->checksum()); - else + if (t->file->table_flags() & HA_HAS_CHECKSUM && + !(check_opt->flags & T_EXTEND)) + protocol->store((ulonglong)t->file->checksum()); + else if (!(t->file->table_flags() & HA_HAS_CHECKSUM) && + check_opt->flags & T_QUICK) protocol->store_null(); + else + { + /* calculating table's checksum */ + ha_checksum crc=0; + if (t->file->rnd_init(1)) + protocol->store_null(); + else + { + while (!t->file->rnd_next(t->record[0])) + { + ha_checksum row_crc=0; + if (t->record[0] != t->field[0]->ptr) + row_crc=my_checksum(row_crc, t->record[0], + t->field[0]->ptr - t->record[0]); + + for (uint i=0; i < t->fields; i++ ) + { + Field *f=t->field[i]; + if (f->type() == FIELD_TYPE_BLOB) + { + String tmp; + f->val_str(&tmp,&tmp); + row_crc=my_checksum(row_crc, tmp.ptr(), tmp.length()); + } + else + row_crc=my_checksum(row_crc, f->ptr, f->pack_length()); + } + + crc+=row_crc; + } + protocol->store((ulonglong)crc); + } + } #ifdef EMBEDDED_LIBRARY thd->net.last_errno= 0; // these errors shouldn't get client #endif diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9be2b21debc..a63d70620fd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1080,7 +1080,7 @@ opt_select_from: | select_from select_lock_type; udf_func_type: - /* empty */ { $$ = UDFTYPE_FUNCTION; } + /* empty */ { $$ = UDFTYPE_FUNCTION; } | AGGREGATE_SYM { $$ = UDFTYPE_AGGREGATE; }; udf_type: @@ -1547,7 +1547,7 @@ opt_ident: opt_component: /* empty */ { $$.str= 0; $$.length= 0; } | '.' ident { $$=$2; }; - + string_list: text_string { Lex->interval_list.push_back($1); } | string_list ',' text_string { Lex->interval_list.push_back($3); }; @@ -1760,10 +1760,16 @@ checksum: LEX *lex=Lex; lex->sql_command = SQLCOM_CHECKSUM; } - table_list - {} + table_list opt_checksum_type + {} ; +opt_checksum_type: + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + ; + repair: REPAIR opt_no_write_to_binlog table_or_tables { From 3d3f11f83254b3be969f13778587b7d0d8881ed3 Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 3 Sep 2003 11:37:59 +0200 Subject: [PATCH 16/30] making InnoDB to return the row exactly the same (bytewise) as it was written to it --- innobase/row/row0sel.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/innobase/row/row0sel.c b/innobase/row/row0sel.c index 97a69f76eaa..362b632a65e 100644 --- a/innobase/row/row0sel.c +++ b/innobase/row/row0sel.c @@ -2069,7 +2069,7 @@ row_sel_store_mysql_rec( ulint len; byte* blob_buf; ulint i; - + ut_ad(prebuilt->mysql_template); if (prebuilt->blob_heap != NULL) { @@ -2077,9 +2077,9 @@ row_sel_store_mysql_rec( prebuilt->blob_heap = NULL; } - /* Mark all columns as not SQL NULL */ + /* Mark all columns as SQL NULL */ - memset(mysql_rec, '\0', prebuilt->null_bitmap_len); + memset(mysql_rec, 255, prebuilt->null_bitmap_len); for (i = 0; i < prebuilt->n_template; i++) { @@ -2134,16 +2134,21 @@ row_sel_store_mysql_rec( data = blob_buf; } - + row_sel_field_store_in_mysql_format( mysql_rec + templ->mysql_col_offset, templ->mysql_col_len, data, len, templ->type, templ->is_unsigned); if (extern_field_heap) { - mem_heap_free(extern_field_heap); + mem_heap_free(extern_field_heap); extern_field_heap = NULL; - } + } + + if (templ->mysql_null_bit_mask) { + mysql_rec[templ->mysql_null_byte_offset] &= + ~(byte) (templ->mysql_null_bit_mask); + } } else { /* MySQL seems to assume the field for an SQL NULL value is set to zero. Not taking this into account @@ -2151,19 +2156,13 @@ row_sel_store_mysql_rec( bug number 154 in the MySQL bug database: GROUP BY and DISTINCT could treat NULL values inequal. */ - memset(mysql_rec + templ->mysql_col_offset, '\0', + memset(mysql_rec + templ->mysql_col_offset, + ((templ->type == DATA_VARCHAR || + templ->type == DATA_VARMYSQL || + templ->type == DATA_BINARY) ? ' ' : '\0'), templ->mysql_col_len); - - if (!templ->mysql_null_bit_mask) { - fprintf(stderr, -"InnoDB: Error: trying to return an SQL NULL field in a non-null\n" -"innoDB: column! Table name %s\n", prebuilt->table->name); - } else { - mysql_rec[templ->mysql_null_byte_offset] |= - (byte) (templ->mysql_null_bit_mask); - } } - } + } return(TRUE); } From 424a647069ba3354cfbb5df641a553e6423c0a02 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Wed, 3 Sep 2003 16:43:41 +0400 Subject: [PATCH 17/30] post-merge fixes --- mysql-test/r/func_crypt.result | 12 ++++++------ sql/slave.cc | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index bd4c6d41d39..5ee0f0f3e93 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -3,7 +3,7 @@ length(encrypt('foo', 'ff')) <> 0 1 select password('abc'); password('abc') -*0d3ced9bec10a777aec23ccc353a8c08a633045e +*0D3CED9BEC10A777AEC23CCC353A8C08A633045E select password(''); password('') @@ -15,7 +15,7 @@ old_password('') select password('gabbagabbahey'); password('gabbagabbahey') -*b0f99d2963660dd7e16b751ec9ee2f17b6a68fa6 +*B0F99D2963660DD7E16B751EC9EE2F17B6A68FA6 select old_password('idkfa'); old_password('idkfa') 5c078dc54ca0fcca @@ -43,7 +43,7 @@ old_password('') select password('idkfa'); password('idkfa') -*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +*B669C9DAC3AA6F2254B03CDEF8DFDD6B2D1054BA select old_password('idkfa'); old_password('idkfa') 5c078dc54ca0fcca @@ -64,13 +64,13 @@ old_password('idkfa') set old_passwords=off; select password('idkfa '); password('idkfa ') -*2dc31d90647b4c1abc9231563d2236e96c9a2db2 +*2DC31D90647B4C1ABC9231563D2236E96C9A2DB2 select password('idkfa'); password('idkfa') -*b669c9dac3aa6f2254b03cdef8dfdd6b2d1054ba +*B669C9DAC3AA6F2254B03CDEF8DFDD6B2D1054BA select password(' idkfa'); password(' idkfa') -*12b099e56bb7fe8d43c78fd834a9d1d11178d045 +*12B099E56BB7FE8D43C78FD834A9D1D11178D045 select old_password('idkfa'); old_password('idkfa') 5c078dc54ca0fcca diff --git a/sql/slave.cc b/sql/slave.cc index c10f0417585..dd25fe5e75e 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -1554,7 +1554,7 @@ void init_master_info_with_options(MASTER_INFO* mi) 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, MAX_PASSWORD_LENGTH); mi->port = master_port; mi->connect_retry = master_connect_retry; From 2335c7a0d4656e099917d0702966a8c50d91d3db Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Wed, 3 Sep 2003 17:29:51 +0400 Subject: [PATCH 18/30] fixed after-merge bug in CHANG_USER command --- sql/sql_parse.cc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 383b6b0f581..0a9a57d86bb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1192,14 +1192,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? *passwd++ : strlen(passwd); db+= passwd_len + 1; - /* Convert database name to utf8 */ - String convdb; - convdb.copy(db, strlen(db), thd->variables.character_set_client, - system_charset_info); - db= convdb.c_ptr(); - - - /* Small check for incomming packet */ if ((uint) ((uchar*) db - net->read_pos) > packet_length) { @@ -1207,6 +1199,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, break; } + /* Convert database name to utf8 */ + String convdb; + convdb.copy(db, strlen(db), thd->variables.character_set_client, + system_charset_info); + db= convdb.c_ptr(); + /* Save user and privileges */ uint save_master_access= thd->master_access; uint save_db_access= thd->db_access; From f1a4f7d0378ba8dac191602117b0c654a49ea0f3 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Wed, 3 Sep 2003 18:07:00 +0400 Subject: [PATCH 19/30] Monty explanation for net_store_length(uint) put to the comment --- sql/protocol.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sql/protocol.cc b/sql/protocol.cc index 99d1e03c8a7..d1eb3460fc8 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -371,7 +371,13 @@ bool send_old_password_request(THD *thd) #endif /* EMBEDDED_LIBRARY */ /* - Faster net_store_length when we know length is a 32 bit integer + Faster net_store_length when we know that length is less than 65536. + We keep a separate version for that range because it's widely used in + libmysql. + uint is used as agrument type because of MySQL type conventions: + uint for 0..65536 + ulong for 0..4294967296 + ulonglong for bigger numbers. */ char *net_store_length(char *pkg, uint length) From 2317fa3ba5312dd6fde14c89930ee35c53b6e3fe Mon Sep 17 00:00:00 2001 From: "serg@serg.mylan" <> Date: Wed, 3 Sep 2003 16:31:01 +0200 Subject: [PATCH 20/30] C cleanups --- include/mysql_com.h | 2 +- libmysql/libmysql.c | 5 +++-- sql/password.c | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/mysql_com.h b/include/mysql_com.h index 8d61641cf29..b8dc877f125 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -319,7 +319,7 @@ void randominit(struct rand_struct *, unsigned long seed1, double my_rnd(struct rand_struct *); void create_random_string(char *to, uint length, struct rand_struct *rand_st); -void hash_password(ulong *to, const char *password, uint password_len); +void hash_password(unsigned long *to, const char *password, uint password_len); void make_scrambled_password_323(char *to, const char *password); void scramble_323(char *to, const char *message, const char *password); my_bool check_scramble_323(const char *, const char *message, diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 67576a961ef..0a9e1114fc5 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -594,6 +594,8 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db) { char buff[512],*end=buff; + NET *net= &mysql->net; + ulong pkt_length; DBUG_ENTER("mysql_change_user"); if (!user) @@ -627,8 +629,7 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Write authentication package */ simple_command(mysql,COM_CHANGE_USER, buff,(ulong) (end-buff),1); - NET *net= &mysql->net; - ulong pkt_length= net_safe_read(mysql); + pkt_length= net_safe_read(mysql); if (pkt_length == packet_error) goto error; diff --git a/sql/password.c b/sql/password.c index 16227aab611..9f4910d8c60 100644 --- a/sql/password.c +++ b/sql/password.c @@ -170,15 +170,15 @@ void scramble_323(char *to, const char *message, const char *password) if (password && password[0]) { - char *to_start=to; + char extra, *to_start=to; + const char *message_end= message + SCRAMBLE_LENGTH_323; hash_password(hash_pass,password, strlen(password)); hash_password(hash_message, message, SCRAMBLE_LENGTH_323); randominit(&rand_st,hash_pass[0] ^ hash_message[0], hash_pass[1] ^ hash_message[1]); - const char *message_end= message + SCRAMBLE_LENGTH_323; for (; message < message_end; message++) *to++= (char) (floor(my_rnd(&rand_st)*31)+64); - char extra=(char) (floor(my_rnd(&rand_st)*31)); + extra=(char) (floor(my_rnd(&rand_st)*31)); while (to_start != to) *(to_start++)^=extra; } From fbbc9a00af510e69cdb7b99b6ad391079eb9dde2 Mon Sep 17 00:00:00 2001 From: "kostja@oak.local" <> Date: Wed, 3 Sep 2003 21:52:42 +0400 Subject: [PATCH 21/30] fixed bug - check_user shall not be included into the embedded library. --- sql/sql_parse.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a05b98e85ba..a5d83c2951d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -176,6 +176,7 @@ end: } +#ifndef EMBEDDED_LIBRARY /* Check if user exist and password supplied is correct. @@ -351,6 +352,9 @@ static int check_user(THD *thd, enum enum_server_command command, DBUG_RETURN(-1); } +#endif // EMBEDDED_LIBRARY + + /* Check for maximum allowable user connections, if the mysqld server is started with corresponding variable that is greater then 0. From b34c3934d947f24776ee8b019cc7f65903864709 Mon Sep 17 00:00:00 2001 From: "wax@kishkin.ru" <> Date: Thu, 4 Sep 2003 06:44:16 +0600 Subject: [PATCH 22/30] BUG correct bug 1085 (a problem with min/max functions) add tests of bugs 833,836,1085 --- mysql-test/r/func_gconcat.result | 18 ++++++++++++++++++ mysql-test/t/func_gconcat.test | 21 +++++++++++++++++++++ sql/item_sum.cc | 8 ++++---- sql/item_sum.h | 4 ++-- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/func_gconcat.result b/mysql-test/r/func_gconcat.result index a4493e7c95c..5c3c2f10000 100644 --- a/mysql-test/r/func_gconcat.result +++ b/mysql-test/r/func_gconcat.result @@ -167,6 +167,11 @@ t2.URL_ID = t1.URL_ID group by REQ_ID; REQ_ID URL 1 X 5 X,X,X +select REQ_ID, Group_Concat(URL) as URL, Min(t1.URL_ID) urll, +Max(t1.URL_ID) urlg from t1, t2 where t2.URL_ID = t1.URL_ID group by REQ_ID; +REQ_ID URL urll urlg +1 X 4 4 +5 X,X,X 4 5 drop table t1; drop table t2; create table t1 (id int, name varchar(16)); @@ -178,3 +183,16 @@ select distinct ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') a with distinct: cutoff at length of shortname 1:longername,1:evenlongername drop table t1; +create table t1(id int); +create table t2(id int); +insert into t1 values(0),(1); +select group_concat(t1.id) FROM t1,t2; +group_concat(t1.id) +NULL +drop table t1; +drop table t2; +create table t1 (bar varchar(32)); +insert into t1 values('test'),('test2'); +select * from t1 having group_concat(bar)=''; +bar +drop table t1; diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index f426f9ca4ee..b10f6d2af21 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -91,6 +91,11 @@ insert into t2 values (1,4), (5,4), (5,5); --replace_result www.help.com X www.host.com X www.google.com X select REQ_ID, Group_Concat(URL) as URL from t1, t2 where t2.URL_ID = t1.URL_ID group by REQ_ID; +# check min/max function +--replace_result www.help.com X www.host.com X www.google.com X +select REQ_ID, Group_Concat(URL) as URL, Min(t1.URL_ID) urll, +Max(t1.URL_ID) urlg from t1, t2 where t2.URL_ID = t1.URL_ID group by REQ_ID; + drop table t1; drop table t2; @@ -99,3 +104,19 @@ insert into t1 values (1,'longername'),(1,'evenlongername'); select ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'without distinct: how it should be' from t1; select distinct ifnull(group_concat(concat(t1.id, ':', t1.name)), 'shortname') as 'with distinct: cutoff at length of shortname' from t1; drop table t1; + +# check zero rows +create table t1(id int); +create table t2(id int); +insert into t1 values(0),(1); +select group_concat(t1.id) FROM t1,t2; +drop table t1; +drop table t2; + +# check having +create table t1 (bar varchar(32)); +insert into t1 values('test'),('test2'); +select * from t1 having group_concat(bar)=''; +drop table t1; + + diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 07e62ae35de..814612cfca8 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -1460,7 +1460,7 @@ int group_concat_key_cmp_with_distinct(void* arg, byte* key1, for (uint i= 0; i < item->arg_count_field; i++) { Item *field_item= item->args[i]; - Field *field= field_item->real_item()->tmp_table_field(); + Field *field= field_item->real_item()->get_tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1491,7 +1491,7 @@ int group_concat_key_cmp_with_order(void* arg, byte* key1, byte* key2) { ORDER *order_item= item->order[i]; Item *item= *order_item->item; - Field *field= item->real_item()->tmp_table_field(); + Field *field= item->real_item()->get_tmp_table_field(); if (field) { uint offset= field->abs_offset; @@ -1542,7 +1542,7 @@ int dump_leaf_key(byte* key, uint32 count __attribute__((unused)), Item *show_item= group_concat_item->args[i]; if (!show_item->const_item()) { - Field *f= show_item->real_item()->tmp_table_field(); + Field *f= show_item->real_item()->get_tmp_table_field(); char *sv= f->ptr; f->ptr= (char *)key + f->abs_offset; String *res= f->val_str(&tmp,&tmp2); @@ -1709,7 +1709,7 @@ bool Item_func_group_concat::add() Item *show_item= args[i]; if (!show_item->const_item()) { - Field *f= show_item->real_item()->tmp_table_field(); + Field *f= show_item->real_item()->get_tmp_table_field(); if (!f->is_null()) { record_is_null= FALSE; diff --git a/sql/item_sum.h b/sql/item_sum.h index 7dca0502cb0..b2377a96833 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -386,8 +386,8 @@ class Item_sum_hybrid :public Item_sum Item_sum_hybrid(THD *thd, Item_sum_hybrid &item): Item_sum(thd, item), value(item.value), tmp_value(item.tmp_value), sum(item.sum), sum_int(item.sum_int), hybrid_type(item.hybrid_type), - cmp_sign(item.cmp_sign), used_table_cache(used_table_cache), - cmp_charset(item.cmp_charset) {} + hybrid_field_type(item.hybrid_field_type),cmp_sign(item.cmp_sign), + used_table_cache(used_table_cache), cmp_charset(item.cmp_charset) {} bool fix_fields(THD *, TABLE_LIST *, Item **); table_map used_tables() const { return used_table_cache; } bool const_item() const { return !used_table_cache; } From 548d7dc948607e8ac228463f42c161aa50288086 Mon Sep 17 00:00:00 2001 From: "gluh@gluh.mysql.r18.ru" <> Date: Thu, 4 Sep 2003 17:12:20 +0500 Subject: [PATCH 23/30] Fix for bug 1176 --- mysql-test/r/derived.result | 8 ++++++++ mysql-test/t/derived.test | 13 +++++++++++++ sql/sql_derived.cc | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/derived.result b/mysql-test/r/derived.result index f9e52174469..944253bd527 100644 --- a/mysql-test/r/derived.result +++ b/mysql-test/r/derived.result @@ -196,3 +196,11 @@ drop table t1,t2; SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1; x 1 +create table a1 select 1 as a; +select 2 as a from (select * from a1) b; +ERROR 3D000: No Database Selected +use test; +select 2 as a from (select * from a1) b; +a +2 +drop table a1; diff --git a/mysql-test/t/derived.test b/mysql-test/t/derived.test index c3edbabcd53..3c735878e46 100644 --- a/mysql-test/t/derived.test +++ b/mysql-test/t/derived.test @@ -94,3 +94,16 @@ drop table t1,t2; # derived table reference # SELECT a.x FROM (SELECT 1 AS x) AS a HAVING a.x = 1; + +# +# Test for select if database is not selected. +# +# Connect without a database +create table a1 select 1 as a; +connect (con1,localhost,mysqltest_1,,*NO-ONE*,$MASTER_MYPORT,master.sock); +connection con1; +--error 1046 +select 2 as a from (select * from a1) b; +use test; +select 2 as a from (select * from a1) b; +drop table a1; diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 771d68e8462..5d05fea4fe3 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -96,7 +96,7 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit, else res= check_access(thd, SELECT_ACL, any_db); if (res) - DBUG_RETURN(-1); + DBUG_RETURN(1); if (!(res=open_and_lock_tables(thd,tables))) { From c7297eba3eb34fbad81bfb16aa9ee55dfde0c770 Mon Sep 17 00:00:00 2001 From: "hf@deer.(none)" <> Date: Sat, 6 Sep 2003 18:50:30 +0500 Subject: [PATCH 24/30] Fix for LOAD DATA to work with embedded library --- libmysqld/lib_sql.cc | 1 - sql/sql_load.cc | 30 +++++++++++++++++++++++------- sql/sql_parse.cc | 4 ++-- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 05c36f353fd..00ec550273c 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -34,7 +34,6 @@ static char inited, org_my_init_done; #include "../sql/mysqld.cc" #endif -#define SCRAMBLE_LENGTH 8 C_MODE_START #include #include "errmsg.h" diff --git a/sql/sql_load.cc b/sql/sql_load.cc index d030eaf617c..dd6bdf45e82 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -77,9 +77,6 @@ static int read_sep_field(THD *thd,COPY_INFO &info,TABLE *table, List &fields, READ_INFO &read_info, String &enclosed); - -#ifndef EMBEDDED_LIBRARY - int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, List &fields, enum enum_duplicates handle_duplicates, bool read_file_from_client,thr_lock_type lock_type) @@ -91,7 +88,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, String *field_term=ex->field_term,*escaped=ex->escaped, *enclosed=ex->enclosed; bool is_fifo=0; +#ifndef EMBEDDED_LIBRARY LOAD_FILE_INFO lf_info; +#endif char *db = table_list->db; // This is never null /* If no current database, use database where table is located */ char *tdb= thd->db ? thd->db : db; @@ -184,6 +183,17 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } else { +#ifdef EMBEDDED_LIBRARY + char *chk_name= ex->file_name; + while ((*chk_name == ' ') || (*chk_name == 't')) + chk_name++; + if (*chk_name == FN_CURLIB) + { + sprintf(name, "%s%s", mysql_data_home, ex->file_name); + unpack_filename(name, name); + } + else +#endif /*EMBEDDED_LIBRARY*/ unpack_filename(name,ex->file_name); #if !defined(__WIN__) && !defined(OS2) && ! defined(__NETWARE__) MY_STAT stat_info; @@ -225,6 +235,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, DBUG_RETURN(-1); // Can't allocate buffers } +#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) { lf_info.thd = thd; @@ -238,6 +249,8 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, lf_info.log_delayed= log_delayed; read_info.set_io_cache_arg((void*) &lf_info); } +#endif /*!EMBEDDED_LIBRARY*/ + restore_record(table,default_values); thd->count_cuted_fields=1; /* calc cuted fields */ @@ -293,6 +306,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, { if (transactional_table) ha_autocommit_or_rollback(thd,error); +#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) { if (lf_info.wrote_create_file) @@ -315,6 +329,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, mysql_bin_log.write(&d); } } +#endif /*!EMBEDDED_LIBRARY*/ error= -1; // Error on read goto err; } @@ -327,6 +342,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, if (!log_delayed) thd->options|=OPTION_STATUS_NO_TRANS_UPDATE; +#ifndef EMBEDDED_LIBRARY if (mysql_bin_log.is_open()) { if (opt_old_rpl_compat) @@ -348,6 +364,7 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, } } } +#endif /*!EMBEDDED_LIBRARY*/ if (transactional_table) error=ha_autocommit_or_rollback(thd,error); err: @@ -359,8 +376,6 @@ err: DBUG_RETURN(error); } -#endif /* EMBEDDED_LIBRARY */ - /**************************************************************************** ** Read of rows of fixed size + optional garage + optonal newline ****************************************************************************/ @@ -640,11 +655,12 @@ READ_INFO::READ_INFO(File file_par, uint tot_length, CHARSET_INFO *cs, mysys/mf_iocache.c. So we work around the problem with a manual assignment */ + need_end_io_cache = 1; + +#ifndef EMBEDDED_LIBRARY if (get_it_from_net) cache.read_function = _my_b_net_read; - need_end_io_cache = 1; -#ifndef EMBEDDED_LIBRARY if (!opt_old_rpl_compat && mysql_bin_log.is_open()) cache.pre_read = cache.pre_close = (IO_CACHE_CALLBACK) log_loaded_block; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a5d83c2951d..7e520aabf21 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2755,7 +2755,7 @@ mysql_execute_command(THD *thd) case SQLCOM_CHANGE_DB: mysql_change_db(thd,select_lex->db); break; -#ifndef EMBEDDED_LIBRARY + case SQLCOM_LOAD: { uint privilege= (lex->duplicates == DUP_REPLACE ? @@ -2782,7 +2782,7 @@ mysql_execute_command(THD *thd) lex->duplicates, (bool) lex->local_file, lex->lock_option); break; } -#endif /* EMBEDDED_LIBRARY */ + case SQLCOM_SET_OPTION: if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) || (res= open_and_lock_tables(thd,tables)))) From a02094ef103e7c8f41ffa78ebbbb5d9a21d7468d Mon Sep 17 00:00:00 2001 From: "bell@sanja.is.com.ua" <> Date: Sun, 7 Sep 2003 20:35:10 +0300 Subject: [PATCH 25/30] fixed row union processing --- mysql-test/r/subselect.result | 8 ++++++++ mysql-test/t/subselect.test | 10 ++++++++++ sql/item_subselect.cc | 2 +- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index dd8aeba8563..625fb9f9c29 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1361,3 +1361,11 @@ userid pmtotal pmnew calc_total calc_new 1 0 0 9 3 2 0 0 4 2 drop table t1, t2; +create table t1 (s1 char(5)); +select (select 'a','b' from t1 union select 'a','b' from t1) from t1; +ERROR 21000: Cardinality error (more/less than 1 columns) +insert into t1 values ('tttt'); +select * from t1 where ('a','b')=(select 'a','b' from t1 union select 'a','b' from t1); +s1 +tttt +drop table t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index c9dba498428..4b8d63b681e 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -922,3 +922,13 @@ insert into t2 values(1,0,0),(2,0,0); insert into t1 values(1,0),(1,0),(1,0),(1,12),(1,15),(1,123),(1,12312),(1,12312),(1,123),(2,0),(2,0),(2,1),(2,2); select userid,pmtotal,pmnew, (select count(rd) from t1 where toid=t2.userid) calc_total, (select count(rd) from t1 where rd=0 and toid=t2.userid) calc_new from t2 where userid in (select distinct toid from t1); drop table t1, t2; + +# +# row union +# +create table t1 (s1 char(5)); +-- error 1240 +select (select 'a','b' from t1 union select 'a','b' from t1) from t1; +insert into t1 values ('tttt'); +select * from t1 where ('a','b')=(select 'a','b' from t1 union select 'a','b' from t1); +drop table t1; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index d383bf65c88..79366086a8d 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -846,7 +846,7 @@ void subselect_union_engine::fix_length_and_dec(Item_cache **row) SELECT_LEX *sl= unit->first_select(); bool fake= 0; res_type= set_row(sl, item, row, &fake); - for (sl= sl->next_select(); sl; sl->next_select()) + for (sl= sl->next_select(); sl; sl= sl->next_select()) { List_iterator_fast li(sl->item_list); Item *sel_item; From 2ba517a3f14653006f2e309494256bfd47c34d4d Mon Sep 17 00:00:00 2001 From: "hf@deer.(none)" <> Date: Mon, 8 Sep 2003 13:11:18 +0500 Subject: [PATCH 26/30] fix for #1210 --- include/errmsg.h | 1 + libmysql/errmsg.c | 9 ++++++--- libmysqld/lib_sql.cc | 7 ++++--- libmysqld/libmysqld.c | 16 ++++++++++++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/include/errmsg.h b/include/errmsg.h index 1f4e6e12f00..a354c125e36 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -86,3 +86,4 @@ extern const char *client_errors[]; /* Error messages */ #define CR_SHARED_MEMORY_CONNECT_SET_ERROR 2045 #define CR_CONN_UNKNOW_PROTOCOL 2046 #define CR_INVALID_CONN_HANDLE 2047 +#define CR_MYSQL_SERVER_INIT_MISSED 2048 diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index bbb85885886..d27e981aaab 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -71,7 +71,8 @@ const char *client_errors[]= "Can't open shared memory. Server abandoded and don't sent the answer event (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", - "Invalid connection handle" + "Invalid connection handle", + "mysql_server_init wasn't called" }; /* Start of code added by Roberto M. Serqueira - martinsc@uol.com.br - 05.24.2001 */ @@ -126,7 +127,8 @@ const char *client_errors[]= "Can't open shared memory. Server abandoded and don't sent the answer event (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", - "Invalid connection handle" + "Invalid connection handle", + "mysql_server_init wasn't called" }; #else /* ENGLISH */ @@ -179,7 +181,8 @@ const char *client_errors[]= "Can't open shared memory. Server abandoded and don't sent the answer event (%lu)", "Can't open shared memory. Can't send the request event to server (%lu)", "Wrong or unknown protocol", - "Invalid connection handle" + "Invalid connection handle", + "mysql_server_init wasn't called" }; #endif diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 00ec550273c..31deb1afb01 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -26,7 +26,8 @@ static int fake_argc= 1; static char *fake_argv[]= {(char *)"", 0}; static const char *fake_groups[] = { "server", "embedded", 0 }; -static char inited, org_my_init_done; +static char org_my_init_done; +char server_inited; #if defined (__WIN__) #include "../sql/mysqld.cpp" @@ -181,9 +182,9 @@ int STDCALL mysql_server_init(int argc, char **argv, char **groups) /* Only call MY_INIT() if it hasn't been called before */ - if (!inited) + if (!server_inited) { - inited=1; + server_inited=1; org_my_init_done=my_init_done; } if (!org_my_init_done) diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c index 48b3397ee7c..0c772587c4b 100644 --- a/libmysqld/libmysqld.c +++ b/libmysqld/libmysqld.c @@ -79,6 +79,8 @@ struct passwd *getpwuid(uid_t); char* getlogin(void); #endif +extern char server_inited; + #ifdef __WIN__ static my_bool is_NT(void) { @@ -210,6 +212,20 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, db ? db : "(Null)", user ? user : "(Null)")); +#ifdef EMBEDDED_LIBRARY + /* + Here we check that mysql_server_init was called before. + Actually we can perform the test for client (not embedded) library as well. + But i'm afraid some old applications will be broken then. + */ + if (!server_inited) + { + mysql->net.last_errno=CR_MYSQL_SERVER_INIT_MISSED; + strmov(mysql->net.last_error,ER(mysql->net.last_errno)); + goto error; + } +#endif /*EMBEDDED_LIBRARY*/ + if (mysql->options.methods_to_use == MYSQL_OPT_USE_REMOTE_CONNECTION) cli_mysql_real_connect(mysql, host, user, passwd, db, port, unix_socket, client_flag); From 9807a3fa554656d85467e10173d3710b21a6fb97 Mon Sep 17 00:00:00 2001 From: "hf@deer.(none)" <> Date: Mon, 8 Sep 2003 15:08:53 +0500 Subject: [PATCH 27/30] Trimming of embedded library code --- sql/sql_parse.cc | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7e520aabf21..d6e0b95af8c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1155,7 +1155,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, mysql_log.write(thd,command,"%s",thd->db); break; } -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case COM_REGISTER_SLAVE: { if (!register_slave(thd, (uchar*)packet, packet_length)) @@ -1661,7 +1661,7 @@ mysql_execute_command(THD *thd) } #endif } -#endif /* EMBEDDED_LIBRARY */ +#endif /* !EMBEDDED_LIBRARY */ /* TODO: make derived tables processing 'inside' SELECT processing. TODO: solve problem with depended derived tables in subselects @@ -1837,7 +1837,7 @@ mysql_execute_command(THD *thd) break; } -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_SHOW_SLAVE_HOSTS: { if (check_global_access(thd, REPL_SLAVE_ACL)) @@ -1883,7 +1883,7 @@ mysql_execute_command(THD *thd) } -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_CHANGE_MASTER: { if (check_global_access(thd, SUPER_ACL)) @@ -1920,7 +1920,7 @@ mysql_execute_command(THD *thd) else res = load_master_data(thd); break; -#endif /* EMBEDDED_LIBRARY */ +#endif /* HAVE_REPLICATION */ #ifdef HAVE_INNOBASE_DB case SQLCOM_SHOW_INNODB_STATUS: @@ -1932,7 +1932,7 @@ mysql_execute_command(THD *thd) } #endif -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_LOAD_MASTER_TABLE: { if (!tables->db) @@ -1964,7 +1964,7 @@ mysql_execute_command(THD *thd) UNLOCK_ACTIVE_MI; break; } -#endif /* EMBEDDED_LIBRARY */ +#endif /* HAVE_REPLICATION */ case SQLCOM_CREATE_TABLE: { @@ -2081,7 +2081,7 @@ mysql_execute_command(THD *thd) res = mysql_create_index(thd, tables, lex->key_list); break; -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION case SQLCOM_SLAVE_START: { LOCK_ACTIVE_MI; @@ -2114,7 +2114,7 @@ mysql_execute_command(THD *thd) UNLOCK_ACTIVE_MI; break; } -#endif +#endif /* HAVE_REPLICATION */ case SQLCOM_ALTER_TABLE: #if defined(DONT_ALLOW_SHOW_COMMANDS) @@ -4376,7 +4376,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, refresh_status(); if (options & REFRESH_THREADS) flush_thread_cache(); -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION if (options & REFRESH_MASTER) { tmp_write_to_binlog= 0; @@ -4391,7 +4391,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, result=load_des_key_file(des_key_file); } #endif -#ifndef EMBEDDED_LIBRARY +#ifdef HAVE_REPLICATION if (options & REFRESH_SLAVE) { tmp_write_to_binlog= 0; From cf0c41aab4e2892e14d70e5f7bb2ae0c2449dd3a Mon Sep 17 00:00:00 2001 From: "hf@deer.(none)" <> Date: Mon, 8 Sep 2003 17:00:39 +0500 Subject: [PATCH 28/30] fix for #1218 --- sql/sql_parse.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d6e0b95af8c..ff66892262d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -2620,8 +2620,10 @@ mysql_execute_command(THD *thd) break; #endif case SQLCOM_SHOW_PROCESSLIST: +#ifndef EMBEDDED_LIBRARY if (!thd->priv_user[0] && check_global_access(thd,PROCESS_ACL)) break; +#endif mysqld_list_processes(thd,thd->master_access & PROCESS_ACL ? NullS : thd->priv_user,lex->verbose); break; From 90bd948e1e416f3cf1b692f1e679b250f625dbbc Mon Sep 17 00:00:00 2001 From: "hf@deer.(none)" <> Date: Mon, 8 Sep 2003 18:49:23 +0500 Subject: [PATCH 29/30] fix for #1219 --- libmysqld/lib_sql.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 31deb1afb01..5b3278694e3 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -37,6 +37,7 @@ char server_inited; C_MODE_START #include +#undef ER #include "errmsg.h" #include @@ -55,12 +56,13 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, { my_bool result= 1; THD *thd=(THD *) mysql->thd; + NET *net= &mysql->net; /* Check that we are calling the client functions in right order */ if (mysql->status != MYSQL_STATUS_READY) { - strmov(thd->net.last_error, - ER(thd->net.last_errno=CR_COMMANDS_OUT_OF_SYNC)); + strmov(net->last_error, + ER(net->last_errno=CR_COMMANDS_OUT_OF_SYNC)); return 1; } @@ -76,12 +78,12 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command, if (!skip_check) result= thd->net.last_errno ? -1 : 0; - if ((mysql->net.last_errno= thd->net.last_errno)) + if ((net->last_errno= thd->net.last_errno)) { - memcpy(mysql->net.last_error, thd->net.last_error, - sizeof(mysql->net.last_error)); - memcpy(mysql->net.sqlstate, thd->net.sqlstate, - sizeof(mysql->net.sqlstate)); + memcpy(net->last_error, net->last_error, + sizeof(net->last_error)); + memcpy(net->sqlstate, thd->net.sqlstate, + sizeof(net->sqlstate)); } mysql->warning_count= ((THD*)mysql->thd)->total_warn_count; return result; From 5e328057134ef81e4bf4f8896fd74a61e464f60c Mon Sep 17 00:00:00 2001 From: "Sinisa@sinisa.nasamreza.org" <> Date: Tue, 9 Sep 2003 15:23:38 +0300 Subject: [PATCH 30/30] Fix for a bug #1226. Happens when braces are used on a single select, which leads to the uninitialized global parameters structure. --- mysql-test/r/subselect.result | 6 ++++++ mysql-test/t/subselect.test | 2 ++ sql/sql_yacc.yy | 5 +++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result index 625fb9f9c29..52cd500944c 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -1368,4 +1368,10 @@ insert into t1 values ('tttt'); select * from t1 where ('a','b')=(select 'a','b' from t1 union select 'a','b' from t1); s1 tttt +explain (select * from t1); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 system NULL NULL NULL NULL 1 +(select * from t1); +s1 +tttt drop table t1; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 4b8d63b681e..bd6a4037f3d 100644 --- a/mysql-test/t/subselect.test +++ b/mysql-test/t/subselect.test @@ -931,4 +931,6 @@ create table t1 (s1 char(5)); select (select 'a','b' from t1 union select 'a','b' from t1) from t1; insert into t1 values ('tttt'); select * from t1 where ('a','b')=(select 'a','b' from t1 union select 'a','b' from t1); +explain (select * from t1); +(select * from t1); drop table t1; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c753123b2be..b84a21b1440 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2012,8 +2012,9 @@ select_init: YYABORT; } /* select in braces, can't contain global parameters */ - sel->master_unit()->global_parameters= - sel->master_unit()->fake_select_lex; + if (sel->master_unit()->fake_select_lex) + sel->master_unit()->global_parameters= + sel->master_unit()->fake_select_lex; } union_opt; select_init2: