1
0
mirror of https://github.com/MariaDB/server.git synced 2025-04-18 21:44:20 +03:00

MDEV-36380 User has unauthorized access to a sequence through a view with security invoker

check sequence privileges in Item_func_nextval::fix_fields(),
just like column privileges are checked in Item_field::fix_fields()

remove sequence specific hacks that kinda made sequence privilege
checks works, but not in all cases. And they were too lax,
didn't requre SELECT privilege for NEXTVAL.

also fixes

MDEV-36413 User without any privileges to a sequence can read from it and modify it via column default
This commit is contained in:
Sergei Golubchik 2025-04-11 08:28:42 +02:00
parent 10b2187a94
commit 9ce0720f36
11 changed files with 209 additions and 26 deletions

View File

@ -1985,4 +1985,50 @@ connection default;
DROP VIEW v1;
DROP USER foo;
DROP USER FOO;
#
# MDEV-36380: User has unauthorized access to a sequence through
# a view with security invoker
#
create database db;
use db;
create sequence s;
create sql security invoker view vin as select nextval(s);
create sql security definer view vdn as select nextval(s);
create sql security invoker view vil as select lastval(s);
create sql security definer view vdl as select lastval(s);
create sql security invoker view vis as select setval(s,20);
create sql security definer view vds as select setval(s,30);
create user u@localhost;
grant select on db.vin to u@localhost;
grant select on db.vdn to u@localhost;
grant select on db.vil to u@localhost;
grant select on db.vdl to u@localhost;
grant select on db.vis to u@localhost;
grant select on db.vds to u@localhost;
connect con1,localhost,u,,db;
select nextval(s);
ERROR 42000: SELECT, INSERT command denied to user 'u'@'localhost' for table `db`.`s`
select * from vin;
ERROR HY000: View 'db.vin' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from vdn;
nextval(s)
1
select lastval(s);
ERROR 42000: SELECT command denied to user 'u'@'localhost' for table `db`.`s`
select * from vil;
ERROR HY000: View 'db.vil' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from vdl;
lastval(s)
1
select setval(s,10);
ERROR 42000: INSERT command denied to user 'u'@'localhost' for table `db`.`s`
select * from vis;
ERROR HY000: View 'db.vis' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them
select * from vds;
setval(s,30)
30
disconnect con1;
connection default;
drop database db;
drop user u@localhost;
# End of 10.5 tests

View File

@ -2239,4 +2239,51 @@ DROP VIEW v1;
DROP USER foo;
DROP USER FOO;
--echo #
--echo # MDEV-36380: User has unauthorized access to a sequence through
--echo # a view with security invoker
--echo #
create database db;
use db;
create sequence s;
create sql security invoker view vin as select nextval(s);
create sql security definer view vdn as select nextval(s);
create sql security invoker view vil as select lastval(s);
create sql security definer view vdl as select lastval(s);
create sql security invoker view vis as select setval(s,20);
create sql security definer view vds as select setval(s,30);
create user u@localhost;
grant select on db.vin to u@localhost;
grant select on db.vdn to u@localhost;
grant select on db.vil to u@localhost;
grant select on db.vdl to u@localhost;
grant select on db.vis to u@localhost;
grant select on db.vds to u@localhost;
--connect (con1,localhost,u,,db)
--error ER_TABLEACCESS_DENIED_ERROR
select nextval(s);
--error ER_VIEW_INVALID
select * from vin;
--disable_ps2_protocol
select * from vdn;
--enable_ps2_protocol
--error ER_TABLEACCESS_DENIED_ERROR
select lastval(s);
--error ER_VIEW_INVALID
select * from vil;
select * from vdl;
--error ER_TABLEACCESS_DENIED_ERROR
select setval(s,10);
--error ER_VIEW_INVALID
select * from vis;
select * from vds;
--disconnect con1
--connection default
drop database db;
drop user u@localhost;
--echo # End of 10.5 tests

View File

@ -47,14 +47,57 @@ next_not_cached_value minimum_value maximum_value start_value increment cache_si
11 1 9223372036854775806 1 1 1000 0 0
connection only_alter;
select next value for s1;
ERROR 42000: INSERT command denied to user 'only_alter'@'localhost' for table `mysqltest_1`.`s1`
ERROR 42000: SELECT, INSERT command denied to user 'only_alter'@'localhost' for table `mysqltest_1`.`s1`
alter sequence s1 restart= 11;
select * from s1;
ERROR 42000: SELECT command denied to user 'only_alter'@'localhost' for table `mysqltest_1`.`s1`
connection default;
drop database mysqltest_1;
drop user 'normal'@'%';
drop user 'read_only'@'%';
drop user 'read_write'@'%';
drop user 'alter'@'%';
drop user 'only_alter'@'%';
drop sequence s1;
#
# MDEV-36413 User without any privileges to a sequence can read from
# it and modify it via column default
#
create sequence s1;
create sequence s2;
select * from s2;
next_not_cached_value minimum_value maximum_value start_value increment cache_size cycle_option cycle_count
1 1 9223372036854775806 1 1 1000 0 0
create table t2 (a int not null default(nextval(s1)));
insert into t2 values();
create user u;
grant create, insert, select, drop on mysqltest_1.t1 to u;
grant insert, select on mysqltest_1.s1 to u;
grant select on mysqltest_1.t2 to u;
connect con1,localhost,u,,mysqltest_1;
select nextval(s2);
ERROR 42000: SELECT, INSERT command denied to user 'u'@'localhost' for table `mysqltest_1`.`s2`
show create sequence s2;
ERROR 42000: SHOW command denied to user 'u'@'localhost' for table `mysqltest_1`.`s2`
create table t1 (a int not null default(nextval(s1)));
drop table t1;
create table t1 (a int not null default(nextval(s1))) select a from t2;
insert into t1 values();
select * from t1;
a
1
2
drop table t1;
create table t1 (a int not null default(nextval(s1))) select a from (select t2.a from t2,t2 as t3 where t2.a=t3.a) as t4;
drop table t1;
create table t1 (a int not null default(nextval(s2)));
ERROR 42000: SELECT, INSERT command denied to user 'u'@'localhost' for table `mysqltest_1`.`s2`
create table t1 (a int not null default(nextval(s1)),
b int not null default(nextval(s2)));
ERROR 42000: SELECT, INSERT command denied to user 'u'@'localhost' for table `mysqltest_1`.`s2`
disconnect con1;
connection default;
drop user u;
drop database mysqltest_1;
#
# End of 10.11 tests
#

View File

@ -60,10 +60,58 @@ select * from s1;
#
connection default;
drop database mysqltest_1;
drop user 'normal'@'%';
drop user 'read_only'@'%';
drop user 'read_write'@'%';
drop user 'alter'@'%';
drop user 'only_alter'@'%';
drop sequence s1;
--echo #
--echo # MDEV-36413 User without any privileges to a sequence can read from
--echo # it and modify it via column default
--echo #
create sequence s1;
create sequence s2;
select * from s2;
create table t2 (a int not null default(nextval(s1)));
insert into t2 values();
create user u;
grant create, insert, select, drop on mysqltest_1.t1 to u;
grant insert, select on mysqltest_1.s1 to u;
grant select on mysqltest_1.t2 to u;
--connect(con1,localhost,u,,mysqltest_1)
--error ER_TABLEACCESS_DENIED_ERROR
select nextval(s2);
--error ER_TABLEACCESS_DENIED_ERROR
show create sequence s2;
create table t1 (a int not null default(nextval(s1)));
drop table t1;
create table t1 (a int not null default(nextval(s1))) select a from t2;
insert into t1 values();
select * from t1;
drop table t1;
create table t1 (a int not null default(nextval(s1))) select a from (select t2.a from t2,t2 as t3 where t2.a=t3.a) as t4;
drop table t1;
--error ER_TABLEACCESS_DENIED_ERROR
create table t1 (a int not null default(nextval(s2)));
--error ER_TABLEACCESS_DENIED_ERROR
create table t1 (a int not null default(nextval(s1)),
b int not null default(nextval(s2)));
--disconnect con1
--connection default
drop user u;
#
# Cleanup
#
drop database mysqltest_1;
--echo #
--echo # End of 10.11 tests
--echo #

View File

@ -174,7 +174,7 @@ create sequence s_db.s2;
drop sequence s_db.s2;
connection m_normal_2;
select next value for s_db.s1;
ERROR 42000: INSERT command denied to user 'normal_2'@'localhost' for table `s_db`.`s1`
ERROR 42000: SELECT, INSERT command denied to user 'normal_2'@'localhost' for table `s_db`.`s1`
create sequence s_db.s2;
ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table `s_db`.`s2`
connection m_normal_1;

View File

@ -285,7 +285,7 @@ create sequence s_db.s2;
drop sequence s_db.s2;
connection m_normal_2;
select NEXT VALUE for s_db.s1;
ERROR 42000: INSERT command denied to user 'normal_2'@'localhost' for table `s_db`.`s1`
ERROR 42000: SELECT, INSERT command denied to user 'normal_2'@'localhost' for table `s_db`.`s1`
create sequence s_db.s2;
ERROR 42000: CREATE command denied to user 'normal_2'@'localhost' for table `s_db`.`s2`
connection m_normal_1;

View File

@ -1,5 +1,4 @@
--source include/have_sequence.inc
--source include/have_innodb.inc
#
# Test sequences with views

View File

@ -7092,6 +7092,16 @@ longlong Item_func_cursor_rowcount::val_int()
/*****************************************************************************
SEQUENCE functions
*****************************************************************************/
bool Item_func_nextval::check_access_and_fix_fields(THD *thd, Item **ref,
privilege_t want_access)
{
table_list->sequence= false;
bool error= check_single_table_access(thd, want_access, table_list, false);
table_list->sequence= true;
if (error && table_list->belong_to_view)
table_list->hide_view_error(thd);
return error || Item_longlong_func::fix_fields(thd, ref);
}
longlong Item_func_nextval::val_int()
{

View File

@ -3774,11 +3774,14 @@ class Item_func_nextval :public Item_longlong_func
protected:
TABLE_LIST *table_list;
TABLE *table;
bool check_access_and_fix_fields(THD *, Item **ref, privilege_t);
public:
Item_func_nextval(THD *thd, TABLE_LIST *table_list_arg):
Item_longlong_func(thd), table_list(table_list_arg) {}
longlong val_int() override;
const char *func_name() const override { return "nextval"; }
bool fix_fields(THD *thd, Item **ref) override
{ return check_access_and_fix_fields(thd, ref, INSERT_ACL | SELECT_ACL); }
bool fix_length_and_dec() override
{
unsigned_flag= 0;
@ -3820,6 +3823,8 @@ class Item_func_lastval :public Item_func_nextval
public:
Item_func_lastval(THD *thd, TABLE_LIST *table_list_arg):
Item_func_nextval(thd, table_list_arg) {}
bool fix_fields(THD *thd, Item **ref) override
{ return check_access_and_fix_fields(thd, ref, SELECT_ACL); }
longlong val_int() override;
const char *func_name() const override { return "lastval"; }
Item *do_get_copy(THD *thd) const override
@ -3840,6 +3845,8 @@ public:
: Item_func_nextval(thd, table_list_arg),
nextval(nextval_arg), round(round_arg), is_used(is_used_arg)
{}
bool fix_fields(THD *thd, Item **ref) override
{ return check_access_and_fix_fields(thd, ref, INSERT_ACL); }
longlong val_int() override;
const char *func_name() const override { return "setval"; }
void print(String *str, enum_query_type query_type) override;

View File

@ -8304,19 +8304,11 @@ bool check_grant(THD *thd, privilege_t want_access, TABLE_LIST *tables,
/*
If sequence is used as part of NEXT VALUE, PREVIOUS VALUE or SELECT,
we need to modify the requested access rights depending on how the
sequence is used.
the privilege will be checked in ::fix_fields()
*/
if (t_ref->sequence &&
!(want_access & ~(SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL)))
{
/*
We want to have either SELECT or INSERT rights to sequences depending
on how they are accessed
*/
orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
INSERT_ACL : SELECT_ACL);
}
continue;
const ACL_internal_table_access *access=
get_cached_table_access(&t_ref->grant.m_internal,

View File

@ -7332,18 +7332,9 @@ check_table_access(THD *thd, privilege_t requirements, TABLE_LIST *tables,
DBUG_PRINT("info", ("derived: %d view: %d", table_ref->derived != 0,
table_ref->view != 0));
if (table_ref->is_anonymous_derived_table())
if (table_ref->is_anonymous_derived_table() || table_ref->sequence)
continue;
if (table_ref->sequence)
{
/* We want to have either SELECT or INSERT rights to sequences depending
on how they are accessed
*/
want_access= ((table_ref->lock_type == TL_WRITE_ALLOW_WRITE) ?
INSERT_ACL : SELECT_ACL);
}
if (check_access(thd, want_access, table_ref->get_db_name().str,
&table_ref->grant.privilege,
&table_ref->grant.m_internal,