Commit cf4231a7 authored by Praveenkumar Hulakund's avatar Praveenkumar Hulakund

Bug#18790730 - CROSS-DATABASE FOREIGN KEY WITHOUT PERMISSIONS

               CHECK.

Analysis:
----------
Issue here is, while creating or altering the InnoDB table,
if the foreign key defined on the table references a parent
table on which the user has no access privileges then the
table is created without reporting any error. 

Currently the privilege level REFERENCES_ACL is unused
and is not used for access evaluation while creating the
table with a foreign key constraint or adding the foreign
key constraint to a table. But when no privileges are granted
to user then also access evaluation on parent table is ignored.

Fix:
---------
For DMLs, irrelevant of the fact, support does not want any
changes to avoid permission checks on every operation.

So, as a fix, added a function "check_fk_parent_table_access" 
to check whether any of the SELECT_ACL, INSERT_ACL, UDPATE_ACL,
DELETE_ACL or REFERENCE_ACL privileges are granted for user
at table level. If none of them is granted then error is reported.
This function is called during the table creation and alter 
operation. 
parent 958695b1
...@@ -868,6 +868,7 @@ struct handlerton ...@@ -868,6 +868,7 @@ struct handlerton
#define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported #define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported
#define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables #define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables
#define HTON_NO_PARTITION (1 << 8) //You can not partition these tables #define HTON_NO_PARTITION (1 << 8) //You can not partition these tables
#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 9) //Foreign key constraint supported.
class Ha_trx_info; class Ha_trx_info;
......
...@@ -5251,6 +5251,121 @@ bool check_global_access(THD *thd, ulong want_access) ...@@ -5251,6 +5251,121 @@ bool check_global_access(THD *thd, ulong want_access)
#endif #endif
} }
/**
Checks foreign key's parent table access.
@param thd [in] Thread handler
@param create_info [in] Create information (like MAX_ROWS, ENGINE or
temporary table flag)
@param alter_info [in] Initial list of columns and indexes for the
table to be created
@retval
false ok.
@retval
true error or access denied. Error is sent to client in this case.
*/
bool check_fk_parent_table_access(THD *thd,
HA_CREATE_INFO *create_info,
Alter_info *alter_info)
{
Key *key;
List_iterator<Key> key_iterator(alter_info->key_list);
handlerton *db_type= create_info->db_type ? create_info->db_type :
ha_default_handlerton(thd);
// Return if engine does not support Foreign key Constraint.
if (!ha_check_storage_engine_flag(db_type, HTON_SUPPORTS_FOREIGN_KEYS))
return false;
while ((key= key_iterator++))
{
if (key->type == Key::FOREIGN_KEY)
{
TABLE_LIST parent_table;
bool is_qualified_table_name;
Foreign_key *fk_key= (Foreign_key *)key;
LEX_STRING db_name;
LEX_STRING table_name= { fk_key->ref_table->table.str,
fk_key->ref_table->table.length };
const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL |
DELETE_ACL | REFERENCES_ACL);
// Check if tablename is valid or not.
DBUG_ASSERT(table_name.str != NULL);
if (check_table_name(table_name.str, table_name.length, false))
{
my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str);
return true;
}
if (fk_key->ref_table->db.str)
{
is_qualified_table_name= true;
db_name.str= (char *) thd->memdup(fk_key->ref_table->db.str,
fk_key->ref_table->db.length+1);
db_name.length= fk_key->ref_table->db.length;
// Check if database name is valid or not.
if (fk_key->ref_table->db.str && check_db_name(&db_name))
{
my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
return true;
}
}
else if (thd->lex->copy_db_to(&db_name.str, &db_name.length))
return true;
else
is_qualified_table_name= false;
// if lower_case_table_names is set then convert tablename to lower case.
if (lower_case_table_names)
{
table_name.str= (char *) thd->memdup(fk_key->ref_table->table.str,
fk_key->ref_table->table.length+1);
table_name.length= my_casedn_str(files_charset_info, table_name.str);
}
parent_table.init_one_table(db_name.str, db_name.length,
table_name.str, table_name.length,
table_name.str, TL_IGNORE);
/*
Check if user has any of the "privileges" at table level on
"parent_table".
Having privilege on any of the parent_table column is not
enough so checking whether user has any of the "privileges"
at table level only here.
*/
if (check_some_access(thd, privileges, &parent_table) ||
parent_table.grant.want_privilege)
{
if (is_qualified_table_name)
{
const size_t qualified_table_name_len= NAME_LEN + 1 + NAME_LEN + 1;
char *qualified_table_name= (char *) thd->alloc(qualified_table_name_len);
my_snprintf(qualified_table_name, qualified_table_name_len, "%s.%s",
db_name.str, table_name.str);
table_name.str= qualified_table_name;
}
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0),
"REFERENCES",
thd->security_ctx->priv_user,
thd->security_ctx->host_or_ip,
table_name.str);
return true;
}
}
}
return false;
}
/**************************************************************************** /****************************************************************************
Check stack size; Send error if there isn't enough stack to continue Check stack size; Send error if there isn't enough stack to continue
****************************************************************************/ ****************************************************************************/
...@@ -7072,8 +7187,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, ...@@ -7072,8 +7187,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE))
goto err; goto err;
} }
error= FALSE;
if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info))
goto err;
error= FALSE;
err: err:
DBUG_RETURN(error); DBUG_RETURN(error);
} }
......
...@@ -45,6 +45,9 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables); ...@@ -45,6 +45,9 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables);
bool insert_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables);
bool create_table_precheck(THD *thd, TABLE_LIST *tables, bool create_table_precheck(THD *thd, TABLE_LIST *tables,
TABLE_LIST *create_table); TABLE_LIST *create_table);
bool check_fk_parent_table_access(THD *thd,
HA_CREATE_INFO *create_info,
Alter_info *alter_info);
bool parse_sql(THD *thd, bool parse_sql(THD *thd,
Parser_state *parser_state, Parser_state *parser_state,
......
...@@ -6004,6 +6004,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6004,6 +6004,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err; goto err;
} }
/*
If foreign key is added then check permission to access parent table.
In function "check_fk_parent_table_access", create_info->db_type is used
to identify whether engine supports FK constraint or not. Since
create_info->db_type is set here, check to parent table access is delayed
till this point for the alter operation.
*/
if ((alter_info->flags & ALTER_FOREIGN_KEY) &&
check_fk_parent_table_access(thd, create_info, alter_info))
goto err;
/* /*
If this is an ALTER TABLE and no explicit row type specified reuse If this is an ALTER TABLE and no explicit row type specified reuse
the table's row type. the table's row type.
......
...@@ -2251,7 +2251,7 @@ innobase_init( ...@@ -2251,7 +2251,7 @@ innobase_init(
innobase_hton->start_consistent_snapshot=innobase_start_trx_and_assign_read_view; innobase_hton->start_consistent_snapshot=innobase_start_trx_and_assign_read_view;
innobase_hton->flush_logs=innobase_flush_logs; innobase_hton->flush_logs=innobase_flush_logs;
innobase_hton->show_status=innobase_show_status; innobase_hton->show_status=innobase_show_status;
innobase_hton->flags=HTON_NO_FLAGS; innobase_hton->flags=HTON_SUPPORTS_FOREIGN_KEYS;
innobase_hton->release_temporary_latches=innobase_release_temporary_latches; innobase_hton->release_temporary_latches=innobase_release_temporary_latches;
innobase_hton->alter_table_flags = innobase_alter_table_flags; innobase_hton->alter_table_flags = innobase_alter_table_flags;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment