1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

Fix for bug#36544 "DROP USER does not remove stored function

privileges".

The first problem was that DROP USER didn't properly remove privileges 
on stored functions from in-memory structures. So the dropped user
could have called stored functions on which he had privileges before
being dropped while his connection was still around.
Even worse if a new user with the same name was created he would
inherit privileges on stored functions from the dropped user.
Similar thing happened with old user name and function privileges
during RENAME USER.

This problem stemmed from the fact that the handle_grant_data() function
which handled DROP/RENAME USER didn't take any measures to update
in-memory hash with information about function privileges after
updating them on disk.

This patch solves this problem by adding code doing just that.

The second problem was that RENAME USER didn't properly update in-memory
structures describing table-level privileges and privileges on stored 
procedures. As result such privileges could have been lost after a rename
(i.e. not associated with the new name of user) and inherited by a new
user with the same name as the old name of the original user.

This problem was caused by code handling RENAME USER in
handle_grant_struct() which [sic!]:
a) tried to update wrong (tables) hash when updating stored procedure
   privileges for new user name.
b) passed wrong arguments to function performing the hash update and
   didn't take into account the way in which such update could have
   changed the order of the hash elements.

This patch solves this problem by ensuring that a) the correct hash
is updated, b) correct arguments are used for the hash_update()
function and c) we take into account possible changes in the order
of hash elements.
This commit is contained in:
Dmitry Lenev
2011-02-07 14:01:19 +03:00
parent 6908d1ef78
commit e960abc7cf
6 changed files with 451 additions and 53 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2000-2003 MySQL AB
/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -5048,18 +5048,15 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
}
/*
/**
Handle an in-memory privilege structure.
SYNOPSIS
handle_grant_struct()
struct_no The number of the structure to handle (0..3).
drop If user_from is to be dropped.
user_from The the user to be searched/dropped/renamed.
user_to The new name for the user if to be renamed,
NULL otherwise.
@param struct_no The number of the structure to handle (0..4).
@param drop If user_from is to be dropped.
@param user_from The the user to be searched/dropped/renamed.
@param user_to The new name for the user if to be renamed, NULL otherwise.
DESCRIPTION
@note
Scan through all elements in an in-memory grant structure and apply
the requested operation.
Delete from grant structure if drop is true.
@ -5069,12 +5066,12 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop,
0 acl_users
1 acl_dbs
2 column_priv_hash
3 procs_priv_hash
3 proc_priv_hash
4 func_priv_hash
RETURN
> 0 At least one element matched.
0 OK, but no element matched.
-1 Wrong arguments to function
@retval > 0 At least one element matched.
@retval 0 OK, but no element matched.
@retval -1 Wrong arguments to function.
*/
static int handle_grant_struct(uint struct_no, bool drop,
@ -5088,6 +5085,7 @@ static int handle_grant_struct(uint struct_no, bool drop,
ACL_USER *UNINIT_VAR(acl_user);
ACL_DB *UNINIT_VAR(acl_db);
GRANT_NAME *UNINIT_VAR(grant_name);
HASH *UNINIT_VAR(grant_name_hash);
DBUG_ENTER("handle_grant_struct");
DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
struct_no, user_from->user.str, user_from->host.str));
@ -5104,9 +5102,15 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
elements= column_priv_hash.records;
grant_name_hash= &column_priv_hash;
break;
case 3:
elements= proc_priv_hash.records;
grant_name_hash= &proc_priv_hash;
break;
case 4:
elements= func_priv_hash.records;
grant_name_hash= &func_priv_hash;
break;
default:
return -1;
@ -5136,16 +5140,13 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx);
case 3:
case 4:
grant_name= (GRANT_NAME*) hash_element(grant_name_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
case 3:
grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx);
user= grant_name->user;
host= grant_name->host.hostname;
break;
default:
assert(0);
}
@ -5176,14 +5177,25 @@ static int handle_grant_struct(uint struct_no, bool drop,
break;
case 2:
hash_delete(&column_priv_hash, (byte*) grant_name);
break;
case 3:
hash_delete(&proc_priv_hash, (byte*) grant_name);
case 4:
hash_delete(grant_name_hash, (byte*) grant_name);
break;
}
elements--;
/*
- If we are iterating through an array then we just have moved all
elements after the current element one position closer to its head.
This means that we have to take another look at the element at
current position as it is a new element from the array's tail.
- If we are iterating through a hash the current element was replaced
with one of elements from the tail. So we also have to take a look
at the new element in current position.
Note that in our HASH implementation hash_delete() won't move any
elements with position after current one to position before the
current (i.e. from the tail to the head), so it is safe to continue
iteration without re-starting.
*/
idx--;
}
else if ( user_to )
@ -5201,22 +5213,41 @@ static int handle_grant_struct(uint struct_no, bool drop,
case 2:
case 3:
/*
Update the grant structure with the new user name and
host name
*/
grant_name->set_user_details(user_to->host.str, grant_name->db,
user_to->user.str, grant_name->tname,
TRUE);
case 4:
{
/*
Save old hash key and its length to be able properly update
element position in hash.
*/
char *old_key= grant_name->hash_key;
size_t old_key_length= grant_name->key_length;
/*
Since username is part of the hash key, when the user name
is renamed, the hash key is changed. Update the hash to
ensure that the position matches the new hash key value
*/
hash_update(&column_priv_hash, (byte*) grant_name,
(byte *) grant_name->hash_key, grant_name->key_length);
break;
/*
Update the grant structure with the new user name and host name.
*/
grant_name->set_user_details(user_to->host.str, grant_name->db,
user_to->user.str, grant_name->tname,
TRUE);
/*
Since username is part of the hash key, when the user name
is renamed, the hash key is changed. Update the hash to
ensure that the position matches the new hash key value
*/
hash_update(grant_name_hash, (byte*) grant_name, (byte*) old_key,
old_key_length);
/*
hash_update() operation could have moved element from the tail
of the hash to the current position. So we need to take a look
at the element in current position once again.
Thanks to the fact that hash_update() for our HASH implementation
won't move any elements from the tail of the hash to the positions
before the current one (a.k.a. head) it is safe to continue
iteration without restarting.
*/
idx--;
break;
}
}
}
else
@ -5302,7 +5333,7 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
}
}
/* Handle procedures table. */
/* Handle stored routines table. */
if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0)
{
/* Handle of table failed, don't touch in-memory array. */
@ -5319,6 +5350,15 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop,
if (! drop && ! user_to)
goto end;
}
/* Handle funcs array. */
if (((handle_grant_struct(4, drop, user_from, user_to) && ! result) ||
found) && ! result)
{
result= 1; /* At least one record/element found. */
/* If search is requested, we do not need to search further. */
if (! drop && ! user_to)
goto end;
}
}
/* Handle tables table. */