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