3871 Ashish Agarwal 2012-04-16
WL#2739: Auditing Password Security
added:
include/mysql/plugin_validate_password.h
mysql-test/include/have_validate_password_plugin.inc
mysql-test/r/validate_password_plugin.result
mysql-test/t/validate_password_plugin-master.opt
mysql-test/t/validate_password_plugin.test
plugin/password_validation/
plugin/password_validation/CMakeLists.txt
plugin/password_validation/dictionary.txt
plugin/password_validation/validate_password.cc
modified:
include/CMakeLists.txt
include/mysql/plugin.h
mysql-test/include/plugin.defs
sql/item_create.cc
sql/item_func.cc
sql/item_func.h
sql/item_strfunc.cc
sql/share/errmsg-utf8.txt
sql/sql_acl.cc
sql/sql_acl.h
sql/sql_plugin.cc
sql/sql_yacc.yy
3870 Sujatha Sivakumar 2012-04-12 [merge]
upmerge from mysql-5.5 -> mysql-trunk.
modified:
mysql-test/extra/rpl_tests/rpl_row_basic.test
mysql-test/suite/rpl/r/rpl_row_basic_2myisam.result
mysql-test/suite/rpl/r/rpl_row_basic_3innodb.result
mysql-test/suite/rpl/r/rpl_row_basic_allow_batching.result
sql/log_event.cc
=== modified file 'include/CMakeLists.txt'
--- a/include/CMakeLists.txt 2012-02-16 09:51:14 +0000
+++ b/include/CMakeLists.txt 2012-04-16 12:25:21 +0000
@@ -30,6 +30,7 @@ SET(HEADERS_ABI
mysql/plugin.h
mysql/plugin_audit.h
mysql/plugin_ftparser.h
+ mysql/plugin_validate_password.h
)
SET(HEADERS
=== modified file 'include/mysql/plugin.h'
--- a/include/mysql/plugin.h 2012-03-06 14:29:42 +0000
+++ b/include/mysql/plugin.h 2012-04-16 12:25:21 +0000
@@ -86,7 +86,8 @@ typedef struct st_mysql_xid MYSQL_XID;
#define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */
#define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */
#define MYSQL_AUTHENTICATION_PLUGIN 7 /* The authentication plugin type */
-#define MYSQL_MAX_PLUGIN_TYPE_NUM 8 /* The number of plugin types */
+#define MYSQL_VALIDATE_PASSWORD_PLUGIN 8 /* validate password plugin type */
+#define MYSQL_MAX_PLUGIN_TYPE_NUM 9 /* The number of plugin types */
/* We use the following strings to define licenses for plugins */
#define PLUGIN_LICENSE_PROPRIETARY 0
=== added file 'include/mysql/plugin_validate_password.h'
--- a/include/mysql/plugin_validate_password.h 1970-01-01 00:00:00 +0000
+++ b/include/mysql/plugin_validate_password.h 2012-04-16 12:25:21 +0000
@@ -0,0 +1,43 @@
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+/*************************************************************************
+ API for validate_password plugin. (MYSQL_VALIDATE_PASSWORD_PLUGIN)
+*/
+
+#include <mysql/plugin.h>
+
+#define MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION 0x0100
+
+/*************************************************************************
+ The descriptor structure for the plugin, that is referred from
+ st_mysql_plugin.
+*/
+
+struct st_mysql_validate_password
+{
+ int interface_version; /* version plugin uses */
+ /**
+ This function retuns TRUE for passwords which satisfy the password
+ policy (as choosen by plugin variable) and FALSE for all other
+ password
+ */
+ int (*validate_password)(const char *password);
+ /**
+ This function returns the highest policy number which the password
+ satisfy.
+ */
+ int (*validate_password_strength)(const char *password);
+};
=== added file 'mysql-test/include/have_validate_password_plugin.inc'
--- a/mysql-test/include/have_validate_password_plugin.inc 1970-01-01 00:00:00 +0000
+++ b/mysql-test/include/have_validate_password_plugin.inc 2012-04-16 12:25:21 +0000
@@ -0,0 +1,20 @@
+#
+# Check if server has support for loading plugins
+#
+if (`SELECT @@have_dynamic_loading != 'YES'`) {
+ --skip validate password requires dynamic loading
+}
+
+#
+# Check if the variable VALIDATE_PASSWORD is set
+#
+if (!$VALIDATE_PASSWORD) {
+ --skip validate password requires the environment variable \$VALIDATE_PASSWORD to be set (normally done by mtr)
+}
+
+#
+# Check if --plugin-dir was setup for validate password
+#
+if (`SELECT CONCAT('--plugin-dir=', REPLACE(@@plugin_dir, '\\\\', '/')) != '$VALIDATE_PASSWORD_OPT/'`) {
+ --skip validate password requires that --plugin-dir is set to the validate password dir (either the .opt file does not contain \$VALIDATE_PASSWORD_OPT or another plugin is in use)
+}
=== modified file 'mysql-test/include/plugin.defs'
--- a/mysql-test/include/plugin.defs 2012-02-20 16:29:18 +0000
+++ b/mysql-test/include/plugin.defs 2012-04-16 12:25:21 +0000
@@ -44,3 +44,4 @@ adt_null plugin/audit_null AU
libdaemon_example plugin/daemon_example DAEMONEXAMPLE
libmemcached plugin/innodb_memcached/daemon_memcached DAEMON_MEMCACHED daemon_memcached
innodb_engine plugin/innodb_memcached/innodb_memcache INNODB_ENGINE
+validate_password plugin/password_validation VALIDATE_PASSWORD
=== added file 'mysql-test/r/validate_password_plugin.result'
--- a/mysql-test/r/validate_password_plugin.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/validate_password_plugin.result 2012-04-16 12:25:21 +0000
@@ -0,0 +1,90 @@
+INSTALL PLUGIN validate_password SONAME 'validate_password.so';
+INSTALL PLUGIN validate_password SONAME 'validate_password.so';
+ERROR HY000: Function 'validate_password' already exists
+CREATE USER 'base_user'@'localhost' IDENTIFIED BY 'password1A#';
+password policy low
+SET @@global.validate_password_policy_number=1;
+CREATE USER 'user'@'localhost' IDENTIFIED BY '';
+ERROR HY000: not a valid password ''
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('pass');
+ERROR HY000: not a valid password 'pass'
+UPDATE mysql.user SET PASSWORD= PASSWORD('password') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password123';
+password policy medium
+SET @@global.validate_password_policy_number=2;
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'pass';
+ERROR HY000: not a valid password 'pass'
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1');
+ERROR HY000: not a valid password 'password1'
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A#';
+password policy strong
+SET @@global.validate_password_policy_number=3;
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1';
+ERROR HY000: not a valid password 'password1'
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A');
+ERROR HY000: not a valid password 'password1A'
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A#'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A##';
+SET @@global.validate_password_policy_number= 1;
+SET @@global.validate_password_length= 12;
+SET PASSWORD FOR 'base_user'@'localhost'= password('password');
+ERROR HY000: not a valid password 'password'
+UPDATE mysql.user SET PASSWORD= password('password1A#') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A#'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234';
+SET @@global.validate_password_length= 8;
+SET @@global.validate_password_policy_number= 2;
+SET @@global.validate_password_numbers= 3;
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1A#';
+ERROR HY000: not a valid password 'password1A#'
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password123A#');
+SET @@global.validate_password_numbers= 4;
+UPDATE mysql.user SET PASSWORD= PASSWORD('password123A#') WHERE user='base_user';
+ERROR HY000: not a valid password 'password123A#'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234A#';
+SET @@global.validate_password_numbers= 1;
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1A';
+ERROR HY000: not a valid password 'password1A'
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+SET @@global.validate_password_special_chars= 3;
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#$') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A#$'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A#$!';
+SET @@global.validate_password_special_chars= 1;
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1';
+ERROR HY000: not a valid password 'password1'
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+SET @@global.validate_password_mixed_case= 2;
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A#'
+UPDATE mysql.user SET PASSWORD= PASSWORD('1234567AB#') WHERE user='base_user';
+ERROR HY000: not a valid password '1234567AB#'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1AB#';
+SET @@global.validate_password_mixed_case= 1;
+SET @@global.validate_password_policy_number= 3;
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A$');
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+ERROR HY000: not a valid password 'password1A#'
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1AB#';
+SELECT VALIDATE_PASSWORD_STRENGTH('password', 0);
+ERROR 42000: Incorrect parameter count in the call to native function 'VALIDATE_PASSWORD_STRENGTH'
+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 ''
+SELECT VALIDATE_PASSWORD_STRENGTH('pass');
+ERROR HY000: not a valid password 'pass'
+SELECT VALIDATE_PASSWORD_STRENGTH('password');
+VALIDATE_PASSWORD_STRENGTH('password')
+1
+SELECT VALIDATE_PASSWORD_STRENGTH('password1A#');
+VALIDATE_PASSWORD_STRENGTH('password1A#')
+2
+SELECT VALIDATE_PASSWORD_STRENGTH('password1A$');
+VALIDATE_PASSWORD_STRENGTH('password1A$')
+3
+DROP USER 'base_user'@'localhost';
+UNINSTALL PLUGIN validate_password;
=== added file 'mysql-test/t/validate_password_plugin-master.opt'
--- a/mysql-test/t/validate_password_plugin-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/validate_password_plugin-master.opt 2012-04-16 12:25:21 +0000
@@ -0,0 +1 @@
+$VALIDATE_PASSWORD_OPT
=== added file 'mysql-test/t/validate_password_plugin.test'
--- a/mysql-test/t/validate_password_plugin.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/t/validate_password_plugin.test 2012-04-16 12:25:21 +0000
@@ -0,0 +1,112 @@
+--source include/have_validate_password_plugin.inc
+
+INSTALL PLUGIN validate_password SONAME 'validate_password.so';
+--error ER_UDF_EXISTS
+INSTALL PLUGIN validate_password SONAME 'validate_password.so';
+
+CREATE USER 'base_user'@'localhost' IDENTIFIED BY 'password1A#';
+
+# test for all the three password policy
+
+--echo password policy low
+
+SET @@global.validate_password_policy_number=1;
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY '';
+--error ER_NOT_VALID_PASSWORD
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('pass');
+UPDATE mysql.user SET PASSWORD= PASSWORD('password') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password123';
+
+--echo password policy medium
+
+SET @@global.validate_password_policy_number=2;
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'pass';
+--error ER_NOT_VALID_PASSWORD
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1');
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A#';
+
+--echo password policy strong
+
+SET @@global.validate_password_policy_number=3;
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1';
+--error ER_NOT_VALID_PASSWORD
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A');
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A##';
+
+# test for password length option
+
+SET @@global.validate_password_policy_number= 1;
+SET @@global.validate_password_length= 12;
+--error ER_NOT_VALID_PASSWORD
+SET PASSWORD FOR 'base_user'@'localhost'= password('password');
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= password('password1A#') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234';
+SET @@global.validate_password_length= 8;
+
+# test for number of digits in a password
+
+SET @@global.validate_password_policy_number= 2;
+SET @@global.validate_password_numbers= 3;
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1A#';
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password123A#');
+SET @@global.validate_password_numbers= 4;
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password123A#') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1234A#';
+SET @@global.validate_password_numbers= 1;
+
+# test for number of special characters in password
+
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1A';
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+SET @@global.validate_password_special_chars= 3;
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#$') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1A#$!';
+SET @@global.validate_password_special_chars= 1;
+
+# test for number of uppercase and lowercase
+
+--error ER_NOT_VALID_PASSWORD
+CREATE USER 'user'@'localhost' IDENTIFIED BY 'password1';
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A#');
+SET @@global.validate_password_mixed_case= 2;
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('1234567AB#') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1AB#';
+SET @@global.validate_password_mixed_case= 1;
+
+# test for dictionary file
+
+SET @@global.validate_password_policy_number= 3;
+SET PASSWORD FOR 'base_user'@'localhost'= PASSWORD('password1A$');
+--error ER_NOT_VALID_PASSWORD
+UPDATE mysql.user SET PASSWORD= PASSWORD('password1A#') WHERE user='base_user';
+GRANT USAGE ON *.* TO 'base_user'@'localhost' IDENTIFIED BY 'password1AB#';
+
+--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('password1A$');
+
+DROP USER 'base_user'@'localhost';
+UNINSTALL PLUGIN validate_password;
=== added directory 'plugin/password_validation'
=== added file 'plugin/password_validation/CMakeLists.txt'
--- a/plugin/password_validation/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ b/plugin/password_validation/CMakeLists.txt 2012-04-16 12:25:21 +0000
@@ -0,0 +1,18 @@
+# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+#
+# 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; version 2 of the License.
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+MYSQL_ADD_PLUGIN(validate_password validate_password.cc
+ MODULE_ONLY)
+INSTALL(FILES dictionary.txt DESTINATION ${INSTALL_PLUGINDIR})
=== added file 'plugin/password_validation/dictionary.txt'
--- a/plugin/password_validation/dictionary.txt 1970-01-01 00:00:00 +0000
+++ b/plugin/password_validation/dictionary.txt 2012-04-16 12:25:21 +0000
@@ -0,0 +1,101 @@
+123456
+12345
+123456789
+password
+iloveyou
+princess
+1234567
+rockyou
+12345678
+abc123
+nicole
+daniel
+babygirl
+monkey
+lovely
+jessica
+654321
+michael
+ashley
+qwerty
+111111
+iloveu
+000000
+michelle
+tigger
+sunshine
+chocolate
+password1
+soccer
+anthony
+friends
+butterfly
+purple
+angel
+jordan
+liverpool
+justin
+loveme
+fuckyou
+123123
+football
+secret
+andrea
+carlos
+jennifer
+joshua
+bubbles
+1234567890
+superman
+hannah
+amanda
+loveyou
+pretty
+basketball
+andrew
+angels
+tweety
+flower
+playboy
+hello
+elizabeth
+hottie
+tinkerbell
+charlie
+samantha
+barbie
+chelsea
+lovers
+teamo
+jasmine
+brandon
+666666
+shadow
+melissa
+eminem
+matthew
+robert
+danielle
+forever
+family
+jonathan
+987654321
+computer
+whatever
+dragon
+vanessa
+cookie
+naruto
+summer
+sweety
+spongebob
+joseph
+junior
+softball
+taylor
+yellow
+daniela
+lauren
+mickey
+princesa
+password1A#
=== added file 'plugin/password_validation/validate_password.cc'
--- a/plugin/password_validation/validate_password.cc 1970-01-01 00:00:00 +0000
+++ b/plugin/password_validation/validate_password.cc 2012-04-16 12:25:21 +0000
@@ -0,0 +1,207 @@
+/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
+
+ 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; version 2 of the License.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include <string>
+#include <mysql/plugin_validate_password.h>
+#include <m_ctype.h>
+#include <map>
+#include <mysql/psi/mysql_file.h>
+#include <sql_plugin.h>
+
+#define PASSWORD_STRENGTH_REJECTED 0
+#define PASSWORD_STRENGTH_LOW 1
+#define PASSWORD_STRENGTH_MEDIUM 2
+#define PASSWORD_STRENGTH_STRONG 3
+
+typedef std::map<std::string, int> maptype;
+maptype dictionary_words;
+
+static uint validate_password_length;
+static uint validate_password_numbers;
+static uint validate_password_mixed_case;
+static uint validate_password_special_chars;
+static uint validate_password_policy_number;
+static char *validate_password_dictionary_file;
+
+static int validate_password_policy(const char *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)
+ {
+ if (policy == PASSWORD_STRENGTH_LOW)
+ return 1;
+ while (*c != '\0')
+ {
+ if (my_isdigit(&my_charset_latin1, *c))
+ has_numbers++;
+ else if (my_isupper(&my_charset_latin1, *c))
+ has_upper++;
+ else if (my_islower(&my_charset_latin1, *c))
+ has_lower++;
+ else
+ has_special_chars++;
+ c++;
+ }
+
+ if (has_upper >= validate_password_mixed_case && has_lower >=
+ validate_password_mixed_case && has_special_chars >=
+ validate_password_special_chars && has_numbers >=
+ validate_password_numbers)
+ {
+ if (policy == PASSWORD_STRENGTH_MEDIUM)
+ return 1;
+ std::string str= password;
+ maptype::iterator itr;
+ itr= dictionary_words.find(str);
+ if (itr == dictionary_words.end())
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int validate_password(const char *password)
+{
+ return validate_password_policy(password, validate_password_policy_number);
+}
+
+static int validate_password_strength(const char *password)
+{
+ if (validate_password_policy(password, PASSWORD_STRENGTH_LOW))
+ {
+ int policy= PASSWORD_STRENGTH_LOW;
+ if (validate_password_policy(password, PASSWORD_STRENGTH_MEDIUM))
+ {
+ policy= PASSWORD_STRENGTH_MEDIUM;
+ if (validate_password_policy(password, PASSWORD_STRENGTH_STRONG))
+ policy= PASSWORD_STRENGTH_STRONG;
+ }
+ return policy;
+ }
+ return PASSWORD_STRENGTH_REJECTED;
+}
+
+/*
+ Plugin type-specific descriptor
+*/
+
+static struct st_mysql_validate_password validate_password_descriptor=
+{
+ MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION, /* interface version */
+ validate_password, /* validate function */
+ validate_password_strength /* validate strength function */
+};
+
+static int validate_password_init(void *arg __attribute__((unused)))
+{
+ MYSQL_FILE *fp;
+ char buff[128]; /*maximum length of word stored in dictionary file */
+ uint count= 0;
+ char *dictionary_file;
+ char default_dictionary_file[FN_REFLEN];
+ fn_format(default_dictionary_file, "dictionary.txt", opt_plugin_dir,
+ "", MYF(0));
+ dictionary_file= (validate_password_dictionary_file ?
+ validate_password_dictionary_file :
+ default_dictionary_file);
+
+ if (!(fp= mysql_file_fopen
+ (0, dictionary_file, O_RDONLY, MYF(0))))
+ return 1;
+ while (mysql_file_fgets(buff, sizeof(buff) - 1, fp))
+ {
+ buff[strlen(buff) - 1]= '\0';
+ std::string str= (const char *) buff;
+ dictionary_words.insert(std::pair<std::string, int>(str, count));
+ count++;
+ }
+ if (mysql_file_fclose(fp, MYF(0)))
+ return 1;
+ return 0;
+}
+
+static int validate_password_deinit(void *arg __attribute__((unused)))
+{
+ if (!dictionary_words.empty())
+ dictionary_words.clear();
+ return 0;
+}
+
+/*
+ Plugin system variables.
+*/
+
+static MYSQL_SYSVAR_UINT(length, validate_password_length,
+ PLUGIN_VAR_RQCMDARG,
+ "Password_validate_length to check for minimum password_length",
+ NULL, NULL, 8, 0, 0, 0);
+
+static MYSQL_SYSVAR_UINT(numbers, validate_password_numbers,
+ PLUGIN_VAR_RQCMDARG,
+ "password_validate_numbers to ensure minimum numeric character in password",
+ NULL, NULL, 1, 0, 0, 0);
+
+static MYSQL_SYSVAR_UINT(mixed_case, validate_password_mixed_case,
+ PLUGIN_VAR_RQCMDARG,
+ "Password_validate_mixed_case to ensure minimum upper/lower case in password",
+ NULL, NULL, 1, 0, 0, 0);
+
+static MYSQL_SYSVAR_UINT(special_chars, validate_password_special_chars,
+ PLUGIN_VAR_RQCMDARG,
+ "password_validate_special to ensure minimum special character in password",
+ NULL, NULL, 1, 0, 0, 0);
+
+static MYSQL_SYSVAR_UINT(policy_number, validate_password_policy_number,
+ PLUGIN_VAR_RQCMDARG,
+ "password_validate_policy choosen policy to validate password",
+ NULL, NULL, 2, 1, 3, 0);
+
+static MYSQL_SYSVAR_STR(dictionary_file, validate_password_dictionary_file,
+ PLUGIN_VAR_READONLY,
+ "password_validate_dictionary file to be loaded and check for password",
+ NULL, NULL, NULL);
+
+static struct st_mysql_sys_var* validate_password_system_variables[]= {
+ MYSQL_SYSVAR(length),
+ MYSQL_SYSVAR(numbers),
+ MYSQL_SYSVAR(mixed_case),
+ MYSQL_SYSVAR(special_chars),
+ MYSQL_SYSVAR(policy_number),
+ MYSQL_SYSVAR(dictionary_file),
+ NULL
+};
+
+mysql_declare_plugin(validate_password)
+{
+ MYSQL_VALIDATE_PASSWORD_PLUGIN, /* type */
+ &validate_password_descriptor, /* descriptor */
+ "validate_password", /* name */
+ "Oracle Corporation", /* author */
+ "check password strength", /* description */
+ PLUGIN_LICENSE_GPL,
+ validate_password_init, /* init function (when loaded) */
+ validate_password_deinit, /* deinit function (when unloaded) */
+ 0x0100, /* version */
+ NULL,
+ validate_password_system_variables, /* system variables */
+ NULL,
+ 0,
+}
+mysql_declare_plugin_end;
=== modified file 'sql/item_create.cc'
--- a/sql/item_create.cc 2012-03-30 15:38:01 +0000
+++ b/sql/item_create.cc 2012-04-16 12:25:21 +0000
@@ -2481,6 +2481,19 @@ protected:
};
+class Create_func_validate_password_strength : public Create_func_arg1
+{
+public:
+ virtual Item *create(THD *thd, Item *arg1);
+
+ static Create_func_validate_password_strength s_singleton;
+
+protected:
+ Create_func_validate_password_strength() {}
+ virtual ~Create_func_validate_password_strength() {}
+};
+
+
class Create_func_version : public Create_func_arg0
{
public:
@@ -5180,6 +5193,16 @@ Create_func_uuid_short::create(THD *thd)
}
+Create_func_validate_password_strength
+ Create_func_validate_password_strength::s_singleton;
+
+Item*
+Create_func_validate_password_strength::create(THD *thd, Item *arg1)
+{
+ return new (thd->mem_root) Item_func_validate_password_strength(arg1);
+}
+
+
Create_func_version Create_func_version::s_singleton;
Item*
@@ -5607,6 +5630,7 @@ static Native_func_registry func_array[]
{ { C_STRING_WITH_LEN("UPPER") }, BUILDER(Create_func_ucase)},
{ { C_STRING_WITH_LEN("UUID") }, BUILDER(Create_func_uuid)},
{ { C_STRING_WITH_LEN("UUID_SHORT") }, BUILDER(Create_func_uuid_short)},
+ { { C_STRING_WITH_LEN("VALIDATE_PASSWORD_STRENGTH") }, BUILDER(Create_func_validate_password_strength)},
{ { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
{ { C_STRING_WITH_LEN("WEEKDAY") }, BUILDER(Create_func_weekday)},
{ { C_STRING_WITH_LEN("WEEKOFYEAR") }, BUILDER(Create_func_weekofyear)},
=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc 2012-03-30 15:38:01 +0000
+++ b/sql/item_func.cc 2012-04-16 12:25:21 +0000
@@ -3227,6 +3227,17 @@ void Item_func_locate::print(String *str
}
+longlong Item_func_validate_password_strength::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ String *field;
+ if (!(field= args[0]->val_str(&value)))
+ return 0;
+ const char * password= field->ptr();
+ return (check_password_strength(password));
+}
+
+
longlong Item_func_field::val_int()
{
DBUG_ASSERT(fixed == 1);
=== modified file 'sql/item_func.h'
--- a/sql/item_func.h 2012-03-30 15:38:01 +0000
+++ b/sql/item_func.h 2012-04-16 12:25:21 +0000
@@ -1090,6 +1090,17 @@ public:
};
+class Item_func_validate_password_strength :public Item_int_func
+{
+ String value;
+public:
+ Item_func_validate_password_strength(Item *a) :Item_int_func(a) {}
+ longlong val_int();
+ const char *func_name() const { return "validate_password_strength"; }
+ void fix_length_and_dec() { max_length=10; }
+};
+
+
class Item_func_field :public Item_int_func
{
String value,tmp;
=== modified file 'sql/item_strfunc.cc'
--- a/sql/item_strfunc.cc 2012-03-19 17:59:14 +0000
+++ b/sql/item_strfunc.cc 2012-04-16 12:25:21 +0000
@@ -1927,6 +1927,7 @@ String *Item_func_password::val_str_asci
return 0;
if (res->length() == 0)
return make_empty_result();
+ check_password_validation(res->ptr());
my_make_scrambled_password(tmp_value, res->ptr(), res->length());
str->set(tmp_value, SCRAMBLED_PASSWORD_CHAR_LENGTH, &my_charset_latin1);
return str;
=== modified file 'sql/share/errmsg-utf8.txt'
--- a/sql/share/errmsg-utf8.txt 2012-04-11 16:52:28 +0000
+++ b/sql/share/errmsg-utf8.txt 2012-04-16 12:25:21 +0000
@@ -6745,6 +6745,8 @@ ER_UNKNOWN_ALTER_ALGORITHM
eng "Unknown ALGORITHM '%s'"
ER_UNKNOWN_ALTER_LOCK
eng "Unknown LOCK type '%s'"
+ER_NOT_VALID_PASSWORD
+ eng "not a valid password '%s'"
ER_MTS_CHANGE_MASTER_CANT_RUN_WITH_GAPS
eng "CHANGE MASTER cannot be executed when the slave was stopped with an error or killed in MTS mode. Consider using RESET SLAVE or START SLAVE UNTIL."
=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc 2012-03-30 15:38:01 +0000
+++ b/sql/sql_acl.cc 2012-04-16 12:25:21 +0000
@@ -49,6 +49,7 @@
#include "sql_connect.h"
#include "hostname.h"
#include "sql_db.h"
+#include <mysql/plugin_validate_password.h>
using std::min;
using std::max;
@@ -179,7 +180,11 @@ static LEX_STRING native_password_plugin
static LEX_STRING old_password_plugin_name= {
C_STRING_WITH_LEN("mysql_old_password")
};
-
+
+static LEX_STRING validate_password_plugin_name= {
+ C_STRING_WITH_LEN("validate_password")
+};
+
/// @todo make it configurable
LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
@@ -9905,3 +9910,43 @@ mysql_declare_plugin(mysql_password)
}
mysql_declare_plugin_end;
+/*********************************************************************
+ PASSWORD_VALIDATION_CODE, invoking appropriate plugin to validate
+ the password strength.
+**********************************************************************/
+
+/* for validate_password_strength SQL function */
+
+int check_password_strength(const char *password)
+{
+ int res= 0;
+ plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
+ MYSQL_VALIDATE_PASSWORD_PLUGIN);
+ if (plugin)
+ {
+ 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);
+ plugin_unlock(0, plugin);
+ }
+ return(res);
+}
+
+/* called when new user is created or exsisting password is changed */
+
+void check_password_validation(const char *password)
+{
+ plugin_ref plugin= my_plugin_lock_by_name(0, &validate_password_plugin_name,
+ MYSQL_VALIDATE_PASSWORD_PLUGIN);
+ if (plugin)
+ {
+ st_mysql_validate_password *password_validate=
+ (st_mysql_validate_password *) plugin_decl(plugin)->info;
+
+ if (!password_validate->validate_password(password))
+ my_error(ER_NOT_VALID_PASSWORD, MYF(0), password);
+ plugin_unlock(0, plugin);
+ }
+}
=== modified file 'sql/sql_acl.h'
--- a/sql/sql_acl.h 2012-03-06 14:29:42 +0000
+++ b/sql/sql_acl.h 2012-04-16 12:25:21 +0000
@@ -251,6 +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_validation(const char *password);
#ifdef NO_EMBEDDED_ACCESS_CHECKS
#define check_grant(A,B,C,D,E,F) 0
=== modified file 'sql/sql_plugin.cc'
--- a/sql/sql_plugin.cc 2012-03-31 18:30:05 +0000
+++ b/sql/sql_plugin.cc 2012-04-16 12:25:21 +0000
@@ -33,6 +33,7 @@
#include "sql_audit.h"
#include <mysql/plugin_auth.h>
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
+#include <mysql/plugin_validate_password.h>
#include <algorithm>
@@ -72,7 +73,8 @@ const LEX_STRING plugin_type_names[MYSQL
{ C_STRING_WITH_LEN("INFORMATION SCHEMA") },
{ C_STRING_WITH_LEN("AUDIT") },
{ C_STRING_WITH_LEN("REPLICATION") },
- { C_STRING_WITH_LEN("AUTHENTICATION") }
+ { C_STRING_WITH_LEN("AUTHENTICATION") },
+ { C_STRING_WITH_LEN("VALIDATE PASSWORD") }
};
extern int initialize_schema_table(st_plugin_int *plugin);
@@ -89,13 +91,13 @@ extern int finalize_audit_plugin(st_plug
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
0,ha_initialize_handlerton,0,0,initialize_schema_table,
- initialize_audit_plugin,0,0
+ initialize_audit_plugin,0,0,0
};
plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
0,ha_finalize_handlerton,0,0,finalize_schema_table,
- finalize_audit_plugin,0,0
+ finalize_audit_plugin,0,0,0
};
#ifdef HAVE_DLOPEN
@@ -121,7 +123,8 @@ static int min_plugin_info_interface_ver
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
MYSQL_AUDIT_INTERFACE_VERSION,
MYSQL_REPLICATION_INTERFACE_VERSION,
- MYSQL_AUTHENTICATION_INTERFACE_VERSION
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -132,7 +135,8 @@ static int cur_plugin_info_interface_ver
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
MYSQL_AUDIT_INTERFACE_VERSION,
MYSQL_REPLICATION_INTERFACE_VERSION,
- MYSQL_AUTHENTICATION_INTERFACE_VERSION
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ MYSQL_VALIDATE_PASSWORD_INTERFACE_VERSION
};
/* support for Services */
=== modified file 'sql/sql_yacc.yy'
--- a/sql/sql_yacc.yy 2012-04-12 08:24:17 +0000
+++ b/sql/sql_yacc.yy 2012-04-16 12:25:21 +0000
@@ -14199,6 +14199,7 @@ text_or_password:
TEXT_STRING { $$=$1.str;}
| PASSWORD '(' TEXT_STRING ')'
{
+ check_password_validation($3.str);
$$= $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) :
@@ -14707,6 +14708,7 @@ grant_user:
$$=$1; $1->password=$4;
if (Lex->sql_command == SQLCOM_REVOKE)
MYSQL_YYABORT;
+ check_password_validation($4.str);
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:3870 to 3871) WL#2739 | Ashish Agarwal | 20 Apr |