From: Martin Hansson Date: August 27 2009 5:35pm Subject: bzr commit into mysql-5.1-bugteam branch (martin.hansson:2936) Bug#35996 List-Archive: http://lists.mysql.com/commits/81772 X-Bug: 35996 Message-Id: <200908271736.n7RHagvm017254@riff-raff> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============1702410798==" --===============1702410798== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///data0/martin/bzr/bug35996/5.1bt-gca-commmit/ based on revid:davi.arnaut@stripped 2936 Martin Hansson 2009-08-27 Bug#35996: Security Breach In Smashed TEMPTABLE Views There were no errors displayed when issuing a SHOW CREATE VIEW for views that reference base tables for which the user did not have sufficient privileges to see the table structure. If the view referenced a view with the same lack of privileges, however, an error was raised correctly. This came about because the 'access denied' error message was first issued during normal access checking for the referenced base table, then converted into a generic 'view invalid' message for the referencing view in order to hide details of the table structure which were otherwise visible in the error message. Later still, all 'view invalid' errors were cleared and a warning issued instead, the rationale being that we should not get errors simply because a view referenced a nonexisting object. At this point all information about the initial causes of the error condition were lost. Fixed by implementing a specialized subclass of Internal_error_handler and removing error handling that manipulates error messages. @ mysql-test/r/information_schema_db.result Bug#35996: Changed result. @ mysql-test/r/view_grant.result Bug#35996: Test result. @ mysql-test/t/information_schema_db.test Bug#35996: Changed test. In this case the user has only INSERT privilege on the view's underlying view, where SELECT is required to see view definition. @ mysql-test/t/view_grant.test Bug#35996: Test case. @ sql/sql_base.cc Bug#35996: Partial removal of old style of error handling. @ sql/sql_show.cc Bug#35996: Implementation of the new Internal_error_handler subclass. modified: mysql-test/r/information_schema_db.result mysql-test/r/view_grant.result mysql-test/t/information_schema_db.test mysql-test/t/view_grant.test sql/sql_base.cc sql/sql_show.cc === modified file 'mysql-test/r/information_schema_db.result' --- a/mysql-test/r/information_schema_db.result 2009-05-15 15:47:50 +0000 +++ b/mysql-test/r/information_schema_db.result 2009-08-27 17:35:35 +0000 @@ -199,8 +199,7 @@ show fields from testdb_1.v1; Field Type Null Key Default Extra f1 char(4) YES NULL show create view v2; -View Create View character_set_client collation_connection -v2 CREATE ALGORITHM=UNDEFINED DEFINER=`testdb_2`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select `v1`.`f1` AS `f1` from `testdb_1`.`v1` latin1 latin1_swedish_ci +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table show create view testdb_1.v1; ERROR 42000: SHOW VIEW command denied to user 'testdb_2'@'localhost' for table 'v1' select table_name from information_schema.columns a === modified file 'mysql-test/r/view_grant.result' --- a/mysql-test/r/view_grant.result 2009-05-15 12:57:51 +0000 +++ b/mysql-test/r/view_grant.result 2009-08-27 17:35:35 +0000 @@ -1044,3 +1044,45 @@ DROP DATABASE mysqltest1; DROP VIEW test.v3; DROP USER mysqluser1@localhost; USE test; +CREATE USER mysqluser1@localhost; +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +GRANT USAGE, SELECT, CREATE VIEW, SHOW VIEW +ON mysqltest2.* TO mysqluser1@localhost; +USE mysqltest1; +CREATE TABLE t1( a INT ); +CREATE VIEW v1 AS SELECT 1 AS a; +GRANT SELECT ON t1 TO mysqluser1@localhost; +GRANT SELECT ON v1 TO mysqluser1@localhost; +CREATE VIEW v_t1 AS SELECT * FROM mysqltest1.t1; +CREATE VIEW v_v1 AS SELECT * FROM mysqltest1.v1; +REVOKE SELECT ON t1 FROM mysqluser1@localhost; +REVOKE SELECT ON v1 FROM mysqluser1@localhost; +SHOW CREATE VIEW v_t1; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +SHOW CREATE VIEW v_v1; +ERROR HY000: EXPLAIN/SHOW can not be issued; lacking privileges for underlying table +DROP USER mysqluser1@localhost; +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +USE test; +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v1 AS SELECT f1() AS a; +DROP FUNCTION f1; +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `f1`() AS `a` latin1 latin1_swedish_ci +Warnings: +Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +DROP VIEW v1; +CREATE TABLE t1( a INT ); +CREATE DEFINER = no_such_user@no_such_host VIEW v1 AS SELECT * FROM t1; +Warnings: +Note 1449 The user specified as a definer ('no_such_user'@'no_such_host') does not exist +SHOW CREATE VIEW v1; +View Create View character_set_client collation_connection +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`no_such_user`@`no_such_host` SQL SECURITY DEFINER VIEW `v1` AS select `test`.`t1`.`a` AS `a` from `t1` latin1 latin1_swedish_ci +Warnings: +Warning 1356 View 'test.v1' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them +DROP TABLE t1; +DROP VIEW v1; === modified file 'mysql-test/t/information_schema_db.test' --- a/mysql-test/t/information_schema_db.test 2009-05-15 09:59:31 +0000 +++ b/mysql-test/t/information_schema_db.test 2009-08-27 17:35:35 +0000 @@ -191,6 +191,7 @@ show fields from v4; show fields from v2; show fields from testdb_1.v1; +--error ER_VIEW_NO_EXPLAIN show create view v2; --error ER_TABLEACCESS_DENIED_ERROR show create view testdb_1.v1; === modified file 'mysql-test/t/view_grant.test' --- a/mysql-test/t/view_grant.test 2009-05-15 12:57:51 +0000 +++ b/mysql-test/t/view_grant.test 2009-08-27 17:35:35 +0000 @@ -1385,3 +1385,53 @@ USE test; # Wait till we reached the initial number of concurrent sessions --source include/wait_until_count_sessions.inc +# +# Bug#35996: Security Breach In Smashed TEMPTABLE Views +# +-- source include/not_embedded.inc +CREATE USER mysqluser1@localhost; +CREATE DATABASE mysqltest1; +CREATE DATABASE mysqltest2; +GRANT USAGE, SELECT, CREATE VIEW, SHOW VIEW +ON mysqltest2.* TO mysqluser1@localhost; + +USE mysqltest1; + +CREATE TABLE t1( a INT ); +CREATE VIEW v1 AS SELECT 1 AS a; + +GRANT SELECT ON t1 TO mysqluser1@localhost; +GRANT SELECT ON v1 TO mysqluser1@localhost; + +--connect (connection1, localhost, mysqluser1,, mysqltest2) +CREATE VIEW v_t1 AS SELECT * FROM mysqltest1.t1; +CREATE VIEW v_v1 AS SELECT * FROM mysqltest1.v1; + +--connection default +REVOKE SELECT ON t1 FROM mysqluser1@localhost; +REVOKE SELECT ON v1 FROM mysqluser1@localhost; + +--connection connection1 +--error ER_VIEW_NO_EXPLAIN +SHOW CREATE VIEW v_t1; +--error ER_VIEW_NO_EXPLAIN +SHOW CREATE VIEW v_v1; + +--disconnect connection1 +--connection default +DROP USER mysqluser1@localhost; +DROP DATABASE mysqltest1; +DROP DATABASE mysqltest2; +USE test; + +CREATE FUNCTION f1() RETURNS INT RETURN 1; +CREATE VIEW v1 AS SELECT f1() AS a; +DROP FUNCTION f1; +SHOW CREATE VIEW v1; +DROP VIEW v1; + +CREATE TABLE t1( a INT ); +CREATE DEFINER = no_such_user@no_such_host VIEW v1 AS SELECT * FROM t1; +SHOW CREATE VIEW v1; +DROP TABLE t1; +DROP VIEW v1; === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2009-05-30 13:32:28 +0000 +++ b/sql/sql_base.cc 2009-08-27 17:35:35 +0000 @@ -7644,7 +7644,12 @@ bool setup_tables_and_check_access(THD * check_single_table_access(thd, first_table ? want_access_first : want_access, leaves_tmp, FALSE)) { - tables->hide_view_error(thd); + /* + Unless the Show_create_error_handler is in effect, revert to old + method of supressing errors. + */ + if (!thd->get_internal_handler()) + tables->hide_view_error(thd); return TRUE; } first_table= 0; === modified file 'sql/sql_show.cc' --- a/sql/sql_show.cc 2009-05-15 12:57:51 +0000 +++ b/sql/sql_show.cc 2009-08-27 17:35:35 +0000 @@ -584,6 +584,125 @@ find_files(THD *thd, List *f } +/** + An Internal_error_handler that downgrades SHOW CREATE VIEW errors for + certain invalid views to warnings. + + This happens in the cases when a view's + underlying object (e.g. referenced in its SELECT list) does not exist. + + - For views referencing non-existing functions + + - For views referencing non-existing tables + */ +class Show_create_error_handler : public Internal_error_handler +{ +private: + + /** + The purpose of the Show_create_error_handler is to hide details of + underlying tables for which we have no privileges behind ER_VIEW_INVALID + messages. But this obviously does not apply if we lack privileges on the + view itself. Unfortunately the information about for which table privilege + checking failed is not available at the point of calling + handle_error(). The only way for us to check is by reconstructing the + actual error message and use strcmp() to see if it's the same. + */ + char view_access_denied_message[MYSQL_ERRMSG_SIZE]; + + TABLE_LIST *table; + bool within_handler; + + /** + We record the user and host at construction of this error handler, since + the privilege checking procedure will manipulate the security context in + thd. + */ + const char *original_user, *original_host; + +public: + explicit Show_create_error_handler(THD *thd, TABLE_LIST *table_list) : + table(table_list), within_handler(FALSE) + { + Security_context *sctx = test(table->security_ctx) ? + table->security_ctx : thd->security_ctx; + original_user= sctx->priv_user; + original_host= sctx->host_or_ip; + my_snprintf(view_access_denied_message, MYSQL_ERRMSG_SIZE, + ER(ER_TABLEACCESS_DENIED_ERROR), "SHOW VIEW", + sctx->priv_user, sctx->host_or_ip, table->get_table_name()); + } + + bool handle_error(uint sql_errno, const char *message, + MYSQL_ERROR::enum_warning_level level, THD *thd) + { + + /* + Only view errors are handled. + The handler does not handle the errors raised by itself, obviously. + */ + if (!table->view || within_handler) + return FALSE; + + within_handler= TRUE; + bool is_handled; + + switch (sql_errno) { + case ER_NO_SUCH_TABLE: + /* If an underlying table is missing, just warn. */ + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_VIEW_INVALID, + ER(ER_VIEW_INVALID), + table->get_db_name(), + table->get_table_name()); + is_handled= TRUE; + break; + case ER_VIEW_INVALID: + case ER_NO_SUCH_USER: + /* + If a user or an underlying function does not exist, issue warning + only. Unfortunately we have to manipulate error queue, this is + because this class was not involved when those errors were issued, + and the code issuing them relies on later code to correct the error + messages/codes. + */ + mysql_reset_errors(thd, true); + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_VIEW_INVALID, + ER(ER_VIEW_INVALID), + table->get_db_name(), + table->get_table_name()); + is_handled= TRUE; + break; + case ER_TABLEACCESS_DENIED_ERROR: + if (!strcmp(view_access_denied_message, message)) + /* If access to the view itself is not granted, don't interfere. */ + is_handled= FALSE; + /* + Missing privilege on an underlying table, throw an error. We only + care about the current user's privileges, however. check_grant may + check access to underlying tables using the view definer's + credentials, in which case thd->security_ctx is that of the definer. + */ + else if (!strcmp(original_user, thd->security_ctx->priv_user) && + !strcmp(original_host, thd->security_ctx->host_or_ip)) + { + my_error(ER_VIEW_NO_EXPLAIN, MYF(0)); + is_handled= TRUE; + } + else + is_handled= TRUE; + break; + default: + is_handled= FALSE; + } + + within_handler= FALSE; + return is_handled; + } +}; + + bool mysqld_show_create(THD *thd, TABLE_LIST *table_list) { @@ -597,27 +716,12 @@ mysqld_show_create(THD *thd, TABLE_LIST /* We want to preserve the tree for views. */ thd->lex->view_prepare_mode= TRUE; - /* Only one table for now, but VIEW can involve several tables */ - if (open_normal_and_derived_tables(thd, table_list, 0)) - { - if (!table_list->view || - thd->is_error() && thd->main_da.sql_errno() != ER_VIEW_INVALID) - DBUG_RETURN(TRUE); - - /* - Clear all messages with 'error' level status and - issue a warning with 'warning' level status in - case of invalid view and last error is ER_VIEW_INVALID - */ - mysql_reset_errors(thd, true); - thd->clear_error(); - - push_warning_printf(thd,MYSQL_ERROR::WARN_LEVEL_WARN, - ER_VIEW_INVALID, - ER(ER_VIEW_INVALID), - table_list->view_db.str, - table_list->view_name.str); - } + Show_create_error_handler view_error_suppressor(thd, table_list); + thd->push_internal_handler(&view_error_suppressor); + bool error= open_normal_and_derived_tables(thd, table_list, 0); + thd->pop_internal_handler(); + if (error && thd->main_da.is_error()) + DBUG_RETURN(TRUE); /* TODO: add environment variables show when it become possible */ if (thd->lex->only_view && !table_list->view) --===============1702410798== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/martin.hansson@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: martin.hansson@stripped # target_branch: file:///data0/martin/bzr/bug35996/5.1bt-gca-commmit/ # testament_sha1: 86816df26a0a5c3a4435fc6a5ca2e1e656ab0956 # timestamp: 2009-08-27 19:35:42 +0200 # base_revision_id: davi.arnaut@stripped\ # y7y7n931lbgnesb4 # # Begin bundle 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 rn0fB+UuX/4u5IpwoSDxIOCo --===============1702410798==--