Commit 5550f2b8 authored by unknown's avatar unknown

Avoid spurious error when restoring INFORMATION_SCHEMA as the current

database after failing to execute a stored procedure in an inaccessible
database. (Bug #12318)


mysql-test/r/sp-security.result:
  Update results
mysql-test/t/sp-security.test:
  Add regression test
sql/mysql_priv.h:
  Add additional argument to mysql_change_db()
sql/sp.cc:
  Use mysql_change_db(), get rid of sp_change_db().
sql/sp.h:
  Get rid of sp_change_db().
sql/sql_db.cc:
  Handle no_access_check flag to mysql_change_db, and remove the send_ok()
  call.
sql/sql_parse.cc:
  Add extra argument to mysql_change_db(), and call send_ok() after
  successful calls to same (since it no longer does it for us).
parent f7f6b3e9
...@@ -236,3 +236,16 @@ drop procedure bug7291_2; ...@@ -236,3 +236,16 @@ drop procedure bug7291_2;
drop procedure bug7291_0; drop procedure bug7291_0;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost;
drop user user1@localhost; drop user user1@localhost;
drop database if exists mysqltest_1;
create database mysqltest_1;
create procedure mysqltest_1.p1()
begin
select 1 from dual;
end//
grant usage on *.* to mysqltest_1@localhost;
call mysqltest_1.p1();
ERROR 42000: execute command denied to user 'mysqltest_1'@'localhost' for routine 'mysqltest_1.p1'
drop procedure mysqltest_1.p1;
drop database mysqltest_1;
revoke usage on *.* from mysqltest_1@localhost;
drop user mysqltest_1@localhost;
...@@ -371,3 +371,39 @@ drop procedure bug7291_0; ...@@ -371,3 +371,39 @@ drop procedure bug7291_0;
disconnect user1; disconnect user1;
REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost; REVOKE ALL PRIVILEGES, GRANT OPTION FROM user1@localhost;
drop user user1@localhost; drop user user1@localhost;
#
# Bug #12318: Wrong error message when accessing an inaccessible stored
# procedure in another database when the current database is
# information_schema.
#
--disable_warnings
drop database if exists mysqltest_1;
--enable_warnings
create database mysqltest_1;
delimiter //;
create procedure mysqltest_1.p1()
begin
select 1 from dual;
end//
delimiter ;//
grant usage on *.* to mysqltest_1@localhost;
connect (n1,localhost,mysqltest_1,,information_schema,$MASTER_MYPORT,$MASTER_MYSOCK);
connection n1;
--error 1370
call mysqltest_1.p1();
disconnect n1;
connection default;
drop procedure mysqltest_1.p1;
drop database mysqltest_1;
revoke usage on *.* from mysqltest_1@localhost;
drop user mysqltest_1@localhost;
# End of 5.0 bugs.
...@@ -587,7 +587,7 @@ int quick_rm_table(enum db_type base,const char *db, ...@@ -587,7 +587,7 @@ int quick_rm_table(enum db_type base,const char *db,
const char *table_name); const char *table_name);
void close_cached_table(THD *thd, TABLE *table); void close_cached_table(THD *thd, TABLE *table);
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list); bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
bool mysql_change_db(THD *thd,const char *name); bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
void mysql_parse(THD *thd,char *inBuf,uint length); void mysql_parse(THD *thd,char *inBuf,uint length);
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
bool is_update_query(enum enum_sql_command command); bool is_update_query(enum enum_sql_command command);
......
...@@ -427,7 +427,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ...@@ -427,7 +427,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
LEX *newlex= thd->lex; LEX *newlex= thd->lex;
sp_head *sp= newlex->sphead; sp_head *sp= newlex->sphead;
if (dbchanged && (ret= sp_change_db(thd, olddb, 1))) if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
goto done; goto done;
if (sp) if (sp)
{ {
...@@ -438,7 +438,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) ...@@ -438,7 +438,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
} }
else else
{ {
if (dbchanged && (ret= sp_change_db(thd, olddb, 1))) if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
goto done; goto done;
*sphp= thd->lex->sphead; *sphp= thd->lex->sphead;
(*sphp)->set_info((char *)definer, (uint)strlen(definer), (*sphp)->set_info((char *)definer, (uint)strlen(definer),
...@@ -594,7 +594,7 @@ db_create_routine(THD *thd, int type, sp_head *sp) ...@@ -594,7 +594,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
done: done:
close_thread_tables(thd); close_thread_tables(thd);
if (dbchanged) if (dbchanged)
(void)sp_change_db(thd, olddb, 1); (void)mysql_change_db(thd, olddb, 1);
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
...@@ -1612,112 +1612,10 @@ sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen, ...@@ -1612,112 +1612,10 @@ sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
} }
else else
{ {
int ret= sp_change_db(thd, newdb, no_access_check); int ret= mysql_change_db(thd, newdb, no_access_check);
if (! ret) if (! ret)
*dbchangedp= TRUE; *dbchangedp= TRUE;
DBUG_RETURN(ret); DBUG_RETURN(ret);
} }
} }
/*
Change database.
SYNOPSIS
sp_change_db()
thd Thread handler
name Database name
empty_is_ok True= it's ok with "" as name
no_access_check True= don't do access check
DESCRIPTION
This is the same as mysql_change_db(), but with some extra
arguments for Stored Procedure usage; doing implicit "use"
when executing an SP in a different database.
We also use different error routines, since this might be
invoked from a function when executing a query or statement.
Note: We would have prefered to reuse mysql_change_db(), but
the error handling in particular made that too awkward, so
we (reluctantly) have a "copy" here.
RETURN VALUES
0 ok
1 error
*/
int
sp_change_db(THD *thd, char *name, bool no_access_check)
{
int length, db_length;
char *dbname=my_strdup((char*) name,MYF(MY_WME));
char path[FN_REFLEN];
HA_CREATE_INFO create;
DBUG_ENTER("sp_change_db");
DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check));
db_length= (!dbname ? 0 : strip_sp(dbname));
if (dbname && db_length)
{
if ((db_length > NAME_LEN) || check_db_name(dbname))
{
my_error(ER_WRONG_DB_NAME, MYF(0), dbname);
x_free(dbname);
DBUG_RETURN(1);
}
}
if (dbname && db_length)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (! no_access_check)
{
ulong db_access;
if (test_all_bits(thd->master_access,DB_ACLS))
db_access=DB_ACLS;
else
db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
thd->master_access);
if (!(db_access & DB_ACLS) &&
(!grant_option || check_grant_db(thd,dbname)))
{
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
thd->priv_user,
thd->priv_host,
dbname);
mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
thd->priv_user,
thd->priv_host,
dbname);
my_free(dbname,MYF(0));
DBUG_RETURN(1);
}
}
#endif
(void) sprintf(path,"%s/%s",mysql_data_home,dbname);
length=unpack_dirname(path,path); // Convert if not unix
if (length && path[length-1] == FN_LIBCHAR)
path[length-1]=0; // remove ending '\'
if (access(path,F_OK))
{
my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
my_free(dbname,MYF(0));
DBUG_RETURN(1);
}
}
x_free(thd->db);
thd->db=dbname; // THD::~THD will free this
thd->db_length=db_length;
if (dbname && db_length)
{
strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
load_db_opt(thd, path, &create);
thd->db_charset= create.default_table_charset ?
create.default_table_charset :
thd->variables.collation_server;
thd->variables.collation_database= thd->db_charset;
}
DBUG_RETURN(0);
}
...@@ -112,8 +112,4 @@ int ...@@ -112,8 +112,4 @@ int
sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax, sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax,
bool no_access_check, bool *dbchangedp); bool no_access_check, bool *dbchangedp);
// Like mysql_change_db() but handles empty db name and the send_ok() problem.
int
sp_change_db(THD *thd, char *db, bool no_access_check);
#endif /* _SP_H_ */ #endif /* _SP_H_ */
...@@ -996,8 +996,9 @@ static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, ...@@ -996,8 +996,9 @@ static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp,
SYNOPSIS SYNOPSIS
mysql_change_db() mysql_change_db()
thd Thread handler thd Thread handler
name Databasename name Databasename
no_access_check True= don't do access check
DESCRIPTION DESCRIPTION
Becasue the database name may have been given directly from the Becasue the database name may have been given directly from the
...@@ -1009,15 +1010,16 @@ static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, ...@@ -1009,15 +1010,16 @@ static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp,
replication slave SQL thread (for that thread, setting of thd->db is done replication slave SQL thread (for that thread, setting of thd->db is done
in ::exec_event() methods of log_event.cc). in ::exec_event() methods of log_event.cc).
This function does not send the error message to the client, if that This function does not send anything, including error messages to the
should be sent to the client, call net_send_error after this function client, if that should be sent to the client, call net_send_error after
this function.
RETURN VALUES RETURN VALUES
0 ok 0 ok
1 error 1 error
*/ */
bool mysql_change_db(THD *thd, const char *name) bool mysql_change_db(THD *thd, const char *name, bool no_access_check)
{ {
int length, db_length; int length, db_length;
char *dbname=my_strdup((char*) name,MYF(MY_WME)); char *dbname=my_strdup((char*) name,MYF(MY_WME));
...@@ -1053,23 +1055,25 @@ bool mysql_change_db(THD *thd, const char *name) ...@@ -1053,23 +1055,25 @@ bool mysql_change_db(THD *thd, const char *name)
} }
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
if (test_all_bits(thd->master_access,DB_ACLS)) if (!no_access_check) {
db_access=DB_ACLS; if (test_all_bits(thd->master_access,DB_ACLS))
else db_access=DB_ACLS;
db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) | else
thd->master_access); db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname))) thd->master_access);
{ if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), {
thd->priv_user, my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
thd->priv_host, thd->priv_user,
dbname); thd->priv_host,
mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), dbname);
thd->priv_user, mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
thd->priv_host, thd->priv_user,
dbname); thd->priv_host,
my_free(dbname,MYF(0)); dbname);
DBUG_RETURN(1); my_free(dbname,MYF(0));
DBUG_RETURN(1);
}
} }
#endif #endif
(void) sprintf(path,"%s/%s",mysql_data_home,dbname); (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
...@@ -1083,12 +1087,12 @@ bool mysql_change_db(THD *thd, const char *name) ...@@ -1083,12 +1087,12 @@ bool mysql_change_db(THD *thd, const char *name)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
end: end:
send_ok(thd);
x_free(thd->db); x_free(thd->db);
thd->db=dbname; // THD::~THD will free this thd->db=dbname; // THD::~THD will free this
thd->db_length=db_length; thd->db_length=db_length;
#ifndef NO_EMBEDDED_ACCESS_CHECKS #ifndef NO_EMBEDDED_ACCESS_CHECKS
thd->db_access=db_access; if (!no_access_check)
thd->db_access=db_access;
#endif #endif
if (schema_db) if (schema_db)
{ {
......
...@@ -275,7 +275,7 @@ int check_user(THD *thd, enum enum_server_command command, ...@@ -275,7 +275,7 @@ int check_user(THD *thd, enum enum_server_command command,
{ {
thd->db= 0; thd->db= 0;
thd->db_length= 0; thd->db_length= 0;
if (mysql_change_db(thd, db)) if (mysql_change_db(thd, db, FALSE))
{ {
/* Send the error to the client */ /* Send the error to the client */
net_send_error(thd); net_send_error(thd);
...@@ -284,8 +284,7 @@ int check_user(THD *thd, enum enum_server_command command, ...@@ -284,8 +284,7 @@ int check_user(THD *thd, enum enum_server_command command,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
} }
else send_ok(thd);
send_ok(thd);
DBUG_RETURN(0); DBUG_RETURN(0);
#else #else
...@@ -410,7 +409,7 @@ int check_user(THD *thd, enum enum_server_command command, ...@@ -410,7 +409,7 @@ int check_user(THD *thd, enum enum_server_command command,
/* Change database if necessary */ /* Change database if necessary */
if (db && db[0]) if (db && db[0])
{ {
if (mysql_change_db(thd, db)) if (mysql_change_db(thd, db, FALSE))
{ {
/* Send error to the client */ /* Send error to the client */
net_send_error(thd); net_send_error(thd);
...@@ -419,8 +418,7 @@ int check_user(THD *thd, enum enum_server_command command, ...@@ -419,8 +418,7 @@ int check_user(THD *thd, enum enum_server_command command,
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
} }
else send_ok(thd);
send_ok(thd);
thd->password= test(passwd_len); // remember for error messages thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */ /* Ready to handle queries */
DBUG_RETURN(0); DBUG_RETURN(0);
...@@ -1514,8 +1512,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1514,8 +1512,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
&LOCK_status); &LOCK_status);
thd->convert_string(&tmp, system_charset_info, thd->convert_string(&tmp, system_charset_info,
packet, strlen(packet), thd->charset()); packet, strlen(packet), thd->charset());
if (!mysql_change_db(thd, tmp.str)) if (!mysql_change_db(thd, tmp.str, FALSE))
{
mysql_log.write(thd,command,"%s",thd->db); mysql_log.write(thd,command,"%s",thd->db);
send_ok(thd);
}
break; break;
} }
#ifdef HAVE_REPLICATION #ifdef HAVE_REPLICATION
...@@ -3407,7 +3408,8 @@ mysql_execute_command(THD *thd) ...@@ -3407,7 +3408,8 @@ mysql_execute_command(THD *thd)
} }
#endif #endif
case SQLCOM_CHANGE_DB: case SQLCOM_CHANGE_DB:
mysql_change_db(thd,select_lex->db); if (!mysql_change_db(thd,select_lex->db,FALSE))
send_ok(thd);
break; break;
case SQLCOM_LOAD: case SQLCOM_LOAD:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment