From 2df531fdc44a1bf410a79624932533ecb44f84ce Mon Sep 17 00:00:00 2001 From: Alexey Kopytov Date: Wed, 27 May 2009 14:20:57 +0400 Subject: [PATCH] Bug #44767: invalid memory reads in password() and old_password() functions The PASSWORD() and OLD_PASSWORD() functions could lead to memory reads outside of an internal buffer when used with BLOB arguments. String::c_ptr() assumes there is at least one extra byte in the internally allocated buffer when adding the trailing '\0'. This, however, may not be the case when a String object was initialized with externally allocated buffer. The bug was fixed by adding an additional "length" argument to make_scrambled_password_323() and make_scrambled_password() in order to avoid String::c_ptr() calls for PASSWORD()/OLD_PASSWORD(). However, since the make_scrambled_password[_323] functions are a part of the client library ABI, the functions with the new interfaces were implemented with the 'my_' prefix in their names, with the old functions changed to be wrappers around the new ones to maintain interface compatibility. --- mysql-test/r/func_crypt.result | 11 +++++++ mysql-test/t/func_crypt.test | 12 ++++++++ sql/item_strfunc.cc | 14 +++++---- sql/item_strfunc.h | 4 +-- sql/mysql_priv.h | 6 ++++ sql/password.c | 55 ++++++++++++++++++++++++++++------ sql/sql_yacc.yy | 11 +++---- 7 files changed, 91 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/func_crypt.result b/mysql-test/r/func_crypt.result index afdec0f4d06..42fa18b17b2 100644 --- a/mysql-test/r/func_crypt.result +++ b/mysql-test/r/func_crypt.result @@ -95,3 +95,14 @@ Note 1003 select password(_latin1'idkfa ') AS `password('idkfa ')`,old_password( select encrypt('1234','_.'); encrypt('1234','_.') # +# +# Bug #44767: invalid memory reads in password() and old_password() +# functions +# +CREATE TABLE t1(c1 MEDIUMBLOB); +INSERT INTO t1 VALUES (REPEAT('a', 1024)); +SELECT OLD_PASSWORD(c1), PASSWORD(c1) FROM t1; +OLD_PASSWORD(c1) PASSWORD(c1) +77023ffe214c04ff *82E58A2C08AAFE72C8EB523069CD8ADB33F78F58 +DROP TABLE t1; +End of 5.0 tests diff --git a/mysql-test/t/func_crypt.test b/mysql-test/t/func_crypt.test index cc3cdb9564d..6dedeaa0fef 100644 --- a/mysql-test/t/func_crypt.test +++ b/mysql-test/t/func_crypt.test @@ -56,3 +56,15 @@ explain extended select password('idkfa '), old_password('idkfa'); select encrypt('1234','_.'); # End of 4.1 tests + +--echo # +--echo # Bug #44767: invalid memory reads in password() and old_password() +--echo # functions +--echo # + +CREATE TABLE t1(c1 MEDIUMBLOB); +INSERT INTO t1 VALUES (REPEAT('a', 1024)); +SELECT OLD_PASSWORD(c1), PASSWORD(c1) FROM t1; +DROP TABLE t1; + +--echo End of 5.0 tests diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index bc2dcb9c61b..71d3a34cd27 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -1554,16 +1554,17 @@ String *Item_func_password::val_str(String *str) return 0; if (res->length() == 0) return &my_empty_string; - make_scrambled_password(tmp_value, res->c_ptr()); + my_make_scrambled_password(tmp_value, res->ptr(), res->length()); str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, res->charset()); return str; } -char *Item_func_password::alloc(THD *thd, const char *password) +char *Item_func_password::alloc(THD *thd, const char *password, + size_t pass_len) { char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); if (buff) - make_scrambled_password(buff, password); + my_make_scrambled_password(buff, password, pass_len); return buff; } @@ -1577,16 +1578,17 @@ String *Item_func_old_password::val_str(String *str) return 0; if (res->length() == 0) return &my_empty_string; - make_scrambled_password_323(tmp_value, res->c_ptr()); + my_make_scrambled_password_323(tmp_value, res->ptr(), res->length()); str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH_323, res->charset()); return str; } -char *Item_func_old_password::alloc(THD *thd, const char *password) +char *Item_func_old_password::alloc(THD *thd, const char *password, + size_t pass_len) { char *buff= (char *) thd->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); if (buff) - make_scrambled_password_323(buff, password); + my_make_scrambled_password_323(buff, password, pass_len); return buff; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 1c5346ab074..f2cb4d2d32a 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -281,7 +281,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); + static char *alloc(THD *thd, const char *password, size_t pass_len); }; @@ -300,7 +300,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"; } - static char *alloc(THD *thd, const char *password); + static char *alloc(THD *thd, const char *password, size_t pass_len); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9768668f103..55bcd30999d 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1677,6 +1677,12 @@ extern void turn_parser_debug_on(); SQL_CRYPT *get_crypt_for_frm(void); #endif +/* password.c */ +extern "C" void my_make_scrambled_password_323(char *to, const char *password, + size_t pass_len); +extern "C" void my_make_scrambled_password(char *to, const char *password, + size_t pass_len); + #include "sql_view.h" /* Some inline functions for more speed */ diff --git a/sql/password.c b/sql/password.c index 57ed3e6ab0f..e12074549a4 100644 --- a/sql/password.c +++ b/sql/password.c @@ -137,16 +137,35 @@ void hash_password(ulong *result, const char *password, uint password_len) Create password to be stored in user database from raw string Used for pre-4.1 password handling SYNOPSIS - make_scrambled_password_323() + my_make_scrambled_password_323() to OUT store scrambled password here password IN user-supplied password + pass_len IN length of password string +*/ + +void my_make_scrambled_password_323(char *to, const char *password, + size_t pass_len) +{ + ulong hash_res[2]; + hash_password(hash_res, password, (uint) pass_len); + sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]); +} + + +/* + Wrapper around my_make_scrambled_password_323() to maintain client lib ABI + compatibility. + In server code usage of my_make_scrambled_password_323() is preferred to + avoid strlen(). + SYNOPSIS + make_scrambled_password_323() + to OUT store scrambled password here + password IN NULL-terminated string with user-supplied password */ void make_scrambled_password_323(char *to, const char *password) { - ulong hash_res[2]; - hash_password(hash_res, password, (uint) strlen(password)); - sprintf(to, "%08lx%08lx", hash_res[0], hash_res[1]); + my_make_scrambled_password_323(to, password, strlen(password)); } @@ -383,20 +402,21 @@ my_crypt(char *to, const uchar *s1, const uchar *s2, uint len) The result of this function is used as return value from PASSWORD() and is stored in the database. SYNOPSIS - make_scrambled_password() + my_make_scrambled_password() buf OUT buffer of size 2*SHA1_HASH_SIZE + 2 to store hex string - password IN NULL-terminated password string + password IN password string + pass_len IN length of password string */ -void -make_scrambled_password(char *to, const char *password) +void my_make_scrambled_password(char *to, const char *password, + size_t pass_len) { SHA1_CONTEXT sha1_context; uint8 hash_stage2[SHA1_HASH_SIZE]; mysql_sha1_reset(&sha1_context); /* stage 1: hash password */ - mysql_sha1_input(&sha1_context, (uint8 *) password, (uint) strlen(password)); + mysql_sha1_input(&sha1_context, (uint8 *) password, (uint) pass_len); mysql_sha1_result(&sha1_context, (uint8 *) to); /* stage 2: hash stage1 output */ mysql_sha1_reset(&sha1_context); @@ -409,6 +429,23 @@ make_scrambled_password(char *to, const char *password) } +/* + Wrapper around my_make_scrambled_password() to maintain client lib ABI + compatibility. + In server code usage of my_make_scrambled_password() is preferred to + avoid strlen(). + 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) +{ + my_make_scrambled_password(to, password, strlen(password)); +} + + /* Produce an obscure octet sequence from password and random string, recieved from the server. This sequence corresponds to the diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 61020a3eed0..b6a215a2306 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -10092,15 +10092,16 @@ text_or_password: | PASSWORD '(' TEXT_STRING ')' { $$= $3.length ? YYTHD->variables.old_passwords ? - Item_func_old_password::alloc(YYTHD, $3.str) : - Item_func_password::alloc(YYTHD, $3.str) : + Item_func_old_password::alloc(YYTHD, $3.str, $3.length) : + Item_func_password::alloc(YYTHD, $3.str, $3.length) : $3.str; if ($$ == NULL) MYSQL_YYABORT; } | OLD_PASSWORD '(' TEXT_STRING ')' { - $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str) : + $$= $3.length ? Item_func_old_password::alloc(YYTHD, $3.str, + $3.length) : $3.str; if ($$ == NULL) MYSQL_YYABORT; @@ -10545,7 +10546,7 @@ grant_user: (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH_323+1); if (buff == NULL) MYSQL_YYABORT; - make_scrambled_password_323(buff, $4.str); + my_make_scrambled_password_323(buff, $4.str, $4.length); $1->password.str= buff; $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; } @@ -10555,7 +10556,7 @@ grant_user: (char *) YYTHD->alloc(SCRAMBLED_PASSWORD_CHAR_LENGTH+1); if (buff == NULL) MYSQL_YYABORT; - make_scrambled_password(buff, $4.str); + my_make_scrambled_password(buff, $4.str, $4.length); $1->password.str= buff; $1->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; }