sql/auth/dynamic_privilege_table.cc (176 lines of code) (raw):
/* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql_base.h"
#include "records.h"
#include "auth_internal.h"
#include "auth_common.h"
#include "sql_auth_cache.h"
#include "mysqld_error.h"
#include "current_thd.h"
#include "sql_class.h"
#include "dynamic_privilege_table.h"
#include <string>
#define MYSQL_DYNAMIC_PRIV_FIELD_USER 0
#define MYSQL_DYNAMIC_PRIV_FIELD_HOST 1
#define MYSQL_DYNAMIC_PRIV_FIELD_PRIV 2
#define MYSQL_DYNAMIC_PRIV_FIELD_GRANT 3
Dynamic_privilege_register g_dynamic_privilege_register;
/**
This function returns a pointer to a global variable allocated on the heap.
@return A pointer to the dynamic privilege register.
*/
Dynamic_privilege_register *get_dynamic_privilege_register(void)
{
return &g_dynamic_privilege_register;
}
/**
*/
void unregister_all_dynamic_privileges(void)
{
DBUG_ASSERT(assert_acl_cache_write_lock(current_thd));
try {
g_dynamic_privilege_register.clear();
}
catch(...)
{
// ignore
}
}
/**
Given an open table handler this function refresh the list of dynamic
privilege grants by reading the dynamic_privilege table.
If an error is raised, this function will set the DA.
@param thd The thread handle
@param tablelst An handle to an open table
@return Error state
@retval true An error occurred.
@retval false Success
*/
bool populate_dynamic_privilege_caches(THD *thd, TABLE_LIST *tablelst)
{
DBUG_ENTER("populate_dynamic_privilege_caches");
bool error= false;
DBUG_ASSERT(assert_acl_cache_write_lock(thd));
READ_RECORD read_record_info;
if (!tablelst[0].table->key_info)
{
TABLE *table= tablelst[0].table;
my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(true);
}
TABLE *table= tablelst[0].table;
table->use_all_columns();
if (init_read_record(&read_record_info, thd, table,
NULL, 1, 1, FALSE))
{
my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(true);
}
int read_rec_errcode;
MEM_ROOT tmp_mem;
char percentile_character[2]= { '%', '\0' };
/*
We need the the dynamic privilege register in order to register any unknown
privilege identifiers.
*/
SERVICE_TYPE(registry) *r= mysql_plugin_registry_acquire();
{
my_service<SERVICE_TYPE(dynamic_privilege_register)>
service("dynamic_privilege_register.mysql_server", r);
if (!service.is_valid())
{
DBUG_RETURN(true);
}
init_alloc_root(PSI_NOT_INSTRUMENTED, &tmp_mem, 256, 0);
while (!error &&
!(read_rec_errcode= read_record_info.read_record(&read_record_info)))
{
char *host= get_field(&tmp_mem,
table->field[MYSQL_DYNAMIC_PRIV_FIELD_HOST]);
if (host == 0)
host= &percentile_character[0];
char *user= get_field(&tmp_mem,
table->field[MYSQL_DYNAMIC_PRIV_FIELD_USER]);
char *priv= get_field(&tmp_mem,
table->field[MYSQL_DYNAMIC_PRIV_FIELD_PRIV]);
char *with_grant_option=
get_field(&tmp_mem,
table->field[MYSQL_DYNAMIC_PRIV_FIELD_GRANT]);
my_caseup_str(system_charset_info, priv);
LEX_CSTRING str_priv= { priv, strlen(priv) };
LEX_CSTRING str_user= { user, strlen(user) };
LEX_CSTRING str_host= { host, strlen(host) };
Update_dynamic_privilege_table no_update;
if (grant_dynamic_privilege(str_priv, str_user, str_host,
(*with_grant_option == 'Y' ? true : false),
no_update))
{
/*
This privilege ID hasn't been registered yet. It can happen when a
previously grant has been given but the plugin or component which owns
the privilege ID isn't loaded yet.
The policy is that any privilege ID that exist in mysql.global_grants
is a valid privilege ID.
*/
if (service->register_privilege(str_priv.str, str_priv.length) ||
grant_dynamic_privilege(str_priv, str_user, str_host,
(*with_grant_option == 'Y' ? true : false),
no_update))
{
/*
Only if we fail a second time we assume that the error was critical
and operation have to be aborted.
We don't return immediately here because we need to release the
registry first.
*/
error= true;
}
}
}
end_read_record(&read_record_info);
/*
To avoid any issues with inconsistencies we unconditionally increase
acl cache version here.
*/
get_global_acl_cache()->increase_version();
} // exit scope
mysql_plugin_registry_release(r);
free_root(&tmp_mem, MYF(0));
DBUG_RETURN(error);
}
/**
Delete or insert a row in the mysql.dynamic_privilege table.
@param thd Thread handler
@param table The opened table to be modified
@param auth_id Target authorization ID
@param privilege Privilege object ID
@param with_grant_option Flag indicating if the grant option is set
@param delete_option Flag indicating if this is an insert or delete
If an error has occurred the DA is not set.
@see Update_dynamic_privilege_table
@return Error state
@retval true An error occurred
@retval false Success
*/
bool modify_dynamic_privileges_in_table(THD *thd, TABLE *table,
const Auth_id_ref &auth_id,
const LEX_CSTRING &privilege,
bool with_grant_option,
bool delete_option)
{
DBUG_ENTER("modify_dynamic_privileges_in_table");
int ret= 0;
uchar user_key[MAX_KEY_LENGTH];
if (!table->key_info)
{
my_error(ER_TABLE_CORRUPT, MYF(0), table->s->db.str,
table->s->table_name.str);
DBUG_RETURN(true);
}
table->use_all_columns();
table->field[MYSQL_DYNAMIC_PRIV_FIELD_HOST]
->store(auth_id.second.str, auth_id.second.length,
system_charset_info);
table->field[MYSQL_DYNAMIC_PRIV_FIELD_USER]
->store(auth_id.first.str, auth_id.first.length,
system_charset_info);
table->field[MYSQL_DYNAMIC_PRIV_FIELD_PRIV]
->store(privilege.str, privilege.length,
system_charset_info);
key_copy(user_key, table->record[0], table->key_info,
table->key_info->key_length);
table->field[MYSQL_DYNAMIC_PRIV_FIELD_GRANT]
->store((with_grant_option == true ? "Y" : "N"), 1, system_charset_info);
ret= table->file->ha_index_read_idx_map(table->record[0], 0, user_key,
HA_WHOLE_KEY,
HA_READ_KEY_EXACT);
if (delete_option)
{
if (ret == 0)
{
DBUG_PRINT("note",("Delete dynamic privilege %s for `%s`@`%s`",
privilege.str, auth_id.first.str,
auth_id.second.str));
ret= table->file->ha_delete_row(table->record[0]);
}
else if (ret == HA_ERR_KEY_NOT_FOUND)
{
/* If the key didn't exist the record is already gone and all is well. */
DBUG_RETURN(false);
}
}
else if (ret == HA_ERR_KEY_NOT_FOUND && !delete_option)
{
/* Insert new edge into table */
DBUG_PRINT("note",("Insert dynamic privilege %s for `%s`@`%s` %s",
privilege.str, auth_id.first.str, auth_id.second.str,
(with_grant_option == true ? "WITH GRANT OPTION" : "")));
ret= table->file->ha_write_row(table->record[0]);
}
DBUG_RETURN(ret != 0);
}
bool iterate_all_dynamic_privileges(THD *thd,
std::function<bool (const char*)> action)
{
Acl_cache_lock_guard acl_cache_lock(thd,
Acl_cache_lock_mode::READ_MODE);
if (!acl_cache_lock.lock())
return true;
Dynamic_privilege_register::iterator it=
get_dynamic_privilege_register()->begin();
Dynamic_privilege_register::iterator end_it=
get_dynamic_privilege_register()->end();
while (it != end_it)
{
if (action(it->c_str()))
return true;
++it;
}
return false;
}