1set @old_size = @@global.optimizer_trace_max_mem_size;
2set global optimizer_trace_max_mem_size=1048576;
3select user();
4user()
5root@localhost
6create database somedb;
7use somedb;
8create table t1(a varchar(100));
9insert into t1 values("first");
10create table t2(a varchar(100));
11insert into t2 values("first");
12create table t3(a varchar(100));
13insert into t3 values("first");
14SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
15Warnings:
16Warning	3090	Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It will be removed in a future release.
17create procedure p1() sql security definer
18begin
19declare b int;
20if (select count(*) from t1)
21then
22select 22 into b from dual;
23end if;
24select a into b from t1 limit 1;
25insert into t1 values(current_user());
26end|
27create function f1() returns int sql security definer
28begin
29declare b int;
30select 48 into b from dual;
31select a into b from t1 limit 1;
32insert into t1 values(current_user());
33return 36;
34end|
35create trigger trg2 before insert on t2 for each row
36begin
37insert into t3 select * from t3;
38end|
39SET sql_mode = default;
40create sql security definer view v1 as select * from t1;
41create user user1@localhost identified by '';
42grant all on *.* to user1@localhost with grant option;
43
44select user();
45user()
46user1@localhost
47set optimizer_trace="enabled=on";
48show grants;
49Grants for user1@localhost
50GRANT ALL PRIVILEGES ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
51
52# ==========================================================
53# Part A.
54# Test that security context changes are allowed when, and only
55# when, invoker has all global privileges.
56# ==========================================================
57
58# Because invoker has all global privileges, all traces are visible:
59set optimizer_trace_offset=0,optimizer_trace_limit=100;
60call p1();
61select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
62QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
63call p1()	20	0
64set b@0 NULL	20	0
65jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	2731	0
66select 22 into b from dual	407	0
67select a into b from t1 limit 1	2164	0
68insert into t1 values(current_user())	20	0
69# this SET always purges all remembered traces
70set optimizer_trace_offset=0,optimizer_trace_limit=100;
71select f1();
72f1()
7336
74select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
75QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
76select f1()	214	0
77select f1()	413	0
78set b@0 NULL	20	0
79select 48 into b from dual	407	0
80select a into b from t1 limit 1	2164	0
81insert into t1 values(current_user())	20	0
82freturn 3 36	20	0
83set optimizer_trace_offset=0,optimizer_trace_limit=100;
84select * from v1;
85a
86first
87root@localhost
88root@localhost
89select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
90QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
91select * from v1	898	0
92select * from v1	2156	0
93set optimizer_trace_offset=0,optimizer_trace_limit=100;
94insert into t2 values(current_user());
95select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
96QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
97insert into t2 values(current_user())	20	0
98insert into t2 values(current_user())	20	0
99insert into t3 select * from t3	2532	0
100
101# Show that really all global privileges are needed: let root
102# revoke just one from user1. Because user1 does not have all global
103# privileges anymore, security context changes are forbidden,
104# thus there is no trace.
105
106select user();
107user()
108root@localhost
109revoke shutdown on *.* from user1@localhost;
110
111select user();
112user()
113user1@localhost
114set optimizer_trace="enabled=on";
115show grants;
116Grants for user1@localhost
117GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE ON *.* TO 'user1'@'localhost' WITH GRANT OPTION
118set optimizer_trace_offset=0,optimizer_trace_limit=100;
119call p1();
120# In CALL we execute stored procedure and notice a security
121# context change. The context change is probably only relevant
122# for substatements, but we still hide CALL. This is to be
123# consistent with what we do when routine body should not be
124# exposed. And it also feels safer to disable I_S output as
125# soon as possible.
126# Ps-protocol-specific note: mysqltest uses normal protocol for CALL
127select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
128QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
129	0	1
130set optimizer_trace_offset=0,optimizer_trace_limit=100;
131select f1();
132f1()
13336
134select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
135QUERY	TRACE	INSUFFICIENT_PRIVILEGES
136select f1()	{
137  "steps": [
138    {
139      "join_preparation": {
140        "select#": 1,
141        "steps": [
142          {
143            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
144          }
145        ]
146      }
147    }
148  ]
149}	0
150		1
151set optimizer_trace_offset=0,optimizer_trace_limit=100;
152select * from v1;
153a
154first
155root@localhost
156root@localhost
157root@localhost
158root@localhost
159select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
160QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
161	0	1
162	0	1
163set optimizer_trace_offset=0,optimizer_trace_limit=100;
164insert into t2 values(current_user());
165select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
166QUERY	TRACE	INSUFFICIENT_PRIVILEGES
167insert into t2 values(current_user())	{
168  "steps": [
169  ]
170}	0
171		1
172
173# Verify that user1 cannot circumvent security checks by
174# setting @@optimizer_trace_offset so that I_S output is disabled
175# before the object (routine) is checked, and enabled in the
176# middle of object usage, when 'offset' is passed.
177
178set optimizer_trace_offset=2,optimizer_trace_limit=1;
179call p1();
180# Even though the routine's execution started before
181# 'offset', it detected the security context changes. So the
182# trace of CALL gets the "missing privilege" mark but we don't
183# see it as CALL was before 'offset'.
184select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
185QUERY	TRACE	INSUFFICIENT_PRIVILEGES
186
187# Finally, verify that if the routine's definer does modify
188# @@optimizer_trace from "enabled=off" to "enabled=on", in the
189# body of the routine, then tracing works. This is no security
190# issue, as it was done by the routine's definer.
191
192select user();
193user()
194root@localhost
195create procedure p2() sql security definer
196begin
197declare b int;
198set optimizer_trace="enabled=on";
199select 22 into b from dual;
200end|
201
202select user();
203user()
204user1@localhost
205set optimizer_trace="enabled=off";
206set optimizer_trace_offset=0,optimizer_trace_limit=100;
207call p2();
208select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
209QUERY	TRACE	INSUFFICIENT_PRIVILEGES
210select 22 into b from dual	{
211  "steps": [
212    {
213      "join_preparation": {
214        "select#": 1,
215        "steps": [
216          {
217            "expanded_query": "/* select#1 */ select 22 AS `22`"
218          }
219        ]
220      }
221    },
222    {
223      "join_optimization": {
224        "select#": 1,
225        "steps": [
226        ]
227      }
228    },
229    {
230      "join_execution": {
231        "select#": 1,
232        "steps": [
233        ]
234      }
235    }
236  ]
237}	0
238# Variable is as set by the routine
239select @@optimizer_trace;
240@@optimizer_trace
241enabled=on,one_line=off
242
243# ==========================================================
244# Part B.
245# Do same tests but with SQL SECURITY INVOKER objects, to verify that
246# the restriction on security context changes is not present.
247# ==========================================================
248
249select user();
250user()
251root@localhost
252alter procedure p1 sql security invoker;
253alter function f1 sql security invoker;
254alter sql security invoker view v1 as select * from t1;
255# Triggers cannot be SQL SECURITY INVOKER so we don't test
256# them here.
257alter procedure p2 sql security invoker;
258delete from t1 where a<>"first";
259
260select user();
261user()
262user1@localhost
263set optimizer_trace_offset=0,optimizer_trace_limit=100;
264call p1();
265select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
266QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
267call p1()	20	0
268set b@0 NULL	20	0
269jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	2731	0
270select 22 into b from dual	407	0
271select a into b from t1 limit 1	2164	0
272insert into t1 values(current_user())	20	0
273set optimizer_trace_offset=0,optimizer_trace_limit=100;
274select f1();
275f1()
27636
277select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
278QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
279select f1()	214	0
280select f1()	413	0
281set b@0 NULL	20	0
282select 48 into b from dual	407	0
283select a into b from t1 limit 1	2164	0
284insert into t1 values(current_user())	20	0
285freturn 3 36	20	0
286set optimizer_trace_offset=0,optimizer_trace_limit=100;
287select * from v1;
288a
289first
290user1@localhost
291user1@localhost
292select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
293QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
294select * from v1	898	0
295select * from v1	2156	0
296set optimizer_trace_offset=2,optimizer_trace_limit=1;
297call p1();
298select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
299QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
300jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	2731	0
301set optimizer_trace="enabled=off";
302set optimizer_trace_offset=0,optimizer_trace_limit=100;
303call p2();
304# SELECT substatement is traced (no security context change)
305select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
306QUERY	TRACE	INSUFFICIENT_PRIVILEGES
307select 22 into b from dual	{
308  "steps": [
309    {
310      "join_preparation": {
311        "select#": 1,
312        "steps": [
313          {
314            "expanded_query": "/* select#1 */ select 22 AS `22`"
315          }
316        ]
317      }
318    },
319    {
320      "join_optimization": {
321        "select#": 1,
322        "steps": [
323        ]
324      }
325    },
326    {
327      "join_execution": {
328        "select#": 1,
329        "steps": [
330        ]
331      }
332    }
333  ]
334}	0
335select @@optimizer_trace;
336@@optimizer_trace
337enabled=on,one_line=off
338
339# ==========================================================
340# Part C.
341# User1 got traces. Determine the minimum set of privileges he
342# needed for that.
343# ==========================================================
344
345drop procedure p2;
346select user();
347user()
348root@localhost
349revoke all privileges, grant option from user1@localhost;
350# Grant minimum privileges to use the routines and views,
351# without considering optimizer trace:
352grant execute on procedure p1 to user1@localhost;
353grant execute on function f1 to user1@localhost;
354grant select (a) on v1 to user1@localhost;
355# Objects above are SQL SECURITY INVOKER, so invoker needs
356# privileges on objects used internally:
357grant select (a) on t1 to user1@localhost;
358grant insert (a) on t1 to user1@localhost;
359delete from t1 where a<>"first";
360
361select user();
362user()
363user1@localhost
364set optimizer_trace="enabled=on";
365show grants;
366Grants for user1@localhost
367GRANT EXECUTE ON FUNCTION `somedb`.`f1` TO 'user1'@'localhost'
368GRANT EXECUTE ON PROCEDURE `somedb`.`p1` TO 'user1'@'localhost'
369GRANT SELECT (a) ON `somedb`.`v1` TO 'user1'@'localhost'
370GRANT SELECT (a), INSERT (a) ON `somedb`.`t1` TO 'user1'@'localhost'
371GRANT USAGE ON *.* TO 'user1'@'localhost'
372
373# Those privileges are not enough to see traces:
374set optimizer_trace_offset=0,optimizer_trace_limit=100;
375call p1();
376# In CALL we execute stored procedure and notice that body should
377# not be exposed. The trace of this CALL would not expose the
378# body. Trace of substatements would. But, due to
379# implementation, CALL is hidden.
380select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
381QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
382	0	1
383set optimizer_trace_offset=0,optimizer_trace_limit=100;
384select f1();
385f1()
38636
387# SELECT is hidden (same reason as for CALL).
388# Ps-protocol-specific note: preparation of SELECT above does not
389# execute f1, so does not risk exposing body, so its trace is
390# visible.
391select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
392QUERY	TRACE	INSUFFICIENT_PRIVILEGES
393select f1()	{
394  "steps": [
395    {
396      "join_preparation": {
397        "select#": 1,
398        "steps": [
399          {
400            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
401          }
402        ]
403      }
404    }
405  ]
406}	0
407		1
408set optimizer_trace_offset=0,optimizer_trace_limit=100;
409select * from v1;
410a
411first
412user1@localhost
413user1@localhost
414# Cannot see anything as it would expose body of view
415select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
416QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
417	0	1
418	0	1
419
420# C.0) Add more privileges:
421
422select user();
423user()
424root@localhost
425# - for use of t1 in routines and view:
426grant select on t1 to user1@localhost;
427# - for use of routines:
428grant select on mysql.proc to user1@localhost;
429# - for use of view:
430grant select, show view on v1 to user1@localhost;
431delete from t1 where a<>"first";
432
433select user();
434user()
435user1@localhost
436set optimizer_trace_offset=0,optimizer_trace_limit=100;
437call p1();
438select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
439QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
440call p1()	20	0
441set b@0 NULL	20	0
442jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	2731	0
443select 22 into b from dual	407	0
444select a into b from t1 limit 1	2164	0
445insert into t1 values(current_user())	20	0
446# Trace exposed body of routine, and content of t1, which we
447# could see anyway:
448show create procedure p1;
449Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
450p1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`root`@`localhost` PROCEDURE `p1`()
451    SQL SECURITY INVOKER
452begin
453declare b int;
454if (select count(*) from t1)
455then
456select 22 into b from dual;
457end if;
458select a into b from t1 limit 1;
459insert into t1 values(current_user());
460end	latin1	latin1_swedish_ci	latin1_swedish_ci
461select * from t1 limit 1;
462a
463first
464set optimizer_trace_offset=0,optimizer_trace_limit=100;
465select f1();
466f1()
46736
468select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
469QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
470select f1()	214	0
471select f1()	413	0
472set b@0 NULL	20	0
473select 48 into b from dual	407	0
474select a into b from t1 limit 1	2164	0
475insert into t1 values(current_user())	20	0
476freturn 3 36	20	0
477# Trace exposed body of routine, and content of t1, which we
478# could see anyway:
479show create function f1;
480Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
481f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
482    SQL SECURITY INVOKER
483begin
484declare b int;
485select 48 into b from dual;
486select a into b from t1 limit 1;
487insert into t1 values(current_user());
488return 36;
489end	latin1	latin1_swedish_ci	latin1_swedish_ci
490set optimizer_trace_offset=0,optimizer_trace_limit=100;
491select * from v1;
492a
493first
494user1@localhost
495user1@localhost
496select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
497QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
498select * from v1	898	0
499select * from v1	2156	0
500# Trace exposed body of view, and content of t1, which we
501# could see anyway:
502show create view v1;
503View	Create View	character_set_client	collation_connection
504v1	CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
505
506# Now remove each privilege to verify that it was needed:
507# C.1) remove table-level SELECT privilege on t1
508
509select user();
510user()
511root@localhost
512revoke select on t1 from user1@localhost;
513grant select (a) on t1 to user1@localhost;
514delete from t1 where a<>"first";
515
516select user();
517user()
518user1@localhost
519set optimizer_trace_offset=0,optimizer_trace_limit=100;
520call p1();
521# Cannot see those substatements which use t1
522select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
523QUERY	TRACE	INSUFFICIENT_PRIVILEGES
524call p1()	{
525  "steps": [
526  ]
527}	0
528set b@0 NULL	{
529  "steps": [
530  ]
531}	0
532		1
533select 22 into b from dual	{
534  "steps": [
535    {
536      "join_preparation": {
537        "select#": 1,
538        "steps": [
539          {
540            "expanded_query": "/* select#1 */ select 22 AS `22`"
541          }
542        ]
543      }
544    },
545    {
546      "join_optimization": {
547        "select#": 1,
548        "steps": [
549        ]
550      }
551    },
552    {
553      "join_execution": {
554        "select#": 1,
555        "steps": [
556        ]
557      }
558    }
559  ]
560}	0
561		1
562		1
563set optimizer_trace_offset=0,optimizer_trace_limit=100;
564select f1();
565f1()
56636
567# Cannot see those substatements which use t1
568select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
569QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
570select f1()	214	0
571select f1()	413	0
572set b@0 NULL	20	0
573select 48 into b from dual	407	0
574	0	1
575	0	1
576freturn 3 36	20	0
577# Trace exposed body of routine, which we could see anyway:
578set optimizer_trace="enabled=off";
579show create function f1;
580Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
581f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`root`@`localhost` FUNCTION `f1`() RETURNS int(11)
582    SQL SECURITY INVOKER
583begin
584declare b int;
585select 48 into b from dual;
586select a into b from t1 limit 1;
587insert into t1 values(current_user());
588return 36;
589end	latin1	latin1_swedish_ci	latin1_swedish_ci
590set optimizer_trace="enabled=on";
591set optimizer_trace_offset=0,optimizer_trace_limit=100;
592select * from v1;
593a
594first
595user1@localhost
596user1@localhost
597# Cannot see anything as it might expose some data from columns
598# of t1
599select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
600QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
601	0	1
602	0	1
603
604# C.2) remove table-level SELECT privilege on mysql.proc
605
606select user();
607user()
608root@localhost
609# Put back privilege removed in C.1
610grant select on t1 to user1@localhost;
611# And remove a next one:
612revoke select on mysql.proc from user1@localhost;
613delete from t1 where a<>"first";
614
615select user();
616user()
617user1@localhost
618# We have no right to see routines' bodies:
619set optimizer_trace="enabled=off";
620show create procedure p1;
621Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
622p1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
623show create function f1;
624Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
625f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
626# Verify that optimizer trace does not influence the privilege
627# checking in SHOW CREATE:
628set optimizer_trace="enabled=on";
629show create procedure p1;
630Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
631p1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
632show create function f1;
633Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
634f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	NULL	latin1	latin1_swedish_ci	latin1_swedish_ci
635set optimizer_trace_offset=0,optimizer_trace_limit=100;
636call p1();
637# Cannot see anything as it would expose body of routine
638select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
639QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
640	0	1
641set optimizer_trace_offset=0,optimizer_trace_limit=100;
642select f1();
643f1()
64436
645select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
646QUERY	TRACE	INSUFFICIENT_PRIVILEGES
647select f1()	{
648  "steps": [
649    {
650      "join_preparation": {
651        "select#": 1,
652        "steps": [
653          {
654            "expanded_query": "/* select#1 */ select `f1`() AS `f1()`"
655          }
656        ]
657      }
658    }
659  ]
660}	0
661		1
662
663# C.3) remove table-level SELECT privilege on view
664
665select user();
666user()
667root@localhost
668# Put back privilege removed in C.2
669grant select on mysql.proc to user1@localhost;
670# And remove a next one:
671revoke select on v1 from user1@localhost;
672grant select (a) on v1 to user1@localhost;
673delete from t1 where a<>"first";
674
675select user();
676user()
677user1@localhost
678set optimizer_trace_offset=0,optimizer_trace_limit=100;
679select * from v1;
680a
681first
682# Cannot see anything as it might expose some data from columns
683# of v1
684select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
685QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
686	0	1
687	0	1
688
689# C.4) remove SHOW VIEW privilege on view
690
691select user();
692user()
693root@localhost
694# Put back privilege removed in C.3
695grant select on v1 to user1@localhost;
696# And remove a next one:
697revoke show view on v1 from user1@localhost;
698delete from t1 where a<>"first";
699
700select user();
701user()
702user1@localhost
703set optimizer_trace="enabled=off";
704# We have no right to see view's body:
705show create view v1;
706ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
707set optimizer_trace="enabled=on";
708# Verify that optimizer trace does not influence the privilege
709# checking in SHOW CREATE:
710show create view v1;
711ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
712set optimizer_trace_offset=0,optimizer_trace_limit=100;
713select * from v1;
714a
715first
716# Cannot see anything as it would expose body of view
717select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
718QUERY	TRACE	INSUFFICIENT_PRIVILEGES
719		1
720		1
721
722# ==========================================================
723# Part D.
724# Like Part C, but instead of SQL SECURITY INVOKER objects
725# created by root and used by User1, let's have SQL SECURITY
726# DEFINER objects created and used by User1. Determine the
727# minimum set of privileges he needs for that.
728# ==========================================================
729
730select user();
731user()
732root@localhost
733drop procedure p1;
734drop function f1;
735drop view v1;
736drop trigger trg2;
737revoke all privileges, grant option from user1@localhost;
738# Grant minimum privileges to create and use objects,
739# without considering optimizer trace:
740grant create routine on somedb.* to user1@localhost;
741grant trigger on t2 to user1@localhost;
742grant create view on somedb.* to user1@localhost;
743grant select (a) on t1 to user1@localhost;
744grant insert (a) on t1 to user1@localhost;
745grant insert (a) on t2 to user1@localhost;
746grant select (a) on t3 to user1@localhost;
747grant insert (a) on t3 to user1@localhost;
748delete from t1 where a<>"first";
749
750select user();
751user()
752user1@localhost
753set optimizer_trace="enabled=on";
754SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
755Warnings:
756Warning	3090	Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It will be removed in a future release.
757create procedure p1() sql security definer
758begin
759declare b int;
760if (select count(*) from t1)
761then
762select 22 into b from dual;
763end if;
764select a into b from t1 limit 1;
765insert into t1 values(current_user());
766end|
767create function f1() returns int sql security definer
768begin
769declare b int;
770select 48 into b from dual;
771select a into b from t1 limit 1;
772insert into t1 values(current_user());
773return 36;
774end|
775create trigger trg2 before insert on t2 for each row
776begin
777insert into t3 select * from t3;
778end|
779create sql security definer view v1 as select * from t1;
780SET sql_mode = default;
781# Creating a view is not enough to be able to SELECT it...
782select user();
783user()
784root@localhost
785grant select (a) on v1 to user1@localhost;
786
787select user();
788user()
789user1@localhost
790# Those privileges are not enough to see traces:
791set optimizer_trace_offset=0,optimizer_trace_limit=100;
792call p1();
793# Can see body of routine (as definer), but not statements using t1
794select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
795QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
796call p1()	20	0
797set b@0 NULL	20	0
798	0	1
799select 22 into b from dual	407	0
800	0	1
801	0	1
802set optimizer_trace_offset=0,optimizer_trace_limit=100;
803select f1();
804f1()
80536
806# Can see body of routine (as definer), but not statements using t1
807select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
808QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
809select f1()	214	0
810select f1()	413	0
811set b@0 NULL	20	0
812select 48 into b from dual	407	0
813	0	1
814	0	1
815freturn 3 36	20	0
816show create function f1;
817Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
818f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
819begin
820declare b int;
821select 48 into b from dual;
822select a into b from t1 limit 1;
823insert into t1 values(current_user());
824return 36;
825end	latin1	latin1_swedish_ci	latin1_swedish_ci
826set optimizer_trace_offset=0,optimizer_trace_limit=100;
827select * from v1;
828a
829first
830user1@localhost
831user1@localhost
832# Cannot see anything as it might expose some data from columns
833# of t1
834select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
835QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
836	0	1
837	0	1
838set optimizer_trace_offset=0,optimizer_trace_limit=100;
839insert into t2 values(current_user());
840# Cannot see anything as it might expose some data from
841# columns of t2
842select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
843QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
844	0	1
845	0	1
846# Also test a query accessing t1 in FROM clause:
847set optimizer_trace_offset=0,optimizer_trace_limit=100;
848select a from (select a from t1 where a like "f%") as tt where a like "fi%";
849a
850first
851select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
852QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
853	0	1
854	0	1
855
856# D.0) Add more privileges:
857
858select user();
859user()
860root@localhost
861# - for use of t1 in routines and view:
862grant select on t1 to user1@localhost;
863# - for use of view:
864grant select, show view on v1 to user1@localhost;
865# - for use of trigger
866grant select on t2 to user1@localhost;
867grant select on t3 to user1@localhost;
868delete from t1 where a<>"first";
869
870select user();
871user()
872user1@localhost
873set optimizer_trace_offset=0,optimizer_trace_limit=100;
874call p1();
875select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
876QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
877call p1()	20	0
878set b@0 NULL	20	0
879jump_if_not 3(3) (select count(0) from `somedb`.`t1`)	2731	0
880select 22 into b from dual	407	0
881select a into b from t1 limit 1	2164	0
882insert into t1 values(current_user())	20	0
883# Trace exposed body of routine, and content of t1, which we
884# could see anyway:
885show create procedure p1;
886Procedure	sql_mode	Create Procedure	character_set_client	collation_connection	Database Collation
887p1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`user1`@`localhost` PROCEDURE `p1`()
888begin
889declare b int;
890if (select count(*) from t1)
891then
892select 22 into b from dual;
893end if;
894select a into b from t1 limit 1;
895insert into t1 values(current_user());
896end	latin1	latin1_swedish_ci	latin1_swedish_ci
897select * from t1 limit 1;
898a
899first
900set optimizer_trace_offset=0,optimizer_trace_limit=100;
901select f1();
902f1()
90336
904select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
905QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
906select f1()	214	0
907select f1()	413	0
908set b@0 NULL	20	0
909select 48 into b from dual	407	0
910select a into b from t1 limit 1	2164	0
911insert into t1 values(current_user())	20	0
912freturn 3 36	20	0
913# Trace exposed body of routine, and content of t1, which we
914# could see anyway:
915show create function f1;
916Function	sql_mode	Create Function	character_set_client	collation_connection	Database Collation
917f1	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`user1`@`localhost` FUNCTION `f1`() RETURNS int(11)
918begin
919declare b int;
920select 48 into b from dual;
921select a into b from t1 limit 1;
922insert into t1 values(current_user());
923return 36;
924end	latin1	latin1_swedish_ci	latin1_swedish_ci
925set optimizer_trace_offset=0,optimizer_trace_limit=100;
926select * from v1;
927a
928first
929user1@localhost
930user1@localhost
931select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
932QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
933select * from v1	898	0
934select * from v1	2156	0
935# Trace exposed body of view, and content of t1, which we
936# could see anyway:
937show create view v1;
938View	Create View	character_set_client	collation_connection
939v1	CREATE ALGORITHM=UNDEFINED DEFINER=`user1`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select `t1`.`a` AS `a` from `t1`	latin1	latin1_swedish_ci
940set optimizer_trace_offset=0,optimizer_trace_limit=100;
941insert into t2 values(current_user());
942select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
943QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
944insert into t2 values(current_user())	20	0
945insert into t2 values(current_user())	20	0
946insert into t3 select * from t3	2532	0
947# Trace exposed body of trigger, and content of t2/t3, which we
948# could see anyway:
949show create trigger trg2;
950Trigger	sql_mode	SQL Original Statement	character_set_client	collation_connection	Database Collation	Created
951trg2	ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION	CREATE DEFINER=`user1`@`localhost` trigger trg2 before insert on t2 for each row
952begin
953insert into t3 select * from t3;
954end	latin1	latin1_swedish_ci	latin1_swedish_ci	#
955select * from t2, t3 limit 1;
956a	a
957first	first
958# Trace exposed content of t1 which we could see anyway:
959set optimizer_trace_offset=0,optimizer_trace_limit=100;
960select a from (select a from t1 where a like "f%") as tt where a like "fi%";
961a
962first
963select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
964QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
965select a from (select a from t1 where a like "f%") as tt where a like "fi%"	1060	0
966select a from (select a from t1 where a like "f%") as tt where a like "fi%"	3280	0
967
968# For routines, as they only use t1 and we added only one
969# privilege on t1, we have nothing to remove.
970
971# Now remove each privilege to verify that it was needed for
972# the view.
973# D.1) remove table-level SELECT privilege on v1
974
975select user();
976user()
977root@localhost
978revoke select on v1 from user1@localhost;
979grant select (a) on v1 to user1@localhost;
980
981select user();
982user()
983user1@localhost
984set optimizer_trace_offset=0,optimizer_trace_limit=100;
985select * from v1;
986a
987first
988user1@localhost
989user1@localhost
990# Cannot see anything as it might expose some data from columns
991# of v1
992select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
993QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
994	0	1
995	0	1
996
997# D.2) remove table-level SHOW VIEW privilege on v1
998
999select user();
1000user()
1001root@localhost
1002# Put back privilege removed in D.1
1003grant select on v1 to user1@localhost;
1004# And remove a next one:
1005revoke show view on v1 from user1@localhost;
1006
1007select user();
1008user()
1009user1@localhost
1010# We have no right to see view's body:
1011show create view v1;
1012ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
1013set optimizer_trace_offset=0,optimizer_trace_limit=100;
1014select * from v1;
1015a
1016first
1017user1@localhost
1018user1@localhost
1019# Cannot see anything as it would expose body of view
1020select QUERY, TRACE, INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
1021QUERY	TRACE	INSUFFICIENT_PRIVILEGES
1022		1
1023		1
1024
1025# D.3) remove table-level SELECT privilege on t1
1026
1027select user();
1028user()
1029root@localhost
1030# Put back privilege removed in D.2
1031grant show view on v1 to user1@localhost;
1032# And remove a next one:
1033revoke select on t1 from user1@localhost;
1034grant select (a) on t1 to user1@localhost;
1035
1036select user();
1037user()
1038user1@localhost
1039set optimizer_trace_offset=0,optimizer_trace_limit=100;
1040select * from v1;
1041a
1042first
1043user1@localhost
1044user1@localhost
1045# Cannot see anything as it might expose some data from columns
1046# of t1
1047select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
1048QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
1049	0	1
1050	0	1
1051
1052# Now remove each privilege to verify that it was needed for
1053# the trigger:
1054# D.4) remove table-level SELECT privilege on t2
1055
1056select user();
1057user()
1058root@localhost
1059revoke select on t2 from user1@localhost;
1060grant select (a) on t2 to user1@localhost;
1061
1062select user();
1063user()
1064user1@localhost
1065set optimizer_trace_offset=0,optimizer_trace_limit=100;
1066insert into t2 values(current_user());
1067# Cannot see anything as it might expose some data from
1068# columns of t2
1069select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
1070QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
1071	0	1
1072	0	1
1073
1074# D.5) remove table-level SELECT privilege on t3
1075
1076
1077select user();
1078user()
1079root@localhost
1080# Put back privilege removed in D.4
1081grant select on t2 to user1@localhost;
1082# And remove a next one:
1083revoke select on t3 from user1@localhost;
1084grant select (a) on t3 to user1@localhost;
1085
1086select user();
1087user()
1088user1@localhost
1089set optimizer_trace_offset=0,optimizer_trace_limit=100;
1090insert into t2 values(current_user());
1091# Cannot see substatement as it might expose some data from
1092# columns of t3
1093select QUERY, length(TRACE), INSUFFICIENT_PRIVILEGES from information_schema.OPTIMIZER_TRACE;
1094QUERY	length(TRACE)	INSUFFICIENT_PRIVILEGES
1095insert into t2 values(current_user())	20	0
1096insert into t2 values(current_user())	20	0
1097	0	1
1098
1099# Cleanup
1100select user();
1101user()
1102root@localhost
1103drop user user1@localhost;
1104
1105# ==========================================================
1106# Part E.
1107# Misc tests.
1108# ==========================================================
1109
1110select user();
1111user()
1112root@localhost
1113drop view v1;
1114create sql security definer view v1 as select * from t1 where 'secret';
1115create user user1@localhost identified by '';
1116grant create, insert, select on somedb.* to user1@localhost;
1117grant create routine on somedb.* to user1@localhost;
1118
1119select user();
1120user()
1121user1@localhost
1122user1 cannot see view's body:
1123show create view v1;
1124ERROR 42000: SHOW VIEW command denied to user 'user1'@'localhost' for table 'v1'
1125SET sql_mode = 'ONLY_FULL_GROUP_BY,NO_ENGINE_SUBSTITUTION';
1126Warnings:
1127Warning	3090	Changing sql mode 'NO_AUTO_CREATE_USER' is deprecated. It will be removed in a future release.
1128user1 creates a procedure
1129create procedure proc() sql security definer
1130begin
1131set optimizer_trace="enabled=on";
1132set optimizer_trace_offset=0,optimizer_trace_limit=100;
1133select * from v1 limit 0;
1134create table leak select * from information_schema.optimizer_trace;
1135set optimizer_trace="enabled=off";
1136end|
1137SET sql_mode = default;
1138select user();
1139user()
1140root@localhost
1141root runs procedure, without fear of risk as it is SQL SECURITY DEFINER
1142call proc();
1143a
1144
1145select user();
1146user()
1147user1@localhost
1148user1 cannot see view's body:
1149select * from leak;
1150QUERY	TRACE	MISSING_BYTES_BEYOND_MAX_MEM_SIZE	INSUFFICIENT_PRIVILEGES
1151
1152# Cleanup
1153select user();
1154user()
1155root@localhost
1156drop database somedb;
1157drop user user1@localhost;
1158set @@global.optimizer_trace_max_mem_size = @old_size;
1159