MDEV-37950: INSERT ... RETURNING exposes columns for which the user lacks

SELECT privilege

Analysis:
When setup_fields() is called, the want_privilege is set to NO_ACL, so
correct priveleges are not checked.
Fix:
Since RETURNING requires SELECT_ACL privelige, when we are setting up
the returning fields for the given query, set want_privilege to SELECT_ACL.
Reset to original value of want_privilege once done.
This commit is contained in:
Rucha Deodhar 2025-12-04 18:35:50 +05:30
parent 6892722577
commit ca63e2c627
3 changed files with 115 additions and 4 deletions

View File

@ -121,3 +121,46 @@ connection default;
DROP DATABASE meow;
set local sql_mode=default;
set global sql_mode=default;
#
# MDEV-37950: INSERT ... RETURNING exposes columns for which
# the user lacks SELECT privilege
#
CREATE USER regular;
GRANT INSERT ON *.* TO regular;
GRANT DELETE ON *.* TO regular;
CREATE DATABASE test1;
DROP TABLE IF EXISTS test1.t_trigger_test1;
Warnings:
Note 1051 Unknown table 'test1.t_trigger_test1'
CREATE TABLE test1.t_trigger_test1 (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
note VARCHAR(100)
);
CREATE TRIGGER test1.trg_before_insert
BEFORE INSERT ON test1.t_trigger_test1
FOR EACH ROW
BEGIN
SET NEW.name = CONCAT('BEFORE_', NEW.name);
END |
INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING *;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING id, name, note;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
# same for DELETE because delete with "WHERE" still requires to
# read from the table, which basically means having select privileges
DELETE FROM test1.t_trigger_test1 WHERE id=1;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING id;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING *;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
DELETE FROM test1.t_trigger_test1 RETURNING *;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
DELETE FROM test1.t_trigger_test1 RETURNING id;
ERROR 42000: SELECT command denied to user 'regular'@'localhost' for column 'id' in table 't_trigger_test1'
DELETE FROM test1.t_trigger_test1;
DROP TRIGGER test1.trg_before_insert;
DROP TABLE test1.t_trigger_test1;
DROP USER regular;
DROP DATABASE test1;

View File

@ -158,3 +158,60 @@ DROP DATABASE meow;
set local sql_mode=default;
set global sql_mode=default;
--echo #
--echo # MDEV-37950: INSERT ... RETURNING exposes columns for which
--echo # the user lacks SELECT privilege
--echo #
CREATE USER regular;
GRANT INSERT ON *.* TO regular;
GRANT DELETE ON *.* TO regular;
CREATE DATABASE test1;
DROP TABLE IF EXISTS test1.t_trigger_test1;
CREATE TABLE test1.t_trigger_test1 (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
note VARCHAR(100)
);
DELIMITER |;
CREATE TRIGGER test1.trg_before_insert
BEFORE INSERT ON test1.t_trigger_test1
FOR EACH ROW
BEGIN
SET NEW.name = CONCAT('BEFORE_', NEW.name);
END |
DELIMITER ;|
change_user regular;
--error ER_COLUMNACCESS_DENIED_ERROR
INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING *;
--error ER_COLUMNACCESS_DENIED_ERROR
INSERT INTO test1.t_trigger_test1 (name) VALUES ('Alice') RETURNING id, name, note;
--echo # same for DELETE because delete with "WHERE" still requires to
--echo # read from the table, which basically means having select privileges
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM test1.t_trigger_test1 WHERE id=1;
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING id;
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM test1.t_trigger_test1 WHERE id=1 RETURNING *;
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM test1.t_trigger_test1 RETURNING *;
--error ER_COLUMNACCESS_DENIED_ERROR
DELETE FROM test1.t_trigger_test1 RETURNING id;
DELETE FROM test1.t_trigger_test1;
change_user root;
DROP TRIGGER test1.trg_before_insert;
DROP TABLE test1.t_trigger_test1;
DROP USER regular;
DROP DATABASE test1;

View File

@ -7909,12 +7909,23 @@ bool setup_fields(THD *thd, Ref_ptr_array ref_pointer_array,
int setup_returning_fields(THD* thd, TABLE_LIST* table_list)
{
GRANT_INFO *saved_grant;
int res= 0;
if (!thd->lex->has_returning())
return 0;
return setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL,
thd->lex->returning(), true)
|| setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list,
MARK_COLUMNS_READ, NULL, NULL, 0, THD_WHERE::RETURNING);
saved_grant= &table_list->table->grant;
table_list->table->grant.want_privilege|= SELECT_ACL;
res= setup_wild(thd, table_list, thd->lex->returning()->item_list, NULL,
thd->lex->returning(), true)
|| setup_fields(thd, Ref_ptr_array(), thd->lex->returning()->item_list,
MARK_COLUMNS_READ, NULL, NULL, 0, THD_WHERE::RETURNING);
table_list->table->grant= *saved_grant;
return res;
}