summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--client/CMakeLists.txt2
-rw-r--r--client/client_priv.h2
-rw-r--r--client/mysql.cc18
-rw-r--r--client/mysqltest.cc26
-rw-r--r--config.h.cmake2
-rw-r--r--configure.in3
-rw-r--r--include/Makefile.am7
-rw-r--r--include/errmsg.h3
-rw-r--r--include/my_global.h12
-rw-r--r--include/my_no_pthread.h1
-rw-r--r--include/my_sys.h6
-rw-r--r--include/mysql.h54
-rw-r--r--include/mysql.h.pp37
-rw-r--r--include/mysql/client_plugin.h146
-rw-r--r--include/mysql/client_plugin.h.pp41
-rw-r--r--include/mysql/plugin.h3
-rw-r--r--include/mysql/plugin_auth.h118
-rw-r--r--include/mysql/plugin_auth.h.pp205
-rw-r--r--include/mysql/plugin_auth_common.h105
-rw-r--r--include/mysql_com.h15
-rw-r--r--include/sql_common.h63
-rw-r--r--libmysql/CMakeLists.txt3
-rw-r--r--libmysql/Makefile.shared14
-rw-r--r--libmysql/client_settings.h5
-rw-r--r--libmysql/errmsg.c3
-rw-r--r--libmysql/libmysql.c101
-rw-r--r--libmysqld/CMakeLists.txt1
-rw-r--r--libmysqld/Makefile.am2
-rw-r--r--libmysqld/embedded_priv.h2
-rw-r--r--libmysqld/lib_sql.cc80
-rw-r--r--libmysqld/libmysqld.c8
-rw-r--r--mysql-test/include/have_plugin_auth.inc4
-rwxr-xr-xmysql-test/mysql-test-run.pl41
-rw-r--r--mysql-test/r/1st.result1
-rw-r--r--mysql-test/r/change_user.result36
-rw-r--r--mysql-test/r/connect.result3
-rw-r--r--mysql-test/r/events_bugs.result1
-rw-r--r--mysql-test/r/grant.result136
-rw-r--r--mysql-test/r/grant2.result6
-rw-r--r--mysql-test/r/grant_cache_no_prot.result2
-rw-r--r--mysql-test/r/grant_cache_ps_prot.result2
-rw-r--r--mysql-test/r/information_schema.result4
-rw-r--r--mysql-test/r/log_tables_upgrade.result1
-rw-r--r--mysql-test/r/mysql_upgrade.result6
-rw-r--r--mysql-test/r/mysqlcheck.result2
-rw-r--r--mysql-test/r/plugin_auth.result212
-rw-r--r--mysql-test/r/ps.result6
-rw-r--r--mysql-test/r/sp_notembedded.result6
-rw-r--r--mysql-test/r/system_mysql_db.result3
-rw-r--r--mysql-test/suite/federated/federated.result2
-rw-r--r--mysql-test/suite/funcs_1/r/innodb_trig_03e.result1
-rw-r--r--mysql-test/suite/funcs_1/r/is_columns_mysql.result14
-rw-r--r--mysql-test/suite/funcs_1/r/is_key_column_usage.result4
-rw-r--r--mysql-test/suite/funcs_1/r/is_statistics.result4
-rw-r--r--mysql-test/suite/funcs_1/r/is_statistics_mysql.result4
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_constraints.result1
-rw-r--r--mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result1
-rw-r--r--mysql-test/suite/funcs_1/r/is_tables_mysql.result23
-rw-r--r--mysql-test/suite/funcs_1/r/is_user_privileges.result1655
-rw-r--r--mysql-test/suite/funcs_1/r/memory_trig_03e.result1
-rw-r--r--mysql-test/suite/funcs_1/r/myisam_trig_03e.result1
-rw-r--r--mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result1
-rw-r--r--mysql-test/suite/funcs_1/r/processlist_priv_ps.result1
-rw-r--r--mysql-test/suite/funcs_1/t/is_user_privileges.test40
-rw-r--r--mysql-test/suite/perfschema/r/column_privilege.result1
-rw-r--r--mysql-test/suite/perfschema/r/pfs_upgrade.result10
-rw-r--r--mysql-test/suite/perfschema/r/privilege.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_do_grant.result4
-rw-r--r--mysql-test/suite/rpl/r/rpl_ignore_table.result1
-rw-r--r--mysql-test/suite/rpl/r/rpl_stm_000001.result1
-rw-r--r--mysql-test/suite/sys_vars/r/external_user_basic.result3
-rw-r--r--mysql-test/suite/sys_vars/r/proxy_user_basic.result3
-rw-r--r--mysql-test/suite/sys_vars/t/external_user_basic.test1
-rw-r--r--mysql-test/suite/sys_vars/t/proxy_user_basic.test1
-rw-r--r--mysql-test/t/change_user.test48
-rw-r--r--mysql-test/t/grant.test6
-rw-r--r--mysql-test/t/grant2.test4
-rw-r--r--mysql-test/t/plugin_auth-master.opt2
-rw-r--r--mysql-test/t/plugin_auth.test298
-rw-r--r--mysql-test/t/system_mysql_db_fix40123.test2
-rw-r--r--mysql-test/t/system_mysql_db_fix50030.test2
-rw-r--r--mysql-test/t/system_mysql_db_fix50117.test2
-rw-r--r--plugin/auth/CMakeLists.txt32
-rw-r--r--plugin/auth/Makefile.am16
-rw-r--r--plugin/auth/auth_socket.c93
-rw-r--r--plugin/auth/dialog.c325
-rw-r--r--plugin/auth/plug.in12
-rw-r--r--plugin/auth/test_plugin.c200
-rw-r--r--scripts/mysql_system_tables.sql6
-rw-r--r--scripts/mysql_system_tables_data.sql14
-rw-r--r--scripts/mysql_system_tables_fix.sql5
-rw-r--r--sql-common/Makefile.am2
-rw-r--r--sql-common/client.c1115
-rw-r--r--sql-common/client_plugin.c453
-rw-r--r--sql/CMakeLists.txt1
-rw-r--r--sql/Makefile.am12
-rw-r--r--sql/client_settings.h4
-rw-r--r--sql/ha_ndbcluster.cc2
-rw-r--r--sql/ha_ndbcluster_binlog.cc2
-rw-r--r--sql/lex.h5
-rw-r--r--sql/mysqld.cc3
-rw-r--r--sql/password.c12
-rw-r--r--sql/protocol.cc18
-rw-r--r--sql/protocol.h1
-rw-r--r--sql/set_var.cc6
-rw-r--r--sql/share/errmsg-utf8.txt29
-rw-r--r--sql/sp_head.h1
-rw-r--r--sql/sql_acl.cc2848
-rw-r--r--sql/sql_acl.h58
-rw-r--r--sql/sql_audit.h2
-rw-r--r--sql/sql_builtin.cc.in4
-rw-r--r--sql/sql_class.cc56
-rw-r--r--sql/sql_class.h14
-rw-r--r--sql/sql_connect.cc459
-rw-r--r--sql/sql_connect.h4
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h3
-rw-r--r--sql/sql_parse.cc177
-rw-r--r--sql/sql_plugin.cc12
-rw-r--r--sql/sql_yacc.yy54
-rw-r--r--sql/structs.h2
-rw-r--r--sql/sys_vars.cc8
-rw-r--r--sql/sys_vars.h61
-rw-r--r--sql/table.cc20
-rw-r--r--tests/mysql_client_test.c1
127 files changed, 8248 insertions, 1616 deletions
diff --git a/Makefile.am b/Makefile.am
index 7ab9a9aa061..ef815238998 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -267,7 +267,9 @@ test-full-qa:
API_PREPROCESSOR_HEADER = $(top_srcdir)/include/mysql/plugin.h \
$(top_srcdir)/include/mysql.h \
$(top_srcdir)/include/mysql/psi/psi_abi_v1.h \
- $(top_srcdir)/include/mysql/psi/psi_abi_v2.h
+ $(top_srcdir)/include/mysql/psi/psi_abi_v2.h \
+ $(top_srcdir)/include/mysql/client_plugin.h \
+ $(top_srcdir)/include/mysql/plugin_auth.h
#
# Rules for checking that the abi/api has not changed.
diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt
index d0bb8d43edd..5be39396edf 100644
--- a/client/CMakeLists.txt
+++ b/client/CMakeLists.txt
@@ -69,3 +69,5 @@ ADD_EXECUTABLE(echo echo.c)
SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap
PROPERTIES HAS_CXX TRUE)
+ADD_DEFINITIONS(-DHAVE_DLOPEN)
+
diff --git a/client/client_priv.h b/client/client_priv.h
index e7911fc31f7..1a81768adc4 100644
--- a/client/client_priv.h
+++ b/client/client_priv.h
@@ -84,6 +84,8 @@ enum options_client
OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE,
OPT_WRITE_BINLOG, OPT_DUMP_DATE,
OPT_INIT_COMMAND,
+ OPT_PLUGIN_DIR,
+ OPT_DEFAULT_PLUGIN,
OPT_MAX_CLIENT_OPTION
};
diff --git a/client/mysql.cc b/client/mysql.cc
index 01a786c13af..a985c135361 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -169,6 +169,7 @@ static int wait_time = 5;
static STATUS status;
static ulong select_limit,max_join_size,opt_connect_timeout=0;
static char mysql_charsets_dir[FN_REFLEN+1];
+static char *opt_plugin_dir= 0, *opt_default_auth;
static const char *xmlmeta[] = {
"&", "&",
"<", "&lt;",
@@ -1567,6 +1568,13 @@ static struct my_option my_long_options[] =
{"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.",
&show_warnings, &show_warnings, 0, GET_BOOL, NO_ARG,
0, 0, 0, 0, 0, 0},
+ {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+ (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default_auth", OPT_PLUGIN_DIR,
+ "Default authentication client-side plugin to use.",
+ (uchar**) &opt_default_auth, (uchar**) &opt_default_auth, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
@@ -4298,9 +4306,15 @@ sql_real_connect(char *host,char *database,char *user,char *password,
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ if (opt_default_auth && *opt_default_auth)
+ mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth);
+
if (!mysql_real_connect(&mysql, host, user, password,
- database, opt_mysql_port, opt_mysql_unix_port,
- connect_flag | CLIENT_MULTI_STATEMENTS))
+ database, opt_mysql_port, opt_mysql_unix_port,
+ connect_flag | CLIENT_MULTI_STATEMENTS))
{
if (!silent ||
(mysql_errno(&mysql) != CR_CONN_HOST_ERROR &&
diff --git a/client/mysqltest.cc b/client/mysqltest.cc
index 20f0f6d3164..fabe0e1ea3e 100644
--- a/client/mysqltest.cc
+++ b/client/mysqltest.cc
@@ -36,6 +36,7 @@
#include "client_priv.h"
#include <mysql_version.h>
#include <mysqld_error.h>
+#include <sql_common.h>
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>
@@ -190,6 +191,8 @@ static ulonglong timer_now(void);
static ulong connection_retry_sleep= 100000; /* Microseconds */
+static char *opt_plugin_dir= 0;
+
/* Precompiled re's */
static my_regex_t ps_re; /* the query can be run using PS protocol */
static my_regex_t sp_re; /* the query can be run as a SP */
@@ -3728,13 +3731,15 @@ void do_change_user(struct st_command *command)
}
if (!ds_user.length)
+ {
dynstr_set(&ds_user, mysql->user);
- if (!ds_passwd.length)
- dynstr_set(&ds_passwd, mysql->passwd);
+ if (!ds_passwd.length)
+ dynstr_set(&ds_passwd, mysql->passwd);
- if (!ds_db.length)
- dynstr_set(&ds_db, mysql->db);
+ if (!ds_db.length)
+ dynstr_set(&ds_db, mysql->db);
+ }
DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'",
cur_con->name, ds_user.str, ds_passwd.str, ds_db.str));
@@ -5064,6 +5069,7 @@ void do_connect(struct st_command *command)
static DYNAMIC_STRING ds_port;
static DYNAMIC_STRING ds_sock;
static DYNAMIC_STRING ds_options;
+ static DYNAMIC_STRING ds_default_auth;
#ifdef HAVE_SMEM
static DYNAMIC_STRING ds_shm;
#endif
@@ -5075,7 +5081,8 @@ void do_connect(struct st_command *command)
{ "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" },
{ "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" },
{ "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" },
- { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }
+ { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" },
+ { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" }
};
DBUG_ENTER("do_connect");
@@ -5222,6 +5229,12 @@ void do_connect(struct st_command *command)
if (ds_database.length == 0)
dynstr_set(&ds_database, opt_db);
+ if (opt_plugin_dir && *opt_plugin_dir)
+ mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir);
+
+ if (ds_default_auth.length)
+ mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str);
+
/* Special database to allow one to connect without a database name */
if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*"))
dynstr_set(&ds_database, "");
@@ -6000,6 +6013,9 @@ static struct my_option my_long_options[] =
"Number of seconds before connection timeout.",
&opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG,
120, 0, 3600 * 12, 0, 0, 0},
+ {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.",
+ (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0,
+ GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};
diff --git a/config.h.cmake b/config.h.cmake
index c484edb65a5..40ea1fd4dff 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -603,6 +603,8 @@
#cmakedefine PLUGINDIR "@PLUGINDIR@"
#cmakedefine DEFAULT_SYSCONFDIR "@DEFAULT_SYSCONFDIR@"
+#cmakedefine SO_EXT "@CMAKE_SHARED_MODULE_SUFFIX@"
+
#define PACKAGE "mysql"
#define PACKAGE_BUGREPORT ""
#define PACKAGE_NAME "MySQL Server"
diff --git a/configure.in b/configure.in
index 7121a521f87..80c436940a8 100644
--- a/configure.in
+++ b/configure.in
@@ -1657,9 +1657,8 @@ case "$with_mysqld_ldflags " in
;;
*)
- # Check for dlopen, needed for user definable functions
+ # Check for dlopen, needed for user definable functions and plugins
# This must be checked after threads on AIX
- # We only need this for mysqld, not for the clients.
my_save_LIBS="$LIBS"
LIBS=""
diff --git a/include/Makefile.am b/include/Makefile.am
index e30588de065..9dbd818d592 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -24,7 +24,9 @@ HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \
my_list.h my_alloc.h typelib.h mysql/plugin.h \
mysql/plugin_audit.h mysql/plugin_ftparser.h
pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \
- my_xml.h mysql_embed.h mysql/services.h \
+ my_xml.h mysql_embed.h mysql/plugin_auth.h \
+ mysql/client_plugin.h mysql/plugin_auth_common.h \
+ mysql/services.h \
mysql/service_my_snprintf.h mysql/service_thd_alloc.h \
my_pthread.h my_no_pthread.h \
decimal.h errmsg.h my_global.h my_net.h \
@@ -53,7 +55,8 @@ pkgpsiinclude_HEADERS = mysql/psi/psi.h mysql/psi/mysql_thread.h \
EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base \
CMakeLists.txt \
mysql/psi/psi_abi_v1.h.pp \
- mysql/psi/psi_abi_v2.h.pp
+ mysql/psi/psi_abi_v2.h.pp \
+ mysql/plugin_auth.h.pp mysql/client_plugin.h.pp
# Remove built files and the symlinked directories
CLEANFILES = $(BUILT_SOURCES) readline openssl probes_mysql.d probes_mysql_nodtrace.h
diff --git a/include/errmsg.h b/include/errmsg.h
index c55c94af169..f1d7dd65f97 100644
--- a/include/errmsg.h
+++ b/include/errmsg.h
@@ -101,7 +101,8 @@ extern const char *client_errors[]; /* Error messages */
#define CR_STMT_CLOSED 2056
#define CR_NEW_STMT_METADATA 2057
#define CR_ALREADY_CONNECTED 2058
-#define CR_ERROR_LAST /*Copy last error nr:*/ 2058
+#define CR_AUTH_PLUGIN_CANNOT_LOAD 2059
+#define CR_ERROR_LAST /*Copy last error nr:*/ 2059
/* Add error numbers before CR_ERROR_LAST and change it accordingly. */
#endif /* ERRMSG_INCLUDED */
diff --git a/include/my_global.h b/include/my_global.h
index 1c615cc5ca2..378afe79904 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -452,6 +452,16 @@ extern "C" int madvise(void *addr, size_t len, int behav);
#define LINT_INIT(var)
#endif
+#ifndef SO_EXT
+#ifdef _WIN32
+#define SO_EXT ".dll"
+#elif defined(__APPLE__)
+#define SO_EXT ".dylib"
+#else
+#define SO_EXT ".so"
+#endif
+#endif
+
/*
Suppress uninitialized variable warning without generating code.
@@ -1365,7 +1375,7 @@ do { doubleget_union _tmp; \
#endif
#ifndef HAVE_DLERROR
-#define dlerror() ""
+#define dlerror() "No support for dynamic loading (static build?)"
#endif
diff --git a/include/my_no_pthread.h b/include/my_no_pthread.h
index a805a9151f8..633a5b94a6c 100644
--- a/include/my_no_pthread.h
+++ b/include/my_no_pthread.h
@@ -47,6 +47,7 @@
#define rw_wrlock(A)
#define rw_unlock(A)
#define rwlock_destroy(A)
+#define safe_mutex_assert_owner(mp)
#define mysql_mutex_init(A, B, C) do {} while (0)
#define mysql_mutex_lock(A) do {} while (0)
diff --git a/include/my_sys.h b/include/my_sys.h
index 95689535be5..23c9b2da55f 100644
--- a/include/my_sys.h
+++ b/include/my_sys.h
@@ -197,7 +197,7 @@ extern void my_large_free(uchar *ptr);
#define my_alloca(SZ) alloca((size_t) (SZ))
#define my_afree(PTR) {}
#else
-#define my_alloca(SZ) my_malloc(SZ,MYF(0))
+#define my_alloca(SZ) my_malloc(SZ,MYF(MY_FAE))
#define my_afree(PTR) my_free(PTR)
#endif /* HAVE_ALLOCA */
@@ -824,6 +824,10 @@ extern void set_prealloc_root(MEM_ROOT *root, char *ptr);
extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
size_t prealloc_size);
extern char *strdup_root(MEM_ROOT *root,const char *str);
+static inline char *safe_strdup_root(MEM_ROOT *root, const char *str)
+{
+ return str ? strdup_root(root, str) : 0;
+}
extern char *strmake_root(MEM_ROOT *root,const char *str,size_t len);
extern void *memdup_root(MEM_ROOT *root,const void *str, size_t len);
extern int get_defaults_options(int argc, char **argv,
diff --git a/include/mysql.h b/include/mysql.h
index dc6e4eb19a6..7d949597702 100644
--- a/include/mysql.h
+++ b/include/mysql.h
@@ -167,9 +167,15 @@ enum mysql_option
MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
- MYSQL_OPT_SSL_VERIFY_SERVER_CERT
+ MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH
};
+/**
+ @todo remove the "extension", move st_mysql_options completely
+ out of mysql.h
+*/
+struct st_mysql_options_extention;
+
struct st_mysql_options {
unsigned int connect_timeout, read_timeout, write_timeout;
unsigned int port, protocol;
@@ -203,7 +209,7 @@ struct st_mysql_options {
void (*local_infile_end)(void *);
int (*local_infile_error)(void *, char *, unsigned int);
void *local_infile_userdata;
- void *extension;
+ struct st_mysql_options_extention *extension;
};
enum mysql_status
@@ -638,38 +644,6 @@ enum enum_stmt_attr_type
};
-typedef struct st_mysql_methods
-{
- my_bool (*read_query_result)(MYSQL *mysql);
- my_bool (*advanced_command)(MYSQL *mysql,
- enum enum_server_command command,
- const unsigned char *header,
- unsigned long header_length,
- const unsigned char *arg,
- unsigned long arg_length,
- my_bool skip_check,
- MYSQL_STMT *stmt);
- MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
- unsigned int fields);
- MYSQL_RES * (*use_result)(MYSQL *mysql);
- void (*fetch_lengths)(unsigned long *to,
- MYSQL_ROW column, unsigned int field_count);
- void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
-#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
- MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
- my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
- int (*stmt_execute)(MYSQL_STMT *stmt);
- int (*read_binary_rows)(MYSQL_STMT *stmt);
- int (*unbuffered_fetch)(MYSQL *mysql, char **row);
- void (*free_embedded_thd)(MYSQL *mysql);
- const char *(*read_statistics)(MYSQL *mysql);
- my_bool (*next_result)(MYSQL *mysql);
- int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
- int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
-#endif
-} MYSQL_METHODS;
-
-
MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql);
int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
unsigned long length);
@@ -732,18 +706,6 @@ int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
#endif
#define HAVE_MYSQL_REAL_CONNECT
-/*
- The following functions are mainly exported because of mysqlbinlog;
- They are not for general usage
-*/
-
-#define simple_command(mysql, command, arg, length, skip_check) \
- (*(mysql)->methods->advanced_command)(mysql, command, 0, \
- 0, arg, length, skip_check, NULL)
-#define stmt_command(mysql, command, arg, length, stmt) \
- (*(mysql)->methods->advanced_command)(mysql, command, 0, \
- 0, arg, length, 1, stmt)
-
#ifdef __cplusplus
}
#endif
diff --git a/include/mysql.h.pp b/include/mysql.h.pp
index 531062aee80..7200a04d304 100644
--- a/include/mysql.h.pp
+++ b/include/mysql.h.pp
@@ -130,13 +130,13 @@ void create_random_string(char *to, unsigned int length, struct rand_struct *ran
void hash_password(unsigned long *to, const char *password, unsigned int password_len);
void make_scrambled_password_323(char *to, const char *password);
void scramble_323(char *to, const char *message, const char *password);
-my_bool check_scramble_323(const char *, const char *message,
+my_bool check_scramble_323(const unsigned char *reply, const char *message,
unsigned long *salt);
void get_salt_from_password_323(unsigned long *res, const char *password);
void make_password_from_salt_323(char *to, const unsigned long *salt);
void make_scrambled_password(char *to, const char *password);
void scramble(char *to, const char *message, const char *password);
-my_bool check_scramble(const char *reply, const char *message,
+my_bool check_scramble(const unsigned char *reply, const char *message,
const unsigned char *hash_stage2);
void get_salt_from_password(unsigned char *res, const char *password);
void make_password_from_salt(char *to, const unsigned char *hash_stage2);
@@ -262,8 +262,9 @@ enum mysql_option
MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION,
MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH,
MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT,
- MYSQL_OPT_SSL_VERIFY_SERVER_CERT
+ MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH
};
+struct st_mysql_options_extention;
struct st_mysql_options {
unsigned int connect_timeout, read_timeout, write_timeout;
unsigned int port, protocol;
@@ -293,7 +294,7 @@ struct st_mysql_options {
void (*local_infile_end)(void *);
int (*local_infile_error)(void *, char *, unsigned int);
void *local_infile_userdata;
- void *extension;
+ struct st_mysql_options_extention *extension;
};
enum mysql_status
{
@@ -547,34 +548,6 @@ enum enum_stmt_attr_type
STMT_ATTR_CURSOR_TYPE,
STMT_ATTR_PREFETCH_ROWS
};
-typedef struct st_mysql_methods
-{
- my_bool (*read_query_result)(MYSQL *mysql);
- my_bool (*advanced_command)(MYSQL *mysql,
- enum enum_server_command command,
- const unsigned char *header,
- unsigned long header_length,
- const unsigned char *arg,
- unsigned long arg_length,
- my_bool skip_check,
- MYSQL_STMT *stmt);
- MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
- unsigned int fields);
- MYSQL_RES * (*use_result)(MYSQL *mysql);
- void (*fetch_lengths)(unsigned long *to,
- MYSQL_ROW column, unsigned int field_count);
- void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
- MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
- my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
- int (*stmt_execute)(MYSQL_STMT *stmt);
- int (*read_binary_rows)(MYSQL_STMT *stmt);
- int (*unbuffered_fetch)(MYSQL *mysql, char **row);
- void (*free_embedded_thd)(MYSQL *mysql);
- const char *(*read_statistics)(MYSQL *mysql);
- my_bool (*next_result)(MYSQL *mysql);
- int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd);
- int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
-} MYSQL_METHODS;
MYSQL_STMT * mysql_stmt_init(MYSQL *mysql);
int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
unsigned long length);
diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h
new file mode 100644
index 00000000000..9631b090b14
--- /dev/null
+++ b/include/mysql/client_plugin.h
@@ -0,0 +1,146 @@
+#ifndef MYSQL_CLIENT_PLUGIN_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ MySQL Client Plugin API
+
+ This file defines the API for plugins that work on the client side
+*/
+#define MYSQL_CLIENT_PLUGIN_INCLUDED
+
+#include <stdarg.h>
+#include <stdlib.h>
+
+/* known plugin types */
+#define MYSQL_CLIENT_reserved1 0
+#define MYSQL_CLIENT_reserved2 1
+#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2
+
+#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100
+
+#define MYSQL_CLIENT_MAX_PLUGINS 3
+
+#define mysql_declare_client_plugin(X) \
+ MYSQL_PLUGIN_EXPORT struct st_mysql_client_plugin_ ## X \
+ _mysql_client_plugin_declaration_ = { \
+ MYSQL_CLIENT_ ## X ## _PLUGIN, \
+ MYSQL_CLIENT_ ## X ## _PLUGIN_INTERFACE_VERSION,
+#define mysql_end_client_plugin }
+
+/* generic plugin header structure */
+#define MYSQL_CLIENT_PLUGIN_HEADER \
+ int type; \
+ unsigned int interface_version; \
+ const char *name; \
+ const char *author; \
+ const char *desc; \
+ unsigned int version[3]; \
+ int (*init)(char *, size_t, int, va_list); \
+ int (*deinit)();
+
+struct st_mysql_client_plugin
+{
+ MYSQL_CLIENT_PLUGIN_HEADER
+};
+
+struct st_mysql;
+
+/******** authentication plugin specific declarations *********/
+#include <mysql/plugin_auth_common.h>
+
+struct st_mysql_client_plugin_AUTHENTICATION
+{
+ MYSQL_CLIENT_PLUGIN_HEADER
+ int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
+};
+
+/******** using plugins ************/
+
+/**
+ loads a plugin and initializes it
+
+ @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+ and last_errno/last_error, for error reporting
+ @param name a name of the plugin to load
+ @param type type of plugin that should be loaded, -1 to disable type check
+ @param argc number of arguments to pass to the plugin initialization
+ function
+ @param ... arguments for the plugin initialization function
+
+ @retval
+ a pointer to the loaded plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_load_plugin(struct st_mysql *mysql, const char *name, int type,
+ int argc, ...);
+
+/**
+ loads a plugin and initializes it, taking va_list as an argument
+
+ This is the same as mysql_load_plugin, but take va_list instead of
+ a list of arguments.
+
+ @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+ and last_errno/last_error, for error reporting
+ @param name a name of the plugin to load
+ @param type type of plugin that should be loaded, -1 to disable type check
+ @param argc number of arguments to pass to the plugin initialization
+ function
+ @param args arguments for the plugin initialization function
+
+ @retval
+ a pointer to the loaded plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type,
+ int argc, va_list args);
+
+/**
+ finds an already loaded plugin by name, or loads it, if necessary
+
+ @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used,
+ and last_errno/last_error, for error reporting
+ @param name a name of the plugin to load
+ @param type type of plugin that should be loaded
+
+ @retval
+ a pointer to the plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type);
+
+/**
+ adds a plugin structure to the list of loaded plugins
+
+ This is useful if an application has the necessary functionality
+ (for example, a special load data handler) statically linked into
+ the application binary. It can use this function to register the plugin
+ directly, avoiding the need to factor it out into a shared object.
+
+ @param mysql MYSQL structure. It is only used for error reporting
+ @param plugin an st_mysql_client_plugin structure to register
+
+ @retval
+ a pointer to the plugin, or NULL in case of a failure
+*/
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(struct st_mysql *mysql,
+ struct st_mysql_client_plugin *plugin);
+
+#endif
+
diff --git a/include/mysql/client_plugin.h.pp b/include/mysql/client_plugin.h.pp
new file mode 100644
index 00000000000..20d353422dd
--- /dev/null
+++ b/include/mysql/client_plugin.h.pp
@@ -0,0 +1,41 @@
+#include <stdarg.h>
+#include <stdlib.h>
+struct st_mysql_client_plugin
+{
+ int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; int (*init)(char *, size_t, int, va_list); int (*deinit)();
+};
+struct st_mysql;
+#include <mysql/plugin_auth_common.h>
+typedef struct st_plugin_vio_info
+{
+ enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+ MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+ int socket;
+} MYSQL_PLUGIN_VIO_INFO;
+typedef struct st_plugin_vio
+{
+ int (*read_packet)(struct st_plugin_vio *vio,
+ unsigned char **buf);
+ int (*write_packet)(struct st_plugin_vio *vio,
+ const unsigned char *packet,
+ int packet_len);
+ void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+} MYSQL_PLUGIN_VIO;
+struct st_mysql_client_plugin_AUTHENTICATION
+{
+ int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; int (*init)(char *, size_t, int, va_list); int (*deinit)();
+ int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql);
+};
+typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql,
+ int type, const char *prompt, char *buf, int buf_len);
+struct st_mysql_client_plugin *
+mysql_load_plugin(struct st_mysql *mysql, const char *name, int type,
+ int argc, ...);
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type,
+ int argc, va_list args);
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type);
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(struct st_mysql *mysql,
+ struct st_mysql_client_plugin *plugin);
diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h
index 19cf0ed050d..01ca76983a6 100644
--- a/include/mysql/plugin.h
+++ b/include/mysql/plugin.h
@@ -83,7 +83,8 @@ typedef struct st_mysql_xid MYSQL_XID;
#define MYSQL_INFORMATION_SCHEMA_PLUGIN 4 /* The I_S plugin type */
#define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */
#define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */
-#define MYSQL_MAX_PLUGIN_TYPE_NUM 7 /* The number of plugin types */
+#define MYSQL_AUTHENTICATION_PLUGIN 7 /* The authentication plugin type */
+#define MYSQL_MAX_PLUGIN_TYPE_NUM 8 /* The number of plugin types */
/* We use the following strings to define licenses for plugins */
#define PLUGIN_LICENSE_PROPRIETARY 0
diff --git a/include/mysql/plugin_auth.h b/include/mysql/plugin_auth.h
new file mode 100644
index 00000000000..8fed53c59fa
--- /dev/null
+++ b/include/mysql/plugin_auth.h
@@ -0,0 +1,118 @@
+#ifndef MYSQL_PLUGIN_AUTH_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ Authentication Plugin API.
+
+ This file defines the API for server authentication plugins.
+*/
+
+#define MYSQL_PLUGIN_AUTH_INCLUDED
+
+#include <mysql/plugin.h>
+
+#define MYSQL_AUTHENTICATION_INTERFACE_VERSION 0x0100
+
+#include <mysql/plugin_auth_common.h>
+
+/**
+ Provides server plugin access to authentication information
+*/
+typedef struct st_mysql_server_auth_info
+{
+ /**
+ User name as sent by the client and shown in USER().
+ NULL if the client packet with the user name was not received yet.
+ */
+ char *user_name;
+
+ /**
+ Length of user_name
+ */
+ unsigned int user_name_length;
+
+ /**
+ A corresponding column value from the mysql.user table for the
+ matching account name
+ */
+ const char *auth_string;
+
+ /**
+ Length of auth_string
+ */
+ unsigned long auth_string_length;
+
+ /**
+ Matching account name as found in the mysql.user table.
+ A plugin can override it with another name that will be
+ used by MySQL for authorization, and shown in CURRENT_USER()
+ */
+ char authenticated_as[MYSQL_USERNAME_LENGTH+1];
+
+
+ /**
+ The unique user name that was used by the plugin to authenticate.
+ Plugins should put null-terminated UTF-8 here.
+ Available through the @@EXTERNAL_USER variable.
+ */
+ char external_user[512];
+
+ /**
+ This only affects the "Authentication failed. Password used: %s"
+ error message. has the following values :
+ 0 : %s will be NO.
+ 1 : %s will be YES.
+ 2 : there will be no %s.
+ Set it as appropriate or ignore at will.
+ */
+ int password_used;
+
+ /**
+ Set to the name of the connected client if it can be resolved, or to
+ the address otherwise
+ */
+ const char *host_or_ip;
+
+ /**
+ Length of host_or_ip
+ */
+ unsigned int host_or_ip_length;
+
+} MYSQL_SERVER_AUTH_INFO;
+
+/**
+ Server authentication plugin descriptor
+*/
+struct st_mysql_auth
+{
+ int interface_version; /**< version plugin uses */
+ /**
+ A plugin that a client must use for authentication with this server
+ plugin. Can be NULL to mean "any plugin".
+ */
+ const char *client_auth_plugin;
+ /**
+ Function provided by the plugin which should perform authentication (using
+ the vio functions if necessary) and return 0 if successful. The plugin can
+ also fill the info.authenticated_as field if a different username should be
+ used for authorization.
+ */
+ int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info);
+};
+#endif
+
diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp
new file mode 100644
index 00000000000..550e2e852a7
--- /dev/null
+++ b/include/mysql/plugin_auth.h.pp
@@ -0,0 +1,205 @@
+#include <mysql/plugin.h>
+#include <mysql/services.h>
+#include <mysql/service_my_snprintf.h>
+#include <stdarg.h>
+#include <stdlib.h>
+extern struct my_snprintf_service_st {
+ size_t (*my_snprintf_type)(char*, size_t, const char*, ...);
+ size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list);
+} *my_snprintf_service;
+size_t my_snprintf(char* to, size_t n, const char* fmt, ...);
+size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap);
+#include <mysql/service_thd_alloc.h>
+#include <stdlib.h>
+struct st_mysql_lex_string
+{
+ char *str;
+ size_t length;
+};
+typedef struct st_mysql_lex_string MYSQL_LEX_STRING;
+extern struct thd_alloc_service_st {
+ void *(*thd_alloc_func)(void*, unsigned int);
+ void *(*thd_calloc_func)(void*, unsigned int);
+ char *(*thd_strdup_func)(void*, const char *);
+ char *(*thd_strmake_func)(void*, const char *, unsigned int);
+ void *(*thd_memdup_func)(void*, const void*, unsigned int);
+ MYSQL_LEX_STRING *(*thd_make_lex_string_func)(void*, MYSQL_LEX_STRING *,
+ const char *, unsigned int, int);
+} *thd_alloc_service;
+void *thd_alloc(void* thd, unsigned int size);
+void *thd_calloc(void* thd, unsigned int size);
+char *thd_strdup(void* thd, const char *str);
+char *thd_strmake(void* thd, const char *str, unsigned int size);
+void *thd_memdup(void* thd, const void* str, unsigned int size);
+MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str,
+ const char *str, unsigned int size,
+ int allocate_lex_string);
+struct st_mysql_xid {
+ long formatID;
+ long gtrid_length;
+ long bqual_length;
+ char data[128];
+};
+typedef struct st_mysql_xid MYSQL_XID;
+enum enum_mysql_show_type
+{
+ SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG,
+ SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR,
+ SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE,
+ SHOW_always_last
+};
+struct st_mysql_show_var {
+ const char *name;
+ char *value;
+ enum enum_mysql_show_type type;
+};
+typedef int (*mysql_show_var_func)(void*, struct st_mysql_show_var*, char *);
+struct st_mysql_sys_var;
+struct st_mysql_value;
+typedef int (*mysql_var_check_func)(void* thd,
+ struct st_mysql_sys_var *var,
+ void *save, struct st_mysql_value *value);
+typedef void (*mysql_var_update_func)(void* thd,
+ struct st_mysql_sys_var *var,
+ void *var_ptr, const void *save);
+struct st_mysql_plugin
+{
+ int type;
+ void *info;
+ const char *name;
+ const char *author;
+ const char *descr;
+ int license;
+ int (*init)(void *);
+ int (*deinit)(void *);
+ unsigned int version;
+ struct st_mysql_show_var *status_vars;
+ struct st_mysql_sys_var **system_vars;
+ void * __reserved1;
+};
+#include "plugin_ftparser.h"
+#include "plugin.h"
+enum enum_ftparser_mode
+{
+ MYSQL_FTPARSER_SIMPLE_MODE= 0,
+ MYSQL_FTPARSER_WITH_STOPWORDS= 1,
+ MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2
+};
+enum enum_ft_token_type
+{
+ FT_TOKEN_EOF= 0,
+ FT_TOKEN_WORD= 1,
+ FT_TOKEN_LEFT_PAREN= 2,
+ FT_TOKEN_RIGHT_PAREN= 3,
+ FT_TOKEN_STOPWORD= 4
+};
+typedef struct st_mysql_ftparser_boolean_info
+{
+ enum enum_ft_token_type type;
+ int yesno;
+ int weight_adjust;
+ char wasign;
+ char trunc;
+ char prev;
+ char *quot;
+} MYSQL_FTPARSER_BOOLEAN_INFO;
+typedef struct st_mysql_ftparser_param
+{
+ int (*mysql_parse)(struct st_mysql_ftparser_param *,
+ char *doc, int doc_len);
+ int (*mysql_add_word)(struct st_mysql_ftparser_param *,
+ char *word, int word_len,
+ MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info);
+ void *ftparser_state;
+ void *mysql_ftparam;
+ struct charset_info_st *cs;
+ char *doc;
+ int length;
+ int flags;
+ enum enum_ftparser_mode mode;
+} MYSQL_FTPARSER_PARAM;
+struct st_mysql_ftparser
+{
+ int interface_version;
+ int (*parse)(MYSQL_FTPARSER_PARAM *param);
+ int (*init)(MYSQL_FTPARSER_PARAM *param);
+ int (*deinit)(MYSQL_FTPARSER_PARAM *param);
+};
+struct st_mysql_daemon
+{
+ int interface_version;
+};
+struct st_mysql_information_schema
+{
+ int interface_version;
+};
+struct st_mysql_storage_engine
+{
+ int interface_version;
+};
+struct handlerton;
+ struct Mysql_replication {
+ int interface_version;
+ };
+struct st_mysql_value
+{
+ int (*value_type)(struct st_mysql_value *);
+ const char *(*val_str)(struct st_mysql_value *, char *buffer, int *length);
+ int (*val_real)(struct st_mysql_value *, double *realbuf);
+ int (*val_int)(struct st_mysql_value *, long long *intbuf);
+ int (*is_unsigned)(struct st_mysql_value *);
+};
+int thd_in_lock_tables(const void* thd);
+int thd_tablespace_op(const void* thd);
+long long thd_test_options(const void* thd, long long test_options);
+int thd_sql_command(const void* thd);
+const char *thd_proc_info(void* thd, const char *info);
+void **thd_ha_data(const void* thd, const struct handlerton *hton);
+int thd_tx_isolation(const void* thd);
+char *thd_security_context(void* thd, char *buffer, unsigned int length,
+ unsigned int max_query_len);
+void thd_inc_row_count(void* thd);
+int mysql_tmpfile(const char *prefix);
+int thd_killed(const void* thd);
+unsigned long thd_get_thread_id(const void* thd);
+void thd_get_xid(const void* thd, MYSQL_XID *xid);
+void mysql_query_cache_invalidate4(void* thd,
+ const char *key, unsigned int key_length,
+ int using_trx);
+void *thd_get_ha_data(const void* thd, const struct handlerton *hton);
+void thd_set_ha_data(void* thd, const struct handlerton *hton,
+ const void *ha_data);
+#include <mysql/plugin_auth_common.h>
+typedef struct st_plugin_vio_info
+{
+ enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+ MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+ int socket;
+} MYSQL_PLUGIN_VIO_INFO;
+typedef struct st_plugin_vio
+{
+ int (*read_packet)(struct st_plugin_vio *vio,
+ unsigned char **buf);
+ int (*write_packet)(struct st_plugin_vio *vio,
+ const unsigned char *packet,
+ int packet_len);
+ void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+} MYSQL_PLUGIN_VIO;
+typedef struct st_mysql_server_auth_info
+{
+ const char *user_name;
+ unsigned int user_name_length;
+ const char *auth_string;
+ unsigned long auth_string_length;
+ char authenticated_as[48 +1];
+ char external_user[512];
+ int password_used;
+ const char *host_or_ip;
+ unsigned int host_or_ip_length;
+} MYSQL_SERVER_AUTH_INFO;
+struct st_mysql_auth
+{
+ int interface_version;
+ const char *client_auth_plugin;
+ int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info);
+};
diff --git a/include/mysql/plugin_auth_common.h b/include/mysql/plugin_auth_common.h
new file mode 100644
index 00000000000..4ad92d01bfb
--- /dev/null
+++ b/include/mysql/plugin_auth_common.h
@@ -0,0 +1,105 @@
+#ifndef MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ This file defines constants and data structures that are the same for
+ both client- and server-side authentication plugins.
+*/
+#define MYSQL_PLUGIN_AUTH_COMMON_INCLUDED
+
+/** the max allowed length for a user name */
+#define MYSQL_USERNAME_LENGTH 48
+
+/**
+ return values of the plugin authenticate_user() method.
+*/
+
+/**
+ Authentication failed. Additionally, all other CR_xxx values
+ (libmysql error code) can be used too.
+
+ The client plugin may set the error code and the error message directly
+ in the MYSQL structure and return CR_ERROR. If a CR_xxx specific error
+ code was returned, an error message in the MYSQL structure will be
+ overwritten. If CR_ERROR is returned without setting the error in MYSQL,
+ CR_UNKNOWN_ERROR will be user.
+*/
+#define CR_ERROR 0
+/**
+ Authentication (client part) was successful. It does not mean that the
+ authentication as a whole was successful, usually it only means
+ that the client was able to send the user name and the password to the
+ server. If CR_OK is returned, the libmysql reads the next packet expecting
+ it to be one of OK, ERROR, or CHANGE_PLUGIN packets.
+*/
+#define CR_OK -1
+/**
+ Authentication was successful.
+ It means that the client has done its part successfully and also that
+ a plugin has read the last packet (one of OK, ERROR, CHANGE_PLUGIN).
+ In this case, libmysql will not read a packet from the server,
+ but it will use the data at mysql->net.read_pos.
+
+ A plugin may return this value if the number of roundtrips in the
+ authentication protocol is not known in advance, and the client plugin
+ needs to read one packet more to determine if the authentication is finished
+ or not.
+*/
+#define CR_OK_HANDSHAKE_COMPLETE -2
+
+typedef struct st_plugin_vio_info
+{
+ enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET,
+ MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol;
+ int socket; /**< it's set, if the protocol is SOCKET or TCP */
+#ifdef _WIN32
+ HANDLE handle; /**< it's set, if the protocol is PIPE or MEMORY */
+#endif
+} MYSQL_PLUGIN_VIO_INFO;
+
+/**
+ Provides plugin access to communication channel
+*/
+typedef struct st_plugin_vio
+{
+ /**
+ Plugin provides a pointer reference and this function sets it to the
+ contents of any incoming packet. Returns the packet length, or -1 if
+ the plugin should terminate.
+ */
+ int (*read_packet)(struct st_plugin_vio *vio,
+ unsigned char **buf);
+
+ /**
+ Plugin provides a buffer with data and the length and this
+ function sends it as a packet. Returns 0 on success, 1 on failure.
+ */
+ int (*write_packet)(struct st_plugin_vio *vio,
+ const unsigned char *packet,
+ int packet_len);
+
+ /**
+ Fills in a st_plugin_vio_info structure, providing the information
+ about the connection.
+ */
+ void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+
+} MYSQL_PLUGIN_VIO;
+
+#endif
+
diff --git a/include/mysql_com.h b/include/mysql_com.h
index 90fe4ac1995..d4223211710 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -162,9 +162,17 @@ enum enum_server_command
#define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */
#define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
+#define CLIENT_PLUGIN_AUTH (1UL << 19) /* Client supports plugin authentication */
+
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
+#ifdef HAVE_COMPRESS
+#define CAN_CLIENT_COMPRESS CLIENT_COMPRESS
+#else
+#define CAN_CLIENT_COMPRESS 0
+#endif
+
/* Gather all possible capabilites (flags) supported by the server */
#define CLIENT_ALL_FLAGS (CLIENT_LONG_PASSWORD | \
CLIENT_FOUND_ROWS | \
@@ -186,7 +194,8 @@ enum enum_server_command
CLIENT_MULTI_RESULTS | \
CLIENT_PS_MULTI_RESULTS | \
CLIENT_SSL_VERIFY_SERVER_CERT | \
- CLIENT_REMEMBER_OPTIONS)
+ CLIENT_REMEMBER_OPTIONS | \
+ CLIENT_PLUGIN_AUTH)
/*
Switch off the flags that are optional and depending on build flags
@@ -518,14 +527,14 @@ void create_random_string(char *to, unsigned int length, struct rand_struct *ran
void hash_password(unsigned long *to, const char *password, unsigned int password_len);
void make_scrambled_password_323(char *to, const char *password);
void scramble_323(char *to, const char *message, const char *password);
-my_bool check_scramble_323(const char *, const char *message,
+my_bool check_scramble_323(const unsigned char *reply, const char *message,
unsigned long *salt);
void get_salt_from_password_323(unsigned long *res, const char *password);
void make_password_from_salt_323(char *to, const unsigned long *salt);
void make_scrambled_password(char *to, const char *password);
void scramble(char *to, const char *message, const char *password);
-my_bool check_scramble(const char *reply, const char *message,
+my_bool check_scramble(const unsigned char *reply, const char *message,
const unsigned char *hash_stage2);
void get_salt_from_password(unsigned char *res, const char *password);
void make_password_from_salt(char *to, const unsigned char *hash_stage2);
diff --git a/include/sql_common.h b/include/sql_common.h
index 5fd8778d62b..a9a3168b691 100644
--- a/include/sql_common.h
+++ b/include/sql_common.h
@@ -16,14 +16,60 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+#define SQL_COMMON_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <mysql.h>
extern const char *unknown_sqlstate;
extern const char *cant_connect_sqlstate;
extern const char *not_error_sqlstate;
-#ifdef __cplusplus
-extern "C" {
+struct st_mysql_options_extention {
+ char *plugin_dir;
+ char *default_auth;
+};
+
+typedef struct st_mysql_methods
+{
+ my_bool (*read_query_result)(MYSQL *mysql);
+ my_bool (*advanced_command)(MYSQL *mysql,
+ enum enum_server_command command,
+ const unsigned char *header,
+ unsigned long header_length,
+ const unsigned char *arg,
+ unsigned long arg_length,
+ my_bool skip_check,
+ MYSQL_STMT *stmt);
+ MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
+ unsigned int fields);
+ MYSQL_RES * (*use_result)(MYSQL *mysql);
+ void (*fetch_lengths)(unsigned long *to,
+ MYSQL_ROW column, unsigned int field_count);
+ void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
+ int (*read_change_user_result)(MYSQL *mysql);
+#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
+ MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
+ my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
+ int (*stmt_execute)(MYSQL_STMT *stmt);
+ int (*read_binary_rows)(MYSQL_STMT *stmt);
+ int (*unbuffered_fetch)(MYSQL *mysql, char **row);
+ void (*free_embedded_thd)(MYSQL *mysql);
+ const char *(*read_statistics)(MYSQL *mysql);
+ my_bool (*next_result)(MYSQL *mysql);
+ int (*read_rows_from_cursor)(MYSQL_STMT *stmt);
#endif
+} MYSQL_METHODS;
+
+#define simple_command(mysql, command, arg, length, skip_check) \
+ (*(mysql)->methods->advanced_command)(mysql, command, 0, \
+ 0, arg, length, skip_check, NULL)
+#define stmt_command(mysql, command, arg, length, stmt) \
+ (*(mysql)->methods->advanced_command)(mysql, command, 0, \
+ 0, arg, length, 1, stmt)
extern CHARSET_INFO *default_client_charset_info;
MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields,
@@ -45,6 +91,19 @@ void set_stmt_errmsg(MYSQL_STMT *stmt, NET *net);
void set_stmt_error(MYSQL_STMT *stmt, int errcode, const char *sqlstate,
const char *err);
void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate);
+void set_mysql_extended_error(MYSQL *mysql, int errcode, const char *sqlstate,
+ const char *format, ...);
+
+/* client side of the pluggable authentication */
+struct st_plugin_vio_info;
+void mpvio_info(Vio *vio, struct st_plugin_vio_info *info);
+int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+ const char *data_plugin, const char *db);
+int mysql_client_plugin_init();
+void mysql_client_plugin_deinit();
+struct st_mysql_client_plugin;
+extern struct st_mysql_client_plugin *mysql_client_builtins[];
+
#ifdef __cplusplus
}
#endif
diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt
index 2ae09c1707a..f5fa1f7a009 100644
--- a/libmysql/CMakeLists.txt
+++ b/libmysql/CMakeLists.txt
@@ -140,6 +140,7 @@ SET(CLIENT_SOURCES
errmsg.c
../sql-common/client.c
../sql-common/my_time.c
+ ../sql-common/client_plugin.c
../sql/net_serv.cc
../sql-common/pack.c
../sql/password.c
@@ -148,7 +149,7 @@ ADD_CONVENIENCE_LIBRARY(clientlib ${CLIENT_SOURCES})
DTRACE_INSTRUMENT(clientlib)
ADD_DEPENDENCIES(clientlib GenError)
-SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES})
+SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL})
# Merge several convenience libraries into one big mysqlclient
# and link them together into shared library.
diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared
index 887af62229a..5a7236f1e6d 100644
--- a/libmysql/Makefile.shared
+++ b/libmysql/Makefile.shared
@@ -23,6 +23,7 @@
MYSQLDATAdir = $(localstatedir)
MYSQLSHAREdir = $(pkgdatadir)
MYSQLBASEdir= $(prefix)
+pkgplugindir = $(pkglibdir)/plugin
## We'll use CLIENT_EXTRA_LDFLAGS for threaded and non-threaded
## until someone complains that they need separate options.
LDADD = @CLIENT_EXTRA_LDFLAGS@ $(target)
@@ -70,26 +71,27 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo \
my_rename.lo my_chsize.lo my_sync.lo \
my_getsystime.lo my_symlink2.lo mf_same.lo
sqlobjects = net.lo
-sql_cmn_objects = pack.lo client.lo my_time.lo
+sql_cmn_objects = pack.lo client.lo my_time.lo client_plugin.lo
# Not needed in the minimum library
mysysobjects2 = my_lib.lo mf_qsort.lo
mysysobjects = $(mysysobjects1) $(mysysobjects2)
target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \
$(sql_cmn_objects) $(vio_objects) $(sqlobjects)
-target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@
+target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ @LIBDL@
vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo
BUILT_SOURCES = link_sources
CLEANFILES = $(target_libadd) $(SHLIBOBJS) \
$(target) $(BUILT_SOURCES)
-DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \
- -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \
+DEFS = -DDEFAULT_CHARSET_HOME='"$(MYSQLBASEdir)"' \
+ -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
-DDEFAULT_HOME_ENV=MYSQL_HOME \
+ -DPLUGINDIR='"$(pkgplugindir)"' \
-DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \
- -DDEFAULT_SYSCONFDIR="\"$(sysconfdir)\"" \
- -DSHAREDIR="\"$(MYSQLSHAREdir)\"" -DDISABLE_DTRACE \
+ -DDEFAULT_SYSCONFDIR='"$(sysconfdir)"' \
+ -DSHAREDIR='"$(MYSQLSHAREdir)"' -DDISABLE_DTRACE \
$(target_defs)
if HAVE_YASSL
diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h
index aaec08d1b1e..5204d03e5af 100644
--- a/libmysql/client_settings.h
+++ b/libmysql/client_settings.h
@@ -28,7 +28,8 @@ extern char * mysql_unix_port;
CLIENT_PROTOCOL_41 | \
CLIENT_SECURE_CONNECTION | \
CLIENT_MULTI_RESULTS | \
- CLIENT_PS_MULTI_RESULTS)
+ CLIENT_PS_MULTI_RESULTS | \
+ CLIENT_PLUGIN_AUTH)
sig_handler my_pipe_sig_handler(int sig);
void read_user_name(char *name);
@@ -67,7 +68,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt);
int cli_read_binary_rows(MYSQL_STMT *stmt);
int cli_unbuffered_fetch(MYSQL *mysql, char **row);
const char * cli_read_statistics(MYSQL *mysql);
-int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd);
+int cli_read_change_user_result(MYSQL *mysql);
#ifdef EMBEDDED_LIBRARY
int init_embedded_server(int argc, char **argv, char **groups);
diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c
index febbded6af2..823f83026c9 100644
--- a/libmysql/errmsg.c
+++ b/libmysql/errmsg.c
@@ -84,7 +84,8 @@ const char *client_errors[]=
"Lost connection to MySQL server at '%s', system error: %d",
"Statement closed indirectly because of a preceeding %s() call",
"The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again",
- "This handle is already connected. Use a separate handle for each connection."
+ "This handle is already connected. Use a separate handle for each connection.",
+ "Authentication plugin '%s' cannot be loaded: %s",
""
};
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 550b1b7b107..c90af040c5f 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -126,6 +126,8 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)),
if (my_init()) /* Will init threads */
return 1;
init_client_errs();
+ if (mysql_client_plugin_init())
+ return 1;
if (!mysql_port)
{
char *env;
@@ -196,6 +198,8 @@ void STDCALL mysql_server_end()
if (!mysql_client_init)
return;
+ mysql_client_plugin_deinit();
+
#ifdef EMBEDDED_LIBRARY
end_embedded_server();
#endif
@@ -345,44 +349,14 @@ mysql_connect(MYSQL *mysql,const char *host,
Change user and database
**************************************************************************/
-int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd)
-{
- NET *net= &mysql->net;
- ulong pkt_length;
-
- pkt_length= cli_safe_read(mysql);
-
- if (pkt_length == packet_error)
- return 1;
-
- if (pkt_length == 1 && net->read_pos[0] == 254 &&
- mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- /*
- By sending this very specific reply server asks us to send scrambled
- password in old format. The reply contains scramble_323.
- */
- scramble_323(buff, mysql->scramble, passwd);
- if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) ||
- net_flush(net))
- {
- set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate);
- return 1;
- }
- /* Read what server thinks about out new auth message report */
- if (cli_safe_read(mysql) == packet_error)
- return 1;
- }
- return 0;
-}
-
my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
const char *passwd, const char *db)
{
- char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2];
- char *end= buff;
int rc;
CHARSET_INFO *saved_cs= mysql->charset;
+ char *saved_user= mysql->user;
+ char *saved_passwd= mysql->passwd;
+ char *saved_db= mysql->db;
DBUG_ENTER("mysql_change_user");
@@ -396,49 +370,11 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
/* Use an empty string instead of NULL. */
- if (!user)
- user="";
- if (!passwd)
- passwd="";
-
- /*
- Store user into the buffer.
- Advance position as strmake returns a pointer to the closing NUL.
- */
- end= strmake(end, user, USERNAME_LENGTH) + 1;
-
- /* write scrambled password according to server capabilities */
- if (passwd[0])
- {
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- *end++= SCRAMBLE_LENGTH;
- scramble(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH;
- }
- else
- {
- scramble_323(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH_323 + 1;
- }
- }
- else
- *end++= '\0'; /* empty password */
- /* Add database if needed */
- end= strmake(end, db ? db : "", NAME_LEN) + 1;
-
- /* Add character set number. */
-
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- int2store(end, (ushort) mysql->charset->number);
- end+= 2;
- }
-
- /* Write authentication package */
- simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1);
+ mysql->user= (char*)(user ? user : "");
+ mysql->passwd= (char*)(passwd ? passwd : "");
+ mysql->db= 0;
- rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd);
+ rc= run_plugin_auth(mysql, 0, 0, 0, db);
/*
The server will close all statements no matter was the attempt
@@ -448,18 +384,21 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user,
if (rc == 0)
{
/* Free old connect information */
- my_free(mysql->user);
- my_free(mysql->passwd);
- my_free(mysql->db);
+ my_free(saved_user);
+ my_free(saved_passwd);
+ my_free(saved_db);
/* alloc new connect information */
- mysql->user= my_strdup(user,MYF(MY_WME));
- mysql->passwd=my_strdup(passwd,MYF(MY_WME));
- mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0;
+ mysql->user= my_strdup(mysql->user, MYF(MY_WME));
+ mysql->passwd= my_strdup(mysql->passwd, MYF(MY_WME));
+ mysql->db= db ? my_strdup(db, MYF(MY_WME)) : 0;
}
else
{
mysql->charset= saved_cs;
+ mysql->user= saved_user;
+ mysql->passwd= saved_passwd;
+ mysql->db= saved_db;
}
DBUG_RETURN(rc);
diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt
index a7efcb024ec..a658ee9d55e 100644
--- a/libmysqld/CMakeLists.txt
+++ b/libmysqld/CMakeLists.txt
@@ -42,6 +42,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc
../libmysql/libmysql.c ../libmysql/errmsg.c ../client/get_password.c
../sql-common/client.c ../sql-common/my_time.c
../sql-common/my_user.c ../sql-common/pack.c
+ ../sql-common/client_plugin.c
../sql/password.c ../sql/discover.cc ../sql/derror.cc
../sql/field.cc ../sql/field_conv.cc
../sql/filesort.cc ../sql/gstream.cc
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index 1ffa349bcfe..e2521bfcac7 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -43,7 +43,7 @@ pkglib_LIBRARIES = libmysqld.a
SUBDIRS = . examples
libmysqld_sources= libmysqld.c lib_sql.cc emb_qcache.cc
libmysqlsources = errmsg.c get_password.c libmysql.c client.c pack.c \
- my_time.c
+ my_time.c client_plugin.c
noinst_HEADERS = embedded_priv.h emb_qcache.h
diff --git a/libmysqld/embedded_priv.h b/libmysqld/embedded_priv.h
index 369b344d4bd..c246693594b 100644
--- a/libmysqld/embedded_priv.h
+++ b/libmysqld/embedded_priv.h
@@ -15,6 +15,8 @@
/* Prototypes for the embedded version of MySQL */
+#include <sql_common.h>
+
C_MODE_START
void lib_connection_phase(NET *net, int phase);
void init_embedded_mysql(MYSQL *mysql, int client_flag);
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index 94927c590cf..a1a57e70499 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -35,7 +35,6 @@ C_MODE_START
#include <mysql.h>
#undef ER
#include "errmsg.h"
-#include <sql_common.h>
#include "embedded_priv.h"
extern unsigned int mysql_server_last_errno;
@@ -414,11 +413,10 @@ static MYSQL_RES * emb_store_result(MYSQL *mysql)
return mysql_store_result(mysql);
}
-int emb_read_change_user_result(MYSQL *mysql,
- char *buff __attribute__((unused)),
- const char *passwd __attribute__((unused)))
+int emb_read_change_user_result(MYSQL *mysql)
{
- return mysql_errno(mysql);
+ mysql->net.read_pos= (uchar*)""; // fake an OK packet
+ return mysql_errno(mysql) ? packet_error : 1 /* length of the OK packet */;
}
MYSQL_METHODS embedded_methods=
@@ -429,6 +427,7 @@ MYSQL_METHODS embedded_methods=
emb_store_result,
emb_fetch_lengths,
emb_flush_use_result,
+ emb_read_change_user_result,
emb_list_fields,
emb_read_prepare_result,
emb_stmt_execute,
@@ -437,7 +436,6 @@ MYSQL_METHODS embedded_methods=
emb_free_embedded_thd,
emb_read_statistics,
emb_read_query_result,
- emb_read_change_user_result,
emb_read_rows_from_cursor
};
@@ -601,6 +599,7 @@ void init_embedded_mysql(MYSQL *mysql, int client_flag)
THD *thd = (THD *)mysql->thd;
thd->mysql= mysql;
mysql->server_version= server_version;
+ mysql->client_flag= client_flag;
init_alloc_root(&mysql->field_alloc, 8192, 0);
}
@@ -664,14 +663,20 @@ err:
int check_embedded_connection(MYSQL *mysql, const char *db)
{
int result;
+ LEX_STRING db_str = { (char*)db, db ? strlen(db) : 0 };
THD *thd= (THD*)mysql->thd;
thd_init_client_charset(thd, mysql->charset->number);
thd->update_charset();
Security_context *sctx= thd->security_ctx;
sctx->host_or_ip= sctx->host= (char*) my_localhost;
strmake(sctx->priv_host, (char*) my_localhost, MAX_HOSTNAME-1);
- sctx->priv_user= sctx->user= my_strdup(mysql->user, MYF(0));
- result= check_user(thd, COM_CONNECT, NULL, 0, db, true);
+ strmake(sctx->priv_user, mysql->user, USERNAME_LENGTH-1);
+ sctx->user= my_strdup(mysql->user, MYF(0));
+ sctx->proxy_user[0]= 0;
+ sctx->master_access= GLOBAL_ACLS; // Full rights
+ /* Change database if necessary */
+ if (!(result= (db && db[0] && mysql_change_db(thd, &db_str, FALSE))))
+ my_ok(thd);
thd->protocol->end_statement();
emb_read_query_result(mysql);
return result;
@@ -680,14 +685,15 @@ int check_embedded_connection(MYSQL *mysql, const char *db)
#else
int check_embedded_connection(MYSQL *mysql, const char *db)
{
+ /*
+ we emulate a COM_CHANGE_USER user here,
+ it's easier than to emulate the complete 3-way handshake
+ */
+ char buf[USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2], *end;
+ NET *net= &mysql->net;
THD *thd= (THD*)mysql->thd;
Security_context *sctx= thd->security_ctx;
- int result;
- char scramble_buff[SCRAMBLE_LENGTH];
- int passwd_len;
- thd_init_client_charset(thd, mysql->charset->number);
- thd->update_charset();
if (mysql->options.client_ip)
{
sctx->host= my_strdup(mysql->options.client_ip, MYF(0));
@@ -698,37 +704,43 @@ int check_embedded_connection(MYSQL *mysql, const char *db)
sctx->host_or_ip= sctx->host;
if (acl_check_host(sctx->host, sctx->ip))
- {
- result= ER_HOST_NOT_PRIVILEGED;
goto err;
- }
- sctx->user= my_strdup(mysql->user, MYF(0));
+ /* construct a COM_CHANGE_USER packet */
+ end= strmake(buf, mysql->user, USERNAME_LENGTH) + 1;
+
+ memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble
+ thd->scramble[SCRAMBLE_LENGTH]= 0;
+
if (mysql->passwd && mysql->passwd[0])
{
- memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble
- thd->scramble[SCRAMBLE_LENGTH]= 0;
- scramble(scramble_buff, thd->scramble, mysql->passwd);
- passwd_len= SCRAMBLE_LENGTH;
+ *end++= SCRAMBLE_LENGTH;
+ scramble(end, thd->scramble, mysql->passwd);
+ end+= SCRAMBLE_LENGTH;
}
else
- passwd_len= 0;
+ *end++= 0;
- if((result= check_user(thd, COM_CONNECT,
- scramble_buff, passwd_len, db, true)))
- goto err;
+ end= strmake(end, db ? db : "", NAME_LEN) + 1;
- return 0;
-err:
+ int2store(end, (ushort) mysql->charset->number);
+ end+= 2;
+
+ /* acl_authenticate() takes the data from thd->net->read_pos */
+ thd->net.read_pos= (uchar*)buf;
+
+ if (acl_authenticate(thd, 0, end - buf))
{
- NET *net= &mysql->net;
- strmake(net->last_error, thd->stmt_da->message(),
- sizeof(net->last_error)-1);
- memcpy(net->sqlstate,
- mysql_errno_to_sqlstate(thd->stmt_da->sql_errno()),
- sizeof(net->sqlstate)-1);
+ x_free(thd->security_ctx->user);
+ goto err;
}
- return result;
+ return 0;
+err:
+ strmake(net->last_error, thd->main_da.message(), sizeof(net->last_error)-1);
+ memcpy(net->sqlstate,
+ mysql_errno_to_sqlstate(thd->main_da.sql_errno()),
+ sizeof(net->sqlstate)-1);
+ return 1;
}
#endif
diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c
index 603fc3bc2f0..f6f0e1ba3db 100644
--- a/libmysqld/libmysqld.c
+++ b/libmysqld/libmysqld.c
@@ -17,7 +17,6 @@
#include <mysql.h>
#include <mysqld_error.h>
#include <my_pthread.h>
-#include "embedded_priv.h"
#include <my_sys.h>
#include <mysys_err.h>
#include <m_string.h>
@@ -28,6 +27,7 @@
#include <signal.h>
#include <time.h>
#include <sql_common.h>
+#include "embedded_priv.h"
#include "client_settings.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
@@ -165,7 +165,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user,
client_flag|=CLIENT_CAPABILITIES;
if (client_flag & CLIENT_MULTI_STATEMENTS)
client_flag|= CLIENT_MULTI_RESULTS;
- client_flag&= ~CLIENT_COMPRESS;
+ /*
+ no compression in embedded as we don't send any data,
+ and no pluggable auth, as we cannot do a client-server dialog
+ */
+ client_flag&= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH);
if (db)
client_flag|=CLIENT_CONNECT_WITH_DB;
diff --git a/mysql-test/include/have_plugin_auth.inc b/mysql-test/include/have_plugin_auth.inc
new file mode 100644
index 00000000000..41f11419c64
--- /dev/null
+++ b/mysql-test/include/have_plugin_auth.inc
@@ -0,0 +1,4 @@
+disable_query_log;
+--require r/true.require
+select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE` FROM INFORMATION_SCHEMA.PLUGINS
+ WHERE PLUGIN_NAME='test_plugin_server';
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index 1a574fe6e6b..a23a8ee7261 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -126,6 +126,9 @@ my $path_vardir_trace; # unix formatted opt_vardir for trace files
my $opt_tmpdir; # Path to use for tmp/ dir
my $opt_tmpdir_pid;
+my $auth_filename; # the name of the authentication test plugin
+my $auth_plugin; # the path to the authentication test plugin
+
END {
if ( defined $opt_tmpdir_pid and $opt_tmpdir_pid == $$ )
{
@@ -1025,6 +1028,22 @@ sub command_line_setup {
"$basedir/sql/share/charsets",
"$basedir/share/charsets");
+ # Look for client test plugin
+ if (IS_WINDOWS)
+ {
+ $auth_filename = "auth_test_plugin.dll";
+ }
+ else
+ {
+ $auth_filename = "auth_test_plugin.so";
+ }
+ $auth_plugin=
+ mtr_file_exists(vs_config_dirs('plugin/auth/',$auth_filename),
+ "$basedir/plugin/auth/.libs/" . $auth_filename,
+ "$basedir/lib/mysql/plugin/" . $auth_filename,
+ "$basedir/lib/plugin/" . $auth_filename);
+
+
if (using_extern())
{
# Connect to the running mysqld and find out what it supports
@@ -1897,6 +1916,24 @@ sub environment_setup {
($lib_udf_example ? dirname($lib_udf_example) : "");
# --------------------------------------------------------------------------
+ # Add the path where mysqld will find the auth test plugin (dialog.so/dll)
+ # --------------------------------------------------------------------------
+ if ($auth_plugin)
+ {
+ $ENV{'PLUGIN_AUTH'}= basename($auth_plugin);
+ $ENV{'PLUGIN_AUTH_OPT'}= "--plugin-dir=".dirname($auth_plugin);
+
+ $ENV{'PLUGIN_AUTH_LOAD'}="--plugin_load=test_plugin_server=".$auth_filename;
+ }
+ else
+ {
+ $ENV{'PLUGIN_AUTH'}= "";
+ $ENV{'PLUGIN_AUTH_OPT'}="--plugin-dir=";
+ $ENV{'PLUGIN_AUTH_LOAD'}="";
+ }
+
+
+ # --------------------------------------------------------------------------
# Add the path where mysqld will find ha_example.so
# --------------------------------------------------------------------------
if ($mysql_version_id >= 50100) {
@@ -4918,6 +4955,10 @@ sub start_mysqltest ($) {
mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir);
mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir);
mtr_add_arg($args, "--logdir=%s/log", $opt_vardir);
+ if ($auth_plugin)
+ {
+ mtr_add_arg($args, "--plugin_dir=%s", dirname($auth_plugin));
+ }
# Log line number and time for each line in .test file
mtr_add_arg($args, "--mark-progress")
diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result
index ae9989ce563..e8562662bfd 100644
--- a/mysql-test/r/1st.result
+++ b/mysql-test/r/1st.result
@@ -21,6 +21,7 @@ ndb_binlog_index
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
diff --git a/mysql-test/r/change_user.result b/mysql-test/r/change_user.result
index 1ed7fcbb8fa..3021b5472bf 100644
--- a/mysql-test/r/change_user.result
+++ b/mysql-test/r/change_user.result
@@ -1,3 +1,39 @@
+grant select on test.* to test_nopw;
+grant select on test.* to test_oldpw identified by password "09301740536db389";
+grant select on test.* to test_newpw identified by "newpw";
+select user(), current_user(), database();
+user() current_user() database()
+root@localhost root@localhost test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<root@localhost> <root@localhost> test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_nopw@localhost> <test_nopw@%> NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_oldpw@localhost> <test_oldpw@%> NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_newpw@localhost> <test_newpw@%> NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<root@localhost> <root@localhost> NULL
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_nopw@localhost> <test_nopw@%> test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_oldpw@localhost> <test_oldpw@%> test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<test_newpw@localhost> <test_newpw@%> test
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+concat('<', user(), '>') concat('<', current_user(), '>') database()
+<root@localhost> <root@localhost> test
+drop user test_nopw;
+drop user test_oldpw;
+drop user test_newpw;
Bug#20023
SELECT @@session.sql_big_selects;
@@session.sql_big_selects
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index 5e6c013bb38..bbd0273c1c6 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -15,6 +15,7 @@ ndb_binlog_index
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
@@ -48,6 +49,7 @@ ndb_binlog_index
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
@@ -89,6 +91,7 @@ ndb_binlog_index
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result
index a5003c936e8..a27c7dc18ee 100644
--- a/mysql-test/r/events_bugs.result
+++ b/mysql-test/r/events_bugs.result
@@ -568,6 +568,7 @@ USE test;
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
SET GLOBAL event_scheduler = ON;
CREATE TABLE events_test.event_log
(id int KEY AUTO_INCREMENT, ev_nm char(40), ev_cnt int, ev_tm timestamp)
diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result
index 65ebbd71c39..84cac386b57 100644
--- a/mysql-test/r/grant.result
+++ b/mysql-test/r/grant.result
@@ -13,8 +13,48 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3
GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost'
grant delete on mysqltest.* to mysqltest_1@localhost;
select * from mysql.user where user="mysqltest_1";
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0
+Host localhost
+User mysqltest_1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type SPECIFIED
+ssl_cipher EDH-RSA-DES-CBC3-SHA
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -44,15 +84,95 @@ delete from mysql.user where user='mysqltest_1';
flush privileges;
grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
select * from mysql.user where user="mysqltest_1";
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0
+Host localhost
+User mysqltest_1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 10
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
select * from mysql.user where user="mysqltest_1";
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0
+Host localhost
+User mysqltest_1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 10
+max_updates 20
+max_connections 30
+max_user_connections 0
+plugin
+authentication_string
show grants for mysqltest_1@localhost;
Grants for mysqltest_1@localhost
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
@@ -164,6 +284,7 @@ Warnings:
Warning 1364 Field 'ssl_cipher' doesn't have a default value
Warning 1364 Field 'x509_issuer' doesn't have a default value
Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
insert into mysql.db (host, db, user, select_priv) values
('localhost', 'a%', 'test11', 'Y'), ('localhost', 'ab%', 'test11', 'Y');
alter table mysql.db order by db asc;
@@ -625,16 +746,19 @@ show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost'
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
flush privileges;
show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost'
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop database ÂÄ;
revoke all privileges on ÂÄ.* from root@localhost;
show grants for root@localhost;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
set names latin1;
create user mysqltest_7@;
set password for mysqltest_7@ = password('systpass');
diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result
index 3032ca854bd..b5e82794658 100644
--- a/mysql-test/r/grant2.result
+++ b/mysql-test/r/grant2.result
@@ -11,7 +11,7 @@ grant create user on *.* to mysqltest_1@localhost;
create user mysqltest_2@localhost;
grant select on `my\_1`.* to mysqltest_2@localhost;
grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
-ERROR 42000: You must have privileges to update tables in the mysql database to be able to change passwords for others
+ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysql'
grant update on mysql.* to mysqltest_1@localhost;
grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
grant select on `my\_1`.* to mysqltest_3@localhost;
@@ -287,6 +287,7 @@ Warnings:
Warning 1364 Field 'ssl_cipher' doesn't have a default value
Warning 1364 Field 'x509_issuer' doesn't have a default value
Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
create user mysqltest_A@'%';
rename user mysqltest_B@'%' to mysqltest_C@'%';
drop user mysqltest_C@'%';
@@ -334,7 +335,7 @@ delete from mysql.user where user like 'mysqltest\_1';
flush privileges;
drop database mysqltest_1;
set password = password("changed");
-ERROR 42000: Access denied for user ''@'localhost' to database 'mysql'
+ERROR 42000: Can't find any matching row in the user table
lock table mysql.user write;
flush privileges;
grant all on *.* to 'mysqltest_1'@'localhost';
@@ -354,6 +355,7 @@ Warnings:
Warning 1364 Field 'ssl_cipher' doesn't have a default value
Warning 1364 Field 'x509_issuer' doesn't have a default value
Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
INSERT INTO mysql.db (host, db, user, select_priv) VALUES
('%','TESTDB','mysqltest_1','Y');
FLUSH PRIVILEGES;
diff --git a/mysql-test/r/grant_cache_no_prot.result b/mysql-test/r/grant_cache_no_prot.result
index 32bb9cce90e..019edb72086 100644
--- a/mysql-test/r/grant_cache_no_prot.result
+++ b/mysql-test/r/grant_cache_no_prot.result
@@ -7,9 +7,11 @@ flush status;
show grants for current_user;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
create database if not exists mysqltest;
create table mysqltest.t1 (a int,b int,c int);
create table mysqltest.t2 (a int,b int,c int);
diff --git a/mysql-test/r/grant_cache_ps_prot.result b/mysql-test/r/grant_cache_ps_prot.result
index 281468ee2e1..e95a858fd9a 100644
--- a/mysql-test/r/grant_cache_ps_prot.result
+++ b/mysql-test/r/grant_cache_ps_prot.result
@@ -7,9 +7,11 @@ flush status;
show grants for current_user;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
create database if not exists mysqltest;
create table mysqltest.t1 (a int,b int,c int);
create table mysqltest.t2 (a int,b int,c int);
diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result
index 21b42577a11..aa47b8c437e 100644
--- a/mysql-test/r/information_schema.result
+++ b/mysql-test/r/information_schema.result
@@ -88,6 +88,7 @@ host
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
@@ -684,6 +685,7 @@ Alter_routine_priv select,insert,update,references
max_questions select,insert,update,references
max_connections select,insert,update,references
max_user_connections select,insert,update,references
+authentication_string select,insert,update,references
use test;
create function sub1(i int) returns int
return i+1;
@@ -870,7 +872,7 @@ AND table_name not like 'ndb%' AND table_name not like 'innodb_%'
GROUP BY TABLE_SCHEMA;
table_schema count(*)
information_schema 30
-mysql 22
+mysql 23
create table t1 (i int, j int);
create trigger trg1 before insert on t1 for each row
begin
diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result
index 5d9be85a48a..10cc6d71eae 100644
--- a/mysql-test/r/log_tables_upgrade.result
+++ b/mysql-test/r/log_tables_upgrade.result
@@ -29,6 +29,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.renamed_general_log OK
mysql.servers OK
mysql.slow_log
diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result
index 58f6ffd4040..3d5a6cfebdb 100644
--- a/mysql-test/r/mysql_upgrade.result
+++ b/mysql-test/r/mysql_upgrade.result
@@ -17,6 +17,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
@@ -49,6 +50,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
@@ -81,6 +83,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
@@ -115,6 +118,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
@@ -153,6 +157,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
@@ -194,6 +199,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
Error : You can't use locks with log tables.
diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result
index 06175955d7f..c51d71510f4 100644
--- a/mysql-test/r/mysqlcheck.result
+++ b/mysql-test/r/mysqlcheck.result
@@ -18,6 +18,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
note : The storage engine for the table doesn't support optimize
@@ -43,6 +44,7 @@ mysql.ndb_binlog_index OK
mysql.plugin OK
mysql.proc OK
mysql.procs_priv OK
+mysql.proxy_priv OK
mysql.servers OK
mysql.slow_log
note : The storage engine for the table doesn't support optimize
diff --git a/mysql-test/r/plugin_auth.result b/mysql-test/r/plugin_auth.result
new file mode 100644
index 00000000000..8a851af0ecb
--- /dev/null
+++ b/mysql-test/r/plugin_auth.result
@@ -0,0 +1,212 @@
+SELECT PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_DESCRIPTION
+FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server';
+PLUGIN_STATUS ACTIVE
+PLUGIN_TYPE AUTHENTICATION
+PLUGIN_DESCRIPTION plugin API test plugin
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+SELECT plugin,authentication_string FROM mysql.user WHERE User='plug';
+plugin authentication_string
+test_plugin_server plug_dest
+## test plugin auth
+ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES)
+GRANT PROXY ON plug_dest TO plug;
+select USER(),CURRENT_USER();
+USER() CURRENT_USER()
+plug@localhost plug_dest@%
+## test SET PASSWORD
+SET PASSWORD = PASSWORD('plug_dest');
+Warnings:
+Note 1698 SET PASSWORD has no significance for users authenticating via plugins
+## test bad credentials
+ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES)
+## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD
+## test correct default plugin
+select USER(),CURRENT_USER();
+USER() CURRENT_USER()
+plug@localhost plug@%
+## test no_auto_create_user sql mode with plugin users
+SET @@sql_mode=no_auto_create_user;
+GRANT INSERT ON TEST.* TO grant_user IDENTIFIED WITH 'test_plugin_server';
+SET @@sql_mode=default;
+DROP USER grant_user;
+## test utf-8 user name
+CREATE USER `Ÿ` IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+GRANT PROXY ON plug_dest TO `Ÿ`;
+select USER(),CURRENT_USER();
+USER() CURRENT_USER()
+Ÿ@localhost plug_dest@%
+DROP USER `Ÿ`;
+## test GRANT ... IDENTIFIED WITH/BY ...
+CREATE DATABASE test_grant_db;
+# create new user via GRANT WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+GRANT PROXY ON plug_dest TO new_grant_user;
+select USER(),CURRENT_USER();
+USER() CURRENT_USER()
+new_grant_user@localhost plug_dest@%
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+REVOKE ALL PRIVILEGES ON test_grant_db.* FROM new_grant_user;
+# try re-create existing user via GRANT IDENTIFIED BY
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+IDENTIFIED BY 'unused_password';
+# make sure password doesn't take precendence
+ERROR 28000: Access denied for user 'new_grant_user'@'localhost' (using password: YES)
+#make sure plugin auth still available
+select USER(),CURRENT_USER();
+USER() CURRENT_USER()
+new_grant_user@localhost plug_dest@%
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+DROP USER new_grant_user;
+# try re-create existing user via GRANT IDENTIFIED WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug already exists
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug_dest
+IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug_dest already exists
+REVOKE SELECT on test_grant_db.* FROM joro
+INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'' at line 2
+REVOKE SELECT on test_grant_db.* FROM joro
+INDENTIFIED BY 'plug_dest_passwd';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY 'plug_dest_passwd'' at line 2
+REVOKE SELECT on test_grant_db.* FROM joro
+INDENTIFIED BY PASSWORD 'plug_dest_passwd';
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY PASSWORD 'plug_dest_passwd'' at line 2
+DROP DATABASE test_grant_db;
+## GRANT PROXY tests
+CREATE USER grant_plug IDENTIFIED WITH 'test_plugin_server'
+AS 'grant_plug_dest';
+CREATE USER grant_plug_dest IDENTIFIED BY 'grant_plug_dest_passwd';
+CREATE USER grant_plug_dest2 IDENTIFIED BY 'grant_plug_dest_passwd2';
+# ALL PRIVILEGES doesn't include PROXY
+GRANT ALL PRIVILEGES ON *.* TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug'@'localhost' (using password: YES)
+GRANT ALL PRIVILEGES,PROXY ON grant_plug_dest TO grant_plug;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PROXY ON grant_plug_dest TO grant_plug' at line 1
+this should fail : can't combine PROXY
+GRANT ALL SELECT,PROXY ON grant_plug_dest TO grant_plug;
+ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT,PROXY ON grant_plug_dest TO grant_plug' at line 1
+# this should fail : no such grant
+REVOKE PROXY ON grant_plug_dest FROM grant_plug;
+ERROR 42000: There is no such grant defined for user 'grant_plug' on host '%'
+in grant_plug_dest_con
+## testing what an ordinary user can grant
+this should fail : no rights to grant all
+GRANT PROXY ON ''@'' TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should fail : not the same user
+GRANT PROXY ON grant_plug TO grant_plug_dest;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should fail : same user, but on a different host
+GRANT PROXY ON grant_plug_dest TO grant_plug;
+ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost'
+this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug_dest2;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug_dest2;
+this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug WITH GRANT OPTION;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug;
+this should fail : can't create users
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug@localhost;
+ERROR 42000: You are not allowed to create a user with GRANT
+in default connection
+# test what root can grant
+should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO grant_plug;
+REVOKE PROXY ON ''@'' FROM grant_plug;
+should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test'
+WITH GRANT OPTION;
+need USAGE : PROXY doesn't contain it.
+GRANT USAGE on *.* TO proxy_admin;
+in proxy_admin_con;
+should work : proxy_admin has proxy to ''@''
+GRANT PROXY ON future_user TO grant_plug;
+in default connection
+SHOW GRANTS FOR grant_plug;
+Grants for grant_plug@%
+GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION
+GRANT PROXY ON 'future_user'@'%' TO 'grant_plug'@'%'
+REVOKE PROXY ON future_user FROM grant_plug;
+SHOW GRANTS FOR grant_plug;
+Grants for grant_plug@%
+GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION
+## testing drop user
+CREATE USER test_drop@localhost;
+GRANT PROXY ON future_user TO test_drop@localhost;
+SHOW GRANTS FOR test_drop@localhost;
+Grants for test_drop@localhost
+GRANT USAGE ON *.* TO 'test_drop'@'localhost'
+GRANT PROXY ON 'future_user'@'%' TO 'test_drop'@'localhost'
+DROP USER test_drop@localhost;
+SELECT * FROM mysql.proxy_priv WHERE Host = 'test_drop' AND User = 'localhost';
+Host User Proxied_Host Proxied_User With_Grant
+DROP USER proxy_admin;
+DROP USER grant_plug,grant_plug_dest,grant_plug_dest2;
+## END GRANT PROXY tests
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## @@proxy_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.proxy_user;
+USER() CURRENT_USER() @@LOCAL.proxy_user
+root@localhost root@localhost NULL
+SELECT @@GLOBAL.proxy_user;
+ERROR HY000: Variable 'proxy_user' is a SESSION variable
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+NULL
+SET GLOBAL proxy_user = 'test';
+ERROR HY000: Variable 'proxy_user' is a read only variable
+SET LOCAL proxy_user = 'test';
+ERROR HY000: Variable 'proxy_user' is a read only variable
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+NULL
+# in connection plug_con
+SELECT @@LOCAL.proxy_user;
+@@LOCAL.proxy_user
+'plug'@'%'
+# in connection default
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## END @@proxy_user tests
+## @@external_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.external_user;
+USER() CURRENT_USER() @@LOCAL.external_user
+root@localhost root@localhost NULL
+SELECT @@GLOBAL.external_user;
+ERROR HY000: Variable 'external_user' is a SESSION variable
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+NULL
+SET GLOBAL external_user = 'test';
+ERROR HY000: Variable 'external_user' is a read only variable
+SET LOCAL external_user = 'test';
+ERROR HY000: Variable 'external_user' is a read only variable
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+NULL
+# in connection plug_con
+SELECT @@LOCAL.external_user;
+@@LOCAL.external_user
+'plug'@'%'
+# in connection default
+## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+## END @@external_user tests
diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result
index 0e75ebd57ec..cf3594f32a8 100644
--- a/mysql-test/r/ps.result
+++ b/mysql-test/r/ps.result
@@ -1194,13 +1194,13 @@ SET @aux= "SELECT COUNT(*)
prepare my_stmt from @aux;
execute my_stmt;
COUNT(*)
-40
+42
execute my_stmt;
COUNT(*)
-40
+42
execute my_stmt;
COUNT(*)
-40
+42
deallocate prepare my_stmt;
drop procedure if exists p1|
drop table if exists t1|
diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result
index e4170cc3a49..7da95416e29 100644
--- a/mysql-test/r/sp_notembedded.result
+++ b/mysql-test/r/sp_notembedded.result
@@ -9,9 +9,11 @@ end|
call bug4902()|
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
call bug4902()|
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop procedure bug4902|
drop procedure if exists bug4902_2|
create procedure bug4902_2()
@@ -206,9 +208,11 @@ create procedure 15298_2 () sql security definer show grants;
call 15298_1();
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
call 15298_2();
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop user mysqltest_1@localhost;
drop procedure 15298_1;
drop procedure 15298_2;
@@ -245,6 +249,8 @@ max_updates, max_connections, max_user_connections)
VALUES('%', 'mysqltest_1', password(''), 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'N',
'N', 'N', 'N', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'Y', 'Y', 'N', '',
'', '', '', '0', '0', '0', '0');
+Warnings:
+Warning 1364 Field 'authentication_string' doesn't have a default value
FLUSH PRIVILEGES;
CREATE PROCEDURE p1(i INT) BEGIN END;
DROP PROCEDURE p1;
diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result
index 679e50fcad9..e82cf229912 100644
--- a/mysql-test/r/system_mysql_db.result
+++ b/mysql-test/r/system_mysql_db.result
@@ -14,6 +14,7 @@ ndb_binlog_index
plugin
proc
procs_priv
+proxy_priv
servers
slow_log
tables_priv
@@ -119,6 +120,8 @@ user CREATE TABLE `user` (
`max_updates` int(11) unsigned NOT NULL DEFAULT '0',
`max_connections` int(11) unsigned NOT NULL DEFAULT '0',
`max_user_connections` int(11) unsigned NOT NULL DEFAULT '0',
+ `plugin` char(60) COLLATE utf8_bin NOT NULL DEFAULT '',
+ `authentication_string` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
show create table func;
diff --git a/mysql-test/suite/federated/federated.result b/mysql-test/suite/federated/federated.result
index db4ffc38213..4cd7b416621 100644
--- a/mysql-test/suite/federated/federated.result
+++ b/mysql-test/suite/federated/federated.result
@@ -60,7 +60,7 @@ CREATE TABLE federated.t1 (
ENGINE="FEDERATED" DEFAULT CHARSET=latin1
CONNECTION='mysql://user:pass@127.0.0.1:SLAVE_PORT/federated/t1';
SELECT * FROM federated.t1;
-ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: YES)
+ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: NO)
DROP TABLE federated.t1;
CREATE TABLE federated.t1 (
`id` int(20) NOT NULL,
diff --git a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
index bc3f7daf5bc..3c77ca3dedc 100644
--- a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result
@@ -580,6 +580,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
index 1ab2b3513f0..767f9e47f13 100644
--- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result
@@ -134,6 +134,11 @@ def mysql procs_priv Routine_name 4 NO char 64 192 NULL NULL utf8 utf8_general_
def mysql procs_priv Routine_type 5 NULL NO enum 9 27 NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE') PRI select,insert,update,references
def mysql procs_priv Timestamp 8 CURRENT_TIMESTAMP NO timestamp NULL NULL NULL NULL NULL NULL timestamp on update CURRENT_TIMESTAMP select,insert,update,references
def mysql procs_priv User 3 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
+def mysql proxy_priv Host 1 NO char 60 180 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references
+def mysql proxy_priv Proxied_Host 3 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
+def mysql proxy_priv Proxied_User 4 NO char 60 180 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references
+def mysql proxy_priv User 2 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references
+def mysql proxy_priv With_Grant 5 0 NO tinyint NULL NULL 3 0 NULL NULL tinyint(1) select,insert,update,references
def mysql servers Db 3 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
def mysql servers Host 2 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
def mysql servers Owner 9 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references
@@ -178,6 +183,7 @@ def mysql time_zone_transition_type Time_zone_id 1 NULL NO int NULL NULL 10 0 NU
def mysql time_zone_transition_type Transition_type_id 2 NULL NO int NULL NULL 10 0 NULL NULL int(10) unsigned PRI select,insert,update,references
def mysql user Alter_priv 17 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Alter_routine_priv 28 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
+def mysql user authentication_string 42 NULL NO text 65535 65535 NULL NULL utf8 utf8_bin text select,insert,update,references
def mysql user Create_priv 8 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Create_routine_priv 27 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Create_tablespace_priv 32 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
@@ -199,6 +205,7 @@ def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL int(11) unsign
def mysql user max_updates 38 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user max_user_connections 40 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references
def mysql user Password 3 NO char 41 41 NULL NULL latin1 latin1_bin char(41) select,insert,update,references
+def mysql user plugin 41 NO char 60 180 NULL NULL utf8 utf8_bin char(60) select,insert,update,references
def mysql user Process_priv 12 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user References_priv 15 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
def mysql user Reload_priv 10 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references
@@ -418,6 +425,11 @@ NULL mysql proc modified timestamp NULL NULL NULL NULL timestamp
3.0000 mysql procs_priv Grantor char 77 231 utf8 utf8_bin char(77)
3.0000 mysql procs_priv Proc_priv set 27 81 utf8 utf8_general_ci set('Execute','Alter Routine','Grant')
NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp
+3.0000 mysql proxy_priv Host char 60 180 utf8 utf8_bin char(60)
+3.0000 mysql proxy_priv User char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql proxy_priv Proxied_Host char 16 48 utf8 utf8_bin char(16)
+3.0000 mysql proxy_priv Proxied_User char 60 180 utf8 utf8_bin char(60)
+NULL mysql proxy_priv With_Grant tinyint NULL NULL NULL NULL tinyint(1)
3.0000 mysql servers Server_name char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Host char 64 192 utf8 utf8_general_ci char(64)
3.0000 mysql servers Db char 64 192 utf8 utf8_general_ci char(64)
@@ -500,3 +512,5 @@ NULL mysql user max_questions int NULL NULL NULL NULL int(11) unsigned
NULL mysql user max_updates int NULL NULL NULL NULL int(11) unsigned
NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned
NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) unsigned
+3.0000 mysql user plugin char 60 180 utf8 utf8_bin char(60)
+1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text
diff --git a/mysql-test/suite/funcs_1/r/is_key_column_usage.result b/mysql-test/suite/funcs_1/r/is_key_column_usage.result
index a81452b7927..2e50a0c36bf 100644
--- a/mysql-test/suite/funcs_1/r/is_key_column_usage.result
+++ b/mysql-test/suite/funcs_1/r/is_key_column_usage.result
@@ -106,6 +106,10 @@ def mysql PRIMARY def mysql procs_priv Db
def mysql PRIMARY def mysql procs_priv User
def mysql PRIMARY def mysql procs_priv Routine_name
def mysql PRIMARY def mysql procs_priv Routine_type
+def mysql PRIMARY def mysql proxy_priv Host
+def mysql PRIMARY def mysql proxy_priv User
+def mysql PRIMARY def mysql proxy_priv Proxied_Host
+def mysql PRIMARY def mysql proxy_priv Proxied_User
def mysql PRIMARY def mysql servers Server_name
def mysql PRIMARY def mysql tables_priv Host
def mysql PRIMARY def mysql tables_priv Db
diff --git a/mysql-test/suite/funcs_1/r/is_statistics.result b/mysql-test/suite/funcs_1/r/is_statistics.result
index 873b328dc2d..0c43883789b 100644
--- a/mysql-test/suite/funcs_1/r/is_statistics.result
+++ b/mysql-test/suite/funcs_1/r/is_statistics.result
@@ -118,6 +118,10 @@ def mysql procs_priv mysql PRIMARY
def mysql procs_priv mysql PRIMARY
def mysql procs_priv mysql PRIMARY
def mysql procs_priv mysql Grantor
+def mysql proxy_priv mysql PRIMARY
+def mysql proxy_priv mysql PRIMARY
+def mysql proxy_priv mysql PRIMARY
+def mysql proxy_priv mysql PRIMARY
def mysql servers mysql PRIMARY
def mysql tables_priv mysql PRIMARY
def mysql tables_priv mysql PRIMARY
diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
index a1fa0cac9f2..584bbeb7af5 100644
--- a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result
@@ -40,6 +40,10 @@ def mysql procs_priv 0 mysql PRIMARY 2 Db A #CARD# NULL NULL BTREE
def mysql procs_priv 0 mysql PRIMARY 3 User A #CARD# NULL NULL BTREE
def mysql procs_priv 0 mysql PRIMARY 4 Routine_name A #CARD# NULL NULL BTREE
def mysql procs_priv 0 mysql PRIMARY 5 Routine_type A #CARD# NULL NULL BTREE
+def mysql proxy_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
+def mysql proxy_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE
+def mysql proxy_priv 0 mysql PRIMARY 3 Proxied_Host A #CARD# NULL NULL BTREE
+def mysql proxy_priv 0 mysql PRIMARY 4 Proxied_User A #CARD# NULL NULL BTREE
def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE
def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE
def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE
diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints.result b/mysql-test/suite/funcs_1/r/is_table_constraints.result
index 7f1c83a8ea5..d4d2c38c9ba 100644
--- a/mysql-test/suite/funcs_1/r/is_table_constraints.result
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints.result
@@ -73,6 +73,7 @@ def mysql PRIMARY mysql ndb_binlog_index
def mysql PRIMARY mysql plugin
def mysql PRIMARY mysql proc
def mysql PRIMARY mysql procs_priv
+def mysql PRIMARY mysql proxy_priv
def mysql PRIMARY mysql servers
def mysql PRIMARY mysql tables_priv
def mysql PRIMARY mysql time_zone
diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
index 8b7ac6994c1..38e9c9034c9 100644
--- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result
@@ -23,6 +23,7 @@ def mysql PRIMARY mysql ndb_binlog_index PRIMARY KEY
def mysql PRIMARY mysql plugin PRIMARY KEY
def mysql PRIMARY mysql proc PRIMARY KEY
def mysql PRIMARY mysql procs_priv PRIMARY KEY
+def mysql PRIMARY mysql proxy_priv PRIMARY KEY
def mysql PRIMARY mysql servers PRIMARY KEY
def mysql PRIMARY mysql tables_priv PRIMARY KEY
def mysql PRIMARY mysql time_zone PRIMARY KEY
diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql.result b/mysql-test/suite/funcs_1/r/is_tables_mysql.result
index 0945401ba43..ae512327807 100644
--- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result
+++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result
@@ -336,6 +336,29 @@ user_comment Procedure privileges
Separator -----------------------------------------------------
TABLE_CATALOG def
TABLE_SCHEMA mysql
+TABLE_NAME proxy_priv
+TABLE_TYPE BASE TABLE
+ENGINE MyISAM
+VERSION 10
+ROW_FORMAT Fixed
+TABLE_ROWS #TBLR#
+AVG_ROW_LENGTH #ARL#
+DATA_LENGTH #DL#
+MAX_DATA_LENGTH #MDL#
+INDEX_LENGTH #IL#
+DATA_FREE #DF#
+AUTO_INCREMENT NULL
+CREATE_TIME #CRT#
+UPDATE_TIME #UT#
+CHECK_TIME #CT#
+TABLE_COLLATION utf8_bin
+CHECKSUM NULL
+CREATE_OPTIONS #CO#
+TABLE_COMMENT #TC#
+user_comment User proxy privileges
+Separator -----------------------------------------------------
+TABLE_CATALOG def
+TABLE_SCHEMA mysql
TABLE_NAME servers
TABLE_TYPE BASE TABLE
ENGINE MyISAM
diff --git a/mysql-test/suite/funcs_1/r/is_user_privileges.result b/mysql-test/suite/funcs_1/r/is_user_privileges.result
index 8f68f8c802d..1ec1ffc4ce1 100644
--- a/mysql-test/suite/funcs_1/r/is_user_privileges.result
+++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result
@@ -69,46 +69,436 @@ GRANT UPDATE ON *.* TO 'testuser2'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
#
# Add GRANT OPTION db_datadict.* to testuser1;
GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
# Establish connection testuser1 (user=testuser1)
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -123,46 +513,436 @@ GRANT SELECT ON *.* TO 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def SELECT NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE SELECT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv Y
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
#
# Here <SELECT YES> is shown correctly for testuser1;
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def SELECT YES
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE SELECT
+IS_GRANTABLE YES
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 Y N N N N N N N N N Y N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv Y
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv Y
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def SELECT YES
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE SELECT
+IS_GRANTABLE YES
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 Y N N N N N N N N N Y N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv Y
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv Y
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
SHOW GRANTS;
Grants for testuser1@localhost
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION
@@ -172,9 +952,14 @@ GRANT SELECT ON `mysql`.`user` TO 'testuser1'@'localhost'
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
ERROR 42000: SELECT command denied to user 'testuser2'@'localhost' for table 'user'
@@ -185,8 +970,10 @@ GRANT INSERT, UPDATE ON *.* TO 'testuser2'@'localhost'
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
ERROR 42000: SELECT command denied to user 'testuser3'@'localhost' for table 'user'
@@ -200,23 +987,158 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -228,8 +1150,10 @@ ERROR 42000: CREATE command denied to user 'testuser1'@'localhost' for table 'tb
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -246,29 +1170,286 @@ GRANT SELECT ON mysql.user TO 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -280,14 +1461,138 @@ USE db_datadict;
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
SHOW GRANTS;
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
@@ -302,23 +1607,158 @@ REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
-'testuser2'@'localhost' def INSERT NO
-'testuser2'@'localhost' def UPDATE NO
-'testuser3'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE INSERT
+IS_GRANTABLE NO
+GRANTEE 'testuser2'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE UPDATE
+IS_GRANTABLE NO
+GRANTEE 'testuser3'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
-Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections
-localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
-localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0
+Host localhost
+User testuser1
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser2
+Password
+Select_priv N
+Insert_priv Y
+Update_priv Y
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
+Host localhost
+User testuser3
+Password
+Select_priv N
+Insert_priv N
+Update_priv N
+Delete_priv N
+Create_priv N
+Drop_priv N
+Reload_priv N
+Shutdown_priv N
+Process_priv N
+File_priv N
+Grant_priv N
+References_priv N
+Index_priv N
+Alter_priv N
+Show_db_priv N
+Super_priv N
+Create_tmp_table_priv N
+Lock_tables_priv N
+Execute_priv N
+Repl_slave_priv N
+Repl_client_priv N
+Create_view_priv N
+Show_view_priv N
+Create_routine_priv N
+Alter_routine_priv N
+Create_user_priv N
+Event_priv N
+Trigger_priv N
+Create_tablespace_priv N
+ssl_type
+ssl_cipher
+x509_issuer
+x509_subject
+max_questions 0
+max_updates 0
+max_connections 0
+max_user_connections 0
+plugin
+authentication_string
# Switch to connection testuser1
SELECT * FROM information_schema.user_privileges
WHERE grantee LIKE '''testuser%'''
ORDER BY grantee, table_catalog, privilege_type;
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
ERROR 42000: SELECT command denied to user 'testuser1'@'localhost' for table 'user'
@@ -341,31 +1781,36 @@ DROP DATABASE IF EXISTS db_datadict;
########################################################################################
SELECT * FROM information_schema.user_privileges
WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
SHOW GRANTS FOR 'testuser1'@'localhost';
ERROR 42000: There is no such grant defined for user 'testuser1' on host 'localhost'
DROP USER 'testuser1'@'localhost';
CREATE USER 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def USAGE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE USAGE
+IS_GRANTABLE NO
SHOW GRANTS FOR 'testuser1'@'localhost';
Grants for testuser1@localhost
GRANT USAGE ON *.* TO 'testuser1'@'localhost'
GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
-'testuser1'@'localhost' def SELECT NO
-'testuser1'@'localhost' def FILE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE SELECT
+IS_GRANTABLE NO
+GRANTEE 'testuser1'@'localhost'
+TABLE_CATALOG def
+PRIVILEGE_TYPE FILE
+IS_GRANTABLE NO
SHOW GRANTS FOR 'testuser1'@'localhost';
Grants for testuser1@localhost
GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost'
DROP USER 'testuser1'@'localhost';
SELECT * FROM information_schema.user_privileges
WHERE grantee = '''testuser1''@''localhost''';
-GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE
SHOW GRANTS FOR 'testuser1'@'localhost';
ERROR 42000: There is no such grant defined for user 'testuser1' on host 'localhost'
########################################################################
diff --git a/mysql-test/suite/funcs_1/r/memory_trig_03e.result b/mysql-test/suite/funcs_1/r/memory_trig_03e.result
index cfbee5f27b7..e8261c93238 100644
--- a/mysql-test/suite/funcs_1/r/memory_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/memory_trig_03e.result
@@ -581,6 +581,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/myisam_trig_03e.result b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
index d21bd176810..8f9b5e6c174 100644
--- a/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
+++ b/mysql-test/suite/funcs_1/r/myisam_trig_03e.result
@@ -581,6 +581,7 @@ root@localhost
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
drop trigger trg1_1;
use priv_db;
diff --git a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
index ed9e9cce054..ce22b9ca8e0 100644
--- a/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_no_prot.result
@@ -65,6 +65,7 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_
SHOW GRANTS;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
CREATE INDEX i_processlist ON processlist (user);
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
DROP TABLE processlist;
diff --git a/mysql-test/suite/funcs_1/r/processlist_priv_ps.result b/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
index db1b385513c..891071269f9 100644
--- a/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
+++ b/mysql-test/suite/funcs_1/r/processlist_priv_ps.result
@@ -65,6 +65,7 @@ ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_
SHOW GRANTS;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
CREATE INDEX i_processlist ON processlist (user);
ERROR 42000: Access denied for user 'root'@'localhost' to database 'information_schema'
DROP TABLE processlist;
diff --git a/mysql-test/suite/funcs_1/t/is_user_privileges.test b/mysql-test/suite/funcs_1/t/is_user_privileges.test
index 253323af9a7..0f0e398d75b 100644
--- a/mysql-test/suite/funcs_1/t/is_user_privileges.test
+++ b/mysql-test/suite/funcs_1/t/is_user_privileges.test
@@ -104,20 +104,26 @@ ORDER BY grantee, table_catalog, privilege_type;
let $my_select2= SELECT * FROM mysql.user
WHERE user LIKE 'testuser%' ORDER BY host, user;
let $my_show= SHOW GRANTS;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo #
--echo # Add GRANT OPTION db_datadict.* to testuser1;
GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo # Establish connection testuser1 (user=testuser1)
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
connect (testuser1, localhost, testuser1, , db_datadict);
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
eval $my_show;
--echo
@@ -128,36 +134,46 @@ connection default;
GRANT SELECT ON *.* TO 'testuser1'@'localhost';
--echo #
--echo # Here <SELECT NO> is shown correctly for testuser1;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
--echo #
--echo # Here <SELECT YES> is shown correctly for testuser1;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo # Switch to connection testuser1
# check that this appears
connection testuser1;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
eval $my_show;
--echo # Establish connection testuser2 (user=testuser2)
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
connect (testuser2, localhost, testuser2, , db_datadict);
+--vertical_results
eval $my_select1;
--error ER_TABLEACCESS_DENIED_ERROR
eval $my_select2;
+--horizontal_results
eval $my_show;
--echo # Establish connection testuser3 (user=testuser3)
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
connect (testuser3, localhost, testuser3, , test);
+--vertical_results
eval $my_select1;
--error ER_TABLEACCESS_DENIED_ERROR
eval $my_select2;
+--horizontal_results
eval $my_show;
--echo
@@ -165,23 +181,29 @@ eval $my_show;
--echo # Switch to connection default
connection default;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo # Switch to connection testuser1
# check for changes
connection testuser1;
+--vertical_results
eval $my_select1;
--error ER_TABLEACCESS_DENIED_ERROR
eval $my_select2;
+--horizontal_results
eval $my_show;
# OK, testuser1 has no privs here
--error ER_TABLEACCESS_DENIED_ERROR
CREATE TABLE db_datadict.tb_55 ( c1 TEXT );
+--vertical_results
eval $my_select1;
--error ER_TABLEACCESS_DENIED_ERROR
eval $my_select2;
+--horizontal_results
eval $my_show;
# OK, testuser1 has no privs here
--error ER_TABLEACCESS_DENIED_ERROR
@@ -193,13 +215,17 @@ CREATE TABLE db_datadict.tb_66 ( c1 TEXT );
connection default;
GRANT ALL ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION;
GRANT SELECT ON mysql.user TO 'testuser1'@'localhost';
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo # Switch to connection testuser1
connection testuser1;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
eval $my_show;
# OK, testuser1 has no privs here
@@ -208,8 +234,10 @@ CREATE TABLE db_datadict.tb_56 ( c1 TEXT );
# using 'USE' lets the server read the privileges new, so now the CREATE works
USE db_datadict;
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
eval $my_show;
--replace_result $other_engine_type <other_engine_type>
eval
@@ -221,15 +249,19 @@ ENGINE = $other_engine_type;
--echo # Switch to connection default
connection default;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'testuser1'@'localhost';
+--vertical_results
eval $my_select1;
eval $my_select2;
+--horizontal_results
--echo # Switch to connection testuser1
# check for changes
connection testuser1;
+--vertical_results
eval $my_select1;
--error ER_TABLEACCESS_DENIED_ERROR
eval $my_select2;
+--horizontal_results
eval $my_show;
# WORKS, as the existing old privileges are used!
--replace_result $other_engine_type <other_engine_type>
@@ -273,19 +305,27 @@ DROP DATABASE IF EXISTS db_datadict;
let $my_select = SELECT * FROM information_schema.user_privileges
WHERE grantee = '''testuser1''@''localhost''';
let $my_show = SHOW GRANTS FOR 'testuser1'@'localhost';
+--vertical_results
eval $my_select;
+--horizontal_results
--error ER_NONEXISTING_GRANT
eval $my_show;
--error 0,ER_CANNOT_USER
DROP USER 'testuser1'@'localhost';
CREATE USER 'testuser1'@'localhost';
+--vertical_results
eval $my_select;
+--horizontal_results
eval $my_show;
GRANT SELECT, FILE ON *.* TO 'testuser1'@'localhost';
+--vertical_results
eval $my_select;
+--horizontal_results
eval $my_show;
DROP USER 'testuser1'@'localhost';
+--vertical_results
eval $my_select;
+--horizontal_results
--error ER_NONEXISTING_GRANT
eval $my_show;
diff --git a/mysql-test/suite/perfschema/r/column_privilege.result b/mysql-test/suite/perfschema/r/column_privilege.result
index 7bbc59ac452..ac6030690dd 100644
--- a/mysql-test/suite/perfschema/r/column_privilege.result
+++ b/mysql-test/suite/perfschema/r/column_privilege.result
@@ -1,6 +1,7 @@
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
grant usage on *.* to 'pfs_user_5'@localhost with GRANT OPTION;
grant SELECT(thread_id, event_id) on performance_schema.EVENTS_WAITS_CURRENT
to 'pfs_user_5'@localhost;
diff --git a/mysql-test/suite/perfschema/r/pfs_upgrade.result b/mysql-test/suite/perfschema/r/pfs_upgrade.result
index 2d0d82ef19c..9e2018f5c11 100644
--- a/mysql-test/suite/perfschema/r/pfs_upgrade.result
+++ b/mysql-test/suite/perfschema/r/pfs_upgrade.result
@@ -26,7 +26,7 @@ ERROR 1050 (42S01) at line 445: Table 'SETUP_CONSUMERS' already exists
ERROR 1050 (42S01) at line 462: Table 'SETUP_INSTRUMENTS' already exists
ERROR 1050 (42S01) at line 482: Table 'SETUP_OBJECTS' already exists
ERROR 1050 (42S01) at line 498: Table 'SETUP_TIMERS' already exists
-ERROR 1644 (HY000) at line 1138: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1142: Unexpected content found in the performance_schema database.
FATAL ERROR: Upgrade failed
show tables like "user_table";
Tables_in_performance_schema (user_table)
@@ -57,7 +57,7 @@ ERROR 1050 (42S01) at line 445: Table 'SETUP_CONSUMERS' already exists
ERROR 1050 (42S01) at line 462: Table 'SETUP_INSTRUMENTS' already exists
ERROR 1050 (42S01) at line 482: Table 'SETUP_OBJECTS' already exists
ERROR 1050 (42S01) at line 498: Table 'SETUP_TIMERS' already exists
-ERROR 1644 (HY000) at line 1138: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1142: Unexpected content found in the performance_schema database.
FATAL ERROR: Upgrade failed
show tables like "user_view";
Tables_in_performance_schema (user_view)
@@ -86,7 +86,7 @@ ERROR 1050 (42S01) at line 445: Table 'SETUP_CONSUMERS' already exists
ERROR 1050 (42S01) at line 462: Table 'SETUP_INSTRUMENTS' already exists
ERROR 1050 (42S01) at line 482: Table 'SETUP_OBJECTS' already exists
ERROR 1050 (42S01) at line 498: Table 'SETUP_TIMERS' already exists
-ERROR 1644 (HY000) at line 1138: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1142: Unexpected content found in the performance_schema database.
FATAL ERROR: Upgrade failed
select name from mysql.proc where db='performance_schema';
name
@@ -115,7 +115,7 @@ ERROR 1050 (42S01) at line 445: Table 'SETUP_CONSUMERS' already exists
ERROR 1050 (42S01) at line 462: Table 'SETUP_INSTRUMENTS' already exists
ERROR 1050 (42S01) at line 482: Table 'SETUP_OBJECTS' already exists
ERROR 1050 (42S01) at line 498: Table 'SETUP_TIMERS' already exists
-ERROR 1644 (HY000) at line 1138: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1142: Unexpected content found in the performance_schema database.
FATAL ERROR: Upgrade failed
select name from mysql.proc where db='performance_schema';
name
@@ -144,7 +144,7 @@ ERROR 1050 (42S01) at line 445: Table 'SETUP_CONSUMERS' already exists
ERROR 1050 (42S01) at line 462: Table 'SETUP_INSTRUMENTS' already exists
ERROR 1050 (42S01) at line 482: Table 'SETUP_OBJECTS' already exists
ERROR 1050 (42S01) at line 498: Table 'SETUP_TIMERS' already exists
-ERROR 1644 (HY000) at line 1138: Unexpected content found in the performance_schema database.
+ERROR 1644 (HY000) at line 1142: Unexpected content found in the performance_schema database.
FATAL ERROR: Upgrade failed
select name from mysql.event where db='performance_schema';
name
diff --git a/mysql-test/suite/perfschema/r/privilege.result b/mysql-test/suite/perfschema/r/privilege.result
index ddbc150a72a..4283b250cee 100644
--- a/mysql-test/suite/perfschema/r/privilege.result
+++ b/mysql-test/suite/perfschema/r/privilege.result
@@ -1,6 +1,7 @@
show grants;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
grant ALL on *.* to 'pfs_user_1'@localhost with GRANT OPTION;
grant ALL on performance_schema.* to 'pfs_user_2'@localhost
with GRANT OPTION;
diff --git a/mysql-test/suite/rpl/r/rpl_do_grant.result b/mysql-test/suite/rpl/r/rpl_do_grant.result
index 1cea2cfa9ad..ce0417cce07 100644
--- a/mysql-test/suite/rpl/r/rpl_do_grant.result
+++ b/mysql-test/suite/rpl/r/rpl_do_grant.result
@@ -207,6 +207,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
##############################################################
### Showing grants for both users: root and user49119 (master)
@@ -217,6 +218,7 @@ GRANT EXECUTE ON PROCEDURE `test`.`p1` TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
## This statement will make the revoke fail because root has no
## execute grant. However, it will still revoke the grant for
@@ -232,6 +234,7 @@ GRANT USAGE ON *.* TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
#############################################################
### Showing grants for both users: root and user49119 (slave)
@@ -242,6 +245,7 @@ GRANT USAGE ON *.* TO 'user49119'@'localhost'
SHOW GRANTS FOR CURRENT_USER;
Grants for root@localhost
GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION
+GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION
##############################################################
DROP TABLE t1;
DROP PROCEDURE p1;
diff --git a/mysql-test/suite/rpl/r/rpl_ignore_table.result b/mysql-test/suite/rpl/r/rpl_ignore_table.result
index e77be425270..a6c59918b93 100644
--- a/mysql-test/suite/rpl/r/rpl_ignore_table.result
+++ b/mysql-test/suite/rpl/r/rpl_ignore_table.result
@@ -34,6 +34,7 @@ Warnings:
Warning 1364 Field 'ssl_cipher' doesn't have a default value
Warning 1364 Field 'x509_issuer' doesn't have a default value
Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
GRANT SELECT ON *.* TO mysqltest6@localhost;
GRANT INSERT ON *.* TO mysqltest6@localhost;
GRANT INSERT ON test.* TO mysqltest6@localhost;
diff --git a/mysql-test/suite/rpl/r/rpl_stm_000001.result b/mysql-test/suite/rpl/r/rpl_stm_000001.result
index ba05d114a60..e1761da2338 100644
--- a/mysql-test/suite/rpl/r/rpl_stm_000001.result
+++ b/mysql-test/suite/rpl/r/rpl_stm_000001.result
@@ -66,6 +66,7 @@ Warnings:
Warning 1364 Field 'ssl_cipher' doesn't have a default value
Warning 1364 Field 'x509_issuer' doesn't have a default value
Warning 1364 Field 'x509_subject' doesn't have a default value
+Warning 1364 Field 'authentication_string' doesn't have a default value
select select_priv,user from mysql.user where user = _binary'blafasel2';
select_priv user
N blafasel2
diff --git a/mysql-test/suite/sys_vars/r/external_user_basic.result b/mysql-test/suite/sys_vars/r/external_user_basic.result
new file mode 100644
index 00000000000..7cb85fb612d
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/external_user_basic.result
@@ -0,0 +1,3 @@
+SELECT @@SESSION.EXTERNAL_USER FROM DUAL;
+@@SESSION.EXTERNAL_USER
+NULL
diff --git a/mysql-test/suite/sys_vars/r/proxy_user_basic.result b/mysql-test/suite/sys_vars/r/proxy_user_basic.result
new file mode 100644
index 00000000000..7cc62bd9665
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/proxy_user_basic.result
@@ -0,0 +1,3 @@
+SELECT @@SESSION.PROXY_USER FROM DUAL;
+@@SESSION.PROXY_USER
+NULL
diff --git a/mysql-test/suite/sys_vars/t/external_user_basic.test b/mysql-test/suite/sys_vars/t/external_user_basic.test
new file mode 100644
index 00000000000..3ba4c2d65a3
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/external_user_basic.test
@@ -0,0 +1 @@
+SELECT @@SESSION.EXTERNAL_USER FROM DUAL;
diff --git a/mysql-test/suite/sys_vars/t/proxy_user_basic.test b/mysql-test/suite/sys_vars/t/proxy_user_basic.test
new file mode 100644
index 00000000000..8c7e2d0d8a3
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/proxy_user_basic.test
@@ -0,0 +1 @@
+SELECT @@SESSION.PROXY_USER FROM DUAL;
diff --git a/mysql-test/t/change_user.test b/mysql-test/t/change_user.test
index 3ed798e8d36..ed2e1d05f86 100644
--- a/mysql-test/t/change_user.test
+++ b/mysql-test/t/change_user.test
@@ -1,4 +1,52 @@
#
+# functional change user tests
+#
+
+grant select on test.* to test_nopw;
+grant select on test.* to test_oldpw identified by password "09301740536db389";
+grant select on test.* to test_newpw identified by "newpw";
+
+select user(), current_user(), database();
+#
+# massaging the data for tests to pass in the embedded server,
+# that has authentication completely disabled.
+#
+
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+
+
+change_user test_nopw;
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_oldpw, oldpw;
+--replace_result <@> <test_oldpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_newpw, newpw;
+--replace_result <@> <test_newpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user root;
+--replace_result <@> <root@localhost> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+
+change_user test_nopw,,test;
+--replace_result <@> <test_nopw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_oldpw,oldpw,test;
+--replace_result <@> <test_oldpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user test_newpw,newpw,test;
+--replace_result <@> <test_newpw@%> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+change_user root,,test;
+--replace_result <@> <root@localhost> @> @localhost>
+select concat('<', user(), '>'), concat('<', current_user(), '>'), database();
+
+drop user test_nopw;
+drop user test_oldpw;
+drop user test_newpw;
+
+#
# Bug#20023 mysql_change_user() resets the value of SQL_BIG_SELECTS
#
diff --git a/mysql-test/t/grant.test b/mysql-test/t/grant.test
index de43d6a74b4..e73f45a6c53 100644
--- a/mysql-test/t/grant.test
+++ b/mysql-test/t/grant.test
@@ -29,7 +29,7 @@ flush privileges;
grant select on mysqltest.* to mysqltest_1@localhost require cipher "EDH-RSA-DES-CBC3-SHA";
show grants for mysqltest_1@localhost;
grant delete on mysqltest.* to mysqltest_1@localhost;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
show grants for mysqltest_1@localhost;
revoke delete on mysqltest.* from mysqltest_1@localhost;
show grants for mysqltest_1@localhost;
@@ -48,10 +48,10 @@ flush privileges;
delete from mysql.user where user='mysqltest_1';
flush privileges;
grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
show grants for mysqltest_1@localhost;
grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30;
-select * from mysql.user where user="mysqltest_1";
+query_vertical select * from mysql.user where user="mysqltest_1";
show grants for mysqltest_1@localhost;
# This is just to double check that one won't ignore results of selects
flush privileges;
diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test
index 447848013f9..6c2ba0dd6fc 100644
--- a/mysql-test/t/grant2.test
+++ b/mysql-test/t/grant2.test
@@ -31,7 +31,7 @@ create user mysqltest_2@localhost;
connect (user_a,localhost,mysqltest_1,,);
connection user_a;
grant select on `my\_1`.* to mysqltest_2@localhost;
---error ER_PASSWORD_NOT_ALLOWED
+--error ER_DBACCESS_DENIED_ERROR
grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass';
disconnect user_a;
connection default;
@@ -405,7 +405,7 @@ drop database mysqltest_1;
# But anonymous users can't change their password
connect (n5,localhost,test,,test,$MASTER_MYPORT,$MASTER_MYSOCK);
connection n5;
---error ER_DBACCESS_DENIED_ERROR
+--error ER_PASSWORD_NO_MATCH
set password = password("changed");
disconnect n5;
connection default;
diff --git a/mysql-test/t/plugin_auth-master.opt b/mysql-test/t/plugin_auth-master.opt
new file mode 100644
index 00000000000..3536d102387
--- /dev/null
+++ b/mysql-test/t/plugin_auth-master.opt
@@ -0,0 +1,2 @@
+$PLUGIN_AUTH_OPT
+$PLUGIN_AUTH_LOAD
diff --git a/mysql-test/t/plugin_auth.test b/mysql-test/t/plugin_auth.test
new file mode 100644
index 00000000000..f5a8bd416a0
--- /dev/null
+++ b/mysql-test/t/plugin_auth.test
@@ -0,0 +1,298 @@
+--source include/have_plugin_auth.inc
+--source include/not_embedded.inc
+
+query_vertical SELECT PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_DESCRIPTION
+ FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server';
+
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+
+SELECT plugin,authentication_string FROM mysql.user WHERE User='plug';
+
+--echo ## test plugin auth
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
+connect(plug_con,localhost,plug,plug_dest);
+--enable_query_log
+
+GRANT PROXY ON plug_dest TO plug;
+
+connect(plug_con,localhost,plug,plug_dest);
+
+connection plug_con;
+select USER(),CURRENT_USER();
+
+--echo ## test SET PASSWORD
+#--error ER_SET_PASSWORD_AUTH_PLUGIN
+SET PASSWORD = PASSWORD('plug_dest');
+
+connection default;
+disconnect plug_con;
+
+--echo ## test bad credentials
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR
+connect(plug_con,localhost,plug,bad_credentials);
+--enable_query_log
+
+--echo ## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD
+--disable_result_log
+--disable_query_log
+--error 2059
+connect(plug_con_wrongp,localhost,plug,plug_dest,,,,,wrong_plugin_name);
+--enable_query_log
+--enable_result_log
+
+--echo ## test correct default plugin
+connect(plug_con_rightp,localhost,plug,plug_dest,,,,,auth_test_plugin);
+connection plug_con_rightp;
+select USER(),CURRENT_USER();
+connection default;
+disconnect plug_con_rightp;
+
+--echo ## test no_auto_create_user sql mode with plugin users
+SET @@sql_mode=no_auto_create_user;
+GRANT INSERT ON TEST.* TO grant_user IDENTIFIED WITH 'test_plugin_server';
+SET @@sql_mode=default;
+DROP USER grant_user;
+
+--echo ## test utf-8 user name
+CREATE USER `Ÿ` IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+GRANT PROXY ON plug_dest TO `Ÿ`;
+
+connect(non_ascii,localhost,Ÿ,plug_dest);
+connection non_ascii;
+select USER(),CURRENT_USER();
+
+connection default;
+disconnect non_ascii;
+DROP USER `Ÿ`;
+
+--echo ## test GRANT ... IDENTIFIED WITH/BY ...
+
+CREATE DATABASE test_grant_db;
+
+--echo # create new user via GRANT WITH
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+ IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+GRANT PROXY ON plug_dest TO new_grant_user;
+
+connect(plug_con_grant,localhost,new_grant_user,plug_dest);
+connection plug_con_grant;
+select USER(),CURRENT_USER();
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+connection default;
+disconnect plug_con_grant;
+REVOKE ALL PRIVILEGES ON test_grant_db.* FROM new_grant_user;
+
+--echo # try re-create existing user via GRANT IDENTIFIED BY
+GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user
+ IDENTIFIED BY 'unused_password';
+
+--echo # make sure password doesn't take precendence
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR
+connect(plug_con_grant_deny,localhost,new_grant_user,unused_password);
+--enable_query_log
+
+--echo #make sure plugin auth still available
+connect(plug_con_grant,localhost,new_grant_user,plug_dest);
+connection plug_con_grant;
+select USER(),CURRENT_USER();
+USE test_grant_db;
+CREATE TABLE t1 (a INT);
+DROP TABLE t1;
+connection default;
+disconnect plug_con_grant;
+
+DROP USER new_grant_user;
+
+--echo # try re-create existing user via GRANT IDENTIFIED WITH
+
+--error ER_GRANT_PLUGIN_USER_EXISTS
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug
+ IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_GRANT_PLUGIN_USER_EXISTS
+GRANT ALL PRIVILEGES ON test_grant_db.* TO plug_dest
+ IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro
+ INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro
+ INDENTIFIED BY 'plug_dest_passwd';
+
+--error ER_PARSE_ERROR
+REVOKE SELECT on test_grant_db.* FROM joro
+ INDENTIFIED BY PASSWORD 'plug_dest_passwd';
+
+DROP DATABASE test_grant_db;
+
+--echo ## GRANT PROXY tests
+
+CREATE USER grant_plug IDENTIFIED WITH 'test_plugin_server'
+ AS 'grant_plug_dest';
+CREATE USER grant_plug_dest IDENTIFIED BY 'grant_plug_dest_passwd';
+CREATE USER grant_plug_dest2 IDENTIFIED BY 'grant_plug_dest_passwd2';
+
+--echo # ALL PRIVILEGES doesn't include PROXY
+GRANT ALL PRIVILEGES ON *.* TO grant_plug;
+--disable_query_log
+--error ER_ACCESS_DENIED_ERROR : this should fail : no grant
+connect(grant_plug_con,localhost,grant_plug,grant_plug_dest);
+--enable_query_log
+
+--error ER_PARSE_ERROR : this should fail : can't combine PROXY
+GRANT ALL PRIVILEGES,PROXY ON grant_plug_dest TO grant_plug;
+
+--echo this should fail : can't combine PROXY
+--error ER_PARSE_ERROR
+GRANT ALL SELECT,PROXY ON grant_plug_dest TO grant_plug;
+
+--echo # this should fail : no such grant
+--error ER_NONEXISTING_GRANT
+REVOKE PROXY ON grant_plug_dest FROM grant_plug;
+
+connect(grant_plug_dest_con,localhost,grant_plug_dest,grant_plug_dest_passwd);
+connection grant_plug_dest_con;
+--echo in grant_plug_dest_con
+
+--echo ## testing what an ordinary user can grant
+--echo this should fail : no rights to grant all
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON ''@'' TO grant_plug;
+
+--echo this should fail : not the same user
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON grant_plug TO grant_plug_dest;
+
+--echo this should fail : same user, but on a different host
+--error ER_ACCESS_DENIED_NO_PASSWORD_ERROR
+GRANT PROXY ON grant_plug_dest TO grant_plug;
+
+--echo this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug_dest2;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug_dest2;
+
+--echo this should work : same user
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug WITH GRANT OPTION;
+REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug;
+
+--echo this should fail : can't create users
+--error ER_CANT_CREATE_USER_WITH_GRANT
+GRANT PROXY ON grant_plug_dest@localhost TO grant_plug@localhost;
+
+connection default;
+--echo in default connection
+disconnect grant_plug_dest_con;
+
+--echo # test what root can grant
+
+--echo should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO grant_plug;
+REVOKE PROXY ON ''@'' FROM grant_plug;
+
+--echo should work : root has PROXY to all users
+GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test'
+ WITH GRANT OPTION;
+
+--echo need USAGE : PROXY doesn't contain it.
+GRANT USAGE on *.* TO proxy_admin;
+
+connect (proxy_admin_con,localhost,proxy_admin,test);
+connection proxy_admin_con;
+--echo in proxy_admin_con;
+
+--echo should work : proxy_admin has proxy to ''@''
+GRANT PROXY ON future_user TO grant_plug;
+
+connection default;
+--echo in default connection
+disconnect proxy_admin_con;
+
+SHOW GRANTS FOR grant_plug;
+REVOKE PROXY ON future_user FROM grant_plug;
+SHOW GRANTS FOR grant_plug;
+
+--echo ## testing drop user
+CREATE USER test_drop@localhost;
+GRANT PROXY ON future_user TO test_drop@localhost;
+SHOW GRANTS FOR test_drop@localhost;
+DROP USER test_drop@localhost;
+SELECT * FROM mysql.proxy_priv WHERE Host = 'test_drop' AND User = 'localhost';
+
+DROP USER proxy_admin;
+
+DROP USER grant_plug,grant_plug_dest,grant_plug_dest2;
+
+--echo ## END GRANT PROXY tests
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+
+--echo ## @@proxy_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+
+SELECT USER(),CURRENT_USER(),@@LOCAL.proxy_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT @@GLOBAL.proxy_user;
+SELECT @@LOCAL.proxy_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET GLOBAL proxy_user = 'test';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET LOCAL proxy_user = 'test';
+SELECT @@LOCAL.proxy_user;
+
+connect(plug_con,localhost,plug,plug_dest);
+connection plug_con;
+--echo # in connection plug_con
+SELECT @@LOCAL.proxy_user;
+connection default;
+--echo # in connection default
+disconnect plug_con;
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+--echo ## END @@proxy_user tests
+
+--echo ## @@external_user tests
+CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest';
+CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd';
+GRANT PROXY ON plug_dest TO plug;
+SELECT USER(),CURRENT_USER(),@@LOCAL.external_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SELECT @@GLOBAL.external_user;
+SELECT @@LOCAL.external_user;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET GLOBAL external_user = 'test';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET LOCAL external_user = 'test';
+SELECT @@LOCAL.external_user;
+
+connect(plug_con,localhost,plug,plug_dest);
+connection plug_con;
+--echo # in connection plug_con
+SELECT @@LOCAL.external_user;
+connection default;
+--echo # in connection default
+disconnect plug_con;
+
+--echo ## cleanup
+DROP USER plug;
+DROP USER plug_dest;
+--echo ## END @@external_user tests
diff --git a/mysql-test/t/system_mysql_db_fix40123.test b/mysql-test/t/system_mysql_db_fix40123.test
index 818993e5f27..d069271a02e 100644
--- a/mysql-test/t/system_mysql_db_fix40123.test
+++ b/mysql-test/t/system_mysql_db_fix40123.test
@@ -72,7 +72,7 @@ CREATE TABLE time_zone_leap_second ( Transition_time bigint signed NOT NULL,
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
-- enable_query_log
diff --git a/mysql-test/t/system_mysql_db_fix50030.test b/mysql-test/t/system_mysql_db_fix50030.test
index 45084177570..53166919f1c 100644
--- a/mysql-test/t/system_mysql_db_fix50030.test
+++ b/mysql-test/t/system_mysql_db_fix50030.test
@@ -78,7 +78,7 @@ INSERT INTO servers VALUES ('test','localhost','test','root','', 0,'','mysql','r
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
-- enable_query_log
diff --git a/mysql-test/t/system_mysql_db_fix50117.test b/mysql-test/t/system_mysql_db_fix50117.test
index bed00239081..872829ae79d 100644
--- a/mysql-test/t/system_mysql_db_fix50117.test
+++ b/mysql-test/t/system_mysql_db_fix50117.test
@@ -97,7 +97,7 @@ CREATE TABLE IF NOT EXISTS ndb_binlog_index (Position BIGINT UNSIGNED NOT NULL,
-- disable_query_log
# Drop all tables created by this test
-DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index;
+DROP TABLE db, host, user, func, plugin, tables_priv, columns_priv, procs_priv, servers, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type, general_log, slow_log, event, ndb_binlog_index, proxy_priv;
-- enable_query_log
diff --git a/plugin/auth/CMakeLists.txt b/plugin/auth/CMakeLists.txt
new file mode 100644
index 00000000000..d616d91bc47
--- /dev/null
+++ b/plugin/auth/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2010 Sun Microsystems, Inc
+#
+# 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(auth dialog.c
+ MODULE_ONLY)
+MYSQL_ADD_PLUGIN(auth_test_plugin test_plugin.c
+ MODULE_ONLY)
+
+CHECK_CXX_SOURCE_COMPILES(
+"#define _GNU_SOURCE
+#include <sys/socket.h>
+int main() {
+ struct ucred cred;
+ getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cred, 0);
+}" HAVE_PEERCRED)
+
+IF(HAVE_PEERCRED)
+ MYSQL_ADD_PLUGIN(auth_socket auth_socket.c
+ MODULE_ONLY)
+ENDIF()
diff --git a/plugin/auth/Makefile.am b/plugin/auth/Makefile.am
new file mode 100644
index 00000000000..ed459b7b2b1
--- /dev/null
+++ b/plugin/auth/Makefile.am
@@ -0,0 +1,16 @@
+pkgplugindir=$(pkglibdir)/plugin
+
+AM_LDFLAGS=-module -rpath $(pkgplugindir)
+AM_CPPFLAGS=-DMYSQL_DYNAMIC_PLUGIN -Wno-pointer-sign -I$(top_srcdir)/include
+
+pkgplugin_LTLIBRARIES= auth.la auth_test_plugin.la
+auth_la_SOURCES= dialog.c
+auth_test_plugin_la_SOURCES= test_plugin.c
+
+if HAVE_PEERCRED
+pkgplugin_LTLIBRARIES+= auth_socket.la
+auth_socket_la_SOURCES= auth_socket.c
+endif
+
+EXTRA_DIST= plug.in
+
diff --git a/plugin/auth/auth_socket.c b/plugin/auth/auth_socket.c
new file mode 100644
index 00000000000..2e584e760c7
--- /dev/null
+++ b/plugin/auth/auth_socket.c
@@ -0,0 +1,93 @@
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ socket_peercred authentication plugin.
+
+ Authentication is successful if the connection is done via a unix socket and
+ the owner of the client process matches the user name that was used when
+ connecting to mysqld.
+*/
+#define _GNU_SOURCE /* for struct ucred */
+
+#include <mysql/plugin_auth.h>
+#include <sys/socket.h>
+#include <pwd.h>
+#include <string.h>
+
+static int socket_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ unsigned char *pkt;
+ MYSQL_PLUGIN_VIO_INFO vio_info;
+ struct ucred cred;
+ socklen_t cred_len= sizeof(cred);
+ struct passwd pwd_buf, *pwd;
+ char buf[1024];
+
+ /* no user name yet ? read the client handshake packet with the user name */
+ if (info->user_name == 0)
+ {
+ if (vio->read_packet(vio, &pkt) < 0)
+ return CR_ERROR;
+ }
+
+ info->password_used = 2;
+
+ vio->info(vio, &vio_info);
+ if (vio_info.protocol != MYSQL_VIO_SOCKET)
+ return CR_ERROR;
+
+ /* get the UID of the client process */
+ if (getsockopt(vio_info.socket, SOL_SOCKET, SO_PEERCRED, &cred, &cred_len))
+ return CR_ERROR;
+
+ if (cred_len != sizeof(cred))
+ return CR_ERROR;
+
+ /* and find the username for this uid */
+ getpwuid_r(cred.uid, &pwd_buf, buf, sizeof(buf), &pwd);
+ if (pwd == NULL)
+ return CR_ERROR;
+
+ /* now it's simple as that */
+ return strcmp(pwd->pw_name, info->user_name) ? CR_ERROR : CR_OK;
+}
+
+static struct st_mysql_auth socket_auth_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ 0,
+ socket_auth
+};
+
+mysql_declare_plugin(socket_auth)
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &socket_auth_handler,
+ "socket_peercred",
+ "Sergei Golubchik",
+ "Unix Socket based authentication",
+ PLUGIN_LICENSE_GPL,
+ NULL,
+ NULL,
+ 0x0100,
+ NULL,
+ NULL,
+ NULL
+}
+mysql_declare_plugin_end;
+
diff --git a/plugin/auth/dialog.c b/plugin/auth/dialog.c
new file mode 100644
index 00000000000..157158abb2a
--- /dev/null
+++ b/plugin/auth/dialog.c
@@ -0,0 +1,325 @@
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ dialog client authentication plugin with examples
+
+ dialog is a general purpose client authentication plugin, it simply
+ asks the user the question, as provided by the server and reports
+ the answer back to the server. No encryption is involved,
+ the answers are sent in clear text.
+
+ Two examples are provided: two_questions server plugin, that asks
+ the password and an "Are you sure?" question with a reply "yes, of course".
+ It demonstrates the usage of "password" (input is hidden) and "ordinary"
+ (input can be echoed) questions, and how to mark the last question,
+ to avoid an extra roundtrip.
+
+ And three_attempts plugin that gives the user three attempts to enter
+ a correct password. It shows the situation when a number of questions
+ is not known in advance.
+*/
+#if defined (WIN32) && !defined (RTLD_DEFAULT)
+# define RTLD_DEFAULT GetModuleHandle(NULL)
+#endif
+
+#include <my_global.h>
+#include <mysql.h>
+#include <mysql/plugin_auth.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined (_GNU_SOURCE)
+# define _GNU_SOURCE /* for RTLD_DEFAULT */
+#endif
+
+/**
+ first byte of the question string is the question "type".
+ It can be a "ordinary" or a "password" question.
+ The last bit set marks a last question in the authentication exchange.
+*/
+#define ORDINARY_QUESTION "\2"
+#define LAST_QUESTION "\3"
+#define PASSWORD_QUESTION "\4"
+#define LAST_PASSWORD "\5"
+
+/********************* SERVER SIDE ****************************************/
+
+/**
+ dialog demo with two questions, one password and one, the last, ordinary.
+*/
+static int two_questions(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ unsigned char *pkt;
+ int pkt_len;
+
+ /* send a password question */
+ if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
+ return CR_ERROR;
+
+ /* read the answer */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ info->password_used = 1;
+
+ /* fail if the password is wrong */
+ if (strcmp((const char *)pkt, info->auth_string))
+ return CR_ERROR;
+
+ /* send the last, ordinary, question */
+ if (vio->write_packet(vio, (const unsigned char *) LAST_QUESTION "Are you sure ?", 15))
+ return CR_ERROR;
+
+ /* read the answer */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ /* check the reply */
+ return strcmp((const char *)pkt, "yes, of course") ? CR_ERROR : CR_OK;
+}
+
+static struct st_mysql_auth two_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ "dialog", /* requires dialog client plugin */
+ two_questions
+};
+
+/* dialog demo where the number of questions is not known in advance */
+static int three_attempts(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ unsigned char *pkt;
+ int pkt_len, i;
+
+ for (i= 0; i < 3; i++)
+ {
+ /* send the prompt */
+ if (vio->write_packet(vio,
+ (const unsigned char *) PASSWORD_QUESTION "Password, please:", 18))
+ return CR_ERROR;
+
+ /* read the password */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ info->password_used = 1;
+
+ /*
+ finish, if the password is correct.
+ note, that we did not mark the prompt packet as "last"
+ */
+ if (strcmp((const char *) pkt, info->auth_string) == 0)
+ return CR_OK;
+ }
+
+ return CR_ERROR;
+}
+
+static struct st_mysql_auth three_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ "dialog", /* requires dialog client plugin */
+ three_attempts
+};
+
+mysql_declare_plugin(dialog)
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &two_handler,
+ "two_questions",
+ "Sergei Golubchik",
+ "Dialog plugin demo 1",
+ PLUGIN_LICENSE_GPL,
+ NULL,
+ NULL,
+ 0x0100,
+ NULL,
+ NULL,
+ NULL
+},
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &three_handler,
+ "three_attempts",
+ "Sergei Golubchik",
+ "Dialog plugin demo 2",
+ PLUGIN_LICENSE_GPL,
+ NULL,
+ NULL,
+ 0x0100,
+ NULL,
+ NULL,
+ NULL
+}
+mysql_declare_plugin_end;
+
+/********************* CLIENT SIDE ***************************************/
+/*
+ This plugin performs a dialog with the user, asking questions and
+ reading answers. Depending on the client it may be desirable to do it
+ using GUI, or console, with or without curses, or read answers
+ from a smardcard, for example.
+
+ To support all this variety, the dialog plugin has a callback function
+ "authentication_dialog_ask". If the client has a function of this name
+ dialog plugin will use it for communication with the user. Otherwise
+ a default gets() based implementation will be used.
+*/
+
+/**
+ type of the mysql_authentication_dialog_ask function
+
+ @param mysql mysql
+ @param type type of the input
+ 1 - ordinary string input
+ 2 - password string
+ @param prompt prompt
+ @param buf a buffer to store the use input
+ @param buf_len the length of the buffer
+
+ @retval a pointer to the user input string.
+ It may be equal to 'buf' or to 'mysql->password'.
+ In all other cases it is assumed to be an allocated
+ string, and the "dialog" plugin will free() it.
+*/
+typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql,
+ int type, const char *prompt, char *buf, int buf_len);
+
+static mysql_authentication_dialog_ask_t ask;
+
+static char *builtin_ask(MYSQL *mysql __attribute__((unused)),
+ int type __attribute__((unused)),
+ const char *prompt,
+ char *buf, int buf_len __attribute__((unused)))
+{
+ fputs(prompt, stdout);
+ fputc(' ', stdout);
+ if (gets(buf) == 0)
+ return 0;
+
+ return buf;
+}
+
+/**
+ The main function of the dialog plugin.
+
+ Read the prompt, ask the question, send the reply, repeat until
+ the server is satisfied.
+
+ @note
+ 1. this plugin shows how a client authentication plugin
+ may read a MySQL protocol OK packet internally - which is important
+ where a number of packets is not known in advance.
+ 2. the first byte of the prompt is special. it is not
+ shown to the user, but signals whether it is the last question
+ (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0),
+ and whether the input is a password (not echoed).
+ 3. the prompt is expected to be sent zero-terminated
+*/
+static int perform_dialog(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ unsigned char *pkt, cmd= 0;
+ int pkt_len, res;
+ char reply_buf[1024], *reply;
+
+ do
+ {
+ /* read the prompt */
+ pkt_len= vio->read_packet(vio, &pkt);
+ if (pkt_len < 0)
+ return CR_ERROR;
+
+ if (pkt == 0)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet, so
+ the first vio->read_packet() does nothing (pkt == 0).
+
+ We send the "password", assuming the client knows what its doing.
+ (in other words, the dialog plugin should be only set as a default
+ authentication plugin on the client if the first question
+ asks for a password - which will be sent in clear text, by the way)
+ */
+ reply= mysql->passwd;
+ }
+ else
+ {
+ cmd= *pkt++;
+
+ /* is it MySQL protocol packet ? */
+ if (cmd == 0 || cmd == 254)
+ return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
+
+ /*
+ asking for a password with an empty prompt means mysql->password
+ otherwise we ask the user and read the reply
+ */
+ if ((cmd >> 1) == 2 && *pkt == 0)
+ reply= mysql->passwd;
+ else
+ reply= ask(mysql, cmd >> 1, (const char *) pkt,
+ reply_buf, sizeof(reply_buf));
+ if (!reply)
+ return CR_ERROR;
+ }
+ /* send the reply to the server */
+ res= vio->write_packet(vio, (const unsigned char *) reply,
+ strlen(reply)+1);
+
+ if (reply != mysql->passwd && reply != reply_buf)
+ free(reply);
+
+ if (res)
+ return CR_ERROR;
+
+ /* repeat unless it was the last question */
+ } while ((cmd & 1) != 1);
+
+ /* the job of reading the ok/error packet is left to the server */
+ return CR_OK;
+}
+
+/**
+ initialization function of the dialog plugin
+
+ Pick up the client's authentication_dialog_ask() function, if exists,
+ or fall back to the default implementation.
+*/
+
+static int init_dialog(char *unused1 __attribute__((unused)),
+ size_t unused2 __attribute__((unused)),
+ int unused3 __attribute__((unused)),
+ va_list unused4 __attribute__((unused)))
+{
+ void *sym= dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask");
+ ask= sym ? (mysql_authentication_dialog_ask_t)sym : builtin_ask;
+ return 0;
+}
+
+mysql_declare_client_plugin(AUTHENTICATION)
+ "dialog",
+ "Sergei Golubchik",
+ "Dialog Client Authentication Plugin",
+ {0,1,0},
+ init_dialog,
+ NULL,
+ perform_dialog
+mysql_end_client_plugin;
+
diff --git a/plugin/auth/plug.in b/plugin/auth/plug.in
new file mode 100644
index 00000000000..776367652ab
--- /dev/null
+++ b/plugin/auth/plug.in
@@ -0,0 +1,12 @@
+MYSQL_PLUGIN(auth, [Collection of Authentication Plugins],
+ [Collection of Authentication Plugins])
+MYSQL_PLUGIN_DYNAMIC(auth, [dialog.la auth_test_plugin.la])
+AC_COMPILE_IFELSE([
+ AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <sys/socket.h>
+]],[
+ struct ucred cred;
+ getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cred, 0);
+])],have_peercred=yes)
+AM_CONDITIONAL(HAVE_PEERCRED, test x$have_peercred = xyes)
diff --git a/plugin/auth/test_plugin.c b/plugin/auth/test_plugin.c
new file mode 100644
index 00000000000..ad0fcaca2e1
--- /dev/null
+++ b/plugin/auth/test_plugin.c
@@ -0,0 +1,200 @@
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ dialog client authentication plugin with examples
+
+ dialog is a general purpose client authentication plugin, it simply
+ asks the user the question, as provided by the server and reports
+ the answer back to the server. No encryption is involved,
+ the answers are sent in clear text.
+
+ Two examples are provided: two_questions server plugin, that asks
+ the password and an "Are you sure?" question with a reply "yes, of course".
+ It demonstrates the usage of "password" (input is hidden) and "ordinary"
+ (input can be echoed) questions, and how to mark the last question,
+ to avoid an extra roundtrip.
+
+ And three_attempts plugin that gives the user three attempts to enter
+ a correct password. It shows the situation when a number of questions
+ is not known in advance.
+*/
+
+#include <my_global.h>
+#include <mysql/plugin_auth.h>
+#include <mysql/client_plugin.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/**
+ first byte of the question string is the question "type".
+ It can be a "ordinary" or a "password" question.
+ The last bit set marks a last question in the authentication exchange.
+*/
+#define ORDINARY_QUESTION "\2"
+#define LAST_QUESTION "\3"
+#define LAST_PASSWORD "\4"
+#define PASSWORD_QUESTION "\5"
+
+/********************* SERVER SIDE ****************************************/
+
+/**
+ dialog test plugin mimicing the ordinary auth mechanism. Used to test the auth plugin API
+*/
+static int auth_test_plugin(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ unsigned char *pkt;
+ int pkt_len;
+
+ /* send a password question */
+ if (vio->write_packet(vio, (const unsigned char *) PASSWORD_QUESTION, 1))
+ return CR_ERROR;
+
+ /* read the answer */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ info->password_used = 1;
+
+ /* fail if the password is wrong */
+ if (strcmp((const char *) pkt, info->auth_string))
+ return CR_ERROR;
+
+ /* copy auth string as a destination name to check it */
+ strcpy (info->authenticated_as, info->auth_string);
+
+ /* copy something into the external user name */
+ strcpy (info->external_user, info->auth_string);
+
+ return CR_OK;
+}
+
+static struct st_mysql_auth auth_test_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ "auth_test_plugin", /* requires test_plugin client's plugin */
+ auth_test_plugin
+};
+
+mysql_declare_plugin(test_plugin)
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &auth_test_handler,
+ "test_plugin_server",
+ "Georgi Kodinov",
+ "plugin API test plugin",
+ PLUGIN_LICENSE_GPL,
+ NULL,
+ NULL,
+ 0x0100,
+ NULL,
+ NULL,
+ NULL
+}
+mysql_declare_plugin_end;
+
+/********************* CLIENT SIDE ***************************************/
+/*
+ client plugin used for testing the plugin API
+*/
+#include <mysql.h>
+
+/**
+ The main function of the test plugin.
+
+ Reads the prompt, check if the handshake is done and if the prompt is a
+ password request and returns the password. Otherwise return error.
+
+ @note
+ 1. this plugin shows how a client authentication plugin
+ may read a MySQL protocol OK packet internally - which is important
+ where a number of packets is not known in advance.
+ 2. the first byte of the prompt is special. it is not
+ shown to the user, but signals whether it is the last question
+ (prompt[0] & 1 == 1) or not last (prompt[0] & 1 == 0),
+ and whether the input is a password (not echoed).
+ 3. the prompt is expected to be sent zero-terminated
+*/
+static int test_plugin_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ unsigned char *pkt, cmd= 0;
+ int pkt_len, res;
+ char *reply;
+
+ do
+ {
+ /* read the prompt */
+ pkt_len= vio->read_packet(vio, &pkt);
+ if (pkt_len < 0)
+ return CR_ERROR;
+
+ if (pkt == 0)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet, so
+ the first vio->read_packet() does nothing (pkt == 0).
+
+ We send the "password", assuming the client knows what its doing.
+ (in other words, the dialog plugin should be only set as a default
+ authentication plugin on the client if the first question
+ asks for a password - which will be sent in cleat text, by the way)
+ */
+ reply= mysql->passwd;
+ }
+ else
+ {
+ cmd= *pkt++;
+
+ /* is it MySQL protocol (0=OK or 254=need old password) packet ? */
+ if (cmd == 0 || cmd == 254)
+ return CR_OK_HANDSHAKE_COMPLETE; /* yes. we're done */
+
+ /*
+ asking for a password with an empty prompt means mysql->password
+ otherwise return an error
+ */
+ if ((cmd == LAST_PASSWORD[0] || cmd == PASSWORD_QUESTION[0]) && *pkt == 0)
+ reply= mysql->passwd;
+ else
+ return CR_ERROR;
+ }
+ if (!reply)
+ return CR_ERROR;
+ /* send the reply to the server */
+ res= vio->write_packet(vio, (const unsigned char *)reply, strlen(reply)+1);
+
+ if (res)
+ return CR_ERROR;
+
+ /* repeat unless it was the last question */
+ } while (cmd != LAST_QUESTION[0] && cmd != PASSWORD_QUESTION[0]);
+
+ /* the job of reading the ok/error packet is left to the server */
+ return CR_OK;
+}
+
+
+mysql_declare_client_plugin(AUTHENTICATION)
+ "auth_test_plugin",
+ "Georgi Kodinov",
+ "Dialog Client Authentication Plugin",
+ {0,1,0},
+ NULL,
+ NULL,
+ test_plugin_client
+mysql_end_client_plugin;
diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql
index b5ee4d6dd41..c7abf3d82a1 100644
--- a/scripts/mysql_system_tables.sql
+++ b/scripts/mysql_system_tables.sql
@@ -28,7 +28,7 @@ set @had_db_table= @@warning_count != 0;
CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges; Merged with database privileges';
-CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) unsigned DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) unsigned DEFAULT 0 NOT NULL, plugin char(60) DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
-- Remember for later if user table already existed
set @had_user_table= @@warning_count != 0;
@@ -498,3 +498,7 @@ PREPARE stmt FROM @str;
EXECUTE stmt;
DROP PREPARE stmt;
+CREATE TABLE IF NOT EXISTS proxy_priv (Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Proxied_Host char(16) binary DEFAULT '' NOT NULL, Proxied_User char(60) binary DEFAULT '' NOT NULL, With_Grant BOOL DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User,Proxied_Host,Proxied_User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User proxy privileges';
+
+-- Remember for later if proxy_priv table already existed
+set @had_proxy_priv_table= @@warning_count != 0;
diff --git a/scripts/mysql_system_tables_data.sql b/scripts/mysql_system_tables_data.sql
index a7988b5198e..293baa46523 100644
--- a/scripts/mysql_system_tables_data.sql
+++ b/scripts/mysql_system_tables_data.sql
@@ -21,11 +21,17 @@ DROP TABLE tmp_db;
-- from local machine if "users" table didn't exist before
CREATE TEMPORARY TABLE tmp_user LIKE user;
set @current_hostname= @@hostname;
-INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0 FROM dual WHERE LOWER( @current_hostname) != 'localhost';
-REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
-REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0);
+INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
+REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','' FROM dual WHERE LOWER( @current_hostname) != 'localhost';
+REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
+REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','');
INSERT INTO tmp_user (host,user) VALUES ('localhost','');
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost';
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
DROP TABLE tmp_user;
+
+CREATE TEMPORARY TABLE tmp_proxy_priv LIKE proxy_priv;
+INSERT INTO tmp_proxy_priv VALUES ('localhost', 'root', '', '', TRUE);
+REPLACE INTO tmp_proxy_priv SELECT @current_hostname, 'root', '', '', TRUE FROM DUAL WHERE LOWER (@current_hostname) != 'localhost';
+INSERT INTO proxy_priv SELECT * FROM tmp_proxy_priv WHERE @had_proxy_priv_table=0;
+DROP TABLE tmp_proxy_priv;
diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql
index 2020971fb1a..ceb910676ab 100644
--- a/scripts/mysql_system_tables_fix.sql
+++ b/scripts/mysql_system_tables_fix.sql
@@ -640,6 +640,11 @@ DROP PREPARE stmt;
drop procedure mysql.die;
+ALTER TABLE user ADD plugin char(60) DEFAULT '' NOT NULL, ADD authentication_string TEXT NOT NULL;
+ALTER TABLE user MODIFY plugin char(60) DEFAULT '' NOT NULL;
+
+CREATE TABLE IF NOT EXISTS proxy_priv (Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Proxied_User char(60) binary DEFAULT '' NOT NULL, Proxied_Host char(16) binary DEFAULT '' NOT NULL, With_Grant BOOL DEFAULT 0 NOT NULL, PRIMARY KEY Host (Host,User,Proxied_Host,Proxied_User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges';
+
# Activate the new, possible modified privilege tables
# This should not be needed, but gives us some extra testing that the above
# changes was correct
diff --git a/sql-common/Makefile.am b/sql-common/Makefile.am
index 3193efee754..2f5a049085f 100644
--- a/sql-common/Makefile.am
+++ b/sql-common/Makefile.am
@@ -14,4 +14,4 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
## Process this file with automake to create Makefile.in
-EXTRA_DIST = client.c pack.c my_time.c my_user.c
+EXTRA_DIST = client.c pack.c my_time.c my_user.c client_plugin.c
diff --git a/sql-common/client.c b/sql-common/client.c
index f1bdcdc158f..3897e44fdaf 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -111,6 +111,10 @@ my_bool net_flush(NET *net);
#include "client_settings.h"
#include <sql_common.h>
+#include <mysql/client_plugin.h>
+#define native_password_plugin_name "mysql_native_password"
+#define old_password_plugin_name "mysql_old_password"
+
uint mysql_port=0;
char *mysql_unix_port= 0;
@@ -373,7 +377,7 @@ void net_clear_error(NET *net)
@param ... variable number of arguments
*/
-static void set_mysql_extended_error(MYSQL *mysql, int errcode,
+void set_mysql_extended_error(MYSQL *mysql, int errcode,
const char *sqlstate,
const char *format, ...)
{
@@ -1145,9 +1149,20 @@ static const char *default_options[]=
"connect-timeout", "local-infile", "disable-local-infile",
"ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name",
"multi-results", "multi-statements", "multi-queries", "secure-auth",
- "report-data-truncation",
+ "report-data-truncation", "plugin-dir", "default-auth",
NullS
};
+enum option_id {
+ OPT_port=1, OPT_socket, OPT_compress, OPT_password, OPT_pipe, OPT_timeout, OPT_user,
+ OPT_init_command, OPT_host, OPT_database, OPT_debug, OPT_return_found_rows,
+ OPT_ssl_key, OPT_ssl_cert, OPT_ssl_ca, OPT_ssl_capath,
+ OPT_character_sets_dir, OPT_default_character_set, OPT_interactive_timeout,
+ OPT_connect_timeout, OPT_local_infile, OPT_disable_local_infile,
+ OPT_replication_probe, OPT_enable_reads_from_master, OPT_repl_parse_query,
+ OPT_ssl_cipher, OPT_max_allowed_packet, OPT_protocol, OPT_shared_memory_base_name,
+ OPT_multi_results, OPT_multi_statements, OPT_multi_queries, OPT_secure_auth,
+ OPT_report_data_truncation, OPT_plugin_dir, OPT_default_auth,
+};
static TYPELIB option_types={array_elements(default_options)-1,
"options",default_options, NULL};
@@ -1178,6 +1193,15 @@ static int add_init_command(struct st_mysql_options *options, const char *cmd)
return 0;
}
+#define EXTENSION_SET_STRING(OPTS, X, STR) \
+ if ((OPTS)->extension) \
+ my_free((OPTS)->extension->X); \
+ else \
+ (OPTS)->extension= (struct st_mysql_options_extention *) \
+ my_malloc(sizeof(struct st_mysql_options_extention), \
+ MYF(MY_WME | MY_ZEROFILL)); \
+ (OPTS)->extension->X= my_strdup((STR), MYF(MY_WME));
+
void mysql_read_default_options(struct st_mysql_options *options,
const char *filename,const char *group)
{
@@ -1212,121 +1236,121 @@ void mysql_read_default_options(struct st_mysql_options *options,
for (end= *option ; *(end= strcend(end,'_')) ; )
*end= '-';
switch (find_type(*option+2,&option_types,2)) {
- case 1: /* port */
+ case OPT_port:
if (opt_arg)
options->port=atoi(opt_arg);
break;
- case 2: /* socket */
+ case OPT_socket:
if (opt_arg)
{
my_free(options->unix_socket);
options->unix_socket=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 3: /* compress */
+ case OPT_compress:
options->compress=1;
options->client_flag|= CLIENT_COMPRESS;
break;
- case 4: /* password */
+ case OPT_password:
if (opt_arg)
{
my_free(options->password);
options->password=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 5:
+ case OPT_pipe:
options->protocol = MYSQL_PROTOCOL_PIPE;
- case 20: /* connect_timeout */
- case 6: /* timeout */
+ case OPT_connect_timeout:
+ case OPT_timeout:
if (opt_arg)
options->connect_timeout=atoi(opt_arg);
break;
- case 7: /* user */
+ case OPT_user:
if (opt_arg)
{
my_free(options->user);
options->user=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 8: /* init-command */
+ case OPT_init_command:
add_init_command(options,opt_arg);
break;
- case 9: /* host */
+ case OPT_host:
if (opt_arg)
{
my_free(options->host);
options->host=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 10: /* database */
+ case OPT_database:
if (opt_arg)
{
my_free(options->db);
options->db=my_strdup(opt_arg,MYF(MY_WME));
}
break;
- case 11: /* debug */
+ case OPT_debug:
#ifdef MYSQL_CLIENT
mysql_debug(opt_arg ? opt_arg : "d:t:o,/tmp/client.trace");
break;
#endif
- case 12: /* return-found-rows */
+ case OPT_return_found_rows:
options->client_flag|=CLIENT_FOUND_ROWS;
break;
#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- case 13: /* ssl_key */
+ case OPT_ssl_key:
my_free(options->ssl_key);
options->ssl_key = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 14: /* ssl_cert */
+ case OPT_ssl_cert:
my_free(options->ssl_cert);
options->ssl_cert = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 15: /* ssl_ca */
+ case OPT_ssl_ca:
my_free(options->ssl_ca);
options->ssl_ca = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 16: /* ssl_capath */
+ case OPT_ssl_capath:
my_free(options->ssl_capath);
options->ssl_capath = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 23: /* ssl_cipher */
+ case OPT_ssl_cipher:
my_free(options->ssl_cipher);
options->ssl_cipher= my_strdup(opt_arg, MYF(MY_WME));
break;
#else
- case 13: /* Ignore SSL options */
- case 14:
- case 15:
- case 16:
- case 23:
+ case OPT_ssl_key:
+ case OPT_ssl_cert:
+ case OPT_ssl_ca:
+ case OPT_ssl_capath:
+ case OPT_ssl_cipher:
break;
#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
- case 17: /* charset-lib */
+ case OPT_character_sets_dir:
my_free(options->charset_dir);
options->charset_dir = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 18:
+ case OPT_default_character_set:
my_free(options->charset_name);
options->charset_name = my_strdup(opt_arg, MYF(MY_WME));
break;
- case 19: /* Interactive-timeout */
+ case OPT_interactive_timeout:
options->client_flag|= CLIENT_INTERACTIVE;
break;
- case 21:
+ case OPT_local_infile:
if (!opt_arg || atoi(opt_arg) != 0)
options->client_flag|= CLIENT_LOCAL_FILES;
else
options->client_flag&= ~CLIENT_LOCAL_FILES;
break;
- case 22:
+ case OPT_disable_local_infile:
options->client_flag&= ~CLIENT_LOCAL_FILES;
break;
- case 24: /* max-allowed-packet */
+ case OPT_max_allowed_packet:
if (opt_arg)
options->max_allowed_packet= atoi(opt_arg);
break;
- case 25: /* protocol */
+ case OPT_protocol:
if ((options->protocol= find_type(opt_arg,
&sql_protocol_typelib,0)) <= 0)
{
@@ -1334,26 +1358,44 @@ void mysql_read_default_options(struct st_mysql_options *options,
exit(1);
}
break;
- case 26: /* shared_memory_base_name */
+ case OPT_shared_memory_base_name:
#ifdef HAVE_SMEM
if (options->shared_memory_base_name != def_shared_memory_base_name)
my_free(options->shared_memory_base_name);
options->shared_memory_base_name=my_strdup(opt_arg,MYF(MY_WME));
#endif
break;
- case 27: /* multi-results */
+ case OPT_multi_results:
options->client_flag|= CLIENT_MULTI_RESULTS;
break;
- case 28: /* multi-statements */
- case 29: /* multi-queries */
+ case OPT_multi_statements:
+ case OPT_multi_queries:
options->client_flag|= CLIENT_MULTI_STATEMENTS | CLIENT_MULTI_RESULTS;
break;
- case 30: /* secure-auth */
+ case OPT_secure_auth:
options->secure_auth= TRUE;
break;
- case 31: /* report-data-truncation */
+ case OPT_report_data_truncation:
options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1;
break;
+ case OPT_plugin_dir:
+ {
+ char buff[FN_REFLEN], buff2[FN_REFLEN];
+ if (strlen(opt_arg) >= FN_REFLEN)
+ opt_arg[FN_REFLEN]= '\0';
+ if (my_realpath(buff, opt_arg, 0))
+ {
+ DBUG_PRINT("warning",("failed to normalize the plugin path: %s",
+ opt_arg));
+ break;
+ }
+ convert_dirname(buff, buff2, NULL);
+ EXTENSION_SET_STRING(options, plugin_dir, buff2);
+ }
+ break;
+ case OPT_default_auth:
+ EXTENSION_SET_STRING(options, default_auth, opt_arg);
+ break;
default:
DBUG_PRINT("warning",("unknown option: %s",option[0]));
}
@@ -1884,6 +1926,11 @@ static int ssl_verify_server_cert(Vio *vio, const char* server_hostname)
static my_bool cli_read_query_result(MYSQL *mysql);
static MYSQL_RES *cli_use_result(MYSQL *mysql);
+int cli_read_change_user_result(MYSQL *mysql)
+{
+ return cli_safe_read(mysql);
+}
+
static MYSQL_METHODS client_methods=
{
cli_read_query_result, /* read_query_result */
@@ -1891,7 +1938,8 @@ static MYSQL_METHODS client_methods=
cli_read_rows, /* read_rows */
cli_use_result, /* use_result */
cli_fetch_lengths, /* fetch_lengths */
- cli_flush_use_result /* flush_use_result */
+ cli_flush_use_result, /* flush_use_result */
+ cli_read_change_user_result /* read_change_user_result */
#ifndef MYSQL_SERVER
,cli_list_fields, /* list_fields */
cli_read_prepare_result, /* read_prepare_result */
@@ -1901,7 +1949,6 @@ static MYSQL_METHODS client_methods=
NULL, /* free_embedded_thd */
cli_read_statistics, /* read_statistics */
cli_read_query_result, /* next_result */
- cli_read_change_user_result, /* read_change_user_result */
cli_read_binary_rows /* read_rows_from_cursor */
#endif
};
@@ -2221,6 +2268,648 @@ int mysql_init_character_set(MYSQL *mysql)
}
C_MODE_END
+/*********** client side authentication support **************************/
+
+typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t;
+static int client_mpvio_write_packet(struct st_plugin_vio*, const uchar*, int);
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql);
+
+static auth_plugin_t native_password_client_plugin=
+{
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+ native_password_plugin_name,
+ "R.J.Silk, Sergei Golubchik",
+ "Native MySQL authentication",
+ {1, 0, 0},
+ NULL,
+ NULL,
+ native_password_auth_client
+};
+
+static auth_plugin_t old_password_client_plugin=
+{
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
+ old_password_plugin_name,
+ "R.J.Silk, Sergei Golubchik",
+ "Old MySQL-3.23 authentication",
+ {1, 0, 0},
+ NULL,
+ NULL,
+ old_password_auth_client
+};
+
+struct st_mysql_client_plugin *mysql_client_builtins[]=
+{
+ (struct st_mysql_client_plugin *)&native_password_client_plugin,
+ (struct st_mysql_client_plugin *)&old_password_client_plugin,
+ 0
+};
+
+
+
+/* this is a "superset" of MYSQL_PLUGIN_VIO, in C++ I use inheritance */
+typedef struct {
+ int (*read_packet)(struct st_plugin_vio *vio, uchar **buf);
+ int (*write_packet)(struct st_plugin_vio *vio, const uchar *pkt, int pkt_len);
+ void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info);
+ /* -= end of MYSQL_PLUGIN_VIO =- */
+ MYSQL *mysql;
+ auth_plugin_t *plugin; /**< what plugin we're under */
+ const char *db;
+ struct {
+ uchar *pkt; /**< pointer into NET::buff */
+ uint pkt_len;
+ } cached_server_reply;
+ int packets_read, packets_written; /**< counters for send/received packets */
+ int mysql_change_user; /**< if it's mysql_change_user() */
+ int last_read_packet_len; /**< the length of the last *read* packet */
+} MCPVIO_EXT;
+
+/**
+ sends a COM_CHANGE_USER command with a caller provided payload
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ n user name - \0-terminated string
+ n password
+ 3.23 scramble - \0-terminated string (9 bytes)
+ otherwise - length (1 byte) coded
+ n database name - \0-terminated string
+ 2 character set number (if the server >= 4.1.x)
+ n client auth plugin name - \0-terminated string,
+ (if the server supports plugin auth)
+
+ @retval 0 ok
+ @retval 1 error
+*/
+static int send_change_user_packet(MCPVIO_EXT *mpvio,
+ const uchar *data, int data_len)
+{
+ MYSQL *mysql= mpvio->mysql;
+ char *buff, *end;
+ int res= 1;
+
+ buff= my_alloca(USERNAME_LENGTH + data_len + 1 + NAME_LEN + 2 + NAME_LEN);
+
+ end= strmake(buff, mysql->user, USERNAME_LENGTH) + 1;
+
+ if (!data_len)
+ *end++= 0;
+ else
+ {
+ if (mysql->client_flag & CLIENT_SECURE_CONNECTION)
+ {
+ DBUG_ASSERT(data_len <= 255);
+ if (data_len > 255)
+ {
+ set_mysql_error(mysql, CR_MALFORMED_PACKET, unknown_sqlstate);
+ goto error;
+ }
+ *end++= data_len;
+ }
+ else
+ {
+ DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1);
+ DBUG_ASSERT(data[SCRAMBLE_LENGTH_323] == 0);
+ }
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ end= strmake(end, mpvio->db ? mpvio->db : "", NAME_LEN) + 1;
+
+ if (mysql->server_capabilities & CLIENT_PROTOCOL_41)
+ {
+ int2store(end, (ushort) mysql->charset->number);
+ end+= 2;
+ }
+
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+ res= simple_command(mysql, COM_CHANGE_USER,
+ (uchar*)buff, (ulong)(end-buff), 1);
+
+error:
+ my_afree(buff);
+ return res;
+}
+
+/**
+ sends a client authentication packet (second packet in the 3-way handshake)
+
+ Packet format (when the server is 4.0 or earlier):
+
+ Bytes Content
+ ----- ----
+ 2 client capabilities
+ 3 max packet size
+ n user name, \0-terminated
+ 9 scramble_323, \0-terminated
+
+ Packet format (when the server is 4.1 or newer):
+
+ Bytes Content
+ ----- ----
+ 4 client capabilities
+ 4 max packet size
+ 1 charset number
+ 23 reserved (always 0)
+ n user name, \0-terminated
+ n plugin auth data (e.g. scramble), length (1 byte) coded
+ n database name, \0-terminated
+ (if CLIENT_CONNECT_WITH_DB is set in the capabilities)
+ n client auth plugin name - \0-terminated string,
+ (if CLIENT_PLUGIN_AUTH is set in the capabilities)
+
+ @retval 0 ok
+ @retval 1 error
+*/
+static int send_client_reply_packet(MCPVIO_EXT *mpvio,
+ const uchar *data, int data_len)
+{
+ MYSQL *mysql= mpvio->mysql;
+ NET *net= &mysql->net;
+ char *buff, *end;
+
+ /* see end= buff+32 below, fixed size of the packet is 32 bytes */
+ buff= my_alloca(33 + USERNAME_LENGTH + data_len + NAME_LEN + NAME_LEN);
+
+ mysql->client_flag|= mysql->options.client_flag;
+ mysql->client_flag|= CLIENT_CAPABILITIES;
+
+ if (mysql->client_flag & CLIENT_MULTI_STATEMENTS)
+ mysql->client_flag|= CLIENT_MULTI_RESULTS;
+
+#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
+ if (mysql->options.ssl_key || mysql->options.ssl_cert ||
+ mysql->options.ssl_ca || mysql->options.ssl_capath ||
+ mysql->options.ssl_cipher)
+ mysql->options.use_ssl= 1;
+ if (mysql->options.use_ssl)
+ mysql->client_flag|= CLIENT_SSL;
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
+ if (mpvio->db)
+ mysql->client_flag|= CLIENT_CONNECT_WITH_DB;
+
+ /* Remove options that server doesn't support */
+ mysql->client_flag= mysql->client_flag &
+ (~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)
+ | mysql->server_capabilities);
+
+#ifndef HAVE_COMPRESS
+ mysql->client_flag&= ~CLIENT_COMPRESS;
+#endif
+
+ if (mysql->client_flag & CLIENT_PROTOCOL_41)
+ {
+ /* 4.1 server and 4.1 client has a 32 byte option flag */
+ int4store(buff,mysql->client_flag);
+ int4store(buff+4, net->max_packet_size);
+ buff[8]= (char) mysql->charset->number;
+ bzero(buff+9, 32-9);
+ end= buff+32;
+ }
+ else
+ {
+ int2store(buff, mysql->client_flag);
+ int3store(buff+2, net->max_packet_size);
+ end= buff+5;
+ }
+#ifdef HAVE_OPENSSL
+ if (mysql->client_flag & CLIENT_SSL)
+ {
+ /* Do the SSL layering. */
+ struct st_mysql_options *options= &mysql->options;
+ struct st_VioSSLFd *ssl_fd;
+
+ /*
+ Send mysql->client_flag, max_packet_size - unencrypted otherwise
+ the server does not know we want to do SSL
+ */
+ if (my_net_write(net, (uchar*)buff, (size_t) (end-buff)) || net_flush(net))
+ {
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending connection information to server",
+ errno);
+ goto error;
+ }
+
+ /* Create the VioSSLConnectorFd - init SSL and load certs */
+ if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
+ options->ssl_cert,
+ options->ssl_ca,
+ options->ssl_capath,
+ options->ssl_cipher)))
+ {
+ set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+ goto error;
+ }
+ mysql->connector_fd= (unsigned char *) ssl_fd;
+
+ /* Connect to the server */
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslconnect(ssl_fd, net->vio,
+ (long) (mysql->options.connect_timeout)))
+ {
+ set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+ goto error;
+ }
+ DBUG_PRINT("info", ("IO layer change done!"));
+
+ /* Verify server cert */
+ if ((mysql->client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
+ ssl_verify_server_cert(net->vio, mysql->host))
+ {
+ set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
+ goto error;
+ }
+ }
+#endif /* HAVE_OPENSSL */
+
+ DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
+ mysql->server_version, mysql->server_capabilities,
+ mysql->server_status, mysql->client_flag));
+
+ compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+ /* This needs to be changed as it's not useful with big packets */
+ if (mysql->user[0])
+ strmake(end, mysql->user, USERNAME_LENGTH);
+ else
+ read_user_name(end);
+
+ /* We have to handle different version of handshake here */
+ DBUG_PRINT("info",("user: %s",end));
+ end= strend(end) + 1;
+ if (data_len)
+ {
+ if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ {
+ *end++= data_len;
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ else
+ {
+ DBUG_ASSERT(data_len == SCRAMBLE_LENGTH_323 + 1); /* incl. \0 at the end */
+ memcpy(end, data, data_len);
+ end+= data_len;
+ }
+ }
+ else
+ *end++= 0;
+
+ /* Add database if needed */
+ if (mpvio->db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
+ {
+ end= strmake(end, mpvio->db, NAME_LEN) + 1;
+ mysql->db= my_strdup(mpvio->db, MYF(MY_WME));
+ }
+
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ end= strmake(end, mpvio->plugin->name, NAME_LEN) + 1;
+
+ /* Write authentication package */
+ if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
+ {
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending authentication information",
+ errno);
+ goto error;
+ }
+ my_afree(buff);
+ return 0;
+
+error:
+ my_afree(buff);
+ return 1;
+}
+
+/**
+ vio->read_packet() callback method for client authentication plugins
+
+ This function is called by a client authentication plugin, when it wants
+ to read data from the server.
+*/
+static int client_mpvio_read_packet(struct st_plugin_vio *mpv, uchar **buf)
+{
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+ MYSQL *mysql= mpvio->mysql;
+ ulong pkt_len;
+
+ /* there are cached data left, feed it to a plugin */
+ if (mpvio->cached_server_reply.pkt)
+ {
+ *buf= mpvio->cached_server_reply.pkt;
+ mpvio->cached_server_reply.pkt= 0;
+ mpvio->packets_read++;
+ return mpvio->cached_server_reply.pkt_len;
+ }
+
+ if (mpvio->packets_read == 0)
+ {
+ /*
+ the server handshake packet came from the wrong plugin,
+ or it's mysql_change_user(). Either way, there is no data
+ for a plugin to read. send a dummy packet to the server
+ to initiate a dialog.
+ */
+ if (client_mpvio_write_packet(mpv, 0, 0))
+ return (int)packet_error;
+ }
+
+ /* otherwise read the data */
+ pkt_len= (*mysql->methods->read_change_user_result)(mysql);
+ mpvio->last_read_packet_len= pkt_len;
+ *buf= mysql->net.read_pos;
+
+ /* was it a request to change plugins ? */
+ if (**buf == 254)
+ return (int)packet_error; /* if yes, this plugin shan't continue */
+
+ /*
+ the server sends \1\255 or \1\254 instead of just \255 or \254 -
+ for us to not confuse it with an error or "change plugin" packets.
+ We remove this escaping \1 here.
+
+ See also server_mpvio_write_packet() where the escaping is done.
+ */
+ if (pkt_len && **buf == 1)
+ {
+ (*buf)++;
+ pkt_len--;
+ }
+ mpvio->packets_read++;
+ return pkt_len;
+}
+
+/**
+ vio->write_packet() callback method for client authentication plugins
+
+ This function is called by a client authentication plugin, when it wants
+ to send data to the server.
+
+ It transparently wraps the data into a change user or authentication
+ handshake packet, if neccessary.
+*/
+static int client_mpvio_write_packet(struct st_plugin_vio *mpv,
+ const uchar *pkt, int pkt_len)
+{
+ int res;
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)mpv;
+
+ if (mpvio->packets_written == 0)
+ {
+ if (mpvio->mysql_change_user)
+ res= send_change_user_packet(mpvio, pkt, pkt_len);
+ else
+ res= send_client_reply_packet(mpvio, pkt, pkt_len);
+ }
+ else
+ {
+ NET *net= &mpvio->mysql->net;
+ if (mpvio->mysql->thd)
+ res= 1; /* no chit-chat in embedded */
+ else
+ res= my_net_write(net, pkt, pkt_len) || net_flush(net);
+ if (res)
+ set_mysql_extended_error(mpvio->mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "sending authentication information",
+ errno);
+ }
+ mpvio->packets_written++;
+ return res;
+}
+
+/**
+ fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+ connection
+*/
+void mpvio_info(Vio *vio, MYSQL_PLUGIN_VIO_INFO *info)
+{
+ bzero(info, sizeof(*info));
+ switch (vio->type) {
+ case VIO_TYPE_TCPIP:
+ info->protocol= MYSQL_VIO_TCP;
+ info->socket= vio->sd;
+ return;
+ case VIO_TYPE_SOCKET:
+ info->protocol= MYSQL_VIO_SOCKET;
+ info->socket= vio->sd;
+ return;
+ case VIO_TYPE_SSL:
+ {
+ struct sockaddr addr;
+ socklen_t addrlen= sizeof(addr);
+ if (getsockname(vio->sd, &addr, &addrlen))
+ return;
+ info->protocol= addr.sa_family == AF_UNIX ?
+ MYSQL_VIO_SOCKET : MYSQL_VIO_TCP;
+ info->socket= vio->sd;
+ return;
+ }
+#ifdef _WIN32
+ case VIO_TYPE_NAMEDPIPE:
+ info->protocol= MYSQL_VIO_PIPE;
+ info->handle= vio->hPipe;
+ return;
+#ifdef HAVE_SMEM
+ case VIO_TYPE_SHARED_MEMORY:
+ info->protocol= MYSQL_VIO_MEMORY;
+ info->handle= vio->handle_file_map; /* or what ? */
+ return;
+#endif
+#endif
+ default: DBUG_ASSERT(0);
+ }
+}
+
+static void client_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_PLUGIN_VIO_INFO *info)
+{
+ MCPVIO_EXT *mpvio= (MCPVIO_EXT*)vio;
+ mpvio_info(mpvio->mysql->net.vio, info);
+}
+
+/**
+ Client side of the plugin driver authentication.
+
+ @note this is used by both the mysql_real_connect and mysql_change_user
+
+ @param mysql mysql
+ @param data pointer to the plugin auth data (scramble) in the
+ handshake packet
+ @param data_len the length of the data
+ @param data_plugin a plugin that data were prepared for
+ or 0 if it's mysql_change_user()
+ @param db initial db to use, can be 0
+
+ @retval 0 ok
+ @retval 1 error
+*/
+int run_plugin_auth(MYSQL *mysql, char *data, uint data_len,
+ const char *data_plugin, const char *db)
+{
+ const char *auth_plugin_name;
+ auth_plugin_t *auth_plugin;
+ MCPVIO_EXT mpvio;
+ ulong pkt_length;
+ int res;
+
+ DBUG_ENTER ("run_plugin_auth");
+ /* determine the default/initial plugin to use */
+ if (mysql->options.extension && mysql->options.extension->default_auth &&
+ mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ auth_plugin_name= mysql->options.extension->default_auth;
+ if (!(auth_plugin= (auth_plugin_t*) mysql_client_find_plugin(mysql,
+ auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+ DBUG_RETURN (1); /* oops, not found */
+ }
+ else
+ {
+ auth_plugin= mysql->server_capabilities & CLIENT_PROTOCOL_41 ?
+ &native_password_client_plugin : &old_password_client_plugin;
+ auth_plugin_name= auth_plugin->name;
+ }
+
+ DBUG_PRINT ("info", ("using plugin %s", auth_plugin_name));
+
+ mysql->net.last_errno= 0; /* just in case */
+
+ if (data_plugin && strcmp(data_plugin, auth_plugin_name))
+ {
+ /* data was prepared for a different plugin, don't show it to this one */
+ data= 0;
+ data_len= 0;
+ }
+
+ mpvio.mysql_change_user= data_plugin == 0;
+ mpvio.cached_server_reply.pkt= (uchar*)data;
+ mpvio.cached_server_reply.pkt_len= data_len;
+ mpvio.read_packet= client_mpvio_read_packet;
+ mpvio.write_packet= client_mpvio_write_packet;
+ mpvio.info= client_mpvio_info;
+ mpvio.mysql= mysql;
+ mpvio.packets_read= mpvio.packets_written= 0;
+ mpvio.db= db;
+ mpvio.plugin= auth_plugin;
+
+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+ DBUG_PRINT ("info", ("authenticate_user returned %s",
+ res == CR_OK ? "CR_OK" :
+ res == CR_ERROR ? "CR_ERROR" :
+ res == CR_OK_HANDSHAKE_COMPLETE ?
+ "CR_OK_HANDSHAKE_COMPLETE" : "error"));
+
+ compile_time_assert(CR_OK == -1);
+ compile_time_assert(CR_ERROR == 0);
+ if (res > CR_OK && mysql->net.read_pos[0] != 254)
+ {
+ /*
+ the plugin returned an error. write it down in mysql,
+ unless the error code is CR_ERROR and mysql->net.last_errno
+ is already set (the plugin has done it)
+ */
+ DBUG_PRINT ("info", ("res=%d", res));
+ if (res > CR_ERROR)
+ set_mysql_error(mysql, res, unknown_sqlstate);
+ else
+ if (!mysql->net.last_errno)
+ set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+ DBUG_RETURN (1);
+ }
+
+ /* read the OK packet (or use the cached value in mysql->net.read_pos */
+ if (res == CR_OK)
+ pkt_length= (*mysql->methods->read_change_user_result)(mysql);
+ else /* res == CR_OK_HANDSHAKE_COMPLETE */
+ pkt_length= mpvio.last_read_packet_len;
+
+ DBUG_PRINT ("info", ("OK packet length=%lu", pkt_length));
+ if (pkt_length == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "reading authorization packet",
+ errno);
+ DBUG_RETURN (1);
+ }
+
+ if (mysql->net.read_pos[0] == 254)
+ {
+ /* The server asked to use a different authentication plugin */
+ if (pkt_length == 1)
+ {
+ /* old "use short scramble" packet */
+ DBUG_PRINT ("info", ("old use short scramble packet from server"));
+ auth_plugin_name= old_password_plugin_name;
+ mpvio.cached_server_reply.pkt= (uchar*)mysql->scramble;
+ mpvio.cached_server_reply.pkt_len= SCRAMBLE_LENGTH + 1;
+ }
+ else
+ {
+ /* new "use different plugin" packet */
+ uint len;
+ auth_plugin_name= (char*)mysql->net.read_pos + 1;
+ len= strlen(auth_plugin_name); /* safe as my_net_read always appends \0 */
+ mpvio.cached_server_reply.pkt_len= pkt_length - len - 2;
+ mpvio.cached_server_reply.pkt= mysql->net.read_pos + len + 2;
+ DBUG_PRINT ("info", ("change plugin packet from server for plugin %s",
+ auth_plugin_name));
+ }
+
+ if (!(auth_plugin= (auth_plugin_t *) mysql_client_find_plugin(mysql,
+ auth_plugin_name, MYSQL_CLIENT_AUTHENTICATION_PLUGIN)))
+ DBUG_RETURN (1);
+
+ mpvio.plugin= auth_plugin;
+ res= auth_plugin->authenticate_user((struct st_plugin_vio *)&mpvio, mysql);
+
+ DBUG_PRINT ("info", ("second authenticate_user returned %s",
+ res == CR_OK ? "CR_OK" :
+ res == CR_ERROR ? "CR_ERROR" :
+ res == CR_OK_HANDSHAKE_COMPLETE ?
+ "CR_OK_HANDSHAKE_COMPLETE" : "error"));
+ if (res > CR_OK)
+ {
+ if (res > CR_ERROR)
+ set_mysql_error(mysql, res, unknown_sqlstate);
+ else
+ if (!mysql->net.last_errno)
+ set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate);
+ DBUG_RETURN (1);
+ }
+
+ if (res != CR_OK_HANDSHAKE_COMPLETE)
+ {
+ /* Read what server thinks about out new auth message report */
+ if (cli_safe_read(mysql) == packet_error)
+ {
+ if (mysql->net.last_errno == CR_SERVER_LOST)
+ set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
+ ER(CR_SERVER_LOST_EXTENDED),
+ "reading final connect information",
+ errno);
+ DBUG_RETURN (1);
+ }
+ }
+ }
+ /*
+ net->read_pos[0] should always be 0 here if the server implements
+ the protocol correctly
+ */
+ DBUG_RETURN (mysql->net.read_pos[0] != 0);
+}
MYSQL * STDCALL
CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
@@ -2228,7 +2917,10 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
uint port, const char *unix_socket,ulong client_flag)
{
char buff[NAME_LEN+USERNAME_LENGTH+100];
- char *end,*host_info= NULL;
+ int scramble_data_len, pkt_scramble_len;
+ char *end,*host_info= 0, *server_version_end, *pkt_end;
+ char *scramble_data;
+ const char *scramble_plugin;
ulong pkt_length;
NET *net= &mysql->net;
#ifdef MYSQL_SERVER
@@ -2600,8 +3292,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
errno);
goto error;
}
+ pkt_end= (char*)net->read_pos + pkt_length;
/* Check if version of protocol matches current one */
-
mysql->protocol_version= net->read_pos[0];
DBUG_DUMP("packet",(uchar*) net->read_pos,10);
DBUG_PRINT("info",("mysql protocol version %d, server=%d",
@@ -2613,31 +3305,29 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
PROTOCOL_VERSION);
goto error;
}
- end=strend((char*) net->read_pos+1);
+ server_version_end= end= strend((char*) net->read_pos+1);
mysql->thread_id=uint4korr(end+1);
end+=5;
/*
- Scramble is split into two parts because old clients does not understand
+ Scramble is split into two parts because old clients do not understand
long scrambles; here goes the first part.
*/
- strmake(mysql->scramble, end, SCRAMBLE_LENGTH_323);
- end+= SCRAMBLE_LENGTH_323+1;
+ scramble_data= end;
+ scramble_data_len= SCRAMBLE_LENGTH_323 + 1;
+ scramble_plugin= old_password_plugin_name;
+ end+= scramble_data_len;
- if (pkt_length >= (uint) (end+1 - (char*) net->read_pos))
+ if (pkt_end >= end + 1)
mysql->server_capabilities=uint2korr(end);
- if (pkt_length >= (uint) (end+18 - (char*) net->read_pos))
+ if (pkt_end >= end + 18)
{
/* New protocol with 16 bytes to describe server characteristics */
mysql->server_language=end[2];
mysql->server_status=uint2korr(end+3);
+ mysql->server_capabilities|= uint2korr(end+5) << 16;
+ pkt_scramble_len= end[7];
}
end+= 18;
- if (pkt_length >= (uint) (end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1 -
- (char *) net->read_pos))
- strmake(mysql->scramble+SCRAMBLE_LENGTH_323, end,
- SCRAMBLE_LENGTH-SCRAMBLE_LENGTH_323);
- else
- mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
if (mysql->options.secure_auth && passwd[0] &&
!(mysql->server_capabilities & CLIENT_SECURE_CONNECTION))
@@ -2656,7 +3346,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
&mysql->unix_socket,unix_socket ?
(uint) strlen(unix_socket)+1 : (uint) 1,
&mysql->server_version,
- (uint) (end - (char*) net->read_pos),
+ (uint) (server_version_end - (char*) net->read_pos + 1),
NullS) ||
!(mysql->user=my_strdup(user,MYF(0))) ||
!(mysql->passwd=my_strdup(passwd,MYF(0))))
@@ -2673,198 +3363,47 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
strmov(mysql->server_version,(char*) net->read_pos+1);
mysql->port=port;
- /*
- Part 2: format and send client info to the server for access check
- */
-
- client_flag|=mysql->options.client_flag;
- client_flag|=CLIENT_CAPABILITIES;
- if (client_flag & CLIENT_MULTI_STATEMENTS)
- client_flag|= CLIENT_MULTI_RESULTS;
-
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- if (mysql->options.ssl_key || mysql->options.ssl_cert ||
- mysql->options.ssl_ca || mysql->options.ssl_capath ||
- mysql->options.ssl_cipher)
- mysql->options.use_ssl= 1;
- if (mysql->options.use_ssl)
- client_flag|=CLIENT_SSL;
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY*/
- if (db)
- client_flag|=CLIENT_CONNECT_WITH_DB;
-
- /* Remove options that server doesn't support */
- client_flag= ((client_flag &
- ~(CLIENT_COMPRESS | CLIENT_SSL | CLIENT_PROTOCOL_41)) |
- (client_flag & mysql->server_capabilities));
-#ifndef HAVE_COMPRESS
- client_flag&= ~CLIENT_COMPRESS;
-#endif
-
- if (client_flag & CLIENT_PROTOCOL_41)
- {
- /* 4.1 server and 4.1 client has a 32 byte option flag */
- int4store(buff,client_flag);
- int4store(buff+4, net->max_packet_size);
- buff[8]= (char) mysql->charset->number;
- bzero(buff+9, 32-9);
- end= buff+32;
- }
- else
- {
- int2store(buff,client_flag);
- int3store(buff+2,net->max_packet_size);
- end= buff+5;
- }
- mysql->client_flag=client_flag;
-
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- if (client_flag & CLIENT_SSL)
+ if (pkt_end >= end + SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323 + 1)
{
- /* Do the SSL layering. */
- struct st_mysql_options *options= &mysql->options;
- struct st_VioSSLFd *ssl_fd;
-
/*
- Send client_flag, max_packet_size - unencrypted otherwise
- the server does not know we want to do SSL
+ move the first scramble part - directly in the NET buffer -
+ to get a full continuous scramble. We've read all the header,
+ and can overwrite it now.
*/
- if (my_net_write(net, (uchar*) buff, (uint) (end-buff)) || net_flush(net))
- {
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending connection information to server",
- errno);
- goto error;
- }
-
- /* Create the VioSSLConnectorFd - init SSL and load certs */
- if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key,
- options->ssl_cert,
- options->ssl_ca,
- options->ssl_capath,
- options->ssl_cipher)))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
- mysql->connector_fd= (void*)ssl_fd;
-
- /* Connect to the server */
- DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslconnect(ssl_fd, mysql->net.vio,
- (long) (mysql->options.connect_timeout)))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
- DBUG_PRINT("info", ("IO layer change done!"));
-
- /* Verify server cert */
- if ((client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) &&
- ssl_verify_server_cert(mysql->net.vio, mysql->host))
- {
- set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate);
- goto error;
- }
-
- }
-#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
-
- DBUG_PRINT("info",("Server version = '%s' capabilites: %lu status: %u client_flag: %lu",
- mysql->server_version,mysql->server_capabilities,
- mysql->server_status, client_flag));
- /* This needs to be changed as it's not useful with big packets */
- if (user && user[0])
- strmake(end,user,USERNAME_LENGTH); /* Max user name */
- else
- read_user_name((char*) end);
-
- /* We have to handle different version of handshake here */
-#ifdef _CUSTOMCONFIG_
-#include "_cust_libmysql.h"
-#endif
- DBUG_PRINT("info",("user: %s",end));
- end= strend(end) + 1;
- if (passwd[0])
- {
- if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
+ memmove(end - SCRAMBLE_LENGTH_323, scramble_data,
+ SCRAMBLE_LENGTH_323);
+ scramble_data= end - SCRAMBLE_LENGTH_323;
+ if (mysql->server_capabilities & CLIENT_PLUGIN_AUTH)
{
- *end++= SCRAMBLE_LENGTH;
- scramble(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH;
+ scramble_data_len= pkt_scramble_len;
+ scramble_plugin= scramble_data + scramble_data_len;
+ if (scramble_data + scramble_data_len > pkt_end)
+ scramble_data_len= pkt_end - scramble_data;
}
else
{
- scramble_323(end, mysql->scramble, passwd);
- end+= SCRAMBLE_LENGTH_323 + 1;
+ scramble_data_len= pkt_end - scramble_data;
+ scramble_plugin= native_password_plugin_name;
}
}
else
- *end++= '\0'; /* empty password */
+ mysql->server_capabilities&= ~CLIENT_SECURE_CONNECTION;
+
+ mysql->client_flag= client_flag;
- /* Add database if needed */
- if (db && (mysql->server_capabilities & CLIENT_CONNECT_WITH_DB))
- {
- end= strmake(end, db, NAME_LEN) + 1;
- mysql->db= my_strdup(db,MYF(MY_WME));
- db= 0;
- }
- /* Write authentication package */
- if (my_net_write(net, (uchar*) buff, (size_t) (end-buff)) || net_flush(net))
- {
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending authentication information",
- errno);
- goto error;
- }
-
/*
- Part 3: Authorization data's been sent. Now server can reply with
- OK-packet, or re-request scrambled password.
+ Part 2: invoke the plugin to send the authentication data to the server
*/
- if ((pkt_length=cli_safe_read(mysql)) == packet_error)
- {
- if (mysql->net.last_errno == CR_SERVER_LOST)
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "reading authorization packet",
- errno);
+ if (run_plugin_auth(mysql, scramble_data, scramble_data_len,
+ scramble_plugin, db))
goto error;
- }
- if (pkt_length == 1 && net->read_pos[0] == 254 &&
- mysql->server_capabilities & CLIENT_SECURE_CONNECTION)
- {
- /*
- By sending this very specific reply server asks us to send scrambled
- password in old format.
- */
- scramble_323(buff, mysql->scramble, passwd);
- if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) ||
- net_flush(net))
- {
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "sending password information",
- errno);
- goto error;
- }
- /* Read what server thinks about out new auth message report */
- if (cli_safe_read(mysql) == packet_error)
- {
- if (mysql->net.last_errno == CR_SERVER_LOST)
- set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
- ER(CR_SERVER_LOST_EXTENDED),
- "reading final connect information",
- errno);
- goto error;
- }
- }
+ /*
+ Part 3: authenticated, finish the initialization of the connection
+ */
- if (client_flag & CLIENT_COMPRESS) /* We will use compression */
+ if (mysql->client_flag & CLIENT_COMPRESS) /* We will use compression */
net->compress=1;
#ifdef CHECK_LICENSE
@@ -2872,7 +3411,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
goto error;
#endif
- if (db && mysql_select_db(mysql, db))
+ if (db && !mysql->db && mysql_select_db(mysql, db))
{
if (mysql->net.last_errno == CR_SERVER_LOST)
set_mysql_extended_error(mysql, CR_SERVER_LOST, unknown_sqlstate,
@@ -2933,7 +3472,7 @@ error:
/* Free alloced memory */
end_server(mysql);
mysql_close_free(mysql);
- if (!(((ulong) client_flag) & CLIENT_REMEMBER_OPTIONS))
+ if (!(client_flag & CLIENT_REMEMBER_OPTIONS))
mysql_close_free_options(mysql);
}
DBUG_RETURN(0);
@@ -3054,6 +3593,12 @@ static void mysql_close_free_options(MYSQL *mysql)
if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
my_free(mysql->options.shared_memory_base_name);
#endif /* HAVE_SMEM */
+ if (mysql->options.extension)
+ {
+ my_free(mysql->options.extension->plugin_dir);
+ my_free(mysql->options.extension->default_auth);
+ my_free(mysql->options.extension);
+ }
bzero((char*) &mysql->options,sizeof(mysql->options));
DBUG_VOID_RETURN;
}
@@ -3528,6 +4073,12 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
else
mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT;
break;
+ case MYSQL_PLUGIN_DIR:
+ EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg);
+ break;
+ case MYSQL_DEFAULT_AUTH:
+ EXTENSION_SET_STRING(&mysql->options, default_auth, arg);
+ break;
default:
DBUG_RETURN(1);
}
@@ -3631,4 +4182,104 @@ int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
return mysql->net.last_errno;
}
+/**
+ client authentication plugin that does native MySQL authentication
+ using a 20-byte (4.1+) scramble
+*/
+static int native_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ int pkt_len;
+ uchar *pkt;
+
+ DBUG_ENTER ("native_password_auth_client");
+
+
+ if (((MCPVIO_EXT *)vio)->mysql_change_user)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet.
+ we use the old scramble.
+ */
+ pkt= (uchar*)mysql->scramble;
+ pkt_len= SCRAMBLE_LENGTH + 1;
+ }
+ else
+ {
+ /* read the scramble */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ if (pkt_len != SCRAMBLE_LENGTH + 1)
+ DBUG_RETURN (CR_SERVER_HANDSHAKE_ERR);
+
+ /* save it in MYSQL */
+ memcpy(mysql->scramble, pkt, SCRAMBLE_LENGTH);
+ mysql->scramble[SCRAMBLE_LENGTH] = 0;
+ }
+
+ if (mysql->passwd[0])
+ {
+ char scrambled[SCRAMBLE_LENGTH + 1];
+ DBUG_PRINT ("info", ("sending scramble"));
+ scramble(scrambled, (char*)pkt, mysql->passwd);
+ if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH))
+ DBUG_RETURN (CR_ERROR);
+ }
+ else
+ {
+ DBUG_PRINT ("info", ("no password"));
+ if (vio->write_packet(vio, 0, 0)) /* no password */
+ DBUG_RETURN (CR_ERROR);
+ }
+
+ DBUG_RETURN (CR_OK);
+}
+
+/**
+ client authentication plugin that does old MySQL authentication
+ using an 8-byte (4.0-) scramble
+*/
+static int old_password_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+ uchar *pkt;
+ int pkt_len;
+
+ DBUG_ENTER ("old_password_auth_client");
+
+ if (((MCPVIO_EXT *)vio)->mysql_change_user)
+ {
+ /*
+ in mysql_change_user() the client sends the first packet.
+ we use the old scramble.
+ */
+ pkt= (uchar*)mysql->scramble;
+ pkt_len= SCRAMBLE_LENGTH_323 + 1;
+ }
+ else
+ {
+ /* read the scramble */
+ if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
+ return CR_ERROR;
+
+ if (pkt_len != SCRAMBLE_LENGTH_323 + 1 &&
+ pkt_len != SCRAMBLE_LENGTH + 1)
+ DBUG_RETURN (CR_SERVER_HANDSHAKE_ERR);
+ /* save it in MYSQL */
+ memcpy(mysql->scramble, pkt, pkt_len);
+ mysql->scramble[pkt_len] = 0;
+ }
+
+ if (mysql->passwd[0])
+ {
+ char scrambled[SCRAMBLE_LENGTH_323 + 1];
+ scramble_323(scrambled, (char*)pkt, mysql->passwd);
+ if (vio->write_packet(vio, (uchar*)scrambled, SCRAMBLE_LENGTH_323 + 1))
+ DBUG_RETURN (CR_ERROR);
+ }
+ else
+ if (vio->write_packet(vio, 0, 0)) /* no password */
+ DBUG_RETURN (CR_ERROR);
+
+ DBUG_RETURN (CR_OK);
+}
diff --git a/sql-common/client_plugin.c b/sql-common/client_plugin.c
new file mode 100644
index 00000000000..9526f5fdc4b
--- /dev/null
+++ b/sql-common/client_plugin.c
@@ -0,0 +1,453 @@
+/* Copyright (C) 2010 Sun Microsystems, Inc.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/**
+ @file
+
+ Support code for the client side (libmysql) plugins
+
+ Client plugins are somewhat different from server plugins, they are simpler.
+
+ They do not need to be installed or in any way explicitly loaded on the
+ client, they are loaded automatically on demand.
+ One client plugin per shared object, soname *must* match the plugin name.
+
+ There is no reference counting and no unloading either.
+*/
+
+#include <my_global.h>
+#include "mysql.h"
+#include <my_sys.h>
+#include <m_string.h>
+#ifdef THREAD
+#include <my_pthread.h>
+#else
+#include <my_no_pthread.h>
+#endif
+
+#include <sql_common.h>
+#include "errmsg.h"
+#include <mysql/client_plugin.h>
+
+struct st_client_plugin_int {
+ struct st_client_plugin_int *next;
+ void *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+};
+
+static my_bool initialized= 0;
+static MEM_ROOT mem_root;
+
+static const char *plugin_declarations_sym= "_mysql_client_plugin_declaration_";
+static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]=
+{
+ 0, /* these two are taken by Connector/C */
+ 0, /* these two are taken by Connector/C */
+ MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION
+};
+
+/*
+ Loaded plugins are stored in a linked list.
+ The list is append-only, the elements are added to the head (like in a stack).
+ The elements are added under a mutex, but the list can be read and traversed
+ without any mutex because once an element is added to the list, it stays
+ there. The main purpose of a mutex is to prevent two threads from
+ loading the same plugin twice in parallel.
+*/
+struct st_client_plugin_int *plugin_list[MYSQL_CLIENT_MAX_PLUGINS];
+#ifdef THREAD
+static pthread_mutex_t LOCK_load_client_plugin;
+#endif
+
+static int is_not_initialized(MYSQL *mysql, const char *name)
+{
+ if (initialized)
+ return 0;
+
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ name, "not initialized");
+ return 1;
+}
+
+/**
+ finds a plugin in the list
+
+ @param name plugin name to search for
+ @param type plugin type
+
+ @note this does NOT necessarily need a mutex, take care!
+
+ @retval a pointer to a found plugin or 0
+*/
+static struct st_mysql_client_plugin *
+find_plugin(const char *name, int type)
+{
+ struct st_client_plugin_int *p;
+
+ DBUG_ASSERT(initialized);
+ DBUG_ASSERT(type >= 0 && type < MYSQL_CLIENT_MAX_PLUGINS);
+ if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+ return 0;
+
+ for (p= plugin_list[type]; p; p= p->next)
+ {
+ if (strcmp(p->plugin->name, name) == 0)
+ return p->plugin;
+ }
+ return NULL;
+}
+
+/**
+ verifies the plugin and adds it to the list
+
+ @param mysql MYSQL structure (for error reporting)
+ @param plugin plugin to install
+ @param dlhandle a handle to the shared object (returned by dlopen)
+ or 0 if the plugin was not dynamically loaded
+ @param argc number of arguments in the 'va_list args'
+ @param args arguments passed to the plugin initialization function
+
+ @retval a pointer to an installed plugin or 0
+*/
+static struct st_mysql_client_plugin *
+add_plugin(MYSQL *mysql, struct st_mysql_client_plugin *plugin, void *dlhandle,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ struct st_client_plugin_int plugin_int, *p;
+ char errbuf[1024];
+
+ DBUG_ASSERT(initialized);
+
+ plugin_int.plugin= plugin;
+ plugin_int.dlhandle= dlhandle;
+
+ if (plugin->type >= MYSQL_CLIENT_MAX_PLUGINS)
+ {
+ errmsg= "Unknown client plugin type";
+ goto err1;
+ }
+
+ if (plugin->interface_version < plugin_version[plugin->type] ||
+ (plugin->interface_version >> 8) >
+ (plugin_version[plugin->type] >> 8))
+ {
+ errmsg= "Incompatible client plugin interface";
+ goto err1;
+ }
+
+ /* Call the plugin initialization function, if any */
+ if (plugin->init && plugin->init(errbuf, sizeof(errbuf), argc, args))
+ {
+ errmsg= errbuf;
+ goto err1;
+ }
+
+ p= (struct st_client_plugin_int *)
+ memdup_root(&mem_root, &plugin_int, sizeof(plugin_int));
+
+ if (!p)
+ {
+ errmsg= "Out of memory";
+ goto err2;
+ }
+
+ safe_mutex_assert_owner(&LOCK_load_client_plugin);
+
+ p->next= plugin_list[plugin->type];
+ plugin_list[plugin->type]= p;
+
+ return plugin;
+
+err2:
+ if (plugin->deinit)
+ plugin->deinit();
+err1:
+ if (dlhandle)
+ dlclose(dlhandle);
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), plugin->name,
+ errmsg);
+ return NULL;
+}
+
+/**
+ Loads plugins which are specified in the environment variable
+ LIBMYSQL_PLUGINS.
+
+ Multiple plugins must be separated by semicolon. This function doesn't
+ return or log an error.
+
+ The function is be called by mysql_client_plugin_init
+
+ @todo
+ Support extended syntax, passing parameters to plugins, for example
+ LIBMYSQL_PLUGINS="plugin1(param1,param2);plugin2;..."
+ or
+ LIBMYSQL_PLUGINS="plugin1=int:param1,str:param2;plugin2;..."
+*/
+static void load_env_plugins(MYSQL *mysql)
+{
+ char *plugs, *free_env, *s= getenv("LIBMYSQL_PLUGINS");
+
+ /* no plugins to load */
+ if(!s)
+ return;
+
+ free_env= plugs= my_strdup(s, MYF(MY_WME));
+
+ do {
+ if ((s= strchr(plugs, ';')))
+ *s= '\0';
+ mysql_load_plugin(mysql, plugs, -1, 0);
+ plugs= s + 1;
+ } while (s);
+
+ my_free(free_env);
+}
+
+/********** extern functions to be used by libmysql *********************/
+
+/**
+ Initializes the client plugin layer.
+
+ This function must be called before any other client plugin function.
+
+ @retval 0 successful
+ @retval != 0 error occured
+*/
+int mysql_client_plugin_init()
+{
+ MYSQL mysql;
+ struct st_mysql_client_plugin **builtin;
+
+ if (initialized)
+ return 0;
+
+ bzero(&mysql, sizeof(mysql)); /* dummy mysql for set_mysql_extended_error */
+
+ pthread_mutex_init(&LOCK_load_client_plugin, MY_MUTEX_INIT_SLOW);
+ init_alloc_root(&mem_root, 128, 128);
+
+ bzero(&plugin_list, sizeof(plugin_list));
+
+ initialized= 1;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ for (builtin= mysql_client_builtins; *builtin; builtin++)
+ add_plugin(&mysql, *builtin, 0, 0, 0);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ load_env_plugins(&mysql);
+
+ return 0;
+}
+
+/**
+ Deinitializes the client plugin layer.
+
+ Unloades all client plugins and frees any associated resources.
+*/
+void mysql_client_plugin_deinit()
+{
+ int i;
+ struct st_client_plugin_int *p;
+
+ if (!initialized)
+ return;
+
+ for (i=0; i < MYSQL_CLIENT_MAX_PLUGINS; i++)
+ for (p= plugin_list[i]; p; p= p->next)
+ {
+ if (p->plugin->deinit)
+ p->plugin->deinit();
+ if (p->dlhandle)
+ dlclose(p->dlhandle);
+ }
+
+ bzero(&plugin_list, sizeof(plugin_list));
+ initialized= 0;
+ free_root(&mem_root, MYF(0));
+ pthread_mutex_destroy(&LOCK_load_client_plugin);
+}
+
+/************* public facing functions, for client consumption *********/
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_register_plugin(MYSQL *mysql,
+ struct st_mysql_client_plugin *plugin)
+{
+ if (is_not_initialized(mysql, plugin->name))
+ return NULL;
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (find_plugin(plugin->name, plugin->type))
+ {
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD,
+ unknown_sqlstate, ER(CR_AUTH_PLUGIN_CANNOT_LOAD),
+ plugin->name, "it is already loaded");
+ plugin= NULL;
+ }
+ else
+ plugin= add_plugin(mysql, plugin, 0, 0, 0);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ return plugin;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin_v(MYSQL *mysql, const char *name, int type,
+ int argc, va_list args)
+{
+ const char *errmsg;
+ char dlpath[FN_REFLEN+1];
+ void *sym, *dlhandle;
+ struct st_mysql_client_plugin *plugin;
+
+ DBUG_ENTER ("mysql_load_plugin_v");
+ DBUG_PRINT ("entry", ("name=%s type=%d int argc=%d", name, type, argc));
+ if (is_not_initialized(mysql, name))
+ {
+ DBUG_PRINT ("leave", ("mysql not initialized"));
+ DBUG_RETURN (NULL);
+ }
+
+ pthread_mutex_lock(&LOCK_load_client_plugin);
+
+ /* make sure the plugin wasn't loaded meanwhile */
+ if (type >= 0 && find_plugin(name, type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ /* Compile dll path */
+ strxnmov(dlpath, sizeof(dlpath) - 1,
+ mysql->options.extension && mysql->options.extension->plugin_dir ?
+ mysql->options.extension->plugin_dir : PLUGINDIR, "/",
+ name, SO_EXT, NullS);
+
+ DBUG_PRINT ("info", ("dlopeninig %s", dlpath));
+ /* Open new dll handle */
+ if (!(dlhandle= dlopen(dlpath, RTLD_NOW)))
+ {
+#if defined(__APPLE__)
+ /* Apple supports plugins with .so also, so try this as well */
+ strxnmov(dlpath, sizeof(dlpath) - 1,
+ mysql->options.extension && mysql->options.extension->plugin_dir ?
+ mysql->options.extension->plugin_dir : PLUGINDIR, "/",
+ name, ".so", NullS);
+ if ((dlhandle= dlopen(dlpath, RTLD_NOW)))
+ goto have_plugin;
+#endif
+ DBUG_PRINT ("info", ("failed to dlopen"));
+ errmsg= dlerror();
+ goto err;
+ }
+
+#if defined(__APPLE__)
+have_plugin:
+#endif
+ if (!(sym= dlsym(dlhandle, plugin_declarations_sym)))
+ {
+ errmsg= "not a plugin";
+ dlclose(dlhandle);
+ goto err;
+ }
+
+ plugin= (struct st_mysql_client_plugin*)sym;
+
+ if (type >=0 && type != plugin->type)
+ {
+ errmsg= "type mismatch";
+ goto err;
+ }
+
+ if (strcmp(name, plugin->name))
+ {
+ errmsg= "name mismatch";
+ goto err;
+ }
+
+ if (type < 0 && find_plugin(name, plugin->type))
+ {
+ errmsg= "it is already loaded";
+ goto err;
+ }
+
+ plugin= add_plugin(mysql, plugin, dlhandle, argc, args);
+
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+
+ DBUG_PRINT ("leave", ("plugin loaded ok"));
+ DBUG_RETURN (plugin);
+
+err:
+ pthread_mutex_unlock(&LOCK_load_client_plugin);
+ DBUG_PRINT ("leave", ("plugin load error : %s", errmsg));
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name, errmsg);
+ DBUG_RETURN (NULL);
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_load_plugin(MYSQL *mysql, const char *name, int type, int argc, ...)
+{
+ struct st_mysql_client_plugin *p;
+ va_list args;
+ va_start(args, argc);
+ p= mysql_load_plugin_v(mysql, name, type, argc, args);
+ va_end(args);
+ return p;
+}
+
+/* see <mysql/client_plugin.h> for a full description */
+struct st_mysql_client_plugin *
+mysql_client_find_plugin(MYSQL *mysql, const char *name, int type)
+{
+ struct st_mysql_client_plugin *p;
+
+ DBUG_ENTER ("mysql_client_find_plugin");
+ DBUG_PRINT ("entry", ("name=%s, type=%d", name, type));
+ if (is_not_initialized(mysql, name))
+ DBUG_RETURN (NULL);
+
+ if (type < 0 || type >= MYSQL_CLIENT_MAX_PLUGINS)
+ {
+ set_mysql_extended_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, unknown_sqlstate,
+ ER(CR_AUTH_PLUGIN_CANNOT_LOAD), name,
+ "invalid type");
+ }
+
+ if ((p= find_plugin(name, type)))
+ {
+ DBUG_PRINT ("leave", ("found %p", p));
+ DBUG_RETURN (p);
+ }
+
+ /* not found, load it */
+ p= mysql_load_plugin(mysql, name, type, 0);
+ DBUG_PRINT ("leave", ("loaded %p", p));
+ DBUG_RETURN (p);
+}
+
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index e8a594c4d8b..d290ad79e04 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -52,6 +52,7 @@ SET (SQL_SOURCE
log_event_old.cc rpl_record_old.cc
message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c
mysqld.cc net_serv.cc keycaches.cc
+ ../sql-common/client_plugin.c
opt_range.cc opt_range.h opt_sum.cc
../sql-common/pack.c parse_file.cc password.c procedure.cc
protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 7fed55f3cd6..b31a6e3c3e0 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -173,7 +173,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
rpl_handler.cc mdl.cc transaction.cc sql_audit.cc \
sha2.cc
-nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c
+nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c client_plugin.c
libndb_la_CPPFLAGS= @ndbcluster_includes@
libndb_la_SOURCES= ha_ndbcluster.cc \
@@ -187,10 +187,10 @@ mysql_tzinfo_to_sql_SOURCES = tztime.cc
mysql_tzinfo_to_sql_CXXFLAGS= -DTZINFO2SQL
DEFS = -DMYSQL_SERVER \
- -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
- -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \
- -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \
- -DPLUGINDIR="\"$(pkgplugindir)\"" \
+ -DDEFAULT_MYSQL_HOME='"$(MYSQLBASEdir)"' \
+ -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \
+ -DSHAREDIR='"$(MYSQLSHAREdir)"' \
+ -DPLUGINDIR='"$(pkgplugindir)"' \
-DHAVE_EVENT_SCHEDULER \
@DEFS@
@@ -214,6 +214,8 @@ link_sources:
@LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c
rm -f client.c
@LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c
+ rm -f client_plugin.c
+ @LN_CP_F@ $(top_srcdir)/sql-common/client_plugin.c client_plugin.c
rm -f my_time.c
@LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c
rm -f my_user.c
diff --git a/sql/client_settings.h b/sql/client_settings.h
index 7d103d5904d..ff35cff2440 100644
--- a/sql/client_settings.h
+++ b/sql/client_settings.h
@@ -21,6 +21,7 @@
#endif /* CLIENT_SETTINGS_INCLUDED */
#include <thr_alarm.h>
+#include <sql_common.h>
#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS | \
@@ -33,7 +34,8 @@
#undef HAVE_SMEM
#undef _CUSTOMCONFIG_
-#define mysql_server_init(a,b,c) 0
+#define mysql_server_init(a,b,c) mysql_client_plugin_init()
+#define mysql_server_end() mysql_client_plugin_deinit()
#ifdef HAVE_REPLICATION
C_MODE_START
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index d4a98265c49..9d394d740a8 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -9516,7 +9516,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused)))
thd->client_capabilities = 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
- thd->main_security_ctx.priv_user = 0;
+ thd->main_security_ctx.priv_user[0] = 0;
/* Do not use user-supplied timeout value for system threads. */
thd->variables.lock_wait_timeout= LONG_TIMEOUT;
diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
index 26fdb8e1425..1f3b707be91 100644
--- a/sql/ha_ndbcluster_binlog.cc
+++ b/sql/ha_ndbcluster_binlog.cc
@@ -3681,7 +3681,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg)
thd->client_capabilities= 0;
my_net_init(&thd->net, 0);
thd->main_security_ctx.master_access= ~0;
- thd->main_security_ctx.priv_user= 0;
+ thd->main_security_ctx.priv_user[0]= 0;
/* Do not use user-supplied timeout value for system threads. */
thd->variables.lock_wait_timeout= LONG_TIMEOUT;
diff --git a/sql/lex.h b/sql/lex.h
index fbedddc6941..6d5d711eb60 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -420,8 +420,9 @@ static SYMBOL symbols[] = {
{ "PROCEDURE", SYM(PROCEDURE_SYM)},
{ "PROCESS" , SYM(PROCESS)},
{ "PROCESSLIST", SYM(PROCESSLIST_SYM)},
- { "PROFILE", SYM(PROFILE_SYM)},
- { "PROFILES", SYM(PROFILES_SYM)},
+ { "PROFILE", SYM(PROFILE_SYM)},
+ { "PROFILES", SYM(PROFILES_SYM)},
+ { "PROXY", SYM(PROXY_SYM)},
{ "PURGE", SYM(PURGE)},
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index afc515a9d8c..5412a424c9c 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -58,6 +58,7 @@
#include "sql_repl.h"
#include "rpl_filter.h"
#include "repl_failsafe.h"
+#include <sql_common.h>
#include <my_stacktrace.h>
#include "mysqld_suffix.h"
#include "mysys_err.h"
@@ -1479,6 +1480,7 @@ void clean_up(bool print_message)
sql_print_information(ER_DEFAULT(ER_SHUTDOWN_COMPLETE),my_progname);
cleanup_errmsgs();
thread_scheduler.end();
+ mysql_client_plugin_deinit();
finish_client_errs();
DBUG_PRINT("quit", ("Error messages freed"));
/* Tell main we are ready */
@@ -3335,6 +3337,7 @@ static int init_common_variables()
if (init_errmessage()) /* Read error messages from file */
return 1;
init_client_errs();
+ mysql_client_plugin_init();
lex_init();
if (item_create_init())
return 1;
diff --git a/sql/password.c b/sql/password.c
index b77cb618a46..3b69705cc87 100644
--- a/sql/password.c
+++ b/sql/password.c
@@ -223,13 +223,13 @@ void scramble_323(char *to, const char *message, const char *password)
*/
my_bool
-check_scramble_323(const char *scrambled, const char *message,
+check_scramble_323(const unsigned char *scrambled, const char *message,
ulong *hash_pass)
{
struct rand_struct rand_st;
ulong hash_message[2];
- char buff[16],*to,extra; /* Big enough for check */
- const char *pos;
+ uchar buff[16],*to,extra; /* Big enough for check */
+ const uchar *pos;
hash_password(hash_message, message, SCRAMBLE_LENGTH_323);
randominit(&rand_st,hash_pass[0] ^ hash_message[0],
@@ -244,7 +244,7 @@ check_scramble_323(const char *scrambled, const char *message,
to=buff;
while (*scrambled)
{
- if (*scrambled++ != (char) (*to++ ^ extra))
+ if (*scrambled++ != (uchar) (*to++ ^ extra))
return 1; /* Wrong password */
}
return 0;
@@ -510,7 +510,7 @@ scramble(char *to, const char *message, const char *password)
*/
my_bool
-check_scramble(const char *scramble_arg, const char *message,
+check_scramble(const uchar *scramble_arg, const char *message,
const uint8 *hash_stage2)
{
SHA1_CONTEXT sha1_context;
@@ -523,7 +523,7 @@ check_scramble(const char *scramble_arg, const char *message,
mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE);
mysql_sha1_result(&sha1_context, buf);
/* encrypt scramble */
- my_crypt((char *) buf, buf, (const uchar *) scramble_arg, SCRAMBLE_LENGTH);
+ my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH);
/* now buf supposedly contains hash_stage1: so we can get hash_stage2 */
mysql_sha1_reset(&sha1_context);
mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE);
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 953656d3a4f..dd3a5d92a87 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -349,24 +349,6 @@ static bool write_eof_packet(THD *thd, NET *net,
}
/**
- Please client to send scrambled_password in old format.
-
- @param thd thread handle
-
- @retval
- 0 ok
- @retval
- !0 error
-*/
-
-bool send_old_password_request(THD *thd)
-{
- NET *net= &thd->net;
- return my_net_write(net, eof_buff, 1) || net_flush(net);
-}
-
-
-/**
@param thd Thread handler
@param sql_errno The error code to send
@param err A pointer to the error message
diff --git a/sql/protocol.h b/sql/protocol.h
index f661c7663e5..1c86c6d6c49 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -204,7 +204,6 @@ public:
void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate);
-bool send_old_password_request(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);
uchar *net_store_data(uchar *to,longlong from);
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 9daaf883ea8..c35a2f33a32 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -735,9 +735,9 @@ int set_var_password::check(THD *thd)
}
if (!user->user.str)
{
- DBUG_ASSERT(thd->security_ctx->priv_user);
- user->user.str= (char *) thd->security_ctx->priv_user;
- user->user.length= strlen(thd->security_ctx->priv_user);
+ DBUG_ASSERT(thd->security_ctx->user);
+ user->user.str= (char *) thd->security_ctx->user;
+ user->user.length= strlen(thd->security_ctx->user);
}
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 9e7fdbfeae5..f3a4ffbece9 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -6346,3 +6346,32 @@ ER_STORED_FUNCTION_PREVENTS_SWITCH_SQL_LOG_BIN
ER_FAILED_READ_FROM_PAR_FILE
eng "Failed to read from the .par file"
swe "Misslyckades läsa från .par filen"
+
+ER_ACCESS_DENIED_NO_PASSWORD_ERROR 28000
+ cze "P-Břístup pro uživatele '%-.48s'@'%-.64s'"
+ dan "Adgang nægtet bruger: '%-.48s'@'%-.64s'"
+ nla "Toegang geweigerd voor gebruiker: '%-.48s'@'%-.64s'"
+ eng "Access denied for user '%-.48s'@'%-.64s'"
+ est "Ligipääs keelatud kasutajale '%-.48s'@'%-.64s'"
+ fre "Accès refusé pour l'utilisateur: '%-.48s'@'@%-.64s'"
+ ger "Benutzer '%-.48s'@'%-.64s' hat keine Zugriffsberechtigung"
+ greek "Δεν επιτέÏεται η Ï€Ïόσβαση στο χÏήστη: '%-.48s'@'%-.64s'"
+ hun "A(z) '%-.48s'@'%-.64s' felhasznalo szamara tiltott eleres."
+ ita "Accesso non consentito per l'utente: '%-.48s'@'%-.64s'"
+ kor "'%-.48s'@'%-.64s' 사용ìžëŠ” ì ‘ê·¼ì´ ê±°ë¶€ ë˜ì—ˆìŠµë‹ˆë‹¤."
+ nor "Tilgang nektet for bruker: '%-.48s'@'%-.64s'"
+ norwegian-ny "Tilgang ikke tillate for brukar: '%-.48s'@'%-.64s'"
+ por "Acesso negado para o usuário '%-.48s'@'%-.64s'"
+ rum "Acces interzis pentru utilizatorul: '%-.48s'@'%-.64s'"
+ rus "ДоÑтуп закрыт Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ '%-.48s'@'%-.64s'"
+ serbian "Pristup je zabranjen korisniku '%-.48s'@'%-.64s'"
+ slo "Zakázaný prístup pre užívateľa: '%-.48s'@'%-.64s'"
+ spa "Acceso negado para usuario: '%-.48s'@'%-.64s'"
+ swe "Användare '%-.48s'@'%-.64s' är ej berättigad att logga in"
+ ukr "ДоÑтуп заборонено Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача: '%-.48s'@'%-.64s'"
+
+ER_SET_PASSWORD_AUTH_PLUGIN
+ eng "SET PASSWORD has no significance for users authenticating via plugins"
+
+ER_GRANT_PLUGIN_USER_EXISTS
+ eng "GRANT with IDENTIFIED WITH is illegal because the user %-.*s already exists"
diff --git a/sql/sp_head.h b/sql/sp_head.h
index b2446c8f680..e72ce6455f6 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -42,6 +42,7 @@
#define TYPE_ENUM_FUNCTION 1
#define TYPE_ENUM_PROCEDURE 2
#define TYPE_ENUM_TRIGGER 3
+#define TYPE_ENUM_PROXY 4
Item_result
sp_map_result_type(enum enum_field_types type);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 19373507955..b5433db5659 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -44,6 +44,11 @@
#include "transaction.h"
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
#include "records.h" // init_read_record, end_read_record
+#include <sql_common.h>
+#include <mysql/plugin_auth.h>
+#include "sql_connect.h"
+#include "hostname.h"
+#include "sql_db.h"
bool mysql_user_table_is_in_short_password_format= false;
@@ -164,7 +169,317 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = {
const TABLE_FIELD_DEF
mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields};
+static LEX_STRING native_password_plugin_name= {
+ C_STRING_WITH_LEN("mysql_native_password")
+};
+
+static LEX_STRING old_password_plugin_name= {
+ C_STRING_WITH_LEN("mysql_old_password")
+};
+
+/// @todo make it configurable
+LEX_STRING *default_auth_plugin_name= &native_password_plugin_name;
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static plugin_ref old_password_plugin;
+#endif
+static plugin_ref native_password_plugin;
+
+/* Classes */
+
+struct acl_host_and_ip
+{
+ char *hostname;
+ long ip,ip_mask; // Used with masked ip:s
+};
+
+class ACL_ACCESS {
+public:
+ ulong sort;
+ ulong access;
+};
+
+/* ACL_HOST is used if no host is specified */
+
+class ACL_HOST :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *db;
+};
+
+class ACL_USER :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ uint hostname_length;
+ USER_RESOURCES user_resource;
+ char *user;
+ uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
+ uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1
+ enum SSL_type ssl_type;
+ const char *ssl_cipher, *x509_issuer, *x509_subject;
+ LEX_STRING plugin;
+ LEX_STRING auth_string;
+
+ ACL_USER *copy(MEM_ROOT *root)
+ {
+ ACL_USER *dst= (ACL_USER *)alloc_root(root, sizeof(ACL_USER));
+ if (!dst)
+ return 0;
+ *dst= *this;
+ dst->user= safe_strdup_root(root, user);
+ dst->ssl_cipher= safe_strdup_root(root, ssl_cipher);
+ dst->x509_issuer= safe_strdup_root(root, x509_issuer);
+ dst->x509_subject= safe_strdup_root(root, x509_subject);
+ if (plugin.str == native_password_plugin_name.str ||
+ plugin.str == old_password_plugin_name.str)
+ dst->plugin= plugin;
+ else
+ dst->plugin.str= strmake_root(root, plugin.str, plugin.length);
+ dst->auth_string.str = safe_strdup_root(root, auth_string.str);
+ dst->host.hostname= safe_strdup_root(root, host.hostname);
+ return dst;
+ }
+};
+
+class ACL_DB :public ACL_ACCESS
+{
+public:
+ acl_host_and_ip host;
+ char *user,*db;
+};
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+static void update_hostname(acl_host_and_ip *host, const char *hostname);
+static ulong get_sort(uint count,...);
+static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
+ const char *ip);
+static bool show_proxy_grants (THD *thd, LEX_USER *user,
+ char *buff, size_t buffsize);
+
+class ACL_PROXY_USER :public ACL_ACCESS
+{
+ acl_host_and_ip host;
+ const char *user;
+ acl_host_and_ip proxied_host;
+ const char *proxied_user;
+ bool with_grant;
+
+ typedef enum {
+ MYSQL_PROXY_PRIV_HOST,
+ MYSQL_PROXY_PRIV_USER,
+ MYSQL_PROXY_PRIV_PROXIED_HOST,
+ MYSQL_PROXY_PRIV_PROXIED_USER,
+ MYSQL_PROXY_PRIV_WITH_GRANT } old_acl_proxy_users;
+public:
+ ACL_PROXY_USER () {};
+
+ void init(const char *host_arg, const char *user_arg,
+ const char *proxied_host_arg, const char *proxied_user_arg,
+ bool with_grant_arg)
+ {
+ user= (user_arg && *user_arg) ? user_arg : NULL;
+ update_hostname (&host,
+ (host_arg && *host_arg) ? host_arg : NULL);
+ proxied_user= (proxied_user_arg && *proxied_user_arg) ?
+ proxied_user_arg : NULL;
+ update_hostname (&proxied_host,
+ (proxied_host_arg && *proxied_host_arg) ?
+ proxied_host_arg : NULL);
+ with_grant= with_grant_arg;
+ sort= get_sort (4, host.hostname, user,
+ proxied_host.hostname, proxied_user);
+ }
+
+ void init(MEM_ROOT *mem, const char *host_arg, const char *user_arg,
+ const char *proxied_host_arg, const char *proxied_user_arg,
+ bool with_grant_arg)
+ {
+ init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL,
+ (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL,
+ (proxied_host_arg && *proxied_host_arg) ?
+ strdup_root (mem, proxied_host_arg) : NULL,
+ (proxied_user_arg && *proxied_user_arg) ?
+ strdup_root (mem, proxied_user_arg) : NULL,
+ with_grant_arg);
+ }
+
+ void init(TABLE *table, MEM_ROOT *mem)
+ {
+ init (get_field(mem, table->field[MYSQL_PROXY_PRIV_HOST]),
+ get_field(mem, table->field[MYSQL_PROXY_PRIV_USER]),
+ get_field(mem, table->field[MYSQL_PROXY_PRIV_PROXIED_HOST]),
+ get_field(mem, table->field[MYSQL_PROXY_PRIV_PROXIED_USER]),
+ table->field[MYSQL_PROXY_PRIV_WITH_GRANT]->val_int() != 0);
+ }
+
+ bool get_with_grant() { return with_grant; }
+ const char *get_user() { return user; }
+ const char *get_host() { return host.hostname; }
+ void set_user(MEM_ROOT *mem, const char *user_arg)
+ {
+ user= user_arg && *user_arg ? strdup_root (mem, user_arg) : NULL;
+ }
+ void set_host(MEM_ROOT *mem, const char *host_arg)
+ {
+ update_hostname (&host,
+ (host_arg && *host_arg) ?
+ strdup_root (mem, host_arg) : NULL);
+ }
+
+ bool check_validity (bool check_no_resolve)
+ {
+ if (check_no_resolve &&
+ (hostname_requires_resolving(host.hostname) ||
+ hostname_requires_resolving(proxied_host.hostname)))
+ {
+ sql_print_warning ("'proxy_priv' entry '%s@%s %s@%s' "
+ "ignored in --skip-name-resolve mode.",
+ proxied_user ? proxied_user : "",
+ proxied_host.hostname ? proxied_host.hostname : "",
+ user ? user : "",
+ host.hostname ? host.hostname : "");
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ bool matches (const char *host_arg, const char *user_arg, const char *ip_arg,
+ const char *proxied_user_arg)
+ {
+ DBUG_ENTER ("ACL_PROXY_USER::matches");
+ DBUG_PRINT ("info", ("compare_hostname(%s,%s,%s) &&"
+ "compare_hostname(%s,%s,%s) &&"
+ "wild_compare (%s,%s) &&"
+ "wild_compare (%s,%s)",
+ host.hostname ? host.hostname : "<NULL>",
+ host_arg ? host_arg : "<NULL>",
+ ip_arg ? ip_arg : "<NULL>",
+ proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+ host_arg ? host_arg : "<NULL>",
+ ip_arg ? ip_arg : "<NULL>",
+ user_arg ? user_arg : "<NULL>",
+ user ? user : "<NULL>",
+ proxied_user_arg ? proxied_user_arg : "<NULL>",
+ proxied_user ? proxied_user : "<NULL>"));
+ DBUG_RETURN (compare_hostname (&host, host_arg, ip_arg) &&
+ compare_hostname (&proxied_host, host_arg, ip_arg) &&
+ (!user ||
+ (user_arg && !wild_compare (user_arg, user, TRUE))) &&
+ (!proxied_user ||
+ (proxied_user && !wild_compare (proxied_user_arg,
+ proxied_user, TRUE))));
+ }
+
+ bool pk_equals (ACL_PROXY_USER *grant)
+ {
+ DBUG_ENTER ("pk_equals");
+ DBUG_PRINT ("info", ("strcmp(%s,%s) &&"
+ "strcmp(%s,%s) &&"
+ "wild_compare (%s,%s) &&"
+ "wild_compare (%s,%s)",
+ user ? user : "<NULL>",
+ grant->user ? grant->user : "<NULL>",
+ proxied_user ? proxied_user : "<NULL>",
+ grant->proxied_user ? grant->proxied_user : "<NULL>",
+ host.hostname ? host.hostname : "<NULL>",
+ grant->host.hostname ? grant->host.hostname : "<NULL>",
+ proxied_host.hostname ? proxied_host.hostname : "<NULL>",
+ grant->proxied_host.hostname ?
+ grant->proxied_host.hostname : "<NULL>"));
+ DBUG_RETURN(((!user && !grant->user) || !strcmp (user, grant->user)) &&
+ ((!proxied_user && !grant->proxied_user) ||
+ !strcmp (proxied_user, grant->proxied_user)) &&
+ ((!host.hostname && !grant->host.hostname) ||
+ !strcmp (host.hostname, grant->host.hostname)) &&
+ ((!proxied_host.hostname && !grant->proxied_host.hostname) ||
+ !strcmp (proxied_host.hostname, grant->proxied_host.hostname)));
+ }
+
+ bool granted_on (const char *host_arg, const char *user_arg)
+ {
+ return (((!user && (!user_arg || !user_arg[0])) ||
+ (user && user_arg && !strcmp (user, user_arg))) &&
+ ((!host.hostname && (!host_arg || !host_arg[0])) ||
+ (host.hostname && host_arg && !strcmp (host.hostname, host_arg))));
+ }
+
+ void print_grant (String *str)
+ {
+ str->append(STRING_WITH_LEN("GRANT PROXY ON '"));
+ if (proxied_user)
+ str->append(proxied_user, strlen(proxied_user));
+ str->append(STRING_WITH_LEN("'@'"));
+ if (proxied_host.hostname)
+ str->append(proxied_host.hostname, strlen(proxied_host.hostname));
+ str->append(STRING_WITH_LEN("' TO '"));
+ if (user)
+ str->append(user, strlen(user));
+ str->append(STRING_WITH_LEN("'@'"));
+ if (host.hostname)
+ str->append(host.hostname, strlen(host.hostname));
+ str->append(STRING_WITH_LEN("'"));
+ if (with_grant)
+ str->append(STRING_WITH_LEN(" WITH GRANT OPTION"));
+ }
+
+ void set_data (ACL_PROXY_USER *grant)
+ {
+ with_grant= grant->with_grant;
+ }
+
+ static int store_pk (TABLE *table,
+ const LEX_STRING *host,
+ const LEX_STRING *user,
+ const LEX_STRING *proxied_host,
+ const LEX_STRING *proxied_user)
+ {
+ DBUG_ENTER ("ACL_PROXY_USER::store_pk");
+ DBUG_PRINT ("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s",
+ host->str ? host->str : "<NULL>",
+ user->str ? user->str : "<NULL>",
+ proxied_host->str ? proxied_host->str : "<NULL>",
+ proxied_user->str ? proxied_user->str : "<NULL>"));
+ if (table->field[MYSQL_PROXY_PRIV_HOST]->store(host->str,
+ host->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXY_PRIV_USER]->store(user->str,
+ user->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXY_PRIV_PROXIED_HOST]->store(proxied_host->str,
+ proxied_host->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+ if (table->field[MYSQL_PROXY_PRIV_PROXIED_USER]->store(proxied_user->str,
+ proxied_user->length,
+ system_charset_info))
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+ }
+
+ static int store_data_record (TABLE *table,
+ const LEX_STRING *host,
+ const LEX_STRING *user,
+ const LEX_STRING *proxied_host,
+ const LEX_STRING *proxied_user,
+ bool with_grant)
+ {
+ DBUG_ENTER ("ACL_PROXY_USER::store_pk");
+ if (store_pk (table, host, user, proxied_host, proxied_user))
+ DBUG_RETURN(TRUE);
+ DBUG_PRINT ("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE"));
+ if (table->field[MYSQL_PROXY_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0,
+ TRUE))
+ DBUG_RETURN(TRUE);
+
+ DBUG_RETURN(FALSE);
+ }
+};
#define FIRST_NON_YN_FIELD 26
@@ -187,7 +502,25 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length,
#define IP_ADDR_STRLEN (3+1+3+1+3+1+3)
#define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1)
-static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs;
+#if defined(HAVE_OPENSSL)
+/*
+ Without SSL the handshake consists of one packet. This packet
+ has both client capabilites and scrambled password.
+ With SSL the handshake might consist of two packets. If the first
+ packet (client capabilities) has CLIENT_SSL flag set, we have to
+ switch to SSL and read the second packet. The scrambled password
+ is in the second packet and client_capabilites field will be ignored.
+ Maybe it is better to accept flags other than CLIENT_SSL from the
+ second packet?
+*/
+#define SSL_HANDSHAKE_SIZE 2
+#define NORMAL_HANDSHAKE_SIZE 6
+#define MIN_HANDSHAKE_SIZE 2
+#else
+#define MIN_HANDSHAKE_SIZE 6
+#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */
+
+static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs,acl_proxy_users;
static MEM_ROOT mem, memex;
static bool initialized=0;
static bool allow_all_hosts=1;
@@ -205,9 +538,6 @@ static ACL_USER *find_acl_user(const char *host, const char *user,
static bool update_user_table(THD *thd, TABLE *table,
const char *host, const char *user,
const char *new_password, uint new_password_len);
-static void update_hostname(acl_host_and_ip *host, const char *hostname);
-static bool compare_hostname(const acl_host_and_ip *host,const char *hostname,
- const char *ip);
static my_bool acl_load(THD *thd, TABLE_LIST *tables);
static my_bool grant_load(THD *thd, TABLE_LIST *tables);
static inline void get_grantor(THD *thd, char* grantor);
@@ -263,6 +593,19 @@ my_bool acl_init(bool dont_read_acl_tables)
(my_hash_get_key) acl_entry_get_key,
(my_hash_free_key) free,
&my_charset_utf8_bin);
+
+ /*
+ cache built-in native authentication plugins,
+ to avoid hash searches and a global mutex lock on every connect
+ */
+ native_password_plugin= my_plugin_lock_by_name(0,
+ &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+ old_password_plugin= my_plugin_lock_by_name(0,
+ &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN);
+
+ if (!native_password_plugin || !old_password_plugin)
+ DBUG_RETURN(1);
+
if (dont_read_acl_tables)
{
DBUG_RETURN(0); /* purecov: tested */
@@ -287,6 +630,37 @@ my_bool acl_init(bool dont_read_acl_tables)
DBUG_RETURN(return_val);
}
+/**
+ Choose from either native or old password plugins when assigning a password
+*/
+
+static bool
+set_user_plugin (ACL_USER *user, int password_len)
+{
+ switch (password_len)
+ {
+ case 0: /* no password */
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH:
+ user->plugin= native_password_plugin_name;
+ return FALSE;
+ case SCRAMBLED_PASSWORD_CHAR_LENGTH_323:
+ user->plugin= old_password_plugin_name;
+ return FALSE;
+ case 45: /* 4.1: to be removed */
+ sql_print_warning("Found 4.1.0 style password for user '%s@%s'. "
+ "Ignoring user. "
+ "You should change password for this user.",
+ user->user ? user->user : "",
+ user->host.hostname ? user->host.hostname : "");
+ return TRUE;
+ default:
+ sql_print_warning("Found invalid password for user: '%s@%s'; "
+ "Ignoring user", user->user ? user->user : "",
+ user->host.hostname ? user->host.hostname : "");
+ return TRUE;
+ }
+}
+
/*
Initialize structures responsible for user/db-level privilege checking
@@ -419,6 +793,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
while (!(read_record_info.read_record(&read_record_info)))
{
ACL_USER user;
+ bzero(&user, sizeof(user));
update_hostname(&user.host, get_field(&mem, table->field[0]));
user.user= get_field(&mem, table->field[1]);
if (check_no_resolve && hostname_requires_resolving(user.host.hostname))
@@ -430,27 +805,15 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
continue;
}
- const char *password= get_field(thd->mem_root, table->field[2]);
+ char *password= get_field(&mem, table->field[2]);
uint password_len= password ? strlen(password) : 0;
+ user.auth_string.str= password ? password : const_cast<char*>("");
+ user.auth_string.length= password_len;
set_user_salt(&user, password, password_len);
- if (user.salt_len == 0 && password_len != 0)
- {
- switch (password_len) {
- case 45: /* 4.1: to be removed */
- sql_print_warning("Found 4.1 style password for user '%s@%s'. "
- "Ignoring user. "
- "You should change password for this user.",
- user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
- break;
- default:
- sql_print_warning("Found invalid password for user: '%s@%s'; "
- "Ignoring user", user.user ? user.user : "",
- user.host.hostname ? user.host.hostname : "");
- break;
- }
- }
- else // password is correct
+
+ if (set_user_plugin(&user, password_len))
+ continue;
+
{
uint next_field;
user.access= get_access(table,3,&next_field) & GLOBAL_ACLS;
@@ -527,13 +890,43 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
ptr= get_field(thd->mem_root, table->field[next_field++]);
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
}
- else
- user.user_resource.user_conn= 0;
+
+ if (table->s->fields >= 41)
+ {
+ /* We may have plugin & auth_String fields */
+ char *tmpstr= get_field(&mem, table->field[next_field++]);
+ if (tmpstr)
+ {
+ if (user.auth_string.length)
+ {
+ sql_print_warning("'user' entry '%s@%s' has both a password "
+ "and an authentication plugin specified. The "
+ "password will be ignored.",
+ user.user ? user.user : "",
+ user.host.hostname ? user.host.hostname : "");
+ }
+ if (my_strcasecmp(system_charset_info, tmpstr,
+ native_password_plugin_name.str) == 0)
+ user.plugin= native_password_plugin_name;
+ else
+ if (my_strcasecmp(system_charset_info, tmpstr,
+ old_password_plugin_name.str) == 0)
+ user.plugin= old_password_plugin_name;
+ else
+ {
+ user.plugin.str= tmpstr;
+ user.plugin.length= strlen(tmpstr);
+ }
+ user.auth_string.str= get_field(&mem, table->field[next_field++]);
+ if (!user.auth_string.str)
+ user.auth_string.str= const_cast<char*>("");
+ user.auth_string.length= strlen(user.auth_string.str);
+ }
+ }
}
else
{
user.ssl_type=SSL_TYPE_NONE;
- bzero((char *)&(user.user_resource),sizeof(user.user_resource));
#ifndef TO_BE_REMOVED
if (table->s->fields <= 13)
{ // Without grant
@@ -617,6 +1010,25 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables)
sizeof(ACL_DB),(qsort_cmp) acl_compare);
end_read_record(&read_record_info);
freeze_size(&acl_dbs);
+
+ init_read_record(&read_record_info,thd,table=tables[3].table,NULL,1,0,FALSE);
+ table->use_all_columns();
+ (void) my_init_dynamic_array(&acl_proxy_users,sizeof(ACL_PROXY_USER),50,100);
+ while (!(read_record_info.read_record(&read_record_info)))
+ {
+ ACL_PROXY_USER proxy;
+ proxy.init(table, &mem);
+ if (proxy.check_validity(check_no_resolve))
+ continue;
+ if (push_dynamic(&acl_proxy_users,(uchar*) &proxy))
+ return TRUE;
+ }
+ my_qsort((uchar*) dynamic_element(&acl_proxy_users,0,ACL_PROXY_USER*),
+ acl_proxy_users.elements,
+ sizeof(ACL_PROXY_USER), (qsort_cmp) acl_compare);
+ end_read_record(&read_record_info);
+ freeze_size(&acl_proxy_users);
+
init_check_host();
initialized=1;
@@ -635,7 +1047,10 @@ void acl_free(bool end)
delete_dynamic(&acl_users);
delete_dynamic(&acl_dbs);
delete_dynamic(&acl_wild_hosts);
+ delete_dynamic(&acl_proxy_users);
my_hash_free(&acl_check_hosts);
+ plugin_unlock(0, native_password_plugin);
+ plugin_unlock(0, old_password_plugin);
if (!end)
acl_cache->clear(1); /* purecov: inspected */
else
@@ -667,8 +1082,8 @@ void acl_free(bool end)
my_bool acl_reload(THD *thd)
{
- TABLE_LIST tables[3];
- DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs;
+ TABLE_LIST tables[4];
+ DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs,old_acl_proxy_users;
MEM_ROOT old_mem;
bool old_initialized;
my_bool return_val= TRUE;
@@ -684,9 +1099,14 @@ my_bool acl_reload(THD *thd)
C_STRING_WITH_LEN("user"), "user", TL_READ);
tables[2].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("db"), "db", TL_READ);
+ tables[3].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxy_priv"),
+ "proxy_priv", TL_READ);
tables[0].next_local= tables[0].next_global= tables+1;
tables[1].next_local= tables[1].next_global= tables+2;
- tables[0].open_type= tables[1].open_type= tables[2].open_type= OT_BASE_ONLY;
+ tables[2].next_local= tables[2].next_global= tables+3;
+ tables[0].open_type= tables[1].open_type= tables[2].open_type=
+ tables[3].open_type= OT_BASE_ONLY;
if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT))
{
@@ -705,6 +1125,7 @@ my_bool acl_reload(THD *thd)
old_acl_hosts=acl_hosts;
old_acl_users=acl_users;
+ old_acl_proxy_users=acl_proxy_users;
old_acl_dbs=acl_dbs;
old_mem=mem;
delete_dynamic(&acl_wild_hosts);
@@ -716,6 +1137,7 @@ my_bool acl_reload(THD *thd)
acl_free(); /* purecov: inspected */
acl_hosts=old_acl_hosts;
acl_users=old_acl_users;
+ acl_proxy_users=old_acl_proxy_users;
acl_dbs=old_acl_dbs;
mem=old_mem;
init_check_host();
@@ -725,6 +1147,7 @@ my_bool acl_reload(THD *thd)
free_root(&old_mem,MYF(0));
delete_dynamic(&old_acl_hosts);
delete_dynamic(&old_acl_users);
+ delete_dynamic(&old_acl_proxy_users);
delete_dynamic(&old_acl_dbs);
}
if (old_initialized)
@@ -830,246 +1253,10 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
/*
- Seek ACL entry for a user, check password, SSL cypher, and if
- everything is OK, update THD user data and USER_RESOURCES struct.
-
- IMPLEMENTATION
- This function does not check if the user has any sensible privileges:
- only user's existence and validity is checked.
- Note, that entire operation is protected by acl_cache_lock.
+ Gets user credentials without authentication and resource limit checks.
SYNOPSIS
acl_getroot()
- thd thread handle. If all checks are OK,
- thd->security_ctx->priv_user/master_access are updated.
- thd->security_ctx->host/ip/user are used for checks.
- mqh user resources; on success mqh is reset, else
- unchanged
- passwd scrambled & crypted password, received from client
- (to check): thd->scramble or thd->scramble_323 is
- used to decrypt passwd, so they must contain
- original random string,
- passwd_len length of passwd, must be one of 0, 8,
- SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH
- 'thd' and 'mqh' are updated on success; other params are IN.
-
- RETURN VALUE
- 0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are
- updated
- 1 user not found or authentication failure
- 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format.
- -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format.
-*/
-
-int acl_getroot(THD *thd, USER_RESOURCES *mqh,
- const char *passwd, uint passwd_len)
-{
- ulong user_access= NO_ACCESS;
- int res= 1;
- ACL_USER *acl_user= 0;
- Security_context *sctx= thd->security_ctx;
- DBUG_ENTER("acl_getroot");
-
- if (!initialized)
- {
- /*
- here if mysqld's been started with --skip-grant-tables option.
- */
- sctx->skip_grants();
- bzero((char*) mqh, sizeof(*mqh));
- DBUG_RETURN(0);
- }
-
- mysql_mutex_lock(&acl_cache->lock);
-
- /*
- Find acl entry in user database. Note, that find_acl_user is not the same,
- because it doesn't take into account the case when user is not empty,
- but acl_user->user is empty
- */
-
- for (uint i=0 ; i < acl_users.elements ; i++)
- {
- ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
- if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user))
- {
- if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip))
- {
- /* check password: it should be empty or valid */
- if (passwd_len == acl_user_tmp->salt_len)
- {
- if (acl_user_tmp->salt_len == 0 ||
- (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ?
- check_scramble(passwd, thd->scramble, acl_user_tmp->salt) :
- check_scramble_323(passwd, thd->scramble,
- (ulong *) acl_user_tmp->salt)) == 0)
- {
- acl_user= acl_user_tmp;
- res= 0;
- }
- }
- else if (passwd_len == SCRAMBLE_LENGTH &&
- acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323)
- res= -1;
- else if (passwd_len == SCRAMBLE_LENGTH_323 &&
- acl_user_tmp->salt_len == SCRAMBLE_LENGTH)
- res= 2;
- /* linear search complete: */
- break;
- }
- }
- }
- /*
- This was moved to separate tree because of heavy HAVE_OPENSSL case.
- If acl_user is not null, res is 0.
- */
-
- if (acl_user)
- {
- /* OK. User found and password checked continue validation */
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- Vio *vio=thd->net.vio;
- SSL *ssl= (SSL*) vio->ssl_arg;
- X509 *cert;
-#endif
-
- /*
- At this point we know that user is allowed to connect
- from given host by given username/password pair. Now
- we check if SSL is required, if user is using SSL and
- if X509 certificate attributes are OK
- */
- switch (acl_user->ssl_type) {
- case SSL_TYPE_NOT_SPECIFIED: // Impossible
- case SSL_TYPE_NONE: // SSL is not required
- user_access= acl_user->access;
- break;
-#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY)
- case SSL_TYPE_ANY: // Any kind of SSL is ok
- if (vio_type(vio) == VIO_TYPE_SSL)
- user_access= acl_user->access;
- break;
- case SSL_TYPE_X509: /* Client should have any valid certificate. */
- /*
- Connections with non-valid certificates are dropped already
- in sslaccept() anyway, so we do not check validity here.
-
- We need to check for absence of SSL because without SSL
- we should reject connection.
- */
- if (vio_type(vio) == VIO_TYPE_SSL &&
- SSL_get_verify_result(ssl) == X509_V_OK &&
- (cert= SSL_get_peer_certificate(ssl)))
- {
- user_access= acl_user->access;
- X509_free(cert);
- }
- break;
- case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
- /*
- We do not check for absence of SSL because without SSL it does
- not pass all checks here anyway.
- If cipher name is specified, we compare it to actual cipher in
- use.
- */
- if (vio_type(vio) != VIO_TYPE_SSL ||
- SSL_get_verify_result(ssl) != X509_V_OK)
- break;
- if (acl_user->ssl_cipher)
- {
- DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
- acl_user->ssl_cipher,SSL_get_cipher(ssl)));
- if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
- user_access= acl_user->access;
- else
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
- acl_user->ssl_cipher,
- SSL_get_cipher(ssl));
- break;
- }
- }
- /* Prepare certificate (if exists) */
- DBUG_PRINT("info",("checkpoint 1"));
- if (!(cert= SSL_get_peer_certificate(ssl)))
- {
- user_access=NO_ACCESS;
- break;
- }
- DBUG_PRINT("info",("checkpoint 2"));
- /* If X509 issuer is specified, we check it... */
- if (acl_user->x509_issuer)
- {
- DBUG_PRINT("info",("checkpoint 3"));
- char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
- acl_user->x509_issuer, ptr));
- if (strcmp(acl_user->x509_issuer, ptr))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 issuer mismatch: should be '%s' "
- "but is '%s'", acl_user->x509_issuer, ptr);
- free(ptr);
- X509_free(cert);
- user_access=NO_ACCESS;
- break;
- }
- user_access= acl_user->access;
- free(ptr);
- }
- DBUG_PRINT("info",("checkpoint 4"));
- /* X509 subject is specified, we check it .. */
- if (acl_user->x509_subject)
- {
- char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
- DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
- acl_user->x509_subject, ptr));
- if (strcmp(acl_user->x509_subject,ptr))
- {
- if (global_system_variables.log_warnings)
- sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
- acl_user->x509_subject, ptr);
- free(ptr);
- X509_free(cert);
- user_access=NO_ACCESS;
- break;
- }
- user_access= acl_user->access;
- free(ptr);
- }
- /* Deallocate the X509 certificate. */
- X509_free(cert);
- break;
-#else /* HAVE_OPENSSL */
- default:
- /*
- If we don't have SSL but SSL is required for this user the
- authentication should fail.
- */
- break;
-#endif /* HAVE_OPENSSL */
- }
- sctx->master_access= user_access;
- sctx->priv_user= acl_user->user ? sctx->user : (char *) "";
- *mqh= acl_user->user_resource;
-
- if (acl_user->host.hostname)
- strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
- else
- *sctx->priv_host= 0;
- }
- mysql_mutex_unlock(&acl_cache->lock);
- DBUG_RETURN(res);
-}
-
-
-/*
- This is like acl_getroot() above, but it doesn't check password,
- and we don't care about the user resources.
-
- SYNOPSIS
- acl_getroot_no_password()
sctx Context which should be initialized
user user name
host host name
@@ -1081,13 +1268,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
TRUE Error
*/
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
- char *ip, char *db)
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+ char *ip, char *db)
{
int res= 1;
uint i;
ACL_USER *acl_user= 0;
- DBUG_ENTER("acl_getroot_no_password");
+ DBUG_ENTER("acl_getroot");
DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'",
(host ? host : "(NULL)"), (ip ? ip : "(NULL)"),
@@ -1110,8 +1297,7 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
sctx->master_access= 0;
sctx->db_access= 0;
- sctx->priv_user= (char *) "";
- *sctx->priv_host= 0;
+ *sctx->priv_user= *sctx->priv_host= 0;
/*
Find acl entry in user database.
@@ -1153,7 +1339,11 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
}
}
sctx->master_access= acl_user->access;
- sctx->priv_user= acl_user->user ? user : (char *) "";
+
+ if (acl_user->user)
+ strmake(sctx->priv_user, user, USERNAME_LENGTH);
+ else
+ *sctx->priv_user= 0;
if (acl_user->host.hostname)
strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
@@ -1179,7 +1369,9 @@ static void acl_update_user(const char *user, const char *host,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
- ulong privileges)
+ ulong privileges,
+ const LEX_STRING *plugin,
+ const LEX_STRING *auth)
{
mysql_mutex_assert_owner(&acl_cache->lock);
@@ -1193,6 +1385,14 @@ static void acl_update_user(const char *user, const char *host,
(acl_user->host.hostname &&
!my_strcasecmp(system_charset_info, host, acl_user->host.hostname)))
{
+ if (plugin->str[0])
+ {
+ acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user->plugin.length= plugin->length;
+ acl_user->auth_string.str= auth->str ?
+ strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ acl_user->auth_string.length= auth->length;
+ }
acl_user->access=privileges;
if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
acl_user->user_resource.questions=mqh->questions;
@@ -1229,7 +1429,9 @@ static void acl_insert_user(const char *user, const char *host,
const char *x509_issuer,
const char *x509_subject,
USER_RESOURCES *mqh,
- ulong privileges)
+ ulong privileges,
+ const LEX_STRING *plugin,
+ const LEX_STRING *auth)
{
ACL_USER acl_user;
@@ -1237,6 +1439,22 @@ static void acl_insert_user(const char *user, const char *host,
acl_user.user=*user ? strdup_root(&mem,user) : 0;
update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0);
+ if (plugin->str[0])
+ {
+ acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length);
+ acl_user.plugin.length= plugin->length;
+ acl_user.auth_string.str= auth->str ?
+ strmake_root(&mem, auth->str, auth->length) : const_cast<char*>("");
+ acl_user.auth_string.length= auth->length;
+ }
+ else
+ {
+ acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ?
+ old_password_plugin_name : native_password_plugin_name;
+ acl_user.auth_string.str= strmake_root(&mem, password, password_len);
+ acl_user.auth_string.length= password_len;
+ }
+
acl_user.access=privileges;
acl_user.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
@@ -1632,8 +1850,18 @@ bool change_password(THD *thd, const char *host, const char *user,
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
goto end;
}
+
+ if (my_strcasecmp(system_charset_info, acl_user->plugin.str,
+ native_password_plugin_name.str) &&
+ my_strcasecmp(system_charset_info, acl_user->plugin.str,
+ old_password_plugin_name.str))
+ {
+ push_warning (thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ ER_SET_PASSWORD_AUTH_PLUGIN, ER(ER_SET_PASSWORD_AUTH_PLUGIN));
+ }
/* update loaded acl entry: */
set_user_salt(acl_user, new_password, new_password_len);
+ set_user_plugin (acl_user, new_password_len);
if (update_user_table(thd, table,
acl_user->host.hostname ? acl_user->host.hostname : "",
@@ -2013,7 +2241,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
see also test_if_create_new_users()
*/
- else if (!password_len && no_auto_create)
+ else if (!password_len && !combo.plugin.length && no_auto_create)
{
my_error(ER_PASSWORD_NO_MATCH, MYF(0), combo.user.str, combo.host.str);
goto end;
@@ -2024,6 +2252,15 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
thd->security_ctx->user, thd->security_ctx->host_or_ip);
goto end;
}
+ else if (combo.plugin.str[0])
+ {
+ if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN))
+ {
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str);
+ goto end;
+ }
+ }
+
old_row_exists = 0;
restore_record(table,s->default_values);
table->field[0]->store(combo.host.str,combo.host.length,
@@ -2037,7 +2274,14 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
{
old_row_exists = 1;
store_record(table,record[1]); // Save copy for update
- if (combo.password.str) // If password given
+ /* what == 'N' means revoke */
+ if (combo.plugin.length && what != 'N')
+ {
+ my_error(ER_GRANT_PLUGIN_USER_EXISTS, MYF(0), combo.user.length,
+ combo.user.str);
+ goto end;
+ }
+ if (combo.password.str) // If password given
table->field[2]->store(password, password_len, system_charset_info);
else if (!rights && !revoke_grant &&
lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
@@ -2118,7 +2362,25 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
(mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE);
mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
+
+ next_field+=4;
+ if (combo.plugin.str[0])
+ {
+ if (table->s->fields >= 41 && combo.plugin.str[0])
+ {
+ table->field[next_field]->store(combo.plugin.str, combo.plugin.length,
+ system_charset_info);
+ table->field[next_field+1]->store(combo.auth.str, combo.auth.length,
+ system_charset_info);
+ }
+ else
+ {
+ my_error (ER_BAD_FIELD_ERROR, MYF(0), "plugin", "mysql.user");
+ goto end;
+ }
+ }
}
+
if (old_row_exists)
{
/*
@@ -2162,7 +2424,9 @@ end:
lex->x509_issuer,
lex->x509_subject,
&lex->mqh,
- rights);
+ rights,
+ &combo.plugin,
+ &combo.auth);
else
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
lex->ssl_type,
@@ -2170,7 +2434,9 @@ end:
lex->x509_issuer,
lex->x509_subject,
&lex->mqh,
- rights);
+ rights,
+ &combo.plugin,
+ &combo.auth);
}
DBUG_RETURN(error);
}
@@ -2285,6 +2551,160 @@ abort:
}
+static void
+acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke)
+{
+ mysql_mutex_assert_owner(&acl_cache->lock);
+
+ DBUG_ENTER ("acl_update_proxy_user");
+ for (uint i= 0 ; i < acl_proxy_users.elements ; i++)
+ {
+ ACL_PROXY_USER *acl_user=
+ dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *);
+
+ if (acl_user->pk_equals(new_value))
+ {
+ if (is_revoke)
+ {
+ DBUG_PRINT ("info", ("delting ACL_PROXY_USER"));
+ delete_dynamic_element(&acl_proxy_users, i);
+ }
+ else
+ {
+ DBUG_PRINT ("info", ("updating ACL_PROXY_USER"));
+ acl_user->set_data(new_value);
+ }
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
+
+
+static void
+acl_insert_proxy_user(ACL_PROXY_USER *new_value)
+{
+ DBUG_ENTER ("acl_insert_proxy_user");
+ mysql_mutex_assert_owner(&acl_cache->lock);
+ (void) push_dynamic(&acl_proxy_users, (uchar *) new_value);
+ my_qsort((uchar*) dynamic_element(&acl_proxy_users,0,ACL_PROXY_USER *),
+ acl_proxy_users.elements,
+ sizeof(ACL_PROXY_USER),(qsort_cmp) acl_compare);
+ DBUG_VOID_RETURN;
+}
+
+
+static int
+replace_proxy_priv_table(THD *thd, TABLE *table, const LEX_USER *user,
+ const LEX_USER *proxied_user, bool with_grant_arg,
+ bool revoke_grant)
+{
+ bool old_row_exists=0;
+ int error;
+ uchar user_key[MAX_KEY_LENGTH];
+ ACL_PROXY_USER new_grant;
+
+ DBUG_ENTER("replace_proxy_priv_table");
+
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(-1);
+ }
+
+ /* Check if there is such a user in user table in memory? */
+ if (!find_acl_user(user->host.str,user->user.str, FALSE))
+ {
+ my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
+ DBUG_RETURN(-1);
+ }
+
+ table->use_all_columns();
+ ACL_PROXY_USER::store_pk (table, &user->host, &user->user,
+ &proxied_user->host, &proxied_user->user);
+
+ key_copy(user_key, table->record[0], table->key_info,
+ table->key_info->key_length);
+
+ table->file->ha_index_init(0, 1);
+ if (table->file->index_read_map(table->record[0],user_key,
+ HA_WHOLE_KEY,
+ HA_READ_KEY_EXACT))
+ {
+ DBUG_PRINT ("info", ("Row not found"));
+ if (revoke_grant)
+ { // no row, no revoke
+ my_error(ER_NONEXISTING_GRANT, MYF(0), user->user.str, user->host.str);
+ goto abort;
+ }
+ old_row_exists = 0;
+ restore_record(table,s->default_values);
+ ACL_PROXY_USER::store_data_record (table, &user->host, &user->user,
+ &proxied_user->host,
+ &proxied_user->user,
+ with_grant_arg);
+ }
+ else
+ {
+ DBUG_PRINT ("info", ("Row found"));
+ old_row_exists = 1;
+ store_record(table,record[1]);
+ }
+
+ if (old_row_exists)
+ {
+ /* update old existing row */
+ if (!revoke_grant)
+ {
+ if ((error= table->file->ha_update_row(table->record[1],
+ table->record[0])) &&
+ error != HA_ERR_RECORD_IS_THE_SAME)
+ goto table_error; /* purecov: inspected */
+ }
+ else
+ {
+ if ((error= table->file->ha_delete_row(table->record[1])))
+ goto table_error; /* purecov: inspected */
+ }
+ }
+ else if ((error= table->file->ha_write_row(table->record[0])))
+ {
+ DBUG_PRINT ("info", ("error inserting the row"));
+ if (table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ goto table_error; /* purecov: inspected */
+ }
+
+ acl_cache->clear(1); // Clear privilege cache
+ if (old_row_exists)
+ {
+ new_grant.init(user->host.str, user->user.str,
+ proxied_user->host.str, proxied_user->user.str,
+ with_grant_arg);
+ acl_update_proxy_user(&new_grant, revoke_grant);
+ }
+ else
+ {
+ new_grant.init (&mem, user->host.str, user->user.str,
+ proxied_user->host.str, proxied_user->user.str,
+ with_grant_arg);
+ acl_insert_proxy_user(&new_grant);
+ }
+
+ table->file->ha_index_end();
+ DBUG_RETURN(0);
+
+ /* This could only happen if the grant tables got corrupted */
+table_error:
+ DBUG_PRINT ("info", ("table error"));
+ table->file->print_error(error,MYF(0)); /* purecov: inspected */
+
+abort:
+ DBUG_PRINT ("info", ("aborting replace_proxy_priv_table"));
+ table->file->ha_index_end();
+ DBUG_RETURN(-1);
+}
+
+
class GRANT_COLUMN :public Sql_alloc
{
public:
@@ -3500,10 +3920,10 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc,
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
- ulong rights, bool revoke_grant)
+ ulong rights, bool revoke_grant, bool is_proxy)
{
List_iterator <LEX_USER> str_list (list);
- LEX_USER *Str, *tmp_Str;
+ LEX_USER *Str, *tmp_Str, *proxied_user;
char tmp_db[NAME_LEN+1];
bool create_new_users=0;
TABLE_LIST tables[2];
@@ -3523,11 +3943,26 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
db=tmp_db;
}
- /* open the mysql.user and mysql.db tables */
+ if (is_proxy)
+ {
+ DBUG_ASSERT (!db);
+ proxied_user= str_list++;
+ }
+
+ /* open the mysql.user and mysql.db or mysql.proxy_priv tables */
tables[0].init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("user"), "user", TL_WRITE);
- tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
- C_STRING_WITH_LEN("db"), "db", TL_WRITE);
+ if (is_proxy)
+
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxy_priv"),
+ "proxy_priv",
+ TL_WRITE);
+ else
+ tables[1].init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("db"),
+ "db",
+ TL_WRITE);
tables[0].next_local= tables[0].next_global= tables+1;
/*
@@ -3613,6 +4048,13 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list,
result= -1;
}
}
+ else if (is_proxy)
+ {
+ if (replace_proxy_priv_table (thd, tables[1].table, Str, proxied_user,
+ rights & GRANT_ACL ? TRUE : FALSE,
+ revoke_grant))
+ result= -1;
+ }
}
mysql_mutex_unlock(&acl_cache->lock);
@@ -5045,6 +5487,12 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
goto end;
}
+ if (show_proxy_grants(thd, lex_user, buff, sizeof(buff)))
+ {
+ error= -1;
+ goto end;
+ }
+
end:
mysql_mutex_unlock(&acl_cache->lock);
mysql_rwlock_unlock(&LOCK_grant);
@@ -5202,7 +5650,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc)
< 0 Error.
*/
-#define GRANT_TABLES 5
+#define GRANT_TABLES 6
int open_grant_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("open_grant_tables");
@@ -5226,10 +5674,14 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
(tables+4)->init_one_table(C_STRING_WITH_LEN("mysql"),
C_STRING_WITH_LEN("procs_priv"),
"procs_priv", TL_WRITE);
+ (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"),
+ C_STRING_WITH_LEN("proxy_priv"),
+ "proxy_priv", TL_WRITE);
tables->next_local= tables->next_global= tables+1;
(tables+1)->next_local= (tables+1)->next_global= tables+2;
(tables+2)->next_local= (tables+2)->next_global= tables+3;
(tables+3)->next_local= (tables+3)->next_global= tables+4;
+ (tables+4)->next_local= (tables+4)->next_global= tables+5;
#ifdef HAVE_REPLICATION
/*
@@ -5243,11 +5695,11 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables)
account in tests.
*/
tables[0].updating=tables[1].updating=tables[2].updating=
- tables[3].updating=tables[4].updating=1;
+ tables[3].updating=tables[4].updating=tables[5].updating= 1;
if (!(thd->spcont || rpl_filter->tables_ok(0, tables)))
DBUG_RETURN(1);
tables[0].updating=tables[1].updating=tables[2].updating=
- tables[3].updating=tables[4].updating=0;;
+ tables[3].updating=tables[4].updating=tables[5].updating=0;
}
#endif
@@ -5376,7 +5828,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
int error;
TABLE *table= tables[table_no].table;
Field *host_field= table->field[0];
- Field *user_field= table->field[table_no ? 2 : 1];
+ Field *user_field= table->field[table_no && table_no != 5 ? 2 : 1];
char *host_str= user_from->host.str;
char *user_str= user_from->user.str;
const char *host;
@@ -5459,12 +5911,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
user= "";
#ifdef EXTRA_DEBUG
- DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
- user, host,
- get_field(thd->mem_root, table->field[1]) /*db*/,
- get_field(thd->mem_root, table->field[3]) /*table*/,
- get_field(thd->mem_root,
- table->field[4]) /*column*/));
+ if (table_no != 5)
+ {
+ DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'",
+ user, host,
+ get_field(thd->mem_root, table->field[1]) /*db*/,
+ get_field(thd->mem_root, table->field[3]) /*table*/,
+ get_field(thd->mem_root,
+ table->field[4]) /*column*/));
+ }
#endif
if (strcmp(user_str, user) ||
my_strcasecmp(system_charset_info, host_str, host))
@@ -5526,6 +5981,7 @@ static int handle_grant_struct(uint struct_no, bool drop,
const char *host;
ACL_USER *acl_user= NULL;
ACL_DB *acl_db= NULL;
+ ACL_PROXY_USER *acl_proxy_user= NULL;
GRANT_NAME *grant_name= NULL;
DBUG_ENTER("handle_grant_struct");
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
@@ -5550,6 +6006,9 @@ static int handle_grant_struct(uint struct_no, bool drop,
case 3:
elements= proc_priv_hash.records;
break;
+ case 5:
+ elements= acl_proxy_users.elements;
+ break;
default:
return -1;
}
@@ -5588,6 +6047,11 @@ static int handle_grant_struct(uint struct_no, bool drop,
user= grant_name->user;
host= grant_name->host.hostname;
break;
+ case 5:
+ acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*);;
+ user= acl_proxy_user->get_user();
+ host= acl_proxy_user->get_host();
+ break;
default:
assert(0);
}
@@ -5623,6 +6087,11 @@ static int handle_grant_struct(uint struct_no, bool drop,
case 3:
my_hash_delete(&proc_priv_hash, (uchar*) grant_name);
break;
+
+ case 5:
+ delete_dynamic_element(&acl_proxy_users, idx);
+ break;
+
}
elements--;
idx--;
@@ -5658,6 +6127,12 @@ static int handle_grant_struct(uint struct_no, bool drop,
my_hash_update(&column_priv_hash, (uchar*) grant_name,
(uchar*) grant_name->hash_key, grant_name->key_length);
break;
+
+ case 5:
+ acl_proxy_user->set_user (&mem, user_to->user.str);
+ acl_proxy_user->set_host (&mem, user_to->host.str);
+ break;
+
}
}
else
@@ -5792,6 +6267,20 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
result= 1; /* At least one record/element found. */
}
}
+
+ /* Handle proxy_priv table. */
+ if ((found= handle_grant_table(tables, 5, drop, user_from, user_to)) < 0)
+ {
+ /* Handle of table failed, don't touch the in-memory array. */
+ result= -1;
+ }
+ else
+ {
+ /* Handle proxy_priv array. */
+ if ((handle_grant_struct(5, drop, user_from, user_to) && ! result) ||
+ found)
+ result= 1; /* At least one record/element found. */
+ }
end:
DBUG_RETURN(result);
}
@@ -6478,38 +6967,44 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
tables->db= (char*)sp_db;
tables->table_name= tables->alias= (char*)sp_name;
- combo->host.length= strlen(combo->host.str);
- combo->user.length= strlen(combo->user.str);
- combo->host.str= thd->strmake(combo->host.str,combo->host.length);
- combo->user.str= thd->strmake(combo->user.str,combo->user.length);
+ thd->make_lex_string(&combo->user,
+ combo->user.str, strlen(combo->user.str), 0);
+ thd->make_lex_string(&combo->host,
+ combo->host.str, strlen(combo->host.str), 0);
+ combo->password= empty_lex_str;
+ combo->plugin= empty_lex_str;
+ combo->auth= empty_lex_str;
- if(au && au->salt_len)
+ if(au)
{
- if (au->salt_len == SCRAMBLE_LENGTH)
- {
- make_password_from_salt(passwd_buff, au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
- }
- else if (au->salt_len == SCRAMBLE_LENGTH_323)
+ if (au->salt_len)
{
- make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
- combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ if (au->salt_len == SCRAMBLE_LENGTH)
+ {
+ make_password_from_salt(passwd_buff, au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH;
+ }
+ else if (au->salt_len == SCRAMBLE_LENGTH_323)
+ {
+ make_password_from_salt_323(passwd_buff, (ulong *) au->salt);
+ combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323;
+ }
+ else
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH,
+ ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH);
+ return TRUE;
+ }
+ combo->password.str= passwd_buff;
}
- else
+
+ if (au->plugin.str != native_password_plugin_name.str &&
+ au->plugin.str != old_password_plugin_name.str)
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_PASSWD_LENGTH,
- ER(ER_PASSWD_LENGTH),
- SCRAMBLED_PASSWORD_CHAR_LENGTH);
- return TRUE;
+ combo->plugin= au->plugin;
+ combo->auth= au->auth_string;
}
- combo->password.str= passwd_buff;
- }
- else
- {
- combo->password.str= (char*)"";
- combo->password.length= 0;
}
if (user_list.push_back(combo))
@@ -6542,6 +7037,127 @@ template class List<LEX_COLUMN>;
template class List<LEX_USER>;
#endif
+/**
+ Validate if a user can proxy as another user
+
+ @thd current thread
+ @param user the logged in user (proxy user)
+ @param authenticated_as the effective user a plugin is trying to
+ impersonate as (proxied user)
+ @return status
+ @retval FALSE OK
+ @retval TRUE user can't impersonate proxied user
+*/
+
+static bool
+acl_find_proxy_user(const char *user, const char *host, const char *ip,
+ const char *authenticated_as, bool *proxy_used)
+{
+ uint i;
+ /* if the proxied and proxy user are the same return OK */
+ DBUG_ENTER ("acl_find_proxy_user");
+ DBUG_PRINT ("info", ("user=%s host=%s ip=%s authenticated_as=%s", user, host, ip, authenticated_as));
+
+ if (!strcmp (authenticated_as, user))
+ {
+ DBUG_PRINT ("info", ("user is the same as authenticated_as"));
+ DBUG_RETURN (FALSE);
+ }
+
+ *proxy_used= TRUE;
+ for (i=0 ; i < acl_proxy_users.elements ; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element (&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->matches (host, user, ip, authenticated_as))
+ DBUG_RETURN(FALSE);
+ }
+
+ DBUG_RETURN (TRUE);
+}
+
+
+bool
+acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
+ bool with_grant)
+{
+ DBUG_ENTER ("acl_check_proxy_grant_access");
+ DBUG_PRINT ("info", ("user=%s host=%s with_grant=%d", user, host, (int) with_grant));
+ if (!initialized)
+ {
+ my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables");
+ DBUG_RETURN(1);
+ }
+
+ /* replication slave thread can do anything */
+ if (thd->slave_thread)
+ {
+ DBUG_PRINT ("info", ("replication slave"));
+ DBUG_RETURN(FALSE);
+ }
+
+ /* one can grant proxy to himself to others */
+ if (!strcmp(thd->security_ctx->user, user) &&
+ !my_strcasecmp(system_charset_info, host,
+ thd->security_ctx->host))
+ {
+ DBUG_PRINT ("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal",
+ thd->security_ctx->user, user,
+ host, thd->security_ctx->host));
+ DBUG_RETURN(FALSE);
+ }
+
+ /* check for matching WITH PROXY rights */
+ for (uint i=0 ; i < acl_proxy_users.elements ; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element (&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->matches (thd->security_ctx->host,
+ thd->security_ctx->user,
+ thd->security_ctx->ip,
+ user) &&
+ proxy->get_with_grant())
+ {
+ DBUG_PRINT ("info", ("found"));
+ DBUG_RETURN(FALSE);
+ }
+ }
+
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->user,
+ thd->security_ctx->host_or_ip);
+ DBUG_RETURN(TRUE);
+}
+
+
+static bool
+show_proxy_grants (THD *thd, LEX_USER *user, char *buff, size_t buffsize)
+{
+ Protocol *protocol= thd->protocol;
+ int error= 0;
+
+ for (uint i=0 ; i < acl_proxy_users.elements ; i++)
+ {
+ ACL_PROXY_USER *proxy= dynamic_element (&acl_proxy_users, i,
+ ACL_PROXY_USER *);
+ if (proxy->granted_on(user->host.str, user->user.str))
+ {
+ String global(buff, buffsize, system_charset_info);
+ global.length(0);
+ proxy->print_grant(&global);
+ protocol->prepare_for_resend();
+ protocol->store(global.ptr(),global.length(),global.charset());
+ if (protocol->write())
+ {
+ error= -1;
+ break;
+ }
+ }
+ }
+ return error;
+}
+
+
#endif /*NO_EMBEDDED_ACCESS_CHECKS */
@@ -7109,3 +7725,1599 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
}
+/****************************************************************************
+ AUTHENTICATION CODE
+ including initial connect handshake, invoking appropriate plugins,
+ client-server plugin negotiation, COM_CHANGE_USER, and native
+ MySQL authentication plugins.
+****************************************************************************/
+
+/* few defines to have less ifdef's in the code below */
+#ifdef EMBEDDED_LIBRARY
+#undef HAVE_OPENSSL
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+#define initialized 0
+#define decrease_user_connections(X) /* nothing */
+#define check_for_max_user_connections(X,Y) 0
+#endif
+#endif
+#ifndef HAVE_OPENSSL
+#define ssl_acceptor_fd 0
+#define sslaccept(A,B,C) 1
+#endif
+
+
+class Thd_charset_adapter
+{
+ THD *thd;
+public:
+ Thd_charset_adapter(THD *thd_arg) : thd (thd_arg) {}
+ bool init_client_charset(uint cs_number)
+ {
+ thd_init_client_charset(thd, cs_number);
+ thd->update_charset();
+ return thd->is_error();
+ }
+
+ CHARSET_INFO *charset() { return thd->charset(); }
+};
+
+
+/**
+ The internal version of what plugins know as MYSQL_PLUGIN_VIO,
+ basically the context of the authentication session
+*/
+struct MPVIO_EXT : public MYSQL_PLUGIN_VIO
+{
+ MYSQL_SERVER_AUTH_INFO auth_info;
+ const ACL_USER *acl_user;
+ plugin_ref plugin; ///< what plugin we're under
+ LEX_STRING db; ///< db name from the handshake packet
+ /** when restarting a plugin this caches the last client reply */
+ struct {
+ char *plugin, *pkt; ///< pointers into NET::buff
+ uint pkt_len;
+ } cached_client_reply;
+ /** this caches the first plugin packet for restart request on the client */
+ struct {
+ char *pkt;
+ uint pkt_len;
+ } cached_server_packet;
+ int packets_read, packets_written; ///< counters for send/received packets
+ uint connect_errors; ///< if there were connect errors for this host
+ /** when plugin returns a failure this tells us what really happened */
+ enum { SUCCESS, FAILURE, RESTART } status;
+
+ /* encapsulation members */
+ ulong client_capabilities;
+ char *scramble;
+ MEM_ROOT *mem_root;
+ struct rand_struct *rand;
+ my_thread_id thread_id;
+ uint *server_status;
+ NET *net;
+ ulong max_client_packet_length;
+ char *ip;
+ char *host;
+ Thd_charset_adapter *charset_adapter;
+ LEX_STRING acl_user_plugin;
+};
+
+/**
+ a helper function to report an access denied error in all the proper places
+*/
+static void login_failed_error(MPVIO_EXT *mpvio, int passwd_used)
+{
+ THD *thd= current_thd;
+ if (passwd_used == 2)
+ {
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip);
+ general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip);
+ /*
+ Log access denied messages to the error log when log-warnings = 2
+ so that the overhead of the general query log is not required to track
+ failed connections.
+ */
+ if (global_system_variables.log_warnings > 1)
+ {
+ sql_print_warning(ER(ER_ACCESS_DENIED_NO_PASSWORD_ERROR),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip);
+ }
+ }
+ else
+ {
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip,
+ passwd_used ? ER(ER_YES) : ER(ER_NO));
+ general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip,
+ passwd_used ? ER(ER_YES) : ER(ER_NO));
+ /*
+ Log access denied messages to the error log when log-warnings = 2
+ so that the overhead of the general query log is not required to track
+ failed connections.
+ */
+ if (global_system_variables.log_warnings > 1)
+ {
+ sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip,
+ passwd_used ? ER(ER_YES) : ER(ER_NO));
+ }
+ }
+}
+
+/**
+ sends a server handshake initialization packet, the very first packet
+ after the connection was established
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ 1 protocol version (always 10)
+ n server version string, \0-terminated
+ 4 thread id
+ 8 first 8 bytes of the plugin provided data (scramble)
+ 1 \0 byte, terminating the first part of a scramble
+ 2 server capabilities (two lower bytes)
+ 1 server character set
+ 2 server status
+ 2 server capabilities (two upper bytes)
+ 1 length of the scramble
+ 10 reserved, always 0
+ n rest of the plugin provided data (at least 12 bytes)
+ 1 \0 byte, terminating the second part of a scramble
+
+ @retval 0 ok
+ @retval 1 error
+*/
+static bool send_server_handshake_packet(MPVIO_EXT *mpvio,
+ const char *data, uint data_len)
+{
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+ DBUG_ASSERT(data_len <= 255);
+
+ char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + data_len + 64);
+ char scramble_buf[SCRAMBLE_LENGTH];
+ char *end= buff;
+
+ DBUG_ENTER ("send_server_handshake_packet");
+ *end++= protocol_version;
+
+ mpvio->client_capabilities= CLIENT_BASIC_FLAGS;
+
+ if (opt_using_transactions)
+ mpvio->client_capabilities|= CLIENT_TRANSACTIONS;
+
+ mpvio->client_capabilities|= CAN_CLIENT_COMPRESS;
+
+ if (ssl_acceptor_fd)
+ {
+ mpvio->client_capabilities |= CLIENT_SSL;
+ mpvio->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT;
+ }
+
+ if (data_len)
+ {
+ mpvio->cached_server_packet.pkt= (char*)memdup_root(mpvio->mem_root,
+ data, data_len);
+ mpvio->cached_server_packet.pkt_len= data_len;
+ }
+
+ if (data_len < SCRAMBLE_LENGTH)
+ {
+ if (data_len)
+ { /*
+ the first packet *must* have at least 20 bytes of a scramble.
+ if a plugin provided less, we pad it to 20 with zeros
+ */
+ memcpy(scramble_buf, data, data_len);
+ bzero(scramble_buf+data_len, SCRAMBLE_LENGTH-data_len);
+ data= scramble_buf;
+ }
+ else
+ {
+ /*
+ if the default plugin does not provide the data for the scramble at
+ all, we generate a scramble internally anyway, just in case the
+ user account (that will be known only later) uses a
+ native_password_plugin (which needs a scramble). If we don't send a
+ scramble now - wasting 20 bytes in the packet -
+ native_password_plugin will have to send it in a separate packet,
+ adding one more round trip.
+ */
+ create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+ data= mpvio->scramble;
+ }
+ data_len= SCRAMBLE_LENGTH;
+ }
+
+ end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1;
+ int4store((uchar*) end, mpvio->thread_id);
+ end+= 4;
+
+ /*
+ Old clients does not understand long scrambles, but can ignore packet
+ tail: that's why first part of the scramble is placed here, and second
+ part at the end of packet.
+ */
+ end= (char*)memcpy(end, data, SCRAMBLE_LENGTH_323);
+ end+= SCRAMBLE_LENGTH_323;
+ *end++= 0;
+
+ int2store(end, mpvio->client_capabilities);
+ /* write server characteristics: up to 16 bytes allowed */
+ end[2]=(char) default_charset_info->number;
+ int2store(end+3, mpvio->server_status[0]);
+ int2store(end+5, mpvio->client_capabilities >> 16);
+ end[7]= data_len;
+ bzero(end+8, 10);
+ end+= 18;
+ /* write scramble tail */
+ end= (char*)memcpy(end, data + SCRAMBLE_LENGTH_323,
+ data_len - SCRAMBLE_LENGTH_323);
+ end+= data_len - SCRAMBLE_LENGTH_323;
+ end= strmake(end, plugin_name(mpvio->plugin)->str,
+ plugin_name(mpvio->plugin)->length);
+
+ int res= my_net_write(mpvio->net, (uchar*) buff, (size_t) (end-buff)) ||
+ net_flush(mpvio->net);
+ my_afree(buff);
+ DBUG_RETURN (res);
+}
+
+static bool secure_auth(MPVIO_EXT *mpvio)
+{
+ THD *thd;
+ if (!opt_secure_auth)
+ return 0;
+ /*
+ If the server is running in secure auth mode, short scrambles are
+ forbidden. Extra juggling to report the same error as the old code.
+ */
+
+ thd= current_thd;
+ if (mpvio->client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip);
+ general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.host_or_ip);
+ }
+ else
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ }
+ return 1;
+}
+
+/**
+ sends a "change plugin" packet, requesting a client to restart authentication
+ using a different authentication plugin
+
+ Packet format:
+
+ Bytes Content
+ ----- ----
+ 1 byte with the value 254
+ n client plugin to use, \0-terminated
+ n plugin provided data
+
+ In a special case of switching from native_password_plugin to
+ old_password_plugin, the packet contains only one - the first - byte,
+ plugin name is omitted, plugin data aren't needed as the scramble was
+ already sent. This one-byte packet is identical to the "use the short
+ scramble" packet in the protocol before plugins were introduced.
+
+ @retval 0 ok
+ @retval 1 error
+*/
+static bool send_plugin_request_packet(MPVIO_EXT *mpvio,
+ const uchar *data, uint data_len)
+{
+ DBUG_ASSERT(mpvio->packets_written == 1);
+ DBUG_ASSERT(mpvio->packets_read == 1);
+ NET *net= mpvio->net;
+ static uchar switch_plugin_request_buf[]= { 254 };
+
+ DBUG_ENTER ("send_plugin_request_packet");
+ mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART
+
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+ DBUG_ASSERT(client_auth_plugin);
+
+ /*
+ we send an old "short 4.0 scramble request", if we need to request a
+ client to use 4.0 auth plugin (short scramble) and the scramble was
+ already sent to the client
+
+ below, cached_client_reply.plugin is the plugin name that client has used,
+ client_auth_plugin is derived from mysql.user table, for the given
+ user account, it's the plugin that the client need to use to login.
+ */
+ bool switch_from_long_to_short_scramble=
+ native_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
+ client_auth_plugin == old_password_plugin_name.str;
+
+ if (switch_from_long_to_short_scramble)
+ DBUG_RETURN (secure_auth(mpvio) ||
+ my_net_write(net, switch_plugin_request_buf, 1) ||
+ net_flush(net));
+
+ /*
+ We never request a client to switch from a short to long scramble.
+ Plugin-aware clients can do that, but traditionally it meant to
+ ask an old 4.0 client to use the new 4.1 authentication protocol.
+ */
+ bool switch_from_short_to_long_scramble=
+ old_password_plugin_name.str == mpvio->cached_client_reply.plugin &&
+ client_auth_plugin == native_password_plugin_name.str;
+
+ if (switch_from_short_to_long_scramble)
+ {
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN (1);
+ }
+
+ DBUG_PRINT ("info", ("requesting client to use the %s plugin",
+ client_auth_plugin));
+ DBUG_RETURN (net_write_command(net, switch_plugin_request_buf[0],
+ (uchar*)client_auth_plugin,
+ strlen(client_auth_plugin)+1,
+ (uchar*)data, data_len));
+}
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+/**
+ Finds acl entry in user database for authentication purposes.
+
+ Finds a user and copies it into mpvio. Reports an authentication
+ failure if a user is not found.
+
+ @note find_acl_user is not the same, because it doesn't take into
+ account the case when user is not empty, but acl_user->user is empty
+
+ @retval 0 found
+ @retval 1 not found
+*/
+static bool find_mpvio_user(MPVIO_EXT *mpvio)
+{
+ DBUG_ENTER ("find_mpvio_user");
+ DBUG_PRINT ("info", ("entry: %s", mpvio->auth_info.user_name));
+ DBUG_ASSERT(mpvio->acl_user == 0);
+ mysql_mutex_lock(&acl_cache->lock);
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user_tmp->user ||
+ !strcmp(mpvio->auth_info.user_name, acl_user_tmp->user)) &&
+ compare_hostname(&acl_user_tmp->host, mpvio->host, mpvio->ip))
+ {
+ mpvio->acl_user= acl_user_tmp->copy(mpvio->mem_root);
+ if (acl_user_tmp->plugin.str == native_password_plugin_name.str ||
+ acl_user_tmp->plugin.str == old_password_plugin_name.str)
+ mpvio->acl_user_plugin= acl_user_tmp->plugin;
+ else
+ make_lex_string_root(mpvio->mem_root,
+ &mpvio->acl_user_plugin,
+ acl_user_tmp->plugin.str,
+ acl_user_tmp->plugin.length, 0);
+ break;
+ }
+ }
+ mysql_mutex_unlock(&acl_cache->lock);
+
+ if (!mpvio->acl_user)
+ {
+ login_failed_error(mpvio, 0);
+ DBUG_RETURN (1);
+ }
+
+ /* user account requires non-default plugin and the client is too old */
+ if (mpvio->acl_user->plugin.str != native_password_plugin_name.str &&
+ mpvio->acl_user->plugin.str != old_password_plugin_name.str &&
+ !(mpvio->client_capabilities & CLIENT_PLUGIN_AUTH))
+ {
+ DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+ native_password_plugin_name.str));
+ DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str,
+ old_password_plugin_name.str));
+ my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
+ general_log_print(current_thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
+ DBUG_RETURN (1);
+ }
+
+ mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str;
+ mpvio->auth_info.auth_string_length=
+ (unsigned long) mpvio->acl_user->auth_string.length;
+ strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ?
+ mpvio->acl_user->user : "", USERNAME_LENGTH);
+ DBUG_PRINT ("info", ("exit: user=%s, auth_string=%s, authenticated as=%s"
+ "plugin=%s",
+ mpvio->auth_info.user_name,
+ mpvio->auth_info.auth_string,
+ mpvio->auth_info.authenticated_as,
+ mpvio->acl_user->plugin.str));
+ DBUG_RETURN (0);
+}
+#endif
+
+/* the packet format is described in send_change_user_packet() */
+static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length)
+{
+ NET *net= mpvio->net;
+
+ char *user= (char*) net->read_pos;
+ char *end= user + packet_length;
+ /* Safe because there is always a trailing \0 at the end of the packet */
+ char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1;
+ char *db= passwd;
+ char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ DBUG_ENTER ("parse_com_change_user_packet");
+ if (passwd >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ DBUG_RETURN (1);
+ }
+
+ /*
+ Old clients send null-terminated string as password; new clients send
+ the size (1 byte) + string (not null-terminated). Hence in case of empty
+ password both send '\0'.
+
+ This strlen() can't be easily deleted without changing protocol.
+
+ Cast *passwd to an unsigned char, so that it doesn't extend the sign for
+ *passwd > 127 and become 2**32-127+ after casting to uint.
+ */
+ uint passwd_len= (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ (uchar)(*passwd++) : strlen(passwd));
+
+ db+= passwd_len + 1;
+ /*
+ Database name is always NUL-terminated, so in case of empty database
+ the packet must contain at least the trailing '\0'.
+ */
+ if (db >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ DBUG_RETURN (1);
+ }
+
+ uint db_len= strlen(db);
+
+ char *ptr= db + db_len + 1;
+
+ if (ptr+1 < end)
+ {
+ if (mpvio->charset_adapter->init_client_charset(uint2korr(ptr)))
+ DBUG_RETURN(1);
+ }
+
+
+ /* Convert database and user names to utf8 */
+ db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db, db_len, mpvio->charset_adapter->charset(),
+ &dummy_errors);
+ db_buff[db_len]= 0;
+
+ user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ mpvio->charset_adapter->charset(),
+ &dummy_errors);
+ user_buff[user_len]= 0;
+
+ /* we should not free mpvio->user here: it's saved by dispatch_command() */
+ if (!(mpvio->auth_info.user_name= my_strndup(user_buff, user_len, MYF(MY_WME))))
+ return 1;
+ mpvio->auth_info.user_name_length= user_len;
+
+ if (make_lex_string_root(mpvio->mem_root,
+ &mpvio->db, db_buff, db_len, 0) == 0)
+ DBUG_RETURN (1); /* The error is set by make_lex_string(). */
+
+ if (!initialized)
+ {
+ // if mysqld's been started with --skip-grant-tables option
+ strmake(mpvio->auth_info.authenticated_as,
+ mpvio->auth_info.user_name, USERNAME_LENGTH);
+
+ mpvio->status= MPVIO_EXT::SUCCESS;
+ DBUG_RETURN (0);
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (find_mpvio_user(mpvio))
+ DBUG_RETURN (1);
+
+ char *client_plugin;
+ if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ client_plugin= ptr + 2;
+ if (client_plugin >= end)
+ {
+ my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
+ DBUG_RETURN (1);
+ }
+ }
+ else
+ {
+ if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
+ client_plugin= native_password_plugin_name.str;
+ else
+ {
+ client_plugin= old_password_plugin_name.str;
+ /*
+ For a passwordless accounts we use native_password_plugin.
+ But when an old 4.0 client connects to it, we change it to
+ old_password_plugin, otherwise MySQL will think that server
+ and client plugins don't match.
+ */
+ if (mpvio->acl_user->auth_string.length == 0)
+ mpvio->acl_user_plugin= old_password_plugin_name;
+ }
+ }
+
+ DBUG_PRINT ("info", ("client_plugin=%s, restart", client_plugin));
+ /*
+ Remember the data part of the packet, to present it to plugin in
+ read_packet()
+ */
+ mpvio->cached_client_reply.pkt= passwd;
+ mpvio->cached_client_reply.pkt_len= passwd_len;
+ mpvio->cached_client_reply.plugin= client_plugin;
+ mpvio->status= MPVIO_EXT::RESTART;
+#endif
+
+ DBUG_RETURN (0);
+}
+
+/* the packet format is described in send_client_reply_packet() */
+static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio,
+ uchar **buff, ulong pkt_len)
+{
+#ifndef EMBEDDED_LIBRARY
+ NET *net= mpvio->net;
+ char *end;
+
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE);
+
+ if (pkt_len < MIN_HANDSHAKE_SIZE)
+ return packet_error;
+
+ if (mpvio->connect_errors)
+ reset_host_errors(mpvio->ip);
+
+ ulong client_capabilities= uint2korr(net->read_pos);
+ if (client_capabilities & CLIENT_PROTOCOL_41)
+ {
+ client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
+ mpvio->max_client_packet_length= uint4korr(net->read_pos+4);
+ DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
+ if (mpvio->charset_adapter->init_client_charset((uint) net->read_pos[8]))
+ return packet_error;
+ end= (char*) net->read_pos+32;
+ }
+ else
+ {
+ mpvio->max_client_packet_length= uint3korr(net->read_pos+2);
+ end= (char*) net->read_pos+5;
+ }
+
+ /* Disable those bits which are not supported by the client. */
+ mpvio->client_capabilities&= client_capabilities;
+
+
+#if defined(HAVE_OPENSSL)
+ DBUG_PRINT("info", ("client capabilities: %lu", mpvio->client_capabilities));
+ if (mpvio->client_capabilities & CLIENT_SSL)
+ {
+ char error_string[1024] __attribute__((unused));
+
+ /* Do the SSL layering. */
+ if (!ssl_acceptor_fd)
+ return packet_error;
+
+ DBUG_PRINT("info", ("IO layer change in progress..."));
+ if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
+ {
+ DBUG_PRINT("error", ("Failed to accept new SSL connection"));
+ return packet_error;
+ }
+
+ DBUG_PRINT("info", ("Reading user information over SSL layer"));
+ pkt_len= my_net_read(net);
+ if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE)
+ {
+ DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
+ pkt_len));
+ return packet_error;
+ }
+ }
+#endif
+
+ if (end >= (char*) net->read_pos+ pkt_len +2)
+ return packet_error;
+
+ if ((mpvio->client_capabilities & CLIENT_TRANSACTIONS) &&
+ opt_using_transactions)
+ net->return_status= mpvio->server_status;
+
+ char *user= end;
+ char *passwd= strend(user)+1;
+ uint user_len= passwd - user - 1, db_len;
+ char *db= passwd;
+ char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
+ char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
+ uint dummy_errors;
+
+ /*
+ Old clients send null-terminated string as password; new clients send
+ the size (1 byte) + string (not null-terminated). Hence in case of empty
+ password both send '\0'.
+
+ This strlen() can't be easily deleted without changing protocol.
+
+ Cast *passwd to an unsigned char, so that it doesn't extend the sign for
+ *passwd > 127 and become 2**32-127+ after casting to uint.
+ */
+ uint passwd_len= mpvio->client_capabilities & CLIENT_SECURE_CONNECTION ?
+ (uchar)(*passwd++) : strlen(passwd);
+
+ if (mpvio->client_capabilities & CLIENT_CONNECT_WITH_DB)
+ {
+ db= db + passwd_len + 1;
+ /* strlen() can't be easily deleted without changing protocol */
+ db_len= strlen(db);
+ }
+ else
+ {
+ db= 0;
+ db_len= 0;
+ }
+
+ if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
+ return packet_error;
+
+ char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0);
+
+ /* Since 4.1 all database names are stored in utf8 */
+ if (db)
+ {
+ db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info,
+ db, db_len, mpvio->charset_adapter->charset(),
+ &dummy_errors);
+ db= db_buff;
+ db_buff[db_len]= 0;
+ }
+
+ user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
+ system_charset_info, user, user_len,
+ mpvio->charset_adapter->charset(),
+ &dummy_errors);
+ user= user_buff;
+ user_buff[user_len]= 0;
+
+ /* If username starts and ends in "'", chop them off */
+ if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
+ {
+ user[user_len-1]= 0;
+ user++;
+ user_len-= 2;
+ }
+
+ if (make_lex_string_root(mpvio->mem_root,
+ &mpvio->db, db, db_len, 0) == 0)
+ return packet_error; /* The error is set by make_lex_string(). */
+ if (mpvio->auth_info.user_name)
+ my_free(mpvio->auth_info.user_name);
+ if (!(mpvio->auth_info.user_name= my_strndup(user, user_len, MYF(MY_WME))))
+ return packet_error; /* The error is set by my_strdup(). */
+ mpvio->auth_info.user_name_length= user_len;
+
+ if (!initialized)
+ {
+ // if mysqld's been started with --skip-grant-tables option
+ mpvio->status= MPVIO_EXT::SUCCESS;
+ return packet_error;
+ }
+
+ if (find_mpvio_user(mpvio))
+ return packet_error;
+
+ if (mpvio->client_capabilities & CLIENT_PLUGIN_AUTH)
+ {
+ if ((client_plugin + strlen(client_plugin)) >
+ (char *)net->read_pos + pkt_len)
+ return packet_error;
+ }
+ else
+ {
+ if (mpvio->client_capabilities & CLIENT_SECURE_CONNECTION)
+ client_plugin= native_password_plugin_name.str;
+ else
+ {
+ client_plugin= old_password_plugin_name.str;
+ /*
+ For a passwordless accounts we use native_password_plugin.
+ But when an old 4.0 client connects to it, we change it to
+ old_password_plugin, otherwise MySQL will think that server
+ and client plugins don't match.
+ */
+ if (mpvio->acl_user->auth_string.length == 0)
+ mpvio->acl_user_plugin= old_password_plugin_name;
+ }
+ }
+
+ /*
+ if the acl_user needs a different plugin to authenticate
+ (specified in GRANT ... AUTHENTICATED VIA plugin_name ..)
+ we need to restart the authentication in the server.
+ But perhaps the client has already used the correct plugin -
+ in that case the authentication on the client may not need to be
+ restarted and a server auth plugin will read the data that the client
+ has just send. Cache them to return in the next server_mpvio_read_packet().
+ */
+ if (my_strcasecmp(system_charset_info, mpvio->acl_user_plugin.str,
+ plugin_name(mpvio->plugin)->str) != 0)
+ {
+ mpvio->cached_client_reply.pkt= passwd;
+ mpvio->cached_client_reply.pkt_len= passwd_len;
+ mpvio->cached_client_reply.plugin= client_plugin;
+ mpvio->status= MPVIO_EXT::RESTART;
+ return packet_error;
+ }
+
+ /*
+ ok, we don't need to restart the authentication on the server.
+ but if the client used the wrong plugin, we need to restart
+ the authentication on the client. Do it here, the server plugin
+ doesn't need to know.
+ */
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+
+ if (client_auth_plugin &&
+ my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin))
+ {
+ mpvio->cached_client_reply.plugin= client_plugin;
+ if (send_plugin_request_packet(mpvio,
+ (uchar*)mpvio->cached_server_packet.pkt,
+ mpvio->cached_server_packet.pkt_len))
+ return packet_error;
+
+ passwd_len= my_net_read(mpvio->net);
+ passwd = (char*)mpvio->net->read_pos;
+ }
+
+ *buff= (uchar*)passwd;
+ return passwd_len;
+#else
+ return 0;
+#endif
+}
+
+
+/**
+ Make sure that when sending plugin supplued data to the client they
+ are not considered a special out-of-band command, like e.g.
+ \255 (error) or \254 (change user request packet).
+ To avoid this we send plugin data packets starting with one of these
+ 2 bytes "wrapped" in a command \1.
+ For the above reason we have to wrap plugin data packets starting with
+ \1 as well.
+*/
+
+#define IS_OUT_OF_BAND_PACKET(packet,packet_len) \
+ ((packet_len) > 0 && \
+ (*(packet) == 1 || *(packet) == 255 || *(packet) == 254))
+
+static inline int
+wrap_plguin_data_into_proper_command(NET *net,
+ const uchar *packet, int packet_len)
+{
+ DBUG_ASSERT(IS_OUT_OF_BAND_PACKET(packet, packet_len));
+ return net_write_command(net, 1, (uchar*)"", 0, packet, packet_len);
+}
+
+
+/**
+ vio->write_packet() callback method for server authentication plugins
+
+ This function is called by a server authentication plugin, when it wants
+ to send data to the client.
+
+ It transparently wraps the data into a handshake packet,
+ and handles plugin negotiation with the client. If necessary,
+ it escapes the plugin data, if it starts with a mysql protocol packet byte.
+*/
+static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param,
+ const uchar *packet, int packet_len)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ int res;
+
+ DBUG_ENTER ("server_mpvio_write_packet");
+ /* reset cached_client_reply */
+ mpvio->cached_client_reply.pkt= 0;
+ /* for the 1st packet we wrap plugin data into the handshake packet */
+ if (mpvio->packets_written == 0)
+ res= send_server_handshake_packet(mpvio, (char*)packet, packet_len);
+ else if (mpvio->status == MPVIO_EXT::RESTART)
+ res= send_plugin_request_packet(mpvio, packet, packet_len);
+ else if (IS_OUT_OF_BAND_PACKET(packet, packet_len))
+ res= wrap_plguin_data_into_proper_command(mpvio->net, packet, packet_len);
+ else
+ {
+ res= my_net_write(mpvio->net, packet, packet_len) ||
+ net_flush(mpvio->net);
+ }
+ mpvio->packets_written++;
+ DBUG_RETURN (res);
+}
+
+/**
+ vio->read_packet() callback method for server authentication plugins
+
+ This function is called by a server authentication plugin, when it wants
+ to read data from the client.
+
+ It transparently extracts the client plugin data, if embedded into
+ a client authentication handshake packet, and handles plugin negotiation
+ with the client, if necessary.
+*/
+static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)param;
+ ulong pkt_len;
+
+ DBUG_ENTER ("server_mpvio_read_packet");
+ if (mpvio->packets_written == 0)
+ {
+ /*
+ plugin wants to read the data without sending anything first.
+ send an empty packet to force a server handshake packet to be sent
+ */
+ if (mpvio->write_packet(mpvio, 0, 0))
+ pkt_len= packet_error;
+ else
+ pkt_len= my_net_read(mpvio->net);
+ }
+ else if (mpvio->cached_client_reply.pkt)
+ {
+ DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART);
+ DBUG_ASSERT(mpvio->packets_read > 0);
+ /*
+ if the have the data cached from the last server_mpvio_read_packet
+ (which can be the case if it's a restarted authentication)
+ and a client has used the correct plugin, then we can return the
+ cached data straight away and avoid one round trip.
+ */
+ const char *client_auth_plugin=
+ ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin;
+ if (client_auth_plugin == 0 ||
+ my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin,
+ client_auth_plugin) == 0)
+ {
+ mpvio->status= MPVIO_EXT::FAILURE;
+ *buf= (uchar*)mpvio->cached_client_reply.pkt;
+ mpvio->cached_client_reply.pkt= 0;
+ mpvio->packets_read++;
+ DBUG_RETURN ((int)mpvio->cached_client_reply.pkt_len);
+ }
+ /*
+ But if the client has used the wrong plugin, the cached data are
+ useless. Furthermore, we have to send a "change plugin" request
+ to the client.
+ */
+ if (mpvio->write_packet(mpvio, 0, 0))
+ pkt_len= packet_error;
+ else
+ pkt_len= my_net_read(mpvio->net);
+ }
+ else
+ pkt_len= my_net_read(mpvio->net);
+
+ if (pkt_len == packet_error)
+ goto err;
+
+ mpvio->packets_read++;
+
+ /*
+ the 1st packet has the plugin data wrapped into the client authentication
+ handshake packet
+ */
+ if (mpvio->packets_read == 1)
+ {
+ pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len);
+ if (pkt_len == packet_error)
+ goto err;
+ }
+ else
+ *buf = mpvio->net->read_pos;
+
+ DBUG_RETURN ((int)pkt_len);
+
+err:
+ if (mpvio->status == MPVIO_EXT::FAILURE)
+ {
+ inc_host_errors(mpvio->ip);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+ }
+ DBUG_RETURN (-1);
+}
+
+/**
+ fills MYSQL_PLUGIN_VIO_INFO structure with the information about the
+ connection
+*/
+static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_PLUGIN_VIO_INFO *info)
+{
+ MPVIO_EXT *mpvio= (MPVIO_EXT*)vio;
+ mpvio_info(mpvio->net->vio, info);
+}
+
+static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user)
+{
+#if defined(HAVE_OPENSSL)
+ Vio *vio=thd->net.vio;
+ SSL *ssl= (SSL*) vio->ssl_arg;
+ X509 *cert;
+#endif
+
+ /*
+ At this point we know that user is allowed to connect
+ from given host by given username/password pair. Now
+ we check if SSL is required, if user is using SSL and
+ if X509 certificate attributes are OK
+ */
+ switch (acl_user->ssl_type) {
+ case SSL_TYPE_NOT_SPECIFIED: // Impossible
+ case SSL_TYPE_NONE: // SSL is not required
+ return 0;
+#if defined(HAVE_OPENSSL)
+ case SSL_TYPE_ANY: // Any kind of SSL is ok
+ return vio_type(vio) != VIO_TYPE_SSL;
+ case SSL_TYPE_X509: /* Client should have any valid certificate. */
+ /*
+ Connections with non-valid certificates are dropped already
+ in sslaccept() anyway, so we do not check validity here.
+
+ We need to check for absence of SSL because without SSL
+ we should reject connection.
+ */
+ if (vio_type(vio) == VIO_TYPE_SSL &&
+ SSL_get_verify_result(ssl) == X509_V_OK &&
+ (cert= SSL_get_peer_certificate(ssl)))
+ {
+ X509_free(cert);
+ return 0;
+ }
+ return 1;
+ case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */
+ /* If a cipher name is specified, we compare it to actual cipher in use. */
+ if (vio_type(vio) != VIO_TYPE_SSL ||
+ SSL_get_verify_result(ssl) != X509_V_OK)
+ return 1;
+ if (acl_user->ssl_cipher)
+ {
+ DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'",
+ acl_user->ssl_cipher,SSL_get_cipher(ssl)));
+ if (strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl)))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'",
+ acl_user->ssl_cipher, SSL_get_cipher(ssl));
+ return 1;
+ }
+ }
+ /* Prepare certificate (if exists) */
+ if (!(cert= SSL_get_peer_certificate(ssl)))
+ return 1;
+ /* If X509 issuer is specified, we check it... */
+ if (acl_user->x509_issuer)
+ {
+ char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing issuers: '%s' and '%s'",
+ acl_user->x509_issuer, ptr));
+ if (strcmp(acl_user->x509_issuer, ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 issuer mismatch: should be '%s' "
+ "but is '%s'", acl_user->x509_issuer, ptr);
+ free(ptr);
+ X509_free(cert);
+ return 1;
+ }
+ free(ptr);
+ }
+ /* X509 subject is specified, we check it .. */
+ if (acl_user->x509_subject)
+ {
+ char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
+ DBUG_PRINT("info",("comparing subjects: '%s' and '%s'",
+ acl_user->x509_subject, ptr));
+ if (strcmp(acl_user->x509_subject,ptr))
+ {
+ if (global_system_variables.log_warnings)
+ sql_print_information("X509 subject mismatch: should be '%s' but is '%s'",
+ acl_user->x509_subject, ptr);
+ free(ptr);
+ X509_free(cert);
+ return 1;
+ }
+ free(ptr);
+ }
+ X509_free(cert);
+ return 0;
+#else /* HAVE_OPENSSL */
+ default:
+ /*
+ If we don't have SSL but SSL is required for this user the
+ authentication should fail.
+ */
+ return 1;
+#endif /* HAVE_OPENSSL */
+ }
+ return 1;
+}
+
+
+static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name,
+ MPVIO_EXT *mpvio)
+{
+ int res= CR_OK, old_status= MPVIO_EXT::FAILURE;
+ bool unlock_plugin= false;
+ plugin_ref plugin;
+
+ if (auth_plugin_name->str == native_password_plugin_name.str)
+ plugin= native_password_plugin;
+ else
+#ifndef EMBEDDED_LIBRARY
+ if (auth_plugin_name->str == old_password_plugin_name.str)
+ plugin= old_password_plugin;
+ else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name,
+ MYSQL_AUTHENTICATION_PLUGIN)))
+ unlock_plugin= true;
+ else
+#endif
+ plugin= NULL;
+
+ mpvio->plugin= plugin;
+ old_status= mpvio->status;
+
+ if (plugin)
+ {
+ st_mysql_auth *auth= (st_mysql_auth*)plugin_decl(plugin)->info;
+ res= auth->authenticate_user(mpvio, &mpvio->auth_info);
+
+ if (unlock_plugin)
+ plugin_unlock(thd, plugin);
+ }
+ else
+ {
+ /* Server cannot load the required plugin. */
+ my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str);
+ res= CR_ERROR;
+ }
+
+ /*
+ If the status was MPVIO_EXT::RESTART before the authenticate_user() call
+ it can never be MPVIO_EXT::RESTART after the call, because any call
+ to write_packet() or read_packet() will reset the status.
+
+ But (!) if a plugin never called a read_packet() or write_packet(), the
+ status will stay unchanged. We'll fix it, by resetting the status here.
+ */
+ if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART)
+ mpvio->status= MPVIO_EXT::FAILURE; // reset to the default
+
+ return res;
+}
+
+
+static void
+server_mpvio_initialize(THD *thd, MPVIO_EXT *mpvio, uint connect_errors,
+ Thd_charset_adapter *charset_adapter)
+{
+ memset(mpvio, 0, sizeof(MPVIO_EXT));
+ mpvio->read_packet= server_mpvio_read_packet;
+ mpvio->write_packet= server_mpvio_write_packet;
+ mpvio->info= server_mpvio_info;
+ mpvio->auth_info.host_or_ip= thd->security_ctx->host_or_ip;
+ mpvio->auth_info.host_or_ip_length=
+ (unsigned int) strlen (thd->security_ctx->host_or_ip);
+ mpvio->auth_info.user_name= thd->security_ctx->user;
+ mpvio->auth_info.user_name_length= thd->security_ctx->user ?
+ (unsigned int) strlen(thd->security_ctx->user) : 0;
+ mpvio->connect_errors= connect_errors;
+ mpvio->status= MPVIO_EXT::FAILURE;
+
+ mpvio->client_capabilities= thd->client_capabilities;
+ mpvio->mem_root= thd->mem_root;
+ mpvio->scramble= thd->scramble;
+ mpvio->rand= &thd->rand;
+ mpvio->thread_id= thd->thread_id;
+ mpvio->server_status= &thd->server_status;
+ mpvio->net= &thd->net;
+ mpvio->ip= thd->security_ctx->ip;
+ mpvio->host= thd->security_ctx->host;
+ mpvio->charset_adapter= charset_adapter;
+}
+
+
+static void
+server_mpvio_update_thd(THD *thd, MPVIO_EXT *mpvio)
+{
+ thd->client_capabilities= mpvio->client_capabilities;
+ thd->max_client_packet_length= mpvio->max_client_packet_length;
+ if (mpvio->client_capabilities & CLIENT_INTERACTIVE)
+ thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
+ thd->security_ctx->user= mpvio->auth_info.user_name;
+ if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
+ thd->variables.sql_mode|= MODE_IGNORE_SPACE;
+}
+
+/**
+ Perform the handshake, authorize the client and update thd sctx variables.
+
+ @param thd thread handle
+ @param connect_errors number of previous failed connect attemps
+ from this host
+ @param com_change_user_pkt_len size of the COM_CHANGE_USER packet
+ (without the first, command, byte) or 0
+ if it's not a COM_CHANGE_USER (that is, if
+ it's a new connection)
+
+ @retval 0 success, thd is updated.
+ @retval 1 error
+*/
+bool
+acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len)
+{
+ int res= CR_OK;
+ MPVIO_EXT mpvio;
+ Thd_charset_adapter charset_adapter(thd);
+
+ const LEX_STRING *auth_plugin_name= default_auth_plugin_name;
+ enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER
+ : COM_CONNECT;
+
+ DBUG_ENTER ("acl_authenticate");
+ compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH);
+
+ server_mpvio_initialize(thd, &mpvio, connect_errors, &charset_adapter);
+
+ DBUG_PRINT ("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len));
+
+ /*
+ Clear thd->db as it points to something, that will be freed when
+ connection is closed. We don't want to accidentally free a wrong
+ pointer if connect failed.
+ */
+ thd->reset_db(NULL, 0);
+
+ if (command == COM_CHANGE_USER)
+ {
+ mpvio.packets_written++; // pretend that a server handshake packet was sent
+ mpvio.packets_read++; // take COM_CHANGE_USER packet into account
+
+ /* Clear variables that are allocated */
+ thd->user_connect= 0;
+
+ if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len))
+ {
+ server_mpvio_update_thd(thd, &mpvio);
+ DBUG_RETURN(1);
+ }
+
+ DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART ||
+ mpvio.status == MPVIO_EXT::SUCCESS);
+ }
+ else
+ {
+ /* mark the thd as having no scramble yet */
+ mpvio.scramble[SCRAMBLE_LENGTH]= 1;
+
+ /*
+ perform the first authentication attempt, with the default plugin.
+ This sends the server handshake packet, reads the client reply
+ with a user name, and performs the authentication if everyone has used
+ the correct plugin.
+ */
+
+ res= do_auth_once(thd, auth_plugin_name, &mpvio);
+ }
+
+ /*
+ retry the authentication, if - after receiving the user name -
+ we found that we need to switch to a non-default plugin
+ */
+ if (mpvio.status == MPVIO_EXT::RESTART)
+ {
+ DBUG_ASSERT (mpvio.acl_user);
+ DBUG_ASSERT(command == COM_CHANGE_USER ||
+ my_strcasecmp(system_charset_info, auth_plugin_name->str,
+ mpvio.acl_user->plugin.str));
+ auth_plugin_name= &mpvio.acl_user->plugin;
+ res= do_auth_once (thd, auth_plugin_name, &mpvio);
+ }
+
+ server_mpvio_update_thd(thd, &mpvio);
+
+ Security_context *sctx= thd->security_ctx;
+ const ACL_USER *acl_user= mpvio.acl_user;
+
+ thd->password= mpvio.auth_info.password_used; // remember for error messages
+
+ /*
+ Log the command here so that the user can check the log
+ for the tried logins and also to detect break-in attempts.
+
+ if sctx->user is unset it's protocol failure, bad packet.
+ */
+ if (mpvio.auth_info.user_name)
+ {
+ if (strcmp(mpvio.auth_info.authenticated_as, mpvio.auth_info.user_name))
+ {
+ general_log_print(thd, command, "%s@%s as %s on %s",
+ mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
+ mpvio.auth_info.authenticated_as ?
+ mpvio.auth_info.authenticated_as : "anonymous",
+ mpvio.db.str ? mpvio.db.str : (char*) "");
+ }
+ else
+ general_log_print(thd, command, (char*) "%s@%s on %s",
+ mpvio.auth_info.user_name, mpvio.auth_info.host_or_ip,
+ mpvio.db.str ? mpvio.db.str : (char*) "");
+ }
+
+ if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS)
+ {
+ DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE);
+
+ if (!thd->is_error())
+ login_failed_error(&mpvio, mpvio.auth_info.password_used);
+ DBUG_RETURN (1);
+ }
+
+ sctx->proxy_user[0]= 0;
+
+ if (initialized) // if not --skip-grant-tables
+ {
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool is_proxy_user= FALSE;
+ const char *auth_user = mpvio.acl_user->user ? mpvio.acl_user->user : "";
+ /* check if the user is allowed to proxy as another user */
+ if (acl_find_proxy_user(auth_user, sctx->host, sctx->ip,
+ mpvio.auth_info.authenticated_as,
+ &is_proxy_user))
+ {
+ if (!thd->is_error())
+ login_failed_error(&mpvio, mpvio.auth_info.password_used);
+ DBUG_RETURN(1);
+ }
+
+ if (is_proxy_user)
+ my_snprintf(sctx->proxy_user, sizeof (sctx->proxy_user) - 1,
+ "'%s'@'%s'", auth_user,
+ acl_user->host.hostname ? acl_user->host.hostname : "");
+#endif
+
+ sctx->master_access= acl_user->access;
+ strmake(sctx->priv_user, mpvio.auth_info.authenticated_as, USERNAME_LENGTH - 1);
+ if (acl_user->host.hostname)
+ strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1);
+ else
+ *sctx->priv_host= 0;
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ OK. Let's check the SSL. Historically it was checked after the password,
+ as an additional layer, not instead of the password
+ (in which case it would've been a plugin too).
+ */
+ if (acl_check_ssl(thd, acl_user))
+ {
+ if (!thd->is_error())
+ login_failed_error(&mpvio, thd->password);
+ DBUG_RETURN (1);
+ }
+
+ /* Don't allow the user to connect if he has done too many queries */
+ if ((acl_user->user_resource.questions || acl_user->user_resource.updates ||
+ acl_user->user_resource.conn_per_hour ||
+ acl_user->user_resource.user_conn ||
+ global_system_variables.max_user_connections) &&
+ get_or_create_user_conn(thd,
+ (opt_old_style_user_limits ? sctx->user : sctx->priv_user),
+ (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host),
+ &acl_user->user_resource))
+ DBUG_RETURN (1); // The error is set by get_or_create_user_conn()
+
+#endif
+ }
+ else
+ sctx->skip_grants();
+
+ if (thd->user_connect &&
+ (thd->user_connect->user_resources.conn_per_hour ||
+ thd->user_connect->user_resources.user_conn ||
+ global_system_variables.max_user_connections) &&
+ check_for_max_user_connections(thd, thd->user_connect))
+ {
+ DBUG_RETURN (1); // The error is set in check_for_max_user_connections()
+ }
+
+ DBUG_PRINT("info",
+ ("Capabilities: %lu packet_length: %ld Host: '%s' "
+ "Login user: '%s' Priv_user: '%s' Using password: %s "
+ "Access: %lu db: '%s'",
+ thd->client_capabilities, thd->max_client_packet_length,
+ sctx->host_or_ip, sctx->user, sctx->priv_user,
+ thd->password ? "yes": "no",
+ sctx->master_access, mpvio.db.str));
+
+ if (command == COM_CONNECT &&
+ !(thd->main_security_ctx.master_access & SUPER_ACL))
+ {
+ mysql_mutex_lock(&LOCK_connection_count);
+ bool count_ok= (connection_count <= max_connections);
+ mysql_mutex_unlock(&LOCK_connection_count);
+ if (!count_ok)
+ { // too many connections
+ my_error(ER_CON_COUNT_ERROR, MYF(0));
+ DBUG_RETURN (1);
+ }
+ }
+
+ /*
+ This is the default access rights for the current database. It's
+ set to 0 here because we don't have an active database yet (and we
+ may not have an active database to set.
+ */
+ sctx->db_access=0;
+
+ /* Change a database if necessary */
+ if (mpvio.db.length)
+ {
+ if (mysql_change_db(thd, &mpvio.db, FALSE))
+ {
+ /* mysql_change_db() has pushed the error message. */
+ if (thd->user_connect)
+ {
+ decrease_user_connections(thd->user_connect);
+ thd->user_connect= 0;
+ }
+ DBUG_RETURN (1);
+ }
+ }
+
+ if (mpvio.auth_info.external_user[0])
+ sctx->external_user= my_strdup(mpvio.auth_info.external_user, MYF(0));
+
+ if (res == CR_OK_HANDSHAKE_COMPLETE)
+ thd->stmt_da->disable_status();
+ else
+ my_ok(thd);
+
+#if defined(MYSQL_SERVER) && !defined(EMBEDDED_LIBRARY)
+ /*
+ Allow the network layer to skip big packets. Although a malicious
+ authenticated session might use this to trick the server to read
+ big packets indefinitely, this is a previously established behavior
+ that needs to be preserved as to not break backwards compatibility.
+ */
+ thd->net.skip_big_packet= TRUE;
+#endif
+
+ /* Ready to handle queries */
+ DBUG_RETURN (0);
+}
+
+/**
+ MySQL Server Password Authentication Plugin
+
+ In the MySQL authentication protocol:
+ 1. the server sends the random scramble to the client
+ 2. client sends the encrypted password back to the server
+ 3. the server checks the password.
+*/
+static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_SERVER_AUTH_INFO *info)
+{
+ uchar *pkt;
+ int pkt_len;
+ MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+
+ DBUG_ENTER ("native_password_authenticate");
+
+ /* generate the scramble, or reuse the old one */
+ if (mpvio->scramble[SCRAMBLE_LENGTH])
+ create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+
+ /* send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)mpvio->scramble, SCRAMBLE_LENGTH + 1))
+ return CR_ERROR;
+
+ /* reply and authenticate */
+
+ /*
+ <digression>
+ This is more complex than it looks.
+
+ The plugin (we) may be called right after the client was connected -
+ and will need to send a scramble, read reply, authenticate.
+
+ Or the plugin may be called after another plugin has sent a scramble,
+ and read the reply. If the client has used the correct client-plugin,
+ we won't need to read anything here from the client, the client
+ has already sent a reply with everything we need for authentication.
+
+ Or the plugin may be called after another plugin has sent a scramble,
+ and read the reply, but the client has used the wrong client-plugin.
+ We'll need to sent a "switch to another plugin" packet to the
+ client and read the reply. "Use the short scramble" packet is a special
+ case of "switch to another plugin" packet.
+
+ Or, perhaps, the plugin may be called after another plugin has
+ done the handshake but did not send a useful scramble. We'll need
+ to send a scramble (and perhaps a "switch to another plugin" packet)
+ and read the reply.
+
+ Besides, a client may be an old one, that doesn't understand plugins.
+ Or doesn't even understand 4.0 scramble.
+
+ And we want to keep the same protocol on the wire unless non-native
+ plugins are involved.
+
+ Anyway, it still looks simple from a plugin point of view:
+ "send the scramble, read the reply and authenticate"
+ All the magic is transparently handled by the server.
+ </digression>
+ */
+
+ /* read the reply with the encrypted password */
+ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+ DBUG_RETURN (CR_ERROR);
+ DBUG_PRINT ("info", ("reply read : pkt_len=%d", pkt_len));
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ DBUG_RETURN (CR_OK);
+#endif
+
+ if (pkt_len == 0) /* no password */
+ DBUG_RETURN (info->auth_string[0] ? CR_ERROR : CR_OK);
+
+ info->password_used = 1;
+ if (pkt_len == SCRAMBLE_LENGTH)
+ {
+ if (!mpvio->acl_user->salt_len)
+ DBUG_RETURN(CR_ERROR);
+
+ DBUG_RETURN (check_scramble(pkt, mpvio->scramble, mpvio->acl_user->salt) ?
+ CR_ERROR : CR_OK);
+ }
+
+ inc_host_errors(mpvio->ip);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+ DBUG_RETURN (CR_ERROR);
+}
+
+static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio,
+ MYSQL_SERVER_AUTH_INFO *info)
+{
+ uchar *pkt;
+ int pkt_len;
+ MPVIO_EXT *mpvio=(MPVIO_EXT*)vio;
+
+ /* generate the scramble, or reuse the old one */
+ if (mpvio->scramble[SCRAMBLE_LENGTH])
+ create_random_string(mpvio->scramble, SCRAMBLE_LENGTH, mpvio->rand);
+
+ /* send it to the client */
+ if (mpvio->write_packet(mpvio, (uchar*)mpvio->scramble, SCRAMBLE_LENGTH + 1))
+ return CR_ERROR;
+
+ /* read the reply and authenticate */
+ if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0)
+ return CR_ERROR;
+
+#ifdef NO_EMBEDDED_ACCESS_CHECKS
+ return CR_OK;
+#endif
+
+ /*
+ legacy: if switch_from_long_to_short_scramble,
+ the password is sent \0-terminated, the pkt_len is always 9 bytes.
+ We need to figure out the correct scramble length here.
+ */
+ if (pkt_len == SCRAMBLE_LENGTH_323+1)
+ pkt_len= strnlen((char*)pkt, pkt_len);
+
+ if (pkt_len == 0) /* no password */
+ return info->auth_string[0] ? CR_ERROR : CR_OK;
+
+ if (secure_auth(mpvio))
+ return CR_ERROR;
+
+ info->password_used = 1;
+
+ if (pkt_len == SCRAMBLE_LENGTH_323)
+ {
+ if (!mpvio->acl_user->salt_len)
+ return CR_ERROR;
+
+ return check_scramble_323(pkt, mpvio->scramble,
+ (ulong *)mpvio->acl_user->salt) ? CR_ERROR : CR_OK;
+ }
+
+ inc_host_errors(mpvio->ip);
+ my_error(ER_HANDSHAKE_ERROR, MYF(0), mpvio->auth_info.host_or_ip);
+ return CR_ERROR;
+}
+
+static struct st_mysql_auth native_password_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ native_password_plugin_name.str,
+ native_password_authenticate
+};
+
+static struct st_mysql_auth old_password_handler=
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ old_password_plugin_name.str,
+ old_password_authenticate
+};
+
+mysql_declare_plugin(mysql_password)
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &native_password_handler, /* type descriptor */
+ native_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Native MySQL authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+},
+{
+ MYSQL_AUTHENTICATION_PLUGIN, /* type constant */
+ &old_password_handler, /* type descriptor */
+ old_password_plugin_name.str, /* Name */
+ "R.J.Silk, Sergei Golubchik", /* Author */
+ "Old MySQL-4.0 authentication", /* Description */
+ PLUGIN_LICENSE_GPL, /* License */
+ NULL, /* Init function */
+ NULL, /* Deinit function */
+ 0x0100, /* Version (1.0) */
+ NULL, /* status variables */
+ NULL, /* system variables */
+ NULL /* config options */
+}
+mysql_declare_plugin_end;
+
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index c0b536b7740..3a7eefa058c 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -173,53 +173,6 @@ enum mysql_db_table_field
extern const TABLE_FIELD_DEF mysql_db_table_def;
extern bool mysql_user_table_is_in_short_password_format;
-/* Classes */
-
-struct acl_host_and_ip
-{
- char *hostname;
- long ip,ip_mask; // Used with masked ip:s
-};
-
-
-class ACL_ACCESS {
-public:
- ulong sort;
- ulong access;
-};
-
-
-/* ACL_HOST is used if no host is specified */
-
-class ACL_HOST :public ACL_ACCESS
-{
-public:
- acl_host_and_ip host;
- char *db;
-};
-
-
-class ACL_USER :public ACL_ACCESS
-{
-public:
- acl_host_and_ip host;
- uint hostname_length;
- USER_RESOURCES user_resource;
- char *user;
- uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form
- uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1
- enum SSL_type ssl_type;
- const char *ssl_cipher, *x509_issuer, *x509_subject;
-};
-
-
-class ACL_DB :public ACL_ACCESS
-{
-public:
- acl_host_and_ip host;
- char *user,*db;
-};
-
/* prototypes */
bool hostname_requires_resolving(const char *hostname);
@@ -228,17 +181,16 @@ my_bool acl_reload(THD *thd);
void acl_free(bool end=0);
ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
-int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
- uint passwd_len);
-bool acl_getroot_no_password(Security_context *sctx, char *user, char *host,
- char *ip, char *db);
+bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len);
+bool acl_getroot(Security_context *sctx, char *user, char *host,
+ char *ip, char *db);
bool acl_check_host(const char *host, const char *ip);
int check_change_password(THD *thd, const char *host, const char *user,
char *password, uint password_len);
bool change_password(THD *thd, const char *host, const char *user,
char *password);
bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list,
- ulong rights, bool revoke);
+ ulong rights, bool revoke, bool is_proxy);
int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list,
List <LEX_COLUMN> &column_list, ulong rights,
bool revoke);
@@ -420,4 +372,6 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info,
const char *schema_name,
const char *table_name);
+bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user,
+ bool with_grant);
#endif /* SQL_ACL_INCLUDED */
diff --git a/sql/sql_audit.h b/sql/sql_audit.h
index 5b6962b9ecb..aad868dc1ab 100644
--- a/sql/sql_audit.h
+++ b/sql/sql_audit.h
@@ -40,7 +40,7 @@ static inline uint make_user_name(THD *thd, char *buf)
{
Security_context *sctx= thd->security_ctx;
return strxnmov(buf, MAX_USER_HOST_SIZE,
- sctx->priv_user ? sctx->priv_user : "", "[",
+ sctx->priv_user[0] ? sctx->priv_user : "", "[",
sctx->user ? sctx->user : "", "] @ ",
sctx->host ? sctx->host : "", " [",
sctx->ip ? sctx->ip : "", "]", NullS) - buf;
diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in
index d9d9b610baf..8265c781e79 100644
--- a/sql/sql_builtin.cc.in
+++ b/sql/sql_builtin.cc.in
@@ -23,7 +23,7 @@ extern "C"
extern
#endif
builtin_plugin
- @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin;
+ @mysql_mandatory_plugins@ @mysql_optional_plugins@ builtin_binlog_plugin, builtin_mysql_password_plugin;
struct st_mysql_plugin *mysql_optional_plugins[]=
{
@@ -32,5 +32,5 @@ struct st_mysql_plugin *mysql_optional_plugins[]=
struct st_mysql_plugin *mysql_mandatory_plugins[]=
{
- builtin_binlog_plugin, @mysql_mandatory_plugins@ 0
+ builtin_binlog_plugin, builtin_mysql_password_plugin, @mysql_mandatory_plugins@ 0
};
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 60a871e9e88..153258c9272 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -532,7 +532,8 @@ THD::THD()
catalog= (char*)"std"; // the only catalog we have for now
main_security_ctx.init();
security_ctx= &main_security_ctx;
- no_errors=password= 0;
+ no_errors= 0;
+ password= 0;
query_start_used= 0;
count_cuted_fields= CHECK_FIELD_IGNORE;
killed= NOT_KILLED;
@@ -1325,6 +1326,20 @@ void THD::cleanup_after_query()
}
+LEX_STRING *
+make_lex_string_root(MEM_ROOT *mem_root,
+ LEX_STRING *lex_str, const char* str, uint length,
+ bool allocate_lex_string)
+{
+ if (allocate_lex_string)
+ if (!(lex_str= (LEX_STRING *)alloc_root(mem_root, sizeof(LEX_STRING))))
+ return 0;
+ if (!(lex_str->str= strmake_root(mem_root, str, length)))
+ return 0;
+ lex_str->length= length;
+ return lex_str;
+}
+
/**
Create a LEX_STRING in this connection.
@@ -1339,13 +1354,8 @@ LEX_STRING *THD::make_lex_string(LEX_STRING *lex_str,
const char* str, uint length,
bool allocate_lex_string)
{
- if (allocate_lex_string)
- if (!(lex_str= (LEX_STRING *)alloc(sizeof(LEX_STRING))))
- return 0;
- if (!(lex_str->str= strmake_root(mem_root, str, length)))
- return 0;
- lex_str->length= length;
- return lex_str;
+ return make_lex_string_root (mem_root, lex_str, str,
+ length, allocate_lex_string);
}
@@ -2911,9 +2921,9 @@ void THD::set_status_var_init()
void Security_context::init()
{
- host= user= priv_user= ip= 0;
+ host= user= ip= external_user= 0;
host_or_ip= "connecting host";
- priv_host[0]= '\0';
+ priv_user[0]= priv_host[0]= '\0';
master_access= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= NO_ACCESS;
@@ -2935,6 +2945,12 @@ void Security_context::destroy()
user= NULL;
}
+ if (external_user)
+ {
+ my_free(external_user);
+ user= NULL;
+ }
+
my_free(ip);
ip= NULL;
}
@@ -2945,8 +2961,7 @@ void Security_context::skip_grants()
/* privileges for the user are unknown everything is allowed */
host_or_ip= (char *)"";
master_access= ~NO_ACCESS;
- priv_user= (char *)"";
- *priv_host= '\0';
+ *priv_user= *priv_host= '\0';
}
@@ -2988,7 +3003,7 @@ bool Security_context::set_user(char *user_arg)
of a statement under credentials of a different user, e.g.
definer of a procedure, we authenticate this user in a local
instance of Security_context by means of this method (and
- ultimately by means of acl_getroot_no_password), and make the
+ ultimately by means of acl_getroot), and make the
local instance active in the thread by re-setting
thd->security_ctx pointer.
@@ -3022,19 +3037,12 @@ change_security_context(THD *thd,
DBUG_ASSERT(definer_user->str && definer_host->str);
*backup= NULL;
- /*
- The current security context may have NULL members
- if we have just started the thread and not authenticated
- any user. This use case is currently in events worker thread.
- */
- needs_change= (thd->security_ctx->priv_user == NULL ||
- strcmp(definer_user->str, thd->security_ctx->priv_user) ||
- thd->security_ctx->priv_host == NULL ||
+ needs_change= (strcmp(definer_user->str, thd->security_ctx->priv_user) ||
my_strcasecmp(system_charset_info, definer_host->str,
thd->security_ctx->priv_host));
if (needs_change)
{
- if (acl_getroot_no_password(this, definer_user->str, definer_host->str,
+ if (acl_getroot(this, definer_user->str, definer_host->str,
definer_host->str, db->str))
{
my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str,
@@ -3392,6 +3400,10 @@ void THD::get_definer(LEX_USER *definer)
definer->host= invoker_host;
definer->password.str= NULL;
definer->password.length= 0;
+ definer->plugin.str= (char *) "";
+ definer->plugin.length= 0;
+ definer->auth.str= (char *) "";
+ definer->auth.length= 0;
}
else
#endif
diff --git a/sql/sql_class.h b/sql/sql_class.h
index b23b65dae2f..53cc3d4251b 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -862,9 +862,13 @@ public:
priv_user - The user privilege we are using. May be "" for anonymous user.
ip - client IP
*/
- char *host, *user, *priv_user, *ip;
+ char *host, *user, *ip;
+ char priv_user[USERNAME_LENGTH];
+ char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5];
/* The host privilege we are using */
char priv_host[MAX_HOSTNAME];
+ /* The external user (if available) */
+ char *external_user;
/* points to host if host is available, otherwise points to ip */
const char *host_or_ip;
ulong master_access; /* Global privileges from mysql.user */
@@ -2100,7 +2104,8 @@ public:
char scramble[SCRAMBLE_LENGTH+1];
bool slave_thread, one_shot_set;
- bool no_errors, password;
+ bool no_errors;
+ uchar password;
/**
Set to TRUE if execution of the current compound statement
can not continue. In particular, disables activation of
@@ -2910,6 +2915,11 @@ my_eof(THD *thd)
#define reenable_binlog(A) (A)->variables.option_bits= tmp_disable_binlog__save_options;}
+LEX_STRING *
+make_lex_string_root(MEM_ROOT *mem_root,
+ LEX_STRING *lex_str, const char* str, uint length,
+ bool allocate_lex_string);
+
/*
Used to hold information about file and file structure in exchange
via non-DB file (...INTO OUTFILE..., ...LOAD DATA...)
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 003203b5466..4822e2d66c3 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -61,9 +61,9 @@
#ifndef NO_EMBEDDED_ACCESS_CHECKS
static HASH hash_user_connections;
-static int get_or_create_user_conn(THD *thd, const char *user,
- const char *host,
- USER_RESOURCES *mqh)
+int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host,
+ const USER_RESOURCES *mqh)
{
int return_val= 0;
size_t temp_len, user_len;
@@ -129,7 +129,6 @@ end:
1 error
*/
-static
int check_for_max_user_connections(THD *thd, USER_CONN *uc)
{
int error=0;
@@ -290,256 +289,6 @@ end:
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-
-/**
- Check if user exist and password supplied is correct.
-
- @param thd thread handle, thd->security_ctx->{host,user,ip} are used
- @param command originator of the check: now check_user is called
- during connect and change user procedures; used for
- logging.
- @param passwd scrambled password received from client
- @param passwd_len length of scrambled password
- @param db database name to connect to, may be NULL
- @param check_count TRUE if establishing a new connection. In this case
- check that we have not exceeded the global
- max_connections limist
-
- @note Host, user and passwd may point to communication buffer.
- Current implementation does not depend on that, but future changes
- should be done with this in mind; 'thd' is INOUT, all other params
- are 'IN'.
-
- @retval 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and
- thd->db are updated; OK is sent to the client.
- @retval 1 error, e.g. access denied or handshake error, not sent to
- the client. A message is pushed into the error stack.
-*/
-
-int
-check_user(THD *thd, enum enum_server_command command,
- const char *passwd, uint passwd_len, const char *db,
- bool check_count)
-{
- DBUG_ENTER("check_user");
- LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 };
-
- /*
- Clear thd->db as it points to something, that will be freed when
- connection is closed. We don't want to accidentally free a wrong
- pointer if connect failed. Also in case of 'CHANGE USER' failure,
- current database will be switched to 'no database selected'.
- */
- thd->reset_db(NULL, 0);
-
-#ifdef NO_EMBEDDED_ACCESS_CHECKS
- thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights
- /* Change database if necessary */
- if (db && db[0])
- {
- if (mysql_change_db(thd, &db_str, FALSE))
- DBUG_RETURN(1);
- }
- my_ok(thd);
- DBUG_RETURN(0);
-#else
-
- my_bool opt_secure_auth_local;
- mysql_mutex_lock(&LOCK_global_system_variables);
- opt_secure_auth_local= opt_secure_auth;
- mysql_mutex_unlock(&LOCK_global_system_variables);
-
- /*
- If the server is running in secure auth mode, short scrambles are
- forbidden.
- */
- if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323)
- {
- my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
- general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
- DBUG_RETURN(1);
- }
- if (passwd_len != 0 &&
- passwd_len != SCRAMBLE_LENGTH &&
- passwd_len != SCRAMBLE_LENGTH_323)
- {
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- DBUG_RETURN(1);
- }
-
- USER_RESOURCES ur;
- int res= acl_getroot(thd, &ur, passwd, passwd_len);
-#ifndef EMBEDDED_LIBRARY
- if (res == -1)
- {
- /*
- This happens when client (new) sends password scrambled with
- scramble(), but database holds old value (scrambled with
- scramble_323()). Here we please client to send scrambled_password
- in old format.
- */
- NET *net= &thd->net;
- if (opt_secure_auth_local)
- {
- my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip);
- general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip);
- DBUG_RETURN(1);
- }
- /* We have to read very specific packet size */
- if (send_old_password_request(thd) ||
- my_net_read(net) != SCRAMBLE_LENGTH_323 + 1)
- {
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- DBUG_RETURN(1);
- }
- /* Final attempt to check the user based on reply */
- /* So as passwd is short, errcode is always >= 0 */
- res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323);
- }
-#endif /*EMBEDDED_LIBRARY*/
- /* here res is always >= 0 */
- if (res == 0)
- {
- if (!(thd->main_security_ctx.master_access &
- NO_ACCESS)) // authentication is OK
- {
- DBUG_PRINT("info",
- ("Capabilities: %lu packet_length: %ld Host: '%s' "
- "Login user: '%s' Priv_user: '%s' Using password: %s "
- "Access: %lu db: '%s'",
- thd->client_capabilities,
- thd->max_client_packet_length,
- thd->main_security_ctx.host_or_ip,
- thd->main_security_ctx.user,
- thd->main_security_ctx.priv_user,
- passwd_len ? "yes": "no",
- thd->main_security_ctx.master_access,
- (thd->db ? thd->db : "*none*")));
-
- if (check_count)
- {
- mysql_mutex_lock(&LOCK_connection_count);
- bool count_ok= connection_count <= max_connections ||
- (thd->main_security_ctx.master_access & SUPER_ACL);
- mysql_mutex_unlock(&LOCK_connection_count);
-
- if (!count_ok)
- { // too many connections
- my_error(ER_CON_COUNT_ERROR, MYF(0));
- DBUG_RETURN(1);
- }
- }
-
- /*
- Log the command before authentication checks, so that the user can
- check the log for the tried login tried and also to detect
- break-in attempts.
- */
- general_log_print(thd, command,
- (thd->main_security_ctx.priv_user ==
- thd->main_security_ctx.user ?
- (char*) "%s@%s on %s" :
- (char*) "%s@%s as anonymous on %s"),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- db ? db : (char*) "");
-
- /*
- This is the default access rights for the current database. It's
- set to 0 here because we don't have an active database yet (and we
- may not have an active database to set.
- */
- thd->main_security_ctx.db_access=0;
-
- /* Don't allow user to connect if he has done too many queries */
- if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
- global_system_variables.max_user_connections) &&
- get_or_create_user_conn(thd,
- (opt_old_style_user_limits ? thd->main_security_ctx.user :
- thd->main_security_ctx.priv_user),
- (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip :
- thd->main_security_ctx.priv_host),
- &ur))
- {
- /* The error is set by get_or_create_user_conn(). */
- DBUG_RETURN(1);
- }
- if (thd->user_connect &&
- (thd->user_connect->user_resources.conn_per_hour ||
- thd->user_connect->user_resources.user_conn ||
- global_system_variables.max_user_connections) &&
- check_for_max_user_connections(thd, thd->user_connect))
- {
- /* The error is set in check_for_max_user_connections(). */
- DBUG_RETURN(1);
- }
-
- /* Change database if necessary */
- if (db && db[0])
- {
- if (mysql_change_db(thd, &db_str, FALSE))
- {
- /* mysql_change_db() has pushed the error message. */
- if (thd->user_connect)
- {
- decrease_user_connections(thd->user_connect);
- thd->user_connect= 0;
- }
- DBUG_RETURN(1);
- }
- }
- my_ok(thd);
- thd->password= test(passwd_len); // remember for error messages
-#ifndef EMBEDDED_LIBRARY
- /*
- Allow the network layer to skip big packets. Although a malicious
- authenticated session might use this to trick the server to read
- big packets indefinitely, this is a previously established behavior
- that needs to be preserved as to not break backwards compatibility.
- */
- thd->net.skip_big_packet= TRUE;
-#endif
- /* Ready to handle queries */
- DBUG_RETURN(0);
- }
- }
- else if (res == 2) // client gave short hash, server has long hash
- {
- my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0));
- general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE));
- DBUG_RETURN(1);
- }
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- /*
- Log access denied messages to the error log when log-warnings = 2
- so that the overhead of the general query log is not required to track
- failed connections.
- */
- if (global_system_variables.log_warnings > 1)
- {
- sql_print_warning(ER(ER_ACCESS_DENIED_ERROR),
- thd->main_security_ctx.user,
- thd->main_security_ctx.host_or_ip,
- passwd_len ? ER(ER_YES) : ER(ER_NO));
- }
- DBUG_RETURN(1);
-#endif /* NO_EMBEDDED_ACCESS_CHECKS */
-}
-
-
/*
Check for maximum allowable user connections, if the mysqld server is
started with corresponding variable that is greater then 0.
@@ -672,17 +421,14 @@ bool init_new_connection_handler_thread()
thd thread handle
RETURN
- 0 success, OK is sent to user, thd is updated.
- -1 error, which is sent to user
- > 0 error code (not sent to user)
+ 0 success, thd is updated.
+ 1 error
*/
static int check_connection(THD *thd)
{
uint connect_errors= 0;
NET *net= &thd->net;
- ulong pkt_len= 0;
- char *end;
DBUG_PRINT("info",
("New connection received on %s", vio_description(net->vio)));
@@ -747,203 +493,10 @@ static int check_connection(THD *thd)
}
vio_keepalive(net->vio, TRUE);
- ulong server_capabilites;
- {
- /* buff[] needs to big enough to hold the server_version variable */
- char buff[SERVER_VERSION_LENGTH + 1 + SCRAMBLE_LENGTH + 1 + 64];
- server_capabilites= CLIENT_BASIC_FLAGS;
-
- if (opt_using_transactions)
- server_capabilites|= CLIENT_TRANSACTIONS;
-#ifdef HAVE_COMPRESS
- server_capabilites|= CLIENT_COMPRESS;
-#endif /* HAVE_COMPRESS */
-#if defined(HAVE_OPENSSL)
- if (ssl_acceptor_fd)
- {
- server_capabilites |= CLIENT_SSL; /* Wow, SSL is available! */
- server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT;
- }
-#endif /* HAVE_OPENSSL */
-
- end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1;
- int4store((uchar*) end, thd->thread_id);
- end+= 4;
- /*
- So as check_connection is the only entry point to authorization
- procedure, scramble is set here. This gives us new scramble for
- each handshake.
- */
- create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand);
- /*
- Old clients does not understand long scrambles, but can ignore packet
- tail: that's why first part of the scramble is placed here, and second
- part at the end of packet.
- */
- end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1;
-
- int2store(end, server_capabilites);
- /* write server characteristics: up to 16 bytes allowed */
- end[2]=(char) default_charset_info->number;
- int2store(end+3, thd->server_status);
- bzero(end+5, 13);
- end+= 18;
- /* write scramble tail */
- end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323,
- SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1;
-
- /* At this point we write connection message and read reply */
- if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0,
- (uchar*) buff, (size_t) (end-buff)) ||
- (pkt_len= my_net_read(net)) == packet_error ||
- pkt_len < MIN_HANDSHAKE_SIZE)
- {
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0),
- thd->main_security_ctx.host_or_ip);
- return 1;
- }
- }
-#ifdef _CUSTOMCONFIG_
-#include "_cust_sql_parse.h"
-#endif
- if (connect_errors)
- reset_host_errors(thd->main_security_ctx.ip);
if (thd->packet.alloc(thd->variables.net_buffer_length))
return 1; /* The error is set by alloc(). */
- thd->client_capabilities= uint2korr(net->read_pos);
- if (thd->client_capabilities & CLIENT_PROTOCOL_41)
- {
- thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16;
- thd->max_client_packet_length= uint4korr(net->read_pos+4);
- DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8]));
- thd_init_client_charset(thd, (uint) net->read_pos[8]);
- thd->update_charset();
- end= (char*) net->read_pos+32;
- }
- else
- {
- thd->max_client_packet_length= uint3korr(net->read_pos+2);
- end= (char*) net->read_pos+5;
- }
- /*
- Disable those bits which are not supported by the server.
- This is a precautionary measure, if the client lies. See Bug#27944.
- */
- thd->client_capabilities&= server_capabilites;
-
- if (thd->client_capabilities & CLIENT_IGNORE_SPACE)
- thd->variables.sql_mode|= MODE_IGNORE_SPACE;
-#if defined(HAVE_OPENSSL)
- DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities));
- if (thd->client_capabilities & CLIENT_SSL)
- {
- /* Do the SSL layering. */
- if (!ssl_acceptor_fd)
- {
- inc_host_errors(thd->main_security_ctx.ip);
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
- DBUG_PRINT("info", ("IO layer change in progress..."));
- if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout))
- {
- DBUG_PRINT("error", ("Failed to accept new SSL connection"));
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
- DBUG_PRINT("info", ("Reading user information over SSL layer"));
- if ((pkt_len= my_net_read(net)) == packet_error ||
- pkt_len < NORMAL_HANDSHAKE_SIZE)
- {
- DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)",
- pkt_len));
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
- }
-#endif /* HAVE_OPENSSL */
-
- if (end >= (char*) net->read_pos+ pkt_len +2)
- {
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
-
- if (thd->client_capabilities & CLIENT_INTERACTIVE)
- thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout;
- if ((thd->client_capabilities & CLIENT_TRANSACTIONS) &&
- opt_using_transactions)
- net->return_status= &thd->server_status;
-
- char *user= end;
- char *passwd= strend(user)+1;
- uint user_len= passwd - user - 1;
- char *db= passwd;
- char db_buff[NAME_LEN + 1]; // buffer to store db in utf8
- char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8
- uint dummy_errors;
-
- /*
- Old clients send null-terminated string as password; new clients send
- the size (1 byte) + string (not null-terminated). Hence in case of empty
- password both send '\0'.
-
- This strlen() can't be easily deleted without changing protocol.
-
- Cast *passwd to an unsigned char, so that it doesn't extend the sign for
- *passwd > 127 and become 2**32-127+ after casting to uint.
- */
- uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : strlen(passwd);
- db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ?
- db + passwd_len + 1 : 0;
- /* strlen() can't be easily deleted without changing protocol */
- uint db_len= db ? strlen(db) : 0;
-
- if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len)
- {
- inc_host_errors(thd->main_security_ctx.ip);
-
- my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip);
- return 1;
- }
-
- /* Since 4.1 all database names are stored in utf8 */
- if (db)
- {
- db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info,
- db, db_len,
- thd->charset(), &dummy_errors)]= 0;
- db= db_buff;
- }
-
- user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1,
- system_charset_info, user, user_len,
- thd->charset(), &dummy_errors)]= '\0';
- user= user_buff;
-
- /* If username starts and ends in "'", chop them off */
- if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'')
- {
- user[user_len-1]= 0;
- user++;
- user_len-= 2;
- }
-
- my_free(thd->main_security_ctx.user);
- if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME))))
- return 1; /* The error is set by my_strdup(). */
- return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE);
+ return acl_authenticate(thd, connect_errors, 0);
}
diff --git a/sql/sql_connect.h b/sql/sql_connect.h
index 2334b7303be..06cb4287f3a 100644
--- a/sql/sql_connect.h
+++ b/sql/sql_connect.h
@@ -40,4 +40,8 @@ int check_user(THD *thd, enum enum_server_command command,
const char *passwd, uint passwd_len, const char *db,
bool check_count);
+int get_or_create_user_conn(THD *thd, const char *user,
+ const char *host, const USER_RESOURCES *mqh);
+int check_for_max_user_connections(THD *thd, USER_CONN *uc);
+
#endif /* SQL_CONNECT_INCLUDED */
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index a0d347f48de..85c7b807ea6 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1811,8 +1811,10 @@ public:
table(0),tables_in_use(0),stacked_inserts(0), status(0), group_count(0)
{
DBUG_ENTER("Delayed_insert constructor");
- thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user;
+ thd.security_ctx->user=(char*) delayed_user;
thd.security_ctx->host=(char*) my_localhost;
+ strmake(thd.security_ctx->priv_user, thd.security_ctx->user,
+ USERNAME_LENGTH);
thd.current_tablenr=0;
thd.command=COM_DELAYED_INSERT;
thd.lex->current_select= 0; // for my_message_sql
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 6411b194a8a..9d9348acc02 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -41,6 +41,7 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
LEX_STRING constant for null-string to be used in parser and other places.
*/
const LEX_STRING null_lex_str= {NULL, 0};
+const LEX_STRING empty_lex_str= {"", 0};
/**
@note The order of the elements of this array must correspond to
the order of elements in enum_binlog_stmt_unsafe.
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index f1b558b8be4..a5a6c34f30a 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -982,6 +982,8 @@ struct st_sp_chistics
enum enum_sp_data_access daccess;
};
+extern const LEX_STRING null_lex_str;
+extern const LEX_STRING empty_lex_str;
struct st_trg_chistics
{
@@ -2037,6 +2039,7 @@ struct LEX: public Query_tables_list
sp_name *spname;
bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
bool all_privileges;
+ bool proxy_priv;
sp_pcontext *spcont;
st_sp_chistics sp_chistics;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 2ef8e9761b1..8a59189e3eb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -501,9 +501,8 @@ static void handle_bootstrap_impl(THD *thd)
#endif /* EMBEDDED_LIBRARY */
thd_proc_info(thd, 0);
- thd->security_ctx->priv_user=
- thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
- thd->security_ctx->priv_host[0]=0;
+ thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME));
+ thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0;
/*
Make the "client" handle multiple results. This is necessary
to enable stored procedures with SELECTs and Dynamic SQL
@@ -976,96 +975,34 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
case COM_CHANGE_USER:
{
status_var_increment(thd->status_var.com_other);
- char *user= (char*) packet, *packet_end= packet + packet_length;
- /* Safe because there is always a trailing \0 at the end of the packet */
- char *passwd= strend(user)+1;
thd->change_user();
thd->clear_error(); // if errors from rollback
- /*
- Old clients send null-terminated string ('\0' for empty string) for
- password. New clients send the size (1 byte) + string (not null
- terminated, so also '\0' for empty string).
+ /* acl_authenticate() takes the data from net->read_pos */
+ net->read_pos= (uchar*)packet;
- Cast *passwd to an unsigned char, so that it doesn't extend the sign
- for *passwd > 127 and become 2**32-127 after casting to uint.
- */
- char db_buff[NAME_LEN+1]; // buffer to store db in utf8
- char *db= passwd;
- char *save_db;
- /*
- If there is no password supplied, the packet must contain '\0',
- in any type of handshake (4.1 or pre-4.1).
- */
- if (passwd >= packet_end)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
- uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ?
- (uchar)(*passwd++) : strlen(passwd));
- uint dummy_errors, save_db_length, db_length;
- int res;
+ uint save_db_length= thd->db_length;
+ char *save_db= thd->db;
+ USER_CONN *save_user_connect= thd->user_connect;
Security_context save_security_ctx= *thd->security_ctx;
- USER_CONN *save_user_connect;
-
- db+= passwd_len + 1;
- /*
- Database name is always NUL-terminated, so in case of empty database
- the packet must contain at least the trailing '\0'.
- */
- if (db >= packet_end)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
- db_length= strlen(db);
-
- char *ptr= db + db_length + 1;
- uint cs_number= 0;
-
- if (ptr < packet_end)
- {
- if (ptr + 2 > packet_end)
- {
- my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0));
- break;
- }
-
- cs_number= uint2korr(ptr);
- }
-
- /* Convert database name to utf8 */
- db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1,
- system_charset_info, db, db_length,
- thd->charset(), &dummy_errors)]= 0;
- db= db_buff;
+ CHARSET_INFO *save_character_set_client=
+ thd->variables.character_set_client;
+ CHARSET_INFO *save_collation_connection=
+ thd->variables.collation_connection;
+ CHARSET_INFO *save_character_set_results=
+ thd->variables.character_set_results;
- /* Save user and privileges */
- save_db_length= thd->db_length;
- save_db= thd->db;
- save_user_connect= thd->user_connect;
-
- if (!(thd->security_ctx->user= my_strdup(user, MYF(0))))
- {
- thd->security_ctx->user= save_security_ctx.user;
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
-
- /* Clear variables that are allocated */
- thd->user_connect= 0;
- thd->security_ctx->priv_user= thd->security_ctx->user;
- res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE);
-
- if (res)
+ if (acl_authenticate(thd, 0, packet_length))
{
my_free(thd->security_ctx->user);
*thd->security_ctx= save_security_ctx;
thd->user_connect= save_user_connect;
- thd->db= save_db;
- thd->db_length= save_db_length;
+ thd->reset_db (save_db, save_db_length);
+ thd->variables.character_set_client= save_character_set_client;
+ thd->variables.collation_connection= save_collation_connection;
+ thd->variables.character_set_results= save_character_set_results;
+ thd->update_charset();
}
else
{
@@ -1076,12 +1013,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
my_free(save_db);
my_free(save_security_ctx.user);
-
- if (cs_number)
- {
- thd_init_client_charset(thd, cs_number);
- thd->update_charset();
- }
}
break;
}
@@ -3844,7 +3775,8 @@ end_with_restore_list:
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
- if (check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
+ if (lex->type != TYPE_ENUM_PROXY &&
+ check_access(thd, lex->grant | lex->grant_tot_col | GRANT_ACL,
first_table ? first_table->db : select_lex->db,
first_table ? &first_table->grant.privilege : NULL,
first_table ? &first_table->grant.m_internal : NULL,
@@ -3854,6 +3786,7 @@ end_with_restore_list:
if (thd->security_ctx->user) // If not replication
{
LEX_USER *user, *tmp_user;
+ bool first_user= TRUE;
List_iterator <LEX_USER> user_list(lex->users_list);
while ((tmp_user= user_list++))
@@ -3868,20 +3801,23 @@ end_with_restore_list:
user->host.str);
// Are we trying to change a password of another user
DBUG_ASSERT(user->host.str != 0);
- if (strcmp(thd->security_ctx->user, user->user.str) ||
- my_strcasecmp(system_charset_info,
- user->host.str, thd->security_ctx->host_or_ip))
+
+ /*
+ GRANT/REVOKE PROXY has the target user as a first entry in the list.
+ */
+ if (lex->type == TYPE_ENUM_PROXY && first_user)
{
- // TODO: use check_change_password()
- if (is_acl_user(user->host.str, user->user.str) &&
- user->password.str &&
- check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1))
- {
- my_message(ER_PASSWORD_NOT_ALLOWED,
- ER(ER_PASSWORD_NOT_ALLOWED), MYF(0));
+ first_user= FALSE;
+ if (acl_check_proxy_grant_access (thd, user->host.str, user->user.str,
+ lex->grant & GRANT_ACL))
goto error;
- }
- }
+ }
+ else if (is_acl_user(user->host.str, user->user.str) &&
+ user->password.str &&
+ check_change_password (thd, user->host.str, user->user.str,
+ user->password.str,
+ user->password.length))
+ goto error;
}
}
if (first_table)
@@ -3916,16 +3852,19 @@ end_with_restore_list:
}
else
{
- if (lex->columns.elements || lex->type)
+ if (lex->columns.elements || (lex->type && lex->type != TYPE_ENUM_PROXY))
{
my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE),
MYF(0));
goto error;
}
else
- /* Conditionally writes to binlog */
- res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
- lex->sql_command == SQLCOM_REVOKE);
+ {
+ /* Conditionally writes to binlog */
+ res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant,
+ lex->sql_command == SQLCOM_REVOKE,
+ lex->type == TYPE_ENUM_PROXY);
+ }
if (!res)
{
if (lex->sql_command == SQLCOM_GRANT)
@@ -4221,8 +4160,8 @@ end_with_restore_list:
if (sp_grant_privileges(thd, lex->sphead->m_db.str, name,
lex->sql_command == SQLCOM_CREATE_PROCEDURE))
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_PROC_AUTO_GRANT_FAIL,
- ER(ER_PROC_AUTO_GRANT_FAIL));
+ ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL));
+ thd->clear_error();
}
/*
@@ -5108,12 +5047,19 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv,
{ // We can never grant this
DBUG_PRINT("error",("No possible access"));
if (!no_errors)
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
- sctx->priv_user,
- sctx->priv_host,
- (thd->password ?
- ER(ER_YES) :
- ER(ER_NO))); /* purecov: tested */
+ {
+ if (thd->password == 2)
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ sctx->priv_user,
+ sctx->priv_host);
+ else
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ sctx->priv_user,
+ sctx->priv_host,
+ (thd->password ?
+ ER(ER_YES) :
+ ER(ER_NO))); /* purecov: tested */
+ }
DBUG_RETURN(TRUE); /* purecov: tested */
}
@@ -7604,8 +7550,9 @@ void get_default_definer(THD *thd, LEX_USER *definer)
definer->host.str= (char *) sctx->priv_host;
definer->host.length= strlen(definer->host.str);
- definer->password.str= NULL;
- definer->password.length= 0;
+ definer->password= null_lex_str;
+ definer->plugin= empty_lex_str;
+ definer->auth= empty_lex_str;
}
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 7e8c1fed999..f749fbec4fb 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -30,6 +30,7 @@
#include <my_pthread.h>
#include <my_getopt.h>
#include <mysql/plugin_audit.h>
+#include <mysql/plugin_auth.h>
#include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
#define REPORT_TO_LOG 1
#define REPORT_TO_USER 2
@@ -65,6 +66,7 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{ C_STRING_WITH_LEN("INFORMATION SCHEMA") },
{ C_STRING_WITH_LEN("AUDIT") },
{ C_STRING_WITH_LEN("REPLICATION") },
+ { C_STRING_WITH_LEN("AUTHENTICATION") }
};
extern int initialize_schema_table(st_plugin_int *plugin);
@@ -81,13 +83,13 @@ extern int finalize_audit_plugin(st_plugin_int *plugin);
plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
0,ha_initialize_handlerton,0,0,initialize_schema_table,
- initialize_audit_plugin
+ initialize_audit_plugin,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
+ finalize_audit_plugin,0,0
};
#ifdef HAVE_DLOPEN
@@ -110,7 +112,8 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
MYSQL_AUDIT_INTERFACE_VERSION,
- MYSQL_REPLICATION_INTERFACE_VERSION
+ MYSQL_REPLICATION_INTERFACE_VERSION,
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION
};
static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
{
@@ -120,7 +123,8 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]=
MYSQL_DAEMON_INTERFACE_VERSION,
MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION,
MYSQL_AUDIT_INTERFACE_VERSION,
- MYSQL_REPLICATION_INTERFACE_VERSION
+ MYSQL_REPLICATION_INTERFACE_VERSION,
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION
};
/* support for Services */
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ca951897055..9c712484faa 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -1185,6 +1185,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token PROCESSLIST_SYM
%token PROFILE_SYM
%token PROFILES_SYM
+%token PROXY_SYM
%token PURGE
%token QUARTER_SYM
%token QUERY_SYM
@@ -12217,6 +12218,9 @@ user:
$$->user = $1;
$$->host.str= (char *) "%";
$$->host.length= 1;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
@@ -12229,6 +12233,9 @@ user:
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
MYSQL_YYABORT;
$$->user = $1; $$->host=$3;
+ $$->password= null_lex_str;
+ $$->plugin= empty_lex_str;
+ $$->auth= empty_lex_str;
if (check_string_char_length(&$$->user, ER(ER_USERNAME),
USERNAME_CHAR_LENGTH,
@@ -12491,6 +12498,7 @@ keyword_sp:
| PROCESSLIST_SYM {}
| PROFILE_SYM {}
| PROFILES_SYM {}
+ | PROXY_SYM {}
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
@@ -12877,7 +12885,7 @@ option_value:
if (!(user=(LEX_USER*) thd->alloc(sizeof(LEX_USER))))
MYSQL_YYABORT;
user->host=null_lex_str;
- user->user.str=thd->security_ctx->priv_user;
+ user->user.str=thd->security_ctx->user;
set_var_password *var= new set_var_password(user, $3);
if (var == NULL)
MYSQL_YYABORT;
@@ -13230,6 +13238,13 @@ revoke_command:
{
Lex->sql_command = SQLCOM_REVOKE_ALL;
}
+ | PROXY_SYM ON user FROM grant_list
+ {
+ LEX *lex= Lex;
+ lex->users_list.push_front ($3);
+ lex->sql_command= SQLCOM_REVOKE;
+ lex->type= TYPE_ENUM_PROXY;
+ }
;
grant:
@@ -13269,6 +13284,13 @@ grant_command:
lex->sql_command= SQLCOM_GRANT;
lex->type= TYPE_ENUM_PROCEDURE;
}
+ | PROXY_SYM ON user TO_SYM grant_list opt_grant_option
+ {
+ LEX *lex= Lex;
+ lex->users_list.push_front ($3);
+ lex->sql_command= SQLCOM_GRANT;
+ lex->type= TYPE_ENUM_PROXY;
+ }
;
opt_table:
@@ -13462,6 +13484,8 @@ grant_user:
user IDENTIFIED_SYM BY TEXT_STRING
{
$$=$1; $1->password=$4;
+ if (Lex->sql_command == SQLCOM_REVOKE)
+ MYSQL_YYABORT;
if ($4.length)
{
if (YYTHD->variables.old_passwords)
@@ -13487,7 +13511,28 @@ grant_user:
}
}
| user IDENTIFIED_SYM BY PASSWORD TEXT_STRING
- { $$= $1; $1->password= $5; }
+ {
+ if (Lex->sql_command == SQLCOM_REVOKE)
+ MYSQL_YYABORT;
+ $$= $1;
+ $1->password= $5;
+ }
+ | user IDENTIFIED_SYM WITH ident_or_text
+ {
+ if (Lex->sql_command == SQLCOM_REVOKE)
+ MYSQL_YYABORT;
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= empty_lex_str;
+ }
+ | user IDENTIFIED_SYM WITH ident_or_text AS TEXT_STRING_sys
+ {
+ if (Lex->sql_command == SQLCOM_REVOKE)
+ MYSQL_YYABORT;
+ $$= $1;
+ $1->plugin= $4;
+ $1->auth= $6;
+ }
| user
{ $$= $1; $1->password= null_lex_str; }
;
@@ -13559,6 +13604,11 @@ grant_options:
| WITH grant_option_list
;
+opt_grant_option:
+ /* empty */ {}
+ | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;}
+ ;
+
grant_option_list:
grant_option_list grant_option {}
| grant_option {}
diff --git a/sql/structs.h b/sql/structs.h
index 5ffcc4dc62e..8290227830c 100644
--- a/sql/structs.h
+++ b/sql/structs.h
@@ -156,7 +156,7 @@ extern const char *show_comp_option_name[];
typedef int *(*update_var)(THD *, struct st_mysql_show_var *);
typedef struct st_lex_user {
- LEX_STRING user, host, password;
+ LEX_STRING user, host, password, plugin, auth;
} LEX_USER;
/*
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index 320e6d9253e..6c456825aa1 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -1419,6 +1419,14 @@ static Sys_var_uint Sys_protocol_version(
READ_ONLY GLOBAL_VAR(protocol_version), NO_CMD_LINE,
VALID_RANGE(0, ~0), DEFAULT(PROTOCOL_VERSION), BLOCK_SIZE(1));
+static Sys_var_proxy_user Sys_proxy_user(
+ "proxy_user", "The proxy user account name used when logging in",
+ IN_SYSTEM_CHARSET);
+
+static Sys_var_external_user Sys_exterenal_user(
+ "external_user", "The external user account used when logging in",
+ IN_SYSTEM_CHARSET);
+
static Sys_var_ulong Sys_read_buff_size(
"read_buffer_size",
"Each thread that does a sequential scan allocates a buffer of "
diff --git a/sql/sys_vars.h b/sql/sys_vars.h
index ca3fc99deba..9dc6890a84e 100644
--- a/sql/sys_vars.h
+++ b/sql/sys_vars.h
@@ -452,6 +452,67 @@ public:
{ return type != STRING_RESULT; }
};
+
+class Sys_var_proxy_user: public sys_var
+{
+public:
+ Sys_var_proxy_user(const char *name_arg,
+ const char *comment, enum charset_enum is_os_charset_arg)
+ : sys_var(&all_sys_vars, name_arg, comment,
+ sys_var::READONLY+sys_var::ONLY_SESSION, 0, -1,
+ NO_ARG, SHOW_CHAR, (intptr)NULL,
+ 0, VARIABLE_NOT_IN_BINLOG,
+ 0, 0,
+ 0, 0, PARSE_NORMAL)
+ {
+ is_os_charset= is_os_charset_arg == IN_FS_CHARSET;
+ option.var_type= GET_STR;
+ }
+ bool do_check(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return false;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ void global_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+ bool check_update_type(Item_result type)
+ { return true; }
+protected:
+ virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return thd->security_ctx->proxy_user[0] ?
+ (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
+ }
+};
+
+class Sys_var_external_user : public Sys_var_proxy_user
+{
+public:
+ Sys_var_external_user(const char *name_arg, const char *comment_arg,
+ enum charset_enum is_os_charset_arg)
+ : Sys_var_proxy_user (name_arg, comment_arg, is_os_charset_arg)
+ {}
+
+protected:
+ virtual uchar *session_value_ptr(THD *thd, LEX_STRING *base)
+ {
+ return thd->security_ctx->proxy_user[0] ?
+ (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL;
+ }
+};
+
/**
The class for string variables. Useful for strings that aren't necessarily
\0-terminated. Otherwise the same as Sys_var_charptr.
diff --git a/sql/table.cc b/sql/table.cc
index bcdfd23b4c1..67f49195b73 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -3786,11 +3786,8 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
{
DBUG_PRINT("info", ("This table is suid view => load contest"));
DBUG_ASSERT(view && view_sctx);
- if (acl_getroot_no_password(view_sctx,
- definer.user.str,
- definer.host.str,
- definer.host.str,
- thd->db))
+ if (acl_getroot(view_sctx, definer.user.str, definer.host.str,
+ definer.host.str, thd->db))
{
if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) ||
(thd->lex->sql_command == SQLCOM_SHOW_FIELDS))
@@ -3809,10 +3806,15 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd)
}
else
{
- my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
- thd->security_ctx->priv_user,
- thd->security_ctx->priv_host,
- (thd->password ? ER(ER_YES) : ER(ER_NO)));
+ if (thd->password == 2)
+ my_error(ER_ACCESS_DENIED_NO_PASSWORD_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host);
+ else
+ my_error(ER_ACCESS_DENIED_ERROR, MYF(0),
+ thd->security_ctx->priv_user,
+ thd->security_ctx->priv_host,
+ (thd->password ? ER(ER_YES) : ER(ER_NO)));
}
DBUG_RETURN(TRUE);
}
diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c
index 61f9d4881cb..edb98f8c1a7 100644
--- a/tests/mysql_client_test.c
+++ b/tests/mysql_client_test.c
@@ -33,6 +33,7 @@
#include <my_getopt.h>
#include <m_string.h>
#include <mysqld_error.h>
+#include <sql_common.h>
#define VER "2.1"
#define MAX_TEST_QUERY_LENGTH 300 /* MAX QUERY BUFFER LENGTH */