3880 Ashish Agarwal 2012-04-30
WL2739: Including password validation on multiple
character set.
modified:
include/mysql/plugin_validate_password.h
mysql-test/r/validate_password_plugin.result
mysql-test/t/validate_password_plugin.test
plugin/password_validation/validate_password.cc
sql/item_func.cc
sql/item_strfunc.cc
sql/sql_acl.cc
sql/sql_acl.h
sql/sql_plugin.h
sql/sql_yacc.yy
3879 Ashish Agarwal 2012-04-24
wl2739: Build failure in windows and some additional changes.
modified:
plugin/password_validation/validate_password.cc
sql/sql_plugin.h
=== modified file 'include/mysql/plugin_validate_password.h'
--- a/include/mysql/plugin_validate_password.h 2012-04-24 11:58:02 +0000
+++ b/include/mysql/plugin_validate_password.h 2012-04-30 06:36:01 +0000
@@ -20,6 +20,7 @@
#define MYSQL_PLUGIN_VALIDATE_PASSWORD_INCLUDED
#include <mysql/plugin.h>
+#include <sql_string.h>
#define MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION 0x0100
@@ -36,11 +37,11 @@ struct st_mysql_validate_password
policy (as choosen by plugin variable) and FALSE for all other
password
*/
- bool (*validate_password)(const char *password);
+ bool (*validate_password)(String *password);
/**
This function returns the highest policy number which the password
satisfy.
*/
- uint (*validate_password_strength)(const char *password);
+ uint (*validate_password_strength)(String *password);
};
#endif
=== modified file 'mysql-test/r/validate_password_plugin.result'
--- a/mysql-test/r/validate_password_plugin.result 2012-04-23 15:41:09 +0000
+++ b/mysql-test/r/validate_password_plugin.result 2012-04-30 06:36:01 +0000
@@ -1,10 +1,11 @@
+CALL mtr.add_suppression("dictionary file not found");
+CALL mtr.add_suppression("dictionary file size too large");
CREATE USER 'base_user'@'localhost' IDENTIFIED BY '';
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
ERROR HY000: Function 'validate_password' already exists
-policy: low= 1, medium= 2, strong= 3
-password policy low (which only check for password length)
-default case: password length should be minimum 8
+# password policy low (which only check for password length)
+# default case: password length should be minimum 8
SET @@global.validate_password_policy_number= 1;
CREATE USER 'user'@'localhost' IDENTIFIED BY '';
ERROR HY000: not a valid password ''
@@ -14,8 +15,8 @@ UPDATE mysql.user SET PASSWORD= PASSWORD
ERROR HY000: not a valid password 'password'
GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234';
SET @@global.validate_password_length= 8;
-password policy medium (check for mixed_case, digits, special_chars)
-default case : atleast 1 mixed_case, 1 digit, 1 special_char
+# password policy medium (check for mixed_case, digits, special_chars)
+# default case : atleast 1 mixed_case, 1 digit, 1 special_char
SET @@global.validate_password_policy_number= 2;
CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';
ERROR HY000: not a valid password 'password'
@@ -41,9 +42,9 @@ SET @@global.validate_password_policy_nu
CREATE USER 'user'@'localhost' IDENTIFIED BY 'password';
ERROR HY000: not a valid password 'password'
SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
-ERROR HY000: not a valid password 'password1A#'
+ERROR HY000: not a valid password 'password1a#'
UPDATE mysql.user SET PASSWORD= PASSWORD('pass12345A#') WHERE user='base_user';
-ERROR HY000: not a valid password 'pass12345A#'
+ERROR HY000: not a valid password 'pass12345a#'
GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'PA12wrd!#';
# test for password_validate_strength function
SELECT VALIDATE_PASSWORD_STRENGTH('password', 0);
@@ -51,9 +52,11 @@ ERROR 42000: Incorrect parameter count i
SELECT VALIDATE_PASSWORD_STRENGTH();
ERROR 42000: Incorrect parameter count in the call to native function 'VALIDATE_PASSWORD_STRENGTH'
SELECT VALIDATE_PASSWORD_STRENGTH('');
-ERROR HY000: not a valid password ''
+VALIDATE_PASSWORD_STRENGTH('')
+0
SELECT VALIDATE_PASSWORD_STRENGTH('pass');
-ERROR HY000: not a valid password 'pass'
+VALIDATE_PASSWORD_STRENGTH('pass')
+0
SELECT VALIDATE_PASSWORD_STRENGTH('password');
VALIDATE_PASSWORD_STRENGTH('password')
1
@@ -63,5 +66,20 @@ VALIDATE_PASSWORD_STRENGTH('password1A#'
SELECT VALIDATE_PASSWORD_STRENGTH('PA12wrd!#');
VALIDATE_PASSWORD_STRENGTH('PA12wrd!#')
3
+SET NAMES 'ujis';
+SELECT VALIDATE_PASSWORD_STRENGTH('PA12wrd!#');
+VALIDATE_PASSWORD_STRENGTH('PA12wrd!#')
+3
+UNINSTALL PLUGIN validate_password;
+# restarting the server with no dictionary file.
+# Restart server.
+INSTALL PLUGIN validate_password SONAME 'validate_password.so';
+SET @@global.validate_password_policy_number= 3;
+# same password was not accepted as it was present in the dictionary file
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+# As no dictionary file is present password strength is 3
+SELECT VALIDATE_PASSWORD_STRENGTH('password1A#');
+VALIDATE_PASSWORD_STRENGTH('password1A#')
+3
DROP USER 'base_user'@'localhost';
UNINSTALL PLUGIN validate_password;
=== modified file 'mysql-test/t/validate_password_plugin.test'
--- a/mysql-test/t/validate_password_plugin.test 2012-04-23 15:41:09 +0000
+++ b/mysql-test/t/validate_password_plugin.test 2012-04-30 06:36:01 +0000
@@ -1,5 +1,7 @@
--source include/not_embedded.inc
--source include/have_validate_password_plugin.inc
+CALL mtr.add_suppression("dictionary file not found");
+CALL mtr.add_suppression("dictionary file size too large");
CREATE USER 'base_user'@'localhost' IDENTIFIED BY '';
--replace_regex /\.dll/.so/
@@ -8,10 +10,10 @@ eval INSTALL PLUGIN validate_password SO
eval INSTALL PLUGIN validate_password SONAME '$VALIDATE_PASSWORD';
# test for all the three password policy
---echo policy: low= 1, medium= 2, strong= 3
+# policy: low= 1, medium= 2, strong= 3
---echo password policy low (which only check for password length)
---echo default case: password length should be minimum 8
+--echo # password policy low (which only check for password length)
+--echo # default case: password length should be minimum 8
SET @@global.validate_password_policy_number= 1;
--error ER_NOT_VALID_PASSWORD
@@ -23,8 +25,8 @@ UPDATE mysql.user SET PASSWORD= PASSWORD
GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234';
SET @@global.validate_password_length= 8;
---echo password policy medium (check for mixed_case, digits, special_chars)
---echo default case : atleast 1 mixed_case, 1 digit, 1 special_char
+--echo # password policy medium (check for mixed_case, digits, special_chars)
+--echo # default case : atleast 1 mixed_case, 1 digit, 1 special_char
SET @@global.validate_password_policy_number= 2;
--error ER_NOT_VALID_PASSWORD
@@ -59,19 +61,59 @@ SET PASSWORD FOR 'base_user'@'localhost'
UPDATE mysql.user SET PASSWORD= PASSWORD('pass12345A#') WHERE user='base_user';
GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'PA12wrd!#';
+
--echo # test for password_validate_strength function
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT VALIDATE_PASSWORD_STRENGTH('password', 0);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT VALIDATE_PASSWORD_STRENGTH();
---error ER_NOT_VALID_PASSWORD
SELECT VALIDATE_PASSWORD_STRENGTH('');
---error ER_NOT_VALID_PASSWORD
SELECT VALIDATE_PASSWORD_STRENGTH('pass');
SELECT VALIDATE_PASSWORD_STRENGTH('password');
SELECT VALIDATE_PASSWORD_STRENGTH('password1A#');
SELECT VALIDATE_PASSWORD_STRENGTH('PA12wrd!#');
+# Multibyte character set that have greater size when converted
+# from uppercase to lowercase.
+
+SET NAMES 'ujis';
+SELECT VALIDATE_PASSWORD_STRENGTH('PA12wrd!#');
+
+UNINSTALL PLUGIN validate_password;
+
+--echo # restarting the server with no dictionary file.
+
+# Write file to make mysql-test-run.pl wait for the server to stop
+-- exec echo "wait" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+# Request shutdown
+-- send_shutdown
+
+# Call script that will poll the server waiting for it to disapear
+-- source include/wait_until_disconnected.inc
+
+--echo # Restart server.
+--exec echo "restart:--loose-validate_password_dictionary_file='NO'" > $MYSQLTEST_VARDIR/tmp/mysqld.1.expect
+
+# Turn on reconnect
+--enable_reconnect
+
+# Call script that will poll the server waiting for it to be back online again
+--source include/wait_until_connected_again.inc
+
+# Turn off reconnect again
+--disable_reconnect
+
+--replace_regex /\.dll/.so/
+eval INSTALL PLUGIN validate_password SONAME '$VALIDATE_PASSWORD';
+
+SET @@global.validate_password_policy_number= 3;
+--echo # same password was not accepted as it was present in the dictionary file
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+
+--echo # As no dictionary file is present password strength is 3
+SELECT VALIDATE_PASSWORD_STRENGTH('password1A#');
+
DROP USER 'base_user'@'localhost';
UNINSTALL PLUGIN validate_password;
=== modified file 'plugin/password_validation/validate_password.cc'
--- a/plugin/password_validation/validate_password.cc 2012-04-24 15:36:30 +0000
+++ b/plugin/password_validation/validate_password.cc 2012-04-30 06:36:01 +0000
@@ -14,23 +14,22 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include <m_ctype.h>
-#include <cctype>
-#include <algorithm>
#include <string>
#include <mysql/plugin_validate_password.h>
#include <set>
-#include <iostream>
+#include <iostream> /* dictionary file read */
#include <fstream>
-#include "sql_plugin.h"
+#include "sql_plugin.h" /* for opt_plugin_dir */
+#include "log.h" /* for warning */
#define MAX_DICTIONARY_FILE_LENGTH 1024 * 1024
-#define MIN_DICTIONARY_WORD_LENGTH 3
+#define MIN_DICTIONARY_WORD_LENGTH 4
enum PASSWORD_POLICY { PASSWORD_STRENGTH_REJECTED, PASSWORD_STRENGTH_LOW,
PASSWORD_STRENGTH_MEDIUM, PASSWORD_STRENGTH_STRONG };
typedef std::string string_type;
-typedef std::set<std::string> set_type;
+typedef std::set<string_type> set_type;
set_type dictionary_words;
static uint validate_password_length;
@@ -40,63 +39,83 @@ static uint validate_password_special_ch
static uint validate_password_policy_number;
static char *validate_password_dictionary_file;
-static bool validate_dictionary_check(const char *password)
+static bool validate_dictionary_check(String *password)
{
- string_type password_str(password);
+ const CHARSET_INFO *cs= password->charset();
+ String tmp_password_str;
+ if (cs->casedn_multiply == 1)
+ {
+ uint len;
+ len= cs->cset->casedn(cs, (char*) password->ptr(), password->length(),
+ (char*) password->ptr(), password->length());
+ password->length(len);
+ }
+ else
+ {
+ uint len= password->length() * cs->casedn_multiply;
+ tmp_password_str.alloc(len);
+ tmp_password_str.set_charset(cs);
+ len= cs->cset->casedn(cs, (char*) password->ptr(), password->length(),
+ (char*) tmp_password_str.ptr(), len);
+ tmp_password_str.length(len);
+ password= &tmp_password_str;
+ }
+ uint substr_pos= 0;
+ uint substr_length= password->length();
+ string_type password_str(password->ptr());
string_type password_substr;
set_type::iterator itr;
- uint password_length= strlen(password);
- uint substr_length= password_length;
- uint substr_pos= 0;
+
while (substr_length >= MIN_DICTIONARY_WORD_LENGTH)
{
substr_pos= 0;
- while (substr_pos + substr_length <= password_length)
+ while (substr_pos + substr_length <= password->length())
{
password_substr= password_str.substr(substr_pos, substr_length);
- std::transform(password_substr.begin(), password_substr.end(),
- password_substr.begin(), (int(*)(int)) std::toupper);
itr= dictionary_words.find(password_substr);
if (itr != dictionary_words.end())
return 0;
substr_pos++;
- }
- substr_length--;
- }
+ }
+ substr_length--;
+ }
return 1;
}
-static bool validate_password_policy(const char *password, uint policy)
+static bool validate_password_policy(String *password, uint policy)
{
uint has_numbers= 0;
uint has_special_chars= 0;
uint has_lower= 0;
uint has_upper= 0;
- uint password_length= strlen(password);
- const char *c= password;
- if (password_length >= validate_password_length)
+ char *beg= (char*) password->ptr();
+ char *end= (char*) password->ptr() + password->length();
+ const CHARSET_INFO *cs= password->charset();
+ if (password->length() >= validate_password_length)
{
if (policy == PASSWORD_STRENGTH_LOW)
return 1;
- while (*c != '\0')
+ while (beg < end)
{
- if (my_isdigit(&my_charset_latin1, *c))
+ int ctype, char_len;
+ char_len= cs->cset->ctype(cs, &ctype, (uchar*) beg, (uchar*) end);
+ if (ctype & _MY_NMR)
has_numbers++;
- else if (my_isupper(&my_charset_latin1, *c))
+ else if (ctype & _MY_U)
has_upper++;
- else if (my_islower(&my_charset_latin1, *c))
+ else if (ctype & _MY_L)
has_lower++;
else
has_special_chars++;
- c++;
+ beg+= (char_len > 0 ? char_len : (char_len < 0 ? -char_len : 1));
}
- if (has_upper >= validate_password_mixed_case_count && has_lower >=
- validate_password_mixed_case_count && has_special_chars >=
- validate_password_special_char_count && has_numbers >=
- validate_password_number_count)
+ if (has_upper >= validate_password_mixed_case_count &&
+ has_lower >= validate_password_mixed_case_count &&
+ has_special_chars >= validate_password_special_char_count &&
+ has_numbers >= validate_password_number_count)
{
- if (policy == PASSWORD_STRENGTH_MEDIUM ||
+ if (policy == PASSWORD_STRENGTH_MEDIUM || dictionary_words.empty() ||
validate_dictionary_check(password))
return 1;
}
@@ -104,12 +123,12 @@ static bool validate_password_policy(con
return 0;
}
-static bool validate_password(const char *password)
+static bool validate_password(String *password)
{
return validate_password_policy(password, validate_password_policy_number);
}
-static uint validate_password_strength(const char *password)
+static uint validate_password_strength(String *password)
{
if (validate_password_policy(password, PASSWORD_STRENGTH_LOW))
{
@@ -117,7 +136,7 @@ static uint validate_password_strength(c
if (validate_password_policy(password, PASSWORD_STRENGTH_MEDIUM))
{
policy= PASSWORD_STRENGTH_MEDIUM;
- if (validate_password_policy(password, PASSWORD_STRENGTH_STRONG))
+ if (dictionary_words.empty() || validate_dictionary_check(password))
policy= PASSWORD_STRENGTH_STRONG;
}
return policy;
@@ -141,29 +160,31 @@ static int validate_password_init(void *
char *dictionary_file;
char default_dictionary_file[FN_REFLEN];
string_type words;
- long file_length;
- fn_format(default_dictionary_file, "dictionary.txt", opt_plugin_dir,
+ ulonglong file_length;
+ fn_format(default_dictionary_file, "dictionary.txt", opt_plugin_dir_ptr,
"", MYF(0));
dictionary_file= (validate_password_dictionary_file ?
validate_password_dictionary_file :
default_dictionary_file);
-
std::ifstream dictionary_stream(dictionary_file);
if (!dictionary_stream)
- return 1;
+ {
+ if (validate_password_dictionary_file)
+ sql_print_warning("dictionary file not found");
+ return 0;
+ }
dictionary_stream.seekg (0, std::ios::end);
- file_length = dictionary_stream.tellg();
+ file_length= dictionary_stream.tellg();
dictionary_stream.seekg (0, std::ios::beg);
if (file_length > MAX_DICTIONARY_FILE_LENGTH)
{
dictionary_stream.close();
- return 1;
+ sql_print_warning("dictionary file size too large");
+ return 0;
}
while (dictionary_stream.good())
{
std::getline(dictionary_stream, words);
- std::transform(words.begin(), words.end(), words.begin(),
- (int(*)(int)) std::toupper);
dictionary_words.insert(words);
}
dictionary_stream.close();
@@ -183,7 +204,7 @@ static int validate_password_deinit(void
static MYSQL_SYSVAR_UINT(length, validate_password_length,
PLUGIN_VAR_RQCMDARG,
- "Password_validate_length to check for minimum password_length",
+ "Password validate length to check for minimum password_length",
NULL, NULL, 8, 0, 0, 0);
static MYSQL_SYSVAR_UINT(number_count, validate_password_number_count,
=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc 2012-04-16 12:25:21 +0000
+++ b/sql/item_func.cc 2012-04-30 06:36:01 +0000
@@ -3233,8 +3233,7 @@ longlong Item_func_validate_password_str
String *field;
if (!(field= args[0]->val_str(&value)))
return 0;
- const char * password= field->ptr();
- return (check_password_strength(password));
+ return (check_password_strength(field));
}
=== modified file 'sql/item_strfunc.cc'
--- a/sql/item_strfunc.cc 2012-04-23 15:41:09 +0000
+++ b/sql/item_strfunc.cc 2012-04-30 06:36:01 +0000
@@ -1925,7 +1925,7 @@ String *Item_func_password::val_str_asci
String *res= args[0]->val_str(str);
if ((null_value=args[0]->null_value))
return 0;
- check_password_policy(res->ptr());
+ check_password_policy(res);
if (res->length() == 0)
return make_empty_result();
my_make_scrambled_password(tmp_value, res->ptr(), res->length());
=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc 2012-04-23 15:41:09 +0000
+++ b/sql/sql_acl.cc 2012-04-30 06:36:01 +0000
@@ -9917,7 +9917,7 @@ mysql_declare_plugin_end;
/* for validate_password_strength SQL function */
-int check_password_strength(const char *password)
+int check_password_strength(String *password)
{
int res= 0;
plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
@@ -9927,8 +9927,7 @@ int check_password_strength(const char *
st_mysql_validate_password *password_strength=
(st_mysql_validate_password *) plugin_decl(plugin)->info;
- if (!(res= password_strength->validate_password_strength(password)))
- my_error(ER_NOT_VALID_PASSWORD, MYF(0), password);
+ res= password_strength->validate_password_strength(password);
plugin_unlock(0, plugin);
}
return(res);
@@ -9936,7 +9935,7 @@ int check_password_strength(const char *
/* called when new user is created or exsisting password is changed */
-void check_password_policy(const char *password)
+void check_password_policy(String *password)
{
plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
MYSQL_VALIDATE_PASSWORD_PLUGIN);
@@ -9946,7 +9945,7 @@ void check_password_policy(const char *p
(st_mysql_validate_password *) plugin_decl(plugin)->info;
if (!password_validate->validate_password(password))
- my_error(ER_NOT_VALID_PASSWORD, MYF(0), password);
+ my_error(ER_NOT_VALID_PASSWORD, MYF(0), password->ptr());
plugin_unlock(0, plugin);
}
}
=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h 2012-04-23 15:41:09 +0000
+++ b/sql/sql_acl.h 2012-04-30 06:36:01 +0000
@@ -251,8 +251,8 @@ int fill_schema_schema_privileges(THD *t
int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, Item *cond);
int fill_schema_column_privileges(THD *thd, TABLE_LIST *tables, Item *cond);
int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr);
-int check_password_strength(const char *password);
-void check_password_policy(const char *password);
+int check_password_strength(String *password);
+void check_password_policy(String *password);
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
=== modified file 'sql/sql_plugin.h'
--- a/sql/sql_plugin.h 2012-04-24 15:36:30 +0000
+++ b/sql/sql_plugin.h 2012-04-30 06:36:01 +0000
@@ -135,8 +135,8 @@ typedef struct st_plugin_int **plugin_re
typedef int (*plugin_type_init)(struct st_plugin_int *);
extern I_List<i_string> *opt_plugin_load_list_ptr;
-extern char *opt_plugin_dir_ptr;
-extern MYSQL_PLUGIN_IMPORT char opt_plugin_dir[FN_REFLEN];
+extern MYSQL_PLUGIN_IMPORT char *opt_plugin_dir_ptr;
+extern char opt_plugin_dir[FN_REFLEN];
extern const LEX_STRING plugin_type_names[];
extern int plugin_init(int *argc, char **argv, int init_flags);
extern void plugin_shutdown(void);
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2012-04-23 15:41:09 +0000
+++ b/sql/sql_yacc.yy 2012-04-30 06:36:01 +0000
@@ -14199,7 +14199,9 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
- check_password_policy($3.str);
+ String *password = new (YYTHD->mem_root) String((const char*)$3.str,
+ YYTHD->variables.character_set_client);
+ check_password_policy(password);
$$= $3.length ? YYTHD->variables.old_passwords ?
Item_func_old_password::alloc(YYTHD, $3.str, $3.length) :
Item_func_password::alloc(YYTHD, $3.str, $3.length) :
@@ -14708,7 +14710,9 @@ grant_user:
$$=$1; $1->password=$4;
if (Lex->sql_command == SQLCOM_REVOKE)
MYSQL_YYABORT;
- check_password_policy($4.str);
+ String *password = new (YYTHD->mem_root) String((const char*)$4.str,
+ YYTHD->variables.character_set_client);
+ check_password_policy(password);
if ($4.length)
{
if (YYTHD->variables.old_passwords)
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (ashish.y.agarwal:3879 to 3880) | Ashish Agarwal | 30 Apr |