From 11d141004af2507cd15751bad7378851a9347527 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 21 Oct 2013 13:34:18 +0400 Subject: [PATCH 001/174] A clean-up for DEV-4890 Valgrind warnings on shutdown on a build with openSSL --- sql/mysqld.cc | 2 +- sql/slave.cc | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index b709d71f817..a92fe49e2bf 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -2094,7 +2094,7 @@ bool one_thread_per_connection_end(THD *thd, bool put_in_cache) /* It's safe to broadcast outside a lock (COND... is not deleted here) */ DBUG_PRINT("signal", ("Broadcasting COND_thread_count")); DBUG_LEAVE; // Must match DBUG_ENTER() -#ifndef EMBEDDED_LIBRARY +#if defined(HAVE_OPENSSL) && !defined(EMBEDDED_LIBRARY) ERR_remove_state(0); #endif my_thread_end(); diff --git a/sql/slave.cc b/sql/slave.cc index 202e624ce71..7941e632124 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -2854,7 +2854,9 @@ err: DBUG_LEAVE; // Must match DBUG_ENTER() my_thread_end(); +#ifdef HAVE_OPENSSL ERR_remove_state(0); +#endif pthread_exit(0); return 0; // Avoid compiler warnings } @@ -3251,7 +3253,9 @@ the slave SQL thread with \"SLAVE START\". We stopped at log \ DBUG_LEAVE; // Must match DBUG_ENTER() my_thread_end(); +#ifdef HAVE_OPENSSL ERR_remove_state(0); +#endif pthread_exit(0); return 0; // Avoid compiler warnings } From afed809297ec43d7825b607b6ebadbabe4f63c2a Mon Sep 17 00:00:00 2001 From: "timour@askmonty.org" Date: Fri, 18 Oct 2013 11:45:25 +0300 Subject: [PATCH 002/174] MDEV-5123 Remove duplicated conditions pushed both to join_tab->select_cond and join_tab->cache_select->cond for blocked joins. BNL and BNLH joins pre-filter the records from a joined table via JOIN_TAB::cache_select->cond. There is no need to re-evaluate the same conditions via JOIN_TAB::select_cond. This patch removes the duplicated conditions from the top-level conjuncts of each pushed condition. The added "Using where" in few EXPLAINs is due to taking into account tab->cache_select->cond in addition to tab->select_cond in JOIN::save_explain_data_intern. --- mysql-test/r/join_cache.result | 49 ++++++++++++- mysql-test/r/limit_rows_examined.result | 14 ++-- mysql-test/r/range.result | 12 ++-- mysql-test/r/range_mrr_icp.result | 12 ++-- mysql-test/r/subselect_mat.result | 4 +- mysql-test/r/subselect_sj.result | 2 +- mysql-test/r/subselect_sj_jcl6.result | 2 +- mysql-test/r/subselect_sj_mat.result | 2 +- mysql-test/t/join_cache.test | 44 ++++++++++++ mysql-test/t/limit_rows_examined.test | 2 +- sql/sql_select.cc | 93 ++++++++++++++++++++++++- sql/sql_select.h | 1 + 12 files changed, 210 insertions(+), 27 deletions(-) diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result index 618ce7e540b..476f3e92caa 100644 --- a/mysql-test/r/join_cache.result +++ b/mysql-test/r/join_cache.result @@ -5058,7 +5058,7 @@ EXPLAIN SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 range PRIMARY PRIMARY 4 NULL 3 Using where; Using index -1 SIMPLE t2 hash_range idx #hash#idx:idx 5:5 const 4 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNLH join) +1 SIMPLE t2 hash_range idx #hash#idx:idx 5:5 const 4 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNLH join) SELECT * FROM t1,t2 WHERE t1.a < 3 and t2.a IS NULL; a a b 1 NULL 10 @@ -5637,4 +5637,51 @@ c set join_buffer_size=default; set optimizer_switch=@tmp_optimizer_switch; DROP table t1,t2,t3; +# +# MDEV-5123 Remove duplicated conditions pushed both to join_tab->select_cond and join_tab->cache_select->cond for blocked joins. +# +set join_cache_level=default; +set expensive_subquery_limit=0; +create table t1 (c1 int); +create table t2 (c2 int); +create table t3 (c3 int); +insert into t1 values (1), (2); +insert into t2 values (1), (2); +insert into t3 values (2); +explain +select count(*) from t1 straight_join t2 +where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1); +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) +2 UNCACHEABLE SUBQUERY t3 system NULL NULL NULL NULL 1 +set @counter=0; +select count(*) from t1 straight_join t2 +where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1); +count(*) +2 +select @counter; +@counter +2 +explain +select count(*) from t1 straight_join t2 +where c1 = c2-0 and +c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and +c2 / 2 = 1; +id select_type table type possible_keys key key_len ref rows Extra +1 PRIMARY t1 ALL NULL NULL NULL NULL 2 +1 PRIMARY t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (flat, BNL join) +2 UNCACHEABLE SUBQUERY t3 system NULL NULL NULL NULL 1 +set @counter=0; +select count(*) from t1 straight_join t2 +where c1 = c2-0 and +c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and +c2 / 2 = 1; +count(*) +1 +select @counter; +@counter +2 +drop table t1,t2,t3; +set expensive_subquery_limit=default; set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/r/limit_rows_examined.result b/mysql-test/r/limit_rows_examined.result index 5dbe01eef4f..130d17ae270 100644 --- a/mysql-test/r/limit_rows_examined.result +++ b/mysql-test/r/limit_rows_examined.result @@ -747,27 +747,27 @@ ERROR HY000: Sort aborted: SHOW STATUS LIKE 'Handler_read%'; Variable_name Value Handler_read_first 0 -Handler_read_key 4 +Handler_read_key 5 Handler_read_last 0 Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 0 Handler_read_rnd_deleted 0 -Handler_read_rnd_next 52 +Handler_read_rnd_next 46 SHOW STATUS LIKE 'Handler_tmp%'; Variable_name Value Handler_tmp_update 0 -Handler_tmp_write 66 +Handler_tmp_write 70 FLUSH STATUS; SELECT a AS field1, alias2.d AS field2, alias2.f AS field3, alias2.e AS field4, b AS field5 FROM t1, t2 AS alias2, t2 AS alias3 WHERE alias3.c IN ( SELECT 1 UNION SELECT 6 ) GROUP BY field1, field2, field3, field4, field5 -LIMIT ROWS EXAMINED 250; +LIMIT ROWS EXAMINED 124; field1 field2 field3 field4 field5 00:21:38 06:07:10 a 2007-06-08 04:35:26 2007-05-28 00:00:00 Warnings: -Warning 1931 Query execution was interrupted. The query examined at least 251 rows, which exceeds LIMIT ROWS EXAMINED (250). The query result may be incomplete. +Warning 1931 Query execution was interrupted. The query examined at least 125 rows, which exceeds LIMIT ROWS EXAMINED (124). The query result may be incomplete. SHOW STATUS LIKE 'Handler_read%'; Variable_name Value Handler_read_first 0 @@ -777,11 +777,11 @@ Handler_read_next 0 Handler_read_prev 0 Handler_read_rnd 2 Handler_read_rnd_deleted 1 -Handler_read_rnd_next 110 +Handler_read_rnd_next 47 SHOW STATUS LIKE 'Handler_tmp%'; Variable_name Value Handler_tmp_update 0 -Handler_tmp_write 133 +Handler_tmp_write 70 drop table t1, t2; MDEV-161 LIMIT_ROWS EXAMINED: query with the limit and NOT EXISTS, without GROUP BY or aggregate, diff --git a/mysql-test/r/range.result b/mysql-test/r/range.result index d41bfaa2c67..e169dcb40b0 100644 --- a/mysql-test/r/range.result +++ b/mysql-test/r/range.result @@ -221,27 +221,27 @@ update t1 set y=x; explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Using join buffer (flat, BNL join) explain select count(*) from t1 where x in (1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref x x 5 const 1 Using index diff --git a/mysql-test/r/range_mrr_icp.result b/mysql-test/r/range_mrr_icp.result index a5c14d99975..0d6bfc2467b 100644 --- a/mysql-test/r/range_mrr_icp.result +++ b/mysql-test/r/range_mrr_icp.result @@ -223,27 +223,27 @@ update t1 set y=x; explain select * from t1, t1 t2 where t1.y = 8 and t2.x between 7 and t1.y+0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 8 and t2.x >= 7 and t2.x <= t1.y+0; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x between t1.y-1 and t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= t1.y-1 and t2.x <= t1.y+1; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 3 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x between 0 and t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select * from t1, t1 t2 where t1.y = 2 and t2.x >= 0 and t2.x <= t1.y; id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref y y 5 const 1 -1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Rowid-ordered scan; Using join buffer (flat, BNL join) +1 SIMPLE t2 range x x 5 NULL 2 Using index condition; Using where; Rowid-ordered scan; Using join buffer (flat, BNL join) explain select count(*) from t1 where x in (1); id select_type table type possible_keys key key_len ref rows Extra 1 SIMPLE t1 ref x x 5 const 1 Using index diff --git a/mysql-test/r/subselect_mat.result b/mysql-test/r/subselect_mat.result index e1c9df3b00b..05635dd0985 100644 --- a/mysql-test/r/subselect_mat.result +++ b/mysql-test/r/subselect_mat.result @@ -1474,7 +1474,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 -2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan +2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where; Rowid-ordered scan SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); pk 2 @@ -2158,7 +2158,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 -2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition +2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); pk 2 diff --git a/mysql-test/r/subselect_sj.result b/mysql-test/r/subselect_sj.result index 51e5c5e54d0..b8040731cb5 100644 --- a/mysql-test/r/subselect_sj.result +++ b/mysql-test/r/subselect_sj.result @@ -803,7 +803,7 @@ EXPLAIN EXTENDED SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 1 PRIMARY eq_ref distinct_key distinct_key 11 func,func 1 100.00 -2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Rowid-ordered scan +2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan Warnings: Note 1003 select `test`.`t1`.`pk` AS `pk` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`pk` > 0)) SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk > 0); diff --git a/mysql-test/r/subselect_sj_jcl6.result b/mysql-test/r/subselect_sj_jcl6.result index 73777dd71c3..a42fc1efaad 100644 --- a/mysql-test/r/subselect_sj_jcl6.result +++ b/mysql-test/r/subselect_sj_jcl6.result @@ -816,7 +816,7 @@ EXPLAIN EXTENDED SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 100.00 1 PRIMARY eq_ref distinct_key distinct_key 11 func,func 1 100.00 -2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Rowid-ordered scan +2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 100.00 Using index condition; Using where; Rowid-ordered scan Warnings: Note 1003 select `test`.`t1`.`pk` AS `pk` from `test`.`t1` semi join (`test`.`t2`) where ((`test`.`t2`.`pk` > 0)) SELECT pk FROM t1 WHERE (a, b) IN (SELECT a, b FROM t2 WHERE pk > 0); diff --git a/mysql-test/r/subselect_sj_mat.result b/mysql-test/r/subselect_sj_mat.result index bcdd82b790c..15cb9eeaa0e 100644 --- a/mysql-test/r/subselect_sj_mat.result +++ b/mysql-test/r/subselect_sj_mat.result @@ -1511,7 +1511,7 @@ EXPLAIN SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); id select_type table type possible_keys key key_len ref rows Extra 1 PRIMARY t1 ALL NULL NULL NULL NULL 2 1 PRIMARY eq_ref distinct_key distinct_key 4 func 1 -2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Rowid-ordered scan +2 MATERIALIZED t2 range PRIMARY PRIMARY 4 NULL 2 Using index condition; Using where; Rowid-ordered scan SELECT pk FROM t1 WHERE (a) IN (SELECT a FROM t2 WHERE pk > 0); pk 2 diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test index 2d06c3e2a30..7859822bcad 100644 --- a/mysql-test/t/join_cache.test +++ b/mysql-test/t/join_cache.test @@ -3636,5 +3636,49 @@ set optimizer_switch=@tmp_optimizer_switch; DROP table t1,t2,t3; +--echo # +--echo # MDEV-5123 Remove duplicated conditions pushed both to join_tab->select_cond and join_tab->cache_select->cond for blocked joins. +--echo # + +set join_cache_level=default; +set expensive_subquery_limit=0; + +create table t1 (c1 int); +create table t2 (c2 int); +create table t3 (c3 int); + +insert into t1 values (1), (2); +insert into t2 values (1), (2); +insert into t3 values (2); + +explain +select count(*) from t1 straight_join t2 +where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1); + +set @counter=0; + +select count(*) from t1 straight_join t2 +where c1 = c2-0 and c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1); + +select @counter; + +explain +select count(*) from t1 straight_join t2 +where c1 = c2-0 and + c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and + c2 / 2 = 1; + +set @counter=0; + +select count(*) from t1 straight_join t2 +where c1 = c2-0 and + c2 <= (select max(c3) from t3 where c3 = 2 and @counter:=@counter+1) and + c2 / 2 = 1; + +select @counter; + +drop table t1,t2,t3; +set expensive_subquery_limit=default; + # this must be the last command in the file set @@optimizer_switch=@save_optimizer_switch; diff --git a/mysql-test/t/limit_rows_examined.test b/mysql-test/t/limit_rows_examined.test index ef28a37108f..45ee483c7aa 100644 --- a/mysql-test/t/limit_rows_examined.test +++ b/mysql-test/t/limit_rows_examined.test @@ -502,7 +502,7 @@ SELECT a AS field1, alias2.d AS field2, alias2.f AS field3, alias2.e AS field4, FROM t1, t2 AS alias2, t2 AS alias3 WHERE alias3.c IN ( SELECT 1 UNION SELECT 6 ) GROUP BY field1, field2, field3, field4, field5 -LIMIT ROWS EXAMINED 250; +LIMIT ROWS EXAMINED 124; SHOW STATUS LIKE 'Handler_read%'; SHOW STATUS LIKE 'Handler_tmp%'; diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 23f504f602c..e30b917fe1c 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10535,6 +10535,87 @@ restart: } } +/** + Remove pushdown conditions that are already checked by the scan phase + of BNL/BNLH joins. + + @note + If the single-table condition for this table will be used by a + blocked join to pre-filter this table's rows, there is no need + to re-check the same single-table condition for each joined record. + + This method removes from JOIN_TAB::select_cond and JOIN_TAB::select::cond + all top-level conjuncts that also appear in in JOIN_TAB::cache_select::cond. +*/ + +void JOIN_TAB::remove_redundant_bnl_scan_conds() +{ + if (!(select_cond && cache_select && cache && + (cache->get_join_alg() == JOIN_CACHE::BNL_JOIN_ALG || + cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG))) + return; + + + /* + select->cond is not processed separately. This method assumes it is always + the same as select_cond. + */ + DBUG_ASSERT(!select || !select->cond || + (select->cond == select_cond)); + + if (is_cond_and(select_cond)) + { + List_iterator pushed_cond_li(*((Item_cond*) select_cond)->argument_list()); + Item *pushed_item; + Item_cond_and *reduced_select_cond= new Item_cond_and; + + if (is_cond_and(cache_select->cond)) + { + List_iterator scan_cond_li(*((Item_cond*) cache_select->cond)->argument_list()); + Item *scan_item; + while ((pushed_item= pushed_cond_li++)) + { + bool found= false; + scan_cond_li.rewind(); + while ((scan_item= scan_cond_li++)) + { + if (pushed_item->eq(scan_item, 0)) + { + found= true; + break; + } + } + if (!found) + reduced_select_cond->add(pushed_item); + } + } + else + { + while ((pushed_item= pushed_cond_li++)) + { + if (!pushed_item->eq(cache_select->cond, 0)) + reduced_select_cond->add(pushed_item); + } + } + + /* + JOIN_CACHE::check_match uses JOIN_TAB::select->cond instead of + JOIN_TAB::select_cond. set_cond() sets both pointers. + */ + if (reduced_select_cond->argument_list()->is_empty()) + set_cond(NULL); + else if (reduced_select_cond->argument_list()->elements == 1) + set_cond(reduced_select_cond->argument_list()->head()); + else + { + reduced_select_cond->quick_fix_field(); + set_cond(reduced_select_cond); + } + } + else if (select_cond->eq(cache_select->cond, 0)) + set_cond(NULL); +} + /* Plan refinement stage: do various setup things for the executor @@ -10786,6 +10867,15 @@ make_join_readinfo(JOIN *join, ulonglong options, uint no_jbuf_after) abort(); /* purecov: end */ } + + tab->remove_redundant_bnl_scan_conds(); + DBUG_EXECUTE("where", + char buff[256]; + String str(buff,sizeof(buff),system_charset_info); + str.length(0); + str.append(tab->table? tab->table->alias.c_ptr() :""); + str.append(" final_pushdown_cond"); + print_where(tab->select_cond, str.c_ptr_safe(), QT_ORDINARY);); } uint n_top_tables= join->join_tab_ranges.head()->end - join->join_tab_ranges.head()->start; @@ -23201,7 +23291,8 @@ int JOIN::save_explain_data_intern(Explain_query *output, bool need_tmp_table, eta->push_extra(ET_RANGE_CHECKED_FOR_EACH_RECORD); eta->range_checked_map= tab->keys; } - else if (tab->select->cond) + else if (tab->select->cond || + (tab->cache_select && tab->cache_select->cond)) { const COND *pushed_cond= tab->table->file->pushed_cond; diff --git a/sql/sql_select.h b/sql/sql_select.h index d4da44756c7..509e5ba67a6 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -544,6 +544,7 @@ typedef struct st_join_table { !(used_sjm_lookup_tables & ~emb_sj_nest->sj_inner_tables)); } + void remove_redundant_bnl_scan_conds(); } JOIN_TAB; From 929466401e494ea59ca5d75be13b966b86704a3e Mon Sep 17 00:00:00 2001 From: "timour@askmonty.org" Date: Fri, 18 Oct 2013 12:09:35 +0300 Subject: [PATCH 003/174] Removed extra empty line --- sql/sql_select.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e30b917fe1c..09c0a8e4daf 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -10555,7 +10555,6 @@ void JOIN_TAB::remove_redundant_bnl_scan_conds() cache->get_join_alg() == JOIN_CACHE::BNLH_JOIN_ALG))) return; - /* select->cond is not processed separately. This method assumes it is always the same as select_cond. From dc5fdacfe01c69c4da89c997c14e4742c7a156dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:06 -0700 Subject: [PATCH 004/174] Added the new roles_mapping table to mysql_system_tables.sql script. --- scripts/mysql_system_tables.sql | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index afacc0e144b..0288fbacf63 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -38,6 +38,15 @@ CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges'; +CREATE TABLE IF NOT EXISTS roles_mapping ( + HostFk char(60) binary DEFAULT '' NOT NULL, + UserFk char(16) binary DEFAULT '' NOT NULL, + RoleHostFK char(60) binary DEFAULT '' NOT NULL, + RoleUserFk char(16) binary DEFAULT '' NOT NULL, + CONSTRAINT FOREIGN KEY (HostFk, UserFk) REFERENCES user (Host, User), + CONSTRAINT FOREIGN KEY (RoleHostFk, RoleUserFk) REFERENCES user (Host, User) +); + -- Remember for later if user table already existed set @had_user_table= @@warning_count != 0; From f401de7a4ab1e25831926433a542b1a4675d6059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:10 -0700 Subject: [PATCH 005/174] Reordered entries to keep the had_user_table variable correct. --- scripts/mysql_system_tables.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index 0288fbacf63..a98a8dace10 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -38,6 +38,9 @@ CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges'; +-- Remember for later if user table already existed +set @had_user_table= @@warning_count != 0; + CREATE TABLE IF NOT EXISTS roles_mapping ( HostFk char(60) binary DEFAULT '' NOT NULL, UserFk char(16) binary DEFAULT '' NOT NULL, @@ -47,9 +50,6 @@ CREATE TABLE IF NOT EXISTS roles_mapping ( CONSTRAINT FOREIGN KEY (RoleHostFk, RoleUserFk) REFERENCES user (Host, User) ); --- Remember for later if user table already existed -set @had_user_table= @@warning_count != 0; - CREATE TABLE IF NOT EXISTS func ( name char(64) binary DEFAULT '' NOT NULL, ret tinyint(1) DEFAULT '0' NOT NULL, dl char(128) DEFAULT '' NOT NULL, type enum ('function','aggregate') COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (name) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='User defined functions'; From 8bb04a90c250fa350ef432c845acdb2ca8ee3d60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:15 -0700 Subject: [PATCH 006/174] Modified test result to accound for the roles_mapping table. --- mysql-test/r/1st.result | 1 + 1 file changed, 1 insertion(+) diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result index e847be49a91..51dec186915 100644 --- a/mysql-test/r/1st.result +++ b/mysql-test/r/1st.result @@ -25,6 +25,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats From 9f512dca3c8e97df634636e90f8c89725c77bd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:22 -0700 Subject: [PATCH 007/174] Initialized roles_mapping table. Performed a check to see if a mapping exists. --- sql/sql_acl.cc | 54 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 2c6364ed61f..b502a347535 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -739,8 +739,9 @@ set_user_plugin (ACL_USER *user, int password_len) SYNOPSIS acl_load() thd Current thread - tables List containing open "mysql.host", "mysql.user" and - "mysql.db" tables. + tables List containing open "mysql.host", "mysql.user", + "mysql.db", "mysql.proxies_priv" and "mysql.roles_mapping" + tables. RETURN VALUES FALSE Success @@ -877,6 +878,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&mem, table->field[0])); user.user= get_field(&mem, table->field[1]); + DBUG_PRINT("info", ("user_entry %s", user.user)); if (check_no_resolve && hostname_requires_resolving(user.host.hostname)) { sql_print_warning("'user' entry '%s@%s' " @@ -1123,6 +1125,37 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } freeze_size(&acl_proxy_users); + if (tables[4].table) + { + if (init_read_record(&read_record_info, thd, table= tables[4].table, + NULL, 1, 1, FALSE)) + goto end; + table->use_all_columns(); + /* account for every role mapping */ + mysql_mutex_lock(&acl_cache->lock); + while (!(read_record_info.read_record(&read_record_info))) + { + char *user_hostname= get_field(&mem, table->field[0]); + char *user_username= get_field(&mem, table->field[1]); + char *role_hostname= get_field(&mem, table->field[2]); + char *role_username= get_field(&mem, table->field[3]); + ACL_USER *user = find_acl_user(user_hostname, user_username, TRUE); + ACL_USER *role = find_acl_user(role_hostname, role_username, TRUE); + if (user == NULL || role == NULL) + { + sql_print_error("Warning: Invalid roles_mapping table entry"); + continue; + } + /* TEMPORARY */ + sql_print_information("Found user %s@%s having role granted %s@%s\n", + user->user, user->host.hostname, + role->user, role->host.hostname); + } + end_read_record(&read_record_info); + mysql_mutex_unlock(&acl_cache->lock); + + } + init_check_host(); initialized=1; @@ -1176,7 +1209,7 @@ void acl_free(bool end) my_bool acl_reload(THD *thd) { - TABLE_LIST tables[4]; + TABLE_LIST tables[5]; DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users; MEM_ROOT old_mem; bool old_initialized; @@ -1196,12 +1229,25 @@ my_bool acl_reload(THD *thd) tables[3].init_one_table(C_STRING_WITH_LEN("mysql"), C_STRING_WITH_LEN("proxies_priv"), "proxies_priv", TL_READ); + tables[4].init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("roles_mapping"), + "roles_mapping", TL_READ); tables[0].next_local= tables[0].next_global= tables + 1; tables[1].next_local= tables[1].next_global= tables + 2; tables[2].next_local= tables[2].next_global= tables + 3; + tables[3].next_local= tables[3].next_global= tables + 4; tables[0].open_type= tables[1].open_type= tables[2].open_type= - tables[3].open_type= OT_BASE_ONLY; + tables[3].open_type= tables[4].open_type= OT_BASE_ONLY; tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + /* + TODO should there be an OPEN_IF_EXISTS strategy aswell for roles_mapping? + I would say yes because the user table implies the existance of the + roles_mapping table only in versions starting from now on + Vicentiu + */ + tables[0].open_strategy= tables[3].open_strategy= + tables[4].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + if (open_and_lock_tables(thd, tables, FALSE, MYSQL_LOCK_IGNORE_TIMEOUT)) { From 875617c0892cde69428fb9cd7de0d4344e9dbc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:39 -0700 Subject: [PATCH 008/174] Added separation between roles and users in the mysql.user table --- sql/sql_acl.cc | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b502a347535..b14f44e9d5b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -542,7 +542,16 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #define NORMAL_HANDSHAKE_SIZE 6 -static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; +static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users, acl_roles; +/* XXX + ***** Potential optimization ***** + role_grants could potentially be a HASH with keys as usernames and values + as a DYNAMIC_ARRAY of pointers to granted roles + XXX + ***** Implementation choice for now ***** + An array representing mappings between acl_users and acl_roles; +*/ +static DYNAMIC_ARRAY role_grants; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; @@ -576,6 +585,11 @@ enum enum_acl_lists PROXY_USERS_ACL }; +typedef struct st_role_grant +{ + ACL_USER *user; + ACL_USER *role; +} ROLE_GRANT_PAIR; /* Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. @@ -829,6 +843,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); + (void) my_init_dynamic_array(&acl_roles,sizeof(ACL_USER), 50, 100, MYF(0)); username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; @@ -875,11 +890,20 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) while (!(read_record_info.read_record(&read_record_info))) { ACL_USER user; + bool is_role= FALSE; bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&mem, table->field[0])); user.user= get_field(&mem, table->field[1]); - DBUG_PRINT("info", ("user_entry %s", user.user)); - if (check_no_resolve && hostname_requires_resolving(user.host.hostname)) + + /* If the user entry is a role, skip password and hostname checks + A user can not log in with a role so some checks are not necessary + */ + if (!user.host.hostname) { + is_role= TRUE; + } + + if (!is_role && check_no_resolve && + hostname_requires_resolving(user.host.hostname)) { sql_print_warning("'user' entry '%s@%s' " "ignored in --skip-name-resolve mode.", @@ -894,7 +918,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.auth_string.length= password_len; set_user_salt(&user, password, password_len); - if (set_user_plugin(&user, password_len)) + if (!is_role && set_user_plugin(&user, password_len)) continue; { @@ -974,7 +998,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.user_resource.user_conn= ptr ? atoi(ptr) : 0; } - if (table->s->fields >= 41) + if (!is_role && table->s->fields >= 41) { /* We may have plugin & auth_String fields */ char *tmpstr= get_field(&mem, table->field[next_field++]); @@ -1016,7 +1040,13 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.access|= SUPER_ACL | EXECUTE_ACL; #endif } - (void) push_dynamic(&acl_users,(uchar*) &user); + if (is_role) { + (void) push_dynamic(&acl_roles,(uchar*) &user); + } + else + { + (void) push_dynamic(&acl_users,(uchar*) &user); + } if (!user.host.hostname || (user.host.hostname[0] == wild_many && !user.host.hostname[1])) allow_all_hosts=1; // Anyone can connect From 334860cccfa0b7428d1d0bdf17a453765a1962f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:49 -0700 Subject: [PATCH 009/174] Added roles mapping internal structure creation TODO: Free structures on flush --- sql/sql_acl.cc | 75 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b14f44e9d5b..c18bcafd942 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -566,6 +566,8 @@ static void init_check_host(void); static void rebuild_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); +static ACL_USER *find_acl_role(const char *host, const char *user, + my_bool exact); static bool update_user_table(THD *thd, TABLE *table, const char *host, const char *user, const char *new_password, uint new_password_len); @@ -587,8 +589,10 @@ enum enum_acl_lists typedef struct st_role_grant { - ACL_USER *user; - ACL_USER *role; + char *user_username; + char *user_hostname; + char *role_username; + char *role_hostname; } ROLE_GRANT_PAIR; /* Convert scrambled password to binary form, according to scramble type, @@ -1041,10 +1045,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) #endif } if (is_role) { + sql_print_information("Found role %s", user.user); (void) push_dynamic(&acl_roles,(uchar*) &user); } else { + sql_print_information("Found user %s", user.user); (void) push_dynamic(&acl_users,(uchar*) &user); } if (!user.host.hostname || @@ -1162,27 +1168,49 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) goto end; table->use_all_columns(); /* account for every role mapping */ - mysql_mutex_lock(&acl_cache->lock); + + /* acquire lock for the find_acl_user/role functions + XXX + Perhaps new wrapper functions should be created that do not check + for the lock in this case as it either is already taken or + it's the first initialisation so no race conditions possible + */ + if (!initialized) + mysql_mutex_lock(&acl_cache->lock); + (void) my_init_dynamic_array(&role_grants,sizeof(ROLE_GRANT_PAIR), 50, 100, + MYF(0)); while (!(read_record_info.read_record(&read_record_info))) { - char *user_hostname= get_field(&mem, table->field[0]); - char *user_username= get_field(&mem, table->field[1]); - char *role_hostname= get_field(&mem, table->field[2]); - char *role_username= get_field(&mem, table->field[3]); - ACL_USER *user = find_acl_user(user_hostname, user_username, TRUE); - ACL_USER *role = find_acl_user(role_hostname, role_username, TRUE); + ROLE_GRANT_PAIR p; + p.user_hostname= get_field(&mem, table->field[0]); + p.user_username= get_field(&mem, table->field[1]); + p.role_hostname= get_field(&mem, table->field[2]); + p.role_username= get_field(&mem, table->field[3]); + ACL_USER *user = find_acl_user((p.user_hostname) ? p.user_hostname: "", + (p.user_username) ? p.user_username: "", + TRUE); + ACL_USER *role = find_acl_role((p.role_hostname) ? p.role_hostname: "", + (p.role_username) ? p.role_username: "", + TRUE); if (user == NULL || role == NULL) { - sql_print_error("Warning: Invalid roles_mapping table entry"); + sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", + p.user_username ? p.user_username : "", + p.user_hostname ? p.user_hostname : "", + p.role_username ? p.role_username : "", + p.role_hostname ? p.role_hostname : "", + user, role); continue; } - /* TEMPORARY */ + + push_dynamic(&role_grants, (uchar*) &p); sql_print_information("Found user %s@%s having role granted %s@%s\n", user->user, user->host.hostname, role->user, role->host.hostname); } end_read_record(&read_record_info); - mysql_mutex_unlock(&acl_cache->lock); + if (!initialized) + mysql_mutex_unlock(&acl_cache->lock); } @@ -2110,20 +2138,22 @@ bool is_acl_user(const char *host, const char *user) /* - Find first entry that matches the current user + Find first entry that matches the current user or role */ - static ACL_USER * -find_acl_user(const char *host, const char *user, my_bool exact) +find_acl_user_table_entry(const char *host, const char *user, my_bool exact, + my_bool is_role) { + DBUG_ENTER("find_acl_user"); DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); mysql_mutex_assert_owner(&acl_cache->lock); - for (uint i=0 ; i < acl_users.elements ; i++) + DYNAMIC_ARRAY *target = (is_role) ? &acl_roles : &acl_users; + for (uint i=0 ; i < target->elements ; i++) { - ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + ACL_USER *acl_user=dynamic_element(target,i,ACL_USER*); DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", user, acl_user->user ? acl_user->user : "", host, @@ -2144,6 +2174,17 @@ find_acl_user(const char *host, const char *user, my_bool exact) DBUG_RETURN(0); } +static ACL_USER * +find_acl_role(const char *host, const char *user, my_bool exact) +{ + return find_acl_user_table_entry(host, user, exact, TRUE); +} +static ACL_USER * +find_acl_user(const char *host, const char *user, my_bool exact) +{ + return find_acl_user_table_entry(host, user, exact, FALSE); +} + /* Comparing of hostnames From e0c908ebe70c3be8b5fe0c0737d10cf462671307 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:57:58 -0700 Subject: [PATCH 010/174] Changed acl_roles to be stored into a HASH. There is an issue with correct searching of keys in the HASH. --- sql/sql_acl.cc | 98 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 27 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c18bcafd942..5f92a2c9a3e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -51,6 +51,7 @@ #include "hostname.h" #include "sql_db.h" #include "sql_array.h" +#include "sql_hset.h" #include "sql_plugin_compat.h" @@ -520,6 +521,13 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, return (uchar*) entry->key; } +uchar* acl_role_get_key(ACL_USER *entry, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length=(uint) (entry->user ? strlen(entry->user) : 0); + return (uchar*) entry->user; +} + #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3) #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \ 1 + USERNAME_LENGTH + 1) @@ -542,7 +550,10 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, #endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ #define NORMAL_HANDSHAKE_SIZE 6 -static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users, acl_roles; +#define ROLE_ASSIGN_COLUMN_IDX 42 + +static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; +static HASH acl_roles; /* XXX ***** Potential optimization ***** role_grants could potentially be a HASH with keys as usernames and values @@ -560,14 +571,14 @@ static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; /* Version of priv tables. incremented by acl_load */ static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0); +static bool check_is_role(TABLE *form); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); static void rebuild_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); -static ACL_USER *find_acl_role(const char *host, const char *user, - my_bool exact); +static ACL_USER *find_acl_role(const char *user); static bool update_user_table(THD *thd, TABLE *table, const char *host, const char *user, const char *new_password, uint new_password_len); @@ -847,7 +858,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); - (void) my_init_dynamic_array(&acl_roles,sizeof(ACL_USER), 50, 100, MYF(0)); + (void) my_hash_init2(&acl_roles,50,system_charset_info, + 0,0,0, (my_hash_get_key) acl_role_get_key, 0,0); + username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH); password_length= table->field[2]->field_length / table->field[2]->charset()->mbmaxlen; @@ -902,9 +915,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) /* If the user entry is a role, skip password and hostname checks A user can not log in with a role so some checks are not necessary */ - if (!user.host.hostname) { - is_role= TRUE; - } + is_role= check_is_role(table); if (!is_role && check_no_resolve && hostname_requires_resolving(user.host.hostname)) @@ -1046,7 +1057,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } if (is_role) { sql_print_information("Found role %s", user.user); - (void) push_dynamic(&acl_roles,(uchar*) &user); + my_hash_insert(&acl_roles, (uchar*) &user); } else { @@ -1169,7 +1180,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); /* account for every role mapping */ - /* acquire lock for the find_acl_user/role functions + /* acquire lock for the find_acl_user functions XXX Perhaps new wrapper functions should be created that do not check for the lock in this case as it either is already taken or @@ -1186,12 +1197,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) p.user_username= get_field(&mem, table->field[1]); p.role_hostname= get_field(&mem, table->field[2]); p.role_username= get_field(&mem, table->field[3]); - ACL_USER *user = find_acl_user((p.user_hostname) ? p.user_hostname: "", + ACL_USER *user= find_acl_user((p.user_hostname) ? p.user_hostname: "", (p.user_username) ? p.user_username: "", TRUE); - ACL_USER *role = find_acl_role((p.role_hostname) ? p.role_hostname: "", - (p.role_username) ? p.role_username: "", - TRUE); + ACL_USER *role= find_acl_role(p.role_username ? p.role_username: ""); if (user == NULL || role == NULL) { sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", @@ -1397,6 +1406,38 @@ static ulong get_access(TABLE *form, uint fieldnr, uint *next_field) return access_bits; } +/* + Check if a user entry in the user table is marked as being a role entry + + IMPLEMENTATION + Access the coresponding column and check the coresponding ENUM of the form + ENUM('N', 'Y') + + SYNOPSIS + check_is_role() + form an open table to read the entry from. + The record should be already read in table->record[0] + + RETURN VALUE + TRUE if the user is marked as a role + FALSE otherwise +*/ + +static inline bool check_is_role(TABLE *form) +{ + char buff[2]; + String res(buff, sizeof(buff), &my_charset_latin1); + /* Table version does not support roles */ + if (form->s->fields <= 42) + return FALSE; + + form->field[ROLE_ASSIGN_COLUMN_IDX]->val_str(&res); + if (my_toupper(&my_charset_latin1, res[0]) == 'Y') + return TRUE; + + return FALSE; +} + /* Return a number which, if sorted 'desc', puts strings in this order: @@ -2138,22 +2179,19 @@ bool is_acl_user(const char *host, const char *user) /* - Find first entry that matches the current user or role + Find first entry that matches the current user */ static ACL_USER * -find_acl_user_table_entry(const char *host, const char *user, my_bool exact, - my_bool is_role) +find_acl_user(const char *host, const char *user, my_bool exact) { - DBUG_ENTER("find_acl_user"); DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); mysql_mutex_assert_owner(&acl_cache->lock); - DYNAMIC_ARRAY *target = (is_role) ? &acl_roles : &acl_users; - for (uint i=0 ; i < target->elements ; i++) + for (uint i=0 ; i < acl_users.elements ; i++) { - ACL_USER *acl_user=dynamic_element(target,i,ACL_USER*); + ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", user, acl_user->user ? acl_user->user : "", host, @@ -2174,18 +2212,24 @@ find_acl_user_table_entry(const char *host, const char *user, my_bool exact, DBUG_RETURN(0); } +/* + Find first entry that matches the current user +*/ static ACL_USER * -find_acl_role(const char *host, const char *user, my_bool exact) +find_acl_role(const char *user) { - return find_acl_user_table_entry(host, user, exact, TRUE); -} -static ACL_USER * -find_acl_user(const char *host, const char *user, my_bool exact) -{ - return find_acl_user_table_entry(host, user, exact, FALSE); + DBUG_ENTER("find_acl_role"); + DBUG_PRINT("enter",("user: '%s'", user)); + + mysql_mutex_assert_owner(&acl_cache->lock); + + DBUG_RETURN((ACL_USER *)my_hash_search(&acl_roles, (uchar *)user, + user ? strlen(user) : 0)); } + + /* Comparing of hostnames From dd5a98da35d5e0107e02ce288a8cd64585e24550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:58:07 -0700 Subject: [PATCH 011/174] Fixed key search in HASH table --- sql/sql_acl.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5f92a2c9a3e..393e9db52fc 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1057,7 +1057,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } if (is_role) { sql_print_information("Found role %s", user.user); - my_hash_insert(&acl_roles, (uchar*) &user); + my_hash_insert(&acl_roles, (uchar*) user.copy(&mem)); } else { @@ -1242,6 +1242,8 @@ void acl_free(bool end) delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); delete_dynamic(&acl_proxy_users); + delete_dynamic(&role_grants); + my_hash_free(&acl_roles); my_hash_free(&acl_check_hosts); plugin_unlock(0, native_password_plugin); plugin_unlock(0, old_password_plugin); From aa465ac3ba1a917ef626153e83b5fd41ab6a964f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 14:58:37 -0700 Subject: [PATCH 012/174] Removed redundant #include "sql_hset.h" It was a leftover from attempting to use Hash_set --- sql/sql_acl.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 393e9db52fc..1122f4a471d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -51,7 +51,6 @@ #include "hostname.h" #include "sql_db.h" #include "sql_array.h" -#include "sql_hset.h" #include "sql_plugin_compat.h" From fdc166979389a43094000e5eeb596315b5cb29b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:00:30 -0700 Subject: [PATCH 013/174] Fixed memory leaks. role_grants is no longer used as it will be added to the ACL_USER class --- sql/sql_acl.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 1122f4a471d..04c64de43b4 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -553,15 +553,6 @@ uchar* acl_role_get_key(ACL_USER *entry, size_t *length, static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; static HASH acl_roles; -/* XXX - ***** Potential optimization ***** - role_grants could potentially be a HASH with keys as usernames and values - as a DYNAMIC_ARRAY of pointers to granted roles - XXX - ***** Implementation choice for now ***** - An array representing mappings between acl_users and acl_roles; -*/ -static DYNAMIC_ARRAY role_grants; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; @@ -1187,8 +1178,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) */ if (!initialized) mysql_mutex_lock(&acl_cache->lock); - (void) my_init_dynamic_array(&role_grants,sizeof(ROLE_GRANT_PAIR), 50, 100, + +/* (void) my_init_dynamic_array(&role_grants,sizeof(ROLE_GRANT_PAIR), 50, 100, MYF(0)); +*/ while (!(read_record_info.read_record(&read_record_info))) { ROLE_GRANT_PAIR p; @@ -1211,12 +1204,14 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) continue; } - push_dynamic(&role_grants, (uchar*) &p); +// push_dynamic(&role_grants, (uchar*) &p); sql_print_information("Found user %s@%s having role granted %s@%s\n", user->user, user->host.hostname, role->user, role->host.hostname); } + end_read_record(&read_record_info); + if (!initialized) mysql_mutex_unlock(&acl_cache->lock); @@ -1241,7 +1236,6 @@ void acl_free(bool end) delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); delete_dynamic(&acl_proxy_users); - delete_dynamic(&role_grants); my_hash_free(&acl_roles); my_hash_free(&acl_check_hosts); plugin_unlock(0, native_password_plugin); @@ -1279,6 +1273,7 @@ my_bool acl_reload(THD *thd) { TABLE_LIST tables[5]; DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users; + HASH old_acl_roles; MEM_ROOT old_mem; bool old_initialized; my_bool return_val= TRUE; @@ -1334,6 +1329,7 @@ my_bool acl_reload(THD *thd) old_acl_hosts= acl_hosts; old_acl_users= acl_users; + old_acl_roles= acl_roles; old_acl_proxy_users= acl_proxy_users; old_acl_dbs= acl_dbs; old_mem= mem; @@ -1346,6 +1342,7 @@ my_bool acl_reload(THD *thd) acl_free(); /* purecov: inspected */ acl_hosts= old_acl_hosts; acl_users= old_acl_users; + acl_roles= old_acl_roles; acl_proxy_users= old_acl_proxy_users; acl_dbs= old_acl_dbs; mem= old_mem; @@ -1358,6 +1355,7 @@ my_bool acl_reload(THD *thd) delete_dynamic(&old_acl_users); delete_dynamic(&old_acl_proxy_users); delete_dynamic(&old_acl_dbs); + my_hash_free(&old_acl_roles); } if (old_initialized) mysql_mutex_unlock(&acl_cache->lock); From dc4126657feffd6e893bc66ffbc866fe2a4d7189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:02:18 -0700 Subject: [PATCH 014/174] Refactored ACL_USER: Changed ACL_USER.user from char * to LEX_STRING. Refactored every section that made use of ACL_USER.user as a char*. This was done so as to be able to quickly check the hash_key of the acl_user. --- sql/sql_acl.cc | 74 +++++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 04c64de43b4..526e91962a0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -218,7 +218,7 @@ public: acl_host_and_ip host; uint hostname_length; USER_RESOURCES user_resource; - char *user; + LEX_STRING user; uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; @@ -232,7 +232,8 @@ public: if (!dst) return 0; *dst= *this; - dst->user= safe_strdup_root(root, user); + dst->user.str= safe_strdup_root(root, user.str); + dst->user.length= user.length; dst->ssl_cipher= safe_strdup_root(root, ssl_cipher); dst->x509_issuer= safe_strdup_root(root, x509_issuer); dst->x509_subject= safe_strdup_root(root, x509_subject); @@ -523,8 +524,8 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, uchar* acl_role_get_key(ACL_USER *entry, size_t *length, my_bool not_used __attribute__((unused))) { - *length=(uint) (entry->user ? strlen(entry->user) : 0); - return (uchar*) entry->user; + *length=(uint) entry->user.length; + return (uchar*) entry->user.str; } #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3) @@ -744,7 +745,7 @@ set_user_plugin (ACL_USER *user, int password_len) return FALSE; default: sql_print_warning("Found invalid password for user: '%s@%s'; " - "Ignoring user", user->user ? user->user : "", + "Ignoring user", user->user.str ? user->user.str : "", user->host.hostname ? user->host.hostname : ""); return TRUE; } @@ -900,7 +901,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) bool is_role= FALSE; bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&mem, table->field[0])); - user.user= get_field(&mem, table->field[1]); + char *username= get_field(&mem, table->field[1]); + user.user.str= username; + user.user.length= username? strlen(username) : 0; /* If the user entry is a role, skip password and hostname checks A user can not log in with a role so some checks are not necessary @@ -912,7 +915,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) { sql_print_warning("'user' entry '%s@%s' " "ignored in --skip-name-resolve mode.", - user.user ? user.user : "", + user.user.str ? user.user.str : "", user.host.hostname ? user.host.hostname : ""); continue; } @@ -1016,7 +1019,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) sql_print_warning("'user' entry '%s@%s' has both a password " "and an authentication plugin specified. The " "password will be ignored.", - user.user ? user.user : "", + user.user.str ? user.user.str : "", user.host.hostname ? user.host.hostname : ""); } user.auth_string.str= get_field(&mem, table->field[next_field++]); @@ -1046,12 +1049,12 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) #endif } if (is_role) { - sql_print_information("Found role %s", user.user); + sql_print_information("Found role %s", user.user.str); my_hash_insert(&acl_roles, (uchar*) user.copy(&mem)); } else { - sql_print_information("Found user %s", user.user); + sql_print_information("Found user %s", user.user.str); (void) push_dynamic(&acl_users,(uchar*) &user); } if (!user.host.hostname || @@ -1206,8 +1209,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) // push_dynamic(&role_grants, (uchar*) &p); sql_print_information("Found user %s@%s having role granted %s@%s\n", - user->user, user->host.hostname, - role->user, role->host.hostname); + user->user.str, user->host.hostname, + role->user.str, role->host.hostname); } end_read_record(&read_record_info); @@ -1547,8 +1550,8 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, for (i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); - if ((!acl_user_tmp->user && !user[0]) || - (acl_user_tmp->user && strcmp(user, acl_user_tmp->user) == 0)) + if ((!acl_user_tmp->user.str && !user[0]) || + (acl_user_tmp->user.str && strcmp(user, acl_user_tmp->user.str) == 0)) { if (compare_hostname(&acl_user_tmp->host, host, ip)) { @@ -1579,7 +1582,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, } sctx->master_access= acl_user->access; - if (acl_user->user) + if (acl_user->user.str) strmake_buf(sctx->priv_user, user); else *sctx->priv_user= 0; @@ -1617,8 +1620,8 @@ static void acl_update_user(const char *user, const char *host, for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); - if ((!acl_user->user && !user[0]) || - (acl_user->user && !strcmp(user,acl_user->user))) + if ((!acl_user->user.str && !user[0]) || + (acl_user->user.str && !strcmp(user,acl_user->user.str))) { if ((!acl_user->host.hostname && !host[0]) || (acl_user->host.hostname && @@ -1683,7 +1686,8 @@ static void acl_insert_user(const char *user, const char *host, mysql_mutex_assert_owner(&acl_cache->lock); - acl_user.user=*user ? strdup_root(&mem,user) : 0; + acl_user.user.str=*user ? strdup_root(&mem,user) : 0; + acl_user.user.length= strlen(user); update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0); if (plugin->str[0]) { @@ -2120,7 +2124,7 @@ bool change_password(THD *thd, const char *host, const char *user, if (update_user_table(thd, table, acl_user->host.hostname ? acl_user->host.hostname : "", - acl_user->user ? acl_user->user : "", + acl_user->user.str ? acl_user->user.str : "", new_password, new_password_len)) { mysql_mutex_unlock(&acl_cache->lock); /* purecov: deadcode */ @@ -2134,7 +2138,7 @@ bool change_password(THD *thd, const char *host, const char *user, { query_length= sprintf(buff,"SET PASSWORD FOR '%-.120s'@'%-.120s'='%-.120s'", - acl_user->user ? acl_user->user : "", + acl_user->user.str ? acl_user->user.str : "", acl_user->host.hostname ? acl_user->host.hostname : "", new_password); thd->clear_error(); @@ -2192,12 +2196,12 @@ find_acl_user(const char *host, const char *user, my_bool exact) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", - user, acl_user->user ? acl_user->user : "", + user, acl_user->user.str ? acl_user->user.str : "", host, acl_user->host.hostname ? acl_user->host.hostname : "")); - if ((!acl_user->user && !user[0]) || - (acl_user->user && !strcmp(user,acl_user->user))) + if ((!acl_user->user.str && !user[0]) || + (acl_user->user.str && !strcmp(user,acl_user->user.str))) { if (exact ? !my_strcasecmp(system_charset_info, host, acl_user->host.hostname ? @@ -5996,7 +6000,7 @@ ACL_USER *check_acl_user(LEX_USER *user_name, { const char *user,*host; acl_user= dynamic_element(&acl_users, counter, ACL_USER*); - if (!(user=acl_user->user)) + if (!(user=acl_user->user.str)) user= ""; if (!(host=acl_user->host.hostname)) host= ""; @@ -6304,7 +6308,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, switch (struct_no) { case USER_ACL: acl_user= dynamic_element(&acl_users, idx, ACL_USER*); - user= acl_user->user; + user= acl_user->user.str; host= acl_user->host.hostname; break; @@ -6382,7 +6386,8 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, { switch ( struct_no ) { case USER_ACL: - acl_user->user= strdup_root(&mem, user_to->user.str); + acl_user->user.str= strdup_root(&mem, user_to->user.str); + acl_user->user.length= user_to->user.length; acl_user->host.hostname= strdup_root(&mem, user_to->host.str); break; @@ -7480,7 +7485,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) { const char *user,*host, *is_grantable="YES"; acl_user=dynamic_element(&acl_users,counter,ACL_USER*); - if (!(user=acl_user->user)) + if (!(user=acl_user->user.str)) user= ""; if (!(host=acl_user->host.hostname)) host= ""; @@ -8282,8 +8287,9 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) for (uint i=0; i < acl_users.elements; i++) { ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); - if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) && - compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) + if ((!acl_user_tmp->user.str || + !strcmp(sctx->user, acl_user_tmp->user.str)) && + compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) { mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); break; @@ -8340,8 +8346,8 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) mpvio->auth_info.user_name_length= strlen(sctx->user); mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str; mpvio->auth_info.auth_string_length= (unsigned long) mpvio->acl_user->auth_string.length; - strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ? - mpvio->acl_user->user : ""); + strmake_buf(mpvio->auth_info.authenticated_as, mpvio->acl_user->user.str ? + mpvio->acl_user->user.str : ""); DBUG_PRINT("info", ("exit: user=%s, auth_string=%s, authenticated as=%s" "plugin=%s", @@ -9202,7 +9208,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, { #ifndef NO_EMBEDDED_ACCESS_CHECKS bool is_proxy_user= FALSE; - const char *auth_user = acl_user->user ? acl_user->user : ""; + const char *auth_user = acl_user->user.str ? acl_user->user.str : ""; ACL_PROXY_USER *proxy_user; /* check if the user is allowed to proxy as another user */ proxy_user= acl_find_proxy_user(auth_user, sctx->host, sctx->ip, @@ -9242,8 +9248,8 @@ bool acl_authenticate(THD *thd, uint connect_errors, #endif sctx->master_access= acl_user->access; - if (acl_user->user) - strmake_buf(sctx->priv_user, acl_user->user); + if (acl_user->user.str) + strmake_buf(sctx->priv_user, acl_user->user.str); else *sctx->priv_user= 0; From 71a504ca16131c69b642a8d193d4f0e2c33d492c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:02:29 -0700 Subject: [PATCH 015/174] Whitespace fixes --- sql/sql_acl.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 526e91962a0..b6e965b2109 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -850,7 +850,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); (void) my_hash_init2(&acl_roles,50,system_charset_info, - 0,0,0, (my_hash_get_key) acl_role_get_key, 0,0); + 0,0,0, (my_hash_get_key) acl_role_get_key, 0,0); username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH); password_length= table->field[2]->field_length / @@ -9279,10 +9279,10 @@ bool acl_authenticate(THD *thd, uint connect_errors, acl_user->user_resource.updates || acl_user->user_resource.conn_per_hour || acl_user->user_resource.user_conn || max_user_connections_checking) && - get_or_create_user_conn(thd, - (opt_old_style_user_limits ? sctx->user : sctx->priv_user), - (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), - &acl_user->user_resource)) + get_or_create_user_conn(thd, + (opt_old_style_user_limits ? sctx->user : sctx->priv_user), + (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), + &acl_user->user_resource)) DBUG_RETURN(1); // The error is set by get_or_create_user_conn() } else @@ -9292,7 +9292,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, (thd->user_connect->user_resources.conn_per_hour || thd->user_connect->user_resources.user_conn || max_user_connections_checking) && - check_for_max_user_connections(thd, thd->user_connect)) + check_for_max_user_connections(thd, thd->user_connect)) { /* Ensure we don't decrement thd->user_connections->connections twice */ thd->user_connect= 0; From ba42300be097b1abe48fd741662be7384d5eff1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:02:38 -0700 Subject: [PATCH 016/174] Stripped whitespaces on all lines from sql/sql_acl.cc --- sql/sql_acl.cc | 222 +++++++++++++++++++++++++------------------------ 1 file changed, 114 insertions(+), 108 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b6e965b2109..379304e530d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -59,15 +59,15 @@ bool mysql_user_table_is_in_short_password_format= false; static const TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { { - { C_STRING_WITH_LEN("Host") }, + { C_STRING_WITH_LEN("Host") }, { C_STRING_WITH_LEN("char(60)") }, {NULL, 0} - }, + }, { - { C_STRING_WITH_LEN("Db") }, + { C_STRING_WITH_LEN("Db") }, { C_STRING_WITH_LEN("char(64)") }, {NULL, 0} - }, + }, { { C_STRING_WITH_LEN("User") }, { C_STRING_WITH_LEN("char(") }, @@ -176,11 +176,11 @@ mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 } static LEX_STRING native_password_plugin_name= { C_STRING_WITH_LEN("mysql_native_password") }; - + static LEX_STRING old_password_plugin_name= { C_STRING_WITH_LEN("mysql_old_password") }; - + /// @todo make it configurable LEX_STRING *default_auth_plugin_name= &native_password_plugin_name; @@ -220,11 +220,17 @@ public: USER_RESOURCES user_resource; LEX_STRING user; uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form - uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 + uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_STRING plugin; LEX_STRING auth_string; + /* + list to hold references to granted roles (ACL_USER instances) + if the instance of the class represents a user, or a user if the + instance of the class represents a role. + */ + DYNAMIC_ARRAY role_grants; ACL_USER *copy(MEM_ROOT *root) { @@ -272,11 +278,11 @@ class ACL_PROXY_USER :public ACL_ACCESS const char *proxied_user; bool with_grant; - typedef enum { - MYSQL_PROXIES_PRIV_HOST, - MYSQL_PROXIES_PRIV_USER, + typedef enum { + MYSQL_PROXIES_PRIV_HOST, + MYSQL_PROXIES_PRIV_USER, MYSQL_PROXIES_PRIV_PROXIED_HOST, - MYSQL_PROXIES_PRIV_PROXIED_USER, + MYSQL_PROXIES_PRIV_PROXIED_USER, MYSQL_PROXIES_PRIV_WITH_GRANT, MYSQL_PROXIES_PRIV_GRANTOR, MYSQL_PROXIES_PRIV_TIMESTAMP } old_acl_proxy_users; @@ -288,11 +294,11 @@ public: bool with_grant_arg) { user= (user_arg && *user_arg) ? user_arg : NULL; - update_hostname (&host, + update_hostname (&host, (host_arg && *host_arg) ? host_arg : NULL); - proxied_user= (proxied_user_arg && *proxied_user_arg) ? + proxied_user= (proxied_user_arg && *proxied_user_arg) ? proxied_user_arg : NULL; - update_hostname (&proxied_host, + update_hostname (&proxied_host, (proxied_host_arg && *proxied_host_arg) ? proxied_host_arg : NULL); with_grant= with_grant_arg; @@ -306,9 +312,9 @@ public: { init ((host_arg && *host_arg) ? strdup_root (mem, host_arg) : NULL, (user_arg && *user_arg) ? strdup_root (mem, user_arg) : NULL, - (proxied_host_arg && *proxied_host_arg) ? + (proxied_host_arg && *proxied_host_arg) ? strdup_root (mem, proxied_host_arg) : NULL, - (proxied_user_arg && *proxied_user_arg) ? + (proxied_user_arg && *proxied_user_arg) ? strdup_root (mem, proxied_user_arg) : NULL, with_grant_arg); } @@ -327,20 +333,20 @@ public: const char *get_host() { return host.hostname; } const char *get_proxied_user() { return proxied_user; } const char *get_proxied_host() { return proxied_host.hostname; } - void set_user(MEM_ROOT *mem, const char *user_arg) - { + void set_user(MEM_ROOT *mem, const char *user_arg) + { user= user_arg && *user_arg ? strdup_root(mem, user_arg) : NULL; } - void set_host(MEM_ROOT *mem, const char *host_arg) - { - update_hostname(&host, - (host_arg && *host_arg) ? + void set_host(MEM_ROOT *mem, const char *host_arg) + { + update_hostname(&host, + (host_arg && *host_arg) ? strdup_root(mem, host_arg) : NULL); } bool check_validity(bool check_no_resolve) { - if (check_no_resolve && + if (check_no_resolve && (hostname_requires_resolving(host.hostname) || hostname_requires_resolving(proxied_host.hostname))) { @@ -377,8 +383,8 @@ public: compare_hostname(&proxied_host, host_arg, ip_arg) && (!user || (user_arg && !wild_compare(user_arg, user, TRUE))) && - (!proxied_user || - (proxied_user && !wild_compare(proxied_user_arg, + (!proxied_user || + (proxied_user && !wild_compare(proxied_user_arg, proxied_user, TRUE)))); } @@ -403,13 +409,13 @@ public: host.hostname ? host.hostname : "", grant->host.hostname ? grant->host.hostname : "", proxied_host.hostname ? proxied_host.hostname : "", - grant->proxied_host.hostname ? + grant->proxied_host.hostname ? grant->proxied_host.hostname : "")); DBUG_RETURN(auth_element_equals(user, grant->user) && auth_element_equals(proxied_user, grant->proxied_user) && auth_element_equals(host.hostname, grant->host.hostname) && - auth_element_equals(proxied_host.hostname, + auth_element_equals(proxied_host.hostname, grant->proxied_host.hostname)); } @@ -447,10 +453,10 @@ public: with_grant= grant->with_grant; } - static int store_pk(TABLE *table, - const LEX_STRING *host, + static int store_pk(TABLE *table, + const LEX_STRING *host, const LEX_STRING *user, - const LEX_STRING *proxied_host, + const LEX_STRING *proxied_host, const LEX_STRING *proxied_user) { DBUG_ENTER("ACL_PROXY_USER::store_pk"); @@ -459,11 +465,11 @@ public: user->str ? user->str : "", proxied_host->str ? proxied_host->str : "", proxied_user->str ? proxied_user->str : "")); - if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str, + if (table->field[MYSQL_PROXIES_PRIV_HOST]->store(host->str, host->length, system_charset_info)) DBUG_RETURN(TRUE); - if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str, + if (table->field[MYSQL_PROXIES_PRIV_USER]->store(user->str, user->length, system_charset_info)) DBUG_RETURN(TRUE); @@ -491,10 +497,10 @@ public: if (store_pk(table, host, user, proxied_host, proxied_user)) DBUG_RETURN(TRUE); DBUG_PRINT("info", ("with_grant=%s", with_grant ? "TRUE" : "FALSE")); - if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, - TRUE)) + if (table->field[MYSQL_PROXIES_PRIV_WITH_GRANT]->store(with_grant ? 1 : 0, + TRUE)) DBUG_RETURN(TRUE); - if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, + if (table->field[MYSQL_PROXIES_PRIV_GRANTOR]->store(grantor, strlen(grantor), system_charset_info)) DBUG_RETURN(TRUE); @@ -597,7 +603,7 @@ typedef struct st_role_grant char *role_hostname; } ROLE_GRANT_PAIR; /* - Convert scrambled password to binary form, according to scramble type, + Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. */ @@ -657,7 +663,7 @@ static bool fix_user_plugin_ptr(ACL_USER *user) user->plugin= old_password_plugin_name; else return true; - + set_user_salt(user, user->auth_string.str, user->auth_string.length); return false; } @@ -734,7 +740,7 @@ my_bool acl_init(bool dont_read_acl_tables) static bool set_user_plugin (ACL_USER *user, int password_len) { - switch (password_len) + switch (password_len) { case 0: /* no password */ case SCRAMBLED_PASSWORD_CHAR_LENGTH: @@ -1134,7 +1140,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) end_read_record(&read_record_info); freeze_size(&acl_dbs); - (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), + (void) my_init_dynamic_array(&acl_proxy_users, sizeof(ACL_PROXY_USER), 50, 100, MYF(0)); if (tables[3].table) { @@ -2110,7 +2116,7 @@ bool change_password(THD *thd, const char *host, const char *user, } /* update loaded acl entry: */ - if (acl_user->plugin.str == native_password_plugin_name.str || + if (acl_user->plugin.str == native_password_plugin_name.str || acl_user->plugin.str == old_password_plugin_name.str) { acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len); @@ -2811,7 +2817,7 @@ abort: } -static void +static void acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) { mysql_mutex_assert_owner(&acl_cache->lock); @@ -2819,7 +2825,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) DBUG_ENTER("acl_update_proxy_user"); for (uint i= 0; i < acl_proxy_users.elements; i++) { - ACL_PROXY_USER *acl_user= + ACL_PROXY_USER *acl_user= dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *); if (acl_user->pk_equals(new_value)) @@ -2841,7 +2847,7 @@ acl_update_proxy_user(ACL_PROXY_USER *new_value, bool is_revoke) } -static void +static void acl_insert_proxy_user(ACL_PROXY_USER *new_value) { DBUG_ENTER("acl_insert_proxy_user"); @@ -2854,9 +2860,9 @@ acl_insert_proxy_user(ACL_PROXY_USER *new_value) } -static int +static int replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user, - const LEX_USER *proxied_user, bool with_grant_arg, + const LEX_USER *proxied_user, bool with_grant_arg, bool revoke_grant) { bool old_row_exists= 0; @@ -2881,7 +2887,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user, } table->use_all_columns(); - ACL_PROXY_USER::store_pk (table, &user->host, &user->user, + ACL_PROXY_USER::store_pk (table, &user->host, &user->user, &proxied_user->host, &proxied_user->user); key_copy(user_key, table->record[0], table->key_info, @@ -3866,7 +3872,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, } #endif - /* + /* The lock api is depending on the thd->lex variable which needs to be re-initialized. */ @@ -3901,7 +3907,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { result= TRUE; continue; - } + } /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, @@ -4107,7 +4113,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, { result= TRUE; continue; - } + } /* Create user if needed */ error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, @@ -4146,7 +4152,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } if (replace_routine_table(thd, grant_name, tables[1].table, *Str, - db_name, table_name, is_proc, rights, + db_name, table_name, is_proc, rights, revoke_grant) != 0) { result= TRUE; @@ -4212,12 +4218,12 @@ bool mysql_grant(THD *thd, const char *db, List &list, tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), C_STRING_WITH_LEN("proxies_priv"), - "proxies_priv", + "proxies_priv", TL_WRITE); else tables[1].init_one_table(C_STRING_WITH_LEN("mysql"), - C_STRING_WITH_LEN("db"), - "db", + C_STRING_WITH_LEN("db"), + "db", TL_WRITE); tables[0].next_local= tables[0].next_global= tables+1; @@ -4289,7 +4295,7 @@ bool mysql_grant(THD *thd, const char *db, List &list, else if (is_proxy) { if (replace_proxies_priv_table (thd, tables[1].table, Str, proxied_user, - rights & GRANT_ACL ? TRUE : FALSE, + rights & GRANT_ACL ? TRUE : FALSE, revoke_grant)) result= -1; } @@ -4973,7 +4979,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, grant= &(table_ref->grant); db_name= table_ref->view_db.str; table_name= table_ref->view_name.str; - if (table_ref->belong_to_view && + if (table_ref->belong_to_view && thd->lex->sql_command == SQLCOM_SHOW_FIELDS) { view_privs= get_column_grant(thd, grant, db_name, table_name, name); @@ -5005,7 +5011,7 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, } -/** +/** @brief check if a query can access a set of columns @param thd the current thread @@ -5014,24 +5020,24 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, @return Operation status @retval 0 Success @retval 1 Falure - @details This function walks over the columns of a table reference + @details This function walks over the columns of a table reference The columns may originate from different tables, depending on the kind of table reference, e.g. join, view. For each table it will retrieve the grant information and will use it to check the required access privileges for the fields requested from it. -*/ -bool check_grant_all_columns(THD *thd, ulong want_access_arg, +*/ +bool check_grant_all_columns(THD *thd, ulong want_access_arg, Field_iterator_table_ref *fields) { Security_context *sctx= thd->security_ctx; ulong want_access= want_access_arg; const char *table_name= NULL; - const char* db_name; + const char* db_name; GRANT_INFO *grant; /* Initialized only to make gcc happy */ GRANT_TABLE *grant_table= NULL; - /* + /* Flag that gets set if privilege checking has to be performed on column level. */ @@ -5069,7 +5075,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, if (want_access) { - GRANT_COLUMN *grant_column= + GRANT_COLUMN *grant_column= column_hash_search(grant_table, field_name, (uint) strlen(field_name)); if (grant_column) @@ -5093,7 +5099,7 @@ err: if (using_column_privileges) my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user, - sctx->host_or_ip, table_name); + sctx->host_or_ip, table_name); else my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, @@ -5240,9 +5246,9 @@ err: /* - Check if routine has any of the + Check if routine has any of the routine level grants - + SYNPOSIS bool check_routine_level_acl() thd Thread handler @@ -5250,11 +5256,11 @@ err: name Routine name RETURN - 0 Ok + 0 Ok 1 error */ -bool check_routine_level_acl(THD *thd, const char *db, const char *name, +bool check_routine_level_acl(THD *thd, const char *db, const char *name, bool is_proc) { bool no_routine_acl= 1; @@ -5747,7 +5753,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } } - if (show_routine_grants(thd, lex_user, &proc_priv_hash, + if (show_routine_grants(thd, lex_user, &proc_priv_hash, STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff))) { error= -1; @@ -6049,7 +6055,7 @@ static int modify_grant_table(TABLE *table, Field *host_field, system_charset_info); user_field->store(user_to->user.str, user_to->user.length, system_charset_info); - if ((error= table->file->ha_update_row(table->record[1], + if ((error= table->file->ha_update_row(table->record[1], table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME) table->file->print_error(error, MYF(0)); @@ -6173,7 +6179,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'", table->s->table_name.str, user_str, host_str)); #endif - while ((error= table->file->ha_rnd_next(table->record[0])) != + while ((error= table->file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) { if (error) @@ -6723,7 +6729,7 @@ bool mysql_drop_user(THD *thd, List &list) { result= TRUE; continue; - } + } if (handle_grant_data(tables, 1, user_name, NULL) <= 0) { append_user(&wrong_users, user_name); @@ -6789,13 +6795,13 @@ bool mysql_rename_user(THD *thd, List &list) { result= TRUE; continue; - } + } tmp_user_to= user_list++; if (!(user_to= get_current_user(thd, tmp_user_to))) { result= TRUE; continue; - } + } DBUG_ASSERT(user_to != 0); /* Syntax enforces pairs of users. */ /* @@ -6811,7 +6817,7 @@ bool mysql_rename_user(THD *thd, List &list) } some_users_renamed= TRUE; } - + /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); @@ -6819,7 +6825,7 @@ bool mysql_rename_user(THD *thd, List &list) if (result) my_error(ER_CANNOT_USER, MYF(0), "RENAME USER", wrong_users.c_ptr_safe()); - + if (some_users_renamed && mysql_bin_log.is_open()) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); @@ -6866,7 +6872,7 @@ bool mysql_revoke_all(THD *thd, List &list) { result= -1; continue; - } + } if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE)) { result= -1; @@ -7262,7 +7268,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, @thd current thread @param user the logged in user (proxy user) - @param authenticated_as the effective user a plugin is trying to + @param authenticated_as the effective user a plugin is trying to impersonate as (proxied user) @return proxy user definition @retval NULL proxy user definition not found or not applicable @@ -7270,7 +7276,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, */ static ACL_PROXY_USER * -acl_find_proxy_user(const char *user, const char *host, const char *ip, +acl_find_proxy_user(const char *user, const char *host, const char *ip, const char *authenticated_as, bool *proxy_used) { uint i; @@ -7285,10 +7291,10 @@ acl_find_proxy_user(const char *user, const char *host, const char *ip, DBUG_RETURN (NULL); } - *proxy_used= TRUE; + *proxy_used= TRUE; for (i=0; i < acl_proxy_users.elements; i++) { - ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, + ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *); if (proxy->matches(host, user, ip, authenticated_as)) DBUG_RETURN(proxy); @@ -7303,7 +7309,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, bool with_grant) { DBUG_ENTER("acl_check_proxy_grant_access"); - DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host, + DBUG_PRINT("info", ("user=%s host=%s with_grant=%d", user, host, (int) with_grant)); if (!initialized) { @@ -7334,7 +7340,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, !my_strcasecmp(system_charset_info, host, thd->security_ctx->priv_host)) { - DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", + DBUG_PRINT("info", ("strcmp (%s, %s) my_casestrcmp (%s, %s) equal", thd->security_ctx->priv_user, user, host, thd->security_ctx->priv_host)); DBUG_RETURN(FALSE); @@ -7343,7 +7349,7 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, /* check for matching WITH PROXY rights */ for (uint i=0; i < acl_proxy_users.elements; i++) { - ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, + ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *); if (proxy->matches(thd->security_ctx->host, thd->security_ctx->user, @@ -7494,7 +7500,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) (strcmp(thd->security_ctx->priv_user, user) || my_strcasecmp(system_charset_info, curr_host, host))) continue; - + want_access= acl_user->access; if (!(want_access & GRANT_ACL)) is_grantable= "NO"; @@ -7517,7 +7523,7 @@ int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond) { if (test_access & j) { - if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0, + if (update_schema_privilege(thd, table, buff, 0, 0, 0, 0, command_array[priv_id], command_lengths[priv_id], is_grantable)) { @@ -7688,7 +7694,7 @@ int fill_schema_table_privileges(THD *thd, TABLE_LIST *tables, COND *cond) } } } - } + } } err: mysql_rwlock_unlock(&LOCK_grant); @@ -8022,9 +8028,9 @@ static void login_failed_error(THD *thd) thd->main_security_ctx.host_or_ip, thd->password ? ER(ER_YES) : ER(ER_NO)); status_var_increment(thd->status_var.access_denied_errors); - /* + /* Log access denied messages to the error log when log-warnings = 2 - so that the overhead of the general query log is not required to track + so that the overhead of the general query log is not required to track failed connections. */ if (global_system_variables.log_warnings > 1) @@ -8032,7 +8038,7 @@ static void login_failed_error(THD *thd) sql_print_warning(ER(access_denied_error_code(thd->password)), thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip, - thd->password ? ER(ER_YES) : ER(ER_NO)); + thd->password ? ER(ER_YES) : ER(ER_NO)); } } @@ -8041,7 +8047,7 @@ static void login_failed_error(THD *thd) after the connection was established Packet format: - + Bytes Content ----- ---- 1 protocol version (always 10) @@ -8135,7 +8141,7 @@ static bool send_server_handshake_packet(MPVIO_EXT *mpvio, end= (char*) memcpy(end, data, SCRAMBLE_LENGTH_323); end+= SCRAMBLE_LENGTH_323; *end++= 0; - + int2store(end, thd->client_capabilities); /* write server characteristics: up to 16 bytes allowed */ end[2]= (char) default_charset_info->number; @@ -8165,7 +8171,7 @@ static bool secure_auth(THD *thd) return 0; /* - If the server is running in secure auth mode, short scrambles are + If the server is running in secure auth mode, short scrambles are forbidden. Extra juggling to report the same error as the old code. */ if (thd->client_capabilities & CLIENT_PROTOCOL_41) @@ -8190,7 +8196,7 @@ static bool secure_auth(THD *thd) using a different authentication plugin Packet format: - + Bytes Content ----- ---- 1 byte with the value 254 @@ -8256,7 +8262,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, DBUG_RETURN (1); } - DBUG_PRINT("info", ("requesting client to use the %s plugin", + DBUG_PRINT("info", ("requesting client to use the %s plugin", client_auth_plugin)); DBUG_RETURN(net_write_command(net, switch_plugin_request_buf[0], (uchar*) client_auth_plugin, @@ -8267,7 +8273,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, #ifndef NO_EMBEDDED_ACCESS_CHECKS /** Finds acl entry in user database for authentication purposes. - + Finds a user and copies it into mpvio. Creates a fake user if no matching user account is found. @@ -8476,7 +8482,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) /* For a passwordless accounts we use native_password_plugin. But when an old 4.0 client connects to it, we change it to - old_password_plugin, otherwise MySQL will think that server + old_password_plugin, otherwise MySQL will think that server and client plugins don't match. */ if (mpvio->acl_user->auth_string.length == 0) @@ -8485,9 +8491,9 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) } DBUG_PRINT("info", ("client_plugin=%s, restart", client_plugin)); - /* - Remember the data part of the packet, to present it to plugin in - read_packet() + /* + Remember the data part of the packet, to present it to plugin in + read_packet() */ mpvio->cached_client_reply.pkt= passwd; mpvio->cached_client_reply.pkt_len= passwd_len; @@ -8703,14 +8709,14 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, /* For a passwordless accounts we use native_password_plugin. But when an old 4.0 client connects to it, we change it to - old_password_plugin, otherwise MySQL will think that server + old_password_plugin, otherwise MySQL will think that server and client plugins don't match. */ if (mpvio->acl_user->auth_string.length == 0) mpvio->acl_user->plugin= old_password_plugin_name; } } - + /* if the acl_user needs a different plugin to authenticate (specified in GRANT ... AUTHENTICATED VIA plugin_name ..) @@ -9016,7 +9022,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) #else /* HAVE_OPENSSL */ default: /* - If we don't have SSL but SSL is required for this user the + If we don't have SSL but SSL is required for this user the authentication should fail. */ return 1; @@ -9122,7 +9128,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, mpvio.status= MPVIO_EXT::FAILURE; mpvio.make_it_fail= false; mpvio.auth_info.host_or_ip= thd->security_ctx->host_or_ip; - mpvio.auth_info.host_or_ip_length= + mpvio.auth_info.host_or_ip_length= (unsigned int) strlen(thd->security_ctx->host_or_ip); DBUG_PRINT("info", ("com_change_user_pkt_len=%u", com_change_user_pkt_len)); @@ -9150,7 +9156,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, the correct plugin. */ - res= do_auth_once(thd, auth_plugin_name, &mpvio); + res= do_auth_once(thd, auth_plugin_name, &mpvio); } /* @@ -9170,7 +9176,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, Security_context *sctx= thd->security_ctx; const ACL_USER *acl_user= mpvio.acl_user; - thd->password= mpvio.auth_info.password_used; // remember for error messages + thd->password= mpvio.auth_info.password_used; // remember for error messages /* Log the command here so that the user can check the log @@ -9232,7 +9238,7 @@ bool acl_authenticate(THD *thd, uint connect_errors, /* we're proxying : find the proxy user definition */ mysql_mutex_lock(&acl_cache->lock); - acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? + acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? proxy_user->get_proxied_host() : "", mpvio.auth_info.authenticated_as, TRUE); if (!acl_proxy_user) @@ -9447,7 +9453,7 @@ static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, DBUG_RETURN(CR_ERROR); } -static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, +static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info) { uchar *pkt; @@ -9494,7 +9500,7 @@ static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, return CR_ERROR; return check_scramble_323(pkt, thd->scramble, - (ulong *) mpvio->acl_user->salt) ? + (ulong *) mpvio->acl_user->salt) ? CR_ERROR : CR_OK; } From 69a3deb58e1842d6b9eae093a01f2bd39d5f9fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:02:47 -0700 Subject: [PATCH 017/174] Modify mysql.user table to contain a is_user column. --- scripts/mysql_system_tables.sql | 2 +- scripts/mysql_system_tables_data.sql | 8 ++++---- scripts/mysql_system_tables_fix.sql | 5 +++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index a98a8dace10..bb397e495a9 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -36,7 +36,7 @@ set @had_db_table= @@warning_count != 0; CREATE TABLE IF NOT EXISTS host ( Host char(60) binary DEFAULT '' NOT NULL, Db char(64) binary DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,Db) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Host privileges; Merged with database privileges'; -CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges'; +CREATE TABLE IF NOT EXISTS user ( Host char(60) binary DEFAULT '' NOT NULL, User char(16) binary DEFAULT '' NOT NULL, Password char(41) character set latin1 collate latin1_bin DEFAULT '' NOT NULL, Select_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Insert_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Update_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Delete_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Drop_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Reload_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Shutdown_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Process_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, File_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Grant_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, References_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Index_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_db_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Super_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tmp_table_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Lock_tables_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Execute_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_slave_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Repl_client_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Show_view_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Alter_routine_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_user_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Event_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Trigger_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, Create_tablespace_priv enum('N','Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, ssl_type enum('','ANY','X509', 'SPECIFIED') COLLATE utf8_general_ci DEFAULT '' NOT NULL, ssl_cipher BLOB NOT NULL, x509_issuer BLOB NOT NULL, x509_subject BLOB NOT NULL, max_questions int(11) unsigned DEFAULT 0 NOT NULL, max_updates int(11) unsigned DEFAULT 0 NOT NULL, max_connections int(11) unsigned DEFAULT 0 NOT NULL, max_user_connections int(11) DEFAULT 0 NOT NULL, plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, authentication_string TEXT NOT NULL, is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL, PRIMARY KEY Host (Host,User) ) engine=MyISAM CHARACTER SET utf8 COLLATE utf8_bin comment='Users and global privileges'; -- Remember for later if user table already existed set @had_user_table= @@warning_count != 0; diff --git a/scripts/mysql_system_tables_data.sql b/scripts/mysql_system_tables_data.sql index a222d22b670..3c84bd65374 100644 --- a/scripts/mysql_system_tables_data.sql +++ b/scripts/mysql_system_tables_data.sql @@ -40,10 +40,10 @@ DROP TABLE tmp_db; -- Fill "user" table with default users allowing root access -- from local machine if "user" table didn't exist before CREATE TEMPORARY TABLE tmp_user LIKE user; -INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'',''); -REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','' FROM dual WHERE LOWER( @current_hostname) != 'localhost'; -REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'',''); -REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'',''); +INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N'); +REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N' FROM dual WHERE LOWER( @current_hostname) != 'localhost'; +REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N'); +REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,'','','N'); INSERT INTO tmp_user (host,user) VALUES ('localhost',''); INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE LOWER(@current_hostname ) != 'localhost'; INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0; diff --git a/scripts/mysql_system_tables_fix.sql b/scripts/mysql_system_tables_fix.sql index c2d63f12399..5e09901a444 100644 --- a/scripts/mysql_system_tables_fix.sql +++ b/scripts/mysql_system_tables_fix.sql @@ -50,6 +50,11 @@ ADD x509_issuer BLOB NOT NULL, ADD x509_subject BLOB NOT NULL; ALTER TABLE user MODIFY ssl_type enum('','ANY','X509', 'SPECIFIED') NOT NULL; +# +# Add roles to the user table +ALTER TABLE user +ADD is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL; + # # tables_priv # From 9e7228dc4ae7cc9eee1df93eb0aeec540ca20c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:02:55 -0700 Subject: [PATCH 018/174] Added implementation for DYNAMIC_ARRAY in ACL_USER TODO: Memory allocated for the array is never freed --- sql/sql_acl.cc | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 379304e530d..20b3bbd5a0d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -230,6 +230,7 @@ public: if the instance of the class represents a user, or a user if the instance of the class represents a role. */ + //TODO this array does not get freed automatically when acl_users is freed DYNAMIC_ARRAY role_grants; ACL_USER *copy(MEM_ROOT *root) @@ -597,10 +598,8 @@ enum enum_acl_lists typedef struct st_role_grant { - char *user_username; - char *user_hostname; - char *role_username; - char *role_hostname; + char *username; + char *hostname; } ROLE_GRANT_PAIR; /* Convert scrambled password to binary form, according to scramble type, @@ -1054,6 +1053,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.access|= SUPER_ACL | EXECUTE_ACL; #endif } + + (void) my_init_dynamic_array(&user.role_grants,sizeof(ROLE_GRANT_PAIR), + 50, 100, MYF(0)); + if (is_role) { sql_print_information("Found role %s", user.user.str); my_hash_insert(&acl_roles, (uchar*) user.copy(&mem)); @@ -1188,32 +1191,31 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (!initialized) mysql_mutex_lock(&acl_cache->lock); -/* (void) my_init_dynamic_array(&role_grants,sizeof(ROLE_GRANT_PAIR), 50, 100, - MYF(0)); -*/ while (!(read_record_info.read_record(&read_record_info))) { - ROLE_GRANT_PAIR p; - p.user_hostname= get_field(&mem, table->field[0]); - p.user_username= get_field(&mem, table->field[1]); - p.role_hostname= get_field(&mem, table->field[2]); - p.role_username= get_field(&mem, table->field[3]); - ACL_USER *user= find_acl_user((p.user_hostname) ? p.user_hostname: "", - (p.user_username) ? p.user_username: "", + ROLE_GRANT_PAIR role_ref; + ROLE_GRANT_PAIR user_ref; + user_ref.hostname= get_field(&mem, table->field[0]); + user_ref.username= get_field(&mem, table->field[1]); + role_ref.hostname= get_field(&mem, table->field[2]); + role_ref.username= get_field(&mem, table->field[3]); + ACL_USER *user= find_acl_user((user_ref.hostname) ? user_ref.hostname: "", + (user_ref.username) ? user_ref.username: "", TRUE); - ACL_USER *role= find_acl_role(p.role_username ? p.role_username: ""); + ACL_USER *role= find_acl_role(role_ref.username ? role_ref.username: ""); if (user == NULL || role == NULL) { sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", - p.user_username ? p.user_username : "", - p.user_hostname ? p.user_hostname : "", - p.role_username ? p.role_username : "", - p.role_hostname ? p.role_hostname : "", + user_ref.username ? user_ref.username : "", + user_ref.hostname ? user_ref.hostname : "", + role_ref.username ? role_ref.username : "", + role_ref.hostname ? role_ref.hostname : "", user, role); continue; } -// push_dynamic(&role_grants, (uchar*) &p); + push_dynamic(&user->role_grants, (uchar*) &role_ref); + push_dynamic(&role->role_grants, (uchar*) &user_ref); sql_print_information("Found user %s@%s having role granted %s@%s\n", user->user.str, user->host.hostname, role->user.str, role->host.hostname); From 89229fb71c5c0db7958915b9ed10a6d0f7f4b0a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:04 -0700 Subject: [PATCH 019/174] Added a delete_function for DYNAMIC_ARRAY. The function calls delete_dynamic, after if calls a free function on every array element. --- include/my_sys.h | 3 +++ mysys/array.c | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/my_sys.h b/include/my_sys.h index 6f2997843a7..e1d07b457bb 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -335,6 +335,9 @@ struct st_my_file_info extern struct st_my_file_info *my_file_info; +/* Free function pointer */ +typedef void (*FREE_FUNC)(void *); + typedef struct st_dynamic_array { uchar *buffer; diff --git a/mysys/array.c b/mysys/array.c index f72b8951870..fe75f2a536c 100644 --- a/mysys/array.c +++ b/mysys/array.c @@ -321,7 +321,24 @@ void delete_dynamic_element(DYNAMIC_ARRAY *array, uint idx) (array->elements-idx)*array->size_of_element); } +/* + Wrapper around delete_dynamic, calling a FREE function on every + element, before releasing the memory + SYNOPSIS + delete_dynamic_recursive() + array + f The function to be called on every element before + deleting the array; +*/ +void delete_dynamic_recursive(DYNAMIC_ARRAY *array, FREE_FUNC f) { + uint i; + char *ptr= (char*) array->buffer; + for (i= 0; i < array->elements; i++, ptr+= array->size_of_element) { + f(ptr); + } + delete_dynamic(array); +} /* Free unused memory From deffce1acea76ceea76765b5907245f887f8de3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:12 -0700 Subject: [PATCH 020/174] Free some memory leaks Still problems with hashtable acl_roles Need to create a copy of grant_roles, currently it uses the same buffer --- include/my_sys.h | 1 + sql/sql_acl.cc | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/my_sys.h b/include/my_sys.h index e1d07b457bb..0c8314a5519 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -794,6 +794,7 @@ extern my_bool allocate_dynamic(DYNAMIC_ARRAY *array, uint max_elements); extern void get_dynamic(DYNAMIC_ARRAY *array,uchar * element,uint array_index); extern void delete_dynamic(DYNAMIC_ARRAY *array); extern void delete_dynamic_element(DYNAMIC_ARRAY *array, uint array_index); +extern void delete_dynamic_recursive(DYNAMIC_ARRAY *array, FREE_FUNC f); extern void freeze_size(DYNAMIC_ARRAY *array); extern int get_index_dynamic(DYNAMIC_ARRAY *array, uchar * element); #define dynamic_array_ptr(array,array_index) ((array)->buffer+(array_index)*(array)->size_of_element) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 20b3bbd5a0d..4705e152f37 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -230,7 +230,6 @@ public: if the instance of the class represents a user, or a user if the instance of the class represents a role. */ - //TODO this array does not get freed automatically when acl_users is freed DYNAMIC_ARRAY role_grants; ACL_USER *copy(MEM_ROOT *root) @@ -251,6 +250,7 @@ public: dst->plugin.str= strmake_root(root, plugin.str, plugin.length); dst->auth_string.str= safe_strdup_root(root, auth_string.str); dst->host.hostname= safe_strdup_root(root, host.hostname); + dst->role_grants= this->role_grants; return dst; } }; @@ -574,6 +574,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); static void rebuild_check_host(void); +static void free_acl_user(ACL_USER *acl_user); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); static ACL_USER *find_acl_role(const char *user); @@ -601,6 +602,13 @@ typedef struct st_role_grant char *username; char *hostname; } ROLE_GRANT_PAIR; + +static +void +free_acl_user(ACL_USER *user) +{ + delete_dynamic(&(user->role_grants)); +} /* Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. @@ -1060,6 +1068,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (is_role) { sql_print_information("Found role %s", user.user.str); my_hash_insert(&acl_roles, (uchar*) user.copy(&mem)); + continue; } else { @@ -1243,7 +1252,7 @@ void acl_free(bool end) { free_root(&mem,MYF(0)); delete_dynamic(&acl_hosts); - delete_dynamic(&acl_users); + delete_dynamic_recursive(&acl_users, (FREE_FUNC)free_acl_user); delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); delete_dynamic(&acl_proxy_users); @@ -1363,7 +1372,7 @@ my_bool acl_reload(THD *thd) { free_root(&old_mem,MYF(0)); delete_dynamic(&old_acl_hosts); - delete_dynamic(&old_acl_users); + delete_dynamic_recursive(&old_acl_users, (FREE_FUNC) free_acl_user); delete_dynamic(&old_acl_proxy_users); delete_dynamic(&old_acl_dbs); my_hash_free(&old_acl_roles); @@ -1907,7 +1916,8 @@ static void init_check_host(void) acl_users.elements, 1, MYF(0)); (void) my_hash_init(&acl_check_hosts,system_charset_info, acl_users.elements, 0, 0, - (my_hash_get_key) check_get_key, 0, 0); + (my_hash_get_key) check_get_key, + (void (*)(void *))free_acl_user, 0); if (!allow_all_hosts) { for (uint i=0 ; i < acl_users.elements ; i++) From 887a1ac862abd28256ae251a40c5eb00dca0d4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:21 -0700 Subject: [PATCH 021/174] Implemented Roles Mappings association between users and roles. No more memory leaks in the code. --- sql/sql_acl.cc | 174 +++++++++++++++++++++++++++++++++++++------------ sql/sql_acl.h | 2 +- 2 files changed, 132 insertions(+), 44 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4705e152f37..c3589461b9f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -250,7 +250,7 @@ public: dst->plugin.str= strmake_root(root, plugin.str, plugin.length); dst->auth_string.str= safe_strdup_root(root, auth_string.str); dst->host.hostname= safe_strdup_root(root, host.hostname); - dst->role_grants= this->role_grants; + bzero(&dst->role_grants, sizeof(role_grants)); return dst; } }; @@ -262,7 +262,6 @@ public: char *user,*db; }; - #ifndef NO_EMBEDDED_ACCESS_CHECKS static void update_hostname(acl_host_and_ip *host, const char *hostname); static ulong get_sort(uint count,...); @@ -528,13 +527,29 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, return (uchar*) entry->key; } -uchar* acl_role_get_key(ACL_USER *entry, size_t *length, - my_bool not_used __attribute__((unused))) +static uchar* acl_role_get_key(ACL_USER *entry, size_t *length, + my_bool not_used __attribute__((unused))) { *length=(uint) entry->user.length; return (uchar*) entry->user.str; } +typedef struct st_role_grant +{ + char *u_uname; + char *u_hname; + char *r_uname; + char *r_hname; + LEX_STRING hashkey; +} ROLE_GRANT_PAIR; + +static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length=(uint) entry->hashkey.length; + return (uchar*) entry->hashkey.str; +} + #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3) #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \ 1 + USERNAME_LENGTH + 1) @@ -561,6 +576,13 @@ uchar* acl_role_get_key(ACL_USER *entry, size_t *length, static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; static HASH acl_roles; +/* + An hash containing mappings user <--> role + + A hash is used so as to make updates quickly + The hashkey used represents all the entries combined +*/ +static HASH acl_roles_mappings; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; @@ -584,6 +606,11 @@ static bool update_user_table(THD *thd, TABLE *table, const char *host, static my_bool acl_load(THD *thd, TABLE_LIST *tables); static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); +static my_bool acl_user_reset_grant(ACL_USER *user, + void * not_used __attribute__((unused))); +static my_bool add_role_user_mapping(ROLE_GRANT_PAIR *entry, + void * not_used __attribute__((unused))); + /* Enumeration of various ACL's and Hashes used in handle_grant_struct() */ @@ -597,12 +624,6 @@ enum enum_acl_lists PROXY_USERS_ACL }; -typedef struct st_role_grant -{ - char *username; - char *hostname; -} ROLE_GRANT_PAIR; - static void free_acl_user(ACL_USER *user) @@ -863,7 +884,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); (void) my_hash_init2(&acl_roles,50,system_charset_info, - 0,0,0, (my_hash_get_key) acl_role_get_key, 0,0); + 0,0,0, (my_hash_get_key) acl_role_get_key, + (void (*)(void *))free_acl_user, 0); username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH); password_length= table->field[2]->field_length / @@ -1062,17 +1084,23 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) #endif } - (void) my_init_dynamic_array(&user.role_grants,sizeof(ROLE_GRANT_PAIR), + (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_USER *), 50, 100, MYF(0)); if (is_role) { - sql_print_information("Found role %s", user.user.str); - my_hash_insert(&acl_roles, (uchar*) user.copy(&mem)); + DBUG_PRINT("info", ("Found role %s", user.user.str)); + ACL_USER *entry= user.copy(&mem); + entry->role_grants = user.role_grants; + my_hash_insert(&acl_roles, (uchar *)entry); + HASH_SEARCH_STATE t; + entry= (ACL_USER *) my_hash_first(&acl_roles, + (uchar *)entry->user.str, entry->user.length, &t); + continue; } else { - sql_print_information("Found user %s", user.user.str); + DBUG_PRINT("info", ("Found user %s", user.user.str)); (void) push_dynamic(&acl_users,(uchar*) &user); } if (!user.host.hostname || @@ -1200,34 +1228,42 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (!initialized) mysql_mutex_lock(&acl_cache->lock); + (void) my_hash_init2(&acl_roles_mappings,50,system_charset_info, + 0,0,0, (my_hash_get_key) acl_role_map_get_key, 0,0); while (!(read_record_info.read_record(&read_record_info))) { - ROLE_GRANT_PAIR role_ref; - ROLE_GRANT_PAIR user_ref; - user_ref.hostname= get_field(&mem, table->field[0]); - user_ref.username= get_field(&mem, table->field[1]); - role_ref.hostname= get_field(&mem, table->field[2]); - role_ref.username= get_field(&mem, table->field[3]); - ACL_USER *user= find_acl_user((user_ref.hostname) ? user_ref.hostname: "", - (user_ref.username) ? user_ref.username: "", - TRUE); - ACL_USER *role= find_acl_role(role_ref.username ? role_ref.username: ""); - if (user == NULL || role == NULL) - { + ROLE_GRANT_PAIR *mapping = (ROLE_GRANT_PAIR *)alloc_root( + &mem, + sizeof(ROLE_GRANT_PAIR)); + mapping->u_hname= get_field(&mem, table->field[0]); + mapping->u_uname= get_field(&mem, table->field[1]); + mapping->r_hname= get_field(&mem, table->field[2]); + mapping->r_uname= get_field(&mem, table->field[3]); + + size_t len[4] = {mapping->u_hname ? strlen(mapping->u_hname) : 0, + mapping->u_uname ? strlen(mapping->u_uname) : 0, + mapping->r_hname ? strlen(mapping->r_hname) : 0, + mapping->r_uname ? strlen(mapping->r_uname) : 0}; + char *buff= (char *)alloc_root(&mem, len[0] + len[1] + len[2] + len[3] + 1); + memcpy(buff, mapping->u_hname, len[0]); + memcpy(buff + len[0], mapping->u_uname, len[1]); + memcpy(buff + len[0] + len[1], mapping->r_hname, len[2]); + memcpy(buff + len[0] + len[1] + len[2], mapping->r_uname, len[3]); + buff[len[0] + len[1] + len[2] + len[3]] = '\0'; + mapping->hashkey.str = buff; + mapping->hashkey.length = len[0] + len[1] + len[2] + len[3]; + + if (add_role_user_mapping(mapping, NULL) == 1) { sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", - user_ref.username ? user_ref.username : "", - user_ref.hostname ? user_ref.hostname : "", - role_ref.username ? role_ref.username : "", - role_ref.hostname ? role_ref.hostname : "", - user, role); + mapping->u_uname ? mapping->u_uname : "", + mapping->u_hname ? mapping->u_hname : "", + mapping->r_uname ? mapping->r_uname : "", + mapping->r_hname ? mapping->r_hname : ""); continue; } - push_dynamic(&user->role_grants, (uchar*) &role_ref); - push_dynamic(&role->role_grants, (uchar*) &user_ref); - sql_print_information("Found user %s@%s having role granted %s@%s\n", - user->user.str, user->host.hostname, - role->user.str, role->host.hostname); + my_hash_insert(&acl_roles_mappings, (uchar*) mapping); + } end_read_record(&read_record_info); @@ -1250,14 +1286,15 @@ end: void acl_free(bool end) { + my_hash_free(&acl_roles); free_root(&mem,MYF(0)); delete_dynamic(&acl_hosts); - delete_dynamic_recursive(&acl_users, (FREE_FUNC)free_acl_user); + delete_dynamic_recursive(&acl_users, (FREE_FUNC) free_acl_user); delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); delete_dynamic(&acl_proxy_users); - my_hash_free(&acl_roles); my_hash_free(&acl_check_hosts); + my_hash_free(&acl_roles_mappings); plugin_unlock(0, native_password_plugin); plugin_unlock(0, old_password_plugin); if (!end) @@ -1293,7 +1330,7 @@ my_bool acl_reload(THD *thd) { TABLE_LIST tables[5]; DYNAMIC_ARRAY old_acl_hosts, old_acl_users, old_acl_dbs, old_acl_proxy_users; - HASH old_acl_roles; + HASH old_acl_roles, old_acl_roles_mappings; MEM_ROOT old_mem; bool old_initialized; my_bool return_val= TRUE; @@ -1350,6 +1387,7 @@ my_bool acl_reload(THD *thd) old_acl_hosts= acl_hosts; old_acl_users= acl_users; old_acl_roles= acl_roles; + old_acl_roles_mappings= acl_roles_mappings; old_acl_proxy_users= acl_proxy_users; old_acl_dbs= acl_dbs; old_mem= mem; @@ -1363,6 +1401,7 @@ my_bool acl_reload(THD *thd) acl_hosts= old_acl_hosts; acl_users= old_acl_users; acl_roles= old_acl_roles; + acl_roles_mappings= old_acl_roles_mappings; acl_proxy_users= old_acl_proxy_users; acl_dbs= old_acl_dbs; mem= old_mem; @@ -1370,12 +1409,14 @@ my_bool acl_reload(THD *thd) } else { + my_hash_free(&old_acl_roles); free_root(&old_mem,MYF(0)); delete_dynamic(&old_acl_hosts); delete_dynamic_recursive(&old_acl_users, (FREE_FUNC) free_acl_user); delete_dynamic(&old_acl_proxy_users); delete_dynamic(&old_acl_dbs); - my_hash_free(&old_acl_roles); + my_hash_free(&old_acl_roles_mappings); + my_hash_free(&old_acl_roles_mappings); } if (old_initialized) mysql_mutex_unlock(&acl_cache->lock); @@ -1916,8 +1957,7 @@ static void init_check_host(void) acl_users.elements, 1, MYF(0)); (void) my_hash_init(&acl_check_hosts,system_charset_info, acl_users.elements, 0, 0, - (my_hash_get_key) check_get_key, - (void (*)(void *))free_acl_user, 0); + (my_hash_get_key) check_get_key, 0, 0); if (!allow_all_hosts) { for (uint i=0 ; i < acl_users.elements ; i++) @@ -1972,7 +2012,55 @@ void rebuild_check_host(void) init_check_host(); } +/* + Rebuild the role grants every time the acl_users is modified + The role grants in the ACL_USER class need to be rebuilt, as they contain + pointers to elements of the acl_users array. +*/ + +my_bool acl_user_reset_grant(ACL_USER *user, + void * not_used __attribute__((unused))) +{ + reset_dynamic(&user->role_grants); + return 0; +} + +my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping, + void * not_used __attribute__((unused))) +{ + ACL_USER *user= find_acl_user((mapping->u_hname) ? mapping->u_hname: "", + (mapping->u_uname) ? mapping->u_uname: "", + TRUE); + ACL_USER *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); + if (user == NULL || role == NULL) + return 1; + + push_dynamic(&user->role_grants, (uchar*) role); + push_dynamic(&role->role_grants, (uchar*) user); + + DBUG_PRINT("info", ("Found user %s@%s having role granted %s@%s\n", + user->user.str, user->host.hostname, + role->user.str, role->host.hostname)); + return 0; +} + + +void rebuild_role_grants(void) +{ + /* + Reset every user's and role's role_grants array + */ + for (uint i=0; i < acl_users.elements; i++) { + ACL_USER * user = dynamic_element(&acl_users, i, ACL_USER *); + reset_dynamic(&user->role_grants); + } + my_hash_iterate(&acl_roles, + (my_hash_walk_action) acl_user_reset_grant, NULL); + + my_hash_iterate(&acl_roles_mappings, + (my_hash_walk_action) add_role_user_mapping, NULL); +} /* Return true if there is no users that can match the given host */ bool acl_check_host(const char *host, const char *ip) diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 3169746419c..3bc1b1eae45 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -215,7 +215,7 @@ bool check_grant_column (THD *thd, GRANT_INFO *grant, const char *name, uint length, Security_context *sctx); bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, const char *name, uint length); -bool check_grant_all_columns(THD *thd, ulong want_access, +bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, TABLE_LIST *procs, bool is_proc, bool no_error); From de523f7fad0cce160753ffe556d5bf632cd03aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:30 -0700 Subject: [PATCH 022/174] Refactored function to allow for better code clarity. --- sql/sql_acl.cc | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c3589461b9f..83cf3315a81 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -608,8 +608,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); static my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))); -static my_bool add_role_user_mapping(ROLE_GRANT_PAIR *entry, - void * not_used __attribute__((unused))); +static my_bool add_role_user_mapping(ROLE_GRANT_PAIR *entry); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -1253,7 +1252,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) mapping->hashkey.str = buff; mapping->hashkey.length = len[0] + len[1] + len[2] + len[3]; - if (add_role_user_mapping(mapping, NULL) == 1) { + if (add_role_user_mapping(mapping) == 1) { sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", mapping->u_uname ? mapping->u_uname : "", mapping->u_hname ? mapping->u_hname : "", @@ -2026,8 +2025,12 @@ my_bool acl_user_reset_grant(ACL_USER *user, return 0; } -my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping, - void * not_used __attribute__((unused))) +/* + Add a the coresponding pointers present in the mapping to the entries in + acl_users and acl_roles +*/ + +my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) { ACL_USER *user= find_acl_user((mapping->u_hname) ? mapping->u_hname: "", (mapping->u_uname) ? mapping->u_uname: "", @@ -2046,6 +2049,15 @@ my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping, } +static my_bool roles_mappings_walk_action(ROLE_GRANT_PAIR *mapping, + void * not_used __attribute__((unused))) +{ + if (add_role_user_mapping(mapping)) { + //the mapping is invalid, the mapping can be safely deleted + my_hash_delete(&acl_roles_mappings, (uchar*) mapping); + } + return 0; +} void rebuild_role_grants(void) { /* @@ -2059,7 +2071,7 @@ void rebuild_role_grants(void) (my_hash_walk_action) acl_user_reset_grant, NULL); my_hash_iterate(&acl_roles_mappings, - (my_hash_walk_action) add_role_user_mapping, NULL); + (my_hash_walk_action) roles_mappings_walk_action, 0); } /* Return true if there is no users that can match the given host */ From 7e18b8f1d656484602f9ec337476c649e383cd92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:40 -0700 Subject: [PATCH 023/174] Moved comment in code to correct place for rebuild_role_grants --- sql/sql_acl.cc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 83cf3315a81..8412c0e44dc 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2011,12 +2011,6 @@ void rebuild_check_host(void) init_check_host(); } -/* - Rebuild the role grants every time the acl_users is modified - - The role grants in the ACL_USER class need to be rebuilt, as they contain - pointers to elements of the acl_users array. -*/ my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))) @@ -2058,6 +2052,14 @@ static my_bool roles_mappings_walk_action(ROLE_GRANT_PAIR *mapping, } return 0; } + +/* + Rebuild the role grants every time the acl_users is modified + + The role grants in the ACL_USER class need to be rebuilt, as they contain + pointers to elements of the acl_users array. +*/ + void rebuild_role_grants(void) { /* From e876aa2b3b7abc8377314e8d44b2c187b62db565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:49 -0700 Subject: [PATCH 024/174] Fixed memory leak caused by user deletion, aswell as invalid free caused by user creation. --- sql/sql_acl.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 8412c0e44dc..d83d29a24fb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1772,6 +1772,8 @@ static void acl_insert_user(const char *user, const char *host, acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0; acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0; acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0; + (void) my_init_dynamic_array(&acl_user.role_grants, sizeof(ACL_USER *), + 50, 100, MYF(0)); (void) push_dynamic(&acl_users,(uchar*) &acl_user); if (!acl_user.host.hostname || @@ -6474,6 +6476,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, elements--; switch ( struct_no ) { case USER_ACL: + free_acl_user(dynamic_element(&acl_users, idx, ACL_USER*)); delete_dynamic_element(&acl_users, idx); break; From 5acc0578793c153d98ef6aa4447e5b03f0997195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:03:58 -0700 Subject: [PATCH 025/174] Roles mappings are now being kept consistent when acl_users gets modified. No cascading changes take place during a user rename. This needs to be addressed. --- sql/sql_acl.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d83d29a24fb..b8584d624af 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -596,6 +596,7 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); static void rebuild_check_host(void); +static void rebuild_role_grants(void); static void free_acl_user(ACL_USER *acl_user); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); @@ -1784,6 +1785,9 @@ static void acl_insert_user(const char *user, const char *host, /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); + /* Rebuild every user's role_grants because the acl_user has been modified + and some grants might now be invalid */ + rebuild_role_grants(); } @@ -6858,6 +6862,9 @@ bool mysql_drop_user(THD *thd, List &list) /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); + /* Rebuild every user's role_grants because the acl_user has been modified + and some grants might now be invalid */ + rebuild_role_grants(); mysql_mutex_unlock(&acl_cache->lock); @@ -6937,6 +6944,9 @@ bool mysql_rename_user(THD *thd, List &list) /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ rebuild_check_host(); + /* Rebuild every user's role_grants because the acl_user has been modified + and some grants might now be invalid */ + rebuild_role_grants(); mysql_mutex_unlock(&acl_cache->lock); From ee1e66468f9e514fa6c5b1a140393654fd71b352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:05:06 -0700 Subject: [PATCH 026/174] Removed no longer required TODO --- sql/sql_acl.cc | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b8584d624af..e073e1a1e63 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1358,13 +1358,6 @@ my_bool acl_reload(THD *thd) tables[3].next_local= tables[3].next_global= tables + 4; tables[0].open_type= tables[1].open_type= tables[2].open_type= tables[3].open_type= tables[4].open_type= OT_BASE_ONLY; - tables[3].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; - /* - TODO should there be an OPEN_IF_EXISTS strategy aswell for roles_mapping? - I would say yes because the user table implies the existance of the - roles_mapping table only in versions starting from now on - Vicentiu - */ tables[0].open_strategy= tables[3].open_strategy= tables[4].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; From 071c4ce88b04347c7d4933b1f6974f16981c0169 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:06:09 -0700 Subject: [PATCH 027/174] Removed no longer needed RoleHostFK as it is not used to link to a Role. Also removed code that loads that column into memory. --- scripts/mysql_system_tables.sql | 5 +---- sql/sql_acl.cc | 21 ++++++++------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/scripts/mysql_system_tables.sql b/scripts/mysql_system_tables.sql index bb397e495a9..de6c33fb4f9 100644 --- a/scripts/mysql_system_tables.sql +++ b/scripts/mysql_system_tables.sql @@ -44,10 +44,7 @@ set @had_user_table= @@warning_count != 0; CREATE TABLE IF NOT EXISTS roles_mapping ( HostFk char(60) binary DEFAULT '' NOT NULL, UserFk char(16) binary DEFAULT '' NOT NULL, - RoleHostFK char(60) binary DEFAULT '' NOT NULL, - RoleUserFk char(16) binary DEFAULT '' NOT NULL, - CONSTRAINT FOREIGN KEY (HostFk, UserFk) REFERENCES user (Host, User), - CONSTRAINT FOREIGN KEY (RoleHostFk, RoleUserFk) REFERENCES user (Host, User) + RoleFk char(16) binary DEFAULT '' NOT NULL ); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index e073e1a1e63..9c180c25527 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -539,7 +539,6 @@ typedef struct st_role_grant char *u_uname; char *u_hname; char *r_uname; - char *r_hname; LEX_STRING hashkey; } ROLE_GRANT_PAIR; @@ -1237,28 +1236,24 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) sizeof(ROLE_GRANT_PAIR)); mapping->u_hname= get_field(&mem, table->field[0]); mapping->u_uname= get_field(&mem, table->field[1]); - mapping->r_hname= get_field(&mem, table->field[2]); - mapping->r_uname= get_field(&mem, table->field[3]); + mapping->r_uname= get_field(&mem, table->field[2]); - size_t len[4] = {mapping->u_hname ? strlen(mapping->u_hname) : 0, + size_t len[3] = {mapping->u_hname ? strlen(mapping->u_hname) : 0, mapping->u_uname ? strlen(mapping->u_uname) : 0, - mapping->r_hname ? strlen(mapping->r_hname) : 0, mapping->r_uname ? strlen(mapping->r_uname) : 0}; - char *buff= (char *)alloc_root(&mem, len[0] + len[1] + len[2] + len[3] + 1); + char *buff= (char *)alloc_root(&mem, len[0] + len[1] + len[2] + 1); memcpy(buff, mapping->u_hname, len[0]); memcpy(buff + len[0], mapping->u_uname, len[1]); - memcpy(buff + len[0] + len[1], mapping->r_hname, len[2]); - memcpy(buff + len[0] + len[1] + len[2], mapping->r_uname, len[3]); - buff[len[0] + len[1] + len[2] + len[3]] = '\0'; + memcpy(buff + len[0] + len[1] + len[2], mapping->r_uname, len[2]); + buff[len[0] + len[1] + len[2]] = '\0'; mapping->hashkey.str = buff; - mapping->hashkey.length = len[0] + len[1] + len[2] + len[3]; + mapping->hashkey.length = len[0] + len[1] + len[2]; if (add_role_user_mapping(mapping) == 1) { - sql_print_error("Invalid roles_mapping table entry '%s@%s', '%s@%s'", + sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'", mapping->u_uname ? mapping->u_uname : "", mapping->u_hname ? mapping->u_hname : "", - mapping->r_uname ? mapping->r_uname : "", - mapping->r_hname ? mapping->r_hname : ""); + mapping->r_uname ? mapping->r_uname : ""); continue; } From 573c73225e6828d56ca11ce25b9387e8819e4e86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:06:20 -0700 Subject: [PATCH 028/174] open_grant_tables now also opens roles_mapping table --- sql/sql_acl.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9c180c25527..34eb4c5f700 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2007,7 +2007,7 @@ void rebuild_check_host(void) my_bool acl_user_reset_grant(ACL_USER *user, - void * not_used __attribute__((unused))) + void * not_used __attribute__((unused))) { reset_dynamic(&user->role_grants); return 0; @@ -6023,7 +6023,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) SYNOPSIS open_grant_tables() thd The current thread. - tables (out) The 4 elements array for the opened tables. + tables (out) The 7 elements array for the opened tables. DESCRIPTION Tables are numbered as follows: @@ -6031,6 +6031,9 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) 1 db 2 tables_priv 3 columns_priv + 4 columns_priv + 5 proxies_priv + 6 roles_mapping RETURN 1 Skip GRANT handling during replication. @@ -6038,7 +6041,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) < 0 Error. */ -#define GRANT_TABLES 6 +#define GRANT_TABLES 7 int open_grant_tables(THD *thd, TABLE_LIST *tables) { Rpl_filter *rpl_filter= thd->rpl_filter; @@ -6066,13 +6069,19 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) (tables+5)->init_one_table(C_STRING_WITH_LEN("mysql"), C_STRING_WITH_LEN("proxies_priv"), "proxies_priv", TL_WRITE); - tables[5].open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + (tables+5)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + (tables+6)->init_one_table(C_STRING_WITH_LEN("mysql"), + C_STRING_WITH_LEN("roles_mapping"), + "roles_mapping", TL_WRITE); + (tables+6)->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + tables->next_local= tables->next_global= tables + 1; (tables+1)->next_local= (tables+1)->next_global= tables + 2; (tables+2)->next_local= (tables+2)->next_global= tables + 3; (tables+3)->next_local= (tables+3)->next_global= tables + 4; (tables+4)->next_local= (tables+4)->next_global= tables + 5; + (tables+5)->next_local= (tables+5)->next_global= tables + 6; #ifdef HAVE_REPLICATION /* From f8d944a6a0c8c6668a7cf7b874740178d7b68f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:06:29 -0700 Subject: [PATCH 029/174] Added a init_role_mapping function to be used for later --- sql/sql_acl.cc | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 34eb4c5f700..3b27a383464 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -549,6 +549,44 @@ static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, return (uchar*) entry->hashkey.str; } +static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry, + char *username, char *hostname, char *rolename) +{ + size_t len[3] = {username ? strlen(username) : 0, + hostname ? strlen(hostname) : 0, + rolename ? strlen(rolename) : 0}; + /* + Create a buffer that holds all 3 NULL terminated strings in succession + To save memory space, the same buffer is used as the hashkey + */ + size_t bufflen = len[0] + len[1] + len[2] + 3; //add the '\0' aswell + char *buff= (char *)alloc_root(mem, bufflen); + /* + Offsets in the buffer for all 3 strings + */ + char *username_pos= buff; + char *hostname_pos= buff + len[0] + 1; + char *rolename_pos= buff + len[0] + len[1] + 2; + + if (username) + memcpy(username_pos, username, len[0]); + username_pos[len[0]]= '\0'; //#1 string terminator + entry->u_uname= username_pos; + + if (hostname) + memcpy(hostname_pos, hostname, len[1]); + hostname_pos[len[1]]= '\0'; //#2 string terminator + entry->u_hname= hostname_pos; + + if (rolename) + memcpy(rolename_pos, rolename, len[2]); + rolename_pos[len[2]]= '\0'; //#3 string terminator + entry->r_uname= rolename_pos; + + entry->hashkey.str = buff; + entry->hashkey.length = bufflen; +} + #define IP_ADDR_STRLEN (3 + 1 + 3 + 1 + 3 + 1 + 3) #define ACL_KEY_LENGTH (IP_ADDR_STRLEN + 1 + NAME_LEN + \ 1 + USERNAME_LENGTH + 1) From ba43f3551b75fce627a3f609d142f63213d98a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:06:39 -0700 Subject: [PATCH 030/174] Refactored some code in acl_load to make use of the new init_role_grant_pair function --- sql/sql_acl.cc | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 3b27a383464..6622b57f1e0 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1267,26 +1267,17 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) (void) my_hash_init2(&acl_roles_mappings,50,system_charset_info, 0,0,0, (my_hash_get_key) acl_role_map_get_key, 0,0); + MEM_ROOT temp_root; + init_alloc_root(&temp_root, ACL_ALLOC_BLOCK_SIZE, 0, MYF(0)); while (!(read_record_info.read_record(&read_record_info))) { - ROLE_GRANT_PAIR *mapping = (ROLE_GRANT_PAIR *)alloc_root( + ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR *)alloc_root( &mem, sizeof(ROLE_GRANT_PAIR)); - mapping->u_hname= get_field(&mem, table->field[0]); - mapping->u_uname= get_field(&mem, table->field[1]); - mapping->r_uname= get_field(&mem, table->field[2]); - - size_t len[3] = {mapping->u_hname ? strlen(mapping->u_hname) : 0, - mapping->u_uname ? strlen(mapping->u_uname) : 0, - mapping->r_uname ? strlen(mapping->r_uname) : 0}; - char *buff= (char *)alloc_root(&mem, len[0] + len[1] + len[2] + 1); - memcpy(buff, mapping->u_hname, len[0]); - memcpy(buff + len[0], mapping->u_uname, len[1]); - memcpy(buff + len[0] + len[1] + len[2], mapping->r_uname, len[2]); - buff[len[0] + len[1] + len[2]] = '\0'; - mapping->hashkey.str = buff; - mapping->hashkey.length = len[0] + len[1] + len[2]; - + char *hostname= get_field(&temp_root, table->field[0]); + char *username= get_field(&temp_root, table->field[1]); + char *rolename= get_field(&temp_root, table->field[2]); + init_role_grant_pair(&mem, mapping, username, hostname, rolename); if (add_role_user_mapping(mapping) == 1) { sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'", mapping->u_uname ? mapping->u_uname : "", @@ -1298,7 +1289,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) my_hash_insert(&acl_roles_mappings, (uchar*) mapping); } - + free_root(&temp_root, MYF(0)); end_read_record(&read_record_info); if (!initialized) From 6988e6c56a4e7c04415bb8e1ce5ed9e17805abea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:06 -0700 Subject: [PATCH 031/174] The acl_roles_mappings in-memory structure holds the following invariant: It will only hold _valid_ entries for as long as it held in memory. Any change regarding acl_users or acl_roles in memory should update the structure immediately. This is why the rebuild_roles_mappings no longer removes invalid entries. In order to keep things consistent with the existing code, the following jobs are assigned to each function: The role of rebuild_roles_mappings is to recreate the links between users and roles. Any other updates are to be done in the functions: handle_grant_* This change prepares the code for the next step, which is cascading updates. --- sql/sql_acl.cc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6622b57f1e0..8cec6d6c53d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2089,14 +2089,26 @@ void rebuild_role_grants(void) Reset every user's and role's role_grants array */ for (uint i=0; i < acl_users.elements; i++) { - ACL_USER * user = dynamic_element(&acl_users, i, ACL_USER *); + ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *); reset_dynamic(&user->role_grants); } my_hash_iterate(&acl_roles, (my_hash_walk_action) acl_user_reset_grant, NULL); - my_hash_iterate(&acl_roles_mappings, - (my_hash_walk_action) roles_mappings_walk_action, 0); + /* + Rebuild the direct links between users and roles in ACL_USER::role_grants + */ + for (uint i=0; i < acl_roles.records; i++) { + ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR*) + my_hash_element(&acl_roles_mappings, i); + /* + The invariant chosen is that acl_roles_mappings should _always_ + only contain valid entries, referencing correct user and role grants. + If add_role_user_mapping detects an invalid entry, it will not add + the mapping into the ACL_USER::role_grants array. + */ + DBUG_ASSERT(add_role_user_mapping(mapping)); + } } /* Return true if there is no users that can match the given host */ From de472770d3aa45da40d622992dd724cfdcc1fb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:14 -0700 Subject: [PATCH 032/174] Removed no longer needed hash_walk_action. The function was used to delete no longer valid entries in the roles_mappings HASH. This job will be delegated to handle_grant_* functions --- sql/sql_acl.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 8cec6d6c53d..26f912eccd3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2066,16 +2066,6 @@ my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) } -static my_bool roles_mappings_walk_action(ROLE_GRANT_PAIR *mapping, - void * not_used __attribute__((unused))) -{ - if (add_role_user_mapping(mapping)) { - //the mapping is invalid, the mapping can be safely deleted - my_hash_delete(&acl_roles_mappings, (uchar*) mapping); - } - return 0; -} - /* Rebuild the role grants every time the acl_users is modified From 7842ef3052001eb38aa1dadca7a5ede67ef81099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:22 -0700 Subject: [PATCH 033/174] Added logic to handle the in-memory roles_mappings struct in handle_data_struct. The logic is not complete yet. --- sql/sql_acl.cc | 73 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 26f912eccd3..b17036c994f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -658,7 +658,8 @@ enum enum_acl_lists COLUMN_PRIVILEGES_HASH, PROC_PRIVILEGES_HASH, FUNC_PRIVILEGES_HASH, - PROXY_USERS_ACL + PROXY_USERS_ACL, + ROLES_MAPPINGS_HASH }; static @@ -6220,6 +6221,12 @@ static int modify_grant_table(TABLE *table, Field *host_field, DBUG_RETURN(error); } +static int handle_roles_mappings_table(TABLE *table, bool drop, + LEX_USER *user_from, LEX_USER *user_to) +{ + /* TODO */ + return 0; +} /* Handle a privilege table. @@ -6269,6 +6276,13 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, DBUG_ENTER("handle_grant_table"); THD *thd= current_thd; + if (table_no == 6) + { + result= handle_roles_mappings_table(tables[6].table, drop, + user_from, user_to); + DBUG_RETURN(result); + } + table->use_all_columns(); if (! table_no) // mysql.user table { @@ -6376,7 +6390,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, /** Handle an in-memory privilege structure. - @param struct_no The number of the structure to handle (0..5). + @param struct_no The number of the structure to handle (0..6). @param drop If user_from is to be dropped. @param user_from The the user to be searched/dropped/renamed. @param user_to The new name for the user if to be renamed, NULL otherwise. @@ -6394,6 +6408,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, 3 PROC_PRIVILEGES_HASH 4 FUNC_PRIVILEGES_HASH 5 PROXY_USERS_ACL + 6 ROLES_MAPPINGS_HASH @retval > 0 At least one element matched. @retval 0 OK, but no element matched. @@ -6411,7 +6426,9 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, ACL_DB *acl_db= NULL; ACL_PROXY_USER *acl_proxy_user= NULL; GRANT_NAME *grant_name= NULL; + ROLE_GRANT_PAIR *role_grant_pair; HASH *grant_name_hash= NULL; + HASH *roles_mappings_hash= NULL; DBUG_ENTER("handle_grant_struct"); DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'", struct_no, user_from->user.str, user_from->host.str)); @@ -6444,6 +6461,10 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, case PROXY_USERS_ACL: elements= acl_proxy_users.elements; break; + case ROLES_MAPPINGS_HASH: + roles_mappings_hash= &acl_roles_mappings; + elements= roles_mappings_hash->records; + break; default: DBUG_ASSERT(0); return -1; @@ -6486,6 +6507,12 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, host= acl_proxy_user->get_host(); break; + case ROLES_MAPPINGS_HASH: + role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx); + user= role_grant_pair->u_uname; + host= role_grant_pair->u_hname; + break; + default: DBUG_ASSERT(0); } @@ -6535,6 +6562,10 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, delete_dynamic_element(&acl_proxy_users, idx); break; + case ROLES_MAPPINGS_HASH: + my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair); + break; + } } else if ( user_to ) @@ -6593,7 +6624,27 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, acl_proxy_user->set_user (&mem, user_to->user.str); acl_proxy_user->set_host (&mem, user_to->host.str); break; + case ROLES_MAPPINGS_HASH: + { + /* + Save old hash key and its length to be able properly update + element position in hash. + */ + char *old_key= role_grant_pair->hashkey.str; + size_t old_key_length= role_grant_pair->hashkey.length; + + init_role_grant_pair(&mem, role_grant_pair, + user_to->user.str, user_to->host.str, + role_grant_pair->r_uname); + + my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, + (uchar*) old_key, old_key_length); + + + } + } + } else { @@ -6752,6 +6803,24 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, result= 1; /* At least one record/element found. */ } } + + /* Handle roles_mappings table. */ + if (tables[6].table) + { + if ((found= handle_grant_table(tables, 6, drop, user_from, user_to)) < 0) + { + /* Handle of table failed, don't touch the in-memory array. */ + result= -1; + } + else + { + /* Handle acl_roles_mappings array */ + if ((handle_grant_struct(ROLES_MAPPINGS_HASH, drop, user_from, user_to) && !result) || + found) + result= 1; /* At least one record/element found */ + } + } + end: DBUG_RETURN(result); } From 2f94e542bddf1da3f4d85b0c08754b03bc5d0d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:31 -0700 Subject: [PATCH 034/174] Whitespace + comment fix --- sql/sql_acl.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b17036c994f..d129ccec5f9 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6587,7 +6587,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, case FUNC_PRIVILEGES_HASH: { /* - Save old hash key and its length to be able properly update + Save old hash key and its length to be able to properly update element position in hash. */ char *old_key= grant_name->hash_key; @@ -6624,10 +6624,11 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, acl_proxy_user->set_user (&mem, user_to->user.str); acl_proxy_user->set_host (&mem, user_to->host.str); break; + case ROLES_MAPPINGS_HASH: { /* - Save old hash key and its length to be able properly update + Save old hash key and its length to be able to properly update element position in hash. */ char *old_key= role_grant_pair->hashkey.str; @@ -6639,8 +6640,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, (uchar*) old_key, old_key_length); - - + break; } } From df53ed13acad2344b0bb002fa6886981d05b72a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:39 -0700 Subject: [PATCH 035/174] Renamed variables in init_role_grant_pair to make the code more consistent. --- sql/sql_acl.cc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d129ccec5f9..590cf566efa 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -552,35 +552,35 @@ static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry, char *username, char *hostname, char *rolename) { - size_t len[3] = {username ? strlen(username) : 0, - hostname ? strlen(hostname) : 0, - rolename ? strlen(rolename) : 0}; + size_t uname_l = username ? strlen(username) : 0; + size_t hname_l = hostname ? strlen(hostname) : 0; + size_t rname_l = rolename ? strlen(rolename) : 0; /* Create a buffer that holds all 3 NULL terminated strings in succession To save memory space, the same buffer is used as the hashkey */ - size_t bufflen = len[0] + len[1] + len[2] + 3; //add the '\0' aswell + size_t bufflen = uname_l + hname_l + rname_l + 3; //add the '\0' aswell char *buff= (char *)alloc_root(mem, bufflen); /* Offsets in the buffer for all 3 strings */ char *username_pos= buff; - char *hostname_pos= buff + len[0] + 1; - char *rolename_pos= buff + len[0] + len[1] + 2; + char *hostname_pos= buff + uname_l + 1; + char *rolename_pos= buff + uname_l + hname_l + 2; - if (username) - memcpy(username_pos, username, len[0]); - username_pos[len[0]]= '\0'; //#1 string terminator + if (username) //prevent undefined behaviour + memcpy(username_pos, username, uname_l); + username_pos[uname_l]= '\0'; //#1 string terminator entry->u_uname= username_pos; - if (hostname) - memcpy(hostname_pos, hostname, len[1]); - hostname_pos[len[1]]= '\0'; //#2 string terminator + if (hostname) //prevent undefined behaviour + memcpy(hostname_pos, hostname, hname_l); + hostname_pos[hname_l]= '\0'; //#2 string terminator entry->u_hname= hostname_pos; - if (rolename) - memcpy(rolename_pos, rolename, len[2]); - rolename_pos[len[2]]= '\0'; //#3 string terminator + if (rolename) //prevent undefined behaviour + memcpy(rolename_pos, rolename, rname_l); + rolename_pos[rname_l]= '\0'; //#3 string terminator entry->r_uname= rolename_pos; entry->hashkey.str = buff; From 0a9428cffce2e4ab42677b3752d724a8ae4d9b66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:50 -0700 Subject: [PATCH 036/174] Added debug warning to add_role_user_mapping. --- sql/sql_acl.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 590cf566efa..7e83b2b9d9c 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2055,7 +2055,12 @@ my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) TRUE); ACL_USER *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); if (user == NULL || role == NULL) + { + DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s", + mapping->u_uname, mapping->u_hname, + mapping->r_uname)); return 1; + } push_dynamic(&user->role_grants, (uchar*) role); push_dynamic(&role->role_grants, (uchar*) user); From 9506a0715910301ea1cba631838c3bed385d170c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:09:58 -0700 Subject: [PATCH 037/174] Added debug info to rebuild_roles_mappings Also fixed a bug regarding the HASH iteration. It previously got the stop condition from a different hashtable and this caused errors when the hash sizes were different. --- sql/sql_acl.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7e83b2b9d9c..01ff5f0e741 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2081,6 +2081,7 @@ my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) void rebuild_role_grants(void) { + DBUG_ENTER("rebuild_role_grants"); /* Reset every user's and role's role_grants array */ @@ -2094,17 +2095,20 @@ void rebuild_role_grants(void) /* Rebuild the direct links between users and roles in ACL_USER::role_grants */ - for (uint i=0; i < acl_roles.records; i++) { + for (uint i=0; i < acl_roles_mappings.records; i++) { ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR*) my_hash_element(&acl_roles_mappings, i); + my_bool status = add_role_user_mapping(mapping); /* The invariant chosen is that acl_roles_mappings should _always_ only contain valid entries, referencing correct user and role grants. If add_role_user_mapping detects an invalid entry, it will not add the mapping into the ACL_USER::role_grants array. */ - DBUG_ASSERT(add_role_user_mapping(mapping)); + DBUG_ASSERT(status == 0); } + + DBUG_VOID_RETURN; } /* Return true if there is no users that can match the given host */ From 13a1f6fd72e73c1eea6913edaaa94a71ea1dd3e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:07 -0700 Subject: [PATCH 038/174] Changed a call to handle_roles_mappings_table: first parameter is now more readable --- sql/sql_acl.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 01ff5f0e741..6a3aa34342c 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6287,8 +6287,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, if (table_no == 6) { - result= handle_roles_mappings_table(tables[6].table, drop, - user_from, user_to); + result= handle_roles_mappings_table(table, drop, user_from, user_to); DBUG_RETURN(result); } From 6bddb93e3c307aa4561f1912df8286b90d7d57ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:15 -0700 Subject: [PATCH 039/174] Implemented half of handle_roles_mappings_table. The function now handles user updates/deletions correctly. --- sql/sql_acl.cc | 70 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6a3aa34342c..d832503cb05 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6230,11 +6230,79 @@ static int modify_grant_table(TABLE *table, Field *host_field, DBUG_RETURN(error); } +/* + Handle the roles_mappings privilege table + + +*/ static int handle_roles_mappings_table(TABLE *table, bool drop, LEX_USER *user_from, LEX_USER *user_to) { + /* + First we need to find out if the user_from represents a user, or a role. + + If the user_from has a hostname different than '' it can not be a user. + If the user_from has an empty hostname, it _could_ be a role, but it is + not mandatory. + + In this case perform a quick lookup in acl_roles to see if + it is already there. If it is not found, than the user fields are updated, + otherwise the role field gets updated. + */ + DBUG_ENTER("handle_roles_mappings_table"); + + int error; + int result= 0; + bool is_role= FALSE; + THD *thd= current_thd; + char *host, *user; + Field *host_field= table->field[0]; + Field *user_field= table->field[1]; + Field *role_field= table->field[2]; + + if (!user_from->host.length && find_acl_role(user_from->user.str)) + { + is_role= TRUE; + } + + table->use_all_columns(); + if (!is_role) + { + if ((error= table->file->ha_rnd_init(1))) + { + table->file->print_error(error, MYF(0)); + result= -1; + } + else + { + while((error= table->file->ha_rnd_next(table->record[0])) != + HA_ERR_END_OF_FILE) + { + if (error) + { + DBUG_PRINT("info", ("scan error: %d", error)); + continue; + } + if (! (host= get_field(thd->mem_root, host_field))) + host= ""; + if (! (user= get_field(thd->mem_root, user_field))) + user= ""; + + if (strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host)) + continue; + result= ((drop || user_to) && + modify_grant_table(table, host_field, user_field, user_to)) ? + -1 : result ? result : 1; /* Error or keep result or found. */ + + } + table->file->ha_rnd_end(); + } + + } + /* TODO */ - return 0; + DBUG_RETURN(result); } /* Handle a privilege table. From 565c6c5a1fa5d83d92167d0db07d9f5db2a35b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:24 -0700 Subject: [PATCH 040/174] Cascading updates for roles_mappings are now fully functional. Renaming a user via RENAME USER command causes either the user columns to update, or the role columns. --- sql/sql_acl.cc | 73 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d832503cb05..4ed61c66c44 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2035,7 +2035,12 @@ void rebuild_check_host(void) init_check_host(); } +/* + Reset a users role_grants dynamic array. + The function can is used as a walk action for hash elements aswell. + +*/ my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))) { @@ -2087,7 +2092,7 @@ void rebuild_role_grants(void) */ for (uint i=0; i < acl_users.elements; i++) { ACL_USER *user= dynamic_element(&acl_users, i, ACL_USER *); - reset_dynamic(&user->role_grants); + acl_user_reset_grant(user, NULL); } my_hash_iterate(&acl_roles, (my_hash_walk_action) acl_user_reset_grant, NULL); @@ -6255,7 +6260,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, int result= 0; bool is_role= FALSE; THD *thd= current_thd; - char *host, *user; + const char *host, *user, *role; Field *host_field= table->field[0]; Field *user_field= table->field[1]; Field *role_field= table->field[2]; @@ -6266,23 +6271,24 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, } table->use_all_columns(); - if (!is_role) + if ((error= table->file->ha_rnd_init(1))) { - if ((error= table->file->ha_rnd_init(1))) + table->file->print_error(error, MYF(0)); + result= -1; + } + else + { + while((error= table->file->ha_rnd_next(table->record[0])) != + HA_ERR_END_OF_FILE) { - table->file->print_error(error, MYF(0)); - result= -1; - } - else - { - while((error= table->file->ha_rnd_next(table->record[0])) != - HA_ERR_END_OF_FILE) + if (error) { - if (error) - { - DBUG_PRINT("info", ("scan error: %d", error)); - continue; - } + DBUG_PRINT("info", ("scan error: %d", error)); + continue; + } + if (!is_role) + { + if (! (host= get_field(thd->mem_root, host_field))) host= ""; if (! (user= get_field(thd->mem_root, user_field))) @@ -6291,16 +6297,43 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, if (strcmp(user_from->user.str, user) || my_strcasecmp(system_charset_info, user_from->host.str, host)) continue; + result= ((drop || user_to) && modify_grant_table(table, host_field, user_field, user_to)) ? -1 : result ? result : 1; /* Error or keep result or found. */ - } - table->file->ha_rnd_end(); + else + { + if (! (role= get_field(thd->mem_root, role_field))) + role= ""; + + if (strcmp(user_from->user.str, role)) + continue; + + error= 0; + + if (drop) /* drop if requested */ + { + if ((error= table->file->ha_delete_row(table->record[0]))) + table->file->print_error(error, MYF(0)); + } + else if (user_to) + { + store_record(table, record[1]); + role_field->store(user_to->user.str, user_to->user.length, + system_charset_info); + if ((error= table->file->ha_update_row(table->record[1], + table->record[0])) && + error != HA_ERR_RECORD_IS_THE_SAME) + table->file->print_error(error, MYF(0)); + } + + /* Error or keep result or found. */ + result= error ? -1 : result ? result : 1; + } } - + table->file->ha_rnd_end(); } - /* TODO */ DBUG_RETURN(result); } From 22ca077ac44bca73394133b9fae001e7caa815c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:32 -0700 Subject: [PATCH 041/174] Removed all tabs from sql_acl.h. Replaced with spaces --- sql/sql_acl.h | 90 +++++++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 3bc1b1eae45..b45b70ad069 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -20,30 +20,30 @@ #include "violite.h" /* SSL_type */ #include "sql_class.h" /* LEX_COLUMN */ -#define SELECT_ACL (1L << 0) -#define INSERT_ACL (1L << 1) -#define UPDATE_ACL (1L << 2) -#define DELETE_ACL (1L << 3) -#define CREATE_ACL (1L << 4) -#define DROP_ACL (1L << 5) -#define RELOAD_ACL (1L << 6) -#define SHUTDOWN_ACL (1L << 7) -#define PROCESS_ACL (1L << 8) -#define FILE_ACL (1L << 9) -#define GRANT_ACL (1L << 10) -#define REFERENCES_ACL (1L << 11) -#define INDEX_ACL (1L << 12) -#define ALTER_ACL (1L << 13) -#define SHOW_DB_ACL (1L << 14) -#define SUPER_ACL (1L << 15) -#define CREATE_TMP_ACL (1L << 16) -#define LOCK_TABLES_ACL (1L << 17) -#define EXECUTE_ACL (1L << 18) -#define REPL_SLAVE_ACL (1L << 19) -#define REPL_CLIENT_ACL (1L << 20) -#define CREATE_VIEW_ACL (1L << 21) -#define SHOW_VIEW_ACL (1L << 22) -#define CREATE_PROC_ACL (1L << 23) +#define SELECT_ACL (1L << 0) +#define INSERT_ACL (1L << 1) +#define UPDATE_ACL (1L << 2) +#define DELETE_ACL (1L << 3) +#define CREATE_ACL (1L << 4) +#define DROP_ACL (1L << 5) +#define RELOAD_ACL (1L << 6) +#define SHUTDOWN_ACL (1L << 7) +#define PROCESS_ACL (1L << 8) +#define FILE_ACL (1L << 9) +#define GRANT_ACL (1L << 10) +#define REFERENCES_ACL (1L << 11) +#define INDEX_ACL (1L << 12) +#define ALTER_ACL (1L << 13) +#define SHOW_DB_ACL (1L << 14) +#define SUPER_ACL (1L << 15) +#define CREATE_TMP_ACL (1L << 16) +#define LOCK_TABLES_ACL (1L << 17) +#define EXECUTE_ACL (1L << 18) +#define REPL_SLAVE_ACL (1L << 19) +#define REPL_CLIENT_ACL (1L << 20) +#define CREATE_VIEW_ACL (1L << 21) +#define SHOW_VIEW_ACL (1L << 22) +#define CREATE_PROC_ACL (1L << 23) #define ALTER_PROC_ACL (1L << 24) #define CREATE_USER_ACL (1L << 25) #define EVENT_ACL (1L << 26) @@ -57,7 +57,7 @@ 4. acl_init() or whatever - to define behaviour for old privilege tables 5. sql_yacc.yy - for GRANT/REVOKE to work */ -#define NO_ACCESS (1L << 30) +#define NO_ACCESS (1L << 30) #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ @@ -106,21 +106,21 @@ #define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) #define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL) #define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ - CREATE_PROC_ACL | ALTER_PROC_ACL ) + CREATE_PROC_ACL | ALTER_PROC_ACL ) #define DB_CHUNK4 (EXECUTE_ACL) #define DB_CHUNK5 (EVENT_ACL | TRIGGER_ACL) #define fix_rights_for_db(A) (((A) & DB_CHUNK0) | \ - (((A) << 4) & DB_CHUNK1) | \ - (((A) << 6) & DB_CHUNK2) | \ - (((A) << 9) & DB_CHUNK3) | \ - (((A) << 2) & DB_CHUNK4))| \ + (((A) << 4) & DB_CHUNK1) | \ + (((A) << 6) & DB_CHUNK2) | \ + (((A) << 9) & DB_CHUNK3) | \ + (((A) << 2) & DB_CHUNK4))| \ (((A) << 9) & DB_CHUNK5) #define get_rights_for_db(A) (((A) & DB_CHUNK0) | \ - (((A) & DB_CHUNK1) >> 4) | \ - (((A) & DB_CHUNK2) >> 6) | \ - (((A) & DB_CHUNK3) >> 9) | \ - (((A) & DB_CHUNK4) >> 2))| \ + (((A) & DB_CHUNK1) >> 4) | \ + (((A) & DB_CHUNK2) >> 6) | \ + (((A) & DB_CHUNK3) >> 9) | \ + (((A) & DB_CHUNK4) >> 2))| \ (((A) & DB_CHUNK5) >> 9) #define TBL_CHUNK0 DB_CHUNK0 #define TBL_CHUNK1 DB_CHUNK1 @@ -137,11 +137,11 @@ #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) #define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) #define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \ - (((A) << 23) & ALTER_PROC_ACL) | \ - (((A) << 8) & GRANT_ACL)) + (((A) << 23) & ALTER_PROC_ACL) | \ + (((A) << 8) & GRANT_ACL)) #define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \ - (((A) & ALTER_PROC_ACL) >> 23) | \ - (((A) & GRANT_ACL) >> 8)) + (((A) & ALTER_PROC_ACL) >> 23) | \ + (((A) & GRANT_ACL) >> 8)) enum mysql_db_table_field { @@ -188,7 +188,7 @@ my_bool acl_init(bool dont_read_acl_tables); my_bool acl_reload(THD *thd); void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, - const char *user, const char *db, my_bool db_is_pattern); + const char *user, const char *db, my_bool db_is_pattern); bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len); bool acl_getroot(Security_context *sctx, char *user, char *host, char *ip, char *db); @@ -196,29 +196,29 @@ bool acl_check_host(const char *host, const char *ip); int check_change_password(THD *thd, const char *host, const char *user, char *password, uint password_len); bool change_password(THD *thd, const char *host, const char *user, - char *password); + char *password); bool mysql_grant(THD *thd, const char *db, List &user_list, ulong rights, bool revoke, bool is_proxy); int mysql_table_grant(THD *thd, TABLE_LIST *table, List &user_list, List &column_list, ulong rights, bool revoke); bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, - List &user_list, ulong rights, - bool revoke, bool write_to_binlog); + List &user_list, ulong rights, + bool revoke, bool write_to_binlog); my_bool grant_init(); void grant_free(void); my_bool grant_reload(THD *thd); bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, bool any_combination_will_do, uint number, bool no_errors); bool check_grant_column (THD *thd, GRANT_INFO *grant, - const char *db_name, const char *table_name, - const char *name, uint length, Security_context *sctx); + const char *db_name, const char *table_name, + const char *name, uint length, Security_context *sctx); bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, const char *name, uint length); bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_error); + TABLE_LIST *procs, bool is_proc, bool no_error); bool check_grant_db(THD *thd,const char *db); ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, From fb3e3b9440033402dfe96fa7dc2a6764d2b19d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:40 -0700 Subject: [PATCH 042/174] Fixed typo --- sql/sql_acl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4ed61c66c44..b3158cfc08b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6246,7 +6246,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, /* First we need to find out if the user_from represents a user, or a role. - If the user_from has a hostname different than '' it can not be a user. + If the user_from has a hostname different than '' it can not be a role. If the user_from has an empty hostname, it _could_ be a role, but it is not mandatory. From 0d103a6f62617b397466d131996ee9d5bc243315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:49 -0700 Subject: [PATCH 043/174] Add a check if user_to is valid to handle_roles_mappings_table --- sql/sql_acl.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b3158cfc08b..cdc5e88ae24 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6251,7 +6251,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, not mandatory. In this case perform a quick lookup in acl_roles to see if - it is already there. If it is not found, than the user fields are updated, + it is already there. If it is not found, then the user fields are updated, otherwise the role field gets updated. */ DBUG_ENTER("handle_roles_mappings_table"); @@ -6266,9 +6266,15 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, Field *role_field= table->field[2]; if (!user_from->host.length && find_acl_role(user_from->user.str)) - { is_role= TRUE; - } + + /* + Check if user_to is a valid role. If it is not a valid role, the change + fails. + */ + if (is_role && user_to && user_to->host.length) + DBUG_RETURN(-1); + table->use_all_columns(); if ((error= table->file->ha_rnd_init(1))) @@ -6334,7 +6340,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, } table->file->ha_rnd_end(); } - /* TODO */ +end: DBUG_RETURN(result); } /* From 096e7aa1e098db914d55c37eee89378eee113a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:10:57 -0700 Subject: [PATCH 044/174] Fix bug with inserting _pointers_ to ACL_USER in the DYNAMIC_ARRAY of granted roles --- sql/sql_acl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index cdc5e88ae24..4065f53db20 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2067,8 +2067,8 @@ my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) return 1; } - push_dynamic(&user->role_grants, (uchar*) role); - push_dynamic(&role->role_grants, (uchar*) user); + push_dynamic(&user->role_grants, (uchar*) &role); + push_dynamic(&role->role_grants, (uchar*) &user); DBUG_PRINT("info", ("Found user %s@%s having role granted %s@%s\n", user->user.str, user->host.hostname, From 6680bb14a40c917e24e09a67894d9c7fd5065be0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:11:05 -0700 Subject: [PATCH 045/174] Removed no longer used label --- sql/sql_acl.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4065f53db20..a5544ae7244 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6340,7 +6340,6 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, } table->file->ha_rnd_end(); } -end: DBUG_RETURN(result); } /* From 7ec24435b324c27412a94cbd71b707c9fd06b8ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:11:13 -0700 Subject: [PATCH 046/174] Added acl_setrole function. The function enables/disables role privileges to the current user via the current security_context --- sql/sql_acl.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ sql/sql_acl.h | 1 + sql/sql_class.cc | 2 +- sql/sql_class.h | 2 ++ 4 files changed, 65 insertions(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index a5544ae7244..b7e9f3f97fb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1672,6 +1672,67 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, DBUG_RETURN(res); } +bool acl_setrole(THD *thd, char *rolename) +{ + bool is_granted; + int result= 0; + + /* clear role privileges */ + mysql_mutex_lock(&acl_cache->lock); + + ACL_USER *role= find_acl_role(rolename); + ACL_USER *acl_user; + + if (!strcasecmp(rolename, "NONE")) { + /* have to clear the privileges */ + /* get the current user */ + acl_user= find_acl_user(thd->security_ctx->host, thd->security_ctx->user, + FALSE); + if (acl_user == NULL) + result= -1; + else + thd->security_ctx->master_access= acl_user->access; + + goto end; + } + + if (role == NULL) { + result= -1; + goto end; + } + + for (uint i=0 ; i < role->role_grants.elements ; i++) + { + acl_user= *(dynamic_element(&role->role_grants, i, ACL_USER**)); + if ((!acl_user->user.str && !thd->security_ctx->user[0]) || + (acl_user->user.str && !strcmp(thd->security_ctx->user, + acl_user->user.str))) + { + if (compare_hostname(&acl_user->host, thd->security_ctx->host, + thd->security_ctx->host)) + { + is_granted= TRUE; + break; + } + } + } + + if (!is_granted) + { + result= 1; + goto end; + } + + /* merge the privileges */ + thd->security_ctx->master_access= acl_user->access | role->access; + /* mark the current role */ + strcpy(thd->security_ctx->priv_role, rolename); + +end: + mysql_mutex_unlock(&acl_cache->lock); + return result; +} + static uchar* check_get_key(ACL_USER *buff, size_t *length, my_bool not_used __attribute__((unused))) { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index b45b70ad069..abc5e8ac25c 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -382,4 +382,5 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info, bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user, bool with_grant); +bool acl_setrole(THD *thd, char *rolename); #endif /* SQL_ACL_INCLUDED */ diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 461d4d02e2f..cec46a0a3a7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3647,7 +3647,7 @@ void Security_context::init() { host= user= ip= external_user= 0; host_or_ip= "connecting host"; - priv_user[0]= priv_host[0]= proxy_user[0]= '\0'; + priv_user[0]= priv_host[0]= proxy_user[0]= priv_role[0]= '\0'; master_access= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS db_access= NO_ACCESS; diff --git a/sql/sql_class.h b/sql/sql_class.h index 053ac98c453..64de6a63ded 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1041,6 +1041,8 @@ public: char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5]; /* The host privilege we are using */ char priv_host[MAX_HOSTNAME]; + /* The role privilege we are using */ + char priv_role[USERNAME_LENGTH]; /* The external user (if available) */ char *external_user; /* points to host if host is available, otherwise points to ip */ From 6062e87f5439e8568cb7efe63bb906a496e425be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:11:21 -0700 Subject: [PATCH 047/174] Created new set_var_role class to handle the SET ROLE command --- sql/set_var.cc | 19 +++++++++++++++++++ sql/set_var.h | 11 +++++++++++ 2 files changed, 30 insertions(+) diff --git a/sql/set_var.cc b/sql/set_var.cc index 4473ed54c46..d09e2bcac77 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -871,6 +871,25 @@ int set_var_password::update(THD *thd) #endif } +/***************************************************************************** + Functions to handle SET ROLE +*****************************************************************************/ +int set_var_role::check(THD *thd) +{ + /* nothing to check */ + return 0; +} + +int set_var_role::update(THD *thd) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + return acl_setrole(thd, this->role.str); +#else + return 0; +#endif +} + + /***************************************************************************** Functions to handle SET NAMES and SET CHARACTER SET *****************************************************************************/ diff --git a/sql/set_var.h b/sql/set_var.h index f248dc2894f..75090d6e1da 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -276,6 +276,17 @@ public: int update(THD *thd); }; +/* For SET ROLE */ + +class set_var_role: public set_var_base +{ + LEX_STRING role; +public: + set_var_role(LEX_STRING role_arg) : role(role_arg) {}; + int check(THD *thd); + int update(THD *thd); +}; + /* For SET NAMES and SET CHARACTER SET */ From 0254c9a4bd46b58e9fbfa887ae63dd6ce26ccf8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:11:29 -0700 Subject: [PATCH 048/174] Added the SET ROLE command to the grammar --- sql/lex.h | 1 + sql/sql_yacc.yy | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/sql/lex.h b/sql/lex.h index 003f3175c61..1d577b02879 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -489,6 +489,7 @@ static SYMBOL symbols[] = { { "REVOKE", SYM(REVOKE)}, { "RIGHT", SYM(RIGHT)}, { "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */ + { "ROLE", SYM(ROLE_SYM)}, { "ROLLBACK", SYM(ROLLBACK_SYM)}, { "ROLLUP", SYM(ROLLUP_SYM)}, { "ROUTINE", SYM(ROUTINE_SYM)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 8c393dc47aa..cf2fcbf4109 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1260,6 +1260,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RETURN_SYM /* SQL-2003-R */ %token REVOKE /* SQL-2003-R */ %token RIGHT /* SQL-2003-R */ +%token ROLE_SYM %token ROLLBACK_SYM /* SQL-2003-R */ %token ROLLUP_SYM /* SQL-2003-R */ %token ROUTINE_SYM /* SQL-2003-N */ @@ -1687,6 +1688,8 @@ END_OF_INPUT %type opt_union_order_or_limit +%type ROLE_SYM + %% @@ -13490,6 +13493,7 @@ keyword_sp: | RESOURCES {} | RESUME_SYM {} | RETURNS_SYM {} + | ROLE_SYM {} | ROLLUP_SYM {} | ROUTINE_SYM {} | ROWS_SYM {} @@ -13839,6 +13843,12 @@ option_value: MYSQL_YYABORT; lex->var_list.push_back(var); } + | ROLE_SYM ident_or_text + { + LEX *lex = Lex; + set_var_role *var= new set_var_role($2); + lex->var_list.push_back(var); + } | PASSWORD equal text_or_password { LEX *lex= thd->lex; From 7d4bfba91ae18ed78d872d38451d809d6d61b971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 15:14:11 -0700 Subject: [PATCH 049/174] Added error message for invalid role --- sql/share/errmsg-utf8.txt | 6 ++++++ sql/sql_acl.cc | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 44466f4d3ae..a6e3a74df0a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6563,3 +6563,9 @@ ER_NO_SUCH_QUERY eng "Unknown query id: %lld" ger "Unbekannte Abfrage-ID: %lld" rus "Неизвестный номер запроса: %lld" +ER_INVALID_ROLE + eng "The role '%s' has not been granted or is invalid." + rum "Rolul '%s' este invalid sau nu a fost acordat." +ER_INVALID_CURRENT_USER + eng "The current user is invalid." + rum "Utilizatorul curent este invalid." diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b7e9f3f97fb..6704d28ae89 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1689,7 +1689,10 @@ bool acl_setrole(THD *thd, char *rolename) acl_user= find_acl_user(thd->security_ctx->host, thd->security_ctx->user, FALSE); if (acl_user == NULL) + { + my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); result= -1; + } else thd->security_ctx->master_access= acl_user->access; @@ -1697,6 +1700,7 @@ bool acl_setrole(THD *thd, char *rolename) } if (role == NULL) { + my_error(ER_INVALID_ROLE, MYF(0), rolename); result= -1; goto end; } @@ -1719,6 +1723,7 @@ bool acl_setrole(THD *thd, char *rolename) if (!is_granted) { + my_error(ER_INVALID_ROLE, MYF(0), rolename); result= 1; goto end; } From 494f0117995bcd786e481960d2c4cdd5d13f0f61 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Thu, 17 Oct 2013 20:38:49 -0700 Subject: [PATCH 050/174] fix the code to compile --- sql/sql_yacc.yy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index cf2fcbf4109..1f7166c6cd6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -783,10 +783,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %parse-param { THD *thd } %lex-param { THD *thd } /* - Currently there are 197 shift/reduce conflicts. + Currently there are 198 shift/reduce conflicts. We should not introduce new conflicts any more. */ -%expect 197 +%expect 198 /* Comments for TOKENS. @@ -1650,7 +1650,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); spatial_key_options fulltext_key_options normal_key_opt fulltext_key_opt spatial_key_opt fulltext_key_opts spatial_key_opts keep_gcc_happy - key_using_alg + key_using_alg shutdown part_column_list server_def server_options_list server_option definer_opt no_definer definer From db25d8f97713e3a4ade8284c57f65c63605c5b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:39:23 -0700 Subject: [PATCH 051/174] Modified set_role_var to implement both a role check in the check() function, as well as only set privileges in the update() function. --- sql/set_var.cc | 10 ++++++++-- sql/set_var.h | 6 +++--- sql/sql_acl.cc | 26 ++++++++++++++++++-------- sql/sql_acl.h | 3 ++- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/sql/set_var.cc b/sql/set_var.cc index d09e2bcac77..33c360ae785 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -876,14 +876,20 @@ int set_var_password::update(THD *thd) *****************************************************************************/ int set_var_role::check(THD *thd) { - /* nothing to check */ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + ulonglong access; + int status= acl_check_setrole(thd, base.str, &access); + save_result.ulonglong_value= access; + return status; +#else return 0; +#endif } int set_var_role::update(THD *thd) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - return acl_setrole(thd, this->role.str); + return acl_setrole(thd, base.str, save_result.ulonglong_value); #else return 0; #endif diff --git a/sql/set_var.h b/sql/set_var.h index 75090d6e1da..11501c4212a 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -278,11 +278,11 @@ public: /* For SET ROLE */ -class set_var_role: public set_var_base +class set_var_role: public set_var { - LEX_STRING role; public: - set_var_role(LEX_STRING role_arg) : role(role_arg) {}; + set_var_role(LEX_STRING role_arg) : + set_var(OPT_SESSION, NULL, &role_arg, NULL){}; int check(THD *thd); int update(THD *thd); }; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6704d28ae89..61989b5de09 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1672,7 +1672,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, DBUG_RETURN(res); } -bool acl_setrole(THD *thd, char *rolename) +int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) { bool is_granted; int result= 0; @@ -1693,8 +1693,8 @@ bool acl_setrole(THD *thd, char *rolename) my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); result= -1; } - else - thd->security_ctx->master_access= acl_user->access; + else if (access) + *access= acl_user->access; goto end; } @@ -1728,16 +1728,26 @@ bool acl_setrole(THD *thd, char *rolename) goto end; } - /* merge the privileges */ - thd->security_ctx->master_access= acl_user->access | role->access; - /* mark the current role */ - strcpy(thd->security_ctx->priv_role, rolename); - + if (access) + { + *access = acl_user->access | role->access; + } end: mysql_mutex_unlock(&acl_cache->lock); return result; } +int acl_setrole(THD *thd, char *rolename, ulonglong access) { + /* merge the privileges */ + thd->security_ctx->master_access= access; + /* mark the current role */ + strmake(thd->security_ctx->priv_role, rolename, + sizeof(thd->security_ctx->priv_role)-1); + return 0; +} + + + static uchar* check_get_key(ACL_USER *buff, size_t *length, my_bool not_used __attribute__((unused))) { diff --git a/sql/sql_acl.h b/sql/sql_acl.h index abc5e8ac25c..0e04d8f86d6 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -382,5 +382,6 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info, bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user, bool with_grant); -bool acl_setrole(THD *thd, char *rolename); +int acl_setrole(THD *thd, char *rolename, ulonglong access); +int acl_check_setrole(THD *thd, char *rolename, ulonglong *access); #endif /* SQL_ACL_INCLUDED */ From e8d6425875f45c6b7a1e81d71abcf83538d4cea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:39:43 -0700 Subject: [PATCH 052/174] Renamed find_acl_user -> find_user_no_anon --- sql/sql_acl.cc | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 61989b5de09..c833fc1b3c2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -635,7 +635,7 @@ static void init_check_host(void); static void rebuild_check_host(void); static void rebuild_role_grants(void); static void free_acl_user(ACL_USER *acl_user); -static ACL_USER *find_acl_user(const char *host, const char *user, +static ACL_USER *find_user_no_anon(const char *host, const char *user, my_bool exact); static ACL_USER *find_acl_role(const char *user); static bool update_user_table(THD *thd, TABLE *table, const char *host, @@ -1257,12 +1257,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) table->use_all_columns(); /* account for every role mapping */ - /* acquire lock for the find_acl_user functions - XXX - Perhaps new wrapper functions should be created that do not check - for the lock in this case as it either is already taken or - it's the first initialisation so no race conditions possible - */ + /* acquire lock for the find_user_no_anon functions */ if (!initialized) mysql_mutex_lock(&acl_cache->lock); @@ -1686,8 +1681,8 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) if (!strcasecmp(rolename, "NONE")) { /* have to clear the privileges */ /* get the current user */ - acl_user= find_acl_user(thd->security_ctx->host, thd->security_ctx->user, - FALSE); + acl_user= find_user_no_anon(thd->security_ctx->host, thd->security_ctx->user, + FALSE); if (acl_user == NULL) { my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); @@ -2131,9 +2126,9 @@ my_bool acl_user_reset_grant(ACL_USER *user, my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) { - ACL_USER *user= find_acl_user((mapping->u_hname) ? mapping->u_hname: "", - (mapping->u_uname) ? mapping->u_uname: "", - TRUE); + ACL_USER *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "", + (mapping->u_uname) ? mapping->u_uname: "", + TRUE); ACL_USER *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); if (user == NULL || role == NULL) { @@ -2338,7 +2333,7 @@ bool change_password(THD *thd, const char *host, const char *user, mysql_mutex_lock(&acl_cache->lock); ACL_USER *acl_user; - if (!(acl_user= find_acl_user(host, user, TRUE))) + if (!(acl_user= find_user_no_anon(host, user, TRUE))) { mysql_mutex_unlock(&acl_cache->lock); my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); @@ -2411,7 +2406,7 @@ bool is_acl_user(const char *host, const char *user) return TRUE; mysql_mutex_lock(&acl_cache->lock); - res= find_acl_user(host, user, TRUE) != NULL; + res= find_user_no_anon(host, user, TRUE) != NULL; mysql_mutex_unlock(&acl_cache->lock); return res; } @@ -2421,9 +2416,9 @@ bool is_acl_user(const char *host, const char *user) Find first entry that matches the current user */ static ACL_USER * -find_acl_user(const char *host, const char *user, my_bool exact) +find_user_no_anon(const char *host, const char *user, my_bool exact) { - DBUG_ENTER("find_acl_user"); + DBUG_ENTER("find_user_no_anon"); DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); mysql_mutex_assert_owner(&acl_cache->lock); @@ -2961,7 +2956,7 @@ static int replace_db_table(TABLE *table, const char *db, } /* Check if there is such a user in user table in memory? */ - if (!find_acl_user(combo.host.str,combo.user.str, FALSE)) + if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); DBUG_RETURN(-1); @@ -3110,7 +3105,7 @@ replace_proxies_priv_table(THD *thd, TABLE *table, const LEX_USER *user, } /* Check if there is such a user in user table in memory? */ - if (!find_acl_user(user->host.str,user->user.str, FALSE)) + if (!find_user_no_anon(user->host.str,user->user.str, FALSE)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); DBUG_RETURN(-1); @@ -3743,7 +3738,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, The following should always succeed as new users are created before this function is called! */ - if (!find_acl_user(combo.host.str,combo.user.str, FALSE)) + if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ @@ -5655,7 +5650,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) mysql_rwlock_rdlock(&LOCK_grant); mysql_mutex_lock(&acl_cache->lock); - acl_user= find_acl_user(lex_user->host.str, lex_user->user.str, TRUE); + acl_user= find_user_no_anon(lex_user->host.str, lex_user->user.str, TRUE); if (!acl_user) { mysql_mutex_unlock(&acl_cache->lock); @@ -6131,7 +6126,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) mysql_mutex_lock(&acl_cache->lock); - if (initialized && (acl_user= find_acl_user(host,user, FALSE))) + if (initialized && (acl_user= find_user_no_anon(host,user, FALSE))) uc->user_resources= acl_user->user_resource; else bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); @@ -7287,7 +7282,7 @@ bool mysql_revoke_all(THD *thd, List &list) result= -1; continue; } - if (!find_acl_user(lex_user->host.str, lex_user->user.str, TRUE)) + if (!find_user_no_anon(lex_user->host.str, lex_user->user.str, TRUE)) { result= -1; continue; @@ -7599,13 +7594,17 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, mysql_mutex_lock(&acl_cache->lock); - if ((au= find_acl_user(combo->host.str=(char*)sctx->host_or_ip,combo->user.str,FALSE))) + if ((au= find_user_no_anon(combo->host.str=(char*)sctx->host_or_ip, + combo->user.str,FALSE))) goto found_acl; - if ((au= find_acl_user(combo->host.str=(char*)sctx->host, combo->user.str,FALSE))) + if ((au= find_user_no_anon(combo->host.str=(char*)sctx->host, + combo->user.str,FALSE))) goto found_acl; - if ((au= find_acl_user(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE))) + if ((au= find_user_no_anon(combo->host.str=(char*)sctx->ip, + combo->user.str,FALSE))) goto found_acl; - if((au= find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE))) + if((au= find_user_no_anon(combo->host.str=(char*)"%", + combo->user.str, FALSE))) goto found_acl; mysql_mutex_unlock(&acl_cache->lock); @@ -8691,7 +8690,7 @@ static bool send_plugin_request_packet(MPVIO_EXT *mpvio, Finds a user and copies it into mpvio. Creates a fake user if no matching user account is found. - @note find_acl_user is not the same, because it doesn't take into + @note find_user_no_anon is not the same, because it doesn't take into account the case when user is not empty, but acl_user->user is empty @retval 0 found @@ -9652,9 +9651,10 @@ bool acl_authenticate(THD *thd, uint connect_errors, /* we're proxying : find the proxy user definition */ mysql_mutex_lock(&acl_cache->lock); - acl_proxy_user= find_acl_user(proxy_user->get_proxied_host() ? - proxy_user->get_proxied_host() : "", - mpvio.auth_info.authenticated_as, TRUE); + acl_proxy_user= find_user_no_anon(proxy_user->get_proxied_host() ? + proxy_user->get_proxied_host() : "", + mpvio.auth_info.authenticated_as, + TRUE); if (!acl_proxy_user) { if (!thd->is_error()) From cf9ebd72c43d30b647e01f4f0b771135fe07817d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:44:51 -0700 Subject: [PATCH 053/174] Refactored find_mpvio_user. The loop that searches for the user is now a separate function. --- sql/sql_acl.cc | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index c833fc1b3c2..ac2c2cf29e3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -636,7 +636,8 @@ static void rebuild_check_host(void); static void rebuild_role_grants(void); static void free_acl_user(ACL_USER *acl_user); static ACL_USER *find_user_no_anon(const char *host, const char *user, - my_bool exact); + my_bool exact); +static ACL_USER *find_user(const char *host, const char *user, const char *ip); static ACL_USER *find_acl_role(const char *user); static bool update_user_table(THD *thd, TABLE *table, const char *host, const char *user, const char *new_password, @@ -2412,6 +2413,26 @@ bool is_acl_user(const char *host, const char *user) } +static ACL_USER * +find_user(const char *host, const char *user, const char *ip) +{ + ACL_USER *result= NULL; + mysql_mutex_assert_owner(&acl_cache->lock); + for (uint i=0; i < acl_users.elements; i++) + { + ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); + if ((!acl_user_tmp->user.str || + !strcmp(user, acl_user_tmp->user.str)) && + compare_hostname(&acl_user_tmp->host, host, ip)) + { + result= acl_user_tmp; + break; + } + } + return result; +} + + /* Find first entry that matches the current user */ @@ -8703,17 +8724,11 @@ static bool find_mpvio_user(MPVIO_EXT *mpvio) DBUG_ASSERT(mpvio->acl_user == 0); mysql_mutex_lock(&acl_cache->lock); - for (uint i=0; i < acl_users.elements; i++) - { - ACL_USER *acl_user_tmp= dynamic_element(&acl_users, i, ACL_USER*); - if ((!acl_user_tmp->user.str || - !strcmp(sctx->user, acl_user_tmp->user.str)) && - compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) - { - mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); - break; - } - } + + ACL_USER *user= find_user(sctx->host, sctx->user, sctx->ip); + if (user) + mpvio->acl_user= user->copy(&mem); + mysql_mutex_unlock(&acl_cache->lock); if (!mpvio->acl_user) From 45903359bf8e5ca21d7c6d5f4ae88754c642da5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:45:00 -0700 Subject: [PATCH 054/174] Fixed USER INVALID error when using anonymous user to login and calling SET ROLE NONE; --- sql/sql_acl.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ac2c2cf29e3..7068971eb28 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1682,8 +1682,8 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) if (!strcasecmp(rolename, "NONE")) { /* have to clear the privileges */ /* get the current user */ - acl_user= find_user_no_anon(thd->security_ctx->host, thd->security_ctx->user, - FALSE); + acl_user= find_user(thd->security_ctx->host, thd->security_ctx->user, + thd->security_ctx->ip); if (acl_user == NULL) { my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); From d96e7fa765a41dc511eac0b20894b69c4a9cc955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:45:11 -0700 Subject: [PATCH 055/174] Added testcase for acl_roles. The testcase checks to see if the create user command sets the is_role column to 'N' by default --- .../r/acl_roles_default_create_user_not_role.result | 6 ++++++ mysql-test/t/acl_roles_default_create_user_not_role.test | 9 +++++++++ 2 files changed, 15 insertions(+) create mode 100644 mysql-test/r/acl_roles_default_create_user_not_role.result create mode 100644 mysql-test/t/acl_roles_default_create_user_not_role.test diff --git a/mysql-test/r/acl_roles_default_create_user_not_role.result b/mysql-test/r/acl_roles_default_create_user_not_role.result new file mode 100644 index 00000000000..1ddb054c092 --- /dev/null +++ b/mysql-test/r/acl_roles_default_create_user_not_role.result @@ -0,0 +1,6 @@ +use mysql; +create user 'test'@'localhost'; +select user, host, is_role from user where user='test' and host='localhost'; +user host is_role +test localhost N +drop user 'test'@'localhost'; diff --git a/mysql-test/t/acl_roles_default_create_user_not_role.test b/mysql-test/t/acl_roles_default_create_user_not_role.test new file mode 100644 index 00000000000..f74a514523b --- /dev/null +++ b/mysql-test/t/acl_roles_default_create_user_not_role.test @@ -0,0 +1,9 @@ +connect (mysql, localhost, root,,); +use mysql; +create user 'test'@'localhost'; + +#check to see if a created user is not a role by default +select user, host, is_role from user where user='test' and host='localhost'; + +drop user 'test'@'localhost'; +disconnect mysql; From 5470c200d5d95a4866156c709a01d1976b4c775c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:45:25 -0700 Subject: [PATCH 056/174] Added testcase for the command SET ROLE. The testcase checks to see if the privileges are set accordingly to a newly created user. This is the most general usecase. --- mysql-test/r/acl_roles_set_role-simple.result | 31 +++++++++++++++++++ mysql-test/t/acl_roles_set_role-simple.test | 27 ++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 mysql-test/r/acl_roles_set_role-simple.result create mode 100644 mysql-test/t/acl_roles_set_role-simple.test diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result new file mode 100644 index 00000000000..3bcd1e76798 --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -0,0 +1,31 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +select user, host from mysql.user; +user host +test_role1 +root 127.0.0.1 +root ::1 +root Arrakis +root localhost +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 +grant select on *.* to 'test_role1'@''; +select * from mysql.user where user='test_role1'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + test_role1 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +set role 'test_role1'; +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where UserFk='test_role1'; +drop user 'test_user'@'localhost'; diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test new file mode 100644 index 00000000000..a9256a657d1 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -0,0 +1,27 @@ + +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +#manualy create role +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +select user, host from mysql.user; +select * from mysql.roles_mapping; +grant select on *.* to 'test_role1'@''; +select * from mysql.user where user='test_role1'; +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +set role 'test_role1'; +select * from mysql.roles_mapping; + +change_user 'root'; +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where UserFk='test_role1'; +drop user 'test_user'@'localhost'; From df16e7598324f87be4da640799ddcab745569b28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:45:39 -0700 Subject: [PATCH 057/174] Updated acl_roles_set_role-simple test to use default sql syntax. Also called show grants before and after set role. Unfortunately the role privileges printing are not implemented yet. --- mysql-test/r/acl_roles_set_role-simple.result | 12 +++++++++++- mysql-test/t/acl_roles_set_role-simple.test | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index 3bcd1e76798..2908cdac438 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -22,10 +22,20 @@ Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv D flush privileges; select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' -set role 'test_role1'; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' select * from mysql.roles_mapping; HostFk UserFk RoleFk localhost test_user test_role1 +set role none; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' delete from mysql.user where user='test_role1'; delete from mysql.roles_mapping where UserFk='test_role1'; drop user 'test_user'@'localhost'; +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test index a9256a657d1..82aa4c185af 100644 --- a/mysql-test/t/acl_roles_set_role-simple.test +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -18,10 +18,17 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; -set role 'test_role1'; +show grants; +set role test_role1; +show grants; +select * from mysql.roles_mapping; + +set role none; +--error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; change_user 'root'; delete from mysql.user where user='test_role1'; delete from mysql.roles_mapping where UserFk='test_role1'; drop user 'test_user'@'localhost'; +flush privileges; From a5b89398075c92aa9775c16c4aaab66a243ae78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:45:49 -0700 Subject: [PATCH 058/174] Added initial_role_grants variable to ACL_USER --- sql/sql_acl.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 7068971eb28..e795aa791b3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -231,6 +231,15 @@ public: instance of the class represents a role. */ DYNAMIC_ARRAY role_grants; + /* + In case of granting a role to a role, the access bits are merged together + via a bit OR operation and placed in the ACL_USER::access field. + + When rebuilding role_grants via the rebuild_role_grant function, + the ACL_USER::access field needs to be reset aswell. The field + initial_role_access holds the initial grants present in the table row. + */ + ulong initial_role_access; ACL_USER *copy(MEM_ROOT *root) { @@ -1130,6 +1139,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) DBUG_PRINT("info", ("Found role %s", user.user.str)); ACL_USER *entry= user.copy(&mem); entry->role_grants = user.role_grants; + /* set initial role access the same as the table row privileges */ + entry->initial_role_access = entry->access; my_hash_insert(&acl_roles, (uchar *)entry); HASH_SEARCH_STATE t; entry= (ACL_USER *) my_hash_first(&acl_roles, From aa4657f872e41aa8886a55185c1bb1d6e03d6cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:49:38 -0700 Subject: [PATCH 059/174] Added comment to justify error message --- sql/sql_acl.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index e795aa791b3..09ddd11c294 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1706,6 +1706,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) goto end; } + /* According to SQL standard, the same error message must be presented */ if (role == NULL) { my_error(ER_INVALID_ROLE, MYF(0), rolename); result= -1; @@ -1728,6 +1729,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) } } + /* According to SQL standard, the same error message must be presented */ if (!is_granted) { my_error(ER_INVALID_ROLE, MYF(0), rolename); @@ -7635,8 +7637,8 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, if ((au= find_user_no_anon(combo->host.str=(char*)sctx->ip, combo->user.str,FALSE))) goto found_acl; - if((au= find_user_no_anon(combo->host.str=(char*)"%", - combo->user.str, FALSE))) + if ((au= find_user_no_anon(combo->host.str=(char*)"%", + combo->user.str, FALSE))) goto found_acl; mysql_mutex_unlock(&acl_cache->lock); From c968a59d6e9e93f2b0cb1a17805625bb7757fcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:49:47 -0700 Subject: [PATCH 060/174] Added a reset_role_grants function specific for roles. The function also resets the initial role access bits. --- sql/sql_acl.cc | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 09ddd11c294..cf29966516e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -656,7 +656,9 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); static my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))); -static my_bool add_role_user_mapping(ROLE_GRANT_PAIR *entry); +static my_bool acl_role_reset_grant(ACL_USER *role, + void * not_used __attribute__((unused))); +static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -1295,8 +1297,8 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } my_hash_insert(&acl_roles_mappings, (uchar*) mapping); - } + free_root(&temp_root, MYF(0)); end_read_record(&read_record_info); @@ -2120,11 +2122,25 @@ void rebuild_check_host(void) init_check_host(); } +/* + Reset a role role_grants dynamic array. + Also, the role's access bits are reset to the ones present in the table. + + The function can be used as a walk action for hash elements aswell. +*/ +my_bool acl_role_reset_grant(ACL_USER *role, + void * not_used __attribute__((unused))) +{ + reset_dynamic(&role->role_grants); + /* Also reset the role access bits */ + role->access= role->initial_role_access; + return 0; +} + /* Reset a users role_grants dynamic array. - The function can is used as a walk action for hash elements aswell. - + The function can be used as a walk action for hash elements aswell. */ my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))) @@ -2180,7 +2196,7 @@ void rebuild_role_grants(void) acl_user_reset_grant(user, NULL); } my_hash_iterate(&acl_roles, - (my_hash_walk_action) acl_user_reset_grant, NULL); + (my_hash_walk_action) acl_role_reset_grant, NULL); /* Rebuild the direct links between users and roles in ACL_USER::role_grants From 9dcc6430b8089554c1985e907e583d0fc8c99f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:49:56 -0700 Subject: [PATCH 061/174] Modified add_role_user_mapping to also handle granting a role to a role. --- sql/sql_acl.cc | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index cf29966516e..00d7ff12d50 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1288,7 +1288,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) char *username= get_field(&temp_root, table->field[1]); char *rolename= get_field(&temp_root, table->field[2]); init_role_grant_pair(&mem, mapping, username, hostname, rolename); - if (add_role_user_mapping(mapping) == 1) { + if (add_role_user_mapping(mapping) == -1) { sql_print_error("Invalid roles_mapping table entry user:'%s@%s', rolename:'%s'", mapping->u_uname ? mapping->u_uname : "", mapping->u_hname ? mapping->u_hname : "", @@ -2152,29 +2152,48 @@ my_bool acl_user_reset_grant(ACL_USER *user, /* Add a the coresponding pointers present in the mapping to the entries in acl_users and acl_roles -*/ -my_bool add_role_user_mapping(ROLE_GRANT_PAIR *mapping) + Return values: + 0: The entry is valid and was added. + -1: The entry is invalid and was not added. + 1: The entry represents a mapping between two roles. +*/ +int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) { ACL_USER *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "", (mapping->u_uname) ? mapping->u_uname: "", TRUE); ACL_USER *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); + + int result= 0; + if (user == NULL || role == NULL) { - DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s", - mapping->u_uname, mapping->u_hname, - mapping->r_uname)); - return 1; + /* There still exists the possibility that the user is actually a role */ + if (user == NULL && role && (!mapping->u_hname || !mapping->u_hname[0]) + && /* in this case the grantee is a role */ + ((user= find_acl_role(mapping->u_uname ? mapping->u_uname: "")))) + { + result= 1; + } + else + { + DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s", + mapping->u_uname, mapping->u_hname, + mapping->r_uname)); + + return -1; + } } push_dynamic(&user->role_grants, (uchar*) &role); push_dynamic(&role->role_grants, (uchar*) &user); - DBUG_PRINT("info", ("Found user %s@%s having role granted %s@%s\n", + DBUG_PRINT("info", ("Found %s %s@%s having role granted %s@%s\n", + (result) ? "role" : "user", user->user.str, user->host.hostname, role->user.str, role->host.hostname)); - return 0; + return result; } @@ -2211,7 +2230,7 @@ void rebuild_role_grants(void) If add_role_user_mapping detects an invalid entry, it will not add the mapping into the ACL_USER::role_grants array. */ - DBUG_ASSERT(status == 0); + DBUG_ASSERT(status >= 0); } DBUG_VOID_RETURN; From 82a5464a6caad702e3e1bce02153ce40a274637f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:06 -0700 Subject: [PATCH 062/174] Removed unused hash search. --- sql/sql_acl.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 00d7ff12d50..a4804d23c77 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1144,9 +1144,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) /* set initial role access the same as the table row privileges */ entry->initial_role_access = entry->access; my_hash_insert(&acl_roles, (uchar *)entry); - HASH_SEARCH_STATE t; - entry= (ACL_USER *) my_hash_first(&acl_roles, - (uchar *)entry->user.str, entry->user.length, &t); continue; } From f22a50b2f9dca8405524015f8219c8de29413148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:15 -0700 Subject: [PATCH 063/174] Added rights propagation for granting a role to a role --- sql/sql_acl.cc | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index a4804d23c77..79126ef2ab6 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -658,7 +658,10 @@ static my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))); static my_bool acl_role_reset_grant(ACL_USER *role, void * not_used __attribute__((unused))); +static my_bool acl_role_propagate_grants(ACL_USER *role, + void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); +static my_bool get_role_access(ACL_USER *role, ulong *access, my_bool use_initial); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -1299,6 +1302,9 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) free_root(&temp_root, MYF(0)); end_read_record(&read_record_info); + my_hash_iterate(&acl_roles, + (my_hash_walk_action) acl_role_propagate_grants, NULL); + if (!initialized) mysql_mutex_unlock(&acl_cache->lock); @@ -2119,6 +2125,15 @@ void rebuild_check_host(void) init_check_host(); } +static my_bool acl_role_propagate_grants(ACL_USER *role, + void * not_used __attribute__((unused))) +{ + ulong access; + get_role_access(role, &access, TRUE); + role->access= access; + return 0; +} + /* Reset a role role_grants dynamic array. Also, the role's access bits are reset to the ones present in the table. @@ -2146,6 +2161,60 @@ my_bool acl_user_reset_grant(ACL_USER *user, return 0; } +/* + The function scans through all roles granted to the role passed as argument + and places the permissions in the access variable. + + Return values: + TRUE: Error or invalid parameteres + FALSE: All ok; +*/ +my_bool get_role_access(ACL_USER *role, ulong *access, my_bool use_initial) +{ + DBUG_ENTER("get_role_access"); + if (!role || !access) + DBUG_RETURN(1); + + ulong result= 0; + HASH explored; /* temporary hash table to hold all explored roles */ + List queue; + + (void) my_hash_init2(&explored,50,system_charset_info, + 0,0,0, (my_hash_get_key) acl_role_get_key, + NULL, 0); + my_hash_insert(&explored, (uchar*) role); + queue.push_back(&role); + while (!queue.is_empty()) + { + ACL_USER *current= *queue.pop(); + result|= (use_initial) ? current->initial_role_access : current->access; + for (uint i=0 ; i < current->role_grants.elements ; i++) + { + ACL_USER *neighbour= *(dynamic_element(¤t->role_grants, + i, ACL_USER**)); + /* check if the neighbour is a role; pass if not*/ + if (neighbour->host.hostname || + !find_acl_role(neighbour->user.str ? current->user.str : "")) + continue; + + /* check if it was already explored */ + HASH_SEARCH_STATE t; + if (my_hash_first(&explored, (uchar *)neighbour->user.str, + neighbour->user.length, &t)) + continue; + + /* add it to the TO-DO exploration queue */ + queue.push_back(&neighbour); + my_hash_insert(&explored, (uchar *)neighbour); + } + } + + my_hash_free(&explored); + + *access= result; + DBUG_RETURN(0); +} + /* Add a the coresponding pointers present in the mapping to the entries in acl_users and acl_roles @@ -2230,6 +2299,9 @@ void rebuild_role_grants(void) DBUG_ASSERT(status >= 0); } + my_hash_iterate(&acl_roles, + (my_hash_walk_action) acl_role_propagate_grants, NULL); + DBUG_VOID_RETURN; } /* Return true if there is no users that can match the given host */ From b4f3ba2643605e08259c0296c3b9e2b7c5db791f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:24 -0700 Subject: [PATCH 064/174] Added testcase to check that granting a role to a role works. --- .../r/acl_roles_set_role-recursive.result | 88 +++++++++++++++++++ .../t/acl_roles_set_role-recursive.test | 63 +++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 mysql-test/r/acl_roles_set_role-recursive.result create mode 100644 mysql-test/t/acl_roles_set_role-recursive.test diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result new file mode 100644 index 00000000000..ac7391b8a91 --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -0,0 +1,88 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +create user 'test_role2'@''; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user; +user host +test_role1 +test_role2 +root 127.0.0.1 +root ::1 +root Arrakis +root localhost +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 + test_role1 test_role2 +grant select on *.* to 'test_role2'@''; +select * from mysql.user where user like 'test_role%'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + test_role1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y + test_role2 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 + test_role1 test_role2 +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role none; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role test_role2; +ERROR HY000: The role 'test_role2' has not been granted or is invalid. +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 + test_role1 test_role2 +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role none; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where UserFk like 'test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where UserFk like 'test_role2'; +drop user 'test_user'@'localhost'; +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test new file mode 100644 index 00000000000..c91d91d8a0d --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -0,0 +1,63 @@ + +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +#manualy create role +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +create user 'test_role2'@''; +#manualy create role +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +select user, host from mysql.user; +select * from mysql.roles_mapping; +grant select on *.* to 'test_role2'@''; +select * from mysql.user where user like 'test_role%'; +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +show grants; +set role test_role1; +show grants; +select * from mysql.roles_mapping; + +show grants; +set role none; +show grants; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +show grants; +--error ER_INVALID_ROLE +set role test_role2; +show grants; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +#Make sure that this still works after an ER_INVALID_ROLE error +show grants; +set role test_role1; +show grants; +select * from mysql.roles_mapping; + +show grants; +set role none; +show grants; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +change_user 'root'; +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where UserFk like 'test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where UserFk like 'test_role2'; +drop user 'test_user'@'localhost'; +flush privileges; From 0624020a76cdb8622cef7f075b32e3b4a229bacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:33 -0700 Subject: [PATCH 065/174] Implemented the detection of the final access bits of a role via a DEPTH FIRST SEARCH from the grant role to role graph. --- sql/sql_acl.cc | 160 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 127 insertions(+), 33 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 79126ef2ab6..2ec7381be2f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -221,6 +221,7 @@ public: LEX_STRING user; uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 + uchar flags; // field used to store various state information enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_STRING plugin; @@ -550,6 +551,15 @@ typedef struct st_role_grant char *r_uname; LEX_STRING hashkey; } ROLE_GRANT_PAIR; +/* + Struct to hold the state of a node during a Depth First Search exploration +*/ +template class NODE_STATE +{ +public: + T *node_data; /* pointer to the node data */ + uint neigh_idx; /* the neighbour that needs to be evaluated next */ +}; static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, my_bool not_used __attribute__((unused))) @@ -619,6 +629,11 @@ static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry, #define NORMAL_HANDSHAKE_SIZE 6 #define ROLE_ASSIGN_COLUMN_IDX 42 +/* various flags valid for ACL_USER */ +#define IS_ROLE (1L << 0) +#define ROLE_VISITED (1L << 1) +#define ROLE_GRANTS_FINAL (1L << 2) + static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; static HASH acl_roles; @@ -661,7 +676,7 @@ static my_bool acl_role_reset_grant(ACL_USER *role, static my_bool acl_role_propagate_grants(ACL_USER *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); -static my_bool get_role_access(ACL_USER *role, ulong *access, my_bool use_initial); +static my_bool get_role_access(ACL_USER *role, ulong *access); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -2129,8 +2144,7 @@ static my_bool acl_role_propagate_grants(ACL_USER *role, void * not_used __attribute__((unused))) { ulong access; - get_role_access(role, &access, TRUE); - role->access= access; + get_role_access(role, &access); return 0; } @@ -2146,6 +2160,7 @@ my_bool acl_role_reset_grant(ACL_USER *role, reset_dynamic(&role->role_grants); /* Also reset the role access bits */ role->access= role->initial_role_access; + role->flags&= ~ROLE_GRANTS_FINAL; return 0; } @@ -2169,49 +2184,122 @@ my_bool acl_user_reset_grant(ACL_USER *user, TRUE: Error or invalid parameteres FALSE: All ok; */ -my_bool get_role_access(ACL_USER *role, ulong *access, my_bool use_initial) +my_bool get_role_access(ACL_USER *role, ulong *access) { DBUG_ENTER("get_role_access"); - if (!role || !access) - DBUG_RETURN(1); + DBUG_ASSERT(role); + DBUG_ASSERT(access); + /* the search operation should always leave the ROLE_VISITED flag clean + for all nodes involved in the search */ + DBUG_ASSERT(!(role->flags & ROLE_VISITED)); - ulong result= 0; - HASH explored; /* temporary hash table to hold all explored roles */ - List queue; - - (void) my_hash_init2(&explored,50,system_charset_info, - 0,0,0, (my_hash_get_key) acl_role_get_key, - NULL, 0); - my_hash_insert(&explored, (uchar*) role); - queue.push_back(&role); - while (!queue.is_empty()) + /* + There exists the possibility that the role's access bits are final + and we can just get the access bits without doing the more expensive + search operation + */ + if (role->flags & ROLE_GRANTS_FINAL) { - ACL_USER *current= *queue.pop(); - result|= (use_initial) ? current->initial_role_access : current->access; - for (uint i=0 ; i < current->role_grants.elements ; i++) + *access= role->access; + DBUG_RETURN(FALSE); + } + + DYNAMIC_ARRAY stack; /* stack used to simulate the recursive calls of DFS + * used a DYNAMIC_ARRAY to reduce the number of + * malloc calls to a minimum */ + NODE_STATE state; /* variable used to insert elements in the stack */ + + state.neigh_idx= 0; + state.node_data= role; + role->flags|= ROLE_VISITED; + + (void) my_init_dynamic_array(&stack,sizeof(NODE_STATE), + 20, 50, MYF(0)); + insert_dynamic(&stack, &state); + + + while (stack.elements) + { + NODE_STATE *curr_state= dynamic_element(&stack, + stack.elements - 1, + NODE_STATE *); + + DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); + + ACL_USER *current= state.node_data; + ACL_USER *neighbour= NULL; + /* iterate through the neighbours until a first valid jump-to + neighbour is found */ + my_bool found= FALSE; + uint i; + for (i= curr_state->neigh_idx; + i < current->role_grants.elements && found == FALSE; i++) { - ACL_USER *neighbour= *(dynamic_element(¤t->role_grants, - i, ACL_USER**)); + neighbour= *(dynamic_element(¤t->role_grants, i, ACL_USER**)); /* check if the neighbour is a role; pass if not*/ - if (neighbour->host.hostname || - !find_acl_role(neighbour->user.str ? current->user.str : "")) + if (!(neighbour->flags & ~IS_ROLE)) continue; - /* check if it was already explored */ - HASH_SEARCH_STATE t; - if (my_hash_first(&explored, (uchar *)neighbour->user.str, - neighbour->user.length, &t)) + /* check if it forms a cycle */ + if (neighbour->flags & ROLE_VISITED) + { + /* TODO the edge needs to be ignored */ continue; + } - /* add it to the TO-DO exploration queue */ - queue.push_back(&neighbour); - my_hash_insert(&explored, (uchar *)neighbour); + /* check if it was already explored, in that case, just set the rights + and move on */ + if (neighbour->flags & ROLE_GRANTS_FINAL) + { + current->access|= neighbour->access; + continue; + } + + /* set the current state search index to the next index + this needs to be done before inserting, so as to make sure that the + pointer is valid + */ + found= TRUE; + } + + if (found) + { + /* we're going to have to take a look at the same neighbour again + once it is done being explored, thus, set the neigh_idx to "i" + which is the current neighbour that will be added on the stack*/ + curr_state->neigh_idx= i; + + /* some sanity checks */ + DBUG_ASSERT(!(neighbour->flags & ROLE_VISITED)); + DBUG_ASSERT(!(neighbour->flags & ROLE_GRANTS_FINAL)); + /* add the neighbour on the stack */ + neighbour->flags|= ROLE_VISITED; + state.neigh_idx= 0; + state.node_data= neighbour; + insert_dynamic(&stack, &state); + } + else + { + /* make sure we got a correct node */ + DBUG_ASSERT(!(curr_state->node_data->flags & ROLE_GRANTS_FINAL)); + DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); + /* + if we have finished with exploring the current node, pop it off the + stack + */ + curr_state= (NODE_STATE *)pop_dynamic(&stack); + curr_state->node_data->flags&= ~ROLE_VISITED; /* clear the visited bit */ + curr_state->node_data->flags|= ROLE_GRANTS_FINAL; + /* add the own role's rights once it's finished exploring */ + curr_state->node_data->access|= curr_state->node_data->initial_role_access; } } - my_hash_free(&explored); - *access= result; + /* cleanup */ + delete_dynamic(&stack); + /* finally set the access */ + *access= role->access; DBUG_RETURN(0); } @@ -2253,7 +2341,13 @@ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) } push_dynamic(&user->role_grants, (uchar*) &role); - push_dynamic(&role->role_grants, (uchar*) &user); + /* + Only add the other link if the grant is between a user + and a role, otherwise, the grant is unidirectional, + so as to prevent cycles in the grant role to role graph. + */ + if (!result) + push_dynamic(&role->role_grants, (uchar*) &user); DBUG_PRINT("info", ("Found %s %s@%s having role granted %s@%s\n", (result) ? "role" : "user", From 7faba82babfd8c826755c971b2470c71e38a8b2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:42 -0700 Subject: [PATCH 066/174] Fixed wrong IS_ROLE check. --- sql/sql_acl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 2ec7381be2f..0b80ae021e2 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2237,7 +2237,7 @@ my_bool get_role_access(ACL_USER *role, ulong *access) { neighbour= *(dynamic_element(¤t->role_grants, i, ACL_USER**)); /* check if the neighbour is a role; pass if not*/ - if (!(neighbour->flags & ~IS_ROLE)) + if (!(neighbour->flags & IS_ROLE)) continue; /* check if it forms a cycle */ From 221558efd523e3041fa6dccdabcdb4c6386474d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:50:51 -0700 Subject: [PATCH 067/174] Extended ACL_USER to create ACL_ROLE. Moved fields corresponding to role entries to the ACL_ROLE class. --- sql/sql_acl.cc | 195 +++++++++++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 79 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 0b80ae021e2..b36b38637de 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -221,26 +221,14 @@ public: LEX_STRING user; uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 - uchar flags; // field used to store various state information enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_STRING plugin; LEX_STRING auth_string; /* list to hold references to granted roles (ACL_USER instances) - if the instance of the class represents a user, or a user if the - instance of the class represents a role. */ DYNAMIC_ARRAY role_grants; - /* - In case of granting a role to a role, the access bits are merged together - via a bit OR operation and placed in the ACL_USER::access field. - - When rebuilding role_grants via the rebuild_role_grant function, - the ACL_USER::access field needs to be reset aswell. The field - initial_role_access holds the initial grants present in the table row. - */ - ulong initial_role_access; ACL_USER *copy(MEM_ROOT *root) { @@ -263,6 +251,24 @@ public: bzero(&dst->role_grants, sizeof(role_grants)); return dst; } + +}; + +class ACL_ROLE :public ACL_USER +{ +public: + uchar flags; // field used to store various state information + /* + In case of granting a role to a role, the access bits are merged together + via a bit OR operation and placed in the ACL_USER::access field. + + When rebuilding role_grants via the rebuild_role_grant function, + the ACL_USER::access field needs to be reset aswell. The field + initial_role_access holds the initial grants present in the table row. + */ + ulong initial_role_access; + DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted + }; class ACL_DB :public ACL_ACCESS @@ -554,11 +560,11 @@ typedef struct st_role_grant /* Struct to hold the state of a node during a Depth First Search exploration */ -template class NODE_STATE +class NODE_STATE { public: - T *node_data; /* pointer to the node data */ - uint neigh_idx; /* the neighbour that needs to be evaluated next */ + ACL_ROLE *node_data; /* pointer to the node data */ + uint neigh_idx; /* the neighbour that needs to be evaluated next */ }; static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, @@ -659,10 +665,11 @@ static void init_check_host(void); static void rebuild_check_host(void); static void rebuild_role_grants(void); static void free_acl_user(ACL_USER *acl_user); +static void free_acl_role(ACL_ROLE *acl_role); static ACL_USER *find_user_no_anon(const char *host, const char *user, my_bool exact); static ACL_USER *find_user(const char *host, const char *user, const char *ip); -static ACL_USER *find_acl_role(const char *user); +static ACL_ROLE *find_acl_role(const char *user); static bool update_user_table(THD *thd, TABLE *table, const char *host, const char *user, const char *new_password, uint new_password_len); @@ -671,12 +678,12 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables); static inline void get_grantor(THD *thd, char* grantor); static my_bool acl_user_reset_grant(ACL_USER *user, void * not_used __attribute__((unused))); -static my_bool acl_role_reset_grant(ACL_USER *role, +static my_bool acl_role_reset_grant(ACL_ROLE *role, void * not_used __attribute__((unused))); -static my_bool acl_role_propagate_grants(ACL_USER *role, +static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); -static my_bool get_role_access(ACL_USER *role, ulong *access); +static my_bool get_role_access(ACL_ROLE *role, ulong *access); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -692,13 +699,42 @@ enum enum_acl_lists ROLES_MAPPINGS_HASH }; +static ACL_ROLE *create_role_from_user(MEM_ROOT *root, ACL_USER *user) +{ + ACL_ROLE *dst= (ACL_ROLE *) alloc_root(root, sizeof(ACL_ROLE)); + if (!dst) + return 0; + *(ACL_USER *)dst= *user; + dst->user.str= safe_strdup_root(root, user->user.str); + dst->user.length= user->user.length; + dst->ssl_cipher= safe_strdup_root(root, user->ssl_cipher); + dst->x509_issuer= safe_strdup_root(root, user->x509_issuer); + dst->x509_subject= safe_strdup_root(root, user->x509_subject); + if (user->plugin.str == native_password_plugin_name.str || + user->plugin.str == old_password_plugin_name.str) + dst->plugin= user->plugin; + else + dst->plugin.str= strmake_root(root, user->plugin.str, user->plugin.length); + dst->auth_string.str= safe_strdup_root(root, user->auth_string.str); + dst->host.hostname= safe_strdup_root(root, user->host.hostname); + bzero(&dst->role_grants, sizeof(dst->role_grants)); + bzero(&dst->parent_grantee, sizeof(dst->parent_grantee)); + dst->flags= 0; + return dst; +} static void free_acl_user(ACL_USER *user) { delete_dynamic(&(user->role_grants)); } -/* +static +void +free_acl_role(ACL_ROLE *role) +{ + delete_dynamic(&(role->role_grants)); + delete_dynamic(&(role->parent_grantee)); +}/* Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. */ @@ -953,7 +989,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) (void) my_init_dynamic_array(&acl_users,sizeof(ACL_USER), 50, 100, MYF(0)); (void) my_hash_init2(&acl_roles,50,system_charset_info, 0,0,0, (my_hash_get_key) acl_role_get_key, - (void (*)(void *))free_acl_user, 0); + (void (*)(void *))free_acl_role, 0); username_char_length= min(table->field[1]->char_length(), USERNAME_CHAR_LENGTH); password_length= table->field[2]->field_length / @@ -1008,9 +1044,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) user.user.str= username; user.user.length= username? strlen(username) : 0; - /* If the user entry is a role, skip password and hostname checks + /* + If the user entry is a role, skip password and hostname checks A user can not log in with a role so some checks are not necessary - */ + */ is_role= check_is_role(table); if (!is_role && check_no_resolve && @@ -1152,15 +1189,17 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) #endif } - (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_USER *), + (void) my_init_dynamic_array(&user.role_grants,sizeof(ACL_ROLE *), 50, 100, MYF(0)); if (is_role) { DBUG_PRINT("info", ("Found role %s", user.user.str)); - ACL_USER *entry= user.copy(&mem); + ACL_ROLE *entry= create_role_from_user(&mem, &user); entry->role_grants = user.role_grants; + (void) my_init_dynamic_array(&entry->parent_grantee, sizeof(ACL_USER *), + 50, 100, MYF(0)); /* set initial role access the same as the table row privileges */ - entry->initial_role_access = entry->access; + entry->initial_role_access= entry->access; my_hash_insert(&acl_roles, (uchar *)entry); continue; @@ -1707,7 +1746,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) /* clear role privileges */ mysql_mutex_lock(&acl_cache->lock); - ACL_USER *role= find_acl_role(rolename); + ACL_ROLE *role= find_acl_role(rolename); ACL_USER *acl_user; if (!strcasecmp(rolename, "NONE")) { @@ -1733,9 +1772,9 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) goto end; } - for (uint i=0 ; i < role->role_grants.elements ; i++) + for (uint i=0 ; i < role->parent_grantee.elements ; i++) { - acl_user= *(dynamic_element(&role->role_grants, i, ACL_USER**)); + acl_user= *(dynamic_element(&role->parent_grantee, i, ACL_USER**)); if ((!acl_user->user.str && !thd->security_ctx->user[0]) || (acl_user->user.str && !strcmp(thd->security_ctx->user, acl_user->user.str))) @@ -2140,7 +2179,7 @@ void rebuild_check_host(void) init_check_host(); } -static my_bool acl_role_propagate_grants(ACL_USER *role, +static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))) { ulong access; @@ -2154,10 +2193,11 @@ static my_bool acl_role_propagate_grants(ACL_USER *role, The function can be used as a walk action for hash elements aswell. */ -my_bool acl_role_reset_grant(ACL_USER *role, +my_bool acl_role_reset_grant(ACL_ROLE *role, void * not_used __attribute__((unused))) { reset_dynamic(&role->role_grants); + reset_dynamic(&role->parent_grantee); /* Also reset the role access bits */ role->access= role->initial_role_access; role->flags&= ~ROLE_GRANTS_FINAL; @@ -2184,13 +2224,15 @@ my_bool acl_user_reset_grant(ACL_USER *user, TRUE: Error or invalid parameteres FALSE: All ok; */ -my_bool get_role_access(ACL_USER *role, ulong *access) +my_bool get_role_access(ACL_ROLE *role, ulong *access) { DBUG_ENTER("get_role_access"); DBUG_ASSERT(role); DBUG_ASSERT(access); - /* the search operation should always leave the ROLE_VISITED flag clean - for all nodes involved in the search */ + /* + The search operation should always leave the ROLE_VISITED flag clean + for all nodes involved in the search + */ DBUG_ASSERT(!(role->flags & ROLE_VISITED)); /* @@ -2204,41 +2246,40 @@ my_bool get_role_access(ACL_USER *role, ulong *access) DBUG_RETURN(FALSE); } - DYNAMIC_ARRAY stack; /* stack used to simulate the recursive calls of DFS - * used a DYNAMIC_ARRAY to reduce the number of - * malloc calls to a minimum */ - NODE_STATE state; /* variable used to insert elements in the stack */ + /* + Stack used to simulate the recursive calls of DFS. + It uses a DYNAMIC_ARRAY to reduce the number of + malloc calls to a minimum + */ + DYNAMIC_ARRAY stack; + NODE_STATE state; /* variable used to insert elements in the stack */ state.neigh_idx= 0; state.node_data= role; role->flags|= ROLE_VISITED; - (void) my_init_dynamic_array(&stack,sizeof(NODE_STATE), - 20, 50, MYF(0)); - insert_dynamic(&stack, &state); - + (void) my_init_dynamic_array(&stack,sizeof(NODE_STATE), 20, 50, MYF(0)); + push_dynamic(&stack, &state); while (stack.elements) { - NODE_STATE *curr_state= dynamic_element(&stack, - stack.elements - 1, - NODE_STATE *); + NODE_STATE *curr_state= dynamic_element(&stack, stack.elements - 1, + NODE_STATE *); DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); - ACL_USER *current= state.node_data; - ACL_USER *neighbour= NULL; - /* iterate through the neighbours until a first valid jump-to - neighbour is found */ + ACL_ROLE *current= state.node_data; + ACL_ROLE *neighbour= NULL; + /* + Iterate through the neighbours until a first valid jump-to + neighbour is found + */ my_bool found= FALSE; uint i; for (i= curr_state->neigh_idx; i < current->role_grants.elements && found == FALSE; i++) { - neighbour= *(dynamic_element(¤t->role_grants, i, ACL_USER**)); - /* check if the neighbour is a role; pass if not*/ - if (!(neighbour->flags & IS_ROLE)) - continue; + neighbour= *(dynamic_element(¤t->role_grants, i, ACL_ROLE**)); /* check if it forms a cycle */ if (neighbour->flags & ROLE_VISITED) @@ -2247,26 +2288,31 @@ my_bool get_role_access(ACL_USER *role, ulong *access) continue; } - /* check if it was already explored, in that case, just set the rights - and move on */ + /* + Check if it was already explored, in that case, just set the rights + and move on + */ if (neighbour->flags & ROLE_GRANTS_FINAL) { current->access|= neighbour->access; continue; } - /* set the current state search index to the next index + /* + Set the current state search index to the next index this needs to be done before inserting, so as to make sure that the pointer is valid - */ + */ found= TRUE; } if (found) { - /* we're going to have to take a look at the same neighbour again + /* + we're going to have to take a look at the same neighbour again once it is done being explored, thus, set the neigh_idx to "i" - which is the current neighbour that will be added on the stack*/ + which is the current neighbour that will be added on the stack + */ curr_state->neigh_idx= i; /* some sanity checks */ @@ -2276,29 +2322,26 @@ my_bool get_role_access(ACL_USER *role, ulong *access) neighbour->flags|= ROLE_VISITED; state.neigh_idx= 0; state.node_data= neighbour; - insert_dynamic(&stack, &state); + push_dynamic(&stack, &state); } else { - /* make sure we got a correct node */ + /* Make sure we got a correct node */ DBUG_ASSERT(!(curr_state->node_data->flags & ROLE_GRANTS_FINAL)); DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); - /* - if we have finished with exploring the current node, pop it off the - stack - */ - curr_state= (NODE_STATE *)pop_dynamic(&stack); + /* Finished with exploring the current node, pop it off the stack */ + curr_state= (NODE_STATE *)pop_dynamic(&stack); curr_state->node_data->flags&= ~ROLE_VISITED; /* clear the visited bit */ curr_state->node_data->flags|= ROLE_GRANTS_FINAL; - /* add the own role's rights once it's finished exploring */ + /* Add the own role's rights once it's finished exploring */ curr_state->node_data->access|= curr_state->node_data->initial_role_access; } } - /* cleanup */ + /* Cleanup */ delete_dynamic(&stack); - /* finally set the access */ + /* Finally set the access */ *access= role->access; DBUG_RETURN(0); } @@ -2317,7 +2360,7 @@ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) ACL_USER *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "", (mapping->u_uname) ? mapping->u_uname: "", TRUE); - ACL_USER *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); + ACL_ROLE *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); int result= 0; @@ -2341,13 +2384,7 @@ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) } push_dynamic(&user->role_grants, (uchar*) &role); - /* - Only add the other link if the grant is between a user - and a role, otherwise, the grant is unidirectional, - so as to prevent cycles in the grant role to role graph. - */ - if (!result) - push_dynamic(&role->role_grants, (uchar*) &user); + push_dynamic(&role->parent_grantee, (uchar*) &user); DBUG_PRINT("info", ("Found %s %s@%s having role granted %s@%s\n", (result) ? "role" : "user", @@ -2681,7 +2718,7 @@ find_user_no_anon(const char *host, const char *user, my_bool exact) /* Find first entry that matches the current user */ -static ACL_USER * +static ACL_ROLE * find_acl_role(const char *user) { DBUG_ENTER("find_acl_role"); @@ -2689,7 +2726,7 @@ find_acl_role(const char *user) mysql_mutex_assert_owner(&acl_cache->lock); - DBUG_RETURN((ACL_USER *)my_hash_search(&acl_roles, (uchar *)user, + DBUG_RETURN((ACL_ROLE *)my_hash_search(&acl_roles, (uchar *)user, user ? strlen(user) : 0)); } From 540673f0461ee6e9d7c2009c69a855717ab4e8d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:01 -0700 Subject: [PATCH 068/174] Fixed comment indentation --- sql/sql_acl.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b36b38637de..1618aeff1c3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -734,7 +734,9 @@ free_acl_role(ACL_ROLE *role) { delete_dynamic(&(role->role_grants)); delete_dynamic(&(role->parent_grantee)); -}/* +} + +/* Convert scrambled password to binary form, according to scramble type, Binary form is stored in user.salt. */ From 68214d21a037bc153dd10426b680d737f2a7ba0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:10 -0700 Subject: [PATCH 069/174] Fixed failing tests due to wrong delete in the testsuite. --- mysql-test/r/acl_roles_set_role-recursive.result | 12 ++++-------- mysql-test/r/acl_roles_set_role-simple.result | 6 +----- mysql-test/t/acl_roles_set_role-recursive.test | 9 +++++---- mysql-test/t/acl_roles_set_role-simple.test | 2 +- sql/sql_acl.cc | 7 ++++--- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index ac7391b8a91..601999bf0c5 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -9,14 +9,11 @@ update mysql.user set is_role='Y' where user='test_role2'; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'test_role1', 'test_role2'); -select user, host from mysql.user; +flush privileges; +select user, host from mysql.user where user not like 'root'; user host test_role1 test_role2 -root 127.0.0.1 -root ::1 -root Arrakis -root localhost test_user localhost select * from mysql.roles_mapping; HostFk UserFk RoleFk @@ -81,8 +78,7 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' delete from mysql.user where user='test_role1'; -delete from mysql.roles_mapping where UserFk like 'test_role1'; delete from mysql.user where user='test_role2'; -delete from mysql.roles_mapping where UserFk like 'test_role2'; -drop user 'test_user'@'localhost'; +delete from mysql.roles_mapping; flush privileges; +drop user 'test_user'@'localhost'; diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index 2908cdac438..bbda2e23901 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -4,13 +4,9 @@ update mysql.user set is_role='Y' where user='test_role1'; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', 'test_user', 'test_role1'); -select user, host from mysql.user; +select user, host from mysql.user where user not like 'root'; user host test_role1 -root 127.0.0.1 -root ::1 -root Arrakis -root localhost test_user localhost select * from mysql.roles_mapping; HostFk UserFk RoleFk diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test index c91d91d8a0d..655176ab018 100644 --- a/mysql-test/t/acl_roles_set_role-recursive.test +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -13,7 +13,9 @@ update mysql.user set is_role='Y' where user='test_role2'; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'test_role1', 'test_role2'); -select user, host from mysql.user; +flush privileges; + +select user, host from mysql.user where user not like 'root'; select * from mysql.roles_mapping; grant select on *.* to 'test_role2'@''; select * from mysql.user where user like 'test_role%'; @@ -56,8 +58,7 @@ select * from mysql.roles_mapping; change_user 'root'; delete from mysql.user where user='test_role1'; -delete from mysql.roles_mapping where UserFk like 'test_role1'; delete from mysql.user where user='test_role2'; -delete from mysql.roles_mapping where UserFk like 'test_role2'; -drop user 'test_user'@'localhost'; +delete from mysql.roles_mapping; flush privileges; +drop user 'test_user'@'localhost'; diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test index 82aa4c185af..33fd2ef6b4e 100644 --- a/mysql-test/t/acl_roles_set_role-simple.test +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -7,7 +7,7 @@ update mysql.user set is_role='Y' where user='test_role1'; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', 'test_user', 'test_role1'); -select user, host from mysql.user; +select user, host from mysql.user where user not like 'root'; select * from mysql.roles_mapping; grant select on *.* to 'test_role1'@''; select * from mysql.user where user='test_role1'; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 1618aeff1c3..94d9c8cbc4b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -543,7 +543,7 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, return (uchar*) entry->key; } -static uchar* acl_role_get_key(ACL_USER *entry, size_t *length, +static uchar* acl_role_get_key(ACL_ROLE *entry, size_t *length, my_bool not_used __attribute__((unused))) { *length=(uint) entry->user.length; @@ -2377,9 +2377,9 @@ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) } else { - DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s", + DBUG_PRINT("warning", ("Invalid add_role_user_mapping '%s'@'%s' %s %p %p", mapping->u_uname, mapping->u_hname, - mapping->r_uname)); + mapping->r_uname, user, role)); return -1; } @@ -2725,6 +2725,7 @@ find_acl_role(const char *user) { DBUG_ENTER("find_acl_role"); DBUG_PRINT("enter",("user: '%s'", user)); + DBUG_PRINT("info", ("Hash elements: %ld", acl_roles.records)); mysql_mutex_assert_owner(&acl_cache->lock); From 00f4f408ea52d63841f3bed9c66be252092f0ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:19 -0700 Subject: [PATCH 070/174] Minor update on tests. Fixed possible failing condition due to different order of table rows --- mysql-test/r/acl_roles_set_role-recursive.result | 8 ++++++-- mysql-test/r/acl_roles_set_role-simple.result | 4 ++-- mysql-test/t/acl_roles_set_role-recursive.test | 6 ++++-- mysql-test/t/acl_roles_set_role-simple.test | 4 ++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 601999bf0c5..50a7f8494ba 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -15,14 +15,18 @@ user host test_role1 test_role2 test_user localhost -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where UserFk like 'test_user'; HostFk UserFk RoleFk localhost test_user test_role1 +select * from mysql.roles_mapping where UserFk like 'test_role1'; +HostFk UserFk RoleFk test_role1 test_role2 grant select on *.* to 'test_role2'@''; -select * from mysql.user where user like 'test_role%'; +select * from mysql.user where user like 'test_role1'; Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role test_role1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user like 'test_role2'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role test_role2 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y flush privileges; select * from mysql.roles_mapping; diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index bbda2e23901..4b2cbdec28d 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -32,6 +32,6 @@ set role none; select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' delete from mysql.user where user='test_role1'; -delete from mysql.roles_mapping where UserFk='test_role1'; -drop user 'test_user'@'localhost'; +delete from mysql.roles_mapping where RoleFk='test_role1'; flush privileges; +drop user 'test_user'@'localhost'; diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test index 655176ab018..983dbb8e1e5 100644 --- a/mysql-test/t/acl_roles_set_role-recursive.test +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -16,9 +16,11 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', flush privileges; select user, host from mysql.user where user not like 'root'; -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where UserFk like 'test_user'; +select * from mysql.roles_mapping where UserFk like 'test_role1'; grant select on *.* to 'test_role2'@''; -select * from mysql.user where user like 'test_role%'; +select * from mysql.user where user like 'test_role1'; +select * from mysql.user where user like 'test_role2'; flush privileges; change_user 'test_user'; diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test index 33fd2ef6b4e..048b6916d83 100644 --- a/mysql-test/t/acl_roles_set_role-simple.test +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -29,6 +29,6 @@ select * from mysql.roles_mapping; change_user 'root'; delete from mysql.user where user='test_role1'; -delete from mysql.roles_mapping where UserFk='test_role1'; -drop user 'test_user'@'localhost'; +delete from mysql.roles_mapping where RoleFk='test_role1'; flush privileges; +drop user 'test_user'@'localhost'; From f37168d40b97693c21a53ca061aac46084c3eee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:28 -0700 Subject: [PATCH 071/174] Split ACL_USER into ACL_USER_BASE and ACL_USER ACL_ROLE now inherits ACL_USER_BASE --- sql/sql_acl.cc | 84 ++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 94d9c8cbc4b..8b784a2bc6e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -212,23 +212,33 @@ public: char *db; }; -class ACL_USER :public ACL_ACCESS +class ACL_USER_BASE :public ACL_ACCESS +{ + +public: + static void *operator new(size_t size, MEM_ROOT *mem_root) + { return (void*) alloc_root(mem_root, size); } + + uchar flags; // field used to store various state information + LEX_STRING user; + /* + list to hold references to granted roles (ACL_USER instances) + */ + DYNAMIC_ARRAY role_grants; +}; + +class ACL_USER :public ACL_USER_BASE { public: acl_host_and_ip host; uint hostname_length; USER_RESOURCES user_resource; - LEX_STRING user; uint8 salt[SCRAMBLE_LENGTH + 1]; // scrambled password in binary form uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; LEX_STRING plugin; LEX_STRING auth_string; - /* - list to hold references to granted roles (ACL_USER instances) - */ - DYNAMIC_ARRAY role_grants; ACL_USER *copy(MEM_ROOT *root) { @@ -254,10 +264,9 @@ public: }; -class ACL_ROLE :public ACL_USER +class ACL_ROLE :public ACL_USER_BASE { public: - uchar flags; // field used to store various state information /* In case of granting a role to a role, the access bits are merged together via a bit OR operation and placed in the ACL_USER::access field. @@ -269,6 +278,8 @@ public: ulong initial_role_access; DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted + ACL_ROLE(ACL_USER * user, MEM_ROOT *mem); + }; class ACL_DB :public ACL_ACCESS @@ -699,35 +710,25 @@ enum enum_acl_lists ROLES_MAPPINGS_HASH }; -static ACL_ROLE *create_role_from_user(MEM_ROOT *root, ACL_USER *user) +ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) { - ACL_ROLE *dst= (ACL_ROLE *) alloc_root(root, sizeof(ACL_ROLE)); - if (!dst) - return 0; - *(ACL_USER *)dst= *user; - dst->user.str= safe_strdup_root(root, user->user.str); - dst->user.length= user->user.length; - dst->ssl_cipher= safe_strdup_root(root, user->ssl_cipher); - dst->x509_issuer= safe_strdup_root(root, user->x509_issuer); - dst->x509_subject= safe_strdup_root(root, user->x509_subject); - if (user->plugin.str == native_password_plugin_name.str || - user->plugin.str == old_password_plugin_name.str) - dst->plugin= user->plugin; - else - dst->plugin.str= strmake_root(root, user->plugin.str, user->plugin.length); - dst->auth_string.str= safe_strdup_root(root, user->auth_string.str); - dst->host.hostname= safe_strdup_root(root, user->host.hostname); - bzero(&dst->role_grants, sizeof(dst->role_grants)); - bzero(&dst->parent_grantee, sizeof(dst->parent_grantee)); - dst->flags= 0; - return dst; + + access= user->access; + sort= user->sort; + this->user.str= safe_strdup_root(root, user->user.str); + this->user.length= user->user.length; + bzero(&role_grants, sizeof(role_grants)); + bzero(&parent_grantee, sizeof(parent_grantee)); + flags= IS_ROLE; } + static void free_acl_user(ACL_USER *user) { delete_dynamic(&(user->role_grants)); } + static void free_acl_role(ACL_ROLE *role) @@ -1196,10 +1197,11 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (is_role) { DBUG_PRINT("info", ("Found role %s", user.user.str)); - ACL_ROLE *entry= create_role_from_user(&mem, &user); + ACL_ROLE *entry= new (&mem) ACL_ROLE(&user, &mem); + //create_role_from_user(&mem, &user); entry->role_grants = user.role_grants; - (void) my_init_dynamic_array(&entry->parent_grantee, sizeof(ACL_USER *), - 50, 100, MYF(0)); + (void) my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 50, 100, MYF(0)); /* set initial role access the same as the table row privileges */ entry->initial_role_access= entry->access; my_hash_insert(&acl_roles, (uchar *)entry); @@ -1749,6 +1751,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) mysql_mutex_lock(&acl_cache->lock); ACL_ROLE *role= find_acl_role(rolename); + ACL_USER_BASE *acl_user_base; ACL_USER *acl_user; if (!strcasecmp(rolename, "NONE")) { @@ -1776,7 +1779,11 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) for (uint i=0 ; i < role->parent_grantee.elements ; i++) { - acl_user= *(dynamic_element(&role->parent_grantee, i, ACL_USER**)); + acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**)); + if (acl_user_base->flags & IS_ROLE) + continue; + + acl_user= (ACL_USER *)acl_user_base; if ((!acl_user->user.str && !thd->security_ctx->user[0]) || (acl_user->user.str && !strcmp(thd->security_ctx->user, acl_user->user.str))) @@ -2359,11 +2366,13 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) */ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) { - ACL_USER *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "", + + ACL_USER_BASE *user= find_user_no_anon((mapping->u_hname) ? mapping->u_hname: "", (mapping->u_uname) ? mapping->u_uname: "", TRUE); ACL_ROLE *role= find_acl_role(mapping->r_uname ? mapping->r_uname: ""); + int result= 0; if (user == NULL || role == NULL) @@ -2388,10 +2397,11 @@ int add_role_user_mapping(ROLE_GRANT_PAIR *mapping) push_dynamic(&user->role_grants, (uchar*) &role); push_dynamic(&role->parent_grantee, (uchar*) &user); - DBUG_PRINT("info", ("Found %s %s@%s having role granted %s@%s\n", + DBUG_PRINT("info", ("Found %s %s@%s having role granted %s\n", (result) ? "role" : "user", - user->user.str, user->host.hostname, - role->user.str, role->host.hostname)); + user->user.str, + (result) ? "" : ((ACL_USER *)user)->host.hostname, + role->user.str)); return result; } From 2755c342e63f0d3a29416dd86522fc7977e623f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:37 -0700 Subject: [PATCH 072/174] Added extra comments to explain the ACL_USER_BASE flags usage, as well as fix an issue with get_role_access. The bug caused roles rights to not be propagated if a push on the stack happened. The newly finished neighbour was never reevaluated. --- sql/sql_acl.cc | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 8b784a2bc6e..77db5cadad7 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -648,7 +648,12 @@ static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry, #define ROLE_ASSIGN_COLUMN_IDX 42 /* various flags valid for ACL_USER */ #define IS_ROLE (1L << 0) +/* Flag to mark that a ROLE has been visited in a DEPTH_FIRST_SEARCH */ #define ROLE_VISITED (1L << 1) +/* + Flag to mark that the ROLE's access bits are final, having been inherited + from other granted roles + */ #define ROLE_GRANTS_FINAL (1L << 2) @@ -2236,6 +2241,7 @@ my_bool acl_user_reset_grant(ACL_USER *user, my_bool get_role_access(ACL_ROLE *role, ulong *access) { DBUG_ENTER("get_role_access"); + DBUG_PRINT("enter",("role: '%s'", role->user.str)); DBUG_ASSERT(role); DBUG_ASSERT(access); /* @@ -2252,6 +2258,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) if (role->flags & ROLE_GRANTS_FINAL) { *access= role->access; + DBUG_PRINT("exit", ("Role access: %lu", *access)); DBUG_RETURN(FALSE); } @@ -2267,7 +2274,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) state.node_data= role; role->flags|= ROLE_VISITED; - (void) my_init_dynamic_array(&stack,sizeof(NODE_STATE), 20, 50, MYF(0)); + (void) my_init_dynamic_array(&stack, sizeof(NODE_STATE), 20, 50, MYF(0)); push_dynamic(&stack, &state); while (stack.elements) @@ -2277,8 +2284,9 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); - ACL_ROLE *current= state.node_data; + ACL_ROLE *current= curr_state->node_data; ACL_ROLE *neighbour= NULL; + DBUG_PRINT("info", ("Examining role %s", current->user.str)); /* Iterate through the neighbours until a first valid jump-to neighbour is found @@ -2289,11 +2297,13 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) i < current->role_grants.elements && found == FALSE; i++) { neighbour= *(dynamic_element(¤t->role_grants, i, ACL_ROLE**)); + DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str)); /* check if it forms a cycle */ if (neighbour->flags & ROLE_VISITED) { /* TODO the edge needs to be ignored */ + DBUG_PRINT("info", ("Found cycle")); continue; } @@ -2303,6 +2313,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) */ if (neighbour->flags & ROLE_GRANTS_FINAL) { + DBUG_PRINT("info", ("Neighbour access is final, merging")); current->access|= neighbour->access; continue; } @@ -2313,6 +2324,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) pointer is valid */ found= TRUE; + break; } if (found) @@ -2344,6 +2356,9 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) curr_state->node_data->flags|= ROLE_GRANTS_FINAL; /* Add the own role's rights once it's finished exploring */ curr_state->node_data->access|= curr_state->node_data->initial_role_access; + DBUG_PRINT("info", + ("Setting final access for node: %s %lu", + curr_state->node_data->user.str, curr_state->node_data->access)); } } @@ -2352,7 +2367,8 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) delete_dynamic(&stack); /* Finally set the access */ *access= role->access; - DBUG_RETURN(0); + DBUG_PRINT("exit", ("Role access: %lu", *access)); + DBUG_RETURN(FALSE); } /* From 1007b9232bd23176e944e08bc951d4da063caabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:46 -0700 Subject: [PATCH 073/174] Added cascading updates from role renames. Also works if a role has been granted to a role. This change only updates _in memory_ structures. --- sql/sql_acl.cc | 67 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 77db5cadad7..ab4989a637c 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -707,6 +707,7 @@ static my_bool get_role_access(ACL_ROLE *role, ulong *access); enum enum_acl_lists { USER_ACL= 0, + ROLE_ACL, DB_ACL, COLUMN_PRIVILEGES_HASH, PROC_PRIVILEGES_HASH, @@ -6905,7 +6906,11 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, int elements; const char *user; const char *host; + const char *role; + my_bool is_role= FALSE; + uint role_not_matched= 1; ACL_USER *acl_user= NULL; + ACL_ROLE *acl_role= NULL; ACL_DB *acl_db= NULL; ACL_PROXY_USER *acl_proxy_user= NULL; GRANT_NAME *grant_name= NULL; @@ -6921,6 +6926,35 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, mysql_mutex_assert_owner(&acl_cache->lock); + /* test if the current query targets a role */ + is_role= (!user_from->host.length && + (acl_role= find_acl_role(user_from->user.str))) ? TRUE : FALSE; + if (is_role && (struct_no != ROLE_ACL || struct_no != ROLES_MAPPINGS_HASH)) + { + DBUG_RETURN(0); + } + else if (struct_no == ROLE_ACL) //no need to scan the structures in this case + { + if (!is_role || (!drop && !user_to)) + DBUG_RETURN(is_role); + + /* this calls for a role update */ + char *old_key= acl_role->user.str; + size_t old_key_length= acl_role->user.length; + if (drop) + { + my_hash_delete(&acl_roles, (uchar*) acl_role); + DBUG_RETURN(1); + } + acl_role->user.str= strdup_root(&mem, user_to->user.str); + acl_role->user.length= user_to->user.length; + + my_hash_update(&acl_roles, (uchar*) acl_role, (uchar*) old_key, + old_key_length); + DBUG_RETURN(1); + + } + /* Get the number of elements in the in-memory structure. */ switch (struct_no) { case USER_ACL: @@ -6994,6 +7028,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, role_grant_pair= (ROLE_GRANT_PAIR *) my_hash_element(roles_mappings_hash, idx); user= role_grant_pair->u_uname; host= role_grant_pair->u_hname; + role= role_grant_pair->r_uname; break; default: @@ -7003,13 +7038,18 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, user= ""; if (! host) host= ""; + if (! role) + role= ""; #ifdef EXTRA_DEBUG DBUG_PRINT("loop",("scan struct: %u index: %u user: '%s' host: '%s'", struct_no, idx, user, host)); #endif + if (strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host)) + my_strcasecmp(system_charset_info, user_from->host.str, host) || + (is_role && (role_not_matched= strcmp(user_from->user.str, role))) + ) continue; result= 1; /* At least one element found. */ @@ -7049,6 +7089,9 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, my_hash_delete(roles_mappings_hash, (uchar*) role_grant_pair); break; + default: + DBUG_ASSERT(0); + break; } } else if ( user_to ) @@ -7117,15 +7160,24 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, char *old_key= role_grant_pair->hashkey.str; size_t old_key_length= role_grant_pair->hashkey.length; - init_role_grant_pair(&mem, role_grant_pair, - user_to->user.str, user_to->host.str, - role_grant_pair->r_uname); + if (role_not_matched) + init_role_grant_pair(&mem, role_grant_pair, + user_to->user.str, user_to->host.str, + role_grant_pair->r_uname); + else + init_role_grant_pair(&mem, role_grant_pair, + role_grant_pair->u_uname, + role_grant_pair->u_hname, + user_to->user.str); my_hash_update(roles_mappings_hash, (uchar*) role_grant_pair, (uchar*) old_key, old_key_length); break; } + default: + DBUG_ASSERT(0); + break; } } @@ -7190,6 +7242,13 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, if (! drop && ! user_to) goto end; } + if ((handle_grant_struct(ROLE_ACL, drop, user_from, user_to)) || found) + { + result= 1; /* At least one record/element found. */ + /* If search is requested, we do not need to search further. */ + if (! drop && ! user_to) + goto end; + } } /* Handle db table. */ From 9fa7f1fcd62dd2f541d0f478b5db7502e9f57e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:51:55 -0700 Subject: [PATCH 074/174] Added cascading role renames to the roles_mappings table. TODO: Use an index search on the table, instead of scanning through it. --- sql/sql_acl.cc | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ab4989a637c..d3d1d13df5e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6614,38 +6614,34 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, LEX_USER *user_from, LEX_USER *user_to) { /* - First we need to find out if the user_from represents a user, or a role. + The first thing that needs to be checked is what we are renaming, + a user, or a role. In order to do this, perform a hash lookup over + acl_roles to find if a key exists. - If the user_from has a hostname different than '' it can not be a role. - If the user_from has an empty hostname, it _could_ be a role, but it is - not mandatory. + If the renaming involves renaming a role, all entries + (HostFK, UserFk) that match user_from will be renamed, + as well as all RoleFk entries that match. - In this case perform a quick lookup in acl_roles to see if - it is already there. If it is not found, then the user fields are updated, - otherwise the role field gets updated. + Otherwise, only matching (HostFk, UserFk) will be renamed. */ DBUG_ENTER("handle_roles_mappings_table"); int error; int result= 0; - bool is_role= FALSE; THD *thd= current_thd; const char *host, *user, *role; + my_bool is_role= FALSE; Field *host_field= table->field[0]; Field *user_field= table->field[1]; Field *role_field= table->field[2]; if (!user_from->host.length && find_acl_role(user_from->user.str)) - is_role= TRUE; - - /* - Check if user_to is a valid role. If it is not a valid role, the change - fails. - */ - if (is_role && user_to && user_to->host.length) - DBUG_RETURN(-1); - + is_role= TRUE; + DBUG_PRINT("info", ("Rewriting %s entry in roles_mappings table: %s %s", + is_role ? "role" : "user", + user_from->user.str, + user_from->host.str)); table->use_all_columns(); if ((error= table->file->ha_rnd_init(1))) { @@ -6662,23 +6658,18 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, DBUG_PRINT("info", ("scan error: %d", error)); continue; } - if (!is_role) - { - if (! (host= get_field(thd->mem_root, host_field))) - host= ""; - if (! (user= get_field(thd->mem_root, user_field))) - user= ""; - - if (strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host)) - continue; + if (! (host= get_field(thd->mem_root, host_field))) + host= ""; + if (! (user= get_field(thd->mem_root, user_field))) + user= ""; + if (!(strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host))) result= ((drop || user_to) && modify_grant_table(table, host_field, user_field, user_to)) ? -1 : result ? result : 1; /* Error or keep result or found. */ - } - else + if (is_role) { if (! (role= get_field(thd->mem_root, role_field))) role= ""; From a0a8f50895ceca51afac9aa381e5878809d5bc4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:52:04 -0700 Subject: [PATCH 075/174] Added a test for acl_roles to test renaming of roles/ usernames --- mysql-test/r/acl_roles_rename_user.result | 50 +++++++++++++++++++++++ mysql-test/t/acl_roles_rename_user.test | 37 +++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 mysql-test/r/acl_roles_rename_user.result create mode 100644 mysql-test/t/acl_roles_rename_user.test diff --git a/mysql-test/r/acl_roles_rename_user.result b/mysql-test/r/acl_roles_rename_user.result new file mode 100644 index 00000000000..a66cdea5007 --- /dev/null +++ b/mysql-test/r/acl_roles_rename_user.result @@ -0,0 +1,50 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +create user 'test_role2'@''; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +flush privileges; +use mysql; +select * from roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 + test_role1 test_role2 +rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost'; +select user, host from user where user like 'test%'; +user host +test_role1 +test_role2 +test_user_rm newhost +select * from roles_mapping; +HostFk UserFk RoleFk +newhost test_user_rm test_role1 + test_role1 test_role2 +rename user 'test_role2'@'' to 'test_role2_rm'@''; +select user, host from user where user like 'test%'; +user host +test_role1 +test_role2_rm +test_user_rm newhost +select * from roles_mapping; +HostFk UserFk RoleFk +newhost test_user_rm test_role1 + test_role1 test_role2_rm +rename user 'test_role1'@'' to 'test_role1_rm'@''; +select user, host from user where user like 'test%'; +user host +test_role1_rm +test_role2_rm +test_user_rm newhost +select * from roles_mapping; +HostFk UserFk RoleFk +newhost test_user_rm test_role1_rm + test_role1_rm test_role2_rm +delete from mysql.roles_mapping; +delete from mysql.user where user like 'test%'; +flush privileges; diff --git a/mysql-test/t/acl_roles_rename_user.test b/mysql-test/t/acl_roles_rename_user.test new file mode 100644 index 00000000000..0919d8338ab --- /dev/null +++ b/mysql-test/t/acl_roles_rename_user.test @@ -0,0 +1,37 @@ + +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +#manualy create role +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +create user 'test_role2'@''; +#manualy create role +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +flush privileges; + +use mysql; +select * from roles_mapping; + +#regular user rename +rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost'; +select user, host from user where user like 'test%'; +select * from roles_mapping; + +rename user 'test_role2'@'' to 'test_role2_rm'@''; +select user, host from user where user like 'test%'; +select * from roles_mapping; + +#role rename +rename user 'test_role1'@'' to 'test_role1_rm'@''; +select user, host from user where user like 'test%'; +select * from roles_mapping; + +delete from mysql.roles_mapping; +delete from mysql.user where user like 'test%'; +flush privileges; From 871f6e1631f08d2cb622742a11c2208c815d27f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:52:12 -0700 Subject: [PATCH 076/174] Removed leftover comment. --- sql/sql_acl.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d3d1d13df5e..df620a8186d 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1204,7 +1204,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) if (is_role) { DBUG_PRINT("info", ("Found role %s", user.user.str)); ACL_ROLE *entry= new (&mem) ACL_ROLE(&user, &mem); - //create_role_from_user(&mem, &user); entry->role_grants = user.role_grants; (void) my_init_dynamic_array(&entry->parent_grantee, sizeof(ACL_USER_BASE *), 50, 100, MYF(0)); From 9272e34a90088144994c5d5e53ef997e95ebace7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:52:21 -0700 Subject: [PATCH 077/174] Minor test update to eliminate random row order. --- mysql-test/r/acl_roles_set_role-recursive.result | 6 ++---- mysql-test/t/acl_roles_set_role-recursive.test | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 50a7f8494ba..e7ebd77fbb0 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -38,9 +38,8 @@ set role test_role1; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk -localhost test_user test_role1 test_role1 test_role2 show grants; Grants for test_user@localhost @@ -68,9 +67,8 @@ set role test_role1; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk -localhost test_user test_role1 test_role1 test_role2 show grants; Grants for test_user@localhost diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test index 983dbb8e1e5..9bf8bdaf8e9 100644 --- a/mysql-test/t/acl_roles_set_role-recursive.test +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -31,7 +31,7 @@ select * from mysql.roles_mapping; show grants; set role test_role1; show grants; -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where HostFk=''; show grants; set role none; @@ -50,7 +50,7 @@ select * from mysql.roles_mapping; show grants; set role test_role1; show grants; -select * from mysql.roles_mapping; +select * from mysql.roles_mapping where HostFk=''; show grants; set role none; From dcf76e6514aeaff5f12d62ac0b25e8617f4f94e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Thu, 17 Oct 2013 20:52:29 -0700 Subject: [PATCH 078/174] Added a more complicated test for recursive role grants. --- .../r/acl_roles_set_role-multiple-role.result | 132 ++++++++++++++++++ .../t/acl_roles_set_role-multiple-role.test | 119 ++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 mysql-test/r/acl_roles_set_role-multiple-role.result create mode 100644 mysql-test/t/acl_roles_set_role-multiple-role.test diff --git a/mysql-test/r/acl_roles_set_role-multiple-role.result b/mysql-test/r/acl_roles_set_role-multiple-role.result new file mode 100644 index 00000000000..9aeb5288ce8 --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-multiple-role.result @@ -0,0 +1,132 @@ +create user 'test_user'@'localhost'; +create user 'r_sel'@''; +create user 'r_ins'@''; +create user 'r_upd'@''; +create user 'r_del'@''; +create user 'r_crt'@''; +create user 'r_drp'@''; +create user 'r_rld'@''; +update mysql.user set Select_priv='Y' where user like 'r_sel'; +update mysql.user set Insert_priv='Y' where user like 'r_ins'; +update mysql.user set Update_priv='Y' where user like 'r_upd'; +update mysql.user set Delete_priv='Y' where user like 'r_del'; +update mysql.user set Create_priv='Y' where user like 'r_crt'; +update mysql.user set Drop_priv ='Y' where user like 'r_drp'; +update mysql.user set Reload_priv='Y' where user like 'r_rld'; +update mysql.user set is_role='Y' where user like 'r\_%'; +select * from mysql.user where user='r_sel'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_sel Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_ins'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_ins N Y N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_upd'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_upd N N Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_del'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_del N N N Y N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_crt'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_crt N N N N Y N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_drp'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_drp N N N N N Y N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +select * from mysql.user where user='r_rld'; +Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections plugin authentication_string is_role + r_rld N N N N N N Y N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 Y +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_sel'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_ins'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_upd'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_del'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_crt'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_drp'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'r_rld'); +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +set role r_sel; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user r_sel +localhost test_user r_ins +localhost test_user r_upd +localhost test_user r_del +localhost test_user r_crt +localhost test_user r_drp +localhost test_user r_rld +set role r_ins; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_sel', +'r_rld'); +flush privileges; +ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation +set role r_rld; +flush privileges; +set role r_sel; +flush privileges; +set role none; +flush privileges; +ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation +set role r_ins; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_sel', +'r_upd'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_sel', +'r_del'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_sel', +'r_crt'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_sel', +'r_drp'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'r_del', +'r_ins'); +set role r_rld; +flush privileges; +set role r_sel; +update mysql.roles_mapping set RoleFk='r_ins' where RoleFk='r_ins_wrong'; +flush privileges; +set role r_sel; +create table mysql.random_test_table (id INT); +insert into mysql.random_test_table values (1); +select * from mysql.random_test_table; +id +1 +delete from mysql.roles_mapping where RoleFk='r_ins'; +flush privileges; +set role r_sel; +insert into mysql.random_test_table values (1); +ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table 'random_test_table' +drop table mysql.random_test_table; +delete from mysql.user where user like 'r\_%'; +delete from mysql.roles_mapping where RoleFk like 'r\_%'; +flush privileges; +drop user 'test_user'@'localhost'; diff --git a/mysql-test/t/acl_roles_set_role-multiple-role.test b/mysql-test/t/acl_roles_set_role-multiple-role.test new file mode 100644 index 00000000000..a046b13bb3f --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-multiple-role.test @@ -0,0 +1,119 @@ +#create a user with no privileges +create user 'test_user'@'localhost'; + +create user 'r_sel'@''; +create user 'r_ins'@''; +create user 'r_upd'@''; +create user 'r_del'@''; +create user 'r_crt'@''; +create user 'r_drp'@''; +create user 'r_rld'@''; +update mysql.user set Select_priv='Y' where user like 'r_sel'; +update mysql.user set Insert_priv='Y' where user like 'r_ins'; +update mysql.user set Update_priv='Y' where user like 'r_upd'; +update mysql.user set Delete_priv='Y' where user like 'r_del'; +update mysql.user set Create_priv='Y' where user like 'r_crt'; +update mysql.user set Drop_priv ='Y' where user like 'r_drp'; +update mysql.user set Reload_priv='Y' where user like 'r_rld'; + +update mysql.user set is_role='Y' where user like 'r\_%'; + +select * from mysql.user where user='r_sel'; +select * from mysql.user where user='r_ins'; +select * from mysql.user where user='r_upd'; +select * from mysql.user where user='r_del'; +select * from mysql.user where user='r_crt'; +select * from mysql.user where user='r_drp'; +select * from mysql.user where user='r_rld'; + +##################################### +#set up roles mapping +##################################### +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_sel'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_ins'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_upd'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_del'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_crt'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_drp'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'r_rld'); +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +show grants; +set role r_sel; +show grants; +select * from mysql.roles_mapping; + +set role r_ins; +show grants; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_sel', + 'r_rld'); +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +flush privileges; +set role r_rld; +flush privileges; +set role r_sel; +flush privileges; +set role none; +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +flush privileges; + +set role r_ins; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_sel', + 'r_upd'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_sel', + 'r_del'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_sel', + 'r_crt'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_sel', + 'r_drp'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'r_del', + 'r_ins'); +set role r_rld; +flush privileges; +set role r_sel; +update mysql.roles_mapping set RoleFk='r_ins' where RoleFk='r_ins_wrong'; +flush privileges; +set role r_sel; + +create table mysql.random_test_table (id INT); +insert into mysql.random_test_table values (1); +select * from mysql.random_test_table; +delete from mysql.roles_mapping where RoleFk='r_ins'; +flush privileges; +set role r_sel; +--error ER_TABLEACCESS_DENIED_ERROR +insert into mysql.random_test_table values (1); +drop table mysql.random_test_table; + +change_user 'root'; +delete from mysql.user where user like 'r\_%'; +delete from mysql.roles_mapping where RoleFk like 'r\_%'; +flush privileges; +drop user 'test_user'@'localhost'; From d40d35660603d0ac764fafadeed6c3a46c1919ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:29:01 -0700 Subject: [PATCH 079/174] Added syntax detection for the GRANT role TO {user | role } command. Also added syntax for GRANT privilege TO { role } command --- sql/mysqld.cc | 1 + sql/sql_acl.cc | 4 +- sql/sql_lex.h | 2 +- sql/sql_parse.cc | 8 ++++ sql/sql_yacc.yy | 114 ++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 104 insertions(+), 25 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index f9c6675400d..8fc7627e247 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3456,6 +3456,7 @@ SHOW_VAR com_status_vars[]= { {"execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS}, {"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS}, {"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, + {"grant_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, {"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS}, {"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS}, {"ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS}, diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index df620a8186d..520b90f4386 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2275,7 +2275,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) role->flags|= ROLE_VISITED; (void) my_init_dynamic_array(&stack, sizeof(NODE_STATE), 20, 50, MYF(0)); - push_dynamic(&stack, &state); + push_dynamic(&stack, (uchar*)&state); while (stack.elements) { @@ -2343,7 +2343,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) neighbour->flags|= ROLE_VISITED; state.neigh_idx= 0; state.node_data= neighbour; - push_dynamic(&stack, &state); + push_dynamic(&stack, (uchar*)&state); } else { diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 18be450caf2..72eb9d7229e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -148,7 +148,7 @@ enum enum_sql_command { SQLCOM_SHOW_TRIGGERS, SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, - SQLCOM_GRANT, + SQLCOM_GRANT, SQLCOM_GRANT_ROLE, SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB, SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 17503e08307..dceeb1cab82 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -367,6 +367,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; @@ -418,6 +419,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ASSIGN_TO_KEYCACHE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_PRELOAD_KEYS]= CF_AUTO_COMMIT_TRANS; @@ -3877,6 +3879,12 @@ end_with_restore_list: } break; } + case SQLCOM_GRANT_ROLE: + { + /* TODO Implement grant */ + my_ok(thd); + break; + } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ case SQLCOM_RESET: /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 1f7166c6cd6..db137ae13c6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1459,6 +1459,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NCHAR_STRING opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty opt_constraint constraint opt_ident opt_if_not_exists_ident + grant_role %type opt_table_alias @@ -1569,7 +1570,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type keyword keyword_sp -%type user grant_user +%type user specified_user grant_user role %type opt_collate @@ -1623,6 +1624,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges grant_ident grant_list grant_option + grant_list_with_roles object_privilege object_privilege_list user_list rename_list clear_privileges flush_options flush_option opt_with_read_lock flush_options_list @@ -13153,24 +13155,8 @@ ident_or_text: | LEX_HOSTNAME { $$=$1;} ; -user: - ident_or_text - { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - $$->user = $1; - $$->host.str= (char *) "%"; - $$->host.length= 1; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; - - if (check_string_char_length(&$$->user, ER(ER_USERNAME), - username_char_length, - system_charset_info, 0)) - MYSQL_YYABORT; - } - | ident_or_text '@' ident_or_text +specified_user: + ident_or_text '@' ident_or_text { if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; @@ -13195,8 +13181,8 @@ user: { if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; - /* - empty LEX_USER means current_user and + /* + empty LEX_USER means current_user and will be handled in the get_current_user() function later */ @@ -13204,6 +13190,46 @@ user: } ; +user: + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host.str= (char *) "%"; + $$->host.length= 1; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; + + if (check_string_char_length(&$$->user, ER(ER_USERNAME), + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } + | + specified_user {$$ = $1;} + ; + +role: + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host.str= (char *) ""; + $$->host.length= 0; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; + + if (check_string_char_length(&$$->user, ER(ER_USERNAME), + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } + ; + /* Keyword that we allow for identifiers (except SP labels) */ keyword: keyword_sp {} @@ -14282,7 +14308,27 @@ grant_command: lex->users_list.push_front ($3); lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROXY; - } + } + | grant_privileges TO_SYM grant_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT_ROLE; + lex->type= 0; + printf("Need to grant privileges to a role / user\n"); + } + | grant_role TO_SYM grant_list_with_roles + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT_ROLE; + lex->type= 0; + printf("The rolename to be granted is: %s\n", $1.str); + } + + ; + +grant_role: + IDENT_sys {$$=$1;} + | TEXT_STRING_sys {$$=$1;} ; opt_table: @@ -14459,6 +14505,30 @@ user_list: } ; +grant_list_with_roles: + role + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } + | specified_user + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } + | grant_list_with_roles ',' role + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + | grant_list_with_roles ',' specified_user + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + ; + + grant_list: grant_user { From 336ec901f425567870bc664818108656f2f161ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:29:22 -0700 Subject: [PATCH 080/174] Removed not needed GRANT privilege TO --- sql/sql_yacc.yy | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index db137ae13c6..9b80a4d731b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -14309,13 +14309,6 @@ grant_command: lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROXY; } - | grant_privileges TO_SYM grant_list - { - LEX *lex= Lex; - lex->sql_command= SQLCOM_GRANT_ROLE; - lex->type= 0; - printf("Need to grant privileges to a role / user\n"); - } | grant_role TO_SYM grant_list_with_roles { LEX *lex= Lex; From a57cdcd69a33ec78b8dfb4e06df00f9b6fae0c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:29:40 -0700 Subject: [PATCH 081/174] Changed GRANT ROLE to use SQLCOM_GRANT_ROLE --- sql/mysqld.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8fc7627e247..df1cdce76b8 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3456,7 +3456,7 @@ SHOW_VAR com_status_vars[]= { {"execute_sql", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_EXECUTE]), SHOW_LONG_STATUS}, {"flush", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_FLUSH]), SHOW_LONG_STATUS}, {"grant", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, - {"grant_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT]), SHOW_LONG_STATUS}, + {"grant_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_GRANT_ROLE]), SHOW_LONG_STATUS}, {"ha_close", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_CLOSE]), SHOW_LONG_STATUS}, {"ha_open", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_OPEN]), SHOW_LONG_STATUS}, {"ha_read", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_HA_READ]), SHOW_LONG_STATUS}, From 51c631c2ae2ad2fe269ec865911952b2a9b0397c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:35:18 -0700 Subject: [PATCH 082/174] Implemented syntax recognition for CREATE ROLE --- sql/mysqld.cc | 1 + sql/sql_lex.h | 1 + sql/sql_parse.cc | 8 ++++++++ sql/sql_yacc.yy | 10 ++++++++++ 4 files changed, 20 insertions(+) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index df1cdce76b8..1e3d8d6ac0f 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3432,6 +3432,7 @@ SHOW_VAR com_status_vars[]= { {"create_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SPFUNCTION]), SHOW_LONG_STATUS}, {"create_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_INDEX]), SHOW_LONG_STATUS}, {"create_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_PROCEDURE]), SHOW_LONG_STATUS}, + {"create_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_ROLE]), SHOW_LONG_STATUS}, {"create_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_SERVER]), SHOW_LONG_STATUS}, {"create_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TABLE]), SHOW_LONG_STATUS}, {"create_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_CREATE_TRIGGER]), SHOW_LONG_STATUS}, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 72eb9d7229e..3fe799092fa 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -169,6 +169,7 @@ enum enum_sql_command { SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, + SQLCOM_CREATE_ROLE, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index dceeb1cab82..c451b785871 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -366,6 +366,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_RENAME_USER]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_DROP_USER]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_CREATE_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; @@ -416,6 +417,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_USER]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_DROP_USER]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_USER]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; @@ -3753,6 +3755,12 @@ end_with_restore_list: my_ok(thd); break; } + case SQLCOM_CREATE_ROLE: + { + /* TODO */ + my_ok(thd); + break; + } case SQLCOM_REVOKE_ALL: { if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) && diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9b80a4d731b..30d75dff926 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2218,6 +2218,10 @@ create: { Lex->sql_command = SQLCOM_CREATE_USER; } + | CREATE ROLE_SYM clear_privileges role_list + { + Lex->sql_command = SQLCOM_CREATE_ROLE; + } | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info { Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; @@ -14319,6 +14323,12 @@ grant_command: ; +role_list: + grant_role + {} + | role_list ',' grant_role + {} + grant_role: IDENT_sys {$$=$1;} | TEXT_STRING_sys {$$=$1;} From fac8c9ef43d09d5f77885696b5c3207ac227b44b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:35:36 -0700 Subject: [PATCH 083/174] Added optional if not exists for create role. --- sql/sql_yacc.yy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 30d75dff926..60e651ba79b 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -2218,7 +2218,7 @@ create: { Lex->sql_command = SQLCOM_CREATE_USER; } - | CREATE ROLE_SYM clear_privileges role_list + | CREATE ROLE_SYM clear_privileges opt_if_not_exists role_list { Lex->sql_command = SQLCOM_CREATE_ROLE; } From ec92a4e0ffda326cf797bf5e4b389c73c69d2bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:36:25 -0700 Subject: [PATCH 084/174] Implemented syntax recognition for DROP ROLE --- sql/mysqld.cc | 1 + sql/sql_lex.h | 2 +- sql/sql_parse.cc | 6 ++++++ sql/sql_yacc.yy | 4 ++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 1e3d8d6ac0f..30934906b67 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3448,6 +3448,7 @@ SHOW_VAR com_status_vars[]= { {"drop_function", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_FUNCTION]), SHOW_LONG_STATUS}, {"drop_index", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_INDEX]), SHOW_LONG_STATUS}, {"drop_procedure", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_PROCEDURE]), SHOW_LONG_STATUS}, + {"drop_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_ROLE]), SHOW_LONG_STATUS}, {"drop_server", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_SERVER]), SHOW_LONG_STATUS}, {"drop_table", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TABLE]), SHOW_LONG_STATUS}, {"drop_trigger", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_DROP_TRIGGER]), SHOW_LONG_STATUS}, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3fe799092fa..3aa89a25c8a 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -169,7 +169,7 @@ enum enum_sql_command { SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, - SQLCOM_CREATE_ROLE, + SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c451b785871..a61429e2dfb 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3761,6 +3761,12 @@ end_with_restore_list: my_ok(thd); break; } + case SQLCOM_DROP_ROLE: + { + /* TODO */ + my_ok(thd); + break; + } case SQLCOM_REVOKE_ALL: { if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) && diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 60e651ba79b..683be0413b9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11001,6 +11001,10 @@ drop: { Lex->sql_command = SQLCOM_DROP_USER; } + | DROP ROLE_SYM clear_privileges opt_if_exists role_list + { + Lex->sql_command = SQLCOM_DROP_ROLE; + } | DROP VIEW_SYM opt_if_exists { LEX *lex= Lex; From dcc9fd4c8e510a3fe81341d3622fa5ef02eb0d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:41:06 -0700 Subject: [PATCH 085/174] Implemented syntax recognition for REVOKE ROLE --- sql/mysqld.cc | 1 + sql/sql_lex.h | 2 +- sql/sql_parse.cc | 3 +++ sql/sql_yacc.yy | 8 ++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 30934906b67..a758280490d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3484,6 +3484,7 @@ SHOW_VAR com_status_vars[]= { {"resignal", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_RESIGNAL]), SHOW_LONG_STATUS}, {"revoke", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE]), SHOW_LONG_STATUS}, {"revoke_all", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ALL]), SHOW_LONG_STATUS}, + {"revoke_role", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_REVOKE_ROLE]), SHOW_LONG_STATUS}, {"rollback", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK]), SHOW_LONG_STATUS}, {"rollback_to_savepoint",(char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_ROLLBACK_TO_SAVEPOINT]), SHOW_LONG_STATUS}, {"savepoint", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SAVEPOINT]), SHOW_LONG_STATUS}, diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 3aa89a25c8a..293081c21db 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -169,7 +169,7 @@ enum enum_sql_command { SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_CREATE_USER, SQLCOM_DROP_USER, SQLCOM_RENAME_USER, - SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, + SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_REVOKE_ROLE, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL, SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a61429e2dfb..fcfd0451290 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -370,6 +370,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_GRANT]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_GRANT_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_REVOKE]= CF_CHANGES_DATA; + sql_command_flags[SQLCOM_REVOKE_ROLE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_OPTIMIZE]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_FUNCTION]= CF_CHANGES_DATA; sql_command_flags[SQLCOM_CREATE_PROCEDURE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -420,6 +421,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_CREATE_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE_ALL]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_REVOKE]|= CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_REVOKE_ROLE]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT]|= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_GRANT_ROLE]|= CF_AUTO_COMMIT_TRANS; @@ -3893,6 +3895,7 @@ end_with_restore_list: } break; } + case SQLCOM_REVOKE_ROLE: case SQLCOM_GRANT_ROLE: { /* TODO Implement grant */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 683be0413b9..386ae2815cd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -14271,6 +14271,14 @@ revoke_command: lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_PROXY; } + | grant_role FROM grant_list_with_roles + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_REVOKE_ROLE; + lex->type= 0; + printf("The rolename to be revoked is: %s\n", $1.str); + } + ; grant: From 01d4f47ef5402bf1a9024e18efff84ce3bcdd7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:41:18 -0700 Subject: [PATCH 086/174] Added GRANT privilege ON database.* TO role; functionality --- sql/sql_acl.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 520b90f4386..b6a0515ad52 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3256,8 +3256,12 @@ static int replace_db_table(TABLE *table, const char *db, /* Check if there is such a user in user table in memory? */ if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE)) { - my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); - DBUG_RETURN(-1); + /* The user could be a role, check if the user is registered as a role */ + if (!combo.host.length && !find_acl_role(combo.user.str)) + { + my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); + DBUG_RETURN(-1); + } } table->use_all_columns(); From 3d17d94cd6baeda453cf1b10029e84c99a83adf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:43:09 -0700 Subject: [PATCH 087/174] Added GRANT privilege ON database.table TO role; functionality --- sql/sql_acl.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b6a0515ad52..17615fc316f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4042,9 +4042,12 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, */ if (!find_user_no_anon(combo.host.str,combo.user.str, FALSE)) { - my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), - MYF(0)); /* purecov: deadcode */ - DBUG_RETURN(-1); /* purecov: deadcode */ + if (!combo.host.length && !find_acl_role(combo.user.str)) + { + my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), + MYF(0)); /* purecov: deadcode */ + DBUG_RETURN(-1); /* purecov: deadcode */ + } } table->use_all_columns(); From 2060937353bfa81fa5bd67d28d14bc339ea55ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 04:47:55 -0700 Subject: [PATCH 088/174] Grant privilege on *.* to role@''; now updates in memory data structures; Revoke privilege on *.* to role@''; also works --- sql/sql_acl.cc | 73 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 17615fc316f..26033c35139 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1837,6 +1837,65 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length, return (uchar*) buff->host.hostname; } +static void acl_update_role(const char *rolename, + ulong privileges) +{ + ACL_ROLE *role; + ulong unused; + mysql_mutex_assert_owner(&acl_cache->lock); + + role= find_acl_role(rolename); + if (!role) + { + return; + } + + /* + Changing privileges of a role causes all other roles that had + this role granted to them to have their rights invalidated. + + We need to rebuild all roles' related access bits. + */ + + role->initial_role_access= privileges; + role->flags&= ~ROLE_GRANTS_FINAL; + role->access= role->initial_role_access; + get_role_access(role, &unused); + + for (uint i= 0; i < role->parent_grantee.elements; i++) + { + ACL_USER_BASE *acl_user_base; + ACL_ROLE *grantee; + acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**)); + if (acl_user_base->flags & IS_ROLE) + { + grantee= (ACL_ROLE *)acl_user_base; + grantee->flags&= ~ROLE_GRANTS_FINAL; + grantee->access= grantee->initial_role_access; + } + } + /* + This needs to be run again after resetting the ROLE_GRANTS_FINAL flag, + because otherwise diamond shaped grants will interfere with the reset + process. + + Example: RoleA -> RoleB; RoleA -> RoleC; RoleB -> RoleC; + We are updating RoleC, and we reset RoleA first. If we were to run + get_role_access without resetting RoleB on RoleA, we would get the old + privileges from RoleC via RoleB into RoleA. + */ + for (uint i= 0; i < role->parent_grantee.elements; i++) + { + ACL_USER_BASE *acl_user_base; + ACL_ROLE *grantee; + acl_user_base= *(dynamic_element(&role->parent_grantee, i, ACL_USER_BASE**)); + if (acl_user_base->flags & IS_ROLE) + { + grantee= (ACL_ROLE *)acl_user_base; + get_role_access(grantee, &unused); + } + } +} static void acl_update_user(const char *user, const char *host, const char *password, uint password_len, @@ -1851,6 +1910,12 @@ static void acl_update_user(const char *user, const char *host, { mysql_mutex_assert_owner(&acl_cache->lock); + if (host[0] == '\0' && find_acl_role(user)) + { + acl_update_role(user, privileges); + return; + } + for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); @@ -3180,10 +3245,10 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, if ((error= table->file->ha_update_row(table->record[1],table->record[0])) && error != HA_ERR_RECORD_IS_THE_SAME) - { // This should never happen - table->file->print_error(error,MYF(0)); /* purecov: deadcode */ - error= -1; /* purecov: deadcode */ - goto end; /* purecov: deadcode */ + { // This should never happen + table->file->print_error(error,MYF(0)); /* purecov: deadcode */ + error= -1; /* purecov: deadcode */ + goto end; /* purecov: deadcode */ } else error= 0; From fe521dc28efe80b9aae45cc4d8f8c3ee6fe4bb52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:11:16 -0700 Subject: [PATCH 089/174] Implemented _non recursive_ role specific grants for table/column level privileges --- sql/sql_acl.cc | 200 ++++++++++++++++++++++++++++++++++++----------- sql/sql_db.cc | 5 ++ sql/sql_parse.cc | 10 +++ sql/table.h | 3 +- 4 files changed, 173 insertions(+), 45 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 26033c35139..091c55ae162 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1690,7 +1690,7 @@ bool acl_getroot(Security_context *sctx, char *user, char *host, sctx->master_access= 0; sctx->db_access= 0; - *sctx->priv_user= *sctx->priv_host= 0; + *sctx->priv_user= *sctx->priv_host= *sctx->priv_role= 0; /* Find acl entry in user database. @@ -1821,10 +1821,24 @@ end: int acl_setrole(THD *thd, char *rolename, ulonglong access) { /* merge the privileges */ - thd->security_ctx->master_access= access; - /* mark the current role */ - strmake(thd->security_ctx->priv_role, rolename, - sizeof(thd->security_ctx->priv_role)-1); + Security_context *sctx= thd->security_ctx; + sctx->master_access= access; + if (thd->db) + { + sctx->db_access= acl_get(sctx->host, + sctx->ip, sctx->user, thd->db, FALSE); + sctx->db_access= acl_get("", "", rolename, thd->db, FALSE); + } + if (!strcasecmp(rolename, "NONE")) + { + thd->security_ctx->priv_role[0]= 0; + } + else + { + /* mark the current role */ + strmake(thd->security_ctx->priv_role, rolename, + sizeof(thd->security_ctx->priv_role)-1); + } return 0; } @@ -2144,12 +2158,15 @@ ulong acl_get(const char *host, const char *ip, { if (compare_hostname(&acl_db->host,host,ip)) { - if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern)) - { - db_access=acl_db->access; - if (acl_db->host.hostname) - goto exit; // Fully specified. Take it - break; /* purecov: tested */ + if (!acl_db->db || !wild_compare(db,acl_db->db,db_is_pattern)) + { + db_access=acl_db->access; + if (acl_db->host.hostname) + goto exit; // Fully specified. Take it + /* XXX is this an alright way to bypass the host table for roles? */ + if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user)) + goto exit; + break; /* purecov: tested */ } } } @@ -3035,6 +3052,8 @@ static bool test_if_create_new_users(THD *thd) db_access=acl_get(sctx->host, sctx->ip, sctx->priv_user, tl.db, 0); + if (sctx->priv_role[0]) + db_access|= acl_get("", "", sctx->priv_role, tl.db, 0); if (!(db_access & INSERT_ACL)) { if (check_grant(thd, INSERT_ACL, &tl, FALSE, UINT_MAX, TRUE)) @@ -5330,6 +5349,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, ulong orig_want_access= want_access; my_bool locked= 0; GRANT_TABLE *grant_table; + GRANT_TABLE *grant_table_role= NULL; DBUG_ENTER("check_grant"); DBUG_ASSERT(number > 0); @@ -5415,14 +5435,21 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, mysql_rwlock_rdlock(&LOCK_grant); } - if (!(grant_table= table_hash_search(sctx->host, sctx->ip, - tl->get_db_name(), - sctx->priv_user, - tl->get_table_name(), - FALSE))) + grant_table= table_hash_search(sctx->host, sctx->ip, + tl->get_db_name(), + sctx->priv_user, + tl->get_table_name(), + FALSE); + if (sctx->priv_role[0]) + grant_table_role= table_hash_search("", "", tl->get_db_name(), + sctx->priv_role, + tl->get_table_name(), + TRUE); + + if (!grant_table && !grant_table_role) { - want_access &= ~tl->grant.privilege; - goto err; // No grants + want_access&= ~tl->grant.privilege; + goto err; } /* @@ -5432,15 +5459,19 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (any_combination_will_do) continue; - tl->grant.grant_table= grant_table; // Remember for column test + tl->grant.grant_table_user= grant_table; // Remember for column test + tl->grant.grant_table_role= grant_table_role; tl->grant.version= grant_version; - tl->grant.privilege|= grant_table->privs; + tl->grant.privilege|= grant_table ? grant_table->privs : 0; + tl->grant.privilege|= grant_table_role ? grant_table_role->privs : 0; tl->grant.want_privilege= ((want_access & COL_ACLS) & ~tl->grant.privilege); if (!(~tl->grant.privilege & want_access)) continue; - if (want_access & ~(grant_table->cols | tl->grant.privilege)) + if (want_access & ~((grant_table ? grant_table->cols : 0) | + (grant_table_role ? grant_table_role->cols : 0) | + tl->grant.privilege)) { want_access &= ~(grant_table->cols | tl->grant.privilege); goto err; // impossible @@ -5492,6 +5523,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, const char *name, uint length, Security_context *sctx) { GRANT_TABLE *grant_table; + GRANT_TABLE *grant_table_role; GRANT_COLUMN *grant_column; ulong want_access= grant->want_privilege & ~grant->privilege; DBUG_ENTER("check_grant_column"); @@ -5506,17 +5538,37 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, if (grant->version != grant_version) { - grant->grant_table= + grant->grant_table_user= table_hash_search(sctx->host, sctx->ip, db_name, sctx->priv_user, table_name, 0); /* purecov: inspected */ + grant->grant_table_role= + sctx->priv_role[0] ? table_hash_search("", "", db_name, + sctx->priv_role, + table_name, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ } - if (!(grant_table= grant->grant_table)) + if (!(grant_table= grant->grant_table_user) && + !(grant_table_role= grant->grant_table_role)) goto err; /* purecov: deadcode */ - grant_column=column_hash_search(grant_table, name, length); - if (grant_column && !(~grant_column->rights & want_access)) + if (grant_table) + { + grant_column= column_hash_search(grant_table, name, length); + if (grant_column) + { + want_access&= ~grant_column->rights; + } + } + if (grant_table_role) + { + grant_column= column_hash_search(grant_table_role, name, length); + if (grant_column) + { + want_access&= ~grant_column->rights; + } + } + if (!want_access) { mysql_rwlock_unlock(&LOCK_grant); DBUG_RETURN(0); @@ -5526,6 +5578,7 @@ err: mysql_rwlock_unlock(&LOCK_grant); char command[128]; get_privilege_desc(command, sizeof(command), want_access); + /* TODO perhaps error should print current rolename aswell */ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user, @@ -5632,6 +5685,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, GRANT_INFO *grant; /* Initialized only to make gcc happy */ GRANT_TABLE *grant_table= NULL; + GRANT_TABLE *grant_table_role= NULL; /* Flag that gets set if privilege checking has to be performed on column level. @@ -5656,15 +5710,20 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, /* reload table if someone has modified any grants */ if (grant->version != grant_version) { - grant->grant_table= + grant->grant_table_user= table_hash_search(sctx->host, sctx->ip, db_name, sctx->priv_user, table_name, 0); /* purecov: inspected */ + grant->grant_table_role= + sctx->priv_role[0] ? table_hash_search("", "", db_name, + sctx->priv_role, + table_name, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ } - grant_table= grant->grant_table; + grant_table= grant->grant_table_user; DBUG_ASSERT (grant_table); + grant_table_role= grant->grant_table_role; } } @@ -5674,8 +5733,23 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, column_hash_search(grant_table, field_name, (uint) strlen(field_name)); if (grant_column) + { using_column_privileges= TRUE; - if (!grant_column || (~grant_column->rights & want_access)) + want_access&= ~grant_column->rights; + } + if (grant_table_role) + { + grant_column= + column_hash_search(grant_table_role, field_name, + (uint) strlen(field_name)); + if (grant_column) + { + using_column_privileges= TRUE; + want_access&= ~grant_column->rights; + } + } + + if (!want_access) goto err; } } @@ -5882,18 +5956,26 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table) Security_context *sctx= thd->security_ctx; const char *db = table->db ? table->db : thd->db; GRANT_TABLE *grant_table; + GRANT_TABLE *grant_table_role= NULL; mysql_rwlock_rdlock(&LOCK_grant); #ifdef EMBEDDED_LIBRARY grant_table= NULL; + grant_table_role= NULL; #else grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, table->table_name, 0); + if (sctx->priv_role[0]) + grant_table_role= table_hash_search("", "", db, sctx->priv_role, + table->table_name, 0); #endif - table->grant.grant_table=grant_table; // Remember for column test + table->grant.grant_table_user= grant_table; // Remember for column test + table->grant.grant_table_role= grant_table_role; table->grant.version=grant_version; if (grant_table) table->grant.privilege|= grant_table->privs; + if (grant_table_role) + table->grant.privilege|= grant_table_role->privs; privilege= table->grant.privilege; mysql_rwlock_unlock(&LOCK_grant); return privilege; @@ -5923,31 +6005,50 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *field_name) { GRANT_TABLE *grant_table; + GRANT_TABLE *grant_table_role; GRANT_COLUMN *grant_column; - ulong priv; + ulong priv= 0; mysql_rwlock_rdlock(&LOCK_grant); /* reload table if someone has modified any grants */ if (grant->version != grant_version) { Security_context *sctx= thd->security_ctx; - grant->grant_table= + grant->grant_table_user= table_hash_search(sctx->host, sctx->ip, db_name, sctx->priv_user, - table_name, 0); /* purecov: inspected */ + table_name, 0); /* purecov: inspected */ + grant->grant_table_role= + sctx->priv_role[0] ? table_hash_search("", "", db_name, + sctx->priv_role, + table_name, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ } - if (!(grant_table= grant->grant_table)) + if (!(grant_table= grant->grant_table_user) && + !(grant_table_role= grant->grant_table_role)) priv= grant->privilege; else { - grant_column= column_hash_search(grant_table, field_name, - (uint) strlen(field_name)); - if (!grant_column) - priv= (grant->privilege | grant_table->privs); - else - priv= (grant->privilege | grant_table->privs | grant_column->rights); + if (grant_table) + { + grant_column= column_hash_search(grant_table, field_name, + (uint) strlen(field_name)); + if (!grant_column) + priv= (grant->privilege | grant_table->privs); + else + priv= (grant->privilege | grant_table->privs | grant_column->rights); + } + + if (grant_table_role) + { + grant_column= column_hash_search(grant_table_role, field_name, + (uint) strlen(field_name)); + if (!grant_column) + priv|= (grant->privilege | grant_table_role->privs); + else + priv|= (grant->privilege | grant_table->privs | grant_column->rights); + } } mysql_rwlock_unlock(&LOCK_grant); return priv; @@ -8661,20 +8762,31 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, /* db privileges */ grant->privilege|= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, 0); + /* db privileges for role */ + if (sctx->priv_role[0]) + grant->privilege|= acl_get("", "", sctx->priv_role, db, 0); /* table privileges */ mysql_rwlock_rdlock(&LOCK_grant); if (grant->version != grant_version) { - grant->grant_table= + grant->grant_table_user= table_hash_search(sctx->host, sctx->ip, db, - sctx->priv_user, - table, 0); /* purecov: inspected */ + sctx->priv_user, + table, 0); /* purecov: inspected */ + grant->grant_table_role= + sctx->priv_role[0] ? table_hash_search("", "", db, + sctx->priv_role, + table, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ } - if (grant->grant_table != 0) + if (grant->grant_table_user != 0) { - grant->privilege|= grant->grant_table->privs; + grant->privilege|= grant->grant_table_user->privs; + } + if (grant->grant_table_role != 0) + { + grant->privilege|= grant->grant_table_role->privs; } mysql_rwlock_unlock(&LOCK_grant); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 39c30959fe4..dabb6b84e26 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1478,6 +1478,11 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) sctx->priv_user, new_db_file_name.str, FALSE) | sctx->master_access; + if (sctx->priv_role) + { + /* include a possible currently set role for access */ + db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE); + } if (!force_switch && !(db_access & DB_ACLS) && diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fcfd0451290..90af112810e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5200,8 +5200,12 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (!(sctx->master_access & SELECT_ACL)) { if (db && (!thd->db || db_is_pattern || strcmp(db, thd->db))) + { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + if (sctx->priv_role) + db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); + } else { /* get access for current db */ @@ -5245,8 +5249,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, } if (db && (!thd->db || db_is_pattern || strcmp(db,thd->db))) + { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); + if (sctx->priv_role) + { + db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); + } + } else db_access= sctx->db_access; DBUG_PRINT("info",("db_access: %lu want_access: %lu", diff --git a/sql/table.h b/sql/table.h index 1e29ca9d095..99dda3b6bf1 100644 --- a/sql/table.h +++ b/sql/table.h @@ -251,7 +251,8 @@ typedef struct st_grant_info @details The version of this copy is found in GRANT_INFO::version. */ - GRANT_TABLE *grant_table; + GRANT_TABLE *grant_table_user; + GRANT_TABLE *grant_table_role; /** @brief Used for cache invalidation when caching privilege information. From c4c09afb1826f1d6f719854dc8a8b65e7f0f35fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:11:31 -0700 Subject: [PATCH 090/174] Fixed _always_ true condition --- sql/sql_parse.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 90af112810e..ac6ac2763b3 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5203,8 +5203,8 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); - if (sctx->priv_role) - db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); + if (sctx->priv_role[0]) + db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); } else { @@ -5252,7 +5252,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, { db_access= acl_get(sctx->host, sctx->ip, sctx->priv_user, db, db_is_pattern); - if (sctx->priv_role) + if (sctx->priv_role[0]) { db_access|= acl_get("", "", sctx->priv_role, db, db_is_pattern); } From 84a2f06fb205df203476af16594176d83e0873b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:11:40 -0700 Subject: [PATCH 091/174] Fixed always true condition that caused crash on database initialisation. --- sql/sql_db.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_db.cc b/sql/sql_db.cc index dabb6b84e26..47c2e3bd406 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -1478,7 +1478,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) sctx->priv_user, new_db_file_name.str, FALSE) | sctx->master_access; - if (sctx->priv_role) + if (sctx->priv_role[0]) { /* include a possible currently set role for access */ db_access|= acl_get("", "", sctx->priv_role, new_db_file_name.str, FALSE); From ccd0c39cf4038cb08b18947fa5530da488abd6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:13:22 -0700 Subject: [PATCH 092/174] Fixed crash caused by dereferencing null pointer. The comparison is no longer necessary there. --- sql/sql_acl.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 091c55ae162..8d8672dbc3e 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -5469,12 +5469,11 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, if (!(~tl->grant.privilege & want_access)) continue; - if (want_access & ~((grant_table ? grant_table->cols : 0) | + if ((want_access&= ~((grant_table ? grant_table->cols : 0) | (grant_table_role ? grant_table_role->cols : 0) | - tl->grant.privilege)) + tl->grant.privilege))) { - want_access &= ~(grant_table->cols | tl->grant.privilege); - goto err; // impossible + goto err; // impossible } } if (locked) From 3566f317c0cf65db32b438a88474860c436a435d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:13:33 -0700 Subject: [PATCH 093/174] Added simple database privilege test for roles. --- .../acl_roles_set_role-database-simple.result | 49 ++++++++++++++++ .../t/acl_roles_set_role-database-simple.test | 58 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 mysql-test/r/acl_roles_set_role-database-simple.result create mode 100644 mysql-test/t/acl_roles_set_role-database-simple.test diff --git a/mysql-test/r/acl_roles_set_role-database-simple.result b/mysql-test/r/acl_roles_set_role-database-simple.result new file mode 100644 index 00000000000..758bc7340f2 --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-database-simple.result @@ -0,0 +1,49 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 +flush privileges; +grant select on mysql.* to test_role1@''; +grant insert, delete on mysql.roles_mapping to test_role1@''; +grant reload on *.* to test_role1@''; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +set role test_role1; +select * from mysql.roles_mapping; +HostFk UserFk RoleFk +localhost test_user test_role1 +insert into mysql.user (user, host) values ('Dummy', 'Dummy'); +ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table 'user' +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role2'); +delete from mysql.roles_mapping where RoleFk='test_role2'; +use mysql; +set role none; +use mysql; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +insert into mysql.user (user, host) values ('Dummy', 'Dummy'); +ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table 'user' +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role2'); +ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +delete from mysql.roles_mapping where RoleFk='test_role2'; +ERROR 42000: DELETE command denied to user 'test_user'@'localhost' for table 'roles_mapping' +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role1@''; +revoke insert, delete on mysql.roles_mapping from test_role1@''; +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-database-simple.test b/mysql-test/t/acl_roles_set_role-database-simple.test new file mode 100644 index 00000000000..56237f38949 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-database-simple.test @@ -0,0 +1,58 @@ +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; + +update mysql.user set is_role='Y' where user='test_role1'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; +flush privileges; + +grant select on mysql.* to test_role1@''; +grant insert, delete on mysql.roles_mapping to test_role1@''; + +grant reload on *.* to test_role1@''; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +set role test_role1; +select * from mysql.roles_mapping; +--error ER_TABLEACCESS_DENIED_ERROR +insert into mysql.user (user, host) values ('Dummy', 'Dummy'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role2'); +delete from mysql.roles_mapping where RoleFk='test_role2'; + +use mysql; + +set role none; + +--error ER_DBACCESS_DENIED_ERROR +use mysql; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; +--error ER_TABLEACCESS_DENIED_ERROR +insert into mysql.user (user, host) values ('Dummy', 'Dummy'); +--error ER_TABLEACCESS_DENIED_ERROR +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role2'); +--error ER_TABLEACCESS_DENIED_ERROR +delete from mysql.roles_mapping where RoleFk='test_role2'; + +change_user 'root'; +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role1@''; +revoke insert, delete on mysql.roles_mapping from test_role1@''; +delete from mysql.user where user='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +flush privileges; + From 81b2856e10a837183b3639d09d8f531a424dabd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:16:38 -0700 Subject: [PATCH 094/174] Refactored yacc grammar to make use of named constants. --- sql/sql_acl.cc | 11 ++++++ sql/sql_acl.h | 3 ++ sql/sql_yacc.yy | 91 ++++++++++++------------------------------------- 3 files changed, 35 insertions(+), 70 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 8d8672dbc3e..734b1343a7f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -184,6 +184,17 @@ static LEX_STRING old_password_plugin_name= { /// @todo make it configurable LEX_STRING *default_auth_plugin_name= &native_password_plugin_name; +/* + Constant used for differentiating specified user names and non specified + usernames. Example: userA -- userA@% +*/ +const char *HOST_NOT_SPECIFIED= "%"; +/* + Constant used in the SET ROLE NONE command +*/ +const char *NONE_ROLE= "NONE"; + + #ifndef NO_EMBEDDED_ACCESS_CHECKS static plugin_ref old_password_plugin; #endif diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 0e04d8f86d6..7f3ee296de8 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -173,6 +173,9 @@ enum mysql_db_table_field extern const TABLE_FIELD_DEF mysql_db_table_def; extern bool mysql_user_table_is_in_short_password_format; +extern const char *HOST_NOT_SPECIFIED; +extern const char *NONE_ROLE; + static inline int access_denied_error_code(int passwd_used) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 386ae2815cd..5ec06bf9eef 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1570,7 +1570,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type keyword keyword_sp -%type user specified_user grant_user role +%type user grant_user %type opt_collate @@ -1624,7 +1624,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges grant_ident grant_list grant_option - grant_list_with_roles object_privilege object_privilege_list user_list rename_list clear_privileges flush_options flush_option opt_with_read_lock flush_options_list @@ -13163,8 +13162,24 @@ ident_or_text: | LEX_HOSTNAME { $$=$1;} ; -specified_user: - ident_or_text '@' ident_or_text +user: + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host.str= (char *)HOST_NOT_SPECIFIED; + $$->host.length= 1; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; + + if (check_string_char_length(&$$->user, ER(ER_USERNAME), + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } + | ident_or_text '@' ident_or_text { if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; @@ -13198,46 +13213,6 @@ specified_user: } ; -user: - ident_or_text - { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - $$->user = $1; - $$->host.str= (char *) "%"; - $$->host.length= 1; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; - - if (check_string_char_length(&$$->user, ER(ER_USERNAME), - username_char_length, - system_charset_info, 0)) - MYSQL_YYABORT; - } - | - specified_user {$$ = $1;} - ; - -role: - ident_or_text - { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - $$->user = $1; - $$->host.str= (char *) ""; - $$->host.length= 0; - $$->password= null_lex_str; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; - - if (check_string_char_length(&$$->user, ER(ER_USERNAME), - username_char_length, - system_charset_info, 0)) - MYSQL_YYABORT; - } - ; - /* Keyword that we allow for identifiers (except SP labels) */ keyword: keyword_sp {} @@ -14271,7 +14246,7 @@ revoke_command: lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_PROXY; } - | grant_role FROM grant_list_with_roles + | grant_role FROM grant_list { LEX *lex= Lex; lex->sql_command= SQLCOM_REVOKE_ROLE; @@ -14325,7 +14300,7 @@ grant_command: lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROXY; } - | grant_role TO_SYM grant_list_with_roles + | grant_role TO_SYM grant_list { LEX *lex= Lex; lex->sql_command= SQLCOM_GRANT_ROLE; @@ -14520,30 +14495,6 @@ user_list: } ; -grant_list_with_roles: - role - { - if (Lex->users_list.push_back($1)) - MYSQL_YYABORT; - } - | specified_user - { - if (Lex->users_list.push_back($1)) - MYSQL_YYABORT; - } - | grant_list_with_roles ',' role - { - if (Lex->users_list.push_back($3)) - MYSQL_YYABORT; - } - | grant_list_with_roles ',' specified_user - { - if (Lex->users_list.push_back($3)) - MYSQL_YYABORT; - } - ; - - grant_list: grant_user { From db850c525fdd7a2bcb24fd08a9d2c44824be788f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:41:13 -0700 Subject: [PATCH 095/174] Added CREATE ROLE support as well as DROP ROLE support. --- .../r/acl_roles_create_and_drop_role.result | 21 ++ .../t/acl_roles_create_and_drop_role.test | 31 +++ sql/share/errmsg-utf8.txt | 5 + sql/sql_acl.cc | 247 +++++++++++++++--- sql/sql_acl.h | 2 + sql/sql_parse.cc | 16 +- sql/sql_yacc.yy | 34 ++- 7 files changed, 312 insertions(+), 44 deletions(-) create mode 100644 mysql-test/r/acl_roles_create_and_drop_role.result create mode 100644 mysql-test/t/acl_roles_create_and_drop_role.test diff --git a/mysql-test/r/acl_roles_create_and_drop_role.result b/mysql-test/r/acl_roles_create_and_drop_role.result new file mode 100644 index 00000000000..9449d020718 --- /dev/null +++ b/mysql-test/r/acl_roles_create_and_drop_role.result @@ -0,0 +1,21 @@ +use mysql; +create role test_role1; +create role test_role2, test_role3; +select user, host, is_role from user where user like 'test'; +user host is_role +drop role test_role1; +drop role test_role2, test_role3; +create role test_role1; +create role test_role1; +ERROR HY000: Operation CREATE ROLE failed for 'test_role1' +create role test_role1, test_role2; +ERROR HY000: Operation CREATE ROLE failed for 'test_role1' +select user, host, is_role from user where user like 'test'; +user host is_role +drop role test_role1; +drop role test_role1; +ERROR HY000: Operation DROP ROLE failed for 'test_role1' +drop role test_role1, test_role2; +ERROR HY000: Operation DROP ROLE failed for 'test_role1' +select user, host, is_role from user where user like 'test'; +user host is_role diff --git a/mysql-test/t/acl_roles_create_and_drop_role.test b/mysql-test/t/acl_roles_create_and_drop_role.test new file mode 100644 index 00000000000..45b10bb9c4b --- /dev/null +++ b/mysql-test/t/acl_roles_create_and_drop_role.test @@ -0,0 +1,31 @@ +connect (mysql, localhost, root,,); +use mysql; + +create role test_role1; +create role test_role2, test_role3; + +--sorted_result +select user, host, is_role from user where user like 'test'; + +drop role test_role1; +drop role test_role2, test_role3; + + +create role test_role1; +--error ER_CANNOT_USER +create role test_role1; +--error ER_CANNOT_USER +create role test_role1, test_role2; + +--sorted_result +select user, host, is_role from user where user like 'test'; + +drop role test_role1; +--error ER_CANNOT_USER +drop role test_role1; +--error ER_CANNOT_USER +drop role test_role1, test_role2; + +--sorted_result +select user, host, is_role from user where user like 'test'; +disconnect mysql; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index a6e3a74df0a..2d4499943ba 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6569,3 +6569,8 @@ ER_INVALID_ROLE ER_INVALID_CURRENT_USER eng "The current user is invalid." rum "Utilizatorul curent este invalid." +ER_INVALID_ROLE_COMMAND + eng "Unable to execute role related command. The user table is in invalid format." + rum "Comanda asupra rolurilor nu poate fi executate. Tabelul "user" este in format invalid." +ER_ROLE_AS_USER + eng "The role '%s' is marked as a user '%s'@'' diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 734b1343a7f..1b6653e7b81 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -290,6 +290,7 @@ public: DYNAMIC_ARRAY parent_grantee; // array of backlinks to elements granted ACL_ROLE(ACL_USER * user, MEM_ROOT *mem); + ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *mem); }; @@ -731,7 +732,8 @@ ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) { access= user->access; - sort= user->sort; + /* set initial role access the same as the table row privileges */ + initial_role_access= user->access; this->user.str= safe_strdup_root(root, user->user.str); this->user.length= user->user.length; bzero(&role_grants, sizeof(role_grants)); @@ -739,6 +741,18 @@ ACL_ROLE::ACL_ROLE(ACL_USER *user, MEM_ROOT *root) flags= IS_ROLE; } +ACL_ROLE::ACL_ROLE(const char * rolename, ulong privileges, MEM_ROOT *root) : + initial_role_access(privileges) +{ + this->access= initial_role_access; + this->user.str= safe_strdup_root(root, rolename); + this->user.length= strlen(rolename); + bzero(&role_grants, sizeof(role_grants)); + bzero(&parent_grantee, sizeof(parent_grantee)); + flags= IS_ROLE; +} + + static void free_acl_user(ACL_USER *user) @@ -1218,8 +1232,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) entry->role_grants = user.role_grants; (void) my_init_dynamic_array(&entry->parent_grantee, sizeof(ACL_USER_BASE *), 50, 100, MYF(0)); - /* set initial role access the same as the table row privileges */ - entry->initial_role_access= entry->access; my_hash_insert(&acl_roles, (uchar *)entry); continue; @@ -1994,6 +2006,15 @@ static void acl_update_user(const char *user, const char *host, } } +static void acl_insert_role(const char *rolename, ulong privileges) +{ + ACL_ROLE *entry; + + mysql_mutex_assert_owner(&acl_cache->lock); + entry= new (&mem) ACL_ROLE(rolename, privileges, &mem); + + my_hash_insert(&acl_roles, (uchar *)entry); +} static void acl_insert_user(const char *user, const char *host, const char *password, uint password_len, @@ -2174,7 +2195,6 @@ ulong acl_get(const char *host, const char *ip, db_access=acl_db->access; if (acl_db->host.hostname) goto exit; // Fully specified. Take it - /* XXX is this an alright way to bypass the host table for roles? */ if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user)) goto exit; break; /* purecov: tested */ @@ -3081,7 +3101,8 @@ static bool test_if_create_new_users(THD *thd) static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, ulong rights, bool revoke_grant, - bool can_create_user, bool no_auto_create) + bool can_create_user, bool no_auto_create, + bool handle_as_role) { int error = -1; bool old_row_exists=0; @@ -3104,7 +3125,14 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, else combo.password= empty_lex_str; - table->use_all_columns(); + /* if the user table is not up to date, we can't handle role updates */ + if (table->s->fields <= 42 && handle_as_role) + { + my_error(ER_INVALID_ROLE_COMMAND, MYF(0)); + DBUG_RETURN(-1); + } + + table->use_all_columns(); table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); table->field[1]->store(combo.user.str,combo.user.length, @@ -3262,6 +3290,17 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, table->field[next_field + 1]->reset(); } } + + /* table format checked earlier */ + if (handle_as_role) + { + if (old_row_exists && !check_is_role(table)) + { + my_error(ER_ROLE_AS_USER, MYF(0), combo.user.str, combo.user.str); + goto end; + } + table->field[ROLE_ASSIGN_COLUMN_IDX]->store("Y", 1, system_charset_info); + } } if (old_row_exists) @@ -3300,27 +3339,37 @@ end: { acl_cache->clear(1); // Clear privilege cache if (old_row_exists) - acl_update_user(combo.user.str, combo.host.str, - combo.password.str, combo.password.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + { + if (handle_as_role) + acl_update_role(combo.user.str, rights); + else + acl_update_user(combo.user.str, combo.host.str, + combo.password.str, combo.password.length, + lex->ssl_type, + lex->ssl_cipher, + lex->x509_issuer, + lex->x509_subject, + &lex->mqh, + rights, + &combo.plugin, + &combo.auth); + } else - acl_insert_user(combo.user.str, combo.host.str, - combo.password.str, combo.password.length, - lex->ssl_type, - lex->ssl_cipher, - lex->x509_issuer, - lex->x509_subject, - &lex->mqh, - rights, - &combo.plugin, - &combo.auth); + { + if (handle_as_role) + acl_insert_role(combo.user.str, rights); + else + acl_insert_user(combo.user.str, combo.host.str, + combo.password.str, combo.password.length, + lex->ssl_type, + lex->ssl_cipher, + lex->x509_issuer, + lex->x509_subject, + &lex->mqh, + rights, + &combo.plugin, + &combo.auth); + } } DBUG_RETURN(error); } @@ -4537,7 +4586,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER)); + MODE_NO_AUTO_CREATE_USER), 0); if (error) { result= TRUE; // Remember error @@ -4743,7 +4792,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, error=replace_user_table(thd, tables[0].table, *Str, 0, revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER)); + MODE_NO_AUTO_CREATE_USER), 0); if (error) { result= TRUE; // Remember error @@ -4900,7 +4949,7 @@ bool mysql_grant(THD *thd, const char *db, List &list, if (replace_user_table(thd, tables[0].table, *Str, (!db ? rights : 0), revoke_grant, create_new_users, test(thd->variables.sql_mode & - MODE_NO_AUTO_CREATE_USER))) + MODE_NO_AUTO_CREATE_USER), 0)) result= -1; else if (db) { @@ -6629,7 +6678,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) 1 db 2 tables_priv 3 columns_priv - 4 columns_priv + 4 procs_priv 5 proxies_priv 6 roles_mapping @@ -7552,6 +7601,15 @@ static void append_user(String *str, LEX_USER *user) str->append('\''); } +static void append_role(String *str, LEX_USER *user) +{ + if (str->length()) + str->append(','); + str->append('\''); + str->append(user->user.str); + str->append('\''); +} + /* Create a list of users. @@ -7603,7 +7661,7 @@ bool mysql_create_user(THD *thd, List &list) } some_users_created= TRUE; - if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0)) + if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0, 0)) { append_user(&wrong_users, user_name); result= TRUE; @@ -7622,6 +7680,69 @@ bool mysql_create_user(THD *thd, List &list) DBUG_RETURN(result); } +/* + Create a list of roles. + + SYNOPSIS + mysql_create_role() + thd The current thread. + list The users to create. + + RETURN + FALSE OK. + TRUE Error. +*/ + +bool mysql_create_role(THD *thd, List &list) +{ + int result; + String wrong_users; + LEX_USER *role_name; + List_iterator role_list(list); + TABLE_LIST tables[GRANT_TABLES]; + bool some_users_created= FALSE; + DBUG_ENTER("mysql_create_role"); + + if ((result= open_grant_tables(thd, tables))) + DBUG_RETURN(result != 1); + + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + + while ((role_name= role_list++)) + { + role_name->host.str= (char *)""; + role_name->host.length= 0; + /* + Search all in-memory structures and grant tables + for a mention of the new user name. + */ + if (handle_grant_data(tables, 0, role_name, NULL)) + { + append_role(&wrong_users, role_name); + result= TRUE; + continue; + } + some_users_created= TRUE; + if (replace_user_table(thd, tables[0].table, *role_name, 0, 0, 1, 0, 1)) + { + append_role(&wrong_users, role_name); + result= TRUE; + } + } + + mysql_mutex_unlock(&acl_cache->lock); + + if (result) + my_error(ER_CANNOT_USER, MYF(0), "CREATE ROLE", wrong_users.c_ptr_safe()); + + if (some_users_created) + result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); + + mysql_rwlock_unlock(&LOCK_grant); +DBUG_RETURN(result); +} + /* Drop a list of users and all their privileges. @@ -7691,6 +7812,68 @@ bool mysql_drop_user(THD *thd, List &list) DBUG_RETURN(result); } +/* + Drop a list of roles and all their privileges. + + SYNOPSIS + mysql_drop_role() + thd The current thread. + list The roles to drop. + + RETURN + FALSE OK. + TRUE Error. +*/ +bool mysql_drop_role(THD *thd, List &list) +{ + int result; + String wrong_users; + LEX_USER *role_name; + List_iterator user_list(list); + TABLE_LIST tables[GRANT_TABLES]; + bool some_users_deleted= FALSE; + ulonglong old_sql_mode= thd->variables.sql_mode; + DBUG_ENTER("mysql_drop_role"); + + /* DROP USER may be skipped on replication client. */ + if ((result= open_grant_tables(thd, tables))) + DBUG_RETURN(result != 1); + + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + + while ((role_name= user_list++)) + { + role_name->host.str= (char *)""; + role_name->host.length= 0; + + if (handle_grant_data(tables, 1, role_name, NULL) <= 0) + { + append_role(&wrong_users, role_name); + result= TRUE; + continue; + } + some_users_deleted= TRUE; + } + + /* Rebuild every user's role_grants because the acl_role has been modified + and some grants might now be invalid */ + rebuild_role_grants(); + + mysql_mutex_unlock(&acl_cache->lock); + + if (result) + my_error(ER_CANNOT_USER, MYF(0), "DROP ROLE", wrong_users.c_ptr_safe()); + + if (some_users_deleted) + result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); + + mysql_rwlock_unlock(&LOCK_grant); + thd->variables.sql_mode= old_sql_mode; + DBUG_RETURN(result); +} /* Rename a user. @@ -7819,7 +8002,7 @@ bool mysql_revoke_all(THD *thd, List &list) } if (replace_user_table(thd, tables[0].table, - *lex_user, ~(ulong)0, 1, 0, 0)) + *lex_user, ~(ulong)0, 1, 0, 0, 0)) { result= -1; continue; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 7f3ee296de8..20d03211fa1 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -234,6 +234,8 @@ bool mysql_create_user(THD *thd, List &list); bool mysql_drop_user(THD *thd, List &list); bool mysql_rename_user(THD *thd, List &list); bool mysql_revoke_all(THD *thd, List &list); +bool mysql_create_role(THD *thd, List &list); +bool mysql_drop_role(THD *thd, List &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ac6ac2763b3..266e86e405c 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3759,14 +3759,22 @@ end_with_restore_list: } case SQLCOM_CREATE_ROLE: { - /* TODO */ - my_ok(thd); + if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) + break; + /* Conditionally writes to binlog */ + if (!(res= mysql_create_role(thd, lex->users_list))) + my_ok(thd); break; } case SQLCOM_DROP_ROLE: { - /* TODO */ - my_ok(thd); + if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) && + check_global_access(thd,CREATE_USER_ACL)) + break; + /* Conditionally writes to binlog */ + if (!(res= mysql_drop_role(thd, lex->users_list))) + my_ok(thd); break; } case SQLCOM_REVOKE_ALL: diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 5ec06bf9eef..43e0194948c 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1459,7 +1459,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NCHAR_STRING opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty opt_constraint constraint opt_ident opt_if_not_exists_ident - grant_role %type opt_table_alias @@ -1570,7 +1569,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type keyword keyword_sp -%type user grant_user +%type user grant_user grant_role %type opt_collate @@ -14251,7 +14250,6 @@ revoke_command: LEX *lex= Lex; lex->sql_command= SQLCOM_REVOKE_ROLE; lex->type= 0; - printf("The rolename to be revoked is: %s\n", $1.str); } ; @@ -14305,20 +14303,40 @@ grant_command: LEX *lex= Lex; lex->sql_command= SQLCOM_GRANT_ROLE; lex->type= 0; - printf("The rolename to be granted is: %s\n", $1.str); } ; role_list: grant_role - {} + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } | role_list ',' grant_role - {} + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + ; grant_role: - IDENT_sys {$$=$1;} - | TEXT_STRING_sys {$$=$1;} + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host.str= (char *)HOST_NOT_SPECIFIED; + $$->host.length= 1; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; + + if (check_string_char_length(&$$->user, ER(ER_USERNAME), + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } ; opt_table: From ce4851c3d0de5c7e59b65d3472c6574eb850a90e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:41:25 -0700 Subject: [PATCH 096/174] Reworked the implementation of create role and drop role. Also fixed issue with drop role not clearing internal memory entry for that role. The issue was due to a condition introduced in handle_grant_data Updated testsuite to also check the possible error conditions. --- .../r/acl_roles_create_and_drop_role.result | 21 +- .../t/acl_roles_create_and_drop_role.test | 20 +- sql/share/errmsg-utf8.txt | 2 - sql/sql_acl.cc | 236 ++++++------------ sql/sql_acl.h | 6 +- sql/sql_parse.cc | 28 +-- 6 files changed, 118 insertions(+), 195 deletions(-) diff --git a/mysql-test/r/acl_roles_create_and_drop_role.result b/mysql-test/r/acl_roles_create_and_drop_role.result index 9449d020718..d4eac2756ec 100644 --- a/mysql-test/r/acl_roles_create_and_drop_role.result +++ b/mysql-test/r/acl_roles_create_and_drop_role.result @@ -1,8 +1,15 @@ use mysql; +create role test_role1@host1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@host1' at line 1 +create role test_role2@host2, test_role1@host1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '@host2, test_role1@host1' at line 1 create role test_role1; create role test_role2, test_role3; -select user, host, is_role from user where user like 'test'; +select user, host, is_role from user where user like 'test%'; user host is_role +test_role1 Y +test_role2 Y +test_role3 Y drop role test_role1; drop role test_role2, test_role3; create role test_role1; @@ -10,12 +17,20 @@ create role test_role1; ERROR HY000: Operation CREATE ROLE failed for 'test_role1' create role test_role1, test_role2; ERROR HY000: Operation CREATE ROLE failed for 'test_role1' -select user, host, is_role from user where user like 'test'; +select user, host, is_role from user where user like 'test%'; user host is_role +test_role1 Y +test_role2 Y drop role test_role1; drop role test_role1; ERROR HY000: Operation DROP ROLE failed for 'test_role1' drop role test_role1, test_role2; ERROR HY000: Operation DROP ROLE failed for 'test_role1' -select user, host, is_role from user where user like 'test'; +drop role root; +ERROR HY000: Operation DROP ROLE failed for 'root' +create user dummy@''; +drop role dummy; +ERROR HY000: Operation DROP ROLE failed for 'dummy' +drop user dummy@''; +select user, host, is_role from user where user like 'test%'; user host is_role diff --git a/mysql-test/t/acl_roles_create_and_drop_role.test b/mysql-test/t/acl_roles_create_and_drop_role.test index 45b10bb9c4b..3bdb1a40041 100644 --- a/mysql-test/t/acl_roles_create_and_drop_role.test +++ b/mysql-test/t/acl_roles_create_and_drop_role.test @@ -1,11 +1,17 @@ connect (mysql, localhost, root,,); use mysql; +#test valid syntax +--error ER_PARSE_ERROR +create role test_role1@host1; +--error ER_PARSE_ERROR +create role test_role2@host2, test_role1@host1; + create role test_role1; create role test_role2, test_role3; --sorted_result -select user, host, is_role from user where user like 'test'; +select user, host, is_role from user where user like 'test%'; drop role test_role1; drop role test_role2, test_role3; @@ -18,7 +24,7 @@ create role test_role1; create role test_role1, test_role2; --sorted_result -select user, host, is_role from user where user like 'test'; +select user, host, is_role from user where user like 'test%'; drop role test_role1; --error ER_CANNOT_USER @@ -26,6 +32,14 @@ drop role test_role1; --error ER_CANNOT_USER drop role test_role1, test_role2; +#test that we can not drop users when calling drop role +--error ER_CANNOT_USER +drop role root; +create user dummy@''; +--error ER_CANNOT_USER +drop role dummy; +drop user dummy@''; + --sorted_result -select user, host, is_role from user where user like 'test'; +select user, host, is_role from user where user like 'test%'; disconnect mysql; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 2d4499943ba..3445af7e78f 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6572,5 +6572,3 @@ ER_INVALID_CURRENT_USER ER_INVALID_ROLE_COMMAND eng "Unable to execute role related command. The user table is in invalid format." rum "Comanda asupra rolurilor nu poate fi executate. Tabelul "user" este in format invalid." -ER_ROLE_AS_USER - eng "The role '%s' is marked as a user '%s'@'' diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 1b6653e7b81..75693d02ea3 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3128,7 +3128,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, /* if the user table is not up to date, we can't handle role updates */ if (table->s->fields <= 42 && handle_as_role) { - my_error(ER_INVALID_ROLE_COMMAND, MYF(0)); + my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0)); DBUG_RETURN(-1); } @@ -3296,7 +3296,6 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, { if (old_row_exists && !check_is_role(table)) { - my_error(ER_ROLE_AS_USER, MYF(0), combo.user.str, combo.user.str); goto end; } table->field[ROLE_ASSIGN_COLUMN_IDX]->store("Y", 1, system_charset_info); @@ -7151,7 +7150,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, /* test if the current query targets a role */ is_role= (!user_from->host.length && (acl_role= find_acl_role(user_from->user.str))) ? TRUE : FALSE; - if (is_role && (struct_no != ROLE_ACL || struct_no != ROLES_MAPPINGS_HASH)) + if (is_role && struct_no != ROLE_ACL && struct_no != ROLES_MAPPINGS_HASH) { DBUG_RETURN(0); } @@ -7590,27 +7589,21 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, } -static void append_user(String *str, LEX_USER *user) +static void append_user(String *str, LEX_USER *user, bool handle_as_role) { if (str->length()) str->append(','); str->append('\''); str->append(user->user.str); - str->append(STRING_WITH_LEN("'@'")); - str->append(user->host.str); + /* hostname part is not relevant for roles, it is always empty */ + if (!handle_as_role) + { + str->append(STRING_WITH_LEN("'@'")); + str->append(user->host.str); + } str->append('\''); } -static void append_role(String *str, LEX_USER *user) -{ - if (str->length()) - str->append(','); - str->append('\''); - str->append(user->user.str); - str->append('\''); -} - - /* Create a list of users. @@ -7618,13 +7611,14 @@ static void append_role(String *str, LEX_USER *user) mysql_create_user() thd The current thread. list The users to create. + handle_as_role Handle the user list as roles if true RETURN FALSE OK. TRUE Error. */ -bool mysql_create_user(THD *thd, List &list) +bool mysql_create_user(THD *thd, List &list, bool handle_as_role) { int result; String wrong_users; @@ -7633,6 +7627,7 @@ bool mysql_create_user(THD *thd, List &list) TABLE_LIST tables[GRANT_TABLES]; bool some_users_created= FALSE; DBUG_ENTER("mysql_create_user"); + DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user")); /* CREATE USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) @@ -7643,27 +7638,45 @@ bool mysql_create_user(THD *thd, List &list) while ((tmp_user_name= user_list++)) { - if (!(user_name= get_current_user(thd, tmp_user_name))) + if (handle_as_role) { - result= TRUE; - continue; + user_name= tmp_user_name; + user_name->host.str= (char *)""; + user_name->host.length= 0; + /* role already exists */ + if (find_acl_role(user_name->user.str)) + { + append_user(&wrong_users, user_name, TRUE); + result = TRUE; + continue; + } + } + else + { + if (!(user_name= get_current_user(thd, tmp_user_name))) + { + result= TRUE; + continue; + } } /* Search all in-memory structures and grant tables - for a mention of the new user name. + for a mention of the new user/role name. */ if (handle_grant_data(tables, 0, user_name, NULL)) { - append_user(&wrong_users, user_name); + append_user(&wrong_users, user_name, handle_as_role); + result= TRUE; continue; } some_users_created= TRUE; - if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0, 0)) + if (replace_user_table(thd, tables[0].table, *user_name, 0, 0, 1, 0, + handle_as_role)) { - append_user(&wrong_users, user_name); + append_user(&wrong_users, user_name, handle_as_role); result= TRUE; } } @@ -7671,7 +7684,9 @@ bool mysql_create_user(THD *thd, List &list) mysql_mutex_unlock(&acl_cache->lock); if (result) - my_error(ER_CANNOT_USER, MYF(0), "CREATE USER", wrong_users.c_ptr_safe()); + my_error(ER_CANNOT_USER, MYF(0), + (handle_as_role) ? "CREATE ROLE" : "CREATE USER", + wrong_users.c_ptr_safe()); if (some_users_created) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); @@ -7680,70 +7695,6 @@ bool mysql_create_user(THD *thd, List &list) DBUG_RETURN(result); } -/* - Create a list of roles. - - SYNOPSIS - mysql_create_role() - thd The current thread. - list The users to create. - - RETURN - FALSE OK. - TRUE Error. -*/ - -bool mysql_create_role(THD *thd, List &list) -{ - int result; - String wrong_users; - LEX_USER *role_name; - List_iterator role_list(list); - TABLE_LIST tables[GRANT_TABLES]; - bool some_users_created= FALSE; - DBUG_ENTER("mysql_create_role"); - - if ((result= open_grant_tables(thd, tables))) - DBUG_RETURN(result != 1); - - mysql_rwlock_wrlock(&LOCK_grant); - mysql_mutex_lock(&acl_cache->lock); - - while ((role_name= role_list++)) - { - role_name->host.str= (char *)""; - role_name->host.length= 0; - /* - Search all in-memory structures and grant tables - for a mention of the new user name. - */ - if (handle_grant_data(tables, 0, role_name, NULL)) - { - append_role(&wrong_users, role_name); - result= TRUE; - continue; - } - some_users_created= TRUE; - if (replace_user_table(thd, tables[0].table, *role_name, 0, 0, 1, 0, 1)) - { - append_role(&wrong_users, role_name); - result= TRUE; - } - } - - mysql_mutex_unlock(&acl_cache->lock); - - if (result) - my_error(ER_CANNOT_USER, MYF(0), "CREATE ROLE", wrong_users.c_ptr_safe()); - - if (some_users_created) - result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - - mysql_rwlock_unlock(&LOCK_grant); -DBUG_RETURN(result); -} - - /* Drop a list of users and all their privileges. @@ -7757,7 +7708,7 @@ DBUG_RETURN(result); TRUE Error. */ -bool mysql_drop_user(THD *thd, List &list) +bool mysql_drop_user(THD *thd, List &list, bool handle_as_role) { int result; String wrong_users; @@ -7767,6 +7718,7 @@ bool mysql_drop_user(THD *thd, List &list) bool some_users_deleted= FALSE; ulonglong old_sql_mode= thd->variables.sql_mode; DBUG_ENTER("mysql_drop_user"); + DBUG_PRINT("entry", ("Handle as %s", handle_as_role ? "role" : "user")); /* DROP USER may be skipped on replication client. */ if ((result= open_grant_tables(thd, tables))) @@ -7779,22 +7731,44 @@ bool mysql_drop_user(THD *thd, List &list) while ((tmp_user_name= user_list++)) { - if (!(user_name= get_current_user(thd, tmp_user_name))) + if (handle_as_role) { - result= TRUE; - continue; + + user_name= tmp_user_name; + user_name->host.str= (char *)""; + user_name->host.length= 0; + if (!find_acl_role(user_name->user.str)) + { + append_user(&wrong_users, user_name, TRUE); + result= TRUE; + continue; + } } + else + { + if (!(user_name= get_current_user(thd, tmp_user_name))) + { + result= TRUE; + continue; + } + } + if (handle_grant_data(tables, 1, user_name, NULL) <= 0) { - append_user(&wrong_users, user_name); + append_user(&wrong_users, user_name, handle_as_role); result= TRUE; continue; } + some_users_deleted= TRUE; } - /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ - rebuild_check_host(); + if (!handle_as_role) + { + /* Rebuild 'acl_check_hosts' since 'acl_users' has been modified */ + rebuild_check_host(); + } + /* Rebuild every user's role_grants because the acl_user has been modified and some grants might now be invalid */ rebuild_role_grants(); @@ -7802,70 +7776,9 @@ bool mysql_drop_user(THD *thd, List &list) mysql_mutex_unlock(&acl_cache->lock); if (result) - my_error(ER_CANNOT_USER, MYF(0), "DROP USER", wrong_users.c_ptr_safe()); - - if (some_users_deleted) - result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); - - mysql_rwlock_unlock(&LOCK_grant); - thd->variables.sql_mode= old_sql_mode; - DBUG_RETURN(result); -} - -/* - Drop a list of roles and all their privileges. - - SYNOPSIS - mysql_drop_role() - thd The current thread. - list The roles to drop. - - RETURN - FALSE OK. - TRUE Error. -*/ -bool mysql_drop_role(THD *thd, List &list) -{ - int result; - String wrong_users; - LEX_USER *role_name; - List_iterator user_list(list); - TABLE_LIST tables[GRANT_TABLES]; - bool some_users_deleted= FALSE; - ulonglong old_sql_mode= thd->variables.sql_mode; - DBUG_ENTER("mysql_drop_role"); - - /* DROP USER may be skipped on replication client. */ - if ((result= open_grant_tables(thd, tables))) - DBUG_RETURN(result != 1); - - thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; - - mysql_rwlock_wrlock(&LOCK_grant); - mysql_mutex_lock(&acl_cache->lock); - - while ((role_name= user_list++)) - { - role_name->host.str= (char *)""; - role_name->host.length= 0; - - if (handle_grant_data(tables, 1, role_name, NULL) <= 0) - { - append_role(&wrong_users, role_name); - result= TRUE; - continue; - } - some_users_deleted= TRUE; - } - - /* Rebuild every user's role_grants because the acl_role has been modified - and some grants might now be invalid */ - rebuild_role_grants(); - - mysql_mutex_unlock(&acl_cache->lock); - - if (result) - my_error(ER_CANNOT_USER, MYF(0), "DROP ROLE", wrong_users.c_ptr_safe()); + my_error(ER_CANNOT_USER, MYF(0), + (handle_as_role) ? "DROP ROLE" : "DROP USER", + wrong_users.c_ptr_safe()); if (some_users_deleted) result |= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); @@ -7930,7 +7843,8 @@ bool mysql_rename_user(THD *thd, List &list) if (handle_grant_data(tables, 0, user_to, NULL) || handle_grant_data(tables, 0, user_from, user_to) <= 0) { - append_user(&wrong_users, user_from); + /* NOTE TODO renaming roles is not yet implemented */ + append_user(&wrong_users, user_from, FALSE); result= TRUE; continue; } diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 20d03211fa1..cace79cb441 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -230,12 +230,10 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, bool mysql_show_grants(THD *thd, LEX_USER *user); void get_privilege_desc(char *to, uint max_length, ulong access); void get_mqh(const char *user, const char *host, USER_CONN *uc); -bool mysql_create_user(THD *thd, List &list); -bool mysql_drop_user(THD *thd, List &list); +bool mysql_create_user(THD *thd, List &list, bool handle_as_role); +bool mysql_drop_user(THD *thd, List &list, bool handle_as_role); bool mysql_rename_user(THD *thd, List &list); bool mysql_revoke_all(THD *thd, List &list); -bool mysql_create_role(THD *thd, List &list); -bool mysql_drop_role(THD *thd, List &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 266e86e405c..7b24973a6e5 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3728,22 +3728,26 @@ end_with_restore_list: } #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_CREATE_USER: + case SQLCOM_CREATE_ROLE: { if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - if (!(res= mysql_create_user(thd, lex->users_list))) + if (!(res= mysql_create_user(thd, lex->users_list, + lex->sql_command == SQLCOM_CREATE_ROLE))) my_ok(thd); break; } case SQLCOM_DROP_USER: + case SQLCOM_DROP_ROLE: { if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) && check_global_access(thd,CREATE_USER_ACL)) break; /* Conditionally writes to binlog */ - if (!(res= mysql_drop_user(thd, lex->users_list))) + if (!(res= mysql_drop_user(thd, lex->users_list, + lex->sql_command == SQLCOM_DROP_ROLE))) my_ok(thd); break; } @@ -3757,26 +3761,6 @@ end_with_restore_list: my_ok(thd); break; } - case SQLCOM_CREATE_ROLE: - { - if (check_access(thd, INSERT_ACL, "mysql", NULL, NULL, 1, 1) && - check_global_access(thd,CREATE_USER_ACL)) - break; - /* Conditionally writes to binlog */ - if (!(res= mysql_create_role(thd, lex->users_list))) - my_ok(thd); - break; - } - case SQLCOM_DROP_ROLE: - { - if (check_access(thd, DELETE_ACL, "mysql", NULL, NULL, 1, 1) && - check_global_access(thd,CREATE_USER_ACL)) - break; - /* Conditionally writes to binlog */ - if (!(res= mysql_drop_role(thd, lex->users_list))) - my_ok(thd); - break; - } case SQLCOM_REVOKE_ALL: { if (check_access(thd, UPDATE_ACL, "mysql", NULL, NULL, 1, 1) && From 3fa2cb2126a5df20ae539b72095c7347343e01bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:41:34 -0700 Subject: [PATCH 097/174] Updated error message in case the user table's format is not up to date and can not support roles --- ...te_and_drop_role_invalid_user_table.result | 14 +++++++++++ ...eate_and_drop_role_invalid_user_table.test | 23 +++++++++++++++++++ sql/sql_acl.cc | 4 +++- 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 mysql-test/r/acl_roles_create_and_drop_role_invalid_user_table.result create mode 100644 mysql-test/t/acl_roles_create_and_drop_role_invalid_user_table.test diff --git a/mysql-test/r/acl_roles_create_and_drop_role_invalid_user_table.result b/mysql-test/r/acl_roles_create_and_drop_role_invalid_user_table.result new file mode 100644 index 00000000000..569997adde0 --- /dev/null +++ b/mysql-test/r/acl_roles_create_and_drop_role_invalid_user_table.result @@ -0,0 +1,14 @@ +use mysql; +alter table user drop column is_role; +flush privileges; +create role test_role; +ERROR HY000: Column count of mysql.user is wrong. Expected 43, found 42. Created with MariaDB 100003, now running 100003. Please use mysql_upgrade to fix this error. +drop role test_role; +ERROR HY000: Operation DROP ROLE failed for 'test_role' +alter table user add column is_role enum('N', 'Y') default 'N' not null +COLLATE utf8_general_ci +after authentication_string; +update user set is_role='N'; +flush privileges; +create role test_role; +drop role test_role; diff --git a/mysql-test/t/acl_roles_create_and_drop_role_invalid_user_table.test b/mysql-test/t/acl_roles_create_and_drop_role_invalid_user_table.test new file mode 100644 index 00000000000..9558b2a1a80 --- /dev/null +++ b/mysql-test/t/acl_roles_create_and_drop_role_invalid_user_table.test @@ -0,0 +1,23 @@ +connect (mysql, localhost, root,,); +use mysql; + +alter table user drop column is_role; + +flush privileges; + +--error ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE +create role test_role; +--error ER_CANNOT_USER +drop role test_role; +alter table user add column is_role enum('N', 'Y') default 'N' not null + COLLATE utf8_general_ci +after authentication_string; + +update user set is_role='N'; + +flush privileges; +create role test_role; +drop role test_role; + + + diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 75693d02ea3..d3fb0b54d7f 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3128,7 +3128,9 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, /* if the user table is not up to date, we can't handle role updates */ if (table->s->fields <= 42 && handle_as_role) { - my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0)); + my_error(ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE, MYF(0), table->alias.c_ptr(), + 43, table->s->fields, + static_cast(table->s->mysql_version), MYSQL_VERSION_ID); DBUG_RETURN(-1); } From 1fe92724794421e68dfc8d058676213c5dfb95bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:41:43 -0700 Subject: [PATCH 098/174] Removed no longer used error message. --- sql/share/errmsg-utf8.txt | 3 --- sql/sql_acl.cc | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 3445af7e78f..a6e3a74df0a 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6569,6 +6569,3 @@ ER_INVALID_ROLE ER_INVALID_CURRENT_USER eng "The current user is invalid." rum "Utilizatorul curent este invalid." -ER_INVALID_ROLE_COMMAND - eng "Unable to execute role related command. The user table is in invalid format." - rum "Comanda asupra rolurilor nu poate fi executate. Tabelul "user" este in format invalid." diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d3fb0b54d7f..b9479678ab1 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3134,7 +3134,7 @@ static int replace_user_table(THD *thd, TABLE *table, LEX_USER &combo, DBUG_RETURN(-1); } - table->use_all_columns(); + table->use_all_columns(); table->field[0]->store(combo.host.str,combo.host.length, system_charset_info); table->field[1]->store(combo.user.str,combo.user.length, From 766ae81aa47047cb2e7b10fef92678aa046eb1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 05:41:52 -0700 Subject: [PATCH 099/174] Fixed bug that caused rename user test case to fail. The bug was caused by not renaming the role if it was previously modified by the handle_grant_struct(ROLE_ACL,...) call. The same function used find_acl_role and would search for the already renamed role when it handled ROLES_MAPPINGS_HASH. This caused it to not rename the role/user correctly. --- sql/sql_acl.cc | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b9479678ab1..d606af6d83a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -6846,13 +6846,8 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, LEX_USER *user_from, LEX_USER *user_to) { /* - The first thing that needs to be checked is what we are renaming, - a user, or a role. In order to do this, perform a hash lookup over - acl_roles to find if a key exists. - - If the renaming involves renaming a role, all entries - (HostFK, UserFk) that match user_from will be renamed, - as well as all RoleFk entries that match. + All entries (HostFK, UserFk) that match user_from will be renamed, + as well as all RoleFk entries that match if user_from.host.str == "" Otherwise, only matching (HostFk, UserFk) will be renamed. */ @@ -6862,16 +6857,11 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, int result= 0; THD *thd= current_thd; const char *host, *user, *role; - my_bool is_role= FALSE; Field *host_field= table->field[0]; Field *user_field= table->field[1]; Field *role_field= table->field[2]; - if (!user_from->host.length && find_acl_role(user_from->user.str)) - is_role= TRUE; - - DBUG_PRINT("info", ("Rewriting %s entry in roles_mappings table: %s %s", - is_role ? "role" : "user", + DBUG_PRINT("info", ("Rewriting entry in roles_mappings table: %s@%s", user_from->user.str, user_from->host.str)); table->use_all_columns(); @@ -6897,11 +6887,11 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, user= ""; if (!(strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host))) + my_strcasecmp(system_charset_info, user_from->host.str, host))) result= ((drop || user_to) && modify_grant_table(table, host_field, user_field, user_to)) ? -1 : result ? result : 1; /* Error or keep result or found. */ - if (is_role) + else { if (! (role= get_field(thd->mem_root, role_field))) role= ""; @@ -6935,6 +6925,7 @@ static int handle_roles_mappings_table(TABLE *table, bool drop, } DBUG_RETURN(result); } + /* Handle a privilege table. @@ -7130,7 +7121,6 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, const char *user; const char *host; const char *role; - my_bool is_role= FALSE; uint role_not_matched= 1; ACL_USER *acl_user= NULL; ACL_ROLE *acl_role= NULL; @@ -7146,20 +7136,22 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, LINT_INIT(user); LINT_INIT(host); + LINT_INIT(role); mysql_mutex_assert_owner(&acl_cache->lock); - /* test if the current query targets a role */ - is_role= (!user_from->host.length && - (acl_role= find_acl_role(user_from->user.str))) ? TRUE : FALSE; - if (is_role && struct_no != ROLE_ACL && struct_no != ROLES_MAPPINGS_HASH) - { + /* No point in querying ROLE ACL if the user_from is not a role */ + if (struct_no == ROLE_ACL && user_from->host.length) DBUG_RETURN(0); - } - else if (struct_no == ROLE_ACL) //no need to scan the structures in this case + + if (struct_no == ROLE_ACL) //no need to scan the structures in this case { - if (!is_role || (!drop && !user_to)) - DBUG_RETURN(is_role); + acl_role= find_acl_role(user_from->user.str); + if (!acl_role) + DBUG_RETURN(0); + + if (!drop && !user_to) //role was found + DBUG_RETURN(1); /* this calls for a role update */ char *old_key= acl_role->user.str; @@ -7269,9 +7261,9 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, struct_no, idx, user, host)); #endif - if (strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host) || - (is_role && (role_not_matched= strcmp(user_from->user.str, role))) + if ((strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host)) && + (role_not_matched= strcmp(user_from->user.str, role)) ) continue; From daf0345a7bafdbf4d7d4c4ab27b2c8342e28c743 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:00:48 -0700 Subject: [PATCH 100/174] Added recursive database roles privilege propagation. The privileges are not correctly updated via grant commands yet. --- ...l_roles_set_role-database-recursive.result | 55 ++++++++++ ...acl_roles_set_role-database-recursive.test | 51 +++++++++ sql/sql_acl.cc | 102 ++++++++++++++++-- 3 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 mysql-test/r/acl_roles_set_role-database-recursive.result create mode 100644 mysql-test/t/acl_roles_set_role-database-recursive.test diff --git a/mysql-test/r/acl_roles_set_role-database-recursive.result b/mysql-test/r/acl_roles_set_role-database-recursive.result new file mode 100644 index 00000000000..cbfb50cdf5a --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-database-recursive.result @@ -0,0 +1,55 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +create user 'test_role2'@''; +update mysql.user set is_role='Y' where user='test_role1'; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role2'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role2 +flush privileges; +select user, host from mysql.db; +user host + % + % +grant select on mysql.* to test_role2@''; +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +set role test_role1; +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role2 +set role none; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +set role test_role2; +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role2 +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role2@''; +delete from mysql.user where user='test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role2'; +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-database-recursive.test b/mysql-test/t/acl_roles_set_role-database-recursive.test new file mode 100644 index 00000000000..c91c7fc5d3f --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-database-recursive.test @@ -0,0 +1,51 @@ +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +create user 'test_role2'@''; + +update mysql.user set is_role='Y' where user='test_role1'; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role2'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; +flush privileges; + +--sorted_result +select user, host from mysql.db; + +grant select on mysql.* to test_role2@''; +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +set role test_role1; +--sorted_result +select * from mysql.roles_mapping; +set role none; +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; +set role test_role2; +--sorted_result +select * from mysql.roles_mapping; + +change_user 'root'; +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role2@''; +delete from mysql.user where user='test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role2'; +flush privileges; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d606af6d83a..5d50b10bfdb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -299,6 +299,7 @@ class ACL_DB :public ACL_ACCESS public: acl_host_and_ip host; char *user,*db; + ulong initial_access; /* access bits present in the table */ }; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -712,6 +713,7 @@ static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); static my_bool get_role_access(ACL_ROLE *role, ulong *access); +static void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source); /* Enumeration of various ACL's and Hashes used in handle_grant_struct() @@ -1279,6 +1281,7 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) } db.access=get_access(table,3); db.access=fix_rights_for_db(db.access); + db.initial_access= db.access; if (lower_case_table_names) { /* @@ -2079,7 +2082,7 @@ static void acl_insert_user(const char *user, const char *host, static void acl_update_db(const char *user, const char *host, const char *db, - ulong privileges) + ulong privileges, bool set_initial_access) { mysql_mutex_assert_owner(&acl_cache->lock); @@ -2099,7 +2102,11 @@ static void acl_update_db(const char *user, const char *host, const char *db, { if (privileges) - acl_db->access=privileges; + { + acl_db->access= privileges; + if (set_initial_access) + acl_db->initial_access= acl_db->access; + } else delete_dynamic_element(&acl_dbs,i); } @@ -2118,13 +2125,15 @@ static void acl_update_db(const char *user, const char *host, const char *db, host Host name db Database name privileges Bitmap of privileges + set_initial_access If marked true, will set the initial_access field + to be set to the same value as privileges. NOTES acl_cache->lock must be locked when calling this */ static void acl_insert_db(const char *user, const char *host, const char *db, - ulong privileges) + ulong privileges, bool set_initial_access) { ACL_DB acl_db; mysql_mutex_assert_owner(&acl_cache->lock); @@ -2132,6 +2141,10 @@ static void acl_insert_db(const char *user, const char *host, const char *db, update_hostname(&acl_db.host, *host ? strdup_root(&mem,host) : 0); acl_db.db=strdup_root(&mem,db); acl_db.access=privileges; + if (set_initial_access) + acl_db.initial_access= acl_db.access; + else + acl_db.initial_access= 0; acl_db.sort=get_sort(3,acl_db.host.hostname,acl_db.db,acl_db.user); (void) push_dynamic(&acl_dbs,(uchar*) &acl_db); my_qsort((uchar*) dynamic_element(&acl_dbs,0,ACL_DB*),acl_dbs.elements, @@ -2343,6 +2356,81 @@ my_bool acl_user_reset_grant(ACL_USER *user, return 0; } +/* + The function merges access bits from a granted role to a grantee. + + It creates data structures if they don't exist for the grantee. + This includes data structures related to database privileges, tables + privileges, column privileges, function and procedures privileges +*/ + +void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) +{ + DBUG_ASSERT(source->flags & ROLE_GRANTS_FINAL); + + /* Merge global access rights */ + target->access|= source->access; + + /* Merge database privileges */ + DYNAMIC_ARRAY target_dbs, source_dbs; /* arrays of pointers to ACL_DB elements */ + ACL_DB *target_db, *source_db; /* elements of the arrays */ + + (void) my_init_dynamic_array(&target_dbs,sizeof(ACL_DB *), 50, 100, MYF(0)); + (void) my_init_dynamic_array(&source_dbs,sizeof(ACL_DB *), 50, 100, MYF(0)); + + /* get all acl_db elements for the source and the target */ + for (uint i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs,i,ACL_DB*); + if (acl_db->user && !acl_db->host.hostname) + { + if (!strcmp(target->user.str, acl_db->user)) + push_dynamic(&target_dbs, (uchar*)&acl_db); + if (!strcmp(source->user.str, acl_db->user)) + push_dynamic(&source_dbs, (uchar*)&acl_db); + } + } + + for (uint i=0 ; i < source_dbs.elements; i++) + { + source_db= *dynamic_element(&source_dbs, i, ACL_DB **); + target_db= NULL; + for (uint j=0; j < target_dbs.elements; j++) + { + ACL_DB *acl_db= *dynamic_element(&target_dbs, i, ACL_DB **); + if (!strcmp(source_db->db, acl_db->db)) /* only need to compare DB here */ + target_db= acl_db; + } + + /* acl_db element found for the target, only need to update acces bits */ + if (target_db) + { + target_db->access|= source_db->access; + } + else + { + /* + Need to create an acl_db element as this role inherits database + privileges + */ + acl_insert_db(target->user.str, "", source_db->db, + source_db->access, FALSE); + } + } + + delete_dynamic(&source_dbs); + delete_dynamic(&target_dbs); + + /* Merge table privileges */ + /* TODO */ + + /* Merge column privileges */ + /* TODO */ + + /* Merge function and procedure privileges */ + /* TODO */ +} + /* The function scans through all roles granted to the role passed as argument and places the permissions in the access variable. @@ -2380,7 +2468,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) It uses a DYNAMIC_ARRAY to reduce the number of malloc calls to a minimum */ - DYNAMIC_ARRAY stack; + DYNAMIC_ARRAY stack; NODE_STATE state; /* variable used to insert elements in the stack */ state.neigh_idx= 0; @@ -2427,7 +2515,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) if (neighbour->flags & ROLE_GRANTS_FINAL) { DBUG_PRINT("info", ("Neighbour access is final, merging")); - current->access|= neighbour->access; + merge_role_grant_privileges(current, neighbour); continue; } @@ -3474,10 +3562,10 @@ static int replace_db_table(TABLE *table, const char *db, acl_cache->clear(1); // Clear privilege cache if (old_row_exists) - acl_update_db(combo.user.str,combo.host.str,db,rights); + acl_update_db(combo.user.str,combo.host.str,db,rights, TRUE); else if (rights) - acl_insert_db(combo.user.str,combo.host.str,db,rights); + acl_insert_db(combo.user.str,combo.host.str,db,rights, TRUE); DBUG_RETURN(0); /* This could only happen if the grant tables got corrupted */ From 2826399e649ba1fc75a4b945fae13851e76756fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:01:01 -0700 Subject: [PATCH 101/174] Fixed failing test due to wrong display order --- mysql-test/r/acl_roles_set_role-multiple-role.result | 8 ++++---- mysql-test/t/acl_roles_set_role-multiple-role.test | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-multiple-role.result b/mysql-test/r/acl_roles_set_role-multiple-role.result index 9aeb5288ce8..680d50de7fd 100644 --- a/mysql-test/r/acl_roles_set_role-multiple-role.result +++ b/mysql-test/r/acl_roles_set_role-multiple-role.result @@ -68,13 +68,13 @@ Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' select * from mysql.roles_mapping; HostFk UserFk RoleFk -localhost test_user r_sel -localhost test_user r_ins -localhost test_user r_upd -localhost test_user r_del localhost test_user r_crt +localhost test_user r_del localhost test_user r_drp +localhost test_user r_ins localhost test_user r_rld +localhost test_user r_sel +localhost test_user r_upd set role r_ins; show grants; Grants for test_user@localhost diff --git a/mysql-test/t/acl_roles_set_role-multiple-role.test b/mysql-test/t/acl_roles_set_role-multiple-role.test index a046b13bb3f..0e47100ffb6 100644 --- a/mysql-test/t/acl_roles_set_role-multiple-role.test +++ b/mysql-test/t/acl_roles_set_role-multiple-role.test @@ -60,6 +60,7 @@ select * from mysql.roles_mapping; show grants; set role r_sel; show grants; +--sorted_result select * from mysql.roles_mapping; set role r_ins; @@ -104,6 +105,7 @@ set role r_sel; create table mysql.random_test_table (id INT); insert into mysql.random_test_table values (1); +--sorted_result select * from mysql.random_test_table; delete from mysql.roles_mapping where RoleFk='r_ins'; flush privileges; From 4a9832680c338217593bcb7c36b66a2df4dfbdbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:09:30 -0700 Subject: [PATCH 102/174] Refactored mysql_show_grants global privilege print into a separate function. The function will be used to help print roles privileges recursively. --- sql/sql_acl.cc | 252 +++++++++++++++++++++++++++---------------------- 1 file changed, 137 insertions(+), 115 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 5d50b10bfdb..81616e0b33b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -309,6 +309,9 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, const char *ip); static bool show_proxy_grants (THD *thd, LEX_USER *user, char *buff, size_t buffsize); +static bool show_global_privileges(THD *thd, LEX_USER *lex_user, + ACL_USER_BASE *acl_entry, bool handle_as_role, + char *buff, size_t buffsize); class ACL_PROXY_USER :public ACL_ACCESS { @@ -6297,122 +6300,11 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } /* Add first global access grants */ + if (show_global_privileges(thd, lex_user, acl_user, FALSE, buff, sizeof(buff))) { - String global(buff,sizeof(buff),system_charset_info); - global.length(0); - global.append(STRING_WITH_LEN("GRANT ")); - - want_access= acl_user->access; - if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL))) - global.append(STRING_WITH_LEN("ALL PRIVILEGES")); - else if (!(want_access & ~GRANT_ACL)) - global.append(STRING_WITH_LEN("USAGE")); - else - { - bool found=0; - ulong j,test_access= want_access & ~GRANT_ACL; - for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1) - { - if (test_access & j) - { - if (found) - global.append(STRING_WITH_LEN(", ")); - found=1; - global.append(command_array[counter],command_lengths[counter]); - } - } - } - global.append (STRING_WITH_LEN(" ON *.* TO '")); - global.append(lex_user->user.str, lex_user->user.length, - system_charset_info); - global.append (STRING_WITH_LEN("'@'")); - global.append(lex_user->host.str,lex_user->host.length, - system_charset_info); - global.append ('\''); - if (acl_user->plugin.str == native_password_plugin_name.str || - acl_user->plugin.str == old_password_plugin_name.str) - { - if (acl_user->auth_string.length) - { - DBUG_ASSERT(acl_user->salt_len); - global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); - global.append(acl_user->auth_string.str, acl_user->auth_string.length); - global.append('\''); - } - } - else - { - global.append(STRING_WITH_LEN(" IDENTIFIED VIA ")); - global.append(acl_user->plugin.str, acl_user->plugin.length); - if (acl_user->auth_string.length) - { - global.append(STRING_WITH_LEN(" USING '")); - global.append(acl_user->auth_string.str, acl_user->auth_string.length); - global.append('\''); - } - } - /* "show grants" SSL related stuff */ - if (acl_user->ssl_type == SSL_TYPE_ANY) - global.append(STRING_WITH_LEN(" REQUIRE SSL")); - else if (acl_user->ssl_type == SSL_TYPE_X509) - global.append(STRING_WITH_LEN(" REQUIRE X509")); - else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) - { - int ssl_options = 0; - global.append(STRING_WITH_LEN(" REQUIRE ")); - if (acl_user->x509_issuer) - { - ssl_options++; - global.append(STRING_WITH_LEN("ISSUER \'")); - global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); - global.append('\''); - } - if (acl_user->x509_subject) - { - if (ssl_options++) - global.append(' '); - global.append(STRING_WITH_LEN("SUBJECT \'")); - global.append(acl_user->x509_subject,strlen(acl_user->x509_subject), - system_charset_info); - global.append('\''); - } - if (acl_user->ssl_cipher) - { - if (ssl_options++) - global.append(' '); - global.append(STRING_WITH_LEN("CIPHER '")); - global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher), - system_charset_info); - global.append('\''); - } - } - if ((want_access & GRANT_ACL) || - (acl_user->user_resource.questions || - acl_user->user_resource.updates || - acl_user->user_resource.conn_per_hour || - acl_user->user_resource.user_conn)) - { - global.append(STRING_WITH_LEN(" WITH")); - if (want_access & GRANT_ACL) - global.append(STRING_WITH_LEN(" GRANT OPTION")); - add_user_option(&global, acl_user->user_resource.questions, - "MAX_QUERIES_PER_HOUR", 0); - add_user_option(&global, acl_user->user_resource.updates, - "MAX_UPDATES_PER_HOUR", 0); - add_user_option(&global, acl_user->user_resource.conn_per_hour, - "MAX_CONNECTIONS_PER_HOUR", 0); - add_user_option(&global, acl_user->user_resource.user_conn, - "MAX_USER_CONNECTIONS", 1); - } - protocol->prepare_for_resend(); - protocol->store(global.ptr(),global.length(),global.charset()); - if (protocol->write()) - { - error= -1; - goto end; - } - } - + error= -1; + goto end; + }; /* Add database access */ for (counter=0 ; counter < acl_dbs.elements ; counter++) { @@ -6625,6 +6517,136 @@ end: DBUG_RETURN(error); } +static bool show_global_privileges(THD *thd, LEX_USER *lex_user, + ACL_USER_BASE *acl_entry, bool handle_as_role, + char *buff, size_t buffsize) +{ + uint counter; + ulong want_access; + Protocol *protocol= thd->protocol; + + String global(buff,sizeof(buff),system_charset_info); + global.length(0); + global.append(STRING_WITH_LEN("GRANT ")); + + want_access= acl_entry->access; + if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL))) + global.append(STRING_WITH_LEN("ALL PRIVILEGES")); + else if (!(want_access & ~GRANT_ACL)) + global.append(STRING_WITH_LEN("USAGE")); + else + { + bool found=0; + ulong j,test_access= want_access & ~GRANT_ACL; + for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1) + { + if (test_access & j) + { + if (found) + global.append(STRING_WITH_LEN(", ")); + found=1; + global.append(command_array[counter],command_lengths[counter]); + } + } + } + global.append (STRING_WITH_LEN(" ON *.* TO '")); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + + if (!handle_as_role) + { + ACL_USER *acl_user= (ACL_USER *)acl_entry; + + global.append (STRING_WITH_LEN("'@'")); + global.append(lex_user->host.str, lex_user->host.length, + system_charset_info); + global.append ('\''); + + if (acl_user->plugin.str == native_password_plugin_name.str || + acl_user->plugin.str == old_password_plugin_name.str) + { + if (acl_user->auth_string.length) + { + DBUG_ASSERT(acl_user->salt_len); + global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); + global.append(acl_user->auth_string.str, acl_user->auth_string.length); + global.append('\''); + } + } + else + { + global.append(STRING_WITH_LEN(" IDENTIFIED VIA ")); + global.append(acl_user->plugin.str, acl_user->plugin.length); + if (acl_user->auth_string.length) + { + global.append(STRING_WITH_LEN(" USING '")); + global.append(acl_user->auth_string.str, acl_user->auth_string.length); + global.append('\''); + } + } + /* "show grants" SSL related stuff */ + if (acl_user->ssl_type == SSL_TYPE_ANY) + global.append(STRING_WITH_LEN(" REQUIRE SSL")); + else if (acl_user->ssl_type == SSL_TYPE_X509) + global.append(STRING_WITH_LEN(" REQUIRE X509")); + else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) + { + int ssl_options = 0; + global.append(STRING_WITH_LEN(" REQUIRE ")); + if (acl_user->x509_issuer) + { + ssl_options++; + global.append(STRING_WITH_LEN("ISSUER \'")); + global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + global.append('\''); + } + if (acl_user->x509_subject) + { + if (ssl_options++) + global.append(' '); + global.append(STRING_WITH_LEN("SUBJECT \'")); + global.append(acl_user->x509_subject,strlen(acl_user->x509_subject), + system_charset_info); + global.append('\''); + } + if (acl_user->ssl_cipher) + { + if (ssl_options++) + global.append(' '); + global.append(STRING_WITH_LEN("CIPHER '")); + global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher), + system_charset_info); + global.append('\''); + } + } + if ((want_access & GRANT_ACL) || + (acl_user->user_resource.questions || + acl_user->user_resource.updates || + acl_user->user_resource.conn_per_hour || + acl_user->user_resource.user_conn)) + { + global.append(STRING_WITH_LEN(" WITH")); + if (want_access & GRANT_ACL) + global.append(STRING_WITH_LEN(" GRANT OPTION")); + add_user_option(&global, acl_user->user_resource.questions, + "MAX_QUERIES_PER_HOUR", 0); + add_user_option(&global, acl_user->user_resource.updates, + "MAX_UPDATES_PER_HOUR", 0); + add_user_option(&global, acl_user->user_resource.conn_per_hour, + "MAX_CONNECTIONS_PER_HOUR", 0); + add_user_option(&global, acl_user->user_resource.user_conn, + "MAX_USER_CONNECTIONS", 1); + } + } + protocol->prepare_for_resend(); + protocol->store(global.ptr(),global.length(),global.charset()); + if (protocol->write()) + return TRUE; + + return FALSE; + +} + static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, const char *type, int typelen, char *buff, int buffsize) From d6114075299d9f0c9d6b313b401d7a78323a34ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:15:50 -0700 Subject: [PATCH 103/174] Refactored mysql_show_grants database privilege print into a separate function. The function will be used to help print roles privileges recursively. --- sql/sql_acl.cc | 151 +++++++++++++++++++++++++++---------------------- 1 file changed, 84 insertions(+), 67 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 81616e0b33b..d3fdba2ae80 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -312,6 +312,8 @@ static bool show_proxy_grants (THD *thd, LEX_USER *user, static bool show_global_privileges(THD *thd, LEX_USER *lex_user, ACL_USER_BASE *acl_entry, bool handle_as_role, char *buff, size_t buffsize); +static bool show_database_privileges(THD *thd, LEX_USER *lex_user, + char *buff, size_t buffsize); class ACL_PROXY_USER :public ACL_ACCESS { @@ -6253,11 +6255,9 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, bool mysql_show_grants(THD *thd,LEX_USER *lex_user) { - ulong want_access; uint counter,index; int error = 0; ACL_USER *acl_user; - ACL_DB *acl_db; char buff[1024]; Protocol *protocol= thd->protocol; DBUG_ENTER("mysql_show_grants"); @@ -6306,72 +6306,10 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) goto end; }; /* Add database access */ - for (counter=0 ; counter < acl_dbs.elements ; counter++) + if (show_database_privileges(thd, lex_user, buff, sizeof(buff))) { - const char *user, *host; - - acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); - if (!(user=acl_db->user)) - user= ""; - if (!(host=acl_db->host.hostname)) - host= ""; - - /* - We do not make SHOW GRANTS case-sensitive here (like REVOKE), - but make it case-insensitive because that's the way they are - actually applied, and showing fewer privileges than are applied - would be wrong from a security point of view. - */ - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) - { - want_access=acl_db->access; - if (want_access) - { - String db(buff,sizeof(buff),system_charset_info); - db.length(0); - db.append(STRING_WITH_LEN("GRANT ")); - - if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL))) - db.append(STRING_WITH_LEN("ALL PRIVILEGES")); - else if (!(want_access & ~GRANT_ACL)) - db.append(STRING_WITH_LEN("USAGE")); - else - { - int found=0, cnt; - ulong j,test_access= want_access & ~GRANT_ACL; - for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1) - { - if (test_access & j) - { - if (found) - db.append(STRING_WITH_LEN(", ")); - found = 1; - db.append(command_array[cnt],command_lengths[cnt]); - } - } - } - db.append (STRING_WITH_LEN(" ON ")); - append_identifier(thd, &db, acl_db->db, strlen(acl_db->db)); - db.append (STRING_WITH_LEN(".* TO '")); - db.append(lex_user->user.str, lex_user->user.length, - system_charset_info); - db.append (STRING_WITH_LEN("'@'")); - // host and lex_user->host are equal except for case - db.append(host, strlen(host), system_charset_info); - db.append ('\''); - if (want_access & GRANT_ACL) - db.append(STRING_WITH_LEN(" WITH GRANT OPTION")); - protocol->prepare_for_resend(); - protocol->store(db.ptr(),db.length(),db.charset()); - if (protocol->write()) - { - error= -1; - goto end; - } - } - } + error= -1; + goto end; } /* Add table & column access */ @@ -6647,6 +6585,85 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, } +static bool show_database_privileges(THD *thd, LEX_USER *lex_user, + char *buff, size_t buffsize) +{ + ACL_DB *acl_db; + ulong want_access; + uint counter; + Protocol *protocol= thd->protocol; + + for (counter=0 ; counter < acl_dbs.elements ; counter++) + { + const char *user, *host; + + acl_db=dynamic_element(&acl_dbs,counter,ACL_DB*); + if (!(user=acl_db->user)) + user= ""; + if (!(host=acl_db->host.hostname)) + host= ""; + + /* + We do not make SHOW GRANTS case-sensitive here (like REVOKE), + but make it case-insensitive because that's the way they are + actually applied, and showing fewer privileges than are applied + would be wrong from a security point of view. + */ + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + want_access=acl_db->access; + if (want_access) + { + String db(buff,sizeof(buff),system_charset_info); + db.length(0); + db.append(STRING_WITH_LEN("GRANT ")); + + if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL))) + db.append(STRING_WITH_LEN("ALL PRIVILEGES")); + else if (!(want_access & ~GRANT_ACL)) + db.append(STRING_WITH_LEN("USAGE")); + else + { + int found=0, cnt; + ulong j,test_access= want_access & ~GRANT_ACL; + for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1) + { + if (test_access & j) + { + if (found) + db.append(STRING_WITH_LEN(", ")); + found = 1; + db.append(command_array[cnt],command_lengths[cnt]); + } + } + } + db.append (STRING_WITH_LEN(" ON ")); + append_identifier(thd, &db, acl_db->db, strlen(acl_db->db)); + db.append (STRING_WITH_LEN(".* TO '")); + db.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + db.append (STRING_WITH_LEN("'@'")); + // host and lex_user->host are equal except for case + db.append(host, strlen(host), system_charset_info); + db.append ('\''); + if (want_access & GRANT_ACL) + db.append(STRING_WITH_LEN(" WITH GRANT OPTION")); + protocol->prepare_for_resend(); + protocol->store(db.ptr(),db.length(),db.charset()); + if (protocol->write()) + { + return TRUE; + } + } + } + } + return FALSE; + +} + + static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, const char *type, int typelen, char *buff, int buffsize) From 0fea3316dd9c98526289f1629343d479e9187f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:17:19 -0700 Subject: [PATCH 104/174] Refactored mysql_show_grants table and column privilege print into a separate function. The function will be used to help print roles privileges recursively. --- sql/sql_acl.cc | 239 ++++++++++++++++++++++++++----------------------- 1 file changed, 127 insertions(+), 112 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d3fdba2ae80..79bf2544629 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -314,6 +314,8 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, char *buff, size_t buffsize); static bool show_database_privileges(THD *thd, LEX_USER *lex_user, char *buff, size_t buffsize); +static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, + char *buff, size_t buffsize); class ACL_PROXY_USER :public ACL_ACCESS { @@ -6255,7 +6257,6 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, bool mysql_show_grants(THD *thd,LEX_USER *lex_user) { - uint counter,index; int error = 0; ACL_USER *acl_user; char buff[1024]; @@ -6305,6 +6306,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) error= -1; goto end; }; + /* Add database access */ if (show_database_privileges(thd, lex_user, buff, sizeof(buff))) { @@ -6313,118 +6315,10 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } /* Add table & column access */ - for (index=0 ; index < column_priv_hash.records ; index++) + if (show_table_and_column_privileges(thd, lex_user, buff, sizeof(buff))) { - const char *user, *host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) - my_hash_element(&column_priv_hash, index); - - if (!(user=grant_table->user)) - user= ""; - if (!(host= grant_table->host.hostname)) - host= ""; - - /* - We do not make SHOW GRANTS case-sensitive here (like REVOKE), - but make it case-insensitive because that's the way they are - actually applied, and showing fewer privileges than are applied - would be wrong from a security point of view. - */ - - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) - { - ulong table_access= grant_table->privs; - if ((table_access | grant_table->cols) != 0) - { - String global(buff, sizeof(buff), system_charset_info); - ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL; - - global.length(0); - global.append(STRING_WITH_LEN("GRANT ")); - - if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL))) - global.append(STRING_WITH_LEN("ALL PRIVILEGES")); - else if (!test_access) - global.append(STRING_WITH_LEN("USAGE")); - else - { - /* Add specific column access */ - int found= 0; - ulong j; - - for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1) - { - if (test_access & j) - { - if (found) - global.append(STRING_WITH_LEN(", ")); - found= 1; - global.append(command_array[counter],command_lengths[counter]); - - if (grant_table->cols) - { - uint found_col= 0; - for (uint col_index=0 ; - col_index < grant_table->hash_columns.records ; - col_index++) - { - GRANT_COLUMN *grant_column = (GRANT_COLUMN*) - my_hash_element(&grant_table->hash_columns,col_index); - if (grant_column->rights & j) - { - if (!found_col) - { - found_col= 1; - /* - If we have a duplicated table level privilege, we - must write the access privilege name again. - */ - if (table_access & j) - { - global.append(STRING_WITH_LEN(", ")); - global.append(command_array[counter], - command_lengths[counter]); - } - global.append(STRING_WITH_LEN(" (")); - } - else - global.append(STRING_WITH_LEN(", ")); - global.append(grant_column->column, - grant_column->key_length, - system_charset_info); - } - } - if (found_col) - global.append(')'); - } - } - } - } - global.append(STRING_WITH_LEN(" ON ")); - append_identifier(thd, &global, grant_table->db, - strlen(grant_table->db)); - global.append('.'); - append_identifier(thd, &global, grant_table->tname, - strlen(grant_table->tname)); - global.append(STRING_WITH_LEN(" TO '")); - global.append(lex_user->user.str, lex_user->user.length, - system_charset_info); - global.append(STRING_WITH_LEN("'@'")); - // host and lex_user->host are equal except for case - global.append(host, strlen(host), system_charset_info); - global.append('\''); - if (table_access & GRANT_ACL) - global.append(STRING_WITH_LEN(" WITH GRANT OPTION")); - protocol->prepare_for_resend(); - protocol->store(global.ptr(),global.length(),global.charset()); - if (protocol->write()) - { - error= -1; - break; - } - } - } + error= -1; + goto end; } if (show_routine_grants(thd, lex_user, &proc_priv_hash, @@ -6663,6 +6557,127 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, } +static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, + char *buff, size_t buffsize) +{ + uint counter, index; + Protocol *protocol= thd->protocol; + + for (index=0 ; index < column_priv_hash.records ; index++) + { + const char *user, *host; + GRANT_TABLE *grant_table= (GRANT_TABLE*) + my_hash_element(&column_priv_hash, index); + + if (!(user=grant_table->user)) + user= ""; + if (!(host= grant_table->host.hostname)) + host= ""; + + /* + We do not make SHOW GRANTS case-sensitive here (like REVOKE), + but make it case-insensitive because that's the way they are + actually applied, and showing fewer privileges than are applied + would be wrong from a security point of view. + */ + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + ulong table_access= grant_table->privs; + if ((table_access | grant_table->cols) != 0) + { + String global(buff, sizeof(buff), system_charset_info); + ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL; + + global.length(0); + global.append(STRING_WITH_LEN("GRANT ")); + + if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL))) + global.append(STRING_WITH_LEN("ALL PRIVILEGES")); + else if (!test_access) + global.append(STRING_WITH_LEN("USAGE")); + else + { + /* Add specific column access */ + int found= 0; + ulong j; + + for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1) + { + if (test_access & j) + { + if (found) + global.append(STRING_WITH_LEN(", ")); + found= 1; + global.append(command_array[counter],command_lengths[counter]); + + if (grant_table->cols) + { + uint found_col= 0; + for (uint col_index=0 ; + col_index < grant_table->hash_columns.records ; + col_index++) + { + GRANT_COLUMN *grant_column = (GRANT_COLUMN*) + my_hash_element(&grant_table->hash_columns,col_index); + if (grant_column->rights & j) + { + if (!found_col) + { + found_col= 1; + /* + If we have a duplicated table level privilege, we + must write the access privilege name again. + */ + if (table_access & j) + { + global.append(STRING_WITH_LEN(", ")); + global.append(command_array[counter], + command_lengths[counter]); + } + global.append(STRING_WITH_LEN(" (")); + } + else + global.append(STRING_WITH_LEN(", ")); + global.append(grant_column->column, + grant_column->key_length, + system_charset_info); + } + } + if (found_col) + global.append(')'); + } + } + } + } + global.append(STRING_WITH_LEN(" ON ")); + append_identifier(thd, &global, grant_table->db, + strlen(grant_table->db)); + global.append('.'); + append_identifier(thd, &global, grant_table->tname, + strlen(grant_table->tname)); + global.append(STRING_WITH_LEN(" TO '")); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + global.append(STRING_WITH_LEN("'@'")); + // host and lex_user->host are equal except for case + global.append(host, strlen(host), system_charset_info); + global.append('\''); + if (table_access & GRANT_ACL) + global.append(STRING_WITH_LEN(" WITH GRANT OPTION")); + protocol->prepare_for_resend(); + protocol->store(global.ptr(),global.length(),global.charset()); + if (protocol->write()) + { + return TRUE; + } + } + } + } + return FALSE; + +} static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, const char *type, int typelen, From 1bfc610dc793992c216209560c62d3eab12bf060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:17:47 -0700 Subject: [PATCH 105/174] Added show role grants functionality to the mysql_show_grants function. --- .../r/acl_roles_set_role-multiple-role.result | 21 +++++++++ .../r/acl_roles_set_role-recursive.result | 10 ++++ mysql-test/r/acl_roles_set_role-simple.result | 2 + .../t/acl_roles_set_role-multiple-role.test | 3 ++ .../t/acl_roles_set_role-recursive.test | 16 +++++++ mysql-test/t/acl_roles_set_role-simple.test | 6 +++ sql/sql_acl.cc | 46 +++++++++++++++++++ 7 files changed, 104 insertions(+) diff --git a/mysql-test/r/acl_roles_set_role-multiple-role.result b/mysql-test/r/acl_roles_set_role-multiple-role.result index 680d50de7fd..ffb0255b045 100644 --- a/mysql-test/r/acl_roles_set_role-multiple-role.result +++ b/mysql-test/r/acl_roles_set_role-multiple-role.result @@ -62,10 +62,24 @@ ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'ro show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT r_crt TO 'test_user'@'localhost' +GRANT r_del TO 'test_user'@'localhost' +GRANT r_drp TO 'test_user'@'localhost' +GRANT r_ins TO 'test_user'@'localhost' +GRANT r_rld TO 'test_user'@'localhost' +GRANT r_sel TO 'test_user'@'localhost' +GRANT r_upd TO 'test_user'@'localhost' set role r_sel; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT r_crt TO 'test_user'@'localhost' +GRANT r_del TO 'test_user'@'localhost' +GRANT r_drp TO 'test_user'@'localhost' +GRANT r_ins TO 'test_user'@'localhost' +GRANT r_rld TO 'test_user'@'localhost' +GRANT r_sel TO 'test_user'@'localhost' +GRANT r_upd TO 'test_user'@'localhost' select * from mysql.roles_mapping; HostFk UserFk RoleFk localhost test_user r_crt @@ -79,6 +93,13 @@ set role r_ins; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT r_crt TO 'test_user'@'localhost' +GRANT r_del TO 'test_user'@'localhost' +GRANT r_drp TO 'test_user'@'localhost' +GRANT r_ins TO 'test_user'@'localhost' +GRANT r_rld TO 'test_user'@'localhost' +GRANT r_sel TO 'test_user'@'localhost' +GRANT r_upd TO 'test_user'@'localhost' select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index e7ebd77fbb0..6661ed0058e 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -34,49 +34,59 @@ ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'ro show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk test_role1 test_role2 show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role none; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role test_role2; ERROR HY000: The role 'test_role2' has not been granted or is invalid. show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk test_role1 test_role2 show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role none; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' delete from mysql.user where user='test_role1'; diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index 4b2cbdec28d..11ed783e02f 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -21,10 +21,12 @@ ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'ro show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping; HostFk UserFk RoleFk localhost test_user test_role1 diff --git a/mysql-test/t/acl_roles_set_role-multiple-role.test b/mysql-test/t/acl_roles_set_role-multiple-role.test index 0e47100ffb6..3d5de6bd7fc 100644 --- a/mysql-test/t/acl_roles_set_role-multiple-role.test +++ b/mysql-test/t/acl_roles_set_role-multiple-role.test @@ -57,13 +57,16 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +--sorted_result show grants; set role r_sel; +--sorted_result show grants; --sorted_result select * from mysql.roles_mapping; set role r_ins; +--sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test index 9bf8bdaf8e9..0a7ba637c85 100644 --- a/mysql-test/t/acl_roles_set_role-recursive.test +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -15,11 +15,16 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'test_role2'); flush privileges; +--sorted_result select user, host from mysql.user where user not like 'root'; +--sorted_result select * from mysql.roles_mapping where UserFk like 'test_user'; +--sorted_result select * from mysql.roles_mapping where UserFk like 'test_role1'; grant select on *.* to 'test_role2'@''; +--sorted_result select * from mysql.user where user like 'test_role1'; +--sorted_result select * from mysql.user where user like 'test_role2'; flush privileges; @@ -28,32 +33,43 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +--sorted_result show grants; set role test_role1; +--sorted_result show grants; select * from mysql.roles_mapping where HostFk=''; +--sorted_result show grants; set role none; +--sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +--sorted_result show grants; --error ER_INVALID_ROLE set role test_role2; +--sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; #Make sure that this still works after an ER_INVALID_ROLE error +--sorted_result show grants; set role test_role1; +--sorted_result show grants; +--sorted_result select * from mysql.roles_mapping where HostFk=''; +--sorted_result show grants; set role none; +--sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test index 048b6916d83..b09bbc02b97 100644 --- a/mysql-test/t/acl_roles_set_role-simple.test +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -7,9 +7,12 @@ update mysql.user set is_role='Y' where user='test_role1'; insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', 'test_user', 'test_role1'); +--sorted_result select user, host from mysql.user where user not like 'root'; +--sorted_result select * from mysql.roles_mapping; grant select on *.* to 'test_role1'@''; +--sorted_result select * from mysql.user where user='test_role1'; flush privileges; @@ -18,9 +21,12 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +--sorted_result show grants; set role test_role1; +--sorted_result show grants; +--sorted_result select * from mysql.roles_mapping; set role none; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 79bf2544629..9041e2c49c1 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -309,6 +309,9 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, const char *ip); static bool show_proxy_grants (THD *thd, LEX_USER *user, char *buff, size_t buffsize); +static bool show_role_grants(THD *thd, LEX_USER *lex_user, + ACL_USER_BASE *acl_entry, + char *buff, size_t buffsize); static bool show_global_privileges(THD *thd, LEX_USER *lex_user, ACL_USER_BASE *acl_entry, bool handle_as_role, char *buff, size_t buffsize); @@ -6300,6 +6303,13 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) DBUG_RETURN(TRUE); } + /* Show granted roles to acl_user */ + if (show_role_grants(thd, lex_user, acl_user, buff, sizeof(buff))) + { + error= -1; + goto end; + } + /* Add first global access grants */ if (show_global_privileges(thd, lex_user, acl_user, FALSE, buff, sizeof(buff))) { @@ -6349,6 +6359,42 @@ end: DBUG_RETURN(error); } +static bool show_role_grants(THD *thd, LEX_USER *lex_user, + ACL_USER_BASE *acl_entry, + char *buff, size_t buffsize) +{ + uint counter; + Protocol *protocol= thd->protocol; + + String grant(buff,sizeof(buff),system_charset_info); + for (counter= 0; counter < acl_entry->role_grants.elements; counter++) + { + grant.length(0); + grant.append(STRING_WITH_LEN("GRANT ")); + ACL_ROLE *acl_role= *(dynamic_element(&acl_entry->role_grants, counter, + ACL_ROLE**)); + grant.append(acl_role->user.str, acl_role->user.length, + system_charset_info); + grant.append(STRING_WITH_LEN(" TO '")); + grant.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + if (!(acl_entry->flags & IS_ROLE)) + { + grant.append(STRING_WITH_LEN("'@'")); + grant.append(lex_user->host.str, lex_user->host.length, + system_charset_info); + } + grant.append('\''); + protocol->prepare_for_resend(); + protocol->store(grant.ptr(),grant.length(),grant.charset()); + if (protocol->write()) + { + return TRUE; + } + } + return FALSE; +} + static bool show_global_privileges(THD *thd, LEX_USER *lex_user, ACL_USER_BASE *acl_entry, bool handle_as_role, char *buff, size_t buffsize) From 34366918997ccdcfeb0c8c4623318c3546a9f4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:22:17 -0700 Subject: [PATCH 106/174] Refactored get_role_access into a generic traverse function. The function now performs a DEPTH FIRST SEARCH on the role graph. At various key points: on_start, on_open, on_cycle, on_finish, the function calls one of the corresponding functions passed as parameters. --- sql/sql_acl.cc | 210 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 158 insertions(+), 52 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 9041e2c49c1..6d3d4052258 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -673,11 +673,16 @@ static void init_role_grant_pair(MEM_ROOT *mem, ROLE_GRANT_PAIR *entry, #define IS_ROLE (1L << 0) /* Flag to mark that a ROLE has been visited in a DEPTH_FIRST_SEARCH */ #define ROLE_VISITED (1L << 1) +/* + Flag to mark that a ROLE and all it's children (granted roles) have + been visited + */ +#define ROLE_EXPLORED (1L << 2) /* Flag to mark that the ROLE's access bits are final, having been inherited from other granted roles */ -#define ROLE_GRANTS_FINAL (1L << 2) +#define ROLE_GRANTS_FINAL (1L << 3) static DYNAMIC_ARRAY acl_hosts, acl_users, acl_dbs, acl_proxy_users; @@ -722,7 +727,25 @@ static my_bool acl_role_reset_grant(ACL_ROLE *role, static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); -static my_bool get_role_access(ACL_ROLE *role, ulong *access); + +static void role_explore_create_list(ACL_ROLE *role, void *context_data); +static bool role_explore_start_access_check(ACL_ROLE *role, void *unused); +static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour, + void *unused); +static void role_explore_set_final_access_bits(ACL_ROLE *current, void *unused); +static int traverse_role_graph(ACL_ROLE *role, + void *context_data, + bool (*on_start) (ACL_ROLE *role, + void *context_data), + bool (*on_open) (ACL_ROLE *current, + ACL_ROLE *neighbour, + void *context_data), + bool (*on_cycle) (ACL_ROLE *current, + ACL_ROLE *neighbour, + void *context_data), + void (*on_finish)(ACL_ROLE *current, + void *context_data)); + static void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source); /* @@ -1891,7 +1914,6 @@ static void acl_update_role(const char *rolename, ulong privileges) { ACL_ROLE *role; - ulong unused; mysql_mutex_assert_owner(&acl_cache->lock); role= find_acl_role(rolename); @@ -1910,7 +1932,11 @@ static void acl_update_role(const char *rolename, role->initial_role_access= privileges; role->flags&= ~ROLE_GRANTS_FINAL; role->access= role->initial_role_access; - get_role_access(role, &unused); + traverse_role_graph(role, + NULL, + role_explore_start_access_check, + role_explore_merge_if_final, NULL, + role_explore_set_final_access_bits); for (uint i= 0; i < role->parent_grantee.elements; i++) { @@ -1931,7 +1957,7 @@ static void acl_update_role(const char *rolename, Example: RoleA -> RoleB; RoleA -> RoleC; RoleB -> RoleC; We are updating RoleC, and we reset RoleA first. If we were to run - get_role_access without resetting RoleB on RoleA, we would get the old + traverse_role_graph without resetting RoleB on RoleA, we would get the old privileges from RoleC via RoleB into RoleA. */ for (uint i= 0; i < role->parent_grantee.elements; i++) @@ -1942,7 +1968,11 @@ static void acl_update_role(const char *rolename, if (acl_user_base->flags & IS_ROLE) { grantee= (ACL_ROLE *)acl_user_base; - get_role_access(grantee, &unused); + traverse_role_graph(grantee, + NULL, + role_explore_start_access_check, + role_explore_merge_if_final, NULL, + role_explore_set_final_access_bits); } } } @@ -2332,8 +2362,11 @@ void rebuild_check_host(void) static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))) { - ulong access; - get_role_access(role, &access); + traverse_role_graph(role, + NULL, + role_explore_start_access_check, + role_explore_merge_if_final, NULL, + role_explore_set_final_access_bits); return 0; } @@ -2441,37 +2474,99 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) /* TODO */ } -/* - The function scans through all roles granted to the role passed as argument - and places the permissions in the access variable. - - Return values: - TRUE: Error or invalid parameteres - FALSE: All ok; -*/ -my_bool get_role_access(ACL_ROLE *role, ulong *access) +static void role_explore_create_list(ACL_ROLE *role, void *context_data) { - DBUG_ENTER("get_role_access"); - DBUG_PRINT("enter",("role: '%s'", role->user.str)); - DBUG_ASSERT(role); - DBUG_ASSERT(access); - /* - The search operation should always leave the ROLE_VISITED flag clean - for all nodes involved in the search - */ - DBUG_ASSERT(!(role->flags & ROLE_VISITED)); + DYNAMIC_ARRAY *list= (DYNAMIC_ARRAY *)context_data; + push_dynamic(list, (uchar*)&role); +} +static bool role_explore_start_access_check(ACL_ROLE *role, + void *unused __attribute__((unused))) +{ /* There exists the possibility that the role's access bits are final and we can just get the access bits without doing the more expensive search operation */ if (role->flags & ROLE_GRANTS_FINAL) + return TRUE; + return FALSE; +} + +static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour, + void *unused __attribute__((unused))) +{ + if (neighbour->flags & ROLE_GRANTS_FINAL) { - *access= role->access; - DBUG_PRINT("exit", ("Role access: %lu", *access)); - DBUG_RETURN(FALSE); + DBUG_PRINT("info", ("Neighbour access is final, merging")); + merge_role_grant_privileges(current, neighbour); + return TRUE; } + return FALSE; +} + +static void role_explore_set_final_access_bits(ACL_ROLE *current, + void *unused __attribute__((unused))) +{ + current->flags|= ROLE_GRANTS_FINAL; + /* Add the own role's rights once it's finished exploring */ + current->access|= current->initial_role_access; + DBUG_PRINT("info", + ("Setting final access for node: %s %lu", + current->user.str, current->access)); +} + +/* + The function scans through all roles granted to the role passed as argument + and places the permissions in the access variable. The traverse method is + a DEPTH FIRST SEARCH. + + The functions passed as parameters (if they are not NULL) are called during + specific events: + + on_start - called before initializing the stack. + on_open - called the first time a neighbour is opened. + on_cycle - called when an an attempt was made to open an already opened + neighbour + on_finish - called when a node has had all it's neighbours explored + + NOTES: + If on_start returns TRUE, the whole exploration stops. + If on_open returns TRUE, the neighbour is ignored and not placed on the stack + If on_cycle returns TRUE, the whole exploration stops. + + + Return values: + 0: Exploration finished after complete exploration; + 1: Exploration finished due to on_start returning TRUE; + 2: Exploration finished due to on_cycle returning TRUE; +*/ +static int traverse_role_graph(ACL_ROLE *role, + void *context_data, + bool (*on_start) (ACL_ROLE *role, + void *context_data), + bool (*on_open) (ACL_ROLE *current, + ACL_ROLE *neighbour, + void *context_data), + bool (*on_cycle) (ACL_ROLE *current, + ACL_ROLE *neighbour, + void *context_data), + void (*on_finish)(ACL_ROLE *current, + void *context_data)) +{ + + DBUG_ENTER("traverse_role_graph"); + DBUG_ASSERT(role); + DBUG_PRINT("enter",("role: '%s'", role->user.str)); + /* + The search operation should always leave the ROLE_VISITED and ROLE_EXPLORED + flags clean for all nodes involved in the search + */ + DBUG_ASSERT(!(role->flags & ROLE_VISITED)); + DBUG_ASSERT(!(role->flags & ROLE_EXPLORED)); + + if (on_start && on_start(role, context_data)) + DBUG_RETURN(1); /* Stack used to simulate the recursive calls of DFS. @@ -2479,13 +2574,16 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) malloc calls to a minimum */ DYNAMIC_ARRAY stack; + DYNAMIC_ARRAY to_clear; NODE_STATE state; /* variable used to insert elements in the stack */ + uint result= 0; state.neigh_idx= 0; state.node_data= role; role->flags|= ROLE_VISITED; (void) my_init_dynamic_array(&stack, sizeof(NODE_STATE), 20, 50, MYF(0)); + (void) my_init_dynamic_array(&to_clear, sizeof(ACL_ROLE *), 20, 50, MYF(0)); push_dynamic(&stack, (uchar*)&state); while (stack.elements) @@ -2504,8 +2602,7 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) */ my_bool found= FALSE; uint i; - for (i= curr_state->neigh_idx; - i < current->role_grants.elements && found == FALSE; i++) + for (i= curr_state->neigh_idx; i < current->role_grants.elements; i++) { neighbour= *(dynamic_element(¤t->role_grants, i, ACL_ROLE**)); DBUG_PRINT("info", ("Examining neighbour role %s", neighbour->user.str)); @@ -2513,22 +2610,21 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) /* check if it forms a cycle */ if (neighbour->flags & ROLE_VISITED) { - /* TODO the edge needs to be ignored */ DBUG_PRINT("info", ("Found cycle")); + /* TODO the edge needs to be ignored */ + if (on_cycle && on_cycle(current, neighbour, context_data)) + { + result= 2; + goto end; + } continue; } /* - Check if it was already explored, in that case, just set the rights - and move on + Check if it was already explored, in that case, move on */ - if (neighbour->flags & ROLE_GRANTS_FINAL) - { - DBUG_PRINT("info", ("Neighbour access is final, merging")); - merge_role_grant_privileges(current, neighbour); + if (neighbour->flags & ROLE_EXPLORED) continue; - } - /* Set the current state search index to the next index this needs to be done before inserting, so as to make sure that the @@ -2549,7 +2645,14 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) /* some sanity checks */ DBUG_ASSERT(!(neighbour->flags & ROLE_VISITED)); - DBUG_ASSERT(!(neighbour->flags & ROLE_GRANTS_FINAL)); + if (on_open && on_open(current, neighbour, context_data)) + { + /* on_open returned TRUE, mark the neighbour as being explored */ + neighbour->flags|= ROLE_EXPLORED; + push_dynamic(&to_clear, (uchar*)&neighbour); + continue; + } + /* add the neighbour on the stack */ neighbour->flags|= ROLE_VISITED; state.neigh_idx= 0; @@ -2559,27 +2662,30 @@ my_bool get_role_access(ACL_ROLE *role, ulong *access) else { /* Make sure we got a correct node */ - DBUG_ASSERT(!(curr_state->node_data->flags & ROLE_GRANTS_FINAL)); DBUG_ASSERT(curr_state->node_data->flags & ROLE_VISITED); /* Finished with exploring the current node, pop it off the stack */ curr_state= (NODE_STATE *)pop_dynamic(&stack); curr_state->node_data->flags&= ~ROLE_VISITED; /* clear the visited bit */ - curr_state->node_data->flags|= ROLE_GRANTS_FINAL; - /* Add the own role's rights once it's finished exploring */ - curr_state->node_data->access|= curr_state->node_data->initial_role_access; - DBUG_PRINT("info", - ("Setting final access for node: %s %lu", - curr_state->node_data->user.str, curr_state->node_data->access)); + curr_state->node_data->flags|= ROLE_EXPLORED; + push_dynamic(&to_clear, (uchar*)&curr_state->node_data); + if (on_finish) + on_finish(curr_state->node_data, context_data); } } +end: /* Cleanup */ + for (uint i= 0; i < to_clear.elements; i++) + { + ACL_ROLE *current= *dynamic_element(&to_clear, i, + ACL_ROLE **); + DBUG_ASSERT(current->flags & ROLE_EXPLORED); + current->flags&= ~ROLE_EXPLORED; + } delete_dynamic(&stack); - /* Finally set the access */ - *access= role->access; - DBUG_PRINT("exit", ("Role access: %lu", *access)); - DBUG_RETURN(FALSE); + delete_dynamic(&to_clear); + DBUG_RETURN(result); } /* From f2ab661999005310cb71c43e9bfa6b57ab5875de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:34:07 -0700 Subject: [PATCH 107/174] Added SHOW GRANTS recursive role print. The output is not completely correct due to recursive role grants not being completly implemented. However, this will help with testing the implementation of set role with recursive grants. --- sql/mysqld.cc | 1 + sql/sp_head.cc | 5 ++ sql/sql_acl.cc | 144 +++++++++++++++++++++++++++++++++++++++-------- sql/sql_acl.h | 2 +- sql/sql_lex.h | 1 + sql/sql_parse.cc | 6 +- sql/sql_yacc.yy | 2 +- 7 files changed, 136 insertions(+), 25 deletions(-) diff --git a/sql/mysqld.cc b/sql/mysqld.cc index a758280490d..dbaf9113dd1 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3517,6 +3517,7 @@ SHOW_VAR com_status_vars[]= { #endif {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS}, {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, + {"show_grants_self", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS_SELF]), SHOW_LONG_STATUS}, {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, {"show_index_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS}, {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 0c887c5e157..8ea0fa5e579 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -223,6 +223,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_FIELDS: case SQLCOM_SHOW_FUNC_CODE: case SQLCOM_SHOW_GRANTS: + case SQLCOM_SHOW_GRANTS_SELF: case SQLCOM_SHOW_ENGINE_STATUS: case SQLCOM_SHOW_ENGINE_LOGS: case SQLCOM_SHOW_ENGINE_MUTEX: @@ -282,9 +283,12 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_CREATE_VIEW: case SQLCOM_CREATE_TRIGGER: case SQLCOM_CREATE_USER: + case SQLCOM_CREATE_ROLE: case SQLCOM_ALTER_TABLE: case SQLCOM_GRANT: + case SQLCOM_GRANT_ROLE: case SQLCOM_REVOKE: + case SQLCOM_REVOKE_ROLE: case SQLCOM_BEGIN: case SQLCOM_RENAME_TABLE: case SQLCOM_RENAME_USER: @@ -292,6 +296,7 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_DROP_DB: case SQLCOM_REVOKE_ALL: case SQLCOM_DROP_USER: + case SQLCOM_DROP_ROLE: case SQLCOM_DROP_VIEW: case SQLCOM_DROP_TRIGGER: case SQLCOM_TRUNCATE: diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 6d3d4052258..b14bad9b600 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -316,8 +316,10 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, ACL_USER_BASE *acl_entry, bool handle_as_role, char *buff, size_t buffsize); static bool show_database_privileges(THD *thd, LEX_USER *lex_user, + bool handle_as_role, char *buff, size_t buffsize); static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, + bool handle_as_role, char *buff, size_t buffsize); class ACL_PROXY_USER :public ACL_ACCESS @@ -3884,6 +3886,7 @@ public: acl_host_and_ip host; char *db, *user, *tname, *hash_key; ulong privs; + ulong init_privs; /* privileges found in physical table */ ulong sort; size_t key_length; GRANT_NAME(const char *h, const char *d,const char *u, @@ -3901,7 +3904,9 @@ class GRANT_TABLE :public GRANT_NAME { public: ulong cols; + ulong init_cols; /* privileges found in physical table */ HASH hash_columns; + HASH init_hash_columns; GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c); @@ -6354,6 +6359,7 @@ static uint command_lengths[]= static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, const char *type, int typelen, + bool handle_as_role, char *buff, int buffsize); @@ -6364,7 +6370,32 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, Send to client grant-like strings depicting user@host privileges */ -bool mysql_show_grants(THD *thd,LEX_USER *lex_user) +bool print_grants_for_role(THD *thd, ACL_ROLE * role, + char *buff, size_t buffsize) +{ + LEX_USER lex_user; + lex_user.user= role->user; + if (show_global_privileges(thd, &lex_user, role, TRUE, buff, buffsize)) + return TRUE; + + if (show_database_privileges(thd, &lex_user, TRUE, buff, buffsize)) + return TRUE; + + if (show_table_and_column_privileges(thd, &lex_user, TRUE, buff, buffsize)) + return TRUE; + + if (show_routine_grants(thd, &lex_user, &proc_priv_hash, + STRING_WITH_LEN("PROCEDURE"), TRUE, buff, buffsize)) + return TRUE; + + if (show_routine_grants(thd, &lex_user, &func_priv_hash, + STRING_WITH_LEN("FUNCTION"), TRUE, buff, buffsize)) + return TRUE; + + return FALSE; + +} +bool mysql_show_grants(THD *thd, LEX_USER *lex_user, bool print_current_role) { int error = 0; ACL_USER *acl_user; @@ -6424,28 +6455,28 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) }; /* Add database access */ - if (show_database_privileges(thd, lex_user, buff, sizeof(buff))) + if (show_database_privileges(thd, lex_user, FALSE, buff, sizeof(buff))) { error= -1; goto end; } /* Add table & column access */ - if (show_table_and_column_privileges(thd, lex_user, buff, sizeof(buff))) + if (show_table_and_column_privileges(thd, lex_user, FALSE, buff, sizeof(buff))) { error= -1; goto end; } if (show_routine_grants(thd, lex_user, &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff))) + STRING_WITH_LEN("PROCEDURE"), FALSE, buff, sizeof(buff))) { error= -1; goto end; } if (show_routine_grants(thd, lex_user, &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff))) + STRING_WITH_LEN("FUNCTION"), FALSE, buff, sizeof(buff))) { error= -1; goto end; @@ -6457,6 +6488,32 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) goto end; } + if (print_current_role) + { + ACL_ROLE *role= find_acl_role(thd->security_ctx->priv_role); + if (role) + { + DYNAMIC_ARRAY role_list; + (void) my_init_dynamic_array(&role_list,sizeof(ACL_ROLE *), + 50, 100, MYF(0)); + /* get a list of all inherited roles */ + traverse_role_graph(role, + &role_list, NULL, NULL, NULL, + role_explore_create_list); + for (uint i= 0; i < role_list.elements; i++) + { + if (print_grants_for_role(thd, + *dynamic_element(&role_list, i, ACL_ROLE **), + buff, sizeof(buff))) + { + error= -1; + goto end; + } + } + delete_dynamic(&role_list); + } + } + end: mysql_mutex_unlock(&acl_cache->lock); mysql_rwlock_unlock(&LOCK_grant); @@ -6513,7 +6570,10 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, global.length(0); global.append(STRING_WITH_LEN("GRANT ")); - want_access= acl_entry->access; + if (handle_as_role) + want_access= ((ACL_ROLE *)acl_entry)->initial_role_access; + else + want_access= acl_entry->access; if (test_all_bits(want_access, (GLOBAL_ACLS & ~ GRANT_ACL))) global.append(STRING_WITH_LEN("ALL PRIVILEGES")); else if (!(want_access & ~GRANT_ACL)) @@ -6632,6 +6692,7 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, } static bool show_database_privileges(THD *thd, LEX_USER *lex_user, + bool handle_as_role, char *buff, size_t buffsize) { ACL_DB *acl_db; @@ -6659,7 +6720,12 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - want_access=acl_db->access; + /* do not print inherited access bits, the role bits present in the + table are what matters */ + if (handle_as_role) + want_access=acl_db->initial_access; + else + want_access=acl_db->access; if (want_access) { String db(buff,sizeof(buff),system_charset_info); @@ -6690,9 +6756,12 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, db.append (STRING_WITH_LEN(".* TO '")); db.append(lex_user->user.str, lex_user->user.length, system_charset_info); - db.append (STRING_WITH_LEN("'@'")); - // host and lex_user->host are equal except for case - db.append(host, strlen(host), system_charset_info); + if (!handle_as_role) + { + db.append (STRING_WITH_LEN("'@'")); + // host and lex_user->host are equal except for case + db.append(host, strlen(host), system_charset_info); + } db.append ('\''); if (want_access & GRANT_ACL) db.append(STRING_WITH_LEN(" WITH GRANT OPTION")); @@ -6710,6 +6779,7 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, } static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, + bool handle_as_role, char *buff, size_t buffsize) { uint counter, index; @@ -6736,11 +6806,23 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - ulong table_access= grant_table->privs; - if ((table_access | grant_table->cols) != 0) + ulong table_access; + ulong cols_access; + if (handle_as_role) + { + table_access= grant_table->init_privs; + cols_access= grant_table->init_cols; + } + else + { + table_access= grant_table->privs; + cols_access= grant_table->cols; + } + + if ((table_access | cols_access) != 0) { String global(buff, sizeof(buff), system_charset_info); - ulong test_access= (table_access | grant_table->cols) & ~GRANT_ACL; + ulong test_access= (table_access | cols_access) & ~GRANT_ACL; global.length(0); global.append(STRING_WITH_LEN("GRANT ")); @@ -6767,12 +6849,18 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, if (grant_table->cols) { uint found_col= 0; + HASH *hash_columns; + if (handle_as_role) + hash_columns= &grant_table->init_hash_columns; + else + hash_columns= &grant_table->hash_columns; + for (uint col_index=0 ; - col_index < grant_table->hash_columns.records ; + col_index < hash_columns->records ; col_index++) { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) - my_hash_element(&grant_table->hash_columns,col_index); + my_hash_element(hash_columns,col_index); if (grant_column->rights & j) { if (!found_col) @@ -6812,9 +6900,12 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, global.append(STRING_WITH_LEN(" TO '")); global.append(lex_user->user.str, lex_user->user.length, system_charset_info); - global.append(STRING_WITH_LEN("'@'")); - // host and lex_user->host are equal except for case - global.append(host, strlen(host), system_charset_info); + if (!handle_as_role) + { + global.append(STRING_WITH_LEN("'@'")); + // host and lex_user->host are equal except for case + global.append(host, strlen(host), system_charset_info); + } global.append('\''); if (table_access & GRANT_ACL) global.append(STRING_WITH_LEN(" WITH GRANT OPTION")); @@ -6833,6 +6924,7 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, const char *type, int typelen, + bool handle_as_role, char *buff, int buffsize) { uint counter, index; @@ -6859,7 +6951,12 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, if (!strcmp(lex_user->user.str,user) && !my_strcasecmp(system_charset_info, lex_user->host.str, host)) { - ulong proc_access= grant_proc->privs; + ulong proc_access; + if (handle_as_role) + proc_access= grant_proc->init_privs; + else + proc_access= grant_proc->privs; + if (proc_access != 0) { String global(buff, buffsize, system_charset_info); @@ -6898,9 +6995,12 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, global.append(STRING_WITH_LEN(" TO '")); global.append(lex_user->user.str, lex_user->user.length, system_charset_info); - global.append(STRING_WITH_LEN("'@'")); - // host and lex_user->host are equal except for case - global.append(host, strlen(host), system_charset_info); + if (!handle_as_role) + { + global.append(STRING_WITH_LEN("'@'")); + // host and lex_user->host are equal except for case + global.append(host, strlen(host), system_charset_info); + } global.append('\''); if (proc_access & GRANT_ACL) global.append(STRING_WITH_LEN(" WITH GRANT OPTION")); diff --git a/sql/sql_acl.h b/sql/sql_acl.h index cace79cb441..4a7e83f12df 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -227,7 +227,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *field_name); -bool mysql_show_grants(THD *thd, LEX_USER *user); +bool mysql_show_grants(THD *thd, LEX_USER *user, bool print_current_role); void get_privilege_desc(char *to, uint max_length, ulong access); void get_mqh(const char *user, const char *host, USER_CONN *uc); bool mysql_create_user(THD *thd, List &list, bool handle_as_role); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 293081c21db..1c1e5520a4f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -146,6 +146,7 @@ enum enum_sql_command { SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, SQLCOM_SHOW_TRIGGERS, + SQLCOM_SHOW_GRANTS_SELF, SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, SQLCOM_GRANT, SQLCOM_GRANT_ROLE, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 7b24973a6e5..f0da666ad1b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -341,6 +341,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_GRANTS_SELF]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; @@ -3990,6 +3991,7 @@ end_with_restore_list: #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: + case SQLCOM_SHOW_GRANTS_SELF: { LEX_USER *grant_user= get_current_user(thd, lex->grant_user); if (!grant_user) @@ -3998,7 +4000,9 @@ end_with_restore_list: !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) || !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0)) { - res = mysql_show_grants(thd, grant_user); + res = mysql_show_grants(thd, grant_user, + (lex->sql_command == SQLCOM_SHOW_GRANTS_SELF) ? + TRUE : FALSE); } break; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 43e0194948c..9ea07f34a3f 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -11757,7 +11757,7 @@ show_param: | GRANTS { LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->sql_command= SQLCOM_SHOW_GRANTS_SELF; LEX_USER *curr_user; if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; From 8c7ca88a6cce44c52ffb3018ed0fbfe85bcfbcac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:34:18 -0700 Subject: [PATCH 108/174] Added comment for database privilege checks. --- sql/sql_acl.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b14bad9b600..53630c6b223 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2250,6 +2250,7 @@ ulong acl_get(const char *host, const char *ip, db_access=acl_db->access; if (acl_db->host.hostname) goto exit; // Fully specified. Take it + /* the host table is not used for roles */ if ((!host || !host[0]) && !acl_db->host.hostname && find_acl_role(user)) goto exit; break; /* purecov: tested */ From d24ead2c6fa9ce89178dfbf464e6dfbcb579e197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:34:27 -0700 Subject: [PATCH 109/174] Various bug fixes. Also updated tests to reflect new show grants functionality. --- mysql-test/r/acl_roles_rename_user.result | 8 +-- .../r/acl_roles_set_role-multiple-role.result | 2 + .../r/acl_roles_set_role-recursive.result | 8 +++ mysql-test/r/acl_roles_set_role-simple.result | 1 + mysql-test/t/acl_roles_rename_user.test | 7 +++ sql/sql_acl.cc | 59 +++++++++++++++---- 6 files changed, 69 insertions(+), 16 deletions(-) diff --git a/mysql-test/r/acl_roles_rename_user.result b/mysql-test/r/acl_roles_rename_user.result index a66cdea5007..9108a34c143 100644 --- a/mysql-test/r/acl_roles_rename_user.result +++ b/mysql-test/r/acl_roles_rename_user.result @@ -13,8 +13,8 @@ flush privileges; use mysql; select * from roles_mapping; HostFk UserFk RoleFk -localhost test_user test_role1 test_role1 test_role2 +localhost test_user test_role1 rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost'; select user, host from user where user like 'test%'; user host @@ -23,8 +23,8 @@ test_role2 test_user_rm newhost select * from roles_mapping; HostFk UserFk RoleFk -newhost test_user_rm test_role1 test_role1 test_role2 +newhost test_user_rm test_role1 rename user 'test_role2'@'' to 'test_role2_rm'@''; select user, host from user where user like 'test%'; user host @@ -33,8 +33,8 @@ test_role2_rm test_user_rm newhost select * from roles_mapping; HostFk UserFk RoleFk -newhost test_user_rm test_role1 test_role1 test_role2_rm +newhost test_user_rm test_role1 rename user 'test_role1'@'' to 'test_role1_rm'@''; select user, host from user where user like 'test%'; user host @@ -43,8 +43,8 @@ test_role2_rm test_user_rm newhost select * from roles_mapping; HostFk UserFk RoleFk -newhost test_user_rm test_role1_rm test_role1_rm test_role2_rm +newhost test_user_rm test_role1_rm delete from mysql.roles_mapping; delete from mysql.user where user like 'test%'; flush privileges; diff --git a/mysql-test/r/acl_roles_set_role-multiple-role.result b/mysql-test/r/acl_roles_set_role-multiple-role.result index ffb0255b045..e795ecbf27b 100644 --- a/mysql-test/r/acl_roles_set_role-multiple-role.result +++ b/mysql-test/r/acl_roles_set_role-multiple-role.result @@ -72,6 +72,7 @@ GRANT r_upd TO 'test_user'@'localhost' set role r_sel; show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'r_sel' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT r_crt TO 'test_user'@'localhost' GRANT r_del TO 'test_user'@'localhost' @@ -92,6 +93,7 @@ localhost test_user r_upd set role r_ins; show grants; Grants for test_user@localhost +GRANT INSERT ON *.* TO 'r_ins' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT r_crt TO 'test_user'@'localhost' GRANT r_del TO 'test_user'@'localhost' diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 6661ed0058e..7ac932452f0 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -38,6 +38,8 @@ GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping where HostFk=''; @@ -45,6 +47,8 @@ HostFk UserFk RoleFk test_role1 test_role2 show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' set role none; @@ -73,6 +77,8 @@ GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping where HostFk=''; @@ -80,6 +86,8 @@ HostFk UserFk RoleFk test_role1 test_role2 show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' set role none; diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index 11ed783e02f..db54592333d 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -25,6 +25,7 @@ GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; show grants; Grants for test_user@localhost +GRANT SELECT ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' select * from mysql.roles_mapping; diff --git a/mysql-test/t/acl_roles_rename_user.test b/mysql-test/t/acl_roles_rename_user.test index 0919d8338ab..e2808c661ae 100644 --- a/mysql-test/t/acl_roles_rename_user.test +++ b/mysql-test/t/acl_roles_rename_user.test @@ -16,20 +16,27 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', flush privileges; use mysql; +--sorted_result select * from roles_mapping; #regular user rename rename user 'test_user'@'localhost' to 'test_user_rm'@'newhost'; +--sorted_result select user, host from user where user like 'test%'; +--sorted_result select * from roles_mapping; rename user 'test_role2'@'' to 'test_role2_rm'@''; +--sorted_result select user, host from user where user like 'test%'; +--sorted_result select * from roles_mapping; #role rename rename user 'test_role1'@'' to 'test_role1_rm'@''; +--sorted_result select user, host from user where user like 'test%'; +--sorted_result select * from roles_mapping; delete from mysql.roles_mapping; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 53630c6b223..51cdf596838 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -730,11 +730,15 @@ static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); -static void role_explore_create_list(ACL_ROLE *role, void *context_data); +static void role_explore_create_list(ACL_ROLE *unused, + ACL_ROLE *role, + void *context_data); static bool role_explore_start_access_check(ACL_ROLE *role, void *unused); static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour, void *unused); -static void role_explore_set_final_access_bits(ACL_ROLE *current, void *unused); +static void role_explore_set_final_access_bits(ACL_ROLE *parent, + ACL_ROLE *current, + void *unused); static int traverse_role_graph(ACL_ROLE *role, void *context_data, bool (*on_start) (ACL_ROLE *role, @@ -745,7 +749,8 @@ static int traverse_role_graph(ACL_ROLE *role, bool (*on_cycle) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context_data), - void (*on_finish)(ACL_ROLE *current, + void (*on_finish)(ACL_ROLE *parent, + ACL_ROLE *current, void *context_data)); static void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source); @@ -2477,7 +2482,8 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) /* TODO */ } -static void role_explore_create_list(ACL_ROLE *role, void *context_data) +static void role_explore_create_list(ACL_ROLE *unused __attribute__((unused)), + ACL_ROLE *role, void *context_data) { DYNAMIC_ARRAY *list= (DYNAMIC_ARRAY *)context_data; push_dynamic(list, (uchar*)&role); @@ -2508,7 +2514,8 @@ static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour, return FALSE; } -static void role_explore_set_final_access_bits(ACL_ROLE *current, +static void role_explore_set_final_access_bits(ACL_ROLE *parent, + ACL_ROLE *current, void *unused __attribute__((unused))) { current->flags|= ROLE_GRANTS_FINAL; @@ -2517,6 +2524,10 @@ static void role_explore_set_final_access_bits(ACL_ROLE *current, DBUG_PRINT("info", ("Setting final access for node: %s %lu", current->user.str, current->access)); + if (parent) + { + merge_role_grant_privileges(parent, current); + } } /* @@ -2554,7 +2565,8 @@ static int traverse_role_graph(ACL_ROLE *role, bool (*on_cycle) (ACL_ROLE *current, ACL_ROLE *neighbour, void *context_data), - void (*on_finish)(ACL_ROLE *current, + void (*on_finish)(ACL_ROLE *parent, + ACL_ROLE *current, void *context_data)) { @@ -2672,7 +2684,19 @@ static int traverse_role_graph(ACL_ROLE *role, curr_state->node_data->flags|= ROLE_EXPLORED; push_dynamic(&to_clear, (uchar*)&curr_state->node_data); if (on_finish) - on_finish(curr_state->node_data, context_data); + { + NODE_STATE *parent= NULL; + if (stack.elements) + { + parent= dynamic_element(&stack, stack.elements - 1, NODE_STATE *); + on_finish(parent->node_data, curr_state->node_data, context_data); + } + else + { + /* no parent node, this is the starting node */ + on_finish(NULL, curr_state->node_data, context_data); + } + } } } @@ -6597,12 +6621,13 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, global.append (STRING_WITH_LEN(" ON *.* TO '")); global.append(lex_user->user.str, lex_user->user.length, system_charset_info); + global.append('\''); if (!handle_as_role) { ACL_USER *acl_user= (ACL_USER *)acl_entry; - global.append (STRING_WITH_LEN("'@'")); + global.append (STRING_WITH_LEN("@'")); global.append(lex_user->host.str, lex_user->host.length, system_charset_info); global.append ('\''); @@ -6683,6 +6708,7 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, "MAX_USER_CONNECTIONS", 1); } } + protocol->prepare_for_resend(); protocol->store(global.ptr(),global.length(),global.charset()); if (protocol->write()) @@ -7656,11 +7682,20 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, struct_no, idx, user, host)); #endif - if ((strcmp(user_from->user.str, user) || - my_strcasecmp(system_charset_info, user_from->host.str, host)) && - (role_not_matched= strcmp(user_from->user.str, role)) - ) + if (struct_no == ROLES_MAPPINGS_HASH) + { + role_not_matched= strcmp(user_from->user.str, role); + if (role_not_matched && + (strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host))) continue; + } + else + { + if (strcmp(user_from->user.str, user) || + my_strcasecmp(system_charset_info, user_from->host.str, host)) + continue; + } result= 1; /* At least one element found. */ if ( drop ) From 4a58599930f5b8b3f000af92800fbe91052ed761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:40:25 -0700 Subject: [PATCH 110/174] Implemented SHOW GRANTS functionality --- mysql-test/r/acl_roles_show_grants.result | 120 ++++++++ mysql-test/t/acl_roles_show_grants.test | 81 ++++++ sql/lex.h | 1 + sql/mysqld.cc | 1 - sql/sp_head.cc | 1 - sql/sql_acl.cc | 321 ++++++++++++++-------- sql/sql_acl.h | 5 +- sql/sql_lex.h | 1 - sql/sql_parse.cc | 13 +- sql/sql_yacc.yy | 24 +- 10 files changed, 425 insertions(+), 143 deletions(-) create mode 100644 mysql-test/r/acl_roles_show_grants.result create mode 100644 mysql-test/t/acl_roles_show_grants.test diff --git a/mysql-test/r/acl_roles_show_grants.result b/mysql-test/r/acl_roles_show_grants.result new file mode 100644 index 00000000000..641964bdcee --- /dev/null +++ b/mysql-test/r/acl_roles_show_grants.result @@ -0,0 +1,120 @@ +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +create user 'test_role2'@''; +update mysql.user set is_role='Y' where user='test_role1'; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role2'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role2 +flush privileges; +select user, host from mysql.db; +user host + % + % +grant select on mysql.* to test_role2@''; +flush privileges; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +GRANT test_role2 TO 'test_user'@'localhost' +set role none; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for test_user@localhost; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +show grants for test_role1; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +show grants for test_role2; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +show grants for CURRENT_USER; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for CURRENT_USER(); +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for CURRENT_ROLE; +ERROR 42000: There is no such grant defined for user 'test_user' on host 'localhost' +show grants for CURRENT_ROLE(); +ERROR 42000: There is no such grant defined for user 'test_user' on host 'localhost' +set role test_role2; +show grants; +Grants for test_user@localhost +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for test_user@localhost; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for test_role1; +Grants for test_role1 +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT test_role2 TO 'test_role1' +show grants for test_role2; +Grants for test_role2 +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role2' +show grants for CURRENT_USER; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for CURRENT_USER(); +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_user'@'localhost' +show grants for CURRENT_ROLE; +Grants for test_role2 +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role2' +show grants for CURRENT_ROLE(); +Grants for test_role2 +GRANT SELECT ON `mysql`.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role2' +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role2@''; +delete from mysql.user where user='test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role2'; +flush privileges; diff --git a/mysql-test/t/acl_roles_show_grants.test b/mysql-test/t/acl_roles_show_grants.test new file mode 100644 index 00000000000..3d8ac80cdc1 --- /dev/null +++ b/mysql-test/t/acl_roles_show_grants.test @@ -0,0 +1,81 @@ +#create a user with no privileges +create user 'test_user'@'localhost'; +create user 'test_role1'@''; +create user 'test_role2'@''; + +update mysql.user set is_role='Y' where user='test_role1'; +update mysql.user set is_role='Y' where user='test_role2'; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role2'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; +flush privileges; + +--sorted_result +select user, host from mysql.db; + +grant select on mysql.* to test_role2@''; +flush privileges; + +change_user 'test_user'; + +--sorted_result +show grants; +set role test_role1; +--sorted_result +show grants; +set role none; +--sorted_result +show grants; + +--error ER_DBACCESS_DENIED_ERROR +show grants for test_user@localhost; +--error ER_DBACCESS_DENIED_ERROR +show grants for test_role1; +--error ER_DBACCESS_DENIED_ERROR +show grants for test_role2; +--sorted_result +show grants for CURRENT_USER; +--sorted_result +show grants for CURRENT_USER(); +--error ER_NONEXISTING_GRANT +show grants for CURRENT_ROLE; +--error ER_NONEXISTING_GRANT +show grants for CURRENT_ROLE(); + +set role test_role2; +--sorted_result +show grants; +--sorted_result +show grants for test_user@localhost; +--sorted_result +show grants for test_role1; +--sorted_result +show grants for test_role2; +--sorted_result +show grants for CURRENT_USER; +--sorted_result +show grants for CURRENT_USER(); +--sorted_result +show grants for CURRENT_ROLE; +--sorted_result +show grants for CURRENT_ROLE(); + + +change_user 'root'; +drop user 'test_user'@'localhost'; +revoke select on mysql.* from test_role2@''; +delete from mysql.user where user='test_role1'; +delete from mysql.user where user='test_role2'; +delete from mysql.roles_mapping where RoleFk='test_role1'; +delete from mysql.roles_mapping where RoleFk='test_role2'; +flush privileges; diff --git a/sql/lex.h b/sql/lex.h index 1d577b02879..7c0c9193b2e 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -153,6 +153,7 @@ static SYMBOL symbols[] = { { "CUBE", SYM(CUBE_SYM)}, { "CURRENT_DATE", SYM(CURDATE)}, { "CURRENT_POS", SYM(CURRENT_POS_SYM)}, + { "CURRENT_ROLE", SYM(CURRENT_ROLE)}, { "CURRENT_TIME", SYM(CURTIME)}, { "CURRENT_TIMESTAMP", SYM(NOW_SYM)}, { "CURRENT_USER", SYM(CURRENT_USER)}, diff --git a/sql/mysqld.cc b/sql/mysqld.cc index dbaf9113dd1..a758280490d 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -3517,7 +3517,6 @@ SHOW_VAR com_status_vars[]= { #endif {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS}, {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, - {"show_grants_self", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS_SELF]), SHOW_LONG_STATUS}, {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, {"show_index_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS}, {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 8ea0fa5e579..25e64339379 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -223,7 +223,6 @@ sp_get_flags_for_command(LEX *lex) case SQLCOM_SHOW_FIELDS: case SQLCOM_SHOW_FUNC_CODE: case SQLCOM_SHOW_GRANTS: - case SQLCOM_SHOW_GRANTS_SELF: case SQLCOM_SHOW_ENGINE_STATUS: case SQLCOM_SHOW_ENGINE_LOGS: case SQLCOM_SHOW_ENGINE_MUTEX: diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 51cdf596838..53c80503a1b 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -193,6 +193,12 @@ const char *HOST_NOT_SPECIFIED= "%"; Constant used in the SET ROLE NONE command */ const char *NONE_ROLE= "NONE"; +/* + Constants, used in the SHOW GRANTS command +*/ +LEX_USER current_user; +LEX_USER current_role; +LEX_USER current_user_and_current_role; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -307,18 +313,24 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); static ulong get_sort(uint count,...); static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, const char *ip); -static bool show_proxy_grants (THD *thd, LEX_USER *user, +static bool show_proxy_grants (THD *thd, + const char *username, const char *hostname, char *buff, size_t buffsize); -static bool show_role_grants(THD *thd, LEX_USER *lex_user, +static bool show_role_grants(THD *thd, + const char *username, const char *hostname, ACL_USER_BASE *acl_entry, char *buff, size_t buffsize); -static bool show_global_privileges(THD *thd, LEX_USER *lex_user, - ACL_USER_BASE *acl_entry, bool handle_as_role, +static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, + bool handle_as_role, char *buff, size_t buffsize); -static bool show_database_privileges(THD *thd, LEX_USER *lex_user, +static bool show_database_privileges(THD *thd, + const char *username, + const char *hostname, bool handle_as_role, char *buff, size_t buffsize); -static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, +static bool show_table_and_column_privileges(THD *thd, + const char *username, + const char *hostname, bool handle_as_role, char *buff, size_t buffsize); @@ -6382,11 +6394,40 @@ static uint command_lengths[]= }; -static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, +static int show_routine_grants(THD *thd, + const char *username, + const char *hostname, + HASH *hash, const char *type, int typelen, bool handle_as_role, char *buff, int buffsize); +bool print_grants_for_role(THD *thd, ACL_ROLE * role, + char *buff, size_t buffsize) +{ + if (show_role_grants(thd, role->user.str, "", role, buff, sizeof(buff))) + return TRUE; + + if (show_global_privileges(thd, role, TRUE, buff, buffsize)) + return TRUE; + + if (show_database_privileges(thd, role->user.str, "", TRUE, buff, buffsize)) + return TRUE; + + if (show_table_and_column_privileges(thd, role->user.str, "", TRUE, buff, buffsize)) + return TRUE; + + if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash, + STRING_WITH_LEN("PROCEDURE"), TRUE, buff, buffsize)) + return TRUE; + + if (show_routine_grants(thd, role->user.str, "", &func_priv_hash, + STRING_WITH_LEN("FUNCTION"), TRUE, buff, buffsize)) + return TRUE; + + return FALSE; + +} /* SHOW GRANTS; Send grants for a user to the client @@ -6395,37 +6436,18 @@ static int show_routine_grants(THD *thd, LEX_USER *lex_user, HASH *hash, Send to client grant-like strings depicting user@host privileges */ -bool print_grants_for_role(THD *thd, ACL_ROLE * role, - char *buff, size_t buffsize) -{ - LEX_USER lex_user; - lex_user.user= role->user; - if (show_global_privileges(thd, &lex_user, role, TRUE, buff, buffsize)) - return TRUE; - - if (show_database_privileges(thd, &lex_user, TRUE, buff, buffsize)) - return TRUE; - - if (show_table_and_column_privileges(thd, &lex_user, TRUE, buff, buffsize)) - return TRUE; - - if (show_routine_grants(thd, &lex_user, &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), TRUE, buff, buffsize)) - return TRUE; - - if (show_routine_grants(thd, &lex_user, &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), TRUE, buff, buffsize)) - return TRUE; - - return FALSE; - -} -bool mysql_show_grants(THD *thd, LEX_USER *lex_user, bool print_current_role) +bool mysql_show_grants(THD *thd, LEX_USER *lex_user) { int error = 0; ACL_USER *acl_user; + ACL_ROLE *acl_role= NULL; char buff[1024]; Protocol *protocol= thd->protocol; + bool print_user_entry= FALSE; + bool print_role_entry= FALSE; + char *username= NULL; + char *hostname= NULL; + char *rolename= NULL; DBUG_ENTER("mysql_show_grants"); LINT_INIT(acl_user); @@ -6437,27 +6459,56 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user, bool print_current_role) mysql_rwlock_rdlock(&LOCK_grant); mysql_mutex_lock(&acl_cache->lock); - - acl_user= find_user_no_anon(lex_user->host.str, lex_user->user.str, TRUE); - if (!acl_user) + if (lex_user == ¤t_user || lex_user == ¤t_role || + lex_user == ¤t_user_and_current_role) { - mysql_mutex_unlock(&acl_cache->lock); - mysql_rwlock_unlock(&LOCK_grant); + username= thd->security_ctx->priv_user; + hostname= thd->security_ctx->priv_host; + rolename= thd->security_ctx->priv_role; + } - my_error(ER_NONEXISTING_GRANT, MYF(0), - lex_user->user.str, lex_user->host.str); - DBUG_RETURN(TRUE); + if (lex_user == ¤t_user) + { + print_user_entry= TRUE; + } + else if (lex_user == ¤t_role) + { + print_role_entry= TRUE; + } + else if (lex_user == ¤t_user_and_current_role) + { + print_user_entry= TRUE; + print_role_entry= TRUE; + } + else + { + /* this lex_user could represent a role */ + if (lex_user->host.str == HOST_NOT_SPECIFIED && + find_acl_role(lex_user->user.str)) + { + rolename= lex_user->user.str; + hostname= (char *)""; + print_role_entry= TRUE; + } + else + { + username= lex_user->user.str; + hostname= lex_user->host.str; + print_user_entry= TRUE; + } } Item_string *field=new Item_string("",0,&my_charset_latin1); List field_list; field->name=buff; field->max_length=1024; - strxmov(buff,"Grants for ",lex_user->user.str,"@", - lex_user->host.str,NullS); + if (print_user_entry == FALSE) + strxmov(buff,"Grants for ",rolename, NullS); + else + strxmov(buff,"Grants for ",username,"@",hostname, NullS); field_list.push_back(field); if (protocol->send_result_set_metadata(&field_list, - Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) + Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) { mysql_mutex_unlock(&acl_cache->lock); mysql_rwlock_unlock(&LOCK_grant); @@ -6465,64 +6516,79 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user, bool print_current_role) DBUG_RETURN(TRUE); } - /* Show granted roles to acl_user */ - if (show_role_grants(thd, lex_user, acl_user, buff, sizeof(buff))) + if (print_user_entry) { - error= -1; - goto end; + acl_user= find_user_no_anon(hostname, username, TRUE); + if (!acl_user) + { + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); + + my_error(ER_NONEXISTING_GRANT, MYF(0), + username, hostname); + DBUG_RETURN(TRUE); + } + + + /* Show granted roles to acl_user */ + if (show_role_grants(thd, username, hostname, acl_user, buff, sizeof(buff))) + { + error= -1; + goto end; + } + + /* Add first global access grants */ + if (show_global_privileges(thd, acl_user, FALSE, buff, sizeof(buff))) + { + error= -1; + goto end; + }; + + /* Add database access */ + if (show_database_privileges(thd, username, hostname, FALSE, buff, sizeof(buff))) + { + error= -1; + goto end; + } + + /* Add table & column access */ + if (show_table_and_column_privileges(thd, username, hostname, FALSE, buff, sizeof(buff))) + { + error= -1; + goto end; + } + + if (show_routine_grants(thd, username, hostname, &proc_priv_hash, + STRING_WITH_LEN("PROCEDURE"), FALSE, buff, sizeof(buff))) + { + error= -1; + goto end; + } + + if (show_routine_grants(thd, username, hostname, &func_priv_hash, + STRING_WITH_LEN("FUNCTION"), FALSE, buff, sizeof(buff))) + { + error= -1; + goto end; + } + + if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff))) + { + error= -1; + goto end; + } } - /* Add first global access grants */ - if (show_global_privileges(thd, lex_user, acl_user, FALSE, buff, sizeof(buff))) + if (print_role_entry) { - error= -1; - goto end; - }; - - /* Add database access */ - if (show_database_privileges(thd, lex_user, FALSE, buff, sizeof(buff))) - { - error= -1; - goto end; - } - - /* Add table & column access */ - if (show_table_and_column_privileges(thd, lex_user, FALSE, buff, sizeof(buff))) - { - error= -1; - goto end; - } - - if (show_routine_grants(thd, lex_user, &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), FALSE, buff, sizeof(buff))) - { - error= -1; - goto end; - } - - if (show_routine_grants(thd, lex_user, &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), FALSE, buff, sizeof(buff))) - { - error= -1; - goto end; - } - - if (show_proxy_grants(thd, lex_user, buff, sizeof(buff))) - { - error= -1; - goto end; - } - - if (print_current_role) - { - ACL_ROLE *role= find_acl_role(thd->security_ctx->priv_role); - if (role) + acl_role= find_acl_role(rolename); + if (acl_role) { DYNAMIC_ARRAY role_list; (void) my_init_dynamic_array(&role_list,sizeof(ACL_ROLE *), 50, 100, MYF(0)); /* get a list of all inherited roles */ - traverse_role_graph(role, + traverse_role_graph(acl_role, &role_list, NULL, NULL, NULL, role_explore_create_list); for (uint i= 0; i < role_list.elements; i++) @@ -6537,6 +6603,17 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user, bool print_current_role) } delete_dynamic(&role_list); } + else + { + if (lex_user == ¤t_role) + { + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); + my_error(ER_NONEXISTING_GRANT, MYF(0), + username, hostname); + DBUG_RETURN(TRUE); + } + } } end: @@ -6547,12 +6624,15 @@ end: DBUG_RETURN(error); } -static bool show_role_grants(THD *thd, LEX_USER *lex_user, - ACL_USER_BASE *acl_entry, - char *buff, size_t buffsize) +static bool show_role_grants(THD *thd, + const char *username, + const char *hostname, + ACL_USER_BASE *acl_entry, + char *buff, size_t buffsize) { uint counter; Protocol *protocol= thd->protocol; + uint hostname_length = strlen(hostname); String grant(buff,sizeof(buff),system_charset_info); for (counter= 0; counter < acl_entry->role_grants.elements; counter++) @@ -6564,12 +6644,12 @@ static bool show_role_grants(THD *thd, LEX_USER *lex_user, grant.append(acl_role->user.str, acl_role->user.length, system_charset_info); grant.append(STRING_WITH_LEN(" TO '")); - grant.append(lex_user->user.str, lex_user->user.length, + grant.append(acl_entry->user.str, acl_entry->user.length, system_charset_info); if (!(acl_entry->flags & IS_ROLE)) { grant.append(STRING_WITH_LEN("'@'")); - grant.append(lex_user->host.str, lex_user->host.length, + grant.append(hostname, hostname_length, system_charset_info); } grant.append('\''); @@ -6583,9 +6663,9 @@ static bool show_role_grants(THD *thd, LEX_USER *lex_user, return FALSE; } -static bool show_global_privileges(THD *thd, LEX_USER *lex_user, - ACL_USER_BASE *acl_entry, bool handle_as_role, - char *buff, size_t buffsize) +static bool show_global_privileges(THD *thd, ACL_USER_BASE *acl_entry, + bool handle_as_role, + char *buff, size_t buffsize) { uint counter; ulong want_access; @@ -6619,7 +6699,7 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, } } global.append (STRING_WITH_LEN(" ON *.* TO '")); - global.append(lex_user->user.str, lex_user->user.length, + global.append(acl_entry->user.str, acl_entry->user.length, system_charset_info); global.append('\''); @@ -6628,7 +6708,7 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, ACL_USER *acl_user= (ACL_USER *)acl_entry; global.append (STRING_WITH_LEN("@'")); - global.append(lex_user->host.str, lex_user->host.length, + global.append(acl_user->host.hostname, acl_user->hostname_length, system_charset_info); global.append ('\''); @@ -6718,7 +6798,9 @@ static bool show_global_privileges(THD *thd, LEX_USER *lex_user, } -static bool show_database_privileges(THD *thd, LEX_USER *lex_user, +static bool show_database_privileges(THD *thd, + const char *username, + const char *hostname, bool handle_as_role, char *buff, size_t buffsize) { @@ -6744,8 +6826,8 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, would be wrong from a security point of view. */ - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + if (!strcmp(username, user) && + !my_strcasecmp(system_charset_info, hostname, host)) { /* do not print inherited access bits, the role bits present in the table are what matters */ @@ -6781,7 +6863,7 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, db.append (STRING_WITH_LEN(" ON ")); append_identifier(thd, &db, acl_db->db, strlen(acl_db->db)); db.append (STRING_WITH_LEN(".* TO '")); - db.append(lex_user->user.str, lex_user->user.length, + db.append(username, strlen(username), system_charset_info); if (!handle_as_role) { @@ -6805,7 +6887,9 @@ static bool show_database_privileges(THD *thd, LEX_USER *lex_user, } -static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, +static bool show_table_and_column_privileges(THD *thd, + const char *username, + const char *hostname, bool handle_as_role, char *buff, size_t buffsize) { @@ -6830,8 +6914,8 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, would be wrong from a security point of view. */ - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + if (!strcmp(username,user) && + !my_strcasecmp(system_charset_info, hostname, host)) { ulong table_access; ulong cols_access; @@ -6925,7 +7009,7 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, append_identifier(thd, &global, grant_table->tname, strlen(grant_table->tname)); global.append(STRING_WITH_LEN(" TO '")); - global.append(lex_user->user.str, lex_user->user.length, + global.append(username, strlen(username), system_charset_info); if (!handle_as_role) { @@ -6949,7 +7033,9 @@ static bool show_table_and_column_privileges(THD *thd, LEX_USER *lex_user, } -static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, +static int show_routine_grants(THD* thd, + const char *username, const char *hostname, + HASH *hash, const char *type, int typelen, bool handle_as_role, char *buff, int buffsize) @@ -6975,8 +7061,8 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, would be wrong from a security point of view. */ - if (!strcmp(lex_user->user.str,user) && - !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + if (!strcmp(username, user) && + !my_strcasecmp(system_charset_info, hostname, host)) { ulong proc_access; if (handle_as_role) @@ -7020,7 +7106,7 @@ static int show_routine_grants(THD* thd, LEX_USER *lex_user, HASH *hash, append_identifier(thd, &global, grant_proc->tname, strlen(grant_proc->tname)); global.append(STRING_WITH_LEN(" TO '")); - global.append(lex_user->user.str, lex_user->user.length, + global.append(username, strlen(username), system_charset_info); if (!handle_as_role) { @@ -8834,7 +8920,8 @@ acl_check_proxy_grant_access(THD *thd, const char *host, const char *user, static bool -show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize) +show_proxy_grants(THD *thd, const char *username, const char *hostname, + char *buff, size_t buffsize) { Protocol *protocol= thd->protocol; int error= 0; @@ -8843,7 +8930,7 @@ show_proxy_grants(THD *thd, LEX_USER *user, char *buff, size_t buffsize) { ACL_PROXY_USER *proxy= dynamic_element(&acl_proxy_users, i, ACL_PROXY_USER *); - if (proxy->granted_on(user->host.str, user->user.str)) + if (proxy->granted_on(hostname, username)) { String global(buff, buffsize, system_charset_info); global.length(0); diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 4a7e83f12df..1340d6fc2e7 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -175,6 +175,9 @@ extern bool mysql_user_table_is_in_short_password_format; extern const char *HOST_NOT_SPECIFIED; extern const char *NONE_ROLE; +extern LEX_USER current_user; +extern LEX_USER current_role; +extern LEX_USER current_user_and_current_role; static inline int access_denied_error_code(int passwd_used) @@ -227,7 +230,7 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *field_name); -bool mysql_show_grants(THD *thd, LEX_USER *user, bool print_current_role); +bool mysql_show_grants(THD *thd, LEX_USER *user); void get_privilege_desc(char *to, uint max_length, ulong access); void get_mqh(const char *user, const char *host, USER_CONN *uc); bool mysql_create_user(THD *thd, List &list, bool handle_as_role); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 1c1e5520a4f..293081c21db 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -146,7 +146,6 @@ enum enum_sql_command { SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CHARSETS, SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_TABLE_STATUS, SQLCOM_SHOW_TRIGGERS, - SQLCOM_SHOW_GRANTS_SELF, SQLCOM_LOAD,SQLCOM_SET_OPTION,SQLCOM_LOCK_TABLES,SQLCOM_UNLOCK_TABLES, SQLCOM_GRANT, SQLCOM_GRANT_ROLE, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f0da666ad1b..d30d476543d 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -341,7 +341,6 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_GRANTS_SELF]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE_DB]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_CREATE]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_MASTER_STAT]= CF_STATUS_COMMAND; @@ -3991,18 +3990,16 @@ end_with_restore_list: #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_SHOW_GRANTS: - case SQLCOM_SHOW_GRANTS_SELF: { - LEX_USER *grant_user= get_current_user(thd, lex->grant_user); + LEX_USER *grant_user= lex->grant_user; if (!grant_user) goto error; - if ((thd->security_ctx->priv_user && - !strcmp(thd->security_ctx->priv_user, grant_user->user.str)) || + if (grant_user == ¤t_user || + grant_user == ¤t_role || + grant_user == ¤t_user_and_current_role || !check_access(thd, SELECT_ACL, "mysql", NULL, NULL, 1, 0)) { - res = mysql_show_grants(thd, grant_user, - (lex->sql_command == SQLCOM_SHOW_GRANTS_SELF) ? - TRUE : FALSE); + res = mysql_show_grants(thd, grant_user); } break; } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 9ea07f34a3f..92770c6aacd 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -910,6 +910,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CUBE_SYM /* SQL-2003-R */ %token CURDATE /* MYSQL-FUNC */ %token CURRENT_USER /* SQL-2003-R */ +%token CURRENT_ROLE /* SQL-2003-R */ %token CURRENT_POS_SYM %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ @@ -11757,12 +11758,8 @@ show_param: | GRANTS { LEX *lex=Lex; - lex->sql_command= SQLCOM_SHOW_GRANTS_SELF; - LEX_USER *curr_user; - if (!(curr_user= (LEX_USER*) lex->thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - bzero(curr_user, sizeof(st_lex_user)); - lex->grant_user= curr_user; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user= ¤t_user_and_current_role; } | GRANTS FOR_SYM user { @@ -11771,6 +11768,12 @@ show_param: lex->grant_user=$3; lex->grant_user->password=null_lex_str; } + | GRANTS FOR_SYM CURRENT_ROLE optional_braces + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user= ¤t_role; + } | CREATE DATABASE opt_if_not_exists ident { Lex->sql_command=SQLCOM_SHOW_CREATE_DB; @@ -13201,14 +13204,7 @@ user: } | CURRENT_USER optional_braces { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) - MYSQL_YYABORT; - /* - empty LEX_USER means current_user and - will be handled in the get_current_user() function - later - */ - bzero($$, sizeof(LEX_USER)); + $$= ¤t_user; } ; From d83bbc1ffc99163aebe6ed6e18b10f43a8edcb1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:42:03 -0700 Subject: [PATCH 111/174] Initialize init_access fields for all privilege data structures. --- sql/sql_acl.cc | 52 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 53c80503a1b..29c402ee532 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3980,7 +3980,7 @@ void GRANT_NAME::set_user_details(const char *h, const char *d, GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, const char *t, ulong p, bool is_routine) - :db(0), tname(0), privs(p) + :db(0), tname(0), privs(p), init_privs(p) { set_user_details(h, d, u, t, is_routine); } @@ -3991,6 +3991,9 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, { (void) my_hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (my_hash_get_key) get_key_column,0,0); + (void) my_hash_init2(&init_hash_columns,4,system_charset_info, + 0,0,0, (my_hash_get_key) get_key_column,0,0); + } @@ -4022,6 +4025,7 @@ GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine) strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (ulong) form->field[6]->val_int(); privs = fix_rights_for_table(privs); + init_privs= privs; } @@ -4038,10 +4042,24 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) return; } cols= (ulong) form->field[7]->val_int(); - cols = fix_rights_for_column(cols); + cols= fix_rights_for_column(cols); + /* + Initial columns privileges are the same as column privileges on creation. + In case of roles, the cols privilege bits can get inherited and thus + cause the cols field to change. The init_cols field is always the same + as the physical table entry + */ + init_cols= cols; (void) my_hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (my_hash_get_key) get_key_column,0,0); + + /* + The same inheritance scheme is true for hash_columns in case of roles. + Init_hash_columns always holds the init_hash_columns + */ + (void) my_hash_init2(&init_hash_columns,4,system_charset_info, + 0,0,0, (my_hash_get_key) get_key_column,0,0); if (cols) { uint key_prefix_len; @@ -4064,6 +4082,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) if (col_privs->file->ha_index_init(0, 1)) { cols= 0; + init_cols= 0; return; } @@ -4071,7 +4090,8 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) (key_part_map)15, HA_READ_KEY_EXACT)) { - cols = 0; /* purecov: deadcode */ + cols= 0; /* purecov: deadcode */ + init_cols= 0; col_privs->file->ha_index_end(); return; } @@ -4086,13 +4106,28 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) fix_rights_for_column(priv)))) { /* Don't use this entry */ - privs = cols = 0; /* purecov: deadcode */ + privs= cols= 0; /* purecov: deadcode */ return; /* purecov: deadcode */ } if (my_hash_insert(&hash_columns, (uchar *) mem_check)) { /* Invalidate this entry */ - privs= cols= 0; + privs= cols= init_privs= init_cols=0; + return; + } + + /* add an entry into the init_hash_columns hash aswell */ + if (!(mem_check = new GRANT_COLUMN(*res, + fix_rights_for_column(priv)))) + { + /* Don't use this entry */ + privs= cols= init_privs= init_cols=0; /* purecov: deadcode */ + return; /* purecov: deadcode */ + } + if (my_hash_insert(&init_hash_columns, (uchar *) mem_check)) + { + /* Invalidate this entry */ + privs= cols= init_privs= init_cols=0; return; } } while (!col_privs->file->ha_index_next(col_privs->record[0]) && @@ -4105,6 +4140,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) GRANT_TABLE::~GRANT_TABLE() { my_hash_free(&hash_columns); + my_hash_free(&init_hash_columns); } @@ -4522,6 +4558,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, if (rights | col_rights) { + grant_table->init_privs= rights; + grant_table->init_cols= col_rights; + grant_table->privs= rights; grant_table->cols= col_rights; } @@ -4642,6 +4681,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, if (rights) { + grant_name->init_privs= rights; grant_name->privs= rights; } else @@ -6394,7 +6434,7 @@ static uint command_lengths[]= }; -static int show_routine_grants(THD *thd, +static int show_routine_grants(THD *thd, const char *username, const char *hostname, HASH *hash, From 6f9d26f09fc5b75c20715233c20d06f07a089daf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:42:59 -0700 Subject: [PATCH 112/174] Show grants now correctly prints procedure privileges. --- .../r/acl_roles_set_role-recursive.result | 4 ++ sql/sql_acl.cc | 56 +++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 7ac932452f0..8971cd1bf8a 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -42,6 +42,7 @@ GRANT SELECT ON *.* TO 'test_role2' GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk test_role1 test_role2 @@ -51,6 +52,7 @@ GRANT SELECT ON *.* TO 'test_role2' GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' set role none; show grants; Grants for test_user@localhost @@ -81,6 +83,7 @@ GRANT SELECT ON *.* TO 'test_role2' GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' select * from mysql.roles_mapping where HostFk=''; HostFk UserFk RoleFk test_role1 test_role2 @@ -90,6 +93,7 @@ GRANT SELECT ON *.* TO 'test_role2' GRANT USAGE ON *.* TO 'test_role1' GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' set role none; show grants; Grants for test_user@localhost diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 29c402ee532..b75ecf1a440 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4106,7 +4106,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) fix_rights_for_column(priv)))) { /* Don't use this entry */ - privs= cols= 0; /* purecov: deadcode */ + privs= cols= init_privs= init_cols=0; /* purecov: deadcode */ return; /* purecov: deadcode */ } if (my_hash_insert(&hash_columns, (uchar *) mem_check)) @@ -5396,6 +5396,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table) } mem_check->privs= fix_rights_for_procedure(mem_check->privs); + mem_check->init_privs= mem_check->privs; if (! mem_check->ok()) delete mem_check; else if (my_hash_insert(hash, (uchar*) mem_check)) @@ -5788,7 +5789,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, tl->get_table_name(), FALSE); if (sctx->priv_role[0]) - grant_table_role= table_hash_search("", "", tl->get_db_name(), + grant_table_role= table_hash_search("", NULL, tl->get_db_name(), sctx->priv_role, tl->get_table_name(), TRUE); @@ -5889,7 +5890,7 @@ bool check_grant_column(THD *thd, GRANT_INFO *grant, sctx->priv_user, table_name, 0); /* purecov: inspected */ grant->grant_table_role= - sctx->priv_role[0] ? table_hash_search("", "", db_name, + sctx->priv_role[0] ? table_hash_search("", NULL, db_name, sctx->priv_role, table_name, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ @@ -6061,7 +6062,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, sctx->priv_user, table_name, 0); /* purecov: inspected */ grant->grant_table_role= - sctx->priv_role[0] ? table_hash_search("", "", db_name, + sctx->priv_role[0] ? table_hash_search("", NULL, db_name, sctx->priv_role, table_name, TRUE) : NULL; grant->version= grant_version; /* purecov: inspected */ @@ -6140,6 +6141,12 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash) { return FALSE; } + if (sctx->priv_role[0] && strcmp(item->user, sctx->priv_role) == 0 && + strcmp(item->db, db) == 0 && + (!item->host.hostname || !item->host.hostname[0])) + { + return FALSE; /* Found current role match */ + } } return TRUE; @@ -6152,11 +6159,12 @@ static bool check_grant_db_routine(THD *thd, const char *db, HASH *hash) Return 1 if access is denied */ -bool check_grant_db(THD *thd,const char *db) +bool check_grant_db(THD *thd, const char *db) { Security_context *sctx= thd->security_ctx; char helping [SAFE_NAME_LEN + USERNAME_LENGTH+2], *end; - uint len; + char helping2 [SAFE_NAME_LEN + USERNAME_LENGTH+2]; + uint len, len2; bool error= TRUE; end= strmov(helping, sctx->priv_user) + 1; @@ -6167,6 +6175,18 @@ bool check_grant_db(THD *thd,const char *db) len= (uint) (end - helping) + 1; + /* + If a role is set, we need to check for privileges + here aswell + */ + if (sctx->priv_role[0]) + { + end= strmov(helping2, sctx->priv_role) + 1; + end= strnmov(end, db, helping2 + sizeof(helping2) - end); + len2= (uint) (end - helping2) + 1; + } + + mysql_rwlock_rdlock(&LOCK_grant); for (uint idx=0 ; idx < column_priv_hash.records ; idx++) @@ -6181,6 +6201,14 @@ bool check_grant_db(THD *thd,const char *db) error= FALSE; /* Found match. */ break; } + if (sctx->priv_role[0] && + len2 < grant_table->key_length && + !memcmp(grant_table->hash_key,helping,len) && + (!grant_table->host.hostname || !grant_table->host.hostname[0])) + { + error= FALSE; /* Found role match */ + break; + } } if (error) @@ -6217,6 +6245,7 @@ bool check_grant_routine(THD *thd, ulong want_access, Security_context *sctx= thd->security_ctx; char *user= sctx->priv_user; char *host= sctx->priv_host; + char *role= sctx->priv_role; DBUG_ENTER("check_grant_routine"); want_access&= ~sctx->master_access; @@ -6230,6 +6259,12 @@ bool check_grant_routine(THD *thd, ulong want_access, if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, table->table_name, is_proc, 0))) table->grant.privilege|= grant_proc->privs; + if (role[0]) /* current role set check */ + { + if ((grant_proc= routine_hash_search("", NULL, table->db, role, + table->table_name, is_proc, 0))) + table->grant.privilege|= grant_proc->privs; + } if (want_access & ~table->grant.privilege) { @@ -6287,6 +6322,15 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, sctx->priv_user, name, is_proc, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); + + if (sctx->priv_role[0]) /* current set role check */ + { + if ((grant_proc= routine_hash_search("", + NULL, db, + sctx->priv_role, + name, is_proc, 0))) + no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); + } mysql_rwlock_unlock(&LOCK_grant); return no_routine_acl; } From 1aedd4a5850d69d93abb2487122981c5eee2bbc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:45:36 -0700 Subject: [PATCH 113/174] Removed init_hash_columns hash and instead added an init_rights field to the hash_columns' original elements (GRANT_COLUMN) --- sql/sql_acl.cc | 47 ++++++++++------------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index b75ecf1a440..d2c920ff118 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -2484,11 +2484,8 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) delete_dynamic(&source_dbs); delete_dynamic(&target_dbs); - /* Merge table privileges */ - /* TODO */ + /* Merge table and column privileges */ - /* Merge column privileges */ - /* TODO */ /* Merge function and procedure privileges */ /* TODO */ @@ -3901,8 +3898,9 @@ class GRANT_COLUMN :public Sql_alloc public: char *column; ulong rights; + ulong init_rights; uint key_length; - GRANT_COLUMN(String &c, ulong y) :rights (y) + GRANT_COLUMN(String &c, ulong y) :rights (y), init_rights(y) { column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length()); } @@ -3943,7 +3941,6 @@ public: ulong cols; ulong init_cols; /* privileges found in physical table */ HASH hash_columns; - HASH init_hash_columns; GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c); @@ -3991,9 +3988,6 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, { (void) my_hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (my_hash_get_key) get_key_column,0,0); - (void) my_hash_init2(&init_hash_columns,4,system_charset_info, - 0,0,0, (my_hash_get_key) get_key_column,0,0); - } @@ -4054,12 +4048,6 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) (void) my_hash_init2(&hash_columns,4,system_charset_info, 0,0,0, (my_hash_get_key) get_key_column,0,0); - /* - The same inheritance scheme is true for hash_columns in case of roles. - Init_hash_columns always holds the init_hash_columns - */ - (void) my_hash_init2(&init_hash_columns,4,system_charset_info, - 0,0,0, (my_hash_get_key) get_key_column,0,0); if (cols) { uint key_prefix_len; @@ -4115,21 +4103,6 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) privs= cols= init_privs= init_cols=0; return; } - - /* add an entry into the init_hash_columns hash aswell */ - if (!(mem_check = new GRANT_COLUMN(*res, - fix_rights_for_column(priv)))) - { - /* Don't use this entry */ - privs= cols= init_privs= init_cols=0; /* purecov: deadcode */ - return; /* purecov: deadcode */ - } - if (my_hash_insert(&init_hash_columns, (uchar *) mem_check)) - { - /* Invalidate this entry */ - privs= cols= init_privs= init_cols=0; - return; - } } while (!col_privs->file->ha_index_next(col_privs->record[0]) && !key_cmp_if_same(col_privs,key,0,key_prefix_len)); col_privs->file->ha_index_end(); @@ -4140,7 +4113,6 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) GRANT_TABLE::~GRANT_TABLE() { my_hash_free(&hash_columns); - my_hash_free(&init_hash_columns); } @@ -4410,7 +4382,10 @@ static int replace_column_table(GRANT_TABLE *g_t, goto end; /* purecov: deadcode */ } if (grant_column) - grant_column->rights = privileges; // Update hash + { + grant_column->rights = privileges; // Update hash + grant_column->init_rights = privileges; + } } else { @@ -7045,10 +7020,7 @@ static bool show_table_and_column_privileges(THD *thd, { uint found_col= 0; HASH *hash_columns; - if (handle_as_role) - hash_columns= &grant_table->init_hash_columns; - else - hash_columns= &grant_table->hash_columns; + hash_columns= &grant_table->hash_columns; for (uint col_index=0 ; col_index < hash_columns->records ; @@ -7056,7 +7028,8 @@ static bool show_table_and_column_privileges(THD *thd, { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) my_hash_element(hash_columns,col_index); - if (grant_column->rights & j) + if ((!handle_as_role && (grant_column->rights & j)) || + (handle_as_role && (grant_column->init_rights & j))) { if (!found_col) { From bbc2771d242c6d91f1834b35e66924220f94e2d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:47:49 -0700 Subject: [PATCH 114/174] SET ROLE now works recursively for table and column level privileges --- ...cl_roles_set_role-table-column-priv.result | 61 ++++ .../r/acl_roles_set_role-table-simple.result | 59 ++++ .../acl_roles_set_role-table-column-priv.test | 57 ++++ .../t/acl_roles_set_role-table-simple.test | 54 ++++ sql/sql_acl.cc | 283 ++++++++++++------ 5 files changed, 428 insertions(+), 86 deletions(-) create mode 100644 mysql-test/r/acl_roles_set_role-table-column-priv.result create mode 100644 mysql-test/r/acl_roles_set_role-table-simple.result create mode 100644 mysql-test/t/acl_roles_set_role-table-column-priv.test create mode 100644 mysql-test/t/acl_roles_set_role-table-simple.test diff --git a/mysql-test/r/acl_roles_set_role-table-column-priv.result b/mysql-test/r/acl_roles_set_role-table-column-priv.result new file mode 100644 index 00000000000..651a57be36a --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-table-column-priv.result @@ -0,0 +1,61 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +grant select (RoleFk) on mysql.roles_mapping to test_role2@''; +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT SELECT (RoleFk) ON `mysql`.`roles_mapping` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +select * from mysql.roles_mapping; +ERROR 42000: command denied to user 'test_user'@'localhost' for table 'roles_mapping' +select RoleFk from mysql.roles_mapping; +RoleFk +test_role1 +test_role2 +show grants; +Grants for test_user@localhost +GRANT SELECT (RoleFk) ON `mysql`.`roles_mapping` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +use mysql; +set role none; +select RoleFk from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +drop user 'test_user'@'localhost'; +select * from mysql.tables_priv; +Host Db User Table_name Grantor Timestamp Table_priv Column_priv + mysql test_role2 roles_mapping root@localhost 0000-00-00 00:00:00 Select +revoke select on mysql.roles_mapping from test_role2@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +flush privileges; diff --git a/mysql-test/r/acl_roles_set_role-table-simple.result b/mysql-test/r/acl_roles_set_role-table-simple.result new file mode 100644 index 00000000000..a009ce75515 --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-table-simple.result @@ -0,0 +1,59 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +grant select on mysql.roles_mapping to test_role2@''; +flush privileges; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +set role test_role1; +show grants; +Grants for test_user@localhost +GRANT SELECT ON `mysql`.`roles_mapping` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +show grants; +Grants for test_user@localhost +GRANT SELECT ON `mysql`.`roles_mapping` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +use mysql; +set role none; +select * from mysql.roles_mapping; +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +drop user 'test_user'@'localhost'; +select * from mysql.tables_priv; +Host Db User Table_name Grantor Timestamp Table_priv Column_priv + mysql test_role2 roles_mapping root@localhost 0000-00-00 00:00:00 Select +revoke select on mysql.roles_mapping from test_role2@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-table-column-priv.test b/mysql-test/t/acl_roles_set_role-table-column-priv.test new file mode 100644 index 00000000000..3424ac8b7e4 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-table-column-priv.test @@ -0,0 +1,57 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; + +grant select (RoleFk) on mysql.roles_mapping to test_role2@''; + +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +--sorted_result +show grants; + +set role test_role1; + +--sorted_result +show grants; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +--sorted_result +select RoleFk from mysql.roles_mapping; + +--sorted_result +show grants; + +use mysql; + +set role none; + +--sorted_result +--error ER_TABLEACCESS_DENIED_ERROR +select RoleFk from mysql.roles_mapping; + +change_user 'root'; +drop user 'test_user'@'localhost'; +select * from mysql.tables_priv; +revoke select on mysql.roles_mapping from test_role2@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; + +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-table-simple.test b/mysql-test/t/acl_roles_set_role-table-simple.test new file mode 100644 index 00000000000..00aa5528fe6 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-table-simple.test @@ -0,0 +1,54 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; + +grant select on mysql.roles_mapping to test_role2@''; + +flush privileges; + +change_user 'test_user'; + +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +--sorted_result +show grants; + +set role test_role1; + +--sorted_result +show grants; + +--sorted_result +select * from mysql.roles_mapping; + +--sorted_result +show grants; + +use mysql; + +set role none; + +--sorted_result +--error ER_TABLEACCESS_DENIED_ERROR +select * from mysql.roles_mapping; + +change_user 'root'; +drop user 'test_user'@'localhost'; +select * from mysql.tables_priv; +revoke select on mysql.roles_mapping from test_role2@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; + +flush privileges; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d2c920ff118..69e619c01b5 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1443,9 +1443,6 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) free_root(&temp_root, MYF(0)); end_read_record(&read_record_info); - my_hash_iterate(&acl_roles, - (my_hash_walk_action) acl_role_propagate_grants, NULL); - if (!initialized) mysql_mutex_unlock(&acl_cache->lock); @@ -2419,78 +2416,6 @@ my_bool acl_user_reset_grant(ACL_USER *user, return 0; } -/* - The function merges access bits from a granted role to a grantee. - - It creates data structures if they don't exist for the grantee. - This includes data structures related to database privileges, tables - privileges, column privileges, function and procedures privileges -*/ - -void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) -{ - DBUG_ASSERT(source->flags & ROLE_GRANTS_FINAL); - - /* Merge global access rights */ - target->access|= source->access; - - /* Merge database privileges */ - DYNAMIC_ARRAY target_dbs, source_dbs; /* arrays of pointers to ACL_DB elements */ - ACL_DB *target_db, *source_db; /* elements of the arrays */ - - (void) my_init_dynamic_array(&target_dbs,sizeof(ACL_DB *), 50, 100, MYF(0)); - (void) my_init_dynamic_array(&source_dbs,sizeof(ACL_DB *), 50, 100, MYF(0)); - - /* get all acl_db elements for the source and the target */ - for (uint i=0 ; i < acl_dbs.elements ; i++) - { - ACL_DB *acl_db= dynamic_element(&acl_dbs,i,ACL_DB*); - if (acl_db->user && !acl_db->host.hostname) - { - if (!strcmp(target->user.str, acl_db->user)) - push_dynamic(&target_dbs, (uchar*)&acl_db); - if (!strcmp(source->user.str, acl_db->user)) - push_dynamic(&source_dbs, (uchar*)&acl_db); - } - } - - for (uint i=0 ; i < source_dbs.elements; i++) - { - source_db= *dynamic_element(&source_dbs, i, ACL_DB **); - target_db= NULL; - for (uint j=0; j < target_dbs.elements; j++) - { - ACL_DB *acl_db= *dynamic_element(&target_dbs, i, ACL_DB **); - if (!strcmp(source_db->db, acl_db->db)) /* only need to compare DB here */ - target_db= acl_db; - } - - /* acl_db element found for the target, only need to update acces bits */ - if (target_db) - { - target_db->access|= source_db->access; - } - else - { - /* - Need to create an acl_db element as this role inherits database - privileges - */ - acl_insert_db(target->user.str, "", source_db->db, - source_db->access, FALSE); - } - } - - delete_dynamic(&source_dbs); - delete_dynamic(&target_dbs); - - /* Merge table and column privileges */ - - - /* Merge function and procedure privileges */ - /* TODO */ -} - static void role_explore_create_list(ACL_ROLE *unused __attribute__((unused)), ACL_ROLE *role, void *context_data) { @@ -3904,6 +3829,12 @@ public: { column= (char*) memdup_root(&memex,c.ptr(), key_length=c.length()); } + + GRANT_COLUMN(GRANT_COLUMN *source) : rights (source->rights), init_rights(0) + { + column= (char *) memdup_root(&memex, source->column, + key_length=source->key_length); + } }; @@ -3914,6 +3845,43 @@ static uchar* get_key_column(GRANT_COLUMN *buff, size_t *length, return (uchar*) buff->column; } +static void merge_grant_table_hash_columns(HASH *target, HASH *source) +{ + MEM_ROOT *memex_ptr= &memex; + for (uint i=0 ; i < source->records ; i++) + { + GRANT_COLUMN *source_col = (GRANT_COLUMN *)my_hash_element(source, i); + GRANT_COLUMN *target_col = (GRANT_COLUMN *) + my_hash_search(target, + (uchar *)source_col->column, + source_col->key_length); + /* target has the column in the hashtable */ + if (target_col) + { + target_col->rights|= source_col->rights; + } + else + { + GRANT_COLUMN *target_col = new (memex_ptr) GRANT_COLUMN(source_col); + my_hash_insert(target, (uchar *)target_col); + } + } +} + +/* same as merge_grant_table_hash_columns, but without + the existing hash check */ +static void copy_grant_table_hash_columns(HASH *target, HASH *source) +{ + + MEM_ROOT *memex_ptr= &memex; + for (uint i=0 ; i < source->records ; i++) + { + GRANT_COLUMN *source_col = (GRANT_COLUMN *)my_hash_element(source, i); + GRANT_COLUMN *target_col = new (memex_ptr) GRANT_COLUMN(source_col); + my_hash_insert(target, (uchar *)target_col); + } +} + class GRANT_NAME :public Sql_alloc { @@ -3945,6 +3913,7 @@ public: GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c); GRANT_TABLE (TABLE *form, TABLE *col_privs); + GRANT_TABLE(GRANT_TABLE *source, char *u); ~GRANT_TABLE(); bool ok() { return privs != 0 || cols != 0; } }; @@ -3990,6 +3959,21 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, 0,0,0, (my_hash_get_key) get_key_column,0,0); } +/* + create a new GRANT_TABLE entry for role inheritance. init_* fields are set + to 0 +*/ +GRANT_TABLE::GRANT_TABLE(GRANT_TABLE *source, char *u) + :GRANT_NAME("", source->db, u, source->tname, + source->privs, FALSE), cols(source->cols) +{ + this->init_cols= 0; + this->init_privs= 0; + (void) my_hash_init2(&hash_columns,4,system_charset_info, + 0,0,0, (my_hash_get_key) get_key_column,0,0); + copy_grant_table_hash_columns(&hash_columns, &source->hash_columns); +} + GRANT_NAME::GRANT_NAME(TABLE *form, bool is_routine) { @@ -5611,6 +5595,11 @@ my_bool grant_reload(THD *thd) return_val= 1; mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + my_hash_iterate(&acl_roles, + (my_hash_walk_action) acl_role_propagate_grants, NULL); + mysql_mutex_unlock(&acl_cache->lock); + grant_version++; mysql_rwlock_unlock(&LOCK_grant); @@ -6044,24 +6033,27 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, } grant_table= grant->grant_table_user; - DBUG_ASSERT (grant_table); grant_table_role= grant->grant_table_role; + DBUG_ASSERT (grant_table || grant_table_role); } } if (want_access) { - GRANT_COLUMN *grant_column= - column_hash_search(grant_table, field_name, - (uint) strlen(field_name)); - if (grant_column) + if (grant_table) { - using_column_privileges= TRUE; - want_access&= ~grant_column->rights; + GRANT_COLUMN *grant_column= + column_hash_search(grant_table, field_name, + (uint) strlen(field_name)); + if (grant_column) + { + using_column_privileges= TRUE; + want_access&= ~grant_column->rights; + } } if (grant_table_role) { - grant_column= + GRANT_COLUMN *grant_column= column_hash_search(grant_table_role, field_name, (uint) strlen(field_name)); if (grant_column) @@ -6178,7 +6170,7 @@ bool check_grant_db(THD *thd, const char *db) } if (sctx->priv_role[0] && len2 < grant_table->key_length && - !memcmp(grant_table->hash_key,helping,len) && + !memcmp(grant_table->hash_key,helping2,len) && (!grant_table->host.hostname || !grant_table->host.hostname[0])) { error= FALSE; /* Found role match */ @@ -7401,10 +7393,129 @@ static int modify_grant_table(TABLE *table, Field *host_field, DBUG_RETURN(error); } +/* + The function merges access bits from a granted role to a grantee. + + It creates data structures if they don't exist for the grantee. + This includes data structures related to database privileges, tables + privileges, column privileges, function and procedures privileges +*/ + +void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) +{ + DBUG_ASSERT(source->flags & ROLE_GRANTS_FINAL); + + /* Merge global access rights */ + target->access|= source->access; + + /* Merge database privileges */ + DYNAMIC_ARRAY target_objs, source_objs; /* arrays of pointers to priv objects */ + + /* elements of the arrays */ + ACL_DB *target_db, *source_db; + GRANT_TABLE *target_table, *source_table; + MEM_ROOT *memex_ptr = &memex; + + (void) my_init_dynamic_array(&target_objs,sizeof(void *), 50, 100, MYF(0)); + (void) my_init_dynamic_array(&source_objs,sizeof(void *), 50, 100, MYF(0)); + + /* get all acl_db elements for the source and the target */ + for (uint i=0 ; i < acl_dbs.elements ; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs,i,ACL_DB*); + if (acl_db->user && (!acl_db->host.hostname || !acl_db->host.hostname[0])) + { + if (!strcmp(target->user.str, acl_db->user)) + push_dynamic(&target_objs, (uchar*)&acl_db); + if (!strcmp(source->user.str, acl_db->user)) + push_dynamic(&source_objs, (uchar*)&acl_db); + } + } + + for (uint i=0 ; i < source_objs.elements; i++) + { + source_db= (ACL_DB *)*dynamic_element(&source_objs, i, void **); + target_db= NULL; + for (uint j=0; j < target_objs.elements; j++) + { + ACL_DB *acl_db= (ACL_DB *)*dynamic_element(&target_objs, i, void **); + if (!strcmp(source_db->db, acl_db->db)) /* only need to compare DB here */ + target_db= acl_db; + } + + /* acl_db element found for the target, only need to update acces bits */ + if (target_db) + { + target_db->access|= source_db->access; + } + else + { + /* + Need to create an acl_db element as this role inherits database + privileges + */ + acl_insert_db(target->user.str, "", source_db->db, + source_db->access, FALSE); + } + } + + reset_dynamic(&source_objs); + reset_dynamic(&target_objs); + + /* Merge table and column privileges */ + for (uint i=0 ; i < column_priv_hash.records ; i++) + { + GRANT_TABLE *grant_table= (GRANT_TABLE *) + my_hash_element(&column_priv_hash, i); + if (grant_table->user && (!grant_table->host.hostname || + !grant_table->host.hostname[0])) + { + if (!strcmp(target->user.str, grant_table->user)) + push_dynamic(&target_objs, (uchar*)&grant_table); + if (!strcmp(source->user.str, grant_table->user)) + push_dynamic(&source_objs, (uchar*)&grant_table); + } + } + + + for (uint i=0 ; i < source_objs.elements; i++) + { + source_table= (GRANT_TABLE *)*dynamic_element(&source_objs, i, void **); + target_table= NULL; + for (uint j=0; j < target_objs.elements; j++) + { + GRANT_TABLE *grant_table= (GRANT_TABLE *)*dynamic_element(&target_objs, i, + void **); + if (!strcmp(source_table->db, grant_table->db) && + !strcmp(source_table->tname, grant_table->tname)) + target_table= grant_table;; + } + + if (target_table) + { + target_table->privs|= source_table->privs; + target_table->cols|= source_table->cols; + merge_grant_table_hash_columns(&target_table->hash_columns, + &source_table->hash_columns); + } + else + { + target_table= new (memex_ptr) GRANT_TABLE(source_table, target->user.str); + my_hash_insert(&column_priv_hash, (uchar *) target_table); + } + + } + + /* Merge function and procedure privileges */ + /* TODO */ + + /* cleanup */ + delete_dynamic(&source_objs); + delete_dynamic(&target_objs); +} + /* Handle the roles_mappings privilege table - - */ static int handle_roles_mappings_table(TABLE *table, bool drop, LEX_USER *user_from, LEX_USER *user_to) From 95ef78e432e66f9b851a81a6878b11f9cc55532f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:49:38 -0700 Subject: [PATCH 115/174] SET ROLE now works recursively for routines. The warnings present in the set_role_routine-simple testcase will be removed when reworking the grant privilege to call. --- .../acl_roles_set_role-routine-simple.result | 100 ++++++++++++++++++ .../t/acl_roles_set_role-routine-simple.test | 84 +++++++++++++++ sql/sql_acl.cc | 60 ++++++++++- 3 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 mysql-test/r/acl_roles_set_role-routine-simple.result create mode 100644 mysql-test/t/acl_roles_set_role-routine-simple.test diff --git a/mysql-test/r/acl_roles_set_role-routine-simple.result b/mysql-test/r/acl_roles_set_role-routine-simple.result new file mode 100644 index 00000000000..5933a3e174d --- /dev/null +++ b/mysql-test/r/acl_roles_set_role-routine-simple.result @@ -0,0 +1,100 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +create role test_role3; +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', +'test_user', +'test_role3'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', +'test_role1', +'test_role2'); +select user, host from mysql.user where user not like 'root'; +user host +test_role1 +test_role2 +test_role3 +test_user localhost +select * from mysql.roles_mapping; +HostFk UserFk RoleFk + test_role1 test_role2 +localhost test_user test_role1 +localhost test_user test_role3 +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); +create procedure mysql.test_proc (OUT param1 INT) +begin +select COUNT(*) into param1 from mysql.roles_mapping; +end| +grant execute on function mysql.test_func to test_role2@''; +grant execute on procedure mysql.test_proc to test_role2@''; +grant execute on mysql.* to test_role3@''; +flush privileges; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +use mysql; +ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +set role test_role1; +use mysql; +call test_proc(@a); +SELECT @a; +@a +3 +SELECT test_func('AABBCCDD'); +test_func('AABBCCDD') +Test string: AABBCCDD +show grants; +Grants for test_user@localhost +GRANT EXECUTE ON FUNCTION `mysql`.`test_func` TO 'test_role2' +GRANT EXECUTE ON PROCEDURE `mysql`.`test_proc` TO 'test_role2' +GRANT USAGE ON *.* TO 'test_role1' +GRANT USAGE ON *.* TO 'test_role2' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role2 TO 'test_role1' +GRANT test_role3 TO 'test_user'@'localhost' +set role none; +show grants; +Grants for test_user@localhost +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +call test_proc(@a); +ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_proc' +SELECT test_func('AABBCCDD'); +ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_func' +set role test_role3; +show grants; +Grants for test_user@localhost +GRANT EXECUTE ON `mysql`.* TO 'test_role3' +GRANT USAGE ON *.* TO 'test_role3' +GRANT USAGE ON *.* TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT test_role3 TO 'test_user'@'localhost' +call test_proc(@a); +SELECT @a; +@a +3 +SELECT test_func('AABBCCDD'); +test_func('AABBCCDD') +Test string: AABBCCDD +drop user 'test_user'@'localhost'; +revoke execute on function mysql.test_func from test_role2@''; +revoke execute on procedure mysql.test_proc from test_role2@''; +revoke execute on mysql.* from test_role3@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +drop function mysql.test_func; +Warnings: +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_func' +drop procedure mysql.test_proc; +Warnings: +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' +Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' +flush privileges; diff --git a/mysql-test/t/acl_roles_set_role-routine-simple.test b/mysql-test/t/acl_roles_set_role-routine-simple.test new file mode 100644 index 00000000000..e731c4dee10 --- /dev/null +++ b/mysql-test/t/acl_roles_set_role-routine-simple.test @@ -0,0 +1,84 @@ +create user 'test_user'@'localhost'; +create role test_role1; +create role test_role2; +create role test_role3; + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role1'); +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', + 'test_user', + 'test_role3'); + +insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', + 'test_role1', + 'test_role2'); +--sorted_result +select user, host from mysql.user where user not like 'root'; +--sorted_result +select * from mysql.roles_mapping; + +create function mysql.test_func (s CHAR(20)) +returns CHAR(50) DETERMINISTIC +return concat('Test string: ',s); + + +delimiter |; +create procedure mysql.test_proc (OUT param1 INT) +begin + select COUNT(*) into param1 from mysql.roles_mapping; +end| +delimiter ;| + + +grant execute on function mysql.test_func to test_role2@''; +grant execute on procedure mysql.test_proc to test_role2@''; + +grant execute on mysql.* to test_role3@''; + +flush privileges; + +change_user 'test_user'; +--sorted_result +show grants; + +--error ER_DBACCESS_DENIED_ERROR +use mysql; +set role test_role1; +use mysql; + +call test_proc(@a); +SELECT @a; + +SELECT test_func('AABBCCDD'); + +--sorted_result +show grants; +set role none; +--sorted_result +show grants; + +--error ER_PROCACCESS_DENIED_ERROR +call test_proc(@a); + +--error ER_PROCACCESS_DENIED_ERROR +SELECT test_func('AABBCCDD'); + +set role test_role3; +--sorted_result +show grants; +call test_proc(@a); +SELECT @a; + +SELECT test_func('AABBCCDD'); + +change_user 'root'; +drop user 'test_user'@'localhost'; +revoke execute on function mysql.test_func from test_role2@''; +revoke execute on procedure mysql.test_proc from test_role2@''; +revoke execute on mysql.* from test_role3@''; +delete from mysql.user where user like'test_%'; +delete from mysql.roles_mapping where RoleFk like 'test%'; +drop function mysql.test_func; +drop procedure mysql.test_proc; +flush privileges; diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 69e619c01b5..700e2d38783 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3895,6 +3895,8 @@ public: GRANT_NAME(const char *h, const char *d,const char *u, const char *t, ulong p, bool is_routine); GRANT_NAME (TABLE *form, bool is_routine); + /* copy from an inherited GRANT_NAME */ + GRANT_NAME(GRANT_NAME *source, char *u, bool is_routine); virtual ~GRANT_NAME() {}; virtual bool ok() { return privs != 0; } void set_user_details(const char *h, const char *d, @@ -3951,6 +3953,12 @@ GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, set_user_details(h, d, u, t, is_routine); } +GRANT_NAME::GRANT_NAME(GRANT_NAME *source, char *u, bool is_routine) + :db(0), tname(0), privs(source->privs), init_privs(0) +{ + set_user_details("", source->db, u, source->tname, is_routine); +} + GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, const char *t, ulong p, ulong c) :GRANT_NAME(h,d,u,t,p, FALSE), cols(c) @@ -7414,6 +7422,7 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) /* elements of the arrays */ ACL_DB *target_db, *source_db; GRANT_TABLE *target_table, *source_table; + GRANT_NAME *target_name, *source_name; MEM_ROOT *memex_ptr = &memex; (void) my_init_dynamic_array(&target_objs,sizeof(void *), 50, 100, MYF(0)); @@ -7488,7 +7497,7 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) void **); if (!strcmp(source_table->db, grant_table->db) && !strcmp(source_table->tname, grant_table->tname)) - target_table= grant_table;; + target_table= grant_table; } if (target_table) @@ -7506,8 +7515,55 @@ void merge_role_grant_privileges(ACL_ROLE *target, ACL_ROLE *source) } + reset_dynamic(&source_objs); + reset_dynamic(&target_objs); + /* Merge function and procedure privileges */ - /* TODO */ + for (uint is_proc= 0; is_proc < 2; is_proc++) { + HASH *hash; + if (is_proc) + hash= &proc_priv_hash; + else + hash= &func_priv_hash; + + for (uint i=0 ; i < hash->records ; i++) + { + GRANT_NAME *grant_name= (GRANT_NAME *) my_hash_element(hash, i); + if (grant_name->user && (!grant_name->host.hostname || + !grant_name->host.hostname[0])) + { + if (!strcmp(target->user.str, grant_name->user)) + push_dynamic(&target_objs, (uchar*)&grant_name); + if (!strcmp(source->user.str, grant_name->user)) + push_dynamic(&source_objs, (uchar*)&grant_name); + } + } + + for (uint i=0 ; i < source_objs.elements; i++) + { + source_name= (GRANT_NAME *)*dynamic_element(&source_objs, i, void **); + target_name= NULL; + for (uint j=0; j < target_objs.elements; j++) + { + GRANT_NAME *grant_name= (GRANT_NAME *)*dynamic_element(&target_objs, i, + void **); + if (!strcmp(source_name->db, grant_name->db) && + !strcmp(source_name->tname, grant_name->tname)) + target_name= grant_name; + } + + if (target_name) + { + target_name->privs|= source_name->privs; + } + else + { + target_name= new (memex_ptr) GRANT_NAME(source_name, target->user.str, + is_proc); + my_hash_insert(hash, (uchar *) target_name); + } + } + } /* cleanup */ delete_dynamic(&source_objs); From 1ac0b920d572ec393a2b482b6fa0686a6708abdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vicen=C8=9Biu=20Ciorbaru?= Date: Fri, 18 Oct 2013 06:49:53 -0700 Subject: [PATCH 116/174] Added GRANT ROLE TO ROLE | USER functionality. The command only currenty affects in memory data structures. Writing to the roles_mapping table needs to be implemented. --- sql/share/errmsg-utf8.txt | 6 + sql/sql_acl.cc | 309 ++++++++++++++++++++++++++++++++++++-- sql/sql_acl.h | 2 + sql/sql_parse.cc | 17 ++- sql/sql_yacc.yy | 42 +++++- 5 files changed, 350 insertions(+), 26 deletions(-) diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index a6e3a74df0a..9b937c67316 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -6569,3 +6569,9 @@ ER_INVALID_ROLE ER_INVALID_CURRENT_USER eng "The current user is invalid." rum "Utilizatorul curent este invalid." +ER_RESERVED_ROLE + eng "Role name '%s' is reserved." + rum "Numele de rol '%s' este rezervat." +ER_CANNOT_GRANT_ROLE + eng "Cannot grant role '%s' to: %s." + rum "Rolul '%s' nu poate fi acordat catre: %s." diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 700e2d38783..9716f7c251c 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -742,6 +742,9 @@ static my_bool acl_role_propagate_grants(ACL_ROLE *role, void * not_used __attribute__((unused))); static int add_role_user_mapping(ROLE_GRANT_PAIR *mapping); +static void reset_role_db_privileges(ACL_ROLE *role); +static void reset_role_table_and_column_privileges(ACL_ROLE *role); +static void reset_role_routine_grant_privileges(ACL_ROLE *role); static void role_explore_create_list(ACL_ROLE *unused, ACL_ROLE *role, void *context_data); @@ -751,6 +754,9 @@ static bool role_explore_merge_if_final(ACL_ROLE *current, ACL_ROLE *neighbour, static void role_explore_set_final_access_bits(ACL_ROLE *parent, ACL_ROLE *current, void *unused); +static bool role_explore_detect_cycle(ACL_ROLE *current, + ACL_ROLE *neighbour, + void *context_data); static int traverse_role_graph(ACL_ROLE *role, void *context_data, bool (*on_start) (ACL_ROLE *role, @@ -1926,17 +1932,11 @@ static uchar* check_get_key(ACL_USER *buff, size_t *length, return (uchar*) buff->host.hostname; } -static void acl_update_role(const char *rolename, - ulong privileges) -{ - ACL_ROLE *role; - mysql_mutex_assert_owner(&acl_cache->lock); - role= find_acl_role(rolename); - if (!role) - { - return; - } +static void acl_update_role_entry(ACL_ROLE *role, ulong privileges) +{ + + mysql_mutex_assert_owner(&acl_cache->lock); /* Changing privileges of a role causes all other roles that had @@ -1993,6 +1993,20 @@ static void acl_update_role(const char *rolename, } } + +static void acl_update_role(const char *rolename, + ulong privileges) +{ + mysql_mutex_assert_owner(&acl_cache->lock); + ACL_ROLE *role= find_acl_role(rolename); + if (!role) + { + return; + } + acl_update_role_entry(role, privileges); +} + + static void acl_update_user(const char *user, const char *host, const char *password, uint password_len, enum SSL_type ssl_type, @@ -2006,10 +2020,14 @@ static void acl_update_user(const char *user, const char *host, { mysql_mutex_assert_owner(&acl_cache->lock); - if (host[0] == '\0' && find_acl_role(user)) + if (host[0] == '\0') { - acl_update_role(user, privileges); - return; + ACL_ROLE *acl_role= find_acl_role(user); + if (acl_role) + { + acl_update_role_entry(acl_role, privileges); + return; + } } for (uint i=0 ; i < acl_users.elements ; i++) @@ -2071,6 +2089,12 @@ static void acl_insert_role(const char *rolename, ulong privileges) mysql_mutex_assert_owner(&acl_cache->lock); entry= new (&mem) ACL_ROLE(rolename, privileges, &mem); + (void) my_init_dynamic_array(&entry->parent_grantee, + sizeof(ACL_USER_BASE *), 50, 100, MYF(0)); + (void) my_init_dynamic_array(&entry->role_grants,sizeof(ACL_ROLE *), + 50, 100, MYF(0)); + + my_hash_insert(&acl_roles, (uchar *)entry); } @@ -2416,6 +2440,7 @@ my_bool acl_user_reset_grant(ACL_USER *user, return 0; } + static void role_explore_create_list(ACL_ROLE *unused __attribute__((unused)), ACL_ROLE *role, void *context_data) { @@ -2433,6 +2458,16 @@ static bool role_explore_start_access_check(ACL_ROLE *role, */ if (role->flags & ROLE_GRANTS_FINAL) return TRUE; + /* + This function is called when the node is first opened by DFS. + If it's ROLE_GRANTS were not final, then it means that it's existing + privilege entries should be placed on their initial grant access state. + */ + + reset_role_db_privileges(role); + reset_role_table_and_column_privileges(role); + reset_role_routine_grant_privileges(role); + return FALSE; } @@ -2464,6 +2499,13 @@ static void role_explore_set_final_access_bits(ACL_ROLE *parent, } } +static bool role_explore_detect_cycle(ACL_ROLE *unused __attribute__((unused)), + ACL_ROLE *unused2 __attribute__((unused)), + void *unused3 __attribute__((unused))) +{ + return TRUE; +} + /* The function scans through all roles granted to the role passed as argument and places the permissions in the access variable. The traverse method is @@ -2560,7 +2602,6 @@ static int traverse_role_graph(ACL_ROLE *role, if (neighbour->flags & ROLE_VISITED) { DBUG_PRINT("info", ("Found cycle")); - /* TODO the edge needs to be ignored */ if (on_cycle && on_cycle(current, neighbour, context_data)) { result= 2; @@ -5102,6 +5143,155 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, DBUG_RETURN(result); } +static void append_user(String *str, const char *u, const char *h, + bool handle_as_role) +{ + if (str->length()) + str->append(','); + str->append('\''); + str->append(u); + /* hostname part is not relevant for roles, it is always empty */ + if (!handle_as_role) + { + str->append(STRING_WITH_LEN("'@'")); + str->append(h); + } + str->append('\''); +} + +bool mysql_grant_role(THD *thd, List &list) +{ + DBUG_ENTER("mysql_grant_role"); + /* + The first entry in the list is the granted role. Need at least two + entries for the command to be valid + */ + DBUG_ASSERT(list.elements >= 2); + bool result= 0; + String wrong_users; + LEX_USER *user, *granted_role; + char *rolename; + char *username; + char *hostname; + bool handle_as_role; + ACL_ROLE *role, *role_as_user; + + List_iterator user_list(list); + granted_role= user_list++; + if (granted_role == ¤t_role) + { + rolename= thd->security_ctx->priv_role; + if (!rolename[0]) + { + my_error(ER_RESERVED_ROLE, MYF(0), "NONE"); + DBUG_RETURN(TRUE); + } + } + else + { + rolename= granted_role->user.str; + } + + mysql_rwlock_wrlock(&LOCK_grant); + mysql_mutex_lock(&acl_cache->lock); + if (!(role= find_acl_role(rolename))) + { + my_error(ER_INVALID_ROLE, MYF(0), rolename); + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); + DBUG_RETURN(TRUE); + } + + while ((user= user_list++)) + { + handle_as_role= FALSE; + /* current_role is treated slightly different */ + if (user == ¤t_role) + { + handle_as_role= TRUE; + /* current_role is NONE */ + if (!thd->security_ctx->priv_role[0]) + { + append_user(&wrong_users, "NONE", "", TRUE); + result= 1; + continue; + } + if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role))) + { + append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE); + result= 1; + continue; + } + /* can not grant current_role to current_role */ + if (granted_role == ¤t_role) + { + append_user(&wrong_users, thd->security_ctx->priv_role, "", TRUE); + result= 1; + continue; + } + username= thd->security_ctx->priv_role; + hostname= (char *)""; + } + else + { + username= user->user.str; + hostname= user->host.str; + if (hostname == HOST_NOT_SPECIFIED) + { + if ((role_as_user= find_acl_role(username))) + { + handle_as_role= TRUE; + hostname= (char *)""; + } + } + } + + ROLE_GRANT_PAIR *mapping= (ROLE_GRANT_PAIR *) + alloc_root(&mem, + sizeof(ROLE_GRANT_PAIR)); + + /* TODO write into roles_mapping table */ + init_role_grant_pair(&mem, mapping, + username, hostname, rolename); + int res= add_role_user_mapping(mapping); + if (res == -1) + { + append_user(&wrong_users, username, hostname, handle_as_role); + result= 1; + continue; + } + + /* + Check if this grant would cause a cycle. It only needs to be run + if we're granting a role to a role + */ + if (handle_as_role && + traverse_role_graph(role, NULL, NULL, NULL, role_explore_detect_cycle, + NULL) == 2) + { + append_user(&wrong_users, username, hostname, TRUE); + result= 1; + continue; + } + + /* only need to propagate grants when granting a role to a role */ + if (handle_as_role) + { + acl_update_role_entry(role_as_user, role_as_user->initial_role_access); + } + } + mysql_mutex_unlock(&acl_cache->lock); + mysql_rwlock_unlock(&LOCK_grant); + + if (result) + my_error(ER_CANNOT_GRANT_ROLE, MYF(0), + rolename, + wrong_users.c_ptr_safe()); + + + DBUG_RETURN(result); +} + bool mysql_grant(THD *thd, const char *db, List &list, ulong rights, bool revoke_grant, bool is_proxy) @@ -7187,6 +7377,96 @@ static int show_routine_grants(THD* thd, return error; } +static void reset_role_db_privileges(ACL_ROLE *role) +{ + char *rolename= role->user.str; + for (uint i=0 ; i < acl_dbs.elements; i++) + { + ACL_DB *acl_db= dynamic_element(&acl_dbs,i,ACL_DB*); + if (acl_db->user && (!acl_db->host.hostname || !acl_db->host.hostname[0]) + && (!strcmp(rolename, acl_db->user))) + { + acl_db->access= acl_db->initial_access; + } + /* this is only an inherited entry that needs to be removed */ + if (!acl_db->access) + { + delete_dynamic_element(&acl_dbs, i); + i--; + } + } +} + +static void reset_role_table_and_column_privileges(ACL_ROLE *role) +{ + char *rolename= role->user.str; + for (uint i=0 ; i < column_priv_hash.records ; i++) + { + GRANT_TABLE *grant_table= (GRANT_TABLE *) + my_hash_element(&column_priv_hash, i); + if (grant_table->user && (!grant_table->host.hostname || + !grant_table->host.hostname[0]) && + !strcmp(rolename, grant_table->user)) + { + grant_table->privs= grant_table->init_privs; + grant_table->cols= grant_table->init_cols; + if (grant_table->privs | grant_table->cols) + { + for (uint j=0 ; j < grant_table->hash_columns.records ; j++) + { + GRANT_COLUMN *grant_column= (GRANT_COLUMN *) + my_hash_element(&grant_table->hash_columns, j); + if (grant_column->init_rights == 0) + { + my_hash_delete(&grant_table->hash_columns, (uchar *)grant_column); + j--; + } + else + { + grant_column->rights= grant_column->init_rights; + } + } + } + else + { + /* delete the record altogether as we have no privileges left */ + my_hash_delete(&column_priv_hash, (uchar *)grant_table); + i--; + } + } + } +} + +static void reset_role_routine_grant_privileges(ACL_ROLE *role) +{ + char *rolename= role->user.str; + for (uint is_proc= 0; is_proc < 2; is_proc++) { + HASH *hash; + if (is_proc) + hash= &proc_priv_hash; + else + hash= &func_priv_hash; + + for (uint i=0 ; i < hash->records ; i++) + { + GRANT_NAME *grant_name= (GRANT_NAME *) my_hash_element(hash, i); + if (grant_name->user && (!grant_name->host.hostname || + !grant_name->host.hostname[0]) && + !strcmp(rolename, grant_name->user)) + { + if (grant_name->init_privs == 0) + { + my_hash_delete(hash, (uchar *)grant_name); + i--; + } + else + { + grant_name->privs= grant_name->init_privs; + } + } + } + } +} /* Make a clear-text version of the requested privilege. */ @@ -8322,7 +8602,6 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, DBUG_RETURN(result); } - static void append_user(String *str, LEX_USER *user, bool handle_as_role) { if (str->length()) diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 1340d6fc2e7..8a2054cee90 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -203,6 +203,8 @@ int check_change_password(THD *thd, const char *host, const char *user, char *password, uint password_len); bool change_password(THD *thd, const char *host, const char *user, char *password); + +bool mysql_grant_role(THD *thd, List &user_list); bool mysql_grant(THD *thd, const char *db, List &user_list, ulong rights, bool revoke, bool is_proxy); int mysql_table_grant(THD *thd, TABLE_LIST *table, List &user_list, diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d30d476543d..9d17d9c3e31 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3866,9 +3866,9 @@ end_with_restore_list: else { /* Conditionally writes to binlog */ - res = mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, - lex->sql_command == SQLCOM_REVOKE, - lex->type == TYPE_ENUM_PROXY); + res= mysql_grant(thd, select_lex->db, lex->users_list, lex->grant, + lex->sql_command == SQLCOM_REVOKE, + lex->type == TYPE_ENUM_PROXY); } if (!res) { @@ -3890,8 +3890,15 @@ end_with_restore_list: case SQLCOM_REVOKE_ROLE: case SQLCOM_GRANT_ROLE: { - /* TODO Implement grant */ - my_ok(thd); + /* TODO access check */ + + if (thd->security_ctx->user) // If not replication + { + if (!(res= mysql_grant_role(thd, lex->users_list))) + my_ok(thd); + } + else + my_ok(thd); break; } #endif /*!NO_EMBEDDED_ACCESS_CHECKS*/ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 92770c6aacd..38f9709f180 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1570,7 +1570,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type keyword keyword_sp -%type user grant_user grant_role +%type user grant_user grant_role user_or_role %type opt_collate @@ -1624,7 +1624,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges grant_ident grant_list grant_option - object_privilege object_privilege_list user_list rename_list + object_privilege object_privilege_list user_list user_and_role_list + rename_list clear_privileges flush_options flush_option opt_with_read_lock flush_options_list equal optional_braces @@ -13208,6 +13209,16 @@ user: } ; +user_or_role: + user + { + $$=$1; + } + | CURRENT_ROLE optional_braces + { + $$= ¤t_role; + } + /* Keyword that we allow for identifiers (except SP labels) */ keyword: keyword_sp {} @@ -14240,8 +14251,8 @@ revoke_command: lex->users_list.push_front ($3); lex->sql_command= SQLCOM_REVOKE; lex->type= TYPE_ENUM_PROXY; - } - | grant_role FROM grant_list + } + | grant_role FROM user_and_role_list { LEX *lex= Lex; lex->sql_command= SQLCOM_REVOKE_ROLE; @@ -14294,11 +14305,13 @@ grant_command: lex->sql_command= SQLCOM_GRANT; lex->type= TYPE_ENUM_PROXY; } - | grant_role TO_SYM grant_list + | grant_role TO_SYM user_and_role_list { LEX *lex= Lex; lex->sql_command= SQLCOM_GRANT_ROLE; - lex->type= 0; + /* The first role is the one that is granted */ + if (Lex->users_list.push_front($1)) + MYSQL_YYABORT; } ; @@ -14333,6 +14346,10 @@ grant_role: system_charset_info, 0)) MYSQL_YYABORT; } + | CURRENT_ROLE optional_braces + { + $$=¤t_role; + } ; opt_table: @@ -14522,6 +14539,19 @@ grant_list: } ; +user_and_role_list: + user_or_role + { + if (Lex->users_list.push_back($1)) + MYSQL_YYABORT; + } + | user_and_role_list ',' user_or_role + { + if (Lex->users_list.push_back($3)) + MYSQL_YYABORT; + } + ; + via_or_with: VIA_SYM | WITH ; using_or_as: USING | AS ; From 8122996a599fcb6dc600f27fddbed47a2579c6b8 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 18 Oct 2013 06:55:26 -0700 Subject: [PATCH 117/174] CURRENT_ROLE() function --- ...l_roles_set_role-database-recursive.result | 12 +++++++ .../acl_roles_set_role-database-simple.result | 9 +++++ .../r/acl_roles_set_role-multiple-role.result | 33 +++++++++++++++++++ .../r/acl_roles_set_role-recursive.result | 18 ++++++++++ .../acl_roles_set_role-routine-simple.result | 15 +++++++-- mysql-test/r/acl_roles_set_role-simple.result | 9 +++++ ...cl_roles_set_role-table-column-priv.result | 9 +++++ .../r/acl_roles_set_role-table-simple.result | 9 +++++ mysql-test/r/acl_roles_show_grants.result | 12 +++++++ ...acl_roles_set_role-database-recursive.test | 4 +++ .../t/acl_roles_set_role-database-simple.test | 3 ++ .../t/acl_roles_set_role-multiple-role.test | 11 +++++++ .../t/acl_roles_set_role-recursive.test | 6 ++++ .../t/acl_roles_set_role-routine-simple.test | 4 +++ mysql-test/t/acl_roles_set_role-simple.test | 3 ++ .../acl_roles_set_role-table-column-priv.test | 3 ++ .../t/acl_roles_set_role-table-simple.test | 3 ++ mysql-test/t/acl_roles_show_grants.test | 4 +++ sql/item_strfunc.cc | 26 +++++++++++---- sql/item_strfunc.h | 22 +++++++++++++ sql/sql_yacc.yy | 8 +++++ 21 files changed, 213 insertions(+), 10 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-database-recursive.result b/mysql-test/r/acl_roles_set_role-database-recursive.result index cbfb50cdf5a..f6d3418f9b6 100644 --- a/mysql-test/r/acl_roles_set_role-database-recursive.result +++ b/mysql-test/r/acl_roles_set_role-database-recursive.result @@ -31,16 +31,28 @@ grant select on mysql.* to test_role2@''; flush privileges; select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 select * from mysql.roles_mapping; HostFk UserFk RoleFk test_role1 test_role2 localhost test_user test_role1 localhost test_user test_role2 set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' set role test_role2; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role2 select * from mysql.roles_mapping; HostFk UserFk RoleFk test_role1 test_role2 diff --git a/mysql-test/r/acl_roles_set_role-database-simple.result b/mysql-test/r/acl_roles_set_role-database-simple.result index 758bc7340f2..a243b11fc33 100644 --- a/mysql-test/r/acl_roles_set_role-database-simple.result +++ b/mysql-test/r/acl_roles_set_role-database-simple.result @@ -17,7 +17,13 @@ grant insert, delete on mysql.roles_mapping to test_role1@''; grant reload on *.* to test_role1@''; select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 select * from mysql.roles_mapping; HostFk UserFk RoleFk localhost test_user test_role1 @@ -29,6 +35,9 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('localhost', delete from mysql.roles_mapping where RoleFk='test_role2'; use mysql; set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE use mysql; ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' select * from mysql.roles_mapping; diff --git a/mysql-test/r/acl_roles_set_role-multiple-role.result b/mysql-test/r/acl_roles_set_role-multiple-role.result index e795ecbf27b..0aa105ec0d4 100644 --- a/mysql-test/r/acl_roles_set_role-multiple-role.result +++ b/mysql-test/r/acl_roles_set_role-multiple-role.result @@ -69,7 +69,13 @@ GRANT r_ins TO 'test_user'@'localhost' GRANT r_rld TO 'test_user'@'localhost' GRANT r_sel TO 'test_user'@'localhost' GRANT r_upd TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role r_sel; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_sel show grants; Grants for test_user@localhost GRANT SELECT ON *.* TO 'r_sel' @@ -91,6 +97,9 @@ localhost test_user r_rld localhost test_user r_sel localhost test_user r_upd set role r_ins; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_ins show grants; Grants for test_user@localhost GRANT INSERT ON *.* TO 'r_ins' @@ -110,13 +119,25 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', flush privileges; ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation set role r_rld; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_rld flush privileges; set role r_sel; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_sel flush privileges; set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE flush privileges; ERROR 42000: Access denied; you need (at least one of) the RELOAD privilege(s) for this operation set role r_ins; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_ins insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'r_sel', 'r_upd'); @@ -133,11 +154,20 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'r_del', 'r_ins'); set role r_rld; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_rld flush privileges; set role r_sel; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_sel update mysql.roles_mapping set RoleFk='r_ins' where RoleFk='r_ins_wrong'; flush privileges; set role r_sel; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_sel create table mysql.random_test_table (id INT); insert into mysql.random_test_table values (1); select * from mysql.random_test_table; @@ -146,6 +176,9 @@ id delete from mysql.roles_mapping where RoleFk='r_ins'; flush privileges; set role r_sel; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost r_sel insert into mysql.random_test_table values (1); ERROR 42000: INSERT command denied to user 'test_user'@'localhost' for table 'random_test_table' drop table mysql.random_test_table; diff --git a/mysql-test/r/acl_roles_set_role-recursive.result b/mysql-test/r/acl_roles_set_role-recursive.result index 8971cd1bf8a..505e1f5f730 100644 --- a/mysql-test/r/acl_roles_set_role-recursive.result +++ b/mysql-test/r/acl_roles_set_role-recursive.result @@ -35,7 +35,13 @@ show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT ON *.* TO 'test_role2' @@ -54,6 +60,9 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' @@ -66,6 +75,9 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' set role test_role2; ERROR HY000: The role 'test_role2' has not been granted or is invalid. +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' @@ -77,6 +89,9 @@ Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT ON *.* TO 'test_role2' @@ -95,6 +110,9 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' diff --git a/mysql-test/r/acl_roles_set_role-routine-simple.result b/mysql-test/r/acl_roles_set_role-routine-simple.result index 5933a3e174d..29f8abb9995 100644 --- a/mysql-test/r/acl_roles_set_role-routine-simple.result +++ b/mysql-test/r/acl_roles_set_role-routine-simple.result @@ -40,7 +40,13 @@ GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role3 TO 'test_user'@'localhost' use mysql; ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 use mysql; call test_proc(@a); SELECT @a; @@ -60,6 +66,9 @@ GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' GRANT test_role3 TO 'test_user'@'localhost' set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' @@ -70,6 +79,9 @@ ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine SELECT test_func('AABBCCDD'); ERROR 42000: execute command denied to user 'test_user'@'localhost' for routine 'mysql.test_func' set role test_role3; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role3 show grants; Grants for test_user@localhost GRANT EXECUTE ON `mysql`.* TO 'test_role3' @@ -91,10 +103,7 @@ revoke execute on mysql.* from test_role3@''; delete from mysql.user where user like'test_%'; delete from mysql.roles_mapping where RoleFk like 'test%'; drop function mysql.test_func; -Warnings: -Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_func' drop procedure mysql.test_proc; Warnings: Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' -Warning 1403 There is no such grant defined for user 'test_role1' on host '' on routine 'test_proc' flush privileges; diff --git a/mysql-test/r/acl_roles_set_role-simple.result b/mysql-test/r/acl_roles_set_role-simple.result index db54592333d..aa68052ae18 100644 --- a/mysql-test/r/acl_roles_set_role-simple.result +++ b/mysql-test/r/acl_roles_set_role-simple.result @@ -22,7 +22,13 @@ show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT ON *.* TO 'test_role1' @@ -32,6 +38,9 @@ select * from mysql.roles_mapping; HostFk UserFk RoleFk localhost test_user test_role1 set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' delete from mysql.user where user='test_role1'; diff --git a/mysql-test/r/acl_roles_set_role-table-column-priv.result b/mysql-test/r/acl_roles_set_role-table-column-priv.result index 651a57be36a..bbb964b5d20 100644 --- a/mysql-test/r/acl_roles_set_role-table-column-priv.result +++ b/mysql-test/r/acl_roles_set_role-table-column-priv.result @@ -24,7 +24,13 @@ show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT (RoleFk) ON `mysql`.`roles_mapping` TO 'test_role2' @@ -49,6 +55,9 @@ GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' use mysql; set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE select RoleFk from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' drop user 'test_user'@'localhost'; diff --git a/mysql-test/r/acl_roles_set_role-table-simple.result b/mysql-test/r/acl_roles_set_role-table-simple.result index a009ce75515..02730a61e3e 100644 --- a/mysql-test/r/acl_roles_set_role-table-simple.result +++ b/mysql-test/r/acl_roles_set_role-table-simple.result @@ -24,7 +24,13 @@ show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT ON `mysql`.`roles_mapping` TO 'test_role2' @@ -47,6 +53,9 @@ GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' use mysql; set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE select * from mysql.roles_mapping; ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for table 'roles_mapping' drop user 'test_user'@'localhost'; diff --git a/mysql-test/r/acl_roles_show_grants.result b/mysql-test/r/acl_roles_show_grants.result index 641964bdcee..a28330379b5 100644 --- a/mysql-test/r/acl_roles_show_grants.result +++ b/mysql-test/r/acl_roles_show_grants.result @@ -34,7 +34,13 @@ Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_user'@'localhost' +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE set role test_role1; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role1 show grants; Grants for test_user@localhost GRANT SELECT ON `mysql`.* TO 'test_role2' @@ -45,6 +51,9 @@ GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' GRANT test_role2 TO 'test_user'@'localhost' set role none; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost NONE show grants; Grants for test_user@localhost GRANT USAGE ON *.* TO 'test_user'@'localhost' @@ -71,6 +80,9 @@ ERROR 42000: There is no such grant defined for user 'test_user' on host 'localh show grants for CURRENT_ROLE(); ERROR 42000: There is no such grant defined for user 'test_user' on host 'localhost' set role test_role2; +select current_user(), current_role(); +current_user() current_role() +test_user@localhost test_role2 show grants; Grants for test_user@localhost GRANT SELECT ON `mysql`.* TO 'test_role2' diff --git a/mysql-test/t/acl_roles_set_role-database-recursive.test b/mysql-test/t/acl_roles_set_role-database-recursive.test index c91c7fc5d3f..98b2e76cb08 100644 --- a/mysql-test/t/acl_roles_set_role-database-recursive.test +++ b/mysql-test/t/acl_roles_set_role-database-recursive.test @@ -31,13 +31,17 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result select * from mysql.roles_mapping; set role none; +select current_user(), current_role(); --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; set role test_role2; +select current_user(), current_role(); --sorted_result select * from mysql.roles_mapping; diff --git a/mysql-test/t/acl_roles_set_role-database-simple.test b/mysql-test/t/acl_roles_set_role-database-simple.test index 56237f38949..fdcd4cfceef 100644 --- a/mysql-test/t/acl_roles_set_role-database-simple.test +++ b/mysql-test/t/acl_roles_set_role-database-simple.test @@ -22,7 +22,9 @@ change_user 'test_user'; --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); select * from mysql.roles_mapping; --error ER_TABLEACCESS_DENIED_ERROR insert into mysql.user (user, host) values ('Dummy', 'Dummy'); @@ -34,6 +36,7 @@ delete from mysql.roles_mapping where RoleFk='test_role2'; use mysql; set role none; +select current_user(), current_role(); --error ER_DBACCESS_DENIED_ERROR use mysql; diff --git a/mysql-test/t/acl_roles_set_role-multiple-role.test b/mysql-test/t/acl_roles_set_role-multiple-role.test index 3d5de6bd7fc..ce406301f4e 100644 --- a/mysql-test/t/acl_roles_set_role-multiple-role.test +++ b/mysql-test/t/acl_roles_set_role-multiple-role.test @@ -59,13 +59,16 @@ select * from mysql.roles_mapping; --sorted_result show grants; +select current_user(), current_role(); set role r_sel; +select current_user(), current_role(); --sorted_result show grants; --sorted_result select * from mysql.roles_mapping; set role r_ins; +select current_user(), current_role(); --sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR @@ -76,14 +79,18 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', --error ER_SPECIFIC_ACCESS_DENIED_ERROR flush privileges; set role r_rld; +select current_user(), current_role(); flush privileges; set role r_sel; +select current_user(), current_role(); flush privileges; set role none; +select current_user(), current_role(); --error ER_SPECIFIC_ACCESS_DENIED_ERROR flush privileges; set role r_ins; +select current_user(), current_role(); insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'r_sel', 'r_upd'); @@ -100,11 +107,14 @@ insert into mysql.roles_mapping (HostFk, UserFk, RoleFk) values ('', 'r_del', 'r_ins'); set role r_rld; +select current_user(), current_role(); flush privileges; set role r_sel; +select current_user(), current_role(); update mysql.roles_mapping set RoleFk='r_ins' where RoleFk='r_ins_wrong'; flush privileges; set role r_sel; +select current_user(), current_role(); create table mysql.random_test_table (id INT); insert into mysql.random_test_table values (1); @@ -113,6 +123,7 @@ select * from mysql.random_test_table; delete from mysql.roles_mapping where RoleFk='r_ins'; flush privileges; set role r_sel; +select current_user(), current_role(); --error ER_TABLEACCESS_DENIED_ERROR insert into mysql.random_test_table values (1); drop table mysql.random_test_table; diff --git a/mysql-test/t/acl_roles_set_role-recursive.test b/mysql-test/t/acl_roles_set_role-recursive.test index 0a7ba637c85..b3d09bc8d53 100644 --- a/mysql-test/t/acl_roles_set_role-recursive.test +++ b/mysql-test/t/acl_roles_set_role-recursive.test @@ -35,7 +35,9 @@ select * from mysql.roles_mapping; --sorted_result show grants; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result show grants; select * from mysql.roles_mapping where HostFk=''; @@ -43,6 +45,7 @@ select * from mysql.roles_mapping where HostFk=''; --sorted_result show grants; set role none; +select current_user(), current_role(); --sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR @@ -52,6 +55,7 @@ select * from mysql.roles_mapping; show grants; --error ER_INVALID_ROLE set role test_role2; +select current_user(), current_role(); --sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR @@ -61,6 +65,7 @@ select * from mysql.roles_mapping; --sorted_result show grants; set role test_role1; +select current_user(), current_role(); --sorted_result show grants; --sorted_result @@ -69,6 +74,7 @@ select * from mysql.roles_mapping where HostFk=''; --sorted_result show grants; set role none; +select current_user(), current_role(); --sorted_result show grants; --error ER_TABLEACCESS_DENIED_ERROR diff --git a/mysql-test/t/acl_roles_set_role-routine-simple.test b/mysql-test/t/acl_roles_set_role-routine-simple.test index e731c4dee10..eb4174a9882 100644 --- a/mysql-test/t/acl_roles_set_role-routine-simple.test +++ b/mysql-test/t/acl_roles_set_role-routine-simple.test @@ -44,7 +44,9 @@ show grants; --error ER_DBACCESS_DENIED_ERROR use mysql; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); use mysql; call test_proc(@a); @@ -55,6 +57,7 @@ SELECT test_func('AABBCCDD'); --sorted_result show grants; set role none; +select current_user(), current_role(); --sorted_result show grants; @@ -65,6 +68,7 @@ call test_proc(@a); SELECT test_func('AABBCCDD'); set role test_role3; +select current_user(), current_role(); --sorted_result show grants; call test_proc(@a); diff --git a/mysql-test/t/acl_roles_set_role-simple.test b/mysql-test/t/acl_roles_set_role-simple.test index b09bbc02b97..210623e7d78 100644 --- a/mysql-test/t/acl_roles_set_role-simple.test +++ b/mysql-test/t/acl_roles_set_role-simple.test @@ -23,13 +23,16 @@ select * from mysql.roles_mapping; --sorted_result show grants; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result show grants; --sorted_result select * from mysql.roles_mapping; set role none; +select current_user(), current_role(); --error ER_TABLEACCESS_DENIED_ERROR select * from mysql.roles_mapping; diff --git a/mysql-test/t/acl_roles_set_role-table-column-priv.test b/mysql-test/t/acl_roles_set_role-table-column-priv.test index 3424ac8b7e4..11969758f0c 100644 --- a/mysql-test/t/acl_roles_set_role-table-column-priv.test +++ b/mysql-test/t/acl_roles_set_role-table-column-priv.test @@ -25,7 +25,9 @@ select * from mysql.roles_mapping; --sorted_result show grants; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result show grants; @@ -42,6 +44,7 @@ show grants; use mysql; set role none; +select current_user(), current_role(); --sorted_result --error ER_TABLEACCESS_DENIED_ERROR diff --git a/mysql-test/t/acl_roles_set_role-table-simple.test b/mysql-test/t/acl_roles_set_role-table-simple.test index 00aa5528fe6..0a212178409 100644 --- a/mysql-test/t/acl_roles_set_role-table-simple.test +++ b/mysql-test/t/acl_roles_set_role-table-simple.test @@ -25,7 +25,9 @@ select * from mysql.roles_mapping; --sorted_result show grants; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result show grants; @@ -39,6 +41,7 @@ show grants; use mysql; set role none; +select current_user(), current_role(); --sorted_result --error ER_TABLEACCESS_DENIED_ERROR diff --git a/mysql-test/t/acl_roles_show_grants.test b/mysql-test/t/acl_roles_show_grants.test index 3d8ac80cdc1..38b20e627a2 100644 --- a/mysql-test/t/acl_roles_show_grants.test +++ b/mysql-test/t/acl_roles_show_grants.test @@ -30,10 +30,13 @@ change_user 'test_user'; --sorted_result show grants; +select current_user(), current_role(); set role test_role1; +select current_user(), current_role(); --sorted_result show grants; set role none; +select current_user(), current_role(); --sorted_result show grants; @@ -53,6 +56,7 @@ show grants for CURRENT_ROLE; show grants for CURRENT_ROLE(); set role test_role2; +select current_user(), current_role(); --sorted_result show grants; --sorted_result diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 02d64952459..66fae0bed10 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2332,16 +2332,28 @@ bool Item_func_current_user::fix_fields(THD *thd, Item **ref) if (Item_func_sysconst::fix_fields(thd, ref)) return TRUE; - Security_context *ctx= -#ifndef NO_EMBEDDED_ACCESS_CHECKS - (context->security_ctx - ? context->security_ctx : thd->security_ctx); -#else - thd->security_ctx; -#endif /*NO_EMBEDDED_ACCESS_CHECKS*/ + Security_context *ctx= context->security_ctx + ? context->security_ctx : thd->security_ctx; return init(ctx->priv_user, ctx->priv_host); } +bool Item_func_current_role::fix_fields(THD *thd, Item **ref) +{ + if (Item_func_sysconst::fix_fields(thd, ref)) + return 1; + + Security_context *ctx= context->security_ctx + ? context->security_ctx : thd->security_ctx; + + const char *role= ctx->priv_role[0] ? ctx->priv_role : NONE_ROLE; + + if (str_value.copy(role, strlen(role), system_charset_info)) + return 1; + + str_value.mark_as_const(); + return 0; +} + void Item_func_soundex::fix_length_and_dec() { diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index e09dee18d4f..8d9bda4902e 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -580,6 +580,28 @@ public: }; +class Item_func_current_role :public Item_func_sysconst +{ + Name_resolution_context *context; + +public: + Item_func_current_role(Name_resolution_context *context_arg) + : context(context_arg) {} + bool fix_fields(THD *thd, Item **ref); + void fix_length_and_dec() + { max_length= username_char_length * SYSTEM_CHARSET_MBMAXLEN; } + int save_in_field(Field *field, bool no_conversions) + { return save_str_value_in_field(field, &str_value); } + const char *func_name() const { return "current_role"; } + const char *fully_qualified_func_name() const { return "current_role()"; } + String *val_str(String *) + { + DBUG_ASSERT(fixed == 1); + return (null_value ? 0 : &str_value); + } +}; + + class Item_func_soundex :public Item_str_func { String tmp_value; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 38f9709f180..9ed7e8e8173 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -8648,6 +8648,14 @@ function_call_keyword: Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); Lex->safe_to_cache_query= 0; } + | CURRENT_ROLE optional_braces + { + $$= new (thd->mem_root) Item_func_current_role(Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } | DATE_SYM '(' expr ')' { $$= new (thd->mem_root) Item_date_typecast($3); From 72d8b533cc102aad6be5046a0fe8b8e63ec1e218 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Fri, 18 Oct 2013 08:10:51 -0700 Subject: [PATCH 118/174] Fixes for mysql-test failures mysql-test/r/acl_roles_show_grants.result: one can do SHOW GRANTS for himself mysql-test/t/acl_roles_set_role-table-column-priv.test: correct error message mysql-test/t/acl_roles_show_grants.test: one can SHOW GRANTS for himself sql/sql_acl.cc: bugfixing: * don't assign with && - it can shortcut and the second assignment won't be executed * correct the test in check_grant_all_columns() - want_access should not be modified * sql/sql_cmd.h.OTHER: add new commands at the end sql/sql_db.cc: don't call acl_get() if all privileges are already satisfied (crashes when run with --skip-grants, because acl data stuctures aren't initialized) sql/sql_parse.cc: * test for current_user in get_current_user() * map explicitly specified user@host to current_user --- ...cl_roles_set_role-table-column-priv.result | 2 +- mysql-test/r/acl_roles_show_grants.result | 5 ++- mysql-test/r/connect.result | 3 ++ mysql-test/r/grant.result | 3 ++ mysql-test/r/information_schema.result | 2 + .../r/information_schema_all_engines.result | 2 +- mysql-test/r/log_tables_upgrade.result | 1 + mysql-test/r/mysql_upgrade.result | 7 ++++ mysql-test/r/mysql_upgrade_ssl.result | 1 + mysql-test/r/mysqlcheck.result | 4 ++ mysql-test/r/ps.result | 6 +-- mysql-test/r/system_mysql_db.result | 2 + mysql-test/r/system_mysql_db_fix40123.result | 2 + mysql-test/r/system_mysql_db_fix50030.result | 2 + mysql-test/r/system_mysql_db_fix50117.result | 2 + .../suite/funcs_1/r/is_columns_mysql.result | 8 ++++ .../suite/funcs_1/r/is_tables_mysql.result | 23 +++++++++++ .../suite/funcs_1/r/is_user_privileges.result | 33 ++++++++++++++++ .../acl_roles_set_role-table-column-priv.test | 2 +- mysql-test/t/acl_roles_show_grants.test | 1 - mysql-test/t/system_mysql_db_fix40123.test | 4 +- mysql-test/t/system_mysql_db_fix50030.test | 2 +- mysql-test/t/system_mysql_db_fix50117.test | 2 +- scripts/mysql_system_tables.sql | 6 +-- sql/sql_acl.cc | 38 +++++++++---------- sql/sql_db.cc | 21 +++++----- sql/sql_lex.h | 4 +- sql/sql_parse.cc | 7 +++- 28 files changed, 145 insertions(+), 50 deletions(-) diff --git a/mysql-test/r/acl_roles_set_role-table-column-priv.result b/mysql-test/r/acl_roles_set_role-table-column-priv.result index bbb964b5d20..4f02b3ec246 100644 --- a/mysql-test/r/acl_roles_set_role-table-column-priv.result +++ b/mysql-test/r/acl_roles_set_role-table-column-priv.result @@ -40,7 +40,7 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_role1' select * from mysql.roles_mapping; -ERROR 42000: command denied to user 'test_user'@'localhost' for table 'roles_mapping' +ERROR 42000: SELECT command denied to user 'test_user'@'localhost' for column 'HostFk' in table 'roles_mapping' select RoleFk from mysql.roles_mapping; RoleFk test_role1 diff --git a/mysql-test/r/acl_roles_show_grants.result b/mysql-test/r/acl_roles_show_grants.result index a28330379b5..7ac434385a7 100644 --- a/mysql-test/r/acl_roles_show_grants.result +++ b/mysql-test/r/acl_roles_show_grants.result @@ -60,7 +60,10 @@ GRANT USAGE ON *.* TO 'test_user'@'localhost' GRANT test_role1 TO 'test_user'@'localhost' GRANT test_role2 TO 'test_user'@'localhost' show grants for test_user@localhost; -ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' +Grants for test_user@localhost +GRANT test_role2 TO 'test_user'@'localhost' +GRANT test_role1 TO 'test_user'@'localhost' +GRANT USAGE ON *.* TO 'test_user'@'localhost' show grants for test_role1; ERROR 42000: Access denied for user 'test_user'@'localhost' to database 'mysql' show grants for test_role2; diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index b90e558cfaa..1ac1f89eeac 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -19,6 +19,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -57,6 +58,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -103,6 +105,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index f3910b67dd3..bf67bb5dd6f 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -55,6 +55,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -126,6 +127,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 @@ -173,6 +175,7 @@ max_connections 30 max_user_connections 0 plugin authentication_string +is_role N show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index bf25b1c28ee..eabae78a5f2 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -99,6 +99,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log t1 @@ -706,6 +707,7 @@ max_questions select,insert,update,references max_connections select,insert,update,references max_user_connections select,insert,update,references authentication_string select,insert,update,references +is_role select,insert,update,references use test; create function sub1(i int) returns int return i+1; diff --git a/mysql-test/r/information_schema_all_engines.result b/mysql-test/r/information_schema_all_engines.result index 3e95d9892cb..62f3ee85530 100644 --- a/mysql-test/r/information_schema_all_engines.result +++ b/mysql-test/r/information_schema_all_engines.result @@ -424,4 +424,4 @@ Wildcard: inf_rmation_schema SELECT table_schema, count(*) FROM information_schema.TABLES WHERE table_schema IN ('mysql', 'INFORMATION_SCHEMA', 'test', 'mysqltest') AND table_name<>'ndb_binlog_index' AND table_name<>'ndb_apply_status' GROUP BY TABLE_SCHEMA; table_schema count(*) information_schema 59 -mysql 27 +mysql 28 diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result index abae156ac5e..bae329073a7 100644 --- a/mysql-test/r/log_tables_upgrade.result +++ b/mysql-test/r/log_tables_upgrade.result @@ -37,6 +37,7 @@ mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK mysql.renamed_general_log OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result index be51cf8ca11..e1c7e78de92 100644 --- a/mysql-test/r/mysql_upgrade.result +++ b/mysql-test/r/mysql_upgrade.result @@ -24,6 +24,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -65,6 +66,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -106,6 +108,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -149,6 +152,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -196,6 +200,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -246,6 +251,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -299,6 +305,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK diff --git a/mysql-test/r/mysql_upgrade_ssl.result b/mysql-test/r/mysql_upgrade_ssl.result index 3b84664708c..c8c4252c5c2 100644 --- a/mysql-test/r/mysql_upgrade_ssl.result +++ b/mysql-test/r/mysql_upgrade_ssl.result @@ -26,6 +26,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index b25a9be8872..3f5c08d7294 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -20,6 +20,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -48,6 +49,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -74,6 +76,7 @@ mysql.plugin OK mysql.proc OK mysql.procs_priv OK mysql.proxies_priv OK +mysql.roles_mapping OK mysql.servers OK mysql.table_stats OK mysql.tables_priv OK @@ -100,6 +103,7 @@ mysql.plugin Table is already up to date mysql.proc Table is already up to date mysql.procs_priv Table is already up to date mysql.proxies_priv Table is already up to date +mysql.roles_mapping Table is already up to date mysql.servers Table is already up to date mysql.table_stats Table is already up to date mysql.tables_priv Table is already up to date diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 95217d9716a..4f41151f555 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1201,13 +1201,13 @@ SET @aux= "SELECT COUNT(*) prepare my_stmt from @aux; execute my_stmt; COUNT(*) -42 +43 execute my_stmt; COUNT(*) -42 +43 execute my_stmt; COUNT(*) -42 +43 deallocate prepare my_stmt; drop procedure if exists p1| drop table if exists t1| diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index a92decefbc8..47d831af115 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -18,6 +18,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -126,6 +127,7 @@ user CREATE TABLE `user` ( `max_user_connections` int(11) NOT NULL DEFAULT '0', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, + `is_role` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; diff --git a/mysql-test/r/system_mysql_db_fix40123.result b/mysql-test/r/system_mysql_db_fix40123.result index 04b20c42af3..052e2966f70 100644 --- a/mysql-test/r/system_mysql_db_fix40123.result +++ b/mysql-test/r/system_mysql_db_fix40123.result @@ -18,6 +18,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -124,6 +125,7 @@ user CREATE TABLE `user` ( `max_updates` int(11) unsigned NOT NULL DEFAULT '0', `max_connections` int(11) unsigned NOT NULL DEFAULT '0', `max_user_connections` int(11) NOT NULL DEFAULT '0', + `is_role` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`Host`,`User`) diff --git a/mysql-test/r/system_mysql_db_fix50030.result b/mysql-test/r/system_mysql_db_fix50030.result index 04b20c42af3..052e2966f70 100644 --- a/mysql-test/r/system_mysql_db_fix50030.result +++ b/mysql-test/r/system_mysql_db_fix50030.result @@ -18,6 +18,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -124,6 +125,7 @@ user CREATE TABLE `user` ( `max_updates` int(11) unsigned NOT NULL DEFAULT '0', `max_connections` int(11) unsigned NOT NULL DEFAULT '0', `max_user_connections` int(11) NOT NULL DEFAULT '0', + `is_role` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`Host`,`User`) diff --git a/mysql-test/r/system_mysql_db_fix50117.result b/mysql-test/r/system_mysql_db_fix50117.result index 04b20c42af3..052e2966f70 100644 --- a/mysql-test/r/system_mysql_db_fix50117.result +++ b/mysql-test/r/system_mysql_db_fix50117.result @@ -18,6 +18,7 @@ plugin proc procs_priv proxies_priv +roles_mapping servers slow_log table_stats @@ -124,6 +125,7 @@ user CREATE TABLE `user` ( `max_updates` int(11) unsigned NOT NULL DEFAULT '0', `max_connections` int(11) unsigned NOT NULL DEFAULT '0', `max_user_connections` int(11) NOT NULL DEFAULT '0', + `is_role` enum('N','Y') COLLATE utf8_bin NOT NULL DEFAULT 'N', `plugin` char(64) CHARACTER SET latin1 NOT NULL DEFAULT '', `authentication_string` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`Host`,`User`) diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result index 3e128da635d..f9d414e1433 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result @@ -161,6 +161,9 @@ def mysql proxies_priv Proxied_user 4 NO char 16 48 NULL NULL NULL utf8 utf8_bi def mysql proxies_priv Timestamp 7 CURRENT_TIMESTAMP NO timestamp NULL NULL NULL NULL 0 NULL NULL timestamp on update CURRENT_TIMESTAMP select,insert,update,references def mysql proxies_priv User 2 NO char 16 48 NULL NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references def mysql proxies_priv With_grant 5 0 NO tinyint NULL NULL 3 0 NULL NULL NULL tinyint(1) select,insert,update,references +def mysql roles_mapping HostFk 1 NO char 60 60 NULL NULL NULL latin1 latin1_bin char(60) select,insert,update,references +def mysql roles_mapping RoleFk 3 NO char 16 16 NULL NULL NULL latin1 latin1_bin char(16) select,insert,update,references +def mysql roles_mapping UserFk 2 NO char 16 16 NULL NULL NULL latin1 latin1_bin char(16) select,insert,update,references def mysql servers Db 3 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Host 2 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Owner 9 NO char 64 192 NULL NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references @@ -224,6 +227,7 @@ def mysql user Grant_priv 14 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci e def mysql user Host 1 NO char 60 180 NULL NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references def mysql user Index_priv 16 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Insert_priv 5 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references +def mysql user is_role 43 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Lock_tables_priv 21 N NO enum 1 3 NULL NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user max_connections 39 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned select,insert,update,references def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL NULL int(11) unsigned select,insert,update,references @@ -480,6 +484,9 @@ NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp NULL mysql proxies_priv With_grant tinyint NULL NULL NULL NULL tinyint(1) 3.0000 mysql proxies_priv Grantor char 77 231 utf8 utf8_bin char(77) NULL mysql proxies_priv Timestamp timestamp NULL NULL NULL NULL timestamp +1.0000 mysql roles_mapping HostFk char 60 60 latin1 latin1_bin char(60) +1.0000 mysql roles_mapping UserFk char 16 16 latin1 latin1_bin char(16) +1.0000 mysql roles_mapping RoleFk char 16 16 latin1 latin1_bin char(16) 3.0000 mysql servers Server_name char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Host char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Db char 64 192 utf8 utf8_general_ci char(64) @@ -567,3 +574,4 @@ NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) 1.0000 mysql user plugin char 64 64 latin1 latin1_swedish_ci char(64) 1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text +3.0000 mysql user is_role enum 1 3 utf8 utf8_general_ci enum('N','Y') diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql.result b/mysql-test/suite/funcs_1/r/is_tables_mysql.result index c113e6d7fce..2b18129423b 100644 --- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result @@ -428,6 +428,29 @@ user_comment User proxy privileges Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql +TABLE_NAME roles_mapping +TABLE_TYPE BASE TABLE +ENGINE MYISAM_OR_MARIA +VERSION 10 +ROW_FORMAT Fixed +TABLE_ROWS #TBLR# +AVG_ROW_LENGTH #ARL# +DATA_LENGTH #DL# +MAX_DATA_LENGTH #MDL# +INDEX_LENGTH #IL# +DATA_FREE #DF# +AUTO_INCREMENT NULL +CREATE_TIME #CRT# +UPDATE_TIME #UT# +CHECK_TIME #CT# +TABLE_COLLATION latin1_swedish_ci +CHECKSUM NULL +CREATE_OPTIONS #CO# +TABLE_COMMENT #TC# +user_comment +Separator ----------------------------------------------------- +TABLE_CATALOG def +TABLE_SCHEMA mysql TABLE_NAME servers TABLE_TYPE BASE TABLE ENGINE MYISAM_OR_MARIA diff --git a/mysql-test/suite/funcs_1/r/is_user_privileges.result b/mysql-test/suite/funcs_1/r/is_user_privileges.result index 1ec1ffc4ce1..7b031ea5cbc 100644 --- a/mysql-test/suite/funcs_1/r/is_user_privileges.result +++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result @@ -129,6 +129,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser2 Password @@ -171,6 +172,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser3 Password @@ -213,6 +215,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N # # Add GRANT OPTION db_datadict.* to testuser1; GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION; @@ -279,6 +282,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser2 Password @@ -321,6 +325,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser3 Password @@ -363,6 +368,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N # Establish connection testuser1 (user=testuser1) SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' @@ -415,6 +421,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser2 Password @@ -457,6 +464,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser3 Password @@ -499,6 +507,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N SHOW GRANTS; Grants for testuser1@localhost GRANT USAGE ON *.* TO 'testuser1'@'localhost' @@ -573,6 +582,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser2 Password @@ -615,6 +625,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N Host localhost User testuser3 Password @@ -657,6 +668,7 @@ max_connections 0 max_user_connections 0 plugin authentication_string +is_role N GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION; # # Here