@ -265,6 +265,7 @@ public:
const char * ssl_cipher , * x509_issuer , * x509_subject ;
LEX_STRING plugin ;
LEX_STRING auth_string ;
LEX_STRING default_rolename ;
ACL_USER * copy ( MEM_ROOT * root )
{
@ -284,6 +285,8 @@ public:
dst - > plugin . str = strmake_root ( root , plugin . str , plugin . length ) ;
dst - > auth_string . str = safe_strdup_root ( root , auth_string . str ) ;
dst - > host . hostname = safe_strdup_root ( root , host . hostname ) ;
dst - > default_rolename . str = safe_strdup_root ( root , default_rolename . str ) ;
dst - > default_rolename . length = default_rolename . length ;
bzero ( & dst - > role_grants , sizeof ( role_grants ) ) ;
return dst ;
}
@ -702,6 +705,7 @@ bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username,
# define NORMAL_HANDSHAKE_SIZE 6
# define ROLE_ASSIGN_COLUMN_IDX 43
# define DEFAULT_ROLE_COLUMN_IDX 44
/* various flags valid for ACL_USER */
# define IS_ROLE (1L << 0)
/* Flag to mark that a ROLE is on the recursive DEPTH_FIRST_SEARCH stack */
@ -1347,6 +1351,14 @@ static bool acl_load(THD *thd, TABLE_LIST *tables)
( void ) my_init_dynamic_array ( & user . role_grants , sizeof ( ACL_ROLE * ) ,
8 , 8 , MYF ( 0 ) ) ;
/* check default role, if any */
if ( ! is_role & & table - > s - > fields > = 45 )
{
user . default_rolename . str = get_field ( & acl_memroot , table - > field [ 44 ] ) ;
user . default_rolename . length = user . default_rolename . str ?
strlen ( user . default_rolename . str ) : 0 ;
}
if ( is_role )
{
DBUG_PRINT ( " info " , ( " Found role %s " , user . user . str ) ) ;
@ -1867,7 +1879,8 @@ bool acl_getroot(Security_context *sctx, char *user, char *host,
DBUG_RETURN ( res ) ;
}
int acl_check_setrole ( THD * thd , char * rolename , ulonglong * access )
int check_user_can_set_role ( const char * host , const char * user ,
const char * rolename , ulonglong * access )
{
ACL_ROLE * role ;
ACL_USER_BASE * acl_user_base ;
@ -1882,8 +1895,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
{
/* have to clear the privileges */
/* get the current user */
acl_user = find_user_exact ( thd - > security_ctx - > priv_host ,
thd - > security_ctx - > priv_user ) ;
acl_user = find_user_exact ( host , user ) ;
if ( acl_user = = NULL )
{
my_error ( ER_INVALID_CURRENT_USER , MYF ( 0 ) , rolename ) ;
@ -1911,9 +1923,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
continue ;
acl_user = ( ACL_USER * ) acl_user_base ;
/* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
if ( acl_user - > wild_eq ( thd - > security_ctx - > priv_user ,
thd - > security_ctx - > host ) )
if ( acl_user - > wild_eq ( user , host ) )
{
is_granted = TRUE ;
break ;
@ -1935,6 +1945,15 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access)
end :
mysql_mutex_unlock ( & acl_cache - > lock ) ;
return result ;
}
int acl_check_setrole ( THD * thd , char * rolename , ulonglong * access )
{
/* Yes! priv_user@host. Don't ask why - that's what check_access() does. */
return check_user_can_set_role ( thd - > security_ctx - > host ,
thd - > security_ctx - > priv_user ,
rolename , access ) ;
}
@ -1960,7 +1979,6 @@ int acl_setrole(THD *thd, char *rolename, ulonglong access)
return 0 ;
}
static uchar * check_get_key ( ACL_USER * buff , size_t * length ,
my_bool not_used __attribute__ ( ( unused ) ) )
{
@ -2068,6 +2086,7 @@ static void acl_insert_user(const char *user, const char *host,
mysql_mutex_assert_owner ( & acl_cache - > lock ) ;
bzero ( & acl_user , sizeof ( acl_user ) ) ;
acl_user . user . str = * user ? strdup_root ( & acl_memroot , user ) : 0 ;
acl_user . user . length = strlen ( user ) ;
update_hostname ( & acl_user . host , safe_strdup_root ( & acl_memroot , host ) ) ;
@ -2518,41 +2537,40 @@ bool acl_check_host(const char *host, const char *ip)
return 1 ; // Host is not allowed
}
/**
Check if the user is allowed to change password
Check if the user is allowed to alter the mysql . user table
@ param thd THD
@ param host Hostname for the user
@ param user User name
@ param new_password New password
@ param new_password_len The length of the new password
new_password cannot be NULL
@ return Error status
@ retval 0 OK
@ retval 1 ERROR ; In this case the error is sent to the client .
@ retval 1 skip grant tables error
@ retval 2 ANONYMOUS user error
@ retval 3 the entry to change is a role , not a user
@ retval 4 no UPDATE_ACL error
*/
int check_change_password ( THD * thd , const char * host , const char * user ,
char * new_password , uint new_password_len )
int check_alter_user ( THD * thd , const char * host , const char * user )
{
int error = 1 ;
if ( ! initialized )
{
my_error ( ER_OPTION_PREVENTS_STATEMENT , MYF ( 0 ) , " --skip-grant-tables " ) ;
return ( 1 ) ;
goto end ;
}
if ( ! thd - > slave_thread & & ! thd - > security_ctx - > priv_user [ 0 ] )
{
my_message ( ER_PASSWORD_ANONYMOUS_USER , ER ( ER_PASSWORD_ANONYMOUS_USER ) ,
MYF ( 0 ) ) ;
return ( 1 ) ;
goto end ;
}
if ( ! host ) // Role
{
my_error ( ER_PASSWORD_NO_MATCH , MYF ( 0 ) ) ;
return 1 ;
goto end ;
}
if ( ! thd - > slave_thread & &
( strcmp ( thd - > security_ctx - > priv_user , user ) | |
@ -2560,16 +2578,44 @@ int check_change_password(THD *thd, const char *host, const char *user,
thd - > security_ctx - > priv_host ) ) )
{
if ( check_access ( thd , UPDATE_ACL , " mysql " , NULL , NULL , 1 , 0 ) )
return ( 1 ) ;
goto end ;
}
error = 0 ;
end :
return error ;
}
/**
Check if the user is allowed to change password
@ param thd THD
@ param host Hostname for the user
@ param user User name
@ param new_password New password
@ param new_password_len The length of the new password
new_password cannot be NULL
@ return Error status
@ retval 0 OK
@ retval 1 ERROR ; In this case the error is sent to the client .
*/
int check_change_password ( THD * thd , const char * host , const char * user ,
char * new_password , uint new_password_len )
{
if ( check_alter_user ( thd , host , user ) )
return 1 ;
size_t len = strlen ( new_password ) ;
if ( len & & len ! = SCRAMBLED_PASSWORD_CHAR_LENGTH & &
len ! = SCRAMBLED_PASSWORD_CHAR_LENGTH_323 )
{
my_error ( ER_PASSWD_LENGTH , MYF ( 0 ) , SCRAMBLED_PASSWORD_CHAR_LENGTH ) ;
return - 1 ;
return 1 ;
}
return ( 0 ) ;
return 0 ;
}
@ -2669,6 +2715,152 @@ end:
DBUG_RETURN ( result ) ;
}
int acl_check_set_default_role ( THD * thd , const char * host , const char * user )
{
return check_alter_user ( thd , host , user ) ;
}
int acl_set_default_role ( THD * thd , const char * host , const char * user ,
const char * rolename )
{
TABLE_LIST tables ;
TABLE * table ;
char user_key [ MAX_KEY_LENGTH ] ;
int result = 1 ;
int error ;
bool clear_role = FALSE ;
Rpl_filter * rpl_filter ;
enum_binlog_format save_binlog_format ;
DBUG_ENTER ( " acl_set_default_role " ) ;
DBUG_PRINT ( " enter " , ( " host: '%s' user: '%s' rolename: '%s' " ,
safe_str ( user ) , safe_str ( host ) , safe_str ( rolename ) ) ) ;
if ( rolename = = current_role . str ) {
if ( ! thd - > security_ctx - > priv_role [ 0 ] )
rolename = " NONE " ;
else
rolename = thd - > security_ctx - > priv_role ;
}
if ( check_user_can_set_role ( host , user , rolename , NULL ) )
DBUG_RETURN ( result ) ;
if ( ! strcasecmp ( rolename , " NONE " ) )
clear_role = TRUE ;
tables . init_one_table ( " mysql " , 5 , " user " , 4 , " user " , TL_WRITE ) ;
# ifdef HAVE_REPLICATION
/*
GRANT and REVOKE are applied the slave in / exclusion rules as they are
some kind of updates to the mysql . % tables .
*/
if ( thd - > slave_thread & &
( rpl_filter = thd - > system_thread_info . rpl_sql_info - > rpl_filter ) - > is_on ( ) )
{
/*
The tables must be marked " updating " so that tables_ok ( ) takes them into
account in tests . It ' s ok to leave ' updating ' set after tables_ok .
*/
tables . updating = 1 ;
/* Thanks to bzero, tables.next==0 */
if ( ! ( thd - > spcont | | rpl_filter - > tables_ok ( 0 , & tables ) ) )
DBUG_RETURN ( 0 ) ;
}
# endif
if ( ! ( table = open_ltable ( thd , & tables , TL_WRITE , MYSQL_LOCK_IGNORE_TIMEOUT ) ) )
DBUG_RETURN ( 1 ) ;
/*
This statement will be replicated as a statement , even when using
row - based replication . The flag will be reset at the end of the
statement .
This has to be handled here as it ' s called by set_var . cc , which is
not automaticly handled by sql_parse . cc
*/
save_binlog_format = thd - > set_current_stmt_binlog_format_stmt ( ) ;
mysql_mutex_lock ( & acl_cache - > lock ) ;
ACL_USER * acl_user ;
if ( ! ( acl_user = find_user_exact ( host , user ) ) )
{
mysql_mutex_unlock ( & acl_cache - > lock ) ;
my_message ( ER_PASSWORD_NO_MATCH , ER ( ER_PASSWORD_NO_MATCH ) , MYF ( 0 ) ) ;
goto end ;
}
if ( ! clear_role ) {
/* set new default_rolename */
acl_user - > default_rolename . str = safe_strdup_root ( & acl_memroot , rolename ) ;
acl_user - > default_rolename . length = strlen ( rolename ) ;
}
else
{
/* clear the default_rolename */
acl_user - > default_rolename . str = NULL ;
acl_user - > default_rolename . length = 0 ;
}
/* update the mysql.user table with the new default role */
table - > use_all_columns ( ) ;
if ( table - > s - > fields < 45 )
{
my_error ( ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE , MYF ( 0 ) ,
table - > alias . c_ptr ( ) , DEFAULT_ROLE_COLUMN_IDX + 1 , table - > s - > fields ,
static_cast < int > ( table - > s - > mysql_version ) , MYSQL_VERSION_ID ) ;
mysql_mutex_unlock ( & acl_cache - > lock ) ;
goto end ;
}
table - > field [ 0 ] - > store ( host , ( uint ) strlen ( host ) , system_charset_info ) ;
table - > field [ 1 ] - > store ( user , ( uint ) strlen ( user ) , system_charset_info ) ;
key_copy ( ( uchar * ) user_key , table - > record [ 0 ] , table - > key_info ,
table - > key_info - > key_length ) ;
if ( table - > file - > ha_index_read_idx_map ( table - > record [ 0 ] , 0 ,
( uchar * ) user_key , HA_WHOLE_KEY ,
HA_READ_KEY_EXACT ) )
{
mysql_mutex_unlock ( & acl_cache - > lock ) ;
my_message ( ER_PASSWORD_NO_MATCH , ER ( ER_PASSWORD_NO_MATCH ) , MYF ( 0 ) ) ;
goto end ;
}
store_record ( table , record [ 1 ] ) ;
table - > field [ DEFAULT_ROLE_COLUMN_IDX ] - > store ( acl_user - > default_rolename . str ,
acl_user - > default_rolename . length ,
system_charset_info ) ;
if ( ( error = table - > file - > ha_update_row ( table - > record [ 1 ] , table - > record [ 0 ] ) ) & &
error ! = HA_ERR_RECORD_IS_THE_SAME )
{
mysql_mutex_unlock ( & acl_cache - > lock ) ;
table - > file - > print_error ( error , MYF ( 0 ) ) ; /* purecov: deadcode */
goto end ;
}
acl_cache - > clear ( 1 ) ;
mysql_mutex_unlock ( & acl_cache - > lock ) ;
result = 0 ;
if ( mysql_bin_log . is_open ( ) )
{
char buff [ 512 ] ;
int query_length =
sprintf ( buff , " SET DEFAULT ROLE '%-.120s' FOR '%-.120s'@'%-.120s' " ,
safe_str ( acl_user - > default_rolename . str ) ,
safe_str ( acl_user - > user . str ) ,
safe_str ( acl_user - > host . hostname ) ) ;
thd - > clear_error ( ) ;
result = thd - > binlog_query ( THD : : STMT_QUERY_TYPE , buff , query_length ,
FALSE , FALSE , FALSE , 0 ) ;
}
end :
close_mysql_tables ( thd ) ;
thd - > restore_stmt_binlog_format ( save_binlog_format ) ;
DBUG_RETURN ( result ) ;
}
/*
Find user in ACL
@ -12075,6 +12267,22 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
*/
sctx - > db_access = 0 ;
# ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
In case the user has a default role set , attempt to set that role
*/
if ( acl_user - > default_rolename . length ) {
ulonglong access = 0 ;
int result ;
result = acl_check_setrole ( thd , acl_user - > default_rolename . str , & access ) ;
if ( ! result )
result = acl_setrole ( thd , acl_user - > default_rolename . str , access ) ;
if ( result )
thd - > clear_error ( ) ; // even if the default role was not granted, do not
// close the connection
}
# endif
/* Change a database if necessary */
if ( mpvio . db . length )
{