diff options
author | Praveenkumar Hulakund <praveenkumar.hulakund@oracle.com> | 2014-09-10 10:50:17 +0530 |
---|---|---|
committer | Praveenkumar Hulakund <praveenkumar.hulakund@oracle.com> | 2014-09-10 10:50:17 +0530 |
commit | cf4231a7f9760e62f93a323300488ff74e23b530 (patch) | |
tree | 30cf43b20d190a1bb22d76c54f0792dff5ce877f /sql | |
parent | 958695b144655d84d405bec3d0bb769a2a84a056 (diff) | |
download | mariadb-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.
Diffstat (limited to 'sql')
-rw-r--r-- | sql/handler.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 120 | ||||
-rw-r--r-- | sql/sql_parse.h | 3 | ||||
-rw-r--r-- | sql/sql_table.cc | 12 |
4 files changed, 135 insertions, 1 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. |