List:Commits« Previous MessageNext Message »
From:Ashish Agarwal Date:April 16 2012 12:27pm
Subject:bzr push into mysql-trunk branch (ashish.y.agarwal:3870 to 3871) WL#2739
View as plain text  
 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#2739Ashish Agarwal20 Apr