diff options
Diffstat (limited to 'sql/sql_acl.cc')
-rw-r--r-- | sql/sql_acl.cc | 1356 |
1 files changed, 984 insertions, 372 deletions
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 56884ad9dd1..b76bbe8c730 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -1,15 +1,15 @@ -/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB - +/* Copyright (C) 2000-2003 MySQL AB + 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ @@ -28,25 +28,29 @@ #include "mysql_priv.h" #include "sql_acl.h" #include "hash_filo.h" +#ifdef HAVE_REPLICATION +#include "sql_repl.h" //for tables_ok() +#endif #include <m_ctype.h> +#include <assert.h> #include <stdarg.h> -/* - ACL_HOST is used if no host is specified - */ - struct acl_host_and_ip { char *hostname; long ip,ip_mask; // Used with masked ip:s }; + class ACL_ACCESS { public: ulong sort; - uint access; + ulong access; }; + +/* ACL_HOST is used if no host is specified */ + class ACL_HOST :public ACL_ACCESS { public: @@ -54,15 +58,20 @@ public: char *db; }; + class ACL_USER :public ACL_ACCESS { public: acl_host_and_ip host; uint hostname_length; + USER_RESOURCES user_resource; char *user,*password; ulong salt[2]; + enum SSL_type ssl_type; + const char *ssl_cipher, *x509_issuer, *x509_subject; }; + class ACL_DB :public ACL_ACCESS { public: @@ -70,14 +79,16 @@ public: char *user,*db; }; + class acl_entry :public hash_filo_element { public: - uint access; + ulong access; uint16 length; char key[1]; // Key will be stored here }; + static byte* acl_entry_get_key(acl_entry *entry,uint *length, my_bool not_used __attribute__((unused))) { @@ -95,7 +106,7 @@ static HASH acl_check_hosts, hash_tables; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; -static uint get_access(TABLE *form,uint fieldnr); +static ulong get_access(TABLE *form,uint fieldnr); static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b); static ulong get_sort(uint count,...); static void init_check_host(void); @@ -103,32 +114,50 @@ static ACL_USER *find_acl_user(const char *host, const char *user); static bool update_user_table(THD *thd, const char *host, const char *user, const char *new_password); static void update_hostname(acl_host_and_ip *host, const char *hostname); -static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, +static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); -int acl_init(bool dont_read_acl_tables) +/* + Read grant privileges from the privilege tables in the 'mysql' database. + + SYNOPSIS + acl_init() + thd Thread handler + dont_read_acl_tables Set to 1 if run with --skip-grant + + RETURN VALUES + 0 ok + 1 Could not initialize grant's +*/ + + +my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) { THD *thd; TABLE_LIST tables[3]; TABLE *table; READ_RECORD read_record_info; + MYSQL_LOCK *lock; + my_bool return_val=1; DBUG_ENTER("acl_init"); if (!acl_cache) acl_cache=new hash_filo(ACL_CACHE_SIZE,0,0, (hash_get_key) acl_entry_get_key, - (void (*)(void*)) free); + (hash_free_key) free); if (dont_read_acl_tables) DBUG_RETURN(0); /* purecov: tested */ + /* + To be able to run this from boot, we allocate a temporary THD + */ if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: inspected */ + thd->store_globals(); + acl_cache->clear(1); // Clear locked hostname cache - thd->version=refresh_version; - thd->mysys_var=my_thread_var; - thd->current_tablenr=0; - thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].alias=tables[0].real_name=(char*) "host"; tables[1].alias=tables[1].real_name=(char*) "user"; @@ -140,22 +169,20 @@ int acl_init(bool dont_read_acl_tables) if (open_tables(thd,tables)) { - close_thread_tables(thd); /* purecov: inspected */ - delete thd; /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + sql_print_error("Fatal error: Can't open privilege tables: %s", + thd->net.last_error); + goto end; } TABLE *ptr[3]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; ptr[2]= tables[2].table; - MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,3); - if (!lock) + if (!(lock=mysql_lock_tables(thd,ptr,3))) { - close_thread_tables(thd); /* purecov: inspected */ - delete thd; /* purecov: inspected */ - DBUG_RETURN(1); /* purecov: inspected */ + sql_print_error("Fatal error: Can't lock privilege tables: %s", + thd->net.last_error); + goto end; } - init_sql_alloc(&mem,1024,0); init_read_record(&read_record_info,thd,table= tables[0].table,NULL,1,0); VOID(my_init_dynamic_array(&acl_hosts,sizeof(ACL_HOST),20,50)); @@ -163,15 +190,15 @@ int acl_init(bool dont_read_acl_tables) { ACL_HOST host; update_hostname(&host.host,get_field(&mem, table,0)); - host.db=get_field(&mem, table,1); - host.access=get_access(table,2); - host.access=fix_rights_for_db(host.access); - host.sort=get_sort(2,host.host.hostname,host.db); + host.db= get_field(&mem, table,1); + host.access= get_access(table,2); + host.access= fix_rights_for_db(host.access); + host.sort= get_sort(2,host.host.hostname,host.db); #ifndef TO_BE_REMOVED if (table->fields == 8) { // Without grant if (host.access & CREATE_ACL) - host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + host.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL; } #endif VOID(push_dynamic(&acl_hosts,(gptr) &host)); @@ -186,11 +213,11 @@ int acl_init(bool dont_read_acl_tables) if (table->field[2]->field_length == 8 && protocol_version == PROTOCOL_VERSION) { - sql_print_error( - "Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */ + sql_print_error("Old 'user' table. (Check README or the Reference manual). Continuing --old-protocol"); /* purecov: tested */ protocol_version=9; /* purecov: tested */ } + DBUG_PRINT("info",("user table fields: %d",table->fields)); allow_all_hosts=0; while (!(read_record_info.read_record(&read_record_info))) { @@ -203,7 +230,7 @@ int acl_init(bool dont_read_acl_tables) protocol_version == PROTOCOL_VERSION) { sql_print_error( - "Found old style password for user '%s'. Ignoring user. (You may want to restart using --old-protocol)", + "Found old style password for user '%s'. Ignoring user. (You may want to restart mysqld using --old-protocol)", user.user ? user.user : ""); /* purecov: tested */ } else if (length % 8 || length > 16) @@ -212,19 +239,57 @@ int acl_init(bool dont_read_acl_tables) "Found invalid password for user: '%s'@'%s'; Ignoring user", user.user ? user.user : "", user.host.hostname ? user.host.hostname : ""); /* purecov: tested */ - continue; /* purecov: tested */ + continue; /* purecov: tested */ } get_salt_from_password(user.salt,user.password); user.access=get_access(table,3) & GLOBAL_ACLS; user.sort=get_sort(2,user.host.hostname,user.user); - user.hostname_length=user.host.hostname ? (uint) strlen(user.host.hostname) : 0; -#ifndef TO_BE_REMOVED - if (table->fields <= 13) - { // Without grant - if (user.access & CREATE_ACL) - user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + user.hostname_length= (user.host.hostname ? + (uint) strlen(user.host.hostname) : 0); + if (table->fields >= 31) /* Starting from 4.0.2 we have more fields */ + { + char *ssl_type=get_field(&mem, table, 24); + if (!ssl_type) + user.ssl_type=SSL_TYPE_NONE; + else if (!strcmp(ssl_type, "ANY")) + user.ssl_type=SSL_TYPE_ANY; + else if (!strcmp(ssl_type, "X509")) + user.ssl_type=SSL_TYPE_X509; + else /* !strcmp(ssl_type, "SPECIFIED") */ + user.ssl_type=SSL_TYPE_SPECIFIED; + + user.ssl_cipher= get_field(&mem, table, 25); + user.x509_issuer= get_field(&mem, table, 26); + user.x509_subject= get_field(&mem, table, 27); + + char *ptr = get_field(&mem, table, 28); + user.user_resource.questions=atoi(ptr); + ptr = get_field(&mem, table, 29); + user.user_resource.updates=atoi(ptr); + ptr = get_field(&mem, table, 30); + user.user_resource.connections=atoi(ptr); + if (user.user_resource.questions || user.user_resource.updates || + user.user_resource.connections) + mqh_used=1; } + else + { + user.ssl_type=SSL_TYPE_NONE; + bzero((char *)&(user.user_resource),sizeof(user.user_resource)); +#ifndef TO_BE_REMOVED + if (table->fields <= 13) + { // Without grant + if (user.access & CREATE_ACL) + user.access|=REFERENCES_ACL | INDEX_ACL | ALTER_ACL; + } + /* Convert old privileges */ + user.access|= LOCK_TABLES_ACL | CREATE_TMP_ACL | SHOW_DB_ACL; + if (user.access & FILE_ACL) + user.access|= REPL_CLIENT_ACL | REPL_SLAVE_ACL; + if (user.access & PROCESS_ACL) + user.access|= SUPER_ACL | EXECUTE_ACL; #endif + } VOID(push_dynamic(&acl_users,(gptr) &user)); if (!user.host.hostname || user.host.hostname[0] == wild_many && !user.host.hostname[1]) @@ -267,11 +332,21 @@ int acl_init(bool dont_read_acl_tables) init_check_host(); mysql_unlock_tables(thd, lock); + initialized=1; thd->version--; // Force close to free memory + return_val=0; + +end: close_thread_tables(thd); delete thd; - initialized=1; - DBUG_RETURN(0); + if (org_thd) + org_thd->store_globals(); /* purecov: inspected */ + else + { + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + } + DBUG_RETURN(return_val); } @@ -292,20 +367,27 @@ void acl_free(bool end) } } - /* Reload acl list if possible */ -void acl_reload(void) +/* + Forget current privileges and read new privileges from the privilege tables + + SYNOPSIS + acl_reload() + thd Thread handle +*/ + +void acl_reload(THD *thd) { DYNAMIC_ARRAY old_acl_hosts,old_acl_users,old_acl_dbs; MEM_ROOT old_mem; bool old_initialized; DBUG_ENTER("acl_reload"); - if (current_thd && current_thd->locked_tables) + if (thd && thd->locked_tables) { // Can't have locked tables here - current_thd->lock=current_thd->locked_tables; - current_thd->locked_tables=0; - close_thread_tables(current_thd); + thd->lock=thd->locked_tables; + thd->locked_tables=0; + close_thread_tables(thd); } if ((old_initialized=initialized)) VOID(pthread_mutex_lock(&acl_cache->lock)); @@ -317,9 +399,9 @@ void acl_reload(void) delete_dynamic(&acl_wild_hosts); hash_free(&acl_check_hosts); - if (acl_init(0)) + if (acl_init(thd, 0)) { // Error. Revert to old list - acl_free(); /* purecov: inspected */ + acl_free(); /* purecov: inspected */ acl_hosts=old_acl_hosts; acl_users=old_acl_users; acl_dbs=old_acl_dbs; @@ -339,31 +421,38 @@ void acl_reload(void) } -/* Get all access bits from table after fieldnr */ +/* + Get all access bits from table after fieldnr + We know that the access privileges ends when there is no more fields + or the field is not an enum with two elements. +*/ -static uint get_access(TABLE *form,uint fieldnr) +static ulong get_access(TABLE *form, uint fieldnr) { - uint access_bits=0,bit; + ulong access_bits=0,bit; char buff[2]; String res(buff,sizeof(buff)); Field **pos; - for (pos=form->field+fieldnr,bit=1 ; *pos ; pos++ , bit<<=1) + for (pos=form->field+fieldnr, bit=1; + *pos && (*pos)->real_type() == FIELD_TYPE_ENUM && + ((Field_enum*) (*pos))->typelib->count == 2 ; + pos++ , bit<<=1) { (*pos)->val_str(&res,&res); if (toupper(res[0]) == 'Y') - access_bits|=bit; + access_bits|= bit; } return access_bits; } /* - return a number which, if sorted 'desc', puts strings in this order: - no wildcards - wildcards - empty string - */ + Return a number which, if sorted 'desc', puts strings in this order: + no wildcards + wildcards + empty string +*/ static ulong get_sort(uint count,...) { @@ -403,18 +492,26 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) } -/* Get master privilges for user (priviliges for all tables) */ - +/* + Get master privilges for user (priviliges for all tables). + Required before connecting to MySQL +*/ -uint acl_getroot(const char *host, const char *ip, const char *user, - const char *password,const char *message,char **priv_user, - bool old_ver) +ulong acl_getroot(THD *thd, const char *host, const char *ip, const char *user, + const char *password,const char *message, + char **priv_user, char *priv_host, + bool old_ver, USER_RESOURCES *mqh) { - uint user_access=NO_ACCESS; + ulong user_access=NO_ACCESS; *priv_user=(char*) user; + DBUG_ENTER("acl_getroot"); + bzero((char *)mqh,sizeof(USER_RESOURCES)); if (!initialized) - return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */ + { + // If no data allow anything + DBUG_RETURN((ulong) ~NO_ACCESS); /* purecov: tested */ + } VOID(pthread_mutex_lock(&acl_cache->lock)); /* @@ -433,9 +530,121 @@ uint acl_getroot(const char *host, const char *ip, const char *user, !check_scramble(password,message,acl_user->salt, (my_bool) old_ver))) { - user_access=acl_user->access; + Vio *vio=thd->net.vio; +#ifdef HAVE_OPENSSL + SSL *ssl= (SSL*) vio->ssl_arg; +#endif + /* + In this point we know that user is allowed to connect + from given host by given username/password pair. Now + we check if SSL is required, if user is using SSL and + if X509 certificate attributes are OK + */ + switch (acl_user->ssl_type) { + case SSL_TYPE_NOT_SPECIFIED: // Impossible + case SSL_TYPE_NONE: /* SSL is not required to connect */ + user_access=acl_user->access; + break; +#ifdef HAVE_OPENSSL + case SSL_TYPE_ANY: /* Any kind of SSL is good enough */ + if (vio_type(vio) == VIO_TYPE_SSL) + user_access=acl_user->access; + break; + case SSL_TYPE_X509: /* Client should have any valid certificate. */ + /* + We need to check for absence of SSL because without SSL + we should reject connection. + */ + if (vio_type(vio) == VIO_TYPE_SSL && + SSL_get_verify_result(ssl) == X509_V_OK && + SSL_get_peer_certificate(ssl)) + user_access=acl_user->access; + break; + case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ + /* + We need to check for absence of SSL because without SSL + we should reject connection. + */ + if (vio_type(vio) == VIO_TYPE_SSL && + SSL_get_verify_result(ssl) == X509_V_OK) + { + if (acl_user->ssl_cipher) + { + DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(ssl))); + if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl))) + user_access=acl_user->access; + else + { + if (global_system_variables.log_warnings) + sql_print_error("X509 ciphers mismatch: should be '%s' but is '%s'", + acl_user->ssl_cipher, + SSL_get_cipher(ssl)); + user_access=NO_ACCESS; + break; + } + } + /* Prepare certificate (if exists) */ + DBUG_PRINT("info",("checkpoint 1")); + X509* cert=SSL_get_peer_certificate(ssl); + DBUG_PRINT("info",("checkpoint 2")); + /* If X509 issuer is speified, we check it... */ + if (acl_user->x509_issuer) + { + DBUG_PRINT("info",("checkpoint 3")); + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + acl_user->x509_issuer, ptr)); + if (strcmp(acl_user->x509_issuer, ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 issuer mismatch: should be '%s' but is '%s'", + acl_user->x509_issuer, ptr); + user_access=NO_ACCESS; + free(ptr); + break; + } + user_access=acl_user->access; + free(ptr); + } + DBUG_PRINT("info",("checkpoint 4")); + /* X509 subject is specified, we check it .. */ + if (acl_user->x509_subject) + { + char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (strcmp(acl_user->x509_subject,ptr)) + { + if (global_system_variables.log_warnings) + sql_print_error("X509 subject mismatch: '%s' vs '%s'", + acl_user->x509_subject, ptr); + user_access=NO_ACCESS; + } + else + user_access=acl_user->access; + free(ptr); + } + break; + } +#else /* HAVE_OPENSSL */ + default: + /* + If we don't have SSL but SSL is required for this user the + authentication should fail. + */ + break; +#endif /* HAVE_OPENSSL */ + } + + *mqh=acl_user->user_resource; if (!acl_user->user) *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ + if (acl_user->host.hostname) + strmake(priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *priv_host= 0; break; } #ifndef ALLOW_DOWNGRADE_OF_USERS @@ -445,7 +654,7 @@ uint acl_getroot(const char *host, const char *ip, const char *user, } } VOID(pthread_mutex_unlock(&acl_cache->lock)); - return user_access; + DBUG_RETURN(user_access); } @@ -462,7 +671,13 @@ static byte* check_get_key(ACL_USER *buff,uint *length, } static void acl_update_user(const char *user, const char *host, - const char *password, uint privileges) + const char *password, + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, + ulong privileges) { for (uint i=0 ; i < acl_users.elements ; i++) { @@ -472,9 +687,26 @@ static void acl_update_user(const char *user, const char *host, !strcmp(user,acl_user->user)) { if (!acl_user->host.hostname && !host[0] || - acl_user->host.hostname && !strcmp(host,acl_user->host.hostname)) + acl_user->host.hostname && + !my_strcasecmp(host,acl_user->host.hostname)) { acl_user->access=privileges; + if (mqh->bits & 1) + acl_user->user_resource.questions=mqh->questions; + if (mqh->bits & 2) + acl_user->user_resource.updates=mqh->updates; + if (mqh->bits & 4) + acl_user->user_resource.connections=mqh->connections; + if (ssl_type != SSL_TYPE_NOT_SPECIFIED) + { + acl_user->ssl_type= ssl_type; + acl_user->ssl_cipher= (ssl_cipher ? strdup_root(&mem,ssl_cipher) : + 0); + acl_user->x509_issuer= (x509_issuer ? strdup_root(&mem,x509_issuer) : + 0); + acl_user->x509_subject= (x509_subject ? + strdup_root(&mem,x509_subject) : 0); + } if (password) { if (!password[0]) @@ -494,15 +726,26 @@ static void acl_update_user(const char *user, const char *host, static void acl_insert_user(const char *user, const char *host, const char *password, - uint privileges) + enum SSL_type ssl_type, + const char *ssl_cipher, + const char *x509_issuer, + const char *x509_subject, + USER_RESOURCES *mqh, + ulong privileges) { ACL_USER acl_user; - acl_user.user=strdup_root(&mem,user); + acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host,strdup_root(&mem,host)); acl_user.password=0; acl_user.access=privileges; + acl_user.user_resource = *mqh; acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); acl_user.hostname_length=(uint) strlen(acl_user.host.hostname); + acl_user.ssl_type= (ssl_type != SSL_TYPE_NOT_SPECIFIED ? + ssl_type : SSL_TYPE_NONE); + acl_user.ssl_cipher= ssl_cipher ? strdup_root(&mem,ssl_cipher) : 0; + acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0; + acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0; if (password) { acl_user.password=(char*) ""; // Just point at something @@ -524,7 +767,7 @@ static void acl_insert_user(const char *user, const char *host, static void acl_update_db(const char *user, const char *host, const char *db, - uint privileges) + ulong privileges) { for (uint i=0 ; i < acl_dbs.elements ; i++) { @@ -534,7 +777,7 @@ static void acl_update_db(const char *user, const char *host, const char *db, !strcmp(user,acl_db->user)) { if (!acl_db->host.hostname && !host[0] || - acl_db->host.hostname && !strcmp(host,acl_db->host.hostname)) + acl_db->host.hostname && !my_strcasecmp(host,acl_db->host.hostname)) { if (!acl_db->db && !db[0] || acl_db->db && !strcmp(db,acl_db->db)) @@ -550,11 +793,25 @@ static void acl_update_db(const char *user, const char *host, const char *db, } +/* + Insert a user/db/host combination into the global acl_cache + + SYNOPSIS + acl_insert_db() + user User name + host Host name + db Database name + privileges Bitmap of privileges + + NOTES + acl_cache->lock must be locked when calling this +*/ + static void acl_insert_db(const char *user, const char *host, const char *db, - uint privileges) + ulong privileges) { ACL_DB acl_db; - /* The acl_cache mutex is locked by mysql_grant */ + safe_mutex_assert_owner(&acl_cache->lock); acl_db.user=strdup_root(&mem,user); update_hostname(&acl_db.host,strdup_root(&mem,host)); acl_db.db=strdup_root(&mem,db); @@ -566,21 +823,28 @@ static void acl_insert_db(const char *user, const char *host, const char *db, } -/***************************************************************************** -** Get privilege for a host, user and db combination -*****************************************************************************/ -uint acl_get(const char *host, const char *ip, const char *bin_ip, +/* + Get privilege for a host, user and db combination +*/ + +ulong acl_get(const char *host, const char *ip, const char *bin_ip, const char *user, const char *db) { - uint host_access,db_access,i,key_length; + ulong host_access,db_access; + uint i,key_length; db_access=0; host_access= ~0; - char key[ACL_KEY_LENGTH],*end; + char key[ACL_KEY_LENGTH],*tmp_db,*end; acl_entry *entry; VOID(pthread_mutex_lock(&acl_cache->lock)); memcpy_fixed(&key,bin_ip,sizeof(struct in_addr)); - end=strmov(strmov(key+sizeof(struct in_addr),user)+1,db); + end=strmov((tmp_db=strmov(key+sizeof(struct in_addr),user)+1),db); + if (lower_case_table_names) + { + casedn_str(tmp_db); + db=tmp_db; + } key_length=(uint) (end-key); if ((entry=(acl_entry*) acl_cache->search(key,key_length))) { @@ -646,7 +910,7 @@ int wild_case_compare(const char *str,const char *wildstr) { reg3 int flag; DBUG_ENTER("wild_case_compare"); - + DBUG_PRINT("enter",("str: '%s' wildstr: '%s'",str,wildstr)); while (*wildstr) { while (*wildstr && *wildstr != wild_many && *wildstr != wild_one) @@ -658,7 +922,7 @@ int wild_case_compare(const char *str,const char *wildstr) if (! *wildstr ) DBUG_RETURN (*str != 0); if (*wildstr++ == wild_one) { - if (! *str++) DBUG_RETURN (1); /* One char; skipp */ + if (! *str++) DBUG_RETURN (1); /* One char; skip */ } else { /* Found '*' */ @@ -684,11 +948,14 @@ int wild_case_compare(const char *str,const char *wildstr) DBUG_RETURN (*str != '\0'); } -/***************************************************************************** -** check if there are any possible matching entries for this host -** All host names without wild cards are stored in a hash table, -** entries with wildcards are stored in a dynamic array -*****************************************************************************/ + +/* + Check if there are any possible matching entries for this host + + NOTES + All host names without wild cards are stored in a hash table, + entries with wildcards are stored in a dynamic array +*/ static void init_check_host(void) { @@ -761,45 +1028,78 @@ bool acl_check_host(const char *host, const char *ip) return 1; // Host is not allowed } -/***************************************************************************** -** Change password for the user if it's not an anonymous user -** Note: This should write the error directly to the client! -*****************************************************************************/ -bool change_password(THD *thd, const char *host, const char *user, - char *new_password) +/* + Check if the user is allowed to change password + + SYNOPSIS: + check_change_password() + thd THD + host hostname for the user + user user name + + RETURN VALUE + 0 OK + 1 ERROR ; In this case the error is sent to the client. +*/ + +bool check_change_password(THD *thd, const char *host, const char *user) { - uint length=0; if (!initialized) { send_error(&thd->net, ER_PASSWORD_NOT_ALLOWED); /* purecov: inspected */ - return 1; /* purecov: inspected */ + return(1); /* purecov: inspected */ } - if (!host) - host=thd->ip; /* purecov: tested */ - /* password should always be 0 or 16 chars; simple hack to avoid cracking */ - length=(uint) strlen(new_password); - new_password[length & 16]=0; - if (!thd->slave_thread && (strcmp(thd->user,user) || - my_strcasecmp(host,thd->host ? thd->host : thd->ip))) + my_strcasecmp(host,thd->host_or_ip))) { if (check_access(thd, UPDATE_ACL, "mysql",0,1)) - return 1; + return(1); } if (!thd->slave_thread && !thd->user[0]) { send_error(&thd->net, ER_PASSWORD_ANONYMOUS_USER); - return 1; + return(1); } + return(0); +} + + +/* + Change a password for a user + + SYNOPSIS + change_password() + thd Thread handle + host Hostname + user User name + new_password New password for host@user + + RETURN VALUES + 0 ok + 1 ERROR; In this case the error is sent to the client. +*/ + +bool change_password(THD *thd, const char *host, const char *user, + char *new_password) +{ + uint length=0; + DBUG_ENTER("change_password"); + DBUG_PRINT("enter",("host: '%s' user: '%s' new_password: '%s'", + host,user,new_password)); + DBUG_ASSERT(host != 0); // Ensured by parent + + length=(uint) strlen(new_password); + new_password[length & 16]=0; + VOID(pthread_mutex_lock(&acl_cache->lock)); ACL_USER *acl_user; if (!(acl_user= find_acl_user(host,user))) { send_error(&thd->net, ER_PASSWORD_NO_MATCH); VOID(pthread_mutex_unlock(&acl_cache->lock)); - return 1; + DBUG_RETURN(1); } if (update_user_table(thd, acl_user->host.hostname ? acl_user->host.hostname : "", @@ -808,7 +1108,7 @@ bool change_password(THD *thd, const char *host, const char *user, { VOID(pthread_mutex_unlock(&acl_cache->lock)); /* purecov: deadcode */ send_error(&thd->net,0); /* purecov: deadcode */ - return 1; /* purecov: deadcode */ + DBUG_RETURN(1); /* purecov: deadcode */ } get_salt_from_password(acl_user->salt,new_password); if (!new_password[0]) @@ -819,17 +1119,16 @@ bool change_password(THD *thd, const char *host, const char *user, VOID(pthread_mutex_unlock(&acl_cache->lock)); char buff[460]; - - Query_log_event qinfo(thd, buff); - qinfo.q_len = + ulong query_length= my_sprintf(buff, (buff,"SET PASSWORD FOR \"%-.120s\"@\"%-.120s\"=\"%-.120s\"", acl_user->user ? acl_user->user : "", acl_user->host.hostname ? acl_user->host.hostname : "", new_password)); - mysql_update_log.write(thd,buff,qinfo.q_len); + mysql_update_log.write(thd, buff, query_length); + Query_log_event qinfo(thd, buff, query_length, 0); mysql_bin_log.write(&qinfo); - return 0; + DBUG_RETURN(0); } @@ -840,28 +1139,41 @@ bool change_password(THD *thd, const char *host, const char *user, static ACL_USER * find_acl_user(const char *host, const char *user) { + DBUG_ENTER("find_acl_user"); + DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user)); for (uint i=0 ; i < acl_users.elements ; i++) { ACL_USER *acl_user=dynamic_element(&acl_users,i,ACL_USER*); + DBUG_PRINT("info",("strcmp('%s','%s'), compare_hostname('%s','%s'),", + user, + acl_user->user ? acl_user->user : "", + host, + acl_user->host.hostname ? acl_user->host.hostname : + "")); if (!acl_user->user && !user[0] || acl_user->user && !strcmp(user,acl_user->user)) { - if (compare_hostname(&acl_user->host,host,host)) - return acl_user; + if (compare_hostname(&(acl_user->host),host,host)) + { + DBUG_RETURN(acl_user); + } } } - return 0; + DBUG_RETURN(0); } -/***************************************************************************** - Handle comparing of hostname - A hostname may be of type: - hostname (May include wildcards); monty.pp.sci.fi - ip (May include wildcards); 192.168.0.0 - ip/netmask 192.168.0.0/255.255.255.0 - A net mask of 0.0.0.0 is not allowed. -*****************************************************************************/ +/* + Comparing of hostnames + + NOTES + A hostname may be of type: + hostname (May include wildcards); monty.pp.sci.fi + ip (May include wildcards); 192.168.0.0 + ip/netmask 192.168.0.0/255.255.255.0 + + A net mask of 0.0.0.0 is not allowed. +*/ static const char *calc_ip(const char *ip, long *val, char end) { @@ -908,9 +1220,9 @@ static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, } -/**************************************************************************** -** Code to update grants in the user and database privilege tables -****************************************************************************/ +/* + Update grants in the user and database privilege tables +*/ static bool update_user_table(THD *thd, const char *host, const char *user, const char *new_password) @@ -924,6 +1236,24 @@ static bool update_user_table(THD *thd, const char *host, const char *user, bzero((char*) &tables,sizeof(tables)); tables.alias=tables.real_name=(char*) "user"; tables.db=(char*) "mysql"; +#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 && table_rules_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 (!tables_ok(0, &tables)) + DBUG_RETURN(0); + } +#endif + if (!(table=open_ltable(thd,&tables,TL_WRITE))) DBUG_RETURN(1); /* purecov: deadcode */ table->field[0]->store(host,(uint) strlen(host)); @@ -959,10 +1289,10 @@ static bool test_if_create_new_users(THD *thd) if (opt_safe_user_create && !(thd->master_access & INSERT_ACL)) { TABLE_LIST tl; - uint db_access; + ulong db_access; bzero((char*) &tl,sizeof(tl)); tl.db= (char*) "mysql"; - tl.real_name= (char*) "user"; + tl.real_name= (char*) "user"; db_access=acl_get(thd->host, thd->ip, (char*) &thd->remote.sin_addr, thd->priv_user, tl.db); if (!(db_access & INSERT_ACL)) @@ -976,17 +1306,19 @@ static bool test_if_create_new_users(THD *thd) /**************************************************************************** -** Handle GRANT commands + Handle GRANT commands ****************************************************************************/ -static int replace_user_table(TABLE *table, const LEX_USER &combo, - uint rights, char what, bool create_user) +static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, + ulong rights, bool revoke_grant, + bool create_user) { int error = -1; - uint i,j; bool old_row_exists=0; char *password,empty_string[1]; + char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_user_table"); + safe_mutex_assert_owner(&acl_cache->lock); password=empty_string; empty_string[0]=0; @@ -996,8 +1328,8 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, if (combo.password.length != HASH_PASSWORD_LENGTH) { my_printf_error(ER_PASSWORD_NO_MATCH, - "Password hash should be a %d-digit hexadecimal number", - MYF(0),HASH_PASSWORD_LENGTH); + "Password hash should be a %d-digit hexadecimal number", + MYF(0),HASH_PASSWORD_LENGTH); DBUG_RETURN(-1); } password=combo.password.str; @@ -1012,7 +1344,6 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, { if (!create_user) { - THD *thd=current_thd; if (what == 'N') my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT), MYF(0),combo.user.str,combo.host.str); @@ -1020,7 +1351,7 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, my_printf_error(ER_NO_PERMISSION_TO_CREATE_USER, ER(ER_NO_PERMISSION_TO_CREATE_USER), MYF(0),thd->user, - thd->host ? thd->host : thd->ip ? thd->ip: ""); + thd->host_or_ip); error= -1; goto end; } @@ -1038,15 +1369,70 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, table->field[2]->store(password,(uint) strlen(password)); } - for (i = 3, j = SELECT_ACL; // starting from reload - i < table->fields; - i++, j <<= 1) + /* Update table columns with new privileges */ + + Field **tmp_field; + ulong priv; + for (tmp_field= table->field+3, priv = SELECT_ACL; + *tmp_field && (*tmp_field)->real_type() == FIELD_TYPE_ENUM && + ((Field_enum*) (*tmp_field))->typelib->count == 2 ; + tmp_field++, priv <<= 1) { - if (j & rights) // set requested privileges - table->field[i]->store(&what,1); + if (priv & rights) // set requested privileges + (*tmp_field)->store(&what,1); } rights=get_access(table,3); + DBUG_PRINT("info",("table->fields: %d",table->fields)); + if (table->fields >= 31) /* From 4.0.0 we have more fields */ + { + /* We write down SSL related ACL stuff */ + switch (thd->lex.ssl_type) { + case SSL_TYPE_ANY: + table->field[24]->store("ANY",3); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + case SSL_TYPE_X509: + table->field[24]->store("X509",4); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + case SSL_TYPE_SPECIFIED: + table->field[24]->store("SPECIFIED",9); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + if (thd->lex.ssl_cipher) + table->field[25]->store(thd->lex.ssl_cipher, + strlen(thd->lex.ssl_cipher)); + if (thd->lex.x509_issuer) + table->field[26]->store(thd->lex.x509_issuer, + strlen(thd->lex.x509_issuer)); + if (thd->lex.x509_subject) + table->field[27]->store(thd->lex.x509_subject, + strlen(thd->lex.x509_subject)); + break; + case SSL_TYPE_NOT_SPECIFIED: + break; + case SSL_TYPE_NONE: + table->field[24]->store("",0); + table->field[25]->store("",0); + table->field[26]->store("",0); + table->field[27]->store("",0); + break; + } + USER_RESOURCES mqh = thd->lex.mqh; + if (mqh.bits & 1) + table->field[28]->store((longlong) mqh.questions); + if (mqh.bits & 2) + table->field[29]->store((longlong) mqh.updates); + if (mqh.bits & 4) + table->field[30]->store((longlong) mqh.connections); + mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections; + } if (old_row_exists) { /* @@ -1073,16 +1459,28 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, } error=0; // Privileges granted / revoked - end: +end: if (!error) { acl_cache->clear(1); // Clear privilege cache if (!combo.password.str) password=0; // No password given on command if (old_row_exists) - acl_update_user(combo.user.str,combo.host.str,password,rights); + acl_update_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); else - acl_insert_user(combo.user.str,combo.host.str,password,rights); + acl_insert_user(combo.user.str,combo.host.str,password, + thd->lex.ssl_type, + thd->lex.ssl_cipher, + thd->lex.x509_issuer, + thd->lex.x509_subject, + &thd->lex.mqh, + rights); } table->file->index_end(); DBUG_RETURN(error); @@ -1090,19 +1488,21 @@ static int replace_user_table(TABLE *table, const LEX_USER &combo, /* -** change grants in the mysql.db table + change grants in the mysql.db table */ static int replace_db_table(TABLE *table, const char *db, const LEX_USER &combo, - uint rights, char what) + ulong rights, bool revoke_grant) { - uint i,j,store_rights; + uint i; + ulong priv,store_rights; bool old_row_exists=0; int error; + char what= (revoke_grant) ? 'N' : 'Y'; DBUG_ENTER("replace_db_table"); - // is there such a user in user table in memory ???? + /* Check if there is such a user in user table in memory? */ if (!initialized || !find_acl_user(combo.host.str,combo.user.str)) { my_error(ER_PASSWORD_NO_MATCH,MYF(0)); @@ -1114,7 +1514,7 @@ static int replace_db_table(TABLE *table, const char *db, table->field[2]->store(combo.user.str,combo.user.length); table->file->index_init(0); if (table->file->index_read(table->record[0],(byte*) table->field[0]->ptr,0, - HA_READ_KEY_EXACT)) + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -1135,9 +1535,9 @@ static int replace_db_table(TABLE *table, const char *db, } store_rights=get_rights_for_db(rights); - for (i = 3, j = 1; i < table->fields; i++, j <<= 1) + for (i= 3, priv= 1; i < table->fields; i++, priv <<= 1) { - if (j & store_rights) // do it if priv is chosen + if (priv & store_rights) // do it if priv is chosen table->field [i]->store(&what,1); // set requested privileges } rights=get_access(table,3); @@ -1145,7 +1545,7 @@ static int replace_db_table(TABLE *table, const char *db, if (old_row_exists) { - // update old existing row + /* update old existing row */ if (rights) { if ((error=table->file->update_row(table->record[1],table->record[0]))) @@ -1172,11 +1572,11 @@ static int replace_db_table(TABLE *table, const char *db, DBUG_RETURN(0); /* This could only happen if the grant tables got corrupted */ - table_error: +table_error: table->file->print_error(error,MYF(0)); /* purecov: deadcode */ table->file->index_end(); - abort: +abort: DBUG_RETURN(-1); } @@ -1185,13 +1585,15 @@ class GRANT_COLUMN :public Sql_alloc { public: char *column; - uint rights, key_length; - GRANT_COLUMN(String &c, uint y) :rights (y) + ulong rights; + uint key_length; + GRANT_COLUMN(String &c, ulong y) :rights (y) { - column= memdup_root(&memex,c.ptr(),key_length=c.length()); + column= memdup_root(&memex,c.ptr(), key_length=c.length()); } }; + static byte* get_key_column(GRANT_COLUMN *buff,uint *length, my_bool not_used __attribute__((unused))) { @@ -1199,15 +1601,17 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length, return (byte*) buff->column; } + class GRANT_TABLE :public Sql_alloc { public: char *host,*db,*user,*tname, *hash_key; - uint privs, cols, key_length; + ulong privs, cols; ulong sort; + uint key_length; HASH hash_columns; GRANT_TABLE (const char *h, const char *d,const char *u, const char *t, - uint p,uint c) + ulong p, ulong c) : privs(p), cols(c) { host = strdup_root(&memex,h); @@ -1215,6 +1619,11 @@ public: user = strdup_root(&memex,u); sort= get_sort(3,host,db,user); tname= strdup_root(&memex,t); + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); @@ -1228,7 +1637,9 @@ public: host = get_field(&memex,form,0); db = get_field(&memex,form,1); - user = get_field(&memex,form,2); if (!user) user=(char*) ""; + user = get_field(&memex,form,2); + if (!user) + user=(char*) ""; sort= get_sort(3,host,db,user); tname = get_field(&memex,form,3); if (!host || !db || !tname) @@ -1237,11 +1648,17 @@ public: privs = cols = 0; /* purecov: inspected */ return; /* purecov: inspected */ } - key_length = (uint) strlen(db) + (uint) strlen(user) + (uint) strlen (tname) + 3; + if (lower_case_table_names) + { + casedn_str(db); + casedn_str(tname); + } + key_length = ((uint) strlen(db) + (uint) strlen(user) + + (uint) strlen(tname) + 3); hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); - privs = (uint) form->field[6]->val_int(); - cols = (uint) form->field[7]->val_int(); + privs = (ulong) form->field[6]->val_int(); + cols = (ulong) form->field[7]->val_int(); privs = fix_rights_for_table(privs); cols = fix_rights_for_column(cols); @@ -1274,11 +1691,11 @@ public: GRANT_COLUMN *mem_check; /* As column name is a string, we don't have to supply a buffer */ res=col_privs->field[4]->val_str(&column_name,&column_name); - uint priv= (uint) col_privs->field[6]->val_int(); + ulong priv= (ulong) col_privs->field[6]->val_int(); if (!(mem_check = new GRANT_COLUMN(*res, fix_rights_for_column(priv)))) { - // Don't use this entry + /* Don't use this entry */ privs = cols = 0; /* purecov: deadcode */ return; /* purecov: deadcode */ } @@ -1290,6 +1707,7 @@ public: bool ok() { return privs != 0 || cols != 0; } }; + static byte* get_grant_table(GRANT_TABLE *buff,uint *length, my_bool not_used __attribute__((unused))) { @@ -1297,11 +1715,13 @@ static byte* get_grant_table(GRANT_TABLE *buff,uint *length, return (byte*) buff->hash_key; } + void free_grant_table(GRANT_TABLE *grant_table) { hash_free(&grant_table->hash_columns); } + /* Search after a matching grant. Prefer exact grants before not exact ones */ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, @@ -1312,6 +1732,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, char helping [NAME_LEN*2+USERNAME_LENGTH+3]; uint len; GRANT_TABLE *grant_table,*found=0; + safe_mutex_assert_owner(&LOCK_grant); len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; for (grant_table=(GRANT_TABLE*) hash_search(&hash_tables,(byte*) helping, @@ -1321,7 +1742,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, { if (exact) { - if ((host && !strcmp(host,grant_table->host)) || + if ((host && !my_strcasecmp(host,grant_table->host)) || (ip && !strcmp(ip,grant_table->host))) return grant_table; } @@ -1339,8 +1760,7 @@ static GRANT_TABLE *table_hash_search(const char *host,const char* ip, inline GRANT_COLUMN * -column_hash_search(GRANT_TABLE *t, const char *cname, - uint length) +column_hash_search(GRANT_TABLE *t, const char *cname, uint length) { return (GRANT_COLUMN*) hash_search(&t->hash_columns, (byte*) cname,length); } @@ -1350,7 +1770,7 @@ static int replace_column_table(GRANT_TABLE *g_t, TABLE *table, const LEX_USER &combo, List <LEX_COLUMN> &columns, const char *db, const char *table_name, - uint rights, bool revoke_grant) + ulong rights, bool revoke_grant) { int error=0,result=0; uint key_length; @@ -1374,7 +1794,7 @@ static int replace_column_table(GRANT_TABLE *g_t, table->file->index_init(0); while ((xx=iter++)) { - uint privileges = xx->rights; + ulong privileges = xx->rights; bool old_row_exists=0; key_restore(table,key,0,key_length); table->field[4]->store(xx->column.ptr(),xx->column.length()); @@ -1397,7 +1817,7 @@ static int replace_column_table(GRANT_TABLE *g_t, } else { - uint tmp= (uint) table->field[6]->val_int(); + ulong tmp= (ulong) table->field[6]->val_int(); tmp=fix_rights_for_column(tmp); if (revoke_grant) @@ -1454,10 +1874,10 @@ static int replace_column_table(GRANT_TABLE *g_t, key_length, HA_READ_KEY_EXACT)) goto end; - // Scan trough all rows with the same host,db,user and table + /* Scan through all rows with the same host,db,user and table */ do { - uint privileges = (uint) table->field[6]->val_int(); + ulong privileges = (ulong) table->field[6]->val_int(); privileges=fix_rights_for_column(privileges); store_record(table,1); @@ -1504,7 +1924,7 @@ static int replace_column_table(GRANT_TABLE *g_t, !key_cmp(table,key,0,key_length)); } - end: +end: table->file->index_end(); DBUG_RETURN(result); } @@ -1513,19 +1933,22 @@ static int replace_column_table(GRANT_TABLE *g_t, static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, TABLE *table, const LEX_USER &combo, const char *db, const char *table_name, - uint rights, uint kolone, bool revoke_grant) + ulong rights, ulong col_rights, + bool revoke_grant) { - char grantor[HOSTNAME_LENGTH+1+USERNAME_LENGTH]; + char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; int old_row_exists = 1; int error=0; - uint store_table_rights,store_col_rights; + ulong store_table_rights, store_col_rights; DBUG_ENTER("replace_table_table"); + safe_mutex_assert_owner(&LOCK_grant); - strxmov(grantor,thd->user,"@",thd->host ? thd->host : thd->ip ? thd->ip :"", - NullS); + strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); - // The following should always succeed as new users are created before - // this function is called! + /* + The following should always succeed as new users are created before + this function is called! + */ if (!find_acl_user(combo.host.str,combo.user.str)) { my_error(ER_PASSWORD_NO_MATCH,MYF(0)); /* purecov: deadcode */ @@ -1560,24 +1983,24 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, restore_record(table,1); // Get saved record } - store_table_rights=get_rights_for_table(rights); - store_col_rights=get_rights_for_column(kolone); + store_table_rights= get_rights_for_table(rights); + store_col_rights= get_rights_for_column(col_rights); if (old_row_exists) { - uint j,k; + ulong j,k; store_record(table,1); - j = (uint) table->field[6]->val_int(); - k = (uint) table->field[7]->val_int(); + j = (ulong) table->field[6]->val_int(); + k = (ulong) table->field[7]->val_int(); if (revoke_grant) { - // column rights are already fixed in mysql_table_grant ! + /* column rights are already fixed in mysql_table_grant */ store_table_rights=j & ~store_table_rights; } else { - store_table_rights|=j; - store_col_rights|=k; + store_table_rights|= j; + store_col_rights|= k; } } @@ -1585,7 +2008,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, table->field[6]->store((longlong) store_table_rights); table->field[7]->store((longlong) store_col_rights); rights=fix_rights_for_table(store_table_rights); - kolone=fix_rights_for_column(store_col_rights); + col_rights=fix_rights_for_column(store_col_rights); if (old_row_exists) { @@ -1604,10 +2027,10 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, goto table_error; /* purecov: deadcode */ } - if (rights | kolone) + if (rights | col_rights) { - grant_table->privs = rights; - grant_table->cols = kolone; + grant_table->privs= rights; + grant_table->cols= col_rights; } else { @@ -1615,19 +2038,36 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, } DBUG_RETURN(0); - /* This should never happen */ - table_error: + /* This should never happen */ +table_error: table->file->print_error(error,MYF(0)); /* purecov: deadcode */ DBUG_RETURN(-1); /* purecov: deadcode */ } -int mysql_table_grant (THD *thd, TABLE_LIST *table_list, - List <LEX_USER> &user_list, - List <LEX_COLUMN> &columns, uint rights, - bool revoke_grant) +/* + Store table level and column level grants in the privilege tables + + SYNOPSIS + mysql_table_grant() + thd Thread handle + table_list List of tables to give grant + user_list List of users to give grant + columns List of columns to give grant + rights Table level grant + revoke_grant Set to 1 if this is a REVOKE command + + RETURN + 0 ok + 1 error +*/ + +int mysql_table_grant(THD *thd, TABLE_LIST *table_list, + List <LEX_USER> &user_list, + List <LEX_COLUMN> &columns, ulong rights, + bool revoke_grant) { - uint column_priv = 0; + ulong column_priv= 0; List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str; TABLE_LIST tables[3]; @@ -1648,33 +2088,33 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, if (columns.elements && !revoke_grant) { TABLE *table; - class LEX_COLUMN *check; - List_iterator <LEX_COLUMN> iter(columns); + class LEX_COLUMN *column; + List_iterator <LEX_COLUMN> column_iter(columns); if (!(table=open_ltable(thd,table_list,TL_READ))) DBUG_RETURN(-1); - while ((check = iter++)) + while ((column = column_iter++)) { - if (!find_field_in_table(thd,table,check->column.ptr(), - check->column.length(),0,0)) + if (!find_field_in_table(thd,table,column->column.ptr(), + column->column.length(),0,0)) { my_printf_error(ER_BAD_FIELD_ERROR,ER(ER_BAD_FIELD_ERROR),MYF(0), - check->column.c_ptr(),table_list->alias); + column->column.c_ptr(), table_list->alias); DBUG_RETURN(-1); } - column_priv |= check->rights | (rights & COL_ACLS); + column_priv|= column->rights; } close_thread_tables(thd); } else if (!(rights & CREATE_ACL) && !revoke_grant) { char buf[FN_REFLEN]; - sprintf(buf,"%s/%s/%s.frm",mysql_data_home,table_list->db, + sprintf(buf,"%s/%s/%s.frm",mysql_data_home, table_list->db, table_list->real_name); fn_format(buf,buf,"","",4+16+32); if (access(buf,F_OK)) { - my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db,table_list->real_name); + my_error(ER_NO_SUCH_TABLE,MYF(0),table_list->db, table_list->alias); DBUG_RETURN(-1); } } @@ -1693,6 +2133,23 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; +#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 && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= tables[2].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(0); + } +#endif + if (open_and_lock_tables(thd,tables)) { // Should never happen close_thread_tables(thd); /* purecov: deadcode */ @@ -1724,11 +2181,8 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, } /* Create user if needed */ pthread_mutex_lock(&acl_cache->lock); - error=replace_user_table(tables[0].table, - *Str, - 0, - revoke_grant ? 'N' : 'Y', - create_new_users); + error=replace_user_table(thd, tables[0].table, *Str, + 0, revoke_grant, create_new_users); pthread_mutex_unlock(&acl_cache->lock); if (error) { @@ -1766,21 +2220,21 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, /* If revoke_grant, calculate the new column privilege for tables_priv */ if (revoke_grant) { - class LEX_COLUMN *check; - List_iterator <LEX_COLUMN> iter(columns); + class LEX_COLUMN *column; + List_iterator <LEX_COLUMN> column_iter(columns); GRANT_COLUMN *grant_column; /* Fix old grants */ - while ((check = iter++)) + while ((column = column_iter++)) { grant_column = column_hash_search(grant_table, - check->column.ptr(), - check->column.length()); + column->column.ptr(), + column->column.length()); if (grant_column) - grant_column->rights&= ~(check->rights | rights); + grant_column->rights&= ~(column->rights | rights); } /* scan trough all columns to get new column grant */ - column_priv=0; + column_priv= 0; for (uint idx=0 ; idx < grant_table->hash_columns.records ; idx++) { grant_column= (GRANT_COLUMN*) hash_element(&grant_table->hash_columns, @@ -1821,17 +2275,17 @@ int mysql_table_grant (THD *thd, TABLE_LIST *table_list, pthread_mutex_unlock(&LOCK_grant); if (!result) send_ok(&thd->net); - /* Tables are automaticly closed */ + /* Tables are automatically closed */ DBUG_RETURN(result); } -int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, - bool revoke_grant) +int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, + ulong rights, bool revoke_grant) { List_iterator <LEX_USER> str_list (list); LEX_USER *Str; - char what; + char tmp_db[NAME_LEN+1]; bool create_new_users=0; TABLE_LIST tables[2]; DBUG_ENTER("mysql_grant"); @@ -1839,10 +2293,15 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, if (!initialized) { send_error(&(thd->net), ER_UNKNOWN_COM_ERROR); /* purecov: tested */ - return 1; /* purecov: tested */ + DBUG_RETURN(1); /* purecov: tested */ } - what = (revoke_grant) ? 'N' : 'Y'; + if (lower_case_table_names && db) + { + strmov(tmp_db,db); + casedn_str(tmp_db); + db=tmp_db; + } /* open the mysql.user and mysql.db tables */ @@ -1853,6 +2312,24 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; tables[0].table=tables[1].table=0; + +#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 && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(0); + } +#endif + if (open_and_lock_tables(thd,tables)) { // This should never happen close_thread_tables(thd); /* purecov: deadcode */ @@ -1862,7 +2339,7 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, if (!revoke_grant) create_new_users= test_if_create_new_users(thd); - // go through users in user_list + /* go through users in user_list */ pthread_mutex_lock(&LOCK_grant); VOID(pthread_mutex_lock(&acl_cache->lock)); grant_version++; @@ -1882,15 +2359,26 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, result= -1; continue; } - if ((replace_user_table(tables[0].table, + if ((replace_user_table(thd, + tables[0].table, *Str, - (!db ? rights : 0), what, create_new_users))) + (!db ? rights : 0), revoke_grant, + create_new_users))) result= -1; - else + else if (db) { - if (db && replace_db_table(tables[1].table, db, *Str, rights & DB_ACLS, - what)) - result= -1; + ulong db_rights= rights & DB_ACLS; + if (db_rights == rights) + { + if (replace_db_table(tables[1].table, db, *Str, db_rights, + revoke_grant)) + result= -1; + } + else + { + net_printf(&thd->net,ER_WRONG_USAGE,"DB GRANT","GLOBAL PRIVILEGES"); + result= 1; + } } } VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -1902,7 +2390,8 @@ int mysql_grant (THD *thd, const char *db, List <LEX_USER> &list, uint rights, DBUG_RETURN(result); } - /* Free grant array if possible */ + +/* Free grant array if possible */ void grant_free(void) { @@ -1916,11 +2405,12 @@ void grant_free(void) /* Init grant array if possible */ -int grant_init (void) +my_bool grant_init(THD *org_thd) { THD *thd; TABLE_LIST tables[2]; - int error = 0; + MYSQL_LOCK *lock; + my_bool return_val= 1; TABLE *t_table, *c_table; DBUG_ENTER("grant_init"); @@ -1929,16 +2419,15 @@ int grant_init (void) (hash_free_key) free_grant_table,0); init_sql_alloc(&memex,1024,0); + /* Don't do anything if running with --skip-grant */ if (!initialized) DBUG_RETURN(0); /* purecov: tested */ + if (!(thd=new THD)) DBUG_RETURN(1); /* purecov: deadcode */ - - thd->version=refresh_version; - thd->mysys_var=my_thread_var; - thd->current_tablenr=0; - thd->open_tables=0; - thd->db=my_strdup("mysql",MYF(0)); + thd->store_globals(); + thd->db= my_strdup("mysql",MYF(0)); + thd->db_length=5; // Safety bzero((char*) &tables,sizeof(tables)); tables[0].alias=tables[0].real_name= (char*) "tables_priv"; tables[1].alias=tables[1].real_name= (char*) "columns_priv"; @@ -1947,84 +2436,89 @@ int grant_init (void) tables[0].db=tables[1].db=thd->db; if (open_tables(thd,tables)) - { // No grant tables - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ - } + goto end; + TABLE *ptr[2]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; - MYSQL_LOCK *lock=mysql_lock_tables(thd,ptr,2); - if (!lock) - { - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ - } + if (!(lock=mysql_lock_tables(thd,ptr,2))) + goto end; t_table = tables[0].table; c_table = tables[1].table; t_table->file->index_init(0); if (t_table->file->index_first(t_table->record[0])) { t_table->file->index_end(); - mysql_unlock_tables(thd, lock); - thd->version--; // Force close to free memory - close_thread_tables(thd); - delete thd; - DBUG_RETURN(0); // Empty table is ok! + return_val= 0; + goto end_unlock; } - grant_option = TRUE; + grant_option= TRUE; t_table->file->index_end(); - MEM_ROOT *old_root=my_pthread_getspecific_ptr(MEM_ROOT*,THR_MALLOC); + /* Will be restored by org_thd->store_globals() */ my_pthread_setspecific_ptr(THR_MALLOC,&memex); - while (!error) + do { GRANT_TABLE *mem_check; if (!(mem_check=new GRANT_TABLE(t_table,c_table)) || mem_check->ok() && hash_insert(&hash_tables,(byte*) mem_check)) { /* This could only happen if we are out memory */ - my_pthread_setspecific_ptr(THR_MALLOC,old_root); /* purecov: deadcode */ - grant_option = FALSE; /* purecov: deadcode */ - mysql_unlock_tables(thd, lock); /* purecov: deadcode */ - close_thread_tables(thd); /* purecov: deadcode */ - delete thd; /* purecov: deadcode */ - DBUG_RETURN(1); /* purecov: deadcode */ + grant_option= FALSE; /* purecov: deadcode */ + goto end_unlock; } - error = t_table->file->index_next(t_table->record[0]); } - my_pthread_setspecific_ptr(THR_MALLOC,old_root); + while (!t_table->file->index_next(t_table->record[0])); + + return_val=0; // Return ok + +end_unlock: mysql_unlock_tables(thd, lock); thd->version--; // Force close to free memory + +end: close_thread_tables(thd); delete thd; - DBUG_RETURN(0); + if (org_thd) + org_thd->store_globals(); + else + { + /* Remember that we don't have a THD */ + my_pthread_setspecific_ptr(THR_THD, 0); + } + DBUG_RETURN(return_val); } -/* Reload grant array if possible */ +/* + Reload grant array if possible + + SYNOPSIS + grant_reload() + thd Thread handler -void grant_reload(void) + NOTES + Locked tables are checked by acl_init and doesn't have to be checked here +*/ + +void grant_reload(THD *thd) { - HASH old_hash_tables;bool old_grant_option; + HASH old_hash_tables; + bool old_grant_option; MEM_ROOT old_mem; DBUG_ENTER("grant_reload"); - // Locked tables are checked by acl_init and doesn't have to be checked here - pthread_mutex_lock(&LOCK_grant); grant_version++; old_hash_tables=hash_tables; - old_grant_option = grant_option; + old_grant_option= grant_option; old_mem = memex; - if (grant_init()) + if (grant_init(thd)) { // Error. Revert to old hash grant_free(); /* purecov: deadcode */ hash_tables=old_hash_tables; /* purecov: deadcode */ - grant_option = old_grant_option; /* purecov: deadcode */ + grant_option= old_grant_option; /* purecov: deadcode */ memex = old_mem; /* purecov: deadcode */ } else @@ -2038,11 +2532,11 @@ void grant_reload(void) /**************************************************************************** -** Check grants -** All errors are written directly to the client if command name is given ! + Check grants + All errors are written directly to the client if command name is given ! ****************************************************************************/ -bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, +bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, uint show_table, bool no_errors) { TABLE_LIST *table; @@ -2060,8 +2554,8 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, table->grant.want_privilege=0; continue; // Already checked } - const char *db = table->db ? table->db : thd->db; - GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip,db,user, + GRANT_TABLE *grant_table = table_hash_search(thd->host,thd->ip, + table->db,user, table->real_name,0); if (!grant_table) { @@ -2089,13 +2583,13 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, pthread_mutex_unlock(&LOCK_grant); return 0; - err: +err: pthread_mutex_unlock(&LOCK_grant); if (!no_errors) // Not a silent skip of table { const char *command=""; if (want_access & SELECT_ACL) - command ="select"; + command ="select"; else if (want_access & INSERT_ACL) command = "insert"; else if (want_access & UPDATE_ACL) @@ -2115,7 +2609,7 @@ bool check_grant(THD *thd, uint want_access, TABLE_LIST *tables, net_printf(&thd->net,ER_TABLEACCESS_DENIED_ERROR, command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, table ? table->real_name : "unknown"); } return 1; @@ -2128,7 +2622,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; - uint want_access=table->grant.want_privilege; + ulong want_access=table->grant.want_privilege; if (!want_access) return 0; // Already checked if (!grant_option) @@ -2136,7 +2630,7 @@ bool check_grant_column(THD *thd,TABLE *table, const char *name, pthread_mutex_lock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { @@ -2169,19 +2663,14 @@ err: err2: if (!show_tables) { - const char *command=""; - if (want_access & SELECT_ACL) - command ="select"; - else if (want_access & INSERT_ACL) - command = "insert"; - else if (want_access & UPDATE_ACL) - command = "update"; + char command[128]; + get_privilege_desc(command, sizeof(command), want_access); my_printf_error(ER_COLUMNACCESS_DENIED_ERROR, ER(ER_COLUMNACCESS_DENIED_ERROR), MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, name, table ? table->real_name : "unknown"); } @@ -2189,7 +2678,7 @@ err2: } -bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) +bool check_grant_all_columns(THD *thd, ulong want_access, TABLE *table) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; @@ -2201,7 +2690,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) pthread_mutex_lock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { @@ -2211,7 +2700,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) table->real_name,0); /* purecov: inspected */ table->grant.version=grant_version; /* purecov: inspected */ } - // The following should always be true + /* The following should always be true */ if (!(grant_table=table->grant.grant_table)) goto err; /* purecov: inspected */ @@ -2226,7 +2715,7 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) return 0; /* We must use my_printf_error() here! */ - err: +err: pthread_mutex_unlock(&LOCK_grant); const char *command=""; @@ -2239,18 +2728,18 @@ bool check_grant_all_columns(THD *thd,uint want_access, TABLE *table) MYF(0), command, thd->priv_user, - thd->host ? thd->host : (thd->ip ? thd->ip : "unknown"), + thd->host_or_ip, field ? field->field_name : "unknown", table->real_name); return 1; } -/**************************************************************************** -** Check if a user has the right to access a database -** Access is accepted if he has a grant for any table in the database -** Return 1 if access is denied -****************************************************************************/ +/* + Check if a user has the right to access a database + Access is accepted if he has a grant for any table in the database + Return 1 if access is denied +*/ bool check_grant_db(THD *thd,const char *db) { @@ -2278,10 +2767,10 @@ bool check_grant_db(THD *thd,const char *db) } /***************************************************************************** -** Functions to retrieve the grant for a table/column (for SHOW functions) + Functions to retrieve the grant for a table/column (for SHOW functions) *****************************************************************************/ -uint get_table_grant(THD *thd, TABLE_LIST *table) +ulong get_table_grant(THD *thd, TABLE_LIST *table) { uint privilege; char *user = thd->priv_user; @@ -2290,7 +2779,7 @@ uint get_table_grant(THD *thd, TABLE_LIST *table) pthread_mutex_lock(&LOCK_grant); grant_table = table_hash_search(thd->host,thd->ip,db,user, - table->real_name,0); + table->real_name, 0); table->grant.grant_table=grant_table; // Remember for column test table->grant.version=grant_version; if (grant_table) @@ -2301,14 +2790,14 @@ uint get_table_grant(THD *thd, TABLE_LIST *table) } -uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field) +ulong get_column_grant(THD *thd, TABLE_LIST *table, Field *field) { GRANT_TABLE *grant_table; GRANT_COLUMN *grant_column; - uint priv; + ulong priv; pthread_mutex_lock(&LOCK_grant); - // reload table if someone has modified any grants + /* reload table if someone has modified any grants */ if (table->grant.version != grant_version) { table->grant.grant_table= @@ -2333,20 +2822,46 @@ uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field) return priv; } +/* Help function for mysql_show_grants */ -/***************************************************************************** -** SHOW GRANTS : send to client grant-like strings depicting user@host -** privileges -*****************************************************************************/ +static void add_user_option(String *grant, ulong value, const char *name) +{ + if (value) + { + char buff[22], *p; // just as in int2str + grant->append(' '); + grant->append(name, strlen(name)); + grant->append(' '); + p=int10_to_str(value, buff, 10); + grant->append(buff,p-buff); + } +} static const char *command_array[]= -{"SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP","RELOAD","SHUTDOWN", - "PROCESS","FILE","GRANT","REFERENCES","INDEX","ALTER"}; -static int command_lengths[]={6,6,6,6,6,4,6,8,7,4,5,10,5,5}; +{ + "SELECT", "INSERT","UPDATE","DELETE","CREATE", "DROP", "RELOAD","SHUTDOWN", + "PROCESS","FILE","GRANT","REFERENCES","INDEX", "ALTER", "SHOW DATABASES", + "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", + "REPLICATION SLAVE", "REPLICATION CLIENT", +}; + +static uint command_lengths[]= +{ + 6,6,6,6,6,4,6,8,7,4,5,10,5,5,14,5,23,11,7,17,18 +}; -int mysql_show_grants(THD *thd,LEX_USER *lex_user) + +/* + SHOW GRANTS; Send grants for a user to the client + + IMPLEMENTATION + Send to client grant-like strings depicting user@host privileges +*/ + +int mysql_show_grants(THD *thd,LEX_USER *lex_user) { - uint counter, want_access,index; + ulong want_access; + uint counter,index; int error = 0; ACL_USER *acl_user; ACL_DB *acl_db; char buff[1024]; @@ -2379,10 +2894,10 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (!(host=acl_user->host.hostname)) host="%"; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) break; } - if (counter == acl_users.elements) + if (counter == acl_users.elements) { my_printf_error(ER_NONEXISTING_GRANT,ER(ER_NONEXISTING_GRANT), MYF(0),lex_user->user.str,lex_user->host.str); @@ -2403,7 +2918,6 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) VOID(pthread_mutex_lock(&acl_cache->lock)); /* Add first global access grants */ - if (acl_user->access || acl_user->password) { want_access=acl_user->access; String global(buff,sizeof(buff)); @@ -2414,13 +2928,13 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append("ALL PRIVILEGES",14); else if (!(want_access & ~GRANT_ACL)) global.append("USAGE",5); - else + else { bool found=0; - uint j,test_access= want_access & ~GRANT_ACL; + ulong j,test_access= want_access & ~GRANT_ACL; for (counter=0, j = SELECT_ACL;j <= GLOBAL_ACLS;counter++,j <<= 1) { - if (test_access & j) + if (test_access & j) { if (found) global.append(", ",2); @@ -2430,7 +2944,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } global.append (" ON *.* TO '",12); - global.append(lex_user->user.str,lex_user->user.length); + global.append(lex_user->user.str,lex_user->user.length); global.append ("'@'",3); global.append(lex_user->host.str,lex_user->host.length); global.append ('\''); @@ -2442,8 +2956,53 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(passd_buff); global.append('\''); } - if (want_access & GRANT_ACL) - global.append(" WITH GRANT OPTION",18); + /* "show grants" SSL related stuff */ + if (acl_user->ssl_type == SSL_TYPE_ANY) + global.append(" REQUIRE SSL",12); + else if (acl_user->ssl_type == SSL_TYPE_X509) + global.append(" REQUIRE X509",13); + else if (acl_user->ssl_type == SSL_TYPE_SPECIFIED) + { + int ssl_options = 0; + global.append(" REQUIRE ",9); + if (acl_user->x509_issuer) + { + ssl_options++; + global.append("ISSUER \'",8); + global.append(acl_user->x509_issuer,strlen(acl_user->x509_issuer)); + global.append('\''); + } + if (acl_user->x509_subject) + { + if (ssl_options++) + global.append(' '); + global.append("SUBJECT \'",9); + global.append(acl_user->x509_subject,strlen(acl_user->x509_subject)); + global.append('\''); + } + if (acl_user->ssl_cipher) + { + if (ssl_options++) + global.append(' '); + global.append("CIPHER '",8); + global.append(acl_user->ssl_cipher,strlen(acl_user->ssl_cipher)); + global.append('\''); + } + } + if ((want_access & GRANT_ACL) || + (acl_user->user_resource.questions | acl_user->user_resource.updates | + acl_user->user_resource.connections)) + { + global.append(" WITH",5); + if (want_access & GRANT_ACL) + global.append(" GRANT OPTION",13); + add_user_option(&global, acl_user->user_resource.questions, + "MAX_QUERIES_PER_HOUR"); + add_user_option(&global, acl_user->user_resource.updates, + "MAX_UPDATES_PER_HOUR"); + add_user_option(&global, acl_user->user_resource.connections, + "MAX_CONNECTIONS_PER_HOUR"); + } thd->packet.length(0); net_store_data(&thd->packet,global.ptr(),global.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2465,10 +3024,10 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) host=""; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) { want_access=acl_db->access; - if (want_access) + if (want_access) { String db(buff,sizeof(buff)); db.length(0); @@ -2476,10 +3035,12 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) if (test_all_bits(want_access,(DB_ACLS & ~GRANT_ACL))) db.append("ALL PRIVILEGES",14); + else if (!(want_access & ~GRANT_ACL)) + db.append("USAGE",5); else { int found=0, cnt; - uint j,test_access= want_access & ~GRANT_ACL; + ulong j,test_access= want_access & ~GRANT_ACL; for (cnt=0, j = SELECT_ACL; j <= DB_ACLS; cnt++,j <<= 1) { if (test_access & j) @@ -2491,15 +3052,15 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - db.append (" ON `",5); + db.append(" ON `",5); db.append(acl_db->db); - db.append ("`.* TO '",8); - db.append(lex_user->user.str,lex_user->user.length); - db.append ("'@'",3); + db.append("`.* TO '",8); + db.append(lex_user->user.str,lex_user->user.length); + db.append("'@'",3); db.append(lex_user->host.str, lex_user->host.length); - db.append ('\''); + db.append('\''); if (want_access & GRANT_ACL) - db.append(" WITH GRANT OPTION",18); + db.append(" WITH GRANT OPTION",18); thd->packet.length(0); net_store_data(&thd->packet,db.ptr(),db.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2516,7 +3077,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) for (index=0 ; index < hash_tables.records ; index++) { const char *user,*host; - GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index); + GRANT_TABLE *grant_table= (GRANT_TABLE*) hash_element(&hash_tables,index); if (!(user=grant_table->user)) user=""; @@ -2524,46 +3085,58 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) host=""; if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str,host)) + !my_strcasecmp(lex_user->host.str,host)) { - want_access=grant_table->privs; - if ((want_access | grant_table->cols) != 0) + ulong table_access= grant_table->privs; + if ((table_access | grant_table->cols) != 0) { String global(buff,sizeof(buff)); global.length(0); global.append("GRANT ",6); - if (test_all_bits(grant_table->privs,(TABLE_ACLS & ~GRANT_ACL))) + if (test_all_bits(table_access, (TABLE_ACLS & ~GRANT_ACL))) global.append("ALL PRIVILEGES",14); - else + else if (!(table_access & ~GRANT_ACL)) + global.append("USAGE",5); + else { - int found=0; - uint j,test_access= (want_access | grant_table->cols) & ~GRANT_ACL; + int found= 0; + ulong j,test_access= (table_access | grant_table->cols) & ~GRANT_ACL; - for (counter=0, j = SELECT_ACL;j <= TABLE_ACLS; counter++,j <<= 1) + for (counter= 0, j= SELECT_ACL; j <= TABLE_ACLS; counter++, j<<= 1) { - if (test_access & j) + if (test_access & j) { if (found) global.append(", ",2); - found = 1; + found= 1; global.append(command_array[counter],command_lengths[counter]); - if (grant_table->cols) + if (grant_table->cols) { - uint found_col=0; + uint found_col= 0; for (uint col_index=0 ; col_index < grant_table->hash_columns.records ; col_index++) { GRANT_COLUMN *grant_column = (GRANT_COLUMN*) hash_element(&grant_table->hash_columns,col_index); - if (grant_column->rights & j) + if (grant_column->rights & j) { - if (!found_col) + if (!found_col) { + found_col= 1; + /* + If we have a duplicated table level privilege, we + must write the access privilege name again. + */ + if (table_access & j) + { + global.append(", ", 2); + global.append(command_array[counter], + command_lengths[counter]); + } global.append(" (",2); - found_col=1; } else global.append(", ",2); @@ -2577,17 +3150,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } - global.append(" ON ",4); + global.append(" ON `",5); global.append(grant_table->db); - global.append(".",1); + global.append("`.`",3); global.append(grant_table->tname); - global.append(" TO '",5); - global.append(lex_user->user.str,lex_user->user.length); + global.append("` TO '",6); + global.append(lex_user->user.str,lex_user->user.length); global.append("'@'",3); - global.append(lex_user->host.str,lex_user->host.length); + global.append(lex_user->host.str,lex_user->host.length); global.append('\''); - if (want_access & GRANT_ACL) - global.append(" WITH GRANT OPTION",18); + if (table_access & GRANT_ACL) + global.append(" WITH GRANT OPTION",18); thd->packet.length(0); net_store_data(&thd->packet,global.ptr(),global.length()); if (my_net_write(&thd->net,(char*) thd->packet.ptr(), @@ -2600,7 +3173,7 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } } - end: +end: VOID(pthread_mutex_unlock(&acl_cache->lock)); pthread_mutex_unlock(&LOCK_grant); @@ -2609,8 +3182,47 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) } +/* + Make a clear-text version of the requested privilege. +*/ + +void get_privilege_desc(char *to, uint max_length, ulong access) +{ + uint pos; + char *start=to; + DBUG_ASSERT(max_length >= 30); // For end ',' removal + + if (access) + { + max_length--; // Reserve place for end-zero + for (pos=0 ; access ; pos++, access>>=1) + { + if ((access & 1) && + command_lengths[pos] + (uint) (to-start) < max_length) + { + to= strmov(to, command_array[pos]); + *to++=','; + } + } + to--; // Remove end ',' + } + *to=0; +} + + +void get_mqh(const char *user, const char *host, USER_CONN *uc) +{ + ACL_USER *acl_user; + if (initialized && (acl_user= find_acl_user(host,user))) + uc->user_resources= acl_user->user_resource; + else + bzero((char*) &uc->user_resources, sizeof(uc->user_resources)); +} + + + /***************************************************************************** -** Instantiate used templates + Instantiate used templates *****************************************************************************/ #ifdef __GNUC__ |