summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorunknown <pem@mysql.comhem.se>2003-12-13 16:40:52 +0100
committerunknown <pem@mysql.comhem.se>2003-12-13 16:40:52 +0100
commita6f85eeac15b520f34aed0782e10825b20a038cc (patch)
tree2412468479c77dc2c4cc6b2c28be3e07131647ef
parent8630ca9a098d21bfa65dd3769160c9b57844d3be (diff)
downloadmariadb-git-a6f85eeac15b520f34aed0782e10825b20a038cc.tar.gz
WL#1365: Implement definer's rights execution of stored procedures.
(Also put the hostpart back in the definer column.) mysql-test/r/sp-error.result: Moved error test from sp.test mysql-test/r/sp.result: Moved error test to sp-error.test. Put hostpart back into definer column in mysql.proc. mysql-test/t/sp-error.test: Moved error test from sp.test mysql-test/t/sp.test: Moved error test to sp-error.test. Put hostpart back into definer column in mysql.proc. sql/item_func.cc: (Maybe) switch security context before invoking a stored function. sql/sp.cc: Renamed creator into definer, for more consistent terminology, and put the hostpart back. sql/sp_head.cc: Some fixes in the way things are allocated, and moved set_info() definition here from sp_head.h. creator is now called definer, and is split into a user and host part. Added functions for (possible) change and restore of privileges, for sql security definer calls. sql/sp_head.h: Moved set_info() definition here from sp_head.h. creator is now called definer, and is split into a user and host part. Added functions for (possible) change and restore of privileges, for sql security definer calls. sql/sql_acl.cc: New function acl_getroot_no_password() for getting the privileges used when calling an SP with sql security definer. sql/sql_acl.h: New function acl_getroot_no_password() for getting the privileges used when calling an SP with sql security definer. sql/sql_parse.cc: (Maybe) switch security context before invoking a stored procedure. sql/sql_yacc.yy: Fixed typo.
-rw-r--r--mysql-test/r/sp-error.result11
-rw-r--r--mysql-test/r/sp-security.result44
-rw-r--r--mysql-test/r/sp.result21
-rw-r--r--mysql-test/t/sp-error.test21
-rw-r--r--mysql-test/t/sp-security.test99
-rw-r--r--mysql-test/t/sp.test20
-rw-r--r--sql/item_func.cc10
-rw-r--r--sql/sp.cc28
-rw-r--r--sql/sp_head.cc102
-rw-r--r--sql/sp_head.h35
-rw-r--r--sql/sql_acl.cc60
-rw-r--r--sql/sql_acl.h1
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_yacc.yy2
14 files changed, 392 insertions, 67 deletions
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
index 22f6d37f8de..ac952549803 100644
--- a/mysql-test/r/sp-error.result
+++ b/mysql-test/r/sp-error.result
@@ -280,4 +280,15 @@ create function bug1654()
returns int
return (select sum(t.data) from test.t2 t);
ERROR 0A000: Statements like SELECT, INSERT, UPDATE (and others) are not allowed in a FUNCTION
+drop table if exists table_1;
+create table t3 (column_1_0 int);
+create procedure bug1653()
+update t3 set column_1 = 0;
+call bug1653();
+ERROR 42S22: Unknown column 'column_1' in 'field list'
+drop table t3;
+create table t3 (column_1 int);
+call bug1653();
+drop procedure bug1653;
+drop table t3;
drop table t1;
diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result
new file mode 100644
index 00000000000..00e22ceebd4
--- /dev/null
+++ b/mysql-test/r/sp-security.result
@@ -0,0 +1,44 @@
+use test;
+grant usage on *.* to dummy@localhost;
+drop database if exists db1_secret;
+create database db1_secret;
+use db1_secret;
+create table t1 ( u varchar(64), i int );
+create procedure stamp(i int)
+insert into db1_secret.t1 values (user(), i);
+show procedure status like 'stamp';
+Name Type Definer Modified Created Security_type Comment
+stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+call stamp(1);
+select * from t1;
+u i
+root@localhost 1
+call stamp(2);
+select * from db1_secret.t1;
+ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret'
+call stamp(3);
+select * from db1_secret.t1;
+ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
+select * from t1;
+u i
+root@localhost 1
+dummy@localhost 2
+anon@localhost 3
+alter procedure stamp sql security invoker;
+show procedure status like 'stamp';
+Name Type Definer Modified Created Security_type Comment
+stamp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER
+call stamp(4);
+select * from t1;
+u i
+root@localhost 1
+dummy@localhost 2
+anon@localhost 3
+root@localhost 4
+call stamp(5);
+ERROR 42000: Access denied for user: 'dummy'@'localhost' to database 'db1_secret'
+call stamp(6);
+ERROR 42000: Access denied for user: ''@'localhost' to database 'db1_secret'
+use test;
+drop database db1_secret;
+delete from mysql.user where user='dummy';
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
index 5c506e7bf14..05e6096f8ac 100644
--- a/mysql-test/r/sp.result
+++ b/mysql-test/r/sp.result
@@ -866,17 +866,6 @@ avg 0 4.4
delete from t1;
delete from t2;
drop procedure bug1874;
-drop table if exists table_1;
-create table t3 (column_1_0 int);
-create procedure bug1653()
-update t3 set column_1 = 0;
-call bug1653();
-ERROR 42S22: Unknown column 'column_1' in 'field list'
-drop table t3;
-create table t3 (column_1 int);
-call bug1653();
-drop procedure bug1653;
-drop table t3;
drop table if exists fac;
create table fac (n int unsigned not null primary key, f bigint unsigned);
create procedure ifac(n int unsigned)
@@ -918,7 +907,7 @@ n f
drop table fac;
show function status like '%f%';
Name Type Definer Modified Created Security_type Comment
-fac FUNCTION root 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+fac FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
drop procedure ifac;
drop function fac;
show function status like '%f%';
@@ -1011,8 +1000,8 @@ end loop;
end
show procedure status like '%p%';
Name Type Definer Modified Created Security_type Comment
-ip PROCEDURE root 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
-opp PROCEDURE root 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+ip PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
+opp PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER
call ip(200);
select * from primes where i=45 or i=100 or i=199;
i p
@@ -1074,7 +1063,7 @@ comment "111111111111" sql security invoker
insert into test.t1 values (x, y);
show procedure status like 'bar';
Name Type Definer Modified Created Security_type Comment
-bar PROCEDURE root 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111
+bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 INVOKER 111111111111
alter procedure bar name bar2 comment "2222222222" sql security definer;
alter procedure bar2 name bar comment "3333333333";
alter procedure bar;
@@ -1085,7 +1074,7 @@ bar CREATE PROCEDURE bar(x char(16), y int)
insert into test.t1 values (x, y)
show procedure status like 'bar';
Name Type Definer Modified Created Security_type Comment
-bar PROCEDURE root 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333
+bar PROCEDURE root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER 3333333333
drop procedure bar;
drop table t1;
drop table t2;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
index 042e9baa47f..ab13559b860 100644
--- a/mysql-test/t/sp-error.test
+++ b/mysql-test/t/sp-error.test
@@ -379,6 +379,27 @@ create function bug1654()
returns int
return (select sum(t.data) from test.t2 t)|
+#
+# BUG#1653
+#
+--disable_warnings
+drop table if exists table_1|
+--enable_warnings
+create table t3 (column_1_0 int)|
+
+create procedure bug1653()
+ update t3 set column_1 = 0|
+
+--error 1054
+call bug1653()|
+drop table t3|
+create table t3 (column_1 int)|
+call bug1653()|
+
+drop procedure bug1653|
+drop table t3|
+
+
drop table t1|
delimiter ;|
diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test
new file mode 100644
index 00000000000..923438c8525
--- /dev/null
+++ b/mysql-test/t/sp-security.test
@@ -0,0 +1,99 @@
+#
+# Testing SQL SECURITY of stored procedures
+#
+
+connect (con1root,localhost,root,,);
+
+connection con1root;
+use test;
+
+# Create dummy user with no particular access rights
+grant usage on *.* to dummy@localhost;
+
+--disable_warnings
+drop database if exists db1_secret;
+--enable_warnings
+# Create our secret database
+create database db1_secret;
+
+use db1_secret;
+
+create table t1 ( u varchar(64), i int );
+
+# Our test procedure
+create procedure stamp(i int)
+ insert into db1_secret.t1 values (user(), i);
+--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+show procedure status like 'stamp';
+
+# root can, of course
+call stamp(1);
+select * from t1;
+
+connect (con2dummy,localhost,dummy,,);
+connect (con3anon,localhost,anon,,);
+
+#
+# Dummy can
+#
+connection con2dummy;
+
+# This should work...
+call stamp(2);
+
+# ...but not this
+--error 1044
+select * from db1_secret.t1;
+
+#
+# Anonymous can
+#
+connection con3anon;
+
+# This should work...
+call stamp(3);
+
+# ...but not this
+--error 1044
+select * from db1_secret.t1;
+
+#
+# Check it out
+#
+connection con1root;
+select * from t1;
+
+#
+# Change to invoker's rights
+#
+alter procedure stamp sql security invoker;
+--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
+show procedure status like 'stamp';
+
+# root still can
+call stamp(4);
+select * from t1;
+
+#
+# Dummy cannot
+#
+connection con2dummy;
+
+# This should not work
+--error 1044
+call stamp(5);
+
+#
+# Anonymous cannot
+#
+connection con3anon;
+
+# This should not work
+--error 1044
+call stamp(6);
+
+# Clean up
+connection con1root;
+use test;
+drop database db1_secret;
+delete from mysql.user where user='dummy';
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
index 3681d9a9517..cda03e46e37 100644
--- a/mysql-test/t/sp.test
+++ b/mysql-test/t/sp.test
@@ -1013,26 +1013,6 @@ delete from t1|
delete from t2|
drop procedure bug1874|
-#
-# BUG#1653
-#
---disable_warnings
-drop table if exists table_1|
---enable_warnings
-create table t3 (column_1_0 int)|
-
-create procedure bug1653()
- update t3 set column_1 = 0|
-
---error 1054
-call bug1653()|
-drop table t3|
-create table t3 (column_1 int)|
-call bug1653()|
-
-drop procedure bug1653|
-drop table t3|
-
#
# Some "real" examples
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 10e405393a2..428ca5aa90e 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -3069,13 +3069,21 @@ Item_func_sp::execute(Item **itp)
{
DBUG_ENTER("Item_func_sp::execute");
THD *thd= current_thd;
+ st_sp_security_context save_ctx;
+ int res;
if (! m_sp)
m_sp= sp_find_function(thd, &m_name);
if (! m_sp)
DBUG_RETURN(-1);
- DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp));
+ sp_change_security_context(thd, m_sp, &save_ctx);
+
+ res= m_sp->execute_function(thd, args, arg_count, itp);
+
+ sp_restore_security_context(thd, m_sp, &save_ctx);
+
+ DBUG_RETURN(res);
}
enum enum_field_types
diff --git a/sql/sp.cc b/sql/sp.cc
index bc7f0bf32d7..10eb9c0c6f0 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -121,10 +121,10 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
const char *params, *returns, *body;
int ret;
bool opened;
- const char *creator;
+ const char *definer;
longlong created;
longlong modified;
- st_sp_chistics *chistics;
+ st_sp_chistics chistics;
char *ptr;
uint length;
char buff[65];
@@ -147,8 +147,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
ret= SP_GET_FIELD_FAILED;
goto done;
}
- chistics= (st_sp_chistics *)thd->alloc(sizeof(st_sp_chistics));
- chistics->detistic= (ptr[0] == 'N' ? FALSE : TRUE);
+ bzero((char *)&chistics, sizeof(chistics));
+ chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE);
if ((ptr= get_field(&thd->mem_root,
table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL)
@@ -156,7 +156,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
ret= SP_GET_FIELD_FAILED;
goto done;
}
- chistics->suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID);
+ chistics.suid= (ptr[0] == 'I' ? IS_NOT_SUID : IS_SUID);
if ((params= get_field(&thd->mem_root,
table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL)
@@ -181,7 +181,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
}
// Get additional information
- if ((creator= get_field(&thd->mem_root,
+ if ((definer= get_field(&thd->mem_root,
table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL)
{
ret= SP_GET_FIELD_FAILED;
@@ -198,8 +198,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
ptr= 0;
if ((length= str.length()))
ptr= thd->strmake(str.ptr(), length);
- chistics->comment.str= ptr;
- chistics->comment.length= length;
+ chistics.comment.str= ptr;
+ chistics.comment.length= length;
if (opened)
{
@@ -224,7 +224,7 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
params, strlen(params),
returns, strlen(returns),
body, strlen(body),
- chistics);
+ &chistics);
lex_start(thd, (uchar*)defstr, deflen);
if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
{
@@ -243,8 +243,8 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
else
{
*sphp= thd->lex->sphead;
- (*sphp)->set_info((char *) creator, (uint) strlen(creator),
- created, modified, chistics);
+ (*sphp)->set_info((char *)definer, (uint)strlen(definer),
+ created, modified, &chistics);
}
thd->lex->sql_command= oldcmd;
thd->variables.sql_mode= old_sql_mode;
@@ -265,6 +265,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
int ret;
TABLE *table;
TABLE_LIST tables;
+ char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
memset(&tables, 0, sizeof(tables));
tables.db= (char*)"mysql";
@@ -275,6 +276,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
else
{
restore_record(table, default_values); // Get default values for fields
+ strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
if (table->fields != MYSQL_PROC_FIELD_COUNT)
{
@@ -300,7 +302,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
table->field[MYSQL_PROC_FIELD_BODY]->
store(sp->m_body.str, sp->m_body.length, system_charset_info);
table->field[MYSQL_PROC_FIELD_DEFINER]->
- store(thd->user, (uint)strlen(thd->user), system_charset_info);
+ store(definer, (uint)strlen(definer), system_charset_info);
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
table->field[MYSQL_PROC_FIELD_SQL_MODE]->
store((longlong)thd->variables.sql_mode);
@@ -853,7 +855,7 @@ create_string(THD *thd, ulong *lenp,
ptr+= my_sprintf(ptr, (ptr, (char *)" DETERMINISTIC\n"));
if (chistics->suid == IS_NOT_SUID)
ptr+= my_sprintf(ptr, (ptr, (char *)" SQL SECURITY INVOKER\n"));
- if (chistics->comment.str)
+ if (chistics->comment.length)
ptr+= my_sprintf(ptr, (ptr, (char *)" COMMENT '%*s'\n",
chistics->comment.length,
chistics->comment.str));
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index cfaf08b7972..66c811c1e19 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -19,6 +19,7 @@
#endif
#include "mysql_priv.h"
+#include "sql_acl.h"
#include "sp_head.h"
#include "sp.h"
#include "sp_pcontext.h"
@@ -183,9 +184,10 @@ sp_head::init_strings(LEX_STRING *name, LEX *lex)
DBUG_PRINT("info", ("name: %*s", name->length, name->str));
m_name.length= name->length;
- m_name.str= lex->thd->strmake(name->str, name->length);
+ m_name.str= strmake_root(&m_mem_root, name->str, name->length);
m_params.length= m_param_end- m_param_begin;
- m_params.str= lex->thd->strmake((char *)m_param_begin, m_params.length);
+ m_params.str= strmake_root(&m_mem_root,
+ (char *)m_param_begin, m_params.length);
if (m_returns_begin && m_returns_end)
{
/* QQ KLUDGE: We can't seem to cut out just the type in the parser
@@ -202,12 +204,13 @@ sp_head::init_strings(LEX_STRING *name, LEX *lex)
p-= 1;
m_returns_end= (uchar *)p+1;
m_retstr.length= m_returns_end - m_returns_begin;
- m_retstr.str= lex->thd->strmake((char *)m_returns_begin, m_retstr.length);
+ m_retstr.str= strmake_root(&m_mem_root,
+ (char *)m_returns_begin, m_retstr.length);
}
m_body.length= lex->end_of_query - m_body_begin;
- m_body.str= lex->thd->strmake((char *)m_body_begin, m_body.length);
+ m_body.str= strmake_root(&m_mem_root, (char *)m_body_begin, m_body.length);
m_defstr.length= lex->end_of_query - lex->buf;
- m_defstr.str= lex->thd->strmake((char *)lex->buf, m_defstr.length);
+ m_defstr.str= strmake_root(&m_mem_root, (char *)lex->buf, m_defstr.length);
DBUG_VOID_RETURN;
}
@@ -664,6 +667,34 @@ sp_head::backpatch(sp_label_t *lab)
}
}
+void
+sp_head::set_info(char *definer, uint definerlen,
+ longlong created, longlong modified,
+ st_sp_chistics *chistics)
+{
+ char *p= strchr(definer, '@');
+ uint len;
+
+ if (! p)
+ p= definer; // Weird...
+ len= p-definer;
+ m_definer_user.str= strmake_root(&m_mem_root, definer, len);
+ m_definer_user.length= len;
+ len= definerlen-len-1;
+ m_definer_host.str= strmake_root(&m_mem_root, p+1, len);
+ m_definer_host.length= len;
+ m_created= created;
+ m_modified= modified;
+ m_chistics= (st_sp_chistics *)alloc_root(&m_mem_root, sizeof(st_sp_chistics));
+ memcpy(m_chistics, chistics, sizeof(st_sp_chistics));
+ if (m_chistics->comment.length == 0)
+ m_chistics->comment.str= 0;
+ else
+ m_chistics->comment.str= strmake_root(&m_mem_root,
+ m_chistics->comment.str,
+ m_chistics->comment.length);
+}
+
int
sp_head::show_create_procedure(THD *thd)
{
@@ -1041,3 +1072,64 @@ sp_instr_cfetch::execute(THD *thd, uint *nextp)
*nextp= m_ip+1;
DBUG_RETURN(res);
}
+
+
+//
+// Security context swapping
+//
+
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ ctxp->changed= (sp->m_chistics->suid != IS_NOT_SUID &&
+ (strcmp(sp->m_definer_user.str, thd->priv_user) ||
+ strcmp(sp->m_definer_host.str, thd->priv_host)));
+
+ if (ctxp->changed)
+ {
+ ctxp->master_access= thd->master_access;
+ ctxp->db_access= thd->db_access;
+ ctxp->db= thd->db;
+ ctxp->db_length= thd->db_length;
+ ctxp->priv_user= thd->priv_user;
+ strncpy(ctxp->priv_host, thd->priv_host, sizeof(ctxp->priv_host));
+ ctxp->user= thd->user;
+ ctxp->host= thd->host;
+ ctxp->ip= thd->ip;
+
+ /* Change thise just to do the acl_getroot_no_password */
+ thd->user= sp->m_definer_user.str;
+ thd->host= thd->ip = sp->m_definer_host.str;
+
+ if (acl_getroot_no_password(thd))
+ { // Failed, run as invoker for now
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->db= ctxp->db;
+ thd->db_length= ctxp->db_length;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+
+ /* Restore these immiediately */
+ thd->user= ctxp->user;
+ thd->host= ctxp->host;
+ thd->ip= ctxp->ip;
+ }
+}
+
+void
+sp_restore_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp)
+{
+ if (ctxp->changed)
+ {
+ ctxp->changed= FALSE;
+ thd->master_access= ctxp->master_access;
+ thd->db_access= ctxp->db_access;
+ thd->db= ctxp->db;
+ thd->db_length= ctxp->db_length;
+ thd->priv_user= ctxp->priv_user;
+ strncpy(thd->priv_host, ctxp->priv_host, sizeof(thd->priv_host));
+ }
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 33c6bd4fe38..bf3dc012a08 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -61,8 +61,8 @@ public:
LEX_STRING m_retstr; // For FUNCTIONs only
LEX_STRING m_body;
LEX_STRING m_defstr;
- char *m_creator;
- uint m_creatorlen;
+ LEX_STRING m_definer_user;
+ LEX_STRING m_definer_host;
longlong m_created;
longlong m_modified;
// Pointers set during parsing
@@ -159,16 +159,9 @@ public:
return sp_map_result_type(m_returns);
}
- void set_info(char *creator, uint creatorlen,
+ void set_info(char *definer, uint definerlen,
longlong created, longlong modified,
- st_sp_chistics *chistics)
- {
- m_creator= creator;
- m_creatorlen= creatorlen;
- m_created= created;
- m_modified= modified;
- m_chistics= chistics;
- }
+ st_sp_chistics *chistics);
inline void reset_thd_mem_root(THD *thd)
{
@@ -642,4 +635,24 @@ private:
}; // class sp_instr_cfetch : public sp_instr
+struct st_sp_security_context
+{
+ bool changed;
+ uint master_access;
+ uint db_access;
+ char *db;
+ uint db_length;
+ char *priv_user;
+ char priv_host[MAX_HOSTNAME];
+ char *user;
+ char *host;
+ char *ip;
+};
+
+void
+sp_change_security_context(THD *thd, sp_head *sp, st_sp_security_context *ctxp);
+
+void
+sp_restore_security_context(THD *thd, sp_head *sp,st_sp_security_context *ctxp);
+
#endif /* _SP_HEAD_H_ */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 479c0e5af7b..2527edc1643 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -778,6 +778,66 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh,
}
+/*
+ * This is like acl_getroot() above, but it doesn't check password,
+ * and we don't care about the user resources.
+ * Used to get access rights for SQL SECURITY DEFINER invokation of
+ * stored procedures.
+ */
+int acl_getroot_no_password(THD *thd)
+{
+ ulong user_access= NO_ACCESS;
+ int res= 1;
+ ACL_USER *acl_user= 0;
+ DBUG_ENTER("acl_getroot_no_password");
+
+ if (!initialized)
+ {
+ /*
+ here if mysqld's been started with --skip-grant-tables option.
+ */
+ thd->priv_user= (char *) ""; // privileges for
+ *thd->priv_host= '\0'; // the user are unknown
+ thd->master_access= ~NO_ACCESS; // everything is allowed
+ DBUG_RETURN(0);
+ }
+
+ VOID(pthread_mutex_lock(&acl_cache->lock));
+
+ /*
+ Find acl entry in user database.
+ This is specially tailored to suit the check we do for CALL of
+ a stored procedure; thd->user is set to what is actually a
+ priv_user, which can be ''.
+ */
+ for (uint i=0 ; i < acl_users.elements ; i++)
+ {
+ acl_user= dynamic_element(&acl_users,i,ACL_USER*);
+ if ((!acl_user->user && (!thd->user || !thd->user[0])) ||
+ (acl_user->user && strcmp(thd->user, acl_user->user) == 0))
+ {
+ if (compare_hostname(&acl_user->host, thd->host, thd->ip))
+ {
+ res= 0;
+ break;
+ }
+ }
+ }
+
+ if (acl_user)
+ {
+ thd->master_access= acl_user->access;
+ thd->priv_user= acl_user->user ? thd->user : (char *) "";
+
+ if (acl_user->host.hostname)
+ strmake(thd->priv_host, acl_user->host.hostname, MAX_HOSTNAME);
+ else
+ *thd->priv_host= 0;
+ }
+ VOID(pthread_mutex_unlock(&acl_cache->lock));
+ DBUG_RETURN(res);
+}
+
static byte* check_get_key(ACL_USER *buff,uint *length,
my_bool not_used __attribute__((unused)))
{
diff --git a/sql/sql_acl.h b/sql/sql_acl.h
index 8b8115b10db..9858b403d27 100644
--- a/sql/sql_acl.h
+++ b/sql/sql_acl.h
@@ -139,6 +139,7 @@ ulong acl_get(const char *host, const char *ip,
const char *user, const char *db, my_bool db_is_pattern);
int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd,
uint passwd_len);
+int acl_getroot_no_password(THD *thd);
bool acl_check_host(const char *host, const char *ip);
bool check_change_password(THD *thd, const char *host, const char *user);
bool change_password(THD *thd, const char *host, const char *user,
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 08a11d7c729..f5f07a651bd 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3495,6 +3495,7 @@ mysql_execute_command(THD *thd)
}
else
{
+ st_sp_security_context save_ctx;
uint smrx;
LINT_INIT(smrx);
@@ -3526,8 +3527,12 @@ mysql_execute_command(THD *thd)
thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
}
+ sp_change_security_context(thd, sp, &save_ctx);
+
res= sp->execute_procedure(thd, &lex->value_list);
+ sp_restore_security_context(thd, sp, &save_ctx);
+
#ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok;
#endif
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index cbafd69b9ac..a4832e1d913 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -2712,7 +2712,7 @@ alter:
LEX *lex= Lex;
bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics));
- Lex->name= 0;
+ lex->name= 0;
}
sp_a_chistics
{