summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPraveenkumar Hulakund <praveenkumar.hulakund@oracle.com>2014-09-10 10:50:17 +0530
committerPraveenkumar Hulakund <praveenkumar.hulakund@oracle.com>2014-09-10 10:50:17 +0530
commitcf4231a7f9760e62f93a323300488ff74e23b530 (patch)
tree30cf43b20d190a1bb22d76c54f0792dff5ce877f
parent958695b144655d84d405bec3d0bb769a2a84a056 (diff)
downloadmariadb-git-cf4231a7f9760e62f93a323300488ff74e23b530.tar.gz
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.
-rw-r--r--sql/handler.h1
-rw-r--r--sql/sql_parse.cc120
-rw-r--r--sql/sql_parse.h3
-rw-r--r--sql/sql_table.cc12
-rw-r--r--storage/innobase/handler/ha_innodb.cc2
5 files changed, 136 insertions, 2 deletions
diff --git a/sql/handler.h b/sql/handler.h
index 5eb2d6b440a..17306fe7dd4 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -868,6 +868,7 @@ struct handlerton
#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_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;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ea63d23d182..f52f56447f4 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -5251,6 +5251,121 @@ bool check_global_access(THD *thd, ulong want_access)
#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
****************************************************************************/
@@ -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))
goto err;
}
- error= FALSE;
+ if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info))
+ goto err;
+
+ error= FALSE;
err:
DBUG_RETURN(error);
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 9a55174b0fb..f7b4d530543 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -45,6 +45,9 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables);
bool insert_precheck(THD *thd, TABLE_LIST *tables);
bool create_table_precheck(THD *thd, TABLE_LIST *tables,
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,
Parser_state *parser_state,
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 8b4873cb834..e8de6a6928a 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -6005,6 +6005,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
/*
+ 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
the table's row type.
Note : this is the same as if the row type was specified explicitly.
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index adedc4fa961..8f299aed213 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -2251,7 +2251,7 @@ innobase_init(
innobase_hton->start_consistent_snapshot=innobase_start_trx_and_assign_read_view;
innobase_hton->flush_logs=innobase_flush_logs;
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->alter_table_flags = innobase_alter_table_flags;