diff options
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r-- | sql/sql_acl.cc | 258 |
1 files changed, 181 insertions, 77 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 723d825de17..cb7e35fae09 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2011, Monty Program Ab + Copyright (c) 2009, 2013, Monty Program Ab This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -50,6 +50,7 @@ #include "sql_connect.h" #include "hostname.h" #include "sql_db.h" +#include "sql_array.h" bool mysql_user_table_is_in_short_password_format= false; @@ -560,6 +561,18 @@ static bool update_user_table(THD *thd, TABLE *table, const char *host, static my_bool acl_load(THD *thd, TABLE_LIST *tables); static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); +/* + Enumeration of various ACL's and Hashes used in handle_grant_struct() +*/ +enum enum_acl_lists +{ + USER_ACL= 0, + DB_ACL, + COLUMN_PRIVILEGES_HASH, + PROC_PRIVILEGES_HASH, + FUNC_PRIVILEGES_HASH, + PROXY_USERS_ACL +}; /* Convert scrambled password to binary form, according to scramble type, @@ -688,7 +701,7 @@ my_bool acl_init(bool dont_read_acl_tables) return_val= acl_reload(thd); delete thd; /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); + set_current_thd(0); DBUG_RETURN(return_val); } @@ -749,8 +762,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) acl_cache->clear(1); // Clear locked hostname cache - init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0); - (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50); + init_sql_alloc(&mem, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); + (void) my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST), 20, 50, MYF(0)); if (tables[0].table) // "host" table may not exist (e.g. in MySQL 5.6.7+) { if (init_read_record(&read_record_info, thd, table= tables[0].table, @@ -768,7 +781,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) convert db to lower case and give a warning if the db wasn't already in lower case */ - (void) strmov(tmp_name, host.db); + char *end = strnmov(tmp_name, host.db, sizeof(tmp_name)); + if (end >= tmp_name + sizeof(tmp_name)) + { + sql_print_warning(ER(ER_WRONG_DB_NAME), host.db); + continue; + } my_casedn_str(files_charset_info, host.db); if (strcmp(host.db, tmp_name) != 0) sql_print_warning("'host' entry '%s|%s' had database in mixed " @@ -808,7 +826,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) NULL, 1, 1, FALSE)) goto end; table->use_all_columns(); - (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER),50,100); + (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; if (password_length < SCRAMBLED_PASSWORD_CHAR_LENGTH_323) @@ -1009,7 +1027,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) NULL, 1, 1, FALSE)) goto end; table->use_all_columns(); - (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB),50,100); + (void) my_init_dynamic_array(&acl_dbs,sizeof(ACL_DB), 50, 100, MYF(0)); while (!(read_record_info.read_record(&read_record_info))) { ACL_DB db; @@ -1038,7 +1056,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) convert db to lower case and give a warning if the db wasn't already in lower case */ - (void)strmov(tmp_name, db.db); + char *end = strnmov(tmp_name, db.db, sizeof(tmp_name)); + if (end >= tmp_name + sizeof(tmp_name)) + { + sql_print_warning(ER(ER_WRONG_DB_NAME), db.db); + continue; + } my_casedn_str(files_charset_info, db.db); if (strcmp(db.db, tmp_name) != 0) { @@ -1067,7 +1090,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) freeze_size(&acl_dbs); (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), - 50, 100); + 50, 100, MYF(0)); if (tables[3].table) { if (init_read_record(&read_record_info, thd, table= tables[3].table, @@ -1723,7 +1746,7 @@ static void init_check_host(void) { DBUG_ENTER("init_check_host"); (void) my_init_dynamic_array(&acl_wild_hosts,sizeof(struct acl_host_and_ip), - acl_users.elements,1); + acl_users.elements, 1, MYF(0)); (void) my_hash_init(&acl_check_hosts,system_charset_info, acl_users.elements, 0, 0, (my_hash_get_key) check_get_key, 0, 0); @@ -2699,7 +2722,13 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user, get_grantor(thd, grantor); - table->file->ha_index_init(0, 1); + if ((error= table->file->ha_index_init(0, 1))) + { + table->file->print_error(error, MYF(0)); + DBUG_PRINT("info", ("ha_index_init error")); + DBUG_RETURN(-1); + } + if (table->file->ha_index_read_map(table->record[0], user_key, HA_WHOLE_KEY, HA_READ_KEY_EXACT)) @@ -2941,7 +2970,12 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) key_copy(key, col_privs->record[0], col_privs->key_info, key_prefix_len); col_privs->field[4]->store("",0, &my_charset_latin1); - col_privs->file->ha_index_init(0, 1); + if (col_privs->file->ha_index_init(0, 1)) + { + cols= 0; + return; + } + if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key, (key_part_map)15, HA_READ_KEY_EXACT)) @@ -3005,15 +3039,23 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, const char *user, const char *tname, bool exact, bool name_tolower) { - char helping [SAFE_NAME_LEN*2+USERNAME_LENGTH+3], *name_ptr; + char helping[SAFE_NAME_LEN*2+USERNAME_LENGTH+3]; + char *hend = helping + sizeof(helping); uint len; GRANT_NAME *grant_name,*found=0; HASH_SEARCH_STATE state; - name_ptr= strmov(strmov(helping, user) + 1, db) + 1; - len = (uint) (strmov(name_ptr, tname) - helping) + 1; + char *db_ptr= strmov(helping, user) + 1; + char *tname_ptr= strnmov(db_ptr, db, hend - db_ptr) + 1; + if (tname_ptr > hend) + return 0; // invalid name = not found + char *end= strnmov(tname_ptr, tname, hend - tname_ptr) + 1; + if (end > hend) + return 0; // invalid name = not found + + len = (uint) (end - helping); if (name_tolower) - my_casedn_str(files_charset_info, name_ptr); + my_casedn_str(files_charset_info, tname_ptr); for (grant_name= (GRANT_NAME*) my_hash_first(name_hash, (uchar*) helping, len, &state); grant_name ; @@ -3073,7 +3115,7 @@ static int replace_column_table(GRANT_TABLE *g_t, const char *db, const char *table_name, ulong rights, bool revoke_grant) { - int error=0,result=0; + int result=0; uchar key[MAX_KEY_LENGTH]; uint key_prefix_length; KEY_PART_INFO *key_part= table->key_info->key_part; @@ -3100,7 +3142,13 @@ static int replace_column_table(GRANT_TABLE *g_t, List_iterator <LEX_COLUMN> iter(columns); class LEX_COLUMN *column; - table->file->ha_index_init(0, 1); + int error= table->file->ha_index_init(0, 1); + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(-1); + } + while ((column= iter++)) { ulong privileges= column->rights; @@ -3970,7 +4018,12 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &list, if (lower_case_table_names && db) { - strmov(tmp_db,db); + char *end= strnmov(tmp_db,db, sizeof(tmp_db)); + if (end >= tmp_db + sizeof(tmp_db)) + { + my_error(ER_WRONG_DB_NAME ,MYF(0), db); + DBUG_RETURN(TRUE); + } my_casedn_str(files_charset_info, tmp_db); db=tmp_db; } @@ -4123,7 +4176,7 @@ my_bool grant_init() return_val= grant_reload(thd); delete thd; /* Remember that we don't have a THD */ - my_pthread_setspecific_ptr(THR_THD, 0); + set_current_thd(0); DBUG_RETURN(return_val); } @@ -4157,7 +4210,10 @@ static my_bool grant_load_procs_priv(TABLE *p_table) (void) my_hash_init(&func_priv_hash, &my_charset_utf8_bin, 0,0,0, (my_hash_get_key) get_grant_table, 0,0); - p_table->file->ha_index_init(0, 1); + + if (p_table->file->ha_index_init(0, 1)) + DBUG_RETURN(TRUE); + p_table->use_all_columns(); if (!p_table->file->ha_index_first(p_table->record[0])) @@ -4258,7 +4314,10 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) t_table = tables[0].table; c_table = tables[1].table; - t_table->file->ha_index_init(0, 1); + + if (t_table->file->ha_index_init(0, 1)) + goto end_index_init; + t_table->use_all_columns(); c_table->use_all_columns(); @@ -4303,9 +4362,10 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) return_val=0; // Return ok end_unlock: - thd->variables.sql_mode= old_sql_mode; t_table->file->ha_index_end(); my_pthread_setspecific_ptr(THR_MALLOC, save_mem_root_ptr); +end_index_init: + thd->variables.sql_mode= old_sql_mode; DBUG_RETURN(return_val); } @@ -4414,7 +4474,7 @@ my_bool grant_reload(THD *thd) opertion possible in case of failure. */ old_mem= memex; - init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); + init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); if ((return_val= grant_load(thd, tables))) { // Error. Revert to old hash @@ -5994,20 +6054,19 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, Delete from grant structure if drop is true. Update in grant structure if drop is false and user_to is not NULL. Search in grant structure if drop is false and user_to is NULL. - Structures are numbered as follows: - 0 acl_users - 1 acl_dbs - 2 column_priv_hash - 3 proc_priv_hash - 4 func_priv_hash - 5 acl_proxy_users + Structures are enumerated as follows: + 0 ACL_USER + 1 ACL_DB + 2 COLUMN_PRIVILEGES_HASH + 3 PROC_PRIVILEGES_HASH + 4 FUNC_PRIVILEGES_HASH + 5 PROXY_USERS_ACL @retval > 0 At least one element matched. @retval 0 OK, but no element matched. - @retval -1 Wrong arguments to function. */ -static int handle_grant_struct(uint struct_no, bool drop, +static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, LEX_USER *user_from, LEX_USER *user_to) { int result= 0; @@ -6031,28 +6090,29 @@ static int handle_grant_struct(uint struct_no, bool drop, /* Get the number of elements in the in-memory structure. */ switch (struct_no) { - case 0: + case USER_ACL: elements= acl_users.elements; break; - case 1: + case DB_ACL: elements= acl_dbs.elements; break; - case 2: + case COLUMN_PRIVILEGES_HASH: grant_name_hash= &column_priv_hash; elements= grant_name_hash->records; break; - case 3: + case PROC_PRIVILEGES_HASH: grant_name_hash= &proc_priv_hash; elements= grant_name_hash->records; break; - case 4: + case FUNC_PRIVILEGES_HASH: grant_name_hash= &func_priv_hash; elements= grant_name_hash->records; break; - case 5: + case PROXY_USERS_ACL: elements= acl_proxy_users.elements; break; default: + DBUG_ASSERT(0); return -1; } @@ -6067,27 +6127,27 @@ static int handle_grant_struct(uint struct_no, bool drop, Get a pointer to the element. */ switch (struct_no) { - case 0: + case USER_ACL: acl_user= dynamic_element(&acl_users, idx, ACL_USER*); user= acl_user->user; host= acl_user->host.hostname; break; - case 1: + case DB_ACL: acl_db= dynamic_element(&acl_dbs, idx, ACL_DB*); user= acl_db->user; host= acl_db->host.hostname; break; - case 2: - case 3: - case 4: + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: grant_name= (GRANT_NAME*) my_hash_element(grant_name_hash, idx); user= grant_name->user; host= grant_name->host.hostname; break; - case 5: + case PROXY_USERS_ACL: acl_proxy_user= dynamic_element(&acl_proxy_users, idx, ACL_PROXY_USER*); user= acl_proxy_user->get_user(); host= acl_proxy_user->get_host(); @@ -6113,21 +6173,21 @@ static int handle_grant_struct(uint struct_no, bool drop, if ( drop ) { switch ( struct_no ) { - case 0: + case USER_ACL: delete_dynamic_element(&acl_users, idx); break; - case 1: + case DB_ACL: delete_dynamic_element(&acl_dbs, idx); break; - case 2: - case 3: - case 4: + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: my_hash_delete(grant_name_hash, (uchar*) grant_name); break; - case 5: + case PROXY_USERS_ACL: delete_dynamic_element(&acl_proxy_users, idx); break; @@ -6151,19 +6211,19 @@ static int handle_grant_struct(uint struct_no, bool drop, else if ( user_to ) { switch ( struct_no ) { - case 0: + case USER_ACL: acl_user->user= strdup_root(&mem, user_to->user.str); acl_user->host.hostname= strdup_root(&mem, user_to->host.str); break; - case 1: + case DB_ACL: acl_db->user= strdup_root(&mem, user_to->user.str); acl_db->host.hostname= strdup_root(&mem, user_to->host.str); break; - case 2: - case 3: - case 4: + case COLUMN_PRIVILEGES_HASH: + case PROC_PRIVILEGES_HASH: + case FUNC_PRIVILEGES_HASH: { /* Save old hash key and its length to be able properly update @@ -6199,11 +6259,10 @@ static int handle_grant_struct(uint struct_no, bool drop, break; } - case 5: + case PROXY_USERS_ACL: acl_proxy_user->set_user (&mem, user_to->user.str); acl_proxy_user->set_host (&mem, user_to->host.str); break; - } } else @@ -6260,7 +6319,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle user array. */ - if ((handle_grant_struct(0, drop, user_from, user_to)) || found) + if ((handle_grant_struct(USER_ACL, drop, user_from, user_to)) || found) { result= 1; /* At least one record/element found. */ /* If search is requested, we do not need to search further. */ @@ -6278,7 +6337,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle db array. */ - if (((handle_grant_struct(1, drop, user_from, user_to) && ! result) || + if (((handle_grant_struct(DB_ACL, drop, user_from, user_to) && ! result) || found) && ! result) { result= 1; /* At least one record/element found. */ @@ -6297,7 +6356,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle procs array. */ - if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) || + if (((handle_grant_struct(PROC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) || found) && ! result) { result= 1; /* At least one record/element found. */ @@ -6306,7 +6365,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, goto end; } /* Handle funcs array. */ - if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) || + if (((handle_grant_struct(FUNC_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) || found) && ! result) { result= 1; /* At least one record/element found. */ @@ -6341,7 +6400,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle columns hash. */ - if (((handle_grant_struct(2, drop, user_from, user_to) && ! result) || + if (((handle_grant_struct(COLUMN_PRIVILEGES_HASH, drop, user_from, user_to) && ! result) || found) && ! result) result= 1; /* At least one record/element found. */ } @@ -6358,7 +6417,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, else { /* Handle proxies_priv array. */ - if ((handle_grant_struct(5, drop, user_from, user_to) && !result) || + if ((handle_grant_struct(PROXY_USERS_ACL, drop, user_from, user_to) && !result) || found) result= 1; /* At least one record/element found. */ } @@ -7086,14 +7145,25 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, DBUG_RETURN(FALSE); } - /* one can grant proxy to himself to others */ - if (!strcmp(thd->security_ctx->user, user) && + /* + one can grant proxy for self to others. + Security context in THD contains two pairs of (user,host): + 1. (user,host) pair referring to inbound connection. + 2. (priv_user,priv_host) pair obtained from mysql.user table after doing + authnetication of incoming connection. + Privileges should be checked wrt (priv_user, priv_host) tuple, because + (user,host) pair obtained from inbound connection may have different + values than what is actually stored in mysql.user table and while granting + or revoking proxy privilege, user is expected to provide entries mentioned + in mysql.user table. + */ + if (!strcmp(thd->security_ctx->priv_user, user) && !my_strcasecmp(system_charset_info, host, - thd->security_ctx->host)) + thd->security_ctx->priv_host)) { DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", - thd->security_ctx->user, user, - host, thd->security_ctx->host)); + thd->security_ctx->priv_user, user, + host, thd->security_ctx->priv_host)); DBUG_RETURN(FALSE); } @@ -7759,6 +7829,7 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO } cached_server_packet; int packets_read, packets_written; ///< counters for send/received packets uint connect_errors; ///< if there were connect errors for this host + bool make_it_fail; /** when plugin returns a failure this tells us what really happened */ enum { SUCCESS, FAILURE, RESTART } status; }; @@ -8024,14 +8095,14 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, /** Finds acl entry in user database for authentication purposes. - Finds a user and copies it into mpvio. Reports an authentication - failure if a user is not found. + Finds a user and copies it into mpvio. Creates a fake user + if no matching user account is found. @note find_acl_user is not the same, because it doesn't take into account the case when user is not empty, but acl_user->user is empty @retval 0 found - @retval 1 not found + @retval 1 error */ static bool find_mpvio_user(MPVIO_EXT *mpvio) { @@ -8054,8 +8125,27 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) if (!mpvio->acl_user) { - login_failed_error(mpvio->thd); - DBUG_RETURN (1); + /* + A matching user was not found. Fake it. Take any user, make the + authentication fail later. + This way we get a realistically looking failure, with occasional + "change auth plugin" requests even for nonexistent users. The ratio + of "change auth plugin" request will be the same for real and + nonexistent users. + Note, that we cannot pick any user at random, it must always be + the same user account for the incoming sctx->user name. + */ + ulong nr1=1, nr2=4; + CHARSET_INFO *cs= &my_charset_latin1; + cs->coll->hash_sort(cs, (uchar*) sctx->user, strlen(sctx->user), &nr1, &nr2); + + mysql_mutex_lock(&acl_cache->lock); + uint i= nr1 % acl_users.elements; + ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); + mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); + mysql_mutex_unlock(&acl_cache->lock); + + mpvio->make_it_fail= true; } /* user account requires non-default plugin and the client is too old */ @@ -8182,6 +8272,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) } #ifndef NO_EMBEDDED_ACCESS_CHECKS + thd->password= passwd_len > 0; if (find_mpvio_user(mpvio)) DBUG_RETURN(1); @@ -8477,8 +8568,8 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, mpvio->cached_server_packet.pkt_len)) return packet_error; - passwd_len= my_net_read(&mpvio->thd->net); - passwd= (char*)mpvio->thd->net.read_pos; + passwd_len= my_net_read(&thd->net); + passwd= (char*)thd->net.read_pos; } *buff= (uchar*) passwd; @@ -8580,6 +8671,10 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf) *buf= (uchar*) mpvio->cached_client_reply.pkt; mpvio->cached_client_reply.pkt= 0; mpvio->packets_read++; + + if (mpvio->make_it_fail) + goto err; + DBUG_RETURN ((int) mpvio->cached_client_reply.pkt_len); } @@ -8614,6 +8709,9 @@ static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf) else *buf= mpvio->thd->net.read_pos; + if (mpvio->make_it_fail) + goto err; + DBUG_RETURN((int)pkt_len); err: @@ -8621,7 +8719,12 @@ err: { inc_host_errors(mpvio->thd->security_ctx->ip); if (!mpvio->thd->is_error()) - my_error(ER_HANDSHAKE_ERROR, MYF(0)); + { + if (mpvio->make_it_fail) + login_failed_error(mpvio->thd); + else + my_error(ER_HANDSHAKE_ERROR, MYF(0)); + } } DBUG_RETURN(-1); } @@ -8826,6 +8929,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, mpvio.thd= thd; mpvio.connect_errors= connect_errors; mpvio.status= MPVIO_EXT::FAILURE; + mpvio.make_it_fail= false; mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip; mpvio.auth_info.host_or_ip_length= (unsigned int) strlen(thd->security_ctx->host_or_ip); |