diff options
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r-- | sql/sql_acl.cc | 2162 |
1 files changed, 1466 insertions, 696 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 886ff90f35b..4c2649cea56 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -365,6 +365,10 @@ static bool show_table_and_column_privileges(THD *, const char *, const char *, static int show_routine_grants(THD *, const char *, const char *, HASH *, const char *, int, char *, int); +class Grant_tables; +class User_table; +class Proxies_priv_table; + class ACL_PROXY_USER :public ACL_ACCESS { acl_host_and_ip host; @@ -412,14 +416,7 @@ public: with_grant_arg); } - void init(TABLE *table, MEM_ROOT *mem) - { - init (get_field(mem, table->field[MYSQL_PROXIES_PRIV_HOST]), - get_field(mem, table->field[MYSQL_PROXIES_PRIV_USER]), - get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_HOST]), - get_field(mem, table->field[MYSQL_PROXIES_PRIV_PROXIED_USER]), - table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->val_int() != 0); - } + void init(const Proxies_priv_table& proxies_priv_table, MEM_ROOT *mem); bool get_with_grant() { return with_grant; } const char *get_user() { return user; } @@ -731,7 +728,6 @@ static DYNAMIC_ARRAY acl_wild_hosts; static Hash_filo<acl_entry> *acl_cache; static uint grant_version=0; /* Version of priv tables. incremented by acl_load */ static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0); -static bool check_is_role(TABLE *form); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); @@ -742,12 +738,12 @@ static ACL_USER *find_user_wild(const char *host, const char *user, const char * static ACL_ROLE *find_acl_role(const char *user); static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, const LEX_STRING *h, const LEX_STRING *r); static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host); -static bool update_user_table(THD *, TABLE *, const char *, const char *, const +static bool update_user_table(THD *, const User_table &, const char *, const char *, const char *, uint); -static bool acl_load(THD *thd, TABLE_LIST *tables); -static bool grant_load(THD *thd, TABLE_LIST *tables); +static bool acl_load(THD *thd, const Grant_tables& grant_tables); static inline void get_grantor(THD *thd, char* grantor); static bool add_role_user_mapping(const char *uname, const char *hname, const char *rname); +static bool get_YN_as_bool(Field *field); #define ROLE_CYCLE_FOUND 2 static int traverse_role_graph_up(ACL_ROLE *, void *, @@ -784,31 +780,641 @@ static const int Table_procs_priv= 1 << PROCS_PRIV_TABLE; static const int Table_proxies_priv= 1 << PROXIES_PRIV_TABLE; static const int Table_roles_mapping= 1 << ROLES_MAPPING_TABLE; -static int open_grant_tables(THD *thd, TABLE_LIST *tables, - enum thr_lock_type lock_type, int tables_to_open); +/** + Base class representing a generic grant table from the mysql database. + + The potential tables that this class can represent are: + user, db, columns_priv, tables_priv, host, procs_priv, proxies_priv, + roles_mapping + + Objects belonging to this parent class can only be constructed by the + Grants_table class. This ensures the correct initialization of the objects. +*/ +class Grant_table_base +{ + public: + /* Number of fields for this Grant Table. */ + uint num_fields() const { return tl.table->s->fields; } + /* Check if the table exists after an attempt to open it was made. + Some tables, such as the host table in MySQL 5.6.7+ are missing. */ + bool table_exists() const { return tl.table; }; + /* Initializes the READ_RECORD structure provided as a parameter + to read through the whole table, with all columns available. Cleaning up + is the caller's job. */ + bool init_read_record(READ_RECORD* info, THD* thd) const + { + DBUG_ASSERT(tl.table); + bool result= ::init_read_record(info, thd, tl.table, NULL, NULL, 1, + true, false); + if (!result) + tl.table->use_all_columns(); + return result; + } + + /* Return the number of privilege columns for this table. */ + uint num_privileges() const { return num_privilege_cols; } + /* Return a privilege column by index. */ + Field* priv_field(uint privilege_idx) const + { + DBUG_ASSERT(privilege_idx < num_privileges()); + return tl.table->field[start_privilege_column + privilege_idx]; + } + + /* Fetch the privileges from the table as a set of bits. The first column + is represented by the first bit in the result, the second column by the + second bit, etc. */ + ulong get_access() const + { + return get_access(start_privilege_column, + start_privilege_column + num_privileges() - 1); + } + + /* Return the underlying TABLE handle. */ + TABLE* table() const + { + return tl.table; + } + + /** Check if the table was opened, issue an error otherwise. */ + int no_such_table() const + { + if (table_exists()) + return 0; + + my_error(ER_NO_SUCH_TABLE, MYF(0), tl.db, tl.alias); + return 1; + } + + + protected: + friend class Grant_tables; + + Grant_table_base() : start_privilege_column(0), num_privilege_cols(0) + { + bzero(&tl, sizeof(tl)); + }; + + /* Initialization sequence common for all grant tables. This should be called + after all table-specific initialization is performed. */ + void init(enum thr_lock_type lock_type, bool is_optional) + { + tl.open_type= OT_BASE_ONLY; + if (lock_type >= TL_WRITE_ALLOW_WRITE) + tl.updating= 1; + if (is_optional) + tl.open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + } + + /* + Get all access bits from table between start_field and end_field indices. + + IMPLEMENTATION + The record should be already read in table->record[0]. All privileges + are specified as an ENUM(Y,N). + + SYNOPSIS + get_access() + start_field_idx The field index at which the first privilege + specification begins. + end_field_idx The field index at which the last privilege + specification is located. + + RETURN VALUE + privilege mask + */ + ulong get_access(uint start_field_idx, uint end_field_idx) const + { + ulong access_bits= 0, bit= 1; + for (uint i = start_field_idx; i <= end_field_idx; i++, bit<<=1) + { + if (get_YN_as_bool(tl.table->field[i])) + access_bits|= bit; + } + return access_bits; + } + + /* Compute how many privilege columns this table has. This method + can only be called after the table has been opened. + + IMPLEMENTATION + A privilege column is of type enum('Y', 'N'). Privilege columns are + expected to be one after another. + */ + void compute_num_privilege_cols() + { + if (!table_exists()) // Table does not exist or not opened. + return; + + num_privilege_cols= 0; + for (uint i= 0; i < num_fields(); i++) + { + Field *field= tl.table->field[i]; + if (num_privilege_cols > 0 && field->real_type() != MYSQL_TYPE_ENUM) + return; + if (field->real_type() == MYSQL_TYPE_ENUM && + static_cast<Field_enum*>(field)->typelib->count == 2) + { + num_privilege_cols++; + if (num_privilege_cols == 1) + start_privilege_column= i; + } + } + } + + /* The index at which privilege columns start. */ + uint start_privilege_column; + /* The number of privilege columns in the table. */ + uint num_privilege_cols; + + TABLE_LIST tl; +}; + +class User_table: public Grant_table_base +{ + public: + /* Field getters return NULL if the column is not present in the table. + This is consistent only if the table is in a supported version. We do + not guard against corrupt tables. (yet) */ + Field* host() const + { return get_field(0); } + Field* user() const + { return get_field(1); } + Field* password() const + { return have_password() ? NULL : tl.table->field[2]; } + /* Columns after privilege columns. */ + Field* ssl_type() const + { return get_field(start_privilege_column + num_privileges()); } + Field* ssl_cipher() const + { return get_field(start_privilege_column + num_privileges() + 1); } + Field* x509_issuer() const + { return get_field(start_privilege_column + num_privileges() + 2); } + Field* x509_subject() const + { return get_field(start_privilege_column + num_privileges() + 3); } + Field* max_questions() const + { return get_field(start_privilege_column + num_privileges() + 4); } + Field* max_updates() const + { return get_field(start_privilege_column + num_privileges() + 5); } + Field* max_connections() const + { return get_field(start_privilege_column + num_privileges() + 6); } + Field* max_user_connections() const + { return get_field(start_privilege_column + num_privileges() + 7); } + Field* plugin() const + { return get_field(start_privilege_column + num_privileges() + 8); } + Field* authentication_string() const + { return get_field(start_privilege_column + num_privileges() + 9); } + Field* password_expired() const + { return get_field(start_privilege_column + num_privileges() + 10); } + Field* is_role() const + { return get_field(start_privilege_column + num_privileges() + 11); } + Field* default_role() const + { return get_field(start_privilege_column + num_privileges() + 12); } + Field* max_statement_time() const + { return get_field(start_privilege_column + num_privileges() + 13); } + + /* + Check if a user entry in the user table is marked as being a role entry + + IMPLEMENTATION + Access the coresponding column and check the coresponding ENUM of the form + ENUM('N', 'Y') + + SYNOPSIS + check_is_role() + form an open table to read the entry from. + The record should be already read in table->record[0] + + RETURN VALUE + TRUE if the user is marked as a role + FALSE otherwise + */ + bool check_is_role() const + { + /* Table version does not support roles */ + if (!is_role()) + return false; + + return get_YN_as_bool(is_role()); + } + + + private: + friend class Grant_tables; + + /* Only Grant_tables can instantiate this class. */ + User_table() {}; + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("user"), + NULL, lock_type); + Grant_table_base::init(lock_type, false); + } + + /* The user table is a bit different compared to the other Grant tables. + Usually, we only add columns to the grant tables when adding functionality. + This makes it easy to test which version of the table we are using, by + just looking at the number of fields present in the table. + + In MySQL 5.7.6 the Password column was removed. We need to guard for that. + The field-fetching methods for the User table return NULL if the field + doesn't exist. This simplifies checking of table "version", as we don't + have to make use of num_fields() any more. + */ + inline Field* get_field(uint field_num) const + { + if (field_num >= num_fields()) + return NULL; + + return tl.table->field[field_num]; + } + + /* Normally password column is the third column in the table. If privileges + start on the third column instead, we are missing the password column. + This means we are using a MySQL 5.7.6+ data directory. */ + bool have_password() const { return start_privilege_column == 2; } + +}; + +class Db_table: public Grant_table_base +{ + public: + Field* host() const { return tl.table->field[0]; } + Field* db() const { return tl.table->field[1]; } + Field* user() const { return tl.table->field[2]; } + + private: + friend class Grant_tables; + + Db_table() {}; -const LEX_STRING acl_table_names[]= // matches enum_acl_tables + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("db"), + NULL, lock_type); + Grant_table_base::init(lock_type, false); + } +}; + +class Tables_priv_table: public Grant_table_base { - { C_STRING_WITH_LEN("user") }, - { C_STRING_WITH_LEN("db") }, - { C_STRING_WITH_LEN("tables_priv") }, - { C_STRING_WITH_LEN("columns_priv") }, - { C_STRING_WITH_LEN("host") }, - { C_STRING_WITH_LEN("procs_priv") }, - { C_STRING_WITH_LEN("proxies_priv") }, - { C_STRING_WITH_LEN("roles_mapping") } + public: + Field* host() const { return tl.table->field[0]; } + Field* db() const { return tl.table->field[1]; } + Field* user() const { return tl.table->field[2]; } + Field* table_name() const { return tl.table->field[3]; } + Field* grantor() const { return tl.table->field[4]; } + Field* timestamp() const { return tl.table->field[5]; } + Field* table_priv() const { return tl.table->field[6]; } + Field* column_priv() const { return tl.table->field[7]; } + + private: + friend class Grant_tables; + + Tables_priv_table() {}; + + void init(enum thr_lock_type lock_type, Grant_table_base *next_table= NULL) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("tables_priv"), + NULL, lock_type); + Grant_table_base::init(lock_type, false); + } }; -/** check if the table was opened, issue an error otherwise */ -static int no_such_table(TABLE_LIST *tl) +class Columns_priv_table: public Grant_table_base { - if (tl->table) - return 0; + public: + Field* host() const { return tl.table->field[0]; } + Field* db() const { return tl.table->field[1]; } + Field* user() const { return tl.table->field[2]; } + Field* table_name() const { return tl.table->field[3]; } + Field* column_name() const { return tl.table->field[4]; } + Field* timestamp() const { return tl.table->field[5]; } + Field* column_priv() const { return tl.table->field[6]; } - my_error(ER_NO_SUCH_TABLE, MYF(0), tl->db, tl->alias); - return 1; + private: + friend class Grant_tables; + + Columns_priv_table() {}; + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("columns_priv"), + NULL, lock_type); + Grant_table_base::init(lock_type, false); + } +}; + +class Host_table: public Grant_table_base +{ + public: + Field* host() const { return tl.table->field[0]; } + Field* db() const { return tl.table->field[1]; } + + private: + friend class Grant_tables; + + Host_table() {} + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("host"), + NULL, lock_type); + Grant_table_base::init(lock_type, true); + } +}; + +class Procs_priv_table: public Grant_table_base +{ + public: + Field* host() const { return tl.table->field[0]; } + Field* db() const { return tl.table->field[1]; } + Field* user() const { return tl.table->field[2]; } + Field* routine_name() const { return tl.table->field[3]; } + Field* routine_type() const { return tl.table->field[4]; } + Field* grantor() const { return tl.table->field[5]; } + Field* proc_priv() const { return tl.table->field[6]; } + Field* timestamp() const { return tl.table->field[7]; } + + private: + friend class Grant_tables; + + Procs_priv_table() {} + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("procs_priv"), + NULL, lock_type); + Grant_table_base::init(lock_type, true); + } +}; + +class Proxies_priv_table: public Grant_table_base +{ + public: + Field* host() const { return tl.table->field[0]; } + Field* user() const { return tl.table->field[1]; } + Field* proxied_host() const { return tl.table->field[2]; } + Field* proxied_user() const { return tl.table->field[3]; } + Field* with_grant() const { return tl.table->field[4]; } + Field* grantor() const { return tl.table->field[5]; } + Field* timestamp() const { return tl.table->field[6]; } + + private: + friend class Grant_tables; + + Proxies_priv_table() {} + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("proxies_priv"), + NULL, lock_type); + Grant_table_base::init(lock_type, true); + } +}; + +class Roles_mapping_table: public Grant_table_base +{ + public: + Field* host() const { return tl.table->field[0]; } + Field* user() const { return tl.table->field[1]; } + Field* role() const { return tl.table->field[2]; } + Field* admin_option() const { return tl.table->field[3]; } + + private: + friend class Grant_tables; + + Roles_mapping_table() {} + + void init(enum thr_lock_type lock_type) + { + /* We are relying on init_one_table zeroing out the TABLE_LIST structure. */ + tl.init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("roles_mapping"), + NULL, lock_type); + Grant_table_base::init(lock_type, true); + } +}; + +/** + Class that represents a collection of grant tables. +*/ +class Grant_tables +{ + public: + /* When constructing the Grant_tables object, we initialize only + the tables which are going to be opened. + @param which_tables Bitmap of which tables to open. + @param lock_type Lock type to use when opening tables. + */ + Grant_tables(int which_tables, enum thr_lock_type lock_type) + { + DBUG_ENTER("Grant_tables::Grant_tables"); + DBUG_PRINT("info", ("which_tables: %x, lock_type: %u", + which_tables, lock_type)); + DBUG_ASSERT(which_tables); /* At least one table must be opened. */ + Grant_table_base* prev= NULL; + /* We start from the last table, Table_roles_mapping, such that + the first one in the linked list is Table_user. */ + if (which_tables & Table_roles_mapping) + { + m_roles_mapping_table.init(lock_type); + prev= &m_roles_mapping_table; + } + if (which_tables & Table_proxies_priv) + { + m_proxies_priv_table.init(lock_type); + link_tables(&m_proxies_priv_table, prev); + prev= &m_proxies_priv_table; + } + if (which_tables & Table_procs_priv) + { + m_procs_priv_table.init(lock_type); + link_tables(&m_procs_priv_table, prev); + prev= &m_procs_priv_table; + } + if (which_tables & Table_host) + { + m_host_table.init(lock_type); + link_tables(&m_host_table, prev); + prev= &m_host_table; + } + if (which_tables & Table_columns_priv) + { + m_columns_priv_table.init(lock_type); + link_tables(&m_columns_priv_table, prev); + prev= &m_columns_priv_table; + } + if (which_tables & Table_tables_priv) + { + m_tables_priv_table.init(lock_type); + link_tables(&m_tables_priv_table, prev); + prev= &m_tables_priv_table; + } + if (which_tables & Table_db) + { + m_db_table.init(lock_type); + link_tables(&m_db_table, prev); + prev= &m_db_table; + } + if (which_tables & Table_user) + { + m_user_table.init(lock_type); + link_tables(&m_user_table, prev); + prev= &m_user_table; + } + + first_table_in_list= prev; + DBUG_VOID_RETURN; + } + + /* Before any operation is possible on grant tables, they must be opened. + This opens the tables according to the lock type specified during + construction. + + @retval 1 replication filters matched. Abort the operation, + but return OK (!) + @retval 0 tables were opened successfully + @retval -1 error, tables could not be opened + */ + int open_and_lock(THD *thd) + { + DBUG_ENTER("Grant_tables::open_and_lock"); + DBUG_ASSERT(first_table_in_list); +#ifdef HAVE_REPLICATION + if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE && + thd->slave_thread && !thd->spcont) + { + /* + GRANT and REVOKE are applied the slave in/exclusion rules as they are + some kind of updates to the mysql.% tables. + */ + Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (rpl_filter->is_on() && + !rpl_filter->tables_ok(0, &first_table_in_list->tl)) + DBUG_RETURN(1); + } +#endif + if (open_and_lock_tables(thd, &first_table_in_list->tl, FALSE, + MYSQL_LOCK_IGNORE_TIMEOUT)) + DBUG_RETURN(-1); + + /* + We can read privilege tables even when !initialized. + This can be acl_load() - server startup or FLUSH PRIVILEGES + */ + if (first_table_in_list->tl.lock_type >= TL_WRITE_ALLOW_WRITE && + !initialized) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(-1); + } + + /* The privilge columns vary based on MariaDB version. Figure out + how many we have after we've opened the table. */ + m_user_table.compute_num_privilege_cols(); + m_db_table.compute_num_privilege_cols(); + m_tables_priv_table.compute_num_privilege_cols(); + m_columns_priv_table.compute_num_privilege_cols(); + m_host_table.compute_num_privilege_cols(); + m_procs_priv_table.compute_num_privilege_cols(); + m_proxies_priv_table.compute_num_privilege_cols(); + m_roles_mapping_table.compute_num_privilege_cols(); + DBUG_RETURN(0); + } + + inline const User_table& user_table() const + { + return m_user_table; + } + + inline const Db_table& db_table() const + { + return m_db_table; + } + + + inline const Tables_priv_table& tables_priv_table() const + { + return m_tables_priv_table; + } + + inline const Columns_priv_table& columns_priv_table() const + { + return m_columns_priv_table; + } + + inline const Host_table& host_table() const + { + return m_host_table; + } + + inline const Procs_priv_table& procs_priv_table() const + { + return m_procs_priv_table; + } + + inline const Proxies_priv_table& proxies_priv_table() const + { + return m_proxies_priv_table; + } + + inline const Roles_mapping_table& roles_mapping_table() const + { + return m_roles_mapping_table; + } + + private: + User_table m_user_table; + Db_table m_db_table; + Tables_priv_table m_tables_priv_table; + Columns_priv_table m_columns_priv_table; + Host_table m_host_table; + Procs_priv_table m_procs_priv_table; + Proxies_priv_table m_proxies_priv_table; + Roles_mapping_table m_roles_mapping_table; + + /* The grant tables are set-up in a linked list. We keep the head of it. */ + Grant_table_base *first_table_in_list; + /** + Chain two grant tables' TABLE_LIST members. + */ + static void link_tables(Grant_table_base *from, Grant_table_base *to) + { + DBUG_ASSERT(from); + if (to) + from->tl.next_local= from->tl.next_global= &to->tl; + else + from->tl.next_local= from->tl.next_global= NULL; + } +}; + + +void ACL_PROXY_USER::init(const Proxies_priv_table& proxies_priv_table, + MEM_ROOT *mem) +{ + init(get_field(mem, proxies_priv_table.host()), + get_field(mem, proxies_priv_table.user()), + get_field(mem, proxies_priv_table.proxied_host()), + get_field(mem, proxies_priv_table.proxied_user()), + proxies_priv_table.with_grant()->val_int() != 0); } + + /* Enumeration of various ACL's and Hashes used in handle_grant_struct() */ @@ -1045,7 +1651,7 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) if (user->pwhash.length && user->pwhash.length != check_length) { - my_error(ER_PASSWD_LENGTH, MYF(0), check_length); + my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length); return true; } @@ -1140,7 +1746,7 @@ bool acl_init(bool dont_read_acl_tables) /* To be able to run this from boot, we allocate a temporary THD */ - if (!(thd=new THD)) + if (!(thd=new THD(0))) DBUG_RETURN(1); /* purecov: inspected */ thd->thread_stack= (char*) &thd; thd->store_globals(); @@ -1194,32 +1800,30 @@ static bool set_user_plugin (ACL_USER *user, int password_len) TRUE Error */ -static bool acl_load(THD *thd, TABLE_LIST *tables) +static bool acl_load(THD *thd, const Grant_tables& tables) { - TABLE *table; READ_RECORD read_record_info; - bool return_val= TRUE; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; char tmp_name[SAFE_NAME_LEN+1]; int password_length; - ulonglong old_sql_mode= thd->variables.sql_mode; + Sql_mode_save old_mode_save(thd); DBUG_ENTER("acl_load"); thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; grant_version++; /* Privileges updated */ + const Host_table& host_table= tables.host_table(); init_sql_alloc(&acl_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); - if ((table= tables[HOST_TABLE].table)) // "host" table may not exist (e.g. in MySQL 5.6.7+) + if (host_table.table_exists()) // "host" table may not exist (e.g. in MySQL 5.6.7+) { - if (init_read_record(&read_record_info, thd, table, NULL, 1, 1, FALSE)) - goto end; - table->use_all_columns(); + if (host_table.init_read_record(&read_record_info, thd)) + DBUG_RETURN(true); while (!(read_record_info.read_record(&read_record_info))) { ACL_HOST host; - update_hostname(&host.host,get_field(&acl_memroot, table->field[0])); - host.db= get_field(&acl_memroot, table->field[1]); + update_hostname(&host.host, get_field(&acl_memroot, host_table.host())); + host.db= get_field(&acl_memroot, host_table.db()); if (lower_case_table_names && host.db) { /* @@ -1240,9 +1844,9 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) "possible to remove this privilege using REVOKE.", host.host.hostname, host.db); } - host.access= get_access(table,2); + host.access= host_table.get_access(); host.access= fix_rights_for_db(host.access); - host.sort= get_sort(2,host.host.hostname,host.db); + host.sort= get_sort(2, host.host.hostname, host.db); if (check_no_resolve && hostname_requires_resolving(host.host.hostname)) { sql_print_warning("'host' entry '%s|%s' " @@ -1252,7 +1856,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) continue; } #ifndef TO_BE_REMOVED - if (table->s->fields == 8) + if (host_table.num_fields() == 8) { // Without grant if (host.access & CREATE_ACL) host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; @@ -1260,60 +1864,62 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) #endif (void) push_dynamic(&acl_hosts,(uchar*) &host); } - my_qsort((uchar*) dynamic_element(&acl_hosts,0,ACL_HOST*),acl_hosts.elements, - sizeof(ACL_HOST),(qsort_cmp) acl_compare); + my_qsort((uchar*) dynamic_element(&acl_hosts, 0, ACL_HOST*), + acl_hosts.elements, sizeof(ACL_HOST),(qsort_cmp) acl_compare); end_read_record(&read_record_info); } freeze_size(&acl_hosts); - if (init_read_record(&read_record_info, thd, table=tables[USER_TABLE].table, - NULL, 1, 1, FALSE)) - goto end; - table->use_all_columns(); + const User_table& user_table= tables.user_table(); + if (user_table.init_read_record(&read_record_info, thd)) + DBUG_RETURN(true); - username_char_length= MY_MIN(table->field[1]->char_length(), + username_char_length= MY_MIN(user_table.user()->char_length(), USERNAME_CHAR_LENGTH); - password_length= table->field[2]->field_length / - table->field[2]->charset()->mbmaxlen; - if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + if (user_table.password()) // Password column might be missing. (MySQL 5.7.6+) { - sql_print_error("Fatal error: mysql.user table is damaged or in " - "unsupported 3.20 format."); - goto end; - } + password_length= user_table.password()->field_length / + user_table.password()->charset()->mbmaxlen; + if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) + { + sql_print_error("Fatal error: mysql.user table is damaged or in " + "unsupported 3.20 format."); + DBUG_RETURN(TRUE); + } - DBUG_PRINT("info",("user table fields: %d, password length: %d", - table->s->fields, password_length)); + DBUG_PRINT("info",("user table fields: %d, password length: %d", + user_table.num_fields(), password_length)); - mysql_mutex_lock(&LOCK_global_system_variables); - if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) - { - if (opt_secure_auth) + mysql_mutex_lock(&LOCK_global_system_variables); + if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH) { - mysql_mutex_unlock(&LOCK_global_system_variables); - sql_print_error("Fatal error: mysql.user table is in old format, " - "but server started with --secure-auth option."); - goto end; + if (opt_secure_auth) + { + mysql_mutex_unlock(&LOCK_global_system_variables); + sql_print_error("Fatal error: mysql.user table is in old format, " + "but server started with --secure-auth option."); + DBUG_RETURN(TRUE); + } + mysql_user_table_is_in_short_password_format= true; + if (global_system_variables.old_passwords) + mysql_mutex_unlock(&LOCK_global_system_variables); + else + { + extern sys_var *Sys_old_passwords_ptr; + Sys_old_passwords_ptr->value_origin= sys_var::AUTO; + global_system_variables.old_passwords= 1; + mysql_mutex_unlock(&LOCK_global_system_variables); + sql_print_warning("mysql.user table is not updated to new password format; " + "Disabling new password usage until " + "mysql_fix_privilege_tables is run"); + } + thd->variables.old_passwords= 1; } - mysql_user_table_is_in_short_password_format= true; - if (global_system_variables.old_passwords) - mysql_mutex_unlock(&LOCK_global_system_variables); else { - extern sys_var *Sys_old_passwords_ptr; - Sys_old_passwords_ptr->value_origin= sys_var::AUTO; - global_system_variables.old_passwords= 1; + mysql_user_table_is_in_short_password_format= false; mysql_mutex_unlock(&LOCK_global_system_variables); - sql_print_warning("mysql.user table is not updated to new password format; " - "Disabling new password usage until " - "mysql_fix_privilege_tables is run"); } - thd->variables.old_passwords= 1; - } - else - { - mysql_user_table_is_in_short_password_format= false; - mysql_mutex_unlock(&LOCK_global_system_variables); } allow_all_hosts=0; @@ -1322,8 +1928,8 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) ACL_USER user; bool is_role= FALSE; bzero(&user, sizeof(user)); - update_hostname(&user.host, get_field(&acl_memroot, table->field[0])); - char *username= get_field(&acl_memroot, table->field[1]); + update_hostname(&user.host, get_field(&acl_memroot, user_table.host())); + char *username= get_field(&acl_memroot, user_table.user()); user.user.str= username; user.user.length= safe_strlen(username); @@ -1331,7 +1937,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) If the user entry is a role, skip password and hostname checks A user can not log in with a role so some checks are not necessary */ - is_role= check_is_role(table); + is_role= user_table.check_is_role(); if (is_role && is_invalid_role_name(username)) { @@ -1349,7 +1955,9 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) continue; } - char *password= get_field(&acl_memroot, table->field[2]); + char *password= const_cast<char*>(""); + if (user_table.password()) + password= get_field(&acl_memroot, user_table.password()); uint password_len= safe_strlen(password); user.auth_string.str= safe_str(password); user.auth_string.length= password_len; @@ -1357,30 +1965,29 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) if (!is_role && set_user_plugin(&user, password_len)) continue; - + { - uint next_field; - user.access= get_access(table,3,&next_field) & GLOBAL_ACLS; + user.access= user_table.get_access() & GLOBAL_ACLS; /* if it is pre 5.0.1 privilege table then map CREATE privilege on CREATE VIEW & SHOW VIEW privileges */ - if (table->s->fields <= 31 && (user.access & CREATE_ACL)) + if (user_table.num_fields() <= 31 && (user.access & CREATE_ACL)) user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); /* if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on CREATE PROCEDURE & ALTER PROCEDURE privileges */ - if (table->s->fields <= 33 && (user.access & CREATE_ACL)) + if (user_table.num_fields() <= 33 && (user.access & CREATE_ACL)) user.access|= CREATE_PROC_ACL; - if (table->s->fields <= 33 && (user.access & ALTER_ACL)) + if (user_table.num_fields() <= 33 && (user.access & ALTER_ACL)) user.access|= ALTER_PROC_ACL; /* pre 5.0.3 did not have CREATE_USER_ACL */ - if (table->s->fields <= 36 && (user.access & GRANT_ACL)) + if (user_table.num_fields() <= 36 && (user.access & GRANT_ACL)) user.access|= CREATE_USER_ACL; @@ -1388,13 +1995,13 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) if it is pre 5.1.6 privilege table then map CREATE privilege on CREATE|ALTER|DROP|EXECUTE EVENT */ - if (table->s->fields <= 37 && (user.access & SUPER_ACL)) + if (user_table.num_fields() <= 37 && (user.access & SUPER_ACL)) user.access|= EVENT_ACL; /* if it is pre 5.1.6 privilege then map TRIGGER privilege on CREATE. */ - if (table->s->fields <= 38 && (user.access & SUPER_ACL)) + if (user_table.num_fields() <= 38 && (user.access & SUPER_ACL)) user.access|= TRIGGER_ACL; user.sort= get_sort(2, user.host.hostname, user.user.str); @@ -1403,9 +2010,9 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) user.user_resource.max_statement_time= 0.0; /* Starting from 4.0.2 we have more fields */ - if (table->s->fields >= 31) + if (user_table.ssl_type()) { - char *ssl_type=get_field(thd->mem_root, table->field[next_field++]); + char *ssl_type=get_field(thd->mem_root, user_table.ssl_type()); if (!ssl_type) user.ssl_type=SSL_TYPE_NONE; else if (!strcmp(ssl_type, "ANY")) @@ -1415,40 +2022,43 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) else /* !strcmp(ssl_type, "SPECIFIED") */ user.ssl_type=SSL_TYPE_SPECIFIED; - user.ssl_cipher= get_field(&acl_memroot, table->field[next_field++]); - user.x509_issuer= get_field(&acl_memroot, table->field[next_field++]); - user.x509_subject= get_field(&acl_memroot, table->field[next_field++]); + user.ssl_cipher= get_field(&acl_memroot, user_table.ssl_cipher()); + user.x509_issuer= get_field(&acl_memroot, user_table.x509_issuer()); + user.x509_subject= get_field(&acl_memroot, user_table.x509_subject()); - char *ptr = get_field(thd->mem_root, table->field[next_field++]); + char *ptr = get_field(thd->mem_root, user_table.max_questions()); user.user_resource.questions=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, table->field[next_field++]); + ptr = get_field(thd->mem_root, user_table.max_updates()); user.user_resource.updates=ptr ? atoi(ptr) : 0; - ptr = get_field(thd->mem_root, table->field[next_field++]); + ptr = get_field(thd->mem_root, user_table.max_connections()); user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0; if (user.user_resource.questions || user.user_resource.updates || user.user_resource.conn_per_hour) mqh_used=1; - if (table->s->fields >= 36) + if (user_table.max_user_connections()) { /* Starting from 5.0.3 we have max_user_connections field */ - ptr= get_field(thd->mem_root, table->field[next_field++]); + ptr= get_field(thd->mem_root, user_table.max_user_connections()); user.user_resource.user_conn= ptr ? atoi(ptr) : 0; } - if (!is_role && table->s->fields >= 41) + if (!is_role && user_table.plugin()) { /* We may have plugin & auth_String fields */ - char *tmpstr= get_field(&acl_memroot, table->field[next_field++]); + char *tmpstr= get_field(&acl_memroot, user_table.plugin()); if (tmpstr) { user.plugin.str= tmpstr; user.plugin.length= strlen(user.plugin.str); user.auth_string.str= - safe_str(get_field(&acl_memroot, table->field[next_field++])); + safe_str(get_field(&acl_memroot, + user_table.authentication_string())); user.auth_string.length= strlen(user.auth_string.str); - if (user.auth_string.length && password_len) + if (user.auth_string.length && password_len && + (user.auth_string.length != password_len || + memcmp(user.auth_string.str, password, password_len))) { sql_print_warning("'user' entry '%s@%s' has both a password " "and an authentication plugin specified. The " @@ -1461,11 +2071,11 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) } } - if (table->s->fields > MAX_STATEMENT_TIME_COLUMN_IDX) + if (user_table.max_statement_time()) { /* Starting from 10.1.1 we can have max_statement_time */ ptr= get_field(thd->mem_root, - table->field[MAX_STATEMENT_TIME_COLUMN_IDX]); + user_table.max_statement_time()); user.user_resource.max_statement_time= ptr ? atof(ptr) : 0.0; } } @@ -1473,7 +2083,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) { user.ssl_type=SSL_TYPE_NONE; #ifndef TO_BE_REMOVED - if (table->s->fields <= 13) + if (user_table.num_fields() <= 13) { // Without grant if (user.access & CREATE_ACL) user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; @@ -1491,10 +2101,10 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) 8, 8, MYF(0)); /* check default role, if any */ - if (!is_role && table->s->fields > DEFAULT_ROLE_COLUMN_IDX) + if (!is_role && user_table.default_role()) { user.default_rolename.str= - get_field(&acl_memroot, table->field[DEFAULT_ROLE_COLUMN_IDX]); + get_field(&acl_memroot, user_table.default_role()); user.default_rolename.length= safe_strlen(user.default_rolename.str); } @@ -1524,19 +2134,18 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) end_read_record(&read_record_info); freeze_size(&acl_users); - if (init_read_record(&read_record_info, thd, table=tables[DB_TABLE].table, - NULL, 1, 1, FALSE)) - goto end; - table->use_all_columns(); + const Db_table& db_table= tables.db_table(); + if (db_table.init_read_record(&read_record_info, thd)) + DBUG_RETURN(TRUE); while (!(read_record_info.read_record(&read_record_info))) { ACL_DB db; - db.user=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_USER]); - const char *hostname= get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_HOST]); + db.user=get_field(&acl_memroot, db_table.user()); + const char *hostname= get_field(&acl_memroot, db_table.host()); if (!hostname && find_acl_role(db.user)) hostname= ""; update_hostname(&db.host, hostname); - db.db=get_field(&acl_memroot, table->field[MYSQL_DB_FIELD_DB]); + db.db=get_field(&acl_memroot, db_table.db()); if (!db.db) { sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped"); @@ -1545,11 +2154,11 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) if (check_no_resolve && hostname_requires_resolving(db.host.hostname)) { sql_print_warning("'db' entry '%s %s@%s' " - "ignored in --skip-name-resolve mode.", + "ignored in --skip-name-resolve mode.", db.db, safe_str(db.user), safe_str(db.host.hostname)); continue; } - db.access=get_access(table,3); + db.access= db_table.get_access(); db.access=fix_rights_for_db(db.access); db.initial_access= db.access; if (lower_case_table_names) @@ -1576,7 +2185,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) } db.sort=get_sort(3,db.host.hostname,db.db,db.user); #ifndef TO_BE_REMOVED - if (table->s->fields <= 9) + if (db_table.num_fields() <= 9) { // Without grant if (db.access & CREATE_ACL) db.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; @@ -1589,23 +2198,19 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) end_read_record(&read_record_info); freeze_size(&acl_dbs); - if ((table= tables[PROXIES_PRIV_TABLE].table)) + const Proxies_priv_table& proxies_priv_table= tables.proxies_priv_table(); + if (proxies_priv_table.table_exists()) { - if (init_read_record(&read_record_info, thd, table, - NULL, 1, 1, FALSE)) - goto end; - table->use_all_columns(); + if (proxies_priv_table.init_read_record(&read_record_info, thd)) + DBUG_RETURN(TRUE); while (!(read_record_info.read_record(&read_record_info))) { ACL_PROXY_USER proxy; - proxy.init(table, &acl_memroot); + proxy.init(proxies_priv_table, &acl_memroot); if (proxy.check_validity(check_no_resolve)) continue; if (push_dynamic(&acl_proxy_users, (uchar*) &proxy)) - { - end_read_record(&read_record_info); - goto end; - } + DBUG_RETURN(TRUE); } my_qsort((uchar*) dynamic_element(&acl_proxy_users, 0, ACL_PROXY_USER*), acl_proxy_users.elements, @@ -1619,20 +2224,20 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) } freeze_size(&acl_proxy_users); - if ((table= tables[ROLES_MAPPING_TABLE].table)) + const Roles_mapping_table& roles_mapping_table= tables.roles_mapping_table(); + if (roles_mapping_table.table_exists()) { - if (init_read_record(&read_record_info, thd, table, NULL, 1, 1, FALSE)) - goto end; - table->use_all_columns(); + if (roles_mapping_table.init_read_record(&read_record_info, thd)) + DBUG_RETURN(TRUE); MEM_ROOT temp_root; init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); while (!(read_record_info.read_record(&read_record_info))) { - char *hostname= safe_str(get_field(&temp_root, table->field[0])); - char *username= safe_str(get_field(&temp_root, table->field[1])); - char *rolename= safe_str(get_field(&temp_root, table->field[2])); - bool with_grant_option= get_YN_as_bool(table->field[3]); + char *hostname= safe_str(get_field(&temp_root, roles_mapping_table.host())); + char *username= safe_str(get_field(&temp_root, roles_mapping_table.user())); + char *rolename= safe_str(get_field(&temp_root, roles_mapping_table.role())); + bool with_grant_option= get_YN_as_bool(roles_mapping_table.admin_option()); if (add_role_user_mapping(username, hostname, rolename)) { sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'", @@ -1660,12 +2265,7 @@ static bool acl_load(THD *thd, TABLE_LIST *tables) init_check_host(); initialized=1; - return_val= FALSE; - -end: - end_read_record(&read_record_info); - thd->variables.sql_mode= old_sql_mode; - DBUG_RETURN(return_val); + DBUG_RETURN(FALSE); } @@ -1713,19 +2313,19 @@ void acl_free(bool end) bool acl_reload(THD *thd) { - TABLE_LIST tables[TABLES_MAX]; DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users; HASH old_acl_roles, old_acl_roles_mappings; MEM_ROOT old_mem; int result; DBUG_ENTER("acl_reload"); + Grant_tables tables(Table_host | Table_user | Table_db | Table_proxies_priv | + Table_roles_mapping, TL_READ); /* To avoid deadlocks we should obtain table locks before obtaining acl_cache->lock mutex. */ - if ((result= open_grant_tables(thd, tables, TL_READ, Table_host | - Table_user | Table_db | Table_proxies_priv | Table_roles_mapping))) + if ((result= tables.open_and_lock(thd))) { DBUG_ASSERT(result <= 0); /* @@ -1828,34 +2428,6 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) return access_bits; } -/* - Check if a user entry in the user table is marked as being a role entry - - IMPLEMENTATION - Access the coresponding column and check the coresponding ENUM of the form - ENUM('N', 'Y') - - SYNOPSIS - check_is_role() - form an open table to read the entry from. - The record should be already read in table->record[0] - - RETURN VALUE - TRUE if the user is marked as a role - FALSE otherwise -*/ - -static bool check_is_role(TABLE *form) -{ - char buff[2]; - String res(buff, sizeof(buff), &my_charset_latin1); - /* Table version does not support roles */ - if (form->s->fields <= ROLE_ASSIGN_COLUMN_IDX) - return FALSE; - - return get_YN_as_bool(form->field[ROLE_ASSIGN_COLUMN_IDX]); -} - /* Return a number which, if sorted 'desc', puts strings in this order: @@ -2013,8 +2585,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, sctx->master_access= acl_role->access; if (acl_role->user.str) - strmake_buf(sctx->priv_user, user); - sctx->priv_host[0]= 0; + strmake_buf(sctx->priv_role, user); } } @@ -2041,7 +2612,7 @@ static int check_user_can_set_role(const char *user, const char *host, acl_user= find_user_wild(host, user, ip); if (acl_user == NULL) { - my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); + my_error(ER_INVALID_CURRENT_USER, MYF(0)); result= -1; } else if (access) @@ -2438,7 +3009,8 @@ exit: (entry= (acl_entry*) malloc(sizeof(acl_entry)+key_length))) { entry->access=(db_access & host_access); - entry->length=key_length; + DBUG_ASSERT(key_length < 0xffff); + entry->length=(uint16)key_length; memcpy((uchar*) entry->key,key,key_length); acl_cache->add(entry); } @@ -2774,7 +3346,7 @@ bool check_change_password(THD *thd, LEX_USER *user) */ bool change_password(THD *thd, LEX_USER *user) { - TABLE_LIST tables[TABLES_MAX]; + Grant_tables tables(Table_user, TL_WRITE); /* Buffer should be extended when password length is extended. */ char buff[512]; ulong query_length= 0; @@ -2809,7 +3381,7 @@ bool change_password(THD *thd, LEX_USER *user) WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); } - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user))) + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); result= 1; @@ -2838,7 +3410,7 @@ bool change_password(THD *thd, LEX_USER *user) ER_SET_PASSWORD_AUTH_PLUGIN, ER_THD(thd, ER_SET_PASSWORD_AUTH_PLUGIN)); - if (update_user_table(thd, tables[USER_TABLE].table, + if (update_user_table(thd, tables.user_table(), safe_str(acl_user->host.hostname), safe_str(acl_user->user.str), user->pwhash.str, user->pwhash.length)) @@ -2883,8 +3455,7 @@ int acl_check_set_default_role(THD *thd, const char *host, const char *user) int acl_set_default_role(THD *thd, const char *host, const char *user, const char *rolename) { - TABLE_LIST tables[TABLES_MAX]; - TABLE *table; + Grant_tables tables(Table_user, TL_WRITE); char user_key[MAX_KEY_LENGTH]; int result= 1; int error; @@ -2920,19 +3491,6 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, safe_str(rolename), safe_str(user), safe_str(host)); } - if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) - { - thd->set_query(buff, query_length, system_charset_info); - // Attention!!! here is implicit goto error; - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); - } - - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user))) - DBUG_RETURN(result != 1); - - table= tables[USER_TABLE].table; - result= 1; - /* This statement will be replicated as a statement, even when using row-based replication. The flag will be reset at the end of the @@ -2942,76 +3500,99 @@ int acl_set_default_role(THD *thd, const char *host, const char *user, */ save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); - mysql_mutex_lock(&acl_cache->lock); - ACL_USER *acl_user; - if (!(acl_user= find_user_exact(host, user))) + if (WSREP(thd) && !IF_WSREP(thd->wsrep_applier, 0)) { - mysql_mutex_unlock(&acl_cache->lock); - my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), - MYF(0)); - goto end; + thd->set_query(buff, query_length, system_charset_info); + // Attention!!! here is implicit goto error; + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, (char*)"user", NULL); } - if (!clear_role) { - /* set new default_rolename */ - acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename); - acl_user->default_rolename.length= strlen(rolename); - } - else + /* + Extra block due to WSREP_TO_ISOLATION_BEGIN using goto. + TODO(cvicentiu) Should move this block out in a new function. + */ { - /* clear the default_rolename */ - acl_user->default_rolename.str = NULL; - acl_user->default_rolename.length = 0; - } + if ((result= tables.open_and_lock(thd))) + DBUG_RETURN(result != 1); - /* update the mysql.user table with the new default role */ - table->use_all_columns(); - if (table->s->fields <= DEFAULT_ROLE_COLUMN_IDX) - { - my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), - table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1, table->s->fields, - static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID); - mysql_mutex_unlock(&acl_cache->lock); - goto end; - } - table->field[0]->store(host,(uint) strlen(host), system_charset_info); - table->field[1]->store(user,(uint) strlen(user), system_charset_info); - key_copy((uchar *) user_key, table->record[0], table->key_info, - table->key_info->key_length); + const User_table& user_table= tables.user_table(); + TABLE *table= user_table.table(); - if (table->file->ha_index_read_idx_map(table->record[0], 0, - (uchar *) user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) - { - mysql_mutex_unlock(&acl_cache->lock); - my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), - MYF(0)); - goto end; - } - store_record(table, record[1]); - table->field[DEFAULT_ROLE_COLUMN_IDX]->store(acl_user->default_rolename.str, - acl_user->default_rolename.length, - system_charset_info); - if ((error=table->file->ha_update_row(table->record[1],table->record[0])) && - error != HA_ERR_RECORD_IS_THE_SAME) - { - mysql_mutex_unlock(&acl_cache->lock); - table->file->print_error(error,MYF(0)); /* purecov: deadcode */ - goto end; - } + result= 1; - acl_cache->clear(1); - mysql_mutex_unlock(&acl_cache->lock); - result= 0; - if (mysql_bin_log.is_open()) - { - DBUG_ASSERT(query_length); - thd->clear_error(); - result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, - FALSE, FALSE, FALSE, 0); + mysql_mutex_lock(&acl_cache->lock); + ACL_USER *acl_user; + if (!(acl_user= find_user_exact(host, user))) + { + mysql_mutex_unlock(&acl_cache->lock); + my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), + MYF(0)); + goto end; + } + + if (!clear_role) + { + /* set new default_rolename */ + acl_user->default_rolename.str= safe_strdup_root(&acl_memroot, rolename); + acl_user->default_rolename.length= strlen(rolename); + } + else + { + /* clear the default_rolename */ + acl_user->default_rolename.str = NULL; + acl_user->default_rolename.length = 0; + } + + /* update the mysql.user table with the new default role */ + tables.user_table().table()->use_all_columns(); + if (!tables.user_table().default_role()) + { + my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), + table->alias.c_ptr(), DEFAULT_ROLE_COLUMN_IDX + 1, + tables.user_table().num_fields(), + static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID); + mysql_mutex_unlock(&acl_cache->lock); + goto end; + } + user_table.host()->store(host,(uint) strlen(host), system_charset_info); + user_table.user()->store(user,(uint) strlen(user), system_charset_info); + key_copy((uchar *) user_key, table->record[0], table->key_info, + table->key_info->key_length); + + if (table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *) user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) + { + mysql_mutex_unlock(&acl_cache->lock); + my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), + MYF(0)); + goto end; + } + store_record(table, record[1]); + user_table.default_role()->store(acl_user->default_rolename.str, + acl_user->default_rolename.length, + system_charset_info); + if ((error=table->file->ha_update_row(table->record[1],table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) + { + mysql_mutex_unlock(&acl_cache->lock); + table->file->print_error(error,MYF(0)); /* purecov: deadcode */ + goto end; + } + + acl_cache->clear(1); + mysql_mutex_unlock(&acl_cache->lock); + result= 0; + if (mysql_bin_log.is_open()) + { + DBUG_ASSERT(query_length); + thd->clear_error(); + result= thd->binlog_query(THD::STMT_QUERY_TYPE, buff, query_length, + FALSE, FALSE, FALSE, 0); + } + end: + close_mysql_tables(thd); } -end: - close_mysql_tables(thd); #ifdef WITH_WSREP WSREP_ERROR_LABEL: @@ -3283,6 +3864,28 @@ bool hostname_requires_resolving(const char *hostname) } +void set_authentication_plugin_from_password(const User_table& user_table, + const char* password, + uint password_length) +{ + if (password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH || + password_length == 0) + { + user_table.plugin()->store(native_password_plugin_name.str, + native_password_plugin_name.length, + system_charset_info); + } + else + { + DBUG_ASSERT(password_length == SCRAMBLED_PASSWORD_CHAR_LENGTH_323); + user_table.plugin()->store(old_password_plugin_name.str, + old_password_plugin_name.length, + system_charset_info); + } + user_table.authentication_string()->store(password, + password_length, + system_charset_info); +} /** Update record for user in mysql.user privilege table with new password. @@ -3296,18 +3899,19 @@ bool hostname_requires_resolving(const char *hostname) @see change_password */ -static bool update_user_table(THD *thd, TABLE *table, +static bool update_user_table(THD *thd, const User_table& user_table, const char *host, const char *user, - const char *new_password, uint new_password_len) + const char *new_password, uint new_password_len) { char user_key[MAX_KEY_LENGTH]; int error; DBUG_ENTER("update_user_table"); DBUG_PRINT("enter",("user: %s host: %s",user,host)); + TABLE *table= user_table.table(); table->use_all_columns(); - table->field[0]->store(host,(uint) strlen(host), system_charset_info); - table->field[1]->store(user,(uint) strlen(user), system_charset_info); + user_table.host()->store(host,(uint) strlen(host), system_charset_info); + user_table.user()->store(user,(uint) strlen(user), system_charset_info); key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3316,15 +3920,25 @@ static bool update_user_table(THD *thd, TABLE *table, HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER_THD(thd, ER_PASSWORD_NO_MATCH), - MYF(0)); /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ + MYF(0)); /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } store_record(table,record[1]); - table->field[2]->store(new_password, new_password_len, system_charset_info); + + if (user_table.plugin()) + { + set_authentication_plugin_from_password(user_table, new_password, + new_password_len); + } + + if (user_table.password()) + user_table.password()->store(new_password, new_password_len, system_charset_info); + + if ((error=table->file->ha_update_row(table->record[1],table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME) { - table->file->print_error(error,MYF(0)); /* purecov: deadcode */ + table->file->print_error(error,MYF(0)); /* purecov: deadcode */ DBUG_RETURN(1); } DBUG_RETURN(0); @@ -3370,8 +3984,9 @@ static bool test_if_create_new_users(THD *thd) Handle GRANT commands ****************************************************************************/ -static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, - ulong rights, bool revoke_grant, +static int replace_user_table(THD *thd, const User_table &user_table, + LEX_USER &combo, + ulong rights, bool revoke_grant, bool can_create_user, bool no_auto_create) { int error = -1; @@ -3380,6 +3995,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, uchar user_key[MAX_KEY_LENGTH]; bool handle_as_role= combo.is_role(); LEX *lex= thd->lex; + TABLE *table= user_table.table(); DBUG_ENTER("replace_user_table"); mysql_mutex_assert_owner(&acl_cache->lock); @@ -3398,18 +4014,18 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, combo.pwhash= empty_lex_str; /* if the user table is not up to date, we can't handle role updates */ - if (table->s->fields <= ROLE_ASSIGN_COLUMN_IDX && handle_as_role) + if (!user_table.is_role() && handle_as_role) { my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), - table->alias.c_ptr(), ROLE_ASSIGN_COLUMN_IDX + 1, table->s->fields, + "user", ROLE_ASSIGN_COLUMN_IDX + 1, user_table.num_fields(), static_cast<int>(table->s->mysql_version), MYSQL_VERSION_ID); DBUG_RETURN(-1); } table->use_all_columns(); - table->field[0]->store(combo.host.str,combo.host.length, + user_table.host()->store(combo.host.str,combo.host.length, system_charset_info); - table->field[1]->store(combo.user.str,combo.user.length, + user_table.user()->store(combo.user.str,combo.user.length, system_charset_info); key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); @@ -3458,9 +4074,9 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, old_row_exists = 0; restore_record(table,s->default_values); - table->field[0]->store(combo.host.str,combo.host.length, + user_table.host()->store(combo.host.str,combo.host.length, system_charset_info); - table->field[1]->store(combo.user.str,combo.user.length, + user_table.user()->store(combo.user.str,combo.user.length, system_charset_info); } else @@ -3475,102 +4091,119 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, /* Update table columns with new privileges */ - Field **tmp_field; ulong priv; - uint next_field; - for (tmp_field= table->field+3, priv = SELECT_ACL; - *tmp_field && (*tmp_field)->real_type() == MYSQL_TYPE_ENUM && - ((Field_enum*) (*tmp_field))->typelib->count == 2 ; - tmp_field++, priv <<= 1) + priv = SELECT_ACL; + for (uint i= 0; i < user_table.num_privileges(); i++, priv <<= 1) { - if (priv & rights) // set requested privileges - (*tmp_field)->store(&what, 1, &my_charset_latin1); + if (priv & rights) + user_table.priv_field(i)->store(&what, 1, &my_charset_latin1); } - rights= get_access(table, 3, &next_field); - DBUG_PRINT("info",("table fields: %d",table->s->fields)); - if (combo.pwhash.str[0]) - table->field[2]->store(combo.pwhash.str, combo.pwhash.length, system_charset_info); - if (table->s->fields >= 31) /* From 4.0.0 we have more fields */ + + rights= user_table.get_access(); + + DBUG_PRINT("info",("table fields: %d", user_table.num_fields())); + /* If we don't have a password column, we'll use the authentication_string + column later. */ + if (combo.pwhash.str[0] && user_table.password()) + user_table.password()->store(combo.pwhash.str, combo.pwhash.length, + system_charset_info); + /* We either have the password column, the plugin column, or both. Otherwise + we have a corrupt user table. */ + DBUG_ASSERT(user_table.password() || user_table.plugin()); + if (user_table.ssl_type()) /* From 4.0.0 we have more fields */ { /* We write down SSL related ACL stuff */ switch (lex->ssl_type) { case SSL_TYPE_ANY: - table->field[next_field]->store(STRING_WITH_LEN("ANY"), - &my_charset_latin1); - table->field[next_field+1]->store("", 0, &my_charset_latin1); - table->field[next_field+2]->store("", 0, &my_charset_latin1); - table->field[next_field+3]->store("", 0, &my_charset_latin1); + user_table.ssl_type()->store(STRING_WITH_LEN("ANY"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); break; case SSL_TYPE_X509: - table->field[next_field]->store(STRING_WITH_LEN("X509"), - &my_charset_latin1); - table->field[next_field+1]->store("", 0, &my_charset_latin1); - table->field[next_field+2]->store("", 0, &my_charset_latin1); - table->field[next_field+3]->store("", 0, &my_charset_latin1); + user_table.ssl_type()->store(STRING_WITH_LEN("X509"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); break; case SSL_TYPE_SPECIFIED: - table->field[next_field]->store(STRING_WITH_LEN("SPECIFIED"), - &my_charset_latin1); - table->field[next_field+1]->store("", 0, &my_charset_latin1); - table->field[next_field+2]->store("", 0, &my_charset_latin1); - table->field[next_field+3]->store("", 0, &my_charset_latin1); + user_table.ssl_type()->store(STRING_WITH_LEN("SPECIFIED"), + &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); if (lex->ssl_cipher) - table->field[next_field+1]->store(lex->ssl_cipher, - strlen(lex->ssl_cipher), system_charset_info); + user_table.ssl_cipher()->store(lex->ssl_cipher, + strlen(lex->ssl_cipher), + system_charset_info); if (lex->x509_issuer) - table->field[next_field+2]->store(lex->x509_issuer, - strlen(lex->x509_issuer), system_charset_info); + user_table.x509_issuer()->store(lex->x509_issuer, + strlen(lex->x509_issuer), + system_charset_info); if (lex->x509_subject) - table->field[next_field+3]->store(lex->x509_subject, - strlen(lex->x509_subject), system_charset_info); + user_table.x509_subject()->store(lex->x509_subject, + strlen(lex->x509_subject), + system_charset_info); break; case SSL_TYPE_NOT_SPECIFIED: break; case SSL_TYPE_NONE: - table->field[next_field]->store("", 0, &my_charset_latin1); - table->field[next_field+1]->store("", 0, &my_charset_latin1); - table->field[next_field+2]->store("", 0, &my_charset_latin1); - table->field[next_field+3]->store("", 0, &my_charset_latin1); + user_table.ssl_type()->store("", 0, &my_charset_latin1); + user_table.ssl_cipher()->store("", 0, &my_charset_latin1); + user_table.x509_issuer()->store("", 0, &my_charset_latin1); + user_table.x509_subject()->store("", 0, &my_charset_latin1); break; } - next_field+=4; USER_RESOURCES mqh= lex->mqh; if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) - table->field[next_field]->store((longlong) mqh.questions, TRUE); + user_table.max_questions()->store((longlong) mqh.questions, TRUE); if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR) - table->field[next_field+1]->store((longlong) mqh.updates, TRUE); + user_table.max_updates()->store((longlong) mqh.updates, TRUE); if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR) - table->field[next_field+2]->store((longlong) mqh.conn_per_hour, TRUE); - if (table->s->fields >= 36 && + user_table.max_connections()->store((longlong) mqh.conn_per_hour, TRUE); + if (user_table.max_user_connections() && (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) - table->field[next_field+3]->store((longlong) mqh.user_conn, FALSE); - next_field+= 4; - if (table->s->fields >= 41) + user_table.max_user_connections()->store((longlong) mqh.user_conn, FALSE); + if (user_table.plugin()) { - table->field[next_field]->set_notnull(); - table->field[next_field + 1]->set_notnull(); + user_table.plugin()->set_notnull(); + user_table.authentication_string()->set_notnull(); if (combo.plugin.str[0]) { DBUG_ASSERT(combo.pwhash.str[0] == 0); - table->field[2]->reset(); - 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); + if (user_table.password()) + user_table.password()->reset(); + user_table.plugin()->store(combo.plugin.str, combo.plugin.length, + system_charset_info); + user_table.authentication_string()->store(combo.auth.str, combo.auth.length, + system_charset_info); } if (combo.pwhash.str[0]) { DBUG_ASSERT(combo.plugin.str[0] == 0); - table->field[next_field]->reset(); - table->field[next_field + 1]->reset(); + /* We have Password column. */ + if (user_table.password()) + { + user_table.plugin()->reset(); + user_table.authentication_string()->reset(); + } + else + { + /* We do not have Password column. Use PLUGIN && Authentication_string + columns instead. */ + set_authentication_plugin_from_password(user_table, + combo.pwhash.str, + combo.pwhash.length); + } } - if (table->s->fields > MAX_STATEMENT_TIME_COLUMN_IDX) + if (user_table.max_statement_time()) { if (mqh.specified_limits & USER_RESOURCES::MAX_STATEMENT_TIME) - table->field[MAX_STATEMENT_TIME_COLUMN_IDX]-> - store(mqh.max_statement_time); + user_table.max_statement_time()->store(mqh.max_statement_time); } } mqh_used= (mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour || @@ -3579,11 +4212,11 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, /* table format checked earlier */ if (handle_as_role) { - if (old_row_exists && !check_is_role(table)) + if (old_row_exists && !user_table.check_is_role()) { goto end; } - table->field[ROLE_ASSIGN_COLUMN_IDX]->store("Y", 1, system_charset_info); + user_table.is_role()->store("Y", 1, system_charset_info); } } @@ -3593,7 +4226,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, We should NEVER delete from the user table, as a uses can still use mysqld even if he doesn't have any privileges in the user table! */ - if (cmp_record(table,record[1])) + if (cmp_record(table, record[1])) { if ((error= table->file->ha_update_row(table->record[1],table->record[0])) && @@ -4384,6 +5017,8 @@ table_hash_search(const char *host, const char *ip, const char *db, static GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, uint length) { + if (!my_hash_inited(&t->hash_columns)) + return (GRANT_COLUMN*) 0; return (GRANT_COLUMN*) my_hash_search(&t->hash_columns, (uchar*) cname, length); } @@ -5303,8 +5938,8 @@ static bool merge_role_db_privileges(ACL_ROLE *grantee, const char *dbname, (that should be merged) are sorted together. The grantee's ACL_DB element is not necessarily the first and may be not present at all. */ - ACL_DB **first= NULL, *UNINIT_VAR(merged); - ulong UNINIT_VAR(access), update_flags= 0; + ACL_DB **first= NULL, *merged= NULL; + ulong access= 0, update_flags= 0; for (ACL_DB **cur= dbs.front(); cur <= dbs.back(); cur++) { if (!first || (!dbname && strcmp(cur[0]->db, cur[-1]->db))) @@ -5509,8 +6144,8 @@ static bool merge_role_table_and_column_privileges(ACL_ROLE *grantee, } grants.sort(table_name_sort); - GRANT_TABLE **first= NULL, *UNINIT_VAR(merged), **cur; - ulong UNINIT_VAR(privs), UNINIT_VAR(cols), update_flags= 0; + GRANT_TABLE **first= NULL, *merged= NULL, **cur; + ulong privs= 0, cols= 0, update_flags= 0; for (cur= grants.front(); cur <= grants.back(); cur++) { if (!first || @@ -5633,8 +6268,8 @@ static bool merge_role_routine_grant_privileges(ACL_ROLE *grantee, } grants.sort(routine_name_sort); - GRANT_NAME **first= NULL, *UNINIT_VAR(merged); - ulong UNINIT_VAR(privs); + GRANT_NAME **first= NULL, *merged= NULL; + ulong privs= 0 ; for (GRANT_NAME **cur= grants.front(); cur <= grants.back(); cur++) { if (!first || @@ -5784,7 +6419,6 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, int result; List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str, *tmp_Str; - TABLE_LIST tables[TABLES_MAX]; bool create_new_users=0; char *db_name, *table_name; DBUG_ENTER("mysql_table_grant"); @@ -5873,8 +6507,9 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, */ thd->lex->sql_command= backup.sql_command; - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | - Table_tables_priv | maybe_columns_priv))) + Grant_tables tables(Table_user | Table_tables_priv | maybe_columns_priv, + TL_WRITE); + if ((result= tables.open_and_lock(thd))) { thd->lex->restore_backup_query_tables_list(&backup); DBUG_RETURN(result != 1); @@ -5899,7 +6534,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ error= copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables[USER_TABLE].table, *Str, + replace_user_table(thd, tables.user_table(), *Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER)); @@ -5970,16 +6605,20 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, /* update table and columns */ - if (replace_table_table(thd, grant_table, tables[TABLES_PRIV_TABLE].table, + /* TODO(cvicentiu) refactor replace_table_table to use Tables_priv_table + instead of TABLE directly. */ + if (replace_table_table(thd, grant_table, tables.tables_priv_table().table(), *Str, db_name, table_name, rights, column_priv, revoke_grant)) { /* Should only happen if table is crashed */ result= TRUE; /* purecov: deadcode */ } - else if (tables[COLUMNS_PRIV_TABLE].table) + else if (tables.columns_priv_table().table_exists()) { - if (replace_column_table(grant_table, tables[COLUMNS_PRIV_TABLE].table, + /* TODO(cvicentiu) refactor replace_column_table to use Columns_priv_table + instead of TABLE directly. */ + if (replace_column_table(grant_table, tables.columns_priv_table().table(), *Str, columns, db_name, table_name, rights, revoke_grant)) { @@ -6030,7 +6669,6 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, { List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str, *tmp_Str; - TABLE_LIST tables[TABLES_MAX]; bool create_new_users= 0, result; char *db_name, *table_name; DBUG_ENTER("mysql_routine_grant"); @@ -6049,8 +6687,8 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, DBUG_RETURN(TRUE); } - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | - Table_procs_priv))) + Grant_tables tables(Table_user | Table_procs_priv, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -6074,7 +6712,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } /* Create user if needed */ if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables[USER_TABLE].table, *Str, + replace_user_table(thd, tables.user_table(), *Str, 0, revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -6108,8 +6746,10 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } } - if (no_such_table(tables + PROCS_PRIV_TABLE) || - replace_routine_table(thd, grant_name, tables[PROCS_PRIV_TABLE].table, + /* TODO(cvicentiu) refactor replace_routine_table to use Tables_procs_priv + instead of TABLE directly. */ + if (tables.procs_priv_table().no_such_table() || + replace_routine_table(thd, grant_name, tables.procs_priv_table().table(), *Str, db_name, table_name, is_proc, rights, revoke_grant) != 0) { @@ -6249,9 +6889,8 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) no_auto_create_user= MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER); - TABLE_LIST tables[TABLES_MAX]; - if ((result= open_grant_tables(thd, tables, TL_WRITE, - Table_user | Table_roles_mapping))) + Grant_tables tables(Table_user | Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); mysql_rwlock_wrlock(&LOCK_grant); @@ -6350,7 +6989,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) user_combo.user = username; if (copy_and_check_auth(&user_combo, &user_combo, thd) || - replace_user_table(thd, tables[USER_TABLE].table, user_combo, 0, + replace_user_table(thd, tables.user_table(), user_combo, 0, false, create_new_user, no_auto_create_user)) { @@ -6416,7 +7055,9 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) } /* write into the roles_mapping table */ - if (replace_roles_mapping_table(tables[ROLES_MAPPING_TABLE].table, + /* TODO(cvicentiu) refactor replace_roles_mapping_table to use + Roles_mapping_table instead of TABLE directly. */ + if (replace_roles_mapping_table(tables.roles_mapping_table().table(), &username, &hostname, &rolename, thd->lex->with_admin_option, hash_entry, revoke)) @@ -6467,7 +7108,6 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, LEX_USER *Str, *tmp_Str, *proxied_user= NULL; char tmp_db[SAFE_NAME_LEN+1]; bool create_new_users=0, result; - TABLE_LIST tables[TABLES_MAX]; DBUG_ENTER("mysql_grant"); if (lower_case_table_names && db) @@ -6488,8 +7128,9 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, proxied_user= str_list++; } - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | - (is_proxy ? Table_proxies_priv : Table_db)))) + Grant_tables tables(Table_user | (is_proxy ? Table_proxies_priv : Table_db), + TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -6518,7 +7159,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } if (copy_and_check_auth(Str, tmp_Str, thd) || - replace_user_table(thd, tables[USER_TABLE].table, *Str, + replace_user_table(thd, tables.user_table(), *Str, (!db ? rights : 0), revoke_grant, create_new_users, MY_TEST(thd->variables.sql_mode & MODE_NO_AUTO_CREATE_USER))) @@ -6528,7 +7169,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, ulong db_rights= rights & DB_ACLS; if (db_rights == rights) { - if (replace_db_table(tables[DB_TABLE].table, db, *Str, db_rights, + if (replace_db_table(tables.db_table().table(), db, *Str, db_rights, revoke_grant)) result= true; } @@ -6540,8 +7181,10 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, } else if (is_proxy) { - if (no_such_table(tables + PROXIES_PRIV_TABLE) || - replace_proxies_priv_table (thd, tables[PROXIES_PRIV_TABLE].table, + /* TODO(cvicentiu) refactor replace_proxies_priv_table to use + Proxies_priv_table instead of TABLE directly. */ + if (tables.proxies_priv_table().no_such_table() || + replace_proxies_priv_table (thd, tables.proxies_priv_table().table(), Str, proxied_user, rights & GRANT_ACL ? TRUE : FALSE, revoke_grant)) @@ -6596,7 +7239,7 @@ bool grant_init() bool return_val; DBUG_ENTER("grant_init"); - if (!(thd= new THD)) + if (!(thd= new THD(0))) DBUG_RETURN(1); /* purecov: deadcode */ thd->thread_stack= (char*) &thd; thd->store_globals(); @@ -6621,15 +7264,16 @@ bool grant_init() @retval TRUE Error */ -static bool grant_load(THD *thd, TABLE_LIST *tables) +static bool grant_load(THD *thd, + const Tables_priv_table& tables_priv, + const Columns_priv_table& columns_priv, + const Procs_priv_table& procs_priv) { - MEM_ROOT *memex_ptr; bool return_val= 1; TABLE *t_table, *c_table, *p_table; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; - MEM_ROOT **save_mem_root_ptr= my_pthread_getspecific_ptr(MEM_ROOT**, - THR_MALLOC); - ulonglong old_sql_mode= thd->variables.sql_mode; + MEM_ROOT *save_mem_root= thd->mem_root; + sql_mode_t old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("grant_load"); thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; @@ -6643,9 +7287,9 @@ static bool grant_load(THD *thd, TABLE_LIST *tables) 0,0,0, (my_hash_get_key) get_grant_table, 0,0); init_sql_alloc(&grant_memroot, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); - t_table= tables[TABLES_PRIV_TABLE].table; - c_table= tables[COLUMNS_PRIV_TABLE].table; - p_table= tables[PROCS_PRIV_TABLE].table; // this can be NULL + t_table= tables_priv.table(); + c_table= columns_priv.table(); + p_table= procs_priv.table(); // this can be NULL if (t_table->file->ha_index_init(0, 1)) goto end_index_init; @@ -6653,15 +7297,15 @@ static bool grant_load(THD *thd, TABLE_LIST *tables) t_table->use_all_columns(); c_table->use_all_columns(); - memex_ptr= &grant_memroot; - my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); + thd->mem_root= &grant_memroot; if (!t_table->file->ha_index_first(t_table->record[0])) { do { GRANT_TABLE *mem_check; - if (!(mem_check=new (memex_ptr) GRANT_TABLE(t_table,c_table))) + /* TODO(cvicentiu) convert this to use tables_priv and columns_priv. */ + if (!(mem_check= new (&grant_memroot) GRANT_TABLE(t_table, c_table))) { /* This could only happen if we are out memory */ goto end_unlock; @@ -6706,7 +7350,7 @@ static bool grant_load(THD *thd, TABLE_LIST *tables) { GRANT_NAME *mem_check; HASH *hash; - if (!(mem_check=new (memex_ptr) GRANT_NAME(p_table, TRUE))) + if (!(mem_check= new (&grant_memroot) GRANT_NAME(p_table, TRUE))) { /* This could only happen if we are out memory */ goto end_unlock_p; @@ -6723,12 +7367,12 @@ static bool grant_load(THD *thd, TABLE_LIST *tables) continue; } } - if (p_table->field[4]->val_int() == TYPE_ENUM_PROCEDURE) + if (procs_priv.routine_type()->val_int() == TYPE_ENUM_PROCEDURE) { hash= &proc_priv_hash; } else - if (p_table->field[4]->val_int() == TYPE_ENUM_FUNCTION) + if (procs_priv.routine_type()->val_int() == TYPE_ENUM_FUNCTION) { hash= &func_priv_hash; } @@ -6759,7 +7403,7 @@ end_unlock_p: p_table->file->ha_index_end(); end_unlock: t_table->file->ha_index_end(); - my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); + thd->mem_root= save_mem_root; end_index_init: thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(return_val); @@ -6796,7 +7440,6 @@ static my_bool propagate_role_grants_action(void *role_ptr, bool grant_reload(THD *thd) { - TABLE_LIST tables[TABLES_MAX]; HASH old_column_priv_hash, old_proc_priv_hash, old_func_priv_hash; MEM_ROOT old_mem; int result; @@ -6807,8 +7450,9 @@ bool grant_reload(THD *thd) obtaining LOCK_grant rwlock. */ - if ((result= open_grant_tables(thd, tables, TL_READ, Table_tables_priv | - Table_columns_priv | Table_procs_priv))) + Grant_tables tables(Table_tables_priv | Table_columns_priv| Table_procs_priv, + TL_READ); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); mysql_rwlock_wrlock(&LOCK_grant); @@ -6823,7 +7467,10 @@ bool grant_reload(THD *thd) */ old_mem= grant_memroot; - if ((result= grant_load(thd, tables))) + if ((result= grant_load(thd, + tables.tables_priv_table(), + tables.columns_priv_table(), + tables.procs_priv_table()))) { // Error. Revert to old hash DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ @@ -6937,6 +7584,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, tl->correspondent_table ? tl->correspondent_table : tl; sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; + if (tl->with || !tl->db || + (tl->select_lex && + (tl->with= tl->select_lex->find_table_def_in_with_clauses(tl)))) + continue; + const ACL_internal_table_access *access= get_cached_table_access(&t_ref->grant.m_internal, t_ref->get_db_name(), @@ -6974,8 +7626,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, /* It is subquery in the FROM clause. VIEW set t_ref->derived after table opening, but this function always called before table opening. + + NOTE: is_derived() can't be used here because subquery in this case + the FROM clase (derived tables) can be not be marked yet. */ - if (!t_ref->referencing_view) + if (t_ref->is_anonymous_derived_table() || t_ref->schema_table) { /* If it's a temporary table created for a subquery in the FROM @@ -7190,7 +7845,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, GRANT_INFO *grant; const char *db_name; const char *table_name; - Security_context *sctx= MY_TEST(table_ref->security_ctx) ? + Security_context *sctx= table_ref->security_ctx ? table_ref->security_ctx : thd->security_ctx; if (table_ref->view || table_ref->field_translation) @@ -7707,6 +8362,94 @@ static void add_user_option(String *grant, double value, const char *name) } } +static void add_user_parameters(String *result, ACL_USER* acl_user, + bool with_grant) +{ + result->append(STRING_WITH_LEN("@'")); + result->append(acl_user->host.hostname, acl_user->hostname_length, + system_charset_info); + result->append('\''); + + if (acl_user->plugin.str == native_password_plugin_name.str || + acl_user->plugin.str == old_password_plugin_name.str) + { + if (acl_user->auth_string.length) + { + DBUG_ASSERT(acl_user->salt_len); + result->append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); + result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append('\''); + } + } + else + { + result->append(STRING_WITH_LEN(" IDENTIFIED VIA ")); + result->append(acl_user->plugin.str, acl_user->plugin.length); + if (acl_user->auth_string.length) + { + result->append(STRING_WITH_LEN(" USING '")); + result->append(acl_user->auth_string.str, acl_user->auth_string.length); + result->append('\''); + } + } + /* "show grants" SSL related stuff */ + if (acl_user->ssl_type == SSL_TYPE_ANY) + result->append(STRING_WITH_LEN(" REQUIRE SSL")); + else if (acl_user->ssl_type == SSL_TYPE_X509) + result->append(STRING_WITH_LEN(" REQUIRE X509")); + else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) + { + int ssl_options = 0; + result->append(STRING_WITH_LEN(" REQUIRE ")); + if (acl_user->x509_issuer) + { + ssl_options++; + result->append(STRING_WITH_LEN("ISSUER \'")); + result->append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + result->append('\''); + } + if (acl_user->x509_subject) + { + if (ssl_options++) + result->append(' '); + result->append(STRING_WITH_LEN("SUBJECT \'")); + result->append(acl_user->x509_subject,strlen(acl_user->x509_subject), + system_charset_info); + result->append('\''); + } + if (acl_user->ssl_cipher) + { + if (ssl_options++) + result->append(' '); + result->append(STRING_WITH_LEN("CIPHER '")); + result->append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher), + system_charset_info); + result->append('\''); + } + } + if (with_grant || + (acl_user->user_resource.questions || + acl_user->user_resource.updates || + acl_user->user_resource.conn_per_hour || + acl_user->user_resource.user_conn || + acl_user->user_resource.max_statement_time != 0.0)) + { + result->append(STRING_WITH_LEN(" WITH")); + if (with_grant) + result->append(STRING_WITH_LEN(" GRANT OPTION")); + add_user_option(result, acl_user->user_resource.questions, + "MAX_QUERIES_PER_HOUR", false); + add_user_option(result, acl_user->user_resource.updates, + "MAX_UPDATES_PER_HOUR", false); + add_user_option(result, acl_user->user_resource.conn_per_hour, + "MAX_CONNECTIONS_PER_HOUR", false); + add_user_option(result, acl_user->user_resource.user_conn, + "MAX_USER_CONNECTIONS", true); + add_user_option(result, acl_user->user_resource.max_statement_time, + "MAX_STATEMENT_TIME"); + } +} + static const char *command_array[]= { "SELECT", "INSERT", "UPDATE", "DELETE", "CREATE", "DROP", "RELOAD", @@ -7752,6 +8495,122 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role) } +/** checks privileges for SHOW GRANTS and SHOW CREATE USER + + @note that in case of SHOW CREATE USER the parser guarantees + that a role can never happen here, so *rolename will never + be assigned to +*/ +static bool check_show_access(THD *thd, LEX_USER *lex_user, char **username, + char **hostname, char **rolename) +{ + DBUG_ENTER("check_show_access"); + + if (lex_user->user.str == current_user.str) + { + *username= thd->security_ctx->priv_user; + *hostname= thd->security_ctx->priv_host; + } + else if (lex_user->user.str == current_role.str) + { + *rolename= thd->security_ctx->priv_role; + } + else if (lex_user->user.str == current_user_and_current_role.str) + { + *username= thd->security_ctx->priv_user; + *hostname= thd->security_ctx->priv_host; + *rolename= thd->security_ctx->priv_role; + } + else + { + Security_context *sctx= thd->security_ctx; + bool do_check_access; + + lex_user= get_current_user(thd, lex_user); + if (!lex_user) + DBUG_RETURN(TRUE); + + if (lex_user->is_role()) + { + *rolename= lex_user->user.str; + do_check_access= strcmp(*rolename, sctx->priv_role); + } + else + { + *username= lex_user->user.str; + *hostname= lex_user->host.str; + do_check_access= strcmp(*username, sctx->priv_user) || + strcmp(*hostname, sctx->priv_host); + } + + if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0)) + DBUG_RETURN(TRUE); + } + DBUG_RETURN(FALSE); +} + +bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) +{ + char *username= NULL, *hostname= NULL; + char buff[1024]; //Show create user should not take more than 1024 bytes. + Protocol *protocol= thd->protocol; + bool error= false; + ACL_USER *acl_user; + DBUG_ENTER("mysql_show_create_user"); + + if (check_show_access(thd, lex_user, &username, &hostname, NULL)) + DBUG_RETURN(TRUE); + + List<Item> field_list; + strxmov(buff, "CREATE USER for ", username, "@", hostname, NullS); + Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0); + if (!field) + DBUG_RETURN(true); // Error given my my_alloc() + + field->name= buff; + field->max_length= sizeof(buff); + field_list.push_back(field, thd->mem_root); + if (protocol->send_result_set_metadata(&field_list, + Protocol::SEND_NUM_ROWS | + Protocol::SEND_EOF)) + DBUG_RETURN(true); + + String result(buff, sizeof(buff), system_charset_info); + result.length(0); + mysql_rwlock_rdlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + + acl_user= find_user_exact(hostname, username); + + // User not found in the internal data structures. + if (!acl_user) + { + my_error(ER_PASSWORD_NO_MATCH, MYF(0)); + error= true; + goto end; + } + + result.append("CREATE USER '"); + result.append(username); + result.append('\''); + + add_user_parameters(&result, acl_user, false); + + protocol->prepare_for_resend(); + protocol->store(result.ptr(), result.length(), result.charset()); + if (protocol->write()) + { + error= true; + } + my_eof(thd); + +end: + mysql_rwlock_unlock(&LOCK_grant); + mysql_mutex_unlock(&acl_cache->lock); + + DBUG_RETURN(error); +} + static int show_grants_callback(ACL_USER_BASE *role, void *data) { @@ -7762,7 +8621,6 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data) return 0; } - void mysql_show_grants_get_fields(THD *thd, List<Item> *fields, const char *name) { @@ -7787,9 +8645,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) ACL_ROLE *acl_role= NULL; char buff[1024]; Protocol *protocol= thd->protocol; - char *username= NULL; - char *hostname= NULL; - char *rolename= NULL; + char *username= NULL, *hostname= NULL, *rolename= NULL; DBUG_ENTER("mysql_show_grants"); if (!initialized) @@ -7798,46 +8654,8 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) DBUG_RETURN(TRUE); } - if (lex_user->user.str == current_user.str) - { - username= thd->security_ctx->priv_user; - hostname= thd->security_ctx->priv_host; - } - else if (lex_user->user.str == current_role.str) - { - rolename= thd->security_ctx->priv_role; - } - else if (lex_user->user.str == current_user_and_current_role.str) - { - username= thd->security_ctx->priv_user; - hostname= thd->security_ctx->priv_host; - rolename= thd->security_ctx->priv_role; - } - else - { - Security_context *sctx= thd->security_ctx; - bool do_check_access; - - lex_user= get_current_user(thd, lex_user); - if (!lex_user) - DBUG_RETURN(TRUE); - - if (lex_user->is_role()) - { - rolename= lex_user->user.str; - do_check_access= strcmp(rolename, sctx->priv_role); - } - else - { - username= lex_user->user.str; - hostname= lex_user->host.str; - do_check_access= strcmp(username, sctx->priv_user) || - strcmp(hostname, sctx->priv_host); - } - - if (do_check_access && check_access(thd, SELECT_ACL, "mysql", 0, 0, 1, 0)) - DBUG_RETURN(TRUE); - } + if (check_show_access(thd, lex_user, &username, &hostname, &rolename)) + DBUG_RETURN(TRUE); DBUG_ASSERT(rolename || username); List<Item> field_list; @@ -8029,93 +8847,7 @@ static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, global.append('\''); if (!handle_as_role) - { - ACL_USER *acl_user= (ACL_USER *)acl_entry; - - global.append (STRING_WITH_LEN("@'")); - global.append(acl_user->host.hostname, acl_user->hostname_length, - system_charset_info); - global.append ('\''); - - if (acl_user->plugin.str == native_password_plugin_name.str || - acl_user->plugin.str == old_password_plugin_name.str) - { - if (acl_user->auth_string.length) - { - DBUG_ASSERT(acl_user->salt_len); - global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); - global.append(acl_user->auth_string.str, acl_user->auth_string.length); - global.append('\''); - } - } - else - { - global.append(STRING_WITH_LEN(" IDENTIFIED VIA ")); - global.append(acl_user->plugin.str, acl_user->plugin.length); - if (acl_user->auth_string.length) - { - global.append(STRING_WITH_LEN(" USING '")); - global.append(acl_user->auth_string.str, acl_user->auth_string.length); - global.append('\''); - } - } - /* "show grants" SSL related stuff */ - if (acl_user->ssl_type == SSL_TYPE_ANY) - global.append(STRING_WITH_LEN(" REQUIRE SSL")); - else if (acl_user->ssl_type == SSL_TYPE_X509) - global.append(STRING_WITH_LEN(" REQUIRE X509")); - else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) - { - int ssl_options = 0; - global.append(STRING_WITH_LEN(" REQUIRE ")); - if (acl_user->x509_issuer) - { - ssl_options++; - global.append(STRING_WITH_LEN("ISSUER \'")); - global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); - global.append('\''); - } - if (acl_user->x509_subject) - { - if (ssl_options++) - global.append(' '); - global.append(STRING_WITH_LEN("SUBJECT \'")); - global.append(acl_user->x509_subject,strlen(acl_user->x509_subject), - system_charset_info); - global.append('\''); - } - if (acl_user->ssl_cipher) - { - if (ssl_options++) - global.append(' '); - global.append(STRING_WITH_LEN("CIPHER '")); - global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher), - system_charset_info); - global.append('\''); - } - } - if ((want_access & GRANT_ACL) || - (acl_user->user_resource.questions || - acl_user->user_resource.updates || - acl_user->user_resource.conn_per_hour || - acl_user->user_resource.user_conn || - acl_user->user_resource.max_statement_time != 0.0)) - { - global.append(STRING_WITH_LEN(" WITH")); - if (want_access & GRANT_ACL) - global.append(STRING_WITH_LEN(" GRANT OPTION")); - add_user_option(&global, acl_user->user_resource.questions, - "MAX_QUERIES_PER_HOUR", false); - add_user_option(&global, acl_user->user_resource.updates, - "MAX_UPDATES_PER_HOUR", false); - add_user_option(&global, acl_user->user_resource.conn_per_hour, - "MAX_CONNECTIONS_PER_HOUR", false); - add_user_option(&global, acl_user->user_resource.user_conn, - "MAX_USER_CONNECTIONS", true); - add_user_option(&global, acl_user->user_resource.max_statement_time, - "MAX_STATEMENT_TIME"); - } - } + add_user_parameters(&global, (ACL_USER *)acl_entry, (want_access & GRANT_ACL)); protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); @@ -8503,71 +9235,6 @@ static int check_role_is_granted_callback(ACL_USER_BASE *grantee, void *data) } /* - Initialize a TABLE_LIST array and open grant tables - - All tables will be opened with the same lock type, either read or write. - - @retval 1 replication filters matched. Abort the operation, but return OK (!) - @retval 0 tables were opened successfully - @retval -1 error, tables could not be opened -*/ - -static int open_grant_tables(THD *thd, TABLE_LIST *tables, - enum thr_lock_type lock_type, int tables_to_open) -{ - DBUG_ENTER("open_grant_tables"); - - /* - We can read privilege tables even when !initialized. - This can be acl_load() - server startup or FLUSH PRIVILEGES - */ - if (lock_type >= TL_WRITE_ALLOW_WRITE && !initialized) - { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); - DBUG_RETURN(-1); - } - - int prev= -1; - bzero(tables, sizeof(TABLE_LIST) * TABLES_MAX); - for (int cur=TABLES_MAX-1, mask= 1 << cur; mask; cur--, mask >>= 1) - { - if ((tables_to_open & mask) == 0) - continue; - tables[cur].init_one_table(C_STRING_WITH_LEN("mysql"), - acl_table_names[cur].str, - acl_table_names[cur].length, - acl_table_names[cur].str, lock_type); - tables[cur].open_type= OT_BASE_ONLY; - if (lock_type >= TL_WRITE_ALLOW_WRITE) - tables[cur].updating= 1; - if (cur >= FIRST_OPTIONAL_TABLE) - tables[cur].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - if (prev != -1) - tables[cur].next_local= tables[cur].next_global= & tables[prev]; - prev= cur; - } - -#ifdef HAVE_REPLICATION - if (lock_type >= TL_WRITE_ALLOW_WRITE && thd->slave_thread && !thd->spcont) - { - /* - GRANT and REVOKE are applied the slave in/exclusion rules as they are - some kind of updates to the mysql.% tables. - */ - Rpl_filter *rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; - if (rpl_filter->is_on() && !rpl_filter->tables_ok(0, tables)) - DBUG_RETURN(1); - } -#endif - - if (open_and_lock_tables(thd, tables + prev, FALSE, - MYSQL_LOCK_IGNORE_TIMEOUT)) - DBUG_RETURN(-1); - - DBUG_RETURN(0); -} - -/* Modify a privilege table. SYNOPSIS @@ -8706,8 +9373,8 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, SYNOPSIS handle_grant_table() - tables The array with the four open tables. - table_no The number of the table to handle (0..4). + grant_table An open grant table handle. + which_table Which grant table to handle. drop If user_from is to be dropped. user_from The the user to be searched/dropped/renamed. user_to The new name for the user if to be renamed, @@ -8725,18 +9392,21 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, > 0 At least one record matched. 0 OK, but no record matched. < 0 Error. + + TODO(cvicentiu) refactor handle_grant_table to use + Grant_table_base instead of TABLE directly. */ -static int handle_grant_table(THD *thd, TABLE_LIST *tables, - enum enum_acl_tables table_no, bool drop, +static int handle_grant_table(THD *thd, const Grant_table_base& grant_table, + enum enum_acl_tables which_table, bool drop, LEX_USER *user_from, LEX_USER *user_to) { int result= 0; int error; - TABLE *table= tables[table_no].table; + TABLE *table= grant_table.table(); Field *host_field= table->field[0]; - Field *user_field= table->field[table_no == USER_TABLE || - table_no == PROXIES_PRIV_TABLE ? 1 : 2]; + Field *user_field= table->field[which_table == USER_TABLE || + which_table == PROXIES_PRIV_TABLE ? 1 : 2]; const char *host_str= user_from->host.str; const char *user_str= user_from->user.str; const char *host; @@ -8745,14 +9415,14 @@ static int handle_grant_table(THD *thd, TABLE_LIST *tables, uint key_prefix_length; DBUG_ENTER("handle_grant_table"); - if (table_no == ROLES_MAPPING_TABLE) + if (which_table == ROLES_MAPPING_TABLE) { result= handle_roles_mappings_table(table, drop, user_from, user_to); DBUG_RETURN(result); } table->use_all_columns(); - if (table_no == USER_TABLE) // mysql.user table + if (which_table == USER_TABLE) // mysql.user table { /* The 'user' table has an unique index on (host, user). @@ -8777,7 +9447,8 @@ static int handle_grant_table(THD *thd, TABLE_LIST *tables, HA_READ_KEY_EXACT); if (!error && !*host_str) { // verify that we got a role or a user, as needed - if (check_is_role(table) != user_from->is_role()) + if (static_cast<const User_table&>(grant_table).check_is_role() != + user_from->is_role()) error= HA_ERR_KEY_NOT_FOUND; } if (error) @@ -8828,7 +9499,7 @@ static int handle_grant_table(THD *thd, TABLE_LIST *tables, user= safe_str(get_field(thd->mem_root, user_field)); #ifdef EXTRA_DEBUG - if (table_no != PROXIES_PRIV_TABLE) + if (which_table != PROXIES_PRIV_TABLE) { DBUG_PRINT("loop",("scan fields: '%s'@'%s' '%s' '%s' '%s'", user, host, @@ -9247,7 +9918,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, < 0 Error. */ -static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, +static int handle_grant_data(THD *thd, Grant_tables& tables, bool drop, LEX_USER *user_from, LEX_USER *user_to) { int result= 0; @@ -9270,7 +9941,8 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle db table. */ - if ((found= handle_grant_table(thd, tables, DB_TABLE, drop, user_from, + if ((found= handle_grant_table(thd, tables.db_table(), + DB_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch the in-memory array. */ @@ -9291,7 +9963,8 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle stored routines table. */ - if ((found= handle_grant_table(thd, tables, PROCS_PRIV_TABLE, drop, + if ((found= handle_grant_table(thd, tables.procs_priv_table(), + PROCS_PRIV_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch in-memory array. */ @@ -9320,7 +9993,8 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle tables table. */ - if ((found= handle_grant_table(thd, tables, TABLES_PRIV_TABLE, drop, + if ((found= handle_grant_table(thd, tables.tables_priv_table(), + TABLES_PRIV_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch columns and in-memory array. */ @@ -9337,7 +10011,8 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle columns table. */ - if ((found= handle_grant_table(thd, tables, COLUMNS_PRIV_TABLE, drop, + if ((found= handle_grant_table(thd, tables.columns_priv_table(), + COLUMNS_PRIV_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch the in-memory array. */ @@ -9355,9 +10030,10 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle proxies_priv table. */ - if (tables[PROXIES_PRIV_TABLE].table) + if (tables.proxies_priv_table().table_exists()) { - if ((found= handle_grant_table(thd, tables, PROXIES_PRIV_TABLE, drop, + if ((found= handle_grant_table(thd, tables.proxies_priv_table(), + PROXIES_PRIV_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch the in-memory array. */ @@ -9375,9 +10051,10 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle roles_mapping table. */ - if (tables[ROLES_MAPPING_TABLE].table) + if (tables.roles_mapping_table().table_exists()) { - if ((found= handle_grant_table(thd, tables, ROLES_MAPPING_TABLE, drop, + if ((found= handle_grant_table(thd, tables.roles_mapping_table(), + ROLES_MAPPING_TABLE, drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch the in-memory array. */ @@ -9395,8 +10072,8 @@ static int handle_grant_data(THD *thd, TABLE_LIST *tables, bool drop, } /* Handle user table. */ - if ((found= handle_grant_table(thd, tables, USER_TABLE, drop, user_from, - user_to)) < 0) + if ((found= handle_grant_table(thd, tables.user_table(), USER_TABLE, + drop, user_from, user_to)) < 0) { /* Handle of table failed, don't touch the in-memory array. */ result= -1; @@ -9435,7 +10112,6 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) String wrong_users; LEX_USER *user_name; List_iterator <LEX_USER> user_list(list); - TABLE_LIST tables[TABLES_MAX]; bool binlog= false; DBUG_ENTER("mysql_create_user"); DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user")); @@ -9444,10 +10120,11 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) DBUG_RETURN(TRUE); /* CREATE USER may be skipped on replication client. */ - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | Table_db | - Table_tables_priv | Table_columns_priv | - Table_procs_priv | Table_proxies_priv | - Table_roles_mapping))) + Grant_tables tables(Table_user | Table_db | + Table_tables_priv | Table_columns_priv | + Table_procs_priv | Table_proxies_priv | + Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); mysql_rwlock_wrlock(&LOCK_grant); @@ -9528,7 +10205,7 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) } } - if (replace_user_table(thd, tables[USER_TABLE].table, *user_name, 0, 0, 1, 0)) + if (replace_user_table(thd, tables.user_table(), *user_name, 0, 0, 1, 0)) { append_user(thd, &wrong_users, user_name); result= TRUE; @@ -9550,7 +10227,9 @@ bool mysql_create_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) if (grantee) add_role_user_mapping(grantee, role); - if (replace_roles_mapping_table(tables[ROLES_MAPPING_TABLE].table, + /* TODO(cvicentiu) refactor replace_roles_mapping_table to use + Roles_mapping_table instead of TABLE directly. */ + if (replace_roles_mapping_table(tables.roles_mapping_table().table(), &thd->lex->definer->user, &thd->lex->definer->host, &user_name->user, true, @@ -9601,17 +10280,17 @@ bool mysql_drop_user(THD *thd, List <LEX_USER> &list, bool handle_as_role) String wrong_users; LEX_USER *user_name, *tmp_user_name; List_iterator <LEX_USER> user_list(list); - TABLE_LIST tables[TABLES_MAX]; bool binlog= false; - ulonglong old_sql_mode= thd->variables.sql_mode; + sql_mode_t old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("mysql_drop_user"); DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user")); /* DROP USER may be skipped on replication client. */ - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | Table_db | - Table_tables_priv | Table_columns_priv | - Table_procs_priv | Table_proxies_priv | - Table_roles_mapping))) + Grant_tables tables(Table_user | Table_db | + Table_tables_priv | Table_columns_priv | + Table_procs_priv | Table_proxies_priv | + Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; @@ -9713,15 +10392,15 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) LEX_USER *user_from, *tmp_user_from; LEX_USER *user_to, *tmp_user_to; List_iterator <LEX_USER> user_list(list); - TABLE_LIST tables[TABLES_MAX]; bool some_users_renamed= FALSE; DBUG_ENTER("mysql_rename_user"); /* RENAME USER may be skipped on replication client. */ - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | Table_db | - Table_tables_priv | Table_columns_priv | - Table_procs_priv | Table_proxies_priv | - Table_roles_mapping))) + Grant_tables tables(Table_user | Table_db | + Table_tables_priv | Table_columns_priv | + Table_procs_priv | Table_proxies_priv | + Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -9783,6 +10462,74 @@ bool mysql_rename_user(THD *thd, List <LEX_USER> &list) DBUG_RETURN(result); } +/* + Alter a user's connection and resource settings. + + SYNOPSIS + mysql_alter_user() + thd The current thread. + list The users to alter. + + RETURN + > 0 Error. Error message already sent. + 0 OK. +*/ +int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) +{ + DBUG_ENTER("mysql_alter_user"); + int result= 0; + String wrong_users; + + /* The only table we're altering is the user table. */ + Grant_tables tables(Table_user, TL_WRITE); + if ((result= tables.open_and_lock(thd))) + DBUG_RETURN(result != 1); + + /* Lock ACL data structures until we finish altering all users. */ + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + + LEX_USER *tmp_lex_user; + List_iterator<LEX_USER> users_list_iterator(users_list); + while ((tmp_lex_user= users_list_iterator++)) + { + LEX_USER* lex_user= get_current_user(thd, tmp_lex_user, false); + if (!lex_user || + fix_lex_user(thd, lex_user) || + replace_user_table(thd, tables.user_table(), *lex_user, 0, + false, false, true)) + { + thd->clear_error(); + append_user(thd, &wrong_users, tmp_lex_user); + result= TRUE; + continue; + } + } + + /* Unlock ACL data structures. */ + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); + + if (result) + { + /* 'if exists' flag leads to warnings instead of errors. */ + if (thd->lex->create_info.if_exists()) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_CANNOT_USER, + ER_THD(thd, ER_CANNOT_USER), + "ALTER USER", wrong_users.c_ptr_safe()); + result= FALSE; + } + else + { + my_error(ER_CANNOT_USER, MYF(0), + "ALTER USER", + wrong_users.c_ptr_safe()); + } + } + DBUG_RETURN(result); +} /* Revoke all privileges from a list of users. @@ -9803,13 +10550,13 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) uint counter, revoked, is_proc; int result; ACL_DB *acl_db; - TABLE_LIST tables[TABLES_MAX]; DBUG_ENTER("mysql_revoke_all"); - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | Table_db | - Table_tables_priv | Table_columns_priv | - Table_procs_priv | Table_proxies_priv | - Table_roles_mapping))) + Grant_tables tables(Table_user | Table_db | + Table_tables_priv | Table_columns_priv | + Table_procs_priv | Table_proxies_priv | + Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -9835,7 +10582,7 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) continue; } - if (replace_user_table(thd, tables[USER_TABLE].table, *lex_user, + if (replace_user_table(thd, tables.user_table(), *lex_user, ~(ulong)0, 1, 0, 0)) { result= -1; @@ -9862,8 +10609,10 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str, user) && !strcmp(lex_user->host.str, host)) { - if (!replace_db_table(tables[DB_TABLE].table, acl_db->db, *lex_user, - ~(ulong)0, 1)) + /* TODO(cvicentiu) refactor replace_db_table to use + Db_table instead of TABLE directly. */ + if (!replace_db_table(tables.db_table().table(), acl_db->db, *lex_user, + ~(ulong)0, 1)) { /* Don't increment counter as replace_db_table deleted the @@ -9892,9 +10641,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) { + /* TODO(cvicentiu) refactor replace_db_table to use + Db_table instead of TABLE directly. */ if (replace_table_table(thd, grant_table, - tables[TABLES_PRIV_TABLE].table, - *lex_user, grant_table->db, + tables.tables_priv_table().table(), + *lex_user, grant_table->db, grant_table->tname, ~(ulong)0, 0, 1)) { result= -1; @@ -9907,8 +10658,10 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) continue; } List<LEX_COLUMN> columns; + /* TODO(cvicentiu) refactor replace_db_table to use + Db_table instead of TABLE directly. */ if (!replace_column_table(grant_table, - tables[COLUMNS_PRIV_TABLE].table, + tables.columns_priv_table().table(), *lex_user, columns, grant_table->db, grant_table->tname, ~(ulong)0, 1)) { @@ -9932,20 +10685,21 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) user= safe_str(grant_proc->user); host= safe_str(grant_proc->host.hostname); - if (!strcmp(lex_user->user.str,user) && + if (!strcmp(lex_user->user.str,user) && !strcmp(lex_user->host.str, host)) - { - if (replace_routine_table(thd, grant_proc, - tables[PROCS_PRIV_TABLE].table, *lex_user, + { + if (replace_routine_table(thd, grant_proc, + tables.procs_priv_table().table(), + *lex_user, grant_proc->db, grant_proc->tname, is_proc, ~(ulong)0, 1) == 0) - { - revoked= 1; - continue; - } - result= -1; // Something went wrong - } - counter++; + { + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; } } while (revoked); @@ -9971,7 +10725,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) ROLE_GRANT_PAIR *pair = find_role_grant_pair(&lex_user->user, &lex_user->host, &role_grant->user); - if (replace_roles_mapping_table(tables[ROLES_MAPPING_TABLE].table, + /* TODO(cvicentiu) refactor replace_roles_mapping_table to use + Roles_mapping_table instead of TABLE directly. */ + if (replace_roles_mapping_table(tables.roles_mapping_table().table(), &lex_user->user, &lex_user->host, &role_grant->user, false, pair, true)) { @@ -10041,7 +10797,7 @@ public: virtual bool handle_condition(THD *thd, uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl); @@ -10056,12 +10812,12 @@ Silence_routine_definer_errors::handle_condition( THD *thd, uint sql_errno, const char*, - Sql_condition::enum_warning_level level, + Sql_condition::enum_warning_level *level, const char* msg, Sql_condition ** cond_hdl) { *cond_hdl= NULL; - if (level == Sql_condition::WARN_LEVEL_ERROR) + if (*level == Sql_condition::WARN_LEVEL_ERROR) { switch (sql_errno) { @@ -10101,15 +10857,15 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, { uint counter, revoked; int result; - TABLE_LIST tables[TABLES_MAX]; HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); - if ((result= open_grant_tables(thd, tables, TL_WRITE, Table_user | Table_db | - Table_tables_priv | Table_columns_priv | - Table_procs_priv | Table_proxies_priv | - Table_roles_mapping))) + Grant_tables tables(Table_user | Table_db | + Table_tables_priv | Table_columns_priv | + Table_procs_priv | Table_proxies_priv | + Table_roles_mapping, TL_WRITE); + if ((result= tables.open_and_lock(thd))) DBUG_RETURN(result != 1); DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row()); @@ -10134,9 +10890,9 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, lex_user.user.length= strlen(grant_proc->user); lex_user.host.str= safe_str(grant_proc->host.hostname); lex_user.host.length= strlen(lex_user.host.str); - if (replace_routine_table(thd, grant_proc, - tables[PROCS_PRIV_TABLE].table, lex_user, - grant_proc->db, grant_proc->tname, + if (replace_routine_table(thd, grant_proc, + tables.procs_priv_table().table(), lex_user, + grant_proc->db, grant_proc->tname, is_proc, ~(ulong)0, 1) == 0) { revoked= 1; @@ -10591,7 +11347,7 @@ int fill_schema_applicable_roles(THD *thd, TABLE_LIST *tables, COND *cond) int wild_case_compare(CHARSET_INFO *cs, const char *str,const char *wildstr) { - reg3 int flag; + int flag; DBUG_ENTER("wild_case_compare"); DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr)); while (*wildstr) @@ -11372,7 +12128,13 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio, data_len= SCRAMBLE_LENGTH; } - end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1; + /* When server version is specified in config file, don't include + the replication hack prefix. */ + if (using_custom_server_version) + end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1; + else + end= strxnmov(end, SERVER_VERSION_LENGTH, RPL_VERSION_HACK, server_version, NullS) + 1; + int4store((uchar*) end, mpvio->auth_info.thd->thread_id); end+= 4; @@ -11392,7 +12154,8 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio, int2store(end+5, thd->client_capabilities >> 16); end[7]= data_len; DBUG_EXECUTE_IF("poison_srv_handshake_scramble_len", end[7]= -100;); - bzero(end + 8, 10); + bzero(end + 8, 6); + int4store(end + 14, thd->client_capabilities >> 32); end+= 18; /* write scramble tail */ end= (char*) memcpy(end, data + SCRAMBLE_LENGTH_323, @@ -11471,7 +12234,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, const char *client_auth_plugin= ((st_mysql_auth *) (plugin_decl(mpvio->plugin)->info))->client_auth_plugin; - DBUG_EXECUTE_IF("auth_disconnect", { vio_close(net->vio); DBUG_RETURN(1); }); + DBUG_EXECUTE_IF("auth_disconnect", { DBUG_RETURN(1); }); DBUG_ASSERT(client_auth_plugin); /* @@ -11605,7 +12368,7 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) static bool read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs) { - size_t length; + ulonglong length; char *ptr_save= *ptr; /* not enough bytes to hold the length */ @@ -11627,10 +12390,10 @@ read_client_connect_attrs(char **ptr, char *end, CHARSET_INFO *from_cs) return true; #ifdef HAVE_PSI_THREAD_INTERFACE - if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, length, from_cs) && + if (PSI_THREAD_CALL(set_thread_connect_attrs)(*ptr, (size_t)length, from_cs) && current_thd->variables.log_warnings) - sql_print_warning("Connection attributes of length %lu were truncated", - (unsigned long) length); + sql_print_warning("Connection attributes of length %llu were truncated", + length); #endif return false; } @@ -11648,7 +12411,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) 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; + uint user_len= (uint)(passwd - user - 1); char *db= passwd; char db_buff[SAFE_NAME_LEN + 1]; // buffer to store db in utf8 char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 @@ -11695,7 +12458,6 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) { if (thd_init_client_charset(thd, uint2korr(next_field))) DBUG_RETURN(1); - thd->update_charset(); next_field+= 2; } @@ -11772,7 +12534,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) { my_message(ER_UNKNOWN_COM_ERROR, ER_THD(thd, ER_UNKNOWN_COM_ERROR), MYF(0)); - DBUG_RETURN(packet_error); + DBUG_RETURN(1); } DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin)); @@ -11809,18 +12571,27 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, */ DBUG_ASSERT(net->read_pos[pkt_len] == 0); - ulong client_capabilities= uint2korr(net->read_pos); + ulonglong client_capabilities= uint2korr(net->read_pos); + compile_time_assert(sizeof(client_capabilities) >= 8); if (client_capabilities & CLIENT_PROTOCOL_41) { - if (pkt_len < 4) + if (pkt_len < 32) return packet_error; client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; + if (!(client_capabilities & CLIENT_MYSQL)) + { + // it is client with mariadb extensions + ulonglong ext_client_capabilities= + (((ulonglong)uint4korr(net->read_pos + 28)) << 32); + client_capabilities|= ext_client_capabilities; + } } /* Disable those bits which are not supported by the client. */ + compile_time_assert(sizeof(thd->client_capabilities) >= 8); thd->client_capabilities&= client_capabilities; - DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); + DBUG_PRINT("info", ("client capabilities: %llu", thd->client_capabilities)); if (thd->client_capabilities & CLIENT_SSL) { unsigned long errptr __attribute__((unused)); @@ -11848,13 +12619,10 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, if (client_capabilities & CLIENT_PROTOCOL_41) { - if (pkt_len < 32) - return packet_error; thd->max_client_packet_length= uint4korr(net->read_pos+4); DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); if (thd_init_client_charset(thd, (uint) net->read_pos[8])) return packet_error; - thd->update_charset(); end= (char*) net->read_pos+32; } else @@ -11882,7 +12650,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, char *user= end; char *passwd= strend(user)+1; - uint user_len= passwd - user - 1, db_len; + uint user_len= (uint)(passwd - user - 1), db_len; char *db= passwd; char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 uint dummy_errors; @@ -11897,15 +12665,22 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, 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; + ulonglong len; + size_t passwd_len; + if (!(thd->client_capabilities & CLIENT_SECURE_CONNECTION)) - passwd_len= strlen(passwd); + len= strlen(passwd); else if (!(thd->client_capabilities & CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA)) - passwd_len= (uchar)(*passwd++); + len= (uchar)(*passwd++); else - passwd_len= safe_net_field_length_ll((uchar**)&passwd, + { + len= safe_net_field_length_ll((uchar**)&passwd, net->read_pos + pkt_len - (uchar*)passwd); - + if (len > pkt_len) + return packet_error; + } + + passwd_len= (size_t)len; db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? db + passwd_len + 1 : 0; @@ -11941,14 +12716,9 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, mostly for backward compatibility (to truncate long usernames, as old 5.1 did) */ - { - CHARSET_INFO *cs= system_charset_info; - int err; - - user_len= (uint) cs->cset->well_formed_len(cs, user, user + user_len, - username_char_length, &err); - user[user_len]= '\0'; - } + user_len= Well_formed_prefix(system_charset_info, user, user_len, + username_char_length).length(); + user[user_len]= '\0'; Security_context *sctx= thd->security_ctx; @@ -12629,7 +13399,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) } DBUG_PRINT("info", - ("Capabilities: %lu packet_length: %ld Host: '%s' " + ("Capabilities: %llu 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, |